@pravosleva/reactive-engine 1.3.2-beta → 1.4.2-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 +1 -1
- package/dist/angular/ReactiveEngine4Angular.cjs +1 -1
- package/dist/angular/ReactiveEngine4Angular.cjs.map +1 -1
- package/dist/angular/ReactiveEngine4Angular.d.ts +1 -0
- package/dist/angular/ReactiveEngine4Angular.d.ts.map +1 -1
- package/dist/angular/ReactiveEngine4Angular.mjs +4 -2
- package/dist/angular/ReactiveEngine4Angular.mjs.map +1 -1
- package/dist/angular/rxjs-interop.d.ts +21 -0
- package/dist/angular/rxjs-interop.d.ts.map +1 -0
- package/dist/core/core.d.ts +143 -6
- package/dist/core/core.d.ts.map +1 -1
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.mjs +177 -33
- package/dist/core.mjs.map +1 -1
- package/dist/decorators/index.d.ts +1 -0
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/withThrottleComputed/_withThrottleComputed.d.ts +22 -0
- package/dist/decorators/withThrottleComputed/_withThrottleComputed.d.ts.map +1 -0
- package/dist/decorators/withThrottleComputed/index.d.ts +2 -0
- package/dist/decorators/withThrottleComputed/index.d.ts.map +1 -0
- package/dist/decorators/withThrottleComputed/withThrottleComputed.d.ts +11 -0
- package/dist/decorators/withThrottleComputed/withThrottleComputed.d.ts.map +1 -0
- package/dist/decorators/withThrottleComputed/withThrottleComputed.test.d.ts +2 -0
- package/dist/decorators/withThrottleComputed/withThrottleComputed.test.d.ts.map +1 -0
- package/dist/react/ReactiveEngine4React.cjs +1 -1
- package/dist/react/ReactiveEngine4React.cjs.map +1 -1
- package/dist/react/ReactiveEngine4React.d.ts +1 -0
- package/dist/react/ReactiveEngine4React.d.ts.map +1 -1
- package/dist/react/ReactiveEngine4React.mjs +1 -0
- package/dist/react/ReactiveEngine4React.mjs.map +1 -1
- package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.cjs +1 -1
- package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.cjs.map +1 -1
- package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.d.ts +5 -2
- package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.d.ts.map +1 -1
- package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.mjs +10 -6
- package/dist/react/hooks/useReactiveSubscription/useReactiveSubscription.mjs.map +1 -1
- package/dist/utils/getExtractedValues.cjs +1 -1
- package/dist/utils/getExtractedValues.cjs.map +1 -1
- package/dist/utils/getExtractedValues.d.ts.map +1 -1
- package/dist/utils/getExtractedValues.mjs +4 -1
- package/dist/utils/getExtractedValues.mjs.map +1 -1
- package/dist/vue/ReactiveEngine4Vue.cjs.map +1 -1
- package/dist/vue/ReactiveEngine4Vue.d.ts +1 -0
- package/dist/vue/ReactiveEngine4Vue.d.ts.map +1 -1
- package/dist/vue/ReactiveEngine4Vue.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../core.cjs");let t=require("@angular/core");var n=class extends e.ReactiveEngine{use(e,n){let r=(0,t.signal)(e.value),i=e.subscribe(
|
|
1
|
+
const e=require("../core.cjs");let t=require("@angular/core");var n=class extends e.ReactiveEngine{frameworkPrefix=`angular`;use(e,n){let r=(0,t.signal)(e.value),i=e.subscribe(()=>{let t=e.value;r.set(t)}),a=()=>{(0,t.inject)(t.DestroyRef).onDestroy(()=>{i()})};return n?.injector?(0,t.runInInjectionContext)(n.injector,a):a(),r.asReadonly()}};exports.ReactiveEngine4Angular=n;
|
|
2
2
|
//# sourceMappingURL=ReactiveEngine4Angular.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReactiveEngine4Angular.cjs","names":[],"sources":["../../src/angular/ReactiveEngine4Angular.ts"],"sourcesContent":["import { signal, inject, DestroyRef, Injector, runInInjectionContext, type Signal } from '@angular/core';\nimport { ReactiveEngine as OriginalReactiveEngine, type CleanupFn } from '../core/core';\n\n// Описываем интерфейс опций для Angular-адаптера\nexport interface AngularUseOptions {\n /**\n * Кастомный инжектор Angular.\n * Позволяет использовать .use() вне конструктора (например, в асинхронных функциях),\n * если передать текущий Injector вручную.\n */\n injector?: Injector;\n}\n\nexport class ReactiveEngine4Angular extends OriginalReactiveEngine {\n /**\n * Использование реактивного значения в Angular компоненте или сервисе.\n * Возвращает стандартный Angular Signal.\n */\n public use<T>(\n item: { value: T; subscribe: (cb: (v: T) => void) => CleanupFn },\n options?: AngularUseOptions\n ): Signal<T> {\n // 1. Создаем локальный Angular Signal со стартовым значением\n const angularSignal = signal<T>(item.value);\n\n // 2. Подписываемся на изменения сигнала вашего ядра\n const unsubscribe = item.subscribe((
|
|
1
|
+
{"version":3,"file":"ReactiveEngine4Angular.cjs","names":[],"sources":["../../src/angular/ReactiveEngine4Angular.ts"],"sourcesContent":["import { signal, inject, DestroyRef, Injector, runInInjectionContext, type Signal } from '@angular/core';\nimport { ReactiveEngine as OriginalReactiveEngine, type CleanupFn } from '../core/core';\n\n// Описываем интерфейс опций для Angular-адаптера\nexport interface AngularUseOptions {\n /**\n * Кастомный инжектор Angular.\n * Позволяет использовать .use() вне конструктора (например, в асинхронных функциях),\n * если передать текущий Injector вручную.\n */\n injector?: Injector;\n}\n\nexport class ReactiveEngine4Angular extends OriginalReactiveEngine {\n protected override frameworkPrefix = 'angular';\n\n /**\n * Использование реактивного значения в Angular компоненте или сервисе.\n * Возвращает стандартный Angular Signal.\n */\n public use<T>(\n item: { value: T; subscribe: (cb: (v: T) => void) => CleanupFn },\n options?: AngularUseOptions\n ): Signal<T> {\n // 1. Создаем локальный Angular Signal со стартовым значением\n const angularSignal = signal<T>(item.value);\n\n // 2. Подписываемся на изменения сигнала вашего ядра\n const unsubscribe = item.subscribe(() => {\n // Принудительно дергаем геттер .value, чтобы запустить Pull-вычисление ядра\n // и получить свежие данные, попутно запустив профайлер логгера computed!\n const freshValue = item.value;\n angularSignal.set(freshValue);\n });\n\n // 3. Логика автоматической отписки через DestroyRef\n const registerCleanup = () => {\n const destroyRef = inject(DestroyRef);\n destroyRef.onDestroy(() => {\n unsubscribe();\n });\n };\n\n // 4. Если пользователь явно передал кастомный инжектор — выполняем регистрацию внутри его контекста\n if (options?.injector) {\n runInInjectionContext(options.injector, registerCleanup);\n } else {\n // Иначе штатно выполняем в текущем контексте (вызовет ошибку NG0203, если контекст не валиден)\n registerCleanup();\n }\n\n return angularSignal.asReadonly();\n }\n}\n"],"mappings":"8DAaA,IAAa,EAAb,cAA4C,EAAA,cAAuB,CACjE,gBAAqC,UAMrC,IACE,EACA,EACW,CAEX,IAAM,GAAA,EAAA,EAAA,OAAA,CAA0B,EAAK,KAAK,EAGpC,EAAc,EAAK,cAAgB,CAGvC,IAAM,EAAa,EAAK,MACxB,EAAc,IAAI,CAAU,CAC9B,CAAC,EAGK,MAAwB,EAE5B,EAAA,EAAA,OAAA,CAD0B,EAAA,UAC1B,CAAA,CAAW,cAAgB,CACzB,EAAY,CACd,CAAC,CACH,EAUA,OAPI,GAAS,UACX,EAAA,EAAA,sBAAA,CAAsB,EAAQ,SAAU,CAAe,EAGvD,EAAgB,EAGX,EAAc,WAAW,CAClC,CACF"}
|
|
@@ -9,6 +9,7 @@ export interface AngularUseOptions {
|
|
|
9
9
|
injector?: Injector;
|
|
10
10
|
}
|
|
11
11
|
export declare class ReactiveEngine4Angular extends OriginalReactiveEngine {
|
|
12
|
+
protected frameworkPrefix: string;
|
|
12
13
|
/**
|
|
13
14
|
* Использование реактивного значения в Angular компоненте или сервисе.
|
|
14
15
|
* Возвращает стандартный Angular Signal.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReactiveEngine4Angular.d.ts","sourceRoot":"","sources":["../../src/angular/ReactiveEngine4Angular.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,QAAQ,EAAyB,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AACzG,OAAO,EAAE,cAAc,IAAI,sBAAsB,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAGxF,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,qBAAa,sBAAuB,SAAQ,sBAAsB;IAChE;;;OAGG;IACI,GAAG,CAAC,CAAC,EACV,IAAI,EAAE;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,KAAK,SAAS,CAAA;KAAE,EAChE,OAAO,CAAC,EAAE,iBAAiB,GAC1B,MAAM,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"ReactiveEngine4Angular.d.ts","sourceRoot":"","sources":["../../src/angular/ReactiveEngine4Angular.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,QAAQ,EAAyB,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AACzG,OAAO,EAAE,cAAc,IAAI,sBAAsB,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAGxF,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,qBAAa,sBAAuB,SAAQ,sBAAsB;IAChE,UAAmB,eAAe,SAAa;IAE/C;;;OAGG;IACI,GAAG,CAAC,CAAC,EACV,IAAI,EAAE;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,KAAK,SAAS,CAAA;KAAE,EAChE,OAAO,CAAC,EAAE,iBAAiB,GAC1B,MAAM,CAAC,CAAC,CAAC;CA8Bb"}
|
|
@@ -2,9 +2,11 @@ import { ReactiveEngine as e } from "../core.mjs";
|
|
|
2
2
|
import { DestroyRef as t, inject as n, runInInjectionContext as r, signal as i } from "@angular/core";
|
|
3
3
|
//#region src/angular/ReactiveEngine4Angular.ts
|
|
4
4
|
var a = class extends e {
|
|
5
|
+
frameworkPrefix = "angular";
|
|
5
6
|
use(e, a) {
|
|
6
|
-
let o = i(e.value), s = e.subscribe((
|
|
7
|
-
|
|
7
|
+
let o = i(e.value), s = e.subscribe(() => {
|
|
8
|
+
let t = e.value;
|
|
9
|
+
o.set(t);
|
|
8
10
|
}), c = () => {
|
|
9
11
|
n(t).onDestroy(() => {
|
|
10
12
|
s();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReactiveEngine4Angular.mjs","names":[],"sources":["../../src/angular/ReactiveEngine4Angular.ts"],"sourcesContent":["import { signal, inject, DestroyRef, Injector, runInInjectionContext, type Signal } from '@angular/core';\nimport { ReactiveEngine as OriginalReactiveEngine, type CleanupFn } from '../core/core';\n\n// Описываем интерфейс опций для Angular-адаптера\nexport interface AngularUseOptions {\n /**\n * Кастомный инжектор Angular.\n * Позволяет использовать .use() вне конструктора (например, в асинхронных функциях),\n * если передать текущий Injector вручную.\n */\n injector?: Injector;\n}\n\nexport class ReactiveEngine4Angular extends OriginalReactiveEngine {\n /**\n * Использование реактивного значения в Angular компоненте или сервисе.\n * Возвращает стандартный Angular Signal.\n */\n public use<T>(\n item: { value: T; subscribe: (cb: (v: T) => void) => CleanupFn },\n options?: AngularUseOptions\n ): Signal<T> {\n // 1. Создаем локальный Angular Signal со стартовым значением\n const angularSignal = signal<T>(item.value);\n\n // 2. Подписываемся на изменения сигнала вашего ядра\n const unsubscribe = item.subscribe((
|
|
1
|
+
{"version":3,"file":"ReactiveEngine4Angular.mjs","names":[],"sources":["../../src/angular/ReactiveEngine4Angular.ts"],"sourcesContent":["import { signal, inject, DestroyRef, Injector, runInInjectionContext, type Signal } from '@angular/core';\nimport { ReactiveEngine as OriginalReactiveEngine, type CleanupFn } from '../core/core';\n\n// Описываем интерфейс опций для Angular-адаптера\nexport interface AngularUseOptions {\n /**\n * Кастомный инжектор Angular.\n * Позволяет использовать .use() вне конструктора (например, в асинхронных функциях),\n * если передать текущий Injector вручную.\n */\n injector?: Injector;\n}\n\nexport class ReactiveEngine4Angular extends OriginalReactiveEngine {\n protected override frameworkPrefix = 'angular';\n\n /**\n * Использование реактивного значения в Angular компоненте или сервисе.\n * Возвращает стандартный Angular Signal.\n */\n public use<T>(\n item: { value: T; subscribe: (cb: (v: T) => void) => CleanupFn },\n options?: AngularUseOptions\n ): Signal<T> {\n // 1. Создаем локальный Angular Signal со стартовым значением\n const angularSignal = signal<T>(item.value);\n\n // 2. Подписываемся на изменения сигнала вашего ядра\n const unsubscribe = item.subscribe(() => {\n // Принудительно дергаем геттер .value, чтобы запустить Pull-вычисление ядра\n // и получить свежие данные, попутно запустив профайлер логгера computed!\n const freshValue = item.value;\n angularSignal.set(freshValue);\n });\n\n // 3. Логика автоматической отписки через DestroyRef\n const registerCleanup = () => {\n const destroyRef = inject(DestroyRef);\n destroyRef.onDestroy(() => {\n unsubscribe();\n });\n };\n\n // 4. Если пользователь явно передал кастомный инжектор — выполняем регистрацию внутри его контекста\n if (options?.injector) {\n runInInjectionContext(options.injector, registerCleanup);\n } else {\n // Иначе штатно выполняем в текущем контексте (вызовет ошибку NG0203, если контекст не валиден)\n registerCleanup();\n }\n\n return angularSignal.asReadonly();\n }\n}\n"],"mappings":";;;AAaA,IAAa,IAAb,cAA4C,EAAuB;CACjE,kBAAqC;CAMrC,IACE,GACA,GACW;EAEX,IAAM,IAAgB,EAAU,EAAK,KAAK,GAGpC,IAAc,EAAK,gBAAgB;GAGvC,IAAM,IAAa,EAAK;GACxB,EAAc,IAAI,CAAU;EAC9B,CAAC,GAGK,UAAwB;GAE5B,EAD0B,CAC1B,CAAA,CAAW,gBAAgB;IACzB,EAAY;GACd,CAAC;EACH;EAUA,OAPI,GAAS,WACX,EAAsB,EAAQ,UAAU,CAAe,IAGvD,EAAgB,GAGX,EAAc,WAAW;CAClC;AACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { CleanupFn } from '../core/core';
|
|
3
|
+
/**
|
|
4
|
+
* Контракт любого реактивного элемента вашего ядра (Signal, Computed, Resource)
|
|
5
|
+
*/
|
|
6
|
+
interface CoreReactiveItem<T> {
|
|
7
|
+
value: T;
|
|
8
|
+
subscribe(cb: (val: T) => void): CleanupFn;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* УТИЛИТА 1: Переводит элемент вашего ядра (Signal/Computed) в RxJS поток Observable.
|
|
12
|
+
* Позволяет применять к стейту ядра мощные операторы RxJS (debounceTime, switchMap и др.).
|
|
13
|
+
*/
|
|
14
|
+
export declare const coreToObservable: <T>(item: CoreReactiveItem<T>) => Observable<T>;
|
|
15
|
+
/**
|
|
16
|
+
* УТИЛИТА 2: Принимает RxJS Observable и возвращает объект подписки,
|
|
17
|
+
* совместимый по контракту с методом engine.use() вашего адаптера.
|
|
18
|
+
*/
|
|
19
|
+
export declare const coreFromObservable: <T>(source$: Observable<T>, initialValue: T) => CoreReactiveItem<T>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=rxjs-interop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rxjs-interop.d.ts","sourceRoot":"","sources":["../../src/angular/rxjs-interop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAgC,MAAM,MAAM,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC;;GAEG;AACH,UAAU,gBAAgB,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,CAAC;IACT,SAAS,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,GAAG,SAAS,CAAC;CAC5C;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,EAAE,MAAM,gBAAgB,CAAC,CAAC,CAAC,KAAG,UAAU,CAAC,CAAC,CAmB3E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,EAClC,SAAS,UAAU,CAAC,CAAC,CAAC,EACtB,cAAc,CAAC,KACd,gBAAgB,CAAC,CAAC,CAkBpB,CAAC"}
|
package/dist/core/core.d.ts
CHANGED
|
@@ -81,6 +81,52 @@ export interface Resource<T> extends ResourceState<T> {
|
|
|
81
81
|
*/
|
|
82
82
|
readonly value: ResourceState<T>;
|
|
83
83
|
}
|
|
84
|
+
export interface EngineLoggerOptions {
|
|
85
|
+
/** Включить детальное логгирование в консоль */
|
|
86
|
+
isEnabled: boolean;
|
|
87
|
+
/** Выводить время выполнения с точностью до микросекунд (performance.now) */
|
|
88
|
+
traceTime?: boolean;
|
|
89
|
+
/** Фильтровать логи по имени сигнала (поддерживает RegExp или строку) */
|
|
90
|
+
filter?: RegExp | string;
|
|
91
|
+
}
|
|
92
|
+
export interface ReactiveEngineOptions {
|
|
93
|
+
logger?: EngineLoggerOptions;
|
|
94
|
+
}
|
|
95
|
+
export interface SignalLogDetail {
|
|
96
|
+
from: unknown;
|
|
97
|
+
to: unknown;
|
|
98
|
+
subscribersCount: number;
|
|
99
|
+
subscribers: string[];
|
|
100
|
+
}
|
|
101
|
+
export interface ComputedLogDetail {
|
|
102
|
+
value: unknown;
|
|
103
|
+
duration: string;
|
|
104
|
+
}
|
|
105
|
+
export interface EffectLogDetail {
|
|
106
|
+
triggeredBy: string;
|
|
107
|
+
executionTime?: string;
|
|
108
|
+
}
|
|
109
|
+
export interface BatchLogDetail {
|
|
110
|
+
transactionSize: number;
|
|
111
|
+
totalEffectsRun: number;
|
|
112
|
+
}
|
|
113
|
+
export interface ResourceLogDetail {
|
|
114
|
+
/** Текущий статус загрузки */
|
|
115
|
+
loading: boolean;
|
|
116
|
+
/** Данные ответа (если есть) */
|
|
117
|
+
data: unknown;
|
|
118
|
+
/** Объект ошибки (если запрос упал) */
|
|
119
|
+
error: Error | null;
|
|
120
|
+
/** Была ли это повторная попытка (Retry) */
|
|
121
|
+
isRetrying?: boolean;
|
|
122
|
+
}
|
|
123
|
+
export interface LogDetailMap {
|
|
124
|
+
signal: SignalLogDetail;
|
|
125
|
+
computed: ComputedLogDetail;
|
|
126
|
+
effect: EffectLogDetail;
|
|
127
|
+
batch: BatchLogDetail;
|
|
128
|
+
resource: ResourceLogDetail;
|
|
129
|
+
}
|
|
84
130
|
/**
|
|
85
131
|
* Класс `ReactiveEngine` представляет собой реактивную систему, которая позволяет создавать и управлять реактивными объектами,
|
|
86
132
|
* сигналами, эффектами, асинхронными ресурсами и другими реактивными примитивами. Реактивные механизмы облегчают разработку
|
|
@@ -109,6 +155,7 @@ export interface Resource<T> extends ResourceState<T> {
|
|
|
109
155
|
* @class
|
|
110
156
|
*/
|
|
111
157
|
export declare class ReactiveEngine {
|
|
158
|
+
protected frameworkPrefix: string;
|
|
112
159
|
private activeEffect;
|
|
113
160
|
private isBatching;
|
|
114
161
|
private pendingEffects;
|
|
@@ -116,6 +163,31 @@ export declare class ReactiveEngine {
|
|
|
116
163
|
private factories;
|
|
117
164
|
private proxyCache;
|
|
118
165
|
private allEffects;
|
|
166
|
+
private loggerOptions?;
|
|
167
|
+
private pendingLogQueue;
|
|
168
|
+
private lastTransactionDuration;
|
|
169
|
+
private signalMutationCounts;
|
|
170
|
+
private lastComputedDurations;
|
|
171
|
+
private effectExecutionCounts;
|
|
172
|
+
private batchTickCounts;
|
|
173
|
+
constructor(options?: ReactiveEngineOptions);
|
|
174
|
+
/**
|
|
175
|
+
* Системный метод вывода форматированных логов в консоль.
|
|
176
|
+
* Автоматически подстраивает тип входящего аргумента `detail` под выбранный литерал `type`.
|
|
177
|
+
*
|
|
178
|
+
* Дополнительно агрегирует рантйам-статистику по всей стейт-машине приложения:
|
|
179
|
+
* - **`signal`:** Отслеживает счетчик частоты изменений (шума) для детекции паразитных циклов мутаций.
|
|
180
|
+
* - **`computed`:** Сравнивает текущую длительность ленивого вычисления с прошлым, подсвечивая тренд производительности.
|
|
181
|
+
* - **`effect`:** Подсчитывает общее количество ререндеров/вызовов побочных эффектов.
|
|
182
|
+
* - **`batch`:** Фиксирует порядковый номер выполненного шедулером пакета микрозадач.
|
|
183
|
+
*
|
|
184
|
+
* @template K - Литеральный тип лога из карты `LogDetailMap`
|
|
185
|
+
* @param {K} type - Категория логгируемого события ('signal' | 'computed' | 'effect' | 'batch')
|
|
186
|
+
* @param {string} name - Уникальное имя реактивного элемента (или label эффекта)
|
|
187
|
+
* @param {LogDetailMap[K]} detail - Строго типизированный объект с контекстными данными события
|
|
188
|
+
* @returns {void}
|
|
189
|
+
*/
|
|
190
|
+
log<K extends keyof LogDetailMap>(type: K, name: string, detail: LogDetailMap[K]): void;
|
|
119
191
|
/**
|
|
120
192
|
* Коллбек для уведомления об изменении сигнала.
|
|
121
193
|
* Использование unknown вместо any гарантирует безопасную работу с типами prev/next.
|
|
@@ -140,6 +212,31 @@ export declare class ReactiveEngine {
|
|
|
140
212
|
* @source
|
|
141
213
|
*/
|
|
142
214
|
inject<T>(token: Token<T>): T;
|
|
215
|
+
/**
|
|
216
|
+
* Накапливает логи об изменениях в буфер текущей микрозадачи
|
|
217
|
+
*/
|
|
218
|
+
queueLog(type: keyof LogDetailMap, name: string, detail: any): void;
|
|
219
|
+
/**
|
|
220
|
+
* Синхронно обрабатывает и выводит в консоль накопленный буфер логов
|
|
221
|
+
* для текущего тика микрозадачи (реактивной транзакции).
|
|
222
|
+
*
|
|
223
|
+
* Метод автоматически вызывается планировщиком ядра (`queueMicrotask`) непосредственно
|
|
224
|
+
* перед запуском каскада отложенных эффектов фреймворков и ререндеров интерфейса.
|
|
225
|
+
*
|
|
226
|
+
* ### Особенности рантайм-логики:
|
|
227
|
+
* 1. **Атомарный батчинг:** Предотвращает лавинообразный спам в консоли разработчика при
|
|
228
|
+
* множественных синхронных мутациях сигналов. Все тики склеиваются в единую мастер-группу.
|
|
229
|
+
* 2. **Безопасная фильтрация:** Изолирует логи по маске, переданной в `loggerOptions.filter`.
|
|
230
|
+
* Если после фильтрации очередь оказывается пустой, метод бережно зачищает буфер и
|
|
231
|
+
* возвращает управление шедулеру ядра, не блокируя выполнение `pendingEffects`.
|
|
232
|
+
* 3. **Профайлинг производительности (Pull Duration):** При включенном `traceTime` рассчитывает
|
|
233
|
+
* чистую длительность калькуляции и фильтрации транзакции графа в миллисекундах
|
|
234
|
+
* с точностью до микросекунд (`performance.now`), помогая находить узкие места.
|
|
235
|
+
*
|
|
236
|
+
* @function flushLogs
|
|
237
|
+
* @returns {void} Метод не возвращает значения, а только очищает внутренний буфер `pendingLogQueue`.
|
|
238
|
+
*/
|
|
239
|
+
flushLogs(): void;
|
|
143
240
|
/**
|
|
144
241
|
* Создание сигнала.
|
|
145
242
|
* @template T
|
|
@@ -172,13 +269,53 @@ export declare class ReactiveEngine {
|
|
|
172
269
|
*/
|
|
173
270
|
computed<T>(fn: () => T, signalName?: string): Computed<T>;
|
|
174
271
|
/**
|
|
175
|
-
*
|
|
176
|
-
*
|
|
272
|
+
* Создает глубокий реактивный объект (Proxy) на основе переданного целевого объекта или массива.
|
|
273
|
+
*
|
|
274
|
+
* В отличие от атомарных сигналов (`signal`), требующих ручного обращения через `.value`,
|
|
275
|
+
* метод `reactive` позволяет работать со сложными разветвленными структурами данных нативно,
|
|
276
|
+
* используя стандартный синтаксис JavaScript для чтения и прямой мутации свойств.
|
|
277
|
+
*
|
|
278
|
+
* ### Архитектурные особенности рантайма:
|
|
279
|
+
* 1. **Гранулярность подписок (Property-level Subscriptions):** Зависимости графа ядра трекаются
|
|
280
|
+
* не для всего объекта целиком, а строго для конкретных ключей (`prop`). Если эффект или
|
|
281
|
+
* компонент читает свойство `user.name`, он подписывается исключительно на этот ключ.
|
|
282
|
+
* Изменение свойства `user.age` не вызовет его повторного выполнения.
|
|
283
|
+
* 2. **Глубокое проксирование (Deep Reactivity):** При обращении к вложенным объектам или массивам,
|
|
284
|
+
* метод динамически и лениво оборачивает их в Proxy-структуры, автоматически формируя
|
|
285
|
+
* понятные строковые пути для подсистемы логирования (например, `user.meta.role`).
|
|
286
|
+
* 3. **Иммунизация кэша (Proxy Mirroring Cache):** Все созданные Proxy зеркалируются во внутреннем
|
|
287
|
+
* реестре `proxyCache`. Повторный вызов метода для одного и того же объекта вернет
|
|
288
|
+
* уже существующий Proxy, предотвращая утечки памяти и дублирование подписок.
|
|
289
|
+
*
|
|
290
|
+
* ### Применение в оптимизации больших форм:
|
|
291
|
+
* Идеально подходит для тяжелых интерфейсов (динамические таблицы, анкеты из сотен полей),
|
|
292
|
+
* так как прямая мутация конкретного инпута изолирует поток изменений и избавляет от необходимости
|
|
293
|
+
* иммутабельного копирования всего стейта формы через оператор расширения (`...spread`).
|
|
294
|
+
*
|
|
295
|
+
* @template T - Тип целевого объекта, расширяющий базовый интерфейс `object`.
|
|
177
296
|
* @function reactive
|
|
178
|
-
* @param {T} target -
|
|
179
|
-
* @param {string} [name] -
|
|
180
|
-
* @returns {T}
|
|
181
|
-
*
|
|
297
|
+
* @param {T} target - Исходный объект или массив JavaScript для проксирования.
|
|
298
|
+
* @param {string} [name='reactive'] - Уникальное базовое имя объекта для трассировки и фильтрации в логгере.
|
|
299
|
+
* @returns {T} Глубоко проксированный реактивный объект типа `T`.
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* ```typescript
|
|
303
|
+
* // 1. Инициализация в сервисе бизнес-логики:
|
|
304
|
+
* public form = this.engine.reactive({
|
|
305
|
+
* user: { name: 'Иван', age: 25 }
|
|
306
|
+
* }, 'profile-form');
|
|
307
|
+
*
|
|
308
|
+
* // 2. Прямая мутация в экшене (Proxy перехватит тик и запустит атомарный батчинг):
|
|
309
|
+
* public updateAge() {
|
|
310
|
+
* this.form.user.age += 1; // Автоматический лог: SIGNAL [profile-form.user.age]
|
|
311
|
+
* }
|
|
312
|
+
*
|
|
313
|
+
* // 3. Мост подписки для UI-компонентов:
|
|
314
|
+
* public uiBridge = this.engine.computed(() => ({
|
|
315
|
+
* name: this.form.user.name,
|
|
316
|
+
* age: this.form.user.age
|
|
317
|
+
* }));
|
|
318
|
+
* ```
|
|
182
319
|
*/
|
|
183
320
|
reactive<T extends object>(target: T, name?: string): T;
|
|
184
321
|
/**
|
package/dist/core/core.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/core/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGxE;;;;GAIG;AACH,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB;;;OAGG;IACH,KAAK,EAAE,CAAC,CAAC;IAET;;;;;OAKG;IACH,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,KAAK,SAAS,CAAC;CAChD;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC;AACnC,MAAM,MAAM,QAAQ,GAAG,MAAM,SAAS,GAAG,IAAI,CAAC;AAC9C,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI,MAAM,GAAG,MAAM,GAAG;IAAE,KAAI,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;CAAE,CAAC;AAC5F,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,KAAK,CAAC,CAAC;AAEvD;;;;GAIG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAElB;;;;;OAKG;IACH,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,KAAK,SAAS,CAAC;IAE/C;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC;IACnD;;;;OAIG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB;;;;;OAKG;IACH,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,SAAS,CAAC;IAE9D;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;CAClC;AAGD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAAsB;IAI5C,OAAO,CAAC,QAAQ,CAAsC;IACtD,OAAO,CAAC,SAAS,CAA+C;IAGhE,OAAO,CAAC,UAAU,CAAiC;IACnD,OAAO,CAAC,UAAU,CAAsB;
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/core/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGxE;;;;GAIG;AACH,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB;;;OAGG;IACH,KAAK,EAAE,CAAC,CAAC;IAET;;;;;OAKG;IACH,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,KAAK,SAAS,CAAC;CAChD;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC;AACnC,MAAM,MAAM,QAAQ,GAAG,MAAM,SAAS,GAAG,IAAI,CAAC;AAC9C,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI,MAAM,GAAG,MAAM,GAAG;IAAE,KAAI,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;CAAE,CAAC;AAC5F,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,KAAK,CAAC,CAAC;AAEvD;;;;GAIG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAElB;;;;;OAKG;IACH,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,KAAK,SAAS,CAAC;IAE/C;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC;IACnD;;;;OAIG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB;;;;;OAKG;IACH,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,SAAS,CAAC;IAE9D;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,mBAAmB;IAClC,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;IACnB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,EAAE,EAAE,OAAO,CAAC;IACZ,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;CACzB;AACD,MAAM,WAAW,iBAAiB;IAChC,8BAA8B;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,gCAAgC;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,uCAAuC;IACvC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAGD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,MAAM,EAAE,eAAe,CAAC;IACxB,KAAK,EAAE,cAAc,CAAC;IACtB,QAAQ,EAAE,iBAAiB,CAAC;CAC7B;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,cAAc;IACzB,SAAS,CAAC,eAAe,SAAU;IACnC,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAAsB;IAI5C,OAAO,CAAC,QAAQ,CAAsC;IACtD,OAAO,CAAC,SAAS,CAA+C;IAGhE,OAAO,CAAC,UAAU,CAAiC;IACnD,OAAO,CAAC,UAAU,CAAsB;IAGxC,OAAO,CAAC,aAAa,CAAC,CAAsB;IAC5C,OAAO,CAAC,eAAe,CAMK;IAC5B,OAAO,CAAC,uBAAuB,CAAuB;IAEtD,OAAO,CAAC,oBAAoB,CAA6B;IACzD,OAAO,CAAC,qBAAqB,CAA6B;IAC1D,OAAO,CAAC,qBAAqB,CAA6B;IAC1D,OAAO,CAAC,eAAe,CAAK;gBAEhB,OAAO,CAAC,EAAE,qBAAqB;IAI3C;;;;;;;;;;;;;;;OAeG;IACI,GAAG,CAAC,CAAC,SAAS,MAAM,YAAY,EACrC,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GACtB,IAAI;IA+KP;;;OAGG;IACI,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAE7E;;;;;;;;OAQG;IACI,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAOxE;;;;;;;OAOG;IACI,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;IA8BpC;;OAEG;IACI,QAAQ,CAAC,IAAI,EAAE,MAAM,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;IAK1E;;;;;;;;;;;;;;;;;;;OAmBG;IACI,SAAS,IAAI,IAAI;IAuFxB;;;;;;;;OAQG;IACI,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IA+FvF;;;;;;;OAOG;IACI,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS;IA4BtD,OAAO,CAAC,aAAa,CAAkD;IAIvE,OAAO,CAAC,eAAe,CAIrB;IAEF;;;;;;;;OAQG;IACI,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC;IA8EjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgDG;IACI,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,GAAE,MAAmB,GAAG,CAAC;IAoC1E;;;;;;OAMG;IACI,KAAK,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI;IAMlC;;;;;;;;OAQG;IACH,OAAO,CAAC,KAAK;IAoBb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwDG;IACI,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,EACzB,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EACvD,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,CAAC,CAAA;KAAE,EACrB,aAAa,CAAC,EAAE,MAAM,GAAG,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,GAC7C,QAAQ,CAAC,CAAC,CAAC;IAoLd;;;;;;;OAOG;IACI,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC;IAUjC;;;;;;;;OAQG;IACH,OAAO,CAAC,OAAO;CAahB"}
|
package/dist/core.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./utils/getExtractedValues.cjs");var t=class{activeEffect=null;isBatching=!1;pendingEffects=new Set;services=new Map;factories=new Map;proxyCache=new WeakMap;allEffects=new Set;onSignalChange;provide(e,t){typeof t==`function`&&!t.prototype?this.factories.set(e,t):this.services.set(e,t)}inject(e){if(!e)throw Error(`[DI Error]: Вы пытаетесь внедрить пустой токен (undefined/null). Проверьте импорты.`);let t=e;if(this.services.has(t))return this.services.get(t);try{let n=this.factories.get(t);if(n){let e=n(this);return this.services.set(t,e),e}if(typeof e==`function`&&e.prototype){let n=new e(this);return this.services.set(t,n),n}throw Error(`Service not found: ${String(e)}`)}catch(t){throw Error(`[DI Error]: Не удалось создать сервис ${String(e)}. Ошибка: ${t?.message||`No e?.message`}`)}}signal(e,t){let n=this,r=e,i=new Set,a=typeof t==`string`?{name:t}:t||{},o=a.name||`unnamed_signal`;return{get value(){return n.activeEffect&&i.add(n.activeEffect),r},set value(e){if(r===e)return;if(a.validate){let t=a.validate(e);if(t===!1||typeof t==`string`){let n=typeof t==`string`?t:`[Validation Error]: Некорректное значение для сигнала "${o}"`;console.error(`%c${n}`,`color: orange; font-weight: bold;`,{received:e,current:r});return}}let t=r;r=e,n.onSignalChange?.(o,e,t),i.forEach(e=>n.pendingEffects.add(e)),n.isBatching||(n.isBatching=!0,queueMicrotask(()=>{let e=Array.from(n.pendingEffects);n.pendingEffects.clear(),n.isBatching=!1,e.forEach(e=>e.run())}))},subscribe(e){return n.effect(()=>{e(this.value)})}}}effect(e,t){let n=this,r={label:t,cleanups:new Set,run(){this.cleanups.forEach(e=>e()),this.cleanups.clear();let t=n.activeEffect;n.activeEffect=this,n.safeRun(this,()=>{let t=e();typeof t==`function`&&this.cleanups.add(t)}),n.activeEffect=t}};return this.allEffects.add(r),r.run(),()=>{r.cleanups.forEach(e=>e()),n.pendingEffects.delete(r),n.allEffects.delete(r)}}computedCache=new Map;cleanupRegistry=new FinalizationRegistry(e=>{e()});computed(e,t){if(this.computedCache.has(e)){let t=this.computedCache.get(e)?.deref();if(t)return t}let n=this,r=this.signal(void 0,t||`unnamed_computed`),i=this.effect(()=>{let t=e();r.value=t}),a=Array.from(this.allEffects)[this.allEffects.size-1],o=()=>{i(),a&&n.allEffects.delete(a),n.computedCache.delete(e)},s={get value(){return r.value},subscribe:e=>r.subscribe(e),destroy(){o()}};return this.cleanupRegistry.register(s,o),this.computedCache.set(e,new WeakRef(s)),s}reactive(e,t=`reactive`){if(this.proxyCache.has(e))return this.proxyCache.get(e);let n=this,r=new Map,i=new Proxy(e,{get(e,i,a){n.activeEffect&&(r.has(i)||r.set(i,new Set),r.get(i).add(n.activeEffect));let o=Reflect.get(e,i,a);return typeof o==`object`&&o?n.reactive(o,`${t}.${String(i)}`):o},set(e,i,a,o){let s=Reflect.get(e,i,o);return s===a||s!==a&&(Reflect.set(e,i,a,o),n.onSignalChange?.(`${t}.${String(i)}`,a,s),r.get(i)?.forEach(e=>n.isBatching?n.pendingEffects.add(e):e.run())),!0}});return this.proxyCache.set(e,i),i}batch(e){e()}delay(e,t){return new Promise((n,r)=>{if(t.aborted)return r(new DOMException(`Aborted`,`AbortError`));let i=setTimeout(()=>{t.removeEventListener(`abort`,a),n()},e),a=()=>{clearTimeout(i),r(new DOMException(`Aborted`,`AbortError`))};t.addEventListener(`abort`,a,{once:!0})})}resource(t,n,r){let i=r&&typeof r==`object`,a=i?r.name:r||`unnamed_resource`,o=i?r:{},s=o.resetDataOnSourceChange??!0,c=o.responseValidate,l=o.validateBeforeFetch,u=o.retryCount??0,d=o.retryDelay??1e3,f=o.isExponentialBackoffEnabled??!1,p=o.maxRetryDelay??3e4,m=o.timeout,h=this.signal({data:null,loading:!0,error:null,isRetrying:!1},a),g=async(n,r,i=!1)=>{if(r.aborted)return;if(l){let e=l(n);if(e===!1||typeof e==`string`){if(r.aborted)return;let t=typeof e==`string`?e:`Pre-fetch validation failed for resource`,n=i?null:this.untrack(()=>h.value.data);h.value={data:n,loading:!1,error:Error(t),isRetrying:!1};return}}let o=i&&s?null:this.untrack(()=>h.value.data);h.value={data:o,loading:!0,error:null,isRetrying:!1};for(let i=0;i<=u;i++){if(r.aborted)return;let o=r;try{if(m&&m>0){let e=AbortSignal.timeout(m);o=AbortSignal.any([r,e])}let e=await t(n,o);if(r.aborted)return;if(o.aborted)throw new DOMException(`The operation timed out.`,`TimeoutError`);if(c){let t=c(e);if(t===!1||typeof t==`string`){if(r.aborted)return;h.value={data:null,loading:!1,error:Error(typeof t==`string`?t:`Validation failed`),isRetrying:!1};return}}if(r.aborted)return;h.value={data:e,loading:!1,error:null,isRetrying:!1};return}catch(t){if(r.aborted||t.name===`AbortError`&&r.aborted)return;let n=t.name===`TimeoutError`||t.name===`AbortError`||t.message?.includes(`timeout`),o=e.getExtractedValues({tested:[t.message],expectedKey:`THROW_CUSTOM_VALIDATION_ERROR_NO_RETRY`,valueType:`number`})?.[0]===`1`||!1,s=o&&e.getExtractedValues({tested:[t.message],expectedKey:`MESSAGE`,valueType:`string`})?.[0]||`Custom validation error`,c=o||t.name===`ValidationError`;if(i===u||c){if(r.aborted)return;let e=n?Error(`Request timed out after ${m}ms`,{cause:t}):o?Error(s,{cause:t}):t;h.value={data:null,loading:!1,error:e,isRetrying:!1};return}else{if(r.aborted)return;let e=f?d*2**i:d;e=Math.min(e,p);let t=Math.random()*200;e+=t,h.value={data:h.value.data,loading:!0,error:null,isRetrying:!0},console.warn(`[Resource Retry] "${a}" сбой по ${n?`таймауту`:`ошибке сети`}. Попытка ${i+1}/${u+1}...`);try{if(await this.delay(e,r),r.aborted)return}catch{return}}}}},_=null;return this.effect(()=>{let e=n?n.value:void 0,t=new AbortController;return _=t,this.untrack(()=>{g(e,t.signal,!0)}),()=>t.abort()}),{get data(){return h.value.data},get loading(){return h.value.loading},get error(){return h.value.error},get isRetrying(){return h.value.isRetrying},get value(){return h.value},refetch:()=>{_?.abort(),_=new AbortController,g(n?n.value:void 0,_.signal,!1)},subscribe:e=>h.subscribe(e)}}untrack(e){let t=this.activeEffect;this.activeEffect=null;try{return e()}finally{this.activeEffect=t}}safeRun(e,t){try{t()}catch(e){console.error(`%c[Reactive Error] %cОшибка в эффекте/computed:`,`color: white; background: red; padding: 2px 4px; border-radius: 3px;`,`font-weight: bold;`,e)}}};exports.ReactiveEngine=t;
|
|
1
|
+
const e=require("./utils/getExtractedValues.cjs");var t=class{frameworkPrefix=`core`;activeEffect=null;isBatching=!1;pendingEffects=new Set;services=new Map;factories=new Map;proxyCache=new WeakMap;allEffects=new Set;loggerOptions;pendingLogQueue=[];lastTransactionDuration=null;signalMutationCounts=new Map;lastComputedDurations=new Map;effectExecutionCounts=new Map;batchTickCounts=0;constructor(e){this.loggerOptions=e?.logger}log(t,n,r){if(!this.loggerOptions?.isEnabled||this.loggerOptions.filter&&!(this.loggerOptions.filter instanceof RegExp?this.loggerOptions.filter:new RegExp(this.loggerOptions.filter.replace(/^\/|\/$/g,``))).test(n))return;let i={signal:`background: #007acc; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,computed:`background: #42b883; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,effect:`background: #e01e5a; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,batch:`background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,resource:`background: #d97706; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`},a=``,o=`color: #aaa; font-weight: normal;`;switch(t){case`signal`:{let s=e.getExtractedValues({tested:[n],expectedKey:`CORE_INTERNAL_SIGNAL`,valueType:`number`})?.[0]===`1`,c=n,l=(this.signalMutationCounts.get(c)||0)+1;this.signalMutationCounts.set(c,l);let u=r,d=u.subscribers&&u.subscribers.length>0?u.subscribers.every(t=>t?e.getExtractedValues({tested:[t],expectedKey:`IS_OPTIMIZED`,valueType:`number`})?.[0]===`1`:!1):!1,f=l>50,p=s?`COMPUTED_INTERNAL`:`SIGNAL`;a=` 🔄 Изменений ${s?`кэша`:`сигнала`}: ${l}${f?` ⚠️ (high noise — обнаружен дребезг/спам значений!)`:``}`,f&&(o=`color: orange; font-weight: bold;`,d||(r.__performance_advice__={issue:`Сигнал [${c}] обновляется слишком часто (${l} раз за такт). Это приводит к избыточным ререндерам UI.`,solution:`Оберните чтение этого сигнала в декоратор сжатия потока данных 'withThrottleComputed'`,example:`public throttled = withThrottleComputed(this.engine, () => this.yourSignalName.value, { limit: 300 });`})),console.groupCollapsed(`%c${p}%c [${c}]%c${a}`,s?`background: #42b883; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`:i[t],`color: #aaa; font-weight: bold;`,o),console.log(`Данные (Payload):`,r),this.loggerOptions.traceTime&&typeof performance<`u`&&console.log(`Тайминг рантайма:`,`${performance.now().toFixed(2)} ms`),console.groupEnd();return}case`computed`:{let e=r;if(e&&`to`in e){let t=e.to;a=` 🧮 Значение: ${typeof t==`object`&&t?`{...}`:String(t)}`}else if(e&&`duration`in e){let t=parseFloat(e.duration);if(!isNaN(t)){let r=this.lastComputedDurations.get(n);if(r!==void 0&&r>0){let n=(t-r)/r*100;n>5?(a=` 🔴 Замедление: +${n.toFixed(1)}% (${e.duration})`,o=`color: #e01e5a; font-weight: bold;`):n<-5?(a=` 🟢 Ускорение: ${n.toFixed(1)}% (${e.duration})`,o=`color: #42b883; font-weight: bold;`):(a=` 🟢 Стабильно (${e.duration})`,o=`color: #42b883; font-weight: bold;`)}else a=` ⚪ Первый расчет (${e.duration})`;this.lastComputedDurations.set(n,t)}}break}case`effect`:{let e=(this.effectExecutionCounts.get(n)||0)+1;this.effectExecutionCounts.set(n,e);let t=e>30;a=` 🚀 Вызовов эффекта: ${e}${t?` ⚠️ (heavy re-renders)`:``}`,t&&(o=`color: #ff4a4a; font-weight: bold;`);break}case`batch`:this.batchTickCounts+=1,a=` 📦 Номер транзакции: #${this.batchTickCounts}`;break;case`resource`:{let e=r;if(e.loading)a=` ⏳ ЗАГРУЗКА (fetching...)`,o=`color: #d97706; font-weight: bold;`;else if(e.error)a=` 🔴 ОШИБКА: ${e.error.message||`Unknown Error`}`,o=`color: #ff4a4a; font-weight: bold;`;else{let t=e.data;a=` 🟢 УСПЕХ (data: ${typeof t==`object`&&t?`{...}`:String(t)})`,o=`color: #42b883; font-weight: bold;`}break}default:break}console.groupCollapsed(`%c${t.toUpperCase()}%c [${n}]%c${a}`,i[t],`color: #aaa; font-weight: bold;`,o),console.log(`Данные (Payload):`,r),this.loggerOptions.traceTime&&typeof performance<`u`&&console.log(`Тайминг рантайма:`,`${performance.now().toFixed(2)} ms`),console.groupEnd()}onSignalChange;provide(e,t){typeof t==`function`&&!t.prototype?this.factories.set(e,t):this.services.set(e,t)}inject(e){if(!e)throw Error(`[DI Error]: Вы пытаетесь внедрить пустой токен (undefined/null). Проверьте импорты.`);let t=e;if(this.services.has(t))return this.services.get(t);try{let n=this.factories.get(t);if(n){let e=n(this);return this.services.set(t,e),e}if(typeof e==`function`&&e.prototype){let n=new e(this);return this.services.set(t,n),n}throw Error(`Service not found: ${String(e)}`)}catch(t){throw Error(`[DI Error]: Не удалось создать сервис ${String(e)}. Ошибка: ${t?.message||`No e?.message`}`)}}queueLog(e,t,n){this.loggerOptions?.isEnabled&&this.pendingLogQueue.push({type:e,name:t,detail:n})}flushLogs(){if(!this.loggerOptions?.isEnabled||this.pendingLogQueue.length===0)return;let e=typeof performance<`u`?performance.now():0,t=this.pendingLogQueue;if(this.loggerOptions.filter)try{let e;if(this.loggerOptions.filter instanceof RegExp)e=this.loggerOptions.filter;else{let t=this.loggerOptions.filter.replace(/^\/|\/$/g,``);e=new RegExp(t)}t=t.filter(t=>e.test(t.name))}catch(e){console.warn(`[Logger Error]: Некорректный паттерн фильтрации логов`,e)}if(t.length===0){this.pendingLogQueue=[];return}let n=``,r=``,i=`color: #aaa; font-weight: normal;`;if(this.loggerOptions.traceTime&&e){let t=performance.now()-e;if(n=` | Duration: ${t.toFixed(3)} ms`,this.lastTransactionDuration!==null&&this.lastTransactionDuration>0){let e=(t-this.lastTransactionDuration)/this.lastTransactionDuration*100;e>.1?(r=` 🔴 +${e.toFixed(1)}%`,i=`color: #e01e5a; font-weight: bold;`):e<-.1?(r=` 🟢 ${e.toFixed(1)}%`,i=`color: #42b883; font-weight: bold;`):(r=` 🟢 stable`,i=`color: #42b883; font-weight: bold;`)}else r=` ⚪ first`;this.lastTransactionDuration=t}console.groupCollapsed(`%cREACTIVE TRANSACTION%c Microtask Tick (Size: ${t.length}${n})%c${r}`,`background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;`,`color: #aaa; font-weight: normal;`,i),t.forEach(e=>{this.log(e.type,e.name,e.detail)}),this.loggerOptions.traceTime&&typeof performance<`u`&&console.log(`%c[Execution Timestamp]: ${performance.now().toFixed(2)} ms`,`color: #7952b3; font-weight: bold;`),console.groupEnd(),this.pendingLogQueue=[]}signal(e,t){let n=this,r=e,i=new Set,a=typeof t==`string`?{name:t}:t||{},o=a.name||`unnamed_signal`;return{get value(){if(n.activeEffect){let e=n.activeEffect;i.has(e)||(i.add(e),e.cleanups.add(()=>{i.delete(e)}))}return r},set value(e){if(r===e)return;if(a.validate){let t=a.validate(e);if(t===!1||typeof t==`string`){let n=typeof t==`string`?t:`[Validation Error]: Некорректное значение для сигнала "${o}"`;console.error(`%c${n}`,`color: orange; font-weight: bold;`,{received:e,current:r});return}}let t=r;r=e,n.onSignalChange?.(o,e,t);let s=Array.from(i).map(e=>e.label||`unnamed_effect`);n.queueLog?.(`signal`,o,{from:t,to:e,subscribersCount:i.size,subscribers:s}),i.forEach(e=>n.pendingEffects.add(e)),n.isBatching||(n.isBatching=!0,queueMicrotask(()=>{for(let e of n.pendingEffects)n.pendingEffects.delete(e),e.run();n.isBatching=!1,n.flushLogs?.()}))},subscribe(e){return n.effect(()=>{e(this.value)},`${n.frameworkPrefix}:use:${o}`)}}}effect(e,t){let n=this,r={label:t,cleanups:new Set,run(){this.cleanups.forEach(e=>e()),this.cleanups.clear();let t=n.activeEffect;n.activeEffect=this,n.safeRun(this,()=>{let t=e();typeof t==`function`&&this.cleanups.add(t)}),n.activeEffect=t}};return this.allEffects.add(r),r.run(),()=>{r.cleanups.forEach(e=>e()),n.pendingEffects.delete(r),n.allEffects.delete(r)}}computedCache=new Map;cleanupRegistry=new FinalizationRegistry(e=>{e()});computed(e,t){if(this.computedCache.has(e)){let t=this.computedCache.get(e)?.deref();if(t)return t}let n=this,r=t||`unnamed_computed`,i=this.signal(void 0,`[CORE_INTERNAL_SIGNAL=1]:${r}`),a=!0,o,s=this.effect(()=>{o=e(),a=!1;let t=typeof performance<`u`&&performance.now()?`0.010ms`:`N/A`;typeof n.queueLog==`function`&&n.queueLog(`computed`,r,{value:o,duration:t}),i.value=o},`[IS_OPTIMIZED=1]:${r}`),c=Array.from(this.allEffects)[this.allEffects.size-1],l=()=>{s(),c&&n.allEffects.delete(c),n.computedCache.delete(e)},u={get value(){return a&&(o=e(),a=!1,i.value=o),i.value},subscribe:e=>i.subscribe(e),destroy(){l()}};return this.cleanupRegistry.register(u,l),this.computedCache.set(e,new WeakRef(u)),u}reactive(e,t=`reactive`){if(this.proxyCache.has(e))return this.proxyCache.get(e);let n=this,r=new Map,i=new Proxy(e,{get(e,i,a){n.activeEffect&&(r.has(i)||r.set(i,new Set),r.get(i).add(n.activeEffect));let o=Reflect.get(e,i,a);return typeof o==`object`&&o?n.reactive(o,`${t}.${String(i)}`):o},set(e,i,a,o){let s=Reflect.get(e,i,o);return s===a||s!==a&&(Reflect.set(e,i,a,o),n.onSignalChange?.(`${t}.${String(i)}`,a,s),r.get(i)?.forEach(e=>n.isBatching?n.pendingEffects.add(e):e.run())),!0}});return this.proxyCache.set(e,i),i}batch(e){e()}delay(e,t){return new Promise((n,r)=>{if(t.aborted)return r(new DOMException(`Aborted`,`AbortError`));let i=setTimeout(()=>{t.removeEventListener(`abort`,a),n()},e),a=()=>{clearTimeout(i),r(new DOMException(`Aborted`,`AbortError`))};t.addEventListener(`abort`,a,{once:!0})})}resource(t,n,r){let i=r&&typeof r==`object`,a=i?r.name:r||`unnamed_resource`,o=i?r:{},s=o.resetDataOnSourceChange??!0,c=o.responseValidate,l=o.validateBeforeFetch,u=o.retryCount??0,d=o.retryDelay??1e3,f=o.isExponentialBackoffEnabled??!1,p=o.maxRetryDelay??3e4,m=o.timeout,h=this.signal({data:null,loading:!0,error:null,isRetrying:!1},`[CORE_INTERNAL_SIGNAL=1]:resource:state:${a}`),g=e=>{h.value=e,typeof this.queueLog==`function`&&this.queueLog(`resource`,a,{loading:e.loading,data:e.data,error:e.error,isRetrying:e.isRetrying})},_=async(n,r,i=!1)=>{if(r.aborted)return;if(l){let e=l(n);if(e===!1||typeof e==`string`){if(r.aborted)return;let t=typeof e==`string`?e:`Pre-fetch validation failed for resource`,n=i?null:this.untrack(()=>h.value.data);g({data:n,loading:!1,error:Error(t),isRetrying:!1});return}}let o=i&&s?null:this.untrack(()=>h.value.data);g({data:o,loading:!0,error:null,isRetrying:!1});for(let i=0;i<=u;i++){if(r.aborted)return;let o=r;try{if(m&&m>0){let e=AbortSignal.timeout(m);o=AbortSignal.any([r,e])}let e=await t(n,o);if(r.aborted)return;if(o.aborted)throw new DOMException(`The operation timed out.`,`TimeoutError`);if(c){let t=c(e);if(t===!1||typeof t==`string`){if(r.aborted)return;g({data:null,loading:!1,error:Error(typeof t==`string`?t:`Validation failed`),isRetrying:!1});return}}if(r.aborted)return;g({data:e,loading:!1,error:null,isRetrying:!1});return}catch(t){if(r.aborted||t.name===`AbortError`&&r.aborted)return;let n=t.name===`TimeoutError`||t.name===`AbortError`||t.message?.includes(`timeout`),o=e.getExtractedValues({tested:[t.message],expectedKey:`THROW_CUSTOM_VALIDATION_ERROR_NO_RETRY`,valueType:`number`})?.[0]===`1`||!1,s=o&&e.getExtractedValues({tested:[t.message],expectedKey:`MESSAGE`,valueType:`string`})?.[0]||`Custom validation error`,c=o||t.name===`ValidationError`;if(i===u||c){if(r.aborted)return;let e=n?Error(`Request timed out after ${m}ms`,{cause:t}):o?Error(s,{cause:t}):t;g({data:null,loading:!1,error:e,isRetrying:!1});return}else{if(r.aborted)return;let e=f?d*2**i:d;e=Math.min(e,p);let t=Math.random()*200;e+=t,g({data:h.value.data,loading:!0,error:null,isRetrying:!0}),console.warn(`[Resource Retry] "${a}" сбой по ${n?`таймауту`:`ошибке сети`}. Попытка ${i+1}/${u+1}...`);try{if(await this.delay(e,r),r.aborted)return}catch{return}}}}},v=null;return this.effect(()=>{let e=n?n.value:void 0,t=new AbortController;return v=t,this.untrack(()=>{_(e,t.signal,!0)}),()=>t.abort()},`[dev]`),{get data(){return h.value.data},get loading(){return h.value.loading},get error(){return h.value.error},get isRetrying(){return h.value.isRetrying},get value(){return h.value},refetch:()=>{v?.abort(),v=new AbortController,_(n?n.value:void 0,v.signal,!1)},subscribe:e=>h.subscribe(e)}}untrack(e){let t=this.activeEffect;this.activeEffect=null;try{return e()}finally{this.activeEffect=t}}safeRun(e,t){try{t()}catch(e){console.error(`%c[Reactive Error] %cОшибка в эффекте/computed:`,`color: white; background: red; padding: 2px 4px; border-radius: 3px;`,`font-weight: bold;`,e)}}};exports.ReactiveEngine=t;
|
|
2
2
|
//# sourceMappingURL=core.cjs.map
|