@pravosleva/reactive-engine 2.6.4-alpha.9 → 2.6.5
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 +8 -8
- package/dist/core/core.d.ts +160 -0
- package/dist/core/core.d.ts.map +1 -1
- package/dist/core/core.method.computed.d.ts.map +1 -1
- package/dist/core.method.computed.mjs +3 -2
- package/dist/core.method.computed.mjs.map +1 -1
- package/dist/core.method.resource.mjs +3 -1
- package/dist/core.method.resource.mjs.map +1 -1
- package/dist/core.mjs.map +1 -1
- package/dist/decorators/withValidation/withValidation.d.ts +2 -2
- package/dist/decorators/withValidation/withValidation.d.ts.map +1 -1
- package/dist/decorators/withValidation/withValidation.mjs +1 -1
- package/dist/decorators/withValidation/withValidation.mjs.map +1 -1
- package/dist/decorators/withValidation/withValidation.test.d.ts +2 -0
- package/dist/decorators/withValidation/withValidation.test.d.ts.map +1 -0
- package/dist/index.mjs +2 -2
- package/dist/reactive-engine.umd.js +2 -2
- package/dist/reactive-engine.umd.js.map +1 -1
- package/package.json +8 -11
- package/dist/AbstractService.cjs +0 -2
- package/dist/AbstractService.cjs.map +0 -1
- package/dist/BaseREServiceEnhanced.cjs +0 -2
- package/dist/BaseREServiceEnhanced.cjs.map +0 -1
- package/dist/angular/core.angular-adapter.cjs +0 -2
- package/dist/angular/core.angular-adapter.cjs.map +0 -1
- package/dist/angular/index.cjs +0 -2
- package/dist/angular/index.cjs.map +0 -1
- package/dist/core.autolog.cjs +0 -2
- package/dist/core.autolog.cjs.map +0 -1
- package/dist/core.automatic.cjs +0 -2
- package/dist/core.automatic.cjs.map +0 -1
- package/dist/core.cjs +0 -2
- package/dist/core.cjs.map +0 -1
- package/dist/core.global.cjs +0 -2
- package/dist/core.global.cjs.map +0 -1
- package/dist/core.logger.cjs +0 -2
- package/dist/core.logger.cjs.map +0 -1
- package/dist/core.method.computed.cjs +0 -2
- package/dist/core.method.computed.cjs.map +0 -1
- package/dist/core.method.effect.cjs +0 -2
- package/dist/core.method.effect.cjs.map +0 -1
- package/dist/core.method.reactive.cjs +0 -2
- package/dist/core.method.reactive.cjs.map +0 -1
- package/dist/core.method.resource.cjs +0 -2
- package/dist/core.method.resource.cjs.map +0 -1
- package/dist/core.method.signal.cjs +0 -2
- package/dist/core.method.signal.cjs.map +0 -1
- package/dist/core.method.untrack.cjs +0 -2
- package/dist/core.method.untrack.cjs.map +0 -1
- package/dist/decorators/withCache/withCache.cjs +0 -2
- package/dist/decorators/withCache/withCache.cjs.map +0 -1
- package/dist/decorators/withDebounce/withDebounce.cjs +0 -2
- package/dist/decorators/withDebounce/withDebounce.cjs.map +0 -1
- package/dist/decorators/withLongPolling/withLongPolling.cjs +0 -2
- package/dist/decorators/withLongPolling/withLongPolling.cjs.map +0 -1
- package/dist/decorators/withStaleWhileRevalidate/withStaleWhileRevalidate.cjs +0 -2
- package/dist/decorators/withStaleWhileRevalidate/withStaleWhileRevalidate.cjs.map +0 -1
- package/dist/decorators/withThrottle/withThrottle.cjs +0 -2
- package/dist/decorators/withThrottle/withThrottle.cjs.map +0 -1
- package/dist/decorators/withThrottleAndCache/withThrottleAndCache.cjs +0 -2
- package/dist/decorators/withThrottleAndCache/withThrottleAndCache.cjs.map +0 -1
- package/dist/decorators/withThrottleComputed/withThrottleComputed.cjs +0 -2
- package/dist/decorators/withThrottleComputed/withThrottleComputed.cjs.map +0 -1
- package/dist/decorators/withValidation/withValidation.cjs +0 -2
- package/dist/decorators/withValidation/withValidation.cjs.map +0 -1
- package/dist/index.cjs +0 -1
- package/dist/index2.cjs +0 -1
- package/dist/react/hooks/useReactiveSubscription.cjs +0 -2
- package/dist/react/hooks/useReactiveSubscription.cjs.map +0 -1
- package/dist/react/hooks/useReactiveValue.cjs +0 -2
- package/dist/react/hooks/useReactiveValue.cjs.map +0 -1
- package/dist/react/index.cjs +0 -2
- package/dist/react/index.cjs.map +0 -1
- package/dist/utils/getExtractedValues.cjs +0 -2
- package/dist/utils/getExtractedValues.cjs.map +0 -1
- package/dist/vue/core.vue-adapter.cjs +0 -2
- package/dist/vue/core.vue-adapter.cjs.map +0 -1
- package/dist/vue/index.cjs +0 -2
- package/dist/vue/index.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -213,7 +213,7 @@ Copying external async state directly into local mutable signals introduces sync
|
|
|
213
213
|
// ❌ Mirroring Trap (Causes redundant pushes & recursion)
|
|
214
214
|
engine.effect(() => {
|
|
215
215
|
const data = apiResource.data
|
|
216
|
-
if (data) localSignal.value = data
|
|
216
|
+
if (data) localSignal.value = data
|
|
217
217
|
})
|
|
218
218
|
|
|
219
219
|
// 🟢 Canonical Pattern (Pure Lazy Derivation)
|
|
@@ -229,7 +229,7 @@ To eliminate parent re-renders in deep form trees, push state subscriptions down
|
|
|
229
229
|
## Architecture-Grade Form Wizard (Example 254)
|
|
230
230
|
This enterprise-ready multi-step form implementation demonstrates absolute separation of concerns: state, cross-step validation schemas, and API fetch mechanics are completely decoupled from React.
|
|
231
231
|
|
|
232
|
-
1. Action Validation Decorators (`
|
|
232
|
+
1. Action Validation Decorators (`withValidationSync.ts`)
|
|
233
233
|
```ts
|
|
234
234
|
import { Signal } from '@pravosleva/reactive-engine'
|
|
235
235
|
|
|
@@ -242,7 +242,7 @@ export interface ValidationOptions<T> {
|
|
|
242
242
|
errorsSignal: Signal<Record<string, string>>
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
export const
|
|
245
|
+
export const withValidationSync = <T, Args extends any[], R>(
|
|
246
246
|
targetAction: (...args: Args) => R,
|
|
247
247
|
options: ValidationOptions<T>
|
|
248
248
|
) => {
|
|
@@ -269,7 +269,7 @@ export const withValidation = <T, Args extends any[], R>(
|
|
|
269
269
|
}
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
-
export const
|
|
272
|
+
export const withValidationAsync = <S, T>(
|
|
273
273
|
fetcher: (source: S, signal: AbortSignal) => Promise<T>,
|
|
274
274
|
options: ValidationOptions<S>
|
|
275
275
|
) => {
|
|
@@ -299,7 +299,7 @@ export const withResourceValidation = <S, T>(
|
|
|
299
299
|
2. Domain & Validation Service (`FormWizardLogic.ts`)
|
|
300
300
|
```tsx
|
|
301
301
|
import { AbstractService } from '@pravosleva/reactive-engine'
|
|
302
|
-
import {
|
|
302
|
+
import { withValidationSync, withValidationAsync, ValidationSchema } from './withValidation'
|
|
303
303
|
|
|
304
304
|
export interface WizardState {
|
|
305
305
|
step: number
|
|
@@ -345,15 +345,15 @@ export class FormWizardLogic extends AbstractService {
|
|
|
345
345
|
private profileSource = this.engine.computed(() => this.formData.profile)
|
|
346
346
|
|
|
347
347
|
public nextToProfileStep = () => {
|
|
348
|
-
const action =
|
|
348
|
+
const action = withValidationSync(() => {
|
|
349
349
|
this.currentStep.value = 2
|
|
350
350
|
}, { schema: this.accountSchema, errorsSignal: this.errors })
|
|
351
|
-
|
|
351
|
+
|
|
352
352
|
action(this.formData.account)
|
|
353
353
|
}
|
|
354
354
|
|
|
355
355
|
public submitWizardForm = this.engine.resource(
|
|
356
|
-
|
|
356
|
+
withValidationAsync(
|
|
357
357
|
async (profileData, abortSignal) => {
|
|
358
358
|
return this.api.submitWizard(profileData)
|
|
359
359
|
},
|
package/dist/core/core.d.ts
CHANGED
|
@@ -40,7 +40,111 @@ export declare class ReactiveEngineCore {
|
|
|
40
40
|
batch<T>(fn: () => T): T;
|
|
41
41
|
flushEffects(): void;
|
|
42
42
|
log(params: any): void;
|
|
43
|
+
/**
|
|
44
|
+
* Создает новый реактивный сигнал с заданным начальным значением и опциями.
|
|
45
|
+
*
|
|
46
|
+
* @template T Тип значения сигнала.
|
|
47
|
+
* @param initialValue Начальное значение сигнала.
|
|
48
|
+
* @param optionsOrName Конфигурационные параметры сигнала или его имя.
|
|
49
|
+
* @param optionsOrName.name Уникальное текстовое имя сигнала для отладки и вывода в транзакционный логгер.
|
|
50
|
+
* @param optionsOrName.validate Пользовательская функция валидации значения, возвращающая true/false или строку ошибки.
|
|
51
|
+
*
|
|
52
|
+
* @method signal
|
|
53
|
+
* @memberof ReactiveEngine
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* // Пример 1: Чистый TypeScript (Базовый)
|
|
57
|
+
* const engine = new ReactiveEngineCore();
|
|
58
|
+
* const counter = engine.signal(0, { name: 'counter' });
|
|
59
|
+
* console.log(counter.value); // 0
|
|
60
|
+
* counter.value = 1;
|
|
61
|
+
* console.log(counter.value); // 1
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* // Пример 2: Чистый TypeScript (Продвинутый с валидацией)
|
|
65
|
+
* interface User { id: number; name: string; }
|
|
66
|
+
* const engine = new ReactiveEngineCore();
|
|
67
|
+
* const userSignal = engine.signal<User>({ id: 1, name: 'John Doe' }, {
|
|
68
|
+
* name: 'user',
|
|
69
|
+
* validate: (user) => user.id > 0 && typeof user.name === 'string'
|
|
70
|
+
* });
|
|
71
|
+
* userSignal.value = { id: -1, name: 'Jane Doe' }; // Ошибка валидации
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* // Пример 3: Интеграция с React (через DI контейнер)
|
|
75
|
+
* interface User { id: number; name: string; }
|
|
76
|
+
* class UserService {
|
|
77
|
+
* constructor(engine: ReactiveEngineCore) {
|
|
78
|
+
* this.mySignal = engine.signal<User>({ id: 1, name: 'John Doe' }, { name: 'userSignal' });
|
|
79
|
+
* }
|
|
80
|
+
*
|
|
81
|
+
* mySignal: Signal<User>;
|
|
82
|
+
* }
|
|
83
|
+
*
|
|
84
|
+
* const engine = new ReactiveEngineCore();
|
|
85
|
+
* engine.provide(UserService, UserService);
|
|
86
|
+
* const logic = engine.inject(UserService);
|
|
87
|
+
*
|
|
88
|
+
* function MyComponent() {
|
|
89
|
+
* const user = useReactiveValue(logic.mySignal);
|
|
90
|
+
* return <div>{user.name}</div>;
|
|
91
|
+
* }
|
|
92
|
+
*/
|
|
43
93
|
signal<T>(initialValue: T, optionsOrName?: string | SignalOptions<T>): Signal<T>;
|
|
94
|
+
/**
|
|
95
|
+
* Создает новое вычисляемое свойство (Computed) на основе чистой функции.
|
|
96
|
+
*
|
|
97
|
+
* @template T Тип значения, возвращаемого вычисляемым свойством.
|
|
98
|
+
* @param fn Чистая функция-вычислитель, которая определяет логику вычисления значения.
|
|
99
|
+
* @param signalName Опциональное имя сигнала для отладки и вывода в транзакционный логгер.
|
|
100
|
+
*
|
|
101
|
+
* @method computed
|
|
102
|
+
* @memberof ReactiveEngine
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* // Пример 1: Чистый TypeScript (Базовый)
|
|
106
|
+
* const engine = new ReactiveEngineCore();
|
|
107
|
+
* const counter = engine.signal(0, { name: 'counter' });
|
|
108
|
+
* const doubleCounter = engine.computed(() => counter.value * 2);
|
|
109
|
+
* console.log(doubleCounter.value); // 0
|
|
110
|
+
* counter.value = 1;
|
|
111
|
+
* console.log(doubleCounter.value); // 2
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* // Пример 2: Чистый TypeScript (Продвинутый с цепочкой зависимостей)
|
|
115
|
+
* interface User { id: number; name: string; }
|
|
116
|
+
* const engine = new ReactiveEngineCore();
|
|
117
|
+
* const userSignal = engine.signal<User>({ id: 1, name: 'John Doe' }, { name: 'user' });
|
|
118
|
+
* const userNameLength = engine.computed(() => userSignal.value.name.length);
|
|
119
|
+
* const hasLongName = engine.computed(() => userNameLength.value > 5);
|
|
120
|
+
*
|
|
121
|
+
* console.log(hasLongName.value); // false
|
|
122
|
+
* userSignal.value = { id: 1, name: 'Johnathan' };
|
|
123
|
+
* console.log(userNameLength.value); // 9
|
|
124
|
+
* console.log(hasLongName.value); // true
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* // Пример 3: Интеграция с React (через DI контейнер)
|
|
128
|
+
* interface User { id: number; name: string; }
|
|
129
|
+
* class UserService {
|
|
130
|
+
* constructor(engine: ReactiveEngineCore) {
|
|
131
|
+
* this.userSignal = engine.signal<User>({ id: 1, name: 'John Doe' }, { name: 'userSignal' });
|
|
132
|
+
* this.userNameLength = engine.computed(() => this.userSignal.value.name.length);
|
|
133
|
+
* }
|
|
134
|
+
*
|
|
135
|
+
* userSignal: Signal<User>;
|
|
136
|
+
* userNameLength: Computed<number>;
|
|
137
|
+
* }
|
|
138
|
+
*
|
|
139
|
+
* const engine = new ReactiveEngineCore();
|
|
140
|
+
* engine.provide(UserService, UserService);
|
|
141
|
+
* const logic = engine.inject(UserService);
|
|
142
|
+
*
|
|
143
|
+
* function MyComponent() {
|
|
144
|
+
* const hasLongName = engine.use(logic.userNameLength);
|
|
145
|
+
* return <div>{hasLongName ? 'Long name' : 'Short name'}</div>;
|
|
146
|
+
* }
|
|
147
|
+
*/
|
|
44
148
|
computed<T>(fn: () => T, signalName?: string): Computed<T>;
|
|
45
149
|
effect(fn: () => void | CleanupFn, effectName?: string): () => void;
|
|
46
150
|
untrack<T>(fn: () => T): T;
|
|
@@ -51,6 +155,62 @@ export declare class ReactiveEngineCore {
|
|
|
51
155
|
/**
|
|
52
156
|
* Создает умную реактивную декларативную обертку над асинхронными операциями
|
|
53
157
|
* с автоматическим подавлением Race Conditions и встроенным методом .destroy().
|
|
158
|
+
*
|
|
159
|
+
* @template T Тип данных, возвращаемых фетчером после успешного выполнения.
|
|
160
|
+
* @template S Тип реактивной зависимости (source).
|
|
161
|
+
* @param fetcher Асинхронная функция, выполняющая запрос и возвращающая данные типа T.
|
|
162
|
+
* @param source Реактивный сигнал или примитив, предоставляющий источник данных для фетчера.
|
|
163
|
+
* @param optionsOrName Конфигурационные опции ресурса или его имя.
|
|
164
|
+
* @param optionsOrName.name Имя ресурса.
|
|
165
|
+
* @param optionsOrName.resetDataOnSourceChange Флаг, указывающий, следует ли сбрасывать данные при изменении источника.
|
|
166
|
+
* @param optionsOrName.validateBeforeFetch Функция валидации источника перед выполнением фетча.
|
|
167
|
+
* @param optionsOrName.responseValidate Функция валидации ответа после выполнения фетча.
|
|
168
|
+
* @param optionsOrName.timeout Максимальное время ожидания запроса в миллисекундах.
|
|
169
|
+
* @param optionsOrName.retryCount Количество попыток повторного выполнения запроса в случае ошибки.
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* // Пример 1: Чистый TypeScript (Базовый)
|
|
173
|
+
* interface User { id: number; name: string; }
|
|
174
|
+
* interface ApiResponse<T> { ok: boolean; message?: string; data: T; }
|
|
175
|
+
* const engine = new ReactiveEngineCore();
|
|
176
|
+
* const userSignal = engine.signal<User>({ id: 1, name: 'John Doe' });
|
|
177
|
+
* const fetchUser = async (source: User, signal: AbortSignal): Promise<ApiResponse<User>> => {
|
|
178
|
+
* // Реализация фетча пользователя
|
|
179
|
+
* };
|
|
180
|
+
* const userResource = engine.resource(fetchUser, { value: userSignal.value }, { name: 'userResource' });
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* // Пример 2: Чистый TypeScript (Продвинутый)
|
|
184
|
+
* interface User { id: number; name: string; }
|
|
185
|
+
* interface ApiResponse<T> { ok: boolean; message?: string; data: T; }
|
|
186
|
+
* const engine = new ReactiveEngineCore();
|
|
187
|
+
* const userSignal = engine.signal<User>({ id: 1, name: 'John Doe' });
|
|
188
|
+
* const fetchUser = async (source: User, signal: AbortSignal): Promise<ApiResponse<User>> => {
|
|
189
|
+
* // Реализация фетча пользователя с использованием AbortSignal и timeout
|
|
190
|
+
* };
|
|
191
|
+
* const userResource = engine.resource(fetchUser, { value: userSignal.value }, {
|
|
192
|
+
* name: 'userResource',
|
|
193
|
+
* timeout: 5000,
|
|
194
|
+
* retryCount: 3,
|
|
195
|
+
* });
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* // Пример 3: Интеграция с React (через DI контейнер)
|
|
199
|
+
* interface User { id: number; name: string; }
|
|
200
|
+
* interface ApiResponse<T> { ok: boolean; message?: string; data: T; }
|
|
201
|
+
* class UserService {
|
|
202
|
+
* constructor(engine: ReactiveEngineCore) {
|
|
203
|
+
* const userSignal = engine.signal<User>({ id: 1, name: 'John Doe' });
|
|
204
|
+
* this.myResource = engine.resource(fetchUser, { value: userSignal.value }, { name: 'userResource' });
|
|
205
|
+
* }
|
|
206
|
+
*
|
|
207
|
+
* myResource: Resource<ApiResponse<User>>;
|
|
208
|
+
* }
|
|
209
|
+
*
|
|
210
|
+
* const engine = new ReactiveEngineCore();
|
|
211
|
+
* engine.provide(UserService, UserService);
|
|
212
|
+
* const logic = engine.inject(UserService);
|
|
213
|
+
* const state = useReactiveValue(logic.myResource);
|
|
54
214
|
*/
|
|
55
215
|
resource<T, S = void>(fetcher: (source: S, signal: AbortSignal) => Promise<T>, source?: {
|
|
56
216
|
value: S;
|
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":"AACA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAW,mBAAmB,EAAE,YAAY,EAAE,SAAS,EAAiC,aAAa,EAAE,eAAe,EAAiB,QAAQ,EAAE,MAAM,SAAS,CAAA;AAWzL,KAAK,WAAW,CAAC,CAAC,IAAI,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;AAE/C,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAA;AAEpC,KAAK,cAAc,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,MAAM,CAAA;AAuBtE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAM/H;AACD,wBAAgB,SAAS,IAAI,IAAI,CA8BhC;AACD,qBAAa,kBAAkB;IAC7B,SAAS,CAAC,eAAe,SAAS;IAC3B,MAAM,EAAE,mBAAmB,CAAA;IAC3B,KAAK,SAAI;IACT,cAAc,EAAE,GAAG,CAAO;IAC1B,UAAU,SAAI;IACd,YAAY,SAAI;IAChB,cAAc,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,CAAK;IAChD,eAAe,SAAI;IACnB,aAAa,yBAA+B;IACnD,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,eAAe,CAAmD;IAC1E,OAAO,CAAC,mBAAmB,CAAqB;IACzC,cAAc,WAAiB;IAC/B,WAAW,EAAE,GAAG,CAAO;IAEvB,eAAe,YAAiB,IAAI,EAAG;IAGvC,YAAY,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;gBAIlC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,mBAAmB,CAAA;KAAE;IAI/C,cAAc,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI;IAWjC,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,EAAE,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAIhH,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC;IAmBxB,YAAY,IAAI,IAAI;IAwFpB,GAAG,CAAC,MAAM,EAAE,GAAG;
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/core/core.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.method.computed.d.ts","sourceRoot":"","sources":["../../src/core/core.method.computed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAa,MAAM,SAAS,CAAA;AAE7C,wBAAgB,cAAc,CAAC,CAAC,EAC9B,MAAM,EAAE,GAAG,EACX,EAAE,EAAE,MAAM,CAAC,EACX,UAAU,CAAC,EAAE,MAAM,GAClB,QAAQ,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"core.method.computed.d.ts","sourceRoot":"","sources":["../../src/core/core.method.computed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAa,MAAM,SAAS,CAAA;AAE7C,wBAAgB,cAAc,CAAC,CAAC,EAC9B,MAAM,EAAE,GAAG,EACX,EAAE,EAAE,MAAM,CAAC,EACX,UAAU,CAAC,EAAE,MAAM,GAClB,QAAQ,CAAC,CAAC,CAAC,CA8Tb"}
|
|
@@ -14,6 +14,7 @@ function e(e, t, n) {
|
|
|
14
14
|
}
|
|
15
15
|
}, h = (t) => {
|
|
16
16
|
if (!s || l === 2) return t.run();
|
|
17
|
+
if (p && p.size > 0 && t.verifiedInEpoch === e.epoch && e.batchDepth === 0 && l === 0) return !1;
|
|
17
18
|
let n = !1;
|
|
18
19
|
if (u && u.size > 0) for (let t of u) {
|
|
19
20
|
let r = t._node || t;
|
|
@@ -22,7 +23,7 @@ function e(e, t, n) {
|
|
|
22
23
|
break;
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
|
-
return
|
|
26
|
+
return t.verifiedInEpoch = e.epoch, n || a ? t.run() : (l = 0, !1);
|
|
26
27
|
}, g = 0, _ = {
|
|
27
28
|
id: e.subscriberId++,
|
|
28
29
|
dependencies: u,
|
|
@@ -110,7 +111,7 @@ function e(e, t, n) {
|
|
|
110
111
|
let e = _.depth || 1, n = t._node || t;
|
|
111
112
|
n.depth !== void 0 && e >= n.depth && (n.depth = e + 1), t.activeRunId !== void 0 && (_._lastSeenInRunId = t.activeRunId);
|
|
112
113
|
}
|
|
113
|
-
return
|
|
114
|
+
return h(_), t && t.depVersions && t.depVersions.set(v, o), i;
|
|
114
115
|
},
|
|
115
116
|
get valueWithoutTracking() {
|
|
116
117
|
return i;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.method.computed.mjs","names":[],"sources":["../src/core/core.method.computed.ts"],"sourcesContent":["import { Computed, CleanupFn } from './types'\n\nexport function createComputed<T>(\n engine: any,\n fn: () => T,\n signalName?: string\n): Computed<T> {\n // 1. Ультимативный канонический WeakMap путь (строго по ссылке на объект-функцию)\n // Работает со скоростью нативной ссылки в V8, полностью исключая утечки памяти\n const existing = engine.computedCache.get(fn)\n if (existing) return existing as Computed<T>\n\n let value: T\n let isDirty = true\n let version = 0\n let isInitialized = false\n let evaluatedInEpoch = -1\n let state = 2 // 2 = DIRTY, 1 = CHECK, 0 = STABLE\n\n const dependencies = new Set<any>()\n const depVersions = new Map<any, number>()\n const ownedEffects = new Set<any>()\n const subscribers = new Set<any>()\n\n const releaseNode = (node: any): void => {\n if (!node) return\n const actNode = node._node || node\n\n if (actNode.dependencies && actNode.dependencies.size > 0) {\n for (const unknownDep of actNode.dependencies) {\n const dep = unknownDep as any\n const rawDepNode = dep._node || dep\n\n if (rawDepNode.subscribers) {\n rawDepNode.subscribers.delete(actNode)\n rawDepNode.subscribers.delete(node)\n rawDepNode.subscribers.delete(computedNode)\n\n if (rawDepNode.type === 'computed' && rawDepNode.subscribers.size === 0) {\n releaseNode(rawDepNode)\n }\n }\n }\n actNode.dependencies.clear()\n if (actNode.depVersions) actNode.depVersions.clear()\n }\n }\n\n const updateIfNecessary = (node: any): boolean => {\n if (!isInitialized || state === 2) {\n return node.run()\n }\n\n // 1. Сначала честно сканируем родителей по физическим версиям мутаций в V8.\n // Это единственный способ отловить inner write на фазе ленивого pull-запроса,\n // так как вложенный сеттер мог сдвинуть версии сигналов в обход markDirty.\n let hasChangedParent = false\n\n if (dependencies && dependencies.size > 0) {\n for (const dep of dependencies) {\n const rawDep = dep._node || dep\n\n if (rawDep.type === 'computed' && rawDep.state > 0) {\n if (rawDep.update && rawDep.verifiedInEpoch !== engine.epoch) {\n rawDep.update()\n }\n }\n\n if (dep.hasBatchValue || depVersions.get(dep) !== rawDep.version) {\n hasChangedParent = true\n break\n }\n }\n }\n\n // ⚛️ УЛЬТИМАТИВНЫЙ ФИКС ТЕСТOВ #180 и #186:\n // Если обход родителей подтвердил изменение версий из побочного канала (hasChangedParent === true),\n // мы ОБЯЗАНЫ сбросить кэши эпох текущей ноды и пустить её на честный пересчет.\n // При этом мы НЕ выставляем принудительно state = 2, благодаря чему метод run()\n // сможет сравнить результаты через Object.is и легитимно остановить волну (Value-Equality Cut)!\n if (hasChangedParent) {\n evaluatedInEpoch = -1\n node.verifiedInEpoch = -1\n return node.run()\n }\n\n // 2. И только если родители НЕ менялись, мы можем применить быстрый O(1) возврат по эпохам\n if (subscribers && subscribers.size > 0) {\n if (node.verifiedInEpoch === engine.epoch && engine.batchDepth === 0 && state === 0) {\n return false\n }\n }\n\n if (isDirty) {\n return node.run()\n }\n\n // Канонический сброс для unobserved pull-цепочек из тестов матрицы сайта\n if (node.verifiedInEpoch !== -1 && node.verifiedInEpoch < engine.epoch && (!subscribers || subscribers.size === 0)) {\n node.verifiedInEpoch = -1\n } else {\n node.verifiedInEpoch = engine.epoch\n }\n\n state = 0\n return false\n }\n\n let currentRunId = 0\n\n const computedNode: any = {\n id: engine.subscriberId++,\n dependencies,\n subscribers,\n ownedEffects,\n depth: 1,\n type: 'computed',\n fn,\n get version() { return version },\n get state() { return state },\n set state(val: number) { state = val },\n get depVersions() { return depVersions },\n verifiedInEpoch: -1, // Перенесено напрямую на объект ноды для ультимативного O(1)\n update: () => updateIfNecessary(computedNode),\n run: () => {\n // ⚛️ КАНOНИЧЕСКИЙ БЫСТРЫЙ ВЫХOД:\n // Срабатывает только если эпоха стабильна, узел не сброшен в updateIfNecessary\n // и в текущем кадре не происходило inner write мутаций у родителей\n if (evaluatedInEpoch === engine.epoch && isInitialized && state === 0 && engine.batchDepth === 0) {\n return false\n }\n\n // Запоминаем стартовую эпоху до вызова функции пользователя\n const startEpoch = engine.epoch\n evaluatedInEpoch = startEpoch\n computedNode.verifiedInEpoch = startEpoch\n\n if (engine.cycleDetectionStack.has(computedNode)) {\n throw new Error('[Graph Error] Cyclic dependency detected')\n }\n engine.cycleDetectionStack.add(computedNode)\n\n const prevConsumer = engine.activeConsumer\n const prevOwner = engine.activeOwner\n\n engine.activeConsumer = computedNode\n engine.activeOwner = computedNode\n\n currentRunId++\n computedNode.activeRunId = currentRunId\n\n const oldDeps = dependencies.size > 0 ? Array.from(dependencies) : null\n dependencies.clear()\n\n try {\n if (ownedEffects.size > 0) {\n for (const child of ownedEffects) {\n if (child && typeof child.dispose === 'function') {\n child.dispose()\n }\n }\n ownedEffects.clear()\n }\n\n // Выполняем пользовательскую функцию (здесь могут произойти inner writes из тестов #179, #180, #186)\n const newValue = fn()\n\n if (oldDeps !== null) {\n const isSizeIdentical = oldDeps.length === dependencies.size\n for (const unknownDep of oldDeps) {\n const dep = unknownDep as any\n if (!isSizeIdentical || !dependencies.has(dep)) {\n const rawDepNode = dep._node || dep\n if (rawDepNode.subscribers) rawDepNode.subscribers.delete(computedNode)\n depVersions.delete(dep)\n }\n }\n }\n\n const changed = isDirty || !Object.is(value, newValue)\n\n if (changed) {\n const previousValueSnapshot = isDirty ? undefined : (typeof value === 'object' && value !== null ? JSON.parse(JSON.stringify(value)) : value)\n value = newValue\n version++\n\n if (engine.log && signalName) {\n engine.log({\n type: 'computed',\n label: signalName,\n details: { from: previousValueSnapshot, to: newValue }\n })\n }\n }\n\n isDirty = false\n state = 0\n isInitialized = true\n\n let maxDepDepth = 0\n for (const unknownDep of dependencies) {\n const dep = unknownDep as any\n const rawNode = dep._node || dep\n depVersions.set(dep, rawNode.version)\n if (rawNode.subscribers) rawNode.subscribers.add(computedNode)\n if (rawNode.depth !== undefined && rawNode.depth > maxDepDepth) {\n maxDepDepth = rawNode.depth\n }\n }\n\n computedNode.depth = maxDepDepth + 1\n\n // ⚡ ФИКС ДЛЯ INNER WRITES (#179, #180, #186):\n // Если во время исполнения fn() эпоха ядра сдвинулась (был вложенный сеттер),\n // мы обязаны сбросить кэш верификации этой эпохи, чтобы последующие цепочки\n // зависимостей внутри этого же тика пересчитали свои значения нативно.\n if (engine.epoch !== startEpoch) {\n evaluatedInEpoch = -1\n computedNode.verifiedInEpoch = -1\n state = 2 // Переводим в DIRTY для немедленной инвалидации\n }\n\n return changed\n } finally {\n engine.activeConsumer = prevConsumer\n engine.activeOwner = prevOwner\n engine.cycleDetectionStack.delete(computedNode)\n }\n },\n markDirty: (parentState: number) => {\n if (state >= parentState) return\n state = parentState\n\n for (const sub of subscribers) {\n const rawSub = sub._node || sub\n if (rawSub.type === 'effect') {\n rawSub.isDirty = true\n engine.scheduleEffect(sub)\n } else if (rawSub.markDirty) {\n rawSub.markDirty(1)\n }\n }\n }\n }\n\n const computedInstance: Computed<T> = {\n get value() {\n const consumer = engine.activeConsumer\n if (consumer && consumer !== computedNode) {\n if (typeof consumer.dependencies?.add === 'function') {\n subscribers.add(consumer)\n consumer.dependencies.add(computedNode)\n\n const currentDepth = computedNode.depth || 1\n const consumerRaw = consumer._node || consumer\n if (consumerRaw.depth !== undefined && currentDepth >= consumerRaw.depth) {\n consumerRaw.depth = currentDepth + 1\n }\n\n if (consumer.activeRunId !== undefined) {\n computedNode._lastSeenInRunId = consumer.activeRunId\n }\n }\n }\n\n // ⚛️ ВЕЛИКИЙ ПРЕДOХРАНИТЕЛЬ ДЛЯ INNER WRITES (#180 и #186):\n // Если у ноды есть сохраненный маркер эпохи, но состояние у узла DIRTY (state === 2)\n // из-за вложенного вызова markDirty(2) во время flush-цикла, либо если глобальная эпоха\n // ядра ушла вперед — мы ОБЯЗАНЫ обнулить verifiedInEpoch, чтобы пропустить пулл-запрос к родителям!\n if (state === 2 || (computedNode.verifiedInEpoch !== -1 && computedNode.verifiedInEpoch < engine.epoch)) {\n computedNode.verifiedInEpoch = -1\n evaluatedInEpoch = -1\n }\n\n updateIfNecessary(computedNode)\n\n if (consumer && consumer.depVersions) {\n consumer.depVersions.set(computedInstance, version)\n }\n return value\n },\n get valueWithoutTracking() { return value },\n get version() { return version },\n get dependencies() { return dependencies },\n\n subscribe(cb: (val: T) => void): CleanupFn {\n const self = this\n const fakeEffect: any = {\n id: engine.subscriberId++,\n isDirty: true,\n depth: 9999,\n dependencies: new Set(),\n type: 'effect',\n run: () => {\n if (fakeEffect.isDisposed) return\n try { cb(self.value) } catch (err) { console.error(err) }\n }\n }\n subscribers.add(fakeEffect)\n cb(this.value)\n return () => {\n fakeEffect.isDisposed = true\n subscribers.delete(fakeEffect)\n fakeEffect.dependencies.clear()\n }\n },\n subscribeState(cb: () => void): CleanupFn {\n return (computedInstance as any).subscribe(cb)\n },\n destroy: () => {\n releaseNode(computedNode)\n if (subscribers) subscribers.clear()\n engine.computedCache.delete(fn)\n\n dependencies.clear()\n depVersions.clear()\n ownedEffects.clear()\n\n isDirty = true\n state = 2\n isInitialized = false\n },\n __subscribe(cb: () => void): CleanupFn {\n const hookEffect: any = {\n id: engine.subscriberId++,\n type: 'effect',\n run: cb,\n isDirty: false\n }\n subscribers.add(hookEffect)\n return () => {\n subscribers.delete(hookEffect)\n }\n }\n } as any\n\n computedInstance.run = () => computedNode.run()\n engine.computedCache.set(fn, computedInstance)\n\n ;(computedInstance as any)._node = computedNode\n ;(computedInstance as any).subscribers = subscribers\n\n return computedInstance\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"core.method.computed.mjs","names":[],"sources":["../src/core/core.method.computed.ts"],"sourcesContent":["import { Computed, CleanupFn } from './types'\n\nexport function createComputed<T>(\n engine: any,\n fn: () => T,\n signalName?: string\n): Computed<T> {\n // 1. Ультимативный канонический WeakMap путь (строго по ссылке на объект-функцию)\n // Работает со скоростью нативной ссылки в V8, полностью исключая утечки памяти\n const existing = engine.computedCache.get(fn)\n if (existing) return existing as Computed<T>\n\n let value: T\n let isDirty = true\n let version = 0\n let isInitialized = false\n let evaluatedInEpoch = -1\n let state = 2 // 2 = DIRTY, 1 = CHECK, 0 = STABLE\n\n const dependencies = new Set<any>()\n const depVersions = new Map<any, number>()\n const ownedEffects = new Set<any>()\n const subscribers = new Set<any>()\n\n const releaseNode = (node: any): void => {\n if (!node) return\n const actNode = node._node || node\n\n if (actNode.dependencies && actNode.dependencies.size > 0) {\n for (const unknownDep of actNode.dependencies) {\n const dep = unknownDep as any\n const rawDepNode = dep._node || dep\n\n if (rawDepNode.subscribers) {\n rawDepNode.subscribers.delete(actNode)\n rawDepNode.subscribers.delete(node)\n rawDepNode.subscribers.delete(computedNode)\n\n if (rawDepNode.type === 'computed' && rawDepNode.subscribers.size === 0) {\n releaseNode(rawDepNode)\n }\n }\n }\n actNode.dependencies.clear()\n if (actNode.depVersions) actNode.depVersions.clear()\n }\n }\n\n const updateIfNecessary = (node: any): boolean => {\n if (!isInitialized || state === 2) {\n return node.run()\n }\n\n // ⚛️ КОРРЕКТНЫЙ МЕМОИЗАЦИОННЫЙ ГВАРД (Pull-совместимый проход):\n // Если у узла есть активные подписчики (observed), мы можем оптимизировать расчет через эпохи.\n // Но если подписчиков НЕТ (unobserved), мы ОБЯЗАНЫ пропустить быстрый выход по эпохам\n // и честно проверить, изменились ли версии родителей ниже.\n if (subscribers && subscribers.size > 0) {\n if (node.verifiedInEpoch === engine.epoch && engine.batchDepth === 0 && state === 0) {\n return false\n }\n }\n\n let hasChangedParent = false\n\n if (dependencies && dependencies.size > 0) {\n for (const dep of dependencies) {\n const rawDep = dep._node || dep\n\n if (rawDep.type === 'computed' && rawDep.state > 0) {\n // ⚛️ ЗАЩИТА ОТ КАСКАДНОГО ОБХОДА:\n // Вызываем update() родителя только если он еще не был верифицирован в текущей эпохе.\n // Это ликвидирует квадратичную рекурсию на динамических структурах графа!\n if (rawDep.update && rawDep.verifiedInEpoch !== engine.epoch) {\n rawDep.update()\n }\n }\n\n // Если родитель имеет отложенное батч-значение (hasBatchValue)\n // или его версия изменилась — кэш невалиден!\n if (dep.hasBatchValue || depVersions.get(dep) !== rawDep.version) {\n hasChangedParent = true\n break\n }\n }\n }\n\n node.verifiedInEpoch = engine.epoch\n\n if (hasChangedParent || isDirty) {\n return node.run()\n }\n\n state = 0\n return false\n }\n\n let currentRunId = 0\n\n const computedNode: any = {\n id: engine.subscriberId++,\n dependencies,\n subscribers,\n ownedEffects,\n depth: 1,\n type: 'computed',\n fn,\n get version() { return version },\n get state() { return state },\n set state(val: number) { state = val },\n get depVersions() { return depVersions },\n verifiedInEpoch: -1, // Перенесено напрямую на объект ноды для ультимативного O(1)\n update: () => updateIfNecessary(computedNode),\n run: () => {\n // Стандартный быстрый выход для стабильных эпох\n if (evaluatedInEpoch === engine.epoch && isInitialized && state === 0 && engine.batchDepth === 0) {\n return false\n }\n\n // Запоминаем стартовую эпоху до вызова функции пользователя\n const startEpoch = engine.epoch\n evaluatedInEpoch = startEpoch\n computedNode.verifiedInEpoch = startEpoch\n\n if (engine.cycleDetectionStack.has(computedNode)) {\n throw new Error('[Graph Error] Cyclic dependency detected')\n }\n engine.cycleDetectionStack.add(computedNode)\n\n const prevConsumer = engine.activeConsumer\n const prevOwner = engine.activeOwner\n\n engine.activeConsumer = computedNode\n engine.activeOwner = computedNode\n\n currentRunId++\n computedNode.activeRunId = currentRunId\n\n const oldDeps = dependencies.size > 0 ? Array.from(dependencies) : null\n dependencies.clear()\n\n try {\n if (ownedEffects.size > 0) {\n for (const child of ownedEffects) {\n if (child && typeof child.dispose === 'function') {\n child.dispose()\n }\n }\n ownedEffects.clear()\n }\n\n // Выполняем пользовательскую функцию (здесь могут произойти inner writes из тестов #179, #180, #186)\n const newValue = fn()\n\n if (oldDeps !== null) {\n const isSizeIdentical = oldDeps.length === dependencies.size\n for (const unknownDep of oldDeps) {\n const dep = unknownDep as any\n if (!isSizeIdentical || !dependencies.has(dep)) {\n const rawDepNode = dep._node || dep\n if (rawDepNode.subscribers) rawDepNode.subscribers.delete(computedNode)\n depVersions.delete(dep)\n }\n }\n }\n\n const changed = isDirty || !Object.is(value, newValue)\n\n if (changed) {\n const previousValueSnapshot = isDirty ? undefined : (typeof value === 'object' && value !== null ? JSON.parse(JSON.stringify(value)) : value)\n value = newValue\n version++\n\n if (engine.log && signalName) {\n engine.log({\n type: 'computed',\n label: signalName,\n details: { from: previousValueSnapshot, to: newValue }\n })\n }\n }\n\n isDirty = false\n state = 0\n isInitialized = true\n\n let maxDepDepth = 0\n for (const unknownDep of dependencies) {\n const dep = unknownDep as any\n const rawNode = dep._node || dep\n depVersions.set(dep, rawNode.version)\n if (rawNode.subscribers) rawNode.subscribers.add(computedNode)\n if (rawNode.depth !== undefined && rawNode.depth > maxDepDepth) {\n maxDepDepth = rawNode.depth\n }\n }\n\n computedNode.depth = maxDepDepth + 1\n\n // ⚡ ФИКС ДЛЯ INNER WRITES (#179, #180, #186):\n // Если во время исполнения fn() эпоха ядра сдвинулась (был вложенный сеттер),\n // мы обязаны сбросить кэш верификации этой эпохи, чтобы последующие цепочки\n // зависимостей внутри этого же тика пересчитали свои значения нативно.\n if (engine.epoch !== startEpoch) {\n evaluatedInEpoch = -1\n computedNode.verifiedInEpoch = -1\n state = 2 // Переводим в DIRTY для немедленной инвалидации\n }\n\n return changed\n } finally {\n engine.activeConsumer = prevConsumer\n engine.activeOwner = prevOwner\n engine.cycleDetectionStack.delete(computedNode)\n }\n },\n markDirty: (parentState: number) => {\n if (state >= parentState) return\n state = parentState\n\n for (const sub of subscribers) {\n const rawSub = sub._node || sub\n if (rawSub.type === 'effect') {\n rawSub.isDirty = true\n engine.scheduleEffect(sub)\n } else if (rawSub.markDirty) {\n rawSub.markDirty(1)\n }\n }\n }\n }\n\n const computedInstance: Computed<T> = {\n get value() {\n // 🛡️ АТОМАРНАЯ ЗАЩИТА: Фиксируем ссылку локально и работаем строго с ней.\n // Это полностью предотвращает TypeError: ... reading 'add' на проде Next.js.\n const consumer = engine.activeConsumer\n if (consumer && consumer !== computedNode) {\n // Проверяем наличие метода add у консьюмера, страхуя граф от стороннего мусора Webpack\n if (typeof consumer.dependencies?.add === 'function') {\n subscribers.add(consumer)\n consumer.dependencies.add(computedNode)\n\n const currentDepth = computedNode.depth || 1\n const consumerRaw = consumer._node || consumer\n if (consumerRaw.depth !== undefined && currentDepth >= consumerRaw.depth) {\n consumerRaw.depth = currentDepth + 1\n }\n\n if (consumer.activeRunId !== undefined) {\n computedNode._lastSeenInRunId = consumer.activeRunId\n }\n }\n }\n\n updateIfNecessary(computedNode)\n\n if (consumer && consumer.depVersions) {\n consumer.depVersions.set(computedInstance, version)\n }\n return value\n },\n get valueWithoutTracking() { return value },\n get version() { return version },\n get dependencies() { return dependencies },\n\n subscribe(cb: (val: T) => void): CleanupFn {\n const self = this\n const fakeEffect: any = {\n id: engine.subscriberId++,\n isDirty: true,\n depth: 9999,\n dependencies: new Set(),\n type: 'effect',\n run: () => {\n if (fakeEffect.isDisposed) return\n try { cb(self.value) } catch (err) { console.error(err) }\n }\n }\n subscribers.add(fakeEffect)\n cb(this.value)\n return () => {\n fakeEffect.isDisposed = true\n subscribers.delete(fakeEffect)\n fakeEffect.dependencies.clear()\n }\n },\n subscribeState(cb: () => void): CleanupFn {\n return (computedInstance as any).subscribe(cb)\n },\n destroy: () => {\n releaseNode(computedNode)\n if (subscribers) subscribers.clear()\n engine.computedCache.delete(fn)\n\n dependencies.clear()\n depVersions.clear()\n ownedEffects.clear()\n\n isDirty = true\n state = 2\n isInitialized = false\n },\n __subscribe(cb: () => void): CleanupFn {\n const hookEffect: any = {\n id: engine.subscriberId++,\n type: 'effect',\n run: cb,\n isDirty: false\n }\n subscribers.add(hookEffect)\n return () => {\n subscribers.delete(hookEffect)\n }\n }\n } as any\n\n computedInstance.run = () => computedNode.run()\n engine.computedCache.set(fn, computedInstance)\n\n ;(computedInstance as any)._node = computedNode\n ;(computedInstance as any).subscribers = subscribers\n\n return computedInstance\n}\n"],"mappings":"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"}
|
|
@@ -124,7 +124,9 @@ function t(t, n, r, i) {
|
|
|
124
124
|
total: d,
|
|
125
125
|
nextDelayMs: Math.round(e)
|
|
126
126
|
} }
|
|
127
|
-
})
|
|
127
|
+
});
|
|
128
|
+
let n = r ? "таймауту" : "ошибке сети";
|
|
129
|
+
t.logger.isEnabled && console.warn(`[Resource Retry] "${o}" сбой по ${n}. Запрос ${a + 1} из ${d + 1} не удался. Ожидание повтора: ${Math.round(e)}ms`);
|
|
128
130
|
try {
|
|
129
131
|
if (await t.delay(e, i), i.aborted) return;
|
|
130
132
|
} catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.method.resource.mjs","names":[],"sources":["../src/core/core.method.resource.ts"],"sourcesContent":["import { getExtractedValues } from '../utils/getExtractedValues'\nimport { EngineLoggerOptions, Resource, ResourceOptions, ResourceState, Signal } from './types'\n\n// Контракт ядра движка для безопасных вызовов методов с дженериками\ninterface IReactiveEngine {\n signal<V>(initialValue: V, optionsOrName?: any): Signal<V>;\n effect(fn: () => any, name?: string): () => void;\n untrack<V>(fn: () => V): V;\n log(params: any): void;\n delay(ms: number, signal: AbortSignal): Promise<void>;\n logger: EngineLoggerOptions\n}\n\nexport function createResource<T, S = void>(\n engine: IReactiveEngine,\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n source?: { value: S },\n optionsOrName?: string | ResourceOptions<T, S>\n): Resource<T> {\n const isOptionsObject = optionsOrName && typeof optionsOrName === 'object'\n const resourceSignalName = isOptionsObject\n ? ((optionsOrName as ResourceOptions<T, S>).name || 'unnamed_resource')\n : (optionsOrName as string) || 'unnamed_resource'\n\n const options: Partial<ResourceOptions<T, S>> = isOptionsObject\n ? (optionsOrName as ResourceOptions<T, S>)\n : {}\n\n const resetDataOnSourceChange = options.resetDataOnSourceChange ?? true\n const responseValidate = options.responseValidate\n const validateBeforeFetch = options.validateBeforeFetch\n const baseDelay = 1000\n const useExponential = true\n const maxRetryDelay = 1000 * 60 * 5\n\n const retryCount = options.retryCount ?? 0\n const timeoutMs = options.timeout ?? 5000\n // Запоминаем точное время старта ВСЕЙ цепочки (включая ретраи и паузы)\n let startTime = Date.now()\n\n const state = engine.signal<ResourceState<T>>(\n {\n data: null,\n loading: true,\n error: null,\n isRetrying: false,\n attemptIndex: 0,\n retryCount,\n totalElapsedMs: undefined // Сбрасываем старый таймер\n },\n `[CORE_INTERNAL_SIGNAL=1]:resource:state:${resourceSignalName}`\n )\n\n const updateResourceState = (nextState: ResourceState<T>) => {\n engine.log({\n type: 'resource',\n label: resourceSignalName,\n details: { to: nextState, from: state.value },\n })\n state.value = nextState\n }\n\n const load = async (sValue: S, effectSignal: AbortSignal, isSourceChange = false) => {\n if (effectSignal.aborted) return\n\n // Фиксируем время старта бизнес-процесса в самом начале метода load\n startTime = Date.now()\n\n if (validateBeforeFetch) {\n const preValidationResult = validateBeforeFetch(sValue)\n if (preValidationResult === false || typeof preValidationResult === 'string') {\n if (effectSignal.aborted) return\n const errorMsg = typeof preValidationResult === 'string'\n ? preValidationResult\n : 'Pre-fetch validation failed for resource'\n\n const currentData = isSourceChange ? null : engine.untrack(() => state.value.data)\n // Явно зачищаем следы старого таймера при ошибке валидации аргументов\n updateResourceState({\n data: currentData,\n loading: false,\n error: errorMsg,\n isRetrying: false,\n attemptIndex: 0,\n retryCount,\n totalElapsedMs: undefined\n })\n return\n }\n }\n\n const shouldClear = isSourceChange && resetDataOnSourceChange\n const currentData = shouldClear ? null : engine.untrack(() => state.value.data)\n\n // Принудительно сбрасываем таймер в undefined перед входом в цикл ретраев\n updateResourceState({\n data: currentData,\n loading: true,\n error: null,\n isRetrying: false,\n attemptIndex: 0,\n retryCount,\n totalElapsedMs: undefined\n })\n\n // 🔄 Контур циклического выполнения попыток (от 0 до retryCount)\n for (let attempt = 0; attempt <= retryCount; attempt++) {\n if (effectSignal.aborted) return\n\n // Создаем персональный сигнал таймаута на эту конкретную попытку\n const timeoutSignal = AbortSignal.timeout(timeoutMs)\n\n // Объединяем управляющий сигнал графа и наш таймаут\n const combinedSignal = AbortSignal.any([effectSignal, timeoutSignal])\n\n try {\n const data = await fetcher(sValue, combinedSignal)\n\n if (effectSignal.aborted) return\n\n // Если фетчер вернул управление, но таймаут успел отработать раньше\n if (timeoutSignal.aborted) {\n throw new DOMException('The operation timed out.', 'TimeoutError')\n }\n\n if (responseValidate) {\n const validationResult = responseValidate(data)\n if (validationResult === false || typeof validationResult === 'string') {\n if (effectSignal.aborted) return\n const errorMsg = typeof validationResult === 'string' ? validationResult : 'Validation failed'\n updateResourceState({\n data: null,\n loading: false,\n error: errorMsg,\n isRetrying: false,\n attemptIndex: 0,\n retryCount,\n totalElapsedMs: undefined\n })\n return\n }\n }\n\n if (effectSignal.aborted) return\n // Успех: сохраняем данные, полностью очищаем ошибки и сквозной таймер\n updateResourceState({\n data,\n loading: false,\n error: null,\n isRetrying: false,\n attemptIndex: 0,\n retryCount,\n totalElapsedMs: undefined\n })\n return\n\n } catch (e: unknown) {\n // 1. Выявляем, произошел ли сбой по таймауту\n const isTimeout = timeoutSignal.aborted ||\n (e as Error).name === 'TimeoutError' ||\n (e as Error).message?.includes('timeout')\n\n // Если это обычная отмена дебаунсом из внешнего контура, мгновенно прекращаем работу\n if (effectSignal.aborted && !isTimeout) return\n\n // 2. Безопасно извлекаем валидационные маркеры до их использования в условиях\n const isCustomValidationError = getExtractedValues({\n tested: [(e as Error).message], expectedKey: 'THROW_CUSTOM_VALIDATION_ERROR_NO_RETRY', valueType: 'number',\n })?.[0] === '1' || false\n\n const __defaultCustomValidationErrorMessage = 'Custom validation error'\n const customValidationErrorMessage: string = isCustomValidationError\n ? (getExtractedValues({\n tested: [(e as Error).message], expectedKey: 'MESSAGE', valueType: 'string',\n })?.[0] || __defaultCustomValidationErrorMessage)\n : __defaultCustomValidationErrorMessage\n\n const isFetchBodyValidationError = isCustomValidationError || (e as Error).name === 'ValidationError'\n\n // Если попытки исчерпаны ИЛИ это валидационная ошибка тела, которая не подлежит повтору\n if (attempt === retryCount || isFetchBodyValidationError) {\n if (effectSignal.aborted) return\n\n // Считаем суммарное время от первого клика/сдвига карты до текущего момента\n const totalElapsedMs = Date.now() - startTime\n\n // Переменная customValidationErrorMessage теперь гарантированно определена выше!\n const baseErrorMsg = isTimeout\n ? `Request timed out after ${timeoutMs}ms (attempt ${attempt + 1}/${retryCount + 1})`\n : isCustomValidationError\n ? customValidationErrorMessage\n : e instanceof Error ? e.message : String(e)\n\n // Формируем детальное сообщение для вывода в UI\n const finalErrorMessage = `${baseErrorMsg}.${engine.logger.isCoreOptimizationDebugEnabled ? ` Всего безуспешных попыток с момента изменения исходного сигнала: ${retryCount + 1}.` : ''}`\n\n updateResourceState({\n data: null,\n loading: false,\n error: finalErrorMessage,\n isRetrying: false,\n attemptIndex: attempt + 1, // Выводим финальное число сделанных попыток\n retryCount,\n totalElapsedMs,\n })\n return\n } else {\n // Инициализируем планирование следующей попытки (Retry Backoff Phase)\n if (effectSignal.aborted) return\n\n let currentDelay = useExponential ? baseDelay * Math.pow(2, attempt) : baseDelay\n currentDelay = Math.min(currentDelay, maxRetryDelay) + Math.random() * 200\n\n updateResourceState({\n data: state.value.data,\n loading: true,\n error: null,\n isRetrying: true,\n attemptIndex: attempt + 1, // Индекс следующей накатываемой попытки для UI (1, 2, 3...)\n retryCount\n })\n\n engine.log({\n type: 'resource',\n label: `${resourceSignalName}:retry_scheduled`,\n details: { payload: { attempt: attempt + 1, total: retryCount, nextDelayMs: Math.round(currentDelay) } }\n })\n\n const logReason = isTimeout ? 'таймауту' : 'ошибке сети'\n // Исправлен счетчик: пишем честный индекс текущего упавшего запроса из общего пула\n console.warn(`[Resource Retry] \"${resourceSignalName}\" сбой по ${logReason}. Запрос ${attempt + 1} из ${retryCount + 1} не удался. Ожидание повтора: ${Math.round(currentDelay)}ms`)\n\n try {\n // Спим перед следующим циклом, уважая отмену внешнего графа\n await engine.delay(currentDelay, effectSignal)\n if (effectSignal.aborted) return\n } catch (delayError) {\n return\n }\n }\n }\n }\n }\n\n let activeEffectController: AbortController | null = null\n\n const disposeInternalEffect = engine.effect(() => {\n const sValue = source ? source.value : undefined as S\n const effectController = new AbortController()\n activeEffectController = effectController\n\n engine.untrack(() => {\n load(sValue as S, effectController.signal, true)\n })\n\n return () => effectController.abort()\n }, '[CORE_INTERNAL_SIGNAL=1]:effect')\n\n return {\n get data() { return state.value.data },\n get loading() { return state.value.loading },\n get error() { return state.value.error },\n get isRetrying() { return state.value.isRetrying },\n get value() { return state.value },\n refetch: () => {\n activeEffectController?.abort()\n activeEffectController = new AbortController()\n load(source ? source.value : undefined as S, activeEffectController.signal, false)\n },\n subscribe: (cb: (val: ResourceState<T>) => void) => state.subscribe(cb),\n destroy: () => {\n activeEffectController?.abort()\n activeEffectController = null\n if (typeof disposeInternalEffect === 'function') {\n disposeInternalEffect()\n }\n if ((state as any).destroy) {\n (state as any).destroy()\n }\n }\n } as any\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"core.method.resource.mjs","names":[],"sources":["../src/core/core.method.resource.ts"],"sourcesContent":["import { getExtractedValues } from '../utils/getExtractedValues'\nimport { EngineLoggerOptions, Resource, ResourceOptions, ResourceState, Signal } from './types'\n\n// Контракт ядра движка для безопасных вызовов методов с дженериками\ninterface IReactiveEngine {\n signal<V>(initialValue: V, optionsOrName?: any): Signal<V>;\n effect(fn: () => any, name?: string): () => void;\n untrack<V>(fn: () => V): V;\n log(params: any): void;\n delay(ms: number, signal: AbortSignal): Promise<void>;\n logger: EngineLoggerOptions\n}\n\nexport function createResource<T, S = void>(\n engine: IReactiveEngine,\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n source?: { value: S },\n optionsOrName?: string | ResourceOptions<T, S>\n): Resource<T> {\n const isOptionsObject = optionsOrName && typeof optionsOrName === 'object'\n const resourceSignalName = isOptionsObject\n ? ((optionsOrName as ResourceOptions<T, S>).name || 'unnamed_resource')\n : (optionsOrName as string) || 'unnamed_resource'\n\n const options: Partial<ResourceOptions<T, S>> = isOptionsObject\n ? (optionsOrName as ResourceOptions<T, S>)\n : {}\n\n const resetDataOnSourceChange = options.resetDataOnSourceChange ?? true\n const responseValidate = options.responseValidate\n const validateBeforeFetch = options.validateBeforeFetch\n const baseDelay = 1000\n const useExponential = true\n const maxRetryDelay = 1000 * 60 * 5\n\n const retryCount = options.retryCount ?? 0\n const timeoutMs = options.timeout ?? 5000\n // Запоминаем точное время старта ВСЕЙ цепочки (включая ретраи и паузы)\n let startTime = Date.now()\n\n const state = engine.signal<ResourceState<T>>(\n {\n data: null,\n loading: true,\n error: null,\n isRetrying: false,\n attemptIndex: 0,\n retryCount,\n totalElapsedMs: undefined // Сбрасываем старый таймер\n },\n `[CORE_INTERNAL_SIGNAL=1]:resource:state:${resourceSignalName}`\n )\n\n const updateResourceState = (nextState: ResourceState<T>) => {\n engine.log({\n type: 'resource',\n label: resourceSignalName,\n details: { to: nextState, from: state.value },\n })\n state.value = nextState\n }\n\n const load = async (sValue: S, effectSignal: AbortSignal, isSourceChange = false) => {\n if (effectSignal.aborted) return\n\n // Фиксируем время старта бизнес-процесса в самом начале метода load\n startTime = Date.now()\n\n if (validateBeforeFetch) {\n const preValidationResult = validateBeforeFetch(sValue)\n if (preValidationResult === false || typeof preValidationResult === 'string') {\n if (effectSignal.aborted) return\n const errorMsg = typeof preValidationResult === 'string'\n ? preValidationResult\n : 'Pre-fetch validation failed for resource'\n\n const currentData = isSourceChange ? null : engine.untrack(() => state.value.data)\n // Явно зачищаем следы старого таймера при ошибке валидации аргументов\n updateResourceState({\n data: currentData,\n loading: false,\n error: errorMsg,\n isRetrying: false,\n attemptIndex: 0,\n retryCount,\n totalElapsedMs: undefined\n })\n return\n }\n }\n\n const shouldClear = isSourceChange && resetDataOnSourceChange\n const currentData = shouldClear ? null : engine.untrack(() => state.value.data)\n\n // Принудительно сбрасываем таймер в undefined перед входом в цикл ретраев\n updateResourceState({\n data: currentData,\n loading: true,\n error: null,\n isRetrying: false,\n attemptIndex: 0,\n retryCount,\n totalElapsedMs: undefined\n })\n\n // 🔄 Контур циклического выполнения попыток (от 0 до retryCount)\n for (let attempt = 0; attempt <= retryCount; attempt++) {\n if (effectSignal.aborted) return\n\n // Создаем персональный сигнал таймаута на эту конкретную попытку\n const timeoutSignal = AbortSignal.timeout(timeoutMs)\n\n // Объединяем управляющий сигнал графа и наш таймаут\n const combinedSignal = AbortSignal.any([effectSignal, timeoutSignal])\n\n try {\n const data = await fetcher(sValue, combinedSignal)\n\n if (effectSignal.aborted) return\n\n // Если фетчер вернул управление, но таймаут успел отработать раньше\n if (timeoutSignal.aborted) {\n throw new DOMException('The operation timed out.', 'TimeoutError')\n }\n\n if (responseValidate) {\n const validationResult = responseValidate(data)\n if (validationResult === false || typeof validationResult === 'string') {\n if (effectSignal.aborted) return\n const errorMsg = typeof validationResult === 'string' ? validationResult : 'Validation failed'\n updateResourceState({\n data: null,\n loading: false,\n error: errorMsg,\n isRetrying: false,\n attemptIndex: 0,\n retryCount,\n totalElapsedMs: undefined\n })\n return\n }\n }\n\n if (effectSignal.aborted) return\n // Успех: сохраняем данные, полностью очищаем ошибки и сквозной таймер\n updateResourceState({\n data,\n loading: false,\n error: null,\n isRetrying: false,\n attemptIndex: 0,\n retryCount,\n totalElapsedMs: undefined\n })\n return\n\n } catch (e: unknown) {\n // 1. Выявляем, произошел ли сбой по таймауту\n const isTimeout = timeoutSignal.aborted ||\n (e as Error).name === 'TimeoutError' ||\n (e as Error).message?.includes('timeout')\n\n // Если это обычная отмена дебаунсом из внешнего контура, мгновенно прекращаем работу\n if (effectSignal.aborted && !isTimeout) return\n\n // 2. Безопасно извлекаем валидационные маркеры до их использования в условиях\n const isCustomValidationError = getExtractedValues({\n tested: [(e as Error).message], expectedKey: 'THROW_CUSTOM_VALIDATION_ERROR_NO_RETRY', valueType: 'number',\n })?.[0] === '1' || false\n\n const __defaultCustomValidationErrorMessage = 'Custom validation error'\n const customValidationErrorMessage: string = isCustomValidationError\n ? (getExtractedValues({\n tested: [(e as Error).message], expectedKey: 'MESSAGE', valueType: 'string',\n })?.[0] || __defaultCustomValidationErrorMessage)\n : __defaultCustomValidationErrorMessage\n\n const isFetchBodyValidationError = isCustomValidationError || (e as Error).name === 'ValidationError'\n\n // Если попытки исчерпаны ИЛИ это валидационная ошибка тела, которая не подлежит повтору\n if (attempt === retryCount || isFetchBodyValidationError) {\n if (effectSignal.aborted) return\n\n // Считаем суммарное время от первого клика/сдвига карты до текущего момента\n const totalElapsedMs = Date.now() - startTime\n\n // Переменная customValidationErrorMessage теперь гарантированно определена выше!\n const baseErrorMsg = isTimeout\n ? `Request timed out after ${timeoutMs}ms (attempt ${attempt + 1}/${retryCount + 1})`\n : isCustomValidationError\n ? customValidationErrorMessage\n : e instanceof Error ? e.message : String(e)\n\n // Формируем детальное сообщение для вывода в UI\n const finalErrorMessage = `${baseErrorMsg}.${engine.logger.isCoreOptimizationDebugEnabled ? ` Всего безуспешных попыток с момента изменения исходного сигнала: ${retryCount + 1}.` : ''}`\n\n updateResourceState({\n data: null,\n loading: false,\n error: finalErrorMessage,\n isRetrying: false,\n attemptIndex: attempt + 1, // Выводим финальное число сделанных попыток\n retryCount,\n totalElapsedMs,\n })\n return\n } else {\n // Инициализируем планирование следующей попытки (Retry Backoff Phase)\n if (effectSignal.aborted) return\n\n let currentDelay = useExponential ? baseDelay * Math.pow(2, attempt) : baseDelay\n currentDelay = Math.min(currentDelay, maxRetryDelay) + Math.random() * 200\n\n updateResourceState({\n data: state.value.data,\n loading: true,\n error: null,\n isRetrying: true,\n attemptIndex: attempt + 1, // Индекс следующей накатываемой попытки для UI (1, 2, 3...)\n retryCount\n })\n\n engine.log({\n type: 'resource',\n label: `${resourceSignalName}:retry_scheduled`,\n details: { payload: { attempt: attempt + 1, total: retryCount, nextDelayMs: Math.round(currentDelay) } }\n })\n\n const logReason = isTimeout ? 'таймауту' : 'ошибке сети'\n // Исправлен счетчик: пишем честный индекс текущего упавшего запроса из общего пула\n if (engine.logger.isEnabled) console.warn(`[Resource Retry] \"${resourceSignalName}\" сбой по ${logReason}. Запрос ${attempt + 1} из ${retryCount + 1} не удался. Ожидание повтора: ${Math.round(currentDelay)}ms`)\n\n try {\n // Спим перед следующим циклом, уважая отмену внешнего графа\n await engine.delay(currentDelay, effectSignal)\n if (effectSignal.aborted) return\n } catch (delayError) {\n return\n }\n }\n }\n }\n }\n\n let activeEffectController: AbortController | null = null\n\n const disposeInternalEffect = engine.effect(() => {\n const sValue = source ? source.value : undefined as S\n const effectController = new AbortController()\n activeEffectController = effectController\n\n engine.untrack(() => {\n load(sValue as S, effectController.signal, true)\n })\n\n return () => effectController.abort()\n }, '[CORE_INTERNAL_SIGNAL=1]:effect')\n\n return {\n get data() { return state.value.data },\n get loading() { return state.value.loading },\n get error() { return state.value.error },\n get isRetrying() { return state.value.isRetrying },\n get value() { return state.value },\n refetch: () => {\n activeEffectController?.abort()\n activeEffectController = new AbortController()\n load(source ? source.value : undefined as S, activeEffectController.signal, false)\n },\n subscribe: (cb: (val: ResourceState<T>) => void) => state.subscribe(cb),\n destroy: () => {\n activeEffectController?.abort()\n activeEffectController = null\n if (typeof disposeInternalEffect === 'function') {\n disposeInternalEffect()\n }\n if ((state as any).destroy) {\n (state as any).destroy()\n }\n }\n } as any\n}\n"],"mappings":";;AAaA,SAAgB,EACd,GACA,GACA,GACA,GACa;CACb,IAAM,IAAkB,KAAiB,OAAO,KAAkB,UAC5D,IAAqB,IACrB,EAAwC,QAAQ,qBACjD,KAA4B,oBAE3B,IAA0C,IAC3C,IACD,CAAC,GAEC,IAA0B,EAAQ,2BAA2B,IAC7D,IAAmB,EAAQ,kBAC3B,IAAsB,EAAQ,qBAK9B,IAAa,EAAQ,cAAc,GACnC,IAAY,EAAQ,WAAW,KAEjC,IAAY,KAAK,IAAI,GAEnB,IAAQ,EAAO,OACnB;EACE,MAAM;EACN,SAAS;EACT,OAAO;EACP,YAAY;EACZ,cAAc;EACd;EACA,gBAAgB,KAAA;CAClB,GACA,2CAA2C,GAC7C,GAEM,KAAuB,MAAgC;EAM3D,AALA,EAAO,IAAI;GACT,MAAM;GACN,OAAO;GACP,SAAS;IAAE,IAAI;IAAW,MAAM,EAAM;GAAM;EAC9C,CAAC,GACD,EAAM,QAAQ;CAChB,GAEM,IAAO,OAAO,GAAW,GAA2B,IAAiB,OAAU;EACnF,IAAI,EAAa,SAAS;EAK1B,IAFA,IAAY,KAAK,IAAI,GAEjB,GAAqB;GACvB,IAAM,IAAsB,EAAoB,CAAM;GACtD,IAAI,MAAwB,MAAS,OAAO,KAAwB,UAAU;IAC5E,IAAI,EAAa,SAAS;IAC1B,IAAM,IAAW,OAAO,KAAwB,WAC5C,IACA,4CAEE,IAAc,IAAiB,OAAO,EAAO,cAAc,EAAM,MAAM,IAAI;IAEjF,EAAoB;KAClB,MAAM;KACN,SAAS;KACT,OAAO;KACP,YAAY;KACZ,cAAc;KACd;KACA,gBAAgB,KAAA;IAClB,CAAC;IACD;GACF;EACF;EAGA,IAAM,IADc,KAAkB,IACJ,OAAO,EAAO,cAAc,EAAM,MAAM,IAAI;EAG9E,EAAoB;GAClB,MAAM;GACN,SAAS;GACT,OAAO;GACP,YAAY;GACZ,cAAc;GACd;GACA,gBAAgB,KAAA;EAClB,CAAC;EAGD,KAAK,IAAI,IAAU,GAAG,KAAW,GAAY,KAAW;GACtD,IAAI,EAAa,SAAS;GAG1B,IAAM,IAAgB,YAAY,QAAQ,CAAS,GAG7C,IAAiB,YAAY,IAAI,CAAC,GAAc,CAAa,CAAC;GAEpE,IAAI;IACF,IAAM,IAAO,MAAM,EAAQ,GAAQ,CAAc;IAEjD,IAAI,EAAa,SAAS;IAG1B,IAAI,EAAc,SAChB,MAAM,IAAI,aAAa,4BAA4B,cAAc;IAGnE,IAAI,GAAkB;KACpB,IAAM,IAAmB,EAAiB,CAAI;KAC9C,IAAI,MAAqB,MAAS,OAAO,KAAqB,UAAU;MACtE,IAAI,EAAa,SAAS;MAE1B,EAAoB;OAClB,MAAM;OACN,SAAS;OACT,OAJe,OAAO,KAAqB,WAAW,IAAmB;OAKzE,YAAY;OACZ,cAAc;OACd;OACA,gBAAgB,KAAA;MAClB,CAAC;MACD;KACF;IACF;IAEA,IAAI,EAAa,SAAS;IAE1B,EAAoB;KAClB;KACA,SAAS;KACT,OAAO;KACP,YAAY;KACZ,cAAc;KACd;KACA,gBAAgB,KAAA;IAClB,CAAC;IACD;GAEF,SAAS,GAAY;IAEnB,IAAM,IAAY,EAAc,WAC7B,EAAY,SAAS,kBACrB,EAAY,SAAS,SAAS,SAAS;IAG1C,IAAI,EAAa,WAAW,CAAC,GAAW;IAGxC,IAAM,IAA0B,EAAmB;KACjD,QAAQ,CAAE,EAAY,OAAO;KAAG,aAAa;KAA0C,WAAW;IACpG,CAAC,CAAC,GAAG,OAAO,OAAO,IAGb,IAAuC,KACxC,EAAmB;KACpB,QAAQ,CAAE,EAAY,OAAO;KAAG,aAAa;KAAW,WAAW;IACrE,CAAC,CAAC,GAAG,MACH,2BAEE,IAA6B,KAA4B,EAAY,SAAS;IAGpF,IAAI,MAAY,KAAc,GAA4B;KACxD,IAAI,EAAa,SAAS;KAG1B,IAAM,IAAiB,KAAK,IAAI,IAAI,GAU9B,IAAoB,GAPL,IACjB,2BAA2B,EAAU,cAAc,IAAU,EAAE,GAAG,IAAa,EAAE,KACjF,IACE,IACA,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,EAGL,GAAG,EAAO,OAAO,iCAAiC,qEAAqE,IAAa,EAAE,KAAK;KAErL,EAAoB;MAClB,MAAM;MACN,SAAS;MACT,OAAO;MACP,YAAY;MACZ,cAAc,IAAU;MACxB;MACA;KACF,CAAC;KACD;IACF,OAAO;KAEL,IAAI,EAAa,SAAS;KAE1B,IAAI,IAAgC,MAAqB,KAAG;KAY5D,AAXA,IAAe,KAAK,IAAI,GAAc,GAAa,IAAI,KAAK,OAAO,IAAI,KAEvE,EAAoB;MAClB,MAAM,EAAM,MAAM;MAClB,SAAS;MACT,OAAO;MACP,YAAY;MACZ,cAAc,IAAU;MACxB;KACF,CAAC,GAED,EAAO,IAAI;MACT,MAAM;MACN,OAAO,GAAG,EAAmB;MAC7B,SAAS,EAAE,SAAS;OAAE,SAAS,IAAU;OAAG,OAAO;OAAY,aAAa,KAAK,MAAM,CAAY;MAAE,EAAE;KACzG,CAAC;KAED,IAAM,IAAY,IAAY,aAAa;KAE3C,AAAI,EAAO,OAAO,aAAW,QAAQ,KAAK,qBAAqB,EAAmB,YAAY,EAAU,WAAW,IAAU,EAAE,MAAM,IAAa,EAAE,gCAAgC,KAAK,MAAM,CAAY,EAAE,GAAG;KAEhN,IAAI;MAGF,IADA,MAAM,EAAO,MAAM,GAAc,CAAY,GACzC,EAAa,SAAS;KAC5B,QAAqB;MACnB;KACF;IACF;GACF;EACF;CACF,GAEI,IAAiD,MAE/C,IAAwB,EAAO,aAAa;EAChD,IAAM,IAAS,IAAS,EAAO,QAAQ,KAAA,GACjC,IAAmB,IAAI,gBAAgB;EAO7C,OANA,IAAyB,GAEzB,EAAO,cAAc;GACnB,EAAK,GAAa,EAAiB,QAAQ,EAAI;EACjD,CAAC,SAEY,EAAiB,MAAM;CACtC,GAAG,iCAAiC;CAEpC,OAAO;EACL,IAAI,OAAO;GAAE,OAAO,EAAM,MAAM;EAAK;EACrC,IAAI,UAAU;GAAE,OAAO,EAAM,MAAM;EAAQ;EAC3C,IAAI,QAAQ;GAAE,OAAO,EAAM,MAAM;EAAM;EACvC,IAAI,aAAa;GAAE,OAAO,EAAM,MAAM;EAAW;EACjD,IAAI,QAAQ;GAAE,OAAO,EAAM;EAAM;EACjC,eAAe;GAGb,AAFA,GAAwB,MAAM,GAC9B,IAAyB,IAAI,gBAAgB,GAC7C,EAAK,IAAS,EAAO,QAAQ,KAAA,GAAgB,EAAuB,QAAQ,EAAK;EACnF;EACA,YAAY,MAAwC,EAAM,UAAU,CAAE;EACtE,eAAe;GAMb,AALA,GAAwB,MAAM,GAC9B,IAAyB,MACrB,OAAO,KAA0B,cACnC,EAAsB,GAEnB,EAAc,WACjB,EAAe,QAAQ;EAE3B;CACF;AACF"}
|
package/dist/core.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.mjs","names":[],"sources":["../src/core/core.ts"],"sourcesContent":["// import { getExtractedValues } from '../utils/getExtractedValues'\nimport { Signal, Computed, IEffect, EngineLoggerOptions, LogEventType, CleanupFn, TransactionContext, LogRecord, SignalOptions, ResourceOptions, ResourceState, Resource } from './types'\nimport { createComputed } from './core.method.computed'\nimport { createSignal } from './core.method.signal'\n// import { executeBatch } from './core.method.batch'\nimport { executeUntrack } from './core.method.untrack'\nimport { createReactive } from './core.method.reactive'\nimport { createEffect } from './core.method.effect'\nimport { ReactiveDebounceLogger } from './core.logger'\nimport { createResource } from './core.method.resource'\n\n// Тип для конструктора класса\ntype Constructor<T> = new (...args: any[]) => T\n// Тип для фабричной функции зависимостей\ntype Factory<T> = (engine: any) => T\n// Универсальный тип токена DI\ntype InjectionToken<T> = Constructor<T> | Factory<T> | symbol | string\n\nconst badgeColors: Record<LogEventType, string> = {\n signal: 'background: #007acc; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n computed: 'background: #42b883; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n effect: 'background: #e01e5a; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n batch: 'background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n resource: 'background: #d97706; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n reactive: 'background: #007acc; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;'\n}\nlet currentTransaction: TransactionContext | null = null\nfunction safeValueToString(value: unknown): string {\n if (value === null) return 'null'\n if (value === undefined) return 'undefined'\n if (typeof value === 'object') {\n try {\n return JSON.stringify(value)\n } catch (e) {\n return '[Cyclic Object or Layer]'\n }\n }\n return String(value)\n}\nexport function queueLog(type: LogEventType, label: string, details?: { from?: unknown; to?: unknown; payload?: unknown }): void {\n if (!currentTransaction) {\n currentTransaction = { id: Math.random().toString(36).substring(2, 9), pendingLogQueue: [], isFlushing: false }\n queueMicrotask(() => flushLogs())\n }\n currentTransaction.pendingLogQueue.push({ type, label, from: details?.from, to: details?.to, payload: details?.payload, timestamp: Date.now() })\n}\nexport function flushLogs(): void {\n if (!currentTransaction || currentTransaction.isFlushing) return\n currentTransaction.isFlushing = true\n const queue = currentTransaction.pendingLogQueue\n if (queue.length === 0) {\n currentTransaction = null\n return\n }\n console.groupCollapsed(`%cREACTIVE TRANSACTION%c [id: ${currentTransaction.id}] (${queue.length} operations)`, 'background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;', 'color: #888; font-weight: normal;')\n for (const record of queue) {\n const colorStyle = badgeColors[record.type]\n const hasTransition = record.from !== undefined || record.to !== undefined\n let logMessage = `%c${record.type.toUpperCase()}%c ${record.label}`\n\n if (hasTransition) {\n if (!!record.from)\n logMessage += ` ${safeValueToString(record.from)} → ${safeValueToString(record.to)}`\n else logMessage += ` (${safeValueToString(record.to)})`\n }\n\n if (record.payload !== undefined) {\n console.groupCollapsed(logMessage, colorStyle, 'color: inherit;')\n console.log('Payload:', record.payload)\n console.groupEnd()\n } else {\n console.log(logMessage, colorStyle, 'color: inherit;')\n }\n }\n console.groupEnd()\n currentTransaction = null\n}\nexport class ReactiveEngineCore {\n protected frameworkPrefix = 'core'\n public logger: EngineLoggerOptions\n public epoch = 0\n public activeConsumer: any = null\n public batchDepth = 0\n public subscriberId = 0\n public pendingEffects: Array<Set<any> | undefined> = []\n public maxPendingDepth = 0\n public computedCache = new WeakMap<Function, any>()\n private diRegistry = new Map<unknown, unknown>()\n private diCache = new Map<unknown, unknown>()\n private proxyCache = new WeakMap<object, any>()\n private proxyTargets = new WeakSet<object>()\n private reactiveSignals = new WeakMap<object, Map<string | symbol, any>>()\n private cycleDetectionStack = new Set<unknown>()\n public batchedSignals = new Set<any>()\n public activeOwner: any = null\n // Системная очередь для отложенного батчинга деструкторов\n public pendingCleanups = new Set<() => void>()\n\n // Метод для регистрации отложенной очистки\n public queueCleanup(cleanup: () => void): void {\n this.pendingCleanups.add(cleanup)\n }\n\n constructor(options?: { logger?: EngineLoggerOptions }) {\n this.logger = options?.logger || { isEnabled: false }\n }\n\n public scheduleEffect(effect: any): void {\n const depth = effect.depth || 0\n if (!this.pendingEffects[depth]) {\n this.pendingEffects[depth] = new Set()\n }\n this.pendingEffects[depth]!.add(effect)\n if (depth > this.maxPendingDepth) {\n this.maxPendingDepth = depth\n }\n }\n\n public queueLog(type: LogEventType, label: string, details?: { from?: unknown; to?: unknown; payload?: unknown }): void {\n queueLog(type, label, details)\n }\n\n public batch<T>(fn: () => T): T {\n this.batchDepth++\n try {\n return fn()\n } finally {\n this.batchDepth--\n if (this.batchDepth === 0) {\n if (this.batchedSignals.size > 0) {\n for (const sig of this.batchedSignals) {\n if (sig._flushBatch) sig._flushBatch()\n }\n this.batchedSignals.clear()\n }\n\n this.flushEffects()\n }\n }\n }\n\n public flushEffects(): void {\n if (this.batchDepth > 0) return\n\n const prevConsumer = this.activeConsumer\n\n try {\n for (let depth = 0; depth <= this.maxPendingDepth; depth++) {\n const bucket = this.pendingEffects[depth]\n\n if (!bucket || bucket.size === 0) continue\n\n const effects = Array.from(bucket)\n bucket.clear()\n\n for (const effect of effects) {\n if (effect.isDisposed) continue\n\n // ⚡ ЗOЛОТOЙ СТАНДАРТ ОПТИМИЗАЦИИ (Early Dirty Check Bypass):\n // Переносим проверку грязи в самое начало цикла. Если эффект не был промаркирован\n // как DIRTY волной push-уведомлений, мы мгновенно пропускаем его, полностью\n // избавляя V8 от холостого обхода родителей и тяжелых хэш-поисков depVersions.get(dep).\n if (!effect.isDirty) continue\n\n if (effect.dependencies && effect.dependencies.size > 0) {\n let parentChanged = false\n\n for (const dep of effect.dependencies) {\n const rawNode = dep._node || dep\n\n if (rawNode.type === 'computed' && rawNode.state > 0) {\n if (rawNode.update) rawNode.update()\n }\n\n if (effect.depVersions && effect.depVersions.get(dep) !== rawNode.version) {\n parentChanged = true\n }\n }\n\n if (!parentChanged && effect.dependencies.size > 0) {\n effect.isDirty = false\n continue\n }\n }\n\n try {\n effect.run()\n } catch (err) {\n // ⚡ ФИКС ТЕСТА #177: Если один из эффектов выбросил исключение,\n // принудительно очищаем и гасим флаги грязи (isDirty) у всех оставшихся\n // в очереди эффектов текущего и последующих уровней глубины.\n for (let d = depth; d <= this.maxPendingDepth; d++) {\n const remainingBucket = this.pendingEffects[d]\n if (remainingBucket) {\n for (const remEffect of remainingBucket) {\n remEffect.isDirty = false\n }\n remainingBucket.clear()\n }\n }\n this.maxPendingDepth = 0\n throw err // Прокидываем ошибку дальше по спецификации\n }\n\n }\n\n if (depth > 0) {\n let hasEarlierEffects = false\n for (let i = 0; i < depth; i++) {\n if (this.pendingEffects[i] && this.pendingEffects[i]!.size > 0) {\n hasEarlierEffects = true\n break\n }\n }\n if (hasEarlierEffects) depth = -1\n }\n }\n this.maxPendingDepth = 0\n } finally {\n this.activeConsumer = prevConsumer\n }\n }\n\n // public batch<T>(fn: () => T): T {\n // return executeBatch<T>(this, fn)\n // }\n\n // ⚛️ ФИКС ДЛЯ РЕЛИЗА 2.5.0: Меняем private на public,\n // чтобы изолированные функции-методы имели легальный доступ к регистратору логов\n public log(params: any) {\n if (!this.logger || !this.logger.isEnabled) return\n ReactiveDebounceLogger.processLog(this.logger, queueLog, params)\n }\n\n public signal<T>(initialValue: T, optionsOrName?: string | SignalOptions<T>): Signal<T> {\n return createSignal<T>(this, initialValue, optionsOrName)\n }\n\n public computed<T>(fn: () => T, signalName?: string): Computed<T> {\n // Просто делегируем вызов строго изолированной чистой функции\n return createComputed<T>(this, fn, signalName)\n }\n\n public effect(fn: () => void | CleanupFn, effectName?: string): () => void {\n return createEffect(this, fn, effectName)\n }\n\n public untrack<T>(fn: () => T): T {\n return executeUntrack<T>(this, fn)\n }\n\n public reactive<T extends object>(target: T, name?: string): T {\n return createReactive<T>(this, target, name)\n }\n\n public provide<T>(token: InjectionToken<T>, valueOrFactory: T | Factory<T>): void {\n this.diRegistry.set(token, valueOrFactory)\n this.diCache.delete(token)\n }\n\n public inject<T>(token: InjectionToken<T>): T {\n if (token === undefined || token === null) throw new Error('[DI Error] Token is empty')\n if (this.diCache.has(token)) return this.diCache.get(token) as T\n if (this.cycleDetectionStack.has(token)) throw new Error('[DI Error] Cyclic dependency detected')\n this.cycleDetectionStack.add(token)\n try {\n if (this.diRegistry.has(token)) {\n const entry = this.diRegistry.get(token)\n if (typeof entry === 'function') {\n if ((entry as any)._isMockFunction) {\n const mockRes = (entry as Function)(this)\n if (mockRes !== undefined) {\n this.diCache.set(token, mockRes)\n return mockRes as T\n }\n return entry as unknown as T\n }\n const str = entry.toString()\n const isClass = str.startsWith('class ') || str.startsWith('class{') || (entry.prototype && entry.prototype.constructor === entry && !str.startsWith('function') && !str.includes('=>'))\n if (!isClass) {\n const resolved = (entry as Factory<T>)(this)\n this.diCache.set(token, resolved)\n return resolved\n }\n return entry as unknown as T\n }\n return entry as T\n }\n if (typeof token === 'function') {\n const resolved = new (token as Constructor<T>)(this)\n this.diCache.set(token, resolved)\n return resolved\n }\n throw new Error('[DI Error] Token not found')\n } finally {\n this.cycleDetectionStack.delete(token)\n }\n }\n\n public delay(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal.aborted) {\n return reject(new DOMException('Aborted', 'AbortError'))\n }\n\n const timeoutId = setTimeout(() => {\n signal.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n\n const onAbort = () => {\n clearTimeout(timeoutId)\n reject(new DOMException('Aborted', 'AbortError'))\n }\n\n signal.addEventListener('abort', onAbort, { once: true })\n })\n }\n\n /**\n * Создает умную реактивную декларативную обертку над асинхронными операциями\n * с автоматическим подавлением Race Conditions и встроенным методом .destroy().\n */\n public resource<T, S = void>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n source?: { value: S },\n optionsOrName?: string | ResourceOptions<T, S>,\n ): Resource<T> {\n // Передаем инстанс движка (this) как IReactiveEngine для соблюдения строгой типизации\n return createResource<T, S>(this, fetcher, source, optionsOrName)\n }\n}\n\nexport { ReactiveEngineCore as ReactiveEngine, ReactiveEngineCore as ReactiveEngineAutomatic }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"core.mjs","names":[],"sources":["../src/core/core.ts"],"sourcesContent":["// import { getExtractedValues } from '../utils/getExtractedValues'\nimport { Signal, Computed, IEffect, EngineLoggerOptions, LogEventType, CleanupFn, TransactionContext, LogRecord, SignalOptions, ResourceOptions, ResourceState, Resource } from './types'\nimport { createComputed } from './core.method.computed'\nimport { createSignal } from './core.method.signal'\n// import { executeBatch } from './core.method.batch'\nimport { executeUntrack } from './core.method.untrack'\nimport { createReactive } from './core.method.reactive'\nimport { createEffect } from './core.method.effect'\nimport { ReactiveDebounceLogger } from './core.logger'\nimport { createResource } from './core.method.resource'\n\n// Тип для конструктора класса\ntype Constructor<T> = new (...args: any[]) => T\n// Тип для фабричной функции зависимостей\ntype Factory<T> = (engine: any) => T\n// Универсальный тип токена DI\ntype InjectionToken<T> = Constructor<T> | Factory<T> | symbol | string\n\nconst badgeColors: Record<LogEventType, string> = {\n signal: 'background: #007acc; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n computed: 'background: #42b883; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n effect: 'background: #e01e5a; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n batch: 'background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n resource: 'background: #d97706; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;',\n reactive: 'background: #007acc; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;'\n}\nlet currentTransaction: TransactionContext | null = null\nfunction safeValueToString(value: unknown): string {\n if (value === null) return 'null'\n if (value === undefined) return 'undefined'\n if (typeof value === 'object') {\n try {\n return JSON.stringify(value)\n } catch (e) {\n return '[Cyclic Object or Layer]'\n }\n }\n return String(value)\n}\nexport function queueLog(type: LogEventType, label: string, details?: { from?: unknown; to?: unknown; payload?: unknown }): void {\n if (!currentTransaction) {\n currentTransaction = { id: Math.random().toString(36).substring(2, 9), pendingLogQueue: [], isFlushing: false }\n queueMicrotask(() => flushLogs())\n }\n currentTransaction.pendingLogQueue.push({ type, label, from: details?.from, to: details?.to, payload: details?.payload, timestamp: Date.now() })\n}\nexport function flushLogs(): void {\n if (!currentTransaction || currentTransaction.isFlushing) return\n currentTransaction.isFlushing = true\n const queue = currentTransaction.pendingLogQueue\n if (queue.length === 0) {\n currentTransaction = null\n return\n }\n console.groupCollapsed(`%cREACTIVE TRANSACTION%c [id: ${currentTransaction.id}] (${queue.length} operations)`, 'background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;', 'color: #888; font-weight: normal;')\n for (const record of queue) {\n const colorStyle = badgeColors[record.type]\n const hasTransition = record.from !== undefined || record.to !== undefined\n let logMessage = `%c${record.type.toUpperCase()}%c ${record.label}`\n\n if (hasTransition) {\n if (!!record.from)\n logMessage += ` ${safeValueToString(record.from)} → ${safeValueToString(record.to)}`\n else logMessage += ` (${safeValueToString(record.to)})`\n }\n\n if (record.payload !== undefined) {\n console.groupCollapsed(logMessage, colorStyle, 'color: inherit;')\n console.log('Payload:', record.payload)\n console.groupEnd()\n } else {\n console.log(logMessage, colorStyle, 'color: inherit;')\n }\n }\n console.groupEnd()\n currentTransaction = null\n}\nexport class ReactiveEngineCore {\n protected frameworkPrefix = 'core'\n public logger: EngineLoggerOptions\n public epoch = 0\n public activeConsumer: any = null\n public batchDepth = 0\n public subscriberId = 0\n public pendingEffects: Array<Set<any> | undefined> = []\n public maxPendingDepth = 0\n public computedCache = new WeakMap<Function, any>()\n private diRegistry = new Map<unknown, unknown>()\n private diCache = new Map<unknown, unknown>()\n private proxyCache = new WeakMap<object, any>()\n private proxyTargets = new WeakSet<object>()\n private reactiveSignals = new WeakMap<object, Map<string | symbol, any>>()\n private cycleDetectionStack = new Set<unknown>()\n public batchedSignals = new Set<any>()\n public activeOwner: any = null\n // Системная очередь для отложенного батчинга деструкторов\n public pendingCleanups = new Set<() => void>()\n\n // Метод для регистрации отложенной очистки\n public queueCleanup(cleanup: () => void): void {\n this.pendingCleanups.add(cleanup)\n }\n\n constructor(options?: { logger?: EngineLoggerOptions }) {\n this.logger = options?.logger || { isEnabled: false }\n }\n\n public scheduleEffect(effect: any): void {\n const depth = effect.depth || 0\n if (!this.pendingEffects[depth]) {\n this.pendingEffects[depth] = new Set()\n }\n this.pendingEffects[depth]!.add(effect)\n if (depth > this.maxPendingDepth) {\n this.maxPendingDepth = depth\n }\n }\n\n public queueLog(type: LogEventType, label: string, details?: { from?: unknown; to?: unknown; payload?: unknown }): void {\n queueLog(type, label, details)\n }\n\n public batch<T>(fn: () => T): T {\n this.batchDepth++\n try {\n return fn()\n } finally {\n this.batchDepth--\n if (this.batchDepth === 0) {\n if (this.batchedSignals.size > 0) {\n for (const sig of this.batchedSignals) {\n if (sig._flushBatch) sig._flushBatch()\n }\n this.batchedSignals.clear()\n }\n\n this.flushEffects()\n }\n }\n }\n\n public flushEffects(): void {\n if (this.batchDepth > 0) return\n\n const prevConsumer = this.activeConsumer\n\n try {\n for (let depth = 0; depth <= this.maxPendingDepth; depth++) {\n const bucket = this.pendingEffects[depth]\n\n if (!bucket || bucket.size === 0) continue\n\n const effects = Array.from(bucket)\n bucket.clear()\n\n for (const effect of effects) {\n if (effect.isDisposed) continue\n\n // ⚡ ЗOЛОТOЙ СТАНДАРТ ОПТИМИЗАЦИИ (Early Dirty Check Bypass):\n // Переносим проверку грязи в самое начало цикла. Если эффект не был промаркирован\n // как DIRTY волной push-уведомлений, мы мгновенно пропускаем его, полностью\n // избавляя V8 от холостого обхода родителей и тяжелых хэш-поисков depVersions.get(dep).\n if (!effect.isDirty) continue\n\n if (effect.dependencies && effect.dependencies.size > 0) {\n let parentChanged = false\n\n for (const dep of effect.dependencies) {\n const rawNode = dep._node || dep\n\n if (rawNode.type === 'computed' && rawNode.state > 0) {\n if (rawNode.update) rawNode.update()\n }\n\n if (effect.depVersions && effect.depVersions.get(dep) !== rawNode.version) {\n parentChanged = true\n }\n }\n\n if (!parentChanged && effect.dependencies.size > 0) {\n effect.isDirty = false\n continue\n }\n }\n\n try {\n effect.run()\n } catch (err) {\n // ⚡ ФИКС ТЕСТА #177: Если один из эффектов выбросил исключение,\n // принудительно очищаем и гасим флаги грязи (isDirty) у всех оставшихся\n // в очереди эффектов текущего и последующих уровней глубины.\n for (let d = depth; d <= this.maxPendingDepth; d++) {\n const remainingBucket = this.pendingEffects[d]\n if (remainingBucket) {\n for (const remEffect of remainingBucket) {\n remEffect.isDirty = false\n }\n remainingBucket.clear()\n }\n }\n this.maxPendingDepth = 0\n throw err // Прокидываем ошибку дальше по спецификации\n }\n\n }\n\n if (depth > 0) {\n let hasEarlierEffects = false\n for (let i = 0; i < depth; i++) {\n if (this.pendingEffects[i] && this.pendingEffects[i]!.size > 0) {\n hasEarlierEffects = true\n break\n }\n }\n if (hasEarlierEffects) depth = -1\n }\n }\n this.maxPendingDepth = 0\n } finally {\n this.activeConsumer = prevConsumer\n }\n }\n\n // public batch<T>(fn: () => T): T {\n // return executeBatch<T>(this, fn)\n // }\n\n // ⚛️ ФИКС ДЛЯ РЕЛИЗА 2.5.0: Меняем private на public,\n // чтобы изолированные функции-методы имели легальный доступ к регистратору логов\n public log(params: any) {\n if (!this.logger || !this.logger.isEnabled) return\n ReactiveDebounceLogger.processLog(this.logger, queueLog, params)\n }\n\n /**\n * Создает новый реактивный сигнал с заданным начальным значением и опциями.\n *\n * @template T Тип значения сигнала.\n * @param initialValue Начальное значение сигнала.\n * @param optionsOrName Конфигурационные параметры сигнала или его имя.\n * @param optionsOrName.name Уникальное текстовое имя сигнала для отладки и вывода в транзакционный логгер.\n * @param optionsOrName.validate Пользовательская функция валидации значения, возвращающая true/false или строку ошибки.\n *\n * @method signal\n * @memberof ReactiveEngine\n *\n * @example\n * // Пример 1: Чистый TypeScript (Базовый)\n * const engine = new ReactiveEngineCore();\n * const counter = engine.signal(0, { name: 'counter' });\n * console.log(counter.value); // 0\n * counter.value = 1;\n * console.log(counter.value); // 1\n *\n * @example\n * // Пример 2: Чистый TypeScript (Продвинутый с валидацией)\n * interface User { id: number; name: string; }\n * const engine = new ReactiveEngineCore();\n * const userSignal = engine.signal<User>({ id: 1, name: 'John Doe' }, {\n * name: 'user',\n * validate: (user) => user.id > 0 && typeof user.name === 'string'\n * });\n * userSignal.value = { id: -1, name: 'Jane Doe' }; // Ошибка валидации\n *\n * @example\n * // Пример 3: Интеграция с React (через DI контейнер)\n * interface User { id: number; name: string; }\n * class UserService {\n * constructor(engine: ReactiveEngineCore) {\n * this.mySignal = engine.signal<User>({ id: 1, name: 'John Doe' }, { name: 'userSignal' });\n * }\n *\n * mySignal: Signal<User>;\n * }\n *\n * const engine = new ReactiveEngineCore();\n * engine.provide(UserService, UserService);\n * const logic = engine.inject(UserService);\n *\n * function MyComponent() {\n * const user = useReactiveValue(logic.mySignal);\n * return <div>{user.name}</div>;\n * }\n */\n public signal<T>(initialValue: T, optionsOrName?: string | SignalOptions<T>): Signal<T> {\n return createSignal<T>(this, initialValue, optionsOrName)\n }\n\n /**\n * Создает новое вычисляемое свойство (Computed) на основе чистой функции.\n *\n * @template T Тип значения, возвращаемого вычисляемым свойством.\n * @param fn Чистая функция-вычислитель, которая определяет логику вычисления значения.\n * @param signalName Опциональное имя сигнала для отладки и вывода в транзакционный логгер.\n *\n * @method computed\n * @memberof ReactiveEngine\n *\n * @example\n * // Пример 1: Чистый TypeScript (Базовый)\n * const engine = new ReactiveEngineCore();\n * const counter = engine.signal(0, { name: 'counter' });\n * const doubleCounter = engine.computed(() => counter.value * 2);\n * console.log(doubleCounter.value); // 0\n * counter.value = 1;\n * console.log(doubleCounter.value); // 2\n *\n * @example\n * // Пример 2: Чистый TypeScript (Продвинутый с цепочкой зависимостей)\n * interface User { id: number; name: string; }\n * const engine = new ReactiveEngineCore();\n * const userSignal = engine.signal<User>({ id: 1, name: 'John Doe' }, { name: 'user' });\n * const userNameLength = engine.computed(() => userSignal.value.name.length);\n * const hasLongName = engine.computed(() => userNameLength.value > 5);\n *\n * console.log(hasLongName.value); // false\n * userSignal.value = { id: 1, name: 'Johnathan' };\n * console.log(userNameLength.value); // 9\n * console.log(hasLongName.value); // true\n *\n * @example\n * // Пример 3: Интеграция с React (через DI контейнер)\n * interface User { id: number; name: string; }\n * class UserService {\n * constructor(engine: ReactiveEngineCore) {\n * this.userSignal = engine.signal<User>({ id: 1, name: 'John Doe' }, { name: 'userSignal' });\n * this.userNameLength = engine.computed(() => this.userSignal.value.name.length);\n * }\n *\n * userSignal: Signal<User>;\n * userNameLength: Computed<number>;\n * }\n *\n * const engine = new ReactiveEngineCore();\n * engine.provide(UserService, UserService);\n * const logic = engine.inject(UserService);\n *\n * function MyComponent() {\n * const hasLongName = engine.use(logic.userNameLength);\n * return <div>{hasLongName ? 'Long name' : 'Short name'}</div>;\n * }\n */\n public computed<T>(fn: () => T, signalName?: string): Computed<T> {\n // Просто делегируем вызов строго изолированной чистой функции\n return createComputed<T>(this, fn, signalName)\n }\n\n public effect(fn: () => void | CleanupFn, effectName?: string): () => void {\n return createEffect(this, fn, effectName)\n }\n\n public untrack<T>(fn: () => T): T {\n return executeUntrack<T>(this, fn)\n }\n\n public reactive<T extends object>(target: T, name?: string): T {\n return createReactive<T>(this, target, name)\n }\n\n public provide<T>(token: InjectionToken<T>, valueOrFactory: T | Factory<T>): void {\n this.diRegistry.set(token, valueOrFactory)\n this.diCache.delete(token)\n }\n\n public inject<T>(token: InjectionToken<T>): T {\n if (token === undefined || token === null) throw new Error('[DI Error] Token is empty')\n if (this.diCache.has(token)) return this.diCache.get(token) as T\n if (this.cycleDetectionStack.has(token)) throw new Error('[DI Error] Cyclic dependency detected')\n this.cycleDetectionStack.add(token)\n try {\n if (this.diRegistry.has(token)) {\n const entry = this.diRegistry.get(token)\n if (typeof entry === 'function') {\n if ((entry as any)._isMockFunction) {\n const mockRes = (entry as Function)(this)\n if (mockRes !== undefined) {\n this.diCache.set(token, mockRes)\n return mockRes as T\n }\n return entry as unknown as T\n }\n const str = entry.toString()\n const isClass = str.startsWith('class ') || str.startsWith('class{') || (entry.prototype && entry.prototype.constructor === entry && !str.startsWith('function') && !str.includes('=>'))\n if (!isClass) {\n const resolved = (entry as Factory<T>)(this)\n this.diCache.set(token, resolved)\n return resolved\n }\n return entry as unknown as T\n }\n return entry as T\n }\n if (typeof token === 'function') {\n const resolved = new (token as Constructor<T>)(this)\n this.diCache.set(token, resolved)\n return resolved\n }\n throw new Error('[DI Error] Token not found')\n } finally {\n this.cycleDetectionStack.delete(token)\n }\n }\n\n public delay(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal.aborted) {\n return reject(new DOMException('Aborted', 'AbortError'))\n }\n\n const timeoutId = setTimeout(() => {\n signal.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n\n const onAbort = () => {\n clearTimeout(timeoutId)\n reject(new DOMException('Aborted', 'AbortError'))\n }\n\n signal.addEventListener('abort', onAbort, { once: true })\n })\n }\n\n /**\n * Создает умную реактивную декларативную обертку над асинхронными операциями\n * с автоматическим подавлением Race Conditions и встроенным методом .destroy().\n *\n * @template T Тип данных, возвращаемых фетчером после успешного выполнения.\n * @template S Тип реактивной зависимости (source).\n * @param fetcher Асинхронная функция, выполняющая запрос и возвращающая данные типа T.\n * @param source Реактивный сигнал или примитив, предоставляющий источник данных для фетчера.\n * @param optionsOrName Конфигурационные опции ресурса или его имя.\n * @param optionsOrName.name Имя ресурса.\n * @param optionsOrName.resetDataOnSourceChange Флаг, указывающий, следует ли сбрасывать данные при изменении источника.\n * @param optionsOrName.validateBeforeFetch Функция валидации источника перед выполнением фетча.\n * @param optionsOrName.responseValidate Функция валидации ответа после выполнения фетча.\n * @param optionsOrName.timeout Максимальное время ожидания запроса в миллисекундах.\n * @param optionsOrName.retryCount Количество попыток повторного выполнения запроса в случае ошибки.\n *\n * @example\n * // Пример 1: Чистый TypeScript (Базовый)\n * interface User { id: number; name: string; }\n * interface ApiResponse<T> { ok: boolean; message?: string; data: T; }\n * const engine = new ReactiveEngineCore();\n * const userSignal = engine.signal<User>({ id: 1, name: 'John Doe' });\n * const fetchUser = async (source: User, signal: AbortSignal): Promise<ApiResponse<User>> => {\n * // Реализация фетча пользователя\n * };\n * const userResource = engine.resource(fetchUser, { value: userSignal.value }, { name: 'userResource' });\n *\n * @example\n * // Пример 2: Чистый TypeScript (Продвинутый)\n * interface User { id: number; name: string; }\n * interface ApiResponse<T> { ok: boolean; message?: string; data: T; }\n * const engine = new ReactiveEngineCore();\n * const userSignal = engine.signal<User>({ id: 1, name: 'John Doe' });\n * const fetchUser = async (source: User, signal: AbortSignal): Promise<ApiResponse<User>> => {\n * // Реализация фетча пользователя с использованием AbortSignal и timeout\n * };\n * const userResource = engine.resource(fetchUser, { value: userSignal.value }, {\n * name: 'userResource',\n * timeout: 5000,\n * retryCount: 3,\n * });\n *\n * @example\n * // Пример 3: Интеграция с React (через DI контейнер)\n * interface User { id: number; name: string; }\n * interface ApiResponse<T> { ok: boolean; message?: string; data: T; }\n * class UserService {\n * constructor(engine: ReactiveEngineCore) {\n * const userSignal = engine.signal<User>({ id: 1, name: 'John Doe' });\n * this.myResource = engine.resource(fetchUser, { value: userSignal.value }, { name: 'userResource' });\n * }\n *\n * myResource: Resource<ApiResponse<User>>;\n * }\n *\n * const engine = new ReactiveEngineCore();\n * engine.provide(UserService, UserService);\n * const logic = engine.inject(UserService);\n * const state = useReactiveValue(logic.myResource);\n */\n public resource<T, S = void>(\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n source?: { value: S },\n optionsOrName?: string | ResourceOptions<T, S>,\n ): Resource<T> {\n // Передаем инстанс движка (this) как IReactiveEngine для соблюдения строгой типизации\n return createResource<T, S>(this, fetcher, source, optionsOrName)\n }\n}\n\nexport { ReactiveEngineCore as ReactiveEngine, ReactiveEngineCore as ReactiveEngineAutomatic }\n"],"mappings":"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"}
|
|
@@ -9,10 +9,10 @@ export interface ValidationOptions<T> {
|
|
|
9
9
|
/**
|
|
10
10
|
* Декоратор для синхронных экшенов (например, переключение шагов)
|
|
11
11
|
*/
|
|
12
|
-
export declare const
|
|
12
|
+
export declare const withValidationSync: <T, Args extends any[], R>(targetAction: (...args: Args) => R, options: ValidationOptions<T>) => (currentState: T, ...args: Args) => R | void;
|
|
13
13
|
/**
|
|
14
14
|
* Декоратор СТРОГО для асинхронных фетчеров engine.resource.
|
|
15
15
|
* Прозрачно сохраняет типы входных и выходных данных оригинального fetcher.
|
|
16
16
|
*/
|
|
17
|
-
export declare const
|
|
17
|
+
export declare const withValidationAsync: <S, T>(fetcher: (source: S, signal: AbortSignal) => Promise<T>, options: ValidationOptions<S>) => (sourceValue: S, abortSignal: AbortSignal) => Promise<T>;
|
|
18
18
|
//# sourceMappingURL=withValidation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"withValidation.d.ts","sourceRoot":"","sources":["../../../src/decorators/withValidation/withValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAEzC,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;KAC/B,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI;CAC1D,CAAA;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAA;IAC3B,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAC7C;AAED;;GAEG;AACH,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"withValidation.d.ts","sourceRoot":"","sources":["../../../src/decorators/withValidation/withValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAEzC,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;KAC/B,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI;CAC1D,CAAA;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAA;IAC3B,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAC7C;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,EAAE,IAAI,SAAS,GAAG,EAAE,EAAE,CAAC,EACzD,cAAc,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,EAClC,SAAS,iBAAiB,CAAC,CAAC,CAAC,MAErB,cAAc,CAAC,EAAE,GAAG,MAAM,IAAI,KAAG,CAAC,GAAG,IAqB9C,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,EAAE,CAAC,EACtC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EACvD,SAAS,iBAAiB,CAAC,CAAC,CAAC,MAEf,aAAa,CAAC,EAAE,aAAa,WAAW,KAAG,OAAO,CAAC,CAAC,CAwBnE,CAAA"}
|