synapse-storage 4.1.2 → 5.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/README.md +142 -12
  2. package/dist/_utils/chunk.util.d.ts +0 -1
  3. package/dist/_utils/deepMerge.util.d.ts +0 -1
  4. package/dist/_utils/error-handling.util.d.ts +0 -1
  5. package/dist/_utils/flatMap.util.d.ts +0 -1
  6. package/dist/_utils/index.d.ts +0 -1
  7. package/dist/_utils/logger-console.util.d.ts +0 -1
  8. package/dist/api/api.module.d.ts +0 -1
  9. package/dist/api/components/endpoint.d.ts +11 -12
  10. package/dist/api/components/endpoint.js.map +1 -1
  11. package/dist/api/components/query-storage.d.ts +0 -1
  12. package/dist/api/components/query-storage.js +1 -1
  13. package/dist/api/components/query-storage.js.map +1 -1
  14. package/dist/api/index.d.ts +0 -1
  15. package/dist/api/types/api.interface.d.ts +0 -1
  16. package/dist/api/types/endpoint.interface.d.ts +0 -1
  17. package/dist/api/types/query.interface.d.ts +0 -1
  18. package/dist/api/utils/api-helpers.d.ts +0 -1
  19. package/dist/{core/storage → api}/utils/cache.util.d.ts +3 -6
  20. package/dist/{core/storage → api}/utils/cache.util.js +4 -7
  21. package/dist/api/utils/cache.util.js.map +1 -0
  22. package/dist/api/utils/create-header-context.d.ts +0 -1
  23. package/dist/api/utils/endpoint-headers.d.ts +0 -1
  24. package/dist/api/utils/fetch-base-query.d.ts +0 -1
  25. package/dist/api/utils/file-utils.d.ts +0 -1
  26. package/dist/api/utils/get-cacheable-headers.d.ts +0 -1
  27. package/dist/core/index.d.ts +0 -1
  28. package/dist/core/selector/index.d.ts +1 -1
  29. package/dist/core/selector/index.js +2 -0
  30. package/dist/core/selector/index.js.map +1 -1
  31. package/dist/core/selector/selector.interface.d.ts +15 -30
  32. package/dist/core/selector/selector.interface.js +2 -2
  33. package/dist/core/selector/selector.interface.js.map +1 -1
  34. package/dist/core/selector/selector.module.d.ts +16 -1
  35. package/dist/core/selector/selector.module.js +82 -20
  36. package/dist/core/selector/selector.module.js.map +1 -1
  37. package/dist/core/selector/selectors.base.d.ts +56 -0
  38. package/dist/core/selector/selectors.base.js +118 -0
  39. package/dist/core/selector/selectors.base.js.map +1 -0
  40. package/dist/core/storage/adapters/async-base-storage.service.d.ts +15 -4
  41. package/dist/core/storage/adapters/async-base-storage.service.js +106 -36
  42. package/dist/core/storage/adapters/async-base-storage.service.js.map +1 -1
  43. package/dist/core/storage/adapters/indexed-DB.service.d.ts +4 -5
  44. package/dist/core/storage/adapters/indexed-DB.service.js +66 -14
  45. package/dist/core/storage/adapters/indexed-DB.service.js.map +1 -1
  46. package/dist/core/storage/adapters/local-storage.service.d.ts +9 -4
  47. package/dist/core/storage/adapters/local-storage.service.js +25 -5
  48. package/dist/core/storage/adapters/local-storage.service.js.map +1 -1
  49. package/dist/core/storage/adapters/memory-storage.service.d.ts +2 -4
  50. package/dist/core/storage/adapters/memory-storage.service.js +5 -5
  51. package/dist/core/storage/adapters/memory-storage.service.js.map +1 -1
  52. package/dist/core/storage/adapters/path.utils.d.ts +0 -1
  53. package/dist/core/storage/adapters/storage-core.d.ts +6 -2
  54. package/dist/core/storage/adapters/storage-core.js +6 -3
  55. package/dist/core/storage/adapters/storage-core.js.map +1 -1
  56. package/dist/core/storage/adapters/sync-base-storage.service.d.ts +20 -4
  57. package/dist/core/storage/adapters/sync-base-storage.service.js +110 -35
  58. package/dist/core/storage/adapters/sync-base-storage.service.js.map +1 -1
  59. package/dist/core/storage/index.d.ts +2 -5
  60. package/dist/core/storage/index.js +1 -5
  61. package/dist/core/storage/index.js.map +1 -1
  62. package/dist/core/storage/middlewares/broadcast.middleware.d.ts +0 -1
  63. package/dist/core/storage/middlewares/index.d.ts +3 -1
  64. package/dist/core/storage/middlewares/index.js +6 -0
  65. package/dist/core/storage/middlewares/index.js.map +1 -1
  66. package/dist/core/storage/middlewares/storage-batching.middleware.d.ts +0 -1
  67. package/dist/core/storage/middlewares/storage-logger.middleware.d.ts +20 -0
  68. package/dist/core/storage/middlewares/storage-logger.middleware.js +53 -0
  69. package/dist/core/storage/middlewares/storage-logger.middleware.js.map +1 -0
  70. package/dist/core/storage/middlewares/storage-shallow-compare.middleware.d.ts +0 -1
  71. package/dist/core/storage/middlewares/storage-shallow-compare.middleware.js +4 -10
  72. package/dist/core/storage/middlewares/storage-shallow-compare.middleware.js.map +1 -1
  73. package/dist/core/storage/middlewares/sync-broadcast.middleware.d.ts +0 -1
  74. package/dist/core/storage/middlewares/sync-storage-batching.middleware.d.ts +0 -1
  75. package/dist/core/storage/middlewares/sync-storage-logger.middleware.d.ts +7 -0
  76. package/dist/core/storage/middlewares/sync-storage-logger.middleware.js +48 -0
  77. package/dist/core/storage/middlewares/sync-storage-logger.middleware.js.map +1 -0
  78. package/dist/core/storage/middlewares/sync-storage-shallow-compare.middleware.d.ts +0 -1
  79. package/dist/core/storage/middlewares/sync-storage-shallow-compare.middleware.js +4 -10
  80. package/dist/core/storage/middlewares/sync-storage-shallow-compare.middleware.js.map +1 -1
  81. package/dist/core/storage/modules/singleton/mixin.util.d.ts +0 -1
  82. package/dist/core/storage/modules/singleton/models.d.ts +0 -1
  83. package/dist/core/storage/modules/singleton/singleton.util.d.ts +0 -1
  84. package/dist/core/storage/storage.interface.d.ts +59 -4
  85. package/dist/core/storage/storage.interface.js +0 -2
  86. package/dist/core/storage/storage.interface.js.map +1 -1
  87. package/dist/core/storage/utils/broadcast.util.d.ts +0 -1
  88. package/dist/core/storage/utils/middleware-module.d.ts +0 -3
  89. package/dist/core/storage/utils/middleware-module.js +0 -8
  90. package/dist/core/storage/utils/middleware-module.js.map +1 -1
  91. package/dist/core/storage/utils/migration.util.d.ts +38 -0
  92. package/dist/core/storage/utils/migration.util.js +48 -0
  93. package/dist/core/storage/utils/migration.util.js.map +1 -0
  94. package/dist/core/storage/utils/path-selector.util.d.ts +0 -1
  95. package/dist/core/storage/utils/state-diff.util.d.ts +8 -1
  96. package/dist/core/storage/utils/state-diff.util.js +17 -1
  97. package/dist/core/storage/utils/state-diff.util.js.map +1 -1
  98. package/dist/core/storage/utils/storage-factory.util.d.ts +7 -9
  99. package/dist/core/storage/utils/storage-factory.util.js +10 -10
  100. package/dist/core/storage/utils/storage-factory.util.js.map +1 -1
  101. package/dist/core/storage/utils/storage-key.d.ts +0 -1
  102. package/dist/index.d.ts +0 -1
  103. package/dist/react/hooks/index.d.ts +2 -1
  104. package/dist/react/hooks/index.js +4 -0
  105. package/dist/react/hooks/index.js.map +1 -1
  106. package/dist/react/hooks/useCreateStorage.d.ts +5 -6
  107. package/dist/react/hooks/useCreateStorage.js +2 -2
  108. package/dist/react/hooks/useCreateStorage.js.map +1 -1
  109. package/dist/react/hooks/useObservable.d.ts +17 -0
  110. package/dist/react/hooks/useObservable.js +38 -0
  111. package/dist/react/hooks/useObservable.js.map +1 -0
  112. package/dist/react/hooks/useSelector.d.ts +0 -1
  113. package/dist/react/hooks/useSelector.js +5 -2
  114. package/dist/react/hooks/useSelector.js.map +1 -1
  115. package/dist/react/hooks/useStorage.d.ts +0 -1
  116. package/dist/react/hooks/useStorageSubscribe.d.ts +0 -1
  117. package/dist/react/hooks/useSubscription.d.ts +13 -0
  118. package/dist/react/hooks/useSubscription.js +23 -0
  119. package/dist/react/hooks/useSubscription.js.map +1 -0
  120. package/dist/react/index.d.ts +0 -1
  121. package/dist/react/utils/awaitSynapse.d.ts +9 -10
  122. package/dist/react/utils/awaitSynapse.js +3 -2
  123. package/dist/react/utils/awaitSynapse.js.map +1 -1
  124. package/dist/react/utils/createSynapseCtx.d.ts +18 -23
  125. package/dist/react/utils/createSynapseCtx.js +64 -39
  126. package/dist/react/utils/createSynapseCtx.js.map +1 -1
  127. package/dist/react/utils/index.d.ts +0 -1
  128. package/dist/reactive/dispatcher/dispatcher.base.d.ts +122 -0
  129. package/dist/reactive/dispatcher/dispatcher.base.js +294 -0
  130. package/dist/reactive/dispatcher/dispatcher.base.js.map +1 -0
  131. package/dist/reactive/dispatcher/dispatcher.module.d.ts +12 -67
  132. package/dist/reactive/dispatcher/dispatcher.module.js +13 -72
  133. package/dist/reactive/dispatcher/dispatcher.module.js.map +1 -1
  134. package/dist/reactive/dispatcher/index.d.ts +1 -1
  135. package/dist/reactive/dispatcher/index.js +2 -0
  136. package/dist/reactive/dispatcher/index.js.map +1 -1
  137. package/dist/reactive/dispatcher/middlewares/index.d.ts +0 -1
  138. package/dist/reactive/dispatcher/middlewares/logger.middleware.d.ts +0 -1
  139. package/dist/reactive/dispatcher/path.util.d.ts +15 -0
  140. package/dist/reactive/dispatcher/path.util.js +34 -0
  141. package/dist/reactive/dispatcher/path.util.js.map +1 -0
  142. package/dist/reactive/dispatcher/standalone.d.ts +1 -150
  143. package/dist/reactive/dispatcher/standalone.js +6 -217
  144. package/dist/reactive/dispatcher/standalone.js.map +1 -1
  145. package/dist/reactive/effects/effects.base.d.ts +62 -0
  146. package/dist/reactive/effects/effects.base.js +90 -0
  147. package/dist/reactive/effects/effects.base.js.map +1 -0
  148. package/dist/reactive/effects/effects.module.d.ts +122 -11
  149. package/dist/reactive/effects/effects.module.js +129 -17
  150. package/dist/reactive/effects/effects.module.js.map +1 -1
  151. package/dist/reactive/effects/index.d.ts +1 -1
  152. package/dist/reactive/effects/index.js +2 -0
  153. package/dist/reactive/effects/index.js.map +1 -1
  154. package/dist/reactive/effects/utils/chunkRequestConsistent.d.ts +0 -1
  155. package/dist/reactive/effects/utils/chunkRequestParallel.d.ts +0 -1
  156. package/dist/reactive/effects/utils/fromRequest.d.ts +0 -1
  157. package/dist/reactive/effects/utils/index.d.ts +0 -1
  158. package/dist/reactive/effects/utils/toObservable.d.ts +0 -1
  159. package/dist/reactive/index.d.ts +0 -1
  160. package/dist/utils/createEventBus.d.ts +47 -46
  161. package/dist/utils/createEventBus.js +152 -174
  162. package/dist/utils/createEventBus.js.map +1 -1
  163. package/dist/utils/createSynapse/createSynapse.d.ts +25 -7
  164. package/dist/utils/createSynapse/createSynapse.js +28 -98
  165. package/dist/utils/createSynapse/createSynapse.js.map +1 -1
  166. package/dist/utils/createSynapse/factory.d.ts +6 -0
  167. package/dist/utils/createSynapse/factory.js +256 -0
  168. package/dist/utils/createSynapse/factory.js.map +1 -0
  169. package/dist/utils/createSynapse/index.d.ts +2 -2
  170. package/dist/utils/createSynapse/index.js.map +1 -1
  171. package/dist/utils/createSynapse/synapse.types.d.ts +87 -0
  172. package/dist/utils/createSynapse/synapse.types.js +11 -0
  173. package/dist/utils/createSynapse/synapse.types.js.map +1 -0
  174. package/dist/utils/createSynapse/types.d.ts +6 -85
  175. package/dist/utils/createSynapse/types.js +2 -1
  176. package/dist/utils/createSynapse/types.js.map +1 -1
  177. package/dist/utils/createSynapse/waitForDependencies.d.ts +0 -1
  178. package/dist/utils/createSynapse/waitForDependencies.js +1 -1
  179. package/dist/utils/createSynapse/waitForDependencies.js.map +1 -1
  180. package/dist/utils/createSynapseAwaiter.d.ts +13 -10
  181. package/dist/utils/createSynapseAwaiter.js +30 -3
  182. package/dist/utils/createSynapseAwaiter.js.map +1 -1
  183. package/dist/utils/dehydrateModule.d.ts +6 -0
  184. package/dist/utils/dehydrateModule.js +43 -0
  185. package/dist/utils/dehydrateModule.js.map +1 -0
  186. package/dist/utils/index.d.ts +3 -3
  187. package/dist/utils/index.js +3 -0
  188. package/dist/utils/index.js.map +1 -1
  189. package/package.json +12 -2
  190. package/dist/_utils/chunk.util.d.ts.map +0 -1
  191. package/dist/_utils/deepMerge.util.d.ts.map +0 -1
  192. package/dist/_utils/error-handling.util.d.ts.map +0 -1
  193. package/dist/_utils/flatMap.util.d.ts.map +0 -1
  194. package/dist/_utils/index.d.ts.map +0 -1
  195. package/dist/_utils/logger-console.util.d.ts.map +0 -1
  196. package/dist/api/api.module.d.ts.map +0 -1
  197. package/dist/api/components/endpoint.d.ts.map +0 -1
  198. package/dist/api/components/query-storage.d.ts.map +0 -1
  199. package/dist/api/example.d.ts +0 -83
  200. package/dist/api/example.d.ts.map +0 -1
  201. package/dist/api/example.js +0 -90
  202. package/dist/api/example.js.map +0 -1
  203. package/dist/api/index.d.ts.map +0 -1
  204. package/dist/api/types/api.interface.d.ts.map +0 -1
  205. package/dist/api/types/endpoint.interface.d.ts.map +0 -1
  206. package/dist/api/types/query.interface.d.ts.map +0 -1
  207. package/dist/api/utils/api-helpers.d.ts.map +0 -1
  208. package/dist/api/utils/create-header-context.d.ts.map +0 -1
  209. package/dist/api/utils/endpoint-headers.d.ts.map +0 -1
  210. package/dist/api/utils/fetch-base-query.d.ts.map +0 -1
  211. package/dist/api/utils/file-utils.d.ts.map +0 -1
  212. package/dist/api/utils/get-cacheable-headers.d.ts.map +0 -1
  213. package/dist/core/index.d.ts.map +0 -1
  214. package/dist/core/selector/index.d.ts.map +0 -1
  215. package/dist/core/selector/selector.interface.d.ts.map +0 -1
  216. package/dist/core/selector/selector.module.d.ts.map +0 -1
  217. package/dist/core/storage/adapters/async-base-storage.service.d.ts.map +0 -1
  218. package/dist/core/storage/adapters/indexed-DB.service.d.ts.map +0 -1
  219. package/dist/core/storage/adapters/local-storage.service.d.ts.map +0 -1
  220. package/dist/core/storage/adapters/memory-storage.service.d.ts.map +0 -1
  221. package/dist/core/storage/adapters/path.utils.d.ts.map +0 -1
  222. package/dist/core/storage/adapters/storage-core.d.ts.map +0 -1
  223. package/dist/core/storage/adapters/sync-base-storage.service.d.ts.map +0 -1
  224. package/dist/core/storage/index.d.ts.map +0 -1
  225. package/dist/core/storage/middlewares/broadcast.middleware.d.ts.map +0 -1
  226. package/dist/core/storage/middlewares/index.d.ts.map +0 -1
  227. package/dist/core/storage/middlewares/storage-batching.middleware.d.ts.map +0 -1
  228. package/dist/core/storage/middlewares/storage-shallow-compare.middleware.d.ts.map +0 -1
  229. package/dist/core/storage/middlewares/sync-broadcast.middleware.d.ts.map +0 -1
  230. package/dist/core/storage/middlewares/sync-storage-batching.middleware.d.ts.map +0 -1
  231. package/dist/core/storage/middlewares/sync-storage-shallow-compare.middleware.d.ts.map +0 -1
  232. package/dist/core/storage/modules/plugin/plugin.interface.d.ts +0 -101
  233. package/dist/core/storage/modules/plugin/plugin.interface.d.ts.map +0 -1
  234. package/dist/core/storage/modules/plugin/plugin.interface.js +0 -8
  235. package/dist/core/storage/modules/plugin/plugin.interface.js.map +0 -1
  236. package/dist/core/storage/modules/plugin/plugin.service.d.ts +0 -49
  237. package/dist/core/storage/modules/plugin/plugin.service.d.ts.map +0 -1
  238. package/dist/core/storage/modules/plugin/plugin.service.js +0 -406
  239. package/dist/core/storage/modules/plugin/plugin.service.js.map +0 -1
  240. package/dist/core/storage/modules/singleton/mixin.util.d.ts.map +0 -1
  241. package/dist/core/storage/modules/singleton/models.d.ts.map +0 -1
  242. package/dist/core/storage/modules/singleton/singleton.util.d.ts.map +0 -1
  243. package/dist/core/storage/storage.interface.d.ts.map +0 -1
  244. package/dist/core/storage/utils/broadcast.util.d.ts.map +0 -1
  245. package/dist/core/storage/utils/cache.util.d.ts.map +0 -1
  246. package/dist/core/storage/utils/cache.util.js.map +0 -1
  247. package/dist/core/storage/utils/middleware-module.d.ts.map +0 -1
  248. package/dist/core/storage/utils/path-selector.util.d.ts.map +0 -1
  249. package/dist/core/storage/utils/state-diff.util.d.ts.map +0 -1
  250. package/dist/core/storage/utils/storage-factory.util.d.ts.map +0 -1
  251. package/dist/core/storage/utils/storage-key.d.ts.map +0 -1
  252. package/dist/core/storage/utils/storage.utils.d.ts +0 -17
  253. package/dist/core/storage/utils/storage.utils.d.ts.map +0 -1
  254. package/dist/core/storage/utils/storage.utils.js +0 -57
  255. package/dist/core/storage/utils/storage.utils.js.map +0 -1
  256. package/dist/index.d.ts.map +0 -1
  257. package/dist/react/hooks/index.d.ts.map +0 -1
  258. package/dist/react/hooks/useCreateStorage.d.ts.map +0 -1
  259. package/dist/react/hooks/useSelector.d.ts.map +0 -1
  260. package/dist/react/hooks/useStorage.d.ts.map +0 -1
  261. package/dist/react/hooks/useStorageSubscribe.d.ts.map +0 -1
  262. package/dist/react/index.d.ts.map +0 -1
  263. package/dist/react/utils/awaitSynapse.d.ts.map +0 -1
  264. package/dist/react/utils/createSynapseCtx.d.ts.map +0 -1
  265. package/dist/react/utils/index.d.ts.map +0 -1
  266. package/dist/reactive/dispatcher/dispatcher.module.d.ts.map +0 -1
  267. package/dist/reactive/dispatcher/index.d.ts.map +0 -1
  268. package/dist/reactive/dispatcher/middlewares/index.d.ts.map +0 -1
  269. package/dist/reactive/dispatcher/middlewares/logger.middleware.d.ts.map +0 -1
  270. package/dist/reactive/dispatcher/standalone.d.ts.map +0 -1
  271. package/dist/reactive/effects/effects.module.d.ts.map +0 -1
  272. package/dist/reactive/effects/index.d.ts.map +0 -1
  273. package/dist/reactive/effects/utils/chunkRequestConsistent.d.ts.map +0 -1
  274. package/dist/reactive/effects/utils/chunkRequestParallel.d.ts.map +0 -1
  275. package/dist/reactive/effects/utils/fromRequest.d.ts.map +0 -1
  276. package/dist/reactive/effects/utils/index.d.ts.map +0 -1
  277. package/dist/reactive/effects/utils/toObservable.d.ts.map +0 -1
  278. package/dist/reactive/index.d.ts.map +0 -1
  279. package/dist/utils/createEventBus.d.ts.map +0 -1
  280. package/dist/utils/createSynapse/createSynapse.d.ts.map +0 -1
  281. package/dist/utils/createSynapse/index.d.ts.map +0 -1
  282. package/dist/utils/createSynapse/types.d.ts.map +0 -1
  283. package/dist/utils/createSynapse/validate.d.ts +0 -2
  284. package/dist/utils/createSynapse/validate.d.ts.map +0 -1
  285. package/dist/utils/createSynapse/validate.js +0 -76
  286. package/dist/utils/createSynapse/validate.js.map +0 -1
  287. package/dist/utils/createSynapse/waitForDependencies.d.ts.map +0 -1
  288. package/dist/utils/createSynapseAwaiter.d.ts.map +0 -1
  289. 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
- import { useSelector } from 'synapse-storage/react'
21
-
22
- const synapse = createSynapse({
23
- storage: new MemoryStorage({
24
- name: 'counter',
25
- initialState: { count: 0 },
26
- }),
27
- createSelectorsFn: (s) => ({
28
- count: s.createSelector((state) => state.count),
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 & Plugins** — extensible sync/async pipelines
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).
@@ -5,4 +5,3 @@
5
5
  * @returns Массив групп элементов
6
6
  */
7
7
  export declare function chunk<T>(array: T[], size?: number): T[][];
8
- //# sourceMappingURL=chunk.util.d.ts.map
@@ -1,2 +1 @@
1
1
  export declare const deepMerge: (target: any, source: any) => any;
2
- //# sourceMappingURL=deepMerge.util.d.ts.map
@@ -47,4 +47,3 @@ export declare function safeCallback<T>(fn: () => T, context: string, logger?: I
47
47
  */
48
48
  export declare function safePromise(promise: Promise<unknown>, context: string, logger?: ILogger | null): void;
49
49
  export {};
50
- //# sourceMappingURL=error-handling.util.d.ts.map
@@ -7,4 +7,3 @@
7
7
  * @returns Новый сглаженный массив
8
8
  */
9
9
  export declare function flatMap<T, R>(array: T[], iteratee: (value: T, index: number, array: T[]) => R | R[]): R[];
10
- //# sourceMappingURL=flatMap.util.d.ts.map
@@ -3,4 +3,3 @@ export * from './deepMerge.util';
3
3
  export * from './error-handling.util';
4
4
  export * from './flatMap.util';
5
5
  export * from './logger-console.util';
6
- //# sourceMappingURL=index.d.ts.map
@@ -6,4 +6,3 @@ export declare const loggerConsole: {
6
6
  groupEnd: () => void;
7
7
  groupCollapsed: (...args: any[]) => void;
8
8
  };
9
- //# sourceMappingURL=logger-console.util.d.ts.map
@@ -43,4 +43,3 @@ export declare class ApiClient<EndpointsFn extends (create: CreateEndpoint) => P
43
43
  destroy(): Promise<void>;
44
44
  }
45
45
  export {};
46
- //# sourceMappingURL=api.module.d.ts.map
@@ -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 readonly endpointSubscribers;
15
+ private endpointSubscribers;
16
16
  /** Сколько раз был вызван метод request */
17
17
  fetchCounts: number;
18
18
  meta: EndpointType['meta'];
19
- private readonly name;
20
- private readonly queryStorage;
21
- private readonly configCurrentEndpoint;
22
- private readonly cacheableHeaderKeys;
23
- private readonly globalRetryConfig;
24
- private readonly baseQueryConfig;
25
- private readonly queryFunction;
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 readonly cacheableHeaders;
28
- private readonly prepareHeaders;
27
+ private cacheableHeaders;
28
+ private prepareHeaders;
29
29
  /** Карта in-flight запросов для дедупликации (cacheKey → Promise) */
30
- private readonly inflightRequests;
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":"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"}
@@ -97,4 +97,3 @@ export declare class QueryStorage {
97
97
  */
98
98
  private removeKeyFromTagIndex;
99
99
  }
100
- //# sourceMappingURL=query-storage.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { handleCleanupError, handleOperationError } from "../../_utils/error-handling.util.js";
2
- import { CacheUtils } from "../../core/storage/utils/cache.util.js";
2
+ import { CacheUtils } from "../utils/cache.util.js";
3
3
 
4
4
 
5
5
 
@@ -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":"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"}
@@ -1,4 +1,3 @@
1
1
  export * from './api.module';
2
2
  export { ResponseFormat, type RetryConfig } from './types/api.interface';
3
3
  export * from './utils/api-helpers';
4
- //# sourceMappingURL=index.d.ts.map
@@ -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
@@ -82,4 +82,3 @@ export interface QueryResult<T = any, E = Error> {
82
82
  fileDownloadResult?: FileDownloadResult;
83
83
  fromCache?: boolean;
84
84
  }
85
- //# sourceMappingURL=query.interface.d.ts.map
@@ -19,4 +19,3 @@ export declare function createUniqueId(name?: string): string;
19
19
  * @returns Обычный объект с заголовками
20
20
  */
21
21
  export declare function headersToObject(headers: Headers): Record<string, string>;
22
- //# sourceMappingURL=api-helpers.d.ts.map
@@ -1,12 +1,9 @@
1
- import { StorageKey, StorageKeyType } from './storage-key';
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
- private static formatDateTime;
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 "./storage-key.js";
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