@pravosleva/reactive-engine 1.6.0 → 1.7.0-beta

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 (52) hide show
  1. package/dist/angular/ReactiveEngine4Angular.cjs +1 -1
  2. package/dist/angular/ReactiveEngine4Angular.cjs.map +1 -1
  3. package/dist/angular/ReactiveEngine4Angular.d.ts +16 -10
  4. package/dist/angular/ReactiveEngine4Angular.d.ts.map +1 -1
  5. package/dist/angular/ReactiveEngine4Angular.mjs +29 -5
  6. package/dist/angular/ReactiveEngine4Angular.mjs.map +1 -1
  7. package/dist/core/core.d.ts.map +1 -1
  8. package/dist/core.cjs +1 -1
  9. package/dist/core.cjs.map +1 -1
  10. package/dist/core.mjs +42 -25
  11. package/dist/core.mjs.map +1 -1
  12. package/dist/decorators/withStaleWhileRevalidate/withStaleWhileRevalidate.cjs +1 -1
  13. package/dist/decorators/withStaleWhileRevalidate/withStaleWhileRevalidate.cjs.map +1 -1
  14. package/dist/decorators/withStaleWhileRevalidate/withStaleWhileRevalidate.d.ts +7 -25
  15. package/dist/decorators/withStaleWhileRevalidate/withStaleWhileRevalidate.d.ts.map +1 -1
  16. package/dist/decorators/withStaleWhileRevalidate/withStaleWhileRevalidate.mjs +23 -8
  17. package/dist/decorators/withStaleWhileRevalidate/withStaleWhileRevalidate.mjs.map +1 -1
  18. package/dist/react/ReactiveEngine4React.cjs +1 -5
  19. package/dist/react/ReactiveEngine4React.cjs.map +1 -1
  20. package/dist/react/ReactiveEngine4React.d.ts +12 -18
  21. package/dist/react/ReactiveEngine4React.d.ts.map +1 -1
  22. package/dist/react/ReactiveEngine4React.mjs +27 -5
  23. package/dist/react/ReactiveEngine4React.mjs.map +1 -1
  24. package/dist/react/hooks/types.d.ts +4 -0
  25. package/dist/react/hooks/types.d.ts.map +1 -1
  26. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.cjs +1 -1
  27. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.cjs.map +1 -1
  28. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.d.ts +4 -10
  29. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.d.ts.map +1 -1
  30. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.mjs +20 -11
  31. package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.mjs.map +1 -1
  32. package/dist/react/hooks/useReactiveValue/useReactiveValue.cjs +1 -1
  33. package/dist/react/hooks/useReactiveValue/useReactiveValue.cjs.map +1 -1
  34. package/dist/react/hooks/useReactiveValue/useReactiveValue.d.ts +10 -41
  35. package/dist/react/hooks/useReactiveValue/useReactiveValue.d.ts.map +1 -1
  36. package/dist/react/hooks/useReactiveValue/useReactiveValue.mjs +19 -10
  37. package/dist/react/hooks/useReactiveValue/useReactiveValue.mjs.map +1 -1
  38. package/dist/reactive-engine.umd.js +2 -2
  39. package/dist/reactive-engine.umd.js.map +1 -1
  40. package/dist/vue/ReactiveEngine4Vue.cjs +1 -1
  41. package/dist/vue/ReactiveEngine4Vue.cjs.map +1 -1
  42. package/dist/vue/ReactiveEngine4Vue.d.ts +21 -6
  43. package/dist/vue/ReactiveEngine4Vue.d.ts.map +1 -1
  44. package/dist/vue/ReactiveEngine4Vue.mjs +31 -8
  45. package/dist/vue/ReactiveEngine4Vue.mjs.map +1 -1
  46. package/dist/vue/composables/useReactiveValue.cjs +1 -1
  47. package/dist/vue/composables/useReactiveValue.cjs.map +1 -1
  48. package/dist/vue/composables/useReactiveValue.d.ts +4 -45
  49. package/dist/vue/composables/useReactiveValue.d.ts.map +1 -1
  50. package/dist/vue/composables/useReactiveValue.mjs +10 -5
  51. package/dist/vue/composables/useReactiveValue.mjs.map +1 -1
  52. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ReactiveEngine4React.cjs","names":[],"sources":["../../src/react/ReactiveEngine4React.ts"],"sourcesContent":["import { useState as useStateFromReact, useEffect as useEffectFromReact } from 'react'\nimport { ReactiveEngine as OriginalReactiveEngine, CleanupFn } from '../core/core'\n\n/**\n * Класс адаптера для интеграции реактивного движка с React.\n * Наследует функциональность оригинального реактивного движка и предоставляет специализированные методы для работы с React.\n */\nexport class ReactiveEngine4React extends OriginalReactiveEngine {\n protected override frameworkPrefix = 'react'\n\n // Описываем строгие типы для адаптеров React, чтобы не ломать встроенные типы React.\n private reactAdapters: {\n useState: typeof useStateFromReact;\n useEffect: typeof useEffectFromReact;\n } = {\n useState: useStateFromReact,\n useEffect: useEffectFromReact,\n }\n\n /**\n * Установка адаптеров React.\n * @function setReactAdapters\n * @param {Function} useState - Функция useState из React.\n * @param {Function} useEffect - Функция useEffect из React.\n * @returns {void}\n * @source\n */\n public setReactAdapters(\n useState: typeof useStateFromReact,\n useEffect: typeof useEffectFromReact\n ): void {\n this.reactAdapters = { useState, useEffect }\n }\n\n /**\n * Использование реактивного значения в React компоненте.\n * @template T\n * @function use\n * @param {{ value: T; subscribe: (cb: (v: T) => void) => CleanupFn }} item - Реактивный объект.\n * @returns {T} - Значение реактивного объекта.\n * @source\n */\n public use<T>(item: { value: T; subscribe: (cb: (v: T) => void) => CleanupFn }): T {\n if (!this.reactAdapters) {\n throw new Error(\"[React Error]: Адаптеры React не установлены. Вызовите engine.setReactAdapters(useState, useEffect).\")\n }\n\n // NOTE: (защита) проверяем, что нам передали объект сигнала\n if (!item || typeof item.subscribe !== 'function') {\n const errorMsg = `\n [Reactive Error]: engine.use() получил некорректный объект!\n Скорее всего, вы пытаетесь подписаться на свойство сервиса, которое не было инициализировано.\n Проверьте, что в классе написано: public mySignal = this.engine.signal(...)\n `\n console.error(errorMsg, { item })\n throw new Error(errorMsg)\n }\n const [val, setVal] = this.reactAdapters.useState(item.value)\n\n this.reactAdapters.useEffect(\n () => {\n return item.subscribe(setVal)\n },\n [item]\n )\n\n return val\n }\n}\n"],"mappings":"sDAOA,IAAa,EAAb,cAA0C,EAAA,cAAuB,CAC/D,gBAAqC,QAGrC,cAGI,CACA,SAAU,EAAA,SACV,UAAW,EAAA,SACb,EAUF,iBACE,EACA,EACM,CACN,KAAK,cAAgB,CAAE,WAAU,WAAU,CAC7C,CAUA,IAAc,EAAqE,CACjF,GAAI,CAAC,KAAK,cACR,MAAU,MAAM,sGAAsG,EAIxH,GAAI,CAAC,GAAQ,OAAO,EAAK,WAAc,WAAY,CACjD,IAAM,EAAW;;;;QAMjB,MADA,QAAQ,MAAM,EAAU,CAAE,MAAK,CAAC,EACtB,MAAM,CAAQ,CAC1B,CACA,GAAM,CAAC,EAAK,GAAU,KAAK,cAAc,SAAS,EAAK,KAAK,EAS5D,OAPA,KAAK,cAAc,cAER,EAAK,UAAU,CAAM,EAE9B,CAAC,CAAI,CACP,EAEO,CACT,CACF"}
1
+ {"version":3,"file":"ReactiveEngine4React.cjs","names":[],"sources":["../../src/react/ReactiveEngine4React.ts"],"sourcesContent":["import { useState as useStateFromReact, useEffect as useEffectFromReact } from 'react'\nimport { ReactiveEngine as OriginalReactiveEngine, type CleanupFn } from '../core/core'\n\nexport interface ISignalLike<V> {\n value: V;\n subscribe: (cb: (v: V) => void) => CleanupFn;\n}\n\n/**\n * Класс адаптера для интеграции реактивного движка с React.\n * Предоставляет полиморфные методы для бесшовной работы с Сигналами и Proxy-объектами.\n */\nexport class ReactiveEngine4React extends OriginalReactiveEngine {\n protected override frameworkPrefix = 'react'\n\n private reactAdapters: {\n useState: typeof useStateFromReact;\n useEffect: typeof useEffectFromReact;\n } = {\n useState: useStateFromReact,\n useEffect: useEffectFromReact,\n }\n\n public setReactAdapters(\n useState: typeof useStateFromReact,\n useEffect: typeof useEffectFromReact\n ): void {\n this.reactAdapters = { useState, useEffect }\n }\n\n /**\n * Переопределяем метод создания реактивных объектов для React-версии движка.\n * Безопасно внедряет скрытое свойство __subscribe в оригинальный граф без double-proxying.\n */\n public override reactive<T extends object>(target: T, name?: string): T {\n if (!Reflect.has(target, '__subscribe')) {\n Object.defineProperty(target, '__subscribe', {\n get: () => {\n return (cb: () => void) => {\n return this.effect(() => {\n try {\n JSON.stringify(originalProxy) // Глубокий прогрев геттеров ядра\n } catch (e) {\n Object.values(originalProxy as Record<string, unknown>)\n }\n cb() // Пинаем колбэк хука React\n }, 'react-proxy-internal-subscription')\n }\n },\n configurable: true,\n enumerable: false\n })\n }\n\n const originalProxy = super.reactive(target, name)\n return originalProxy\n }\n\n /**\n * Использование реактивного значения в React-компоненте.\n * Полиморфно поддерживает как атомарные Сигналы/Computed, так и глубокие реактивные Proxy-объекты.\n */\n public use<T>(item: T): T extends ISignalLike<infer V> ? V : T {\n if (!this.reactAdapters) {\n throw new Error(\"[React Error]: Адаптеры React не установлены. Вызовите engine.setReactAdapters(useState, useEffect).\")\n }\n\n if (item === null || item === undefined || typeof item !== 'object') {\n return item as any\n }\n\n const isSignal = 'subscribe' in item && typeof (item as Record<string, unknown>).subscribe === 'function'\n\n // 1. Сценарий А: Передан примитивный Сигнал или Computed\n if (isSignal) {\n const signalItem = (item as unknown) as ISignalLike<unknown>\n const [val, setVal] = this.reactAdapters.useState<unknown>(signalItem.value)\n\n this.reactAdapters.useEffect(\n () => {\n return signalItem.subscribe(setVal)\n },\n [signalItem]\n )\n\n return val as (T extends ISignalLike<infer V> ? V : T)\n }\n\n // 2. Сценарий Б: Передан Proxy-объект из метода reactive()\n const [, forceUpdate] = this.reactAdapters.useState<unknown[]>([])\n\n if ('__subscribe' in item && typeof (item as Record<string, unknown>).__subscribe === 'function') {\n this.reactAdapters.useEffect(() => {\n const unsubscribe = ((item as Record<string, unknown>).__subscribe as (cb: () => void) => CleanupFn)(() => {\n forceUpdate([]) // Триггерим асинхронный авто-батчинг ререндера React\n })\n return () => unsubscribe()\n }, [item])\n }\n\n return item as (T extends ISignalLike<infer V> ? V : T)\n }\n}\n"],"mappings":"sDAYA,IAAa,EAAb,cAA0C,EAAA,cAAuB,CAC/D,gBAAqC,QAErC,cAGI,CACA,SAAU,EAAA,SACV,UAAW,EAAA,SACb,EAEF,iBACE,EACA,EACM,CACN,KAAK,cAAgB,CAAE,WAAU,WAAU,CAC7C,CAMA,SAA2C,EAAW,EAAkB,CACjE,QAAQ,IAAI,EAAQ,aAAa,GACpC,OAAO,eAAe,EAAQ,cAAe,CAC3C,QACU,GACC,KAAK,WAAa,CACvB,GAAI,CACF,KAAK,UAAU,CAAa,CAC9B,MAAY,CACV,OAAO,OAAO,CAAwC,CACxD,CACA,EAAG,CACL,EAAG,mCAAmC,EAG1C,aAAc,GACd,WAAY,EACd,CAAC,EAGH,IAAM,EAAgB,MAAM,SAAS,EAAQ,CAAI,EACjD,OAAO,CACT,CAMA,IAAc,EAAiD,CAC7D,GAAI,CAAC,KAAK,cACR,MAAU,MAAM,sGAAsG,EAGxH,GAA2C,OAAO,GAAS,WAAvD,EACF,OAAO,EAMT,GAHiB,cAAe,GAAQ,OAAQ,EAAiC,WAAc,WAGjF,CACZ,IAAM,EAAc,EACd,CAAC,EAAK,GAAU,KAAK,cAAc,SAAkB,EAAW,KAAK,EAS3E,OAPA,KAAK,cAAc,cAER,EAAW,UAAU,CAAM,EAEpC,CAAC,CAAU,CACb,EAEO,CACT,CAGA,GAAM,EAAG,GAAe,KAAK,cAAc,SAAoB,CAAC,CAAC,EAWjE,MATI,gBAAiB,GAAQ,OAAQ,EAAiC,aAAgB,YACpF,KAAK,cAAc,cAAgB,CACjC,IAAM,EAAgB,EAAiC,gBAAoD,CACzG,EAAY,CAAC,CAAC,CAChB,CAAC,EACD,UAAa,EAAY,CAC3B,EAAG,CAAC,CAAI,CAAC,EAGJ,CACT,CACF"}
@@ -1,32 +1,26 @@
1
1
  import { useState as useStateFromReact, useEffect as useEffectFromReact } from 'react';
2
2
  import { ReactiveEngine as OriginalReactiveEngine, CleanupFn } from '../core/core';
3
+ export interface ISignalLike<V> {
4
+ value: V;
5
+ subscribe: (cb: (v: V) => void) => CleanupFn;
6
+ }
3
7
  /**
4
8
  * Класс адаптера для интеграции реактивного движка с React.
5
- * Наследует функциональность оригинального реактивного движка и предоставляет специализированные методы для работы с React.
9
+ * Предоставляет полиморфные методы для бесшовной работы с Сигналами и Proxy-объектами.
6
10
  */
7
11
  export declare class ReactiveEngine4React extends OriginalReactiveEngine {
8
12
  protected frameworkPrefix: string;
9
13
  private reactAdapters;
14
+ setReactAdapters(useState: typeof useStateFromReact, useEffect: typeof useEffectFromReact): void;
10
15
  /**
11
- * Установка адаптеров React.
12
- * @function setReactAdapters
13
- * @param {Function} useState - Функция useState из React.
14
- * @param {Function} useEffect - Функция useEffect из React.
15
- * @returns {void}
16
- * @source
16
+ * Переопределяем метод создания реактивных объектов для React-версии движка.
17
+ * Безопасно внедряет скрытое свойство __subscribe в оригинальный граф без double-proxying.
17
18
  */
18
- setReactAdapters(useState: typeof useStateFromReact, useEffect: typeof useEffectFromReact): void;
19
+ reactive<T extends object>(target: T, name?: string): T;
19
20
  /**
20
- * Использование реактивного значения в React компоненте.
21
- * @template T
22
- * @function use
23
- * @param {{ value: T; subscribe: (cb: (v: T) => void) => CleanupFn }} item - Реактивный объект.
24
- * @returns {T} - Значение реактивного объекта.
25
- * @source
21
+ * Использование реактивного значения в React-компоненте.
22
+ * Полиморфно поддерживает как атомарные Сигналы/Computed, так и глубокие реактивные Proxy-объекты.
26
23
  */
27
- use<T>(item: {
28
- value: T;
29
- subscribe: (cb: (v: T) => void) => CleanupFn;
30
- }): T;
24
+ use<T>(item: T): T extends ISignalLike<infer V> ? V : T;
31
25
  }
32
26
  //# sourceMappingURL=ReactiveEngine4React.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ReactiveEngine4React.d.ts","sourceRoot":"","sources":["../../src/react/ReactiveEngine4React.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,iBAAiB,EAAE,SAAS,IAAI,kBAAkB,EAAE,MAAM,OAAO,CAAA;AACtF,OAAO,EAAE,cAAc,IAAI,sBAAsB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAElF;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,sBAAsB;IAC9D,UAAmB,eAAe,SAAU;IAG5C,OAAO,CAAC,aAAa,CAMlB;IAEH;;;;;;;OAOG;IACI,gBAAgB,CACrB,QAAQ,EAAE,OAAO,iBAAiB,EAClC,SAAS,EAAE,OAAO,kBAAkB,GACnC,IAAI;IAIP;;;;;;;OAOG;IACI,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,KAAK,SAAS,CAAA;KAAE,GAAG,CAAC;CA0BnF"}
1
+ {"version":3,"file":"ReactiveEngine4React.d.ts","sourceRoot":"","sources":["../../src/react/ReactiveEngine4React.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,iBAAiB,EAAE,SAAS,IAAI,kBAAkB,EAAE,MAAM,OAAO,CAAA;AACtF,OAAO,EAAE,cAAc,IAAI,sBAAsB,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AAEvF,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,KAAK,EAAE,CAAC,CAAC;IACT,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,KAAK,SAAS,CAAC;CAC9C;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,sBAAsB;IAC9D,UAAmB,eAAe,SAAU;IAE5C,OAAO,CAAC,aAAa,CAMlB;IAEI,gBAAgB,CACrB,QAAQ,EAAE,OAAO,iBAAiB,EAClC,SAAS,EAAE,OAAO,kBAAkB,GACnC,IAAI;IAIP;;;OAGG;IACa,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC;IAwBvE;;;OAGG;IACI,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;CAwC/D"}
@@ -13,14 +13,36 @@ var r = class extends e {
13
13
  useEffect: t
14
14
  };
15
15
  }
16
+ reactive(e, t) {
17
+ Reflect.has(e, "__subscribe") || Object.defineProperty(e, "__subscribe", {
18
+ get: () => (e) => this.effect(() => {
19
+ try {
20
+ JSON.stringify(n);
21
+ } catch {
22
+ Object.values(n);
23
+ }
24
+ e();
25
+ }, "react-proxy-internal-subscription"),
26
+ configurable: !0,
27
+ enumerable: !1
28
+ });
29
+ let n = super.reactive(e, t);
30
+ return n;
31
+ }
16
32
  use(e) {
17
33
  if (!this.reactAdapters) throw Error("[React Error]: Адаптеры React не установлены. Вызовите engine.setReactAdapters(useState, useEffect).");
18
- if (!e || typeof e.subscribe != "function") {
19
- let t = "\n [Reactive Error]: engine.use() получил некорректный объект!\n Скорее всего, вы пытаетесь подписаться на свойство сервиса, которое не было инициализировано.\n Проверьте, что в классе написано: public mySignal = this.engine.signal(...)\n ";
20
- throw console.error(t, { item: e }), Error(t);
34
+ if (typeof e != "object" || !e) return e;
35
+ if ("subscribe" in e && typeof e.subscribe == "function") {
36
+ let t = e, [n, r] = this.reactAdapters.useState(t.value);
37
+ return this.reactAdapters.useEffect(() => t.subscribe(r), [t]), n;
21
38
  }
22
- let [t, n] = this.reactAdapters.useState(e.value);
23
- return this.reactAdapters.useEffect(() => e.subscribe(n), [e]), t;
39
+ let [, t] = this.reactAdapters.useState([]);
40
+ return "__subscribe" in e && typeof e.__subscribe == "function" && this.reactAdapters.useEffect(() => {
41
+ let n = e.__subscribe(() => {
42
+ t([]);
43
+ });
44
+ return () => n();
45
+ }, [e]), e;
24
46
  }
25
47
  };
26
48
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"ReactiveEngine4React.mjs","names":[],"sources":["../../src/react/ReactiveEngine4React.ts"],"sourcesContent":["import { useState as useStateFromReact, useEffect as useEffectFromReact } from 'react'\nimport { ReactiveEngine as OriginalReactiveEngine, CleanupFn } from '../core/core'\n\n/**\n * Класс адаптера для интеграции реактивного движка с React.\n * Наследует функциональность оригинального реактивного движка и предоставляет специализированные методы для работы с React.\n */\nexport class ReactiveEngine4React extends OriginalReactiveEngine {\n protected override frameworkPrefix = 'react'\n\n // Описываем строгие типы для адаптеров React, чтобы не ломать встроенные типы React.\n private reactAdapters: {\n useState: typeof useStateFromReact;\n useEffect: typeof useEffectFromReact;\n } = {\n useState: useStateFromReact,\n useEffect: useEffectFromReact,\n }\n\n /**\n * Установка адаптеров React.\n * @function setReactAdapters\n * @param {Function} useState - Функция useState из React.\n * @param {Function} useEffect - Функция useEffect из React.\n * @returns {void}\n * @source\n */\n public setReactAdapters(\n useState: typeof useStateFromReact,\n useEffect: typeof useEffectFromReact\n ): void {\n this.reactAdapters = { useState, useEffect }\n }\n\n /**\n * Использование реактивного значения в React компоненте.\n * @template T\n * @function use\n * @param {{ value: T; subscribe: (cb: (v: T) => void) => CleanupFn }} item - Реактивный объект.\n * @returns {T} - Значение реактивного объекта.\n * @source\n */\n public use<T>(item: { value: T; subscribe: (cb: (v: T) => void) => CleanupFn }): T {\n if (!this.reactAdapters) {\n throw new Error(\"[React Error]: Адаптеры React не установлены. Вызовите engine.setReactAdapters(useState, useEffect).\")\n }\n\n // NOTE: (защита) проверяем, что нам передали объект сигнала\n if (!item || typeof item.subscribe !== 'function') {\n const errorMsg = `\n [Reactive Error]: engine.use() получил некорректный объект!\n Скорее всего, вы пытаетесь подписаться на свойство сервиса, которое не было инициализировано.\n Проверьте, что в классе написано: public mySignal = this.engine.signal(...)\n `\n console.error(errorMsg, { item })\n throw new Error(errorMsg)\n }\n const [val, setVal] = this.reactAdapters.useState(item.value)\n\n this.reactAdapters.useEffect(\n () => {\n return item.subscribe(setVal)\n },\n [item]\n )\n\n return val\n }\n}\n"],"mappings":";;;AAOA,IAAa,IAAb,cAA0C,EAAuB;CAC/D,kBAAqC;CAGrC,gBAGI;EACU;EACC;CACb;CAUF,iBACE,GACA,GACM;EACN,KAAK,gBAAgB;GAAE;GAAU;EAAU;CAC7C;CAUA,IAAc,GAAqE;EACjF,IAAI,CAAC,KAAK,eACR,MAAU,MAAM,sGAAsG;EAIxH,IAAI,CAAC,KAAQ,OAAO,EAAK,aAAc,YAAY;GACjD,IAAM,IAAW;GAMjB,MADA,QAAQ,MAAM,GAAU,EAAE,QAAK,CAAC,GACtB,MAAM,CAAQ;EAC1B;EACA,IAAM,CAAC,GAAK,KAAU,KAAK,cAAc,SAAS,EAAK,KAAK;EAS5D,OAPA,KAAK,cAAc,gBAER,EAAK,UAAU,CAAM,GAE9B,CAAC,CAAI,CACP,GAEO;CACT;AACF"}
1
+ {"version":3,"file":"ReactiveEngine4React.mjs","names":[],"sources":["../../src/react/ReactiveEngine4React.ts"],"sourcesContent":["import { useState as useStateFromReact, useEffect as useEffectFromReact } from 'react'\nimport { ReactiveEngine as OriginalReactiveEngine, type CleanupFn } from '../core/core'\n\nexport interface ISignalLike<V> {\n value: V;\n subscribe: (cb: (v: V) => void) => CleanupFn;\n}\n\n/**\n * Класс адаптера для интеграции реактивного движка с React.\n * Предоставляет полиморфные методы для бесшовной работы с Сигналами и Proxy-объектами.\n */\nexport class ReactiveEngine4React extends OriginalReactiveEngine {\n protected override frameworkPrefix = 'react'\n\n private reactAdapters: {\n useState: typeof useStateFromReact;\n useEffect: typeof useEffectFromReact;\n } = {\n useState: useStateFromReact,\n useEffect: useEffectFromReact,\n }\n\n public setReactAdapters(\n useState: typeof useStateFromReact,\n useEffect: typeof useEffectFromReact\n ): void {\n this.reactAdapters = { useState, useEffect }\n }\n\n /**\n * Переопределяем метод создания реактивных объектов для React-версии движка.\n * Безопасно внедряет скрытое свойство __subscribe в оригинальный граф без double-proxying.\n */\n public override reactive<T extends object>(target: T, name?: string): T {\n if (!Reflect.has(target, '__subscribe')) {\n Object.defineProperty(target, '__subscribe', {\n get: () => {\n return (cb: () => void) => {\n return this.effect(() => {\n try {\n JSON.stringify(originalProxy) // Глубокий прогрев геттеров ядра\n } catch (e) {\n Object.values(originalProxy as Record<string, unknown>)\n }\n cb() // Пинаем колбэк хука React\n }, 'react-proxy-internal-subscription')\n }\n },\n configurable: true,\n enumerable: false\n })\n }\n\n const originalProxy = super.reactive(target, name)\n return originalProxy\n }\n\n /**\n * Использование реактивного значения в React-компоненте.\n * Полиморфно поддерживает как атомарные Сигналы/Computed, так и глубокие реактивные Proxy-объекты.\n */\n public use<T>(item: T): T extends ISignalLike<infer V> ? V : T {\n if (!this.reactAdapters) {\n throw new Error(\"[React Error]: Адаптеры React не установлены. Вызовите engine.setReactAdapters(useState, useEffect).\")\n }\n\n if (item === null || item === undefined || typeof item !== 'object') {\n return item as any\n }\n\n const isSignal = 'subscribe' in item && typeof (item as Record<string, unknown>).subscribe === 'function'\n\n // 1. Сценарий А: Передан примитивный Сигнал или Computed\n if (isSignal) {\n const signalItem = (item as unknown) as ISignalLike<unknown>\n const [val, setVal] = this.reactAdapters.useState<unknown>(signalItem.value)\n\n this.reactAdapters.useEffect(\n () => {\n return signalItem.subscribe(setVal)\n },\n [signalItem]\n )\n\n return val as (T extends ISignalLike<infer V> ? V : T)\n }\n\n // 2. Сценарий Б: Передан Proxy-объект из метода reactive()\n const [, forceUpdate] = this.reactAdapters.useState<unknown[]>([])\n\n if ('__subscribe' in item && typeof (item as Record<string, unknown>).__subscribe === 'function') {\n this.reactAdapters.useEffect(() => {\n const unsubscribe = ((item as Record<string, unknown>).__subscribe as (cb: () => void) => CleanupFn)(() => {\n forceUpdate([]) // Триггерим асинхронный авто-батчинг ререндера React\n })\n return () => unsubscribe()\n }, [item])\n }\n\n return item as (T extends ISignalLike<infer V> ? V : T)\n }\n}\n"],"mappings":";;;AAYA,IAAa,IAAb,cAA0C,EAAuB;CAC/D,kBAAqC;CAErC,gBAGI;EACU;EACC;CACb;CAEF,iBACE,GACA,GACM;EACN,KAAK,gBAAgB;GAAE;GAAU;EAAU;CAC7C;CAMA,SAA2C,GAAW,GAAkB;EACtE,AAAK,QAAQ,IAAI,GAAQ,aAAa,KACpC,OAAO,eAAe,GAAQ,eAAe;GAC3C,YACU,MACC,KAAK,aAAa;IACvB,IAAI;KACF,KAAK,UAAU,CAAa;IAC9B,QAAY;KACV,OAAO,OAAO,CAAwC;IACxD;IACA,EAAG;GACL,GAAG,mCAAmC;GAG1C,cAAc;GACd,YAAY;EACd,CAAC;EAGH,IAAM,IAAgB,MAAM,SAAS,GAAQ,CAAI;EACjD,OAAO;CACT;CAMA,IAAc,GAAiD;EAC7D,IAAI,CAAC,KAAK,eACR,MAAU,MAAM,sGAAsG;EAGxH,IAA2C,OAAO,KAAS,aAAvD,GACF,OAAO;EAMT,IAHiB,eAAe,KAAQ,OAAQ,EAAiC,aAAc,YAGjF;GACZ,IAAM,IAAc,GACd,CAAC,GAAK,KAAU,KAAK,cAAc,SAAkB,EAAW,KAAK;GAS3E,OAPA,KAAK,cAAc,gBAER,EAAW,UAAU,CAAM,GAEpC,CAAC,CAAU,CACb,GAEO;EACT;EAGA,IAAM,GAAG,KAAe,KAAK,cAAc,SAAoB,CAAC,CAAC;EAWjE,OATI,iBAAiB,KAAQ,OAAQ,EAAiC,eAAgB,cACpF,KAAK,cAAc,gBAAgB;GACjC,IAAM,IAAgB,EAAiC,kBAAoD;IACzG,EAAY,CAAC,CAAC;GAChB,CAAC;GACD,aAAa,EAAY;EAC3B,GAAG,CAAC,CAAI,CAAC,GAGJ;CACT;AACF"}
@@ -13,4 +13,8 @@ export interface ObservableItem<T> {
13
13
  * либо функцию-фабрику, которая его лениво возвращает.
14
14
  */
15
15
  export type ReactiveInput<T> = ObservableItem<T> | (() => ObservableItem<T>);
16
+ export interface ISignalLike<V> {
17
+ value: V;
18
+ subscribe: (cb: (v: V) => void) => CleanupFn;
19
+ }
16
20
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC;;;GAGG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,KAAK,SAAS,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAEtC;;;GAGG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,KAAK,SAAS,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;AAE7E,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,KAAK,EAAE,CAAC,CAAC;IACT,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,KAAK,SAAS,CAAC;CAC9C"}
@@ -1,2 +1,2 @@
1
- let e=require("react");var t=(t,n)=>{let r=(0,e.useRef)(n);r.current=n,(0,e.useLayoutEffect)(()=>{if(!t||typeof t.subscribe!=`function`)return;let e=t.subscribe(e=>{r.current(e)});return()=>{typeof e==`function`&&e()}},[t])};exports.useReactiveSubscription=t;
1
+ let e=require("react");function t(t,n){let r=(0,e.useRef)(n);(0,e.useEffect)(()=>{r.current=n},[n]),(0,e.useEffect)(()=>{if(!(!t||typeof t!=`object`)){if(`subscribe`in t&&typeof t.subscribe==`function`){let e=t.subscribe(e=>{r.current(e)});return()=>e()}if(`__subscribe`in t&&typeof t.__subscribe==`function`){let e=t.__subscribe(()=>{r.current(t)});return()=>e()}}},[t])}exports.useReactiveSubscription=t;
2
2
  //# sourceMappingURL=useReactiveSubscription.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useReactiveSubscription.cjs","names":[],"sources":["../../../../src/react/hooks/useReactiveSubscription/useReactiveSubscription.ts"],"sourcesContent":["import { useLayoutEffect, useRef } from 'react'\nimport { Signal, CleanupFn } from '../../../core'\n\n/**\n * Универсальный хук для безопасной синхронной подписки на изменения Signal, Computed или Resource.\n * Гарантирует отсутствие упущенных тиков (Race Conditions) и мгновенную очистку в React StrictMode.\n *\n * @template T Тип данных внутри реактивного контейнера\n * @param {Pick<Signal<T>, 'subscribe'>} signal Объект подписки (Signal/Computed/Resource)\n * @param {(val: T) => void} callback Функция обратного вызова, принимающая новое значение\n * @returns {void}\n * @source\n */\nexport const useReactiveSubscription = <T>(\n signal: Pick<Signal<T>, 'subscribe'>,\n callback: (val: T) => void\n): void => {\n // 1. Сохраняем актуальный callback в ref.\n // Мы обновляем его синхронно прямо во время рендера, уходя от лишнего useEffect\n const callbackRef = useRef(callback)\n callbackRef.current = callback\n\n // 2. Используем useLayoutEffect вместо useEffect.\n // Подписка оформляется синхронно ДО того, как браузер отрисует пиксели на экране.\n // Это полностью исключает Tearing (пропуск первого асинхронного тика данных из сети/WebSocket).\n useLayoutEffect(() => {\n if (!signal || typeof signal.subscribe !== 'function') return\n\n // Подписываемся на изменения в ядре.\n // Наш метод frameworkPrefix автоматически подставит бэдж \"react:use:...\" в логи!\n const unsubscribe: CleanupFn = signal.subscribe((val) => {\n callbackRef.current(val)\n })\n\n // Возвращаем функцию очистки. В useLayoutEffect она срабатывает мгновенно\n // при unmount, на корню уничтожая \"зомби-эффекты\" в StrictMode.\n return () => {\n if (typeof unsubscribe === 'function') {\n unsubscribe()\n }\n }\n }, [signal])\n}\n"],"mappings":"uBAaA,IAAa,GACX,EACA,IACS,CAGT,IAAM,GAAA,EAAA,EAAA,OAAA,CAAqB,CAAQ,EACnC,EAAY,QAAU,GAKtB,EAAA,EAAA,gBAAA,KAAsB,CACpB,GAAI,CAAC,GAAU,OAAO,EAAO,WAAc,WAAY,OAIvD,IAAM,EAAyB,EAAO,UAAW,GAAQ,CACvD,EAAY,QAAQ,CAAG,CACzB,CAAC,EAID,UAAa,CACP,OAAO,GAAgB,YACzB,EAAY,CAEhB,CACF,EAAG,CAAC,CAAM,CAAC,CACb"}
1
+ {"version":3,"file":"useReactiveSubscription.cjs","names":[],"sources":["../../../../src/react/hooks/useReactiveSubscription/useReactiveSubscription.ts"],"sourcesContent":["import { useEffect, useRef } from 'react'\nimport { ISignalLike } from '../types'\n\ntype CleanupFn = () => void\n\n/**\n * Универсальный хук подписки на реактивные источники ядра для React.\n * Полиморфно поддерживает как примитивные Сигналы/Computed, так и Proxy-объекты reactive().\n */\nexport function useReactiveSubscription<T>(\n item: T,\n callback: (val: T extends ISignalLike<infer V> ? V : T) => void\n): void {\n // Сохраняем ссылку на актуальный колбэк, чтобы защитить от лишних переподписок\n const callbackRef = useRef(callback)\n\n useEffect(() => {\n callbackRef.current = callback\n }, [callback])\n\n useEffect(() => {\n if (!item || typeof item !== 'object') return\n\n // 1. Сценарий А: У объекта есть нативный метод подписки .subscribe (Сигнал / Computed)\n if ('subscribe' in item && typeof (item as Record<string, unknown>).subscribe === 'function') {\n const signalItem = (item as unknown) as ISignalLike<unknown>\n\n const unsubscribe = signalItem.subscribe((newValue) => {\n callbackRef.current(newValue as any)\n })\n\n return () => unsubscribe()\n }\n\n // 2. Сценарий Б: Передан Proxy-объект (используем служебный геттер фреймворк-адаптеров)\n if ('__subscribe' in item && typeof (item as Record<string, unknown>).__subscribe === 'function') {\n const unsubscribe = ((item as Record<string, unknown>).__subscribe as (cb: () => void) => CleanupFn)(() => {\n // При мутации свойств или массивов Proxy пинаем колбэк с актуальным состоянием объекта\n callbackRef.current(item as any)\n })\n\n return () => unsubscribe()\n }\n }, [item])\n}\n"],"mappings":"uBASA,SAAgB,EACd,EACA,EACM,CAEN,IAAM,GAAA,EAAA,EAAA,OAAA,CAAqB,CAAQ,GAEnC,EAAA,EAAA,UAAA,KAAgB,CACd,EAAY,QAAU,CACxB,EAAG,CAAC,CAAQ,CAAC,GAEb,EAAA,EAAA,UAAA,KAAgB,CACV,MAAC,GAAQ,OAAO,GAAS,UAG7B,IAAI,cAAe,GAAQ,OAAQ,EAAiC,WAAc,WAAY,CAG5F,IAAM,EAAc,EAAW,UAAW,GAAa,CACrD,EAAY,QAAQ,CAAe,CACrC,CAAC,EAED,UAAa,EAAY,CAC3B,CAGA,GAAI,gBAAiB,GAAQ,OAAQ,EAAiC,aAAgB,WAAY,CAChG,IAAM,EAAgB,EAAiC,gBAAoD,CAEzG,EAAY,QAAQ,CAAW,CACjC,CAAC,EAED,UAAa,EAAY,CAC3B,CAVA,CAWF,EAAG,CAAC,CAAI,CAAC,CACX"}
@@ -1,13 +1,7 @@
1
- import { Signal } from '../../../core';
1
+ import { ISignalLike } from '../types';
2
2
  /**
3
- * Универсальный хук для безопасной синхронной подписки на изменения Signal, Computed или Resource.
4
- * Гарантирует отсутствие упущенных тиков (Race Conditions) и мгновенную очистку в React StrictMode.
5
- *
6
- * @template T Тип данных внутри реактивного контейнера
7
- * @param {Pick<Signal<T>, 'subscribe'>} signal Объект подписки (Signal/Computed/Resource)
8
- * @param {(val: T) => void} callback Функция обратного вызова, принимающая новое значение
9
- * @returns {void}
10
- * @source
3
+ * Универсальный хук подписки на реактивные источники ядра для React.
4
+ * Полиморфно поддерживает как примитивные Сигналы/Computed, так и Proxy-объекты reactive().
11
5
  */
12
- export declare const useReactiveSubscription: <T>(signal: Pick<Signal<T>, "subscribe">, callback: (val: T) => void) => void;
6
+ export declare function useReactiveSubscription<T>(item: T, callback: (val: T extends ISignalLike<infer V> ? V : T) => void): void;
13
7
  //# sourceMappingURL=useReactiveSubscription.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useReactiveSubscription.d.ts","sourceRoot":"","sources":["../../../../src/react/hooks/useReactiveSubscription/useReactiveSubscription.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAa,MAAM,eAAe,CAAA;AAEjD;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,GAAI,CAAC,EACvC,QAAQ,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,EACpC,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,KACzB,IA0BF,CAAA"}
1
+ {"version":3,"file":"useReactiveSubscription.d.ts","sourceRoot":"","sources":["../../../../src/react/hooks/useReactiveSubscription/useReactiveSubscription.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAItC;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EACvC,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,GAC9D,IAAI,CAgCN"}
@@ -1,17 +1,26 @@
1
- import { useLayoutEffect as e, useRef as t } from "react";
1
+ import { useEffect as e, useRef as t } from "react";
2
2
  //#region src/react/hooks/useReactiveSubscription/useReactiveSubscription.ts
3
- var n = (n, r) => {
3
+ function n(n, r) {
4
4
  let i = t(r);
5
- i.current = r, e(() => {
6
- if (!n || typeof n.subscribe != "function") return;
7
- let e = n.subscribe((e) => {
8
- i.current(e);
9
- });
10
- return () => {
11
- typeof e == "function" && e();
12
- };
5
+ e(() => {
6
+ i.current = r;
7
+ }, [r]), e(() => {
8
+ if (!(!n || typeof n != "object")) {
9
+ if ("subscribe" in n && typeof n.subscribe == "function") {
10
+ let e = n.subscribe((e) => {
11
+ i.current(e);
12
+ });
13
+ return () => e();
14
+ }
15
+ if ("__subscribe" in n && typeof n.__subscribe == "function") {
16
+ let e = n.__subscribe(() => {
17
+ i.current(n);
18
+ });
19
+ return () => e();
20
+ }
21
+ }
13
22
  }, [n]);
14
- };
23
+ }
15
24
  //#endregion
16
25
  export { n as useReactiveSubscription };
17
26
 
@@ -1 +1 @@
1
- {"version":3,"file":"useReactiveSubscription.mjs","names":[],"sources":["../../../../src/react/hooks/useReactiveSubscription/useReactiveSubscription.ts"],"sourcesContent":["import { useLayoutEffect, useRef } from 'react'\nimport { Signal, CleanupFn } from '../../../core'\n\n/**\n * Универсальный хук для безопасной синхронной подписки на изменения Signal, Computed или Resource.\n * Гарантирует отсутствие упущенных тиков (Race Conditions) и мгновенную очистку в React StrictMode.\n *\n * @template T Тип данных внутри реактивного контейнера\n * @param {Pick<Signal<T>, 'subscribe'>} signal Объект подписки (Signal/Computed/Resource)\n * @param {(val: T) => void} callback Функция обратного вызова, принимающая новое значение\n * @returns {void}\n * @source\n */\nexport const useReactiveSubscription = <T>(\n signal: Pick<Signal<T>, 'subscribe'>,\n callback: (val: T) => void\n): void => {\n // 1. Сохраняем актуальный callback в ref.\n // Мы обновляем его синхронно прямо во время рендера, уходя от лишнего useEffect\n const callbackRef = useRef(callback)\n callbackRef.current = callback\n\n // 2. Используем useLayoutEffect вместо useEffect.\n // Подписка оформляется синхронно ДО того, как браузер отрисует пиксели на экране.\n // Это полностью исключает Tearing (пропуск первого асинхронного тика данных из сети/WebSocket).\n useLayoutEffect(() => {\n if (!signal || typeof signal.subscribe !== 'function') return\n\n // Подписываемся на изменения в ядре.\n // Наш метод frameworkPrefix автоматически подставит бэдж \"react:use:...\" в логи!\n const unsubscribe: CleanupFn = signal.subscribe((val) => {\n callbackRef.current(val)\n })\n\n // Возвращаем функцию очистки. В useLayoutEffect она срабатывает мгновенно\n // при unmount, на корню уничтожая \"зомби-эффекты\" в StrictMode.\n return () => {\n if (typeof unsubscribe === 'function') {\n unsubscribe()\n }\n }\n }, [signal])\n}\n"],"mappings":";;AAaA,IAAa,KACX,GACA,MACS;CAGT,IAAM,IAAc,EAAO,CAAQ;CAMnC,AALA,EAAY,UAAU,GAKtB,QAAsB;EACpB,IAAI,CAAC,KAAU,OAAO,EAAO,aAAc,YAAY;EAIvD,IAAM,IAAyB,EAAO,WAAW,MAAQ;GACvD,EAAY,QAAQ,CAAG;EACzB,CAAC;EAID,aAAa;GACX,AAAI,OAAO,KAAgB,cACzB,EAAY;EAEhB;CACF,GAAG,CAAC,CAAM,CAAC;AACb"}
1
+ {"version":3,"file":"useReactiveSubscription.mjs","names":[],"sources":["../../../../src/react/hooks/useReactiveSubscription/useReactiveSubscription.ts"],"sourcesContent":["import { useEffect, useRef } from 'react'\nimport { ISignalLike } from '../types'\n\ntype CleanupFn = () => void\n\n/**\n * Универсальный хук подписки на реактивные источники ядра для React.\n * Полиморфно поддерживает как примитивные Сигналы/Computed, так и Proxy-объекты reactive().\n */\nexport function useReactiveSubscription<T>(\n item: T,\n callback: (val: T extends ISignalLike<infer V> ? V : T) => void\n): void {\n // Сохраняем ссылку на актуальный колбэк, чтобы защитить от лишних переподписок\n const callbackRef = useRef(callback)\n\n useEffect(() => {\n callbackRef.current = callback\n }, [callback])\n\n useEffect(() => {\n if (!item || typeof item !== 'object') return\n\n // 1. Сценарий А: У объекта есть нативный метод подписки .subscribe (Сигнал / Computed)\n if ('subscribe' in item && typeof (item as Record<string, unknown>).subscribe === 'function') {\n const signalItem = (item as unknown) as ISignalLike<unknown>\n\n const unsubscribe = signalItem.subscribe((newValue) => {\n callbackRef.current(newValue as any)\n })\n\n return () => unsubscribe()\n }\n\n // 2. Сценарий Б: Передан Proxy-объект (используем служебный геттер фреймворк-адаптеров)\n if ('__subscribe' in item && typeof (item as Record<string, unknown>).__subscribe === 'function') {\n const unsubscribe = ((item as Record<string, unknown>).__subscribe as (cb: () => void) => CleanupFn)(() => {\n // При мутации свойств или массивов Proxy пинаем колбэк с актуальным состоянием объекта\n callbackRef.current(item as any)\n })\n\n return () => unsubscribe()\n }\n }, [item])\n}\n"],"mappings":";;AASA,SAAgB,EACd,GACA,GACM;CAEN,IAAM,IAAc,EAAO,CAAQ;CAMnC,AAJA,QAAgB;EACd,EAAY,UAAU;CACxB,GAAG,CAAC,CAAQ,CAAC,GAEb,QAAgB;EACV,OAAC,KAAQ,OAAO,KAAS,WAG7B;OAAI,eAAe,KAAQ,OAAQ,EAAiC,aAAc,YAAY;IAG5F,IAAM,IAAc,EAAW,WAAW,MAAa;KACrD,EAAY,QAAQ,CAAe;IACrC,CAAC;IAED,aAAa,EAAY;GAC3B;GAGA,IAAI,iBAAiB,KAAQ,OAAQ,EAAiC,eAAgB,YAAY;IAChG,IAAM,IAAgB,EAAiC,kBAAoD;KAEzG,EAAY,QAAQ,CAAW;IACjC,CAAC;IAED,aAAa,EAAY;GAC3B;EAVA;CAWF,GAAG,CAAC,CAAI,CAAC;AACX"}
@@ -1,2 +1,2 @@
1
- let e=require("react");var t=t=>{let n=typeof t==`function`,r=(0,e.useRef)(t);(0,e.useEffect)(()=>{r.current=t},[t]);let i=(0,e.useMemo)(()=>typeof t==`function`?t():t,[n?void 0:t]),a=(0,e.useCallback)(e=>i.subscribe(e),[i]),o=(0,e.useCallback)(()=>i.value,[i]);return(0,e.useEffect)(()=>()=>{n&&i&&typeof i.destroy==`function`&&i.destroy()},[i,n]),(0,e.useSyncExternalStore)(a,o)};exports.useReactiveValue=t;
1
+ let e=require("react");var t=t=>{let n=typeof t==`function`,r=(0,e.useRef)(t);(0,e.useEffect)(()=>{r.current=t},[t]);let i=(0,e.useMemo)(()=>typeof t==`function`?t():t,[n?void 0:t]),a=i&&typeof i==`object`&&`subscribe`in i&&typeof i.subscribe==`function`,o=(0,e.useCallback)(e=>a?i.subscribe(e):()=>{},[i,a]),s=(0,e.useCallback)(()=>a?i.value:i,[i,a]);if((0,e.useEffect)(()=>()=>{n&&i&&`destroy`in i&&typeof i.destroy==`function`&&i.destroy()},[i,n]),a)return(0,e.useSyncExternalStore)(o,s);let[,c]=(0,e.useState)([]);return(0,e.useEffect)(()=>{if(i&&`__subscribe`in i&&typeof i.__subscribe==`function`){let e=i.__subscribe(()=>{c([])});return()=>e()}},[i]),i};exports.useReactiveValue=t;
2
2
  //# sourceMappingURL=useReactiveValue.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useReactiveValue.cjs","names":[],"sources":["../../../../src/react/hooks/useReactiveValue/useReactiveValue.ts"],"sourcesContent":["import { useSyncExternalStore, useCallback, useMemo, useEffect, useRef } from 'react'\nimport { CleanupFn } from '../../../core'\n\ninterface ObservableItem<T> {\n readonly value: T;\n subscribe: (cb: (val: T) => void) => CleanupFn\n destroy?: () => void\n}\n\ntype ReactiveInput<T> = ObservableItem<T> | (() => ObservableItem<T>)\n\n/**\n * Нативный React-хук для извлечения текущего значения из реактивных примитивов ядра\n * (Signal, Computed, Resource) и автоматического управления подпиской на рендеринг.\n *\n * Опирается на каноничную шим-прослойку `useSyncExternalStore`, что гарантирует 100%\n * совместимость с механизмами батчинга обновлений React 18+ и Concurrent Mode (защита от Tearing).\n *\n * ### Особенности управления памятью:\n * 1. **Глобальные сигналы (Сервисы/Синглтоны):** При размонтировании компонента хук выполняет\n * только стандартную отписку от обновлений. Метод `.destroy()` **не вызывается**,\n * что сохраняет глобальное состояние системы в безопасности.\n * 2. **Локальные фабрики (`() => engine.computed(...)`):** Если в качестве аргумента передана\n * функция-фабрика, хук понимает, что вычисление создано локально для этого экрана.\n * При анмаунте компонента хук автоматически вызовет `.destroy()`, предотвращая утечки памяти в ядре.\n *\n * @template T - Тип данных, инкапсулированных внутри реактивного элемента.\n * @param {ReactiveInput<T>} input - Готовый реактивный элемент ядра или ленивая функция-фабрика, возвращающая его.\n * @returns {T} Актуальное синхронизированное значение реактивного элемента.\n *\n * @example\n * ```tsx\n * import { useReactiveValue } from '@pravosleva/reactive-engine/react';\n * import { userInfoService } from '~/store';\n *\n * // Сценарий 1: Прямая подписка на долгоживущий сигнал сервиса\n * export const CounterDisplay = () => {\n * const counter = useReactiveValue(userInfoService.counter);\n * return <span>Значение: {counter}</span>;\n * };\n *\n * // Сценарий 2: Использование ленивой фабрики для локальных вычислений (авто-очистка ядра при анмаунте)\n * export const FilteredList = ({ query }: { query: string }) => {\n * const filteredData = useReactiveValue(() =>\n * engine.computed(() => userInfoService.list.value.filter(item => item.includes(query)))\n * );\n * return <ul>{filteredData.map(item => <li key={item}>{item}</li>)}</ul>;\n * };\n * ```\n */\nexport const useReactiveValue = <T>(input: ReactiveInput<T>): T => {\n const isFactory = typeof input === 'function'\n\n // NOTE: СТАБИЛИЗАЦИЯ ФАБРИКИ: Сохраняем ссылку на функцию в ref,\n // чтобы не перезапускать вычисления, если разработчик передал инлайн-стрелочную функцию.\n const factoryRef = useRef(input)\n useEffect(() => {\n factoryRef.current = input\n }, [input])\n\n // Вычисляем элемент строго один раз при инициализации, либо при изменении стабильной ссылки на готовый сигнал\n const reactiveItem = useMemo(() => {\n if (typeof input === 'function') {\n // Вызываем фабрику только при первом проходе\n return input()\n }\n return input // Если передан готовый сигнал/ресурс — используем его напрямую\n }, [isFactory ? undefined : input]) // Стабильный массив зависимостей для фабрик!\n\n const subscribe = useCallback(\n (reactCallback: () => void) => {\n return reactiveItem.subscribe(reactCallback)\n },\n [reactiveItem]\n )\n\n const getSnapshot = useCallback(() => {\n return reactiveItem.value\n }, [reactiveItem])\n\n // NOTE: БЕЗОПАСНАЯ ОЧИСТКА ПАМЯТИ:\n // Вызываем .destroy() ТОЛЬКО если объект пришел из локальной фабрики `() => engine.computed(...)`.\n // Если это глобальный сигнал из сервиса — мы делаем только стандартный unmount подписки через useSyncExternalStore.\n useEffect(() => {\n return () => {\n if (isFactory && reactiveItem && typeof reactiveItem.destroy === 'function') {\n reactiveItem.destroy()\n }\n }\n }, [reactiveItem, isFactory])\n\n return useSyncExternalStore(subscribe, getSnapshot)\n}\n"],"mappings":"uBAkDA,IAAa,EAAuB,GAA+B,CACjE,IAAM,EAAY,OAAO,GAAU,WAI7B,GAAA,EAAA,EAAA,OAAA,CAAoB,CAAK,GAC/B,EAAA,EAAA,UAAA,KAAgB,CACd,EAAW,QAAU,CACvB,EAAG,CAAC,CAAK,CAAC,EAGV,IAAM,GAAA,EAAA,EAAA,QAAA,KACA,OAAO,GAAU,WAEZ,EAAM,EAER,EACN,CAAC,EAAY,IAAA,GAAY,CAAK,CAAC,EAE5B,GAAA,EAAA,EAAA,YAAA,CACH,GACQ,EAAa,UAAU,CAAa,EAE7C,CAAC,CAAY,CACf,EAEM,GAAA,EAAA,EAAA,YAAA,KACG,EAAa,MACnB,CAAC,CAAY,CAAC,EAajB,OARA,EAAA,EAAA,UAAA,SACe,CACP,GAAa,GAAgB,OAAO,EAAa,SAAY,YAC/D,EAAa,QAAQ,CAEzB,EACC,CAAC,EAAc,CAAS,CAAC,GAE5B,EAAA,EAAA,qBAAA,CAA4B,EAAW,CAAW,CACpD"}
1
+ {"version":3,"file":"useReactiveValue.cjs","names":[],"sources":["../../../../src/react/hooks/useReactiveValue/useReactiveValue.ts"],"sourcesContent":["import { useSyncExternalStore, useCallback, useMemo, useEffect, useRef, useState } from 'react'\n\ntype CleanupFn = () => void\n\n// Описываем строгий контракт для примитивов реактивного ядра (Signals / Computed / Resources)\nexport interface ISignalLike<V> {\n readonly value: V;\n subscribe: (cb: (val: V) => void) => CleanupFn\n destroy?: () => void\n}\n\ntype ReactiveInput<T> = ISignalLike<T> | (() => ISignalLike<T>) | object | (() => object)\n\n/**\n * Нативный React-хук для извлечения текущего значения из реактивных примитивов ядра\n * (Signal, Computed, Resource) и глубоких реактивных Proxy-объектов (reactive).\n *\n * @template T - Тип входящих данных\n * @param {ReactiveInput<any>} input - Готовый реактивный элемент ядра или функция-фабрика\n * @returns {any} Актуальное синхронизированное значение элемента или сам прокси-объект\n */\nexport const useReactiveValue = <T>(input: ReactiveInput<any>): any => {\n const isFactory = typeof input === 'function'\n\n // Стабилизируем ссылку на фабрику\n const factoryRef = useRef(input)\n useEffect(() => {\n factoryRef.current = input\n }, [input])\n\n // Вычисляем целевой элемент один раз\n const reactiveItem = useMemo(() => {\n if (typeof input === 'function') {\n return input()\n }\n return input\n }, [isFactory ? undefined : input])\n\n // Определяем, является ли объект примитивом (Signal/Computed) или это Proxy-объект reactive()\n const isSignal = reactiveItem &&\n typeof reactiveItem === 'object' &&\n 'subscribe' in reactiveItem &&\n typeof (reactiveItem as Record<string, unknown>).subscribe === 'function'\n\n // ------------------------------------------------====\n // СЦЕНАРИЙ А: Работа с атомарными Сигналами / Computed (useSyncExternalStore)\n // ------------------------------------------------====\n const subscribe = useCallback(\n (reactCallback: () => void) => {\n if (isSignal) {\n return (reactiveItem as ISignalLike<unknown>).subscribe(reactCallback)\n }\n return () => {}\n },\n [reactiveItem, isSignal]\n )\n\n const getSnapshot = useCallback(() => {\n if (isSignal) {\n return (reactiveItem as ISignalLike<unknown>).value\n }\n return reactiveItem\n }, [reactiveItem, isSignal])\n\n // Безопасная очистка локальных фабрик ядра при размонтировании экрана\n useEffect(() => {\n return () => {\n if (isFactory && reactiveItem && 'destroy' in reactiveItem && typeof (reactiveItem as any).destroy === 'function') {\n (reactiveItem as any).destroy()\n }\n }\n }, [reactiveItem, isFactory])\n\n // Если это сигнал — используем Concurrent-безопасныйuseSyncExternalStore\n if (isSignal) {\n return useSyncExternalStore(subscribe, getSnapshot)\n }\n\n // ------------------------------------------------====\n // СЦЕНАРИЙ Б: Работа с глубокими Proxy-объектами / Массивами\n // ------------------------------------------------====\n // Для Proxy-объектовuseSyncExternalStore не подходит, так как у них нет .value.\n // Мы создаем локальный forceUpdate и подписываем его через скрытый геттер __subscribe адаптера\n const [, forceUpdate] = useState([])\n\n useEffect(() => {\n if (reactiveItem && '__subscribe' in reactiveItem && typeof (reactiveItem as any).__subscribe === 'function') {\n const unsubscribe = (reactiveItem as any).__subscribe(() => {\n forceUpdate([]) // Провоцируем ререндер компонента через автобатчинг микрозадач\n })\n return () => unsubscribe()\n }\n }, [reactiveItem])\n\n return reactiveItem\n}\n"],"mappings":"uBAqBA,IAAa,EAAuB,GAAmC,CACrE,IAAM,EAAY,OAAO,GAAU,WAG7B,GAAA,EAAA,EAAA,OAAA,CAAoB,CAAK,GAC/B,EAAA,EAAA,UAAA,KAAgB,CACd,EAAW,QAAU,CACvB,EAAG,CAAC,CAAK,CAAC,EAGV,IAAM,GAAA,EAAA,EAAA,QAAA,KACA,OAAO,GAAU,WACZ,EAAM,EAER,EACN,CAAC,EAAY,IAAA,GAAY,CAAK,CAAC,EAG5B,EAAW,GACA,OAAO,GAAiB,UACxB,cAAe,GACf,OAAQ,EAAyC,WAAc,WAK1E,GAAA,EAAA,EAAA,YAAA,CACH,GACK,EACM,EAAsC,UAAU,CAAa,MAE1D,CAAC,EAEhB,CAAC,EAAc,CAAQ,CACzB,EAEM,GAAA,EAAA,EAAA,YAAA,KACA,EACM,EAAsC,MAEzC,EACN,CAAC,EAAc,CAAQ,CAAC,EAY3B,IATA,EAAA,EAAA,UAAA,SACe,CACP,GAAa,GAAgB,YAAa,GAAgB,OAAQ,EAAqB,SAAY,YACrG,EAAsB,QAAQ,CAElC,EACC,CAAC,EAAc,CAAS,CAAC,EAGxB,EACF,OAAA,EAAA,EAAA,qBAAA,CAA4B,EAAW,CAAW,EAQpD,GAAM,EAAG,IAAA,EAAA,EAAA,SAAA,CAAwB,CAAC,CAAC,EAWnC,OATA,EAAA,EAAA,UAAA,KAAgB,CACd,GAAI,GAAgB,gBAAiB,GAAgB,OAAQ,EAAqB,aAAgB,WAAY,CAC5G,IAAM,EAAe,EAAqB,gBAAkB,CAC1D,EAAY,CAAC,CAAC,CAChB,CAAC,EACD,UAAa,EAAY,CAC3B,CACF,EAAG,CAAC,CAAY,CAAC,EAEV,CACT"}
@@ -1,49 +1,18 @@
1
- import { CleanupFn } from '../../../core';
2
- interface ObservableItem<T> {
3
- readonly value: T;
4
- subscribe: (cb: (val: T) => void) => CleanupFn;
1
+ type CleanupFn = () => void;
2
+ export interface ISignalLike<V> {
3
+ readonly value: V;
4
+ subscribe: (cb: (val: V) => void) => CleanupFn;
5
5
  destroy?: () => void;
6
6
  }
7
- type ReactiveInput<T> = ObservableItem<T> | (() => ObservableItem<T>);
7
+ type ReactiveInput<T> = ISignalLike<T> | (() => ISignalLike<T>) | object | (() => object);
8
8
  /**
9
9
  * Нативный React-хук для извлечения текущего значения из реактивных примитивов ядра
10
- * (Signal, Computed, Resource) и автоматического управления подпиской на рендеринг.
10
+ * (Signal, Computed, Resource) и глубоких реактивных Proxy-объектов (reactive).
11
11
  *
12
- * Опирается на каноничную шим-прослойку `useSyncExternalStore`, что гарантирует 100%
13
- * совместимость с механизмами батчинга обновлений React 18+ и Concurrent Mode (защита от Tearing).
14
- *
15
- * ### Особенности управления памятью:
16
- * 1. **Глобальные сигналы (Сервисы/Синглтоны):** При размонтировании компонента хук выполняет
17
- * только стандартную отписку от обновлений. Метод `.destroy()` **не вызывается**,
18
- * что сохраняет глобальное состояние системы в безопасности.
19
- * 2. **Локальные фабрики (`() => engine.computed(...)`):** Если в качестве аргумента передана
20
- * функция-фабрика, хук понимает, что вычисление создано локально для этого экрана.
21
- * При анмаунте компонента хук автоматически вызовет `.destroy()`, предотвращая утечки памяти в ядре.
22
- *
23
- * @template T - Тип данных, инкапсулированных внутри реактивного элемента.
24
- * @param {ReactiveInput<T>} input - Готовый реактивный элемент ядра или ленивая функция-фабрика, возвращающая его.
25
- * @returns {T} Актуальное синхронизированное значение реактивного элемента.
26
- *
27
- * @example
28
- * ```tsx
29
- * import { useReactiveValue } from '@pravosleva/reactive-engine/react';
30
- * import { userInfoService } from '~/store';
31
- *
32
- * // Сценарий 1: Прямая подписка на долгоживущий сигнал сервиса
33
- * export const CounterDisplay = () => {
34
- * const counter = useReactiveValue(userInfoService.counter);
35
- * return <span>Значение: {counter}</span>;
36
- * };
37
- *
38
- * // Сценарий 2: Использование ленивой фабрики для локальных вычислений (авто-очистка ядра при анмаунте)
39
- * export const FilteredList = ({ query }: { query: string }) => {
40
- * const filteredData = useReactiveValue(() =>
41
- * engine.computed(() => userInfoService.list.value.filter(item => item.includes(query)))
42
- * );
43
- * return <ul>{filteredData.map(item => <li key={item}>{item}</li>)}</ul>;
44
- * };
45
- * ```
12
+ * @template T - Тип входящих данных
13
+ * @param {ReactiveInput<any>} input - Готовый реактивный элемент ядра или функция-фабрика
14
+ * @returns {any} Актуальное синхронизированное значение элемента или сам прокси-объект
46
15
  */
47
- export declare const useReactiveValue: <T>(input: ReactiveInput<T>) => T;
16
+ export declare const useReactiveValue: <T>(input: ReactiveInput<any>) => any;
48
17
  export {};
49
18
  //# sourceMappingURL=useReactiveValue.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useReactiveValue.d.ts","sourceRoot":"","sources":["../../../../src/react/hooks/useReactiveValue/useReactiveValue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAEzC,UAAU,cAAc,CAAC,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,KAAK,SAAS,CAAA;IAC9C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB;AAED,KAAK,aAAa,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,cAAc,CAAC,CAAC,CAAC,CAAC,CAAA;AAErE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,EAAE,OAAO,aAAa,CAAC,CAAC,CAAC,KAAG,CA0C7D,CAAA"}
1
+ {"version":3,"file":"useReactiveValue.d.ts","sourceRoot":"","sources":["../../../../src/react/hooks/useReactiveValue/useReactiveValue.ts"],"names":[],"mappings":"AAEA,KAAK,SAAS,GAAG,MAAM,IAAI,CAAA;AAG3B,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,KAAK,SAAS,CAAA;IAC9C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB;AAED,KAAK,aAAa,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAA;AAEzF;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,EAAE,OAAO,aAAa,CAAC,GAAG,CAAC,KAAG,GA0E/D,CAAA"}
@@ -1,16 +1,25 @@
1
- import { useCallback as e, useEffect as t, useMemo as n, useRef as r, useSyncExternalStore as i } from "react";
1
+ import { useCallback as e, useEffect as t, useMemo as n, useRef as r, useState as i, useSyncExternalStore as a } from "react";
2
2
  //#region src/react/hooks/useReactiveValue/useReactiveValue.ts
3
- var a = (a) => {
4
- let o = typeof a == "function", s = r(a);
3
+ var o = (o) => {
4
+ let s = typeof o == "function", c = r(o);
5
5
  t(() => {
6
- s.current = a;
7
- }, [a]);
8
- let c = n(() => typeof a == "function" ? a() : a, [o ? void 0 : a]), l = e((e) => c.subscribe(e), [c]), u = e(() => c.value, [c]);
9
- return t(() => () => {
10
- o && c && typeof c.destroy == "function" && c.destroy();
11
- }, [c, o]), i(l, u);
6
+ c.current = o;
7
+ }, [o]);
8
+ let l = n(() => typeof o == "function" ? o() : o, [s ? void 0 : o]), u = l && typeof l == "object" && "subscribe" in l && typeof l.subscribe == "function", d = e((e) => u ? l.subscribe(e) : () => {}, [l, u]), f = e(() => u ? l.value : l, [l, u]);
9
+ if (t(() => () => {
10
+ s && l && "destroy" in l && typeof l.destroy == "function" && l.destroy();
11
+ }, [l, s]), u) return a(d, f);
12
+ let [, p] = i([]);
13
+ return t(() => {
14
+ if (l && "__subscribe" in l && typeof l.__subscribe == "function") {
15
+ let e = l.__subscribe(() => {
16
+ p([]);
17
+ });
18
+ return () => e();
19
+ }
20
+ }, [l]), l;
12
21
  };
13
22
  //#endregion
14
- export { a as useReactiveValue };
23
+ export { o as useReactiveValue };
15
24
 
16
25
  //# sourceMappingURL=useReactiveValue.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useReactiveValue.mjs","names":[],"sources":["../../../../src/react/hooks/useReactiveValue/useReactiveValue.ts"],"sourcesContent":["import { useSyncExternalStore, useCallback, useMemo, useEffect, useRef } from 'react'\nimport { CleanupFn } from '../../../core'\n\ninterface ObservableItem<T> {\n readonly value: T;\n subscribe: (cb: (val: T) => void) => CleanupFn\n destroy?: () => void\n}\n\ntype ReactiveInput<T> = ObservableItem<T> | (() => ObservableItem<T>)\n\n/**\n * Нативный React-хук для извлечения текущего значения из реактивных примитивов ядра\n * (Signal, Computed, Resource) и автоматического управления подпиской на рендеринг.\n *\n * Опирается на каноничную шим-прослойку `useSyncExternalStore`, что гарантирует 100%\n * совместимость с механизмами батчинга обновлений React 18+ и Concurrent Mode (защита от Tearing).\n *\n * ### Особенности управления памятью:\n * 1. **Глобальные сигналы (Сервисы/Синглтоны):** При размонтировании компонента хук выполняет\n * только стандартную отписку от обновлений. Метод `.destroy()` **не вызывается**,\n * что сохраняет глобальное состояние системы в безопасности.\n * 2. **Локальные фабрики (`() => engine.computed(...)`):** Если в качестве аргумента передана\n * функция-фабрика, хук понимает, что вычисление создано локально для этого экрана.\n * При анмаунте компонента хук автоматически вызовет `.destroy()`, предотвращая утечки памяти в ядре.\n *\n * @template T - Тип данных, инкапсулированных внутри реактивного элемента.\n * @param {ReactiveInput<T>} input - Готовый реактивный элемент ядра или ленивая функция-фабрика, возвращающая его.\n * @returns {T} Актуальное синхронизированное значение реактивного элемента.\n *\n * @example\n * ```tsx\n * import { useReactiveValue } from '@pravosleva/reactive-engine/react';\n * import { userInfoService } from '~/store';\n *\n * // Сценарий 1: Прямая подписка на долгоживущий сигнал сервиса\n * export const CounterDisplay = () => {\n * const counter = useReactiveValue(userInfoService.counter);\n * return <span>Значение: {counter}</span>;\n * };\n *\n * // Сценарий 2: Использование ленивой фабрики для локальных вычислений (авто-очистка ядра при анмаунте)\n * export const FilteredList = ({ query }: { query: string }) => {\n * const filteredData = useReactiveValue(() =>\n * engine.computed(() => userInfoService.list.value.filter(item => item.includes(query)))\n * );\n * return <ul>{filteredData.map(item => <li key={item}>{item}</li>)}</ul>;\n * };\n * ```\n */\nexport const useReactiveValue = <T>(input: ReactiveInput<T>): T => {\n const isFactory = typeof input === 'function'\n\n // NOTE: СТАБИЛИЗАЦИЯ ФАБРИКИ: Сохраняем ссылку на функцию в ref,\n // чтобы не перезапускать вычисления, если разработчик передал инлайн-стрелочную функцию.\n const factoryRef = useRef(input)\n useEffect(() => {\n factoryRef.current = input\n }, [input])\n\n // Вычисляем элемент строго один раз при инициализации, либо при изменении стабильной ссылки на готовый сигнал\n const reactiveItem = useMemo(() => {\n if (typeof input === 'function') {\n // Вызываем фабрику только при первом проходе\n return input()\n }\n return input // Если передан готовый сигнал/ресурс — используем его напрямую\n }, [isFactory ? undefined : input]) // Стабильный массив зависимостей для фабрик!\n\n const subscribe = useCallback(\n (reactCallback: () => void) => {\n return reactiveItem.subscribe(reactCallback)\n },\n [reactiveItem]\n )\n\n const getSnapshot = useCallback(() => {\n return reactiveItem.value\n }, [reactiveItem])\n\n // NOTE: БЕЗОПАСНАЯ ОЧИСТКА ПАМЯТИ:\n // Вызываем .destroy() ТОЛЬКО если объект пришел из локальной фабрики `() => engine.computed(...)`.\n // Если это глобальный сигнал из сервиса — мы делаем только стандартный unmount подписки через useSyncExternalStore.\n useEffect(() => {\n return () => {\n if (isFactory && reactiveItem && typeof reactiveItem.destroy === 'function') {\n reactiveItem.destroy()\n }\n }\n }, [reactiveItem, isFactory])\n\n return useSyncExternalStore(subscribe, getSnapshot)\n}\n"],"mappings":";;AAkDA,IAAa,KAAuB,MAA+B;CACjE,IAAM,IAAY,OAAO,KAAU,YAI7B,IAAa,EAAO,CAAK;CAC/B,QAAgB;EACd,EAAW,UAAU;CACvB,GAAG,CAAC,CAAK,CAAC;CAGV,IAAM,IAAe,QACf,OAAO,KAAU,aAEZ,EAAM,IAER,GACN,CAAC,IAAY,KAAA,IAAY,CAAK,CAAC,GAE5B,IAAY,GACf,MACQ,EAAa,UAAU,CAAa,GAE7C,CAAC,CAAY,CACf,GAEM,IAAc,QACX,EAAa,OACnB,CAAC,CAAY,CAAC;CAajB,OARA,cACe;EACX,AAAI,KAAa,KAAgB,OAAO,EAAa,WAAY,cAC/D,EAAa,QAAQ;CAEzB,GACC,CAAC,GAAc,CAAS,CAAC,GAErB,EAAqB,GAAW,CAAW;AACpD"}
1
+ {"version":3,"file":"useReactiveValue.mjs","names":[],"sources":["../../../../src/react/hooks/useReactiveValue/useReactiveValue.ts"],"sourcesContent":["import { useSyncExternalStore, useCallback, useMemo, useEffect, useRef, useState } from 'react'\n\ntype CleanupFn = () => void\n\n// Описываем строгий контракт для примитивов реактивного ядра (Signals / Computed / Resources)\nexport interface ISignalLike<V> {\n readonly value: V;\n subscribe: (cb: (val: V) => void) => CleanupFn\n destroy?: () => void\n}\n\ntype ReactiveInput<T> = ISignalLike<T> | (() => ISignalLike<T>) | object | (() => object)\n\n/**\n * Нативный React-хук для извлечения текущего значения из реактивных примитивов ядра\n * (Signal, Computed, Resource) и глубоких реактивных Proxy-объектов (reactive).\n *\n * @template T - Тип входящих данных\n * @param {ReactiveInput<any>} input - Готовый реактивный элемент ядра или функция-фабрика\n * @returns {any} Актуальное синхронизированное значение элемента или сам прокси-объект\n */\nexport const useReactiveValue = <T>(input: ReactiveInput<any>): any => {\n const isFactory = typeof input === 'function'\n\n // Стабилизируем ссылку на фабрику\n const factoryRef = useRef(input)\n useEffect(() => {\n factoryRef.current = input\n }, [input])\n\n // Вычисляем целевой элемент один раз\n const reactiveItem = useMemo(() => {\n if (typeof input === 'function') {\n return input()\n }\n return input\n }, [isFactory ? undefined : input])\n\n // Определяем, является ли объект примитивом (Signal/Computed) или это Proxy-объект reactive()\n const isSignal = reactiveItem &&\n typeof reactiveItem === 'object' &&\n 'subscribe' in reactiveItem &&\n typeof (reactiveItem as Record<string, unknown>).subscribe === 'function'\n\n // ------------------------------------------------====\n // СЦЕНАРИЙ А: Работа с атомарными Сигналами / Computed (useSyncExternalStore)\n // ------------------------------------------------====\n const subscribe = useCallback(\n (reactCallback: () => void) => {\n if (isSignal) {\n return (reactiveItem as ISignalLike<unknown>).subscribe(reactCallback)\n }\n return () => {}\n },\n [reactiveItem, isSignal]\n )\n\n const getSnapshot = useCallback(() => {\n if (isSignal) {\n return (reactiveItem as ISignalLike<unknown>).value\n }\n return reactiveItem\n }, [reactiveItem, isSignal])\n\n // Безопасная очистка локальных фабрик ядра при размонтировании экрана\n useEffect(() => {\n return () => {\n if (isFactory && reactiveItem && 'destroy' in reactiveItem && typeof (reactiveItem as any).destroy === 'function') {\n (reactiveItem as any).destroy()\n }\n }\n }, [reactiveItem, isFactory])\n\n // Если это сигнал — используем Concurrent-безопасныйuseSyncExternalStore\n if (isSignal) {\n return useSyncExternalStore(subscribe, getSnapshot)\n }\n\n // ------------------------------------------------====\n // СЦЕНАРИЙ Б: Работа с глубокими Proxy-объектами / Массивами\n // ------------------------------------------------====\n // Для Proxy-объектовuseSyncExternalStore не подходит, так как у них нет .value.\n // Мы создаем локальный forceUpdate и подписываем его через скрытый геттер __subscribe адаптера\n const [, forceUpdate] = useState([])\n\n useEffect(() => {\n if (reactiveItem && '__subscribe' in reactiveItem && typeof (reactiveItem as any).__subscribe === 'function') {\n const unsubscribe = (reactiveItem as any).__subscribe(() => {\n forceUpdate([]) // Провоцируем ререндер компонента через автобатчинг микрозадач\n })\n return () => unsubscribe()\n }\n }, [reactiveItem])\n\n return reactiveItem\n}\n"],"mappings":";;AAqBA,IAAa,KAAuB,MAAmC;CACrE,IAAM,IAAY,OAAO,KAAU,YAG7B,IAAa,EAAO,CAAK;CAC/B,QAAgB;EACd,EAAW,UAAU;CACvB,GAAG,CAAC,CAAK,CAAC;CAGV,IAAM,IAAe,QACf,OAAO,KAAU,aACZ,EAAM,IAER,GACN,CAAC,IAAY,KAAA,IAAY,CAAK,CAAC,GAG5B,IAAW,KACA,OAAO,KAAiB,YACxB,eAAe,KACf,OAAQ,EAAyC,aAAc,YAK1E,IAAY,GACf,MACK,IACM,EAAsC,UAAU,CAAa,UAE1D,CAAC,GAEhB,CAAC,GAAc,CAAQ,CACzB,GAEM,IAAc,QACd,IACM,EAAsC,QAEzC,GACN,CAAC,GAAc,CAAQ,CAAC;CAY3B,IATA,cACe;EACX,AAAI,KAAa,KAAgB,aAAa,KAAgB,OAAQ,EAAqB,WAAY,cACrG,EAAsB,QAAQ;CAElC,GACC,CAAC,GAAc,CAAS,CAAC,GAGxB,GACF,OAAO,EAAqB,GAAW,CAAW;CAQpD,IAAM,GAAG,KAAe,EAAS,CAAC,CAAC;CAWnC,OATA,QAAgB;EACd,IAAI,KAAgB,iBAAiB,KAAgB,OAAQ,EAAqB,eAAgB,YAAY;GAC5G,IAAM,IAAe,EAAqB,kBAAkB;IAC1D,EAAY,CAAC,CAAC;GAChB,CAAC;GACD,aAAa,EAAY;EAC3B;CACF,GAAG,CAAC,CAAY,CAAC,GAEV;AACT"}
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pravosleva/reactive-engine v1.6.0
2
+ * @pravosleva/reactive-engine v1.7.0-beta
3
3
  * High-performance transactional reactive engine with microtask batching.
4
4
  *
5
5
  * @license MIT
@@ -9,5 +9,5 @@
9
9
  * @contact Telegram Channel: https://t.me/bash_exp_ru/3393
10
10
  * @contact Developer Telegram: https://t.me/pravosleva
11
11
  */
12
- (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports):typeof define==`function`&&define.amd?define([`exports`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.ReactiveEngineLib={}))})(this,function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=({tested:e,expectedKey:t,valueType:n})=>{let r=[];if(!e||!Array.isArray(e))return r;for(let i=0,a=e.length;i<a;i++){let a=e[i];if(typeof a!=`string`)continue;let o;switch(n){case`number`:o=RegExp(`\\[${t}=(?<value>\\d+)\\]`,`g`);break;default:o=RegExp(`\\[${t}=(?<value>.*?)\\]`,`g`);break}for(let e of a.matchAll(o))e?.groups&&r.push(e.groups.value)}return r},n=class{frameworkPrefix=`core`;activeEffect=null;isBatching=!1;pendingEffects=new Set;services=new Map;factories=new Map;proxyCache=new WeakMap;allEffects=new Set;loggerOptions;pendingLogQueue=[];lastTransactionDuration=null;signalMutationCounts=new Map;lastComputedDurations=new Map;effectExecutionCounts=new Map;batchTickCounts=0;constructor(e){this.loggerOptions=e?.logger}log(e,n,r){if(!this.loggerOptions?.isEnabled)return;if(!this.loggerOptions?.isCoreOptimizationDebugEnabled){let e=t({expectedKey:`CORE_INTERNAL_SIGNAL`,tested:[n],valueType:`number`})?.[0]===`1`,r=t({expectedKey:`CORE_INTERNAL_EFFECT`,tested:[n],valueType:`number`})?.[0]===`1`;if(e||r)return}if(this.loggerOptions.filter&&!(this.loggerOptions.filter instanceof RegExp?this.loggerOptions.filter:new RegExp(this.loggerOptions.filter.replace(/^\/|\/$/g,``))).test(n))return;let i={signal:`background: #007acc; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,computed:`background: #42b883; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,effect:`background: #e01e5a; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,batch:`background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,resource:`background: #d97706; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,reactive:`background: #007acc; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`},a=``,o=`color: #aaa; font-weight: normal;`;switch(e){case`signal`:{let s=t({tested:[n],expectedKey:`CORE_INTERNAL_SIGNAL`,valueType:`number`})?.[0]===`1`,c=n,l=(this.signalMutationCounts.get(c)||0)+1;this.signalMutationCounts.set(c,l);let u=r,d=u.subscribers&&u.subscribers.length>0?u.subscribers.every(e=>e?t({tested:[e],expectedKey:`IS_OPTIMIZED`,valueType:`number`})?.[0]===`1`:!1):!1,f=l>50,p=s?`COMPUTED_INTERNAL`:`SIGNAL`;a=` 🔄 Изменений ${s?`кэша`:`сигнала`}: ${l}${f?` ⚠️ (high noise — обнаружен дребезг/спам значений!)`:``}`,f&&(o=`color: orange; font-weight: bold;`,d||(r.__performance_advice__={issue:`Сигнал [${c}] обновляется слишком часто (${l} раз за такт). Это приводит к избыточным ререндерам UI.`,solution:`Оберните чтение этого сигнала в декоратор сжатия потока данных 'withThrottleComputed'`,example:`public throttled = withThrottleComputed(this.engine, () => this.yourSignalName.value, { limit: 300 });`})),console.groupCollapsed(`%c${p}%c [${c}]%c${a}`,s?`background: #42b883; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`:i[e],`color: #aaa; font-weight: bold;`,o),console.log(`Данные (Payload):`,r),this.loggerOptions.traceTime&&typeof performance<`u`&&console.log(`Тайминг рантайма:`,`${performance.now().toFixed(2)} ms`),console.groupEnd();return}case`computed`:{let e=r;if(e&&`to`in e){let t=e.to;a=` 🧮 Значение: ${typeof t==`object`&&t?`{...}`:String(t)}`}else if(e&&`duration`in e){let t=parseFloat(e.duration);if(!isNaN(t)){let r=this.lastComputedDurations.get(n);if(r!==void 0&&r>0){let n=(t-r)/r*100;n>5?(a=` 🔴 Замедление: +${n.toFixed(1)}% (${e.duration})`,o=`color: #e01e5a; font-weight: bold;`):n<-5?(a=` 🟢 Ускорение: ${n.toFixed(1)}% (${e.duration})`,o=`color: #42b883; font-weight: bold;`):(a=` 🟢 Стабильно (${e.duration})`,o=`color: #42b883; font-weight: bold;`)}else a=` ⚪ Первый расчет (${e.duration})`;this.lastComputedDurations.set(n,t)}}break}case`effect`:{let e=(this.effectExecutionCounts.get(n)||0)+1;this.effectExecutionCounts.set(n,e);let t=e>30,i=r;a=` 🟢 Стабильно${i&&`duration`in i?` (${i.duration})`:``} | 🚀 Вызовов: ${e}${t?` ⚠️ (heavy re-renders)`:``}`,o=`color: #42b883; font-weight: bold;`,t&&(o=`color: #ff4a4a; font-weight: bold;`);break}case`batch`:this.batchTickCounts+=1,a=` 📦 Номер транзакции: #${this.batchTickCounts}`;break;case`resource`:{let e=r;if(e.loading)a=` ⏳ ЗАГРУЗКА (fetching...)`,o=`color: #d97706; font-weight: bold;`;else if(e.error)a=` 🔴 ОШИБКА: ${e.error.message||`Unknown Error`}`,o=`color: #ff4a4a; font-weight: bold;`;else{let t=e.data;a=` 🟢 УСПЕХ (data: ${typeof t==`object`&&t?`{...}`:String(t)})`,o=`color: #42b883; font-weight: bold;`}break}default:break}console.groupCollapsed(`%c${e.toUpperCase()}%c [${n}]%c${a}`,i[e],`color: #aaa; font-weight: bold;`,o),console.log(`Данные (Payload):`,r),this.loggerOptions.traceTime&&typeof performance<`u`&&console.log(`Тайминг рантайма:`,`${performance.now().toFixed(2)} ms`),console.groupEnd()}onSignalChange;provide(e,t){typeof t==`function`&&!t.prototype?this.factories.set(e,t):this.services.set(e,t)}inject(e){if(!e)throw Error(`[DI Error]: Вы пытаетесь внедрить пустой токен (undefined/null). Проверьте импорты.`);let t=e;if(this.services.has(t))return this.services.get(t);try{let n=this.factories.get(t);if(n){let e=n(this);return this.services.set(t,e),e}if(typeof e==`function`&&e.prototype){let n=new e(this);return this.services.set(t,n),n}throw Error(`Service not found: ${String(e)}`)}catch(t){throw Error(`[DI Error]: Не удалось создать сервис ${String(e)}. Ошибка: ${t?.message||`No e?.message`}`)}}queueLog(e,t,n){this.loggerOptions?.isEnabled&&this.pendingLogQueue.push({type:e,name:t,detail:n})}flushLogs(){if(!this.loggerOptions?.isEnabled||this.pendingLogQueue.length===0)return;let e=typeof performance<`u`?performance.now():0,t=this.pendingLogQueue;if(this.loggerOptions.filter)try{let e;if(this.loggerOptions.filter instanceof RegExp)e=this.loggerOptions.filter;else{let t=this.loggerOptions.filter.replace(/^\/|\/$/g,``);e=new RegExp(t)}t=t.filter(t=>e.test(t.name))}catch(e){console.warn(`[Logger Error]: Некорректный паттерн фильтрации логов`,e)}if(t.length===0){this.pendingLogQueue=[];return}let n=``,r=``,i=`color: #aaa; font-weight: normal;`;if(this.loggerOptions.traceTime&&e){let t=performance.now()-e;if(n=` | Duration: ${t.toFixed(3)} ms`,this.lastTransactionDuration!==null&&this.lastTransactionDuration>0){let e=(t-this.lastTransactionDuration)/this.lastTransactionDuration*100;e>.1?(r=` 🔴 +${e.toFixed(1)}%`,i=`color: #e01e5a; font-weight: bold;`):e<-.1?(r=` 🟢 ${e.toFixed(1)}%`,i=`color: #42b883; font-weight: bold;`):(r=` 🟢 stable`,i=`color: #42b883; font-weight: bold;`)}else r=` ⚪ first`;this.lastTransactionDuration=t}console.groupCollapsed(`%cREACTIVE TRANSACTION${this.loggerOptions.instanceName?` ${this.loggerOptions.instanceName}`:``}%c Microtask Tick (Size: ${t.length}${n})%c${r}`,`background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,`color: #aaa; font-weight: normal;`,i),t.forEach(e=>{this.log(e.type,e.name,e.detail)}),this.loggerOptions.traceTime&&typeof performance<`u`&&console.log(`%c[Execution Timestamp]: ${performance.now().toFixed(2)} ms`,`color: #7952b3; font-weight: bold;`),console.groupEnd(),this.pendingLogQueue=[]}signal(e,t){let n=this,r=e,i=new Set,a=typeof t==`string`?{name:t}:t||{},o=a.name||`unnamed_signal`;return{get value(){if(n.activeEffect){let e=n.activeEffect;i.has(e)||(i.add(e),e.cleanups.add(()=>{i.delete(e)}))}return r},set value(e){if(r===e)return;if(a.validate){let t=a.validate(e);if(t===!1||typeof t==`string`){let n=typeof t==`string`?t:`[Validation Error]: Некорректное значение для сигнала "${o}"`;console.error(`%c${n}`,`color: orange; font-weight: bold;`,{received:e,current:r});return}}let t=r;r=e,n.onSignalChange?.(o,e,t);let s=Array.from(i).map(e=>e.label||`unnamed_effect`);n.queueLog?.(`signal`,o,{from:t,to:e,subscribersCount:i.size,subscribers:s}),i.forEach(e=>n.pendingEffects.add(e)),n.isBatching||(n.isBatching=!0,queueMicrotask(()=>{for(let e of n.pendingEffects)n.pendingEffects.delete(e),e.run();n.isBatching=!1,n.flushLogs?.()}))},subscribe(e){return n.effect(()=>{e(this.value)},`${n.frameworkPrefix}:use:${o}`)}}}effect(e,t){let n=this,r=!0,i={label:t,cleanups:new Set,run(){let t=Array.from(this.cleanups);this.cleanups.clear(),t.forEach(e=>e());let i=n.activeEffect;n.activeEffect=this;let a=performance.now();n.safeRun(this,()=>{let t=e();typeof t==`function`&&this.cleanups.add(t)}),n.activeEffect=i;let o=performance.now()-a;if(!r&&n.loggerOptions?.isEnabled){let e=n.loggerOptions.filter;(!e||e instanceof RegExp&&this.label&&e.test(this.label))&&typeof n.queueLog==`function`&&n.queueLog(`effect`,this.label||`unnamed-efect-0`,{name:this.label||`unnamed-efect-0`,type:`effect`,detail:{status:`stable`,duration:`${o.toFixed(3)}ms`,timestamp:a}})}r=!1}};return this.allEffects.add(i),i.run(),()=>{i.cleanups.forEach(e=>e()),n.pendingEffects.delete(i),n.allEffects.delete(i)}}computedCache=new Map;cleanupRegistry=new FinalizationRegistry(e=>{e()});computed(e,t){if(this.computedCache.has(e)){let t=this.computedCache.get(e)?.deref();if(t)return t}let n=this,r=t||`unnamed_computed`,i=this.signal(void 0,`[CORE_INTERNAL_SIGNAL=1]:${r}`),a=!0,o,s=this.effect(()=>{o=e(),a=!1;let t=typeof performance<`u`&&performance.now()?`0.010ms`:`N/A`;typeof n.queueLog==`function`&&n.queueLog(`computed`,r,{value:o,duration:t}),i.value=o},`[CORE_INTERNAL_EFFECT=1]:${r}`),c=Array.from(this.allEffects)[this.allEffects.size-1],l=()=>{s(),c&&n.allEffects.delete(c),n.computedCache.delete(e)},u={get value(){return a&&(o=e(),a=!1,i.value=o),i.value},subscribe:e=>i.subscribe(e),destroy(){s(),l(),n.computedCache.delete(e)}};return this.cleanupRegistry.register(u,l),this.computedCache.set(e,new WeakRef(u)),u}reactive(e,t=`reactive`){if(this.proxyCache.has(e))return this.proxyCache.get(e);let n=this,r=new Map,i=new Proxy(e,{get(e,i,a){if(n.activeEffect){let e=n.activeEffect;r.has(i)||r.set(i,new Set);let t=r.get(i);t.has(e)||(t.add(e),e.cleanups.add(()=>{t.delete(e)}))}let o=Reflect.get(e,i,a);return typeof o==`object`&&o?n.reactive(o,`${t}.${String(i)}`):o},set(e,i,a,o){let s=Reflect.get(e,i,o);if(s===a)return!0;if(s!==a){Reflect.set(e,i,a,o),n.onSignalChange?.(`${t}.${String(i)}`,a,s);let c=r.get(i);(c?Array.from(c):[]).forEach(e=>{n.isBatching?n.pendingEffects.add(e):(n.pendingEffects.add(e),n.isBatching||(n.isBatching=!0,queueMicrotask(()=>{for(let e of n.pendingEffects)n.pendingEffects.delete(e),e.run();n.isBatching=!1,n.flushLogs?.()})))}),n.queueLog?.(`reactive`,`${t}.${String(i)}`,{action:`set`,property:String(i),oldValue:s,newValue:a})}return!0}});return this.proxyCache.set(e,i),i}batch(e){e()}delay(e,t){return new Promise((n,r)=>{if(t.aborted)return r(new DOMException(`Aborted`,`AbortError`));let i=setTimeout(()=>{t.removeEventListener(`abort`,a),n()},e),a=()=>{clearTimeout(i),r(new DOMException(`Aborted`,`AbortError`))};t.addEventListener(`abort`,a,{once:!0})})}resource(e,n,r){let i=r&&typeof r==`object`,a=i?r.name:r||`unnamed_resource`,o=i?r:{},s=o.resetDataOnSourceChange??!0,c=o.responseValidate,l=o.validateBeforeFetch,u=o.retryCount??0,d=o.retryDelay??1e3,f=o.isExponentialBackoffEnabled??!1,p=o.maxRetryDelay??3e4,m=o.timeout,h=this.signal({data:null,loading:!0,error:null,isRetrying:!1},`[CORE_INTERNAL_SIGNAL=1]:resource:state:${a}`),g=e=>{h.value=e,typeof this.queueLog==`function`&&this.queueLog(`resource`,a,{loading:e.loading,data:e.data,error:e.error,isRetrying:e.isRetrying})},_=async(n,r,i=!1)=>{if(r.aborted)return;if(l){let e=l(n);if(e===!1||typeof e==`string`){if(r.aborted)return;let t=typeof e==`string`?e:`Pre-fetch validation failed for resource`,n=i?null:this.untrack(()=>h.value.data);g({data:n,loading:!1,error:Error(t),isRetrying:!1});return}}let o=i&&s?null:this.untrack(()=>h.value.data);g({data:o,loading:!0,error:null,isRetrying:!1});for(let i=0;i<=u;i++){if(r.aborted)return;let o=r;try{if(m&&m>0){let e=AbortSignal.timeout(m);o=AbortSignal.any([r,e])}let t=await e(n,o);if(r.aborted)return;if(o.aborted)throw new DOMException(`The operation timed out.`,`TimeoutError`);if(c){let e=c(t);if(e===!1||typeof e==`string`){if(r.aborted)return;g({data:null,loading:!1,error:Error(typeof e==`string`?e:`Validation failed`),isRetrying:!1});return}}if(r.aborted)return;g({data:t,loading:!1,error:null,isRetrying:!1});return}catch(e){if(r.aborted||e.name===`AbortError`&&r.aborted)return;let n=e.name===`TimeoutError`||e.name===`AbortError`||e.message?.includes(`timeout`),o=t({tested:[e.message],expectedKey:`THROW_CUSTOM_VALIDATION_ERROR_NO_RETRY`,valueType:`number`})?.[0]===`1`||!1,s=o&&t({tested:[e.message],expectedKey:`MESSAGE`,valueType:`string`})?.[0]||`Custom validation error`,c=o||e.name===`ValidationError`;if(i===u||c){if(r.aborted)return;let t=n?Error(`Request timed out after ${m}ms`,{cause:e}):o?Error(s,{cause:e}):e;g({data:null,loading:!1,error:t,isRetrying:!1});return}else{if(r.aborted)return;let e=f?d*2**i:d;e=Math.min(e,p);let t=Math.random()*200;e+=t,g({data:h.value.data,loading:!0,error:null,isRetrying:!0}),console.warn(`[Resource Retry] "${a}" сбой по ${n?`таймауту`:`ошибке сети`}. Попытка ${i+1}/${u+1}...`);try{if(await this.delay(e,r),r.aborted)return}catch{return}}}}},v=null;return this.effect(()=>{let e=n?n.value:void 0,t=new AbortController;return v=t,this.untrack(()=>{_(e,t.signal,!0)}),()=>t.abort()},`[dev]`),{get data(){return h.value.data},get loading(){return h.value.loading},get error(){return h.value.error},get isRetrying(){return h.value.isRetrying},get value(){return h.value},refetch:()=>{v?.abort(),v=new AbortController,_(n?n.value:void 0,v.signal,!1)},subscribe:e=>h.subscribe(e)}}untrack(e){let t=this.activeEffect;this.activeEffect=null;try{return e()}finally{this.activeEffect=t}}safeRun(e,t){try{t()}catch(e){console.error(`%c[Reactive Error] %cОшибка в эффекте/computed:`,`color: white; background: red; padding: 2px 4px; border-radius: 3px;`,`font-weight: bold;`,e)}}},r=class{engine;constructor(e){this.engine=e,this.onInit()}createNumericSignal(e,t){return this.engine.signal(e,{name:t,validate:e=>typeof e==`number`&&!isNaN(e)})}createComputed(e){return this.engine.computed(e)}createSignal(e,t){return this.engine.signal(e,t)}onInit(){}},i=r,a=class{engine;constructor(e){this.engine=e,setTimeout(()=>this.onInit(),0)}onInit(){}};e.AbstractService=r,e.BaseREService=i,e.BaseREServiceEnhanced=a,e.ReactiveEngine=n});
12
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports):typeof define==`function`&&define.amd?define([`exports`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.ReactiveEngineLib={}))})(this,function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=({tested:e,expectedKey:t,valueType:n})=>{let r=[];if(!e||!Array.isArray(e))return r;for(let i=0,a=e.length;i<a;i++){let a=e[i];if(typeof a!=`string`)continue;let o;switch(n){case`number`:o=RegExp(`\\[${t}=(?<value>\\d+)\\]`,`g`);break;default:o=RegExp(`\\[${t}=(?<value>.*?)\\]`,`g`);break}for(let e of a.matchAll(o))e?.groups&&r.push(e.groups.value)}return r},n=class{frameworkPrefix=`core`;activeEffect=null;isBatching=!1;pendingEffects=new Set;services=new Map;factories=new Map;proxyCache=new WeakMap;allEffects=new Set;loggerOptions;pendingLogQueue=[];lastTransactionDuration=null;signalMutationCounts=new Map;lastComputedDurations=new Map;effectExecutionCounts=new Map;batchTickCounts=0;constructor(e){this.loggerOptions=e?.logger}log(e,n,r){if(!this.loggerOptions?.isEnabled)return;if(!this.loggerOptions?.isCoreOptimizationDebugEnabled){let e=t({expectedKey:`CORE_INTERNAL_SIGNAL`,tested:[n],valueType:`number`})?.[0]===`1`,r=t({expectedKey:`CORE_INTERNAL_EFFECT`,tested:[n],valueType:`number`})?.[0]===`1`;if(e||r)return}if(this.loggerOptions.filter&&!(this.loggerOptions.filter instanceof RegExp?this.loggerOptions.filter:new RegExp(this.loggerOptions.filter.replace(/^\/|\/$/g,``))).test(n))return;let i={signal:`background: #007acc; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,computed:`background: #42b883; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,effect:`background: #e01e5a; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,batch:`background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,resource:`background: #d97706; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,reactive:`background: #007acc; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`},a=``,o=`color: #aaa; font-weight: normal;`;switch(e){case`signal`:{let s=t({tested:[n],expectedKey:`CORE_INTERNAL_SIGNAL`,valueType:`number`})?.[0]===`1`,c=n,l=(this.signalMutationCounts.get(c)||0)+1;this.signalMutationCounts.set(c,l);let u=r,d=u.subscribers&&u.subscribers.length>0?u.subscribers.every(e=>e?t({tested:[e],expectedKey:`IS_OPTIMIZED`,valueType:`number`})?.[0]===`1`:!1):!1,f=l>50,p=s?`COMPUTED_INTERNAL`:`SIGNAL`;a=` 🔄 Изменений ${s?`кэша`:`сигнала`}: ${l}${f?` ⚠️ (high noise — обнаружен дребезг/спам значений!)`:``}`,f&&(o=`color: orange; font-weight: bold;`,d||(r.__performance_advice__={issue:`Сигнал [${c}] обновляется слишком часто (${l} раз за такт). Это приводит к избыточным ререндерам UI.`,solution:`Оберните чтение этого сигнала в декоратор сжатия потока данных 'withThrottleComputed'`,example:`public throttled = withThrottleComputed(this.engine, () => this.yourSignalName.value, { limit: 300 });`})),console.groupCollapsed(`%c${p}%c [${c}]%c${a}`,s?`background: #42b883; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`:i[e],`color: #aaa; font-weight: bold;`,o),console.log(`Данные (Payload):`,r),this.loggerOptions.traceTime&&typeof performance<`u`&&console.log(`Тайминг рантайма:`,`${performance.now().toFixed(2)} ms`),console.groupEnd();return}case`computed`:{let e=r;if(e&&`to`in e){let t=e.to;a=` 🧮 Значение: ${typeof t==`object`&&t?`{...}`:String(t)}`}else if(e&&`duration`in e){let t=parseFloat(e.duration);if(!isNaN(t)){let r=this.lastComputedDurations.get(n);if(r!==void 0&&r>0){let n=(t-r)/r*100;n>5?(a=` 🔴 Замедление: +${n.toFixed(1)}% (${e.duration})`,o=`color: #e01e5a; font-weight: bold;`):n<-5?(a=` 🟢 Ускорение: ${n.toFixed(1)}% (${e.duration})`,o=`color: #42b883; font-weight: bold;`):(a=` 🟢 Стабильно (${e.duration})`,o=`color: #42b883; font-weight: bold;`)}else a=` ⚪ Первый расчет (${e.duration})`;this.lastComputedDurations.set(n,t)}}break}case`effect`:{let e=(this.effectExecutionCounts.get(n)||0)+1;this.effectExecutionCounts.set(n,e);let t=e>30,i=r;a=` 🟢 Стабильно${i&&`duration`in i?` (${i.duration})`:``} | 🚀 Вызовов: ${e}${t?` ⚠️ (heavy re-renders)`:``}`,o=`color: #42b883; font-weight: bold;`,t&&(o=`color: #ff4a4a; font-weight: bold;`);break}case`batch`:this.batchTickCounts+=1,a=` 📦 Номер транзакции: #${this.batchTickCounts}`;break;case`resource`:{let e=r;if(e.loading)a=` ⏳ ЗАГРУЗКА (fetching...)`,o=`color: #d97706; font-weight: bold;`;else if(e.error)a=` 🔴 ОШИБКА: ${e.error.message||`Unknown Error`}`,o=`color: #ff4a4a; font-weight: bold;`;else{let t=e.data;a=` 🟢 УСПЕХ (data: ${typeof t==`object`&&t?`{...}`:String(t)})`,o=`color: #42b883; font-weight: bold;`}break}default:break}console.groupCollapsed(`%c${e.toUpperCase()}%c [${n}]%c${a}`,i[e],`color: #aaa; font-weight: bold;`,o),console.log(`Данные (Payload):`,r),this.loggerOptions.traceTime&&typeof performance<`u`&&console.log(`Тайминг рантайма:`,`${performance.now().toFixed(2)} ms`),console.groupEnd()}onSignalChange;provide(e,t){typeof t==`function`&&!t.prototype?this.factories.set(e,t):this.services.set(e,t)}inject(e){if(!e)throw Error(`[DI Error]: Вы пытаетесь внедрить пустой токен (undefined/null). Проверьте импорты.`);let t=e;if(this.services.has(t))return this.services.get(t);try{let n=this.factories.get(t);if(n){let e=n(this);return this.services.set(t,e),e}if(typeof e==`function`&&e.prototype){let n=new e(this);return this.services.set(t,n),n}throw Error(`Service not found: ${String(e)}`)}catch(t){throw Error(`[DI Error]: Не удалось создать сервис ${String(e)}. Ошибка: ${t?.message||`No e?.message`}`)}}queueLog(e,t,n){this.loggerOptions?.isEnabled&&this.pendingLogQueue.push({type:e,name:t,detail:n})}flushLogs(){if(!this.loggerOptions?.isEnabled||this.pendingLogQueue.length===0)return;let e=typeof performance<`u`?performance.now():0,t=this.pendingLogQueue;if(this.loggerOptions.filter)try{let e;if(this.loggerOptions.filter instanceof RegExp)e=this.loggerOptions.filter;else{let t=this.loggerOptions.filter.replace(/^\/|\/$/g,``);e=new RegExp(t)}t=t.filter(t=>e.test(t.name))}catch(e){console.warn(`[Logger Error]: Некорректный паттерн фильтрации логов`,e)}if(t.length===0){this.pendingLogQueue=[];return}let n=``,r=``,i=`color: #aaa; font-weight: normal;`;if(this.loggerOptions.traceTime&&e){let t=performance.now()-e;if(n=` | Duration: ${t.toFixed(3)} ms`,this.lastTransactionDuration!==null&&this.lastTransactionDuration>0){let e=(t-this.lastTransactionDuration)/this.lastTransactionDuration*100;e>.1?(r=` 🔴 +${e.toFixed(1)}%`,i=`color: #e01e5a; font-weight: bold;`):e<-.1?(r=` 🟢 ${e.toFixed(1)}%`,i=`color: #42b883; font-weight: bold;`):(r=` 🟢 stable`,i=`color: #42b883; font-weight: bold;`)}else r=` ⚪ first`;this.lastTransactionDuration=t}console.groupCollapsed(`%cREACTIVE TRANSACTION${this.loggerOptions.instanceName?` ${this.loggerOptions.instanceName}`:``}%c Microtask Tick (Size: ${t.length}${n})%c${r}`,`background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,`color: #aaa; font-weight: normal;`,i),t.forEach(e=>{this.log(e.type,e.name,e.detail)}),this.loggerOptions.traceTime&&typeof performance<`u`&&console.log(`%c[Execution Timestamp]: ${performance.now().toFixed(2)} ms`,`color: #7952b3; font-weight: bold;`),console.groupEnd(),this.pendingLogQueue=[]}signal(e,t){let n=this,r=e,i=new Set,a=typeof t==`string`?{name:t}:t||{},o=a.name||`unnamed_signal`;return{get value(){if(n.activeEffect){let e=n.activeEffect;i.has(e)||(i.add(e),e.cleanups.add(()=>{i.delete(e)}))}return r},set value(e){if(a?.validate&&a.validate(e)!==!0){let t=a.validate(e);console.error(`[ReactiveEngine] Validation failed for signal "${o}": ${typeof t==`string`?t:`Unknown reason`}`,{value:e});return}if((e===null||typeof e!=`object`&&typeof e!=`function`)&&r===e)return;let t=r;r=e,n.onSignalChange?.(o,e,t),i.forEach(e=>{e!==n.activeEffect&&(n.isBatching?n.pendingEffects.add(e):(n.pendingEffects.add(e),n.isBatching||(n.isBatching=!0,queueMicrotask(()=>{for(let e of n.pendingEffects)n.pendingEffects.delete(e),e.run();n.isBatching=!1,n.flushLogs?.()}))))})},subscribe(e){return n.effect(()=>{e(this.value)},`${n.frameworkPrefix}:use:${o}`)}}}effect(e,t){let n=this,r=!0,i={label:t,cleanups:new Set,run(){let t=Array.from(this.cleanups);this.cleanups.clear(),t.forEach(e=>e());let i=n.activeEffect;n.activeEffect=this;let a=performance.now();n.safeRun(this,()=>{let t=e();typeof t==`function`&&this.cleanups.add(t)}),n.activeEffect=i;let o=performance.now()-a;if(!r&&n.loggerOptions?.isEnabled){let e=n.loggerOptions.filter;(!e||e instanceof RegExp&&this.label&&e.test(this.label))&&typeof n.queueLog==`function`&&n.queueLog(`effect`,this.label||`unnamed-efect-0`,{name:this.label||`unnamed-efect-0`,type:`effect`,detail:{status:`stable`,duration:`${o.toFixed(3)}ms`,timestamp:a}})}r=!1}};return this.allEffects.add(i),i.run(),()=>{i.cleanups.forEach(e=>e()),n.pendingEffects.delete(i),n.allEffects.delete(i)}}computedCache=new Map;cleanupRegistry=new FinalizationRegistry(e=>{e()});computed(e,t){if(this.computedCache.has(e)){let t=this.computedCache.get(e)?.deref();if(t)return t}let n=this,r=t||`unnamed_computed`,i=this.signal(void 0,`[CORE_INTERNAL_SIGNAL=1]:${r}`),a=!0,o,s=this.effect(()=>{o=e(),a=!1;let t=typeof performance<`u`&&performance.now()?`0.010ms`:`N/A`;typeof n.queueLog==`function`&&n.queueLog(`computed`,r,{value:o,duration:t}),i.value=o},`[CORE_INTERNAL_EFFECT=1]:${r}`),c=Array.from(this.allEffects)[this.allEffects.size-1],l=()=>{s(),c&&n.allEffects.delete(c),n.computedCache.delete(e)},u={get value(){return a&&(o=e(),a=!1,i.value=o),i.value},subscribe:e=>i.subscribe(e),destroy(){s(),l(),n.computedCache.delete(e)}};return this.cleanupRegistry.register(u,l),this.computedCache.set(e,new WeakRef(u)),u}reactive(e,t=`reactive`){if(this.proxyCache.has(e))return this.proxyCache.get(e);let n=this,r=new Map,i=new Proxy(e,{get(e,i,a){if(n.activeEffect){let e=n.activeEffect;r.has(i)||r.set(i,new Set);let t=r.get(i);t.has(e)||(t.add(e),e.cleanups.add(()=>{t.delete(e)}))}if(Array.isArray(e)&&typeof i==`string`&&[`push`,`pop`,`shift`,`unshift`,`splice`,`sort`,`reverse`].includes(i)){let t=e[i];return function(...a){let o=n.isBatching;n.isBatching=!0;let s=t.apply(e,a);return r.forEach((e,t)=>{t!==i&&Array.from(e).forEach(e=>{e!==n.activeEffect&&n.pendingEffects.add(e)})}),n.isBatching=o,!n.isBatching&&n.pendingEffects.size>0&&(n.isBatching=!0,queueMicrotask(()=>{for(let e of n.pendingEffects)n.pendingEffects.delete(e),e.run();n.isBatching=!1,n.flushLogs?.()})),s}}let o=Reflect.get(e,i,a);return typeof o==`object`&&o?n.reactive(o,`${t}.${String(i)}`):o},set(e,i,a,o){let s=Reflect.get(e,i,o);if(s===a)return!0;if(s!==a){Reflect.set(e,i,a,o),n.onSignalChange?.(`${t}.${String(i)}`,a,s);let c=r.get(i),l=c?Array.from(c):[];if(Array.isArray(e)){let e=r.get(`length`);e&&l.push(...Array.from(e))}l.forEach(e=>{e!==n.activeEffect&&(n.isBatching?n.pendingEffects.add(e):(n.pendingEffects.add(e),n.isBatching||(n.isBatching=!0,queueMicrotask(()=>{for(let e of n.pendingEffects)n.pendingEffects.delete(e),e.run();n.isBatching=!1,n.flushLogs?.()}))))}),n.queueLog?.(`reactive`,`${t}.${String(i)}`,{action:`set`,property:String(i),oldValue:s,newValue:a})}return!0}});return this.proxyCache.set(e,i),i}batch(e){e()}delay(e,t){return new Promise((n,r)=>{if(t.aborted)return r(new DOMException(`Aborted`,`AbortError`));let i=setTimeout(()=>{t.removeEventListener(`abort`,a),n()},e),a=()=>{clearTimeout(i),r(new DOMException(`Aborted`,`AbortError`))};t.addEventListener(`abort`,a,{once:!0})})}resource(e,n,r){let i=r&&typeof r==`object`,a=i?r.name:r||`unnamed_resource`,o=i?r:{},s=o.resetDataOnSourceChange??!0,c=o.responseValidate,l=o.validateBeforeFetch,u=o.retryCount??0,d=o.retryDelay??1e3,f=o.isExponentialBackoffEnabled??!1,p=o.maxRetryDelay??3e4,m=o.timeout,h=this.signal({data:null,loading:!0,error:null,isRetrying:!1},`[CORE_INTERNAL_SIGNAL=1]:resource:state:${a}`),g=e=>{h.value=e,typeof this.queueLog==`function`&&this.queueLog(`resource`,a,{loading:e.loading,data:e.data,error:e.error,isRetrying:e.isRetrying})},_=async(n,r,i=!1)=>{if(r.aborted)return;if(l){let e=l(n);if(e===!1||typeof e==`string`){if(r.aborted)return;let t=typeof e==`string`?e:`Pre-fetch validation failed for resource`,n=i?null:this.untrack(()=>h.value.data);g({data:n,loading:!1,error:Error(t),isRetrying:!1});return}}let o=i&&s?null:this.untrack(()=>h.value.data);g({data:o,loading:!0,error:null,isRetrying:!1});for(let i=0;i<=u;i++){if(r.aborted)return;let o=r;try{if(m&&m>0){let e=AbortSignal.timeout(m);o=AbortSignal.any([r,e])}let t=await e(n,o);if(r.aborted)return;if(o.aborted)throw new DOMException(`The operation timed out.`,`TimeoutError`);if(c){let e=c(t);if(e===!1||typeof e==`string`){if(r.aborted)return;g({data:null,loading:!1,error:Error(typeof e==`string`?e:`Validation failed`),isRetrying:!1});return}}if(r.aborted)return;g({data:t,loading:!1,error:null,isRetrying:!1});return}catch(e){if(r.aborted||e.name===`AbortError`&&r.aborted)return;let n=e.name===`TimeoutError`||e.name===`AbortError`||e.message?.includes(`timeout`),o=t({tested:[e.message],expectedKey:`THROW_CUSTOM_VALIDATION_ERROR_NO_RETRY`,valueType:`number`})?.[0]===`1`||!1,s=o&&t({tested:[e.message],expectedKey:`MESSAGE`,valueType:`string`})?.[0]||`Custom validation error`,c=o||e.name===`ValidationError`;if(i===u||c){if(r.aborted)return;let t=n?Error(`Request timed out after ${m}ms`,{cause:e}):o?Error(s,{cause:e}):e;g({data:null,loading:!1,error:t,isRetrying:!1});return}else{if(r.aborted)return;let e=f?d*2**i:d;e=Math.min(e,p);let t=Math.random()*200;e+=t,g({data:h.value.data,loading:!0,error:null,isRetrying:!0}),console.warn(`[Resource Retry] "${a}" сбой по ${n?`таймауту`:`ошибке сети`}. Попытка ${i+1}/${u+1}...`);try{if(await this.delay(e,r),r.aborted)return}catch{return}}}}},v=null;return this.effect(()=>{let e=n?n.value:void 0,t=new AbortController;return v=t,this.untrack(()=>{_(e,t.signal,!0)}),()=>t.abort()},`[dev]`),{get data(){return h.value.data},get loading(){return h.value.loading},get error(){return h.value.error},get isRetrying(){return h.value.isRetrying},get value(){return h.value},refetch:()=>{v?.abort(),v=new AbortController,_(n?n.value:void 0,v.signal,!1)},subscribe:e=>h.subscribe(e)}}untrack(e){let t=this.activeEffect;this.activeEffect=null;try{return e()}finally{this.activeEffect=t}}safeRun(e,t){try{t()}catch(e){console.error(`%c[Reactive Error] %cОшибка в эффекте/computed:`,`color: white; background: red; padding: 2px 4px; border-radius: 3px;`,`font-weight: bold;`,e)}}},r=class{engine;constructor(e){this.engine=e,this.onInit()}createNumericSignal(e,t){return this.engine.signal(e,{name:t,validate:e=>typeof e==`number`&&!isNaN(e)})}createComputed(e){return this.engine.computed(e)}createSignal(e,t){return this.engine.signal(e,t)}onInit(){}},i=r,a=class{engine;constructor(e){this.engine=e,setTimeout(()=>this.onInit(),0)}onInit(){}};e.AbstractService=r,e.BaseREService=i,e.BaseREServiceEnhanced=a,e.ReactiveEngine=n});
13
13
  //# sourceMappingURL=reactive-engine.umd.js.map