synapse-storage 4.1.2 → 5.0.3
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 +142 -12
- package/dist/_utils/chunk.util.d.ts +0 -1
- package/dist/_utils/deepMerge.util.d.ts +0 -1
- package/dist/_utils/error-handling.util.d.ts +0 -1
- package/dist/_utils/flatMap.util.d.ts +0 -1
- package/dist/_utils/index.d.ts +0 -1
- package/dist/_utils/logger-console.util.d.ts +0 -1
- package/dist/api/api.module.d.ts +0 -1
- package/dist/api/components/endpoint.d.ts +11 -12
- package/dist/api/components/endpoint.js.map +1 -1
- package/dist/api/components/query-storage.d.ts +0 -1
- package/dist/api/components/query-storage.js +1 -1
- package/dist/api/components/query-storage.js.map +1 -1
- package/dist/api/index.d.ts +0 -1
- package/dist/api/types/api.interface.d.ts +0 -1
- package/dist/api/types/endpoint.interface.d.ts +0 -1
- package/dist/api/types/query.interface.d.ts +0 -1
- package/dist/api/utils/api-helpers.d.ts +0 -1
- package/dist/{core/storage → api}/utils/cache.util.d.ts +3 -6
- package/dist/{core/storage → api}/utils/cache.util.js +4 -7
- package/dist/api/utils/cache.util.js.map +1 -0
- package/dist/api/utils/create-header-context.d.ts +0 -1
- package/dist/api/utils/endpoint-headers.d.ts +0 -1
- package/dist/api/utils/fetch-base-query.d.ts +0 -1
- package/dist/api/utils/file-utils.d.ts +0 -1
- package/dist/api/utils/get-cacheable-headers.d.ts +0 -1
- package/dist/core/index.d.ts +0 -1
- package/dist/core/selector/index.d.ts +1 -1
- package/dist/core/selector/index.js +2 -0
- package/dist/core/selector/index.js.map +1 -1
- package/dist/core/selector/selector.interface.d.ts +15 -30
- package/dist/core/selector/selector.interface.js +2 -2
- package/dist/core/selector/selector.interface.js.map +1 -1
- package/dist/core/selector/selector.module.d.ts +16 -1
- package/dist/core/selector/selector.module.js +82 -20
- package/dist/core/selector/selector.module.js.map +1 -1
- package/dist/core/selector/selectors.base.d.ts +56 -0
- package/dist/core/selector/selectors.base.js +118 -0
- package/dist/core/selector/selectors.base.js.map +1 -0
- package/dist/core/storage/adapters/async-base-storage.service.d.ts +15 -4
- package/dist/core/storage/adapters/async-base-storage.service.js +106 -36
- package/dist/core/storage/adapters/async-base-storage.service.js.map +1 -1
- package/dist/core/storage/adapters/indexed-DB.service.d.ts +4 -5
- package/dist/core/storage/adapters/indexed-DB.service.js +66 -14
- package/dist/core/storage/adapters/indexed-DB.service.js.map +1 -1
- package/dist/core/storage/adapters/local-storage.service.d.ts +9 -4
- package/dist/core/storage/adapters/local-storage.service.js +25 -5
- package/dist/core/storage/adapters/local-storage.service.js.map +1 -1
- package/dist/core/storage/adapters/memory-storage.service.d.ts +2 -4
- package/dist/core/storage/adapters/memory-storage.service.js +5 -5
- package/dist/core/storage/adapters/memory-storage.service.js.map +1 -1
- package/dist/core/storage/adapters/path.utils.d.ts +0 -1
- package/dist/core/storage/adapters/storage-core.d.ts +6 -2
- package/dist/core/storage/adapters/storage-core.js +6 -3
- package/dist/core/storage/adapters/storage-core.js.map +1 -1
- package/dist/core/storage/adapters/sync-base-storage.service.d.ts +20 -4
- package/dist/core/storage/adapters/sync-base-storage.service.js +110 -35
- package/dist/core/storage/adapters/sync-base-storage.service.js.map +1 -1
- package/dist/core/storage/index.d.ts +2 -5
- package/dist/core/storage/index.js +1 -5
- package/dist/core/storage/index.js.map +1 -1
- package/dist/core/storage/middlewares/broadcast.middleware.d.ts +0 -1
- package/dist/core/storage/middlewares/index.d.ts +3 -1
- package/dist/core/storage/middlewares/index.js +6 -0
- package/dist/core/storage/middlewares/index.js.map +1 -1
- package/dist/core/storage/middlewares/storage-batching.middleware.d.ts +0 -1
- package/dist/core/storage/middlewares/storage-logger.middleware.d.ts +20 -0
- package/dist/core/storage/middlewares/storage-logger.middleware.js +53 -0
- package/dist/core/storage/middlewares/storage-logger.middleware.js.map +1 -0
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.d.ts +0 -1
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.js +4 -10
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.js.map +1 -1
- package/dist/core/storage/middlewares/sync-broadcast.middleware.d.ts +0 -1
- package/dist/core/storage/middlewares/sync-storage-batching.middleware.d.ts +0 -1
- package/dist/core/storage/middlewares/sync-storage-logger.middleware.d.ts +7 -0
- package/dist/core/storage/middlewares/sync-storage-logger.middleware.js +48 -0
- package/dist/core/storage/middlewares/sync-storage-logger.middleware.js.map +1 -0
- package/dist/core/storage/middlewares/sync-storage-shallow-compare.middleware.d.ts +0 -1
- package/dist/core/storage/middlewares/sync-storage-shallow-compare.middleware.js +4 -10
- package/dist/core/storage/middlewares/sync-storage-shallow-compare.middleware.js.map +1 -1
- package/dist/core/storage/modules/singleton/mixin.util.d.ts +0 -1
- package/dist/core/storage/modules/singleton/models.d.ts +0 -1
- package/dist/core/storage/modules/singleton/singleton.util.d.ts +0 -1
- package/dist/core/storage/storage.interface.d.ts +59 -4
- package/dist/core/storage/storage.interface.js +0 -2
- package/dist/core/storage/storage.interface.js.map +1 -1
- package/dist/core/storage/utils/broadcast.util.d.ts +0 -1
- package/dist/core/storage/utils/middleware-module.d.ts +0 -3
- package/dist/core/storage/utils/middleware-module.js +0 -8
- package/dist/core/storage/utils/middleware-module.js.map +1 -1
- package/dist/core/storage/utils/migration.util.d.ts +38 -0
- package/dist/core/storage/utils/migration.util.js +48 -0
- package/dist/core/storage/utils/migration.util.js.map +1 -0
- package/dist/core/storage/utils/path-selector.util.d.ts +0 -1
- package/dist/core/storage/utils/state-diff.util.d.ts +8 -1
- package/dist/core/storage/utils/state-diff.util.js +17 -1
- package/dist/core/storage/utils/state-diff.util.js.map +1 -1
- package/dist/core/storage/utils/storage-factory.util.d.ts +7 -9
- package/dist/core/storage/utils/storage-factory.util.js +10 -10
- package/dist/core/storage/utils/storage-factory.util.js.map +1 -1
- package/dist/core/storage/utils/storage-key.d.ts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/react/hooks/index.d.ts +2 -1
- package/dist/react/hooks/index.js +4 -0
- package/dist/react/hooks/index.js.map +1 -1
- package/dist/react/hooks/useCreateStorage.d.ts +5 -6
- package/dist/react/hooks/useCreateStorage.js +2 -2
- package/dist/react/hooks/useCreateStorage.js.map +1 -1
- package/dist/react/hooks/useObservable.d.ts +17 -0
- package/dist/react/hooks/useObservable.js +38 -0
- package/dist/react/hooks/useObservable.js.map +1 -0
- package/dist/react/hooks/useSelector.d.ts +0 -1
- package/dist/react/hooks/useSelector.js +5 -2
- package/dist/react/hooks/useSelector.js.map +1 -1
- package/dist/react/hooks/useStorage.d.ts +0 -1
- package/dist/react/hooks/useStorageSubscribe.d.ts +0 -1
- package/dist/react/hooks/useSubscription.d.ts +13 -0
- package/dist/react/hooks/useSubscription.js +23 -0
- package/dist/react/hooks/useSubscription.js.map +1 -0
- package/dist/react/index.d.ts +0 -1
- package/dist/react/utils/awaitSynapse.d.ts +9 -10
- package/dist/react/utils/awaitSynapse.js +3 -2
- package/dist/react/utils/awaitSynapse.js.map +1 -1
- package/dist/react/utils/createSynapseCtx.d.ts +18 -23
- package/dist/react/utils/createSynapseCtx.js +64 -39
- package/dist/react/utils/createSynapseCtx.js.map +1 -1
- package/dist/react/utils/index.d.ts +0 -1
- package/dist/reactive/dispatcher/dispatcher.base.d.ts +122 -0
- package/dist/reactive/dispatcher/dispatcher.base.js +294 -0
- package/dist/reactive/dispatcher/dispatcher.base.js.map +1 -0
- package/dist/reactive/dispatcher/dispatcher.module.d.ts +12 -67
- package/dist/reactive/dispatcher/dispatcher.module.js +13 -72
- package/dist/reactive/dispatcher/dispatcher.module.js.map +1 -1
- package/dist/reactive/dispatcher/index.d.ts +1 -1
- package/dist/reactive/dispatcher/index.js +2 -0
- package/dist/reactive/dispatcher/index.js.map +1 -1
- package/dist/reactive/dispatcher/middlewares/index.d.ts +0 -1
- package/dist/reactive/dispatcher/middlewares/logger.middleware.d.ts +0 -1
- package/dist/reactive/dispatcher/path.util.d.ts +15 -0
- package/dist/reactive/dispatcher/path.util.js +34 -0
- package/dist/reactive/dispatcher/path.util.js.map +1 -0
- package/dist/reactive/dispatcher/standalone.d.ts +1 -150
- package/dist/reactive/dispatcher/standalone.js +6 -217
- package/dist/reactive/dispatcher/standalone.js.map +1 -1
- package/dist/reactive/effects/effects.base.d.ts +62 -0
- package/dist/reactive/effects/effects.base.js +90 -0
- package/dist/reactive/effects/effects.base.js.map +1 -0
- package/dist/reactive/effects/effects.module.d.ts +122 -11
- package/dist/reactive/effects/effects.module.js +129 -17
- package/dist/reactive/effects/effects.module.js.map +1 -1
- package/dist/reactive/effects/index.d.ts +1 -1
- package/dist/reactive/effects/index.js +2 -0
- package/dist/reactive/effects/index.js.map +1 -1
- package/dist/reactive/effects/utils/chunkRequestConsistent.d.ts +0 -1
- package/dist/reactive/effects/utils/chunkRequestParallel.d.ts +0 -1
- package/dist/reactive/effects/utils/fromRequest.d.ts +0 -1
- package/dist/reactive/effects/utils/index.d.ts +0 -1
- package/dist/reactive/effects/utils/toObservable.d.ts +0 -1
- package/dist/reactive/index.d.ts +0 -1
- package/dist/utils/createEventBus.d.ts +47 -46
- package/dist/utils/createEventBus.js +152 -174
- package/dist/utils/createEventBus.js.map +1 -1
- package/dist/utils/createSynapse/createSynapse.d.ts +25 -7
- package/dist/utils/createSynapse/createSynapse.js +28 -98
- package/dist/utils/createSynapse/createSynapse.js.map +1 -1
- package/dist/utils/createSynapse/factory.d.ts +6 -0
- package/dist/utils/createSynapse/factory.js +256 -0
- package/dist/utils/createSynapse/factory.js.map +1 -0
- package/dist/utils/createSynapse/index.d.ts +2 -2
- package/dist/utils/createSynapse/index.js.map +1 -1
- package/dist/utils/createSynapse/synapse.types.d.ts +87 -0
- package/dist/utils/createSynapse/synapse.types.js +11 -0
- package/dist/utils/createSynapse/synapse.types.js.map +1 -0
- package/dist/utils/createSynapse/types.d.ts +6 -85
- package/dist/utils/createSynapse/types.js +2 -1
- package/dist/utils/createSynapse/types.js.map +1 -1
- package/dist/utils/createSynapse/waitForDependencies.d.ts +0 -1
- package/dist/utils/createSynapse/waitForDependencies.js +1 -1
- package/dist/utils/createSynapse/waitForDependencies.js.map +1 -1
- package/dist/utils/createSynapseAwaiter.d.ts +13 -10
- package/dist/utils/createSynapseAwaiter.js +30 -3
- package/dist/utils/createSynapseAwaiter.js.map +1 -1
- package/dist/utils/dehydrateModule.d.ts +6 -0
- package/dist/utils/dehydrateModule.js +43 -0
- package/dist/utils/dehydrateModule.js.map +1 -0
- package/dist/utils/index.d.ts +3 -3
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -1
- package/package.json +12 -2
- package/dist/_utils/chunk.util.d.ts.map +0 -1
- package/dist/_utils/deepMerge.util.d.ts.map +0 -1
- package/dist/_utils/error-handling.util.d.ts.map +0 -1
- package/dist/_utils/flatMap.util.d.ts.map +0 -1
- package/dist/_utils/index.d.ts.map +0 -1
- package/dist/_utils/logger-console.util.d.ts.map +0 -1
- package/dist/api/api.module.d.ts.map +0 -1
- package/dist/api/components/endpoint.d.ts.map +0 -1
- package/dist/api/components/query-storage.d.ts.map +0 -1
- package/dist/api/example.d.ts +0 -83
- package/dist/api/example.d.ts.map +0 -1
- package/dist/api/example.js +0 -90
- package/dist/api/example.js.map +0 -1
- package/dist/api/index.d.ts.map +0 -1
- package/dist/api/types/api.interface.d.ts.map +0 -1
- package/dist/api/types/endpoint.interface.d.ts.map +0 -1
- package/dist/api/types/query.interface.d.ts.map +0 -1
- package/dist/api/utils/api-helpers.d.ts.map +0 -1
- package/dist/api/utils/create-header-context.d.ts.map +0 -1
- package/dist/api/utils/endpoint-headers.d.ts.map +0 -1
- package/dist/api/utils/fetch-base-query.d.ts.map +0 -1
- package/dist/api/utils/file-utils.d.ts.map +0 -1
- package/dist/api/utils/get-cacheable-headers.d.ts.map +0 -1
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/selector/index.d.ts.map +0 -1
- package/dist/core/selector/selector.interface.d.ts.map +0 -1
- package/dist/core/selector/selector.module.d.ts.map +0 -1
- package/dist/core/storage/adapters/async-base-storage.service.d.ts.map +0 -1
- package/dist/core/storage/adapters/indexed-DB.service.d.ts.map +0 -1
- package/dist/core/storage/adapters/local-storage.service.d.ts.map +0 -1
- package/dist/core/storage/adapters/memory-storage.service.d.ts.map +0 -1
- package/dist/core/storage/adapters/path.utils.d.ts.map +0 -1
- package/dist/core/storage/adapters/storage-core.d.ts.map +0 -1
- package/dist/core/storage/adapters/sync-base-storage.service.d.ts.map +0 -1
- package/dist/core/storage/index.d.ts.map +0 -1
- package/dist/core/storage/middlewares/broadcast.middleware.d.ts.map +0 -1
- package/dist/core/storage/middlewares/index.d.ts.map +0 -1
- package/dist/core/storage/middlewares/storage-batching.middleware.d.ts.map +0 -1
- package/dist/core/storage/middlewares/storage-shallow-compare.middleware.d.ts.map +0 -1
- package/dist/core/storage/middlewares/sync-broadcast.middleware.d.ts.map +0 -1
- package/dist/core/storage/middlewares/sync-storage-batching.middleware.d.ts.map +0 -1
- package/dist/core/storage/middlewares/sync-storage-shallow-compare.middleware.d.ts.map +0 -1
- package/dist/core/storage/modules/plugin/plugin.interface.d.ts +0 -101
- package/dist/core/storage/modules/plugin/plugin.interface.d.ts.map +0 -1
- package/dist/core/storage/modules/plugin/plugin.interface.js +0 -8
- package/dist/core/storage/modules/plugin/plugin.interface.js.map +0 -1
- package/dist/core/storage/modules/plugin/plugin.service.d.ts +0 -49
- package/dist/core/storage/modules/plugin/plugin.service.d.ts.map +0 -1
- package/dist/core/storage/modules/plugin/plugin.service.js +0 -406
- package/dist/core/storage/modules/plugin/plugin.service.js.map +0 -1
- package/dist/core/storage/modules/singleton/mixin.util.d.ts.map +0 -1
- package/dist/core/storage/modules/singleton/models.d.ts.map +0 -1
- package/dist/core/storage/modules/singleton/singleton.util.d.ts.map +0 -1
- package/dist/core/storage/storage.interface.d.ts.map +0 -1
- package/dist/core/storage/utils/broadcast.util.d.ts.map +0 -1
- package/dist/core/storage/utils/cache.util.d.ts.map +0 -1
- package/dist/core/storage/utils/cache.util.js.map +0 -1
- package/dist/core/storage/utils/middleware-module.d.ts.map +0 -1
- package/dist/core/storage/utils/path-selector.util.d.ts.map +0 -1
- package/dist/core/storage/utils/state-diff.util.d.ts.map +0 -1
- package/dist/core/storage/utils/storage-factory.util.d.ts.map +0 -1
- package/dist/core/storage/utils/storage-key.d.ts.map +0 -1
- package/dist/core/storage/utils/storage.utils.d.ts +0 -17
- package/dist/core/storage/utils/storage.utils.d.ts.map +0 -1
- package/dist/core/storage/utils/storage.utils.js +0 -57
- package/dist/core/storage/utils/storage.utils.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/react/hooks/index.d.ts.map +0 -1
- package/dist/react/hooks/useCreateStorage.d.ts.map +0 -1
- package/dist/react/hooks/useSelector.d.ts.map +0 -1
- package/dist/react/hooks/useStorage.d.ts.map +0 -1
- package/dist/react/hooks/useStorageSubscribe.d.ts.map +0 -1
- package/dist/react/index.d.ts.map +0 -1
- package/dist/react/utils/awaitSynapse.d.ts.map +0 -1
- package/dist/react/utils/createSynapseCtx.d.ts.map +0 -1
- package/dist/react/utils/index.d.ts.map +0 -1
- package/dist/reactive/dispatcher/dispatcher.module.d.ts.map +0 -1
- package/dist/reactive/dispatcher/index.d.ts.map +0 -1
- package/dist/reactive/dispatcher/middlewares/index.d.ts.map +0 -1
- package/dist/reactive/dispatcher/middlewares/logger.middleware.d.ts.map +0 -1
- package/dist/reactive/dispatcher/standalone.d.ts.map +0 -1
- package/dist/reactive/effects/effects.module.d.ts.map +0 -1
- package/dist/reactive/effects/index.d.ts.map +0 -1
- package/dist/reactive/effects/utils/chunkRequestConsistent.d.ts.map +0 -1
- package/dist/reactive/effects/utils/chunkRequestParallel.d.ts.map +0 -1
- package/dist/reactive/effects/utils/fromRequest.d.ts.map +0 -1
- package/dist/reactive/effects/utils/index.d.ts.map +0 -1
- package/dist/reactive/effects/utils/toObservable.d.ts.map +0 -1
- package/dist/reactive/index.d.ts.map +0 -1
- package/dist/utils/createEventBus.d.ts.map +0 -1
- package/dist/utils/createSynapse/createSynapse.d.ts.map +0 -1
- package/dist/utils/createSynapse/index.d.ts.map +0 -1
- package/dist/utils/createSynapse/types.d.ts.map +0 -1
- package/dist/utils/createSynapse/validate.d.ts +0 -2
- package/dist/utils/createSynapse/validate.d.ts.map +0 -1
- package/dist/utils/createSynapse/validate.js +0 -76
- package/dist/utils/createSynapse/validate.js.map +0 -1
- package/dist/utils/createSynapse/waitForDependencies.d.ts.map +0 -1
- package/dist/utils/createSynapseAwaiter.d.ts.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { SynapseError } from "../../_utils/error-handling.util.js";
|
|
2
|
+
import { SelectorModule, deepEquals } from "./selector.module.js";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Определяет, передан ли в конструктор готовый модуль селекторов или storage.
|
|
10
|
+
* Модуль распознаётся по методу `createSelector` (у `IStorage` его нет).
|
|
11
|
+
*/ function isSelectorModule(source) {
|
|
12
|
+
return typeof source.createSelector === 'function';
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Публичный class-based слой селекторов. Селекторы объявляются как поля класса через
|
|
16
|
+
* фабрики `this.select` / `this.combine` / `this.keyed` — поля сразу настоящие
|
|
17
|
+
* `SelectorAPI` (eager-материализация, никаких рецептов).
|
|
18
|
+
*
|
|
19
|
+
* Внешние селекторы (cross-store) передаются параметрами конструктора подкласса:
|
|
20
|
+
* parameter properties присваиваются ДО инициализаторов полей, поэтому `this.core`
|
|
21
|
+
* в полях доступен корректно.
|
|
22
|
+
*
|
|
23
|
+
* Внутреннее состояние базы — hard-private (`#`-поля/методы): их имена в отдельном
|
|
24
|
+
* namespace и НЕ конфликтуют с полями-селекторами подкласса (можно объявить селектор
|
|
25
|
+
* `track`, `module` и т.п.). Зарезервированы лишь `protected`/публичные члены
|
|
26
|
+
* (`select`/`combine`/`keyed`/`destroy`) — их именами селекторы называть нельзя.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* class PostsSelectors extends Selectors<PostsState> {
|
|
31
|
+
* constructor(storage: IStorage<PostsState>, private readonly core: CoreSelectors) {
|
|
32
|
+
* super(storage)
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* private readonly api = this.select((s) => s.api)
|
|
36
|
+
* readonly list = this.select((s) => s.list)
|
|
37
|
+
* readonly isPostsLoading = this.combine([this.api], (a) => a.postsRequest.status === 'loading')
|
|
38
|
+
* // cross-store: пересчитывается при изменении чужого стора
|
|
39
|
+
* readonly currentUserId = this.combine([this.core.profile], (p) => p?.user_info?.id ?? null)
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*/ class Selectors {
|
|
43
|
+
/** Модуль, поверх которого работает class-слой. */ #module;
|
|
44
|
+
/** Владеем ли мы модулем (создали из storage) — только тогда destroy уничтожает его целиком. */ #ownsModule;
|
|
45
|
+
/** id всех созданных нами селекторов — для точечной очистки общего (чужого) модуля. */ #ownSelectorIds = [];
|
|
46
|
+
/** Кэши keyed-фабрик — очищаются при destroy. */ #keyedCaches = [];
|
|
47
|
+
/** Принимает `storage` (создаёт и владеет своим `SelectorModule`) либо готовый модуль. */ constructor(source){
|
|
48
|
+
if (isSelectorModule(source)) {
|
|
49
|
+
this.#module = source;
|
|
50
|
+
this.#ownsModule = false;
|
|
51
|
+
} else {
|
|
52
|
+
this.#module = new SelectorModule(source);
|
|
53
|
+
this.#ownsModule = true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Простой селектор: мемоизация по ссылке стейта + трекинг затронутых ключей. */ select(selector, options) {
|
|
57
|
+
return this.#track(this.#module.createSelector(selector, options));
|
|
58
|
+
}
|
|
59
|
+
/** Combined-селектор; зависимости — любые `SelectorAPI`, в т.ч. из других сторов. */ combine(deps, fn, options) {
|
|
60
|
+
if (process.env.NODE_ENV !== 'production') this.#assertDepsDefined(deps);
|
|
61
|
+
return this.#track(this.#module.createSelector(deps, fn, options));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Dev-проверка зависимостей `combine`. Ловит частую ловушку: cross-store eager-селектор
|
|
65
|
+
* (`this.combine([this.core.x], …)`, где `core` — parameter property конструктора) при
|
|
66
|
+
* `useDefineForClassFields: true` (дефолт target ES2022) видит `this.core === undefined`
|
|
67
|
+
* на момент инициализатора поля — зависимость молча оказывается `undefined`, селектор не
|
|
68
|
+
* пересчитывается и тихо отдаёт мусор. Бросаем понятную ошибку вместо тихого сбоя.
|
|
69
|
+
*/ #assertDepsDefined(deps) {
|
|
70
|
+
const badIndex = deps.findIndex((dep)=>dep == null || typeof dep.subscribe !== 'function');
|
|
71
|
+
if (badIndex !== -1) {
|
|
72
|
+
throw new SynapseError(`combine(): зависимость #${badIndex} === ${String(deps[badIndex])} (не SelectorAPI). ` + 'Похоже, cross-store селектор инициализируется ДО присваивания parameter property ' + '(`this.<dep>` ещё undefined в момент инициализатора поля). Включите ' + '`"useDefineForClassFields": false` в tsconfig, либо создавайте такие селекторы ' + 'в теле конструктора после `super()`.', 'Selectors.combine');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Параметрический (keyed) селектор: один `SelectorAPI` на ключ (кэш по ключу).
|
|
77
|
+
*
|
|
78
|
+
* Слайсы соседних ключей живут под общим родителем (`s.byTarget[key]`), а storage при
|
|
79
|
+
* обновлении пере-клонирует всю ветку — поэтому ссылка соседнего ключа не сохраняется.
|
|
80
|
+
* Чтобы обновление ключа A не уведомляло подписчиков ключа B, keyed-селекторы по
|
|
81
|
+
* умолчанию сравнивают значения структурно (`deepEquals`). Опции можно переопределить.
|
|
82
|
+
*/ keyed(fn, options) {
|
|
83
|
+
const cache = new Map();
|
|
84
|
+
this.#keyedCaches.push(cache);
|
|
85
|
+
return (key)=>{
|
|
86
|
+
let api = cache.get(key);
|
|
87
|
+
if (!api) {
|
|
88
|
+
api = this.#track(this.#module.createSelector(fn(key), {
|
|
89
|
+
equals: deepEquals,
|
|
90
|
+
...options
|
|
91
|
+
}));
|
|
92
|
+
cache.set(key, api);
|
|
93
|
+
}
|
|
94
|
+
return api;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Уничтожает свой модуль (только если владеет им). Если модуль передан снаружи —
|
|
99
|
+
* удаляет из него лишь свои селекторы, чужие не трогает.
|
|
100
|
+
*/ destroy() {
|
|
101
|
+
if (this.#ownsModule) {
|
|
102
|
+
this.#module.destroy();
|
|
103
|
+
} else {
|
|
104
|
+
for (const id of this.#ownSelectorIds){
|
|
105
|
+
this.#module.removeSelector(id);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
this.#keyedCaches.forEach((cache)=>cache.clear());
|
|
109
|
+
}
|
|
110
|
+
/** Регистрирует id селектора для последующей точечной очистки общего модуля. */ #track(api) {
|
|
111
|
+
this.#ownSelectorIds.push(api.getId());
|
|
112
|
+
return api;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export { Selectors };
|
|
117
|
+
|
|
118
|
+
//# sourceMappingURL=selectors.base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core/selector/selectors.base.js","sources":["../../../src/core/selector/selectors.base.ts"],"sourcesContent":["import { SynapseError } from '../../_utils/error-handling.util'\nimport type { IStorage } from '../storage'\nimport type { ISelectorModule, SelectorAPI, SelectorOptions } from './selector.interface'\nimport { deepEquals, SelectorModule } from './selector.module'\n\n/**\n * Определяет, передан ли в конструктор готовый модуль селекторов или storage.\n * Модуль распознаётся по методу `createSelector` (у `IStorage` его нет).\n */\nfunction isSelectorModule<TState extends Record<string, any>>(source: IStorage<TState> | ISelectorModule<TState>): source is ISelectorModule<TState> {\n return typeof (source as ISelectorModule<TState>).createSelector === 'function'\n}\n\n/**\n * Публичный class-based слой селекторов. Селекторы объявляются как поля класса через\n * фабрики `this.select` / `this.combine` / `this.keyed` — поля сразу настоящие\n * `SelectorAPI` (eager-материализация, никаких рецептов).\n *\n * Внешние селекторы (cross-store) передаются параметрами конструктора подкласса:\n * parameter properties присваиваются ДО инициализаторов полей, поэтому `this.core`\n * в полях доступен корректно.\n *\n * Внутреннее состояние базы — hard-private (`#`-поля/методы): их имена в отдельном\n * namespace и НЕ конфликтуют с полями-селекторами подкласса (можно объявить селектор\n * `track`, `module` и т.п.). Зарезервированы лишь `protected`/публичные члены\n * (`select`/`combine`/`keyed`/`destroy`) — их именами селекторы называть нельзя.\n *\n * @example\n * ```ts\n * class PostsSelectors extends Selectors<PostsState> {\n * constructor(storage: IStorage<PostsState>, private readonly core: CoreSelectors) {\n * super(storage)\n * }\n *\n * private readonly api = this.select((s) => s.api)\n * readonly list = this.select((s) => s.list)\n * readonly isPostsLoading = this.combine([this.api], (a) => a.postsRequest.status === 'loading')\n * // cross-store: пересчитывается при изменении чужого стора\n * readonly currentUserId = this.combine([this.core.profile], (p) => p?.user_info?.id ?? null)\n * }\n * ```\n */\nexport abstract class Selectors<TState extends Record<string, any>> {\n /** Модуль, поверх которого работает class-слой. */\n readonly #module: ISelectorModule<TState>\n\n /** Владеем ли мы модулем (создали из storage) — только тогда destroy уничтожает его целиком. */\n readonly #ownsModule: boolean\n\n /** id всех созданных нами селекторов — для точечной очистки общего (чужого) модуля. */\n readonly #ownSelectorIds: string[] = []\n\n /** Кэши keyed-фабрик — очищаются при destroy. */\n readonly #keyedCaches: Array<Map<any, SelectorAPI<any>>> = []\n\n /** Принимает `storage` (создаёт и владеет своим `SelectorModule`) либо готовый модуль. */\n constructor(source: IStorage<TState> | ISelectorModule<TState>) {\n if (isSelectorModule<TState>(source)) {\n this.#module = source\n this.#ownsModule = false\n } else {\n this.#module = new SelectorModule<TState>(source)\n this.#ownsModule = true\n }\n }\n\n /** Простой селектор: мемоизация по ссылке стейта + трекинг затронутых ключей. */\n protected select<R>(selector: (state: TState) => R, options?: SelectorOptions<R>): SelectorAPI<R> {\n return this.#track(this.#module.createSelector(selector, options))\n }\n\n /** Combined-селектор; зависимости — любые `SelectorAPI`, в т.ч. из других сторов. */\n protected combine<Deps extends unknown[], R>(deps: { [K in keyof Deps]: SelectorAPI<Deps[K]> }, fn: (...args: Deps) => R, options?: SelectorOptions<R>): SelectorAPI<R> {\n if (process.env.NODE_ENV !== 'production') this.#assertDepsDefined(deps)\n return this.#track(this.#module.createSelector(deps, fn, options))\n }\n\n /**\n * Dev-проверка зависимостей `combine`. Ловит частую ловушку: cross-store eager-селектор\n * (`this.combine([this.core.x], …)`, где `core` — parameter property конструктора) при\n * `useDefineForClassFields: true` (дефолт target ES2022) видит `this.core === undefined`\n * на момент инициализатора поля — зависимость молча оказывается `undefined`, селектор не\n * пересчитывается и тихо отдаёт мусор. Бросаем понятную ошибку вместо тихого сбоя.\n */\n #assertDepsDefined(deps: ReadonlyArray<SelectorAPI<any>>): void {\n const badIndex = deps.findIndex((dep) => dep == null || typeof dep.subscribe !== 'function')\n if (badIndex !== -1) {\n throw new SynapseError(\n `combine(): зависимость #${badIndex} === ${String(deps[badIndex])} (не SelectorAPI). ` +\n 'Похоже, cross-store селектор инициализируется ДО присваивания parameter property ' +\n '(`this.<dep>` ещё undefined в момент инициализатора поля). Включите ' +\n '`\"useDefineForClassFields\": false` в tsconfig, либо создавайте такие селекторы ' +\n 'в теле конструктора после `super()`.',\n 'Selectors.combine',\n )\n }\n }\n\n /**\n * Параметрический (keyed) селектор: один `SelectorAPI` на ключ (кэш по ключу).\n *\n * Слайсы соседних ключей живут под общим родителем (`s.byTarget[key]`), а storage при\n * обновлении пере-клонирует всю ветку — поэтому ссылка соседнего ключа не сохраняется.\n * Чтобы обновление ключа A не уведомляло подписчиков ключа B, keyed-селекторы по\n * умолчанию сравнивают значения структурно (`deepEquals`). Опции можно переопределить.\n */\n protected keyed<K extends string | number, R>(fn: (key: K) => (state: TState) => R, options?: SelectorOptions<R>): (key: K) => SelectorAPI<R> {\n const cache = new Map<K, SelectorAPI<R>>()\n this.#keyedCaches.push(cache)\n\n return (key: K): SelectorAPI<R> => {\n let api = cache.get(key)\n if (!api) {\n api = this.#track(this.#module.createSelector(fn(key), { equals: deepEquals, ...options }))\n cache.set(key, api)\n }\n return api\n }\n }\n\n /**\n * Уничтожает свой модуль (только если владеет им). Если модуль передан снаружи —\n * удаляет из него лишь свои селекторы, чужие не трогает.\n */\n destroy(): void {\n if (this.#ownsModule) {\n this.#module.destroy()\n } else {\n for (const id of this.#ownSelectorIds) {\n this.#module.removeSelector(id)\n }\n }\n this.#keyedCaches.forEach((cache) => cache.clear())\n }\n\n /** Регистрирует id селектора для последующей точечной очистки общего модуля. */\n #track<R>(api: SelectorAPI<R>): SelectorAPI<R> {\n this.#ownSelectorIds.push(api.getId())\n return api\n }\n}\n"],"names":["SynapseError","deepEquals","SelectorModule","isSelectorModule","source","Selectors","selector","options","deps","fn","process","badIndex","dep","String","cache","Map","key","api","id"],"mappings":";;;;;AAA+D;AAGD;AAE9D;;;CAGC,GACD,SAASG,gBAAgBA,CAAqCC,MAAkD;IAC9G,OAAO,OAAQA,OAAmC,cAAc,KAAK;AACvE;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BC,GACM,MAAeC,SAASA;IAC7B,iDAAiD,GACxC,OAAO,CAAyB;IAEzC,8FAA8F,GACrF,WAAW,CAAS;IAE7B,qFAAqF,GAC5E,eAAe,GAAa,EAAE;IAEvC,+CAA+C,GACtC,YAAY,GAAsC,EAAE;IAE7D,wFAAwF,GACxF,YAAYD,MAAkD,CAAE;QAC9D,IAAID,gBAAgBA,CAASC,SAAS;YACpC,IAAI,CAAC,OAAO,GAAGA;YACf,IAAI,CAAC,WAAW,GAAG;QACrB,OAAO;YACL,IAAI,CAAC,OAAO,GAAG,IAAIF,cAAcA,CAASE;YAC1C,IAAI,CAAC,WAAW,GAAG;QACrB;IACF;IAEA,+EAA+E,GACrE,OAAUE,QAA8B,EAAEC,OAA4B,EAAkB;QAChG,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAACD,UAAUC;IAC3D;IAEA,mFAAmF,GACzE,QAAmCC,IAAiD,EAAEC,EAAwB,EAAEF,OAA4B,EAAkB;QACtK,IAAIG,QAAQ,GAAG,CAAC,QAAQ,KAAK,cAAc,IAAI,CAAC,kBAAkB,CAACF;QACnE,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAACA,MAAMC,IAAIF;IAC3D;IAEA;;;;;;GAMC,GACD,kBAAkB,CAACC,IAAqC;QACtD,MAAMG,WAAWH,KAAK,SAAS,CAAC,CAACI,MAAQA,OAAO,QAAQ,OAAOA,IAAI,SAAS,KAAK;QACjF,IAAID,aAAa,CAAC,GAAG;YACnB,MAAM,IAAIX,YAAYA,CACpB,CAAC,wBAAwB,EAAEW,SAAS,KAAK,EAAEE,OAAOL,IAAI,CAACG,SAAS,EAAE,mBAAmB,CAAC,GACpF,sFACA,yEACA,oFACA,wCACF;QAEJ;IACF;IAEA;;;;;;;GAOC,GACS,MAAoCF,EAAoC,EAAEF,OAA4B,EAA8B;QAC5I,MAAMO,QAAQ,IAAIC;QAClB,IAAI,CAAC,YAAY,CAAC,IAAI,CAACD;QAEvB,OAAO,CAACE;YACN,IAAIC,MAAMH,MAAM,GAAG,CAACE;YACpB,IAAI,CAACC,KAAK;gBACRA,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAACR,GAAGO,MAAM;oBAAE,QAAQf,UAAUA;oBAAE,GAAGM,OAAO;gBAAC;gBACxFO,MAAM,GAAG,CAACE,KAAKC;YACjB;YACA,OAAOA;QACT;IACF;IAEA;;;GAGC,GACD,UAAgB;QACd,IAAI,IAAI,CAAC,WAAW,EAAE;YACpB,IAAI,CAAC,OAAO,CAAC,OAAO;QACtB,OAAO;YACL,KAAK,MAAMC,MAAM,IAAI,CAAC,eAAe,CAAE;gBACrC,IAAI,CAAC,OAAO,CAAC,cAAc,CAACA;YAC9B;QACF;QACA,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAACJ,QAAUA,MAAM,KAAK;IAClD;IAEA,8EAA8E,GAC9E,MAAM,CAAIG,GAAmB;QAC3B,IAAI,CAAC,eAAe,CAAC,IAAI,CAACA,IAAI,KAAK;QACnC,OAAOA;IACT;AACF"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { IAsyncPluginExecutor } from '../modules/plugin/plugin.interface';
|
|
2
1
|
import { AsyncDefaultMiddlewares, AsyncStorageConfig, IAsyncStorage, IEventEmitter, ILogger, StorageType } from '../storage.interface';
|
|
3
2
|
import { StorageKeyType } from '../utils/storage-key';
|
|
4
3
|
import { PathSelector, StorageCore } from './storage-core';
|
|
@@ -10,12 +9,15 @@ import { PathSelector, StorageCore } from './storage-core';
|
|
|
10
9
|
*/
|
|
11
10
|
export declare abstract class AsyncBaseStorage<T extends Record<string, any>> extends StorageCore<T> implements IAsyncStorage<T> {
|
|
12
11
|
protected readonly config: AsyncStorageConfig<T>;
|
|
13
|
-
protected readonly pluginExecutor?: IAsyncPluginExecutor | undefined;
|
|
14
12
|
abstract readonly type: StorageType;
|
|
15
13
|
private middlewareModule;
|
|
16
14
|
private initializedMiddlewares;
|
|
17
15
|
private selectorPathCache;
|
|
18
|
-
|
|
16
|
+
/** Версии ключей для защиты от race condition в subscribeByKey (async get). */
|
|
17
|
+
private keyVersions;
|
|
18
|
+
/** Инкремент версии ключа при каждом изменении — читается в subscribeByKey. */
|
|
19
|
+
protected trackKeyVersion(keyStr: string): void;
|
|
20
|
+
constructor(config: AsyncStorageConfig<T>, eventEmitter?: IEventEmitter, logger?: ILogger);
|
|
19
21
|
protected abstract doGet(key: StorageKeyType): Promise<any>;
|
|
20
22
|
protected abstract doSet(key: StorageKeyType, value: any): Promise<void>;
|
|
21
23
|
protected abstract doUpdate(updates: Array<{
|
|
@@ -33,6 +35,10 @@ export declare abstract class AsyncBaseStorage<T extends Record<string, any>> ex
|
|
|
33
35
|
protected initializeMiddlewares(): void;
|
|
34
36
|
protected getDefaultMiddleware(): AsyncDefaultMiddlewares;
|
|
35
37
|
protected initializeWithMiddlewares(): Promise<void>;
|
|
38
|
+
/** Читает сохранённую версию схемы. По умолчанию `undefined` (нет персистентности). */
|
|
39
|
+
protected readPersistedVersion(): Promise<number | undefined>;
|
|
40
|
+
/** Сохраняет версию схемы рядом с данными. По умолчанию no-op. */
|
|
41
|
+
protected writePersistedVersion(_version: number): Promise<void>;
|
|
36
42
|
private getRawState;
|
|
37
43
|
get<R>(key: StorageKeyType): Promise<R | undefined>;
|
|
38
44
|
set<R>(key: StorageKeyType, value: R): Promise<void>;
|
|
@@ -40,10 +46,15 @@ export declare abstract class AsyncBaseStorage<T extends Record<string, any>> ex
|
|
|
40
46
|
remove(key: StorageKeyType): Promise<void>;
|
|
41
47
|
clear(): Promise<void>;
|
|
42
48
|
reset(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* SSR-гидрация: заменяет всё состояние переданным снапшотом. Намеренно НЕ требует
|
|
51
|
+
* `ready()` — типичный сценарий вызвать её до `initialize()`, чтобы инициализация
|
|
52
|
+
* не перезатёрла серверное состояние `initialState`-ом (см. `initializeWithMiddlewares`).
|
|
53
|
+
*/
|
|
54
|
+
hydrate(state: T): Promise<void>;
|
|
43
55
|
keys(): Promise<string[]>;
|
|
44
56
|
has(key: StorageKeyType): Promise<boolean>;
|
|
45
57
|
getState(): Promise<T>;
|
|
46
58
|
protected subscribeByKey(key: string, callback: (value: any) => void): VoidFunction;
|
|
47
59
|
protected subscribeBySelector<R>(pathSelector: PathSelector<T, R>, callback: (value: R) => void): VoidFunction;
|
|
48
60
|
}
|
|
49
|
-
//# sourceMappingURL=async-base-storage.service.d.ts.map
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { batchingMiddleware } from "../middlewares/storage-batching.middleware.js";
|
|
2
|
+
import { loggerMiddleware } from "../middlewares/storage-logger.middleware.js";
|
|
2
3
|
import { shallowCompareMiddleware } from "../middlewares/storage-shallow-compare.middleware.js";
|
|
3
4
|
import { StorageEvents } from "../storage.interface.js";
|
|
4
5
|
import { AsyncMiddlewareModule, VALUE_NOT_CHANGED } from "../utils/middleware-module.js";
|
|
6
|
+
import { decideMigration } from "../utils/migration.util.js";
|
|
5
7
|
import { createDummyState, extractPath } from "../utils/path-selector.util.js";
|
|
6
8
|
import { createLazyClone, findChangedPaths, isEqual } from "../utils/state-diff.util.js";
|
|
7
9
|
import { getValueByPath } from "./path.utils.js";
|
|
@@ -22,6 +24,10 @@ import { GLOBAL_SUBSCRIPTION_KEY, StorageCore } from "./storage-core.js";
|
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
25
31
|
|
|
26
32
|
/**
|
|
27
33
|
* Базовый класс для асинхронных хранилищ (IndexedDB).
|
|
@@ -30,12 +36,15 @@ import { GLOBAL_SUBSCRIPTION_KEY, StorageCore } from "./storage-core.js";
|
|
|
30
36
|
* Совместим с текущим API (переименован из BaseStorage).
|
|
31
37
|
*/ class AsyncBaseStorage extends StorageCore {
|
|
32
38
|
config;
|
|
33
|
-
pluginExecutor;
|
|
34
39
|
middlewareModule;
|
|
35
40
|
initializedMiddlewares = null;
|
|
36
41
|
selectorPathCache = new WeakMap();
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
/** Версии ключей для защиты от race condition в subscribeByKey (async get). */ keyVersions = new Map();
|
|
43
|
+
/** Инкремент версии ключа при каждом изменении — читается в subscribeByKey. */ trackKeyVersion(keyStr) {
|
|
44
|
+
this.keyVersions.set(keyStr, (this.keyVersions.get(keyStr) ?? 0) + 1);
|
|
45
|
+
}
|
|
46
|
+
constructor(config, eventEmitter, logger){
|
|
47
|
+
super(config, eventEmitter, logger), this.config = config;
|
|
39
48
|
this.middlewareModule = new AsyncMiddlewareModule({
|
|
40
49
|
getState: ()=>this.getRawState(),
|
|
41
50
|
doGet: this.doGet.bind(this),
|
|
@@ -54,7 +63,6 @@ import { GLOBAL_SUBSCRIPTION_KEY, StorageCore } from "./storage-core.js";
|
|
|
54
63
|
}
|
|
55
64
|
async performCleanup() {
|
|
56
65
|
// Обходим публичный clear() (избегая ensureReady после _isDestroyed = true)
|
|
57
|
-
await this.pluginExecutor?.executeOnClear();
|
|
58
66
|
await this.doClear();
|
|
59
67
|
await this.doDestroy();
|
|
60
68
|
if (this.initializedMiddlewares) {
|
|
@@ -76,18 +84,59 @@ import { GLOBAL_SUBSCRIPTION_KEY, StorageCore } from "./storage-core.js";
|
|
|
76
84
|
getDefaultMiddleware() {
|
|
77
85
|
return {
|
|
78
86
|
batching: (options = {})=>batchingMiddleware(options),
|
|
79
|
-
shallowCompare: (options = {})=>shallowCompareMiddleware(options)
|
|
87
|
+
shallowCompare: (options = {})=>shallowCompareMiddleware(options),
|
|
88
|
+
logger: (options = {})=>loggerMiddleware(options)
|
|
80
89
|
};
|
|
81
90
|
}
|
|
82
91
|
async initializeWithMiddlewares() {
|
|
83
92
|
try {
|
|
84
93
|
const state = await this.getRawState();
|
|
85
94
|
const hasExistingState = Object.keys(state).length > 0;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
95
|
+
// Миграция выключена (version не задан) — прежнее поведение: засеять initialState на пустом.
|
|
96
|
+
if (this.config.version === undefined) {
|
|
97
|
+
if (!hasExistingState && this.config.initialState) {
|
|
98
|
+
await this.middlewareModule.dispatch({
|
|
99
|
+
type: 'init',
|
|
100
|
+
value: this.config.initialState
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const decision = decideMigration({
|
|
106
|
+
hasExisting: hasExistingState,
|
|
107
|
+
existingState: state,
|
|
108
|
+
persistedVersion: await this.readPersistedVersion(),
|
|
109
|
+
targetVersion: this.config.version,
|
|
110
|
+
migrate: this.config.migrate
|
|
111
|
+
});
|
|
112
|
+
switch(decision.kind){
|
|
113
|
+
case 'seed':
|
|
114
|
+
{
|
|
115
|
+
if (this.config.initialState) {
|
|
116
|
+
await this.middlewareModule.dispatch({
|
|
117
|
+
type: 'init',
|
|
118
|
+
value: this.config.initialState
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
await this.writePersistedVersion(this.config.version);
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
case 'migrate':
|
|
125
|
+
{
|
|
126
|
+
await this.middlewareModule.dispatch({
|
|
127
|
+
type: 'reset',
|
|
128
|
+
value: decision.state
|
|
129
|
+
});
|
|
130
|
+
await this.writePersistedVersion(this.config.version);
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
case 'bump':
|
|
134
|
+
{
|
|
135
|
+
await this.writePersistedVersion(this.config.version);
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
case 'none':
|
|
139
|
+
break;
|
|
91
140
|
}
|
|
92
141
|
} catch (error) {
|
|
93
142
|
this.logger?.error('Ошибка инициализации хранилища', {
|
|
@@ -96,6 +145,11 @@ import { GLOBAL_SUBSCRIPTION_KEY, StorageCore } from "./storage-core.js";
|
|
|
96
145
|
throw error;
|
|
97
146
|
}
|
|
98
147
|
}
|
|
148
|
+
// ─── Persisted schema version (persist-migration) ───────────────────────────
|
|
149
|
+
/** Читает сохранённую версию схемы. По умолчанию `undefined` (нет персистентности). */ async readPersistedVersion() {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
/** Сохраняет версию схемы рядом с данными. По умолчанию no-op. */ async writePersistedVersion(_version) {}
|
|
99
153
|
// ─── Internal state access ──────────────────────────────────────────────────
|
|
100
154
|
async getRawState() {
|
|
101
155
|
try {
|
|
@@ -117,19 +171,12 @@ import { GLOBAL_SUBSCRIPTION_KEY, StorageCore } from "./storage-core.js";
|
|
|
117
171
|
timestamp: Date.now(),
|
|
118
172
|
key
|
|
119
173
|
};
|
|
120
|
-
const
|
|
174
|
+
const finalResult = await this.middlewareModule.dispatch({
|
|
121
175
|
type: 'get',
|
|
122
176
|
key,
|
|
123
177
|
metadata
|
|
124
178
|
});
|
|
125
|
-
|
|
126
|
-
await this.emitEvent({
|
|
127
|
-
type: StorageEvents.STORAGE_SELECT,
|
|
128
|
-
payload: {
|
|
129
|
-
key,
|
|
130
|
-
value: finalResult
|
|
131
|
-
}
|
|
132
|
-
});
|
|
179
|
+
// Чтения не эмитят событий — это горячий путь, а STORAGE_SELECT нигде не потребляется.
|
|
133
180
|
return finalResult;
|
|
134
181
|
} catch (error) {
|
|
135
182
|
this.logger?.error('Error getting value', {
|
|
@@ -147,15 +194,13 @@ import { GLOBAL_SUBSCRIPTION_KEY, StorageCore } from "./storage-core.js";
|
|
|
147
194
|
timestamp: Date.now(),
|
|
148
195
|
key
|
|
149
196
|
};
|
|
150
|
-
const
|
|
151
|
-
const middlewareResult = await this.middlewareModule.dispatch({
|
|
197
|
+
const finalResult = await this.middlewareModule.dispatch({
|
|
152
198
|
type: 'set',
|
|
153
199
|
key,
|
|
154
|
-
value
|
|
200
|
+
value,
|
|
155
201
|
metadata
|
|
156
202
|
});
|
|
157
|
-
if (
|
|
158
|
-
const finalResult = await this.pluginExecutor?.executeAfterSet(key, middlewareResult, metadata) ?? middlewareResult;
|
|
203
|
+
if (finalResult === VALUE_NOT_CHANGED) return;
|
|
159
204
|
this._stateCache = await this.getRawState();
|
|
160
205
|
const keyStr = key.toString();
|
|
161
206
|
const changedPaths = [
|
|
@@ -210,17 +255,12 @@ import { GLOBAL_SUBSCRIPTION_KEY, StorageCore } from "./storage-core.js";
|
|
|
210
255
|
for (const path of changedPaths){
|
|
211
256
|
changedTopLevelKeys.add(path.split('.')[0]);
|
|
212
257
|
}
|
|
213
|
-
const updates =
|
|
214
|
-
const keyMetadata = {
|
|
215
|
-
...metadata,
|
|
216
|
-
key
|
|
217
|
-
};
|
|
218
|
-
const processedValue = await this.pluginExecutor?.executeBeforeSet(newState[key], keyMetadata) ?? newState[key];
|
|
258
|
+
const updates = Array.from(changedTopLevelKeys).map((key)=>{
|
|
219
259
|
return {
|
|
220
260
|
key,
|
|
221
|
-
value:
|
|
261
|
+
value: newState[key]
|
|
222
262
|
};
|
|
223
|
-
})
|
|
263
|
+
});
|
|
224
264
|
const result = await this.middlewareModule.dispatch({
|
|
225
265
|
type: 'update',
|
|
226
266
|
value: updates,
|
|
@@ -313,15 +353,12 @@ import { GLOBAL_SUBSCRIPTION_KEY, StorageCore } from "./storage-core.js";
|
|
|
313
353
|
timestamp: Date.now(),
|
|
314
354
|
key
|
|
315
355
|
};
|
|
316
|
-
const preventDeletion = await this.pluginExecutor?.executeBeforeDelete(key, metadata);
|
|
317
|
-
if (preventDeletion === false) return;
|
|
318
356
|
const middlewareResult = await this.middlewareModule.dispatch({
|
|
319
357
|
type: 'delete',
|
|
320
358
|
key,
|
|
321
359
|
metadata
|
|
322
360
|
});
|
|
323
361
|
if (middlewareResult === false) return;
|
|
324
|
-
await this.pluginExecutor?.executeAfterDelete(key, metadata);
|
|
325
362
|
this._stateCache = await this.getRawState();
|
|
326
363
|
const keyStr = key.toString();
|
|
327
364
|
const changedPaths = [
|
|
@@ -335,6 +372,8 @@ import { GLOBAL_SUBSCRIPTION_KEY, StorageCore } from "./storage-core.js";
|
|
|
335
372
|
result: middlewareResult,
|
|
336
373
|
changedPaths
|
|
337
374
|
});
|
|
375
|
+
// Ключ удалён — освобождаем слот версии, чтобы Map не рос на динамических ключах.
|
|
376
|
+
this.keyVersions.delete(keyStr);
|
|
338
377
|
await this.emitEvent({
|
|
339
378
|
type: StorageEvents.STORAGE_UPDATE,
|
|
340
379
|
payload: {
|
|
@@ -355,11 +394,11 @@ import { GLOBAL_SUBSCRIPTION_KEY, StorageCore } from "./storage-core.js";
|
|
|
355
394
|
async clear() {
|
|
356
395
|
this.ensureReady();
|
|
357
396
|
try {
|
|
358
|
-
await this.pluginExecutor?.executeOnClear();
|
|
359
397
|
await this.middlewareModule.dispatch({
|
|
360
398
|
type: 'clear'
|
|
361
399
|
});
|
|
362
400
|
this._stateCache = {};
|
|
401
|
+
this.keyVersions.clear();
|
|
363
402
|
} catch (error) {
|
|
364
403
|
this.logger?.error('Error clearing storage', {
|
|
365
404
|
error
|
|
@@ -378,6 +417,7 @@ import { GLOBAL_SUBSCRIPTION_KEY, StorageCore } from "./storage-core.js";
|
|
|
378
417
|
this._stateCache = initialState ? {
|
|
379
418
|
...initialState
|
|
380
419
|
} : {};
|
|
420
|
+
this.keyVersions.clear();
|
|
381
421
|
const changedPaths = Object.keys(this._stateCache);
|
|
382
422
|
this.notifySubscribers(GLOBAL_SUBSCRIPTION_KEY, {
|
|
383
423
|
type: StorageEvents.STORAGE_CLEAR,
|
|
@@ -396,6 +436,36 @@ import { GLOBAL_SUBSCRIPTION_KEY, StorageCore } from "./storage-core.js";
|
|
|
396
436
|
throw error;
|
|
397
437
|
}
|
|
398
438
|
}
|
|
439
|
+
/**
|
|
440
|
+
* SSR-гидрация: заменяет всё состояние переданным снапшотом. Намеренно НЕ требует
|
|
441
|
+
* `ready()` — типичный сценарий вызвать её до `initialize()`, чтобы инициализация
|
|
442
|
+
* не перезатёрла серверное состояние `initialState`-ом (см. `initializeWithMiddlewares`).
|
|
443
|
+
*/ async hydrate(state) {
|
|
444
|
+
try {
|
|
445
|
+
await this.doSet('', state);
|
|
446
|
+
this._stateCache = await this.getRawState();
|
|
447
|
+
// Если включён persist-migration — фиксируем текущую версию: серверный снапшот
|
|
448
|
+
// уже в актуальной схеме, миграцию на нём запускать не нужно.
|
|
449
|
+
if (this.config.version !== undefined) {
|
|
450
|
+
await this.writePersistedVersion(this.config.version);
|
|
451
|
+
}
|
|
452
|
+
const changedPaths = Object.keys(this._stateCache);
|
|
453
|
+
for (const key of changedPaths){
|
|
454
|
+
this.notifySubscribers(key, this._stateCache[key]);
|
|
455
|
+
}
|
|
456
|
+
this.notifySubscribers(GLOBAL_SUBSCRIPTION_KEY, {
|
|
457
|
+
type: StorageEvents.STORAGE_UPDATE,
|
|
458
|
+
key: changedPaths,
|
|
459
|
+
value: this._stateCache,
|
|
460
|
+
changedPaths
|
|
461
|
+
});
|
|
462
|
+
} catch (error) {
|
|
463
|
+
this.logger?.error('Error hydrating storage', {
|
|
464
|
+
error
|
|
465
|
+
});
|
|
466
|
+
throw error;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
399
469
|
async keys() {
|
|
400
470
|
this.ensureReady();
|
|
401
471
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core/storage/adapters/async-base-storage.service.js","sources":["../../../../src/core/storage/adapters/async-base-storage.service.ts"],"sourcesContent":["import { batchingMiddleware } from '../middlewares/storage-batching.middleware'\nimport { shallowCompareMiddleware } from '../middlewares/storage-shallow-compare.middleware'\nimport { IAsyncPluginExecutor } from '../modules/plugin/plugin.interface'\nimport { AsyncDefaultMiddlewares, AsyncStorageConfig, IAsyncStorage, IEventEmitter, ILogger, StorageEvents, StorageType } from '../storage.interface'\nimport { AsyncMiddlewareModule, Middleware, VALUE_NOT_CHANGED } from '../utils/middleware-module'\nimport { createDummyState, extractPath } from '../utils/path-selector.util'\nimport { createLazyClone, findChangedPaths, isEqual } from '../utils/state-diff.util'\nimport { StorageKeyType } from '../utils/storage-key'\nimport { getValueByPath } from './path.utils'\nimport { GLOBAL_SUBSCRIPTION_KEY, PathSelector, StorageCore } from './storage-core'\n\n/**\n * Базовый класс для асинхронных хранилищ (IndexedDB).\n *\n * Все CRUD-операции возвращают Promise.\n * Совместим с текущим API (переименован из BaseStorage).\n */\nexport abstract class AsyncBaseStorage<T extends Record<string, any>> extends StorageCore<T> implements IAsyncStorage<T> {\n abstract readonly type: StorageType\n\n private middlewareModule: AsyncMiddlewareModule\n private initializedMiddlewares: Middleware[] | null = null\n private selectorPathCache = new WeakMap<PathSelector<any, any>, string>()\n\n constructor(\n protected readonly config: AsyncStorageConfig<T>,\n protected readonly pluginExecutor?: IAsyncPluginExecutor,\n eventEmitter?: IEventEmitter,\n logger?: ILogger,\n ) {\n super(config, eventEmitter, logger)\n this.middlewareModule = new AsyncMiddlewareModule({\n getState: () => this.getRawState(),\n doGet: this.doGet.bind(this),\n doSet: this.doSet.bind(this),\n doUpdate: this.doUpdate.bind(this),\n doDelete: this.doDelete.bind(this),\n doClear: this.doClear.bind(this),\n doKeys: this.doKeys.bind(this),\n notifySubscribers: this.notifySubscribers.bind(this),\n })\n }\n\n // ─── Abstract async do* methods ─────────────────────────────────────────────\n\n protected abstract doGet(key: StorageKeyType): Promise<any>\n protected abstract doSet(key: StorageKeyType, value: any): Promise<void>\n protected abstract doUpdate(updates: Array<{ key: StorageKeyType; value: any }>): Promise<void>\n protected abstract doDelete(key: StorageKeyType): Promise<boolean>\n protected abstract doClear(): Promise<void>\n protected abstract doKeys(): Promise<string[]>\n protected abstract doHas(key: StorageKeyType): Promise<boolean>\n protected abstract doInitialize(): Promise<this>\n protected abstract doDestroy(): Promise<void>\n\n // ─── Lifecycle hooks ────────────────────────────────────────────────────────\n\n protected async performInitialize(): Promise<void> {\n await this.doInitialize()\n this._stateCache = await this.getRawState()\n }\n\n protected async performCleanup(): Promise<void> {\n // Обходим публичный clear() (избегая ensureReady после _isDestroyed = true)\n await this.pluginExecutor?.executeOnClear()\n await this.doClear()\n\n await this.doDestroy()\n\n if (this.initializedMiddlewares) {\n await Promise.all(\n this.initializedMiddlewares.map(async (middleware) => {\n if ('cleanup' in middleware) {\n await middleware.cleanup?.()\n }\n }),\n )\n this.initializedMiddlewares = null\n }\n }\n\n // ─── Middleware initialization ──────────────────────────────────────────────\n\n protected initializeMiddlewares(): void {\n if (this.config.middlewares && !this.initializedMiddlewares) {\n this.initializedMiddlewares = this.config.middlewares(() => this.getDefaultMiddleware())\n this.initializedMiddlewares.forEach((middleware) => this.middlewareModule.use(middleware))\n }\n }\n\n protected getDefaultMiddleware(): AsyncDefaultMiddlewares {\n return {\n batching: (options = {}) => batchingMiddleware(options),\n shallowCompare: (options = {}) => shallowCompareMiddleware(options),\n }\n }\n\n protected async initializeWithMiddlewares(): Promise<void> {\n try {\n const state = await this.getRawState()\n const hasExistingState = Object.keys(state).length > 0\n\n if (!hasExistingState && this.config.initialState) {\n await this.middlewareModule.dispatch({\n type: 'init',\n value: this.config.initialState,\n })\n }\n } catch (error) {\n this.logger?.error('Ошибка инициализации хранилища', { error })\n throw error\n }\n }\n\n // ─── Internal state access ──────────────────────────────────────────────────\n\n private async getRawState(): Promise<T> {\n try {\n const value = await this.doGet('')\n return value || {}\n } catch (error) {\n this.logger?.error('Error getting state', { error })\n throw error\n }\n }\n\n // ─── Public async API ───────────────────────────────────────────────────────\n\n public async get<R>(key: StorageKeyType): Promise<R | undefined> {\n this.ensureReady()\n\n try {\n const metadata = { operation: 'get', timestamp: Date.now(), key }\n\n const middlewareResult = await this.middlewareModule.dispatch({\n type: 'get',\n key,\n metadata,\n })\n\n const finalResult = (await this.pluginExecutor?.executeAfterGet(key, middlewareResult, metadata)) ?? middlewareResult\n\n await this.emitEvent({\n type: StorageEvents.STORAGE_SELECT,\n payload: { key, value: finalResult },\n })\n\n return finalResult\n } catch (error) {\n this.logger?.error('Error getting value', { key, error })\n throw error\n }\n }\n\n public async set<R>(key: StorageKeyType, value: R): Promise<void> {\n this.ensureReady()\n\n try {\n const metadata = { operation: 'set', timestamp: Date.now(), key }\n\n const processedValue = (await this.pluginExecutor?.executeBeforeSet(value, metadata)) ?? value\n\n const middlewareResult = await this.middlewareModule.dispatch({\n type: 'set',\n key,\n value: processedValue,\n metadata,\n })\n\n if (middlewareResult === VALUE_NOT_CHANGED) return\n\n const finalResult = (await this.pluginExecutor?.executeAfterSet(key, middlewareResult, metadata)) ?? middlewareResult\n\n this._stateCache = await this.getRawState()\n\n const keyStr = key.toString()\n const changedPaths = [keyStr]\n\n this.notifySubscribers(key, finalResult)\n\n this.notifySubscribers(GLOBAL_SUBSCRIPTION_KEY, {\n type: StorageEvents.STORAGE_UPDATE,\n key,\n value: finalResult,\n changedPaths,\n })\n\n await this.emitEvent({\n type: StorageEvents.STORAGE_UPDATE,\n payload: { key, value: finalResult, changedPaths },\n })\n } catch (error) {\n this.logger?.error('Error setting value', { key, error })\n throw error\n }\n }\n\n public async update(updater: (state: T) => void): Promise<void> {\n this.ensureReady()\n\n try {\n const metadata = { operation: 'update', timestamp: Date.now() }\n\n const currentState = (await this.getState()) as T\n const newState = createLazyClone(currentState)\n updater(newState)\n\n const changedPaths = findChangedPaths(currentState, newState)\n\n if (changedPaths.size === 0) {\n if (this.logger?.debug) {\n this.logger.debug('No changes detected in update')\n }\n return\n }\n\n if (this.logger?.debug) {\n this.logger.debug('Changed paths:', { paths: Array.from(changedPaths) })\n }\n\n const changedTopLevelKeys = new Set<string>()\n for (const path of changedPaths) {\n changedTopLevelKeys.add(path.split('.')[0])\n }\n\n const updates = await Promise.all(\n Array.from(changedTopLevelKeys).map(async (key: string) => {\n const keyMetadata = { ...metadata, key }\n const processedValue = (await this.pluginExecutor?.executeBeforeSet(newState[key], keyMetadata)) ?? newState[key]\n return { key, value: processedValue }\n }),\n )\n\n const result = await this.middlewareModule.dispatch({\n type: 'update',\n value: updates,\n metadata: {\n ...metadata,\n batchUpdate: true,\n changedPaths: Array.from(changedPaths),\n },\n })\n\n let updatedValues: Record<string, any> = {}\n if (Array.isArray(result)) {\n result.forEach((update: any) => {\n if (update && typeof update === 'object' && 'key' in update && 'value' in update) {\n updatedValues[update.key as string] = update.value\n }\n })\n } else if (result && typeof result === 'object') {\n updatedValues = { ...result }\n }\n\n const actuallyChangedKeys = Object.keys(updatedValues).filter((key) => !isEqual(currentState[key], updatedValues[key]))\n\n if (actuallyChangedKeys.length === 0) {\n if (this.logger?.debug) {\n this.logger.debug('No actual changes after middleware processing')\n }\n return\n }\n\n const finalUpdates: Record<string, any> = {}\n actuallyChangedKeys.forEach((key) => {\n finalUpdates[key] = updatedValues[key]\n })\n\n if (this.logger?.debug) {\n this.logger.debug('Notifying subscribers about changes:', { keys: actuallyChangedKeys })\n }\n\n this._stateCache = { ...currentState, ...finalUpdates } as T\n\n this.notifySubscribers(GLOBAL_SUBSCRIPTION_KEY, {\n type: StorageEvents.STORAGE_UPDATE,\n key: actuallyChangedKeys,\n value: finalUpdates,\n changedPaths: Array.from(changedPaths),\n })\n\n for (const path of changedPaths) {\n try {\n const topLevelKey = path.split('.')[0]\n if (topLevelKey in finalUpdates) {\n let value\n if (path === topLevelKey) {\n value = finalUpdates[topLevelKey]\n } else {\n const restPath = path.substring(topLevelKey.length + 1)\n value = getValueByPath(finalUpdates[topLevelKey], restPath)\n }\n if (value !== undefined) {\n this.notifySubscribers(path, value)\n }\n }\n } catch (error) {\n this.logger?.error('Error notifying path subscribers', { path, error })\n }\n }\n\n await this.emitEvent({\n type: StorageEvents.STORAGE_UPDATE,\n payload: {\n state: finalUpdates,\n key: actuallyChangedKeys,\n changedPaths: Array.from(changedPaths),\n },\n })\n } catch (error) {\n this.logger?.error('Error updating state', { error })\n throw error\n }\n }\n\n public async remove(key: StorageKeyType): Promise<void> {\n this.ensureReady()\n\n try {\n const metadata = { operation: 'delete', timestamp: Date.now(), key }\n\n const preventDeletion = await this.pluginExecutor?.executeBeforeDelete(key, metadata)\n if (preventDeletion === false) return\n\n const middlewareResult = await this.middlewareModule.dispatch({\n type: 'delete',\n key,\n metadata,\n })\n\n if (middlewareResult === false) return\n\n await this.pluginExecutor?.executeAfterDelete(key, metadata)\n\n this._stateCache = await this.getRawState()\n\n const keyStr = key.toString()\n const changedPaths = [keyStr]\n\n this.notifySubscribers(key, undefined)\n this.notifySubscribers(GLOBAL_SUBSCRIPTION_KEY, {\n type: StorageEvents.STORAGE_UPDATE,\n key,\n value: undefined,\n result: middlewareResult,\n changedPaths,\n })\n\n await this.emitEvent({\n type: StorageEvents.STORAGE_UPDATE,\n payload: { key, value: undefined, result: middlewareResult, changedPaths },\n })\n } catch (error) {\n this.logger?.error('Error deleting value', { key, error })\n throw error\n }\n }\n\n public async clear(): Promise<void> {\n this.ensureReady()\n\n try {\n await this.pluginExecutor?.executeOnClear()\n\n await this.middlewareModule.dispatch({ type: 'clear' })\n\n this._stateCache = {} as T\n } catch (error) {\n this.logger?.error('Error clearing storage', { error })\n throw error\n }\n }\n\n public async reset(): Promise<void> {\n this.ensureReady()\n\n try {\n const initialState = this.config.initialState\n\n await this.middlewareModule.dispatch({\n type: 'reset',\n value: initialState,\n })\n\n this._stateCache = initialState ? ({ ...initialState } as T) : ({} as T)\n\n const changedPaths = Object.keys(this._stateCache)\n\n this.notifySubscribers(GLOBAL_SUBSCRIPTION_KEY, {\n type: StorageEvents.STORAGE_CLEAR,\n changedPaths,\n })\n\n this.emitEvent({\n type: StorageEvents.STORAGE_CLEAR,\n payload: { changedPaths },\n })\n } catch (error) {\n this.logger?.error('Error resetting storage', { error })\n throw error\n }\n }\n\n public async keys(): Promise<string[]> {\n this.ensureReady()\n\n try {\n return await this.middlewareModule.dispatch({ type: 'keys' })\n } catch (error) {\n this.logger?.error('Error getting keys', { error })\n throw error\n }\n }\n\n public async has(key: StorageKeyType): Promise<boolean> {\n this.ensureReady()\n try {\n return await this.doHas(key)\n } catch (error) {\n this.logger?.error('Error checking value existence', { key, error })\n throw error\n }\n }\n\n public async getState(): Promise<T> {\n this.ensureReady()\n return this.getRawState()\n }\n\n // ─── Subscriptions (async — with race condition protection) ────────────────\n\n protected subscribeByKey(key: string, callback: (value: any) => void): VoidFunction {\n if (!this.subscribers.has(key)) {\n this.subscribers.set(key, new Set())\n }\n this.subscribers.get(key)!.add(callback)\n\n // Запоминаем версию ключа до асинхронного get().\n // Если между вызовом get() и его разрешением произойдёт set(),\n // версия увеличится и мы не отправим устаревшее начальное значение.\n const versionAtSubscribe = this.keyVersions.get(key) ?? 0\n\n this.get(key).then((value) => {\n try {\n const currentVersion = this.keyVersions.get(key) ?? 0\n if (currentVersion === versionAtSubscribe) {\n callback(value)\n }\n } catch (error) {\n this.logger?.error('Error in initial callback', { key, error })\n }\n })\n\n return () => {\n const subscribers = this.subscribers.get(key)\n if (subscribers) {\n subscribers.delete(callback)\n if (subscribers.size === 0) {\n this.subscribers.delete(key)\n }\n }\n }\n }\n\n protected subscribeBySelector<R>(pathSelector: PathSelector<T, R>, callback: (value: R) => void): VoidFunction {\n const dummyState = createDummyState<T>()\n const fullPath = extractPath(pathSelector, dummyState, this.selectorPathCache)\n\n if (this.logger?.debug) {\n this.logger.debug('Subscribing to path:', { path: fullPath })\n }\n\n const wrappedCallback = async (value: any) => {\n try {\n if (value === undefined || value === null) {\n const currentState = (await this.getState()) as T\n const selectedValue = pathSelector(currentState)\n callback(selectedValue as R)\n return\n }\n\n if (typeof value !== 'object' || value === null) {\n callback(value as R)\n return\n }\n\n const currentState = (await this.getState()) as T\n const selectedValue = pathSelector(currentState)\n callback(selectedValue as R)\n } catch (error) {\n this.logger?.error('Error in selector callback', { path: fullPath, error })\n callback(value as R)\n }\n }\n\n if (!fullPath) {\n return this.subscribeToAll(() => {\n this.getState().then((state) => {\n callback(pathSelector(state as T))\n })\n })\n }\n\n return this.subscribeByKey(fullPath, wrappedCallback)\n }\n}\n"],"names":["batchingMiddleware","shallowCompareMiddleware","StorageEvents","AsyncMiddlewareModule","VALUE_NOT_CHANGED","createDummyState","extractPath","createLazyClone","findChangedPaths","isEqual","getValueByPath","GLOBAL_SUBSCRIPTION_KEY","StorageCore","AsyncBaseStorage","WeakMap","config","pluginExecutor","eventEmitter","logger","Promise","middleware","options","state","hasExistingState","Object","error","value","key","metadata","Date","middlewareResult","finalResult","processedValue","keyStr","changedPaths","updater","currentState","newState","Array","changedTopLevelKeys","Set","path","updates","keyMetadata","result","updatedValues","update","actuallyChangedKeys","finalUpdates","topLevelKey","restPath","undefined","preventDeletion","initialState","callback","versionAtSubscribe","currentVersion","subscribers","pathSelector","dummyState","fullPath","wrappedCallback","selectedValue"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"core/storage/adapters/async-base-storage.service.js","sources":["../../../../src/core/storage/adapters/async-base-storage.service.ts"],"sourcesContent":["import { batchingMiddleware } from '../middlewares/storage-batching.middleware'\nimport { loggerMiddleware } from '../middlewares/storage-logger.middleware'\nimport { shallowCompareMiddleware } from '../middlewares/storage-shallow-compare.middleware'\nimport { AsyncDefaultMiddlewares, AsyncStorageConfig, IAsyncStorage, IEventEmitter, ILogger, StorageEvents, StorageType } from '../storage.interface'\nimport { AsyncMiddlewareModule, Middleware, VALUE_NOT_CHANGED } from '../utils/middleware-module'\nimport { decideMigration } from '../utils/migration.util'\nimport { createDummyState, extractPath } from '../utils/path-selector.util'\nimport { createLazyClone, findChangedPaths, isEqual } from '../utils/state-diff.util'\nimport { StorageKeyType } from '../utils/storage-key'\nimport { getValueByPath } from './path.utils'\nimport { GLOBAL_SUBSCRIPTION_KEY, PathSelector, StorageCore } from './storage-core'\n\n/**\n * Базовый класс для асинхронных хранилищ (IndexedDB).\n *\n * Все CRUD-операции возвращают Promise.\n * Совместим с текущим API (переименован из BaseStorage).\n */\nexport abstract class AsyncBaseStorage<T extends Record<string, any>> extends StorageCore<T> implements IAsyncStorage<T> {\n abstract readonly type: StorageType\n\n private middlewareModule: AsyncMiddlewareModule\n private initializedMiddlewares: Middleware[] | null = null\n private selectorPathCache = new WeakMap<PathSelector<any, any>, string>()\n\n /** Версии ключей для защиты от race condition в subscribeByKey (async get). */\n private keyVersions = new Map<string, number>()\n\n /** Инкремент версии ключа при каждом изменении — читается в subscribeByKey. */\n protected trackKeyVersion(keyStr: string): void {\n this.keyVersions.set(keyStr, (this.keyVersions.get(keyStr) ?? 0) + 1)\n }\n\n constructor(\n protected readonly config: AsyncStorageConfig<T>,\n eventEmitter?: IEventEmitter,\n logger?: ILogger,\n ) {\n super(config, eventEmitter, logger)\n this.middlewareModule = new AsyncMiddlewareModule({\n getState: () => this.getRawState(),\n doGet: this.doGet.bind(this),\n doSet: this.doSet.bind(this),\n doUpdate: this.doUpdate.bind(this),\n doDelete: this.doDelete.bind(this),\n doClear: this.doClear.bind(this),\n doKeys: this.doKeys.bind(this),\n notifySubscribers: this.notifySubscribers.bind(this),\n })\n }\n\n // ─── Abstract async do* methods ─────────────────────────────────────────────\n\n protected abstract doGet(key: StorageKeyType): Promise<any>\n protected abstract doSet(key: StorageKeyType, value: any): Promise<void>\n protected abstract doUpdate(updates: Array<{ key: StorageKeyType; value: any }>): Promise<void>\n protected abstract doDelete(key: StorageKeyType): Promise<boolean>\n protected abstract doClear(): Promise<void>\n protected abstract doKeys(): Promise<string[]>\n protected abstract doHas(key: StorageKeyType): Promise<boolean>\n protected abstract doInitialize(): Promise<this>\n protected abstract doDestroy(): Promise<void>\n\n // ─── Lifecycle hooks ────────────────────────────────────────────────────────\n\n protected async performInitialize(): Promise<void> {\n await this.doInitialize()\n this._stateCache = await this.getRawState()\n }\n\n protected async performCleanup(): Promise<void> {\n // Обходим публичный clear() (избегая ensureReady после _isDestroyed = true)\n await this.doClear()\n\n await this.doDestroy()\n\n if (this.initializedMiddlewares) {\n await Promise.all(\n this.initializedMiddlewares.map(async (middleware) => {\n if ('cleanup' in middleware) {\n await middleware.cleanup?.()\n }\n }),\n )\n this.initializedMiddlewares = null\n }\n }\n\n // ─── Middleware initialization ──────────────────────────────────────────────\n\n protected initializeMiddlewares(): void {\n if (this.config.middlewares && !this.initializedMiddlewares) {\n this.initializedMiddlewares = this.config.middlewares(() => this.getDefaultMiddleware())\n this.initializedMiddlewares.forEach((middleware) => this.middlewareModule.use(middleware))\n }\n }\n\n protected getDefaultMiddleware(): AsyncDefaultMiddlewares {\n return {\n batching: (options = {}) => batchingMiddleware(options),\n shallowCompare: (options = {}) => shallowCompareMiddleware(options),\n logger: (options = {}) => loggerMiddleware(options),\n }\n }\n\n protected async initializeWithMiddlewares(): Promise<void> {\n try {\n const state = await this.getRawState()\n const hasExistingState = Object.keys(state).length > 0\n\n // Миграция выключена (version не задан) — прежнее поведение: засеять initialState на пустом.\n if (this.config.version === undefined) {\n if (!hasExistingState && this.config.initialState) {\n await this.middlewareModule.dispatch({ type: 'init', value: this.config.initialState })\n }\n return\n }\n\n const decision = decideMigration({\n hasExisting: hasExistingState,\n existingState: state,\n persistedVersion: await this.readPersistedVersion(),\n targetVersion: this.config.version,\n migrate: this.config.migrate,\n })\n\n switch (decision.kind) {\n case 'seed': {\n if (this.config.initialState) {\n await this.middlewareModule.dispatch({ type: 'init', value: this.config.initialState })\n }\n await this.writePersistedVersion(this.config.version)\n break\n }\n case 'migrate': {\n await this.middlewareModule.dispatch({ type: 'reset', value: decision.state })\n await this.writePersistedVersion(this.config.version)\n break\n }\n case 'bump': {\n await this.writePersistedVersion(this.config.version)\n break\n }\n case 'none':\n break\n }\n } catch (error) {\n this.logger?.error('Ошибка инициализации хранилища', { error })\n throw error\n }\n }\n\n // ─── Persisted schema version (persist-migration) ───────────────────────────\n\n /** Читает сохранённую версию схемы. По умолчанию `undefined` (нет персистентности). */\n protected async readPersistedVersion(): Promise<number | undefined> {\n return undefined\n }\n\n /** Сохраняет версию схемы рядом с данными. По умолчанию no-op. */\n protected async writePersistedVersion(_version: number): Promise<void> {}\n\n // ─── Internal state access ──────────────────────────────────────────────────\n\n private async getRawState(): Promise<T> {\n try {\n const value = await this.doGet('')\n return value || {}\n } catch (error) {\n this.logger?.error('Error getting state', { error })\n throw error\n }\n }\n\n // ─── Public async API ───────────────────────────────────────────────────────\n\n public async get<R>(key: StorageKeyType): Promise<R | undefined> {\n this.ensureReady()\n\n try {\n const metadata = { operation: 'get', timestamp: Date.now(), key }\n\n const finalResult = await this.middlewareModule.dispatch({\n type: 'get',\n key,\n metadata,\n })\n\n // Чтения не эмитят событий — это горячий путь, а STORAGE_SELECT нигде не потребляется.\n return finalResult\n } catch (error) {\n this.logger?.error('Error getting value', { key, error })\n throw error\n }\n }\n\n public async set<R>(key: StorageKeyType, value: R): Promise<void> {\n this.ensureReady()\n\n try {\n const metadata = { operation: 'set', timestamp: Date.now(), key }\n\n const finalResult = await this.middlewareModule.dispatch({\n type: 'set',\n key,\n value,\n metadata,\n })\n\n if (finalResult === VALUE_NOT_CHANGED) return\n\n this._stateCache = await this.getRawState()\n\n const keyStr = key.toString()\n const changedPaths = [keyStr]\n\n this.notifySubscribers(key, finalResult)\n\n this.notifySubscribers(GLOBAL_SUBSCRIPTION_KEY, {\n type: StorageEvents.STORAGE_UPDATE,\n key,\n value: finalResult,\n changedPaths,\n })\n\n await this.emitEvent({\n type: StorageEvents.STORAGE_UPDATE,\n payload: { key, value: finalResult, changedPaths },\n })\n } catch (error) {\n this.logger?.error('Error setting value', { key, error })\n throw error\n }\n }\n\n public async update(updater: (state: T) => void): Promise<void> {\n this.ensureReady()\n\n try {\n const metadata = { operation: 'update', timestamp: Date.now() }\n\n const currentState = (await this.getState()) as T\n const newState = createLazyClone(currentState)\n updater(newState)\n\n const changedPaths = findChangedPaths(currentState, newState)\n\n if (changedPaths.size === 0) {\n if (this.logger?.debug) {\n this.logger.debug('No changes detected in update')\n }\n return\n }\n\n if (this.logger?.debug) {\n this.logger.debug('Changed paths:', { paths: Array.from(changedPaths) })\n }\n\n const changedTopLevelKeys = new Set<string>()\n for (const path of changedPaths) {\n changedTopLevelKeys.add(path.split('.')[0])\n }\n\n const updates = Array.from(changedTopLevelKeys).map((key: string) => {\n return { key, value: newState[key] }\n })\n\n const result = await this.middlewareModule.dispatch({\n type: 'update',\n value: updates,\n metadata: {\n ...metadata,\n batchUpdate: true,\n changedPaths: Array.from(changedPaths),\n },\n })\n\n let updatedValues: Record<string, any> = {}\n if (Array.isArray(result)) {\n result.forEach((update: any) => {\n if (update && typeof update === 'object' && 'key' in update && 'value' in update) {\n updatedValues[update.key as string] = update.value\n }\n })\n } else if (result && typeof result === 'object') {\n updatedValues = { ...result }\n }\n\n const actuallyChangedKeys = Object.keys(updatedValues).filter((key) => !isEqual(currentState[key], updatedValues[key]))\n\n if (actuallyChangedKeys.length === 0) {\n if (this.logger?.debug) {\n this.logger.debug('No actual changes after middleware processing')\n }\n return\n }\n\n const finalUpdates: Record<string, any> = {}\n actuallyChangedKeys.forEach((key) => {\n finalUpdates[key] = updatedValues[key]\n })\n\n if (this.logger?.debug) {\n this.logger.debug('Notifying subscribers about changes:', { keys: actuallyChangedKeys })\n }\n\n this._stateCache = { ...currentState, ...finalUpdates } as T\n\n this.notifySubscribers(GLOBAL_SUBSCRIPTION_KEY, {\n type: StorageEvents.STORAGE_UPDATE,\n key: actuallyChangedKeys,\n value: finalUpdates,\n changedPaths: Array.from(changedPaths),\n })\n\n for (const path of changedPaths) {\n try {\n const topLevelKey = path.split('.')[0]\n if (topLevelKey in finalUpdates) {\n let value\n if (path === topLevelKey) {\n value = finalUpdates[topLevelKey]\n } else {\n const restPath = path.substring(topLevelKey.length + 1)\n value = getValueByPath(finalUpdates[topLevelKey], restPath)\n }\n if (value !== undefined) {\n this.notifySubscribers(path, value)\n }\n }\n } catch (error) {\n this.logger?.error('Error notifying path subscribers', { path, error })\n }\n }\n\n await this.emitEvent({\n type: StorageEvents.STORAGE_UPDATE,\n payload: {\n state: finalUpdates,\n key: actuallyChangedKeys,\n changedPaths: Array.from(changedPaths),\n },\n })\n } catch (error) {\n this.logger?.error('Error updating state', { error })\n throw error\n }\n }\n\n public async remove(key: StorageKeyType): Promise<void> {\n this.ensureReady()\n\n try {\n const metadata = { operation: 'delete', timestamp: Date.now(), key }\n\n const middlewareResult = await this.middlewareModule.dispatch({\n type: 'delete',\n key,\n metadata,\n })\n\n if (middlewareResult === false) return\n\n this._stateCache = await this.getRawState()\n\n const keyStr = key.toString()\n const changedPaths = [keyStr]\n\n this.notifySubscribers(key, undefined)\n this.notifySubscribers(GLOBAL_SUBSCRIPTION_KEY, {\n type: StorageEvents.STORAGE_UPDATE,\n key,\n value: undefined,\n result: middlewareResult,\n changedPaths,\n })\n\n // Ключ удалён — освобождаем слот версии, чтобы Map не рос на динамических ключах.\n this.keyVersions.delete(keyStr)\n\n await this.emitEvent({\n type: StorageEvents.STORAGE_UPDATE,\n payload: { key, value: undefined, result: middlewareResult, changedPaths },\n })\n } catch (error) {\n this.logger?.error('Error deleting value', { key, error })\n throw error\n }\n }\n\n public async clear(): Promise<void> {\n this.ensureReady()\n\n try {\n await this.middlewareModule.dispatch({ type: 'clear' })\n\n this._stateCache = {} as T\n this.keyVersions.clear()\n } catch (error) {\n this.logger?.error('Error clearing storage', { error })\n throw error\n }\n }\n\n public async reset(): Promise<void> {\n this.ensureReady()\n\n try {\n const initialState = this.config.initialState\n\n await this.middlewareModule.dispatch({\n type: 'reset',\n value: initialState,\n })\n\n this._stateCache = initialState ? ({ ...initialState } as T) : ({} as T)\n this.keyVersions.clear()\n\n const changedPaths = Object.keys(this._stateCache)\n\n this.notifySubscribers(GLOBAL_SUBSCRIPTION_KEY, {\n type: StorageEvents.STORAGE_CLEAR,\n changedPaths,\n })\n\n this.emitEvent({\n type: StorageEvents.STORAGE_CLEAR,\n payload: { changedPaths },\n })\n } catch (error) {\n this.logger?.error('Error resetting storage', { error })\n throw error\n }\n }\n\n /**\n * SSR-гидрация: заменяет всё состояние переданным снапшотом. Намеренно НЕ требует\n * `ready()` — типичный сценарий вызвать её до `initialize()`, чтобы инициализация\n * не перезатёрла серверное состояние `initialState`-ом (см. `initializeWithMiddlewares`).\n */\n public async hydrate(state: T): Promise<void> {\n try {\n await this.doSet('', state)\n this._stateCache = await this.getRawState()\n\n // Если включён persist-migration — фиксируем текущую версию: серверный снапшот\n // уже в актуальной схеме, миграцию на нём запускать не нужно.\n if (this.config.version !== undefined) {\n await this.writePersistedVersion(this.config.version)\n }\n\n const changedPaths = Object.keys(this._stateCache)\n for (const key of changedPaths) {\n this.notifySubscribers(key, (this._stateCache as Record<string, any>)[key])\n }\n this.notifySubscribers(GLOBAL_SUBSCRIPTION_KEY, {\n type: StorageEvents.STORAGE_UPDATE,\n key: changedPaths,\n value: this._stateCache,\n changedPaths,\n })\n } catch (error) {\n this.logger?.error('Error hydrating storage', { error })\n throw error\n }\n }\n\n public async keys(): Promise<string[]> {\n this.ensureReady()\n\n try {\n return await this.middlewareModule.dispatch({ type: 'keys' })\n } catch (error) {\n this.logger?.error('Error getting keys', { error })\n throw error\n }\n }\n\n public async has(key: StorageKeyType): Promise<boolean> {\n this.ensureReady()\n try {\n return await this.doHas(key)\n } catch (error) {\n this.logger?.error('Error checking value existence', { key, error })\n throw error\n }\n }\n\n public async getState(): Promise<T> {\n this.ensureReady()\n return this.getRawState()\n }\n\n // ─── Subscriptions (async — with race condition protection) ────────────────\n\n protected subscribeByKey(key: string, callback: (value: any) => void): VoidFunction {\n if (!this.subscribers.has(key)) {\n this.subscribers.set(key, new Set())\n }\n this.subscribers.get(key)!.add(callback)\n\n // Запоминаем версию ключа до асинхронного get().\n // Если между вызовом get() и его разрешением произойдёт set(),\n // версия увеличится и мы не отправим устаревшее начальное значение.\n const versionAtSubscribe = this.keyVersions.get(key) ?? 0\n\n this.get(key).then((value) => {\n try {\n const currentVersion = this.keyVersions.get(key) ?? 0\n if (currentVersion === versionAtSubscribe) {\n callback(value)\n }\n } catch (error) {\n this.logger?.error('Error in initial callback', { key, error })\n }\n })\n\n return () => {\n const subscribers = this.subscribers.get(key)\n if (subscribers) {\n subscribers.delete(callback)\n if (subscribers.size === 0) {\n this.subscribers.delete(key)\n }\n }\n }\n }\n\n protected subscribeBySelector<R>(pathSelector: PathSelector<T, R>, callback: (value: R) => void): VoidFunction {\n const dummyState = createDummyState<T>()\n const fullPath = extractPath(pathSelector, dummyState, this.selectorPathCache)\n\n if (this.logger?.debug) {\n this.logger.debug('Subscribing to path:', { path: fullPath })\n }\n\n const wrappedCallback = async (value: any) => {\n try {\n if (value === undefined || value === null) {\n const currentState = (await this.getState()) as T\n const selectedValue = pathSelector(currentState)\n callback(selectedValue as R)\n return\n }\n\n if (typeof value !== 'object' || value === null) {\n callback(value as R)\n return\n }\n\n const currentState = (await this.getState()) as T\n const selectedValue = pathSelector(currentState)\n callback(selectedValue as R)\n } catch (error) {\n this.logger?.error('Error in selector callback', { path: fullPath, error })\n callback(value as R)\n }\n }\n\n if (!fullPath) {\n return this.subscribeToAll(() => {\n this.getState().then((state) => {\n callback(pathSelector(state as T))\n })\n })\n }\n\n return this.subscribeByKey(fullPath, wrappedCallback)\n }\n}\n"],"names":["batchingMiddleware","loggerMiddleware","shallowCompareMiddleware","StorageEvents","AsyncMiddlewareModule","VALUE_NOT_CHANGED","decideMigration","createDummyState","extractPath","createLazyClone","findChangedPaths","isEqual","getValueByPath","GLOBAL_SUBSCRIPTION_KEY","StorageCore","AsyncBaseStorage","WeakMap","Map","keyStr","config","eventEmitter","logger","Promise","middleware","options","state","hasExistingState","Object","undefined","decision","error","_version","value","key","metadata","Date","finalResult","changedPaths","updater","currentState","newState","Array","changedTopLevelKeys","Set","path","updates","result","updatedValues","update","actuallyChangedKeys","finalUpdates","topLevelKey","restPath","middlewareResult","initialState","callback","versionAtSubscribe","currentVersion","subscribers","pathSelector","dummyState","fullPath","wrappedCallback","selectedValue"],"mappings":"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"}
|