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
package/README.md
CHANGED
|
@@ -15,33 +15,163 @@ npm install synapse-storage
|
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
```typescript
|
|
18
|
-
import { MemoryStorage } from 'synapse-storage/core'
|
|
18
|
+
import { MemoryStorage, Selectors } from 'synapse-storage/core'
|
|
19
|
+
import { Dispatcher } from 'synapse-storage/reactive'
|
|
19
20
|
import { createSynapse } from 'synapse-storage/utils'
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
|
|
22
|
+
class CounterDispatcher extends Dispatcher<{ count: number }> {
|
|
23
|
+
inc = this.action((store) => store.update((s) => { s.count++ }))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class CounterSelectors extends Selectors<{ count: number }> {
|
|
27
|
+
count = this.select((s) => s.count)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const counter = createSynapse(async () => {
|
|
31
|
+
const storage = new MemoryStorage({ name: 'counter', initialState: { count: 0 } })
|
|
32
|
+
return {
|
|
33
|
+
storage,
|
|
34
|
+
dispatcher: new CounterDispatcher(storage),
|
|
35
|
+
selectors: new CounterSelectors(storage),
|
|
36
|
+
}
|
|
30
37
|
})
|
|
31
38
|
```
|
|
32
39
|
|
|
40
|
+
> **Two independent layers.** `synapse-storage/core` is the *State Manager* — reactive
|
|
41
|
+
> storages (`MemoryStorage`/`LocalStorage`/`IndexedDB`) and selectors, usable on their own.
|
|
42
|
+
> On top sits the *Business Logic Layer* — `Dispatcher` / `Effects` / `createSynapse`.
|
|
43
|
+
> `rxjs` and `react` are optional peers: take only what you use.
|
|
44
|
+
|
|
33
45
|
## Key Features
|
|
34
46
|
|
|
35
47
|
- **Sync & Async Storage** — MemoryStorage, LocalStorage (synchronous), IndexedDB (async) with unified API
|
|
36
48
|
- **Selectors** — memoized computed values with dependency tracking
|
|
37
49
|
- **Immer-like Updates** — mutate state directly inside `update()` callbacks
|
|
38
50
|
- **API Client** — HTTP client with tag-based caching and invalidation
|
|
51
|
+
- **Persist Migrations** — `version` + `migrate(oldState, oldVersion)` for localStorage/IndexedDB
|
|
52
|
+
- **SSR Hydration** — `storage.hydrate(state)` to seed server-rendered state
|
|
39
53
|
- **React Integration** — hooks on `useSyncExternalStore` (Concurrent Mode safe)
|
|
40
54
|
- **RxJS Effects** — dispatchers, effects, and watchers (Redux-Observable style)
|
|
41
|
-
- **Middleware
|
|
55
|
+
- **Middleware** — extensible sync/async pipelines (batching, shallowCompare, logger, broadcast)
|
|
42
56
|
- **EventBus** — decoupled inter-module communication with wildcards
|
|
43
57
|
- **Cross-tab Sync** — BroadcastChannel middleware for multi-tab state
|
|
44
58
|
|
|
59
|
+
## Class-based modules
|
|
60
|
+
|
|
61
|
+
A module is four thin classes over the same engines. Action / selector names come from
|
|
62
|
+
**field names**, API lifecycles are **callable groups**, and assembly is a **lazy singleton
|
|
63
|
+
handle**.
|
|
64
|
+
|
|
65
|
+
> **v5 note.** The functional API (`createSynapse(config)`, `defineAction`,
|
|
66
|
+
> `createDispatcher`, `createApiActions`, `createSelectorsFn`) was removed in **v5.0.0**.
|
|
67
|
+
> Class-based modules are the only form. On v4.x both forms coexist — see the migration
|
|
68
|
+
> table below.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { Dispatcher, Effects, ofType, validateMap, fromRequest, apiResult } from 'synapse-storage/reactive'
|
|
72
|
+
import { Selectors, MemoryStorage } from 'synapse-storage/core'
|
|
73
|
+
import { createSynapse } from 'synapse-storage'
|
|
74
|
+
|
|
75
|
+
// — Dispatcher: action name = field name. apiActions returns a CALLABLE group —
|
|
76
|
+
class PostsDispatcher extends Dispatcher<PostsState> {
|
|
77
|
+
// d.loadPosts(params) = init intent; d.loadPosts.loading/.success/.failure/.reset = lifecycle
|
|
78
|
+
loadPosts = this.apiActions<PostsFindAllParams>((s) => s.api.postsRequest)
|
|
79
|
+
mounted = this.signal<FeedPayload>('Feed mounted') // pure signal
|
|
80
|
+
applyPosts = this.action((store, page: PostsPage) => // (storage, params) => result
|
|
81
|
+
store.update((s) => { s.list = page.data }))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// — Selectors: eager fields, cross-store deps via constructor —
|
|
85
|
+
class PostsSelectors extends Selectors<PostsState> {
|
|
86
|
+
constructor(storage: IStorage<PostsState>, private core: CoreSelectors) { super(storage) }
|
|
87
|
+
private readonly api = this.select((s) => s.api) // private = intermediate
|
|
88
|
+
list = this.select((s) => s.list)
|
|
89
|
+
isPostsLoading = this.combine([this.api], (a) => a.postsRequest.status === 'loading')
|
|
90
|
+
currentUserId = this.combine([this.core.profile], (p) => p?.id ?? null) // cross-store
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// — Effects: services/external stores via constructor, captured in the closure —
|
|
94
|
+
class PostsEffects extends Effects<PostsState, PostsDispatcher> {
|
|
95
|
+
constructor(private api: PostsEndpoints) { super() }
|
|
96
|
+
load = this.effect((action$, state$, { dispatcher: d }) =>
|
|
97
|
+
action$.pipe(
|
|
98
|
+
ofType(d.loadPosts), // catches ONLY init
|
|
99
|
+
validateMap({
|
|
100
|
+
loadingAction: () => d.loadPosts.loading(),
|
|
101
|
+
errorAction: (e) => d.loadPosts.failure(String(e)),
|
|
102
|
+
apiCall: ([action]) => fromRequest(this.api.getPosts.request(action.payload)).pipe(
|
|
103
|
+
apiResult((page) => { d.applyPosts(page); d.loadPosts.success() }),
|
|
104
|
+
),
|
|
105
|
+
}),
|
|
106
|
+
))
|
|
107
|
+
override onDestroy() { /* close sockets etc. */ }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// — Assembly: lazy singleton handle. Factory runs once on first await/ready(), not on import —
|
|
111
|
+
export const postsSynapse = createSynapse(async () => {
|
|
112
|
+
const core = await coreSynapse // handle is thenable
|
|
113
|
+
const storage = new MemoryStorage<PostsState>({ name: 'posts', initialState })
|
|
114
|
+
return {
|
|
115
|
+
storage,
|
|
116
|
+
dependencies: [core],
|
|
117
|
+
dispatcher: new PostsDispatcher(storage),
|
|
118
|
+
selectors: new PostsSelectors(storage, core.selectors),
|
|
119
|
+
effects: new PostsEffects(api),
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
const { storage, state$, dispatcher, actions, selectors } = await postsSynapse
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Rules to keep in mind
|
|
127
|
+
|
|
128
|
+
- **`ofType(d.loadPosts)` matches only `init`.** To react to a result, listen explicitly:
|
|
129
|
+
`ofType(d.loadPosts.success)`.
|
|
130
|
+
- **Services only in closures.** A constructor service (`this.api`) may be captured inside
|
|
131
|
+
the `this.effect(fn)` recipe, but not dereferenced in a field initializer — parameter
|
|
132
|
+
properties are assigned *after* derived-class field initializers run.
|
|
133
|
+
- **Reserved field names** (`storage`, `action$`, `actions`, `dispatch`, `watchers`, `use`,
|
|
134
|
+
`destroy`) cannot be used for actions; a field-alias (one action under two names) is
|
|
135
|
+
rejected at finalization with a clear error.
|
|
136
|
+
- **Cross-store eager selectors** require `useDefineForClassFields: false` (so field
|
|
137
|
+
initializers run after parameter-property assignment), or initialize those selectors in
|
|
138
|
+
the constructor body.
|
|
139
|
+
|
|
140
|
+
### React
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
import { createSynapseCtx, useObservable, useSubscription } from 'synapse-storage/react'
|
|
144
|
+
|
|
145
|
+
// Pass the handle (not a call) — factory starts lazily on first Provider mount:
|
|
146
|
+
export const { contextSynapse: withPosts, useSynapseSelectors, useSynapseActions } =
|
|
147
|
+
createSynapseCtx(postsSynapse, { loadingComponent: <Spinner /> })
|
|
148
|
+
|
|
149
|
+
// Reactive reads straight in the component (write still goes through actions):
|
|
150
|
+
const debounced = useObservable(() => selectors.searchQuery.$.pipe(debounceTime(300), distinctUntilChanged()),
|
|
151
|
+
'',
|
|
152
|
+
[selectors],
|
|
153
|
+
)
|
|
154
|
+
useSubscription(() => selectors.lastId.$.pipe(skip(1), tap(scrollToEnd)), [selectors])
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Migration from v4 (functional → class-based)
|
|
158
|
+
|
|
159
|
+
On v4.x the migration is mechanical and per-file — convert one module, leave the rest on
|
|
160
|
+
the old form (cross-dependencies stay compatible). In v5.0.0 the functional form is gone.
|
|
161
|
+
See the full `pokemon-class` example in `packages/examples` (next to the functional
|
|
162
|
+
`pokemon-advanced`).
|
|
163
|
+
|
|
164
|
+
| Old (functional) | New (class-based) |
|
|
165
|
+
|-------------------------------------------------------------|-----------------------------------------------------------|
|
|
166
|
+
| `defineAction<S>()` + `createDispatcher(...)` registry | fields on `class extends Dispatcher<S>` |
|
|
167
|
+
| `createApiActions` flattened into 5 keys by hand | one `this.apiActions(accessor)` callable group |
|
|
168
|
+
| `dispatcher.dispatch.loadPostsLoading()` | `d.loadPosts.loading()` |
|
|
169
|
+
| `createSelectorsFn: (s) => ({ ... })` | fields on `class extends Selectors<S>` |
|
|
170
|
+
| external selectors typed twice (value + manual type) | a constructor parameter (`private core: CoreSelectors`) |
|
|
171
|
+
| 6-slot `Effect<...>` generics + `services`/`externalStates` | `class extends Effects<S, D, Ext?>`, deps via constructor |
|
|
172
|
+
| `createFeatureSynapse` userland wrapper | built-in lazy handle from `createSynapse(factory)` |
|
|
173
|
+
| `createSynapseCtx(getPostsSynapse())` (eager on import) | `createSynapseCtx(postsSynapse)` (lazy handle) |
|
|
174
|
+
|
|
45
175
|
## Documentation
|
|
46
176
|
|
|
47
177
|
Full documentation, API reference, and examples available on [GitHub](https://github.com/Vlad92msk/synapse).
|
package/dist/_utils/index.d.ts
CHANGED
package/dist/api/api.module.d.ts
CHANGED
|
@@ -12,22 +12,22 @@ export interface EndpointClassOptions<RequestParams extends Record<string, any>,
|
|
|
12
12
|
baseQueryConfig: CreateApiClientOptions['baseQuery'];
|
|
13
13
|
}
|
|
14
14
|
export declare class EndpointClass<RequestParams extends Record<string, any>, RequestResponse> implements EndpointType<RequestParams, RequestResponse> {
|
|
15
|
-
private
|
|
15
|
+
private endpointSubscribers;
|
|
16
16
|
/** Сколько раз был вызван метод request */
|
|
17
17
|
fetchCounts: number;
|
|
18
18
|
meta: EndpointType['meta'];
|
|
19
|
-
private
|
|
20
|
-
private
|
|
21
|
-
private
|
|
22
|
-
private
|
|
23
|
-
private
|
|
24
|
-
private
|
|
25
|
-
private
|
|
19
|
+
private name;
|
|
20
|
+
private queryStorage;
|
|
21
|
+
private configCurrentEndpoint;
|
|
22
|
+
private cacheableHeaderKeys;
|
|
23
|
+
private globalRetryConfig;
|
|
24
|
+
private baseQueryConfig;
|
|
25
|
+
private queryFunction;
|
|
26
26
|
/** Массив заголовков, которые нужно включить в ключ кэширования */
|
|
27
|
-
private
|
|
28
|
-
private
|
|
27
|
+
private cacheableHeaders;
|
|
28
|
+
private prepareHeaders;
|
|
29
29
|
/** Карта in-flight запросов для дедупликации (cacheKey → Promise) */
|
|
30
|
-
private
|
|
30
|
+
private inflightRequests;
|
|
31
31
|
constructor(options: EndpointClassOptions<RequestParams, RequestResponse>);
|
|
32
32
|
request(params: RequestParams, options?: QueryOptions): RequestResponseModify<RequestResponse>;
|
|
33
33
|
/**
|
|
@@ -54,4 +54,3 @@ export declare class EndpointClass<RequestParams extends Record<string, any>, Re
|
|
|
54
54
|
reset(): Promise<void>;
|
|
55
55
|
destroy(): void;
|
|
56
56
|
}
|
|
57
|
-
//# sourceMappingURL=endpoint.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api/components/endpoint.js","sources":["../../../src/api/components/endpoint.ts"],"sourcesContent":["import { CreateApiClientOptions, RetryConfig } from '../types/api.interface'\nimport { Endpoint as EndpointType, EndpointConfig, EndpointState, RequestResponseModify, RequestState } from '../types/endpoint.interface'\nimport { QueryOptions, QueryResult, Unsubscribe } from '../types/query.interface'\nimport { createUniqueId, headersToObject } from '../utils/api-helpers'\nimport { createHeaderContext } from '../utils/create-header-context'\nimport { createPrepareHeaders, prepareRequestHeaders } from '../utils/endpoint-headers'\nimport { fetchBaseQuery } from '../utils/fetch-base-query'\nimport { getCacheableHeaders } from '../utils/get-cacheable-headers'\nimport { QueryStorage } from './query-storage'\n\n/** HTTP-статусы, при которых делать retry по умолчанию */\nconst DEFAULT_RETRY_ON = [0, 408, 429, 500, 502, 503, 504]\n\nexport interface EndpointClassOptions<RequestParams extends Record<string, any>, RequestResponse> {\n name: string\n queryStorage: QueryStorage\n config: EndpointConfig<RequestParams, RequestResponse>\n cacheableHeaderKeys: CreateApiClientOptions['cacheableHeaderKeys']\n globalCacheConfig: CreateApiClientOptions['cache']\n globalRetryConfig: CreateApiClientOptions['retry']\n baseQueryConfig: CreateApiClientOptions['baseQuery']\n}\n\nexport class EndpointClass<RequestParams extends Record<string, any>, RequestResponse> implements EndpointType<RequestParams, RequestResponse> {\n private readonly endpointSubscribers = new Set<(state: EndpointState) => void>()\n\n /** Сколько раз был вызван метод request */\n fetchCounts: number = 0\n\n meta: EndpointType['meta'] = {\n cache: false,\n invalidatesTags: [],\n name: '',\n tags: [],\n }\n\n private readonly name: string\n private readonly queryStorage: QueryStorage\n private readonly configCurrentEndpoint: EndpointConfig<RequestParams, RequestResponse>\n private readonly cacheableHeaderKeys: CreateApiClientOptions['cacheableHeaderKeys']\n private readonly globalRetryConfig: CreateApiClientOptions['retry']\n private readonly baseQueryConfig: CreateApiClientOptions['baseQuery']\n\n private readonly queryFunction: ReturnType<typeof fetchBaseQuery>\n\n /** Массив заголовков, которые нужно включить в ключ кэширования */\n private readonly cacheableHeaders: string[]\n\n private readonly prepareHeaders: ReturnType<typeof createPrepareHeaders>\n\n /** Карта in-flight запросов для дедупликации (cacheKey → Promise) */\n private readonly inflightRequests = new Map<string, Promise<QueryResult<RequestResponse, Error>>>()\n\n constructor(options: EndpointClassOptions<RequestParams, RequestResponse>) {\n this.name = options.name\n this.queryStorage = options.queryStorage\n this.configCurrentEndpoint = options.config\n this.cacheableHeaderKeys = options.cacheableHeaderKeys\n this.globalRetryConfig = options.globalRetryConfig\n this.baseQueryConfig = options.baseQueryConfig\n\n // 1. Создаем функцию подготовки заголовков\n this.prepareHeaders = createPrepareHeaders(this.baseQueryConfig.prepareHeaders, this.configCurrentEndpoint.prepareHeaders)\n // 2. Создаем функцию исполнения запроса\n this.queryFunction = fetchBaseQuery({\n baseUrl: this.baseQueryConfig.baseUrl,\n fetchFn: this.baseQueryConfig.fetchFn,\n timeout: this.baseQueryConfig.timeout,\n credentials: this.baseQueryConfig.credentials,\n })\n // 3. Создаем массив тех заголовков, которые нужно включить в ключ кэширования\n this.cacheableHeaders = [...(this.cacheableHeaderKeys || []), ...(this.configCurrentEndpoint.includeCacheableHeaderKeys || [])].filter(\n (key) => !this.configCurrentEndpoint.excludeCacheableHeaderKeys?.includes(key),\n )\n // 4. Сохраняем информацию в meta\n this.meta.name = this.name\n this.meta.tags = this.configCurrentEndpoint.tags ?? this.meta.tags\n this.meta.invalidatesTags = this.configCurrentEndpoint.invalidatesTags ?? this.meta.invalidatesTags\n this.meta.cache = this.queryStorage.createCacheConfig(this.configCurrentEndpoint) ?? this.meta.cache\n }\n\n public request(params: RequestParams, options?: QueryOptions): RequestResponseModify<RequestResponse> {\n // 1. Подготовка и инициализация\n this.fetchCounts++\n const requestId = createUniqueId(this.name)\n const controller = new AbortController()\n const requestSubscribers = new Set<(state: RequestState<RequestResponse, RequestParams>) => void>()\n const currentState: RequestState<RequestResponse, RequestParams> = {\n status: 'idle',\n requestParams: params,\n headers: {},\n error: undefined,\n data: undefined,\n fromCache: false,\n }\n\n // Связываем пользовательский signal с внутренним controller\n if (options?.signal) {\n if (options.signal.aborted) {\n controller.abort()\n } else {\n options.signal.addEventListener('abort', () => controller.abort(), { once: true })\n }\n }\n\n // 2. Функция нотификации подписчиков запроса\n const notifyRequestSubscribers = (newState: Partial<RequestState<RequestResponse, RequestParams>>) => {\n Object.assign(currentState, newState)\n requestSubscribers.forEach((cb) => cb({ ...currentState }))\n }\n\n // 3. Запускаем выполнение запроса\n const waitPromise = this.executeRequest(params, options, controller, notifyRequestSubscribers)\n\n // 4. Возвращаем объект с методами управления запросом\n return {\n id: requestId,\n\n subscribe(listener, subscribeOptions = {}) {\n const { autoUnsubscribe = true } = subscribeOptions\n requestSubscribers.add(listener)\n listener(currentState)\n\n const unsubscribe = () => requestSubscribers.delete(listener)\n\n if (autoUnsubscribe) {\n waitPromise.finally(() => unsubscribe())\n }\n\n return unsubscribe\n },\n\n wait: () => waitPromise,\n\n waitWithCallbacks(handlers = {}) {\n const { idle, loading, success, error } = handlers\n\n this.subscribe(\n (state: RequestState<RequestResponse, RequestParams>) => {\n switch (state.status) {\n case 'idle':\n idle?.(state)\n break\n case 'loading':\n loading?.(state)\n break\n case 'success':\n success?.(state.data, state)\n break\n case 'error':\n error?.(state.error, state)\n break\n }\n },\n { autoUnsubscribe: true },\n )\n\n return waitPromise\n },\n\n abort: () => {\n if (!controller.signal.aborted) {\n controller.abort()\n }\n },\n\n then: (onfulfilled, onrejected) => waitPromise.then(onfulfilled, onrejected),\n catch: (onrejected) => waitPromise.catch(onrejected),\n finally: (onfinally) => waitPromise.finally(onfinally),\n }\n }\n\n /**\n * Определяет итоговую конфигурацию retry: вызов → эндпоинт → глобальная\n */\n private resolveRetryConfig(options?: QueryOptions): RetryConfig | undefined {\n return options?.retry ?? this.configCurrentEndpoint.retry ?? this.globalRetryConfig\n }\n\n /**\n * Выполняет сетевой запрос с кэшированием, дедупликацией и retry\n */\n private async executeRequest(\n params: RequestParams,\n options: QueryOptions | undefined,\n controller: AbortController,\n notify: (state: Partial<RequestState<RequestResponse, RequestParams>>) => void,\n ): Promise<QueryResult<RequestResponse, Error>> {\n const headerContext = createHeaderContext({ requestParams: params }, options?.context || {})\n\n try {\n // 1. Формируем заголовки\n const headers = await prepareRequestHeaders(this.prepareHeaders, headerContext)\n const headersForCache = getCacheableHeaders(headers, options?.cacheableHeaderKeys ? options.cacheableHeaderKeys : this.cacheableHeaders)\n\n // 2. Формируем requestDefinition для определения метода\n const requestDefinition = this.configCurrentEndpoint.request(params, options?.context)\n\n // 3. Проверяем кэширование (с учётом HTTP-метода)\n const shouldCache = this.queryStorage.shouldCache(this.configCurrentEndpoint, options, requestDefinition.method)\n const [cacheKey, cacheParams] = this.queryStorage.createCacheKey(this.name, { ...params, ...headersForCache })\n const cacheKeyStr = String(cacheKey)\n\n // 4. Проверяем кэш\n if (shouldCache) {\n const cachedResult = await this.queryStorage.getCachedResult<QueryResult<RequestResponse>>(cacheKey)\n if (cachedResult) {\n notify({\n fromCache: true,\n status: 'success',\n data: cachedResult.data,\n error: undefined,\n headers: cachedResult.headers,\n requestParams: params,\n })\n return { ...cachedResult, fromCache: true }\n }\n }\n\n // 5. Дедупликация: если запрос с таким же ключом уже летит — ждём его\n if (shouldCache && this.inflightRequests.has(cacheKeyStr)) {\n notify({ fromCache: false, status: 'loading' })\n const result = await this.inflightRequests.get(cacheKeyStr)!\n if (!result.ok) {\n notify({\n fromCache: true,\n status: 'error',\n data: undefined,\n error: result.error,\n headers: result.headers,\n requestParams: params,\n })\n return { ...result, fromCache: true }\n }\n notify({\n fromCache: true,\n status: 'success',\n data: result.data,\n error: undefined,\n headers: result.headers,\n requestParams: params,\n })\n return { ...result, fromCache: true }\n }\n\n // 6. Выполняем запрос (с retry и post-processing)\n notify({ fromCache: false, status: 'loading' })\n\n const retryConfig = this.resolveRetryConfig(options)\n const fetchPromise = this.executeFetch(requestDefinition, options, controller, headers, retryConfig, shouldCache, cacheKey, cacheParams ?? {})\n\n // Регистрируем в inflight для дедупликации (только для кэшируемых)\n if (shouldCache) {\n this.inflightRequests.set(cacheKeyStr, fetchPromise)\n fetchPromise.finally(() => this.inflightRequests.delete(cacheKeyStr)).catch(() => {})\n }\n\n const response = await fetchPromise\n\n // 7. Обрабатываем результат\n if (response.ok) {\n notify({\n fromCache: false,\n status: 'success',\n data: response.data,\n error: undefined,\n headers: response.headers,\n requestParams: params,\n })\n this.notifyEndpointSubscribers('success')\n return { ...response, fromCache: false }\n } else {\n // invalidateOnError: инвалидируем кэш при ошибке если включено\n if (shouldCache) {\n const cacheConfig = this.queryStorage.createCacheConfig(this.configCurrentEndpoint)\n if (cacheConfig.invalidateOnError !== false) {\n await this.queryStorage.invalidateCache(cacheKey)\n }\n }\n\n notify({\n fromCache: false,\n status: 'error',\n data: undefined,\n error: response.error,\n headers: response.headers,\n requestParams: params,\n })\n this.notifyEndpointSubscribers('error', response.error)\n throw response.error\n }\n } catch (error) {\n notify({\n fromCache: false,\n status: 'error',\n data: undefined,\n error: error as Error,\n headers: undefined,\n requestParams: params,\n })\n throw error\n }\n }\n\n /**\n * Выполняет HTTP-запрос с retry, инвалидацией тегов и кэшированием результата\n */\n private async executeFetch(\n requestDefinition: ReturnType<EndpointConfig<RequestParams, RequestResponse>['request']>,\n options: QueryOptions | undefined,\n controller: AbortController,\n headers: Headers,\n retryConfig: RetryConfig | undefined,\n shouldCache: boolean,\n cacheKey: ReturnType<QueryStorage['createCacheKey']>[0],\n cacheParams: Record<string, any>,\n ): Promise<QueryResult<RequestResponse, Error>> {\n // Выполняем HTTP-запрос (с retry если настроен)\n const response = await this.fetchWithRetry(requestDefinition, options, controller, headers, retryConfig)\n\n // Post-processing при успешном ответе\n if (response.ok) {\n const { headers: responseHeaders, ...restResponse } = response\n\n // Инвалидируем кэш по тегам\n if (this.configCurrentEndpoint.invalidatesTags?.length) {\n await this.queryStorage.invalidateCacheByTags(this.configCurrentEndpoint.invalidatesTags)\n }\n\n // Сохраняем в кэш\n if (shouldCache) {\n const currentCacheConfig = this.queryStorage.createCacheConfig(this.configCurrentEndpoint)\n await this.queryStorage.setCachedResult(\n cacheKey,\n { ...restResponse, headers: headersToObject(responseHeaders) },\n currentCacheConfig,\n cacheParams,\n this.configCurrentEndpoint.tags ?? [],\n )\n }\n }\n\n return response\n }\n\n /**\n * Выполняет HTTP-запрос с повторными попытками\n */\n private async fetchWithRetry(\n requestDefinition: ReturnType<EndpointConfig<RequestParams, RequestResponse>['request']>,\n options: QueryOptions | undefined,\n controller: AbortController,\n headers: Headers,\n retryConfig?: RetryConfig,\n ): Promise<QueryResult<RequestResponse, Error>> {\n const maxAttempts = (retryConfig?.count ?? 0) + 1\n const retryOn = retryConfig?.retryOn ?? DEFAULT_RETRY_ON\n const getDelay = (attempt: number): number => {\n if (typeof retryConfig?.delay === 'function') return retryConfig.delay(attempt)\n return retryConfig?.delay ?? 1000\n }\n\n let lastResponse!: QueryResult<RequestResponse, Error>\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n // Если запрос отменён — бросаем ошибку (перехватывается в executeRequest)\n if (controller.signal.aborted) throw new DOMException('The operation was aborted.', 'AbortError')\n\n const mergedOptions: QueryOptions = { ...options, signal: controller.signal }\n lastResponse = await this.queryFunction<RequestResponse, RequestParams>(requestDefinition, mergedOptions, headers)\n\n // Успех или не-retryable статус — возвращаем сразу\n if (lastResponse.ok || !retryOn.includes(lastResponse.status) || attempt === maxAttempts - 1) {\n return lastResponse\n }\n\n // Ждём перед следующей попыткой\n const delay = getDelay(attempt)\n await new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, delay)\n // Если запрос отменили во время ожидания — прерываем delay\n controller.signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer)\n resolve()\n },\n { once: true },\n )\n })\n }\n\n return lastResponse\n }\n\n /**\n * Уведомляет подписчиков эндпоинта об изменении состояния\n */\n private notifyEndpointSubscribers(status: 'success' | 'error', error?: Error): void {\n const endpointState: EndpointState = {\n status,\n fetchCounts: this.fetchCounts,\n meta: this.meta,\n cacheableHeaders: this.cacheableHeaders,\n error,\n }\n this.endpointSubscribers.forEach((cb) => cb(endpointState))\n }\n\n public subscribe(cb: (state: EndpointState) => void): Unsubscribe {\n this.endpointSubscribers.add(cb)\n\n const currentState: EndpointState = {\n status: 'idle',\n fetchCounts: this.fetchCounts,\n meta: this.meta,\n cacheableHeaders: this.cacheableHeaders,\n error: undefined,\n }\n\n cb(currentState)\n return () => this.endpointSubscribers.delete(cb)\n }\n\n public async reset() {\n this.fetchCounts = 0\n\n // Инвалидируем кэш по тегам эндпоинта\n if (this.meta.tags.length) {\n await this.queryStorage.invalidateCacheByTags(this.meta.tags)\n }\n }\n\n public destroy() {\n this.endpointSubscribers.clear()\n this.inflightRequests.clear()\n }\n}\n"],"names":["createUniqueId","headersToObject","createHeaderContext","createPrepareHeaders","prepareRequestHeaders","fetchBaseQuery","getCacheableHeaders","DEFAULT_RETRY_ON","EndpointClass","Set","Map","options","key","params","requestId","controller","AbortController","requestSubscribers","currentState","undefined","notifyRequestSubscribers","newState","Object","cb","waitPromise","listener","subscribeOptions","autoUnsubscribe","unsubscribe","handlers","idle","loading","success","error","state","onfulfilled","onrejected","onfinally","notify","headerContext","headers","headersForCache","requestDefinition","shouldCache","cacheKey","cacheParams","cacheKeyStr","String","cachedResult","result","retryConfig","fetchPromise","response","cacheConfig","responseHeaders","restResponse","currentCacheConfig","maxAttempts","retryOn","getDelay","attempt","lastResponse","DOMException","mergedOptions","delay","Promise","resolve","timer","setTimeout","clearTimeout","status","endpointState"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api/components/endpoint.js","sources":["../../../src/api/components/endpoint.ts"],"sourcesContent":["import { CreateApiClientOptions, RetryConfig } from '../types/api.interface'\nimport { Endpoint as EndpointType, EndpointConfig, EndpointState, RequestResponseModify, RequestState } from '../types/endpoint.interface'\nimport { QueryOptions, QueryResult, Unsubscribe } from '../types/query.interface'\nimport { createUniqueId, headersToObject } from '../utils/api-helpers'\nimport { createHeaderContext } from '../utils/create-header-context'\nimport { createPrepareHeaders, prepareRequestHeaders } from '../utils/endpoint-headers'\nimport { fetchBaseQuery } from '../utils/fetch-base-query'\nimport { getCacheableHeaders } from '../utils/get-cacheable-headers'\nimport { QueryStorage } from './query-storage'\n\n/** HTTP-статусы, при которых делать retry по умолчанию */\nconst DEFAULT_RETRY_ON = [0, 408, 429, 500, 502, 503, 504]\n\nexport interface EndpointClassOptions<RequestParams extends Record<string, any>, RequestResponse> {\n name: string\n queryStorage: QueryStorage\n config: EndpointConfig<RequestParams, RequestResponse>\n cacheableHeaderKeys: CreateApiClientOptions['cacheableHeaderKeys']\n globalCacheConfig: CreateApiClientOptions['cache']\n globalRetryConfig: CreateApiClientOptions['retry']\n baseQueryConfig: CreateApiClientOptions['baseQuery']\n}\n\nexport class EndpointClass<RequestParams extends Record<string, any>, RequestResponse> implements EndpointType<RequestParams, RequestResponse> {\n private endpointSubscribers = new Set<(state: EndpointState) => void>()\n\n /** Сколько раз был вызван метод request */\n fetchCounts: number = 0\n\n meta: EndpointType['meta'] = {\n cache: false,\n invalidatesTags: [],\n name: '',\n tags: [],\n }\n\n private name: string\n private queryStorage: QueryStorage\n private configCurrentEndpoint: EndpointConfig<RequestParams, RequestResponse>\n private cacheableHeaderKeys: CreateApiClientOptions['cacheableHeaderKeys']\n private globalRetryConfig: CreateApiClientOptions['retry']\n private baseQueryConfig: CreateApiClientOptions['baseQuery']\n\n private queryFunction: ReturnType<typeof fetchBaseQuery>\n\n /** Массив заголовков, которые нужно включить в ключ кэширования */\n private cacheableHeaders: string[]\n\n private prepareHeaders: ReturnType<typeof createPrepareHeaders>\n\n /** Карта in-flight запросов для дедупликации (cacheKey → Promise) */\n private inflightRequests = new Map<string, Promise<QueryResult<RequestResponse, Error>>>()\n\n constructor(options: EndpointClassOptions<RequestParams, RequestResponse>) {\n this.name = options.name\n this.queryStorage = options.queryStorage\n this.configCurrentEndpoint = options.config\n this.cacheableHeaderKeys = options.cacheableHeaderKeys\n this.globalRetryConfig = options.globalRetryConfig\n this.baseQueryConfig = options.baseQueryConfig\n\n // 1. Создаем функцию подготовки заголовков\n this.prepareHeaders = createPrepareHeaders(this.baseQueryConfig.prepareHeaders, this.configCurrentEndpoint.prepareHeaders)\n // 2. Создаем функцию исполнения запроса\n this.queryFunction = fetchBaseQuery({\n baseUrl: this.baseQueryConfig.baseUrl,\n fetchFn: this.baseQueryConfig.fetchFn,\n timeout: this.baseQueryConfig.timeout,\n credentials: this.baseQueryConfig.credentials,\n })\n // 3. Создаем массив тех заголовков, которые нужно включить в ключ кэширования\n this.cacheableHeaders = [...(this.cacheableHeaderKeys || []), ...(this.configCurrentEndpoint.includeCacheableHeaderKeys || [])].filter(\n (key) => !this.configCurrentEndpoint.excludeCacheableHeaderKeys?.includes(key),\n )\n // 4. Сохраняем информацию в meta\n this.meta.name = this.name\n this.meta.tags = this.configCurrentEndpoint.tags ?? this.meta.tags\n this.meta.invalidatesTags = this.configCurrentEndpoint.invalidatesTags ?? this.meta.invalidatesTags\n this.meta.cache = this.queryStorage.createCacheConfig(this.configCurrentEndpoint) ?? this.meta.cache\n }\n\n public request(params: RequestParams, options?: QueryOptions): RequestResponseModify<RequestResponse> {\n // 1. Подготовка и инициализация\n this.fetchCounts++\n const requestId = createUniqueId(this.name)\n const controller = new AbortController()\n const requestSubscribers = new Set<(state: RequestState<RequestResponse, RequestParams>) => void>()\n const currentState: RequestState<RequestResponse, RequestParams> = {\n status: 'idle',\n requestParams: params,\n headers: {},\n error: undefined,\n data: undefined,\n fromCache: false,\n }\n\n // Связываем пользовательский signal с внутренним controller\n if (options?.signal) {\n if (options.signal.aborted) {\n controller.abort()\n } else {\n options.signal.addEventListener('abort', () => controller.abort(), { once: true })\n }\n }\n\n // 2. Функция нотификации подписчиков запроса\n const notifyRequestSubscribers = (newState: Partial<RequestState<RequestResponse, RequestParams>>) => {\n Object.assign(currentState, newState)\n requestSubscribers.forEach((cb) => cb({ ...currentState }))\n }\n\n // 3. Запускаем выполнение запроса\n const waitPromise = this.executeRequest(params, options, controller, notifyRequestSubscribers)\n\n // 4. Возвращаем объект с методами управления запросом\n return {\n id: requestId,\n\n subscribe(listener, subscribeOptions = {}) {\n const { autoUnsubscribe = true } = subscribeOptions\n requestSubscribers.add(listener)\n listener(currentState)\n\n const unsubscribe = () => requestSubscribers.delete(listener)\n\n if (autoUnsubscribe) {\n waitPromise.finally(() => unsubscribe())\n }\n\n return unsubscribe\n },\n\n wait: () => waitPromise,\n\n waitWithCallbacks(handlers = {}) {\n const { idle, loading, success, error } = handlers\n\n this.subscribe(\n (state: RequestState<RequestResponse, RequestParams>) => {\n switch (state.status) {\n case 'idle':\n idle?.(state)\n break\n case 'loading':\n loading?.(state)\n break\n case 'success':\n success?.(state.data, state)\n break\n case 'error':\n error?.(state.error, state)\n break\n }\n },\n { autoUnsubscribe: true },\n )\n\n return waitPromise\n },\n\n abort: () => {\n if (!controller.signal.aborted) {\n controller.abort()\n }\n },\n\n then: (onfulfilled, onrejected) => waitPromise.then(onfulfilled, onrejected),\n catch: (onrejected) => waitPromise.catch(onrejected),\n finally: (onfinally) => waitPromise.finally(onfinally),\n }\n }\n\n /**\n * Определяет итоговую конфигурацию retry: вызов → эндпоинт → глобальная\n */\n private resolveRetryConfig(options?: QueryOptions): RetryConfig | undefined {\n return options?.retry ?? this.configCurrentEndpoint.retry ?? this.globalRetryConfig\n }\n\n /**\n * Выполняет сетевой запрос с кэшированием, дедупликацией и retry\n */\n private async executeRequest(\n params: RequestParams,\n options: QueryOptions | undefined,\n controller: AbortController,\n notify: (state: Partial<RequestState<RequestResponse, RequestParams>>) => void,\n ): Promise<QueryResult<RequestResponse, Error>> {\n const headerContext = createHeaderContext({ requestParams: params }, options?.context || {})\n\n try {\n // 1. Формируем заголовки\n const headers = await prepareRequestHeaders(this.prepareHeaders, headerContext)\n const headersForCache = getCacheableHeaders(headers, options?.cacheableHeaderKeys ? options.cacheableHeaderKeys : this.cacheableHeaders)\n\n // 2. Формируем requestDefinition для определения метода\n const requestDefinition = this.configCurrentEndpoint.request(params, options?.context)\n\n // 3. Проверяем кэширование (с учётом HTTP-метода)\n const shouldCache = this.queryStorage.shouldCache(this.configCurrentEndpoint, options, requestDefinition.method)\n const [cacheKey, cacheParams] = this.queryStorage.createCacheKey(this.name, { ...params, ...headersForCache })\n const cacheKeyStr = String(cacheKey)\n\n // 4. Проверяем кэш\n if (shouldCache) {\n const cachedResult = await this.queryStorage.getCachedResult<QueryResult<RequestResponse>>(cacheKey)\n if (cachedResult) {\n notify({\n fromCache: true,\n status: 'success',\n data: cachedResult.data,\n error: undefined,\n headers: cachedResult.headers,\n requestParams: params,\n })\n return { ...cachedResult, fromCache: true }\n }\n }\n\n // 5. Дедупликация: если запрос с таким же ключом уже летит — ждём его\n if (shouldCache && this.inflightRequests.has(cacheKeyStr)) {\n notify({ fromCache: false, status: 'loading' })\n const result = await this.inflightRequests.get(cacheKeyStr)!\n if (!result.ok) {\n notify({\n fromCache: true,\n status: 'error',\n data: undefined,\n error: result.error,\n headers: result.headers,\n requestParams: params,\n })\n return { ...result, fromCache: true }\n }\n notify({\n fromCache: true,\n status: 'success',\n data: result.data,\n error: undefined,\n headers: result.headers,\n requestParams: params,\n })\n return { ...result, fromCache: true }\n }\n\n // 6. Выполняем запрос (с retry и post-processing)\n notify({ fromCache: false, status: 'loading' })\n\n const retryConfig = this.resolveRetryConfig(options)\n const fetchPromise = this.executeFetch(requestDefinition, options, controller, headers, retryConfig, shouldCache, cacheKey, cacheParams ?? {})\n\n // Регистрируем в inflight для дедупликации (только для кэшируемых)\n if (shouldCache) {\n this.inflightRequests.set(cacheKeyStr, fetchPromise)\n fetchPromise.finally(() => this.inflightRequests.delete(cacheKeyStr)).catch(() => {})\n }\n\n const response = await fetchPromise\n\n // 7. Обрабатываем результат\n if (response.ok) {\n notify({\n fromCache: false,\n status: 'success',\n data: response.data,\n error: undefined,\n headers: response.headers,\n requestParams: params,\n })\n this.notifyEndpointSubscribers('success')\n return { ...response, fromCache: false }\n } else {\n // invalidateOnError: инвалидируем кэш при ошибке если включено\n if (shouldCache) {\n const cacheConfig = this.queryStorage.createCacheConfig(this.configCurrentEndpoint)\n if (cacheConfig.invalidateOnError !== false) {\n await this.queryStorage.invalidateCache(cacheKey)\n }\n }\n\n notify({\n fromCache: false,\n status: 'error',\n data: undefined,\n error: response.error,\n headers: response.headers,\n requestParams: params,\n })\n this.notifyEndpointSubscribers('error', response.error)\n throw response.error\n }\n } catch (error) {\n notify({\n fromCache: false,\n status: 'error',\n data: undefined,\n error: error as Error,\n headers: undefined,\n requestParams: params,\n })\n throw error\n }\n }\n\n /**\n * Выполняет HTTP-запрос с retry, инвалидацией тегов и кэшированием результата\n */\n private async executeFetch(\n requestDefinition: ReturnType<EndpointConfig<RequestParams, RequestResponse>['request']>,\n options: QueryOptions | undefined,\n controller: AbortController,\n headers: Headers,\n retryConfig: RetryConfig | undefined,\n shouldCache: boolean,\n cacheKey: ReturnType<QueryStorage['createCacheKey']>[0],\n cacheParams: Record<string, any>,\n ): Promise<QueryResult<RequestResponse, Error>> {\n // Выполняем HTTP-запрос (с retry если настроен)\n const response = await this.fetchWithRetry(requestDefinition, options, controller, headers, retryConfig)\n\n // Post-processing при успешном ответе\n if (response.ok) {\n const { headers: responseHeaders, ...restResponse } = response\n\n // Инвалидируем кэш по тегам\n if (this.configCurrentEndpoint.invalidatesTags?.length) {\n await this.queryStorage.invalidateCacheByTags(this.configCurrentEndpoint.invalidatesTags)\n }\n\n // Сохраняем в кэш\n if (shouldCache) {\n const currentCacheConfig = this.queryStorage.createCacheConfig(this.configCurrentEndpoint)\n await this.queryStorage.setCachedResult(\n cacheKey,\n { ...restResponse, headers: headersToObject(responseHeaders) },\n currentCacheConfig,\n cacheParams,\n this.configCurrentEndpoint.tags ?? [],\n )\n }\n }\n\n return response\n }\n\n /**\n * Выполняет HTTP-запрос с повторными попытками\n */\n private async fetchWithRetry(\n requestDefinition: ReturnType<EndpointConfig<RequestParams, RequestResponse>['request']>,\n options: QueryOptions | undefined,\n controller: AbortController,\n headers: Headers,\n retryConfig?: RetryConfig,\n ): Promise<QueryResult<RequestResponse, Error>> {\n const maxAttempts = (retryConfig?.count ?? 0) + 1\n const retryOn = retryConfig?.retryOn ?? DEFAULT_RETRY_ON\n const getDelay = (attempt: number): number => {\n if (typeof retryConfig?.delay === 'function') return retryConfig.delay(attempt)\n return retryConfig?.delay ?? 1000\n }\n\n let lastResponse!: QueryResult<RequestResponse, Error>\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n // Если запрос отменён — бросаем ошибку (перехватывается в executeRequest)\n if (controller.signal.aborted) throw new DOMException('The operation was aborted.', 'AbortError')\n\n const mergedOptions: QueryOptions = { ...options, signal: controller.signal }\n lastResponse = await this.queryFunction<RequestResponse, RequestParams>(requestDefinition, mergedOptions, headers)\n\n // Успех или не-retryable статус — возвращаем сразу\n if (lastResponse.ok || !retryOn.includes(lastResponse.status) || attempt === maxAttempts - 1) {\n return lastResponse\n }\n\n // Ждём перед следующей попыткой\n const delay = getDelay(attempt)\n await new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, delay)\n // Если запрос отменили во время ожидания — прерываем delay\n controller.signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer)\n resolve()\n },\n { once: true },\n )\n })\n }\n\n return lastResponse\n }\n\n /**\n * Уведомляет подписчиков эндпоинта об изменении состояния\n */\n private notifyEndpointSubscribers(status: 'success' | 'error', error?: Error): void {\n const endpointState: EndpointState = {\n status,\n fetchCounts: this.fetchCounts,\n meta: this.meta,\n cacheableHeaders: this.cacheableHeaders,\n error,\n }\n this.endpointSubscribers.forEach((cb) => cb(endpointState))\n }\n\n public subscribe(cb: (state: EndpointState) => void): Unsubscribe {\n this.endpointSubscribers.add(cb)\n\n const currentState: EndpointState = {\n status: 'idle',\n fetchCounts: this.fetchCounts,\n meta: this.meta,\n cacheableHeaders: this.cacheableHeaders,\n error: undefined,\n }\n\n cb(currentState)\n return () => this.endpointSubscribers.delete(cb)\n }\n\n public async reset() {\n this.fetchCounts = 0\n\n // Инвалидируем кэш по тегам эндпоинта\n if (this.meta.tags.length) {\n await this.queryStorage.invalidateCacheByTags(this.meta.tags)\n }\n }\n\n public destroy() {\n this.endpointSubscribers.clear()\n this.inflightRequests.clear()\n }\n}\n"],"names":["createUniqueId","headersToObject","createHeaderContext","createPrepareHeaders","prepareRequestHeaders","fetchBaseQuery","getCacheableHeaders","DEFAULT_RETRY_ON","EndpointClass","Set","Map","options","key","params","requestId","controller","AbortController","requestSubscribers","currentState","undefined","notifyRequestSubscribers","newState","Object","cb","waitPromise","listener","subscribeOptions","autoUnsubscribe","unsubscribe","handlers","idle","loading","success","error","state","onfulfilled","onrejected","onfinally","notify","headerContext","headers","headersForCache","requestDefinition","shouldCache","cacheKey","cacheParams","cacheKeyStr","String","cachedResult","result","retryConfig","fetchPromise","response","cacheConfig","responseHeaders","restResponse","currentCacheConfig","maxAttempts","retryOn","getDelay","attempt","lastResponse","DOMException","mergedOptions","delay","Promise","resolve","timer","setTimeout","clearTimeout","status","endpointState"],"mappings":";;;;;;;;;;;AAGsE;AACF;AACmB;AAC7B;AACU;AAGpE,wDAAwD,GACxD,MAAMO,gBAAgBA,GAAG;IAAC;IAAG;IAAK;IAAK;IAAK;IAAK;IAAK;CAAI;AAYnD,MAAMC,aAAaA;IAChB,sBAAsB,IAAIC,MAAqC;IAEvE,yCAAyC,GACzC,cAAsB,EAAC;IAEvB,OAA6B;QAC3B,OAAO;QACP,iBAAiB,EAAE;QACnB,MAAM;QACN,MAAM,EAAE;IACV,EAAC;IAEO,KAAY;IACZ,aAA0B;IAC1B,sBAAqE;IACrE,oBAAkE;IAClE,kBAAkD;IAClD,gBAAoD;IAEpD,cAAgD;IAExD,iEAAiE,GACzD,iBAA0B;IAE1B,eAAuD;IAE/D,mEAAmE,GAC3D,mBAAmB,IAAIC,MAA2D;IAE1F,YAAYC,OAA6D,CAAE;QACzE,IAAI,CAAC,IAAI,GAAGA,QAAQ,IAAI;QACxB,IAAI,CAAC,YAAY,GAAGA,QAAQ,YAAY;QACxC,IAAI,CAAC,qBAAqB,GAAGA,QAAQ,MAAM;QAC3C,IAAI,CAAC,mBAAmB,GAAGA,QAAQ,mBAAmB;QACtD,IAAI,CAAC,iBAAiB,GAAGA,QAAQ,iBAAiB;QAClD,IAAI,CAAC,eAAe,GAAGA,QAAQ,eAAe;QAE9C,2CAA2C;QAC3C,IAAI,CAAC,cAAc,GAAGR,oBAAoBA,CAAC,IAAI,CAAC,eAAe,CAAC,cAAc,EAAE,IAAI,CAAC,qBAAqB,CAAC,cAAc;QACzH,wCAAwC;QACxC,IAAI,CAAC,aAAa,GAAGE,cAAcA,CAAC;YAClC,SAAS,IAAI,CAAC,eAAe,CAAC,OAAO;YACrC,SAAS,IAAI,CAAC,eAAe,CAAC,OAAO;YACrC,SAAS,IAAI,CAAC,eAAe,CAAC,OAAO;YACrC,aAAa,IAAI,CAAC,eAAe,CAAC,WAAW;QAC/C;QACA,8EAA8E;QAC9E,IAAI,CAAC,gBAAgB,GAAG;eAAK,IAAI,CAAC,mBAAmB,IAAI,EAAE;eAAO,IAAI,CAAC,qBAAqB,CAAC,0BAA0B,IAAI,EAAE;SAAE,CAAC,MAAM,CACpI,CAACO,MAAQ,CAAC,IAAI,CAAC,qBAAqB,CAAC,0BAA0B,EAAE,SAASA;QAE5E,iCAAiC;QACjC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI;QAC1B,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,qBAAqB,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI;QAClE,IAAI,CAAC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,qBAAqB,CAAC,eAAe,IAAI,IAAI,CAAC,IAAI,CAAC,eAAe;QACnG,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,iBAAiB,CAAC,IAAI,CAAC,qBAAqB,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK;IACtG;IAEO,QAAQC,MAAqB,EAAEF,OAAsB,EAA0C;QACpG,gCAAgC;QAChC,IAAI,CAAC,WAAW;QAChB,MAAMG,YAAYd,cAAcA,CAAC,IAAI,CAAC,IAAI;QAC1C,MAAMe,aAAa,IAAIC;QACvB,MAAMC,qBAAqB,IAAIR;QAC/B,MAAMS,eAA6D;YACjE,QAAQ;YACR,eAAeL;YACf,SAAS,CAAC;YACV,OAAOM;YACP,MAAMA;YACN,WAAW;QACb;QAEA,4DAA4D;QAC5D,IAAIR,SAAS,QAAQ;YACnB,IAAIA,QAAQ,MAAM,CAAC,OAAO,EAAE;gBAC1BI,WAAW,KAAK;YAClB,OAAO;gBACLJ,QAAQ,MAAM,CAAC,gBAAgB,CAAC,SAAS,IAAMI,WAAW,KAAK,IAAI;oBAAE,MAAM;gBAAK;YAClF;QACF;QAEA,6CAA6C;QAC7C,MAAMK,2BAA2B,CAACC;YAChCC,OAAO,MAAM,CAACJ,cAAcG;YAC5BJ,mBAAmB,OAAO,CAAC,CAACM,KAAOA,GAAG;oBAAE,GAAGL,YAAY;gBAAC;QAC1D;QAEA,kCAAkC;QAClC,MAAMM,cAAc,IAAI,CAAC,cAAc,CAACX,QAAQF,SAASI,YAAYK;QAErE,sDAAsD;QACtD,OAAO;YACL,IAAIN;YAEJ,WAAUW,QAAQ,EAAEC,mBAAmB,CAAC,CAAC;gBACvC,MAAM,EAAEC,kBAAkB,IAAI,EAAE,GAAGD;gBACnCT,mBAAmB,GAAG,CAACQ;gBACvBA,SAASP;gBAET,MAAMU,cAAc,IAAMX,mBAAmB,MAAM,CAACQ;gBAEpD,IAAIE,iBAAiB;oBACnBH,YAAY,OAAO,CAAC,IAAMI;gBAC5B;gBAEA,OAAOA;YACT;YAEA,MAAM,IAAMJ;YAEZ,mBAAkBK,WAAW,CAAC,CAAC;gBAC7B,MAAM,EAAEC,IAAI,EAAEC,OAAO,EAAEC,OAAO,EAAEC,KAAK,EAAE,GAAGJ;gBAE1C,IAAI,CAAC,SAAS,CACZ,CAACK;oBACC,OAAQA,MAAM,MAAM;wBAClB,KAAK;4BACHJ,OAAOI;4BACP;wBACF,KAAK;4BACHH,UAAUG;4BACV;wBACF,KAAK;4BACHF,UAAUE,MAAM,IAAI,EAAEA;4BACtB;wBACF,KAAK;4BACHD,QAAQC,MAAM,KAAK,EAAEA;4BACrB;oBACJ;gBACF,GACA;oBAAE,iBAAiB;gBAAK;gBAG1B,OAAOV;YACT;YAEA,OAAO;gBACL,IAAI,CAACT,WAAW,MAAM,CAAC,OAAO,EAAE;oBAC9BA,WAAW,KAAK;gBAClB;YACF;YAEA,MAAM,CAACoB,aAAaC,aAAeZ,YAAY,IAAI,CAACW,aAAaC;YACjE,OAAO,CAACA,aAAeZ,YAAY,KAAK,CAACY;YACzC,SAAS,CAACC,YAAcb,YAAY,OAAO,CAACa;QAC9C;IACF;IAEA;;GAEC,GACO,mBAAmB1B,OAAsB,EAA2B;QAC1E,OAAOA,SAAS,SAAS,IAAI,CAAC,qBAAqB,CAAC,KAAK,IAAI,IAAI,CAAC,iBAAiB;IACrF;IAEA;;GAEC,GACD,MAAc,eACZE,MAAqB,EACrBF,OAAiC,EACjCI,UAA2B,EAC3BuB,MAA8E,EAChC;QAC9C,MAAMC,gBAAgBrC,mBAAmBA,CAAC;YAAE,eAAeW;QAAO,GAAGF,SAAS,WAAW,CAAC;QAE1F,IAAI;YACF,yBAAyB;YACzB,MAAM6B,UAAU,MAAMpC,qBAAqBA,CAAC,IAAI,CAAC,cAAc,EAAEmC;YACjE,MAAME,kBAAkBnC,mBAAmBA,CAACkC,SAAS7B,SAAS,sBAAsBA,QAAQ,mBAAmB,GAAG,IAAI,CAAC,gBAAgB;YAEvI,wDAAwD;YACxD,MAAM+B,oBAAoB,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC7B,QAAQF,SAAS;YAE9E,kDAAkD;YAClD,MAAMgC,cAAc,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,IAAI,CAAC,qBAAqB,EAAEhC,SAAS+B,kBAAkB,MAAM;YAC/G,MAAM,CAACE,UAAUC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE;gBAAE,GAAGhC,MAAM;gBAAE,GAAG4B,eAAe;YAAC;YAC5G,MAAMK,cAAcC,OAAOH;YAE3B,mBAAmB;YACnB,IAAID,aAAa;gBACf,MAAMK,eAAe,MAAM,IAAI,CAAC,YAAY,CAAC,eAAe,CAA+BJ;gBAC3F,IAAII,cAAc;oBAChBV,OAAO;wBACL,WAAW;wBACX,QAAQ;wBACR,MAAMU,aAAa,IAAI;wBACvB,OAAO7B;wBACP,SAAS6B,aAAa,OAAO;wBAC7B,eAAenC;oBACjB;oBACA,OAAO;wBAAE,GAAGmC,YAAY;wBAAE,WAAW;oBAAK;gBAC5C;YACF;YAEA,sEAAsE;YACtE,IAAIL,eAAe,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAACG,cAAc;gBACzDR,OAAO;oBAAE,WAAW;oBAAO,QAAQ;gBAAU;gBAC7C,MAAMW,SAAS,MAAM,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAACH;gBAC/C,IAAI,CAACG,OAAO,EAAE,EAAE;oBACdX,OAAO;wBACL,WAAW;wBACX,QAAQ;wBACR,MAAMnB;wBACN,OAAO8B,OAAO,KAAK;wBACnB,SAASA,OAAO,OAAO;wBACvB,eAAepC;oBACjB;oBACA,OAAO;wBAAE,GAAGoC,MAAM;wBAAE,WAAW;oBAAK;gBACtC;gBACAX,OAAO;oBACL,WAAW;oBACX,QAAQ;oBACR,MAAMW,OAAO,IAAI;oBACjB,OAAO9B;oBACP,SAAS8B,OAAO,OAAO;oBACvB,eAAepC;gBACjB;gBACA,OAAO;oBAAE,GAAGoC,MAAM;oBAAE,WAAW;gBAAK;YACtC;YAEA,kDAAkD;YAClDX,OAAO;gBAAE,WAAW;gBAAO,QAAQ;YAAU;YAE7C,MAAMY,cAAc,IAAI,CAAC,kBAAkB,CAACvC;YAC5C,MAAMwC,eAAe,IAAI,CAAC,YAAY,CAACT,mBAAmB/B,SAASI,YAAYyB,SAASU,aAAaP,aAAaC,UAAUC,eAAe,CAAC;YAE5I,mEAAmE;YACnE,IAAIF,aAAa;gBACf,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAACG,aAAaK;gBACvCA,aAAa,OAAO,CAAC,IAAM,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAACL,cAAc,KAAK,CAAC,KAAO;YACrF;YAEA,MAAMM,WAAW,MAAMD;YAEvB,4BAA4B;YAC5B,IAAIC,SAAS,EAAE,EAAE;gBACfd,OAAO;oBACL,WAAW;oBACX,QAAQ;oBACR,MAAMc,SAAS,IAAI;oBACnB,OAAOjC;oBACP,SAASiC,SAAS,OAAO;oBACzB,eAAevC;gBACjB;gBACA,IAAI,CAAC,yBAAyB,CAAC;gBAC/B,OAAO;oBAAE,GAAGuC,QAAQ;oBAAE,WAAW;gBAAM;YACzC,OAAO;gBACL,+DAA+D;gBAC/D,IAAIT,aAAa;oBACf,MAAMU,cAAc,IAAI,CAAC,YAAY,CAAC,iBAAiB,CAAC,IAAI,CAAC,qBAAqB;oBAClF,IAAIA,YAAY,iBAAiB,KAAK,OAAO;wBAC3C,MAAM,IAAI,CAAC,YAAY,CAAC,eAAe,CAACT;oBAC1C;gBACF;gBAEAN,OAAO;oBACL,WAAW;oBACX,QAAQ;oBACR,MAAMnB;oBACN,OAAOiC,SAAS,KAAK;oBACrB,SAASA,SAAS,OAAO;oBACzB,eAAevC;gBACjB;gBACA,IAAI,CAAC,yBAAyB,CAAC,SAASuC,SAAS,KAAK;gBACtD,MAAMA,SAAS,KAAK;YACtB;QACF,EAAE,OAAOnB,OAAO;YACdK,OAAO;gBACL,WAAW;gBACX,QAAQ;gBACR,MAAMnB;gBACN,OAAOc;gBACP,SAASd;gBACT,eAAeN;YACjB;YACA,MAAMoB;QACR;IACF;IAEA;;GAEC,GACD,MAAc,aACZS,iBAAwF,EACxF/B,OAAiC,EACjCI,UAA2B,EAC3ByB,OAAgB,EAChBU,WAAoC,EACpCP,WAAoB,EACpBC,QAAuD,EACvDC,WAAgC,EACc;QAC9C,gDAAgD;QAChD,MAAMO,WAAW,MAAM,IAAI,CAAC,cAAc,CAACV,mBAAmB/B,SAASI,YAAYyB,SAASU;QAE5F,sCAAsC;QACtC,IAAIE,SAAS,EAAE,EAAE;YACf,MAAM,EAAE,SAASE,eAAe,EAAE,GAAGC,cAAc,GAAGH;YAEtD,4BAA4B;YAC5B,IAAI,IAAI,CAAC,qBAAqB,CAAC,eAAe,EAAE,QAAQ;gBACtD,MAAM,IAAI,CAAC,YAAY,CAAC,qBAAqB,CAAC,IAAI,CAAC,qBAAqB,CAAC,eAAe;YAC1F;YAEA,kBAAkB;YAClB,IAAIT,aAAa;gBACf,MAAMa,qBAAqB,IAAI,CAAC,YAAY,CAAC,iBAAiB,CAAC,IAAI,CAAC,qBAAqB;gBACzF,MAAM,IAAI,CAAC,YAAY,CAAC,eAAe,CACrCZ,UACA;oBAAE,GAAGW,YAAY;oBAAE,SAAStD,eAAeA,CAACqD;gBAAiB,GAC7DE,oBACAX,aACA,IAAI,CAAC,qBAAqB,CAAC,IAAI,IAAI,EAAE;YAEzC;QACF;QAEA,OAAOO;IACT;IAEA;;GAEC,GACD,MAAc,eACZV,iBAAwF,EACxF/B,OAAiC,EACjCI,UAA2B,EAC3ByB,OAAgB,EAChBU,WAAyB,EACqB;QAC9C,MAAMO,cAAeP,CAAAA,aAAa,SAAS,KAAK;QAChD,MAAMQ,UAAUR,aAAa,WAAW3C,gBAAgBA;QACxD,MAAMoD,WAAW,CAACC;YAChB,IAAI,OAAOV,aAAa,UAAU,YAAY,OAAOA,YAAY,KAAK,CAACU;YACvE,OAAOV,aAAa,SAAS;QAC/B;QAEA,IAAIW;QAEJ,IAAK,IAAID,UAAU,GAAGA,UAAUH,aAAaG,UAAW;YACtD,0EAA0E;YAC1E,IAAI7C,WAAW,MAAM,CAAC,OAAO,EAAE,MAAM,IAAI+C,aAAa,8BAA8B;YAEpF,MAAMC,gBAA8B;gBAAE,GAAGpD,OAAO;gBAAE,QAAQI,WAAW,MAAM;YAAC;YAC5E8C,eAAe,MAAM,IAAI,CAAC,aAAa,CAAiCnB,mBAAmBqB,eAAevB;YAE1G,mDAAmD;YACnD,IAAIqB,aAAa,EAAE,IAAI,CAACH,QAAQ,QAAQ,CAACG,aAAa,MAAM,KAAKD,YAAYH,cAAc,GAAG;gBAC5F,OAAOI;YACT;YAEA,gCAAgC;YAChC,MAAMG,QAAQL,SAASC;YACvB,MAAM,IAAIK,QAAc,CAACC;gBACvB,MAAMC,QAAQC,WAAWF,SAASF;gBAClC,2DAA2D;gBAC3DjD,WAAW,MAAM,CAAC,gBAAgB,CAChC,SACA;oBACEsD,aAAaF;oBACbD;gBACF,GACA;oBAAE,MAAM;gBAAK;YAEjB;QACF;QAEA,OAAOL;IACT;IAEA;;GAEC,GACO,0BAA0BS,MAA2B,EAAErC,KAAa,EAAQ;QAClF,MAAMsC,gBAA+B;YACnCD;YACA,aAAa,IAAI,CAAC,WAAW;YAC7B,MAAM,IAAI,CAAC,IAAI;YACf,kBAAkB,IAAI,CAAC,gBAAgB;YACvCrC;QACF;QACA,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAACV,KAAOA,GAAGgD;IAC9C;IAEO,UAAUhD,EAAkC,EAAe;QAChE,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAACA;QAE7B,MAAML,eAA8B;YAClC,QAAQ;YACR,aAAa,IAAI,CAAC,WAAW;YAC7B,MAAM,IAAI,CAAC,IAAI;YACf,kBAAkB,IAAI,CAAC,gBAAgB;YACvC,OAAOC;QACT;QAEAI,GAAGL;QACH,OAAO,IAAM,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAACK;IAC/C;IAEA,MAAa,QAAQ;QACnB,IAAI,CAAC,WAAW,GAAG;QAEnB,sCAAsC;QACtC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;YACzB,MAAM,IAAI,CAAC,YAAY,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;QAC9D;IACF;IAEO,UAAU;QACf,IAAI,CAAC,mBAAmB,CAAC,KAAK;QAC9B,IAAI,CAAC,gBAAgB,CAAC,KAAK;IAC7B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api/components/query-storage.js","sources":["../../../src/api/components/query-storage.ts"],"sourcesContent":["import { handleCleanupError, handleOperationError } from '../../_utils/error-handling.util'\nimport { IStorage, StorageKeyType } from '../../core'\nimport { CacheEntry, CacheUtils } from '../../core/storage/utils/cache.util'\nimport { CacheConfig, CreateApiClientOptions, StorageOption } from '../types/api.interface'\nimport { EndpointConfig } from '../types/endpoint.interface'\nimport { QueryOptions } from '../types/query.interface'\n\n/**\n * Менеджер хранилища для API\n * Объединяет в себе функционал хранилища и управления кэшем\n */\nexport class QueryStorage {\n /** Экземпляр хранилища */\n private storage: IStorage | null = null\n\n private cleanupInterval: NodeJS.Timeout | number | null = null\n\n /** Индекс тегов: tag → Set<cacheKey> для быстрой инвалидации */\n private tagIndex = new Map<string, Set<string>>()\n\n /** Настройки кэша по умолчанию */\n private defaultCacheOptions: Exclude<CacheConfig, boolean> = {\n ttl: 5 * 60 * 1000, // 5 минут по умолчанию\n cleanup: {\n enabled: true,\n interval: 10 * 60 * 1000, // 10 минут\n },\n invalidateOnError: true,\n }\n\n /** Флаг завершённой инициализации */\n private _initialized = false\n\n /** Промис текущей инициализации */\n private _initPromise: Promise<this> | null = null\n\n constructor(\n private readonly storageExternal: StorageOption,\n private readonly globalCacheConfig: CreateApiClientOptions['cache'],\n ) {}\n\n public async initialize(): Promise<this> {\n if (this._initialized) return this\n if (this._initPromise) return this._initPromise\n\n this._initPromise = this._doInitialize()\n return this._initPromise\n }\n\n private async _doInitialize(): Promise<this> {\n try {\n // 1. Создаем хранилище\n await this.createStorage()\n // 2. Перестраиваем индекс тегов из существующих записей в storage\n await this.rebuildTagIndex()\n // 3. Запускаем периодическую очистку, если это указано в настройках\n this.startCleanupInterval()\n\n this._initialized = true\n return this\n } catch (error) {\n this._initPromise = null\n throw error\n }\n }\n\n private async createStorage() {\n try {\n // Резолвим storage: может быть инстанс или фабрика\n const s: IStorage = typeof this.storageExternal === 'function' ? await this.storageExternal() : this.storageExternal\n\n await s.initialize()\n this.storage = s\n } catch (error) {\n handleOperationError('QueryStorage: storage initialization error', error)\n }\n }\n\n private startCleanupInterval(): void {\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval)\n this.cleanupInterval = null\n }\n\n // Получаем настройки очистки\n const cleanupConfig = typeof this.globalCacheConfig === 'object' ? this.globalCacheConfig.cleanup : this.defaultCacheOptions.cleanup\n\n // Запускаем интервал очистки, если он включен\n if (cleanupConfig?.enabled && cleanupConfig.interval) {\n this.cleanupInterval = setInterval(() => {\n this.cleanup().catch((err) => handleCleanupError('QueryStorage: cache cleanup error', err))\n }, cleanupConfig.interval)\n }\n }\n\n /**\n * Получает экземпляр хранилища\n */\n public getStorage(): IStorage | null {\n return this.storage\n }\n\n /**\n * Создает ключ кэша для запроса с учетом заголовков\n * @param endpoint Имя эндпоинта\n * @param params Параметры запроса (все что посчитаем нужным)\n */\n public createCacheKey<CacheParams extends Record<string, any>>(endpoint: string, params: CacheParams) {\n return CacheUtils.createApiKey(endpoint, params)\n }\n\n /**\n * Получает результат запроса из кэша\n */\n public async getCachedResult<T>(cacheKey: StorageKeyType): Promise<T | undefined> {\n if (!this.storage) throw new Error('Хранилище не инициализировано')\n\n const cachedEntry = await this.storage.get<CacheEntry<T>>(cacheKey)\n if (!cachedEntry) return undefined\n\n // Проверяем срок годности кэша\n if (CacheUtils.isExpired(cachedEntry.metadata)) {\n this.removeKeyFromTagIndex(String(cacheKey), cachedEntry.metadata.tags)\n await this.storage.remove(cacheKey)\n return undefined\n }\n\n // Обновляем метаданные кэша (счетчик доступа, время обновления)\n const updatedEntry: CacheEntry<T> = {\n ...cachedEntry,\n metadata: CacheUtils.updateMetadata(cachedEntry.metadata),\n }\n await this.storage.set(cacheKey, updatedEntry)\n\n return cachedEntry.data\n }\n\n /**\n * Сохраняет результат запроса в кэш\n * @param cacheKey Ключ кэша\n * @param data Данные для кэширования\n * @param cacheOptions Метаданные\n * @param cacheParams Параметры которые влияли на создание ключа\n * @param tags Тэги эндпоинта\n */\n public async setCachedResult<T, CacheParams extends Record<string, any>>(\n cacheKey: StorageKeyType,\n data: T,\n cacheOptions: Exclude<CacheConfig, boolean>,\n cacheParams: CacheParams,\n tags: string[],\n ): Promise<void> {\n if (!this.storage) throw new Error('Хранилище не инициализировано')\n\n // Создаем метаданные кэша\n const cacheMetadata = CacheUtils.createMetadata(cacheOptions.ttl, tags)\n\n // Создаем запись кэша\n const cacheEntry: CacheEntry<T> = {\n data,\n metadata: cacheMetadata,\n params: cacheParams,\n }\n\n await this.storage.set(cacheKey, cacheEntry)\n\n // Обновляем индекс тегов\n const keyStr = String(cacheKey)\n for (const tag of tags) {\n let keys = this.tagIndex.get(tag)\n if (!keys) {\n keys = new Set()\n this.tagIndex.set(tag, keys)\n }\n keys.add(keyStr)\n }\n }\n\n /**\n * Проверяет, должен ли запрос быть кэширован\n * @param endpointConfig Конфигурация эндпоинта\n * @param options Опции запроса\n * @param method HTTP-метод запроса (только GET кэшируется по REST-стандарту)\n * @returns true если запрос должен кэшироваться\n */\n public shouldCache(endpointConfig?: EndpointConfig, options?: QueryOptions, method?: string) {\n // Мутации (POST/PUT/DELETE/PATCH) не кэшируются по REST-стандарту\n if (method && method !== 'GET') return false\n // Если глобальный кэш отключен, возвращаем false\n if (this.globalCacheConfig === false) return false\n // Если эндпоинт явно отключает кэш, возвращаем false\n if (endpointConfig?.cache === false) return false\n // Если по какой то причине указали время кэша 0\n if (typeof endpointConfig?.cache === 'object' && endpointConfig?.cache.ttl === 0) return false\n // Если при вызове самого запроса явно указали НЕ кэшировать\n if (options?.disableCache === true) return false\n // Если настройки нигде не указаны - по умолчанию НЕ кэшируем\n if (this.globalCacheConfig === undefined && endpointConfig?.cache === undefined) return false\n\n return true\n }\n\n /**\n * Создает итоговую конфигурацию кэширования для конкретного эндпоинта\n * Объединяет глобальный конфиг с текущим\n * @param endpointConfig Конфигурация эндпоинта\n */\n public createCacheConfig(endpointConfig?: EndpointConfig) {\n // Создаем опции по умолчанию\n let resultConfig = this.defaultCacheOptions\n\n // Если в глобальном конфиге кэш передан как объект а не boolean - по умолчанию станет он\n if (typeof this.globalCacheConfig === 'object') {\n resultConfig = this.globalCacheConfig\n }\n // Если в настройках эндпоинта кэш как объект - дополняем этими параметрами итоговый объект кэша\n if (typeof endpointConfig?.cache === 'object') {\n const endpointCache = endpointConfig.cache as Exclude<CacheConfig, boolean>\n resultConfig = {\n ...resultConfig,\n ...endpointCache,\n }\n }\n\n return resultConfig\n }\n\n /**\n * Инвалидирует кэш по тегам (использует индекс для O(1) поиска по тегу)\n * @param tags Теги для инвалидации\n */\n public async invalidateCacheByTags(tags: string[]): Promise<void> {\n if (!this.storage) throw new Error('Хранилище не инициализировано')\n\n // Собираем все ключи для удаления через индекс\n const keysToRemove = new Set<string>()\n for (const tag of tags) {\n const keys = this.tagIndex.get(tag)\n if (keys) {\n keys.forEach((k) => keysToRemove.add(k))\n this.tagIndex.delete(tag)\n }\n }\n\n // Удаляем из остальных тегов индекса (ключ может быть в нескольких тегах)\n for (const key of keysToRemove) {\n for (const [tag, keys] of this.tagIndex) {\n keys.delete(key)\n if (keys.size === 0) this.tagIndex.delete(tag)\n }\n }\n\n // Удаляем записи из хранилища\n await Promise.all([...keysToRemove].map((key) => this.storage!.remove(key)))\n }\n\n /**\n * Инвалидирует кэш по ключу\n * @param cacheKey Ключ кэша\n */\n public async invalidateCache(cacheKey: StorageKeyType): Promise<void> {\n if (!this.storage) throw new Error('Хранилище не инициализировано')\n\n // Читаем теги записи для очистки индекса\n const cachedEntry = await this.storage.get<CacheEntry<any>>(cacheKey)\n if (cachedEntry) {\n this.removeKeyFromTagIndex(String(cacheKey), cachedEntry.metadata.tags)\n }\n\n await this.storage.remove(cacheKey)\n }\n\n /**\n * Выполняет очистку всех просроченных записей кэша\n */\n public async cleanup(): Promise<void> {\n if (!this.storage) {\n throw new Error('Хранилище не инициализировано')\n }\n\n const keys = await this.storage.keys()\n for (const key of keys) {\n const value = await this.storage.get<CacheEntry<any>>(key)\n if (value && CacheUtils.isExpired(value.metadata)) {\n this.removeKeyFromTagIndex(String(key), value.metadata.tags)\n await this.storage.remove(key)\n }\n }\n }\n\n /**\n * Уничтожает хранилище и освобождает ресурсы\n */\n public async destroy(): Promise<void> {\n // Останавливаем интервал очистки\n if (this.cleanupInterval) {\n globalThis.clearInterval(this.cleanupInterval)\n this.cleanupInterval = null\n }\n\n // Очищаем индекс тегов\n this.tagIndex.clear()\n\n // Очищаем хранилище\n if (this.storage) {\n await this.storage.destroy()\n this.storage = null\n }\n\n // Сбрасываем состояние инициализации\n this._initialized = false\n this._initPromise = null\n }\n\n /**\n * Перестраивает индекс тегов из существующих записей в storage\n * Вызывается при инициализации для восстановления после перезагрузки\n */\n private async rebuildTagIndex(): Promise<void> {\n if (!this.storage) return\n\n this.tagIndex.clear()\n const keys = await this.storage.keys()\n\n for (const key of keys) {\n const entry = await this.storage.get<CacheEntry<any>>(key)\n if (!entry?.metadata?.tags) continue\n\n // Удаляем протухшие записи сразу\n if (CacheUtils.isExpired(entry.metadata)) {\n await this.storage.remove(key)\n continue\n }\n\n const keyStr = String(key)\n for (const tag of entry.metadata.tags) {\n let tagKeys = this.tagIndex.get(tag)\n if (!tagKeys) {\n tagKeys = new Set()\n this.tagIndex.set(tag, tagKeys)\n }\n tagKeys.add(keyStr)\n }\n }\n }\n\n /**\n * Удаляет ключ из индекса тегов\n */\n private removeKeyFromTagIndex(key: string, tags?: string[]): void {\n if (!tags) return\n for (const tag of tags) {\n const keys = this.tagIndex.get(tag)\n if (keys) {\n keys.delete(key)\n if (keys.size === 0) this.tagIndex.delete(tag)\n }\n }\n }\n}\n"],"names":["handleCleanupError","handleOperationError","CacheUtils","QueryStorage","Map","storageExternal","globalCacheConfig","error","s","clearInterval","cleanupConfig","setInterval","err","endpoint","params","cacheKey","Error","cachedEntry","undefined","String","updatedEntry","data","cacheOptions","cacheParams","tags","cacheMetadata","cacheEntry","keyStr","tag","keys","Set","endpointConfig","options","method","resultConfig","endpointCache","keysToRemove","k","key","Promise","value","globalThis","entry","tagKeys"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api/components/query-storage.js","sources":["../../../src/api/components/query-storage.ts"],"sourcesContent":["import { handleCleanupError, handleOperationError } from '../../_utils/error-handling.util'\nimport { IStorage, StorageKeyType } from '../../core'\nimport { CacheConfig, CreateApiClientOptions, StorageOption } from '../types/api.interface'\nimport { EndpointConfig } from '../types/endpoint.interface'\nimport { QueryOptions } from '../types/query.interface'\nimport { CacheEntry, CacheUtils } from '../utils/cache.util'\n\n/**\n * Менеджер хранилища для API\n * Объединяет в себе функционал хранилища и управления кэшем\n */\nexport class QueryStorage {\n /** Экземпляр хранилища */\n private storage: IStorage | null = null\n\n private cleanupInterval: NodeJS.Timeout | number | null = null\n\n /** Индекс тегов: tag → Set<cacheKey> для быстрой инвалидации */\n private tagIndex = new Map<string, Set<string>>()\n\n /** Настройки кэша по умолчанию */\n private defaultCacheOptions: Exclude<CacheConfig, boolean> = {\n ttl: 5 * 60 * 1000, // 5 минут по умолчанию\n cleanup: {\n enabled: true,\n interval: 10 * 60 * 1000, // 10 минут\n },\n invalidateOnError: true,\n }\n\n /** Флаг завершённой инициализации */\n private _initialized = false\n\n /** Промис текущей инициализации */\n private _initPromise: Promise<this> | null = null\n\n constructor(\n private readonly storageExternal: StorageOption,\n private readonly globalCacheConfig: CreateApiClientOptions['cache'],\n ) {}\n\n public async initialize(): Promise<this> {\n if (this._initialized) return this\n if (this._initPromise) return this._initPromise\n\n this._initPromise = this._doInitialize()\n return this._initPromise\n }\n\n private async _doInitialize(): Promise<this> {\n try {\n // 1. Создаем хранилище\n await this.createStorage()\n // 2. Перестраиваем индекс тегов из существующих записей в storage\n await this.rebuildTagIndex()\n // 3. Запускаем периодическую очистку, если это указано в настройках\n this.startCleanupInterval()\n\n this._initialized = true\n return this\n } catch (error) {\n this._initPromise = null\n throw error\n }\n }\n\n private async createStorage() {\n try {\n // Резолвим storage: может быть инстанс или фабрика\n const s: IStorage = typeof this.storageExternal === 'function' ? await this.storageExternal() : this.storageExternal\n\n await s.initialize()\n this.storage = s\n } catch (error) {\n handleOperationError('QueryStorage: storage initialization error', error)\n }\n }\n\n private startCleanupInterval(): void {\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval)\n this.cleanupInterval = null\n }\n\n // Получаем настройки очистки\n const cleanupConfig = typeof this.globalCacheConfig === 'object' ? this.globalCacheConfig.cleanup : this.defaultCacheOptions.cleanup\n\n // Запускаем интервал очистки, если он включен\n if (cleanupConfig?.enabled && cleanupConfig.interval) {\n this.cleanupInterval = setInterval(() => {\n this.cleanup().catch((err) => handleCleanupError('QueryStorage: cache cleanup error', err))\n }, cleanupConfig.interval)\n }\n }\n\n /**\n * Получает экземпляр хранилища\n */\n public getStorage(): IStorage | null {\n return this.storage\n }\n\n /**\n * Создает ключ кэша для запроса с учетом заголовков\n * @param endpoint Имя эндпоинта\n * @param params Параметры запроса (все что посчитаем нужным)\n */\n public createCacheKey<CacheParams extends Record<string, any>>(endpoint: string, params: CacheParams) {\n return CacheUtils.createApiKey(endpoint, params)\n }\n\n /**\n * Получает результат запроса из кэша\n */\n public async getCachedResult<T>(cacheKey: StorageKeyType): Promise<T | undefined> {\n if (!this.storage) throw new Error('Хранилище не инициализировано')\n\n const cachedEntry = await this.storage.get<CacheEntry<T>>(cacheKey)\n if (!cachedEntry) return undefined\n\n // Проверяем срок годности кэша\n if (CacheUtils.isExpired(cachedEntry.metadata)) {\n this.removeKeyFromTagIndex(String(cacheKey), cachedEntry.metadata.tags)\n await this.storage.remove(cacheKey)\n return undefined\n }\n\n // Обновляем метаданные кэша (счетчик доступа, время обновления)\n const updatedEntry: CacheEntry<T> = {\n ...cachedEntry,\n metadata: CacheUtils.updateMetadata(cachedEntry.metadata),\n }\n await this.storage.set(cacheKey, updatedEntry)\n\n return cachedEntry.data\n }\n\n /**\n * Сохраняет результат запроса в кэш\n * @param cacheKey Ключ кэша\n * @param data Данные для кэширования\n * @param cacheOptions Метаданные\n * @param cacheParams Параметры которые влияли на создание ключа\n * @param tags Тэги эндпоинта\n */\n public async setCachedResult<T, CacheParams extends Record<string, any>>(\n cacheKey: StorageKeyType,\n data: T,\n cacheOptions: Exclude<CacheConfig, boolean>,\n cacheParams: CacheParams,\n tags: string[],\n ): Promise<void> {\n if (!this.storage) throw new Error('Хранилище не инициализировано')\n\n // Создаем метаданные кэша\n const cacheMetadata = CacheUtils.createMetadata(cacheOptions.ttl, tags)\n\n // Создаем запись кэша\n const cacheEntry: CacheEntry<T> = {\n data,\n metadata: cacheMetadata,\n params: cacheParams,\n }\n\n await this.storage.set(cacheKey, cacheEntry)\n\n // Обновляем индекс тегов\n const keyStr = String(cacheKey)\n for (const tag of tags) {\n let keys = this.tagIndex.get(tag)\n if (!keys) {\n keys = new Set()\n this.tagIndex.set(tag, keys)\n }\n keys.add(keyStr)\n }\n }\n\n /**\n * Проверяет, должен ли запрос быть кэширован\n * @param endpointConfig Конфигурация эндпоинта\n * @param options Опции запроса\n * @param method HTTP-метод запроса (только GET кэшируется по REST-стандарту)\n * @returns true если запрос должен кэшироваться\n */\n public shouldCache(endpointConfig?: EndpointConfig, options?: QueryOptions, method?: string) {\n // Мутации (POST/PUT/DELETE/PATCH) не кэшируются по REST-стандарту\n if (method && method !== 'GET') return false\n // Если глобальный кэш отключен, возвращаем false\n if (this.globalCacheConfig === false) return false\n // Если эндпоинт явно отключает кэш, возвращаем false\n if (endpointConfig?.cache === false) return false\n // Если по какой то причине указали время кэша 0\n if (typeof endpointConfig?.cache === 'object' && endpointConfig?.cache.ttl === 0) return false\n // Если при вызове самого запроса явно указали НЕ кэшировать\n if (options?.disableCache === true) return false\n // Если настройки нигде не указаны - по умолчанию НЕ кэшируем\n if (this.globalCacheConfig === undefined && endpointConfig?.cache === undefined) return false\n\n return true\n }\n\n /**\n * Создает итоговую конфигурацию кэширования для конкретного эндпоинта\n * Объединяет глобальный конфиг с текущим\n * @param endpointConfig Конфигурация эндпоинта\n */\n public createCacheConfig(endpointConfig?: EndpointConfig) {\n // Создаем опции по умолчанию\n let resultConfig = this.defaultCacheOptions\n\n // Если в глобальном конфиге кэш передан как объект а не boolean - по умолчанию станет он\n if (typeof this.globalCacheConfig === 'object') {\n resultConfig = this.globalCacheConfig\n }\n // Если в настройках эндпоинта кэш как объект - дополняем этими параметрами итоговый объект кэша\n if (typeof endpointConfig?.cache === 'object') {\n const endpointCache = endpointConfig.cache as Exclude<CacheConfig, boolean>\n resultConfig = {\n ...resultConfig,\n ...endpointCache,\n }\n }\n\n return resultConfig\n }\n\n /**\n * Инвалидирует кэш по тегам (использует индекс для O(1) поиска по тегу)\n * @param tags Теги для инвалидации\n */\n public async invalidateCacheByTags(tags: string[]): Promise<void> {\n if (!this.storage) throw new Error('Хранилище не инициализировано')\n\n // Собираем все ключи для удаления через индекс\n const keysToRemove = new Set<string>()\n for (const tag of tags) {\n const keys = this.tagIndex.get(tag)\n if (keys) {\n keys.forEach((k) => keysToRemove.add(k))\n this.tagIndex.delete(tag)\n }\n }\n\n // Удаляем из остальных тегов индекса (ключ может быть в нескольких тегах)\n for (const key of keysToRemove) {\n for (const [tag, keys] of this.tagIndex) {\n keys.delete(key)\n if (keys.size === 0) this.tagIndex.delete(tag)\n }\n }\n\n // Удаляем записи из хранилища\n await Promise.all([...keysToRemove].map((key) => this.storage!.remove(key)))\n }\n\n /**\n * Инвалидирует кэш по ключу\n * @param cacheKey Ключ кэша\n */\n public async invalidateCache(cacheKey: StorageKeyType): Promise<void> {\n if (!this.storage) throw new Error('Хранилище не инициализировано')\n\n // Читаем теги записи для очистки индекса\n const cachedEntry = await this.storage.get<CacheEntry<any>>(cacheKey)\n if (cachedEntry) {\n this.removeKeyFromTagIndex(String(cacheKey), cachedEntry.metadata.tags)\n }\n\n await this.storage.remove(cacheKey)\n }\n\n /**\n * Выполняет очистку всех просроченных записей кэша\n */\n public async cleanup(): Promise<void> {\n if (!this.storage) {\n throw new Error('Хранилище не инициализировано')\n }\n\n const keys = await this.storage.keys()\n for (const key of keys) {\n const value = await this.storage.get<CacheEntry<any>>(key)\n if (value && CacheUtils.isExpired(value.metadata)) {\n this.removeKeyFromTagIndex(String(key), value.metadata.tags)\n await this.storage.remove(key)\n }\n }\n }\n\n /**\n * Уничтожает хранилище и освобождает ресурсы\n */\n public async destroy(): Promise<void> {\n // Останавливаем интервал очистки\n if (this.cleanupInterval) {\n globalThis.clearInterval(this.cleanupInterval)\n this.cleanupInterval = null\n }\n\n // Очищаем индекс тегов\n this.tagIndex.clear()\n\n // Очищаем хранилище\n if (this.storage) {\n await this.storage.destroy()\n this.storage = null\n }\n\n // Сбрасываем состояние инициализации\n this._initialized = false\n this._initPromise = null\n }\n\n /**\n * Перестраивает индекс тегов из существующих записей в storage\n * Вызывается при инициализации для восстановления после перезагрузки\n */\n private async rebuildTagIndex(): Promise<void> {\n if (!this.storage) return\n\n this.tagIndex.clear()\n const keys = await this.storage.keys()\n\n for (const key of keys) {\n const entry = await this.storage.get<CacheEntry<any>>(key)\n if (!entry?.metadata?.tags) continue\n\n // Удаляем протухшие записи сразу\n if (CacheUtils.isExpired(entry.metadata)) {\n await this.storage.remove(key)\n continue\n }\n\n const keyStr = String(key)\n for (const tag of entry.metadata.tags) {\n let tagKeys = this.tagIndex.get(tag)\n if (!tagKeys) {\n tagKeys = new Set()\n this.tagIndex.set(tag, tagKeys)\n }\n tagKeys.add(keyStr)\n }\n }\n }\n\n /**\n * Удаляет ключ из индекса тегов\n */\n private removeKeyFromTagIndex(key: string, tags?: string[]): void {\n if (!tags) return\n for (const tag of tags) {\n const keys = this.tagIndex.get(tag)\n if (keys) {\n keys.delete(key)\n if (keys.size === 0) this.tagIndex.delete(tag)\n }\n }\n }\n}\n"],"names":["handleCleanupError","handleOperationError","CacheUtils","QueryStorage","Map","storageExternal","globalCacheConfig","error","s","clearInterval","cleanupConfig","setInterval","err","endpoint","params","cacheKey","Error","cachedEntry","undefined","String","updatedEntry","data","cacheOptions","cacheParams","tags","cacheMetadata","cacheEntry","keyStr","tag","keys","Set","endpointConfig","options","method","resultConfig","endpointCache","keysToRemove","k","key","Promise","value","globalThis","entry","tagKeys"],"mappings":";;;;;AAA2F;AAK/B;AAE5D;;;CAGC,GACM,MAAMG,YAAYA;;;IACvB,wBAAwB,GAChB,UAA2B,KAAI;IAE/B,kBAAkD,KAAI;IAE9D,8DAA8D,GACtD,WAAW,IAAIC,MAA0B;IAEjD,gCAAgC,GACxB,sBAAqD;QAC3D,KAAK,IAAI,KAAK;QACd,SAAS;YACP,SAAS;YACT,UAAU,KAAK,KAAK;QACtB;QACA,mBAAmB;IACrB,EAAC;IAED,mCAAmC,GAC3B,eAAe,MAAK;IAE5B,iCAAiC,GACzB,eAAqC,KAAI;IAEjD,YACmBC,eAA8B,EAC9BC,iBAAkD,CACnE;aAFiBD,kBAAAA;aACAC,oBAAAA;IAChB;IAEH,MAAa,aAA4B;QACvC,IAAI,IAAI,CAAC,YAAY,EAAE,OAAO,IAAI;QAClC,IAAI,IAAI,CAAC,YAAY,EAAE,OAAO,IAAI,CAAC,YAAY;QAE/C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,aAAa;QACtC,OAAO,IAAI,CAAC,YAAY;IAC1B;IAEA,MAAc,gBAA+B;QAC3C,IAAI;YACF,uBAAuB;YACvB,MAAM,IAAI,CAAC,aAAa;YACxB,kEAAkE;YAClE,MAAM,IAAI,CAAC,eAAe;YAC1B,oEAAoE;YACpE,IAAI,CAAC,oBAAoB;YAEzB,IAAI,CAAC,YAAY,GAAG;YACpB,OAAO,IAAI;QACb,EAAE,OAAOC,OAAO;YACd,IAAI,CAAC,YAAY,GAAG;YACpB,MAAMA;QACR;IACF;IAEA,MAAc,gBAAgB;QAC5B,IAAI;YACF,mDAAmD;YACnD,MAAMC,IAAc,OAAO,IAAI,CAAC,eAAe,KAAK,aAAa,MAAM,IAAI,CAAC,eAAe,KAAK,IAAI,CAAC,eAAe;YAEpH,MAAMA,EAAE,UAAU;YAClB,IAAI,CAAC,OAAO,GAAGA;QACjB,EAAE,OAAOD,OAAO;YACdN,oBAAoBA,CAAC,8CAA8CM;QACrE;IACF;IAEQ,uBAA6B;QACnC,IAAI,IAAI,CAAC,eAAe,EAAE;YACxBE,cAAc,IAAI,CAAC,eAAe;YAClC,IAAI,CAAC,eAAe,GAAG;QACzB;QAEA,6BAA6B;QAC7B,MAAMC,gBAAgB,OAAO,IAAI,CAAC,iBAAiB,KAAK,WAAW,IAAI,CAAC,iBAAiB,CAAC,OAAO,GAAG,IAAI,CAAC,mBAAmB,CAAC,OAAO;QAEpI,8CAA8C;QAC9C,IAAIA,eAAe,WAAWA,cAAc,QAAQ,EAAE;YACpD,IAAI,CAAC,eAAe,GAAGC,YAAY;gBACjC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,CAACC,MAAQZ,kBAAkBA,CAAC,qCAAqCY;YACxF,GAAGF,cAAc,QAAQ;QAC3B;IACF;IAEA;;GAEC,GACM,aAA8B;QACnC,OAAO,IAAI,CAAC,OAAO;IACrB;IAEA;;;;GAIC,GACM,eAAwDG,QAAgB,EAAEC,MAAmB,EAAE;QACpG,OAAOZ,uBAAuB,CAACW,UAAUC;IAC3C;IAEA;;GAEC,GACD,MAAa,gBAAmBC,QAAwB,EAA0B;QAChF,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,IAAIC,MAAM;QAEnC,MAAMC,cAAc,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAgBF;QAC1D,IAAI,CAACE,aAAa,OAAOC;QAEzB,+BAA+B;QAC/B,IAAIhB,oBAAoB,CAACe,YAAY,QAAQ,GAAG;YAC9C,IAAI,CAAC,qBAAqB,CAACE,OAAOJ,WAAWE,YAAY,QAAQ,CAAC,IAAI;YACtE,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAACF;YAC1B,OAAOG;QACT;QAEA,gEAAgE;QAChE,MAAME,eAA8B;YAClC,GAAGH,WAAW;YACd,UAAUf,yBAAyB,CAACe,YAAY,QAAQ;QAC1D;QACA,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAACF,UAAUK;QAEjC,OAAOH,YAAY,IAAI;IACzB;IAEA;;;;;;;GAOC,GACD,MAAa,gBACXF,QAAwB,EACxBM,IAAO,EACPC,YAA2C,EAC3CC,WAAwB,EACxBC,IAAc,EACC;QACf,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,IAAIR,MAAM;QAEnC,0BAA0B;QAC1B,MAAMS,gBAAgBvB,yBAAyB,CAACoB,aAAa,GAAG,EAAEE;QAElE,sBAAsB;QACtB,MAAME,aAA4B;YAChCL;YACA,UAAUI;YACV,QAAQF;QACV;QAEA,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAACR,UAAUW;QAEjC,yBAAyB;QACzB,MAAMC,SAASR,OAAOJ;QACtB,KAAK,MAAMa,OAAOJ,KAAM;YACtB,IAAIK,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACD;YAC7B,IAAI,CAACC,MAAM;gBACTA,OAAO,IAAIC;gBACX,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACF,KAAKC;YACzB;YACAA,KAAK,GAAG,CAACF;QACX;IACF;IAEA;;;;;;GAMC,GACM,YAAYI,cAA+B,EAAEC,OAAsB,EAAEC,MAAe,EAAE;QAC3F,kEAAkE;QAClE,IAAIA,UAAUA,WAAW,OAAO,OAAO;QACvC,iDAAiD;QACjD,IAAI,IAAI,CAAC,iBAAiB,KAAK,OAAO,OAAO;QAC7C,qDAAqD;QACrD,IAAIF,gBAAgB,UAAU,OAAO,OAAO;QAC5C,gDAAgD;QAChD,IAAI,OAAOA,gBAAgB,UAAU,YAAYA,gBAAgB,MAAM,QAAQ,GAAG,OAAO;QACzF,4DAA4D;QAC5D,IAAIC,SAAS,iBAAiB,MAAM,OAAO;QAC3C,6DAA6D;QAC7D,IAAI,IAAI,CAAC,iBAAiB,KAAKd,aAAaa,gBAAgB,UAAUb,WAAW,OAAO;QAExF,OAAO;IACT;IAEA;;;;GAIC,GACM,kBAAkBa,cAA+B,EAAE;QACxD,6BAA6B;QAC7B,IAAIG,eAAe,IAAI,CAAC,mBAAmB;QAE3C,yFAAyF;QACzF,IAAI,OAAO,IAAI,CAAC,iBAAiB,KAAK,UAAU;YAC9CA,eAAe,IAAI,CAAC,iBAAiB;QACvC;QACA,gGAAgG;QAChG,IAAI,OAAOH,gBAAgB,UAAU,UAAU;YAC7C,MAAMI,gBAAgBJ,eAAe,KAAK;YAC1CG,eAAe;gBACb,GAAGA,YAAY;gBACf,GAAGC,aAAa;YAClB;QACF;QAEA,OAAOD;IACT;IAEA;;;GAGC,GACD,MAAa,sBAAsBV,IAAc,EAAiB;QAChE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,IAAIR,MAAM;QAEnC,+CAA+C;QAC/C,MAAMoB,eAAe,IAAIN;QACzB,KAAK,MAAMF,OAAOJ,KAAM;YACtB,MAAMK,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACD;YAC/B,IAAIC,MAAM;gBACRA,KAAK,OAAO,CAAC,CAACQ,IAAMD,aAAa,GAAG,CAACC;gBACrC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAACT;YACvB;QACF;QAEA,0EAA0E;QAC1E,KAAK,MAAMU,OAAOF,aAAc;YAC9B,KAAK,MAAM,CAACR,KAAKC,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAE;gBACvCA,KAAK,MAAM,CAACS;gBACZ,IAAIT,KAAK,IAAI,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAACD;YAC5C;QACF;QAEA,8BAA8B;QAC9B,MAAMW,QAAQ,GAAG,CAAC;eAAIH;SAAa,CAAC,GAAG,CAAC,CAACE,MAAQ,IAAI,CAAC,OAAO,CAAE,MAAM,CAACA;IACxE;IAEA;;;GAGC,GACD,MAAa,gBAAgBvB,QAAwB,EAAiB;QACpE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,IAAIC,MAAM;QAEnC,yCAAyC;QACzC,MAAMC,cAAc,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAkBF;QAC5D,IAAIE,aAAa;YACf,IAAI,CAAC,qBAAqB,CAACE,OAAOJ,WAAWE,YAAY,QAAQ,CAAC,IAAI;QACxE;QAEA,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAACF;IAC5B;IAEA;;GAEC,GACD,MAAa,UAAyB;QACpC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YACjB,MAAM,IAAIC,MAAM;QAClB;QAEA,MAAMa,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI;QACpC,KAAK,MAAMS,OAAOT,KAAM;YACtB,MAAMW,QAAQ,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAkBF;YACtD,IAAIE,SAAStC,oBAAoB,CAACsC,MAAM,QAAQ,GAAG;gBACjD,IAAI,CAAC,qBAAqB,CAACrB,OAAOmB,MAAME,MAAM,QAAQ,CAAC,IAAI;gBAC3D,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAACF;YAC5B;QACF;IACF;IAEA;;GAEC,GACD,MAAa,UAAyB;QACpC,iCAAiC;QACjC,IAAI,IAAI,CAAC,eAAe,EAAE;YACxBG,WAAW,aAAa,CAAC,IAAI,CAAC,eAAe;YAC7C,IAAI,CAAC,eAAe,GAAG;QACzB;QAEA,uBAAuB;QACvB,IAAI,CAAC,QAAQ,CAAC,KAAK;QAEnB,oBAAoB;QACpB,IAAI,IAAI,CAAC,OAAO,EAAE;YAChB,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO;YAC1B,IAAI,CAAC,OAAO,GAAG;QACjB;QAEA,qCAAqC;QACrC,IAAI,CAAC,YAAY,GAAG;QACpB,IAAI,CAAC,YAAY,GAAG;IACtB;IAEA;;;GAGC,GACD,MAAc,kBAAiC;QAC7C,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;QAEnB,IAAI,CAAC,QAAQ,CAAC,KAAK;QACnB,MAAMZ,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI;QAEpC,KAAK,MAAMS,OAAOT,KAAM;YACtB,MAAMa,QAAQ,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAkBJ;YACtD,IAAI,CAACI,OAAO,UAAU,MAAM;YAE5B,iCAAiC;YACjC,IAAIxC,oBAAoB,CAACwC,MAAM,QAAQ,GAAG;gBACxC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAACJ;gBAC1B;YACF;YAEA,MAAMX,SAASR,OAAOmB;YACtB,KAAK,MAAMV,OAAOc,MAAM,QAAQ,CAAC,IAAI,CAAE;gBACrC,IAAIC,UAAU,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACf;gBAChC,IAAI,CAACe,SAAS;oBACZA,UAAU,IAAIb;oBACd,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACF,KAAKe;gBACzB;gBACAA,QAAQ,GAAG,CAAChB;YACd;QACF;IACF;IAEA;;GAEC,GACO,sBAAsBW,GAAW,EAAEd,IAAe,EAAQ;QAChE,IAAI,CAACA,MAAM;QACX,KAAK,MAAMI,OAAOJ,KAAM;YACtB,MAAMK,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAACD;YAC/B,IAAIC,MAAM;gBACRA,KAAK,MAAM,CAACS;gBACZ,IAAIT,KAAK,IAAI,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAACD;YAC5C;QACF;IACF;AACF"}
|
package/dist/api/index.d.ts
CHANGED
|
@@ -123,4 +123,3 @@ export type ExtractParamsType<T> = T extends EndpointConfig<infer P, any> ? P :
|
|
|
123
123
|
* Извлечение типа результата из конфигурации эндпоинта
|
|
124
124
|
*/
|
|
125
125
|
export type ExtractResultType<T> = T extends EndpointConfig<any, infer R> ? R : never;
|
|
126
|
-
//# sourceMappingURL=api.interface.d.ts.map
|
|
@@ -115,4 +115,3 @@ export interface Endpoint<RequestParams extends Record<string, any> = any, Respo
|
|
|
115
115
|
* Функция для создания типизированных эндпоинтов
|
|
116
116
|
*/
|
|
117
117
|
export type CreateEndpoint = <RequestParams extends Record<string, any>, RequestResult>(config: EndpointConfig<RequestParams, RequestResult>) => EndpointConfig<RequestParams, RequestResult>;
|
|
118
|
-
//# sourceMappingURL=endpoint.interface.d.ts.map
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import { StorageKey, StorageKeyType } from '
|
|
1
|
+
import { StorageKey, StorageKeyType } from '../../core/storage/utils/storage-key';
|
|
2
2
|
export interface CacheMetadata {
|
|
3
3
|
createdAt: number;
|
|
4
4
|
updatedAt: number;
|
|
5
5
|
expiresAt: number;
|
|
6
6
|
tags?: string[];
|
|
7
|
-
createdAtDateTime: string;
|
|
8
|
-
updatedAtDateTime: string;
|
|
9
|
-
expiresAtDateTime: string;
|
|
10
7
|
}
|
|
11
8
|
export interface CacheOptions {
|
|
12
9
|
ttl?: number;
|
|
@@ -23,11 +20,11 @@ export interface CacheEntry<Data, Params extends Record<string, any> = any> {
|
|
|
23
20
|
}
|
|
24
21
|
export declare class CacheUtils {
|
|
25
22
|
static createMetadata(ttl?: number, tags?: string[]): CacheMetadata;
|
|
26
|
-
|
|
23
|
+
/** ISO timestamps are derived lazily (for logging/debugging) instead of being stored in the cache payload. */
|
|
24
|
+
static formatDateTime(timestamp: number): string;
|
|
27
25
|
static isExpired(metadata: CacheMetadata): boolean;
|
|
28
26
|
static updateMetadata(metadata: CacheMetadata): CacheMetadata;
|
|
29
27
|
static createKey(...parts: (string | number)[]): StorageKey;
|
|
30
28
|
static createApiKey(endpoint: string, params?: Record<string, any>): [StorageKeyType, Record<string, any> | undefined];
|
|
31
29
|
static hasAnyTag(metadata: CacheMetadata, tags?: string[]): boolean;
|
|
32
30
|
}
|
|
33
|
-
//# sourceMappingURL=cache.util.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StorageKey } from "
|
|
1
|
+
import { StorageKey } from "../../core/storage/utils/storage-key.js";
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
@@ -10,14 +10,11 @@ class CacheUtils {
|
|
|
10
10
|
createdAt: now,
|
|
11
11
|
updatedAt: now,
|
|
12
12
|
expiresAt,
|
|
13
|
-
tags
|
|
14
|
-
createdAtDateTime: this.formatDateTime(now),
|
|
15
|
-
updatedAtDateTime: this.formatDateTime(now),
|
|
16
|
-
expiresAtDateTime: expiresAt === Infinity ? 'never' : this.formatDateTime(expiresAt)
|
|
13
|
+
tags
|
|
17
14
|
};
|
|
18
15
|
}
|
|
19
|
-
static formatDateTime(timestamp) {
|
|
20
|
-
return new Date(timestamp).toISOString();
|
|
16
|
+
/** ISO timestamps are derived lazily (for logging/debugging) instead of being stored in the cache payload. */ static formatDateTime(timestamp) {
|
|
17
|
+
return timestamp === Infinity ? 'never' : new Date(timestamp).toISOString();
|
|
21
18
|
}
|
|
22
19
|
static isExpired(metadata) {
|
|
23
20
|
return Date.now() > metadata.expiresAt;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api/utils/cache.util.js","sources":["../../../src/api/utils/cache.util.ts"],"sourcesContent":["import { StorageKey, StorageKeyType } from '../../core/storage/utils/storage-key'\n\nexport interface CacheMetadata {\n createdAt: number\n updatedAt: number\n expiresAt: number\n tags?: string[]\n}\n\nexport interface CacheOptions {\n ttl?: number\n cleanup?: {\n enabled: boolean\n interval?: number\n }\n invalidateOnError?: boolean\n}\n\nexport interface CacheEntry<Data, Params extends Record<string, any> = any> {\n data: Data\n metadata: CacheMetadata\n params: Params\n}\n\nexport class CacheUtils {\n static createMetadata(ttl: number = 0, tags: string[] = []): CacheMetadata {\n const now = Date.now()\n const expiresAt = ttl > 0 ? now + ttl : Infinity\n\n return {\n createdAt: now,\n updatedAt: now,\n expiresAt,\n tags,\n }\n }\n\n /** ISO timestamps are derived lazily (for logging/debugging) instead of being stored in the cache payload. */\n static formatDateTime(timestamp: number): string {\n return timestamp === Infinity ? 'never' : new Date(timestamp).toISOString()\n }\n\n static isExpired(metadata: CacheMetadata): boolean {\n return Date.now() > metadata.expiresAt\n }\n\n static updateMetadata(metadata: CacheMetadata): CacheMetadata {\n return {\n ...metadata,\n updatedAt: Date.now(),\n }\n }\n\n static createKey(...parts: (string | number)[]): StorageKey {\n return new StorageKey(parts.join('_'))\n }\n\n static createApiKey(endpoint: string, params?: Record<string, any>): [StorageKeyType, Record<string, any> | undefined] {\n if (!params) return [new StorageKey(endpoint, true), params]\n\n const sortedParams = Object.entries(params)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${k}=${v}`)\n .join('&')\n\n return [new StorageKey(`${endpoint}_${sortedParams}`, true), params]\n }\n\n // Функция для проверки, есть ли у записи определенные теги\n static hasAnyTag(metadata: CacheMetadata, tags: string[] = []): boolean {\n if (!metadata.tags || !tags.length) return false\n return tags.some((tag) => metadata.tags?.includes(tag))\n }\n}\n"],"names":["StorageKey","CacheUtils","ttl","tags","now","Date","expiresAt","Infinity","timestamp","metadata","parts","endpoint","params","sortedParams","Object","a","b","k","v","tag"],"mappings":";;;AAAiF;AAwB1E,MAAMC,UAAUA;IACrB,OAAO,eAAeC,MAAc,CAAC,EAAEC,OAAiB,EAAE,EAAiB;QACzE,MAAMC,MAAMC,KAAK,GAAG;QACpB,MAAMC,YAAYJ,MAAM,IAAIE,MAAMF,MAAMK;QAExC,OAAO;YACL,WAAWH;YACX,WAAWA;YACXE;YACAH;QACF;IACF;IAEA,4GAA4G,GAC5G,OAAO,eAAeK,SAAiB,EAAU;QAC/C,OAAOA,cAAcD,WAAW,UAAU,IAAIF,KAAKG,WAAW,WAAW;IAC3E;IAEA,OAAO,UAAUC,QAAuB,EAAW;QACjD,OAAOJ,KAAK,GAAG,KAAKI,SAAS,SAAS;IACxC;IAEA,OAAO,eAAeA,QAAuB,EAAiB;QAC5D,OAAO;YACL,GAAGA,QAAQ;YACX,WAAWJ,KAAK,GAAG;QACrB;IACF;IAEA,OAAO,UAAU,GAAGK,KAA0B,EAAc;QAC1D,OAAO,IAAIV,UAAUA,CAACU,MAAM,IAAI,CAAC;IACnC;IAEA,OAAO,aAAaC,QAAgB,EAAEC,MAA4B,EAAqD;QACrH,IAAI,CAACA,QAAQ,OAAO;YAAC,IAAIZ,UAAUA,CAACW,UAAU;YAAOC;SAAO;QAE5D,MAAMC,eAAeC,OAAO,OAAO,CAACF,QACjC,IAAI,CAAC,CAAC,CAACG,EAAE,EAAE,CAACC,EAAE,GAAKD,EAAE,aAAa,CAACC,IACnC,GAAG,CAAC,CAAC,CAACC,GAAGC,EAAE,GAAK,GAAGD,EAAE,CAAC,EAAEC,GAAG,EAC3B,IAAI,CAAC;QAER,OAAO;YAAC,IAAIlB,UAAUA,CAAC,GAAGW,SAAS,CAAC,EAAEE,cAAc,EAAE;YAAOD;SAAO;IACtE;IAEA,2DAA2D;IAC3D,OAAO,UAAUH,QAAuB,EAAEN,OAAiB,EAAE,EAAW;QACtE,IAAI,CAACM,SAAS,IAAI,IAAI,CAACN,KAAK,MAAM,EAAE,OAAO;QAC3C,OAAOA,KAAK,IAAI,CAAC,CAACgB,MAAQV,SAAS,IAAI,EAAE,SAASU;IACpD;AACF"}
|
|
@@ -7,4 +7,3 @@ import { ApiContext } from '../types/api.interface';
|
|
|
7
7
|
* @returns - полный контекст для подготовки заголовков
|
|
8
8
|
*/
|
|
9
9
|
export declare function createHeaderContext<RequestParams extends Record<string, any>>(context?: Partial<ApiContext>, optionContext?: Record<string, any>): ApiContext<RequestParams>;
|
|
10
|
-
//# sourceMappingURL=create-header-context.d.ts.map
|
|
@@ -20,4 +20,3 @@ export declare function prepareRequestHeaders<RequestParams extends Record<strin
|
|
|
20
20
|
* @returns - функция для подготовки заголовков
|
|
21
21
|
*/
|
|
22
22
|
export declare function createPrepareHeaders(globalPrepareHeaders?: PrepareHeadersFunction, endpointPrepareHeaders?: PrepareHeadersFunction): PrepareHeadersFunction;
|
|
23
|
-
//# sourceMappingURL=endpoint-headers.d.ts.map
|