synapse-storage 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1038 -0
- package/dist/_examples/_example5.d.ts +1 -0
- package/dist/_examples/_example5.d.ts.map +1 -0
- package/dist/_examples/_example5.js +184 -0
- package/dist/_examples/_example5.js.map +1 -0
- package/dist/_examples/_example6.d.ts +1 -0
- package/dist/_examples/_example6.d.ts.map +1 -0
- package/dist/_examples/_example6.js +168 -0
- package/dist/_examples/_example6.js.map +1 -0
- package/dist/_examples/example1.d.ts +1 -0
- package/dist/_examples/example1.d.ts.map +1 -0
- package/dist/_examples/example1.js +240 -0
- package/dist/_examples/example1.js.map +1 -0
- package/dist/_examples/example2.d.ts +1 -0
- package/dist/_examples/example2.d.ts.map +1 -0
- package/dist/_examples/example2.js +81 -0
- package/dist/_examples/example2.js.map +1 -0
- package/dist/_examples/example3.d.ts +1 -0
- package/dist/_examples/example3.d.ts.map +1 -0
- package/dist/_examples/example3.js +99 -0
- package/dist/_examples/example3.js.map +1 -0
- package/dist/_examples/example4.d.ts +1 -0
- package/dist/_examples/example4.d.ts.map +1 -0
- package/dist/_examples/example4.js +172 -0
- package/dist/_examples/example4.js.map +1 -0
- package/dist/_examples/example5.d.ts +1 -0
- package/dist/_examples/example5.d.ts.map +1 -0
- package/dist/_examples/example5.js +184 -0
- package/dist/_examples/example5.js.map +1 -0
- package/dist/_examples/example6.d.ts +1 -0
- package/dist/_examples/example6.d.ts.map +1 -0
- package/dist/_examples/example6.js +168 -0
- package/dist/_examples/example6.js.map +1 -0
- package/dist/_examples/plugins.d.ts +1 -0
- package/dist/_examples/plugins.d.ts.map +1 -0
- package/dist/_examples/plugins.js +176 -0
- package/dist/_examples/plugins.js.map +1 -0
- package/dist/_examples/pokemons/PokemonList.d.ts +5 -0
- package/dist/_examples/pokemons/PokemonList.d.ts.map +1 -0
- package/dist/_examples/pokemons/PokemonList.js +166 -0
- package/dist/_examples/pokemons/PokemonList.js.map +1 -0
- package/dist/_examples/pokemons/api.d.ts +8 -0
- package/dist/_examples/pokemons/api.d.ts.map +1 -0
- package/dist/_examples/pokemons/api.js +45 -0
- package/dist/_examples/pokemons/api.js.map +1 -0
- package/dist/_examples/pokemons/app.config.d.ts +15 -0
- package/dist/_examples/pokemons/app.config.d.ts.map +1 -0
- package/dist/_examples/pokemons/app.config.js +15 -0
- package/dist/_examples/pokemons/app.config.js.map +1 -0
- package/dist/_examples/pokemons/dispatchers/pokemon.dispatcher.d.ts +9 -0
- package/dist/_examples/pokemons/dispatchers/pokemon.dispatcher.d.ts.map +1 -0
- package/dist/_examples/pokemons/dispatchers/pokemon.dispatcher.js +122 -0
- package/dist/_examples/pokemons/dispatchers/pokemon.dispatcher.js.map +1 -0
- package/dist/_examples/pokemons/effects/pokemon.effects.d.ts +16 -0
- package/dist/_examples/pokemons/effects/pokemon.effects.d.ts.map +1 -0
- package/dist/_examples/pokemons/effects/pokemon.effects.js +39 -0
- package/dist/_examples/pokemons/effects/pokemon.effects.js.map +1 -0
- package/dist/_examples/pokemons/middlewares/pokenon.middlewares.d.ts +4 -0
- package/dist/_examples/pokemons/middlewares/pokenon.middlewares.d.ts.map +1 -0
- package/dist/_examples/pokemons/middlewares/pokenon.middlewares.js +27 -0
- package/dist/_examples/pokemons/middlewares/pokenon.middlewares.js.map +1 -0
- package/dist/_examples/pokemons/pokemon.dispatcher.d.ts +9 -0
- package/dist/_examples/pokemons/pokemon.dispatcher.d.ts.map +1 -0
- package/dist/_examples/pokemons/pokemon.dispatcher.js +122 -0
- package/dist/_examples/pokemons/pokemon.dispatcher.js.map +1 -0
- package/dist/_examples/pokemons/pokemon.effects.d.ts +5 -0
- package/dist/_examples/pokemons/pokemon.effects.d.ts.map +1 -0
- package/dist/_examples/pokemons/pokemon.effects.js +39 -0
- package/dist/_examples/pokemons/pokemon.effects.js.map +1 -0
- package/dist/_examples/pokemons/pokemon.selectors.d.ts +5 -0
- package/dist/_examples/pokemons/pokemon.selectors.d.ts.map +1 -0
- package/dist/_examples/pokemons/pokemon.selectors.js +10 -0
- package/dist/_examples/pokemons/pokemon.selectors.js.map +1 -0
- package/dist/_examples/pokemons/pokemon.storage.d.ts +3 -0
- package/dist/_examples/pokemons/pokemon.storage.d.ts.map +1 -0
- package/dist/_examples/pokemons/pokemon.storage.js +14 -0
- package/dist/_examples/pokemons/pokemon.storage.js.map +1 -0
- package/dist/_examples/pokemons/pokemon1.selectors.d.ts +5 -0
- package/dist/_examples/pokemons/pokemon1.selectors.d.ts.map +1 -0
- package/dist/_examples/pokemons/pokemon1.selectors.js +8 -0
- package/dist/_examples/pokemons/pokemon1.selectors.js.map +1 -0
- package/dist/_examples/pokemons/pokenon.middlewares.d.ts +4 -0
- package/dist/_examples/pokemons/pokenon.middlewares.d.ts.map +1 -0
- package/dist/_examples/pokemons/pokenon.middlewares.js +27 -0
- package/dist/_examples/pokemons/pokenon.middlewares.js.map +1 -0
- package/dist/_examples/pokemons/selectors/pokemon.selectors.d.ts +5 -0
- package/dist/_examples/pokemons/selectors/pokemon.selectors.d.ts.map +1 -0
- package/dist/_examples/pokemons/selectors/pokemon.selectors.js +10 -0
- package/dist/_examples/pokemons/selectors/pokemon.selectors.js.map +1 -0
- package/dist/_examples/pokemons/selectors/pokemon1.selectors.d.ts +5 -0
- package/dist/_examples/pokemons/selectors/pokemon1.selectors.d.ts.map +1 -0
- package/dist/_examples/pokemons/selectors/pokemon1.selectors.js +8 -0
- package/dist/_examples/pokemons/selectors/pokemon1.selectors.js.map +1 -0
- package/dist/_examples/pokemons/storages/pokemon.storage.d.ts +3 -0
- package/dist/_examples/pokemons/storages/pokemon.storage.d.ts.map +1 -0
- package/dist/_examples/pokemons/storages/pokemon.storage.js +14 -0
- package/dist/_examples/pokemons/storages/pokemon.storage.js.map +1 -0
- package/dist/_examples/pokemons/store.d.ts +1 -0
- package/dist/_examples/pokemons/store.d.ts.map +1 -0
- package/dist/_examples/pokemons/store.js +2 -0
- package/dist/_examples/pokemons/store.js.map +1 -0
- package/dist/_examples/pokemons/store1.d.ts +23 -0
- package/dist/_examples/pokemons/store1.d.ts.map +1 -0
- package/dist/_examples/pokemons/store1.js +35 -0
- package/dist/_examples/pokemons/store1.js.map +1 -0
- package/dist/_examples/pokemons/store2.d.ts +23 -0
- package/dist/_examples/pokemons/store2.d.ts.map +1 -0
- package/dist/_examples/pokemons/store2.js +34 -0
- package/dist/_examples/pokemons/store2.js.map +1 -0
- package/dist/_examples/pokemons/types.d.ts +26 -0
- package/dist/_examples/pokemons/types.d.ts.map +1 -0
- package/dist/_examples/pokemons/types.js +2 -0
- package/dist/_examples/pokemons/types.js.map +1 -0
- package/dist/_utils/chunk.util.d.ts +8 -0
- package/dist/_utils/chunk.util.d.ts.map +1 -0
- package/dist/_utils/chunk.util.js +21 -0
- package/dist/_utils/chunk.util.js.map +1 -0
- package/dist/_utils/deepMerge.util.d.ts +2 -0
- package/dist/_utils/deepMerge.util.d.ts.map +1 -0
- package/dist/_utils/deepMerge.util.js +16 -0
- package/dist/_utils/deepMerge.util.js.map +1 -0
- package/dist/_utils/flatMap.util.d.ts +10 -0
- package/dist/_utils/flatMap.util.d.ts.map +1 -0
- package/dist/_utils/flatMap.util.js +23 -0
- package/dist/_utils/flatMap.util.js.map +1 -0
- package/dist/_utils/index.d.ts +4 -0
- package/dist/_utils/index.d.ts.map +1 -0
- package/dist/_utils/index.js +4 -0
- package/dist/_utils/index.js.map +1 -0
- package/dist/api/api.module.d.ts +38 -0
- package/dist/api/api.module.d.ts.map +1 -0
- package/dist/api/api.module.js +82 -0
- package/dist/api/api.module.js.map +1 -0
- package/dist/api/components/endpoint.d.ts +26 -0
- package/dist/api/components/endpoint.d.ts.map +1 -0
- package/dist/api/components/endpoint.js +253 -0
- package/dist/api/components/endpoint.js.map +1 -0
- package/dist/api/components/query-storage.d.ts +84 -0
- package/dist/api/components/query-storage.d.ts.map +1 -0
- package/dist/api/components/query-storage.js +221 -0
- package/dist/api/components/query-storage.js.map +1 -0
- package/dist/api/example.d.ts +83 -0
- package/dist/api/example.d.ts.map +1 -0
- package/dist/api/example.js +85 -0
- package/dist/api/example.js.map +1 -0
- package/dist/api/index.d.ts +4 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +6 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/types/api.interface.d.ts +108 -0
- package/dist/api/types/api.interface.d.ts.map +1 -0
- package/dist/api/types/api.interface.js +19 -0
- package/dist/api/types/api.interface.js.map +1 -0
- package/dist/api/types/endpoint.interface.d.ts +116 -0
- package/dist/api/types/endpoint.interface.d.ts.map +1 -0
- package/dist/api/types/endpoint.interface.js +2 -0
- package/dist/api/types/endpoint.interface.js.map +1 -0
- package/dist/api/types/query.interface.d.ts +87 -0
- package/dist/api/types/query.interface.d.ts.map +1 -0
- package/dist/api/types/query.interface.js +2 -0
- package/dist/api/types/query.interface.js.map +1 -0
- package/dist/api/utils/api-helpers.d.ts +22 -0
- package/dist/api/utils/api-helpers.d.ts.map +1 -0
- package/dist/api/utils/api-helpers.js +44 -0
- package/dist/api/utils/api-helpers.js.map +1 -0
- package/dist/api/utils/create-header-context.d.ts +10 -0
- package/dist/api/utils/create-header-context.d.ts.map +1 -0
- package/dist/api/utils/create-header-context.js +36 -0
- package/dist/api/utils/create-header-context.js.map +1 -0
- package/dist/api/utils/endpoint-headers.d.ts +23 -0
- package/dist/api/utils/endpoint-headers.d.ts.map +1 -0
- package/dist/api/utils/endpoint-headers.js +57 -0
- package/dist/api/utils/endpoint-headers.js.map +1 -0
- package/dist/api/utils/fetch-base-query.d.ts +9 -0
- package/dist/api/utils/fetch-base-query.d.ts.map +1 -0
- package/dist/api/utils/fetch-base-query.js +181 -0
- package/dist/api/utils/fetch-base-query.js.map +1 -0
- package/dist/api/utils/file-utils.d.ts +43 -0
- package/dist/api/utils/file-utils.d.ts.map +1 -0
- package/dist/api/utils/file-utils.js +108 -0
- package/dist/api/utils/file-utils.js.map +1 -0
- package/dist/api/utils/get-cacheable-headers.d.ts +8 -0
- package/dist/api/utils/get-cacheable-headers.d.ts.map +1 -0
- package/dist/api/utils/get-cacheable-headers.js +21 -0
- package/dist/api/utils/get-cacheable-headers.js.map +1 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/selector/index.d.ts +3 -0
- package/dist/core/selector/index.d.ts.map +1 -0
- package/dist/core/selector/index.js +2 -0
- package/dist/core/selector/index.js.map +1 -0
- package/dist/core/selector/selector.interface.d.ts +91 -0
- package/dist/core/selector/selector.interface.d.ts.map +1 -0
- package/dist/core/selector/selector.interface.js +2 -0
- package/dist/core/selector/selector.interface.js.map +1 -0
- package/dist/core/selector/selector.module.d.ts +29 -0
- package/dist/core/selector/selector.module.d.ts.map +1 -0
- package/dist/core/selector/selector.module.js +467 -0
- package/dist/core/selector/selector.module.js.map +1 -0
- package/dist/core/storage/adapters/base-storage.service.d.ts +65 -0
- package/dist/core/storage/adapters/base-storage.service.d.ts.map +1 -0
- package/dist/core/storage/adapters/base-storage.service.js +660 -0
- package/dist/core/storage/adapters/base-storage.service.js.map +1 -0
- package/dist/core/storage/adapters/idb.d.ts +52 -0
- package/dist/core/storage/adapters/idb.d.ts.map +1 -0
- package/dist/core/storage/adapters/idb.js +528 -0
- package/dist/core/storage/adapters/idb.js.map +1 -0
- package/dist/core/storage/adapters/indexed-DB.service.d.ts +63 -0
- package/dist/core/storage/adapters/indexed-DB.service.d.ts.map +1 -0
- package/dist/core/storage/adapters/indexed-DB.service.js +595 -0
- package/dist/core/storage/adapters/indexed-DB.service.js.map +1 -0
- package/dist/core/storage/adapters/indexed-DB.service.old.d.ts +38 -0
- package/dist/core/storage/adapters/indexed-DB.service.old.d.ts.map +1 -0
- package/dist/core/storage/adapters/indexed-DB.service.old.js +318 -0
- package/dist/core/storage/adapters/indexed-DB.service.old.js.map +1 -0
- package/dist/core/storage/adapters/indexed-DB.service1.d.ts +38 -0
- package/dist/core/storage/adapters/indexed-DB.service1.d.ts.map +1 -0
- package/dist/core/storage/adapters/indexed-DB.service1.js +318 -0
- package/dist/core/storage/adapters/indexed-DB.service1.js.map +1 -0
- package/dist/core/storage/adapters/indexed-DB.service2.d.ts +61 -0
- package/dist/core/storage/adapters/indexed-DB.service2.d.ts.map +1 -0
- package/dist/core/storage/adapters/indexed-DB.service2.js +596 -0
- package/dist/core/storage/adapters/indexed-DB.service2.js.map +1 -0
- package/dist/core/storage/adapters/local-storage.service.d.ts +21 -0
- package/dist/core/storage/adapters/local-storage.service.d.ts.map +1 -0
- package/dist/core/storage/adapters/local-storage.service.js +99 -0
- package/dist/core/storage/adapters/local-storage.service.js.map +1 -0
- package/dist/core/storage/adapters/memory-storage.service.d.ts +22 -0
- package/dist/core/storage/adapters/memory-storage.service.d.ts.map +1 -0
- package/dist/core/storage/adapters/memory-storage.service.js +99 -0
- package/dist/core/storage/adapters/memory-storage.service.js.map +1 -0
- package/dist/core/storage/adapters/path.utils.d.ts +5 -0
- package/dist/core/storage/adapters/path.utils.d.ts.map +1 -0
- package/dist/core/storage/adapters/path.utils.js +35 -0
- package/dist/core/storage/adapters/path.utils.js.map +1 -0
- package/dist/core/storage/factory/createSynapseStorage.factory.d.ts +1 -0
- package/dist/core/storage/factory/createSynapseStorage.factory.d.ts.map +1 -0
- package/dist/core/storage/factory/createSynapseStorage.factory.js +2 -0
- package/dist/core/storage/factory/createSynapseStorage.factory.js.map +1 -0
- package/dist/core/storage/factory/index.d.ts +2 -0
- package/dist/core/storage/factory/index.d.ts.map +1 -0
- package/dist/core/storage/factory/index.js +2 -0
- package/dist/core/storage/factory/index.js.map +1 -0
- package/dist/core/storage/index.d.ts +11 -0
- package/dist/core/storage/index.d.ts.map +1 -0
- package/dist/core/storage/index.js +12 -0
- package/dist/core/storage/index.js.map +1 -0
- package/dist/core/storage/middlewares/broadcast.middleware.d.ts +9 -0
- package/dist/core/storage/middlewares/broadcast.middleware.d.ts.map +1 -0
- package/dist/core/storage/middlewares/broadcast.middleware.js +115 -0
- package/dist/core/storage/middlewares/broadcast.middleware.js.map +1 -0
- package/dist/core/storage/middlewares/index.d.ts +4 -0
- package/dist/core/storage/middlewares/index.d.ts.map +1 -0
- package/dist/core/storage/middlewares/index.js +3 -0
- package/dist/core/storage/middlewares/index.js.map +1 -0
- package/dist/core/storage/middlewares/storage-batching.middleware.d.ts +7 -0
- package/dist/core/storage/middlewares/storage-batching.middleware.d.ts.map +1 -0
- package/dist/core/storage/middlewares/storage-batching.middleware.js +36 -0
- package/dist/core/storage/middlewares/storage-batching.middleware.js.map +1 -0
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.d.ts +7 -0
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.d.ts.map +1 -0
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.js +46 -0
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.js.map +1 -0
- package/dist/core/storage/modules/plugin/plugin.interface.d.ts +201 -0
- package/dist/core/storage/modules/plugin/plugin.interface.d.ts.map +1 -0
- package/dist/core/storage/modules/plugin/plugin.interface.js +2 -0
- package/dist/core/storage/modules/plugin/plugin.interface.js.map +1 -0
- package/dist/core/storage/modules/plugin/plugin.service.d.ts +25 -0
- package/dist/core/storage/modules/plugin/plugin.service.d.ts.map +1 -0
- package/dist/core/storage/modules/plugin/plugin.service.js +186 -0
- package/dist/core/storage/modules/plugin/plugin.service.js.map +1 -0
- package/dist/core/storage/storage.interface.d.ts +70 -0
- package/dist/core/storage/storage.interface.d.ts.map +1 -0
- package/dist/core/storage/storage.interface.js +10 -0
- package/dist/core/storage/storage.interface.js.map +1 -0
- package/dist/core/storage/utils/batch.utils.d.ts +33 -0
- package/dist/core/storage/utils/batch.utils.d.ts.map +1 -0
- package/dist/core/storage/utils/batch.utils.js +88 -0
- package/dist/core/storage/utils/batch.utils.js.map +1 -0
- package/dist/core/storage/utils/broadcast.util.d.ts +48 -0
- package/dist/core/storage/utils/broadcast.util.d.ts.map +1 -0
- package/dist/core/storage/utils/broadcast.util.js +162 -0
- package/dist/core/storage/utils/broadcast.util.js.map +1 -0
- package/dist/core/storage/utils/cache.util.d.ts +33 -0
- package/dist/core/storage/utils/cache.util.d.ts.map +1 -0
- package/dist/core/storage/utils/cache.util.js +47 -0
- package/dist/core/storage/utils/cache.util.js.map +1 -0
- package/dist/core/storage/utils/middleware-module.d.ts +46 -0
- package/dist/core/storage/utils/middleware-module.d.ts.map +1 -0
- package/dist/core/storage/utils/middleware-module.js +109 -0
- package/dist/core/storage/utils/middleware-module.js.map +1 -0
- package/dist/core/storage/utils/storage-key.d.ts +11 -0
- package/dist/core/storage/utils/storage-key.d.ts.map +1 -0
- package/dist/core/storage/utils/storage-key.js +21 -0
- package/dist/core/storage/utils/storage-key.js.map +1 -0
- package/dist/core/storage/utils/storage.utils.d.ts +17 -0
- package/dist/core/storage/utils/storage.utils.d.ts.map +1 -0
- package/dist/core/storage/utils/storage.utils.js +57 -0
- package/dist/core/storage/utils/storage.utils.js.map +1 -0
- package/dist/examples/_example5.d.ts +1 -0
- package/dist/examples/_example5.d.ts.map +1 -0
- package/dist/examples/_example5.js +184 -0
- package/dist/examples/_example5.js.map +1 -0
- package/dist/examples/_example6.d.ts +1 -0
- package/dist/examples/_example6.d.ts.map +1 -0
- package/dist/examples/_example6.js +168 -0
- package/dist/examples/_example6.js.map +1 -0
- package/dist/examples/plugins.d.ts +1 -0
- package/dist/examples/plugins.d.ts.map +1 -0
- package/dist/examples/plugins.js +176 -0
- package/dist/examples/plugins.js.map +1 -0
- package/dist/examples/pokemons/PokemonList.d.ts +2 -0
- package/dist/examples/pokemons/PokemonList.d.ts.map +1 -0
- package/dist/examples/pokemons/PokemonList.js +110 -0
- package/dist/examples/pokemons/PokemonList.js.map +1 -0
- package/dist/examples/pokemons/api.d.ts +22 -0
- package/dist/examples/pokemons/api.d.ts.map +1 -0
- package/dist/examples/pokemons/api.js +43 -0
- package/dist/examples/pokemons/api.js.map +1 -0
- package/dist/examples/pokemons/app.config.d.ts +15 -0
- package/dist/examples/pokemons/app.config.d.ts.map +1 -0
- package/dist/examples/pokemons/app.config.js +15 -0
- package/dist/examples/pokemons/app.config.js.map +1 -0
- package/dist/examples/pokemons/dispatchers/pokemon.dispatcher.js +128 -0
- package/dist/examples/pokemons/dispatchers/pokemon.dispatcher.js.map +1 -0
- package/dist/examples/pokemons/effects/pokemon.effects.d.ts +16 -0
- package/dist/examples/pokemons/effects/pokemon.effects.d.ts.map +1 -0
- package/dist/examples/pokemons/effects/pokemon.effects.js +51 -0
- package/dist/examples/pokemons/effects/pokemon.effects.js.map +1 -0
- package/dist/examples/pokemons/middlewares/pokenon.middlewares.d.ts +4 -0
- package/dist/examples/pokemons/middlewares/pokenon.middlewares.d.ts.map +1 -0
- package/dist/examples/pokemons/middlewares/pokenon.middlewares.js +28 -0
- package/dist/examples/pokemons/middlewares/pokenon.middlewares.js.map +1 -0
- package/dist/examples/pokemons/selectors/pokemon.selectors.d.ts +13 -0
- package/dist/examples/pokemons/selectors/pokemon.selectors.d.ts.map +1 -0
- package/dist/examples/pokemons/selectors/pokemon.selectors.js +7 -0
- package/dist/examples/pokemons/selectors/pokemon.selectors.js.map +1 -0
- package/dist/examples/pokemons/storages/pokemon.storage.d.ts +5 -0
- package/dist/examples/pokemons/storages/pokemon.storage.d.ts.map +1 -0
- package/dist/examples/pokemons/storages/pokemon.storage.js +13 -0
- package/dist/examples/pokemons/storages/pokemon.storage.js.map +1 -0
- package/dist/examples/pokemons/store.d.ts +35 -0
- package/dist/examples/pokemons/store.d.ts.map +1 -0
- package/dist/examples/pokemons/store.js +21 -0
- package/dist/examples/pokemons/store.js.map +1 -0
- package/dist/examples/pokemons/types.d.ts +26 -0
- package/dist/examples/pokemons/types.d.ts.map +1 -0
- package/dist/examples/pokemons/types.js +2 -0
- package/dist/examples/pokemons/types.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/react/hooks/index.d.ts +3 -0
- package/dist/react/hooks/index.d.ts.map +1 -0
- package/dist/react/hooks/index.js +3 -0
- package/dist/react/hooks/index.js.map +1 -0
- package/dist/react/hooks/useSelector.d.ts +22 -0
- package/dist/react/hooks/useSelector.d.ts.map +1 -0
- package/dist/react/hooks/useSelector.js +104 -0
- package/dist/react/hooks/useSelector.js.map +1 -0
- package/dist/react/hooks/useStorageSubscribe.d.ts +12 -0
- package/dist/react/hooks/useStorageSubscribe.d.ts.map +1 -0
- package/dist/react/hooks/useStorageSubscribe.js +49 -0
- package/dist/react/hooks/useStorageSubscribe.js.map +1 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +3 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/utils/createSynapse.d.ts +47 -0
- package/dist/react/utils/createSynapse.d.ts.map +1 -0
- package/dist/react/utils/createSynapse.js +83 -0
- package/dist/react/utils/createSynapse.js.map +1 -0
- package/dist/react/utils/createSynapseContext.d.ts +29 -0
- package/dist/react/utils/createSynapseContext.d.ts.map +1 -0
- package/dist/react/utils/createSynapseContext.js +112 -0
- package/dist/react/utils/createSynapseContext.js.map +1 -0
- package/dist/react/utils/createSynapseCtx.d.ts +30 -0
- package/dist/react/utils/createSynapseCtx.d.ts.map +1 -0
- package/dist/react/utils/createSynapseCtx.js +124 -0
- package/dist/react/utils/createSynapseCtx.js.map +1 -0
- package/dist/react/utils/index.d.ts +2 -0
- package/dist/react/utils/index.d.ts.map +1 -0
- package/dist/react/utils/index.js +2 -0
- package/dist/react/utils/index.js.map +1 -0
- package/dist/reactive/dispatcher/dispatcher.module.d.ts +195 -0
- package/dist/reactive/dispatcher/dispatcher.module.d.ts.map +1 -0
- package/dist/reactive/dispatcher/dispatcher.module.js +288 -0
- package/dist/reactive/dispatcher/dispatcher.module.js.map +1 -0
- package/dist/reactive/dispatcher/index.d.ts +3 -0
- package/dist/reactive/dispatcher/index.d.ts.map +1 -0
- package/dist/reactive/dispatcher/index.js +3 -0
- package/dist/reactive/dispatcher/index.js.map +1 -0
- package/dist/reactive/dispatcher/middlewares/index.d.ts +2 -0
- package/dist/reactive/dispatcher/middlewares/index.d.ts.map +1 -0
- package/dist/reactive/dispatcher/middlewares/index.js +2 -0
- package/dist/reactive/dispatcher/middlewares/index.js.map +1 -0
- package/dist/reactive/dispatcher/middlewares/logger.middleware.d.ts +31 -0
- package/dist/reactive/dispatcher/middlewares/logger.middleware.d.ts.map +1 -0
- package/dist/reactive/dispatcher/middlewares/logger.middleware.js +126 -0
- package/dist/reactive/dispatcher/middlewares/logger.middleware.js.map +1 -0
- package/dist/reactive/effects/effects.module.d.ts +140 -0
- package/dist/reactive/effects/effects.module.d.ts.map +1 -0
- package/dist/reactive/effects/effects.module.js +263 -0
- package/dist/reactive/effects/effects.module.js.map +1 -0
- package/dist/reactive/effects/index.d.ts +2 -0
- package/dist/reactive/effects/index.d.ts.map +1 -0
- package/dist/reactive/effects/index.js +2 -0
- package/dist/reactive/effects/index.js.map +1 -0
- package/dist/reactive/effects/utils/chunkRequestConsistent.d.ts +12 -0
- package/dist/reactive/effects/utils/chunkRequestConsistent.d.ts.map +1 -0
- package/dist/reactive/effects/utils/chunkRequestConsistent.js +16 -0
- package/dist/reactive/effects/utils/chunkRequestConsistent.js.map +1 -0
- package/dist/reactive/effects/utils/chunkRequestParallel.d.ts +12 -0
- package/dist/reactive/effects/utils/chunkRequestParallel.d.ts.map +1 -0
- package/dist/reactive/effects/utils/chunkRequestParallel.js +13 -0
- package/dist/reactive/effects/utils/chunkRequestParallel.js.map +1 -0
- package/dist/reactive/effects/utils/index.d.ts +3 -0
- package/dist/reactive/effects/utils/index.d.ts.map +1 -0
- package/dist/reactive/effects/utils/index.js +3 -0
- package/dist/reactive/effects/utils/index.js.map +1 -0
- package/dist/reactive/index.d.ts +3 -0
- package/dist/reactive/index.d.ts.map +1 -0
- package/dist/reactive/index.js +3 -0
- package/dist/reactive/index.js.map +1 -0
- package/dist/utils/chunk.util.d.ts +8 -0
- package/dist/utils/chunk.util.d.ts.map +1 -0
- package/dist/utils/chunk.util.js +21 -0
- package/dist/utils/chunk.util.js.map +1 -0
- package/dist/utils/createSynapse.d.ts +52 -0
- package/dist/utils/createSynapse.d.ts.map +1 -0
- package/dist/utils/createSynapse.js +87 -0
- package/dist/utils/createSynapse.js.map +1 -0
- package/dist/utils/deepMerge.util.d.ts +2 -0
- package/dist/utils/deepMerge.util.d.ts.map +1 -0
- package/dist/utils/deepMerge.util.js +16 -0
- package/dist/utils/deepMerge.util.js.map +1 -0
- package/dist/utils/flatMap.util.d.ts +10 -0
- package/dist/utils/flatMap.util.d.ts.map +1 -0
- package/dist/utils/flatMap.util.js +23 -0
- package/dist/utils/flatMap.util.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +111 -0
package/README.md
ADDED
|
@@ -0,0 +1,1038 @@
|
|
|
1
|
+
# Synapse
|
|
2
|
+
|
|
3
|
+
Synapse — это набор инструментов для управления состоянием + API-клиент.
|
|
4
|
+
|
|
5
|
+
## Особенности
|
|
6
|
+
|
|
7
|
+
- **Не привязан к конкретному фреймворку**: Вы можете использовать Synapse в контексте любого фреймворка или независимо от него
|
|
8
|
+
- **Разнообразные адаптеры хранилищ**: Выбирайте между Memory, LocalStorage или IndexedDB в зависимости от ваших потребностей
|
|
9
|
+
- **Различный способ получения данных**: Создавайте и комбинируйте селекторы для вычисляемых значений на основе состояния в стиле Redux или просто подписывайтесь на конкретное свойство в хранилище
|
|
10
|
+
- Возможность создания вычисляемых селекторов в стиле Redux
|
|
11
|
+
- Возможность прямой подписки на конкретное свойство в хранилище
|
|
12
|
+
- Возможность подписки на реактивное состояние
|
|
13
|
+
- **Надежный API-клиент**: Создайте удобный API-клиент для вашего приложения (похож на RTK Query)
|
|
14
|
+
- **Поддержка middleware**: Расширяйте функциональность с помощью пользовательских middleware
|
|
15
|
+
- **Система плагинов**: Используйте готовые или создавайте собственные плагины для расширения функциональности
|
|
16
|
+
- **Отдельные возможности для реактивного подхода**: Возможность гибкой работы с api-запросами в стиле Redux-Observable и RxJS
|
|
17
|
+
|
|
18
|
+
## Автор
|
|
19
|
+
|
|
20
|
+
**Владислав** — Senior Frontend Developer (React, TypeScript)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
> ### 🔎 Нахожусь в поиске новых карьерных возможностей!
|
|
24
|
+
>
|
|
25
|
+
> [GitHub](https://github.com/Vlad92msk/) | [LinkedIn](https://www.linkedin.com/in/vlad-firsov/)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## Установка
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install synapse-storage
|
|
32
|
+
# или
|
|
33
|
+
yarn add synapse-storage
|
|
34
|
+
# или
|
|
35
|
+
pnpm add synapse-storage
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## В своих примерах я буду использовать top-level-await, поэтому:
|
|
40
|
+
|
|
41
|
+
### Версии Node.js
|
|
42
|
+
- ✅ Node.js ≥ 14.8.0 (минимальная версия с поддержкой top-level-await)
|
|
43
|
+
- ✅ Node.js ≥ 16.0.0 (рекомендуется для полной поддержки)
|
|
44
|
+
|
|
45
|
+
### Версии TypeScript
|
|
46
|
+
- ✅ TypeScript ≥ 3.8 (базовая поддержка)
|
|
47
|
+
- ✅ TypeScript ≥ 4.5 (улучшенная поддержка)
|
|
48
|
+
- ✅ TypeScript ≥ 5.0 (рекомендуется, полная поддержка)
|
|
49
|
+
|
|
50
|
+
### Параметр `target` в tsconfig.json
|
|
51
|
+
- ✅ ES2022 (рекомендуется)
|
|
52
|
+
- ✅ ESNext
|
|
53
|
+
- ❌ ES2021 или ниже (не поддерживает top-level-await)
|
|
54
|
+
|
|
55
|
+
### Параметр `module` в tsconfig.json
|
|
56
|
+
- ✅ ESNext (рекомендуется)
|
|
57
|
+
- ✅ NodeNext
|
|
58
|
+
- ✅ ES2022
|
|
59
|
+
- ❌ CommonJS (не поддерживает top-level-await)
|
|
60
|
+
- ❌ AMD, UMD, System (не поддерживают top-level-await)
|
|
61
|
+
|
|
62
|
+
### Параметр `moduleResolution` в tsconfig.json
|
|
63
|
+
- ✅ bundler (для проектов использующих Vite, Webpack 5, esbuild)
|
|
64
|
+
- ✅ node16 или nodenext (для Node.js проектов)
|
|
65
|
+
- ✅ node (для совместимости со старыми проектами, не рекомендуется)
|
|
66
|
+
- ❓ classic (может работать с ограничениями)
|
|
67
|
+
|
|
68
|
+
### Бандлеры и сборщики
|
|
69
|
+
- ✅ Vite (полная поддержка)
|
|
70
|
+
- ✅ Webpack 5+ (с правильной конфигурацией)
|
|
71
|
+
- ✅ esbuild (полная поддержка)
|
|
72
|
+
- ✅ Rollup (с плагином @rollup/plugin-dynamic-import-vars)
|
|
73
|
+
- ✅ Next.js ≥ 12 (нужен правильный build target)
|
|
74
|
+
- ❌ Webpack 4 и ниже
|
|
75
|
+
|
|
76
|
+
### Пример минимальной конфигурации tsconfig.json
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"compilerOptions": {
|
|
81
|
+
"target": "ES2022",
|
|
82
|
+
"module": "ESNext",
|
|
83
|
+
"moduleResolution": "bundler",
|
|
84
|
+
"esModuleInterop": true
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Быстрый старт
|
|
90
|
+
|
|
91
|
+
Импорты:
|
|
92
|
+
```typescript
|
|
93
|
+
// Инструменты создания и управления хранилищем
|
|
94
|
+
import {
|
|
95
|
+
// Хранилища
|
|
96
|
+
MemoryStorage,
|
|
97
|
+
IndexedDBStorage,
|
|
98
|
+
LocalStorage,
|
|
99
|
+
|
|
100
|
+
// Интерфейсы для хранилищ
|
|
101
|
+
IStorage,
|
|
102
|
+
|
|
103
|
+
// middleware для хранилища
|
|
104
|
+
broadcastMiddleware,
|
|
105
|
+
|
|
106
|
+
// Для создания кастомных плагинов хранилища
|
|
107
|
+
StoragePluginModule,
|
|
108
|
+
IStoragePlugin,
|
|
109
|
+
PluginContext,
|
|
110
|
+
StorageKeyType,
|
|
111
|
+
|
|
112
|
+
// Для создания кастомных middlewares хранилища
|
|
113
|
+
Middleware,
|
|
114
|
+
MiddlewareAPI,
|
|
115
|
+
NextFunction,
|
|
116
|
+
|
|
117
|
+
// Модуль создания вычисляемых селекторов в Redux стиле
|
|
118
|
+
SelectorModule,
|
|
119
|
+
ISelectorModule
|
|
120
|
+
} from 'synapse-storage/core'
|
|
121
|
+
|
|
122
|
+
// Инструменты для использования реактивного подхода (немного похоже на Redux-Observable)
|
|
123
|
+
import {
|
|
124
|
+
// Инструменты для создания Dispatcher
|
|
125
|
+
createDispatcher,
|
|
126
|
+
loggerDispatcherMiddleware,
|
|
127
|
+
|
|
128
|
+
// Инструменты для создания Effects (напоминает Redux-Observable)
|
|
129
|
+
EffectsModule,
|
|
130
|
+
combineEffects,
|
|
131
|
+
createEffect,
|
|
132
|
+
ofType,
|
|
133
|
+
ofTypes,
|
|
134
|
+
selectorMap,
|
|
135
|
+
validateMap
|
|
136
|
+
} from 'synapse-storage/reactive';
|
|
137
|
+
|
|
138
|
+
// Инструменты для работы с api
|
|
139
|
+
import { ApiClient, ResponseFormat } from 'synapse-storage/api'
|
|
140
|
+
|
|
141
|
+
// Несколько инструментов для удобного использования в React
|
|
142
|
+
import { useStorageSubscribe, useSelector, createSynapseCtx } from 'synapse-storage/react'
|
|
143
|
+
|
|
144
|
+
import { createSynapse } from 'synapse-storage/utils'
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Вот простой пример использования Synapse с React:
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
import { IndexedDBStorage, LocalStorage, MemoryStorage } from 'synapse-storage/core'
|
|
151
|
+
import { useEffect, useState } from 'react'
|
|
152
|
+
|
|
153
|
+
// Создаем экземпляр хранилища (MemoryStorage / LocalStorage / IndexedDBStorage)
|
|
154
|
+
const counterStorage = await new MemoryStorage({
|
|
155
|
+
name: 'counter',
|
|
156
|
+
initialState: { value: 0 }
|
|
157
|
+
}).initialize();
|
|
158
|
+
|
|
159
|
+
function Counter() {
|
|
160
|
+
const [count, setCount] = useState(0);
|
|
161
|
+
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
// Подписываемся на изменения
|
|
164
|
+
return counterStorage.subscribe('value', setCount);
|
|
165
|
+
}, []);
|
|
166
|
+
|
|
167
|
+
const increment = () => {
|
|
168
|
+
counterStorage.update(state => {
|
|
169
|
+
state.value++;
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<div>
|
|
175
|
+
<p>Счетчик: {count}</p>
|
|
176
|
+
<button onClick={increment}>Увеличить</button>
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
Более подробные примеры можно найти в src/examples:
|
|
182
|
+
- пример использования api-client(api-example.md)
|
|
183
|
+
- пример комбинированного использования хранилищ всех типов, демонтсрация возможностей подписок и работы базовых middlewares(base-storage-example.md)
|
|
184
|
+
- расширенный пример комплексного использования, включая реактивный подход (pokemons)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
## Адаптеры хранилищ
|
|
188
|
+
|
|
189
|
+
Synapse предоставляет три типа адаптеров хранилищ:
|
|
190
|
+
|
|
191
|
+
### MemoryStorage
|
|
192
|
+
|
|
193
|
+
In-memory хранилище для временных данных, которые очищаются при перезагрузке страницы.
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
const memoryStorage = await new MemoryStorage({
|
|
197
|
+
name: 'tempStorage',
|
|
198
|
+
}).initialize();
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### LocalStorage
|
|
202
|
+
|
|
203
|
+
Хранилище на основе Web Storage API для небольших объемов данных, которые сохраняются между сессиями.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
const localStorage = await new LocalStorage({
|
|
207
|
+
name: 'appStorage',
|
|
208
|
+
}).initialize();
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### IndexedDBStorage
|
|
212
|
+
|
|
213
|
+
Хранилище на основе IndexedDB для больших объемов данных и сложных структур.
|
|
214
|
+
Создается немного иначе, но довольно похожим образом
|
|
215
|
+
```typescript
|
|
216
|
+
import { IndexedDBStorage } from 'synapse-storage/core'
|
|
217
|
+
import { IDBApi, IDBCore } from './types'
|
|
218
|
+
|
|
219
|
+
export const { CORE, API } = await IndexedDBStorage.createStorages<{
|
|
220
|
+
CORE: IDBCore
|
|
221
|
+
API: IDBApi
|
|
222
|
+
}>(
|
|
223
|
+
'social-network', // Название базы данных в indexDB
|
|
224
|
+
// Таблицы:
|
|
225
|
+
{
|
|
226
|
+
// === Хранение запросов для кэширования ===
|
|
227
|
+
API: {
|
|
228
|
+
name: 'api',
|
|
229
|
+
// eventEmitter: ,
|
|
230
|
+
// initialState: ,
|
|
231
|
+
// middlewares: ,
|
|
232
|
+
// pluginExecutor: ,
|
|
233
|
+
},
|
|
234
|
+
// === Основные данные проекта ===
|
|
235
|
+
CORE: {
|
|
236
|
+
name: 'core',
|
|
237
|
+
initialState: {
|
|
238
|
+
currentUserProfile: undefined,
|
|
239
|
+
},
|
|
240
|
+
//...
|
|
241
|
+
},
|
|
242
|
+
// Другие объекты (хранилища)
|
|
243
|
+
},
|
|
244
|
+
console, // logger (может быть любой, который имплементируют интерфейс ILogger)
|
|
245
|
+
)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Селекторы
|
|
249
|
+
|
|
250
|
+
Селекторы предоставляют удобный способ доступа к данным в хранилище:
|
|
251
|
+
|
|
252
|
+
### Базовые подписки на свойства хранилища
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// Подписка на конкретное свойство (по пути)
|
|
256
|
+
const unsubscribe1 = storage.subscribe('value', (event) => {
|
|
257
|
+
console.log('Новое значение:', event);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Подписка на свойство (через функцию селектора)
|
|
261
|
+
const unsubscribe2 = storage.subscribe((state) => state.value, (event) => {
|
|
262
|
+
console.log('Новое значение:', event);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Подписка на вложенные свойства
|
|
266
|
+
const unsubscribe3 = storage.subscribe('user.settings.theme', (event) => {
|
|
267
|
+
console.log('Новая тема:', event);
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Селекторы для вычисляемых значений (в стиле Redux)
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
// Создание модуля селекторов
|
|
275
|
+
const counterSelector = new SelectorModule(counterStorage);
|
|
276
|
+
|
|
277
|
+
// Создание простого селектора
|
|
278
|
+
const countValueSelector = counterSelector.createSelector(s => s.value);
|
|
279
|
+
|
|
280
|
+
// Комбинирование селекторов
|
|
281
|
+
const doubledCountSelector = counterSelector.createSelector(
|
|
282
|
+
[countValueSelector],
|
|
283
|
+
count => count * 2,
|
|
284
|
+
// Опционально:
|
|
285
|
+
// {
|
|
286
|
+
// equals: , // Функция сравнения
|
|
287
|
+
// name: 'doubledCountSelector' // Имя селектора
|
|
288
|
+
// }
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
// Подписка на изменения вычисляемого значения
|
|
292
|
+
doubledCountSelector.subscribe({
|
|
293
|
+
notify: value => console.log('Удвоенное значение:', value)
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Одноразовое получение значения
|
|
297
|
+
doubledCountSelector.select().then(value => {
|
|
298
|
+
console.log('Текущее удвоенное значение:', value);
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## API-клиент
|
|
303
|
+
|
|
304
|
+
Synapse включает в себя API-клиент с поддержкой кеширования:
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
const api = new ApiClient({
|
|
308
|
+
// Настройка кеширования запросов
|
|
309
|
+
cacheableHeaderKeys: ['X-Auth-Token'],
|
|
310
|
+
storage: API, // Передаем готовое экземпляр готового хранилища
|
|
311
|
+
// Настройки кеша
|
|
312
|
+
cache: {
|
|
313
|
+
ttl: 5 * 60 * 1000, // Время жизни кеша: 5 минут
|
|
314
|
+
invalidateOnError: true, // Инвалидация кеша при ошибке
|
|
315
|
+
cleanup: {
|
|
316
|
+
enabled: true, // Периодическая очистка кеша
|
|
317
|
+
interval: 10 * 60 * 1000, // Интервал очистки: 10 минут
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
// Базовые настройки запроса
|
|
321
|
+
baseQuery: {
|
|
322
|
+
baseUrl: 'https://api.example.com',
|
|
323
|
+
timeout: 10000, // 10 секунд
|
|
324
|
+
prepareHeaders: async (headers, context) => {
|
|
325
|
+
// Установка заголовков
|
|
326
|
+
headers.set('X-Auth-Token', 'some-token');
|
|
327
|
+
// Получение данных из хранилища или cookies
|
|
328
|
+
const token = context.getCookie('token');
|
|
329
|
+
if (token) {
|
|
330
|
+
headers.set('Authorization', `Bearer ${token}`);
|
|
331
|
+
}
|
|
332
|
+
return headers;
|
|
333
|
+
},
|
|
334
|
+
credentials: 'same-origin',
|
|
335
|
+
},
|
|
336
|
+
// Определение эндпоинтов
|
|
337
|
+
endpoints: async (create) => ({
|
|
338
|
+
getData: create({
|
|
339
|
+
request: (params) => ({
|
|
340
|
+
path: '/data',
|
|
341
|
+
method: 'GET',
|
|
342
|
+
query: params,
|
|
343
|
+
}),
|
|
344
|
+
// Можно указать специфичные настройки кеша для эндпоинта
|
|
345
|
+
cache: {
|
|
346
|
+
ttl: 60 * 1000, // 1 минута для этого эндпоинта
|
|
347
|
+
},
|
|
348
|
+
}),
|
|
349
|
+
}),
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// Инициализация
|
|
353
|
+
const myApi = await api.init();
|
|
354
|
+
|
|
355
|
+
// Использование с подпиской на состояние запроса
|
|
356
|
+
const request = myApi.getEndpoints().getData.request({ id: 1 });
|
|
357
|
+
|
|
358
|
+
// Вариант 1: Подписка на изменения состояния запроса
|
|
359
|
+
request.subscribe((state) => {
|
|
360
|
+
switch (state.status) {
|
|
361
|
+
case 'idle':
|
|
362
|
+
console.log('Запрос неактивен');
|
|
363
|
+
break;
|
|
364
|
+
case 'loading':
|
|
365
|
+
console.log('Загрузка данных...');
|
|
366
|
+
break;
|
|
367
|
+
case 'success':
|
|
368
|
+
console.log('Данные получены:', state.data);
|
|
369
|
+
break;
|
|
370
|
+
case 'error':
|
|
371
|
+
console.log('Ошибка:', state.error);
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// Вариант 2: Ожидание результата запроса
|
|
377
|
+
const response = await request.wait();
|
|
378
|
+
|
|
379
|
+
// Вариант 3: Ожидание с колбеками для разных состояний
|
|
380
|
+
request.waitWithCallbacks({
|
|
381
|
+
loading: () => console.log('Загрузка...'),
|
|
382
|
+
success: (data) => console.log('Данные:', data),
|
|
383
|
+
error: (error) => console.error('Ошибка:', error),
|
|
384
|
+
});
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
## Реактивный подход
|
|
388
|
+
Synapse предоставляет инструменты для использования реактивного подхода, напоминающий Redux-Observable.
|
|
389
|
+
|
|
390
|
+
Пример создания Диспетчера:
|
|
391
|
+
```typescript
|
|
392
|
+
import { createDispatcher, loggerDispatcherMiddleware } from 'synapse-storage/reactive'
|
|
393
|
+
import { PokemonStorage } from '../storages/pokemon.storage'
|
|
394
|
+
import { createPokemonAlertMiddleware } from '../middlewares/pokenon.middlewares'
|
|
395
|
+
import { Pokemon } from '../types'
|
|
396
|
+
|
|
397
|
+
// const myWorker = new Worker('path-to-my-worker')
|
|
398
|
+
|
|
399
|
+
export interface AlertPayload {
|
|
400
|
+
message: string
|
|
401
|
+
type: 'info' | 'warning' | 'error' | 'success'
|
|
402
|
+
duration?: number // Длительность показа в миллисекундах
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Функция для создания диспетчера
|
|
406
|
+
export function createPokemonDispatcher(storage: PokemonStorage) {
|
|
407
|
+
// Создаем middleware: логгер
|
|
408
|
+
const loggerMiddleware = loggerDispatcherMiddleware({
|
|
409
|
+
collapsed: true, // Сворачиваем группы в консоли для компактности
|
|
410
|
+
colors: {
|
|
411
|
+
title: '#3498db', // Кастомный синий цвет для заголовка
|
|
412
|
+
},
|
|
413
|
+
duration: true,
|
|
414
|
+
diff: true,
|
|
415
|
+
showFullState: true,
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
// Создаем middleware: alertM (просто для примера)
|
|
419
|
+
const alertM = createPokemonAlertMiddleware()
|
|
420
|
+
|
|
421
|
+
return createDispatcher({
|
|
422
|
+
storage,
|
|
423
|
+
middlewares: [loggerMiddleware, alertM],
|
|
424
|
+
}, (storage, { createWatcher, createAction }) => ({
|
|
425
|
+
// watcher для отслеживания текущего ID
|
|
426
|
+
watchCurrentId: createWatcher({...}),
|
|
427
|
+
// Загрузка покемона по ID
|
|
428
|
+
loadPokemon: createAction<number, { id: number }>({...}),
|
|
429
|
+
|
|
430
|
+
loadPokemonRequest: createAction<number, { id: number }>({...}),
|
|
431
|
+
// Успешное получение данных
|
|
432
|
+
success: createAction<{ data?: Pokemon}, { data?: Pokemon }>({...}, {
|
|
433
|
+
// Функция мемоизации (пока не тестировал)
|
|
434
|
+
// memoize: (currentArgs: any[], previousArgs: any[], previousResult: any) => true,
|
|
435
|
+
// Веб-воркер для выполнения действия (пока не тестировал)
|
|
436
|
+
// worker: myWorker,
|
|
437
|
+
}),
|
|
438
|
+
failure: createAction<Error, { err: Error }>({...}),
|
|
439
|
+
next: createAction<void, { id: number }>({...}),
|
|
440
|
+
prev: createAction<void, { id: number }>({...}),
|
|
441
|
+
showAlert: createAction<AlertPayload, void>({...}),
|
|
442
|
+
}))
|
|
443
|
+
// Альтернативный вариант добавления:
|
|
444
|
+
// .use(logger)
|
|
445
|
+
// .use(alertM)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Экспортируем тип диспетчера
|
|
449
|
+
export type PokemonDispatcher = ReturnType<typeof createPokemonDispatcher>
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
Пример создания Эффекта:
|
|
453
|
+
```typescript
|
|
454
|
+
import { EMPTY, from, mapTo, of, tap } from 'rxjs'
|
|
455
|
+
import { catchError, map, switchMap } from 'rxjs/operators'
|
|
456
|
+
|
|
457
|
+
import {
|
|
458
|
+
ofType, // Слушает 1 событие
|
|
459
|
+
ofTypes, // Слушает несколько событий
|
|
460
|
+
createEffect, // Функция создания эффекта
|
|
461
|
+
combineEffects, // Объединяет несколько эффектов в один
|
|
462
|
+
selectorMap, // Выбор частей состояния с помощью селекторов
|
|
463
|
+
validateMap // Оператор для удобной работы с запросом
|
|
464
|
+
} from 'synapse-storage/reactive'
|
|
465
|
+
import { pokemonEndpoints } from '../api.md'
|
|
466
|
+
import { AppConfig } from '../app.config'
|
|
467
|
+
import { PokemonDispatcher } from '../pokemon.dispatcher'
|
|
468
|
+
import { Pokemon, PokemonState } from '../types'
|
|
469
|
+
|
|
470
|
+
// Определяем типы для наших эффектов
|
|
471
|
+
type PokemonDispatcherType = { pokemonDispatcher: PokemonDispatcher }
|
|
472
|
+
type PokemonApiType = { pokemonApi: typeof pokemonEndpoints }
|
|
473
|
+
|
|
474
|
+
// Эффект для навигации
|
|
475
|
+
export const navigationEffect = createEffect<
|
|
476
|
+
PokemonState,
|
|
477
|
+
PokemonDispatcherType,
|
|
478
|
+
PokemonApiType,
|
|
479
|
+
AppConfig,
|
|
480
|
+
any //ExternalStorages
|
|
481
|
+
>((action$, state$, externalStorages, { pokemonDispatcher }, _, config) =>
|
|
482
|
+
action$.pipe(
|
|
483
|
+
ofTypes([pokemonDispatcher.dispatch.next, pokemonDispatcher.dispatch.prev]),
|
|
484
|
+
switchMap((action) => {
|
|
485
|
+
const { id } = action.payload
|
|
486
|
+
return of(() => pokemonDispatcher.dispatch.loadPokemon(id))
|
|
487
|
+
}),
|
|
488
|
+
),
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
// Эффект для отслеживания изменений ID
|
|
492
|
+
export const watchIdEffect = createEffect<
|
|
493
|
+
PokemonState,
|
|
494
|
+
PokemonDispatcherType,
|
|
495
|
+
PokemonApiType,
|
|
496
|
+
AppConfig,
|
|
497
|
+
any //ExternalStorages
|
|
498
|
+
>((action$, state$, externalStorages, { pokemonDispatcher }) =>
|
|
499
|
+
action$.pipe(
|
|
500
|
+
ofType(pokemonDispatcher.watchers.watchCurrentId),
|
|
501
|
+
selectorMap(
|
|
502
|
+
state$,
|
|
503
|
+
//... selectors
|
|
504
|
+
),
|
|
505
|
+
// tap(([action, [loading, currentId]]) => {...}),
|
|
506
|
+
mapTo(null),
|
|
507
|
+
),
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
// Эффект для загрузки данных покемона
|
|
511
|
+
export const loadPokemonEffect = createEffect<
|
|
512
|
+
PokemonState,
|
|
513
|
+
PokemonDispatcherType,
|
|
514
|
+
PokemonApiType,
|
|
515
|
+
AppConfig,
|
|
516
|
+
any //ExternalStorages
|
|
517
|
+
>((
|
|
518
|
+
action$, // Поток событий
|
|
519
|
+
state$, // Поток состояния
|
|
520
|
+
externalStorages, // Потоки внешних хранилищ
|
|
521
|
+
{ pokemonDispatcher }, // Диспетчеры которые мы передали
|
|
522
|
+
{ pokemonApi }, // различные API которые мы передали
|
|
523
|
+
config // Конфигурация, которую мы передали
|
|
524
|
+
) =>
|
|
525
|
+
action$.pipe(
|
|
526
|
+
// Я использую отдельный action loadPokemon который уведомляет о намерении сделать запрос
|
|
527
|
+
// Для того, чтобы не устанавливать loading сразу
|
|
528
|
+
ofType(pokemonDispatcher.dispatch.loadPokemon),
|
|
529
|
+
selectorMap(state$, (state) => state.currentId), // Получаем данные из текущего хранилища
|
|
530
|
+
selectorMap(externalStorages.someExternalStorage, (state) => state.someValue1), // Получаем данные из внешнего хранилища
|
|
531
|
+
validateMap({
|
|
532
|
+
apiCall: ([action, [currentId], [someValue1]]) => {
|
|
533
|
+
const { id } = action.payload
|
|
534
|
+
|
|
535
|
+
return from(
|
|
536
|
+
// Использую waitWithCallbacks чтобы иметь доступ к методу loading
|
|
537
|
+
pokemonApi.fetchPokemonById.request({ id }).waitWithCallbacks({
|
|
538
|
+
// Вызывается только тогда, когда запрос реально отправляется, а не берется из кэша
|
|
539
|
+
loading: (request) => {
|
|
540
|
+
// Именно в в этот момент установится loading и другая необходимая логика
|
|
541
|
+
pokemonDispatcher.dispatch.loadPokemonRequest(id)
|
|
542
|
+
},
|
|
543
|
+
// Можно использовать так:
|
|
544
|
+
// success: (data, request) => {
|
|
545
|
+
// console.log('SUCCESS', request)
|
|
546
|
+
// pokemonDispatcher.dispatch.success({ data })
|
|
547
|
+
// },
|
|
548
|
+
// error: (error, request) => {
|
|
549
|
+
// console.log('ERROR', error, request)
|
|
550
|
+
// pokemonDispatcher.dispatch.failure(error!)
|
|
551
|
+
// },
|
|
552
|
+
}),
|
|
553
|
+
// Можно более стандартным способом:
|
|
554
|
+
).pipe(
|
|
555
|
+
switchMap(({ data }) => {
|
|
556
|
+
return of(pokemonDispatcher.dispatch.success({ data }))
|
|
557
|
+
}),
|
|
558
|
+
catchError((err) => of(pokemonDispatcher.dispatch.failure(err))),
|
|
559
|
+
)
|
|
560
|
+
},
|
|
561
|
+
}),
|
|
562
|
+
),
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
// Объединяем все эффекты в один и экспортируем
|
|
566
|
+
export const pokemonEffects = combineEffects(
|
|
567
|
+
navigationEffect,
|
|
568
|
+
watchIdEffect,
|
|
569
|
+
loadPokemonEffect
|
|
570
|
+
)
|
|
571
|
+
```
|
|
572
|
+
---
|
|
573
|
+
## Пример организации кода и использования утилиты createSynapse
|
|
574
|
+
|
|
575
|
+
Предлагаемая структура файлов
|
|
576
|
+
|
|
577
|
+
```md
|
|
578
|
+
📦some-directory
|
|
579
|
+
└── 📂synapses
|
|
580
|
+
│ └── 📂core
|
|
581
|
+
│ │ ├── 📄core.dispatcher.ts
|
|
582
|
+
│ │ ├── 📄core.synapse.ts
|
|
583
|
+
│ │ └── ...
|
|
584
|
+
│ └── 📂user-info
|
|
585
|
+
│ │ ├── 📄user-info.context.tsx
|
|
586
|
+
│ │ ├── 📄user-info.dispatcher.ts
|
|
587
|
+
│ │ ├── 📄user-info.effects.ts
|
|
588
|
+
│ │ ├── 📄user-info.selectors.ts
|
|
589
|
+
│ │ ├── 📄user-info.store.ts
|
|
590
|
+
│ │ └── 📄user-info.synapse.ts
|
|
591
|
+
│ └──...
|
|
592
|
+
│
|
|
593
|
+
└── 📄indexdb.config.ts
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
```typescript
|
|
597
|
+
// user-info.store.ts
|
|
598
|
+
// === СОЗДАНИЕ ХРАНИЛИЩА НУЖНОГОТИПА ===
|
|
599
|
+
export async function createUserInfoStorage() {
|
|
600
|
+
return new MemoryStorage<AboutUserUserInfo>({
|
|
601
|
+
name: 'user-info',
|
|
602
|
+
initialState: {
|
|
603
|
+
userInfoInit: undefined,
|
|
604
|
+
isChangeActive: false,
|
|
605
|
+
fieldsInit: {},
|
|
606
|
+
fields: {},
|
|
607
|
+
},
|
|
608
|
+
}).initialize()
|
|
609
|
+
}
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
// user-info.dispatcher.ts
|
|
614
|
+
// === СОЗДАНИЕ ДИСПЕТЧЕРА ===
|
|
615
|
+
|
|
616
|
+
import { IStorage } from 'synapse-storage/core'
|
|
617
|
+
import { createDispatcher, loggerDispatcherMiddleware } from 'synapse-storage/reactive'
|
|
618
|
+
|
|
619
|
+
export function createUserInfoDispatcher(store: IStorage<AboutUserUserInfo>) {
|
|
620
|
+
const loggerMiddleware = loggerDispatcherMiddleware({...})
|
|
621
|
+
|
|
622
|
+
return createDispatcher({ storage: store }, (storage, { createAction, createWatcher }) => ({
|
|
623
|
+
setCurrentUserProfile: createAction<UserProfileInfo, UserProfileInfo>({
|
|
624
|
+
type: 'setCurrentUserProfile',
|
|
625
|
+
// meta: ,
|
|
626
|
+
// action: async () => {...}),
|
|
627
|
+
}),
|
|
628
|
+
|
|
629
|
+
setActiveChange: createAction<void, void>({
|
|
630
|
+
type: 'setActiveChange',
|
|
631
|
+
// meta: ,
|
|
632
|
+
// action: async () => {...}),
|
|
633
|
+
})
|
|
634
|
+
// Другие диспетчеры ...
|
|
635
|
+
})).use(loggerMiddleware)
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
export type UserInfoDispatcher = ReturnType<typeof createUserInfoDispatcher>
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
```typescript
|
|
642
|
+
// user-info.dispatcher.ts
|
|
643
|
+
// === СОЗДАНИЕ СЕЛЕКТОРОВ ===
|
|
644
|
+
import { ISelectorModule } from 'synapse-storage/core'
|
|
645
|
+
|
|
646
|
+
export const createUserInfoSelectors = (selectorModule: ISelectorModule<AboutUserUserInfo>) => {
|
|
647
|
+
const currentUserProfile = selectorModule.createSelector((s) => s.userInfoInit)
|
|
648
|
+
const fieldsInit = selectorModule.createSelector((s) => s.fieldsInit)
|
|
649
|
+
|
|
650
|
+
const isChangeActive = selectorModule.createSelector((s) => s.isChangeActive)
|
|
651
|
+
|
|
652
|
+
const fields = selectorModule.createSelector((s) => s.fields)
|
|
653
|
+
// Для React
|
|
654
|
+
// Комопнент будет ререндериться всегда, когда меняется возвращаемое селектором значение
|
|
655
|
+
// Для уменьшения ререндеров советую создавать точечные селекторы
|
|
656
|
+
// Если для отображения information у вас отдельный компонент - лучше создать отдельный для него селектор
|
|
657
|
+
const fieldInformation = selectorModule.createSelector((s) => s.fields.information)
|
|
658
|
+
const fieldPosition = selectorModule.createSelector((s) => s.fields.position)
|
|
659
|
+
//...
|
|
660
|
+
|
|
661
|
+
return ({
|
|
662
|
+
currentUserProfile,
|
|
663
|
+
isChangeActive,
|
|
664
|
+
//...
|
|
665
|
+
})
|
|
666
|
+
}
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
```typescript
|
|
670
|
+
// user-info.effects.ts
|
|
671
|
+
// === СОЗДАНИЕ ЭФФЕКТОВ ===
|
|
672
|
+
import { EMPTY, from, of } from 'rxjs'
|
|
673
|
+
import { catchError, map } from 'rxjs/operators'
|
|
674
|
+
import { combineEffects, createEffect, ofType, validateMap } from 'synapse-storage/reactive'
|
|
675
|
+
|
|
676
|
+
type CurrentDispatchers = {
|
|
677
|
+
userInfoDispatcher: UserInfoDispatcher
|
|
678
|
+
coreIdbDispatcher: CoreDispatcher
|
|
679
|
+
};
|
|
680
|
+
type CurrentApis = {
|
|
681
|
+
userInfoAPi: typeof userInfoEndpoints
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Добавляем полученный профиль пользователя в текущий СТор
|
|
686
|
+
*/
|
|
687
|
+
const loadUserInfoById = createEffect<
|
|
688
|
+
AboutUserUserInfo,
|
|
689
|
+
CurrentDispatchers,
|
|
690
|
+
CurrentApis,
|
|
691
|
+
any
|
|
692
|
+
>((action$, state$, { userInfoDispatcher, coreIdbDispatcher }) => action$.pipe(
|
|
693
|
+
// Подписываемся на изменения в стороннем Synapse
|
|
694
|
+
ofType(coreIdbDispatcher.watchers.watchCurrentUserProfile),
|
|
695
|
+
map((s) => {
|
|
696
|
+
if (!s.payload) return EMPTY
|
|
697
|
+
// Берем данные из стороннего Synapse и кладем в текущий
|
|
698
|
+
return userInfoDispatcher.dispatch.setCurrentUserProfile(s.payload)
|
|
699
|
+
}),
|
|
700
|
+
))
|
|
701
|
+
|
|
702
|
+
const updateUserProfile = createEffect<
|
|
703
|
+
AboutUserUserInfo,
|
|
704
|
+
CurrentDispatchers,
|
|
705
|
+
CurrentApis,
|
|
706
|
+
any
|
|
707
|
+
>((action$, state$, { userInfoDispatcher }, { userInfoAPi }) => action$.pipe(
|
|
708
|
+
ofType(userInfoDispatcher.dispatch.submit),
|
|
709
|
+
validateMap({
|
|
710
|
+
// Валидация перед запросом
|
|
711
|
+
validator: (action) => ({
|
|
712
|
+
skipAction: userInfoDispatcher.dispatch.reset(),
|
|
713
|
+
conditions: [Boolean(action.payload)]
|
|
714
|
+
}),
|
|
715
|
+
apiCall: (action) => {
|
|
716
|
+
return from(
|
|
717
|
+
userInfoAPi.getUserById.request({ user_id: 1 }).waitWithCallbacks({
|
|
718
|
+
// Вызывается только тогда, когда запрос реально отправляется, а не берется из кэша
|
|
719
|
+
loading: (request) => {
|
|
720
|
+
// Именно в в этот момент установится loading и другая необходимая логика
|
|
721
|
+
// userInfoDispatcher.dispatch.request(id)
|
|
722
|
+
},
|
|
723
|
+
// Можно использовать так:
|
|
724
|
+
success: (data, request) => {
|
|
725
|
+
// userInfoDispatcher.dispatch.success({ data })
|
|
726
|
+
},
|
|
727
|
+
error: (error, request) => {
|
|
728
|
+
// userInfoDispatcher.dispatch.failure(error!)
|
|
729
|
+
},
|
|
730
|
+
}),
|
|
731
|
+
)
|
|
732
|
+
},
|
|
733
|
+
}),
|
|
734
|
+
))
|
|
735
|
+
|
|
736
|
+
export const userInfoEffects = combineEffects(
|
|
737
|
+
loadUserInfoById,
|
|
738
|
+
updateUserProfile,
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
```typescript
|
|
744
|
+
// user-info.synapse.ts
|
|
745
|
+
// === СОЗДАНИЕ Synapse ===
|
|
746
|
+
import { createSynapse } from 'synapse-storage/utils'
|
|
747
|
+
import { createUserInfoDispatcher } from './user-info.dispatcher'
|
|
748
|
+
import { userInfoEffects } from './user-info.effects'
|
|
749
|
+
import { createUserInfoSelectors } from './user-info.selectors'
|
|
750
|
+
import { createUserInfoStorage } from './user-info.store'
|
|
751
|
+
import { userInfoEndpoints } from '../../api/user-info.api'
|
|
752
|
+
import { coreSynapseIDB } from '../core/core.synapse'
|
|
753
|
+
|
|
754
|
+
export const userInfoSynapse = await createSynapse({
|
|
755
|
+
// Передаем хранилище
|
|
756
|
+
// Это может быть
|
|
757
|
+
// 1 - Функция, которая фозвращает готовое ранилище
|
|
758
|
+
createStorageFn: createUserInfoStorage,
|
|
759
|
+
// 2 - Класс для создания хранилища (initialize() убдет вызван внутри)
|
|
760
|
+
// storage: new MemoryStorage<AboutUserUserInfo>({
|
|
761
|
+
// name: 'user-info',
|
|
762
|
+
// initialState: {
|
|
763
|
+
// userInfoInit: undefined,
|
|
764
|
+
// isChangeActive: false,
|
|
765
|
+
// fieldsInit: {},
|
|
766
|
+
// fields: {},
|
|
767
|
+
// },
|
|
768
|
+
// }),
|
|
769
|
+
// Функция создания диспетчеров (Опционально)
|
|
770
|
+
createDispatcherFn: createUserInfoDispatcher,
|
|
771
|
+
// Функция создания селекторов (Опционально)
|
|
772
|
+
createSelectorsFn: createUserInfoSelectors,
|
|
773
|
+
// Конфигурация для эффектов (Опционально)
|
|
774
|
+
createEffectConfig: (userInfoDispatcher) => ({
|
|
775
|
+
// Диспетчеры для эффектов
|
|
776
|
+
dispatchers: {
|
|
777
|
+
userInfoDispatcher, // Текущий, для управления соственных хранилищем
|
|
778
|
+
coreIdbDispatcher: coreSynapseIDB.dispatcher, // Внешний, для взаиможействия с внешними хранилищами
|
|
779
|
+
//...
|
|
780
|
+
},
|
|
781
|
+
// Дополнительное АПИ по вашему усмотрения (у меня это API Clients)
|
|
782
|
+
api: {
|
|
783
|
+
userInfoAPi: userInfoEndpoints,
|
|
784
|
+
},
|
|
785
|
+
}),
|
|
786
|
+
// Эффекты которые будут запущены для этого synapse
|
|
787
|
+
effects: [userInfoEffects],
|
|
788
|
+
})
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
```tsx
|
|
792
|
+
// user-info.context.tsx
|
|
793
|
+
// === СОЗДАНИЕ React Context ===
|
|
794
|
+
import { createSynapseCtx } from 'synapse-storage/react'
|
|
795
|
+
import { userInfoSynapse } from './user-info.synapse'
|
|
796
|
+
|
|
797
|
+
// Получаем все необходимые инструменты для работы в компонете
|
|
798
|
+
export const {
|
|
799
|
+
contextSynapse: useUserInfoContextSynapse,
|
|
800
|
+
useSynapseActions: useUserInfoSynapseActions,
|
|
801
|
+
useSynapseSelectors: useUserInfoSynapseSelectors,
|
|
802
|
+
useSynapseState$: useUserInfoSynapseState$,
|
|
803
|
+
useSynapseStorage: useUserInfoSynapseStorage,
|
|
804
|
+
cleanupSynapse: useUserInfoCleanupSynapse,
|
|
805
|
+
} = createSynapseCtx(userInfoSynapse, {
|
|
806
|
+
loadingComponent: <div>loading</div>, // Компонент, который будет отображаться пока выполняется асинхронная загрузка Synapse
|
|
807
|
+
})
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
Вы можете связывать Synapse между собой
|
|
811
|
+
|
|
812
|
+
```typescript
|
|
813
|
+
// core.synapse.ts
|
|
814
|
+
export const coreSynapseIDB = await createSynapse({
|
|
815
|
+
storage: CORE,
|
|
816
|
+
createSelectorsFn: (selectorModule) => {
|
|
817
|
+
const currentUserProfile = selectorModule.createSelector((s) => s.currentUserProfile, { name: 'currentUserProfile' })
|
|
818
|
+
|
|
819
|
+
return ({
|
|
820
|
+
currentUserProfile,
|
|
821
|
+
})
|
|
822
|
+
},
|
|
823
|
+
createDispatcherFn: createCoreDispatcher,
|
|
824
|
+
})
|
|
825
|
+
|
|
826
|
+
// user-info.synapse.ts
|
|
827
|
+
import { createSynapse } from 'synapse-storage/utils'
|
|
828
|
+
import { coreSynapseIDB } from '../core/core.synapse'
|
|
829
|
+
|
|
830
|
+
export const userInfoSynapse = await createSynapse({
|
|
831
|
+
// Передаем внешие селекторы
|
|
832
|
+
externalSelectors: {
|
|
833
|
+
coreSelectors: coreSynapseIDB.selectors
|
|
834
|
+
},
|
|
835
|
+
// TypeScript подскажет интерфейс
|
|
836
|
+
// createSelectorsFn: (currentSelectorModule, { coreSelectors }) => {...},
|
|
837
|
+
})
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
Таким образом вы можете резделить функционал на слои
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
---
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
Полноценный рабочий пример можно найти в папке src/examples/pokemons
|
|
847
|
+
Там показано еще больше возможностей которые дает этот подход.
|
|
848
|
+
|
|
849
|
+
## Middleware и плагины
|
|
850
|
+
|
|
851
|
+
Synapse предоставляет две системы расширения функциональности: middleware и плагины. Они выполняют разные роли и имеют разную область применения.
|
|
852
|
+
|
|
853
|
+
### Middleware
|
|
854
|
+
|
|
855
|
+
Middleware в Synapse работают по принципу "цепочки обработчиков" и позволяют перехватывать любые операции хранилища. Каждое middleware может модифицировать действия до и после их обработки базовым хранилищем.
|
|
856
|
+
|
|
857
|
+
```typescript
|
|
858
|
+
const storage = await new MemoryStorage({
|
|
859
|
+
name: 'appState',
|
|
860
|
+
middlewares: (getDefaultMiddleware) => {
|
|
861
|
+
const { shallowCompare, batching } = getDefaultMiddleware();
|
|
862
|
+
return [
|
|
863
|
+
// Синхронизация между вкладками браузера
|
|
864
|
+
broadcastMiddleware({
|
|
865
|
+
storageName: 'appState',
|
|
866
|
+
storageType: 'memory',
|
|
867
|
+
}),
|
|
868
|
+
// Предотвращает ненужные обновления при одинаковых значениях
|
|
869
|
+
shallowCompare(),
|
|
870
|
+
// Группирует операции для оптимизации
|
|
871
|
+
batching({
|
|
872
|
+
batchSize: 10, // Максимальное количество операций в батче
|
|
873
|
+
batchDelay: 300, // Задержка перед обработкой батча (мс)
|
|
874
|
+
}),
|
|
875
|
+
];
|
|
876
|
+
},
|
|
877
|
+
}).initialize();
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
#### Порядок выполнения middleware
|
|
881
|
+
|
|
882
|
+
Middleware выполняются в порядке их объявления в массиве:
|
|
883
|
+
1. Действие проходит через все middleware сверху вниз
|
|
884
|
+
2. Затем выполняется базовая операция хранилища
|
|
885
|
+
3. Результат проходит через middleware снизу вверх
|
|
886
|
+
|
|
887
|
+
```
|
|
888
|
+
Action → BroadcastMiddleware → ShallowCompare → Batching → Base Operation
|
|
889
|
+
Result ← BroadcastMiddleware ← ShallowCompare ← Batching ← Base Operation
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
#### Создание пользовательского middleware
|
|
893
|
+
|
|
894
|
+
```typescript
|
|
895
|
+
import { Middleware } from 'synapse-storage/core';
|
|
896
|
+
|
|
897
|
+
const loggingMiddleware = (): Middleware => ({
|
|
898
|
+
// Уникальное имя middleware
|
|
899
|
+
name: 'logging',
|
|
900
|
+
|
|
901
|
+
// Инициализация при добавлении middleware к хранилищу
|
|
902
|
+
setup: (api) => {
|
|
903
|
+
console.log('Logging middleware initialized');
|
|
904
|
+
},
|
|
905
|
+
|
|
906
|
+
// Основная логика перехвата и обработки действий
|
|
907
|
+
reducer: (api) => (next) => async (action) => {
|
|
908
|
+
console.log('Before action:', action);
|
|
909
|
+
|
|
910
|
+
try {
|
|
911
|
+
// Вызов следующего middleware в цепочке
|
|
912
|
+
const result = await next(action);
|
|
913
|
+
|
|
914
|
+
console.log('After action:', {
|
|
915
|
+
action,
|
|
916
|
+
result,
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
return result;
|
|
920
|
+
} catch (error) {
|
|
921
|
+
console.error('Action error:', error);
|
|
922
|
+
throw error;
|
|
923
|
+
}
|
|
924
|
+
},
|
|
925
|
+
|
|
926
|
+
// Очистка ресурсов при уничтожении хранилища
|
|
927
|
+
cleanup: () => {
|
|
928
|
+
console.log('Logging middleware cleanup');
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
### Плагины
|
|
934
|
+
|
|
935
|
+
Плагины в Synapse представляют собой систему обработчиков событий хранилища с определенным жизненным циклом. В отличие от middleware, они не формируют цепочку, а работают как независимые "наблюдатели" за операциями хранилища.
|
|
936
|
+
|
|
937
|
+
```typescript
|
|
938
|
+
import { IStoragePlugin, StoragePluginModule } from 'synapse-storage/core';
|
|
939
|
+
|
|
940
|
+
// Создаем модуль плагинов
|
|
941
|
+
const plugins = new StoragePluginModule(
|
|
942
|
+
undefined, // Родительский модуль плагинов (опционально)
|
|
943
|
+
console, // Логгер
|
|
944
|
+
'appStorage' // Имя хранилища
|
|
945
|
+
);
|
|
946
|
+
|
|
947
|
+
// Пример плагина валидации
|
|
948
|
+
class ValidationPlugin implements IStoragePlugin {
|
|
949
|
+
name = 'validation';
|
|
950
|
+
private validators = new Map();
|
|
951
|
+
private options: any;
|
|
952
|
+
|
|
953
|
+
constructor(options = {}) {
|
|
954
|
+
this.options = options;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// Добавление правила валидации для ключа
|
|
958
|
+
addValidator(key, validator) {
|
|
959
|
+
this.validators.set(key, validator);
|
|
960
|
+
return this;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// Вызывается перед сохранением значения
|
|
964
|
+
async onBeforeSet(value, context) {
|
|
965
|
+
const { key } = context.metadata || {};
|
|
966
|
+
|
|
967
|
+
if (key && this.validators.has(key)) {
|
|
968
|
+
const validator = this.validators.get(key);
|
|
969
|
+
const result = validator(value);
|
|
970
|
+
|
|
971
|
+
if (!result.valid) {
|
|
972
|
+
if (this.options.throwOnInvalid) {
|
|
973
|
+
throw new Error(`Validation failed for ${key}: ${result.message}`);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
this.options.onValidationError?.(key, value, result.message);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
return value;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// Инициализация плагина
|
|
984
|
+
async initialize() {
|
|
985
|
+
console.log('Validation plugin initialized');
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// Очистка ресурсов
|
|
989
|
+
async destroy() {
|
|
990
|
+
this.validators.clear();
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// Добавление плагинов в модуль
|
|
995
|
+
await plugins.add(new ValidationPlugin({
|
|
996
|
+
throwOnInvalid: true,
|
|
997
|
+
onValidationError: (key, value, message) => {
|
|
998
|
+
console.error(`Validation error: ${message}`);
|
|
999
|
+
}
|
|
1000
|
+
}));
|
|
1001
|
+
|
|
1002
|
+
// Создание хранилища с плагинами
|
|
1003
|
+
const storage = await new MemoryStorage(
|
|
1004
|
+
{ name: 'app-storage' },
|
|
1005
|
+
plugins // Передаем модуль плагинов
|
|
1006
|
+
).initialize();
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
#### Жизненный цикл плагинов
|
|
1010
|
+
|
|
1011
|
+
Плагины имеют следующие методы жизненного цикла:
|
|
1012
|
+
|
|
1013
|
+
1. **Инициализация**: `initialize()` - вызывается при добавлении плагина в хранилище
|
|
1014
|
+
2. **Операции хранилища**:
|
|
1015
|
+
- `onBeforeSet` / `onAfterSet` - до/после сохранения значения
|
|
1016
|
+
- `onBeforeGet` / `onAfterGet` - до/после получения значения
|
|
1017
|
+
- `onBeforeDelete` / `onAfterDelete` - до/после удаления значения
|
|
1018
|
+
- `onClear` - при очистке хранилища
|
|
1019
|
+
3. **Уничтожение**: `destroy()` - вызывается при удалении плагина или уничтожении хранилища
|
|
1020
|
+
|
|
1021
|
+
#### Когда использовать middleware, а когда плагины?
|
|
1022
|
+
|
|
1023
|
+
- **Middleware** лучше использовать для:
|
|
1024
|
+
- Перехвата всех операций хранилища в одном месте
|
|
1025
|
+
- Изменения поведения базовых операций хранилища
|
|
1026
|
+
- Оптимизации (батчинг, дедупликация)
|
|
1027
|
+
- Синхронизации между хранилищами/вкладками
|
|
1028
|
+
|
|
1029
|
+
- **Плагины** лучше использовать для:
|
|
1030
|
+
- Обработки конкретных событий хранилища
|
|
1031
|
+
- Валидации данных
|
|
1032
|
+
- Логирования операций
|
|
1033
|
+
- Реализации бизнес-логики, связанной с хранением данных
|
|
1034
|
+
- Интеграции с внешними сервисами
|
|
1035
|
+
|
|
1036
|
+
## Лицензия
|
|
1037
|
+
|
|
1038
|
+
MIT
|