@pravosleva/reactive-engine 0.2.7-beta → 0.2.9-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.
package/README.md CHANGED
@@ -1,138 +1,6 @@
1
1
  # 🚀 ReactiveEngine Core Framework
2
2
  A lightweight, type-safe reactive engine built with TypeScript, featuring Dependency Injection and seamless React integration.
3
3
 
4
- https://t.me/bash_exp_ru/3393
5
-
6
- ```bash
7
- yarn add @pravosleva/reactive-engine
8
- ```
9
-
10
- ## 📦 Core Components
11
- ### 1. ReactiveEngine
12
- The central hub of the system. It manages state, effects, DI, and the bridge to React.
13
-
14
- - signal<T>(value, options) — Creates an atomic state unit.
15
- - computed<T>(fn) — Derives data with automatic caching.
16
- - reactive<T>(obj) — Provides deep reactivity for objects and arrays via Proxy.
17
- - resource<T>(fetcher, source?) — Handles async requests with auto-abort (Race Condition protection).
18
- - inject<T>(Class) — Retrieves or instantiates a singleton service.
19
- - use(signal) — A React hook to subscribe to reactive updates.
20
- - untrack(fn) — Executes a function without creating reactive dependencies.
21
-
22
- ### 2. BaseREService
23
- An abstract class for encapsulating business logic.
24
- - Rule: Always initialize signals and resources directly in class fields to ensure correct DI instantiation and type inference.
25
-
26
- ## 🛠 Quick Start
27
- ### Step 1: Initialization (Entry Point)
28
- Create and export a single instance of the engine to be used throughout your app.
29
-
30
- Your instance in your local `~/utils/engine.ts`:
31
- ```ts
32
- import { useState, useEffect } from 'react';
33
- import { ReactiveEngine } from '@pravosleva/reactive-engine';
34
-
35
- export const engine = new ReactiveEngine();
36
- // Bridge the engine with React hooks
37
- engine.setReactAdapters(useState, useEffect);
38
- ```
39
-
40
- ### Step 2: Define Business Logic (Service)
41
- Inherit from BaseREService to create a reactive store.
42
- ```ts
43
- import { BaseREService, Signal, Computed } from '@pravosleva/reactive-engine';
44
-
45
- export class CounterService extends BaseREService {
46
- // Initialize signals with runtime validation
47
- public count = this.engine.signal(0, {
48
- name: 'counter',
49
- validate: (v) => v >= 0 || "Value cannot be negative"
50
- });
51
-
52
- // Derived state (auto-updates when count changes)
53
- public double = this.engine.computed(() => this.count.value * 2);
54
-
55
- increment = () => this.count.value++;
56
- decrement = () => this.count.value--;
57
- }
58
- ```
59
-
60
- ### Step 3: Use in React Components
61
- Connect your logic to the UI with minimal boilerplate.
62
-
63
- ```ts
64
- import { engine } from '~/utils/engine';
65
- import { CounterService } from './CounterService';
66
-
67
- export const Counter = () => {
68
- // Get the service singleton via DI
69
- const store = engine.inject(CounterService);
70
-
71
- // Subscribe to reactive updates
72
- const count = engine.use(store.count);
73
- const double = engine.use(store.double);
74
-
75
- return (
76
- <div>
77
- <h1>Count: {count} (Double: {double})</h1>
78
- <button onClick={store.increment}>+</button>
79
- <button onClick={store.decrement}>-</button>
80
- </div>
81
- );
82
- };
83
- ```
84
-
85
- ## ⚡️ Advanced Features
86
- Async Resources
87
- The resource method automatically re-fetches data whenever its source dependency changes.
88
- ```ts
89
- this.userProfile = this.engine.resource(
90
- async (id, signal) => {
91
- const res = await fetch(`https://example.com/{id}`, { signal });
92
- if (!res.ok) throw new Error('Not found');
93
- return res.json();
94
- },
95
- this.userId // Re-runs when this signal changes
96
- );
97
- ```
98
-
99
- ### Debugging & Logging
100
- Enable global logging to track every state change in your application:
101
- ```ts
102
- engine.onSignalChange = (name, next, prev) => {
103
- console.log(`%c[${name}]`, 'color: #2196F3; font-weight: bold;', prev, '→', next);
104
- };
105
- ```
106
-
107
- ## ⚠️ Best Practices
108
- - Always Initialize in Fields: Define reactive properties as class fields. Avoid initializing them inside methods to prevent undefined errors during rendering.
109
- - Naming Convention: Provide clear names for signals (e.g., this.engine.signal(0, 'my_signal_name')) for better debugging logs.
110
- - Batching: Use engine.batch(() => { ... }) when updating multiple signals to prevent unnecessary re-renders.
111
- - Untrack: Use engine.untrack(() => signal.value) inside effects if you need to read a value without subscribing to it.
112
-
113
- ## Possible project structure (for example)
114
- ```
115
- src/
116
- ├── utils/
117
- │ └── engine.ts # ReactiveEngine instance
118
- │
119
- ├── services/ # Business-logic (Store)
120
- │ ├── index.ts # DI exports (?)
121
- │ ├── User/
122
- │ │ ├── UserService.ts # User logic
123
- │ │ └── types.ts # DTO & data interfaces
124
- │ └── Counter/
125
- │ └── CounterService.ts
126
- │
127
- ├── components/ # UI-layer (React)
128
- │ ├── Shared/ # Common components
129
- │ └── Features/ # Components with logic
130
- │ └── UserProfile/
131
- │ ├── UserProfile.tsx # Usage of engine.use(store.user)
132
- │ └── styles.module.css
133
- │
134
- ├── hooks/ # Global React-hooks
135
- │ └── useStore.ts # Helpers like useUserStore()
136
- │
137
- └── main.tsx # Entry point (settings like engine.setReactAdapters)
138
- ```
4
+ ## Instruction
5
+ - 🇬🇧 [In English](https://github.com/garage-13/reactive-engine/blob/main/README_EN.md)
6
+ - 🇷🇺 [In Russian](https://github.com/garage-13/reactive-engine/blob/main/README_RU.md)
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/core.ts","../src/BaseREService.ts","../src/BaseREServiceEnhanced.ts"],"sourcesContent":["import { useState, useEffect } from 'react';\n\n// --- ТИПЫ ---\nexport type CleanupFn = () => void;\nexport type EffectFn = () => CleanupFn | void;\nexport type Token<T> = string | symbol | { new(engine: ReactiveEngine, ...args: any[]): T };\nexport type Factory<T> = (engine: ReactiveEngine) => T;\n\nexport interface IEffect {\n run: () => void;\n cleanups: Set<CleanupFn>;\n}\n\nexport interface ResourceState<T> {\n data: T | null;\n loading: boolean;\n error: any | null;\n}\n\nexport interface Signal<T> {\n value: T;\n subscribe: (cb: (val: T) => void) => CleanupFn;\n}\n\nexport interface Computed<T> {\n readonly value: T;\n subscribe: (cb: (val: T) => void) => CleanupFn;\n}\n\nexport interface Resource<T> extends ResourceState<T> {\n refetch: () => void;\n subscribe: (cb: (val: ResourceState<T>) => void) => CleanupFn;\n readonly value: ResourceState<T>;\n}\n\ninterface SignalOptions<T> {\n name?: string;\n validate?: (val: T) => boolean | string;\n}\n\n// --- ЯДРО ---\nexport class ReactiveEngine {\n private activeEffect: IEffect | null = null;\n private isBatching = false;\n private pendingEffects = new Set<IEffect>();\n private services = new Map<Token<any>, any>();\n private factories = new Map<Token<any>, Factory<any>>();\n private proxyCache = new WeakMap<object, any>();\n private allEffects = new Set<IEffect>();\n\n public onSignalChange?: (name: string, next: any, prev: any) => void;\n\n private reactAdapters = {\n useState: useState as any,\n useEffect: useEffect as any,\n };\n\n /** DI: Регистрация */\n public provide<T>(token: Token<T>, valueOrFactory: T | Factory<T>): void {\n if (typeof valueOrFactory === 'function' && !valueOrFactory.prototype) {\n this.factories.set(token, valueOrFactory as Factory<T>);\n } else {\n this.services.set(token, valueOrFactory);\n }\n }\n\n /** DI: Инъекция */\n public inject<T>(token: Token<T>): T {\n if (!token) {\n throw new Error(`[DI Error]: Вы пытаетесь внедрить пустой токен (undefined/null). Проверьте импорты.`);\n }\n\n if (this.services.has(token)) return this.services.get(token);\n\n // NOTE: (new way) Если это класс, но мы забыли его унаследовать от BaseService или передать engine\n try {\n // NOTE: Логика создания инстанса\n const factory = this.factories.get(token);\n if (factory) {\n const instance = factory(this);\n this.services.set(token, instance);\n return instance;\n }\n if (typeof token === 'function' && token.prototype) {\n const instance = new (token as { new(eng: ReactiveEngine): T })(this);\n this.services.set(token, instance);\n return instance;\n }\n throw new Error(`Service not found: ${String(token)}`);\n } catch (e) {\n throw new Error(`[DI Error]: Не удалось создать сервис ${String(token)}. Ошибка: ${(e as Error)?.message || 'No e?.message'}`);\n }\n }\n\n /** Создание Сигнала */\n public signal<T>(initialValue: T, optionsOrName?: string | SignalOptions<T>): Signal<T> {\n const engine = this;\n let val = initialValue;\n const subscribers = new Set<IEffect>();\n\n // Парсим аргументы для обратной совместимости\n const options = typeof optionsOrName === 'string'\n ? { name: optionsOrName }\n : optionsOrName || {};\n\n const name = options.name || 'unnamed_signal';\n\n return {\n get value(): T {\n if (engine.activeEffect) subscribers.add(engine.activeEffect);\n return val;\n },\n set value(newValue: T) {\n if (val === newValue) return;\n\n // ВАЛИДАЦИЯ В RUNTIME\n if (options.validate) {\n const result = options.validate(newValue);\n if (result === false || typeof result === 'string') {\n const errorMsg = typeof result === 'string'\n ? result\n : `[Validation Error]: Некорректное значение для сигнала \"${name}\"`;\n\n console.error(`%c${errorMsg}`, \"color: orange; font-weight: bold;\", {\n received: newValue,\n current: val\n });\n return; // Прерываем обновление, если данные не валидны\n }\n }\n\n const old = val;\n val = newValue;\n engine.onSignalChange?.(name, newValue, old);\n subscribers.forEach(e =>\n engine.isBatching ? engine.pendingEffects.add(e) : e.run()\n );\n },\n subscribe(cb) {\n return engine.effect(() => cb(this.value));\n }\n };\n }\n\n /** Эффект */\n public effect(fn: EffectFn): CleanupFn {\n const engine = this;\n const effectObj: IEffect = {\n cleanups: new Set(),\n run() {\n this.cleanups.forEach(c => c());\n this.cleanups.clear();\n const prev = engine.activeEffect;\n engine.activeEffect = this;\n engine.safeRun(this, () => {\n const cleanup = fn();\n if (typeof cleanup === 'function') this.cleanups.add(cleanup);\n });\n engine.activeEffect = prev;\n }\n };\n this.allEffects.add(effectObj); // Регистрируем эффект в глобальном списке\n effectObj.run();\n return () => {\n effectObj.cleanups.forEach(c => c());\n engine.pendingEffects.delete(effectObj);\n };\n }\n\n /** Вычисляемое значение */\n public computed<T>(fn: () => T, signalName?: string): Computed<T> {\n const sig = this.signal<T>(undefined as any, signalName || 'unnamed_computed');\n this.effect(() => {\n const newValue = fn();\n // Сигнал сам внутри себя проверит (sig.value === newValue)\n // благодаря правке в пункте №1\n sig.value = newValue;\n });\n return {\n get value() { return sig.value; },\n // Подписываемся на внутренний сигнал\n subscribe: (cb) => sig.subscribe(cb)\n };\n }\n\n /** Реактивный объект (Proxy) */\n public reactive<T extends object>(target: T, name: string = 'reactive'): T {\n if (this.proxyCache.has(target)) return this.proxyCache.get(target);\n const engine = this;\n const propsSubscribers = new Map<string | symbol, Set<IEffect>>();\n\n const proxy = new Proxy(target, {\n get(obj, prop, receiver) {\n if (engine.activeEffect) {\n if (!propsSubscribers.has(prop)) propsSubscribers.set(prop, new Set());\n propsSubscribers.get(prop)!.add(engine.activeEffect);\n }\n const value = Reflect.get(obj, prop, receiver);\n return (value !== null && typeof value === 'object')\n ? engine.reactive(value, `${name}.${String(prop)}`)\n : value;\n },\n set(obj, prop, value, receiver) {\n const old = Reflect.get(obj, prop, receiver);\n // ПРОВЕРКА НА ИДЕНТИЧНОСТЬ\n if (old === value) return true;\n\n if (old !== value) {\n Reflect.set(obj, prop, value, receiver);\n engine.onSignalChange?.(`${name}.${String(prop)}`, value, old);\n propsSubscribers.get(prop)?.forEach(e =>\n engine.isBatching ? engine.pendingEffects.add(e) : e.run()\n );\n }\n return true;\n }\n });\n this.proxyCache.set(target, proxy);\n return proxy;\n }\n\n /** Группировка изменений */\n public batch(fn: () => void): void {\n this.isBatching = true;\n try { fn(); } finally {\n queueMicrotask(() => {\n const effects = Array.from(this.pendingEffects);\n this.pendingEffects.clear();\n this.isBatching = false;\n effects.forEach(e => e.run());\n });\n }\n }\n\n /** Асинхронный ресурс */\n public resource<T, S = void>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n source?: { value: S },\n signalName?: string,\n ): Resource<T> {\n const state = this.signal<ResourceState<T>>({ data: null, loading: true, error: null }, signalName || 'unnamed_resource');\n let controller: AbortController | null = null;\n\n const load = async (sValue: S) => {\n controller?.abort();\n controller = new AbortController();\n\n // Читаем текущие данные без создания зависимости!\n const currentData = this.untrack(() => state.value.data);\n\n // Теперь обновление состояния не вызовет перезапуск эффекта\n state.value = { data: currentData, loading: true, error: null };\n\n try {\n const data = await fetcher(sValue, controller.signal);\n if (!controller.signal.aborted) {\n state.value = { data, loading: false, error: null };\n }\n } catch (e: any) {\n if (e.name !== 'AbortError') {\n state.value = { data: null, loading: false, error: e };\n }\n }\n };\n\n // Чтобы эффект не подписывался на state,\n // нам нужно изолировать чтение source от чтения state\n this.effect(() => {\n // Читаем ТОЛЬКО source. Это наша единственная зависимость.\n const sValue = source ? source.value : undefined as S;\n\n // Вызываем load. Внутри load чтение state.value\n // не должно регистрироваться как зависимость этого эффекта.\n load(sValue);\n\n return () => controller?.abort();\n });\n\n return {\n get data() { return state.value.data; },\n get loading() { return state.value.loading; },\n get error() { return state.value.error; },\n get value() { return state.value; },\n refetch: () => load(source ? source.value : undefined as S),\n subscribe: (cb) => state.subscribe(cb)\n };\n }\n\n /**\n * Выполняет функцию без отслеживания зависимостей\n */\n public untrack<T>(fn: () => T): T {\n const prev = this.activeEffect;\n this.activeEffect = null; // Временно \"забываем\" про активный эффект\n try {\n return fn();\n } finally {\n this.activeEffect = prev; // Возвращаем эффект на место\n }\n }\n\n /** React Хук */\n public setReactAdapters(useState: any, useEffect: any) {\n this.reactAdapters = { useState, useEffect };\n }\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 private safeRun(effect: IEffect, fn: () => void) {\n try {\n fn();\n } catch (error) {\n console.error(\n `%c[Reactive Error] %cОшибка в эффекте/computed:`,\n \"color: white; background: red; padding: 2px 4px; border-radius: 3px;\",\n \"font-weight: bold;\",\n error\n );\n // Здесь можно отправить ошибку в Sentry или другой сервис мониторинга\n }\n }\n}\n","import { ReactiveEngine } from './core';\n\n// export abstract class BaseService {\n// constructor(protected engine: ReactiveEngine) {\n// // Вызываем метод инициализации, который можно переопределить в наследниках\n// this.onInit();\n// }\n\n// // NOTE: Жизненный цикл: вызывается сразу после создания сервиса.\n// // Удобно для инициализации сигналов, которые зависят от engine.\n// protected onInit(): void {}\n\n// // NOTE: Полезные хелперы для сокращения кода\n// protected createSignal<T>(val: T, name?: string) {\n// return this.engine.signal(val, name);\n// }\n\n// protected createComputed<T>(fn: () => T) {\n// return this.engine.computed(fn);\n// }\n// }\n\nexport abstract class BaseREService {\n constructor(protected engine: ReactiveEngine) { } // Просто сохраняем engine\n\n protected createNumericSignal(val: number, name: string) {\n return this.engine.signal(val, {\n name,\n validate: (v) => typeof v === 'number' && !isNaN(v)\n });\n }\n}\n","import { ReactiveEngine } from './core';\n\nexport abstract class BaseREServiceEnhanced {\n constructor(protected engine: ReactiveEngine) {\n setTimeout(() => this.onInit(), 0)\n }\n\n protected onInit(): void { }\n}\n"],"names":["ReactiveEngine","useState","useEffect","token","valueOrFactory","factory","instance","e","initialValue","optionsOrName","engine","val","subscribers","options","name","newValue","result","errorMsg","old","cb","fn","effectObj","c","prev","cleanup","signalName","sig","target","propsSubscribers","proxy","obj","prop","receiver","value","effects","fetcher","source","state","controller","load","sValue","currentData","data","item","setVal","effect","error","BaseREService","v","BaseREServiceEnhanced"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/core.ts","../src/BaseREService.ts","../src/BaseREServiceEnhanced.ts"],"sourcesContent":["import { useState, useEffect } from 'react';\n\n// --- ТИПЫ ---\nexport type CleanupFn = () => void;\nexport type EffectFn = () => CleanupFn | void;\nexport type Token<T> = string | symbol | { new(engine: ReactiveEngine, ...args: any[]): T };\nexport type Factory<T> = (engine: ReactiveEngine) => T;\n\nexport interface IEffect {\n run: () => void;\n cleanups: Set<CleanupFn>;\n}\n\nexport interface ResourceState<T> {\n data: T | null;\n loading: boolean;\n error: any | null;\n}\n\n/**\n * Функция для очистки эффекта.\n * @typedef {() => void} CleanupFn\n */\n\n/**\n * Функция эффекта, которая может возвращать функцию очистки.\n * @typedef {() => CleanupFn | void} EffectFn\n */\n\n/**\n * Токен для зависимости.\n * @typedef {string | symbol | { new(engine: ReactiveEngine, ...args: any[]): T }} Token<T>\n */\n\n/**\n * Фабрика для создания сервиса.\n * @template T\n * @typedef {(engine: ReactiveEngine) => T} Factory<T>\n */\n\n/**\n * Интерфейс для эффекта.\n * @interface IEffect\n */\nexport interface IEffect {\n /**\n * Запуск эффекта.\n * @function run\n * @returns {void}\n */\n run: () => void;\n\n /**\n * Коллекция функций очистки.\n * @type {Set<CleanupFn>}\n */\n cleanups: Set<CleanupFn>;\n}\n\n/**\n * Интерфейс для состояния ресурса.\n * @template T\n * @interface ResourceState<T>\n */\nexport interface ResourceState<T> {\n /**\n * Данные ресурса.\n * @type {T | null}\n */\n data: T | null;\n\n /**\n * Состояние загрузки ресурса.\n * @type {boolean}\n */\n loading: boolean;\n\n /**\n * Ошибка ресурса.\n * @type {any | null}\n */\n error: any | null;\n}\n\n/**\n * Интерфейс для сигнала.\n * @template T\n * @interface Signal<T>\n */\nexport interface Signal<T> {\n /**\n * Значение сигнала.\n * @type {T}\n */\n value: T;\n\n /**\n * Подписка на изменение значения сигнала.\n * @function subscribe\n * @param {Function} cb - Коллбек функция для обработки изменения.\n * @returns {CleanupFn} - Функция для очистки подписки.\n */\n subscribe: (cb: (val: T) => void) => CleanupFn;\n}\n\n/**\n * Интерфейс для вычисляемого значения.\n * @template T\n * @interface Computed<T>\n */\nexport interface Computed<T> {\n /**\n * Только читаемое значение вычисляемого значения.\n * @type {T}\n */\n readonly value: T;\n\n /**\n * Подписка на изменение вычисляемого значения.\n * @function subscribe\n * @param {Function} cb - Коллбек функция для обработки изменения.\n * @returns {CleanupFn} - Функция для очистки подписки.\n */\n subscribe: (cb: (val: T) => void) => CleanupFn;\n}\n\n/**\n * Интерфейс для ресурса.\n * @template T\n * @interface Resource<T>\n */\nexport interface Resource<T> extends ResourceState<T> {\n /**\n * Перезагрузка ресурса.\n * @function refetch\n * @returns {void}\n */\n refetch: () => void;\n\n /**\n * Подписка на изменение состояния ресурса.\n * @function subscribe\n * @param {Function} cb - Коллбек функция для обработки изменения.\n * @returns {CleanupFn} - Функция для очистки подписки.\n */\n subscribe: (cb: (val: ResourceState<T>) => void) => CleanupFn;\n\n /**\n * Только читаемое состояние ресурса.\n * @type {ResourceState<T>}\n */\n readonly value: ResourceState<T>;\n}\n\n/**\n * Интерфейс для опций сигнала.\n * @template T\n * @interface SignalOptions<T>\n */\nexport interface SignalOptions<T> {\n /**\n * Имя сигнала.\n * @type {string}\n */\n name?: string;\n\n /**\n * Валидатор значения сигнала.\n * @function validate\n * @param {T} val - Значение для валидации.\n * @returns {boolean | string} - Результат валидации.\n */\n validate?: (val: T) => boolean | string;\n}\n\n// --- ЯДРО ---\n/**\n * Класс реактивного движка.\n */\nexport class ReactiveEngine {\n private activeEffect: IEffect | null = null;\n private isBatching = false;\n private pendingEffects = new Set<IEffect>();\n private services = new Map<Token<any>, any>();\n private factories = new Map<Token<any>, Factory<any>>();\n private proxyCache = new WeakMap<object, any>();\n private allEffects = new Set<IEffect>();\n\n /**\n * Коллбек для уведомления о изменении сигнала.\n * @type {Function}\n */\n public onSignalChange?: (name: string, next: any, prev: any) => void;\n\n private reactAdapters = {\n useState: useState as any,\n useEffect: useEffect as any,\n };\n\n /**\n * DI: Регистрация зависимости.\n * @template T\n * @function provide\n * @param {Token<T>} token - Токен для зависимости.\n * @param {T | Factory<T>} valueOrFactory - Значение или фабрика для создания сервиса.\n * @returns {void}\n */\n public provide<T>(token: Token<T>, valueOrFactory: T | Factory<T>): void {\n if (typeof valueOrFactory === 'function' && !valueOrFactory.prototype) {\n this.factories.set(token, valueOrFactory as Factory<T>);\n } else {\n this.services.set(token, valueOrFactory);\n }\n }\n\n /**\n * DI: Инъекция; Получение сервиса по токену.\n * @template T\n * @function inject\n * @param {Token<T>} token - Токен для зависимости.\n * @returns {T} - Сервис.\n */\n public inject<T>(token: Token<T>): T {\n if (!token) {\n throw new Error(`[DI Error]: Вы пытаетесь внедрить пустой токен (undefined/null). Проверьте импорты.`);\n }\n\n if (this.services.has(token)) return this.services.get(token);\n\n // NOTE: (new way) Если это класс, но мы забыли его унаследовать от BaseService или передать engine\n try {\n // NOTE: Логика создания инстанса\n const factory = this.factories.get(token);\n if (factory) {\n const instance = factory(this);\n this.services.set(token, instance);\n return instance;\n }\n if (typeof token === 'function' && token.prototype) {\n const instance = new (token as { new(eng: ReactiveEngine): T })(this);\n this.services.set(token, instance);\n return instance;\n }\n throw new Error(`Service not found: ${String(token)}`);\n } catch (e) {\n throw new Error(`[DI Error]: Не удалось создать сервис ${String(token)}. Ошибка: ${(e as Error)?.message || 'No e?.message'}`);\n }\n }\n\n /**\n * Создание сигнала.\n * @template T\n * @function signal\n * @param {T} initialValue - Начальное значение сигнала.\n * @param {string | SignalOptions<T>} [optionsOrName] - Имя или опции сигнала.\n * @returns {Signal<T>} - Сигнал.\n */\n public signal<T>(initialValue: T, optionsOrName?: string | SignalOptions<T>): Signal<T> {\n const engine = this;\n let val = initialValue;\n const subscribers = new Set<IEffect>();\n\n // Парсим аргументы для обратной совместимости\n const options = typeof optionsOrName === 'string'\n ? { name: optionsOrName }\n : optionsOrName || {};\n\n const name = options.name || 'unnamed_signal';\n\n return {\n get value(): T {\n if (engine.activeEffect) subscribers.add(engine.activeEffect);\n return val;\n },\n set value(newValue: T) {\n if (val === newValue) return;\n\n // ВАЛИДАЦИЯ В RUNTIME\n if (options.validate) {\n const result = options.validate(newValue);\n if (result === false || typeof result === 'string') {\n const errorMsg = typeof result === 'string'\n ? result\n : `[Validation Error]: Некорректное значение для сигнала \"${name}\"`;\n\n console.error(`%c${errorMsg}`, \"color: orange; font-weight: bold;\", {\n received: newValue,\n current: val\n });\n return; // Прерываем обновление, если данные не валидны\n }\n }\n\n const old = val;\n val = newValue;\n engine.onSignalChange?.(name, newValue, old);\n subscribers.forEach(e =>\n engine.isBatching ? engine.pendingEffects.add(e) : e.run()\n );\n },\n subscribe(cb) {\n return engine.effect(() => cb(this.value));\n }\n };\n }\n\n /**\n * Создание эффекта.\n * @function effect\n * @param {EffectFn} fn - Функция эффекта.\n * @returns {CleanupFn} - Функция для очистки эффекта.\n */\n public effect(fn: EffectFn): CleanupFn {\n const engine = this;\n const effectObj: IEffect = {\n cleanups: new Set(),\n run() {\n this.cleanups.forEach(c => c());\n this.cleanups.clear();\n const prev = engine.activeEffect;\n engine.activeEffect = this;\n engine.safeRun(this, () => {\n const cleanup = fn();\n if (typeof cleanup === 'function') this.cleanups.add(cleanup);\n });\n engine.activeEffect = prev;\n }\n };\n this.allEffects.add(effectObj); // Регистрируем эффект в глобальном списке\n effectObj.run();\n return () => {\n effectObj.cleanups.forEach(c => c());\n engine.pendingEffects.delete(effectObj);\n };\n }\n\n /**\n * Создание вычисляемого значения.\n * @template T\n * @function computed\n * @param {Function} fn - Функция для вычисления значения.\n * @param {string} [signalName] - Имя сигнала.\n * @returns {Computed<T>} - Вычисляемое значение.\n */\n public computed<T>(fn: () => T, signalName?: string): Computed<T> {\n const sig = this.signal<T>(undefined as any, signalName || 'unnamed_computed');\n this.effect(() => {\n const newValue = fn();\n // Сигнал сам внутри себя проверит (sig.value === newValue)\n // благодаря правке в пункте №1\n sig.value = newValue;\n });\n return {\n get value() { return sig.value; },\n // Подписываемся на внутренний сигнал\n subscribe: (cb) => sig.subscribe(cb)\n };\n }\n\n /**\n * Создание реактивного объекта (Proxy).\n * @template T\n * @function reactive\n * @param {T} target - Целевой объект для проксирования.\n * @param {string} [name] - Имя проксируемого объекта.\n * @returns {T} - Реактивный объект.\n */\n public reactive<T extends object>(target: T, name: string = 'reactive'): T {\n if (this.proxyCache.has(target)) return this.proxyCache.get(target);\n const engine = this;\n const propsSubscribers = new Map<string | symbol, Set<IEffect>>();\n\n const proxy = new Proxy(target, {\n get(obj, prop, receiver) {\n if (engine.activeEffect) {\n if (!propsSubscribers.has(prop)) propsSubscribers.set(prop, new Set());\n propsSubscribers.get(prop)!.add(engine.activeEffect);\n }\n const value = Reflect.get(obj, prop, receiver);\n return (value !== null && typeof value === 'object')\n ? engine.reactive(value, `${name}.${String(prop)}`)\n : value;\n },\n set(obj, prop, value, receiver) {\n const old = Reflect.get(obj, prop, receiver);\n // ПРОВЕРКА НА ИДЕНТИЧНОСТЬ\n if (old === value) return true;\n\n if (old !== value) {\n Reflect.set(obj, prop, value, receiver);\n engine.onSignalChange?.(`${name}.${String(prop)}`, value, old);\n propsSubscribers.get(prop)?.forEach(e =>\n engine.isBatching ? engine.pendingEffects.add(e) : e.run()\n );\n }\n return true;\n }\n });\n this.proxyCache.set(target, proxy);\n return proxy;\n }\n\n /**\n * Группировка изменений.\n * @function batch\n * @param {Function} fn - Функция для выполнения в группе.\n * @returns {void}\n */\n public batch(fn: () => void): void {\n this.isBatching = true;\n try { fn(); } finally {\n queueMicrotask(() => {\n const effects = Array.from(this.pendingEffects);\n this.pendingEffects.clear();\n this.isBatching = false;\n effects.forEach(e => e.run());\n });\n }\n }\n\n /**\n * Создание асинхронного ресурса.\n * @template T\n * @function resource\n * @param {Function} fetcher - Функция для загрузки данных.\n * @param {{ value: S }} [source] - Источник данных.\n * @param {string} [signalName] - Имя сигнала ресурса.\n * @returns {Resource<T>} - Асинхронный ресурс.\n */\n public resource<T, S = void>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n source?: { value: S },\n signalName?: string,\n ): Resource<T> {\n const state = this.signal<ResourceState<T>>({ data: null, loading: true, error: null }, signalName || 'unnamed_resource');\n let controller: AbortController | null = null;\n\n const load = async (sValue: S) => {\n controller?.abort();\n controller = new AbortController();\n\n // Читаем текущие данные без создания зависимости!\n const currentData = this.untrack(() => state.value.data);\n\n // Теперь обновление состояния не вызовет перезапуск эффекта\n state.value = { data: currentData, loading: true, error: null };\n\n try {\n const data = await fetcher(sValue, controller.signal);\n if (!controller.signal.aborted) {\n state.value = { data, loading: false, error: null };\n }\n } catch (e: any) {\n if (e.name !== 'AbortError') {\n state.value = { data: null, loading: false, error: e };\n }\n }\n };\n\n // Чтобы эффект не подписывался на state,\n // нам нужно изолировать чтение source от чтения state\n this.effect(() => {\n // Читаем ТОЛЬКО source. Это наша единственная зависимость.\n const sValue = source ? source.value : undefined as S;\n\n // Вызываем load. Внутри load чтение state.value\n // не должно регистрироваться как зависимость этого эффекта.\n load(sValue);\n\n return () => controller?.abort();\n });\n\n return {\n get data() { return state.value.data; },\n get loading() { return state.value.loading; },\n get error() { return state.value.error; },\n get value() { return state.value; },\n refetch: () => load(source ? source.value : undefined as S),\n subscribe: (cb) => state.subscribe(cb)\n };\n }\n\n /**\n * Выполняет функцию без отслеживания зависимостей.\n * @template T\n * @function untrack\n * @param {Function} fn - Функция для выполнения.\n * @returns {T} - Результат выполнения функции.\n */\n public untrack<T>(fn: () => T): T {\n const prev = this.activeEffect;\n this.activeEffect = null; // Временно \"забываем\" про активный эффект\n try {\n return fn();\n } finally {\n this.activeEffect = prev; // Возвращаем эффект на место\n }\n }\n\n /**\n * Установка адаптеров React.\n * @function setReactAdapters\n * @param {Function} useState - Функция useState из React.\n * @param {Function} useEffect - Функция useEffect из React.\n * @returns {void}\n */\n public setReactAdapters(useState: any, useEffect: any) {\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 */\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 /**\n * Безопасное выполнение функции.\n * @private\n * @function safeRun\n * @param {IEffect} effect - Эффект.\n * @param {Function} fn - Функция для выполнения.\n * @returns {void}\n */\n private safeRun(effect: IEffect, fn: () => void) {\n try {\n fn();\n } catch (error) {\n console.error(\n `%c[Reactive Error] %cОшибка в эффекте/computed:`,\n \"color: white; background: red; padding: 2px 4px; border-radius: 3px;\",\n \"font-weight: bold;\",\n error\n );\n // Здесь можно отправить ошибку в Sentry или другой сервис мониторинга\n }\n }\n}\n","import { ReactiveEngine } from './core';\n\n// export abstract class BaseService {\n// constructor(protected engine: ReactiveEngine) {\n// // Вызываем метод инициализации, который можно переопределить в наследниках\n// this.onInit();\n// }\n\n// // NOTE: Жизненный цикл: вызывается сразу после создания сервиса.\n// // Удобно для инициализации сигналов, которые зависят от engine.\n// protected onInit(): void {}\n\n// // NOTE: Полезные хелперы для сокращения кода\n// protected createSignal<T>(val: T, name?: string) {\n// return this.engine.signal(val, name);\n// }\n\n// protected createComputed<T>(fn: () => T) {\n// return this.engine.computed(fn);\n// }\n// }\n\nexport abstract class BaseREService {\n constructor(protected engine: ReactiveEngine) { } // Просто сохраняем engine\n\n protected createNumericSignal(val: number, name: string) {\n return this.engine.signal(val, {\n name,\n validate: (v) => typeof v === 'number' && !isNaN(v)\n });\n }\n}\n","import { ReactiveEngine } from './core';\n\nexport abstract class BaseREServiceEnhanced {\n constructor(protected engine: ReactiveEngine) {\n setTimeout(() => this.onInit(), 0)\n }\n\n protected onInit(): void { }\n}\n"],"names":["ReactiveEngine","useState","useEffect","token","valueOrFactory","factory","instance","e","initialValue","optionsOrName","engine","val","subscribers","options","name","newValue","result","errorMsg","old","cb","fn","effectObj","c","prev","cleanup","signalName","sig","target","propsSubscribers","proxy","obj","prop","receiver","value","effects","fetcher","source","state","controller","load","sValue","currentData","data","item","setVal","effect","error","BaseREService","v","BaseREServiceEnhanced"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -12,8 +12,23 @@ export declare abstract class BaseREServiceEnhanced {
12
12
 
13
13
  export declare type CleanupFn = () => void;
14
14
 
15
+ /**
16
+ * Интерфейс для вычисляемого значения.
17
+ * @template T
18
+ * @interface Computed<T>
19
+ */
15
20
  export declare interface Computed<T> {
21
+ /**
22
+ * Только читаемое значение вычисляемого значения.
23
+ * @type {T}
24
+ */
16
25
  readonly value: T;
26
+ /**
27
+ * Подписка на изменение вычисляемого значения.
28
+ * @function subscribe
29
+ * @param {Function} cb - Коллбек функция для обработки изменения.
30
+ * @returns {CleanupFn} - Функция для очистки подписки.
31
+ */
17
32
  subscribe: (cb: (val: T) => void) => CleanupFn;
18
33
  }
19
34
 
@@ -26,6 +41,44 @@ export declare interface IEffect {
26
41
  cleanups: Set<CleanupFn>;
27
42
  }
28
43
 
44
+ /**
45
+ * Функция для очистки эффекта.
46
+ * @typedef {() => void} CleanupFn
47
+ */
48
+ /**
49
+ * Функция эффекта, которая может возвращать функцию очистки.
50
+ * @typedef {() => CleanupFn | void} EffectFn
51
+ */
52
+ /**
53
+ * Токен для зависимости.
54
+ * @typedef {string | symbol | { new(engine: ReactiveEngine, ...args: any[]): T }} Token<T>
55
+ */
56
+ /**
57
+ * Фабрика для создания сервиса.
58
+ * @template T
59
+ * @typedef {(engine: ReactiveEngine) => T} Factory<T>
60
+ */
61
+ /**
62
+ * Интерфейс для эффекта.
63
+ * @interface IEffect
64
+ */
65
+ export declare interface IEffect {
66
+ /**
67
+ * Запуск эффекта.
68
+ * @function run
69
+ * @returns {void}
70
+ */
71
+ run: () => void;
72
+ /**
73
+ * Коллекция функций очистки.
74
+ * @type {Set<CleanupFn>}
75
+ */
76
+ cleanups: Set<CleanupFn>;
77
+ }
78
+
79
+ /**
80
+ * Класс реактивного движка.
81
+ */
29
82
  export declare class ReactiveEngine {
30
83
  private activeEffect;
31
84
  private isBatching;
@@ -34,42 +87,143 @@ export declare class ReactiveEngine {
34
87
  private factories;
35
88
  private proxyCache;
36
89
  private allEffects;
90
+ /**
91
+ * Коллбек для уведомления о изменении сигнала.
92
+ * @type {Function}
93
+ */
37
94
  onSignalChange?: (name: string, next: any, prev: any) => void;
38
95
  private reactAdapters;
39
- /** DI: Регистрация */
96
+ /**
97
+ * DI: Регистрация зависимости.
98
+ * @template T
99
+ * @function provide
100
+ * @param {Token<T>} token - Токен для зависимости.
101
+ * @param {T | Factory<T>} valueOrFactory - Значение или фабрика для создания сервиса.
102
+ * @returns {void}
103
+ */
40
104
  provide<T>(token: Token<T>, valueOrFactory: T | Factory<T>): void;
41
- /** DI: Инъекция */
105
+ /**
106
+ * DI: Инъекция; Получение сервиса по токену.
107
+ * @template T
108
+ * @function inject
109
+ * @param {Token<T>} token - Токен для зависимости.
110
+ * @returns {T} - Сервис.
111
+ */
42
112
  inject<T>(token: Token<T>): T;
43
- /** Создание Сигнала */
113
+ /**
114
+ * Создание сигнала.
115
+ * @template T
116
+ * @function signal
117
+ * @param {T} initialValue - Начальное значение сигнала.
118
+ * @param {string | SignalOptions<T>} [optionsOrName] - Имя или опции сигнала.
119
+ * @returns {Signal<T>} - Сигнал.
120
+ */
44
121
  signal<T>(initialValue: T, optionsOrName?: string | SignalOptions<T>): Signal<T>;
45
- /** Эффект */
122
+ /**
123
+ * Создание эффекта.
124
+ * @function effect
125
+ * @param {EffectFn} fn - Функция эффекта.
126
+ * @returns {CleanupFn} - Функция для очистки эффекта.
127
+ */
46
128
  effect(fn: EffectFn): CleanupFn;
47
- /** Вычисляемое значение */
129
+ /**
130
+ * Создание вычисляемого значения.
131
+ * @template T
132
+ * @function computed
133
+ * @param {Function} fn - Функция для вычисления значения.
134
+ * @param {string} [signalName] - Имя сигнала.
135
+ * @returns {Computed<T>} - Вычисляемое значение.
136
+ */
48
137
  computed<T>(fn: () => T, signalName?: string): Computed<T>;
49
- /** Реактивный объект (Proxy) */
138
+ /**
139
+ * Создание реактивного объекта (Proxy).
140
+ * @template T
141
+ * @function reactive
142
+ * @param {T} target - Целевой объект для проксирования.
143
+ * @param {string} [name] - Имя проксируемого объекта.
144
+ * @returns {T} - Реактивный объект.
145
+ */
50
146
  reactive<T extends object>(target: T, name?: string): T;
51
- /** Группировка изменений */
147
+ /**
148
+ * Группировка изменений.
149
+ * @function batch
150
+ * @param {Function} fn - Функция для выполнения в группе.
151
+ * @returns {void}
152
+ */
52
153
  batch(fn: () => void): void;
53
- /** Асинхронный ресурс */
154
+ /**
155
+ * Создание асинхронного ресурса.
156
+ * @template T
157
+ * @function resource
158
+ * @param {Function} fetcher - Функция для загрузки данных.
159
+ * @param {{ value: S }} [source] - Источник данных.
160
+ * @param {string} [signalName] - Имя сигнала ресурса.
161
+ * @returns {Resource<T>} - Асинхронный ресурс.
162
+ */
54
163
  resource<T, S = void>(fetcher: (source: S, signal: AbortSignal) => Promise<T>, source?: {
55
164
  value: S;
56
165
  }, signalName?: string): Resource<T>;
57
166
  /**
58
- * Выполняет функцию без отслеживания зависимостей
167
+ * Выполняет функцию без отслеживания зависимостей.
168
+ * @template T
169
+ * @function untrack
170
+ * @param {Function} fn - Функция для выполнения.
171
+ * @returns {T} - Результат выполнения функции.
59
172
  */
60
173
  untrack<T>(fn: () => T): T;
61
- /** React Хук */
174
+ /**
175
+ * Установка адаптеров React.
176
+ * @function setReactAdapters
177
+ * @param {Function} useState - Функция useState из React.
178
+ * @param {Function} useEffect - Функция useEffect из React.
179
+ * @returns {void}
180
+ */
62
181
  setReactAdapters(useState: any, useEffect: any): void;
182
+ /**
183
+ * Использование реактивного значения в React компоненте.
184
+ * @template T
185
+ * @function use
186
+ * @param {{ value: T; subscribe: (cb: (v: T) => void) => CleanupFn }} item - Реактивный объект.
187
+ * @returns {T} - Значение реактивного объекта.
188
+ */
63
189
  use<T>(item: {
64
190
  value: T;
65
191
  subscribe: (cb: (v: T) => void) => CleanupFn;
66
192
  }): T;
193
+ /**
194
+ * Безопасное выполнение функции.
195
+ * @private
196
+ * @function safeRun
197
+ * @param {IEffect} effect - Эффект.
198
+ * @param {Function} fn - Функция для выполнения.
199
+ * @returns {void}
200
+ */
67
201
  private safeRun;
68
202
  }
69
203
 
204
+ /**
205
+ * Интерфейс для ресурса.
206
+ * @template T
207
+ * @interface Resource<T>
208
+ */
70
209
  export declare interface Resource<T> extends ResourceState<T> {
210
+ /**
211
+ * Перезагрузка ресурса.
212
+ * @function refetch
213
+ * @returns {void}
214
+ */
71
215
  refetch: () => void;
216
+ /**
217
+ * Подписка на изменение состояния ресурса.
218
+ * @function subscribe
219
+ * @param {Function} cb - Коллбек функция для обработки изменения.
220
+ * @returns {CleanupFn} - Функция для очистки подписки.
221
+ */
72
222
  subscribe: (cb: (val: ResourceState<T>) => void) => CleanupFn;
223
+ /**
224
+ * Только читаемое состояние ресурса.
225
+ * @type {ResourceState<T>}
226
+ */
73
227
  readonly value: ResourceState<T>;
74
228
  }
75
229
 
@@ -79,13 +233,66 @@ export declare interface ResourceState<T> {
79
233
  error: any | null;
80
234
  }
81
235
 
236
+ /**
237
+ * Интерфейс для состояния ресурса.
238
+ * @template T
239
+ * @interface ResourceState<T>
240
+ */
241
+ export declare interface ResourceState<T> {
242
+ /**
243
+ * Данные ресурса.
244
+ * @type {T | null}
245
+ */
246
+ data: T | null;
247
+ /**
248
+ * Состояние загрузки ресурса.
249
+ * @type {boolean}
250
+ */
251
+ loading: boolean;
252
+ /**
253
+ * Ошибка ресурса.
254
+ * @type {any | null}
255
+ */
256
+ error: any | null;
257
+ }
258
+
259
+ /**
260
+ * Интерфейс для сигнала.
261
+ * @template T
262
+ * @interface Signal<T>
263
+ */
82
264
  export declare interface Signal<T> {
265
+ /**
266
+ * Значение сигнала.
267
+ * @type {T}
268
+ */
83
269
  value: T;
270
+ /**
271
+ * Подписка на изменение значения сигнала.
272
+ * @function subscribe
273
+ * @param {Function} cb - Коллбек функция для обработки изменения.
274
+ * @returns {CleanupFn} - Функция для очистки подписки.
275
+ */
84
276
  subscribe: (cb: (val: T) => void) => CleanupFn;
85
277
  }
86
278
 
87
- declare interface SignalOptions<T> {
279
+ /**
280
+ * Интерфейс для опций сигнала.
281
+ * @template T
282
+ * @interface SignalOptions<T>
283
+ */
284
+ export declare interface SignalOptions<T> {
285
+ /**
286
+ * Имя сигнала.
287
+ * @type {string}
288
+ */
88
289
  name?: string;
290
+ /**
291
+ * Валидатор значения сигнала.
292
+ * @function validate
293
+ * @param {T} val - Значение для валидации.
294
+ * @returns {boolean | string} - Результат валидации.
295
+ */
89
296
  validate?: (val: T) => boolean | string;
90
297
  }
91
298
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.iife.js","sources":["../src/core.ts","../src/BaseREService.ts","../src/BaseREServiceEnhanced.ts"],"sourcesContent":["import { useState, useEffect } from 'react';\n\n// --- ТИПЫ ---\nexport type CleanupFn = () => void;\nexport type EffectFn = () => CleanupFn | void;\nexport type Token<T> = string | symbol | { new(engine: ReactiveEngine, ...args: any[]): T };\nexport type Factory<T> = (engine: ReactiveEngine) => T;\n\nexport interface IEffect {\n run: () => void;\n cleanups: Set<CleanupFn>;\n}\n\nexport interface ResourceState<T> {\n data: T | null;\n loading: boolean;\n error: any | null;\n}\n\nexport interface Signal<T> {\n value: T;\n subscribe: (cb: (val: T) => void) => CleanupFn;\n}\n\nexport interface Computed<T> {\n readonly value: T;\n subscribe: (cb: (val: T) => void) => CleanupFn;\n}\n\nexport interface Resource<T> extends ResourceState<T> {\n refetch: () => void;\n subscribe: (cb: (val: ResourceState<T>) => void) => CleanupFn;\n readonly value: ResourceState<T>;\n}\n\ninterface SignalOptions<T> {\n name?: string;\n validate?: (val: T) => boolean | string;\n}\n\n// --- ЯДРО ---\nexport class ReactiveEngine {\n private activeEffect: IEffect | null = null;\n private isBatching = false;\n private pendingEffects = new Set<IEffect>();\n private services = new Map<Token<any>, any>();\n private factories = new Map<Token<any>, Factory<any>>();\n private proxyCache = new WeakMap<object, any>();\n private allEffects = new Set<IEffect>();\n\n public onSignalChange?: (name: string, next: any, prev: any) => void;\n\n private reactAdapters = {\n useState: useState as any,\n useEffect: useEffect as any,\n };\n\n /** DI: Регистрация */\n public provide<T>(token: Token<T>, valueOrFactory: T | Factory<T>): void {\n if (typeof valueOrFactory === 'function' && !valueOrFactory.prototype) {\n this.factories.set(token, valueOrFactory as Factory<T>);\n } else {\n this.services.set(token, valueOrFactory);\n }\n }\n\n /** DI: Инъекция */\n public inject<T>(token: Token<T>): T {\n if (!token) {\n throw new Error(`[DI Error]: Вы пытаетесь внедрить пустой токен (undefined/null). Проверьте импорты.`);\n }\n\n if (this.services.has(token)) return this.services.get(token);\n\n // NOTE: (new way) Если это класс, но мы забыли его унаследовать от BaseService или передать engine\n try {\n // NOTE: Логика создания инстанса\n const factory = this.factories.get(token);\n if (factory) {\n const instance = factory(this);\n this.services.set(token, instance);\n return instance;\n }\n if (typeof token === 'function' && token.prototype) {\n const instance = new (token as { new(eng: ReactiveEngine): T })(this);\n this.services.set(token, instance);\n return instance;\n }\n throw new Error(`Service not found: ${String(token)}`);\n } catch (e) {\n throw new Error(`[DI Error]: Не удалось создать сервис ${String(token)}. Ошибка: ${(e as Error)?.message || 'No e?.message'}`);\n }\n }\n\n /** Создание Сигнала */\n public signal<T>(initialValue: T, optionsOrName?: string | SignalOptions<T>): Signal<T> {\n const engine = this;\n let val = initialValue;\n const subscribers = new Set<IEffect>();\n\n // Парсим аргументы для обратной совместимости\n const options = typeof optionsOrName === 'string'\n ? { name: optionsOrName }\n : optionsOrName || {};\n\n const name = options.name || 'unnamed_signal';\n\n return {\n get value(): T {\n if (engine.activeEffect) subscribers.add(engine.activeEffect);\n return val;\n },\n set value(newValue: T) {\n if (val === newValue) return;\n\n // ВАЛИДАЦИЯ В RUNTIME\n if (options.validate) {\n const result = options.validate(newValue);\n if (result === false || typeof result === 'string') {\n const errorMsg = typeof result === 'string'\n ? result\n : `[Validation Error]: Некорректное значение для сигнала \"${name}\"`;\n\n console.error(`%c${errorMsg}`, \"color: orange; font-weight: bold;\", {\n received: newValue,\n current: val\n });\n return; // Прерываем обновление, если данные не валидны\n }\n }\n\n const old = val;\n val = newValue;\n engine.onSignalChange?.(name, newValue, old);\n subscribers.forEach(e =>\n engine.isBatching ? engine.pendingEffects.add(e) : e.run()\n );\n },\n subscribe(cb) {\n return engine.effect(() => cb(this.value));\n }\n };\n }\n\n /** Эффект */\n public effect(fn: EffectFn): CleanupFn {\n const engine = this;\n const effectObj: IEffect = {\n cleanups: new Set(),\n run() {\n this.cleanups.forEach(c => c());\n this.cleanups.clear();\n const prev = engine.activeEffect;\n engine.activeEffect = this;\n engine.safeRun(this, () => {\n const cleanup = fn();\n if (typeof cleanup === 'function') this.cleanups.add(cleanup);\n });\n engine.activeEffect = prev;\n }\n };\n this.allEffects.add(effectObj); // Регистрируем эффект в глобальном списке\n effectObj.run();\n return () => {\n effectObj.cleanups.forEach(c => c());\n engine.pendingEffects.delete(effectObj);\n };\n }\n\n /** Вычисляемое значение */\n public computed<T>(fn: () => T, signalName?: string): Computed<T> {\n const sig = this.signal<T>(undefined as any, signalName || 'unnamed_computed');\n this.effect(() => {\n const newValue = fn();\n // Сигнал сам внутри себя проверит (sig.value === newValue)\n // благодаря правке в пункте №1\n sig.value = newValue;\n });\n return {\n get value() { return sig.value; },\n // Подписываемся на внутренний сигнал\n subscribe: (cb) => sig.subscribe(cb)\n };\n }\n\n /** Реактивный объект (Proxy) */\n public reactive<T extends object>(target: T, name: string = 'reactive'): T {\n if (this.proxyCache.has(target)) return this.proxyCache.get(target);\n const engine = this;\n const propsSubscribers = new Map<string | symbol, Set<IEffect>>();\n\n const proxy = new Proxy(target, {\n get(obj, prop, receiver) {\n if (engine.activeEffect) {\n if (!propsSubscribers.has(prop)) propsSubscribers.set(prop, new Set());\n propsSubscribers.get(prop)!.add(engine.activeEffect);\n }\n const value = Reflect.get(obj, prop, receiver);\n return (value !== null && typeof value === 'object')\n ? engine.reactive(value, `${name}.${String(prop)}`)\n : value;\n },\n set(obj, prop, value, receiver) {\n const old = Reflect.get(obj, prop, receiver);\n // ПРОВЕРКА НА ИДЕНТИЧНОСТЬ\n if (old === value) return true;\n\n if (old !== value) {\n Reflect.set(obj, prop, value, receiver);\n engine.onSignalChange?.(`${name}.${String(prop)}`, value, old);\n propsSubscribers.get(prop)?.forEach(e =>\n engine.isBatching ? engine.pendingEffects.add(e) : e.run()\n );\n }\n return true;\n }\n });\n this.proxyCache.set(target, proxy);\n return proxy;\n }\n\n /** Группировка изменений */\n public batch(fn: () => void): void {\n this.isBatching = true;\n try { fn(); } finally {\n queueMicrotask(() => {\n const effects = Array.from(this.pendingEffects);\n this.pendingEffects.clear();\n this.isBatching = false;\n effects.forEach(e => e.run());\n });\n }\n }\n\n /** Асинхронный ресурс */\n public resource<T, S = void>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n source?: { value: S },\n signalName?: string,\n ): Resource<T> {\n const state = this.signal<ResourceState<T>>({ data: null, loading: true, error: null }, signalName || 'unnamed_resource');\n let controller: AbortController | null = null;\n\n const load = async (sValue: S) => {\n controller?.abort();\n controller = new AbortController();\n\n // Читаем текущие данные без создания зависимости!\n const currentData = this.untrack(() => state.value.data);\n\n // Теперь обновление состояния не вызовет перезапуск эффекта\n state.value = { data: currentData, loading: true, error: null };\n\n try {\n const data = await fetcher(sValue, controller.signal);\n if (!controller.signal.aborted) {\n state.value = { data, loading: false, error: null };\n }\n } catch (e: any) {\n if (e.name !== 'AbortError') {\n state.value = { data: null, loading: false, error: e };\n }\n }\n };\n\n // Чтобы эффект не подписывался на state,\n // нам нужно изолировать чтение source от чтения state\n this.effect(() => {\n // Читаем ТОЛЬКО source. Это наша единственная зависимость.\n const sValue = source ? source.value : undefined as S;\n\n // Вызываем load. Внутри load чтение state.value\n // не должно регистрироваться как зависимость этого эффекта.\n load(sValue);\n\n return () => controller?.abort();\n });\n\n return {\n get data() { return state.value.data; },\n get loading() { return state.value.loading; },\n get error() { return state.value.error; },\n get value() { return state.value; },\n refetch: () => load(source ? source.value : undefined as S),\n subscribe: (cb) => state.subscribe(cb)\n };\n }\n\n /**\n * Выполняет функцию без отслеживания зависимостей\n */\n public untrack<T>(fn: () => T): T {\n const prev = this.activeEffect;\n this.activeEffect = null; // Временно \"забываем\" про активный эффект\n try {\n return fn();\n } finally {\n this.activeEffect = prev; // Возвращаем эффект на место\n }\n }\n\n /** React Хук */\n public setReactAdapters(useState: any, useEffect: any) {\n this.reactAdapters = { useState, useEffect };\n }\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 private safeRun(effect: IEffect, fn: () => void) {\n try {\n fn();\n } catch (error) {\n console.error(\n `%c[Reactive Error] %cОшибка в эффекте/computed:`,\n \"color: white; background: red; padding: 2px 4px; border-radius: 3px;\",\n \"font-weight: bold;\",\n error\n );\n // Здесь можно отправить ошибку в Sentry или другой сервис мониторинга\n }\n }\n}\n","import { ReactiveEngine } from './core';\n\n// export abstract class BaseService {\n// constructor(protected engine: ReactiveEngine) {\n// // Вызываем метод инициализации, который можно переопределить в наследниках\n// this.onInit();\n// }\n\n// // NOTE: Жизненный цикл: вызывается сразу после создания сервиса.\n// // Удобно для инициализации сигналов, которые зависят от engine.\n// protected onInit(): void {}\n\n// // NOTE: Полезные хелперы для сокращения кода\n// protected createSignal<T>(val: T, name?: string) {\n// return this.engine.signal(val, name);\n// }\n\n// protected createComputed<T>(fn: () => T) {\n// return this.engine.computed(fn);\n// }\n// }\n\nexport abstract class BaseREService {\n constructor(protected engine: ReactiveEngine) { } // Просто сохраняем engine\n\n protected createNumericSignal(val: number, name: string) {\n return this.engine.signal(val, {\n name,\n validate: (v) => typeof v === 'number' && !isNaN(v)\n });\n }\n}\n","import { ReactiveEngine } from './core';\n\nexport abstract class BaseREServiceEnhanced {\n constructor(protected engine: ReactiveEngine) {\n setTimeout(() => this.onInit(), 0)\n }\n\n protected onInit(): void { }\n}\n"],"names":["ReactiveEngine","useState","useEffect","token","valueOrFactory","factory","instance","e","initialValue","optionsOrName","engine","val","subscribers","options","name","newValue","result","errorMsg","old","cb","fn","effectObj","c","prev","cleanup","signalName","sig","target","propsSubscribers","proxy","obj","prop","receiver","value","effects","fetcher","source","state","controller","load","sValue","currentData","data","item","setVal","effect","error","BaseREService","v","BaseREServiceEnhanced"],"mappings":"sCAyCO,MAAMA,CAAe,CAClB,aAA+B,KAC/B,WAAa,GACb,mBAAqB,IACrB,aAAe,IACf,cAAgB,IAChB,eAAiB,QACjB,eAAiB,IAElB,eAEC,cAAgB,CAAA,SACtBC,EAAAA,SAAA,UACAC,EAAAA,SAAA,EAIK,QAAWC,EAAiBC,EAAsC,CACnE,OAAOA,GAAmB,YAAc,CAACA,EAAe,UAC1D,KAAK,UAAU,IAAID,EAAOC,CAA4B,EAEtD,KAAK,SAAS,IAAID,EAAOC,CAAc,CAE3C,CAGO,OAAUD,EAAoB,CACnC,GAAI,CAACA,EACH,MAAM,IAAI,MAAM,qFAAqF,EAGvG,GAAI,KAAK,SAAS,IAAIA,CAAK,EAAG,OAAO,KAAK,SAAS,IAAIA,CAAK,EAG5D,GAAI,CAEF,MAAME,EAAU,KAAK,UAAU,IAAIF,CAAK,EACxC,GAAIE,EAAS,CACX,MAAMC,EAAWD,EAAQ,IAAI,EAC7B,YAAK,SAAS,IAAIF,EAAOG,CAAQ,EAC1BA,CACT,CACA,GAAI,OAAOH,GAAU,YAAcA,EAAM,UAAW,CAClD,MAAMG,EAAW,IAAKH,EAA0C,IAAI,EACpE,YAAK,SAAS,IAAIA,EAAOG,CAAQ,EAC1BA,CACT,CACA,MAAM,IAAI,MAAM,sBAAsB,OAAOH,CAAK,CAAC,EAAE,CACvD,OAASI,EAAG,CACV,MAAM,IAAI,MAAM,yCAAyC,OAAOJ,CAAK,CAAC,aAAcI,GAAa,SAAW,eAAe,EAAE,CAC/H,CACF,CAGO,OAAUC,EAAiBC,EAAsD,CACtF,MAAMC,EAAS,KACf,IAAIC,EAAMH,EACV,MAAMI,MAAkB,IAGlBC,EAAU,OAAOJ,GAAkB,SACrC,CAAE,KAAMA,CAAA,EACRA,GAAiB,CAAA,EAEfK,EAAOD,EAAQ,MAAQ,iBAE7B,MAAO,CACL,IAAI,OAAW,CACb,OAAIH,EAAO,cAAcE,EAAY,IAAIF,EAAO,YAAY,EACrDC,CACT,EACA,IAAI,MAAMI,EAAa,CACrB,GAAIJ,IAAQI,EAAU,OAGtB,GAAIF,EAAQ,SAAU,CACpB,MAAMG,EAASH,EAAQ,SAASE,CAAQ,EACxC,GAAIC,IAAW,IAAS,OAAOA,GAAW,SAAU,CAClD,MAAMC,EAAW,OAAOD,GAAW,SAC/BA,EACA,0DAA0DF,CAAI,IAElE,QAAQ,MAAM,KAAKG,CAAQ,GAAI,oCAAqC,CAClE,SAAUF,EACV,QAASJ,CAAA,CACV,EACD,MACF,CACF,CAEA,MAAMO,EAAMP,EACZA,EAAMI,EACNL,EAAO,iBAAiBI,EAAMC,EAAUG,CAAG,EAC3CN,EAAY,QAAQL,GAClBG,EAAO,WAAaA,EAAO,eAAe,IAAIH,CAAC,EAAIA,EAAE,IAAA,CAAI,CAE7D,EACA,UAAUY,EAAI,CACZ,OAAOT,EAAO,OAAO,IAAMS,EAAG,KAAK,KAAK,CAAC,CAC3C,CAAA,CAEJ,CAGO,OAAOC,EAAyB,CACrC,MAAMV,EAAS,KACTW,EAAqB,CACzB,aAAc,IACd,KAAM,CACJ,KAAK,SAAS,QAAQC,GAAKA,EAAA,CAAG,EAC9B,KAAK,SAAS,MAAA,EACd,MAAMC,EAAOb,EAAO,aACpBA,EAAO,aAAe,KACtBA,EAAO,QAAQ,KAAM,IAAM,CACzB,MAAMc,EAAUJ,EAAA,EACZ,OAAOI,GAAY,YAAY,KAAK,SAAS,IAAIA,CAAO,CAC9D,CAAC,EACDd,EAAO,aAAea,CACxB,CAAA,EAEF,YAAK,WAAW,IAAIF,CAAS,EAC7BA,EAAU,IAAA,EACH,IAAM,CACXA,EAAU,SAAS,QAAQC,GAAKA,EAAA,CAAG,EACnCZ,EAAO,eAAe,OAAOW,CAAS,CACxC,CACF,CAGO,SAAYD,EAAaK,EAAkC,CAChE,MAAMC,EAAM,KAAK,OAAU,OAAkBD,GAAc,kBAAkB,EAC7E,YAAK,OAAO,IAAM,CAChB,MAAMV,EAAWK,EAAA,EAGjBM,EAAI,MAAQX,CACd,CAAC,EACM,CACL,IAAI,OAAQ,CAAE,OAAOW,EAAI,KAAO,EAEhC,UAAYP,GAAOO,EAAI,UAAUP,CAAE,CAAA,CAEvC,CAGO,SAA2BQ,EAAWb,EAAe,WAAe,CACzE,GAAI,KAAK,WAAW,IAAIa,CAAM,EAAG,OAAO,KAAK,WAAW,IAAIA,CAAM,EAClE,MAAMjB,EAAS,KACTkB,MAAuB,IAEvBC,EAAQ,IAAI,MAAMF,EAAQ,CAC9B,IAAIG,EAAKC,EAAMC,EAAU,CACnBtB,EAAO,eACJkB,EAAiB,IAAIG,CAAI,KAAoB,IAAIA,EAAM,IAAI,GAAK,EACrEH,EAAiB,IAAIG,CAAI,EAAG,IAAIrB,EAAO,YAAY,GAErD,MAAMuB,EAAQ,QAAQ,IAAIH,EAAKC,EAAMC,CAAQ,EAC7C,OAAQC,IAAU,MAAQ,OAAOA,GAAU,SACvCvB,EAAO,SAASuB,EAAO,GAAGnB,CAAI,IAAI,OAAOiB,CAAI,CAAC,EAAE,EAChDE,CACN,EACA,IAAIH,EAAKC,EAAME,EAAOD,EAAU,CAC9B,MAAMd,EAAM,QAAQ,IAAIY,EAAKC,EAAMC,CAAQ,EAE3C,OAAId,IAAQe,GAERf,IAAQe,IACV,QAAQ,IAAIH,EAAKC,EAAME,EAAOD,CAAQ,EACtCtB,EAAO,iBAAiB,GAAGI,CAAI,IAAI,OAAOiB,CAAI,CAAC,GAAIE,EAAOf,CAAG,EAC7DU,EAAiB,IAAIG,CAAI,GAAG,QAAQxB,GAClCG,EAAO,WAAaA,EAAO,eAAe,IAAIH,CAAC,EAAIA,EAAE,IAAA,CAAI,GAGtD,EACT,CAAA,CACD,EACD,YAAK,WAAW,IAAIoB,EAAQE,CAAK,EAC1BA,CACT,CAGO,MAAMT,EAAsB,CACjC,KAAK,WAAa,GAClB,GAAI,CAAEA,EAAA,CAAM,QAAA,CACV,eAAe,IAAM,CACnB,MAAMc,EAAU,MAAM,KAAK,KAAK,cAAc,EAC9C,KAAK,eAAe,MAAA,EACpB,KAAK,WAAa,GAClBA,EAAQ,QAAQ3B,GAAKA,EAAE,IAAA,CAAK,CAC9B,CAAC,CACH,CACF,CAGO,SACL4B,EACAC,EACAX,EACa,CACb,MAAMY,EAAQ,KAAK,OAAyB,CAAE,KAAM,KAAM,QAAS,GAAM,MAAO,MAAQZ,GAAc,kBAAkB,EACxH,IAAIa,EAAqC,KAEzC,MAAMC,EAAO,MAAOC,GAAc,CAChCF,GAAY,MAAA,EACZA,EAAa,IAAI,gBAGjB,MAAMG,EAAc,KAAK,QAAQ,IAAMJ,EAAM,MAAM,IAAI,EAGvDA,EAAM,MAAQ,CAAE,KAAMI,EAAa,QAAS,GAAM,MAAO,IAAA,EAEzD,GAAI,CACF,MAAMC,EAAO,MAAMP,EAAQK,EAAQF,EAAW,MAAM,EAC/CA,EAAW,OAAO,UACrBD,EAAM,MAAQ,CAAE,KAAAK,EAAM,QAAS,GAAO,MAAO,IAAA,EAEjD,OAASnC,EAAQ,CACXA,EAAE,OAAS,eACb8B,EAAM,MAAQ,CAAE,KAAM,KAAM,QAAS,GAAO,MAAO9B,CAAA,EAEvD,CACF,EAIA,YAAK,OAAO,IAAM,CAEhB,MAAMiC,EAASJ,EAASA,EAAO,MAAQ,OAIvC,OAAAG,EAAKC,CAAM,EAEJ,IAAMF,GAAY,MAAA,CAC3B,CAAC,EAEM,CACL,IAAI,MAAO,CAAE,OAAOD,EAAM,MAAM,IAAM,EACtC,IAAI,SAAU,CAAE,OAAOA,EAAM,MAAM,OAAS,EAC5C,IAAI,OAAQ,CAAE,OAAOA,EAAM,MAAM,KAAO,EACxC,IAAI,OAAQ,CAAE,OAAOA,EAAM,KAAO,EAClC,QAAS,IAAME,EAAKH,EAASA,EAAO,MAAQ,MAAc,EAC1D,UAAYjB,GAAOkB,EAAM,UAAUlB,CAAE,CAAA,CAEzC,CAKO,QAAWC,EAAgB,CAChC,MAAMG,EAAO,KAAK,aAClB,KAAK,aAAe,KACpB,GAAI,CACF,OAAOH,EAAA,CACT,QAAA,CACE,KAAK,aAAeG,CACtB,CACF,CAGO,iBAAiBtB,EAAeC,EAAgB,CACrD,KAAK,cAAgB,CAAE,SAAAD,EAAU,UAAAC,CAAAA,CACnC,CAEO,IAAOyC,EAAqE,CACjF,GAAI,CAAC,KAAK,cACR,MAAM,IAAI,MAAM,sGAAsG,EAIxH,GAAI,CAACA,GAAQ,OAAOA,EAAK,WAAc,WAAY,CACjD,MAAM1B,EAAW;AAAA;AAAA;AAAA;AAAA,QAKjB,cAAQ,MAAMA,EAAU,CAAE,KAAA0B,CAAA,CAAM,EAC1B,IAAI,MAAM1B,CAAQ,CAC1B,CACA,KAAM,CAACN,EAAKiC,CAAM,EAAI,KAAK,cAAc,SAASD,EAAK,KAAK,EAE5D,YAAK,cAAc,UACjB,IACSA,EAAK,UAAUC,CAAM,EAE9B,CAACD,CAAI,CAAA,EAGAhC,CACT,CAEQ,QAAQkC,EAAiBzB,EAAgB,CAC/C,GAAI,CACFA,EAAA,CACF,OAAS0B,EAAO,CACd,QAAQ,MACN,kDACA,uEACA,qBACAA,CAAA,CAGJ,CACF,CACF,CCpUO,MAAeC,CAAc,CAClC,YAAsBrC,EAAwB,CAAxB,KAAA,OAAAA,CAA0B,CAA1B,OAEZ,oBAAoBC,EAAaG,EAAc,CACvD,OAAO,KAAK,OAAO,OAAOH,EAAK,CAC7B,KAAAG,EACA,SAAWkC,GAAM,OAAOA,GAAM,UAAY,CAAC,MAAMA,CAAC,CAAA,CACnD,CACH,CACF,CC7BO,MAAeC,CAAsB,CAC1C,YAAsBvC,EAAwB,CAAxB,KAAA,OAAAA,EACpB,WAAW,IAAM,KAAK,OAAA,EAAU,CAAC,CACnC,CAFsB,OAIZ,QAAe,CAAE,CAC7B"}
1
+ {"version":3,"file":"index.iife.js","sources":["../src/core.ts","../src/BaseREService.ts","../src/BaseREServiceEnhanced.ts"],"sourcesContent":["import { useState, useEffect } from 'react';\n\n// --- ТИПЫ ---\nexport type CleanupFn = () => void;\nexport type EffectFn = () => CleanupFn | void;\nexport type Token<T> = string | symbol | { new(engine: ReactiveEngine, ...args: any[]): T };\nexport type Factory<T> = (engine: ReactiveEngine) => T;\n\nexport interface IEffect {\n run: () => void;\n cleanups: Set<CleanupFn>;\n}\n\nexport interface ResourceState<T> {\n data: T | null;\n loading: boolean;\n error: any | null;\n}\n\n/**\n * Функция для очистки эффекта.\n * @typedef {() => void} CleanupFn\n */\n\n/**\n * Функция эффекта, которая может возвращать функцию очистки.\n * @typedef {() => CleanupFn | void} EffectFn\n */\n\n/**\n * Токен для зависимости.\n * @typedef {string | symbol | { new(engine: ReactiveEngine, ...args: any[]): T }} Token<T>\n */\n\n/**\n * Фабрика для создания сервиса.\n * @template T\n * @typedef {(engine: ReactiveEngine) => T} Factory<T>\n */\n\n/**\n * Интерфейс для эффекта.\n * @interface IEffect\n */\nexport interface IEffect {\n /**\n * Запуск эффекта.\n * @function run\n * @returns {void}\n */\n run: () => void;\n\n /**\n * Коллекция функций очистки.\n * @type {Set<CleanupFn>}\n */\n cleanups: Set<CleanupFn>;\n}\n\n/**\n * Интерфейс для состояния ресурса.\n * @template T\n * @interface ResourceState<T>\n */\nexport interface ResourceState<T> {\n /**\n * Данные ресурса.\n * @type {T | null}\n */\n data: T | null;\n\n /**\n * Состояние загрузки ресурса.\n * @type {boolean}\n */\n loading: boolean;\n\n /**\n * Ошибка ресурса.\n * @type {any | null}\n */\n error: any | null;\n}\n\n/**\n * Интерфейс для сигнала.\n * @template T\n * @interface Signal<T>\n */\nexport interface Signal<T> {\n /**\n * Значение сигнала.\n * @type {T}\n */\n value: T;\n\n /**\n * Подписка на изменение значения сигнала.\n * @function subscribe\n * @param {Function} cb - Коллбек функция для обработки изменения.\n * @returns {CleanupFn} - Функция для очистки подписки.\n */\n subscribe: (cb: (val: T) => void) => CleanupFn;\n}\n\n/**\n * Интерфейс для вычисляемого значения.\n * @template T\n * @interface Computed<T>\n */\nexport interface Computed<T> {\n /**\n * Только читаемое значение вычисляемого значения.\n * @type {T}\n */\n readonly value: T;\n\n /**\n * Подписка на изменение вычисляемого значения.\n * @function subscribe\n * @param {Function} cb - Коллбек функция для обработки изменения.\n * @returns {CleanupFn} - Функция для очистки подписки.\n */\n subscribe: (cb: (val: T) => void) => CleanupFn;\n}\n\n/**\n * Интерфейс для ресурса.\n * @template T\n * @interface Resource<T>\n */\nexport interface Resource<T> extends ResourceState<T> {\n /**\n * Перезагрузка ресурса.\n * @function refetch\n * @returns {void}\n */\n refetch: () => void;\n\n /**\n * Подписка на изменение состояния ресурса.\n * @function subscribe\n * @param {Function} cb - Коллбек функция для обработки изменения.\n * @returns {CleanupFn} - Функция для очистки подписки.\n */\n subscribe: (cb: (val: ResourceState<T>) => void) => CleanupFn;\n\n /**\n * Только читаемое состояние ресурса.\n * @type {ResourceState<T>}\n */\n readonly value: ResourceState<T>;\n}\n\n/**\n * Интерфейс для опций сигнала.\n * @template T\n * @interface SignalOptions<T>\n */\nexport interface SignalOptions<T> {\n /**\n * Имя сигнала.\n * @type {string}\n */\n name?: string;\n\n /**\n * Валидатор значения сигнала.\n * @function validate\n * @param {T} val - Значение для валидации.\n * @returns {boolean | string} - Результат валидации.\n */\n validate?: (val: T) => boolean | string;\n}\n\n// --- ЯДРО ---\n/**\n * Класс реактивного движка.\n */\nexport class ReactiveEngine {\n private activeEffect: IEffect | null = null;\n private isBatching = false;\n private pendingEffects = new Set<IEffect>();\n private services = new Map<Token<any>, any>();\n private factories = new Map<Token<any>, Factory<any>>();\n private proxyCache = new WeakMap<object, any>();\n private allEffects = new Set<IEffect>();\n\n /**\n * Коллбек для уведомления о изменении сигнала.\n * @type {Function}\n */\n public onSignalChange?: (name: string, next: any, prev: any) => void;\n\n private reactAdapters = {\n useState: useState as any,\n useEffect: useEffect as any,\n };\n\n /**\n * DI: Регистрация зависимости.\n * @template T\n * @function provide\n * @param {Token<T>} token - Токен для зависимости.\n * @param {T | Factory<T>} valueOrFactory - Значение или фабрика для создания сервиса.\n * @returns {void}\n */\n public provide<T>(token: Token<T>, valueOrFactory: T | Factory<T>): void {\n if (typeof valueOrFactory === 'function' && !valueOrFactory.prototype) {\n this.factories.set(token, valueOrFactory as Factory<T>);\n } else {\n this.services.set(token, valueOrFactory);\n }\n }\n\n /**\n * DI: Инъекция; Получение сервиса по токену.\n * @template T\n * @function inject\n * @param {Token<T>} token - Токен для зависимости.\n * @returns {T} - Сервис.\n */\n public inject<T>(token: Token<T>): T {\n if (!token) {\n throw new Error(`[DI Error]: Вы пытаетесь внедрить пустой токен (undefined/null). Проверьте импорты.`);\n }\n\n if (this.services.has(token)) return this.services.get(token);\n\n // NOTE: (new way) Если это класс, но мы забыли его унаследовать от BaseService или передать engine\n try {\n // NOTE: Логика создания инстанса\n const factory = this.factories.get(token);\n if (factory) {\n const instance = factory(this);\n this.services.set(token, instance);\n return instance;\n }\n if (typeof token === 'function' && token.prototype) {\n const instance = new (token as { new(eng: ReactiveEngine): T })(this);\n this.services.set(token, instance);\n return instance;\n }\n throw new Error(`Service not found: ${String(token)}`);\n } catch (e) {\n throw new Error(`[DI Error]: Не удалось создать сервис ${String(token)}. Ошибка: ${(e as Error)?.message || 'No e?.message'}`);\n }\n }\n\n /**\n * Создание сигнала.\n * @template T\n * @function signal\n * @param {T} initialValue - Начальное значение сигнала.\n * @param {string | SignalOptions<T>} [optionsOrName] - Имя или опции сигнала.\n * @returns {Signal<T>} - Сигнал.\n */\n public signal<T>(initialValue: T, optionsOrName?: string | SignalOptions<T>): Signal<T> {\n const engine = this;\n let val = initialValue;\n const subscribers = new Set<IEffect>();\n\n // Парсим аргументы для обратной совместимости\n const options = typeof optionsOrName === 'string'\n ? { name: optionsOrName }\n : optionsOrName || {};\n\n const name = options.name || 'unnamed_signal';\n\n return {\n get value(): T {\n if (engine.activeEffect) subscribers.add(engine.activeEffect);\n return val;\n },\n set value(newValue: T) {\n if (val === newValue) return;\n\n // ВАЛИДАЦИЯ В RUNTIME\n if (options.validate) {\n const result = options.validate(newValue);\n if (result === false || typeof result === 'string') {\n const errorMsg = typeof result === 'string'\n ? result\n : `[Validation Error]: Некорректное значение для сигнала \"${name}\"`;\n\n console.error(`%c${errorMsg}`, \"color: orange; font-weight: bold;\", {\n received: newValue,\n current: val\n });\n return; // Прерываем обновление, если данные не валидны\n }\n }\n\n const old = val;\n val = newValue;\n engine.onSignalChange?.(name, newValue, old);\n subscribers.forEach(e =>\n engine.isBatching ? engine.pendingEffects.add(e) : e.run()\n );\n },\n subscribe(cb) {\n return engine.effect(() => cb(this.value));\n }\n };\n }\n\n /**\n * Создание эффекта.\n * @function effect\n * @param {EffectFn} fn - Функция эффекта.\n * @returns {CleanupFn} - Функция для очистки эффекта.\n */\n public effect(fn: EffectFn): CleanupFn {\n const engine = this;\n const effectObj: IEffect = {\n cleanups: new Set(),\n run() {\n this.cleanups.forEach(c => c());\n this.cleanups.clear();\n const prev = engine.activeEffect;\n engine.activeEffect = this;\n engine.safeRun(this, () => {\n const cleanup = fn();\n if (typeof cleanup === 'function') this.cleanups.add(cleanup);\n });\n engine.activeEffect = prev;\n }\n };\n this.allEffects.add(effectObj); // Регистрируем эффект в глобальном списке\n effectObj.run();\n return () => {\n effectObj.cleanups.forEach(c => c());\n engine.pendingEffects.delete(effectObj);\n };\n }\n\n /**\n * Создание вычисляемого значения.\n * @template T\n * @function computed\n * @param {Function} fn - Функция для вычисления значения.\n * @param {string} [signalName] - Имя сигнала.\n * @returns {Computed<T>} - Вычисляемое значение.\n */\n public computed<T>(fn: () => T, signalName?: string): Computed<T> {\n const sig = this.signal<T>(undefined as any, signalName || 'unnamed_computed');\n this.effect(() => {\n const newValue = fn();\n // Сигнал сам внутри себя проверит (sig.value === newValue)\n // благодаря правке в пункте №1\n sig.value = newValue;\n });\n return {\n get value() { return sig.value; },\n // Подписываемся на внутренний сигнал\n subscribe: (cb) => sig.subscribe(cb)\n };\n }\n\n /**\n * Создание реактивного объекта (Proxy).\n * @template T\n * @function reactive\n * @param {T} target - Целевой объект для проксирования.\n * @param {string} [name] - Имя проксируемого объекта.\n * @returns {T} - Реактивный объект.\n */\n public reactive<T extends object>(target: T, name: string = 'reactive'): T {\n if (this.proxyCache.has(target)) return this.proxyCache.get(target);\n const engine = this;\n const propsSubscribers = new Map<string | symbol, Set<IEffect>>();\n\n const proxy = new Proxy(target, {\n get(obj, prop, receiver) {\n if (engine.activeEffect) {\n if (!propsSubscribers.has(prop)) propsSubscribers.set(prop, new Set());\n propsSubscribers.get(prop)!.add(engine.activeEffect);\n }\n const value = Reflect.get(obj, prop, receiver);\n return (value !== null && typeof value === 'object')\n ? engine.reactive(value, `${name}.${String(prop)}`)\n : value;\n },\n set(obj, prop, value, receiver) {\n const old = Reflect.get(obj, prop, receiver);\n // ПРОВЕРКА НА ИДЕНТИЧНОСТЬ\n if (old === value) return true;\n\n if (old !== value) {\n Reflect.set(obj, prop, value, receiver);\n engine.onSignalChange?.(`${name}.${String(prop)}`, value, old);\n propsSubscribers.get(prop)?.forEach(e =>\n engine.isBatching ? engine.pendingEffects.add(e) : e.run()\n );\n }\n return true;\n }\n });\n this.proxyCache.set(target, proxy);\n return proxy;\n }\n\n /**\n * Группировка изменений.\n * @function batch\n * @param {Function} fn - Функция для выполнения в группе.\n * @returns {void}\n */\n public batch(fn: () => void): void {\n this.isBatching = true;\n try { fn(); } finally {\n queueMicrotask(() => {\n const effects = Array.from(this.pendingEffects);\n this.pendingEffects.clear();\n this.isBatching = false;\n effects.forEach(e => e.run());\n });\n }\n }\n\n /**\n * Создание асинхронного ресурса.\n * @template T\n * @function resource\n * @param {Function} fetcher - Функция для загрузки данных.\n * @param {{ value: S }} [source] - Источник данных.\n * @param {string} [signalName] - Имя сигнала ресурса.\n * @returns {Resource<T>} - Асинхронный ресурс.\n */\n public resource<T, S = void>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n source?: { value: S },\n signalName?: string,\n ): Resource<T> {\n const state = this.signal<ResourceState<T>>({ data: null, loading: true, error: null }, signalName || 'unnamed_resource');\n let controller: AbortController | null = null;\n\n const load = async (sValue: S) => {\n controller?.abort();\n controller = new AbortController();\n\n // Читаем текущие данные без создания зависимости!\n const currentData = this.untrack(() => state.value.data);\n\n // Теперь обновление состояния не вызовет перезапуск эффекта\n state.value = { data: currentData, loading: true, error: null };\n\n try {\n const data = await fetcher(sValue, controller.signal);\n if (!controller.signal.aborted) {\n state.value = { data, loading: false, error: null };\n }\n } catch (e: any) {\n if (e.name !== 'AbortError') {\n state.value = { data: null, loading: false, error: e };\n }\n }\n };\n\n // Чтобы эффект не подписывался на state,\n // нам нужно изолировать чтение source от чтения state\n this.effect(() => {\n // Читаем ТОЛЬКО source. Это наша единственная зависимость.\n const sValue = source ? source.value : undefined as S;\n\n // Вызываем load. Внутри load чтение state.value\n // не должно регистрироваться как зависимость этого эффекта.\n load(sValue);\n\n return () => controller?.abort();\n });\n\n return {\n get data() { return state.value.data; },\n get loading() { return state.value.loading; },\n get error() { return state.value.error; },\n get value() { return state.value; },\n refetch: () => load(source ? source.value : undefined as S),\n subscribe: (cb) => state.subscribe(cb)\n };\n }\n\n /**\n * Выполняет функцию без отслеживания зависимостей.\n * @template T\n * @function untrack\n * @param {Function} fn - Функция для выполнения.\n * @returns {T} - Результат выполнения функции.\n */\n public untrack<T>(fn: () => T): T {\n const prev = this.activeEffect;\n this.activeEffect = null; // Временно \"забываем\" про активный эффект\n try {\n return fn();\n } finally {\n this.activeEffect = prev; // Возвращаем эффект на место\n }\n }\n\n /**\n * Установка адаптеров React.\n * @function setReactAdapters\n * @param {Function} useState - Функция useState из React.\n * @param {Function} useEffect - Функция useEffect из React.\n * @returns {void}\n */\n public setReactAdapters(useState: any, useEffect: any) {\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 */\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 /**\n * Безопасное выполнение функции.\n * @private\n * @function safeRun\n * @param {IEffect} effect - Эффект.\n * @param {Function} fn - Функция для выполнения.\n * @returns {void}\n */\n private safeRun(effect: IEffect, fn: () => void) {\n try {\n fn();\n } catch (error) {\n console.error(\n `%c[Reactive Error] %cОшибка в эффекте/computed:`,\n \"color: white; background: red; padding: 2px 4px; border-radius: 3px;\",\n \"font-weight: bold;\",\n error\n );\n // Здесь можно отправить ошибку в Sentry или другой сервис мониторинга\n }\n }\n}\n","import { ReactiveEngine } from './core';\n\n// export abstract class BaseService {\n// constructor(protected engine: ReactiveEngine) {\n// // Вызываем метод инициализации, который можно переопределить в наследниках\n// this.onInit();\n// }\n\n// // NOTE: Жизненный цикл: вызывается сразу после создания сервиса.\n// // Удобно для инициализации сигналов, которые зависят от engine.\n// protected onInit(): void {}\n\n// // NOTE: Полезные хелперы для сокращения кода\n// protected createSignal<T>(val: T, name?: string) {\n// return this.engine.signal(val, name);\n// }\n\n// protected createComputed<T>(fn: () => T) {\n// return this.engine.computed(fn);\n// }\n// }\n\nexport abstract class BaseREService {\n constructor(protected engine: ReactiveEngine) { } // Просто сохраняем engine\n\n protected createNumericSignal(val: number, name: string) {\n return this.engine.signal(val, {\n name,\n validate: (v) => typeof v === 'number' && !isNaN(v)\n });\n }\n}\n","import { ReactiveEngine } from './core';\n\nexport abstract class BaseREServiceEnhanced {\n constructor(protected engine: ReactiveEngine) {\n setTimeout(() => this.onInit(), 0)\n }\n\n protected onInit(): void { }\n}\n"],"names":["ReactiveEngine","useState","useEffect","token","valueOrFactory","factory","instance","e","initialValue","optionsOrName","engine","val","subscribers","options","name","newValue","result","errorMsg","old","cb","fn","effectObj","c","prev","cleanup","signalName","sig","target","propsSubscribers","proxy","obj","prop","receiver","value","effects","fetcher","source","state","controller","load","sValue","currentData","data","item","setVal","effect","error","BaseREService","v","BaseREServiceEnhanced"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -7,16 +7,33 @@ class v {
7
7
  factories = /* @__PURE__ */ new Map();
8
8
  proxyCache = /* @__PURE__ */ new WeakMap();
9
9
  allEffects = /* @__PURE__ */ new Set();
10
+ /**
11
+ * Коллбек для уведомления о изменении сигнала.
12
+ * @type {Function}
13
+ */
10
14
  onSignalChange;
11
15
  reactAdapters = {
12
16
  useState: g,
13
17
  useEffect: h
14
18
  };
15
- /** DI: Регистрация */
19
+ /**
20
+ * DI: Регистрация зависимости.
21
+ * @template T
22
+ * @function provide
23
+ * @param {Token<T>} token - Токен для зависимости.
24
+ * @param {T | Factory<T>} valueOrFactory - Значение или фабрика для создания сервиса.
25
+ * @returns {void}
26
+ */
16
27
  provide(e, t) {
17
28
  typeof t == "function" && !t.prototype ? this.factories.set(e, t) : this.services.set(e, t);
18
29
  }
19
- /** DI: Инъекция */
30
+ /**
31
+ * DI: Инъекция; Получение сервиса по токену.
32
+ * @template T
33
+ * @function inject
34
+ * @param {Token<T>} token - Токен для зависимости.
35
+ * @returns {T} - Сервис.
36
+ */
20
37
  inject(e) {
21
38
  if (!e)
22
39
  throw new Error("[DI Error]: Вы пытаетесь внедрить пустой токен (undefined/null). Проверьте импорты.");
@@ -36,7 +53,14 @@ class v {
36
53
  throw new Error(`[DI Error]: Не удалось создать сервис ${String(e)}. Ошибка: ${t?.message || "No e?.message"}`);
37
54
  }
38
55
  }
39
- /** Создание Сигнала */
56
+ /**
57
+ * Создание сигнала.
58
+ * @template T
59
+ * @function signal
60
+ * @param {T} initialValue - Начальное значение сигнала.
61
+ * @param {string | SignalOptions<T>} [optionsOrName] - Имя или опции сигнала.
62
+ * @returns {Signal<T>} - Сигнал.
63
+ */
40
64
  signal(e, t) {
41
65
  const r = this;
42
66
  let n = e;
@@ -68,7 +92,12 @@ class v {
68
92
  }
69
93
  };
70
94
  }
71
- /** Эффект */
95
+ /**
96
+ * Создание эффекта.
97
+ * @function effect
98
+ * @param {EffectFn} fn - Функция эффекта.
99
+ * @returns {CleanupFn} - Функция для очистки эффекта.
100
+ */
72
101
  effect(e) {
73
102
  const t = this, r = {
74
103
  cleanups: /* @__PURE__ */ new Set(),
@@ -85,7 +114,14 @@ class v {
85
114
  r.cleanups.forEach((n) => n()), t.pendingEffects.delete(r);
86
115
  };
87
116
  }
88
- /** Вычисляемое значение */
117
+ /**
118
+ * Создание вычисляемого значения.
119
+ * @template T
120
+ * @function computed
121
+ * @param {Function} fn - Функция для вычисления значения.
122
+ * @param {string} [signalName] - Имя сигнала.
123
+ * @returns {Computed<T>} - Вычисляемое значение.
124
+ */
89
125
  computed(e, t) {
90
126
  const r = this.signal(void 0, t || "unnamed_computed");
91
127
  return this.effect(() => {
@@ -99,7 +135,14 @@ class v {
99
135
  subscribe: (n) => r.subscribe(n)
100
136
  };
101
137
  }
102
- /** Реактивный объект (Proxy) */
138
+ /**
139
+ * Создание реактивного объекта (Proxy).
140
+ * @template T
141
+ * @function reactive
142
+ * @param {T} target - Целевой объект для проксирования.
143
+ * @param {string} [name] - Имя проксируемого объекта.
144
+ * @returns {T} - Реактивный объект.
145
+ */
103
146
  reactive(e, t = "reactive") {
104
147
  if (this.proxyCache.has(e)) return this.proxyCache.get(e);
105
148
  const r = this, n = /* @__PURE__ */ new Map(), i = new Proxy(e, {
@@ -117,7 +160,12 @@ class v {
117
160
  });
118
161
  return this.proxyCache.set(e, i), i;
119
162
  }
120
- /** Группировка изменений */
163
+ /**
164
+ * Группировка изменений.
165
+ * @function batch
166
+ * @param {Function} fn - Функция для выполнения в группе.
167
+ * @returns {void}
168
+ */
121
169
  batch(e) {
122
170
  this.isBatching = !0;
123
171
  try {
@@ -129,7 +177,15 @@ class v {
129
177
  });
130
178
  }
131
179
  }
132
- /** Асинхронный ресурс */
180
+ /**
181
+ * Создание асинхронного ресурса.
182
+ * @template T
183
+ * @function resource
184
+ * @param {Function} fetcher - Функция для загрузки данных.
185
+ * @param {{ value: S }} [source] - Источник данных.
186
+ * @param {string} [signalName] - Имя сигнала ресурса.
187
+ * @returns {Resource<T>} - Асинхронный ресурс.
188
+ */
133
189
  resource(e, t, r) {
134
190
  const n = this.signal({ data: null, loading: !0, error: null }, r || "unnamed_resource");
135
191
  let i = null;
@@ -165,7 +221,11 @@ class v {
165
221
  };
166
222
  }
167
223
  /**
168
- * Выполняет функцию без отслеживания зависимостей
224
+ * Выполняет функцию без отслеживания зависимостей.
225
+ * @template T
226
+ * @function untrack
227
+ * @param {Function} fn - Функция для выполнения.
228
+ * @returns {T} - Результат выполнения функции.
169
229
  */
170
230
  untrack(e) {
171
231
  const t = this.activeEffect;
@@ -176,10 +236,23 @@ class v {
176
236
  this.activeEffect = t;
177
237
  }
178
238
  }
179
- /** React Хук */
239
+ /**
240
+ * Установка адаптеров React.
241
+ * @function setReactAdapters
242
+ * @param {Function} useState - Функция useState из React.
243
+ * @param {Function} useEffect - Функция useEffect из React.
244
+ * @returns {void}
245
+ */
180
246
  setReactAdapters(e, t) {
181
247
  this.reactAdapters = { useState: e, useEffect: t };
182
248
  }
249
+ /**
250
+ * Использование реактивного значения в React компоненте.
251
+ * @template T
252
+ * @function use
253
+ * @param {{ value: T; subscribe: (cb: (v: T) => void) => CleanupFn }} item - Реактивный объект.
254
+ * @returns {T} - Значение реактивного объекта.
255
+ */
183
256
  use(e) {
184
257
  if (!this.reactAdapters)
185
258
  throw new Error("[React Error]: Адаптеры React не установлены. Вызовите engine.setReactAdapters(useState, useEffect).");
@@ -197,6 +270,14 @@ class v {
197
270
  [e]
198
271
  ), t;
199
272
  }
273
+ /**
274
+ * Безопасное выполнение функции.
275
+ * @private
276
+ * @function safeRun
277
+ * @param {IEffect} effect - Эффект.
278
+ * @param {Function} fn - Функция для выполнения.
279
+ * @returns {void}
280
+ */
200
281
  safeRun(e, t) {
201
282
  try {
202
283
  t();
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/core.ts","../src/BaseREService.ts","../src/BaseREServiceEnhanced.ts"],"sourcesContent":["import { useState, useEffect } from 'react';\n\n// --- ТИПЫ ---\nexport type CleanupFn = () => void;\nexport type EffectFn = () => CleanupFn | void;\nexport type Token<T> = string | symbol | { new(engine: ReactiveEngine, ...args: any[]): T };\nexport type Factory<T> = (engine: ReactiveEngine) => T;\n\nexport interface IEffect {\n run: () => void;\n cleanups: Set<CleanupFn>;\n}\n\nexport interface ResourceState<T> {\n data: T | null;\n loading: boolean;\n error: any | null;\n}\n\nexport interface Signal<T> {\n value: T;\n subscribe: (cb: (val: T) => void) => CleanupFn;\n}\n\nexport interface Computed<T> {\n readonly value: T;\n subscribe: (cb: (val: T) => void) => CleanupFn;\n}\n\nexport interface Resource<T> extends ResourceState<T> {\n refetch: () => void;\n subscribe: (cb: (val: ResourceState<T>) => void) => CleanupFn;\n readonly value: ResourceState<T>;\n}\n\ninterface SignalOptions<T> {\n name?: string;\n validate?: (val: T) => boolean | string;\n}\n\n// --- ЯДРО ---\nexport class ReactiveEngine {\n private activeEffect: IEffect | null = null;\n private isBatching = false;\n private pendingEffects = new Set<IEffect>();\n private services = new Map<Token<any>, any>();\n private factories = new Map<Token<any>, Factory<any>>();\n private proxyCache = new WeakMap<object, any>();\n private allEffects = new Set<IEffect>();\n\n public onSignalChange?: (name: string, next: any, prev: any) => void;\n\n private reactAdapters = {\n useState: useState as any,\n useEffect: useEffect as any,\n };\n\n /** DI: Регистрация */\n public provide<T>(token: Token<T>, valueOrFactory: T | Factory<T>): void {\n if (typeof valueOrFactory === 'function' && !valueOrFactory.prototype) {\n this.factories.set(token, valueOrFactory as Factory<T>);\n } else {\n this.services.set(token, valueOrFactory);\n }\n }\n\n /** DI: Инъекция */\n public inject<T>(token: Token<T>): T {\n if (!token) {\n throw new Error(`[DI Error]: Вы пытаетесь внедрить пустой токен (undefined/null). Проверьте импорты.`);\n }\n\n if (this.services.has(token)) return this.services.get(token);\n\n // NOTE: (new way) Если это класс, но мы забыли его унаследовать от BaseService или передать engine\n try {\n // NOTE: Логика создания инстанса\n const factory = this.factories.get(token);\n if (factory) {\n const instance = factory(this);\n this.services.set(token, instance);\n return instance;\n }\n if (typeof token === 'function' && token.prototype) {\n const instance = new (token as { new(eng: ReactiveEngine): T })(this);\n this.services.set(token, instance);\n return instance;\n }\n throw new Error(`Service not found: ${String(token)}`);\n } catch (e) {\n throw new Error(`[DI Error]: Не удалось создать сервис ${String(token)}. Ошибка: ${(e as Error)?.message || 'No e?.message'}`);\n }\n }\n\n /** Создание Сигнала */\n public signal<T>(initialValue: T, optionsOrName?: string | SignalOptions<T>): Signal<T> {\n const engine = this;\n let val = initialValue;\n const subscribers = new Set<IEffect>();\n\n // Парсим аргументы для обратной совместимости\n const options = typeof optionsOrName === 'string'\n ? { name: optionsOrName }\n : optionsOrName || {};\n\n const name = options.name || 'unnamed_signal';\n\n return {\n get value(): T {\n if (engine.activeEffect) subscribers.add(engine.activeEffect);\n return val;\n },\n set value(newValue: T) {\n if (val === newValue) return;\n\n // ВАЛИДАЦИЯ В RUNTIME\n if (options.validate) {\n const result = options.validate(newValue);\n if (result === false || typeof result === 'string') {\n const errorMsg = typeof result === 'string'\n ? result\n : `[Validation Error]: Некорректное значение для сигнала \"${name}\"`;\n\n console.error(`%c${errorMsg}`, \"color: orange; font-weight: bold;\", {\n received: newValue,\n current: val\n });\n return; // Прерываем обновление, если данные не валидны\n }\n }\n\n const old = val;\n val = newValue;\n engine.onSignalChange?.(name, newValue, old);\n subscribers.forEach(e =>\n engine.isBatching ? engine.pendingEffects.add(e) : e.run()\n );\n },\n subscribe(cb) {\n return engine.effect(() => cb(this.value));\n }\n };\n }\n\n /** Эффект */\n public effect(fn: EffectFn): CleanupFn {\n const engine = this;\n const effectObj: IEffect = {\n cleanups: new Set(),\n run() {\n this.cleanups.forEach(c => c());\n this.cleanups.clear();\n const prev = engine.activeEffect;\n engine.activeEffect = this;\n engine.safeRun(this, () => {\n const cleanup = fn();\n if (typeof cleanup === 'function') this.cleanups.add(cleanup);\n });\n engine.activeEffect = prev;\n }\n };\n this.allEffects.add(effectObj); // Регистрируем эффект в глобальном списке\n effectObj.run();\n return () => {\n effectObj.cleanups.forEach(c => c());\n engine.pendingEffects.delete(effectObj);\n };\n }\n\n /** Вычисляемое значение */\n public computed<T>(fn: () => T, signalName?: string): Computed<T> {\n const sig = this.signal<T>(undefined as any, signalName || 'unnamed_computed');\n this.effect(() => {\n const newValue = fn();\n // Сигнал сам внутри себя проверит (sig.value === newValue)\n // благодаря правке в пункте №1\n sig.value = newValue;\n });\n return {\n get value() { return sig.value; },\n // Подписываемся на внутренний сигнал\n subscribe: (cb) => sig.subscribe(cb)\n };\n }\n\n /** Реактивный объект (Proxy) */\n public reactive<T extends object>(target: T, name: string = 'reactive'): T {\n if (this.proxyCache.has(target)) return this.proxyCache.get(target);\n const engine = this;\n const propsSubscribers = new Map<string | symbol, Set<IEffect>>();\n\n const proxy = new Proxy(target, {\n get(obj, prop, receiver) {\n if (engine.activeEffect) {\n if (!propsSubscribers.has(prop)) propsSubscribers.set(prop, new Set());\n propsSubscribers.get(prop)!.add(engine.activeEffect);\n }\n const value = Reflect.get(obj, prop, receiver);\n return (value !== null && typeof value === 'object')\n ? engine.reactive(value, `${name}.${String(prop)}`)\n : value;\n },\n set(obj, prop, value, receiver) {\n const old = Reflect.get(obj, prop, receiver);\n // ПРОВЕРКА НА ИДЕНТИЧНОСТЬ\n if (old === value) return true;\n\n if (old !== value) {\n Reflect.set(obj, prop, value, receiver);\n engine.onSignalChange?.(`${name}.${String(prop)}`, value, old);\n propsSubscribers.get(prop)?.forEach(e =>\n engine.isBatching ? engine.pendingEffects.add(e) : e.run()\n );\n }\n return true;\n }\n });\n this.proxyCache.set(target, proxy);\n return proxy;\n }\n\n /** Группировка изменений */\n public batch(fn: () => void): void {\n this.isBatching = true;\n try { fn(); } finally {\n queueMicrotask(() => {\n const effects = Array.from(this.pendingEffects);\n this.pendingEffects.clear();\n this.isBatching = false;\n effects.forEach(e => e.run());\n });\n }\n }\n\n /** Асинхронный ресурс */\n public resource<T, S = void>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n source?: { value: S },\n signalName?: string,\n ): Resource<T> {\n const state = this.signal<ResourceState<T>>({ data: null, loading: true, error: null }, signalName || 'unnamed_resource');\n let controller: AbortController | null = null;\n\n const load = async (sValue: S) => {\n controller?.abort();\n controller = new AbortController();\n\n // Читаем текущие данные без создания зависимости!\n const currentData = this.untrack(() => state.value.data);\n\n // Теперь обновление состояния не вызовет перезапуск эффекта\n state.value = { data: currentData, loading: true, error: null };\n\n try {\n const data = await fetcher(sValue, controller.signal);\n if (!controller.signal.aborted) {\n state.value = { data, loading: false, error: null };\n }\n } catch (e: any) {\n if (e.name !== 'AbortError') {\n state.value = { data: null, loading: false, error: e };\n }\n }\n };\n\n // Чтобы эффект не подписывался на state,\n // нам нужно изолировать чтение source от чтения state\n this.effect(() => {\n // Читаем ТОЛЬКО source. Это наша единственная зависимость.\n const sValue = source ? source.value : undefined as S;\n\n // Вызываем load. Внутри load чтение state.value\n // не должно регистрироваться как зависимость этого эффекта.\n load(sValue);\n\n return () => controller?.abort();\n });\n\n return {\n get data() { return state.value.data; },\n get loading() { return state.value.loading; },\n get error() { return state.value.error; },\n get value() { return state.value; },\n refetch: () => load(source ? source.value : undefined as S),\n subscribe: (cb) => state.subscribe(cb)\n };\n }\n\n /**\n * Выполняет функцию без отслеживания зависимостей\n */\n public untrack<T>(fn: () => T): T {\n const prev = this.activeEffect;\n this.activeEffect = null; // Временно \"забываем\" про активный эффект\n try {\n return fn();\n } finally {\n this.activeEffect = prev; // Возвращаем эффект на место\n }\n }\n\n /** React Хук */\n public setReactAdapters(useState: any, useEffect: any) {\n this.reactAdapters = { useState, useEffect };\n }\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 private safeRun(effect: IEffect, fn: () => void) {\n try {\n fn();\n } catch (error) {\n console.error(\n `%c[Reactive Error] %cОшибка в эффекте/computed:`,\n \"color: white; background: red; padding: 2px 4px; border-radius: 3px;\",\n \"font-weight: bold;\",\n error\n );\n // Здесь можно отправить ошибку в Sentry или другой сервис мониторинга\n }\n }\n}\n","import { ReactiveEngine } from './core';\n\n// export abstract class BaseService {\n// constructor(protected engine: ReactiveEngine) {\n// // Вызываем метод инициализации, который можно переопределить в наследниках\n// this.onInit();\n// }\n\n// // NOTE: Жизненный цикл: вызывается сразу после создания сервиса.\n// // Удобно для инициализации сигналов, которые зависят от engine.\n// protected onInit(): void {}\n\n// // NOTE: Полезные хелперы для сокращения кода\n// protected createSignal<T>(val: T, name?: string) {\n// return this.engine.signal(val, name);\n// }\n\n// protected createComputed<T>(fn: () => T) {\n// return this.engine.computed(fn);\n// }\n// }\n\nexport abstract class BaseREService {\n constructor(protected engine: ReactiveEngine) { } // Просто сохраняем engine\n\n protected createNumericSignal(val: number, name: string) {\n return this.engine.signal(val, {\n name,\n validate: (v) => typeof v === 'number' && !isNaN(v)\n });\n }\n}\n","import { ReactiveEngine } from './core';\n\nexport abstract class BaseREServiceEnhanced {\n constructor(protected engine: ReactiveEngine) {\n setTimeout(() => this.onInit(), 0)\n }\n\n protected onInit(): void { }\n}\n"],"names":["ReactiveEngine","useState","useEffect","token","valueOrFactory","factory","instance","e","initialValue","optionsOrName","engine","val","subscribers","options","name","newValue","result","errorMsg","old","cb","fn","effectObj","c","prev","cleanup","signalName","sig","target","propsSubscribers","proxy","obj","prop","receiver","value","effects","fetcher","source","state","controller","load","sValue","currentData","data","item","setVal","effect","error","BaseREService","v","BaseREServiceEnhanced"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/core.ts","../src/BaseREService.ts","../src/BaseREServiceEnhanced.ts"],"sourcesContent":["import { useState, useEffect } from 'react';\n\n// --- ТИПЫ ---\nexport type CleanupFn = () => void;\nexport type EffectFn = () => CleanupFn | void;\nexport type Token<T> = string | symbol | { new(engine: ReactiveEngine, ...args: any[]): T };\nexport type Factory<T> = (engine: ReactiveEngine) => T;\n\nexport interface IEffect {\n run: () => void;\n cleanups: Set<CleanupFn>;\n}\n\nexport interface ResourceState<T> {\n data: T | null;\n loading: boolean;\n error: any | null;\n}\n\n/**\n * Функция для очистки эффекта.\n * @typedef {() => void} CleanupFn\n */\n\n/**\n * Функция эффекта, которая может возвращать функцию очистки.\n * @typedef {() => CleanupFn | void} EffectFn\n */\n\n/**\n * Токен для зависимости.\n * @typedef {string | symbol | { new(engine: ReactiveEngine, ...args: any[]): T }} Token<T>\n */\n\n/**\n * Фабрика для создания сервиса.\n * @template T\n * @typedef {(engine: ReactiveEngine) => T} Factory<T>\n */\n\n/**\n * Интерфейс для эффекта.\n * @interface IEffect\n */\nexport interface IEffect {\n /**\n * Запуск эффекта.\n * @function run\n * @returns {void}\n */\n run: () => void;\n\n /**\n * Коллекция функций очистки.\n * @type {Set<CleanupFn>}\n */\n cleanups: Set<CleanupFn>;\n}\n\n/**\n * Интерфейс для состояния ресурса.\n * @template T\n * @interface ResourceState<T>\n */\nexport interface ResourceState<T> {\n /**\n * Данные ресурса.\n * @type {T | null}\n */\n data: T | null;\n\n /**\n * Состояние загрузки ресурса.\n * @type {boolean}\n */\n loading: boolean;\n\n /**\n * Ошибка ресурса.\n * @type {any | null}\n */\n error: any | null;\n}\n\n/**\n * Интерфейс для сигнала.\n * @template T\n * @interface Signal<T>\n */\nexport interface Signal<T> {\n /**\n * Значение сигнала.\n * @type {T}\n */\n value: T;\n\n /**\n * Подписка на изменение значения сигнала.\n * @function subscribe\n * @param {Function} cb - Коллбек функция для обработки изменения.\n * @returns {CleanupFn} - Функция для очистки подписки.\n */\n subscribe: (cb: (val: T) => void) => CleanupFn;\n}\n\n/**\n * Интерфейс для вычисляемого значения.\n * @template T\n * @interface Computed<T>\n */\nexport interface Computed<T> {\n /**\n * Только читаемое значение вычисляемого значения.\n * @type {T}\n */\n readonly value: T;\n\n /**\n * Подписка на изменение вычисляемого значения.\n * @function subscribe\n * @param {Function} cb - Коллбек функция для обработки изменения.\n * @returns {CleanupFn} - Функция для очистки подписки.\n */\n subscribe: (cb: (val: T) => void) => CleanupFn;\n}\n\n/**\n * Интерфейс для ресурса.\n * @template T\n * @interface Resource<T>\n */\nexport interface Resource<T> extends ResourceState<T> {\n /**\n * Перезагрузка ресурса.\n * @function refetch\n * @returns {void}\n */\n refetch: () => void;\n\n /**\n * Подписка на изменение состояния ресурса.\n * @function subscribe\n * @param {Function} cb - Коллбек функция для обработки изменения.\n * @returns {CleanupFn} - Функция для очистки подписки.\n */\n subscribe: (cb: (val: ResourceState<T>) => void) => CleanupFn;\n\n /**\n * Только читаемое состояние ресурса.\n * @type {ResourceState<T>}\n */\n readonly value: ResourceState<T>;\n}\n\n/**\n * Интерфейс для опций сигнала.\n * @template T\n * @interface SignalOptions<T>\n */\nexport interface SignalOptions<T> {\n /**\n * Имя сигнала.\n * @type {string}\n */\n name?: string;\n\n /**\n * Валидатор значения сигнала.\n * @function validate\n * @param {T} val - Значение для валидации.\n * @returns {boolean | string} - Результат валидации.\n */\n validate?: (val: T) => boolean | string;\n}\n\n// --- ЯДРО ---\n/**\n * Класс реактивного движка.\n */\nexport class ReactiveEngine {\n private activeEffect: IEffect | null = null;\n private isBatching = false;\n private pendingEffects = new Set<IEffect>();\n private services = new Map<Token<any>, any>();\n private factories = new Map<Token<any>, Factory<any>>();\n private proxyCache = new WeakMap<object, any>();\n private allEffects = new Set<IEffect>();\n\n /**\n * Коллбек для уведомления о изменении сигнала.\n * @type {Function}\n */\n public onSignalChange?: (name: string, next: any, prev: any) => void;\n\n private reactAdapters = {\n useState: useState as any,\n useEffect: useEffect as any,\n };\n\n /**\n * DI: Регистрация зависимости.\n * @template T\n * @function provide\n * @param {Token<T>} token - Токен для зависимости.\n * @param {T | Factory<T>} valueOrFactory - Значение или фабрика для создания сервиса.\n * @returns {void}\n */\n public provide<T>(token: Token<T>, valueOrFactory: T | Factory<T>): void {\n if (typeof valueOrFactory === 'function' && !valueOrFactory.prototype) {\n this.factories.set(token, valueOrFactory as Factory<T>);\n } else {\n this.services.set(token, valueOrFactory);\n }\n }\n\n /**\n * DI: Инъекция; Получение сервиса по токену.\n * @template T\n * @function inject\n * @param {Token<T>} token - Токен для зависимости.\n * @returns {T} - Сервис.\n */\n public inject<T>(token: Token<T>): T {\n if (!token) {\n throw new Error(`[DI Error]: Вы пытаетесь внедрить пустой токен (undefined/null). Проверьте импорты.`);\n }\n\n if (this.services.has(token)) return this.services.get(token);\n\n // NOTE: (new way) Если это класс, но мы забыли его унаследовать от BaseService или передать engine\n try {\n // NOTE: Логика создания инстанса\n const factory = this.factories.get(token);\n if (factory) {\n const instance = factory(this);\n this.services.set(token, instance);\n return instance;\n }\n if (typeof token === 'function' && token.prototype) {\n const instance = new (token as { new(eng: ReactiveEngine): T })(this);\n this.services.set(token, instance);\n return instance;\n }\n throw new Error(`Service not found: ${String(token)}`);\n } catch (e) {\n throw new Error(`[DI Error]: Не удалось создать сервис ${String(token)}. Ошибка: ${(e as Error)?.message || 'No e?.message'}`);\n }\n }\n\n /**\n * Создание сигнала.\n * @template T\n * @function signal\n * @param {T} initialValue - Начальное значение сигнала.\n * @param {string | SignalOptions<T>} [optionsOrName] - Имя или опции сигнала.\n * @returns {Signal<T>} - Сигнал.\n */\n public signal<T>(initialValue: T, optionsOrName?: string | SignalOptions<T>): Signal<T> {\n const engine = this;\n let val = initialValue;\n const subscribers = new Set<IEffect>();\n\n // Парсим аргументы для обратной совместимости\n const options = typeof optionsOrName === 'string'\n ? { name: optionsOrName }\n : optionsOrName || {};\n\n const name = options.name || 'unnamed_signal';\n\n return {\n get value(): T {\n if (engine.activeEffect) subscribers.add(engine.activeEffect);\n return val;\n },\n set value(newValue: T) {\n if (val === newValue) return;\n\n // ВАЛИДАЦИЯ В RUNTIME\n if (options.validate) {\n const result = options.validate(newValue);\n if (result === false || typeof result === 'string') {\n const errorMsg = typeof result === 'string'\n ? result\n : `[Validation Error]: Некорректное значение для сигнала \"${name}\"`;\n\n console.error(`%c${errorMsg}`, \"color: orange; font-weight: bold;\", {\n received: newValue,\n current: val\n });\n return; // Прерываем обновление, если данные не валидны\n }\n }\n\n const old = val;\n val = newValue;\n engine.onSignalChange?.(name, newValue, old);\n subscribers.forEach(e =>\n engine.isBatching ? engine.pendingEffects.add(e) : e.run()\n );\n },\n subscribe(cb) {\n return engine.effect(() => cb(this.value));\n }\n };\n }\n\n /**\n * Создание эффекта.\n * @function effect\n * @param {EffectFn} fn - Функция эффекта.\n * @returns {CleanupFn} - Функция для очистки эффекта.\n */\n public effect(fn: EffectFn): CleanupFn {\n const engine = this;\n const effectObj: IEffect = {\n cleanups: new Set(),\n run() {\n this.cleanups.forEach(c => c());\n this.cleanups.clear();\n const prev = engine.activeEffect;\n engine.activeEffect = this;\n engine.safeRun(this, () => {\n const cleanup = fn();\n if (typeof cleanup === 'function') this.cleanups.add(cleanup);\n });\n engine.activeEffect = prev;\n }\n };\n this.allEffects.add(effectObj); // Регистрируем эффект в глобальном списке\n effectObj.run();\n return () => {\n effectObj.cleanups.forEach(c => c());\n engine.pendingEffects.delete(effectObj);\n };\n }\n\n /**\n * Создание вычисляемого значения.\n * @template T\n * @function computed\n * @param {Function} fn - Функция для вычисления значения.\n * @param {string} [signalName] - Имя сигнала.\n * @returns {Computed<T>} - Вычисляемое значение.\n */\n public computed<T>(fn: () => T, signalName?: string): Computed<T> {\n const sig = this.signal<T>(undefined as any, signalName || 'unnamed_computed');\n this.effect(() => {\n const newValue = fn();\n // Сигнал сам внутри себя проверит (sig.value === newValue)\n // благодаря правке в пункте №1\n sig.value = newValue;\n });\n return {\n get value() { return sig.value; },\n // Подписываемся на внутренний сигнал\n subscribe: (cb) => sig.subscribe(cb)\n };\n }\n\n /**\n * Создание реактивного объекта (Proxy).\n * @template T\n * @function reactive\n * @param {T} target - Целевой объект для проксирования.\n * @param {string} [name] - Имя проксируемого объекта.\n * @returns {T} - Реактивный объект.\n */\n public reactive<T extends object>(target: T, name: string = 'reactive'): T {\n if (this.proxyCache.has(target)) return this.proxyCache.get(target);\n const engine = this;\n const propsSubscribers = new Map<string | symbol, Set<IEffect>>();\n\n const proxy = new Proxy(target, {\n get(obj, prop, receiver) {\n if (engine.activeEffect) {\n if (!propsSubscribers.has(prop)) propsSubscribers.set(prop, new Set());\n propsSubscribers.get(prop)!.add(engine.activeEffect);\n }\n const value = Reflect.get(obj, prop, receiver);\n return (value !== null && typeof value === 'object')\n ? engine.reactive(value, `${name}.${String(prop)}`)\n : value;\n },\n set(obj, prop, value, receiver) {\n const old = Reflect.get(obj, prop, receiver);\n // ПРОВЕРКА НА ИДЕНТИЧНОСТЬ\n if (old === value) return true;\n\n if (old !== value) {\n Reflect.set(obj, prop, value, receiver);\n engine.onSignalChange?.(`${name}.${String(prop)}`, value, old);\n propsSubscribers.get(prop)?.forEach(e =>\n engine.isBatching ? engine.pendingEffects.add(e) : e.run()\n );\n }\n return true;\n }\n });\n this.proxyCache.set(target, proxy);\n return proxy;\n }\n\n /**\n * Группировка изменений.\n * @function batch\n * @param {Function} fn - Функция для выполнения в группе.\n * @returns {void}\n */\n public batch(fn: () => void): void {\n this.isBatching = true;\n try { fn(); } finally {\n queueMicrotask(() => {\n const effects = Array.from(this.pendingEffects);\n this.pendingEffects.clear();\n this.isBatching = false;\n effects.forEach(e => e.run());\n });\n }\n }\n\n /**\n * Создание асинхронного ресурса.\n * @template T\n * @function resource\n * @param {Function} fetcher - Функция для загрузки данных.\n * @param {{ value: S }} [source] - Источник данных.\n * @param {string} [signalName] - Имя сигнала ресурса.\n * @returns {Resource<T>} - Асинхронный ресурс.\n */\n public resource<T, S = void>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n source?: { value: S },\n signalName?: string,\n ): Resource<T> {\n const state = this.signal<ResourceState<T>>({ data: null, loading: true, error: null }, signalName || 'unnamed_resource');\n let controller: AbortController | null = null;\n\n const load = async (sValue: S) => {\n controller?.abort();\n controller = new AbortController();\n\n // Читаем текущие данные без создания зависимости!\n const currentData = this.untrack(() => state.value.data);\n\n // Теперь обновление состояния не вызовет перезапуск эффекта\n state.value = { data: currentData, loading: true, error: null };\n\n try {\n const data = await fetcher(sValue, controller.signal);\n if (!controller.signal.aborted) {\n state.value = { data, loading: false, error: null };\n }\n } catch (e: any) {\n if (e.name !== 'AbortError') {\n state.value = { data: null, loading: false, error: e };\n }\n }\n };\n\n // Чтобы эффект не подписывался на state,\n // нам нужно изолировать чтение source от чтения state\n this.effect(() => {\n // Читаем ТОЛЬКО source. Это наша единственная зависимость.\n const sValue = source ? source.value : undefined as S;\n\n // Вызываем load. Внутри load чтение state.value\n // не должно регистрироваться как зависимость этого эффекта.\n load(sValue);\n\n return () => controller?.abort();\n });\n\n return {\n get data() { return state.value.data; },\n get loading() { return state.value.loading; },\n get error() { return state.value.error; },\n get value() { return state.value; },\n refetch: () => load(source ? source.value : undefined as S),\n subscribe: (cb) => state.subscribe(cb)\n };\n }\n\n /**\n * Выполняет функцию без отслеживания зависимостей.\n * @template T\n * @function untrack\n * @param {Function} fn - Функция для выполнения.\n * @returns {T} - Результат выполнения функции.\n */\n public untrack<T>(fn: () => T): T {\n const prev = this.activeEffect;\n this.activeEffect = null; // Временно \"забываем\" про активный эффект\n try {\n return fn();\n } finally {\n this.activeEffect = prev; // Возвращаем эффект на место\n }\n }\n\n /**\n * Установка адаптеров React.\n * @function setReactAdapters\n * @param {Function} useState - Функция useState из React.\n * @param {Function} useEffect - Функция useEffect из React.\n * @returns {void}\n */\n public setReactAdapters(useState: any, useEffect: any) {\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 */\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 /**\n * Безопасное выполнение функции.\n * @private\n * @function safeRun\n * @param {IEffect} effect - Эффект.\n * @param {Function} fn - Функция для выполнения.\n * @returns {void}\n */\n private safeRun(effect: IEffect, fn: () => void) {\n try {\n fn();\n } catch (error) {\n console.error(\n `%c[Reactive Error] %cОшибка в эффекте/computed:`,\n \"color: white; background: red; padding: 2px 4px; border-radius: 3px;\",\n \"font-weight: bold;\",\n error\n );\n // Здесь можно отправить ошибку в Sentry или другой сервис мониторинга\n }\n }\n}\n","import { ReactiveEngine } from './core';\n\n// export abstract class BaseService {\n// constructor(protected engine: ReactiveEngine) {\n// // Вызываем метод инициализации, который можно переопределить в наследниках\n// this.onInit();\n// }\n\n// // NOTE: Жизненный цикл: вызывается сразу после создания сервиса.\n// // Удобно для инициализации сигналов, которые зависят от engine.\n// protected onInit(): void {}\n\n// // NOTE: Полезные хелперы для сокращения кода\n// protected createSignal<T>(val: T, name?: string) {\n// return this.engine.signal(val, name);\n// }\n\n// protected createComputed<T>(fn: () => T) {\n// return this.engine.computed(fn);\n// }\n// }\n\nexport abstract class BaseREService {\n constructor(protected engine: ReactiveEngine) { } // Просто сохраняем engine\n\n protected createNumericSignal(val: number, name: string) {\n return this.engine.signal(val, {\n name,\n validate: (v) => typeof v === 'number' && !isNaN(v)\n });\n }\n}\n","import { ReactiveEngine } from './core';\n\nexport abstract class BaseREServiceEnhanced {\n constructor(protected engine: ReactiveEngine) {\n setTimeout(() => this.onInit(), 0)\n }\n\n protected onInit(): void { }\n}\n"],"names":["ReactiveEngine","useState","useEffect","token","valueOrFactory","factory","instance","e","initialValue","optionsOrName","engine","val","subscribers","options","name","newValue","result","errorMsg","old","cb","fn","effectObj","c","prev","cleanup","signalName","sig","target","propsSubscribers","proxy","obj","prop","receiver","value","effects","fetcher","source","state","controller","load","sValue","currentData","data","item","setVal","effect","error","BaseREService","v","BaseREServiceEnhanced"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pravosleva/reactive-engine",
3
- "version": "0.2.7-beta",
3
+ "version": "0.2.9-beta",
4
4
  "description": "JS Reactive Engine",
5
5
  "author": "Den Pol <selection4test@gmail.com> (https://pravosleva.pro)",
6
6
  "homepage": "https://github.com/garage-13/reactive-engine",