synapse-storage 4.1.1 → 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 +9 -2
- package/dist/core/storage/utils/state-diff.util.js +30 -3
- 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,17 @@
|
|
|
1
|
+
import { DependencyList } from 'react';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
/**
|
|
4
|
+
* Подписка на Observable из компонента.
|
|
5
|
+
*
|
|
6
|
+
* `source` — либо готовый `Observable<T>` (например `selector.$`), либо фабрика
|
|
7
|
+
* `() => Observable<T>`, собирающая цепочку (`pipe(debounceTime(...))`) при подписке.
|
|
8
|
+
*
|
|
9
|
+
* До первого эмита возвращается `initialValue`. Подписка снимается на unmount и
|
|
10
|
+
* пересоздаётся при смене `deps` (вся цепочка строится заново — актуально для
|
|
11
|
+
* операторов с состоянием вроде `debounceTime`/`scan`). Если `deps` не переданы:
|
|
12
|
+
* для прямого Observable цепочка пересоздаётся при смене ссылки `source`, для
|
|
13
|
+
* фабрики — создаётся один раз.
|
|
14
|
+
*
|
|
15
|
+
* @template T тип значения потока
|
|
16
|
+
*/
|
|
17
|
+
export declare function useObservable<T>(source: Observable<T> | (() => Observable<T>), initialValue: T, deps?: DependencyList): T;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Подписка на Observable из компонента.
|
|
7
|
+
*
|
|
8
|
+
* `source` — либо готовый `Observable<T>` (например `selector.$`), либо фабрика
|
|
9
|
+
* `() => Observable<T>`, собирающая цепочку (`pipe(debounceTime(...))`) при подписке.
|
|
10
|
+
*
|
|
11
|
+
* До первого эмита возвращается `initialValue`. Подписка снимается на unmount и
|
|
12
|
+
* пересоздаётся при смене `deps` (вся цепочка строится заново — актуально для
|
|
13
|
+
* операторов с состоянием вроде `debounceTime`/`scan`). Если `deps` не переданы:
|
|
14
|
+
* для прямого Observable цепочка пересоздаётся при смене ссылки `source`, для
|
|
15
|
+
* фабрики — создаётся один раз.
|
|
16
|
+
*
|
|
17
|
+
* @template T тип значения потока
|
|
18
|
+
*/ function useObservable(source, initialValue, deps) {
|
|
19
|
+
const [value, setValue] = useState(initialValue);
|
|
20
|
+
// Держим source в ref, чтобы замыкание эффекта всегда читало актуальную фабрику,
|
|
21
|
+
// но переподписка управлялась исключительно через deps.
|
|
22
|
+
const sourceRef = useRef(source);
|
|
23
|
+
sourceRef.current = source;
|
|
24
|
+
const effectDeps = deps ?? (typeof source === 'function' ? [] : [
|
|
25
|
+
source
|
|
26
|
+
]);
|
|
27
|
+
useEffect(()=>{
|
|
28
|
+
const current = sourceRef.current;
|
|
29
|
+
const observable = typeof current === 'function' ? current() : current;
|
|
30
|
+
const subscription = observable.subscribe((next)=>setValue(next));
|
|
31
|
+
return ()=>subscription.unsubscribe();
|
|
32
|
+
}, effectDeps);
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { useObservable };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=useObservable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react/hooks/useObservable.js","sources":["../../../src/react/hooks/useObservable.ts"],"sourcesContent":["import { DependencyList, useEffect, useRef, useState } from 'react'\nimport { Observable } from 'rxjs'\n\n/**\n * Подписка на Observable из компонента.\n *\n * `source` — либо готовый `Observable<T>` (например `selector.$`), либо фабрика\n * `() => Observable<T>`, собирающая цепочку (`pipe(debounceTime(...))`) при подписке.\n *\n * До первого эмита возвращается `initialValue`. Подписка снимается на unmount и\n * пересоздаётся при смене `deps` (вся цепочка строится заново — актуально для\n * операторов с состоянием вроде `debounceTime`/`scan`). Если `deps` не переданы:\n * для прямого Observable цепочка пересоздаётся при смене ссылки `source`, для\n * фабрики — создаётся один раз.\n *\n * @template T тип значения потока\n */\nexport function useObservable<T>(source: Observable<T> | (() => Observable<T>), initialValue: T, deps?: DependencyList): T {\n const [value, setValue] = useState<T>(initialValue)\n\n // Держим source в ref, чтобы замыкание эффекта всегда читало актуальную фабрику,\n // но переподписка управлялась исключительно через deps.\n const sourceRef = useRef(source)\n sourceRef.current = source\n\n const effectDeps = deps ?? (typeof source === 'function' ? [] : [source])\n\n useEffect(() => {\n const current = sourceRef.current\n const observable = typeof current === 'function' ? (current as () => Observable<T>)() : current\n const subscription = observable.subscribe((next) => setValue(next))\n return () => subscription.unsubscribe()\n }, effectDeps)\n\n return value\n}\n"],"names":["useEffect","useRef","useState","useObservable","source","initialValue","deps","value","setValue","sourceRef","effectDeps","current","observable","subscription","next"],"mappings":";;;AAAmE;AAGnE;;;;;;;;;;;;;CAaC,GACM,SAASG,aAAaA,CAAIC,MAA6C,EAAEC,YAAe,EAAEC,IAAqB;IACpH,MAAM,CAACC,OAAOC,SAAS,GAAGN,QAAQA,CAAIG;IAEtC,iFAAiF;IACjF,wDAAwD;IACxD,MAAMI,YAAYR,MAAMA,CAACG;IACzBK,UAAU,OAAO,GAAGL;IAEpB,MAAMM,aAAaJ,QAAS,QAAOF,WAAW,aAAa,EAAE,GAAG;QAACA;KAAM;IAEvEJ,SAASA,CAAC;QACR,MAAMW,UAAUF,UAAU,OAAO;QACjC,MAAMG,aAAa,OAAOD,YAAY,aAAcA,YAAoCA;QACxF,MAAME,eAAeD,WAAW,SAAS,CAAC,CAACE,OAASN,SAASM;QAC7D,OAAO,IAAMD,aAAa,WAAW;IACvC,GAAGH;IAEH,OAAOH;AACT"}
|
|
@@ -27,7 +27,10 @@ function useSelector(selector, options) {
|
|
|
27
27
|
}, [
|
|
28
28
|
selector
|
|
29
29
|
]);
|
|
30
|
-
|
|
30
|
+
// getServerSnapshot === getSnapshot: на сервере `selectSync()` синхронно читает
|
|
31
|
+
// засеянный (hydrate) стор. Без серверного снапшота useSyncExternalStore на сервере
|
|
32
|
+
// падает и откатывается на client-only рендер (контент не попадает в SSR-HTML).
|
|
33
|
+
const value = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
31
34
|
// Подписка на статус готовности storage (используется только при withLoading)
|
|
32
35
|
const subscribeToStatus = useCallback((onStoreChange)=>{
|
|
33
36
|
return selector.onSourceStatusChange(()=>{
|
|
@@ -41,7 +44,7 @@ function useSelector(selector, options) {
|
|
|
41
44
|
}, [
|
|
42
45
|
selector
|
|
43
46
|
]);
|
|
44
|
-
const isLoading = useSyncExternalStore(subscribeToStatus, getStatusSnapshot);
|
|
47
|
+
const isLoading = useSyncExternalStore(subscribeToStatus, getStatusSnapshot, getStatusSnapshot);
|
|
45
48
|
if (options?.withLoading) {
|
|
46
49
|
return {
|
|
47
50
|
data: value,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react/hooks/useSelector.js","sources":["../../../src/react/hooks/useSelector.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from 'react'\n\nimport type { SelectorAPI } from '../../core'\n\ninterface UseSelectorOptions<T> {\n /** Функция сравнения для предотвращения лишних ререндеров */\n equals?: (a: T, b: T) => boolean\n /** Включать ли статус загрузки в возвращаемый результат */\n withLoading?: boolean\n}\n\n/**\n * Хук для использования селекторов в компонентах React.\n * Использует useSyncExternalStore для корректной работы в Concurrent Mode.\n * Подписывается напрямую через selector.subscribe() — без глобального реестра.\n */\nexport function useSelector<T>(selector: SelectorAPI<T>): T\nexport function useSelector<T>(selector: SelectorAPI<T>, options: UseSelectorOptions<T> & { withLoading: true }): { data: T; isLoading: boolean }\nexport function useSelector<T>(selector: SelectorAPI<T>, options: UseSelectorOptions<T> & { withLoading?: false }): T\nexport function useSelector<T>(selector: SelectorAPI<T>, options?: UseSelectorOptions<T>): { data: T; isLoading: boolean } | T {\n const equalsRef = useRef(options?.equals)\n\n // Кеш для мемоизации результата getSnapshot (предотвращает лишние ререндеры)\n const cachedRef = useRef<T | undefined>(undefined)\n\n const subscribe = useCallback(\n (onStoreChange: VoidFunction) => {\n return selector.subscribe({\n notify: () => {\n onStoreChange()\n },\n })\n },\n [selector],\n )\n\n const getSnapshot = useCallback((): T => {\n const value = selector.selectSync()\n\n // Если есть пользовательская функция сравнения — мемоизируем\n const equals = equalsRef.current\n if (equals && cachedRef.current !== undefined && equals(cachedRef.current, value)) {\n return cachedRef.current\n }\n\n cachedRef.current = value\n return value\n }, [selector])\n\n const value = useSyncExternalStore(subscribe, getSnapshot)\n\n // Подписка на статус готовности storage (используется только при withLoading)\n const subscribeToStatus = useCallback(\n (onStoreChange: VoidFunction) => {\n return selector.onSourceStatusChange(() => {\n onStoreChange()\n })\n },\n [selector],\n )\n\n const getStatusSnapshot = useCallback((): boolean => {\n return !selector.isSourceReady()\n }, [selector])\n\n const isLoading = useSyncExternalStore(subscribeToStatus, getStatusSnapshot)\n\n if (options?.withLoading) {\n return { data: value, isLoading }\n }\n\n return value\n}\n\n/**\n * Изоляция ре-рендеров для keyed-map стора (паттерн «equals»). Подписка идёт на\n * ВЕСЬ map (`selector`), но `equals` сравнивает только срез по `key` → компонент\n * ре-рендерится лишь когда меняется его `map[key]` по ссылке. Работает, т.к.\n * иммутабельные мутации по одному ключу не трогают ссылку чужих срезов.\n * Гранулярность живёт в месте вызова, а селекторы остаются плоскими (без фабрик\n * с `Map<key, selector>`).\n *\n * `fallback` ОБЯЗАН быть стабильной ссылкой (module-level константа), иначе\n * `?? fallback` будет давать новый объект каждый тик и рвать нижестоящие `useMemo`.\n */\nexport function useKeyedSliceSelector<V>(selector: SelectorAPI<Record<string, V>>, key: string, fallback: V): V {\n const map = useSelector(selector, {\n equals: (a, b) => a[key] === b[key],\n })\n return map[key] ?? fallback\n}\n"],"names":["useCallback","useRef","useSyncExternalStore","useSelector","selector","options","equalsRef","cachedRef","undefined","subscribe","onStoreChange","getSnapshot","value","equals","subscribeToStatus","getStatusSnapshot","isLoading","useKeyedSliceSelector","key","fallback","map","a","b"],"mappings":";;;AAAiE;AAmB1D,SAASG,WAAWA,CAAIC,QAAwB,EAAEC,OAA+B;IACtF,MAAMC,YAAYL,MAAMA,CAACI,SAAS;IAElC,6EAA6E;IAC7E,MAAME,YAAYN,MAAMA,CAAgBO;IAExC,MAAMC,YAAYT,WAAWA,CAC3B,CAACU;QACC,OAAON,SAAS,SAAS,CAAC;YACxB,QAAQ;gBACNM;YACF;QACF;IACF,GACA;QAACN;KAAS;IAGZ,MAAMO,cAAcX,WAAWA,CAAC;QAC9B,MAAMY,QAAQR,SAAS,UAAU;QAEjC,6DAA6D;QAC7D,MAAMS,SAASP,UAAU,OAAO;QAChC,IAAIO,UAAUN,UAAU,OAAO,KAAKC,aAAaK,OAAON,UAAU,OAAO,EAAEK,QAAQ;YACjF,OAAOL,UAAU,OAAO;QAC1B;QAEAA,UAAU,OAAO,GAAGK;QACpB,OAAOA;IACT,GAAG;QAACR;KAAS;IAEb,MAAMQ,QAAQV,oBAAoBA,CAACO,WAAWE;
|
|
1
|
+
{"version":3,"file":"react/hooks/useSelector.js","sources":["../../../src/react/hooks/useSelector.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from 'react'\n\nimport type { SelectorAPI } from '../../core'\n\ninterface UseSelectorOptions<T> {\n /** Функция сравнения для предотвращения лишних ререндеров */\n equals?: (a: T, b: T) => boolean\n /** Включать ли статус загрузки в возвращаемый результат */\n withLoading?: boolean\n}\n\n/**\n * Хук для использования селекторов в компонентах React.\n * Использует useSyncExternalStore для корректной работы в Concurrent Mode.\n * Подписывается напрямую через selector.subscribe() — без глобального реестра.\n */\nexport function useSelector<T>(selector: SelectorAPI<T>): T\nexport function useSelector<T>(selector: SelectorAPI<T>, options: UseSelectorOptions<T> & { withLoading: true }): { data: T; isLoading: boolean }\nexport function useSelector<T>(selector: SelectorAPI<T>, options: UseSelectorOptions<T> & { withLoading?: false }): T\nexport function useSelector<T>(selector: SelectorAPI<T>, options?: UseSelectorOptions<T>): { data: T; isLoading: boolean } | T {\n const equalsRef = useRef(options?.equals)\n\n // Кеш для мемоизации результата getSnapshot (предотвращает лишние ререндеры)\n const cachedRef = useRef<T | undefined>(undefined)\n\n const subscribe = useCallback(\n (onStoreChange: VoidFunction) => {\n return selector.subscribe({\n notify: () => {\n onStoreChange()\n },\n })\n },\n [selector],\n )\n\n const getSnapshot = useCallback((): T => {\n const value = selector.selectSync()\n\n // Если есть пользовательская функция сравнения — мемоизируем\n const equals = equalsRef.current\n if (equals && cachedRef.current !== undefined && equals(cachedRef.current, value)) {\n return cachedRef.current\n }\n\n cachedRef.current = value\n return value\n }, [selector])\n\n // getServerSnapshot === getSnapshot: на сервере `selectSync()` синхронно читает\n // засеянный (hydrate) стор. Без серверного снапшота useSyncExternalStore на сервере\n // падает и откатывается на client-only рендер (контент не попадает в SSR-HTML).\n const value = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n\n // Подписка на статус готовности storage (используется только при withLoading)\n const subscribeToStatus = useCallback(\n (onStoreChange: VoidFunction) => {\n return selector.onSourceStatusChange(() => {\n onStoreChange()\n })\n },\n [selector],\n )\n\n const getStatusSnapshot = useCallback((): boolean => {\n return !selector.isSourceReady()\n }, [selector])\n\n const isLoading = useSyncExternalStore(subscribeToStatus, getStatusSnapshot, getStatusSnapshot)\n\n if (options?.withLoading) {\n return { data: value, isLoading }\n }\n\n return value\n}\n\n/**\n * Изоляция ре-рендеров для keyed-map стора (паттерн «equals»). Подписка идёт на\n * ВЕСЬ map (`selector`), но `equals` сравнивает только срез по `key` → компонент\n * ре-рендерится лишь когда меняется его `map[key]` по ссылке. Работает, т.к.\n * иммутабельные мутации по одному ключу не трогают ссылку чужих срезов.\n * Гранулярность живёт в месте вызова, а селекторы остаются плоскими (без фабрик\n * с `Map<key, selector>`).\n *\n * `fallback` ОБЯЗАН быть стабильной ссылкой (module-level константа), иначе\n * `?? fallback` будет давать новый объект каждый тик и рвать нижестоящие `useMemo`.\n */\nexport function useKeyedSliceSelector<V>(selector: SelectorAPI<Record<string, V>>, key: string, fallback: V): V {\n const map = useSelector(selector, {\n equals: (a, b) => a[key] === b[key],\n })\n return map[key] ?? fallback\n}\n"],"names":["useCallback","useRef","useSyncExternalStore","useSelector","selector","options","equalsRef","cachedRef","undefined","subscribe","onStoreChange","getSnapshot","value","equals","subscribeToStatus","getStatusSnapshot","isLoading","useKeyedSliceSelector","key","fallback","map","a","b"],"mappings":";;;AAAiE;AAmB1D,SAASG,WAAWA,CAAIC,QAAwB,EAAEC,OAA+B;IACtF,MAAMC,YAAYL,MAAMA,CAACI,SAAS;IAElC,6EAA6E;IAC7E,MAAME,YAAYN,MAAMA,CAAgBO;IAExC,MAAMC,YAAYT,WAAWA,CAC3B,CAACU;QACC,OAAON,SAAS,SAAS,CAAC;YACxB,QAAQ;gBACNM;YACF;QACF;IACF,GACA;QAACN;KAAS;IAGZ,MAAMO,cAAcX,WAAWA,CAAC;QAC9B,MAAMY,QAAQR,SAAS,UAAU;QAEjC,6DAA6D;QAC7D,MAAMS,SAASP,UAAU,OAAO;QAChC,IAAIO,UAAUN,UAAU,OAAO,KAAKC,aAAaK,OAAON,UAAU,OAAO,EAAEK,QAAQ;YACjF,OAAOL,UAAU,OAAO;QAC1B;QAEAA,UAAU,OAAO,GAAGK;QACpB,OAAOA;IACT,GAAG;QAACR;KAAS;IAEb,gFAAgF;IAChF,oFAAoF;IACpF,gFAAgF;IAChF,MAAMQ,QAAQV,oBAAoBA,CAACO,WAAWE,aAAaA;IAE3D,8EAA8E;IAC9E,MAAMG,oBAAoBd,WAAWA,CACnC,CAACU;QACC,OAAON,SAAS,oBAAoB,CAAC;YACnCM;QACF;IACF,GACA;QAACN;KAAS;IAGZ,MAAMW,oBAAoBf,WAAWA,CAAC;QACpC,OAAO,CAACI,SAAS,aAAa;IAChC,GAAG;QAACA;KAAS;IAEb,MAAMY,YAAYd,oBAAoBA,CAACY,mBAAmBC,mBAAmBA;IAE7E,IAAIV,SAAS,aAAa;QACxB,OAAO;YAAE,MAAMO;YAAOI;QAAU;IAClC;IAEA,OAAOJ;AACT;AAEA;;;;;;;;;;CAUC,GACM,SAASK,qBAAqBA,CAAIb,QAAwC,EAAEc,GAAW,EAAEC,QAAW;IACzG,MAAMC,MAAMjB,WAAWA,CAACC,UAAU;QAChC,QAAQ,CAACiB,GAAGC,IAAMD,CAAC,CAACH,IAAI,KAAKI,CAAC,CAACJ,IAAI;IACrC;IACA,OAAOE,GAAG,CAACF,IAAI,IAAIC;AACrB"}
|
|
@@ -13,4 +13,3 @@ import { IStorageBase } from '../../core';
|
|
|
13
13
|
* @returns Значение из хранилища
|
|
14
14
|
*/
|
|
15
15
|
export declare const useStorageSubscribe: <S extends Record<string, any>, R = any>(storage: IStorageBase<S> | null, selector: (state: S) => R) => R | undefined;
|
|
16
|
-
//# sourceMappingURL=useStorageSubscribe.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { DependencyList } from 'react';
|
|
2
|
+
import { Unsubscribable } from 'rxjs';
|
|
3
|
+
/**
|
|
4
|
+
* Императивная подписка-side-effect без возврата значения в рендер.
|
|
5
|
+
*
|
|
6
|
+
* `factory` создаёт подписку (`source$.subscribe(...)`) — её side-effect'ы (логирование,
|
|
7
|
+
* императивные вызовы, диспатч) живут внутри коллбэка `subscribe`. Возвращённый
|
|
8
|
+
* `Unsubscribable` снимается на unmount и при смене `deps` (перед созданием новой
|
|
9
|
+
* подписки).
|
|
10
|
+
*
|
|
11
|
+
* В отличие от `useObservable`, ничего не рендерит — только запускает и гасит подписку.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useSubscription(factory: () => Unsubscribable, deps: DependencyList): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Императивная подписка-side-effect без возврата значения в рендер.
|
|
7
|
+
*
|
|
8
|
+
* `factory` создаёт подписку (`source$.subscribe(...)`) — её side-effect'ы (логирование,
|
|
9
|
+
* императивные вызовы, диспатч) живут внутри коллбэка `subscribe`. Возвращённый
|
|
10
|
+
* `Unsubscribable` снимается на unmount и при смене `deps` (перед созданием новой
|
|
11
|
+
* подписки).
|
|
12
|
+
*
|
|
13
|
+
* В отличие от `useObservable`, ничего не рендерит — только запускает и гасит подписку.
|
|
14
|
+
*/ function useSubscription(factory, deps) {
|
|
15
|
+
useEffect(()=>{
|
|
16
|
+
const subscription = factory();
|
|
17
|
+
return ()=>subscription.unsubscribe();
|
|
18
|
+
}, deps);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { useSubscription };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=useSubscription.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react/hooks/useSubscription.js","sources":["../../../src/react/hooks/useSubscription.ts"],"sourcesContent":["import { DependencyList, useEffect } from 'react'\nimport { Unsubscribable } from 'rxjs'\n\n/**\n * Императивная подписка-side-effect без возврата значения в рендер.\n *\n * `factory` создаёт подписку (`source$.subscribe(...)`) — её side-effect'ы (логирование,\n * императивные вызовы, диспатч) живут внутри коллбэка `subscribe`. Возвращённый\n * `Unsubscribable` снимается на unmount и при смене `deps` (перед созданием новой\n * подписки).\n *\n * В отличие от `useObservable`, ничего не рендерит — только запускает и гасит подписку.\n */\nexport function useSubscription(factory: () => Unsubscribable, deps: DependencyList): void {\n useEffect(() => {\n const subscription = factory()\n return () => subscription.unsubscribe()\n }, deps)\n}\n"],"names":["useEffect","useSubscription","factory","deps","subscription"],"mappings":";;;AAAiD;AAGjD;;;;;;;;;CASC,GACM,SAASC,eAAeA,CAACC,OAA6B,EAAEC,IAAoB;IACjFH,SAASA,CAAC;QACR,MAAMI,eAAeF;QACrB,OAAO,IAAME,aAAa,WAAW;IACvC,GAAGD;AACL"}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { ComponentType, ReactNode } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import { AnySynapseStore } from '../../utils';
|
|
2
|
+
import { type AwaitableSynapse } from '../../utils';
|
|
4
3
|
interface ReactAwaitSynapseOptions {
|
|
5
4
|
loadingComponent?: ReactNode;
|
|
6
5
|
errorComponent?: (error: Error) => ReactNode;
|
|
7
6
|
}
|
|
8
7
|
/**
|
|
9
|
-
* React-обертка для фреймворк-независимой утилиты ожидания Synapse
|
|
10
|
-
* Добавляет React-специфичные методы поверх createSynapseAwaiter
|
|
8
|
+
* React-обертка для фреймворк-независимой утилиты ожидания Synapse.
|
|
9
|
+
* Добавляет React-специфичные методы поверх createSynapseAwaiter. Принимает
|
|
10
|
+
* `SynapseModule`-handle (PromiseLike), Promise готового synapse или сам synapse.
|
|
11
11
|
*/
|
|
12
|
-
export declare function awaitSynapse<TStore extends
|
|
12
|
+
export declare function awaitSynapse<TStore extends AwaitableSynapse>(synapseStorePromise: PromiseLike<TStore> | TStore, options?: ReactAwaitSynapseOptions): {
|
|
13
13
|
withSynapseReady: <ComponentProps>(Component: ComponentType<ComponentProps>) => {
|
|
14
14
|
(props: ComponentProps): import("react/jsx-runtime").JSX.Element;
|
|
15
15
|
displayName: string;
|
|
@@ -18,17 +18,16 @@ export declare function awaitSynapse<TStore extends Record<string, any>, TStorag
|
|
|
18
18
|
isReady: boolean;
|
|
19
19
|
isError: boolean;
|
|
20
20
|
isPending: boolean;
|
|
21
|
-
store:
|
|
21
|
+
store: TStore | undefined;
|
|
22
22
|
error: Error | null;
|
|
23
23
|
};
|
|
24
|
-
waitForReady: () => Promise<
|
|
24
|
+
waitForReady: () => Promise<TStore>;
|
|
25
25
|
isReady: () => boolean;
|
|
26
|
-
getStoreIfReady: () =>
|
|
27
|
-
onReady: (cb: Parameters<(callback: (store:
|
|
26
|
+
getStoreIfReady: () => TStore | undefined;
|
|
27
|
+
onReady: (cb: Parameters<(callback: (store: TStore) => void) => VoidFunction>[0]) => VoidFunction;
|
|
28
28
|
onError: (cb: Parameters<(callback: (error: Error) => void) => VoidFunction>[0]) => VoidFunction;
|
|
29
29
|
getStatus: () => "error" | "ready" | "pending";
|
|
30
30
|
getError: () => Error | null;
|
|
31
31
|
destroy: () => void;
|
|
32
32
|
};
|
|
33
33
|
export {};
|
|
34
|
-
//# sourceMappingURL=awaitSynapse.d.ts.map
|
|
@@ -9,8 +9,9 @@ import { createSynapseAwaiter } from "../../utils/index.js";
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* React-обертка для фреймворк-независимой утилиты ожидания Synapse
|
|
13
|
-
* Добавляет React-специфичные методы поверх createSynapseAwaiter
|
|
12
|
+
* React-обертка для фреймворк-независимой утилиты ожидания Synapse.
|
|
13
|
+
* Добавляет React-специфичные методы поверх createSynapseAwaiter. Принимает
|
|
14
|
+
* `SynapseModule`-handle (PromiseLike), Promise готового synapse или сам synapse.
|
|
14
15
|
*/ function awaitSynapse(synapseStorePromise, options) {
|
|
15
16
|
const { loadingComponent = /*#__PURE__*/ jsx("div", {
|
|
16
17
|
children: "Инициализация..."
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react/utils/awaitSynapse.js","sources":["../../../src/react/utils/awaitSynapse.tsx"],"sourcesContent":["import { ComponentType, PropsWithChildren, ReactNode, useEffect, useState } from 'react'\n\nimport {
|
|
1
|
+
{"version":3,"file":"react/utils/awaitSynapse.js","sources":["../../../src/react/utils/awaitSynapse.tsx"],"sourcesContent":["import { ComponentType, PropsWithChildren, ReactNode, useEffect, useState } from 'react'\n\nimport { type AwaitableSynapse, createSynapseAwaiter } from '../../utils'\n\ninterface ReactAwaitSynapseOptions {\n loadingComponent?: ReactNode\n errorComponent?: (error: Error) => ReactNode\n}\n\n/**\n * React-обертка для фреймворк-независимой утилиты ожидания Synapse.\n * Добавляет React-специфичные методы поверх createSynapseAwaiter. Принимает\n * `SynapseModule`-handle (PromiseLike), Promise готового synapse или сам synapse.\n */\nexport function awaitSynapse<TStore extends AwaitableSynapse>(synapseStorePromise: PromiseLike<TStore> | TStore, options?: ReactAwaitSynapseOptions) {\n const { loadingComponent = <div>Инициализация...</div>, errorComponent = (error: Error) => <div>Ошибка инициализации: {error.message}</div> } = options || {}\n\n const awaiter = createSynapseAwaiter<TStore>(synapseStorePromise)\n\n /**\n * Хук для получения текущего состояния готовности\n */\n function useSynapseReady() {\n const [status, setStatus] = useState<'pending' | 'ready' | 'error'>(() => awaiter.getStatus())\n const [store, setStore] = useState<TStore | undefined>(() => awaiter.getStoreIfReady())\n const [error, setError] = useState<Error | null>(() => awaiter.getError())\n\n useEffect(() => {\n // Проверяем текущее состояние при монтировании\n const currentStatus = awaiter.getStatus()\n const currentStore = awaiter.getStoreIfReady()\n const currentError = awaiter.getError()\n\n setStatus(currentStatus)\n setStore(currentStore)\n setError(currentError)\n\n // Подписываемся на изменения\n const unsubscribeReady = awaiter.onReady((readyStore) => {\n setStatus('ready')\n setStore(readyStore)\n setError(null)\n })\n\n const unsubscribeError = awaiter.onError((err) => {\n setStatus('error')\n setStore(undefined)\n setError(err)\n })\n\n return () => {\n unsubscribeReady()\n unsubscribeError()\n }\n }, [])\n\n return {\n isReady: status === 'ready',\n isError: status === 'error',\n isPending: status === 'pending',\n store,\n error,\n }\n }\n\n /**\n * Обертка, которая ждет готовности Synapse\n */\n function withSynapseReady<ComponentProps>(Component: ComponentType<ComponentProps>) {\n function WrappedComponent(props: ComponentProps) {\n const { isReady, isError, error } = useSynapseReady()\n\n // Показываем ошибку\n if (isError && error) return <>{errorComponent(error)}</>\n\n // Показываем загрузку\n if (!isReady) return <>{loadingComponent}</>\n\n // Рендерим компонент когда все готово\n return <Component {...(props as PropsWithChildren<ComponentProps>)} />\n }\n\n // Устанавливаем отображаемое имя для отладки\n const componentName = Component.displayName || Component.name || 'Component'\n WrappedComponent.displayName = `AwaitSynapse(${componentName})`\n\n return WrappedComponent\n }\n\n return {\n // React методы\n withSynapseReady,\n useSynapseReady,\n\n // Проксируем все методы из awaiter (обёртки сохраняют контекст при деструктуризации)\n waitForReady: () => awaiter.waitForReady(),\n isReady: () => awaiter.isReady(),\n getStoreIfReady: () => awaiter.getStoreIfReady(),\n onReady: (cb: Parameters<typeof awaiter.onReady>[0]) => awaiter.onReady(cb),\n onError: (cb: Parameters<typeof awaiter.onError>[0]) => awaiter.onError(cb),\n getStatus: () => awaiter.getStatus(),\n getError: () => awaiter.getError(),\n destroy: () => awaiter.destroy(),\n }\n}\n"],"names":["useEffect","useState","createSynapseAwaiter","awaitSynapse","synapseStorePromise","options","loadingComponent","errorComponent","error","awaiter","useSynapseReady","status","setStatus","store","setStore","setError","currentStatus","currentStore","currentError","unsubscribeReady","readyStore","unsubscribeError","err","undefined","withSynapseReady","Component","WrappedComponent","props","isReady","isError","componentName","cb"],"mappings":";;;;;;;;AAAwF;AAEf;AAOzE;;;;CAIC,GACM,SAASG,YAAYA,CAAkCC,mBAAiD,EAAEC,OAAkC;IACjJ,MAAM,EAAEC,iCAAmB,IAAC;kBAAI;MAAsB,EAAEC,iBAAiB,CAACC,sBAAiB,KAAC;;gBAAI;gBAAuBA,MAAM,OAAO;;UAAO,EAAE,GAAGH,WAAW,CAAC;IAE5J,MAAMI,UAAUP,oBAAoBA,CAASE;IAE7C;;GAEC,GACD,SAASM;QACP,MAAM,CAACC,QAAQC,UAAU,GAAGX,QAAQA,CAAgC,IAAMQ,QAAQ,SAAS;QAC3F,MAAM,CAACI,OAAOC,SAAS,GAAGb,QAAQA,CAAqB,IAAMQ,QAAQ,eAAe;QACpF,MAAM,CAACD,OAAOO,SAAS,GAAGd,QAAQA,CAAe,IAAMQ,QAAQ,QAAQ;QAEvET,SAASA,CAAC;YACR,+CAA+C;YAC/C,MAAMgB,gBAAgBP,QAAQ,SAAS;YACvC,MAAMQ,eAAeR,QAAQ,eAAe;YAC5C,MAAMS,eAAeT,QAAQ,QAAQ;YAErCG,UAAUI;YACVF,SAASG;YACTF,SAASG;YAET,6BAA6B;YAC7B,MAAMC,mBAAmBV,QAAQ,OAAO,CAAC,CAACW;gBACxCR,UAAU;gBACVE,SAASM;gBACTL,SAAS;YACX;YAEA,MAAMM,mBAAmBZ,QAAQ,OAAO,CAAC,CAACa;gBACxCV,UAAU;gBACVE,SAASS;gBACTR,SAASO;YACX;YAEA,OAAO;gBACLH;gBACAE;YACF;QACF,GAAG,EAAE;QAEL,OAAO;YACL,SAASV,WAAW;YACpB,SAASA,WAAW;YACpB,WAAWA,WAAW;YACtBE;YACAL;QACF;IACF;IAEA;;GAEC,GACD,SAASgB,iBAAiCC,SAAwC;QAChF,SAASC,iBAAiBC,KAAqB;YAC7C,MAAM,EAAEC,OAAO,EAAEC,OAAO,EAAErB,KAAK,EAAE,GAAGE;YAEpC,oBAAoB;YACpB,IAAImB,WAAWrB,OAAO,qBAAO;0BAAGD,eAAeC;;YAE/C,sBAAsB;YACtB,IAAI,CAACoB,SAAS,qBAAO;0BAAGtB;;YAExB,sCAAsC;YACtC,qBAAO,IAACmB;gBAAW,GAAIE,KAAK;;QAC9B;QAEA,6CAA6C;QAC7C,MAAMG,gBAAgBL,UAAU,WAAW,IAAIA,UAAU,IAAI,IAAI;QACjEC,iBAAiB,WAAW,GAAG,CAAC,aAAa,EAAEI,cAAc,CAAC,CAAC;QAE/D,OAAOJ;IACT;IAEA,OAAO;QACL,eAAe;QACfF;QACAd;QAEA,qFAAqF;QACrF,cAAc,IAAMD,QAAQ,YAAY;QACxC,SAAS,IAAMA,QAAQ,OAAO;QAC9B,iBAAiB,IAAMA,QAAQ,eAAe;QAC9C,SAAS,CAACsB,KAA8CtB,QAAQ,OAAO,CAACsB;QACxE,SAAS,CAACA,KAA8CtB,QAAQ,OAAO,CAACsB;QACxE,WAAW,IAAMtB,QAAQ,SAAS;QAClC,UAAU,IAAMA,QAAQ,QAAQ;QAChC,SAAS,IAAMA,QAAQ,OAAO;IAChC;AACF"}
|
|
@@ -1,33 +1,28 @@
|
|
|
1
1
|
import { ComponentType } from 'react';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
3
|
import { IStorage } from '../../core';
|
|
4
|
-
import {
|
|
4
|
+
import { type SynapseModule } from '../../utils';
|
|
5
5
|
interface SimplifiedOptions {
|
|
6
6
|
loadingComponent?: React.ReactNode;
|
|
7
|
+
/**
|
|
8
|
+
* Включает серверный рендер засеянных sync-сторов (Memory/LocalStorage). При `ssr: true`
|
|
9
|
+
* и синхронно-готовом сторе Provider рендерит children сразу (без `loadingComponent`),
|
|
10
|
+
* что даёт контент в серверном HTML и совпадающий первый кадр при гидрации.
|
|
11
|
+
* Для async-сторов (IndexedDB) поведение прежнее — гейт `loadingComponent`.
|
|
12
|
+
*/
|
|
13
|
+
ssr?: boolean;
|
|
7
14
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
useSynapseState$: () => Observable<TStore>;
|
|
17
|
-
cleanupSynapse: () => Promise<void>;
|
|
18
|
-
};
|
|
19
|
-
export declare function createSynapseCtx<TStore extends Record<string, any>, TStorage extends IStorage<TStore>, TSelectors, TActions>(synapseStorePromise: Promise<SynapseStoreWithDispatcher<TStore, TStorage, TSelectors, TActions>> | SynapseStoreWithDispatcher<TStore, TStorage, TSelectors, TActions>, options?: SimplifiedOptions): {
|
|
20
|
-
contextSynapse: <SelfComponentProps>(Component: ComponentType<SelfComponentProps>) => ComponentType<SelfComponentProps>;
|
|
21
|
-
useSynapseStorage: () => TStorage;
|
|
22
|
-
useSynapseSelectors: () => TSelectors;
|
|
23
|
-
useSynapseActions: () => TActions;
|
|
24
|
-
cleanupSynapse: () => Promise<void>;
|
|
25
|
-
};
|
|
26
|
-
export declare function createSynapseCtx<TStore extends Record<string, any>, TStorage extends IStorage<TStore>, TSelectors>(synapseStorePromise: Promise<SynapseStoreBasic<TStore, TStorage, TSelectors>> | SynapseStoreBasic<TStore, TStorage, TSelectors>, options?: SimplifiedOptions): {
|
|
27
|
-
contextSynapse: <SelfComponentProps>(Component: ComponentType<SelfComponentProps>) => ComponentType<SelfComponentProps>;
|
|
28
|
-
useSynapseStorage: () => TStorage;
|
|
15
|
+
export declare function createSynapseCtx<TState extends Record<string, any>, TDispatcher, TSelectors>(synapseModule: SynapseModule<TState, TDispatcher, TSelectors>, options?: SimplifiedOptions): {
|
|
16
|
+
contextSynapse: <SelfComponentProps>(Component: ComponentType<SelfComponentProps>) => ComponentType<SelfComponentProps & {
|
|
17
|
+
dehydratedState?: TState;
|
|
18
|
+
}>;
|
|
19
|
+
dehydrate: (opts?: {
|
|
20
|
+
initialState?: Partial<TState>;
|
|
21
|
+
}) => Promise<TState>;
|
|
22
|
+
useSynapseStorage: () => IStorage<TState>;
|
|
29
23
|
useSynapseSelectors: () => TSelectors;
|
|
24
|
+
useSynapseActions: () => TDispatcher;
|
|
25
|
+
useSynapseState$: () => Observable<TState>;
|
|
30
26
|
cleanupSynapse: () => Promise<void>;
|
|
31
27
|
};
|
|
32
28
|
export {};
|
|
33
|
-
//# sourceMappingURL=createSynapseCtx.d.ts.map
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Fragment, jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, forwardRef, useContext, useEffect, useState } from "react";
|
|
2
|
+
import { createContext, forwardRef, useContext, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { handleCleanupError } from "../../_utils/error-handling.util.js";
|
|
4
|
-
import {
|
|
4
|
+
import { StorageStatus } from "../../core/index.js";
|
|
5
|
+
import { createSynapseAwaiter, dehydrateModule } from "../../utils/index.js";
|
|
6
|
+
|
|
7
|
+
|
|
5
8
|
|
|
6
9
|
|
|
7
10
|
|
|
@@ -13,19 +16,20 @@ import { createSynapseAwaiter } from "../../utils/index.js";
|
|
|
13
16
|
|
|
14
17
|
const ERROR_HOOK_MESSAGE = 'Хук необходимо использовать внутри компонента contextSynapse';
|
|
15
18
|
const ERROR_CONTEXT_INIT = 'Ошибка при инициализации контекста:';
|
|
16
|
-
|
|
17
|
-
function createSynapseCtx(synapseStorePromise, options) {
|
|
19
|
+
function createSynapseCtx(synapseModule, options) {
|
|
18
20
|
const { loadingComponent = /*#__PURE__*/ jsx("div", {
|
|
19
21
|
children: "Инициализация контекста..."
|
|
20
|
-
}) } = options || {};
|
|
21
|
-
// Lazy-инициализация: awaiter создаётся при первом обращении и сбрасывается при cleanup.
|
|
22
|
-
// Сам awaiter (createSynapseAwaiter) инкапсулирует ожидание готовности, статус и подписки.
|
|
23
|
-
let awaiter = null;
|
|
24
|
-
const getAwaiter = ()=>{
|
|
25
|
-
if (!awaiter) awaiter = createSynapseAwaiter(synapseStorePromise);
|
|
26
|
-
return awaiter;
|
|
27
|
-
};
|
|
22
|
+
}), ssr = false } = options || {};
|
|
28
23
|
const SynapseContext = /*#__PURE__*/ createContext(null);
|
|
24
|
+
// ── Awaiter ───────────────────────────────────────────────────────────────
|
|
25
|
+
// Клиент сохраняет прежнюю синглтон-семантику: один awaiter на handle (общий стор,
|
|
26
|
+
// фабрика стартует один раз при первом mount). На сервере синглтон уровня модуля
|
|
27
|
+
// запрещён (request bleed), поэтому там awaiter живёт per-render-tree и не шарится.
|
|
28
|
+
let clientAwaiter = null;
|
|
29
|
+
const getClientAwaiter = ()=>{
|
|
30
|
+
if (!clientAwaiter) clientAwaiter = createSynapseAwaiter(synapseModule);
|
|
31
|
+
return clientAwaiter;
|
|
32
|
+
};
|
|
29
33
|
const useSynapseStorage = ()=>{
|
|
30
34
|
const context = useContext(SynapseContext);
|
|
31
35
|
if (!context) throw new Error(`useSynapseStorage: ${ERROR_HOOK_MESSAGE}`);
|
|
@@ -36,36 +40,59 @@ function createSynapseCtx(synapseStorePromise, options) {
|
|
|
36
40
|
if (!context) throw new Error(`useSynapseSelectors: ${ERROR_HOOK_MESSAGE}`);
|
|
37
41
|
return context.selectors;
|
|
38
42
|
};
|
|
39
|
-
// Условный хук для actions (только если есть dispatcher)
|
|
40
43
|
const useSynapseActions = ()=>{
|
|
41
44
|
const context = useContext(SynapseContext);
|
|
42
45
|
if (!context) throw new Error(`useSynapseActions: ${ERROR_HOOK_MESSAGE}`);
|
|
43
|
-
|
|
44
|
-
return context.actions;
|
|
45
|
-
}
|
|
46
|
-
throw new Error('useSynapseActions: actions недоступны для этого типа хранилища. Убедитесь, что передана функция createDispatcherFn при создании хранилища.');
|
|
46
|
+
return context.actions;
|
|
47
47
|
};
|
|
48
|
-
// Условный хук для state$ (только если есть effects)
|
|
49
48
|
const useSynapseState$ = ()=>{
|
|
50
49
|
const context = useContext(SynapseContext);
|
|
51
50
|
if (!context) throw new Error(`useSynapseState$: ${ERROR_HOOK_MESSAGE}`);
|
|
52
|
-
|
|
53
|
-
return context.state$;
|
|
54
|
-
}
|
|
55
|
-
throw new Error('useSynapseState$: state$ недоступен для этого типа хранилища. Убедитесь, что переданы функции createDispatcherFn и createEffectConfig при создании хранилища.');
|
|
51
|
+
return context.state$;
|
|
56
52
|
};
|
|
53
|
+
// Серверный помощник: тонкая обёртка над server-safe dehydrateModule (вся логика там).
|
|
54
|
+
// initialState — серверные данные под запрос, не статический initialState модуля; ssr — из
|
|
55
|
+
// опций контекста.
|
|
56
|
+
const dehydrate = (opts)=>dehydrateModule(synapseModule, {
|
|
57
|
+
state: opts?.initialState,
|
|
58
|
+
ssr
|
|
59
|
+
});
|
|
57
60
|
/**
|
|
58
|
-
* Декоратор для
|
|
61
|
+
* Декоратор для обёртки компонентов в контекст Synapse.
|
|
59
62
|
*/ function contextSynapse(Component) {
|
|
60
63
|
const WrappedComponent = /*#__PURE__*/ forwardRef(function WrappedComponent(props, ref) {
|
|
61
|
-
const
|
|
62
|
-
|
|
64
|
+
const { dehydratedState, ...restProps } = props;
|
|
65
|
+
// Per-tree awaiter при наличии dehydratedState (изоляция server-рендера); иначе —
|
|
66
|
+
// общий клиентский awaiter (обратная совместимость).
|
|
67
|
+
const treeAwaiterRef = useRef(null);
|
|
68
|
+
const resolveAwaiter = ()=>{
|
|
69
|
+
if (dehydratedState !== undefined) {
|
|
70
|
+
if (!treeAwaiterRef.current) treeAwaiterRef.current = createSynapseAwaiter(synapseModule);
|
|
71
|
+
return treeAwaiterRef.current;
|
|
72
|
+
}
|
|
73
|
+
return getClientAwaiter();
|
|
74
|
+
};
|
|
75
|
+
// Синхронный засев снапшота ДО первого рендера: одинаковый HTML на сервере и клиенте.
|
|
76
|
+
const seedHydration = (store)=>{
|
|
77
|
+
if (store && dehydratedState !== undefined && store.storage.initStatus.status === StorageStatus.READY) {
|
|
78
|
+
store.storage.hydrate(dehydratedState);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const [synapseStore, setSynapseStore] = useState(()=>{
|
|
82
|
+
const store = resolveAwaiter().getStoreIfReady();
|
|
83
|
+
seedHydration(store);
|
|
84
|
+
return store;
|
|
85
|
+
});
|
|
86
|
+
const [error, setError] = useState(()=>resolveAwaiter().getError());
|
|
63
87
|
useEffect(()=>{
|
|
64
|
-
//
|
|
65
|
-
const instance =
|
|
66
|
-
|
|
88
|
+
// На сервере эффект не исполняется — подписки/догрузка стартуют только на клиенте.
|
|
89
|
+
const instance = resolveAwaiter();
|
|
90
|
+
const current = instance.getStoreIfReady();
|
|
91
|
+
seedHydration(current);
|
|
92
|
+
setSynapseStore(current);
|
|
67
93
|
setError(instance.getError());
|
|
68
94
|
const unsubscribeReady = instance.onReady((store)=>{
|
|
95
|
+
seedHydration(store);
|
|
69
96
|
setSynapseStore(store);
|
|
70
97
|
setError(null);
|
|
71
98
|
});
|
|
@@ -78,23 +105,23 @@ function createSynapseCtx(synapseStorePromise, options) {
|
|
|
78
105
|
unsubscribeError();
|
|
79
106
|
};
|
|
80
107
|
}, []);
|
|
81
|
-
// Показываем ошибку если что-то пошло не так
|
|
82
108
|
if (error) return /*#__PURE__*/ jsx("div", {
|
|
83
109
|
children: `${ERROR_CONTEXT_INIT} ${error.message}`
|
|
84
110
|
});
|
|
85
|
-
//
|
|
111
|
+
// SSR-гейт: при ssr и синхронно-готовом сторе (сервер после dehydrate / клиентская
|
|
112
|
+
// гидрация) synapseStore уже есть → рендерим children. Иначе — прежний гейт загрузки
|
|
113
|
+
// (async-сторы и обычный клиентский старт).
|
|
86
114
|
if (!synapseStore) return /*#__PURE__*/ jsx(Fragment, {
|
|
87
115
|
children: loadingComponent
|
|
88
116
|
});
|
|
89
117
|
return /*#__PURE__*/ jsx(SynapseContext.Provider, {
|
|
90
118
|
value: synapseStore,
|
|
91
119
|
children: /*#__PURE__*/ jsx(Component, {
|
|
92
|
-
...
|
|
120
|
+
...restProps,
|
|
93
121
|
ref: ref
|
|
94
122
|
})
|
|
95
123
|
});
|
|
96
124
|
});
|
|
97
|
-
// Устанавливаем отображаемое имя для отладки
|
|
98
125
|
const componentName = Component.displayName || Component.name || 'Component';
|
|
99
126
|
WrappedComponent.displayName = `SynapseContext(${componentName})`;
|
|
100
127
|
// Копируем статические свойства оригинального компонента
|
|
@@ -114,20 +141,18 @@ function createSynapseCtx(synapseStorePromise, options) {
|
|
|
114
141
|
return WrappedComponent;
|
|
115
142
|
}
|
|
116
143
|
const cleanupSynapse = async ()=>{
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
awaiter = null; // сбрасываем сразу, чтобы следующий маунт создал новый awaiter
|
|
144
|
+
const instance = clientAwaiter;
|
|
145
|
+
clientAwaiter = null;
|
|
120
146
|
try {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
await store?.destroy();
|
|
147
|
+
instance?.destroy();
|
|
148
|
+
await synapseModule.destroy();
|
|
124
149
|
} catch (error) {
|
|
125
|
-
instance.destroy();
|
|
126
150
|
handleCleanupError('createSynapseCtx: error during Synapse cleanup', error);
|
|
127
151
|
}
|
|
128
152
|
};
|
|
129
153
|
return {
|
|
130
154
|
contextSynapse,
|
|
155
|
+
dehydrate,
|
|
131
156
|
useSynapseStorage,
|
|
132
157
|
useSynapseSelectors,
|
|
133
158
|
useSynapseActions,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react/utils/createSynapseCtx.js","sources":["../../../src/react/utils/createSynapseCtx.tsx"],"sourcesContent":["import { ComponentType, createContext, forwardRef, PropsWithChildren, useContext, useEffect, useState } from 'react'\nimport { Observable } from 'rxjs'\n\nimport { handleCleanupError } from '../../_utils/error-handling.util'\nimport { IStorage } from '../../core'\nimport { AnySynapseStore, createSynapseAwaiter, SynapseStoreBasic, SynapseStoreWithDispatcher, SynapseStoreWithEffects } from '../../utils'\n\nconst ERROR_HOOK_MESSAGE = 'Хук необходимо использовать внутри компонента contextSynapse'\nconst ERROR_CONTEXT_INIT = 'Ошибка при инициализации контекста:'\n\ninterface SimplifiedOptions {\n loadingComponent?: React.ReactNode\n}\n\n/**\n * Перегрузки для createSynapseCtx в зависимости от типа хранилища\n */\n\n// Для хранилища с effects\nexport function createSynapseCtx<TStore extends Record<string, any>, TStorage extends IStorage<TStore>, TSelectors, TActions>(\n synapseStorePromise: Promise<SynapseStoreWithEffects<TStore, TStorage, TSelectors, TActions>> | SynapseStoreWithEffects<TStore, TStorage, TSelectors, TActions>,\n options?: SimplifiedOptions,\n): {\n contextSynapse: <SelfComponentProps>(Component: ComponentType<SelfComponentProps>) => ComponentType<SelfComponentProps>\n useSynapseStorage: () => TStorage\n useSynapseSelectors: () => TSelectors\n useSynapseActions: () => TActions\n useSynapseState$: () => Observable<TStore>\n cleanupSynapse: () => Promise<void>\n}\n\n// Для хранилища с dispatcher (без effects)\nexport function createSynapseCtx<TStore extends Record<string, any>, TStorage extends IStorage<TStore>, TSelectors, TActions>(\n synapseStorePromise: Promise<SynapseStoreWithDispatcher<TStore, TStorage, TSelectors, TActions>> | SynapseStoreWithDispatcher<TStore, TStorage, TSelectors, TActions>,\n options?: SimplifiedOptions,\n): {\n contextSynapse: <SelfComponentProps>(Component: ComponentType<SelfComponentProps>) => ComponentType<SelfComponentProps>\n useSynapseStorage: () => TStorage\n useSynapseSelectors: () => TSelectors\n useSynapseActions: () => TActions\n cleanupSynapse: () => Promise<void>\n}\n\n// Для базового хранилища\nexport function createSynapseCtx<TStore extends Record<string, any>, TStorage extends IStorage<TStore>, TSelectors>(\n synapseStorePromise: Promise<SynapseStoreBasic<TStore, TStorage, TSelectors>> | SynapseStoreBasic<TStore, TStorage, TSelectors>,\n options?: SimplifiedOptions,\n): {\n contextSynapse: <SelfComponentProps>(Component: ComponentType<SelfComponentProps>) => ComponentType<SelfComponentProps>\n useSynapseStorage: () => TStorage\n useSynapseSelectors: () => TSelectors\n cleanupSynapse: () => Promise<void>\n}\n\n// Основная реализация\nexport function createSynapseCtx<TStore extends Record<string, any>, TStorage extends IStorage<TStore>, TSelectors = any, TActions = any>(\n synapseStorePromise: Promise<AnySynapseStore<TStore, TStorage, TSelectors, TActions>> | AnySynapseStore<TStore, TStorage, TSelectors, TActions>,\n options?: SimplifiedOptions,\n) {\n const { loadingComponent = <div>Инициализация контекста...</div> } = options || {}\n\n // Lazy-инициализация: awaiter создаётся при первом обращении и сбрасывается при cleanup.\n // Сам awaiter (createSynapseAwaiter) инкапсулирует ожидание готовности, статус и подписки.\n let awaiter: ReturnType<typeof createSynapseAwaiter<TStore, TStorage, TSelectors, TActions>> | null = null\n\n const getAwaiter = () => {\n if (!awaiter) awaiter = createSynapseAwaiter(synapseStorePromise)\n return awaiter\n }\n\n const SynapseContext = createContext<AnySynapseStore<TStore, TStorage, TSelectors, TActions> | null>(null)\n\n const useSynapseStorage = (): TStorage => {\n const context = useContext(SynapseContext)\n if (!context) throw new Error(`useSynapseStorage: ${ERROR_HOOK_MESSAGE}`)\n return context.storage\n }\n\n const useSynapseSelectors = (): TSelectors => {\n const context = useContext(SynapseContext)\n if (!context) throw new Error(`useSynapseSelectors: ${ERROR_HOOK_MESSAGE}`)\n return context.selectors\n }\n\n // Условный хук для actions (только если есть dispatcher)\n const useSynapseActions = (): TActions => {\n const context = useContext(SynapseContext)\n if (!context) throw new Error(`useSynapseActions: ${ERROR_HOOK_MESSAGE}`)\n\n if ('actions' in context) {\n return (context as SynapseStoreWithDispatcher<TStore, TStorage, TSelectors, TActions> | SynapseStoreWithEffects<TStore, TStorage, TSelectors, TActions>).actions\n }\n\n throw new Error('useSynapseActions: actions недоступны для этого типа хранилища. Убедитесь, что передана функция createDispatcherFn при создании хранилища.')\n }\n\n // Условный хук для state$ (только если есть effects)\n const useSynapseState$ = (): Observable<TStore> => {\n const context = useContext(SynapseContext)\n if (!context) throw new Error(`useSynapseState$: ${ERROR_HOOK_MESSAGE}`)\n\n if ('state$' in context) {\n return (context as SynapseStoreWithEffects<TStore, TStorage, TSelectors, TActions>).state$\n }\n\n throw new Error('useSynapseState$: state$ недоступен для этого типа хранилища. Убедитесь, что переданы функции createDispatcherFn и createEffectConfig при создании хранилища.')\n }\n\n /**\n * Декоратор для обертывания компонентов в контекст Synapse\n */\n function contextSynapse<SelfComponentProps>(Component: ComponentType<SelfComponentProps>) {\n const WrappedComponent = forwardRef<unknown, SelfComponentProps>(function WrappedComponent(props, ref) {\n const [synapseStore, setSynapseStore] = useState<AnySynapseStore<TStore, TStorage, TSelectors, TActions> | undefined>(() => getAwaiter().getStoreIfReady())\n const [error, setError] = useState<Error | null>(() => getAwaiter().getError())\n\n useEffect(() => {\n // awaiter мог поменять состояние между рендером и эффектом — синхронизируемся\n const instance = getAwaiter()\n setSynapseStore(instance.getStoreIfReady())\n setError(instance.getError())\n\n const unsubscribeReady = instance.onReady((store) => {\n setSynapseStore(store)\n setError(null)\n })\n const unsubscribeError = instance.onError((err) => {\n setSynapseStore(undefined)\n setError(err)\n })\n\n return () => {\n unsubscribeReady()\n unsubscribeError()\n }\n }, [])\n\n // Показываем ошибку если что-то пошло не так\n if (error) return <div>{`${ERROR_CONTEXT_INIT} ${error.message}`}</div>\n\n // Показываем загрузку пока store не готов\n if (!synapseStore) return <>{loadingComponent}</>\n\n return (\n <SynapseContext.Provider value={synapseStore}>\n <Component {...(props as PropsWithChildren<SelfComponentProps>)} ref={ref} />\n </SynapseContext.Provider>\n )\n })\n\n // Устанавливаем отображаемое имя для отладки\n const componentName = Component.displayName || Component.name || 'Component'\n WrappedComponent.displayName = `SynapseContext(${componentName})`\n\n // Копируем статические свойства оригинального компонента\n const excludedKeys = new Set(['$$typeof', 'render', 'defaultProps', 'displayName', 'propTypes'])\n Object.keys(Component).forEach((key) => {\n if (!excludedKeys.has(key)) {\n ;(WrappedComponent as any)[key] = (Component as any)[key]\n }\n })\n\n return WrappedComponent as ComponentType<SelfComponentProps>\n }\n\n const cleanupSynapse = async (): Promise<void> => {\n if (!awaiter) return\n\n const instance = awaiter\n awaiter = null // сбрасываем сразу, чтобы следующий маунт создал новый awaiter\n\n try {\n const store = instance.getStoreIfReady() ?? (await instance.waitForReady())\n instance.destroy()\n await store?.destroy()\n } catch (error) {\n instance.destroy()\n handleCleanupError('createSynapseCtx: error during Synapse cleanup', error)\n }\n }\n\n return {\n contextSynapse,\n useSynapseStorage,\n useSynapseSelectors,\n useSynapseActions,\n useSynapseState$,\n cleanupSynapse,\n }\n}\n"],"names":["createContext","forwardRef","useContext","useEffect","useState","handleCleanupError","createSynapseAwaiter","ERROR_HOOK_MESSAGE","ERROR_CONTEXT_INIT","createSynapseCtx","synapseStorePromise","options","loadingComponent","awaiter","getAwaiter","SynapseContext","useSynapseStorage","context","Error","useSynapseSelectors","useSynapseActions","useSynapseState$","contextSynapse","Component","WrappedComponent","props","ref","synapseStore","setSynapseStore","error","setError","instance","unsubscribeReady","store","unsubscribeError","err","undefined","componentName","excludedKeys","Set","Object","key","cleanupSynapse"],"mappings":";;;;;;;;;;AAAoH;AAG/C;AAEsE;AAE3I,MAAMO,kBAAkBA,GAAG;AAC3B,MAAMC,kBAAkBA,GAAG;AA8C3B,sBAAsB;AACf,SAASC,gBAAgBA,CAC9BC,mBAA+I,EAC/IC,OAA2B;IAE3B,MAAM,EAAEC,iCAAmB,IAAC;kBAAI;MAAgC,EAAE,GAAGD,WAAW,CAAC;IAEjF,yFAAyF;IACzF,2FAA2F;IAC3F,IAAIE,UAAkG;IAEtG,MAAMC,aAAa;QACjB,IAAI,CAACD,SAASA,UAAUP,oBAAoBA,CAACI;QAC7C,OAAOG;IACT;IAEA,MAAME,+BAAiBf,aAAaA,CAAiE;IAErG,MAAMgB,oBAAoB;QACxB,MAAMC,UAAUf,UAAUA,CAACa;QAC3B,IAAI,CAACE,SAAS,MAAM,IAAIC,MAAM,CAAC,mBAAmB,EAAEX,kBAAkBA,EAAE;QACxE,OAAOU,QAAQ,OAAO;IACxB;IAEA,MAAME,sBAAsB;QAC1B,MAAMF,UAAUf,UAAUA,CAACa;QAC3B,IAAI,CAACE,SAAS,MAAM,IAAIC,MAAM,CAAC,qBAAqB,EAAEX,kBAAkBA,EAAE;QAC1E,OAAOU,QAAQ,SAAS;IAC1B;IAEA,yDAAyD;IACzD,MAAMG,oBAAoB;QACxB,MAAMH,UAAUf,UAAUA,CAACa;QAC3B,IAAI,CAACE,SAAS,MAAM,IAAIC,MAAM,CAAC,mBAAmB,EAAEX,kBAAkBA,EAAE;QAExE,IAAI,aAAaU,SAAS;YACxB,OAAQA,QAAiJ,OAAO;QAClK;QAEA,MAAM,IAAIC,MAAM;IAClB;IAEA,qDAAqD;IACrD,MAAMG,mBAAmB;QACvB,MAAMJ,UAAUf,UAAUA,CAACa;QAC3B,IAAI,CAACE,SAAS,MAAM,IAAIC,MAAM,CAAC,kBAAkB,EAAEX,kBAAkBA,EAAE;QAEvE,IAAI,YAAYU,SAAS;YACvB,OAAQA,QAA4E,MAAM;QAC5F;QAEA,MAAM,IAAIC,MAAM;IAClB;IAEA;;GAEC,GACD,SAASI,eAAmCC,SAA4C;QACtF,MAAMC,iCAAmBvB,UAAUA,CAA8B,SAASuB,iBAAiBC,KAAK,EAAEC,GAAG;YACnG,MAAM,CAACC,cAAcC,gBAAgB,GAAGxB,QAAQA,CAAsE,IAAMU,aAAa,eAAe;YACxJ,MAAM,CAACe,OAAOC,SAAS,GAAG1B,QAAQA,CAAe,IAAMU,aAAa,QAAQ;YAE5EX,SAASA,CAAC;gBACR,8EAA8E;gBAC9E,MAAM4B,WAAWjB;gBACjBc,gBAAgBG,SAAS,eAAe;gBACxCD,SAASC,SAAS,QAAQ;gBAE1B,MAAMC,mBAAmBD,SAAS,OAAO,CAAC,CAACE;oBACzCL,gBAAgBK;oBAChBH,SAAS;gBACX;gBACA,MAAMI,mBAAmBH,SAAS,OAAO,CAAC,CAACI;oBACzCP,gBAAgBQ;oBAChBN,SAASK;gBACX;gBAEA,OAAO;oBACLH;oBACAE;gBACF;YACF,GAAG,EAAE;YAEL,6CAA6C;YAC7C,IAAIL,OAAO,qBAAO,IAAC;0BAAK,GAAGrB,kBAAkBA,CAAC,CAAC,EAAEqB,MAAM,OAAO,EAAE;;YAEhE,0CAA0C;YAC1C,IAAI,CAACF,cAAc,qBAAO;0BAAGf;;YAE7B,qBACE,IAACG,eAAe,QAAQ;gBAAC,OAAOY;0BAC9B,kBAACJ;oBAAW,GAAIE,KAAK;oBAA4C,KAAKC;;;QAG5E;QAEA,6CAA6C;QAC7C,MAAMW,gBAAgBd,UAAU,WAAW,IAAIA,UAAU,IAAI,IAAI;QACjEC,iBAAiB,WAAW,GAAG,CAAC,eAAe,EAAEa,cAAc,CAAC,CAAC;QAEjE,yDAAyD;QACzD,MAAMC,eAAe,IAAIC,IAAI;YAAC;YAAY;YAAU;YAAgB;YAAe;SAAY;QAC/FC,OAAO,IAAI,CAACjB,WAAW,OAAO,CAAC,CAACkB;YAC9B,IAAI,CAACH,aAAa,GAAG,CAACG,MAAM;;gBACxBjB,gBAAwB,CAACiB,IAAI,GAAIlB,SAAiB,CAACkB,IAAI;YAC3D;QACF;QAEA,OAAOjB;IACT;IAEA,MAAMkB,iBAAiB;QACrB,IAAI,CAAC7B,SAAS;QAEd,MAAMkB,WAAWlB;QACjBA,UAAU,MAAK,+DAA+D;QAE9E,IAAI;YACF,MAAMoB,QAAQF,SAAS,eAAe,MAAO,MAAMA,SAAS,YAAY;YACxEA,SAAS,OAAO;YAChB,MAAME,OAAO;QACf,EAAE,OAAOJ,OAAO;YACdE,SAAS,OAAO;YAChB1B,kBAAkBA,CAAC,kDAAkDwB;QACvE;IACF;IAEA,OAAO;QACLP;QACAN;QACAG;QACAC;QACAC;QACAqB;IACF;AACF"}
|
|
1
|
+
{"version":3,"file":"react/utils/createSynapseCtx.js","sources":["../../../src/react/utils/createSynapseCtx.tsx"],"sourcesContent":["import { ComponentType, createContext, forwardRef, PropsWithChildren, useContext, useEffect, useRef, useState } from 'react'\nimport { Observable } from 'rxjs'\n\nimport { handleCleanupError } from '../../_utils/error-handling.util'\nimport { IStorage, StorageStatus } from '../../core'\nimport { createSynapseAwaiter, dehydrateModule, type Synapse, type SynapseAwaiter, type SynapseModule } from '../../utils'\n\nconst ERROR_HOOK_MESSAGE = 'Хук необходимо использовать внутри компонента contextSynapse'\nconst ERROR_CONTEXT_INIT = 'Ошибка при инициализации контекста:'\n\ninterface SimplifiedOptions {\n loadingComponent?: React.ReactNode\n /**\n * Включает серверный рендер засеянных sync-сторов (Memory/LocalStorage). При `ssr: true`\n * и синхронно-готовом сторе Provider рендерит children сразу (без `loadingComponent`),\n * что даёт контент в серверном HTML и совпадающий первый кадр при гидрации.\n * Для async-сторов (IndexedDB) поведение прежнее — гейт `loadingComponent`.\n */\n ssr?: boolean\n}\n\nexport function createSynapseCtx<TState extends Record<string, any>, TDispatcher, TSelectors>(\n synapseModule: SynapseModule<TState, TDispatcher, TSelectors>,\n options?: SimplifiedOptions,\n) {\n const { loadingComponent = <div>Инициализация контекста...</div>, ssr = false } = options || {}\n\n type ReadySynapse = Synapse<TState, TDispatcher, TSelectors>\n\n const SynapseContext = createContext<ReadySynapse | null>(null)\n\n // ── Awaiter ───────────────────────────────────────────────────────────────\n // Клиент сохраняет прежнюю синглтон-семантику: один awaiter на handle (общий стор,\n // фабрика стартует один раз при первом mount). На сервере синглтон уровня модуля\n // запрещён (request bleed), поэтому там awaiter живёт per-render-tree и не шарится.\n let clientAwaiter: SynapseAwaiter<ReadySynapse> | null = null\n\n const getClientAwaiter = () => {\n if (!clientAwaiter) clientAwaiter = createSynapseAwaiter<ReadySynapse>(synapseModule)\n return clientAwaiter\n }\n\n const useSynapseStorage = (): IStorage<TState> => {\n const context = useContext(SynapseContext)\n if (!context) throw new Error(`useSynapseStorage: ${ERROR_HOOK_MESSAGE}`)\n return context.storage\n }\n\n const useSynapseSelectors = (): TSelectors => {\n const context = useContext(SynapseContext)\n if (!context) throw new Error(`useSynapseSelectors: ${ERROR_HOOK_MESSAGE}`)\n return context.selectors\n }\n\n const useSynapseActions = (): TDispatcher => {\n const context = useContext(SynapseContext)\n if (!context) throw new Error(`useSynapseActions: ${ERROR_HOOK_MESSAGE}`)\n return context.actions\n }\n\n const useSynapseState$ = (): Observable<TState> => {\n const context = useContext(SynapseContext)\n if (!context) throw new Error(`useSynapseState$: ${ERROR_HOOK_MESSAGE}`)\n return context.state$\n }\n\n // Серверный помощник: тонкая обёртка над server-safe dehydrateModule (вся логика там).\n // initialState — серверные данные под запрос, не статический initialState модуля; ssr — из\n // опций контекста.\n const dehydrate = (opts?: { initialState?: Partial<TState> }): Promise<TState> => dehydrateModule(synapseModule, { state: opts?.initialState, ssr })\n\n /**\n * Декоратор для обёртки компонентов в контекст Synapse.\n */\n function contextSynapse<SelfComponentProps>(Component: ComponentType<SelfComponentProps>) {\n const WrappedComponent = forwardRef<unknown, SelfComponentProps & { dehydratedState?: TState }>(function WrappedComponent(props, ref) {\n const { dehydratedState, ...restProps } = props as SelfComponentProps & { dehydratedState?: TState }\n\n // Per-tree awaiter при наличии dehydratedState (изоляция server-рендера); иначе —\n // общий клиентский awaiter (обратная совместимость).\n const treeAwaiterRef = useRef<SynapseAwaiter<ReadySynapse> | null>(null)\n const resolveAwaiter = (): SynapseAwaiter<ReadySynapse> => {\n if (dehydratedState !== undefined) {\n if (!treeAwaiterRef.current) treeAwaiterRef.current = createSynapseAwaiter<ReadySynapse>(synapseModule)\n return treeAwaiterRef.current\n }\n return getClientAwaiter()\n }\n\n // Синхронный засев снапшота ДО первого рендера: одинаковый HTML на сервере и клиенте.\n const seedHydration = (store: ReadySynapse | undefined) => {\n if (store && dehydratedState !== undefined && store.storage.initStatus.status === StorageStatus.READY) {\n store.storage.hydrate(dehydratedState)\n }\n }\n\n const [synapseStore, setSynapseStore] = useState<ReadySynapse | undefined>(() => {\n const store = resolveAwaiter().getStoreIfReady()\n seedHydration(store)\n return store\n })\n const [error, setError] = useState<Error | null>(() => resolveAwaiter().getError())\n\n useEffect(() => {\n // На сервере эффект не исполняется — подписки/догрузка стартуют только на клиенте.\n const instance = resolveAwaiter()\n const current = instance.getStoreIfReady()\n seedHydration(current)\n setSynapseStore(current)\n setError(instance.getError())\n\n const unsubscribeReady = instance.onReady((store) => {\n seedHydration(store)\n setSynapseStore(store)\n setError(null)\n })\n const unsubscribeError = instance.onError((err) => {\n setSynapseStore(undefined)\n setError(err)\n })\n\n return () => {\n unsubscribeReady()\n unsubscribeError()\n }\n }, [])\n\n if (error) return <div>{`${ERROR_CONTEXT_INIT} ${error.message}`}</div>\n\n // SSR-гейт: при ssr и синхронно-готовом сторе (сервер после dehydrate / клиентская\n // гидрация) synapseStore уже есть → рендерим children. Иначе — прежний гейт загрузки\n // (async-сторы и обычный клиентский старт).\n if (!synapseStore) return <>{loadingComponent}</>\n\n return (\n <SynapseContext.Provider value={synapseStore}>\n <Component {...(restProps as PropsWithChildren<SelfComponentProps>)} ref={ref} />\n </SynapseContext.Provider>\n )\n })\n\n const componentName = Component.displayName || Component.name || 'Component'\n WrappedComponent.displayName = `SynapseContext(${componentName})`\n\n // Копируем статические свойства оригинального компонента\n const excludedKeys = new Set(['$$typeof', 'render', 'defaultProps', 'displayName', 'propTypes'])\n Object.keys(Component).forEach((key) => {\n if (!excludedKeys.has(key)) {\n ;(WrappedComponent as any)[key] = (Component as any)[key]\n }\n })\n\n return WrappedComponent as ComponentType<SelfComponentProps & { dehydratedState?: TState }>\n }\n\n const cleanupSynapse = async (): Promise<void> => {\n const instance = clientAwaiter\n clientAwaiter = null\n try {\n instance?.destroy()\n await synapseModule.destroy()\n } catch (error) {\n handleCleanupError('createSynapseCtx: error during Synapse cleanup', error)\n }\n }\n\n return {\n contextSynapse,\n dehydrate,\n useSynapseStorage,\n useSynapseSelectors,\n useSynapseActions,\n useSynapseState$,\n cleanupSynapse,\n }\n}\n"],"names":["createContext","forwardRef","useContext","useEffect","useRef","useState","handleCleanupError","StorageStatus","createSynapseAwaiter","dehydrateModule","ERROR_HOOK_MESSAGE","ERROR_CONTEXT_INIT","createSynapseCtx","synapseModule","options","loadingComponent","ssr","SynapseContext","clientAwaiter","getClientAwaiter","useSynapseStorage","context","Error","useSynapseSelectors","useSynapseActions","useSynapseState$","dehydrate","opts","contextSynapse","Component","WrappedComponent","props","ref","dehydratedState","restProps","treeAwaiterRef","resolveAwaiter","undefined","seedHydration","store","synapseStore","setSynapseStore","error","setError","instance","current","unsubscribeReady","unsubscribeError","err","componentName","excludedKeys","Set","Object","key","cleanupSynapse"],"mappings":"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"}
|