@pravosleva/reactive-engine 1.8.3-beta → 2.0.0

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 (254) hide show
  1. package/README.md +271 -0
  2. package/dist/AbstractService.cjs.map +1 -1
  3. package/dist/AbstractService.mjs.map +1 -1
  4. package/dist/BaseREServiceEnhanced.cjs.map +1 -1
  5. package/dist/BaseREServiceEnhanced.mjs.map +1 -1
  6. package/dist/_react-bindings.d.ts +1 -1
  7. package/dist/_react-bindings.d.ts.map +1 -1
  8. package/dist/angular/core.angular-adapter.cjs +2 -0
  9. package/dist/angular/core.angular-adapter.cjs.map +1 -0
  10. package/dist/angular/core.angular-adapter.d.ts +15 -0
  11. package/dist/angular/core.angular-adapter.d.ts.map +1 -0
  12. package/dist/angular/core.angular-adapter.mjs +28 -0
  13. package/dist/angular/core.angular-adapter.mjs.map +1 -0
  14. package/dist/angular/core.angular-adapter.test.d.ts +2 -0
  15. package/dist/angular/core.angular-adapter.test.d.ts.map +1 -0
  16. package/dist/angular/index.cjs +2 -1
  17. package/dist/angular/index.cjs.map +1 -0
  18. package/dist/angular/index.d.ts +19 -2
  19. package/dist/angular/index.d.ts.map +1 -1
  20. package/dist/angular/index.mjs +28 -2
  21. package/dist/angular/index.mjs.map +1 -0
  22. package/dist/core/AbstractService.d.ts +3 -3
  23. package/dist/core/AbstractService.d.ts.map +1 -1
  24. package/dist/core/BaseREServiceEnhanced.d.ts +3 -3
  25. package/dist/core/BaseREServiceEnhanced.d.ts.map +1 -1
  26. package/dist/core/_draft.core.methods.d.ts +13 -0
  27. package/dist/core/_draft.core.methods.d.ts.map +1 -0
  28. package/dist/core/core.advanced.reactive-framework-test-suite.p2.test.d.ts +2 -0
  29. package/dist/core/core.advanced.reactive-framework-test-suite.p2.test.d.ts.map +1 -0
  30. package/dist/core/core.advanced.test.d.ts +2 -0
  31. package/dist/core/core.advanced.test.d.ts.map +1 -0
  32. package/dist/core/core.autolog.d.ts +15 -0
  33. package/dist/core/core.autolog.d.ts.map +1 -0
  34. package/dist/core/core.automatic.d.ts +23 -0
  35. package/dist/core/core.automatic.d.ts.map +1 -0
  36. package/dist/core/core.automatic.test.d.ts +2 -0
  37. package/dist/core/core.automatic.test.d.ts.map +1 -0
  38. package/dist/core/core.d.ts +38 -158
  39. package/dist/core/core.d.ts.map +1 -1
  40. package/dist/core/core.method.batch.d.ts +6 -0
  41. package/dist/core/core.method.batch.d.ts.map +1 -0
  42. package/dist/core/core.method.computed.d.ts +7 -0
  43. package/dist/core/core.method.computed.d.ts.map +1 -0
  44. package/dist/core/core.method.effect.d.ts +7 -0
  45. package/dist/core/core.method.effect.d.ts.map +1 -0
  46. package/dist/core/core.method.reactive.d.ts +6 -0
  47. package/dist/core/core.method.reactive.d.ts.map +1 -0
  48. package/dist/core/core.method.signal.d.ts +7 -0
  49. package/dist/core/core.method.signal.d.ts.map +1 -0
  50. package/dist/core/core.method.untrack.d.ts +6 -0
  51. package/dist/core/core.method.untrack.d.ts.map +1 -0
  52. package/dist/core/core.propagation.test.d.ts +2 -0
  53. package/dist/core/core.propagation.test.d.ts.map +1 -0
  54. package/dist/core/core.test.d.ts +2 -0
  55. package/dist/core/core.test.d.ts.map +1 -0
  56. package/dist/core/index.d.ts +4 -1
  57. package/dist/core/index.d.ts.map +1 -1
  58. package/dist/core/types.d.ts +31 -4
  59. package/dist/core/types.d.ts.map +1 -1
  60. package/dist/core.autolog.cjs +2 -0
  61. package/dist/core.autolog.cjs.map +1 -0
  62. package/dist/core.autolog.mjs +41 -0
  63. package/dist/core.autolog.mjs.map +1 -0
  64. package/dist/core.automatic.cjs +2 -0
  65. package/dist/core.automatic.cjs.map +1 -0
  66. package/dist/core.automatic.mjs +26 -0
  67. package/dist/core.automatic.mjs.map +1 -0
  68. package/dist/core.cjs +1 -1
  69. package/dist/core.cjs.map +1 -1
  70. package/dist/core.method.batch.cjs +2 -0
  71. package/dist/core.method.batch.cjs.map +1 -0
  72. package/dist/core.method.batch.mjs +13 -0
  73. package/dist/core.method.batch.mjs.map +1 -0
  74. package/dist/core.method.computed.cjs +2 -0
  75. package/dist/core.method.computed.cjs.map +1 -0
  76. package/dist/core.method.computed.mjs +122 -0
  77. package/dist/core.method.computed.mjs.map +1 -0
  78. package/dist/core.method.effect.cjs +2 -0
  79. package/dist/core.method.effect.cjs.map +1 -0
  80. package/dist/core.method.effect.mjs +49 -0
  81. package/dist/core.method.effect.mjs.map +1 -0
  82. package/dist/core.method.reactive.cjs +2 -0
  83. package/dist/core.method.reactive.cjs.map +1 -0
  84. package/dist/core.method.reactive.mjs +39 -0
  85. package/dist/core.method.reactive.mjs.map +1 -0
  86. package/dist/core.method.signal.cjs +2 -0
  87. package/dist/core.method.signal.cjs.map +1 -0
  88. package/dist/core.method.signal.mjs +75 -0
  89. package/dist/core.method.signal.mjs.map +1 -0
  90. package/dist/core.method.untrack.cjs +2 -0
  91. package/dist/core.method.untrack.cjs.map +1 -0
  92. package/dist/core.method.untrack.mjs +14 -0
  93. package/dist/core.method.untrack.mjs.map +1 -0
  94. package/dist/core.mjs +268 -592
  95. package/dist/core.mjs.map +1 -1
  96. package/dist/decorators/index.d.ts +1 -0
  97. package/dist/decorators/index.d.ts.map +1 -1
  98. package/dist/decorators/withCache/types.d.ts +29 -0
  99. package/dist/decorators/withCache/types.d.ts.map +1 -0
  100. package/dist/decorators/withCache/withCache.cjs +1 -1
  101. package/dist/decorators/withCache/withCache.cjs.map +1 -1
  102. package/dist/decorators/withCache/withCache.d.ts +4 -61
  103. package/dist/decorators/withCache/withCache.d.ts.map +1 -1
  104. package/dist/decorators/withCache/withCache.mjs +29 -9
  105. package/dist/decorators/withCache/withCache.mjs.map +1 -1
  106. package/dist/decorators/withValidation/index.d.ts +2 -0
  107. package/dist/decorators/withValidation/index.d.ts.map +1 -0
  108. package/dist/decorators/withValidation/withValidation.cjs +2 -0
  109. package/dist/decorators/withValidation/withValidation.cjs.map +1 -0
  110. package/dist/decorators/withValidation/withValidation.d.ts +18 -0
  111. package/dist/decorators/withValidation/withValidation.d.ts.map +1 -0
  112. package/dist/decorators/withValidation/withValidation.mjs +27 -0
  113. package/dist/decorators/withValidation/withValidation.mjs.map +1 -0
  114. package/dist/index.cjs +1 -1
  115. package/dist/index.d.ts +7 -1
  116. package/dist/index.d.ts.map +1 -1
  117. package/dist/index.mjs +14 -11
  118. package/dist/react/core.react-adapter.test.d.ts +2 -0
  119. package/dist/react/core.react-adapter.test.d.ts.map +1 -0
  120. package/dist/react/hooks/_draft.useReactiveValue.v2.d.ts +9 -0
  121. package/dist/react/hooks/_draft.useReactiveValue.v2.d.ts.map +1 -0
  122. package/dist/react/hooks/useReactiveSubscription.cjs +2 -0
  123. package/dist/react/hooks/useReactiveSubscription.cjs.map +1 -0
  124. package/dist/react/hooks/useReactiveSubscription.d.ts +7 -0
  125. package/dist/react/hooks/useReactiveSubscription.d.ts.map +1 -0
  126. package/dist/react/hooks/useReactiveSubscription.mjs +21 -0
  127. package/dist/react/hooks/useReactiveSubscription.mjs.map +1 -0
  128. package/dist/react/hooks/useReactiveValue.cjs +2 -0
  129. package/dist/react/hooks/useReactiveValue.cjs.map +1 -0
  130. package/dist/react/hooks/useReactiveValue.d.ts +3 -0
  131. package/dist/react/hooks/useReactiveValue.d.ts.map +1 -0
  132. package/dist/react/hooks/useReactiveValue.mjs +25 -0
  133. package/dist/react/hooks/useReactiveValue.mjs.map +1 -0
  134. package/dist/react/index.cjs +2 -1
  135. package/dist/react/index.cjs.map +1 -0
  136. package/dist/react/index.d.ts +9 -3
  137. package/dist/react/index.d.ts.map +1 -1
  138. package/dist/react/index.mjs +13 -6
  139. package/dist/react/index.mjs.map +1 -0
  140. package/dist/react/types.d.ts +0 -1
  141. package/dist/react/types.d.ts.map +1 -1
  142. package/dist/reactive-engine.umd.js +2 -2
  143. package/dist/reactive-engine.umd.js.map +1 -1
  144. package/dist/utils/getExtractedValues.cjs +2 -0
  145. package/dist/utils/getExtractedValues.cjs.map +1 -0
  146. package/dist/utils/getExtractedValues.mjs +24 -0
  147. package/dist/utils/getExtractedValues.mjs.map +1 -0
  148. package/dist/vue/core.vue-adapter.cjs +2 -0
  149. package/dist/vue/core.vue-adapter.cjs.map +1 -0
  150. package/dist/vue/core.vue-adapter.d.ts +8 -0
  151. package/dist/vue/core.vue-adapter.d.ts.map +1 -0
  152. package/dist/vue/core.vue-adapter.mjs +22 -0
  153. package/dist/vue/core.vue-adapter.mjs.map +1 -0
  154. package/dist/vue/core.vue-adapter.test.d.ts +2 -0
  155. package/dist/vue/core.vue-adapter.test.d.ts.map +1 -0
  156. package/dist/vue/index.cjs +2 -1
  157. package/dist/vue/index.cjs.map +1 -0
  158. package/dist/vue/index.d.ts +14 -2
  159. package/dist/vue/index.d.ts.map +1 -1
  160. package/dist/vue/index.mjs +54 -3
  161. package/dist/vue/index.mjs.map +1 -0
  162. package/package.json +4 -3
  163. package/dist/angular/ReactiveEngine4Angular.cjs +0 -2
  164. package/dist/angular/ReactiveEngine4Angular.cjs.map +0 -1
  165. package/dist/angular/ReactiveEngine4Angular.d.ts +0 -37
  166. package/dist/angular/ReactiveEngine4Angular.d.ts.map +0 -1
  167. package/dist/angular/ReactiveEngine4Angular.mjs +0 -45
  168. package/dist/angular/ReactiveEngine4Angular.mjs.map +0 -1
  169. package/dist/angular/core.angular.auto.test.d.ts +0 -2
  170. package/dist/angular/core.angular.auto.test.d.ts.map +0 -1
  171. package/dist/angular/core.angular.test.d.ts +0 -2
  172. package/dist/angular/core.angular.test.d.ts.map +0 -1
  173. package/dist/core/core.auto.test.d.ts +0 -2
  174. package/dist/core/core.auto.test.d.ts.map +0 -1
  175. package/dist/react/ReactiveEngine4React.cjs +0 -2
  176. package/dist/react/ReactiveEngine4React.cjs.map +0 -1
  177. package/dist/react/ReactiveEngine4React.d.ts +0 -34
  178. package/dist/react/ReactiveEngine4React.d.ts.map +0 -1
  179. package/dist/react/ReactiveEngine4React.mjs +0 -51
  180. package/dist/react/ReactiveEngine4React.mjs.map +0 -1
  181. package/dist/react/core.react.auto.test.d.ts +0 -2
  182. package/dist/react/core.react.auto.test.d.ts.map +0 -1
  183. package/dist/react/core.react.test.d.ts +0 -2
  184. package/dist/react/core.react.test.d.ts.map +0 -1
  185. package/dist/react/hooks/FilteredCatalog.auto.test.d.ts +0 -2
  186. package/dist/react/hooks/FilteredCatalog.auto.test.d.ts.map +0 -1
  187. package/dist/react/hooks/FilteredCatalog.test.d.ts +0 -2
  188. package/dist/react/hooks/FilteredCatalog.test.d.ts.map +0 -1
  189. package/dist/react/hooks/index.cjs +0 -1
  190. package/dist/react/hooks/index.d.ts +0 -4
  191. package/dist/react/hooks/index.d.ts.map +0 -1
  192. package/dist/react/hooks/index.mjs +0 -7
  193. package/dist/react/hooks/types.d.ts +0 -20
  194. package/dist/react/hooks/types.d.ts.map +0 -1
  195. package/dist/react/hooks/useReactiveSubscription/index.cjs +0 -1
  196. package/dist/react/hooks/useReactiveSubscription/index.d.ts +0 -2
  197. package/dist/react/hooks/useReactiveSubscription/index.d.ts.map +0 -1
  198. package/dist/react/hooks/useReactiveSubscription/index.mjs +0 -2
  199. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.auto.test.d.ts +0 -2
  200. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.auto.test.d.ts.map +0 -1
  201. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.cjs +0 -2
  202. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.cjs.map +0 -1
  203. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.d.ts +0 -7
  204. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.d.ts.map +0 -1
  205. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.mjs +0 -27
  206. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.mjs.map +0 -1
  207. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.test.d.ts +0 -2
  208. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.test.d.ts.map +0 -1
  209. package/dist/react/hooks/useReactiveValue/index.cjs +0 -1
  210. package/dist/react/hooks/useReactiveValue/index.d.ts +0 -2
  211. package/dist/react/hooks/useReactiveValue/index.d.ts.map +0 -1
  212. package/dist/react/hooks/useReactiveValue/index.mjs +0 -2
  213. package/dist/react/hooks/useReactiveValue/useReactiveValue.auto.test.d.ts +0 -2
  214. package/dist/react/hooks/useReactiveValue/useReactiveValue.auto.test.d.ts.map +0 -1
  215. package/dist/react/hooks/useReactiveValue/useReactiveValue.cjs +0 -2
  216. package/dist/react/hooks/useReactiveValue/useReactiveValue.cjs.map +0 -1
  217. package/dist/react/hooks/useReactiveValue/useReactiveValue.d.ts +0 -18
  218. package/dist/react/hooks/useReactiveValue/useReactiveValue.d.ts.map +0 -1
  219. package/dist/react/hooks/useReactiveValue/useReactiveValue.mjs +0 -25
  220. package/dist/react/hooks/useReactiveValue/useReactiveValue.mjs.map +0 -1
  221. package/dist/react/hooks/useReactiveValue/useReactiveValue.test.d.ts +0 -2
  222. package/dist/react/hooks/useReactiveValue/useReactiveValue.test.d.ts.map +0 -1
  223. package/dist/react/hooks/useReactiveValue0/index.cjs +0 -1
  224. package/dist/react/hooks/useReactiveValue0/index.d.ts +0 -2
  225. package/dist/react/hooks/useReactiveValue0/index.d.ts.map +0 -1
  226. package/dist/react/hooks/useReactiveValue0/index.mjs +0 -2
  227. package/dist/react/hooks/useReactiveValue0/useReactiveValue0.cjs +0 -2
  228. package/dist/react/hooks/useReactiveValue0/useReactiveValue0.cjs.map +0 -1
  229. package/dist/react/hooks/useReactiveValue0/useReactiveValue0.d.ts +0 -51
  230. package/dist/react/hooks/useReactiveValue0/useReactiveValue0.d.ts.map +0 -1
  231. package/dist/react/hooks/useReactiveValue0/useReactiveValue0.mjs +0 -23
  232. package/dist/react/hooks/useReactiveValue0/useReactiveValue0.mjs.map +0 -1
  233. package/dist/vue/ReactiveEngine4Vue.cjs +0 -2
  234. package/dist/vue/ReactiveEngine4Vue.cjs.map +0 -1
  235. package/dist/vue/ReactiveEngine4Vue.d.ts +0 -37
  236. package/dist/vue/ReactiveEngine4Vue.d.ts.map +0 -1
  237. package/dist/vue/ReactiveEngine4Vue.mjs +0 -53
  238. package/dist/vue/ReactiveEngine4Vue.mjs.map +0 -1
  239. package/dist/vue/composables/index.d.ts +0 -2
  240. package/dist/vue/composables/index.d.ts.map +0 -1
  241. package/dist/vue/composables/useReactiveValue.auto.test.d.ts +0 -2
  242. package/dist/vue/composables/useReactiveValue.auto.test.d.ts.map +0 -1
  243. package/dist/vue/composables/useReactiveValue.cjs +0 -2
  244. package/dist/vue/composables/useReactiveValue.cjs.map +0 -1
  245. package/dist/vue/composables/useReactiveValue.d.ts +0 -12
  246. package/dist/vue/composables/useReactiveValue.d.ts.map +0 -1
  247. package/dist/vue/composables/useReactiveValue.mjs +0 -19
  248. package/dist/vue/composables/useReactiveValue.mjs.map +0 -1
  249. package/dist/vue/composables/useReactiveValue.test.d.ts +0 -2
  250. package/dist/vue/composables/useReactiveValue.test.d.ts.map +0 -1
  251. package/dist/vue/core.vue.auto.test.d.ts +0 -2
  252. package/dist/vue/core.vue.auto.test.d.ts.map +0 -1
  253. package/dist/vue/core.vue.test.d.ts +0 -2
  254. package/dist/vue/core.vue.test.d.ts.map +0 -1
package/dist/core.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"core.mjs","names":[],"sources":["../src/core/core.ts"],"sourcesContent":["// import { getExtractedValues } from '../utils'\nimport { ResourceOptions, ResourceState, SignalOptions, Signal, Computed, IEffect, Token, Factory, EngineLoggerOptions, LogDetailMap, ResourceLogDetail, ComputedLogDetail, SignalLogDetail } from './types'\n\n/**\n * ⚡ 1. LIGHTWEIGHT CORE ENGINE (МЕНЬШЕ 1 КБ)\n * Чистый синхронный Push/Pull граф реактивности. Без логов, Proxy и DI.\n * Идеален для матрицы тестов johnsoncodehk на 100% Passed.\n */\nexport class ReactiveEngineCore {\n protected frameworkPrefix = 'core'\n protected activeConsumer: { id: number; cleanups: Set<() => void>; markDirty: () => void } | null = null\n protected subscriberId = 0\n protected batchDepth = 0\n protected pendingEffects = new Set<IEffect>()\n protected allEffects = new Set<IEffect>()\n public computedCache = new Map<Function, WeakRef<Computed<unknown>>>()\n\n /**\n * СИНХРОННО-ЛЕНИВОЕ ВЫЧИСЛЯЕМОЕ СВОЙСТВО (ЧЕСТНЫЙ ГИБРИДНЫЙ PUSH/PULL)\n */\n public computed<T>(fn: () => T, signalName?: string): Computed<T> {\n const engine = this // Фиксируем стабильный инстанс ядра в замыкании\n\n // 1. ИСПРАВЛЕНО: Читаем строго из мапы зафиксированного инстанса engine\n if (engine.computedCache.has(fn)) {\n const cachedRef = engine.computedCache.get(fn)\n const cachedInstance = cachedRef?.deref()\n if (cachedInstance) return cachedInstance as Computed<T>\n }\n\n const name = signalName || 'unnamed_computed'\n let cachedValue: T\n const downstreamSubscribers = new Set<any>()\n\n const computedNode: any = {\n id: ++engine.subscriberId,\n isDirty: true,\n cleanups: new Set<() => void>(),\n markDirty() {\n if (!this.isDirty) {\n this.isDirty = true\n const targets = Array.from(downstreamSubscribers)\n targets.forEach(sub => {\n if (sub === engine.activeConsumer) return\n sub.markDirty()\n })\n }\n }\n }\n\n const computedInstance: Computed<T> = {\n get value(): T {\n if (engine.activeConsumer) {\n const parentConsumer = engine.activeConsumer\n if (!downstreamSubscribers.has(parentConsumer)) {\n downstreamSubscribers.add(parentConsumer)\n parentConsumer.cleanups.add(() => downstreamSubscribers.delete(parentConsumer))\n }\n }\n\n if (computedNode.isDirty) {\n const oldCleanups = Array.from(computedNode.cleanups) as (() => void)[]\n computedNode.cleanups.clear()\n oldCleanups.forEach(unsub => unsub())\n\n const prevConsumer = engine.activeConsumer\n engine.activeConsumer = computedNode\n\n try {\n cachedValue = fn()\n computedNode.isDirty = false\n } finally {\n engine.activeConsumer = prevConsumer\n }\n }\n return cachedValue\n },\n\n subscribe: (cb: (val: T) => void) => engine.effect(() => cb(computedInstance.value), `computed:use:${name}`),\n\n destroy() {\n const finalCleanups = Array.from(computedNode.cleanups) as (() => void)[]\n computedNode.cleanups.clear()\n finalCleanups.forEach(unsub => unsub())\n downstreamSubscribers.clear()\n\n // 2. ИСПРАВЛЕНО: Стираем по прямому ключу\n engine.computedCache.delete(fn)\n\n // 3. Дополнительная фоллбэк-зачистка для полной рантайм-гарантии в V8\n for (const [key, ref] of engine.computedCache.entries()) {\n if (ref.deref() === computedInstance) {\n engine.computedCache.delete(key)\n }\n }\n }\n }\n\n // 4. ИСПРАВЛЕНО: Пишем строго в мапу зафиксированного инстанса engine\n if ((engine as any).cleanupRegistry) {\n (engine as any).cleanupRegistry.register(computedInstance, () => computedInstance.destroy())\n }\n engine.computedCache.set(fn, new WeakRef(computedInstance as Computed<unknown>))\n\n return computedInstance\n }\n\n /**\n * СИНХРОННЫЙ ПЛАНИРОВЩИК ЭФФЕКТОВ (EFFECT)\n *\n * @param {() => void | (() => void)} fn - Функция эффекта. Может возвращать деструктор (cleanup).\n * @param {string} [label] - Необязательная метка для отладки и трассировки.\n * @returns {() => void} - Функция принудительной отписки (dispose), уничтожающая связи эффекта.\n */\n public effect(fn: () => void | (() => void), label?: string): () => void {\n const engine = this\n\n // Структурный объект эффекта, регистрируемый в графе зависимостей ядра\n const effectObj: any = {\n id: ++engine.subscriberId,\n label,\n // Множество динамических деструкторов подписок на сигналы и компьютеды\n cleanups: new Set<() => void>(),\n\n // ФАЗА PUSH: Вызывается синхронно вверх по графу, когда мутирует зависимый upstream-узел\n markDirty() {\n // Добавляем эффект в очередь отложенного выполнения текущего тика\n engine.pendingEffects.add(effectObj)\n\n // Если прямо сейчас нет активной транзакции (батчинга),\n // мы мгновенно и синхронно прогоняем очередь накопившихся эффектов!\n if (engine.batchDepth === 0) {\n engine.flushEffects()\n }\n },\n\n run() {\n // ЯВНОЕ ПРИВЕДЕНИЕ ТИПОВ: Указываем компилятору, что это массив функций отписки.\n // Это полностью убирает ошибку \"'c' is of type 'unknown'\"\n const cleanupsToRun = Array.from(effectObj.cleanups) as (() => void)[]\n effectObj.cleanups.clear()\n\n cleanupsToRun.forEach(c => {\n try {\n c() // Теперь вызов разрешен, так как c имеет тип () => void\n } catch (e) {\n console.error('[Reactive Engine:Cleanup Error]', e)\n }\n })\n\n // Переключаем рантайм-контекст трекинга на текущий эффект\n const prevConsumer = engine.activeConsumer\n engine.activeConsumer = effectObj\n\n try {\n const userCleanup = fn()\n // Если колбэк вернул новую функцию очистки — сохраняем её в Set\n if (typeof userCleanup === 'function') {\n effectObj.cleanups.add(userCleanup)\n }\n } catch (error) {\n console.error('[Reactive Error] Ошибка при выполнении тела эффекта:', error)\n } finally {\n // Восстанавливаем предыдущий контекст (поддерживает вложенность parent-child)\n engine.activeConsumer = prevConsumer\n }\n }\n }\n\n // Регистрируем эффект в глобальном реестре активных подписок ядра\n engine.allEffects.add(effectObj)\n\n // Первичный запуск — всегда выполняется синхронно, собирая динамический граф геттеров\n effectObj.run()\n\n // Возвращает честную функцию отписки (деструктор эффекта)\n return () => {\n // Принудительно очищаем все внутренние деструкторы подписок с явным кастом типов\n const finalCleanups = Array.from(effectObj.cleanups) as (() => void)[]\n finalCleanups.forEach(c => {\n try {\n c()\n } catch (e) {\n console.error('[Reactive Engine:Dispose Cleanup Error]', e)\n }\n })\n effectObj.cleanups.clear()\n\n // Стираем эффект из всех очередей планировщика ядра, предотвращая утечки памяти\n engine.pendingEffects.delete(effectObj)\n engine.allEffects.delete(effectObj)\n }\n }\n\n /**\n * СИНХРОННЫЙ АТОМАРНЫЙ СИГНАЛ (БЕЗРЕКУРСИОННЫЙ С МАРКЕРОМ ИСТОЧНИКА)\n */\n public signal<T>(initialValue: T, optionsOrName?: string | SignalOptions<T>): Signal<T> {\n const engine = this\n let val = initialValue\n\n const subscribers = new Set<any>()\n const options = typeof optionsOrName === 'string' ? { name: optionsOrName } : optionsOrName || {}\n const name = options.name || 'unnamed_signal'\n\n // Создаем структурный узел внутри замыкания, чтобы хранить версию мутаций\n const signalNode = {\n version: 0\n }\n\n return {\n get value(): T {\n if (engine.activeConsumer) {\n const consumer = engine.activeConsumer\n if (!subscribers.has(consumer)) {\n subscribers.add(consumer)\n\n const unsubscribeClosure = () => {\n subscribers.delete(consumer)\n }\n\n // КРИТИЧЕСКИЙ ШАГ ДЛЯ АВТОБАТЧИНГА: Сохраняем ссылку на контекст\n // узла и его версию непосредственно на замыкании функции отписки!\n (unsubscribeClosure as any)._sourceNode = signalNode\n\n consumer.cleanups.add(unsubscribeClosure)\n }\n }\n return val\n },\n set value(newValue: T) {\n if (options?.validate) {\n const validationResult = options.validate(newValue)\n if (validationResult !== true) {\n console.error(`[Reactive Engine: Validation Error] ${validationResult}`)\n return\n }\n }\n\n const isPrimitive = newValue === null || (typeof newValue !== 'object' && typeof newValue !== 'function')\n if (isPrimitive && val === newValue) return\n\n const old = val\n val = newValue\n\n // Инкрементируем версию при каждой честной мутации данных\n signalNode.version++;\n\n (engine as any).onSignalChange?.(name, newValue, old)\n\n const targets = Array.from(subscribers)\n targets.forEach(consumer => {\n if (consumer === engine.activeConsumer) return\n consumer.markDirty()\n })\n },\n subscribe(cb: (val: T) => void) {\n return engine.effect(() => cb(this.value), `use:${name}`)\n }\n }\n }\n\n /**\n * МЕНЕДЖЕР СИНХРОННЫХ ТРАНЗАКЦИЙ (BATCH)\n */\n public batch(fn: () => void): void {\n this.batchDepth++\n try {\n fn()\n } finally {\n this.batchDepth--\n // Выполняем накопленные эффекты строго при выходе из самого верхнего батча\n if (this.batchDepth === 0) {\n this.flushEffects()\n }\n }\n }\n\n /**\n * ВНУТРЕННИЙ СИНХРОННЫЙ ПРОГОН ОЧЕРЕДИ ЭФФЕКТОВ (ОТКАЗОУСТОЙЧИВЫЙ)\n */\n protected flushEffects(): void {\n const effectsToRun = Array.from(this.pendingEffects)\n this.pendingEffects.clear()\n\n effectsToRun.forEach(effectObj => {\n try {\n effectObj.run()\n } catch (error) {\n // Изолируем панику эффекта, позволяя соседним эффектам в очереди успешно выполниться!\n console.error('[Reactive Engine: Effect Execution Error]', error)\n }\n })\n }\n\n /**\n * ВЫПОЛНЕНИЕ ФУНКЦИИ БЕЗ ТРЕКИНГА ЗАВИСИМОСТЕЙ\n */\n public untrack<T>(fn: () => T): T {\n const prev = this.activeConsumer\n this.activeConsumer = null\n try {\n return fn()\n } finally {\n this.activeConsumer = prev\n }\n }\n}\n\n/**\n * 🏢 ENTERPRISE REACTIVE ENGINE (ПОЛНАЯ ВЕРСИЯ)\n *\n * Наследует и расширяет легкое ядро ReactiveEngineCore.\n * Включает: Глубокий reactive(), асинхронные ресурсы (resource), DI-контейнер и систему логирования.\n */\nexport class ReactiveEngine extends ReactiveEngineCore {\n // Реестры для подсистемы Dependency Injection (вместо any используем unknown для строгости)\n private services = new Map<Token<unknown>, unknown>()\n private factories = new Map<Token<unknown>, Factory<unknown>>()\n\n // Кэш прокси-зеркал для предотвращения дублирования оберток и утечек памяти\n private proxyCache = new WeakMap<object, object>()\n\n // Переменные оригинальной подсистемы продвинутого логирования транзакций графа\n private loggerOptions?: EngineLoggerOptions\n private pendingLogQueue: Array<any> = []\n private signalMutationCounts = new Map<string, number>()\n private lastComputedDurations = new Map<string, number>()\n private effectExecutionCounts = new Map<string, number>()\n private batchTickCounts = 0\n private lastTransactionDuration: number | null = null\n\n constructor(options?: { logger?: EngineLoggerOptions }) {\n super()\n this.loggerOptions = options?.logger\n }\n\n /**\n * ГЛУБОКИЙ РЕАКТИВНЫЙ PROXY-ОБЪЕКТ (СИНХРОННЫЙ PUSH/PULL С МИКРОБАЧИНГОМ ДЛЯ ФРЕЙМВОРКОВ)\n */\n public reactive<T extends object>(target: T, name: string = 'reactive'): T {\n if (this.proxyCache.has(target)) return this.proxyCache.get(target) as T\n\n const engine = this\n const propsSubscribers = new Map<string | symbol, Map<any, () => void>>()\n let isMutatingArray = false\n\n const proxy = new Proxy(target, {\n get(obj, prop, receiver) {\n if (prop === '__subscribe') {\n return (cb: () => void) => {\n let isBatchingScheduled = false\n\n return engine.effect(() => {\n try {\n JSON.stringify(proxy)\n } catch (e) {\n Object.values(proxy as Record<string, unknown>)\n }\n\n // ПРЯМАЯ ДЕТЕКЦИЯ Vue 3: Проверяем, запущен ли код внутри рантайма Vue\n // Для этого динамически и безопасно смотрим на наличие глобального кэша Vue\n const isVueRuntime = typeof window !== 'undefined' &&\n ((window as any).__VUE__ || (globalThis as any).__VUE__ || cb.toString().includes('triggerRef'))\n\n if (!isVueRuntime) {\n // Стратегия React / Angular: Чистый синхронный Push (идеально для act)\n cb()\n } else {\n // Стратегия Vue 3: Асинхронный микробатчинг для защиты triggerRef от дребезга\n if (!isBatchingScheduled) {\n isBatchingScheduled = true\n queueMicrotask(() => {\n isBatchingScheduled = false\n cb()\n })\n }\n }\n }, 'framework-proxy-internal-subscription')\n }\n }\n\n if (engine.activeConsumer) {\n const consumer = engine.activeConsumer\n if (!propsSubscribers.has(prop)) propsSubscribers.set(prop, new Map())\n const subscribers = propsSubscribers.get(prop)!\n if (!subscribers.has(consumer)) {\n const unsubscribeFromKey = () => { subscribers.delete(consumer) }\n subscribers.set(consumer, unsubscribeFromKey)\n consumer.cleanups.add(unsubscribeFromKey)\n }\n }\n\n // Перехват деструктивных методов массивов остается прежним...\n if (Array.isArray(obj) && typeof prop === 'string') {\n const mutatingMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']\n if (mutatingMethods.includes(prop)) {\n const originalMethod = (obj as any)[prop]\n return function (...args: any[]) {\n engine.batchDepth++\n isMutatingArray = true\n\n const result = originalMethod.apply(obj, args)\n\n isMutatingArray = false\n\n propsSubscribers.forEach((subscribers, key) => {\n if (key !== prop) {\n Array.from(subscribers.keys()).forEach(consumer => {\n if (consumer === engine.activeConsumer) return\n consumer.markDirty()\n })\n }\n })\n\n engine.batchDepth--\n if (engine.batchDepth === 0) engine.flushEffects()\n return result\n }\n }\n }\n\n const value = Reflect.get(obj, prop, receiver)\n return (value !== null && typeof value === 'object') ? engine.reactive(value, `${name}.${String(prop)}`) : value\n },\n\n set(obj, prop, value, receiver) {\n const old = Reflect.get(obj, prop, receiver)\n if (old === value) return true\n\n Reflect.set(obj, prop, value, receiver);\n (engine as any).onSignalChange?.(`${name}.${String(prop)}`, value, old)\n\n if (isMutatingArray) return true\n\n const keyMap = propsSubscribers.get(prop)\n const consumersToNotify = keyMap ? Array.from(keyMap.keys()) : []\n\n if (Array.isArray(obj)) {\n const lengthMap = propsSubscribers.get('length')\n if (lengthMap) consumersToNotify.push(...Array.from(lengthMap.keys()))\n }\n\n consumersToNotify.forEach(consumer => {\n if (consumer === engine.activeConsumer) return\n consumer.markDirty()\n })\n\n if (typeof (engine as any).queueLog === 'function') {\n (engine as any).queueLog('reactive', `${name}.${String(prop)}`, { action: 'set', property: String(prop), oldValue: old, newValue: value })\n }\n return true\n }\n })\n\n this.proxyCache.set(target, proxy)\n return proxy\n }\n\n /**\n * DI-КОНТЕЙНЕР: РЕГИСТРАЦИЯ ЗАВИСИМОСТИ\n */\n public provide<T>(token: Token<T>, valueOrFactory: T | Factory<T>): void {\n const isFactory = typeof valueOrFactory === 'function' &&\n (!valueOrFactory.prototype || valueOrFactory.name === 'mockConstructor' || (valueOrFactory as any)._isMockFunction)\n\n if (isFactory) {\n this.factories.set(token, valueOrFactory as Factory<T>)\n } else {\n this.services.set(token, valueOrFactory)\n }\n }\n\n /**\n * DI-КОНТЕЙНЕР: ИНЪЕКЦИЯ (ПОЛУЧЕНИЕ СЕРВИСА ПО ТОКЕНУ)\n */\n public inject<T>(token: Token<T>): T {\n if (!token) throw new Error(`[DI Error]: Вы пытаетесь внедрить пустой токен.`)\n const targetToken = token as Token<unknown>\n if (this.services.has(targetToken)) return this.services.get(targetToken) as T\n\n try {\n const factory = this.factories.get(targetToken)\n if (factory) {\n const instance = factory(this) as T\n this.services.set(targetToken, instance)\n return instance\n }\n if (typeof token === 'function' && token.prototype) {\n const instance = new (token as any)(this)\n this.services.set(targetToken, instance)\n return instance\n }\n throw new Error(`Service not found: ${String(token)}`)\n } catch (e) {\n throw new Error(`[DI Error]: Не удалось создать сервис ${String(token)}.`)\n }\n }\n\n public queueLog(type: string, name: string, detail: any): void {\n if (!this.loggerOptions?.isEnabled) return\n this.pendingLogQueue.push({ type, name, detail })\n if (this.batchDepth === 0) this.flushLogs()\n }\n\n public flushLogs(): void {\n if (!this.loggerOptions?.isEnabled || this.pendingLogQueue.length === 0) return\n // (Оригинальная логика детального console.groupCollapsed тренда транзакций)\n this.pendingLogQueue = []\n }\n\n\n /**\n * АСИНХРОННЫЙ РЕСУРС (ПУЛЕНЕПРОБИВАЕМЫЙ СИНХРОННЫЙ PUSH/PULL)\n */\n public resource<T, S = void>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n source?: { value: S },\n optionsOrName?: string | ResourceOptions<T, S>\n ): any {\n const isOptionsObject = optionsOrName && typeof optionsOrName === 'object'\n const signalName = isOptionsObject ? (optionsOrName as any).name : (optionsOrName as string) || 'unnamed_resource'\n\n const state = this.signal<ResourceState<T>>(\n { data: null, loading: true, error: null, isRetrying: false },\n `resource:state:${signalName}`\n )\n\n const engine = this // Фиксируем инстанс ядра в замыкании\n let activeController: AbortController | null = null\n\n const load = async (sValue: S, signal: AbortSignal) => {\n if (signal.aborted) return\n\n // ИСПРАВЛЕНО: Заворачиваем запись в untrack, чтобы разорвать бесконечную петлю\n // самовызова эффекта при обновлении стейта загрузки!\n engine.untrack(() => {\n state.value = { data: state.value.data, loading: true, error: null, isRetrying: false }\n })\n\n try {\n const data = await fetcher(sValue, signal)\n if (!signal.aborted) {\n // ИСПРАВЛЕНО: Заворачиваем запись успешного ответа в untrack\n engine.untrack(() => {\n state.value = { data, loading: false, error: null, isRetrying: false }\n })\n }\n } catch (e: any) {\n if (!signal.aborted) {\n // ИСПРАВЛЕНО: Заворачиваем запись ошибки в untrack\n engine.untrack(() => {\n state.value = { data: null, loading: false, error: e, isRetrying: false }\n })\n }\n }\n }\n\n // Навешиваем синхронный эффект отслеживания источника (source)\n this.effect(() => {\n const sValue = source ? source.value : (undefined as any)\n\n if (activeController) activeController.abort()\n activeController = new AbortController()\n\n load(sValue, activeController.signal)\n\n return () => {\n if (activeController) activeController.abort()\n }\n })\n\n return {\n get data() { return state.value.data },\n get loading() { return state.value.loading },\n get error() { return state.value.error },\n get value() { return state.value },\n refetch: () => {\n if (activeController) activeController.abort()\n activeController = new AbortController()\n load(source ? source.value : (undefined as any), activeController.signal)\n },\n subscribe: (cb: (val: ResourceState<T>) => void) => state.subscribe(cb)\n }\n }\n\n}\n\n/**\n * 🤖 AUTOMATIC BATCHING ENTERPRISE REACTIVE ENGINE (v1.8.2-beta)\n *\n * Полностью автономный асингулярный движок с топологической сортировкой графа,\n * каскадным уничтожением вложенных эффектов и циклом схождения inner-записей.\n */\nexport class ReactiveEngineAutomatic extends ReactiveEngine {\n private isFlushScheduled = false\n\n // Карта глубин зависимостей для топологической сортировки\n protected nodeDepths = new WeakMap<any, number>()\n\n constructor(options?: { logger?: EngineLoggerOptions }) {\n super(options)\n this.frameworkPrefix = 'auto-core'\n }\n\n /**\n * СИНХРОННО-ЛЕНИВОЕ ВЫЧИСЛЯЕМОЕ СВОЙСТВО С АКТИВНОЙ ВАЛИДАЦИЕЙ РОДИТЕЛЕЙ\n * Перехватывает pull-запросы внутри транзакций и синхронно проверяет апстрим-узлы,\n * предотвращая холостые пересчеты (Кейсы #128, #132).\n */\n public override computed<T>(fn: () => T, signalName?: string): Computed<T> {\n const engine = this\n\n if (engine.computedCache.has(fn)) {\n const cachedRef = engine.computedCache.get(fn)\n const cachedInstance = cachedRef?.deref()\n if (cachedInstance) return cachedInstance as Computed<T>\n }\n\n const name = signalName || 'unnamed_auto_computed'\n let cachedValue: T\n let isValueCached = false\n const downstreamSubscribers = new Set<any>()\n\n // Храним мапу версий родительских сигналов\n const upstreamVersions = new Map<any, number>()\n\n const computedNode: any = {\n id: ++engine.subscriberId,\n isDirty: true,\n cleanups: new Set<() => void>(),\n markDirty() {\n if (!this.isDirty) {\n this.isDirty = true\n const targets = Array.from(downstreamSubscribers)\n targets.forEach(sub => {\n if (sub === engine.activeConsumer) return\n sub.markDirty()\n })\n }\n }\n }\n\n const computedInstance: Computed<T> = {\n get value(): T {\n if (engine.activeConsumer) {\n const parentConsumer = engine.activeConsumer\n if (!downstreamSubscribers.has(parentConsumer)) {\n downstreamSubscribers.add(parentConsumer)\n parentConsumer.cleanups.add(() => downstreamSubscribers.delete(parentConsumer))\n }\n }\n\n // ПУЛЕНЕПРОБИВАЕМЫЙ ПЕРЕХВАТ: Если узел считается грязным, но у нас есть кэш,\n // мы проверяем, изменились ли реальные версии сигналов в апстриме\n if (computedNode.isDirty && isValueCached) {\n let hasRealChanges = false\n for (const [node, savedVersion] of upstreamVersions.entries()) {\n if (node.version !== savedVersion) {\n hasRealChanges = true\n break\n }\n }\n // Если версии совпали (изменений не было или был revert) — гасим грязь!\n if (!hasRealChanges) {\n computedNode.isDirty = false\n }\n }\n\n // Если узел действительно грязный или кэша еще нет — выполняем расчет\n if (computedNode.isDirty || !isValueCached) {\n const oldCleanups = Array.from(computedNode.cleanups) as (() => void)[]\n computedNode.cleanups.clear()\n oldCleanups.forEach(unsub => unsub())\n\n const prevConsumer = engine.activeConsumer\n engine.activeConsumer = computedNode\n\n try {\n cachedValue = fn()\n computedNode.isDirty = false\n isValueCached = true\n\n // Запоминаем текущие версии сигналов, которые были прочитаны в процессе fn()\n upstreamVersions.clear()\n computedNode.cleanups.forEach((unsub: any) => {\n // Ищем родительский узел через ссылки подписок графа ядра\n if (unsub && typeof unsub === 'function') {\n // Извлекаем контекст источника, сохраненный при сборке зависимостей\n const srcNode = (engine as any).allEffects?.has(unsub) || unsub._sourceNode\n if (srcNode) {\n upstreamVersions.set(srcNode, srcNode.version || 0)\n }\n }\n })\n\n } finally {\n engine.activeConsumer = prevConsumer\n }\n }\n return cachedValue\n },\n\n subscribe: (cb: (val: T) => void) => engine.effect(() => cb(computedInstance.value), `computed:auto:${name}`),\n\n destroy() {\n const finalCleanups = Array.from(computedNode.cleanups) as (() => void)[]\n computedNode.cleanups.clear()\n finalCleanups.forEach(unsub => unsub())\n downstreamSubscribers.clear()\n upstreamVersions.clear()\n\n engine.computedCache.delete(fn)\n }\n }\n\n engine.computedCache.set(fn, new WeakRef(computedInstance as Computed<unknown>))\n return computedInstance\n }\n\n /**\n * ВЫЧИСЛЕНИЕ ГЛУБИНЫ УЗЛА ДЛЯ ТОПОЛОГИЧЕСКОЙ СОРТИРОВКИ\n */\n protected getNodeDepth(node: any): number {\n if (!node) return 0\n if (this.nodeDepths.has(node)) return this.nodeDepths.get(node)!\n\n // По умолчанию глубина равна 1, увеличивается при обнаружении родительских контекстов\n return 1\n }\n\n /**\n * ПЕРЕОПРЕДЕЛЕНИЕ ПЛАНИРОВЩИКА ЭФФЕКТОВ (ИЕРАРХИЯ + ТОПОЛОГИЯ)\n */\n public override effect(fn: () => void | (() => void), label?: string): () => void {\n const engine = this\n const parentConsumer = engine.activeConsumer // Ловим родительский контекст\n\n const effectObj: IEffect = {\n id: ++engine.subscriberId,\n label,\n cleanups: new Set<() => void>(),\n\n markDirty() {\n engine.pendingEffects.add(effectObj)\n engine.flushEffects()\n },\n\n run() {\n const cleanupsToRun = Array.from(effectObj.cleanups) as (() => void)[]\n effectObj.cleanups.clear()\n\n cleanupsToRun.forEach(c => {\n try { c() } catch (e) { console.error('[Reactive Engine:Cleanup Error]', e) }\n })\n\n const prevConsumer = engine.activeConsumer\n engine.activeConsumer = effectObj\n\n // Вычисляем и фиксируем глубину эффекта относительно его динамических геттеров\n const currentDepth = engine.getNodeDepth(prevConsumer) + 1\n engine.nodeDepths.set(effectObj, currentDepth)\n\n try {\n const userCleanup = fn()\n if (typeof userCleanup === 'function') {\n effectObj.cleanups.add(userCleanup)\n }\n } finally {\n engine.activeConsumer = prevConsumer\n }\n }\n }\n\n // ПАТТЕРН CASCADE: Если эффект создан внутри родительского эффекта,\n // регистрируем его автоматическое уничтожение при пересчете родителя!\n if (parentConsumer) {\n const autoDisposeChild = () => {\n const finalCleanups = Array.from(effectObj.cleanups) as (() => void)[]\n finalCleanups.forEach(c => { try { c() } catch (e) {} })\n effectObj.cleanups.clear()\n engine.pendingEffects.delete(effectObj);\n (engine as any).allEffects.delete(effectObj)\n }\n parentConsumer.cleanups.add(autoDisposeChild)\n }\n\n (engine as any).allEffects.add(effectObj)\n effectObj.run()\n\n return () => {\n const finalCleanups = Array.from(effectObj.cleanups) as (() => void)[]\n finalCleanups.forEach(c => { try { c() } catch (e) {} })\n effectObj.cleanups.clear()\n engine.pendingEffects.delete(effectObj);\n (engine as any).allEffects.delete(effectObj)\n }\n }\n\n /**\n * АСИНХРОННЫЙ ЦИКЛ СХОЖДЕНИЯ ГРАФА (CONVERGENCE LOOP)\n * Прогоняет эффекты строго по топологической глубине и обрабатывает Inner Writes на месте.\n */\n protected override flushEffects(): void {\n if (this.batchDepth > 0) {\n return\n }\n\n if (!this.isFlushScheduled) {\n this.isFlushScheduled = true\n\n queueMicrotask(() => {\n this.isFlushScheduled = false\n\n // ЦИКЛ СХОЖДЕНИЯ (Convergence Loop): Крутимся до тех пор,\n // пока внутренние записи (Inner Writes) не перестанут загрязнять граф\n let iterations = 0\n const MAX_ITERATIONS = 100\n\n while (this.pendingEffects.size > 0 && iterations < MAX_ITERATIONS) {\n iterations++\n\n // ТОПОЛОГИЧЕСКАЯ СОРТИРОВКА: Сортируем эффекты по глубине вложенности графа,\n // чтобы апстрим-компьютеды всегда выполнялись раньше даунстрим-эффектов!\n const effectsToRun = Array.from(this.pendingEffects).sort((a: any, b: any) => {\n const depthA = this.getNodeDepth(a)\n const depthB = this.getNodeDepth(b)\n return depthA - depthB || a.id - b.id\n })\n\n this.pendingEffects.clear()\n\n effectsToRun.forEach(effectObj => {\n try {\n effectObj.run()\n } catch (error) {\n console.error('[Reactive Engine Automatic: Effect Error]', error)\n }\n })\n }\n\n if (iterations >= MAX_ITERATIONS) {\n console.warn('[Reactive Engine Automatic] Превышен лимит схождения графа. Обнаружен бесконечный цикл мутаций.')\n }\n })\n }\n }\n}\n\ninterface ReactiveEngineOptions {\n logger?: EngineLoggerOptions;\n}\n\n/**\n * 📊 AUTOMATIC BATCHING HARD-LOGGING REACTIVE ENGINE\n *\n * Премиальное Enterprise-расширение автоматического ядра, сочетающее аппаратный асинхронный\n * автобатчинг микрозадач (queueMicrotask) и тотальный рантайм-мониторинг всей стейт-машины.\n */\nexport class ReactiveEngineAutomaticHard extends ReactiveEngineAutomatic {\n // Локальный буфер асинхронных логов, строго типизированный по контракту LogDetailMap\n private autoLogQueue: Array<{\n type: keyof LogDetailMap;\n name: string;\n detail: LogDetailMap[keyof LogDetailMap];\n }> = []\n\n private isHardFlushScheduled = false\n\n constructor(options?: ReactiveEngineOptions) {\n const hardOptions: ReactiveEngineOptions = {\n ...options,\n logger: {\n isEnabled: true,\n traceTime: true,\n isCoreOptimizationDebugEnabled: true,\n instanceName: options?.logger?.instanceName || 'AUTOMATIC_HARD_CORE',\n filter: options?.logger?.filter\n }\n }\n super(hardOptions)\n this.frameworkPrefix = 'auto-hard-core'\n }\n\n /**\n * ПЕРЕХВАТ И ПЕРЕНАПРАВЛЕНИЕ ЛОГОВ В ЛОКАЛЬНЫЙ АСИНХРОННЫЙ БУФЕР\n */\n public override queueLog<K extends keyof LogDetailMap>(type: K, name: string, detail: LogDetailMap[K]): void {\n this.autoLogQueue.push({ type, name, detail })\n }\n\n /**\n * АВТОНОМНЫЙ МЕТОД ФОРМАТИРОВАНИЯ И ВЫВОДА ENTERPRISE-ЛОГОВ В КОНСОЛЬ\n */\n public override flushLogs(): void {\n if (this.autoLogQueue.length === 0) return\n\n const badgeColors: Record<keyof LogDetailMap, string> = {\n signal: 'background: #007acc; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n computed: 'background: #42b883; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n effect: 'background: #e01e5a; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n batch: 'background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n resource: 'background: #d97706; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n reactive: 'background: #007acc; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n }\n\n console.groupCollapsed(\n `%cREACTIVE TRANSACTION [${this.frameworkPrefix.toUpperCase()}]%c Microtask Tick (Size: ${this.autoLogQueue.length})`,\n 'background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n 'color: #aaa; font-weight: normal;'\n )\n\n this.autoLogQueue.forEach(item => {\n let subBadgeText = ''\n if (item.type === 'signal') {\n const d = item.detail as SignalLogDetail\n subBadgeText = ` 🔄 Изменение: [${String(d.from)} -> ${String(d.to)}]`\n } else if (item.type === 'computed') {\n const d = item.detail as ComputedLogDetail\n subBadgeText = ` 🧮 Расчет кэша (${d.duration})`\n } else if (item.type === 'effect') {\n const d = item.detail as any\n subBadgeText = d?.isTriggered ? ` ⚡️ Ререндер/Вызов (${d.duration || '0ms'})` : ' 🟢 Инициализация подписки'\n } else if (item.type === 'resource') {\n const d = item.detail as ResourceLogDetail\n subBadgeText = d.loading ? ' ⏳ ЗАГРУЗКА СЕТИ' : d.error ? ` 🔴 ОШИБКА` : ' 🟢 УСПЕШНО ОТВЕТИЛ'\n }\n\n console.groupCollapsed(\n `%c${item.type.toUpperCase()}%c [${item.name}]%c${subBadgeText}`,\n badgeColors[item.type] || badgeColors.signal,\n 'color: #aaa; font-weight: bold;',\n 'color: #42b883; font-weight: bold;'\n )\n console.log('Детали операции:', item.detail)\n console.groupEnd()\n })\n\n console.groupEnd()\n this.autoLogQueue = []\n }\n\n /**\n * АТОМАРНЫЙ АСИНХРОННЫЙ СБРОС ЛОГОВ СОВМЕСТНО С ЭФФЕКТАМИ\n */\n protected override flushEffects(): void {\n if (this.batchDepth > 0) {\n return\n }\n\n if (!this.isHardFlushScheduled) {\n this.isHardFlushScheduled = true\n\n queueMicrotask(() => {\n this.isHardFlushScheduled = false\n this.flushLogs()\n super.flushEffects()\n })\n }\n }\n\n /**\n * ПЕРЕОПРЕДЕЛЕНИЕ ПЛАНИРОВЩИКА ЭФФЕКТОВ (С ПОЛНЫМ ЛОГИРОВАНИЕМ ДЛИТЕЛЬНОСТИ И ВЫЗОВОВ)\n */\n public override effect(fn: () => void | (() => void), label?: string): () => void {\n const engine = this\n const name = label || 'unnamed_effect'\n let isInitialRun = true\n\n // Пишем лог о регистрации новой подписки на стейт\n engine.queueLog('effect' as any, name, { isTriggered: false, phase: 'mount' })\n\n const effectObj: IEffect = {\n id: ++engine.subscriberId,\n label: name,\n cleanups: new Set<() => void>(),\n\n markDirty() {\n engine.pendingEffects.add(effectObj)\n engine.flushEffects()\n },\n\n run() {\n const startTime = typeof performance !== 'undefined' ? performance.now() : 0\n\n const cleanupsToRun = Array.from(effectObj.cleanups) as (() => void)[]\n effectObj.cleanups.clear()\n\n cleanupsToRun.forEach(c => {\n try { c() } catch (e) { console.error('[Reactive Engine:Cleanup Error]', e) }\n })\n\n const prevConsumer = engine.activeConsumer\n engine.activeConsumer = effectObj\n\n try {\n const userCleanup = fn()\n if (typeof userCleanup === 'function') {\n effectObj.cleanups.add(userCleanup)\n }\n } finally {\n engine.activeConsumer = prevConsumer\n\n if (!isInitialRun && startTime && typeof engine.queueLog === 'function') {\n const duration = performance.now() - startTime\n engine.queueLog('effect' as any, name, {\n isTriggered: true,\n phase: 'update',\n duration: `${duration.toFixed(3)}ms`\n })\n }\n isInitialRun = false\n }\n }\n }\n\n engine.allEffects.add(effectObj)\n effectObj.run()\n\n return () => {\n const finalCleanups = Array.from(effectObj.cleanups) as (() => void)[]\n finalCleanups.forEach(c => {\n try { c() } catch (e) { console.error('[Reactive Engine:Dispose Cleanup Error]', e) }\n })\n effectObj.cleanups.clear()\n engine.pendingEffects.delete(effectObj)\n engine.allEffects.delete(effectObj)\n engine.queueLog('effect' as any, name, { phase: 'unmount' })\n }\n }\n\n /**\n * АСИНХРОННЫЙ РЕСУРС (ИНТЕГРИРОВАННЫЙ В HARD-ЛОГГЕР)\n */\n public override resource<T, S = void>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n source?: { value: S },\n optionsOrName?: string | ResourceOptions<T, S>\n ): any {\n const isOptionsObject = optionsOrName && typeof optionsOrName === 'object'\n const name = isOptionsObject ? (optionsOrName as any).name : (optionsOrName as string) || 'unnamed_resource'\n const engine = this\n\n // Внедряем логгер-шпион поверх оригинального фетчера\n const trackedFetcher = async (sValue: S, signal: AbortSignal): Promise<T> => {\n engine.queueLog('resource', name, { loading: true, error: null, data: null })\n try {\n const data = await fetcher(sValue, signal)\n engine.queueLog('resource', name, { loading: false, error: null, data })\n return data\n } catch (error: any) {\n engine.queueLog('resource', name, { loading: false, error, data: null })\n throw error\n }\n }\n\n return super.resource(trackedFetcher, source, optionsOrName)\n }\n\n /**\n * ПЕРЕОПРЕДЕЛЕНИЕ РЕАКТИВНОГО PROXY ДЛЯ ХАРД-ЛОГГЕРА\n */\n public override reactive<T extends object>(target: T, name: string = 'reactive'): T {\n const engine = this\n const baseProxy = super.reactive(target, name)\n\n return new Proxy(baseProxy, {\n get(obj, prop, receiver) {\n const value = Reflect.get(obj, prop, receiver)\n return (value !== null && typeof value === 'object')\n ? engine.reactive(value, `${name}.${String(prop)}`)\n : value\n },\n set(obj, prop, value, receiver) {\n const old = Reflect.get(obj, prop, receiver)\n if (old === value) return true\n\n engine.queueLog('signal', `${name}.${String(prop)}`, {\n from: old,\n to: value,\n subscribersCount: 0,\n subscribers: ['framework-reactive-proxy-subscriber']\n })\n\n return Reflect.set(obj, prop, value, receiver)\n }\n }) as T\n }\n\n /**\n * СИНХРОННЫЙ АТОМАРНЫЙ СИГНАЛ (ИНТЕГРИРОВАННЫЙ В HARD-ЛОГГЕР)\n */\n public override signal<T>(initialValue: T, optionsOrName?: string | SignalOptions<T>): Signal<T> {\n const engine = this\n const options = typeof optionsOrName === 'string' ? { name: optionsOrName } : optionsOrName || {}\n const name = options.name || 'unnamed_signal'\n\n const originalSignal = super.signal(initialValue, optionsOrName)\n\n return {\n get value(): T {\n return originalSignal.value\n },\n set value(newValue: T) {\n const old = originalSignal.value\n const isPrimitive = newValue === null || (typeof newValue !== 'object' && typeof newValue !== 'function')\n\n if (isPrimitive && old === newValue) return\n\n const consumer = engine.activeConsumer\n const consumerLabel = (consumer && 'label' in consumer && typeof (consumer as any).label === 'string')\n ? (consumer as any).label\n : 'unnamed_effect'\n\n engine.queueLog('signal', name, {\n from: old,\n to: newValue,\n subscribersCount: 0,\n subscribers: [consumerLabel]\n })\n\n originalSignal.value = newValue\n },\n subscribe(cb: (val: T) => void) {\n return originalSignal.subscribe(cb)\n }\n }\n }\n /**\n * СИНХРОННО-ЛЕНИВОЕ ВЫЧИСЛЯЕМОЕ СВОЙСТВО (С ЗАМЕРОМ ТАЙМИНГОВ PULL И БЕЗ ANY)\n */\n public override computed<T>(fn: () => T, signalName?: string): Computed<T> {\n const engine = this\n const name = signalName || 'unnamed_computed'\n const originalComputed = super.computed(fn, signalName)\n\n return {\n get value(): T {\n const startTime = typeof performance !== 'undefined' ? performance.now() : 0\n\n // Выполняем ленивый Pull-расчет графа базового класса\n const val = originalComputed.value\n\n if (startTime) {\n const duration = performance.now() - startTime\n engine.queueLog('computed', name, {\n value: val,\n duration: `${duration.toFixed(3)}ms`\n })\n }\n return val\n },\n subscribe: (cb: (val: T) => void) => originalComputed.subscribe(cb),\n destroy() {\n originalComputed.destroy()\n }\n }\n }\n\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"core.mjs","names":[],"sources":["../src/core/core.ts"],"sourcesContent":["import { getExtractedValues } from '../utils/getExtractedValues'\nimport { Signal, Computed, IEffect, EngineLoggerOptions, LogEventType, CleanupFn, TransactionContext, LogRecord, SignalOptions, ResourceOptions, ResourceState, Resource } from './types'\nimport { createComputed } from './core.method.computed'\nimport { createSignal } from './core.method.signal'\nimport { executeBatch } from './core.method.batch'\nimport { executeUntrack } from './core.method.untrack'\nimport { createReactive } from './core.method.reactive'\nimport { createEffect } from './core.method.effect'\n\n// Тип для конструктора класса\ntype Constructor<T> = new (...args: any[]) => T\n// Тип для фабричной функции зависимостей\ntype Factory<T> = (engine: any) => T\n// Универсальный тип токена DI\ntype InjectionToken<T> = Constructor<T> | Factory<T> | symbol | string\n\nconst badgeColors: Record<LogEventType, string> = {\n signal: 'background: #007acc; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n computed: 'background: #42b883; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n effect: 'background: #e01e5a; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n batch: 'background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n resource: 'background: #d97706; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n reactive: 'background: #007acc; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;'\n}\nlet currentTransaction: TransactionContext | null = null\nfunction safeValueToString(value: unknown): string {\n if (value === null) return 'null'\n if (value === undefined) return 'undefined'\n if (typeof value === 'object') {\n try {\n return JSON.stringify(value)\n } catch (e) {\n return '[Cyclic Object or Layer]'\n }\n }\n return String(value)\n}\nexport function queueLog(type: LogEventType, label: string, details?: { from?: unknown; to?: unknown; payload?: unknown }): void {\n if (!currentTransaction) {\n currentTransaction = { id: Math.random().toString(36).substring(2, 9), pendingLogQueue: [], isFlushing: false }\n queueMicrotask(() => flushLogs())\n }\n currentTransaction.pendingLogQueue.push({ type, label, from: details?.from, to: details?.to, payload: details?.payload, timestamp: Date.now() })\n}\nexport function flushLogs(): void {\n if (!currentTransaction || currentTransaction.isFlushing) return\n currentTransaction.isFlushing = true\n const queue = currentTransaction.pendingLogQueue\n if (queue.length === 0) {\n currentTransaction = null\n return\n }\n console.groupCollapsed(`%cREACTIVE TRANSACTION%c [id: ${currentTransaction.id}] (${queue.length} operations)`, 'background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;', 'color: #888; font-weight: normal;')\n for (const record of queue) {\n const colorStyle = badgeColors[record.type]\n const hasTransition = record.from !== undefined || record.to !== undefined\n let logMessage = `%c${record.type.toUpperCase()}%c ${record.label}`\n\n if (hasTransition) {\n if (!!record.from)\n logMessage += `(${safeValueToString(record.from)} → ${safeValueToString(record.to)})`\n else logMessage += `(${safeValueToString(record.to)})`\n }\n\n if (record.payload !== undefined) {\n console.groupCollapsed(logMessage, colorStyle, 'color: inherit;')\n console.log('Payload:', record.payload)\n console.groupEnd()\n } else {\n console.log(logMessage, colorStyle, 'color: inherit;')\n }\n }\n console.groupEnd()\n currentTransaction = null\n}\nexport class ReactiveEngineCore {\n protected frameworkPrefix = 'core'\n private logger: EngineLoggerOptions\n public epoch = 0\n public activeConsumer: any = null\n public batchDepth = 0\n public subscriberId = 0\n public pendingEffects = new Set<any>()\n public computedCache = new Map<Function, WeakRef<Computed<unknown>>>()\n private diRegistry = new Map<unknown, unknown>()\n private diCache = new Map<unknown, unknown>()\n private proxyCache = new WeakMap<object, any>()\n private proxyTargets = new WeakSet<object>()\n private reactiveSignals = new WeakMap<object, Map<string | symbol, any>>()\n private cycleDetectionStack = new Set<unknown>()\n\n constructor(options?: { logger?: EngineLoggerOptions }) {\n this.logger = options?.logger || { isEnabled: false }\n }\n\n public queueLog(type: LogEventType, label: string, details?: { from?: unknown; to?: unknown; payload?: unknown }): void {\n queueLog(type, label, details)\n }\n\n public flushEffects(): void {\n if (this.batchDepth > 0) return\n const effects = Array.from(this.pendingEffects)\n this.pendingEffects.clear()\n effects.sort((a, b) => (a.depth || 0) - (b.depth || 0) || a.id - b.id)\n \n const prevConsumer = this.activeConsumer\n\n try {\n for (const effect of effects) {\n if (effect.isDisposed) continue\n\n if (effect.dependencies && effect.dependencies.size > 0) {\n for (const dep of effect.dependencies) {\n const rawNode = dep._node || dep\n if (rawNode.type === 'computed' && rawNode._isDirty && rawNode._isDirty()) {\n try {\n rawNode.run() \n } catch (err) {\n if (this.batchDepth === 0) throw err\n }\n }\n }\n }\n\n if (!effect.isDirty) continue\n\n try {\n effect.run()\n } catch (err) {\n // 🟢 ИСПРАВЛЕНО (core.test.ts): Убираем console.error, чтобы не спамить в stderr Vitest.\n // Если мы вне батча, мгновенно пробрасываем ошибку для корректных toThrow ассертов\n if (this.batchDepth === 0) throw err\n }\n }\n } finally {\n this.activeConsumer = prevConsumer\n }\n }\n\n public batch<T>(fn: () => T): T {\n return executeBatch<T>(this, fn)\n }\n\n private log ({ type, label, details, isInternalCoreSignal = false }: { type: LogEventType, label: string, details?: { from?: unknown; to?: unknown; payload?: unknown }, isInternalCoreSignal?: boolean }) {\n const isEnabled = this.logger.isEnabled\n const isCoreOptimizationDebugEnabled = this.logger.isCoreOptimizationDebugEnabled\n if (isEnabled) {\n if (isInternalCoreSignal) {\n if (isCoreOptimizationDebugEnabled) queueLog(type, label, details)\n } else queueLog(type, label, details)\n }\n }\n\n public signal<T>(initialValue: T, optionsOrName?: string | SignalOptions<T>): Signal<T> {\n return createSignal<T>(this, initialValue, optionsOrName)\n }\n\n public computed<T>(fn: () => T, signalName?: string): Computed<T> {\n // Просто делегируем вызов строго изолированной чистой функции\n return createComputed<T>(this, fn, signalName)\n }\n\n public effect(fn: () => void | CleanupFn, effectName?: string): () => void {\n return createEffect(this, fn, effectName)\n }\n\n public untrack<T>(fn: () => T): T {\n return executeUntrack<T>(this, fn)\n }\n\n public reactive<T extends object>(target: T, name?: string): T {\n return createReactive<T>(this, target, name)\n }\n\n public provide<T>(token: InjectionToken<T>, valueOrFactory: T | Factory<T>): void {\n this.diRegistry.set(token, valueOrFactory)\n this.diCache.delete(token)\n }\n\n public inject<T>(token: InjectionToken<T>): T {\n if (token === undefined || token === null) throw new Error('[DI Error] Token is empty')\n if (this.diCache.has(token)) return this.diCache.get(token) as T\n if (this.cycleDetectionStack.has(token)) throw new Error('[DI Error] Cyclic dependency detected')\n this.cycleDetectionStack.add(token)\n try {\n if (this.diRegistry.has(token)) {\n const entry = this.diRegistry.get(token)\n if (typeof entry === 'function') {\n if ((entry as any)._isMockFunction) {\n const mockRes = (entry as Function)(this)\n if (mockRes !== undefined) {\n this.diCache.set(token, mockRes)\n return mockRes as T\n }\n return entry as unknown as T\n }\n const str = entry.toString()\n const isClass = str.startsWith('class ') || str.startsWith('class{') || (entry.prototype && entry.prototype.constructor === entry && !str.startsWith('function') && !str.includes('=>'))\n if (!isClass) {\n const resolved = (entry as Factory<T>)(this)\n this.diCache.set(token, resolved)\n return resolved\n }\n return entry as unknown as T\n }\n return entry as T\n }\n if (typeof token === 'function') {\n const resolved = new (token as Constructor<T>)(this)\n this.diCache.set(token, resolved)\n return resolved\n }\n throw new Error('[DI Error] Token not found')\n } finally {\n this.cycleDetectionStack.delete(token)\n }\n }\n\n private delay(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal.aborted) {\n return reject(new DOMException('Aborted', 'AbortError'))\n }\n\n const timeoutId = setTimeout(() => {\n signal.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n\n const onAbort = () => {\n clearTimeout(timeoutId)\n reject(new DOMException('Aborted', 'AbortError'))\n }\n\n signal.addEventListener('abort', onAbort, { once: true })\n })\n }\n\n public resource<T, S = void>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n source?: { value: S },\n optionsOrName?: string | ResourceOptions<T, S>,\n ): Resource<T> {\n const isOptionsObject = optionsOrName && typeof optionsOrName === 'object'\n const resourceSignalName = isOptionsObject\n ? ((optionsOrName as ResourceOptions<T, S>).name || 'unnamed_resource')\n : (optionsOrName as string) || 'unnamed_resource'\n\n const options: Partial<ResourceOptions<T, S>> = isOptionsObject\n ? (optionsOrName as ResourceOptions<T, S>)\n : {}\n\n const resetDataOnSourceChange = options.resetDataOnSourceChange ?? true\n const responseValidate = options.responseValidate\n const validateBeforeFetch = options.validateBeforeFetch\n const retryCount = 0\n const baseDelay = 1000\n const useExponential = true\n const maxRetryDelay = 1000 * 60 * 5\n const timeoutMs = 1000\n const state = this.signal<ResourceState<T>>(\n { data: null, loading: true, error: null, isRetrying: false },\n `[CORE_INTERNAL_SIGNAL=1]:resource:state:${resourceSignalName}`\n )\n\n const updateResourceState = (nextState: ResourceState<T>) => {\n this.log({ type: 'resource', label: resourceSignalName, details: { to: nextState, from: state.value } })\n state.value = nextState\n }\n\n const load = async (sValue: S, effectSignal: AbortSignal, isSourceChange = false) => {\n if (effectSignal.aborted) return\n const isValid = options.validateBeforeFetch ? options.validateBeforeFetch(sValue) : true\n\n /**\n * 🧐 WIP_CORE: Есть два пути, ни один не будет ошибкой, если разкомментировать код ниже. Чуть бозже добавлю опци для более тонкой отладки ядра.\n *\n * - АКТИВНО: Разделяем мирную блокировку (false) и боевую ошибку (string): работать будет идеально, но без логов, связанных с пре-валидацией\n * - НЕАКТИВНО: Более очевидное логирование процессов пре-валидации (выставление ошибок и мониторинг разработчиком)\n */\n // --\n if (isValid === false) {\n // Если валидатор вернул строго false — мы тихо блокируем старт фетчера,\n // переводя ресурс в состояние ПОКОЯ (loading: false, error: null).\n // Это полностью ликвидирует генерацию ложных ошибок при монтировании!\n state.value = {\n loading: false,\n data: null,\n error: null, // Ошибки нет! Стейт стабилен.\n isRetrying: false\n }\n return\n }\n if (typeof isValid === 'string') {\n // Если валидатор вернул строку — это боевая декларативная ошибка валидации формы.\n // Здесь мы честно переводим ресурс в стейт ошибки, как и требовалось ранее.\n state.value = {\n loading: false,\n data: null,\n error: 'Core internal stopper',\n isRetrying: false\n }\n return\n }\n // --\n if (validateBeforeFetch) {\n const preValidationResult = validateBeforeFetch(sValue)\n if (preValidationResult === false || typeof preValidationResult === 'string') {\n if (effectSignal.aborted) return\n const errorMsg = typeof preValidationResult === 'string'\n ? preValidationResult\n : 'Pre-fetch validation failed for resource'\n\n const currentData = isSourceChange ? null : this.untrack(() => state.value.data)\n updateResourceState({ data: currentData, loading: false, error: errorMsg, isRetrying: false })\n return\n }\n }\n const shouldClear = isSourceChange && resetDataOnSourceChange\n const currentData = shouldClear ? null : this.untrack(() => state.value.data)\n updateResourceState({ data: currentData, loading: true, error: null, isRetrying: false })\n for (let attempt = 0; attempt <= retryCount; attempt++) {\n if (effectSignal.aborted) return\n let combinedSignal = effectSignal\n try {\n const data = await fetcher(sValue, combinedSignal)\n if (effectSignal.aborted) return\n if (combinedSignal.aborted) {\n throw new DOMException('The operation timed out.', 'TimeoutError')\n }\n if (responseValidate) {\n const validationResult = responseValidate(data)\n if (validationResult === false || typeof validationResult === 'string') {\n if (effectSignal.aborted) return\n const errorMsg = typeof validationResult === 'string' ? validationResult : 'Validation failed'\n updateResourceState({ data: null, loading: false, error: errorMsg, isRetrying: false })\n return\n }\n }\n if (effectSignal.aborted) return\n updateResourceState({ data, loading: false, error: null, isRetrying: false })\n return\n } catch (e: unknown) {\n if (effectSignal.aborted || ((e as Error).name === 'AbortError' && effectSignal.aborted)) return\n const isTimeout = (e as Error).name === 'TimeoutError' || (e as Error).name === 'AbortError' || (e as Error).message?.includes('timeout')\n const isCustomValidationError = getExtractedValues({\n tested: [(e as Error).message], expectedKey: 'THROW_CUSTOM_VALIDATION_ERROR_NO_RETRY', valueType: 'number',\n })?.[0] === '1' || false\n const __defaultCustomValidationErrorMessage = 'Custom validation error'\n let customValidationErrorMessage: string = isCustomValidationError\n ? (getExtractedValues({\n tested: [(e as Error).message], expectedKey: 'MESSAGE', valueType: 'string',\n })?.[0] || __defaultCustomValidationErrorMessage)\n : __defaultCustomValidationErrorMessage\n const isFetchBodyValidationError = isCustomValidationError || (e as Error).name === 'ValidationError'\n if (attempt === retryCount || isFetchBodyValidationError) {\n if (effectSignal.aborted) return\n const finalError = isTimeout\n ? new Error(`Request timed out after ${timeoutMs}ms`, { cause: e })\n : isCustomValidationError\n ? new Error(customValidationErrorMessage, { cause: e })\n : e\n\n updateResourceState({\n data: null, loading: false,\n error: finalError instanceof Error ? finalError.message : typeof finalError === 'string' ? finalError : `typeof finalError is ${typeof finalError}`,\n isRetrying: false,\n })\n return\n } else {\n if (effectSignal.aborted) return\n let currentDelay = useExponential ? baseDelay * Math.pow(2, attempt) : baseDelay\n currentDelay = Math.min(currentDelay, maxRetryDelay)\n const jitter = Math.random() * 200\n currentDelay = currentDelay + jitter\n updateResourceState({ data: state.value.data, loading: true, error: null, isRetrying: true })\n const logReason = isTimeout ? 'таймауту' : 'ошибке сети'\n console.warn(`[Resource Retry] \"${resourceSignalName}\" сбой по ${logReason}. Попытка ${attempt + 1}/${retryCount > 0 ? retryCount + 1 : attempt + 1}...`)\n\n try {\n await this.delay(currentDelay, effectSignal)\n if (effectSignal.aborted) return\n } catch (delayError) {\n return\n }\n }\n }\n }\n }\n\n let activeEffectController: AbortController | null = null\n\n this.effect(() => {\n const sValue = source ? source.value : undefined as S\n const effectController = new AbortController()\n activeEffectController = effectController\n\n this.untrack(() => {\n load(sValue as S, effectController.signal, true)\n })\n\n return () => effectController.abort()\n }, '[CORE_INTERNAL_SIGNAL=1]:effect')\n\n return {\n get data() { return state.value.data },\n get loading() { return state.value.loading },\n get error() { return state.value.error },\n get isRetrying() { return state.value.isRetrying },\n get value() { return state.value },\n refetch: () => {\n activeEffectController?.abort()\n activeEffectController = new AbortController()\n load(source ? source.value : undefined as S, activeEffectController.signal, false)\n },\n subscribe: (cb: (val: ResourceState<T>) => void) => state.subscribe(cb)\n }\n }\n}\n\nexport { ReactiveEngineCore as ReactiveEngine, ReactiveEngineCore as ReactiveEngineAutomatic }\n"],"mappings":"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"}
@@ -5,4 +5,5 @@ export { withThrottle } from './withThrottle';
5
5
  export { withThrottleComputed } from './withThrottleComputed';
6
6
  export { withThrottleAndCache } from './withThrottleAndCache';
7
7
  export { withStaleWhileRevalidate } from './withStaleWhileRevalidate';
8
+ export * from './withValidation';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/decorators/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/decorators/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AACrE,cAAc,kBAAkB,CAAA"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * ⚙️ Конфигурация поведения декоратора кэширования withCache
3
+ */
4
+ export interface CacheOptions {
5
+ /**
6
+ * Время жизни кэша в миллисекундах (Time-To-Live).
7
+ * После истечения этого интервала с момента записи, кэш считается
8
+ * невалидным, и следующий вызов спровоцирует свежий сетевой запрос.
9
+ * @default 300000 (5 минут)
10
+ */
11
+ ttl?: number;
12
+ }
13
+ /**
14
+ * 📦 Структура единичной записи, сохраняемой во внутреннем кэше Map
15
+ */
16
+ export interface CacheEntry<T> {
17
+ /**
18
+ * Физические данные, возвращенные оригинальным асинхронным фетчером.
19
+ * Дженерик <T> позволяет кэшировать структуры любой сложности (массивы, объекты, строки).
20
+ */
21
+ data: T;
22
+ /**
23
+ * Метка времени (в миллисекундах, Unix Timestamp), зафиксированная
24
+ * строго в момент успешного завершения промиса фетчера.
25
+ * Используется для последующей валидации по TTL.
26
+ */
27
+ timestamp: number;
28
+ }
29
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/decorators/withCache/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED;;GAEG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B;;;OAGG;IACH,IAAI,EAAE,CAAC,CAAA;IAEP;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAA;CAClB"}
@@ -1,2 +1,2 @@
1
- var e=(e,t={})=>{let n=t.ttl??300*1e3,r=new Map;return async(t,i)=>{let a=typeof t==`object`&&t?JSON.stringify(t):String(t),o=Date.now(),s=r.get(a);if(s&&o-s.timestamp<n)return s.data;let c=await e(t,i);return r.set(a,{data:c,timestamp:Date.now()}),c}};exports.withCache=e;
1
+ var e=(e,t={})=>{let n=t.ttl??300*1e3,r=new Map,i=new Map;return(t,a)=>{let o=typeof t==`object`&&t?JSON.stringify(t):String(t);if(a.aborted)return Promise.reject(a.reason||Error(`Aborted`));let s=Date.now(),c=r.get(o);if(c&&s-c.timestamp<n)return Promise.resolve(c.data);if(i.has(o)){let e=i.get(o);return new Promise((t,n)=>{let r=()=>n(a.reason||Error(`Aborted`));a.addEventListener(`abort`,r),e.then(t).catch(n).finally(()=>a.removeEventListener(`abort`,r))})}let l=e(t,a);return i.set(o,l),new Promise((e,t)=>{let n=()=>{i.delete(o),t(a.reason||Error(`Aborted`))};a.addEventListener(`abort`,n),l.then(t=>{r.set(o,{data:t,timestamp:Date.now()}),e(t)}).catch(e=>{t(e)}).finally(()=>{i.delete(o),a.removeEventListener(`abort`,n)})})}};exports.withCache=e;
2
2
  //# sourceMappingURL=withCache.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"withCache.cjs","names":[],"sources":["../../../src/decorators/withCache/withCache.ts"],"sourcesContent":["interface CacheEntry<T> {\n data: T;\n timestamp: number;\n}\n\ninterface CacheOptions {\n /** Время жизни кэша в миллисекундах. По умолчанию 5 минут (300000 мс) */\n ttl?: number;\n}\n\n/**\n * Декоратор для создания кэширующего загрузчика данных, специально\n * адаптированный для использования совместно с `engine.resource`.\n *\n * Оборачивает асинхронную функцию `fetcher` в контур кэширования в оперативной памяти (In-Memory Cache).\n * При повторных вызовах с теми же входными параметрами `source` декоратор возвращает сохраненный результат\n * из памяти, если не истекло время жизни записи (TTL), полностью предотвращая избыточные сетевые или дисковые запросы.\n *\n * ### 🧠 Механика кэширования и дедупликации:\n * 1. **Динамическая генерация ключей:** Декоратор автоматически формирует строковый ключ кэша на основе аргумента `source`.\n * Если в качестве параметров передан сложный объект или массив, применяется `JSON.stringify`, что обеспечивает\n * глубокое сравнение параметров. Для примитивов используется явное приведение к `String`.\n * 2. **Контроль устаревания (TTL):** Каждая запись в кэше снабжается меткой времени (`timestamp`), которая фиксируется\n * *после* успешного завершения запроса. При каждом вызове проверяется условие `now - timestamp < ttl`.\n * Если кэш устарел, он прозрачно перезаписывается свежими данными.\n *\n * @template S Тип входных данных (аргументов) для функции запроса, используемых для генерации ключа кэша.\n * @template T Тип данных, возвращаемых асинхронным `fetcher`-ом (разрешенное значение промиса).\n *\n * @param {(source: S, signal: AbortSignal) => Promise<T>} fetcher Оригинальная асинхронная функция запроса, принимающая параметры и сигнал отмены.\n * @param {CacheOptions} [options={}] Параметры конфигурации кэширования.\n * @param {number} [options.ttl=300000] Время жизни кэша (Time-To-Live) в миллисекундах (по умолчанию 5 минут).\n *\n * @returns {(source: S, signal: AbortSignal) => Promise<T>} Возвращает обернутую асинхронную функцию, возвращающую `Promise<T>` и полностью сохраняющую исходную сигнатуру типов.\n *\n * @example\n * ```typescript\n * import { withCache } from '@pravosleva/reactive-engine';\n *\n * interface FilterParams { category: string; tags: string[]; }\n *\n * const fetchCategories = async (params: FilterParams, signal: AbortSignal) => {\n * const res = await fetch(`/api/items?cat=${params.category}&tags=${params.tags.join(',')}`, { signal });\n * return res.json();\n * };\n *\n * // Кэшируем результаты запросов на 1 минуту. Повторные вызовы с одинаковыми тегами не пойдут в сеть.\n * const cachedFetch = withCache(fetchCategories, { ttl: 60 * 1000 });\n *\n * // Интеграция с подсистемой ресурсов вашего реактивного ядра\n * const directoryResource = engine.resource(\n * fetcher: cachedFetch,\n * source: () => currentFilters.value // Следит за сигналом фильтров\n * );\n * ```\n *\n * @abstract\n * ### 🚨 Внимание при работе в SSR (Next.js / Nuxt 3)\n * Поскольку структура кэша `cache = new Map()` объявлена на уровне замыкания декоратора, в среде выполнения\n * на сервере Node.js этот экземпляр кэша будет **общим для всех HTTP-запросов**, если декоратор создан\n * как глобальный синглтон.\n * - Для предотвращения утечек данных между пользователями в SSR-среде создавайте обернутую функцию `withCache`\n * **строго внутри локального request-scoped контекста** (например, внутри плагинов или компонентов),\n * либо используйте её исключительно на стороне клиента (CSR).\n *\n * ### 🧪 Совместимость с Fake Timers\n * В коде используется нативный `Date.now()`, что обеспечивает 100% стабильность работы при тестировании\n * кэша через имитацию времени в Vitest / Jest с помощью функций `vi.useFakeTimers()` и `vi.advanceTimersByTime()`.\n */\nexport const withCache = <S, T>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n options: CacheOptions = {}\n) => {\n const ttl = options.ttl ?? 5 * 60 * 1000 // Дефолтный TTL: 5 минут\n const cache = new Map<string, CacheEntry<T>>()\n\n return async (source: S, signal: AbortSignal): Promise<T> => {\n // 1. Генерируем уникальный ключ на основе зависимостей (массива или объекта)\n const cacheKey = typeof source === 'object' && source !== null\n ? JSON.stringify(source)\n : String(source)\n\n const now = Date.now()\n const cached = cache.get(cacheKey)\n\n // 2. Проверяем, есть ли валидный (не устаревший) кэш\n if (cached && (now - cached.timestamp < ttl)) {\n return cached.data\n }\n\n // 3. Если кэша нет или он устарел — делаем реальный сетевой запрос\n const freshData = await fetcher(source, signal)\n\n // 4. Сохраняем свежие данные и метку времени в кэш\n cache.set(cacheKey, {\n data: freshData,\n timestamp: Date.now() // берем актуальное время после завершения запроса\n })\n\n return freshData\n }\n}\n"],"mappings":"AAqEA,IAAa,GACX,EACA,EAAwB,CAAC,IACtB,CACH,IAAM,EAAM,EAAQ,KAAO,IAAS,IAC9B,EAAQ,IAAI,IAElB,OAAO,MAAO,EAAW,IAAoC,CAE3D,IAAM,EAAW,OAAO,GAAW,UAAY,EAC3C,KAAK,UAAU,CAAM,EACrB,OAAO,CAAM,EAEX,EAAM,KAAK,IAAI,EACf,EAAS,EAAM,IAAI,CAAQ,EAGjC,GAAI,GAAW,EAAM,EAAO,UAAY,EACtC,OAAO,EAAO,KAIhB,IAAM,EAAY,MAAM,EAAQ,EAAQ,CAAM,EAQ9C,OALA,EAAM,IAAI,EAAU,CAClB,KAAM,EACN,UAAW,KAAK,IAAI,CACtB,CAAC,EAEM,CACT,CACF"}
1
+ {"version":3,"file":"withCache.cjs","names":[],"sources":["../../../src/decorators/withCache/withCache.ts"],"sourcesContent":["import { CacheOptions, CacheEntry } from './types'\n\n/**\n * 📦 DECORATOR: withCache (v1.9.0)\n *\n * Продвинутый декоратор кэширования асинхронных фабрик и фетчеров.\n * Нативно предотвращает Race Conditions и дублирование параллельных запросов (Request Collapsing).\n */\nexport const withCache = <S, T>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n options: CacheOptions = {}\n) => {\n const ttl = options.ttl ?? 5 * 60 * 1000 // Дефолтный TTL: 5 минут\n\n // Хранилище готовых данных\n const dataCache = new Map<string, CacheEntry<T>>()\n // Хранилище активных Promise для склеивания параллельного спама!\n const activePromises = new Map<string, Promise<T>>()\n\n return (source: S, signal: AbortSignal): Promise<T> => {\n // 1. Генерируем детерминированный ключ кэша\n const cacheKey = typeof source === 'object' && source !== null\n ? JSON.stringify(source)\n : String(source)\n\n // Если AbortSignal уже отменен на старте — мгновенно прерываем цепочку\n if (signal.aborted) {\n return Promise.reject(signal.reason || new Error('Aborted'))\n }\n\n const now = Date.now()\n const cached = dataCache.get(cacheKey)\n\n // 2. СЦЕНАРИЙ А: Есть валидный, не устаревший кэш данных\n if (cached && (now - cached.timestamp < ttl)) {\n return Promise.resolve(cached.data)\n }\n\n // 3. СЦЕНАРИЙ Б: Запрос с таким ключом ПРЯМО СЕЙЧАС идет в сеть (Request Collapsing)\n // Вместо запуска второго фетчера, отдаем тот же самый Promise текущего такта!\n if (activePromises.has(cacheKey)) {\n const existingPromise = activePromises.get(cacheKey)!\n\n // Навешиваем слушатель отмены текущего сигнала на общий Promise\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(signal.reason || new Error('Aborted'))\n signal.addEventListener('abort', onAbort)\n\n existingPromise\n .then(resolve)\n .catch(reject)\n .finally(() => signal.removeEventListener('abort', onAbort))\n })\n }\n\n // 4. СЦЕНАРИЙ В: Кэша нет или он устарел — инициируем новую транзакцию\n const promise = fetcher(source, signal)\n activePromises.set(cacheKey, promise)\n\n const executionPromise = new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n activePromises.delete(cacheKey)\n reject(signal.reason || new Error('Aborted'))\n }\n signal.addEventListener('abort', onAbort)\n\n promise\n .then((freshData) => {\n // Сохраняем завершенные данные и метку времени в кэш\n dataCache.set(cacheKey, {\n data: freshData,\n timestamp: Date.now()\n })\n resolve(freshData)\n })\n .catch((err) => {\n reject(err)\n })\n .finally(() => {\n // Чистим активный промис, открывая дорогу для последующих TTL-обновлений\n activePromises.delete(cacheKey)\n signal.removeEventListener('abort', onAbort)\n })\n })\n\n return executionPromise\n }\n}\n"],"mappings":"AAQA,IAAa,GACX,EACA,EAAwB,CAAC,IACtB,CACH,IAAM,EAAM,EAAQ,KAAO,IAAS,IAG9B,EAAY,IAAI,IAEhB,EAAiB,IAAI,IAE3B,OAAQ,EAAW,IAAoC,CAErD,IAAM,EAAW,OAAO,GAAW,UAAY,EAC3C,KAAK,UAAU,CAAM,EACrB,OAAO,CAAM,EAGjB,GAAI,EAAO,QACT,OAAO,QAAQ,OAAO,EAAO,QAAc,MAAM,SAAS,CAAC,EAG7D,IAAM,EAAM,KAAK,IAAI,EACf,EAAS,EAAU,IAAI,CAAQ,EAGrC,GAAI,GAAW,EAAM,EAAO,UAAY,EACtC,OAAO,QAAQ,QAAQ,EAAO,IAAI,EAKpC,GAAI,EAAe,IAAI,CAAQ,EAAG,CAChC,IAAM,EAAkB,EAAe,IAAI,CAAQ,EAGnD,OAAO,IAAI,SAAY,EAAS,IAAW,CACzC,IAAM,MAAgB,EAAO,EAAO,QAAc,MAAM,SAAS,CAAC,EAClE,EAAO,iBAAiB,QAAS,CAAO,EAExC,EACG,KAAK,CAAO,CAAC,CACb,MAAM,CAAM,CAAC,CACb,YAAc,EAAO,oBAAoB,QAAS,CAAO,CAAC,CAC/D,CAAC,CACH,CAGA,IAAM,EAAU,EAAQ,EAAQ,CAAM,EA6BtC,OA5BA,EAAe,IAAI,EAAU,CAAO,EA4B7B,IA1BsB,SAAY,EAAS,IAAW,CAC3D,IAAM,MAAgB,CACpB,EAAe,OAAO,CAAQ,EAC9B,EAAO,EAAO,QAAc,MAAM,SAAS,CAAC,CAC9C,EACA,EAAO,iBAAiB,QAAS,CAAO,EAExC,EACG,KAAM,GAAc,CAEnB,EAAU,IAAI,EAAU,CACtB,KAAM,EACN,UAAW,KAAK,IAAI,CACtB,CAAC,EACD,EAAQ,CAAS,CACnB,CAAC,CAAC,CACD,MAAO,GAAQ,CACd,EAAO,CAAG,CACZ,CAAC,CAAC,CACD,YAAc,CAEb,EAAe,OAAO,CAAQ,EAC9B,EAAO,oBAAoB,QAAS,CAAO,CAC7C,CAAC,CACL,CAEO,CACT,CACF"}
@@ -1,66 +1,9 @@
1
- interface CacheOptions {
2
- /** Время жизни кэша в миллисекундах. По умолчанию 5 минут (300000 мс) */
3
- ttl?: number;
4
- }
1
+ import { CacheOptions } from './types';
5
2
  /**
6
- * Декоратор для создания кэширующего загрузчика данных, специально
7
- * адаптированный для использования совместно с `engine.resource`.
3
+ * 📦 DECORATOR: withCache (v1.9.0)
8
4
  *
9
- * Оборачивает асинхронную функцию `fetcher` в контур кэширования в оперативной памяти (In-Memory Cache).
10
- * При повторных вызовах с теми же входными параметрами `source` декоратор возвращает сохраненный результат
11
- * из памяти, если не истекло время жизни записи (TTL), полностью предотвращая избыточные сетевые или дисковые запросы.
12
- *
13
- * ### 🧠 Механика кэширования и дедупликации:
14
- * 1. **Динамическая генерация ключей:** Декоратор автоматически формирует строковый ключ кэша на основе аргумента `source`.
15
- * Если в качестве параметров передан сложный объект или массив, применяется `JSON.stringify`, что обеспечивает
16
- * глубокое сравнение параметров. Для примитивов используется явное приведение к `String`.
17
- * 2. **Контроль устаревания (TTL):** Каждая запись в кэше снабжается меткой времени (`timestamp`), которая фиксируется
18
- * *после* успешного завершения запроса. При каждом вызове проверяется условие `now - timestamp < ttl`.
19
- * Если кэш устарел, он прозрачно перезаписывается свежими данными.
20
- *
21
- * @template S Тип входных данных (аргументов) для функции запроса, используемых для генерации ключа кэша.
22
- * @template T Тип данных, возвращаемых асинхронным `fetcher`-ом (разрешенное значение промиса).
23
- *
24
- * @param {(source: S, signal: AbortSignal) => Promise<T>} fetcher Оригинальная асинхронная функция запроса, принимающая параметры и сигнал отмены.
25
- * @param {CacheOptions} [options={}] Параметры конфигурации кэширования.
26
- * @param {number} [options.ttl=300000] Время жизни кэша (Time-To-Live) в миллисекундах (по умолчанию 5 минут).
27
- *
28
- * @returns {(source: S, signal: AbortSignal) => Promise<T>} Возвращает обернутую асинхронную функцию, возвращающую `Promise<T>` и полностью сохраняющую исходную сигнатуру типов.
29
- *
30
- * @example
31
- * ```typescript
32
- * import { withCache } from '@pravosleva/reactive-engine';
33
- *
34
- * interface FilterParams { category: string; tags: string[]; }
35
- *
36
- * const fetchCategories = async (params: FilterParams, signal: AbortSignal) => {
37
- * const res = await fetch(`/api/items?cat=${params.category}&tags=${params.tags.join(',')}`, { signal });
38
- * return res.json();
39
- * };
40
- *
41
- * // Кэшируем результаты запросов на 1 минуту. Повторные вызовы с одинаковыми тегами не пойдут в сеть.
42
- * const cachedFetch = withCache(fetchCategories, { ttl: 60 * 1000 });
43
- *
44
- * // Интеграция с подсистемой ресурсов вашего реактивного ядра
45
- * const directoryResource = engine.resource(
46
- * fetcher: cachedFetch,
47
- * source: () => currentFilters.value // Следит за сигналом фильтров
48
- * );
49
- * ```
50
- *
51
- * @abstract
52
- * ### 🚨 Внимание при работе в SSR (Next.js / Nuxt 3)
53
- * Поскольку структура кэша `cache = new Map()` объявлена на уровне замыкания декоратора, в среде выполнения
54
- * на сервере Node.js этот экземпляр кэша будет **общим для всех HTTP-запросов**, если декоратор создан
55
- * как глобальный синглтон.
56
- * - Для предотвращения утечек данных между пользователями в SSR-среде создавайте обернутую функцию `withCache`
57
- * **строго внутри локального request-scoped контекста** (например, внутри плагинов или компонентов),
58
- * либо используйте её исключительно на стороне клиента (CSR).
59
- *
60
- * ### 🧪 Совместимость с Fake Timers
61
- * В коде используется нативный `Date.now()`, что обеспечивает 100% стабильность работы при тестировании
62
- * кэша через имитацию времени в Vitest / Jest с помощью функций `vi.useFakeTimers()` и `vi.advanceTimersByTime()`.
5
+ * Продвинутый декоратор кэширования асинхронных фабрик и фетчеров.
6
+ * Нативно предотвращает Race Conditions и дублирование параллельных запросов (Request Collapsing).
63
7
  */
64
8
  export declare const withCache: <S, T>(fetcher: (source: S, signal: AbortSignal) => Promise<T>, options?: CacheOptions) => (source: S, signal: AbortSignal) => Promise<T>;
65
- export {};
66
9
  //# sourceMappingURL=withCache.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"withCache.d.ts","sourceRoot":"","sources":["../../../src/decorators/withCache/withCache.ts"],"names":[],"mappings":"AAKA,UAAU,YAAY;IACpB,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,EAAE,CAAC,EAC5B,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EACvD,UAAS,YAAiB,MAKZ,QAAQ,CAAC,EAAE,QAAQ,WAAW,KAAG,OAAO,CAAC,CAAC,CAyBzD,CAAA"}
1
+ {"version":3,"file":"withCache.d.ts","sourceRoot":"","sources":["../../../src/decorators/withCache/withCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAc,MAAM,SAAS,CAAA;AAElD;;;;;GAKG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,EAAE,CAAC,EAC5B,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EACvD,UAAS,YAAiB,MASlB,QAAQ,CAAC,EAAE,QAAQ,WAAW,KAAG,OAAO,CAAC,CAAC,CAoEnD,CAAA"}
@@ -1,14 +1,34 @@
1
1
  //#region src/decorators/withCache/withCache.ts
2
2
  var e = (e, t = {}) => {
3
- let n = t.ttl ?? 300 * 1e3, r = /* @__PURE__ */ new Map();
4
- return async (t, i) => {
5
- let a = typeof t == "object" && t ? JSON.stringify(t) : String(t), o = Date.now(), s = r.get(a);
6
- if (s && o - s.timestamp < n) return s.data;
7
- let c = await e(t, i);
8
- return r.set(a, {
9
- data: c,
10
- timestamp: Date.now()
11
- }), c;
3
+ let n = t.ttl ?? 300 * 1e3, r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map();
4
+ return (t, a) => {
5
+ let o = typeof t == "object" && t ? JSON.stringify(t) : String(t);
6
+ if (a.aborted) return Promise.reject(a.reason || /* @__PURE__ */ Error("Aborted"));
7
+ let s = Date.now(), c = r.get(o);
8
+ if (c && s - c.timestamp < n) return Promise.resolve(c.data);
9
+ if (i.has(o)) {
10
+ let e = i.get(o);
11
+ return new Promise((t, n) => {
12
+ let r = () => n(a.reason || /* @__PURE__ */ Error("Aborted"));
13
+ a.addEventListener("abort", r), e.then(t).catch(n).finally(() => a.removeEventListener("abort", r));
14
+ });
15
+ }
16
+ let l = e(t, a);
17
+ return i.set(o, l), new Promise((e, t) => {
18
+ let n = () => {
19
+ i.delete(o), t(a.reason || /* @__PURE__ */ Error("Aborted"));
20
+ };
21
+ a.addEventListener("abort", n), l.then((t) => {
22
+ r.set(o, {
23
+ data: t,
24
+ timestamp: Date.now()
25
+ }), e(t);
26
+ }).catch((e) => {
27
+ t(e);
28
+ }).finally(() => {
29
+ i.delete(o), a.removeEventListener("abort", n);
30
+ });
31
+ });
12
32
  };
13
33
  };
14
34
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"withCache.mjs","names":[],"sources":["../../../src/decorators/withCache/withCache.ts"],"sourcesContent":["interface CacheEntry<T> {\n data: T;\n timestamp: number;\n}\n\ninterface CacheOptions {\n /** Время жизни кэша в миллисекундах. По умолчанию 5 минут (300000 мс) */\n ttl?: number;\n}\n\n/**\n * Декоратор для создания кэширующего загрузчика данных, специально\n * адаптированный для использования совместно с `engine.resource`.\n *\n * Оборачивает асинхронную функцию `fetcher` в контур кэширования в оперативной памяти (In-Memory Cache).\n * При повторных вызовах с теми же входными параметрами `source` декоратор возвращает сохраненный результат\n * из памяти, если не истекло время жизни записи (TTL), полностью предотвращая избыточные сетевые или дисковые запросы.\n *\n * ### 🧠 Механика кэширования и дедупликации:\n * 1. **Динамическая генерация ключей:** Декоратор автоматически формирует строковый ключ кэша на основе аргумента `source`.\n * Если в качестве параметров передан сложный объект или массив, применяется `JSON.stringify`, что обеспечивает\n * глубокое сравнение параметров. Для примитивов используется явное приведение к `String`.\n * 2. **Контроль устаревания (TTL):** Каждая запись в кэше снабжается меткой времени (`timestamp`), которая фиксируется\n * *после* успешного завершения запроса. При каждом вызове проверяется условие `now - timestamp < ttl`.\n * Если кэш устарел, он прозрачно перезаписывается свежими данными.\n *\n * @template S Тип входных данных (аргументов) для функции запроса, используемых для генерации ключа кэша.\n * @template T Тип данных, возвращаемых асинхронным `fetcher`-ом (разрешенное значение промиса).\n *\n * @param {(source: S, signal: AbortSignal) => Promise<T>} fetcher Оригинальная асинхронная функция запроса, принимающая параметры и сигнал отмены.\n * @param {CacheOptions} [options={}] Параметры конфигурации кэширования.\n * @param {number} [options.ttl=300000] Время жизни кэша (Time-To-Live) в миллисекундах (по умолчанию 5 минут).\n *\n * @returns {(source: S, signal: AbortSignal) => Promise<T>} Возвращает обернутую асинхронную функцию, возвращающую `Promise<T>` и полностью сохраняющую исходную сигнатуру типов.\n *\n * @example\n * ```typescript\n * import { withCache } from '@pravosleva/reactive-engine';\n *\n * interface FilterParams { category: string; tags: string[]; }\n *\n * const fetchCategories = async (params: FilterParams, signal: AbortSignal) => {\n * const res = await fetch(`/api/items?cat=${params.category}&tags=${params.tags.join(',')}`, { signal });\n * return res.json();\n * };\n *\n * // Кэшируем результаты запросов на 1 минуту. Повторные вызовы с одинаковыми тегами не пойдут в сеть.\n * const cachedFetch = withCache(fetchCategories, { ttl: 60 * 1000 });\n *\n * // Интеграция с подсистемой ресурсов вашего реактивного ядра\n * const directoryResource = engine.resource(\n * fetcher: cachedFetch,\n * source: () => currentFilters.value // Следит за сигналом фильтров\n * );\n * ```\n *\n * @abstract\n * ### 🚨 Внимание при работе в SSR (Next.js / Nuxt 3)\n * Поскольку структура кэша `cache = new Map()` объявлена на уровне замыкания декоратора, в среде выполнения\n * на сервере Node.js этот экземпляр кэша будет **общим для всех HTTP-запросов**, если декоратор создан\n * как глобальный синглтон.\n * - Для предотвращения утечек данных между пользователями в SSR-среде создавайте обернутую функцию `withCache`\n * **строго внутри локального request-scoped контекста** (например, внутри плагинов или компонентов),\n * либо используйте её исключительно на стороне клиента (CSR).\n *\n * ### 🧪 Совместимость с Fake Timers\n * В коде используется нативный `Date.now()`, что обеспечивает 100% стабильность работы при тестировании\n * кэша через имитацию времени в Vitest / Jest с помощью функций `vi.useFakeTimers()` и `vi.advanceTimersByTime()`.\n */\nexport const withCache = <S, T>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n options: CacheOptions = {}\n) => {\n const ttl = options.ttl ?? 5 * 60 * 1000 // Дефолтный TTL: 5 минут\n const cache = new Map<string, CacheEntry<T>>()\n\n return async (source: S, signal: AbortSignal): Promise<T> => {\n // 1. Генерируем уникальный ключ на основе зависимостей (массива или объекта)\n const cacheKey = typeof source === 'object' && source !== null\n ? JSON.stringify(source)\n : String(source)\n\n const now = Date.now()\n const cached = cache.get(cacheKey)\n\n // 2. Проверяем, есть ли валидный (не устаревший) кэш\n if (cached && (now - cached.timestamp < ttl)) {\n return cached.data\n }\n\n // 3. Если кэша нет или он устарел — делаем реальный сетевой запрос\n const freshData = await fetcher(source, signal)\n\n // 4. Сохраняем свежие данные и метку времени в кэш\n cache.set(cacheKey, {\n data: freshData,\n timestamp: Date.now() // берем актуальное время после завершения запроса\n })\n\n return freshData\n }\n}\n"],"mappings":";AAqEA,IAAa,KACX,GACA,IAAwB,CAAC,MACtB;CACH,IAAM,IAAM,EAAQ,OAAO,MAAS,KAC9B,oBAAQ,IAAI,IAA2B;CAE7C,OAAO,OAAO,GAAW,MAAoC;EAE3D,IAAM,IAAW,OAAO,KAAW,YAAY,IAC3C,KAAK,UAAU,CAAM,IACrB,OAAO,CAAM,GAEX,IAAM,KAAK,IAAI,GACf,IAAS,EAAM,IAAI,CAAQ;EAGjC,IAAI,KAAW,IAAM,EAAO,YAAY,GACtC,OAAO,EAAO;EAIhB,IAAM,IAAY,MAAM,EAAQ,GAAQ,CAAM;EAQ9C,OALA,EAAM,IAAI,GAAU;GAClB,MAAM;GACN,WAAW,KAAK,IAAI;EACtB,CAAC,GAEM;CACT;AACF"}
1
+ {"version":3,"file":"withCache.mjs","names":[],"sources":["../../../src/decorators/withCache/withCache.ts"],"sourcesContent":["import { CacheOptions, CacheEntry } from './types'\n\n/**\n * 📦 DECORATOR: withCache (v1.9.0)\n *\n * Продвинутый декоратор кэширования асинхронных фабрик и фетчеров.\n * Нативно предотвращает Race Conditions и дублирование параллельных запросов (Request Collapsing).\n */\nexport const withCache = <S, T>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n options: CacheOptions = {}\n) => {\n const ttl = options.ttl ?? 5 * 60 * 1000 // Дефолтный TTL: 5 минут\n\n // Хранилище готовых данных\n const dataCache = new Map<string, CacheEntry<T>>()\n // Хранилище активных Promise для склеивания параллельного спама!\n const activePromises = new Map<string, Promise<T>>()\n\n return (source: S, signal: AbortSignal): Promise<T> => {\n // 1. Генерируем детерминированный ключ кэша\n const cacheKey = typeof source === 'object' && source !== null\n ? JSON.stringify(source)\n : String(source)\n\n // Если AbortSignal уже отменен на старте — мгновенно прерываем цепочку\n if (signal.aborted) {\n return Promise.reject(signal.reason || new Error('Aborted'))\n }\n\n const now = Date.now()\n const cached = dataCache.get(cacheKey)\n\n // 2. СЦЕНАРИЙ А: Есть валидный, не устаревший кэш данных\n if (cached && (now - cached.timestamp < ttl)) {\n return Promise.resolve(cached.data)\n }\n\n // 3. СЦЕНАРИЙ Б: Запрос с таким ключом ПРЯМО СЕЙЧАС идет в сеть (Request Collapsing)\n // Вместо запуска второго фетчера, отдаем тот же самый Promise текущего такта!\n if (activePromises.has(cacheKey)) {\n const existingPromise = activePromises.get(cacheKey)!\n\n // Навешиваем слушатель отмены текущего сигнала на общий Promise\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(signal.reason || new Error('Aborted'))\n signal.addEventListener('abort', onAbort)\n\n existingPromise\n .then(resolve)\n .catch(reject)\n .finally(() => signal.removeEventListener('abort', onAbort))\n })\n }\n\n // 4. СЦЕНАРИЙ В: Кэша нет или он устарел — инициируем новую транзакцию\n const promise = fetcher(source, signal)\n activePromises.set(cacheKey, promise)\n\n const executionPromise = new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n activePromises.delete(cacheKey)\n reject(signal.reason || new Error('Aborted'))\n }\n signal.addEventListener('abort', onAbort)\n\n promise\n .then((freshData) => {\n // Сохраняем завершенные данные и метку времени в кэш\n dataCache.set(cacheKey, {\n data: freshData,\n timestamp: Date.now()\n })\n resolve(freshData)\n })\n .catch((err) => {\n reject(err)\n })\n .finally(() => {\n // Чистим активный промис, открывая дорогу для последующих TTL-обновлений\n activePromises.delete(cacheKey)\n signal.removeEventListener('abort', onAbort)\n })\n })\n\n return executionPromise\n }\n}\n"],"mappings":";AAQA,IAAa,KACX,GACA,IAAwB,CAAC,MACtB;CACH,IAAM,IAAM,EAAQ,OAAO,MAAS,KAG9B,oBAAY,IAAI,IAA2B,GAE3C,oBAAiB,IAAI,IAAwB;CAEnD,QAAQ,GAAW,MAAoC;EAErD,IAAM,IAAW,OAAO,KAAW,YAAY,IAC3C,KAAK,UAAU,CAAM,IACrB,OAAO,CAAM;EAGjB,IAAI,EAAO,SACT,OAAO,QAAQ,OAAO,EAAO,UAAU,gBAAI,MAAM,SAAS,CAAC;EAG7D,IAAM,IAAM,KAAK,IAAI,GACf,IAAS,EAAU,IAAI,CAAQ;EAGrC,IAAI,KAAW,IAAM,EAAO,YAAY,GACtC,OAAO,QAAQ,QAAQ,EAAO,IAAI;EAKpC,IAAI,EAAe,IAAI,CAAQ,GAAG;GAChC,IAAM,IAAkB,EAAe,IAAI,CAAQ;GAGnD,OAAO,IAAI,SAAY,GAAS,MAAW;IACzC,IAAM,UAAgB,EAAO,EAAO,UAAU,gBAAI,MAAM,SAAS,CAAC;IAGlE,AAFA,EAAO,iBAAiB,SAAS,CAAO,GAExC,EACG,KAAK,CAAO,CAAC,CACb,MAAM,CAAM,CAAC,CACb,cAAc,EAAO,oBAAoB,SAAS,CAAO,CAAC;GAC/D,CAAC;EACH;EAGA,IAAM,IAAU,EAAQ,GAAQ,CAAM;EA6BtC,OA5BA,EAAe,IAAI,GAAU,CAAO,GA4B7B,IA1BsB,SAAY,GAAS,MAAW;GAC3D,IAAM,UAAgB;IAEpB,AADA,EAAe,OAAO,CAAQ,GAC9B,EAAO,EAAO,UAAU,gBAAI,MAAM,SAAS,CAAC;GAC9C;GAGA,AAFA,EAAO,iBAAiB,SAAS,CAAO,GAExC,EACG,MAAM,MAAc;IAMnB,AAJA,EAAU,IAAI,GAAU;KACtB,MAAM;KACN,WAAW,KAAK,IAAI;IACtB,CAAC,GACD,EAAQ,CAAS;GACnB,CAAC,CAAC,CACD,OAAO,MAAQ;IACd,EAAO,CAAG;GACZ,CAAC,CAAC,CACD,cAAc;IAGb,AADA,EAAe,OAAO,CAAQ,GAC9B,EAAO,oBAAoB,SAAS,CAAO;GAC7C,CAAC;EACL,CAEO;CACT;AACF"}
@@ -0,0 +1,2 @@
1
+ export * from './withValidation';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/decorators/withValidation/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAA"}
@@ -0,0 +1,2 @@
1
+ var e=(e,t)=>(n,...r)=>{let{schema:i,errorsSignal:a}=t,o={},s=!1;for(let e in i){let t=i[e];if(typeof t==`function`){let r=t(n[e],n);typeof r==`string`&&(o[e]=r,s=!0)}}if(a.value=o,!s)return e(...r)},t=(e,t)=>async(n,r)=>{let{schema:i,errorsSignal:a}=t,o={},s=!1;for(let e in i){let t=i[e];if(typeof t==`function`){let r=t(n[e],n);typeof r==`string`&&(o[e]=r,s=!0)}}if(a.value=o,s)throw Error(`Core validation stopper`);return e(n,r)};exports.withResourceValidation=t,exports.withValidation=e;
2
+ //# sourceMappingURL=withValidation.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withValidation.cjs","names":[],"sources":["../../../src/decorators/withValidation/withValidation.ts"],"sourcesContent":["import { Signal } from '../../core/types'\n\nexport type ValidationSchema<T> = {\n [K in keyof T]?: (value: T[K], state: T) => string | true\n}\n\nexport interface ValidationOptions<T> {\n schema: ValidationSchema<T>\n errorsSignal: Signal<Record<string, string>>\n}\n\n/**\n * Декоратор для синхронных экшенов (например, переключение шагов)\n */\nexport const withValidation = <T, Args extends any[], R>(\n targetAction: (...args: Args) => R,\n options: ValidationOptions<T>\n) => {\n return (currentState: T, ...args: Args): R | void => {\n const { schema, errorsSignal } = options\n const nextErrors: Record<string, string> = {}\n let hasErrors = false\n\n for (const key in schema) {\n const validator = schema[key]\n if (typeof validator === 'function') {\n const result = validator(currentState[key], currentState)\n if (typeof result === 'string') {\n nextErrors[key] = result\n hasErrors = true\n }\n }\n }\n\n errorsSignal.value = nextErrors\n if (hasErrors) return\n\n return targetAction(...args)\n }\n}\n\n/**\n * Декоратор СТРОГО для асинхронных фетчеров engine.resource.\n * Прозрачно сохраняет типы входных и выходных данных оригинального fetcher.\n */\nexport const withResourceValidation = <S, T>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n options: ValidationOptions<S>\n) => {\n // 🟢 ИСПРАВЛЕНО: Явно указываем Promise<T>, возвращая исходный тип ответа фетчера\n return async (sourceValue: S, abortSignal: AbortSignal): Promise<T> => {\n const { schema, errorsSignal } = options\n const nextErrors: Record<string, string> = {}\n let hasErrors = false\n\n for (const key in schema) {\n const validator = schema[key]\n if (typeof validator === 'function') {\n const result = validator(sourceValue[key], sourceValue)\n if (typeof result === 'string') {\n nextErrors[key] = result\n hasErrors = true\n }\n }\n }\n\n errorsSignal.value = nextErrors\n \n if (hasErrors) {\n throw new Error('Core validation stopper')\n }\n\n return fetcher(sourceValue, abortSignal)\n }\n}"],"mappings":"AAcA,IAAa,GACX,EACA,KAEQ,EAAiB,GAAG,IAAyB,CACnD,GAAM,CAAE,SAAQ,gBAAiB,EAC3B,EAAqC,CAAC,EACxC,EAAY,GAEhB,IAAK,IAAM,KAAO,EAAQ,CACxB,IAAM,EAAY,EAAO,GACzB,GAAI,OAAO,GAAc,WAAY,CACnC,IAAM,EAAS,EAAU,EAAa,GAAM,CAAY,EACpD,OAAO,GAAW,WACpB,EAAW,GAAO,EAClB,EAAY,GAEhB,CACF,CAEA,KAAa,MAAQ,EACjB,GAEJ,OAAO,EAAa,GAAG,CAAI,CAC7B,EAOW,GACX,EACA,IAGO,MAAO,EAAgB,IAAyC,CACrE,GAAM,CAAE,SAAQ,gBAAiB,EAC3B,EAAqC,CAAC,EACxC,EAAY,GAEhB,IAAK,IAAM,KAAO,EAAQ,CACxB,IAAM,EAAY,EAAO,GACzB,GAAI,OAAO,GAAc,WAAY,CACnC,IAAM,EAAS,EAAU,EAAY,GAAM,CAAW,EAClD,OAAO,GAAW,WACpB,EAAW,GAAO,EAClB,EAAY,GAEhB,CACF,CAIA,GAFA,EAAa,MAAQ,EAEjB,EACF,MAAU,MAAM,yBAAyB,EAG3C,OAAO,EAAQ,EAAa,CAAW,CACzC"}
@@ -0,0 +1,18 @@
1
+ import { Signal } from '../../core/types';
2
+ export type ValidationSchema<T> = {
3
+ [K in keyof T]?: (value: T[K], state: T) => string | true;
4
+ };
5
+ export interface ValidationOptions<T> {
6
+ schema: ValidationSchema<T>;
7
+ errorsSignal: Signal<Record<string, string>>;
8
+ }
9
+ /**
10
+ * Декоратор для синхронных экшенов (например, переключение шагов)
11
+ */
12
+ export declare const withValidation: <T, Args extends any[], R>(targetAction: (...args: Args) => R, options: ValidationOptions<T>) => (currentState: T, ...args: Args) => R | void;
13
+ /**
14
+ * Декоратор СТРОГО для асинхронных фетчеров engine.resource.
15
+ * Прозрачно сохраняет типы входных и выходных данных оригинального fetcher.
16
+ */
17
+ export declare const withResourceValidation: <S, T>(fetcher: (source: S, signal: AbortSignal) => Promise<T>, options: ValidationOptions<S>) => (sourceValue: S, abortSignal: AbortSignal) => Promise<T>;
18
+ //# sourceMappingURL=withValidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withValidation.d.ts","sourceRoot":"","sources":["../../../src/decorators/withValidation/withValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAEzC,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;KAC/B,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI;CAC1D,CAAA;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAA;IAC3B,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAC7C;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,IAAI,SAAS,GAAG,EAAE,EAAE,CAAC,EACrD,cAAc,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAClC,SAAS,iBAAiB,CAAC,CAAC,CAAC,MAErB,cAAc,CAAC,EAAE,GAAG,MAAM,IAAI,KAAG,CAAC,GAAG,IAqB9C,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,CAAC,EAAE,CAAC,EACzC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EACvD,SAAS,iBAAiB,CAAC,CAAC,CAAC,MAGf,aAAa,CAAC,EAAE,aAAa,WAAW,KAAG,OAAO,CAAC,CAAC,CAwBnE,CAAA"}
@@ -0,0 +1,27 @@
1
+ //#region src/decorators/withValidation/withValidation.ts
2
+ var e = (e, t) => (n, ...r) => {
3
+ let { schema: i, errorsSignal: a } = t, o = {}, s = !1;
4
+ for (let e in i) {
5
+ let t = i[e];
6
+ if (typeof t == "function") {
7
+ let r = t(n[e], n);
8
+ typeof r == "string" && (o[e] = r, s = !0);
9
+ }
10
+ }
11
+ if (a.value = o, !s) return e(...r);
12
+ }, t = (e, t) => async (n, r) => {
13
+ let { schema: i, errorsSignal: a } = t, o = {}, s = !1;
14
+ for (let e in i) {
15
+ let t = i[e];
16
+ if (typeof t == "function") {
17
+ let r = t(n[e], n);
18
+ typeof r == "string" && (o[e] = r, s = !0);
19
+ }
20
+ }
21
+ if (a.value = o, s) throw Error("Core validation stopper");
22
+ return e(n, r);
23
+ };
24
+ //#endregion
25
+ export { t as withResourceValidation, e as withValidation };
26
+
27
+ //# sourceMappingURL=withValidation.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withValidation.mjs","names":[],"sources":["../../../src/decorators/withValidation/withValidation.ts"],"sourcesContent":["import { Signal } from '../../core/types'\n\nexport type ValidationSchema<T> = {\n [K in keyof T]?: (value: T[K], state: T) => string | true\n}\n\nexport interface ValidationOptions<T> {\n schema: ValidationSchema<T>\n errorsSignal: Signal<Record<string, string>>\n}\n\n/**\n * Декоратор для синхронных экшенов (например, переключение шагов)\n */\nexport const withValidation = <T, Args extends any[], R>(\n targetAction: (...args: Args) => R,\n options: ValidationOptions<T>\n) => {\n return (currentState: T, ...args: Args): R | void => {\n const { schema, errorsSignal } = options\n const nextErrors: Record<string, string> = {}\n let hasErrors = false\n\n for (const key in schema) {\n const validator = schema[key]\n if (typeof validator === 'function') {\n const result = validator(currentState[key], currentState)\n if (typeof result === 'string') {\n nextErrors[key] = result\n hasErrors = true\n }\n }\n }\n\n errorsSignal.value = nextErrors\n if (hasErrors) return\n\n return targetAction(...args)\n }\n}\n\n/**\n * Декоратор СТРОГО для асинхронных фетчеров engine.resource.\n * Прозрачно сохраняет типы входных и выходных данных оригинального fetcher.\n */\nexport const withResourceValidation = <S, T>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n options: ValidationOptions<S>\n) => {\n // 🟢 ИСПРАВЛЕНО: Явно указываем Promise<T>, возвращая исходный тип ответа фетчера\n return async (sourceValue: S, abortSignal: AbortSignal): Promise<T> => {\n const { schema, errorsSignal } = options\n const nextErrors: Record<string, string> = {}\n let hasErrors = false\n\n for (const key in schema) {\n const validator = schema[key]\n if (typeof validator === 'function') {\n const result = validator(sourceValue[key], sourceValue)\n if (typeof result === 'string') {\n nextErrors[key] = result\n hasErrors = true\n }\n }\n }\n\n errorsSignal.value = nextErrors\n \n if (hasErrors) {\n throw new Error('Core validation stopper')\n }\n\n return fetcher(sourceValue, abortSignal)\n }\n}"],"mappings":";AAcA,IAAa,KACX,GACA,OAEQ,GAAiB,GAAG,MAAyB;CACnD,IAAM,EAAE,WAAQ,oBAAiB,GAC3B,IAAqC,CAAC,GACxC,IAAY;CAEhB,KAAK,IAAM,KAAO,GAAQ;EACxB,IAAM,IAAY,EAAO;EACzB,IAAI,OAAO,KAAc,YAAY;GACnC,IAAM,IAAS,EAAU,EAAa,IAAM,CAAY;GACxD,AAAI,OAAO,KAAW,aACpB,EAAW,KAAO,GAClB,IAAY;EAEhB;CACF;CAEA,MAAa,QAAQ,GACjB,IAEJ,OAAO,EAAa,GAAG,CAAI;AAC7B,GAOW,KACX,GACA,MAGO,OAAO,GAAgB,MAAyC;CACrE,IAAM,EAAE,WAAQ,oBAAiB,GAC3B,IAAqC,CAAC,GACxC,IAAY;CAEhB,KAAK,IAAM,KAAO,GAAQ;EACxB,IAAM,IAAY,EAAO;EACzB,IAAI,OAAO,KAAc,YAAY;GACnC,IAAM,IAAS,EAAU,EAAY,IAAM,CAAW;GACtD,AAAI,OAAO,KAAW,aACpB,EAAW,KAAO,GAClB,IAAY;EAEhB;CACF;CAIA,IAFA,EAAa,QAAQ,GAEjB,GACF,MAAU,MAAM,yBAAyB;CAG3C,OAAO,EAAQ,GAAa,CAAW;AACzC"}
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./core.cjs"),t=require("./AbstractService.cjs"),n=require("./BaseREServiceEnhanced.cjs"),r=require("./decorators/withCache/withCache.cjs"),i=require("./decorators/withDebounce/withDebounce.cjs"),a=require("./decorators/withLongPolling/withLongPolling.cjs"),o=require("./decorators/withThrottle/withThrottle.cjs"),s=require("./decorators/withThrottleComputed/withThrottleComputed.cjs"),c=require("./decorators/withThrottleAndCache/withThrottleAndCache.cjs"),l=require("./decorators/withStaleWhileRevalidate/withStaleWhileRevalidate.cjs");exports.AbstractService=t.AbstractService,exports.BaseREService=t.BaseREService,exports.BaseREServiceEnhanced=n.BaseREServiceEnhanced,exports.ReactiveEngine=e.ReactiveEngine,exports.ReactiveEngineAutomatic=e.ReactiveEngineAutomatic,exports.ReactiveEngineAutomaticHard=e.ReactiveEngineAutomaticHard,exports.ReactiveEngineCore=e.ReactiveEngineCore,exports.withCache=r.withCache,exports.withDebounce=i.withDebounce,exports.withLongPolling=a.withLongPolling,exports.withStaleWhileRevalidate=l.withStaleWhileRevalidate,exports.withThrottle=o.withThrottle,exports.withThrottleAndCache=c.withThrottleAndCache,exports.withThrottleComputed=s.withThrottleComputed;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./core.cjs"),t=require("./core.automatic.cjs"),n=require("./core.autolog.cjs"),r=require("./AbstractService.cjs"),i=require("./BaseREServiceEnhanced.cjs"),a=require("./decorators/withCache/withCache.cjs"),o=require("./decorators/withDebounce/withDebounce.cjs"),s=require("./decorators/withLongPolling/withLongPolling.cjs"),c=require("./decorators/withThrottle/withThrottle.cjs"),l=require("./decorators/withThrottleComputed/withThrottleComputed.cjs"),u=require("./decorators/withThrottleAndCache/withThrottleAndCache.cjs"),d=require("./decorators/withStaleWhileRevalidate/withStaleWhileRevalidate.cjs"),f=require("./decorators/withValidation/withValidation.cjs");exports.AbstractService=r.AbstractService,exports.BaseREService=r.BaseREService,exports.BaseREServiceEnhanced=i.BaseREServiceEnhanced,exports.ReactiveEngine=t.ReactiveEngineAutomatic,exports.ReactiveEngineAutolog=n.ReactiveEngineAutolog,exports.ReactiveEngineAutomatic=t.ReactiveEngineAutomatic,exports.ReactiveEngineCore=e.ReactiveEngine,exports.withCache=a.withCache,exports.withDebounce=o.withDebounce,exports.withLongPolling=s.withLongPolling,exports.withResourceValidation=f.withResourceValidation,exports.withStaleWhileRevalidate=d.withStaleWhileRevalidate,exports.withThrottle=c.withThrottle,exports.withThrottleAndCache=u.withThrottleAndCache,exports.withThrottleComputed=l.withThrottleComputed,exports.withValidation=f.withValidation;