synapse-storage 4.1.1 → 5.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +9 -2
  96. package/dist/core/storage/utils/state-diff.util.js +30 -3
  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
@@ -1,4 +1,9 @@
1
+ // ────────────────────────────────────────────────────────────────────────────
2
+ // Types
3
+ // ────────────────────────────────────────────────────────────────────────────
1
4
  /**
5
+ * Параметры исполнения действия (мемоизация)
6
+ */ /**
2
7
  * Статусы жизненного цикла API-запроса.
3
8
  *
4
9
  * Сделано const-объектом (а не TS `enum`) намеренно: значения остаются обычными
@@ -15,223 +20,7 @@
15
20
  Error: 'error',
16
21
  Reset: 'reset'
17
22
  };
18
- // ────────────────────────────────────────────────────────────────────────────
19
- // defineAction
20
- // ────────────────────────────────────────────────────────────────────────────
21
- /**
22
- * Создаёт standalone-определение действия.
23
- * Фиксирует TState через первый вызов, TParams/TResult инферятся из action.
24
- *
25
- * @example
26
- * ```ts
27
- * const action = defineAction<MyState>()
28
- *
29
- * export const increment = action({
30
- * action: (storage, amount: number) => {
31
- * storage.update((s) => { s.count += amount })
32
- * return amount
33
- * },
34
- * })
35
- *
36
- * // Void action (без параметров и возврата):
37
- * export const reset = action({
38
- * action: (storage) => {
39
- * storage.update((s) => { s.count = 0 })
40
- * },
41
- * })
42
- * ```
43
- */ function defineAction() {
44
- return (config, executionOptions)=>({
45
- _type: 'action-recipe',
46
- _config: config,
47
- _executionOptions: executionOptions
48
- });
49
- }
50
- // ────────────────────────────────────────────────────────────────────────────
51
- // defineWatcher
52
- // ────────────────────────────────────────────────────────────────────────────
53
- /**
54
- * Создаёт standalone-определение watcher'а.
55
- *
56
- * @example
57
- * ```ts
58
- * export const watchCount = defineWatcher<MyState>()({
59
- * selector: (s) => s.items.length,
60
- * notifyAfterSubscribe: true,
61
- * })
62
- * ```
63
- */ function defineWatcher() {
64
- return (config)=>({
65
- _type: 'watcher-recipe',
66
- _config: config
67
- });
68
- }
69
- // ────────────────────────────────────────────────────────────────────────────
70
- // createApiActions
71
- // ────────────────────────────────────────────────────────────────────────────
72
- /**
73
- * Вычисляет путь к свойству через Proxy-перехват обращений.
74
- */ function resolvePath(accessor) {
75
- const path = [];
76
- const handler = {
77
- get (_, prop) {
78
- if (typeof prop === 'string') {
79
- path.push(prop);
80
- }
81
- return new Proxy({}, handler);
82
- }
83
- };
84
- accessor(new Proxy({}, handler));
85
- return path;
86
- }
87
- /**
88
- * Записывает значение по пути в объекте.
89
- */ function setByPath(obj, path, value) {
90
- let current = obj;
91
- for(let i = 0; i < path.length - 1; i++){
92
- current = current[path[i]];
93
- }
94
- current[path[path.length - 1]] = value;
95
- }
96
- /**
97
- * Создаёт набор шаблонных lifecycle-действий для API-запроса.
98
- * Accessor указывает на поле ApiRequestState в стейте — путь вычисляется автоматически.
99
- *
100
- * @param accessor - Функция-accessor, указывающая на поле ApiRequestState в стейте
101
- *
102
- * @typeParam TInitPayload - Тип payload'а `init`-экшена. По умолчанию `void`
103
- * (init без параметров). Если задать — `init` принимает payload и возвращает
104
- * его, что удобно для intent-паттерна: эффект слушает `init` и читает payload
105
- * намерения (target, фильтры и т.п.), а статус при этом сбрасывается в `idle`.
106
- *
107
- * @example
108
- * ```ts
109
- * const listRequest = createApiActions<MyState>(
110
- * (draft) => draft.api.listRequest
111
- * )
112
- *
113
- * // В dispatcher:
114
- * createDispatcher({ storage }, {
115
- * loadListInit: listRequest.init,
116
- * loadListLoading: listRequest.loading,
117
- * loadListSuccess: listRequest.success,
118
- * loadListFailure: listRequest.failure,
119
- * loadListReset: listRequest.reset,
120
- * })
121
- *
122
- * // init с payload (intent): эффект получит { entityId } из возврата экшена.
123
- * const usersReq = createApiActions<MyState, { entityId: string }>(
124
- * (draft) => draft.api.usersRequest
125
- * )
126
- * ```
127
- */ function createApiActions(accessor) {
128
- const path = resolvePath(accessor);
129
- const action = defineAction();
130
- const update = (storage, request)=>{
131
- storage.update((s)=>setByPath(s, path, request));
132
- };
133
- return {
134
- init: action({
135
- // Сбрасываем статус в idle и пробрасываем payload намерения дальше (эффектам).
136
- action: (storage, payload)=>{
137
- update(storage, {
138
- status: ApiStatus.Idle,
139
- error: null
140
- });
141
- return payload;
142
- }
143
- }),
144
- loading: action({
145
- action: (storage)=>update(storage, {
146
- status: ApiStatus.Loading,
147
- error: null
148
- })
149
- }),
150
- success: action({
151
- action: (storage)=>update(storage, {
152
- status: ApiStatus.Success,
153
- error: null
154
- })
155
- }),
156
- failure: action({
157
- action: (storage, error)=>update(storage, {
158
- status: ApiStatus.Error,
159
- error
160
- })
161
- }),
162
- reset: action({
163
- action: (storage)=>update(storage, {
164
- status: ApiStatus.Reset,
165
- error: null
166
- })
167
- })
168
- };
169
- }
170
- // ────────────────────────────────────────────────────────────────────────────
171
- // createKeyedApiActions
172
- // ────────────────────────────────────────────────────────────────────────────
173
- /**
174
- * Keyed-вариант createApiActions: статус хранится ПО КЛЮЧУ в `Record<string,
175
- * ApiRequestState>`, а не один на весь запрос. Нужен, когда один и тот же запрос
176
- * летит параллельно для нескольких независимых ключей и у каждого свой
177
- * loading/error: комменты по таргетам, детали сущностей по id, per-row действия
178
- * в таблице/ленте, пагинация по секциям — всё, что лежит как `Record<key, data>`.
179
- *
180
- * Все статус-экшены принимают `key` (и возвращают его — удобно эффектам), кроме
181
- * `failure`, который принимает `{ key, error }`. Записи мутируются иммутабельно
182
- * по одному ключу — соседние ключи (их срезы) по ссылке не затрагиваются, что и
183
- * нужно для гранулярной изоляции ре-рендеров (см. useKeyedSliceSelector).
184
- *
185
- * @param accessor - Функция-accessor, указывающая на поле `Record<string, ApiRequestState>`
186
- *
187
- * @example
188
- * ```ts
189
- * const commentsReq = createKeyedApiActions<MyState>((d) => d.api.commentsRequest)
190
- *
191
- * createDispatcher({ storage }, {
192
- * commentsInit: commentsReq.init, // (key) => key
193
- * commentsLoading: commentsReq.loading, // (key) => key
194
- * commentsSuccess: commentsReq.success, // (key) => key
195
- * commentsFailure: commentsReq.failure, // ({ key, error })
196
- * commentsReset: commentsReq.reset, // (key) => key
197
- * })
198
- * ```
199
- */ function createKeyedApiActions(accessor) {
200
- const path = resolvePath(accessor);
201
- const action = defineAction();
202
- const write = (storage, key, request)=>{
203
- // [...path, key] → setByPath доходит до самого Record и кладёт значение по ключу
204
- storage.update((s)=>setByPath(s, [
205
- ...path,
206
- key
207
- ], request));
208
- };
209
- const writer = (status)=>action({
210
- action: (storage, key)=>{
211
- write(storage, key, {
212
- status,
213
- error: null
214
- });
215
- return key;
216
- }
217
- });
218
- return {
219
- init: writer(ApiStatus.Idle),
220
- loading: writer(ApiStatus.Loading),
221
- success: writer(ApiStatus.Success),
222
- reset: writer(ApiStatus.Reset),
223
- failure: action({
224
- action: (storage, payload)=>{
225
- write(storage, payload.key, {
226
- status: ApiStatus.Error,
227
- error: payload.error
228
- });
229
- return payload;
230
- }
231
- })
232
- };
233
- }
234
23
 
235
- export { ApiStatus, createApiActions, createKeyedApiActions, defineAction, defineWatcher };
24
+ export { ApiStatus };
236
25
 
237
26
  //# sourceMappingURL=standalone.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"reactive/dispatcher/standalone.js","sources":["../../../src/reactive/dispatcher/standalone.ts"],"sourcesContent":["import type { IStorage } from '../../core'\n\n// ────────────────────────────────────────────────────────────────────────────\n// Types\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Параметры исполнения действия (мемоизация)\n */\nexport interface ActionExecutionOptions<TParams, TResult> {\n memoize?: (currentArgs: TParams, previousArgs: TParams, previousResult: TResult) => boolean\n}\n\n/**\n * Рецепт действия — standalone-определение, не привязанное к хранилищу.\n * Привязывается к storage при регистрации в createDispatcher.\n */\nexport interface ActionRecipe<TState extends Record<string, any>, TParams, TResult> {\n readonly _type: 'action-recipe'\n readonly _config: {\n action: (storage: IStorage<TState>, params: TParams) => Promise<TResult> | TResult\n meta?: Record<string, any>\n }\n readonly _executionOptions?: ActionExecutionOptions<TParams, TResult>\n}\n\n/**\n * Рецепт watcher'а — standalone-определение, не привязанное к хранилищу.\n */\nexport interface WatcherRecipe<TState extends Record<string, any>, R> {\n readonly _type: 'watcher-recipe'\n readonly _config: {\n selector: (state: TState) => R\n meta?: Record<string, any>\n shouldTrigger?: (prev: R | undefined, current: R) => boolean\n notifyAfterSubscribe?: boolean\n }\n}\n\n/**\n * Статусы жизненного цикла API-запроса.\n *\n * Сделано const-объектом (а не TS `enum`) намеренно: значения остаются обычными\n * строковыми литералами, поэтому `ApiStatus.Loading` и строка `'loading'`\n * взаимозаменяемы и тип обратно совместим с прежним строковым union'ом. С `enum`\n * это не так — его член не присваивается к литералу и наоборот.\n *\n * `ApiStatus` — одновременно значение (для `ApiStatus.Loading` в коде) и тип\n * (union всех статусов).\n */\nexport const ApiStatus = {\n Idle: 'idle',\n Loading: 'loading',\n Success: 'success',\n Error: 'error',\n Reset: 'reset',\n} as const\n\nexport type ApiStatus = (typeof ApiStatus)[keyof typeof ApiStatus]\n\n/**\n * Состояние API-запроса для createApiActions / createKeyedApiActions\n */\nexport interface ApiRequestState {\n status: ApiStatus\n error: string | null\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// defineAction\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Создаёт standalone-определение действия.\n * Фиксирует TState через первый вызов, TParams/TResult инферятся из action.\n *\n * @example\n * ```ts\n * const action = defineAction<MyState>()\n *\n * export const increment = action({\n * action: (storage, amount: number) => {\n * storage.update((s) => { s.count += amount })\n * return amount\n * },\n * })\n *\n * // Void action (без параметров и возврата):\n * export const reset = action({\n * action: (storage) => {\n * storage.update((s) => { s.count = 0 })\n * },\n * })\n * ```\n */\nexport function defineAction<TState extends Record<string, any>>() {\n return <TParams = void, TResult = void>(\n config: {\n action: (storage: IStorage<TState>, params: TParams) => Promise<TResult> | TResult\n meta?: Record<string, any>\n },\n executionOptions?: ActionExecutionOptions<TParams, TResult>,\n ): ActionRecipe<TState, TParams, TResult> => ({\n _type: 'action-recipe' as const,\n _config: config,\n _executionOptions: executionOptions,\n })\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// defineWatcher\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Создаёт standalone-определение watcher'а.\n *\n * @example\n * ```ts\n * export const watchCount = defineWatcher<MyState>()({\n * selector: (s) => s.items.length,\n * notifyAfterSubscribe: true,\n * })\n * ```\n */\nexport function defineWatcher<TState extends Record<string, any>>() {\n return <R>(config: {\n selector: (state: TState) => R\n meta?: Record<string, any>\n shouldTrigger?: (prev: R | undefined, current: R) => boolean\n notifyAfterSubscribe?: boolean\n }): WatcherRecipe<TState, R> => ({\n _type: 'watcher-recipe' as const,\n _config: config,\n })\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// createApiActions\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Вычисляет путь к свойству через Proxy-перехват обращений.\n */\nfunction resolvePath<T>(accessor: (draft: T) => any): string[] {\n const path: string[] = []\n const handler: ProxyHandler<any> = {\n get(_, prop) {\n if (typeof prop === 'string') {\n path.push(prop)\n }\n return new Proxy({}, handler)\n },\n }\n accessor(new Proxy({}, handler) as T)\n return path\n}\n\n/**\n * Записывает значение по пути в объекте.\n */\nfunction setByPath(obj: any, path: string[], value: any): void {\n let current = obj\n for (let i = 0; i < path.length - 1; i++) {\n current = current[path[i]]\n }\n current[path[path.length - 1]] = value\n}\n\n/**\n * Создаёт набор шаблонных lifecycle-действий для API-запроса.\n * Accessor указывает на поле ApiRequestState в стейте — путь вычисляется автоматически.\n *\n * @param accessor - Функция-accessor, указывающая на поле ApiRequestState в стейте\n *\n * @typeParam TInitPayload - Тип payload'а `init`-экшена. По умолчанию `void`\n * (init без параметров). Если задать — `init` принимает payload и возвращает\n * его, что удобно для intent-паттерна: эффект слушает `init` и читает payload\n * намерения (target, фильтры и т.п.), а статус при этом сбрасывается в `idle`.\n *\n * @example\n * ```ts\n * const listRequest = createApiActions<MyState>(\n * (draft) => draft.api.listRequest\n * )\n *\n * // В dispatcher:\n * createDispatcher({ storage }, {\n * loadListInit: listRequest.init,\n * loadListLoading: listRequest.loading,\n * loadListSuccess: listRequest.success,\n * loadListFailure: listRequest.failure,\n * loadListReset: listRequest.reset,\n * })\n *\n * // init с payload (intent): эффект получит { entityId } из возврата экшена.\n * const usersReq = createApiActions<MyState, { entityId: string }>(\n * (draft) => draft.api.usersRequest\n * )\n * ```\n */\nexport function createApiActions<TState extends Record<string, any>, TInitPayload = void>(accessor: (draft: TState) => ApiRequestState) {\n const path = resolvePath(accessor)\n const action = defineAction<TState>()\n\n const update = (storage: IStorage<TState>, request: ApiRequestState) => {\n storage.update((s) => setByPath(s, path, request))\n }\n\n return {\n init: action<TInitPayload, TInitPayload>({\n // Сбрасываем статус в idle и пробрасываем payload намерения дальше (эффектам).\n action: (storage, payload: TInitPayload) => {\n update(storage, { status: ApiStatus.Idle, error: null })\n return payload\n },\n }),\n\n loading: action({\n action: (storage) => update(storage, { status: ApiStatus.Loading, error: null }),\n }),\n\n success: action({\n action: (storage) => update(storage, { status: ApiStatus.Success, error: null }),\n }),\n\n failure: action({\n action: (storage, error: string) => update(storage, { status: ApiStatus.Error, error }),\n }),\n\n reset: action({\n action: (storage) => update(storage, { status: ApiStatus.Reset, error: null }),\n }),\n }\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// createKeyedApiActions\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Keyed-вариант createApiActions: статус хранится ПО КЛЮЧУ в `Record<string,\n * ApiRequestState>`, а не один на весь запрос. Нужен, когда один и тот же запрос\n * летит параллельно для нескольких независимых ключей и у каждого свой\n * loading/error: комменты по таргетам, детали сущностей по id, per-row действия\n * в таблице/ленте, пагинация по секциям — всё, что лежит как `Record<key, data>`.\n *\n * Все статус-экшены принимают `key` (и возвращают его — удобно эффектам), кроме\n * `failure`, который принимает `{ key, error }`. Записи мутируются иммутабельно\n * по одному ключу — соседние ключи (их срезы) по ссылке не затрагиваются, что и\n * нужно для гранулярной изоляции ре-рендеров (см. useKeyedSliceSelector).\n *\n * @param accessor - Функция-accessor, указывающая на поле `Record<string, ApiRequestState>`\n *\n * @example\n * ```ts\n * const commentsReq = createKeyedApiActions<MyState>((d) => d.api.commentsRequest)\n *\n * createDispatcher({ storage }, {\n * commentsInit: commentsReq.init, // (key) => key\n * commentsLoading: commentsReq.loading, // (key) => key\n * commentsSuccess: commentsReq.success, // (key) => key\n * commentsFailure: commentsReq.failure, // ({ key, error })\n * commentsReset: commentsReq.reset, // (key) => key\n * })\n * ```\n */\nexport function createKeyedApiActions<TState extends Record<string, any>>(accessor: (draft: TState) => Record<string, ApiRequestState>) {\n const path = resolvePath(accessor)\n const action = defineAction<TState>()\n\n const write = (storage: IStorage<TState>, key: string, request: ApiRequestState) => {\n // [...path, key] → setByPath доходит до самого Record и кладёт значение по ключу\n storage.update((s) => setByPath(s, [...path, key], request))\n }\n\n const writer = (status: ApiStatus) =>\n action<string, string>({\n action: (storage, key: string) => {\n write(storage, key, { status, error: null })\n return key\n },\n })\n\n return {\n init: writer(ApiStatus.Idle),\n loading: writer(ApiStatus.Loading),\n success: writer(ApiStatus.Success),\n reset: writer(ApiStatus.Reset),\n\n failure: action<{ key: string; error: string }, { key: string; error: string }>({\n action: (storage, payload: { key: string; error: string }) => {\n write(storage, payload.key, { status: ApiStatus.Error, error: payload.error })\n return payload\n },\n }),\n }\n}\n"],"names":["ApiStatus","defineAction","config","executionOptions","defineWatcher","resolvePath","accessor","path","handler","_","prop","Proxy","setByPath","obj","value","current","i","createApiActions","action","update","storage","request","s","payload","error","createKeyedApiActions","write","key","writer","status"],"mappings":"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"}
1
+ {"version":3,"file":"reactive/dispatcher/standalone.js","sources":["../../../src/reactive/dispatcher/standalone.ts"],"sourcesContent":["// ────────────────────────────────────────────────────────────────────────────\n// Types\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Параметры исполнения действия (мемоизация)\n */\nexport interface ActionExecutionOptions<TParams, TResult> {\n memoize?: (currentArgs: TParams, previousArgs: TParams, previousResult: TResult) => boolean\n}\n\n/**\n * Статусы жизненного цикла API-запроса.\n *\n * Сделано const-объектом (а не TS `enum`) намеренно: значения остаются обычными\n * строковыми литералами, поэтому `ApiStatus.Loading` и строка `'loading'`\n * взаимозаменяемы и тип обратно совместим с прежним строковым union'ом. С `enum`\n * это не так — его член не присваивается к литералу и наоборот.\n *\n * `ApiStatus` — одновременно значение (для `ApiStatus.Loading` в коде) и тип\n * (union всех статусов).\n */\nexport const ApiStatus = {\n Idle: 'idle',\n Loading: 'loading',\n Success: 'success',\n Error: 'error',\n Reset: 'reset',\n} as const\n\nexport type ApiStatus = (typeof ApiStatus)[keyof typeof ApiStatus]\n\n/**\n * Состояние API-запроса (`this.apiActions` / `this.keyedApiActions`)\n */\nexport interface ApiRequestState {\n status: ApiStatus\n error: string | null\n}\n"],"names":["ApiStatus"],"mappings":"AAAA,+EAA+E;AAC/E,QAAQ;AACR,+EAA+E;AAE/E;;CAEC,GAKD;;;;;;;;;;CAUC,GACM,MAAMA,SAASA,GAAG;IACvB,MAAM;IACN,SAAS;IACT,SAAS;IACT,OAAO;IACP,OAAO;AACT,EAAU"}
@@ -0,0 +1,62 @@
1
+ import type { Observable } from 'rxjs';
2
+ import type { Action } from '../dispatcher';
3
+ import type { Dispatcher } from '../dispatcher/dispatcher.base';
4
+ import { type Effect, type EffectOptions } from './effects.module';
5
+ /**
6
+ * Контекст эффекта в class-стиле — узкий надтип контекста EffectsModule. Рецепт читает
7
+ * только эти два поля; остальное (services, externalStates) приходит через конструктор
8
+ * класса и захватывается замыканием.
9
+ */
10
+ export interface EffectCtx<TDispatcher, TExternalDispatchers = Record<string, never>> {
11
+ /** Инстанс нашего class-диспетчера: `ofType(d.loadPosts)` + `d.applyPosts(...)`. */
12
+ dispatcher: TDispatcher;
13
+ /** Внешние диспетчеры (их экшены уже влиты в общий `action$`). */
14
+ external: TExternalDispatchers;
15
+ }
16
+ /** Рецепт эффекта: отложенная функция, вызываемая один раз при `EffectsModule.start()`. */
17
+ export type EffectRecipe<TState, TDispatcher, TExternalDispatchers> = (action$: Observable<Action>, state$: Observable<TState>, ctx: EffectCtx<TDispatcher, TExternalDispatchers>) => Observable<unknown>;
18
+ /**
19
+ * Публичный class-based слой эффектов. Эффекты объявляются как поля класса через
20
+ * `this.effect(fn)`. Сервисы и внешние сторы передаются через конструктор и
21
+ * захватываются в замыкание рецепта (`this.api`, `this.core$`).
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * class PostsEffects extends Effects<PostsState, PostsDispatcher> {
26
+ * constructor(private readonly api: PostsEndpoints, private readonly core$: Observable<CoreState>) {
27
+ * super()
28
+ * }
29
+ *
30
+ * readonly loadPosts = this.effect((action$, state$, { dispatcher: d }) =>
31
+ * action$.pipe(ofType(d.loadPosts), validateMap({ apiCall: () => fromRequest(this.api.getPosts.request()) })))
32
+ *
33
+ * override onDestroy() { this.socket.disconnect() }
34
+ * }
35
+ * ```
36
+ *
37
+ * **Правило**: сервисы из конструктора (`this.api`) можно *захватывать в замыкание*
38
+ * рецепта, но не дереференсить прямо в инициализаторе поля — parameter properties
39
+ * присваиваются ПОСЛЕ инициализаторов полей derived-класса.
40
+ */
41
+ export declare abstract class Effects<TState extends Record<string, any>, TDispatcher, TExternalDispatchers extends Record<string, Dispatcher<any>> = Record<string, never>> {
42
+ #private;
43
+ /**
44
+ * Регистрирует рецепт эффекта. Сам рецепт НЕ вызывается при конструировании — он
45
+ * вызывается лениво при `EffectsModule.start()` с реальными потоками. Возвращает тот
46
+ * же `fn`, так что поле остаётся вызываемым рецептом (удобно для юнит-тестов в изоляции).
47
+ *
48
+ * @param fn рецепт `(action$, state$, ctx) => Observable`
49
+ * @param options опции эффекта (например, `resubscribeOnError`)
50
+ */
51
+ protected effect(fn: EffectRecipe<TState, TDispatcher, TExternalDispatchers>, options?: EffectOptions): EffectRecipe<TState, TDispatcher, TExternalDispatchers>;
52
+ /**
53
+ * Список module-совместимых эффектов в порядке объявления полей. Сборщик скармливает
54
+ * его в `effectsModule.addEffects(...)`.
55
+ *
56
+ * Попутно (вне production) предупреждает о полях-функциях, не обёрнутых в `this.effect`.
57
+ * @internal
58
+ */
59
+ getEffects(): Effect[];
60
+ /** Опциональный teardown (закрыть сокет и т.п.) — вызывается сборщиком при `synapse.destroy()`. */
61
+ onDestroy?(): void | Promise<void>;
62
+ }
@@ -0,0 +1,90 @@
1
+ import { logError } from "../../_utils/error-handling.util.js";
2
+ import { EFFECT_NAME, EFFECT_OPTIONS } from "./effects.module.js";
3
+
4
+
5
+
6
+
7
+
8
+ /**
9
+ * Маркер «продукта `this.effect`» на функции-рецепте. По его отсутствию dev-проверка
10
+ * находит поля-функции, которые забыли обернуть в `this.effect(...)` (иначе они не
11
+ * попадут в реестр и молча не запустятся).
12
+ * @internal
13
+ */ const EFFECT_MARKER = Symbol('synapse.effect.recipe');
14
+ /** Имена членов базового класса — не считаются «забытыми эффектами». */ const RESERVED_NAMES = new Set([
15
+ 'onDestroy'
16
+ ]);
17
+ /**
18
+ * Публичный class-based слой эффектов. Эффекты объявляются как поля класса через
19
+ * `this.effect(fn)`. Сервисы и внешние сторы передаются через конструктор и
20
+ * захватываются в замыкание рецепта (`this.api`, `this.core$`).
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * class PostsEffects extends Effects<PostsState, PostsDispatcher> {
25
+ * constructor(private readonly api: PostsEndpoints, private readonly core$: Observable<CoreState>) {
26
+ * super()
27
+ * }
28
+ *
29
+ * readonly loadPosts = this.effect((action$, state$, { dispatcher: d }) =>
30
+ * action$.pipe(ofType(d.loadPosts), validateMap({ apiCall: () => fromRequest(this.api.getPosts.request()) })))
31
+ *
32
+ * override onDestroy() { this.socket.disconnect() }
33
+ * }
34
+ * ```
35
+ *
36
+ * **Правило**: сервисы из конструктора (`this.api`) можно *захватывать в замыкание*
37
+ * рецепта, но не дереференсить прямо в инициализаторе поля — parameter properties
38
+ * присваиваются ПОСЛЕ инициализаторов полей derived-класса.
39
+ */ class Effects {
40
+ /** Реестр module-совместимых эффектов в порядке объявления полей. */ #effects = [];
41
+ /**
42
+ * Регистрирует рецепт эффекта. Сам рецепт НЕ вызывается при конструировании — он
43
+ * вызывается лениво при `EffectsModule.start()` с реальными потоками. Возвращает тот
44
+ * же `fn`, так что поле остаётся вызываемым рецептом (удобно для юнит-тестов в изоляции).
45
+ *
46
+ * @param fn рецепт `(action$, state$, ctx) => Observable`
47
+ * @param options опции эффекта (например, `resubscribeOnError`)
48
+ */ effect(fn, options) {
49
+ // Module-совместимая обёртка: широкий контекст EffectsModule → узкий EffectCtx рецепта.
50
+ const moduleEffect = (action$, state$, context)=>fn(action$, state$, {
51
+ dispatcher: context.dispatcher,
52
+ external: context.externalDispatchers
53
+ });
54
+ if (options) {
55
+ ;
56
+ moduleEffect[EFFECT_OPTIONS] = options;
57
+ }
58
+ this.#effects.push(moduleEffect);
59
+ fn[EFFECT_MARKER] = true;
60
+ return fn;
61
+ }
62
+ /**
63
+ * Список module-совместимых эффектов в порядке объявления полей. Сборщик скармливает
64
+ * его в `effectsModule.addEffects(...)`.
65
+ *
66
+ * Попутно (вне production) предупреждает о полях-функциях, не обёрнутых в `this.effect`.
67
+ * @internal
68
+ */ getEffects() {
69
+ // Имена полей-рецептов идут в порядке объявления — ровно как и #effects (каждый
70
+ // this.effect() пушит в #effects и помечает свой fn). Зипуем их, чтобы EffectsModule
71
+ // мог назвать упавший эффект по имени поля, а не по индексу.
72
+ const recipeNames = [];
73
+ for (const [name, value] of Object.entries(this)){
74
+ if (typeof value === 'function' && value[EFFECT_MARKER]) {
75
+ recipeNames.push(name);
76
+ } else if (process.env.NODE_ENV !== 'production' && typeof value === 'function' && !RESERVED_NAMES.has(name)) {
77
+ logError(`Effects: поле "${name}" — функция, но не обёрнута в this.effect(...). Эффекты регистрируются только через this.effect, иначе они молча не запустятся.`, value, null, 'warn');
78
+ }
79
+ }
80
+ this.#effects.forEach((moduleEffect, i)=>{
81
+ const name = recipeNames[i];
82
+ if (name) moduleEffect[EFFECT_NAME] = name;
83
+ });
84
+ return this.#effects;
85
+ }
86
+ }
87
+
88
+ export { Effects };
89
+
90
+ //# sourceMappingURL=effects.base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reactive/effects/effects.base.js","sources":["../../../src/reactive/effects/effects.base.ts"],"sourcesContent":["import type { Observable } from 'rxjs'\n\nimport { logError } from '../../_utils/error-handling.util'\nimport type { Action } from '../dispatcher'\nimport type { Dispatcher } from '../dispatcher/dispatcher.base'\nimport { type Effect, EFFECT_NAME, EFFECT_OPTIONS, type EffectOptions } from './effects.module'\n\n/**\n * Маркер «продукта `this.effect`» на функции-рецепте. По его отсутствию dev-проверка\n * находит поля-функции, которые забыли обернуть в `this.effect(...)` (иначе они не\n * попадут в реестр и молча не запустятся).\n * @internal\n */\nconst EFFECT_MARKER = Symbol('synapse.effect.recipe')\n\n/** Имена членов базового класса — не считаются «забытыми эффектами». */\nconst RESERVED_NAMES = new Set(['onDestroy'])\n\n/**\n * Контекст эффекта в class-стиле — узкий надтип контекста EffectsModule. Рецепт читает\n * только эти два поля; остальное (services, externalStates) приходит через конструктор\n * класса и захватывается замыканием.\n */\nexport interface EffectCtx<TDispatcher, TExternalDispatchers = Record<string, never>> {\n /** Инстанс нашего class-диспетчера: `ofType(d.loadPosts)` + `d.applyPosts(...)`. */\n dispatcher: TDispatcher\n /** Внешние диспетчеры (их экшены уже влиты в общий `action$`). */\n external: TExternalDispatchers\n}\n\n/** Рецепт эффекта: отложенная функция, вызываемая один раз при `EffectsModule.start()`. */\nexport type EffectRecipe<TState, TDispatcher, TExternalDispatchers> = (\n action$: Observable<Action>,\n state$: Observable<TState>,\n ctx: EffectCtx<TDispatcher, TExternalDispatchers>,\n) => Observable<unknown>\n\n/**\n * Публичный class-based слой эффектов. Эффекты объявляются как поля класса через\n * `this.effect(fn)`. Сервисы и внешние сторы передаются через конструктор и\n * захватываются в замыкание рецепта (`this.api`, `this.core$`).\n *\n * @example\n * ```ts\n * class PostsEffects extends Effects<PostsState, PostsDispatcher> {\n * constructor(private readonly api: PostsEndpoints, private readonly core$: Observable<CoreState>) {\n * super()\n * }\n *\n * readonly loadPosts = this.effect((action$, state$, { dispatcher: d }) =>\n * action$.pipe(ofType(d.loadPosts), validateMap({ apiCall: () => fromRequest(this.api.getPosts.request()) })))\n *\n * override onDestroy() { this.socket.disconnect() }\n * }\n * ```\n *\n * **Правило**: сервисы из конструктора (`this.api`) можно *захватывать в замыкание*\n * рецепта, но не дереференсить прямо в инициализаторе поля — parameter properties\n * присваиваются ПОСЛЕ инициализаторов полей derived-класса.\n */\nexport abstract class Effects<TState extends Record<string, any>, TDispatcher, TExternalDispatchers extends Record<string, Dispatcher<any>> = Record<string, never>> {\n /** Реестр module-совместимых эффектов в порядке объявления полей. */\n readonly #effects: Effect[] = []\n\n /**\n * Регистрирует рецепт эффекта. Сам рецепт НЕ вызывается при конструировании — он\n * вызывается лениво при `EffectsModule.start()` с реальными потоками. Возвращает тот\n * же `fn`, так что поле остаётся вызываемым рецептом (удобно для юнит-тестов в изоляции).\n *\n * @param fn рецепт `(action$, state$, ctx) => Observable`\n * @param options опции эффекта (например, `resubscribeOnError`)\n */\n protected effect(fn: EffectRecipe<TState, TDispatcher, TExternalDispatchers>, options?: EffectOptions): EffectRecipe<TState, TDispatcher, TExternalDispatchers> {\n // Module-совместимая обёртка: широкий контекст EffectsModule → узкий EffectCtx рецепта.\n const moduleEffect: Effect<TState, TDispatcher> = (action$, state$, context) =>\n fn(action$, state$, {\n dispatcher: context.dispatcher,\n external: context.externalDispatchers as unknown as TExternalDispatchers,\n })\n\n if (options) {\n ;(moduleEffect as { [EFFECT_OPTIONS]?: EffectOptions })[EFFECT_OPTIONS] = options\n }\n this.#effects.push(moduleEffect)\n\n // Маркируем сам рецепт, чтобы dev-проверка не приняла его за «забытый эффект».\n ;(fn as { [EFFECT_MARKER]?: true })[EFFECT_MARKER] = true\n return fn\n }\n\n /**\n * Список module-совместимых эффектов в порядке объявления полей. Сборщик скармливает\n * его в `effectsModule.addEffects(...)`.\n *\n * Попутно (вне production) предупреждает о полях-функциях, не обёрнутых в `this.effect`.\n * @internal\n */\n getEffects(): Effect[] {\n // Имена полей-рецептов идут в порядке объявления — ровно как и #effects (каждый\n // this.effect() пушит в #effects и помечает свой fn). Зипуем их, чтобы EffectsModule\n // мог назвать упавший эффект по имени поля, а не по индексу.\n const recipeNames: string[] = []\n for (const [name, value] of Object.entries(this)) {\n if (typeof value === 'function' && (value as { [EFFECT_MARKER]?: true })[EFFECT_MARKER]) {\n recipeNames.push(name)\n } else if (process.env.NODE_ENV !== 'production' && typeof value === 'function' && !RESERVED_NAMES.has(name)) {\n logError(\n `Effects: поле \"${name}\" — функция, но не обёрнута в this.effect(...). Эффекты регистрируются только через this.effect, иначе они молча не запустятся.`,\n value,\n null,\n 'warn',\n )\n }\n }\n\n this.#effects.forEach((moduleEffect, i) => {\n const name = recipeNames[i]\n if (name) (moduleEffect as { [EFFECT_NAME]?: string })[EFFECT_NAME] = name\n })\n\n return this.#effects\n }\n\n /** Опциональный teardown (закрыть сокет и т.п.) — вызывается сборщиком при `synapse.destroy()`. */\n onDestroy?(): void | Promise<void>\n}\n"],"names":["logError","EFFECT_NAME","EFFECT_OPTIONS","EFFECT_MARKER","Symbol","RESERVED_NAMES","Set","Effects","fn","options","moduleEffect","action$","state$","context","recipeNames","name","value","Object","process","i"],"mappings":";;;;;AAE2D;AAGoC;AAE/F;;;;;CAKC,GACD,MAAMG,aAAaA,GAAGC,OAAO;AAE7B,sEAAsE,GACtE,MAAMC,cAAcA,GAAG,IAAIC,IAAI;IAAC;CAAY;AAqB5C;;;;;;;;;;;;;;;;;;;;;;CAsBC,GACM,MAAeC,OAAOA;IAC3B,mEAAmE,GAC1D,QAAQ,GAAa,EAAE;IAEhC;;;;;;;GAOC,GACS,OAAOC,EAA2D,EAAEC,OAAuB,EAA2D;QAC9J,wFAAwF;QACxF,MAAMC,eAA4C,CAACC,SAASC,QAAQC,UAClEL,GAAGG,SAASC,QAAQ;gBAClB,YAAYC,QAAQ,UAAU;gBAC9B,UAAUA,QAAQ,mBAAmB;YACvC;QAEF,IAAIJ,SAAS;;YACTC,YAAqD,CAACR,cAAcA,CAAC,GAAGO;QAC5E;QACA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAACC;QAGjBF,EAAiC,CAACL,aAAaA,CAAC,GAAG;QACrD,OAAOK;IACT;IAEA;;;;;;GAMC,GACD,aAAuB;QACrB,gFAAgF;QAChF,qFAAqF;QACrF,6DAA6D;QAC7D,MAAMM,cAAwB,EAAE;QAChC,KAAK,MAAM,CAACC,MAAMC,MAAM,IAAIC,OAAO,OAAO,CAAC,IAAI,EAAG;YAChD,IAAI,OAAOD,UAAU,cAAeA,KAAoC,CAACb,aAAaA,CAAC,EAAE;gBACvFW,YAAY,IAAI,CAACC;YACnB,OAAO,IAAIG,QAAQ,GAAG,CAAC,QAAQ,KAAK,gBAAgB,OAAOF,UAAU,cAAc,CAACX,cAAcA,CAAC,GAAG,CAACU,OAAO;gBAC5Gf,QAAQA,CACN,CAAC,eAAe,EAAEe,KAAK,+HAA+H,CAAC,EACvJC,OACA,MACA;YAEJ;QACF;QAEA,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAACN,cAAcS;YACnC,MAAMJ,OAAOD,WAAW,CAACK,EAAE;YAC3B,IAAIJ,MAAOL,YAA2C,CAACT,WAAWA,CAAC,GAAGc;QACxE;QAEA,OAAO,IAAI,CAAC,QAAQ;IACtB;AAIF"}