@pravosleva/reactive-engine 2.7.2 → 3.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/core/core.d.ts +92 -203
  2. package/dist/core/core.d.ts.map +1 -1
  3. package/dist/core/core.global.d.ts +64 -8
  4. package/dist/core/core.global.d.ts.map +1 -1
  5. package/dist/core/core.logger.d.ts +2 -0
  6. package/dist/core/core.logger.d.ts.map +1 -1
  7. package/dist/core/core.method.reactive.d.ts +2 -4
  8. package/dist/core/core.method.reactive.d.ts.map +1 -1
  9. package/dist/core/core.method.resource.d.ts +3 -11
  10. package/dist/core/core.method.resource.d.ts.map +1 -1
  11. package/dist/core/types.d.ts +1 -0
  12. package/dist/core/types.d.ts.map +1 -1
  13. package/dist/core.global.mjs +31 -4
  14. package/dist/core.global.mjs.map +1 -1
  15. package/dist/core.logger.mjs +49 -2
  16. package/dist/core.logger.mjs.map +1 -1
  17. package/dist/core.method.reactive.mjs +73 -30
  18. package/dist/core.method.reactive.mjs.map +1 -1
  19. package/dist/core.method.resource.mjs +122 -138
  20. package/dist/core.method.resource.mjs.map +1 -1
  21. package/dist/core.mjs +406 -150
  22. package/dist/core.mjs.map +1 -1
  23. package/dist/index.mjs +12 -12
  24. package/dist/index2.mjs +5 -5
  25. package/dist/react/hooks/useReactiveSubscription.mjs +13 -13
  26. package/dist/react/hooks/useReactiveSubscription.mjs.map +1 -1
  27. package/dist/react/hooks/useReactiveValue.d.ts +0 -4
  28. package/dist/react/hooks/useReactiveValue.d.ts.map +1 -1
  29. package/dist/react/hooks/useReactiveValue.mjs +24 -9
  30. package/dist/react/hooks/useReactiveValue.mjs.map +1 -1
  31. package/dist/reactive-engine.umd.js +2 -2
  32. package/dist/reactive-engine.umd.js.map +1 -1
  33. package/package.json +1 -1
  34. package/dist/core.method.computed.mjs +0 -167
  35. package/dist/core.method.computed.mjs.map +0 -1
  36. package/dist/core.method.effect.mjs +0 -120
  37. package/dist/core.method.effect.mjs.map +0 -1
  38. package/dist/core.method.signal.mjs +0 -106
  39. package/dist/core.method.signal.mjs.map +0 -1
  40. package/dist/core.method.untrack.mjs +0 -14
  41. package/dist/core.method.untrack.mjs.map +0 -1
  42. package/dist/utils/getExtractedValues.mjs +0 -24
  43. package/dist/utils/getExtractedValues.mjs.map +0 -1
@@ -1,5 +1,52 @@
1
1
  //#region src/core/core.logger.ts
2
- var e = class {
2
+ var e = {
3
+ signal: "background: #007acc; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;",
4
+ computed: "background: #42b883; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;",
5
+ effect: "background: #e01e5a; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;",
6
+ batch: "background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;",
7
+ resource: "background: #d97706; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;",
8
+ reactive: "background: #007acc; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;"
9
+ }, t = null;
10
+ function n(e) {
11
+ if (e === null) return "null";
12
+ if (e === void 0) return "undefined";
13
+ if (typeof e == "object") try {
14
+ return JSON.stringify(e);
15
+ } catch {
16
+ return "[Cyclic Object or Layer]";
17
+ }
18
+ return String(e);
19
+ }
20
+ function r(e, n, r) {
21
+ t || (t = {
22
+ id: Math.random().toString(36).substring(2, 9),
23
+ pendingLogQueue: [],
24
+ isFlushing: !1
25
+ }, queueMicrotask(() => i())), t.pendingLogQueue.push({
26
+ type: e,
27
+ label: n,
28
+ from: r?.from,
29
+ to: r?.to,
30
+ payload: r?.payload,
31
+ timestamp: Date.now()
32
+ });
33
+ }
34
+ function i() {
35
+ if (!t || t.isFlushing) return;
36
+ t.isFlushing = !0;
37
+ let r = t.pendingLogQueue;
38
+ if (r.length === 0) {
39
+ t = null;
40
+ return;
41
+ }
42
+ console.groupCollapsed(`%cREACTIVE TRANSACTION%c [id: ${t.id}] (${r.length} operations)`, "background: #7952b3; color: #fff; padding: 2px 6px; border-radius: 4px; font-weight: bold;", "color: #888; font-weight: normal;");
43
+ for (let t of r) {
44
+ let r = e[t.type], i = t.from !== void 0 || t.to !== void 0, a = `%c${t.type.toUpperCase()}%c ${t.label}`;
45
+ i && (t.from === void 0 ? a += ` (${n(t.to)})` : a += ` ${n(t.from)} → ${n(t.to)}`), t.payload === void 0 ? console.log(a, r, "color: inherit;") : (console.groupCollapsed(a, r, "color: inherit;"), console.log("Payload:", t.payload), console.groupEnd());
46
+ }
47
+ console.groupEnd(), t = null;
48
+ }
49
+ var a = class {
3
50
  static churnTracker = /* @__PURE__ */ new Map();
4
51
  static TIME_WINDOW_MS = 1e3;
5
52
  static CHURN_WARNING_THRESHOLD = 3;
@@ -46,6 +93,6 @@ var e = class {
46
93
  }
47
94
  };
48
95
  //#endregion
49
- export { e as ReactiveDebounceLogger };
96
+ export { a as ReactiveDebounceLogger, i as flushLogs, r as queueLog };
50
97
 
51
98
  //# sourceMappingURL=core.logger.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"core.logger.mjs","names":[],"sources":["../src/core/core.logger.ts"],"sourcesContent":["// core.logger.ts\nimport { LogEventType } from \"./types\"\n\ninterface LogDetails {\n from?: unknown;\n to?: unknown;\n payload?: unknown;\n}\n\nexport class ReactiveDebounceLogger {\n private static churnTracker = new Map<string, number[]>()\n\n private static readonly TIME_WINDOW_MS = 1000\n private static readonly CHURN_WARNING_THRESHOLD = 3\n private static readonly CHURN_CRITICAL_THRESHOLD = 6\n\n private static isDeepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false\n return JSON.stringify(a) === JSON.stringify(b)\n }\n\n private static analyzeSignalChurn(label: string): { marker: string; info: string } {\n const now = Date.now()\n if (!this.churnTracker.has(label)) {\n this.churnTracker.set(label, [])\n }\n\n const history = this.churnTracker.get(label)!\n const cutoff = now - this.TIME_WINDOW_MS\n // Фильтруем историю\n const activeHistory = history.filter(ts => ts > cutoff)\n activeHistory.push(now)\n\n const updateCount = activeHistory.length\n const intervalSec = (this.TIME_WINDOW_MS / 1000).toFixed(1)\n\n // ⚛️ ФИКС УТЕЧКИ ПАМЯТИ: Если узел стабилен и старых логов нет,\n // удаляем ключ из Map целиком, чтобы не копить мусор\n if (activeHistory.length <= 1) {\n this.churnTracker.delete(label)\n return { marker: '🟢', info: `[STABLE] (1 изм./${intervalSec}s)` }\n } else {\n this.churnTracker.set(label, activeHistory)\n }\n\n if (updateCount >= this.CHURN_CRITICAL_THRESHOLD) {\n return {\n marker: '🔴',\n info: `[CRITICAL CHURN] (${updateCount} изм./${intervalSec}s) — Нужен debounce!`\n }\n }\n\n if (updateCount >= this.CHURN_WARNING_THRESHOLD) {\n return {\n marker: '🟡',\n info: `[WARNING CHURN] (${updateCount} изм./${intervalSec}s)`\n }\n }\n\n return {\n marker: '🟢',\n info: `[STABLE] (${updateCount} изм./${intervalSec}s)`\n }\n }\n\n public static processLog(\n engineLogger: any,\n queueLogFn: Function,\n params: { type: LogEventType; label: string; details?: LogDetails; isInternalCoreSignal?: boolean }\n ) {\n const { type, label, details, isInternalCoreSignal = false } = params\n\n if (!engineLogger.isEnabled) return\n\n // ⚛️ ФИКС #1: Включаем валидацию RegExp фильтра из настроек\n if (engineLogger.filter) {\n const isMatch = engineLogger.filter instanceof RegExp\n ? engineLogger.filter.test(label)\n : label.includes(engineLogger.filter)\n if (!isMatch) return\n }\n\n if (isInternalCoreSignal && !engineLogger.isCoreOptimizationDebugEnabled) return\n\n if (details && 'from' in details && 'to' in details) {\n if (this.isDeepEqual(details.from, details.to)) {\n // ⚛️ ФИКС #2: Разрешаем логам типов 'resource' выводить IDLE транзакции,\n // иначе глубокое равенство объектов состояния блокирует их вывод\n if ((type === 'computed' || type === 'resource') && engineLogger.isCoreOptimizationDebugEnabled) {\n queueLogFn(type, `🔵 [IDLE] ${label}`, details)\n return\n }\n return\n }\n }\n\n let churnInfo = ''\n if (type === 'signal' || type === 'resource' || type === 'computed') {\n const churn = this.analyzeSignalChurn(label)\n churnInfo = churn.marker === '🟢' ? ` ${churn.marker}` : ` ${churn.marker} ${churn.info}`\n }\n\n queueLogFn(type, `${label}${churnInfo}`, details)\n }\n\n}\n"],"mappings":";AASA,IAAa,IAAb,MAAoC;CAClC,OAAe,+BAAe,IAAI,IAAsB;CAExD,OAAwB,iBAAiB;CACzC,OAAwB,0BAA0B;CAClD,OAAwB,2BAA2B;CAEnD,OAAe,YAAY,GAAY,GAAqB;EAG1D,OAFI,MAAM,IAAU,KAChB,OAAO,KAAM,YAAY,OAAO,KAAM,YAAY,MAAM,QAAQ,MAAM,OAAa,KAChF,KAAK,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC;CAC/C;CAEA,OAAe,mBAAmB,GAAiD;EACjF,IAAM,IAAM,KAAK,IAAI;EACrB,AAAK,KAAK,aAAa,IAAI,CAAK,KAC9B,KAAK,aAAa,IAAI,GAAO,CAAC,CAAC;EAGjC,IAAM,IAAU,KAAK,aAAa,IAAI,CAAK,GACrC,IAAS,IAAM,KAAK,gBAEpB,IAAgB,EAAQ,QAAO,MAAM,IAAK,CAAM;EACtD,EAAc,KAAK,CAAG;EAEtB,IAAM,IAAc,EAAc,QAC5B,KAAe,KAAK,iBAAiB,IAAA,CAAM,QAAQ,CAAC;EAyB1D,OArBI,EAAc,UAAU,KAC1B,KAAK,aAAa,OAAO,CAAK,GACvB;GAAE,QAAQ;GAAM,MAAM,oBAAoB,EAAY;EAAI,MAEjE,KAAK,aAAa,IAAI,GAAO,CAAa,GAGxC,KAAe,KAAK,2BACf;GACL,QAAQ;GACR,MAAM,qBAAqB,EAAY,QAAQ,EAAY;EAC7D,IAGE,KAAe,KAAK,0BACf;GACL,QAAQ;GACR,MAAM,oBAAoB,EAAY,QAAQ,EAAY;EAC5D,IAGK;GACL,QAAQ;GACR,MAAM,aAAa,EAAY,QAAQ,EAAY;EACrD;CACF;CAEA,OAAc,WACZ,GACA,GACA,GACA;EACA,IAAM,EAAE,SAAM,UAAO,YAAS,0BAAuB,OAAU;EAY/D,IAVI,CAAC,EAAa,aAGd,EAAa,UAIX,EAHY,EAAa,kBAAkB,SAC3C,EAAa,OAAO,KAAK,CAAK,IAC9B,EAAM,SAAS,EAAa,MAAM,MAIpC,KAAwB,CAAC,EAAa,gCAAgC;EAE1E,IAAI,KAAW,UAAU,KAAW,QAAQ,KACtC,KAAK,YAAY,EAAQ,MAAM,EAAQ,EAAE,GAAG;GAG9C,KAAK,MAAS,cAAc,MAAS,eAAe,EAAa,gCAAgC;IAC/F,EAAW,GAAM,aAAa,KAAS,CAAO;IAC9C;GACF;GACA;EACF;EAGF,IAAI,IAAY;EAChB,IAAI,MAAS,YAAY,MAAS,cAAc,MAAS,YAAY;GACnE,IAAM,IAAQ,KAAK,mBAAmB,CAAK;GAC3C,IAAY,EAAM,WAAW,OAAO,IAAI,EAAM,WAAW,IAAI,EAAM,OAAO,GAAG,EAAM;EACrF;EAEA,EAAW,GAAM,GAAG,IAAQ,KAAa,CAAO;CAClD;AAEF"}
1
+ {"version":3,"file":"core.logger.mjs","names":[],"sources":["../src/core/core.logger.ts"],"sourcesContent":["import { LogEventType, TransactionContext, LogRecord } from \"./types\"\n\ninterface LogDetails {\n from?: unknown;\n to?: unknown;\n payload?: unknown;\n}\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}\n\nlet currentTransaction: TransactionContext | null = null\n\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}\n\nexport function queueLog(type: LogEventType, label: string, details?: LogDetails): 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({\n type,\n label,\n from: details?.from,\n to: details?.to,\n payload: details?.payload,\n timestamp: Date.now()\n })\n}\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 !== undefined)\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}\n\nexport class ReactiveDebounceLogger {\n private static churnTracker = new Map<string, number[]>()\n\n private static readonly TIME_WINDOW_MS = 1000\n private static readonly CHURN_WARNING_THRESHOLD = 3\n private static readonly CHURN_CRITICAL_THRESHOLD = 6\n\n private static isDeepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false\n return JSON.stringify(a) === JSON.stringify(b)\n }\n\n private static analyzeSignalChurn(label: string): { marker: string; info: string } {\n const now = Date.now()\n if (!this.churnTracker.has(label)) {\n this.churnTracker.set(label, [])\n }\n\n const history = this.churnTracker.get(label)!\n const cutoff = now - this.TIME_WINDOW_MS\n const activeHistory = history.filter(ts => ts > cutoff)\n activeHistory.push(now)\n\n const updateCount = activeHistory.length\n const intervalSec = (this.TIME_WINDOW_MS / 1000).toFixed(1)\n\n if (activeHistory.length <= 1) {\n this.churnTracker.delete(label)\n return { marker: '🟢', info: `[STABLE] (1 изм./${intervalSec}s)` }\n } else {\n this.churnTracker.set(label, activeHistory)\n }\n\n if (updateCount >= this.CHURN_CRITICAL_THRESHOLD) {\n return { marker: '🔴', info: `[CRITICAL CHURN] (${updateCount} изм./${intervalSec}s) — Нужен debounce!` }\n }\n if (updateCount >= this.CHURN_WARNING_THRESHOLD) {\n return { marker: '🟡', info: `[WARNING CHURN] (${updateCount} изм./${intervalSec}s)` }\n }\n return { marker: '🟢', info: `[STABLE] (${updateCount} изм./${intervalSec}s)` }\n }\n\n public static processLog(\n engineLogger: any,\n queueLogFn: Function,\n params: { type: LogEventType; label: string; details?: LogDetails; isInternalCoreSignal?: boolean }\n ) {\n const { type, label, details, isInternalCoreSignal = false } = params\n\n if (!engineLogger.isEnabled) return\n\n if (engineLogger.filter) {\n const isMatch = engineLogger.filter instanceof RegExp\n ? engineLogger.filter.test(label)\n : label.includes(engineLogger.filter)\n if (!isMatch) return\n }\n\n if (isInternalCoreSignal && !engineLogger.isCoreOptimizationDebugEnabled) return\n\n if (details && 'from' in details && 'to' in details) {\n if (this.isDeepEqual(details.from, details.to)) {\n if ((type === 'computed' || type === 'resource') && engineLogger.isCoreOptimizationDebugEnabled) {\n queueLogFn(type, `🔵 [IDLE] ${label}`, details)\n return\n }\n return\n }\n }\n\n let churnInfo = ''\n if (type === 'signal' || type === 'resource' || type === 'computed') {\n const churn = this.analyzeSignalChurn(label)\n churnInfo = churn.marker === '🟢' ? ` ${churn.marker}` : ` ${churn.marker} ${churn.info}`\n }\n\n queueLogFn(type, `${label}${churnInfo}`, details)\n }\n}\n"],"mappings":"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"}
@@ -2,14 +2,27 @@
2
2
  function e(e, t, n) {
3
3
  if (e.proxyTargets.has(t)) return t;
4
4
  if (e.proxyCache.has(t)) return e.proxyCache.get(t);
5
- e.reactiveSignals.has(t) || e.reactiveSignals.set(t, /* @__PURE__ */ new Map());
6
- let r = e.reactiveSignals.get(t), i = new Proxy(t, {
7
- get(t, i, a) {
8
- if (i === "__subscribe" && Array.isArray(t)) return (n) => {
9
- let i = r.get("length");
10
- return i || (i = e.signal(t.length), r.set("length", i)), i.subscribe(n);
11
- };
12
- if (Array.isArray(t) && typeof i == "string" && [
5
+ let r = n || "reactive";
6
+ e.log({
7
+ type: "reactive",
8
+ label: r,
9
+ details: { to: t }
10
+ }), e.reactiveSignals.has(t) || e.reactiveSignals.set(t, /* @__PURE__ */ new Map());
11
+ let i = e.reactiveSignals.get(t), a = Symbol("ownKeys");
12
+ function o(t, n) {
13
+ let a = i.get(t);
14
+ return a || (a = e.signal(n, {
15
+ name: `${r}.${String(t)}`,
16
+ isInternalCoreSignal: !0
17
+ }), i.set(t, a)), a;
18
+ }
19
+ let s = new Proxy(t, {
20
+ ownKeys(e) {
21
+ return o(a, 0).value, Reflect.ownKeys(e);
22
+ },
23
+ get(t, r, s) {
24
+ if (r === "__subscribe" && Array.isArray(t)) return (e) => o("length", t.length).subscribe(e);
25
+ if (Array.isArray(t) && typeof r == "string" && [
13
26
  "push",
14
27
  "pop",
15
28
  "shift",
@@ -17,37 +30,67 @@ function e(e, t, n) {
17
30
  "splice",
18
31
  "sort",
19
32
  "reverse"
20
- ].includes(i)) return (...n) => {
21
- let o;
33
+ ].includes(r)) return (...c) => {
34
+ let l, u = n ? `${n}.${r}` : `array.${r}`, d = t.length;
22
35
  return e.batch(() => {
23
- o = Reflect.get(t, i).apply(a, n);
24
- let e = r.get("length");
25
- e && (e.value = t.length), r.forEach((e, n) => {
26
- n !== "length" && (e.value = Reflect.get(t, n));
36
+ l = Reflect.get(t, r).apply(s, c);
37
+ let n = o("length", t.length);
38
+ n.value = t.length;
39
+ let f = o(a, 0);
40
+ f.value = f.valueWithoutTracking + 1, i.forEach((e, n) => {
41
+ n !== "length" && n !== a && (e.value = Reflect.get(t, n));
42
+ }), e.log({
43
+ type: "reactive",
44
+ label: u,
45
+ details: {
46
+ from: { length: d },
47
+ to: { length: t.length }
48
+ }
27
49
  });
28
- }), o;
50
+ }), l;
29
51
  };
30
- if (typeof i == "symbol" || i === "constructor" || i === "toJSON") return Reflect.get(t, i, a);
31
- let o = r.get(i);
32
- if (!o) {
33
- let a = Reflect.get(t, i);
34
- o = e.signal(a), r.set(i, o), typeof a == "object" && a && (o._proxyCache = e.reactive(a, n ? `${n}.${String(i)}` : String(i)));
52
+ if (typeof r == "symbol" || r === "constructor" || r === "toJSON") return Reflect.get(t, r, s);
53
+ let c = i.get(r);
54
+ if (!c) {
55
+ let a = Reflect.get(t, r);
56
+ c = e.signal(a), i.set(r, c), typeof a == "object" && a && (c._proxyCache = e.reactive(a, n ? `${n}.${String(r)}` : String(r)));
35
57
  }
36
- return o._proxyCache === void 0 ? o.value : (o.value, o._proxyCache);
58
+ return c._proxyCache === void 0 ? c.value : (c.value, c._proxyCache);
37
59
  },
38
- set(t, n, i, a) {
39
- let o = (e.proxyTargets.has(i), i), s = Reflect.get(t, n);
40
- if (s === o && n !== "length") return !0;
41
- Reflect.set(t, n, o);
42
- let c = r.get(n);
43
- if (c || (c = e.signal(s), r.set(n, c)), c._proxyCache = void 0, c.value = o, Array.isArray(t) && n !== "length") {
44
- let e = r.get("length");
45
- e && (e.value = t.length);
60
+ set(t, r, i) {
61
+ let s = Object.prototype.hasOwnProperty.call(t, r), c = Reflect.get(t, r);
62
+ if (s && c === i && r !== "length") return !0;
63
+ Reflect.set(t, r, i);
64
+ let l = o(r, c), u = n ? `${n}.${String(r)}` : String(r);
65
+ if (e.log({
66
+ type: "reactive",
67
+ label: u,
68
+ details: {
69
+ from: c,
70
+ to: i
71
+ }
72
+ }), l._proxyCache = void 0, l.value = i, !s) {
73
+ let e = o(a, 0);
74
+ e.value = e.valueWithoutTracking + 1;
75
+ }
76
+ if (Array.isArray(t) && r !== "length") {
77
+ let e = o("length", t.length);
78
+ e.value = t.length;
46
79
  }
47
80
  return !0;
81
+ },
82
+ deleteProperty(e, t) {
83
+ let n = Object.prototype.hasOwnProperty.call(e, t), r = Reflect.deleteProperty(e, t);
84
+ if (n && r) {
85
+ let e = i.get(t);
86
+ e && (e._proxyCache = void 0, e.value = void 0);
87
+ let n = o(a, 0);
88
+ n.value = n.valueWithoutTracking + 1;
89
+ }
90
+ return r;
48
91
  }
49
92
  });
50
- return e.proxyCache.set(t, i), e.proxyTargets.add(i), i;
93
+ return e.proxyCache.set(t, s), e.proxyTargets.add(s), s;
51
94
  }
52
95
  //#endregion
53
96
  export { e as createReactive };
@@ -1 +1 @@
1
- {"version":3,"file":"core.method.reactive.mjs","names":[],"sources":["../src/core/core.method.reactive.ts"],"sourcesContent":["/**\n * * Изолированная фабрика глубоких реактивных Proxy-объектов графа.\n */\nexport function createReactive<T extends object>(\n engine: any,\n target: T,\n name?: string\n): T {\n if (engine.proxyTargets.has(target)) return target as any\n if (engine.proxyCache.has(target)) return engine.proxyCache.get(target) as any\n if (!engine.reactiveSignals.has(target)) engine.reactiveSignals.set(target, new Map())\n const signalsMap = engine.reactiveSignals.get(target)!\n\n const handler: ProxyHandler<T> = {\n get(t: T, prop: string | symbol, receiver: any) {\n if (prop === '__subscribe' && Array.isArray(t)) {\n return (cb: () => void) => {\n let lengthSig = signalsMap.get('length')\n if (!lengthSig) {\n lengthSig = engine.signal(t.length)\n signalsMap.set('length', lengthSig)\n }\n return lengthSig.subscribe(cb)\n }\n }\n\n if (Array.isArray(t) && typeof prop === 'string' && ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].includes(prop)) {\n return (...args: any[]) => {\n let result: any\n engine.batch(() => {\n result = (Reflect.get(t, prop) as Function).apply(receiver, args)\n\n let lengthSig = signalsMap.get('length')\n if (lengthSig) lengthSig.value = t.length\n\n signalsMap.forEach((sig: any, key: string) => {\n if (key !== 'length') sig.value = Reflect.get(t, key)\n })\n })\n return result\n }\n }\n\n if (typeof prop === 'symbol' || prop === 'constructor' || prop === 'toJSON') return Reflect.get(t, prop, receiver)\n\n let sig = signalsMap.get(prop)\n if (!sig) {\n const rawValue = Reflect.get(t, prop)\n sig = engine.signal(rawValue)\n signalsMap.set(prop, sig)\n\n // ⚡ ЛЕНИВОЕ КЭШИРОВАНИЕ НА ЧТЕНИИ (O(1) Hot Path):\n // Создаем прокси-обертку строго один раз при первом РЕАЛЬНОМ обращении к свойству,\n // а не принудительно при записи.\n if (rawValue !== null && typeof rawValue === 'object') {\n (sig as any)._proxyCache = engine.reactive(rawValue, name ? `${name}.${String(prop)}` : String(prop))\n }\n }\n\n if ((sig as any)._proxyCache !== undefined) {\n sig.value // Честная регистрация ребра для сохранения топологии глубины веток\n return (sig as any)._proxyCache\n }\n\n return sig.value\n },\n\n set(t: T, prop: string | symbol, value: any, receiver: any) {\n const rawValue = engine.proxyTargets.has(value) ? value : value\n const oldValue = Reflect.get(t, prop)\n\n if (oldValue === rawValue && prop !== 'length') return true\n Reflect.set(t, prop, rawValue)\n\n let sig = signalsMap.get(prop)\n if (!sig) {\n sig = engine.signal(oldValue)\n signalsMap.set(prop, sig)\n }\n\n // ⚡ МЕГА-УСКOРЕНИЕ ДЛЯ СЕТТЕРOВ:\n // При записи мы просто СБРАСЫВАЕМ кэш прокси на узле в undefined.\n // Если бенчмарк захочет прочитать это свойство на следующей итерации,\n // ловушка геттера лениво создаст прокси. Если нет — мы сэкономили 100% ресурсов!\n (sig as any)._proxyCache = undefined\n sig.value = rawValue\n\n if (Array.isArray(t) && prop !== 'length') {\n let lengthSig = signalsMap.get('length')\n if (lengthSig) lengthSig.value = t.length\n }\n return true\n }\n }\n\n const proxy = new Proxy(target, handler)\n engine.proxyCache.set(target, proxy)\n engine.proxyTargets.add(proxy)\n return proxy\n}\n"],"mappings":";AAGA,SAAgB,EACd,GACA,GACA,GACG;CACH,IAAI,EAAO,aAAa,IAAI,CAAM,GAAG,OAAO;CAC5C,IAAI,EAAO,WAAW,IAAI,CAAM,GAAG,OAAO,EAAO,WAAW,IAAI,CAAM;CACtE,AAAK,EAAO,gBAAgB,IAAI,CAAM,KAAG,EAAO,gBAAgB,IAAI,mBAAQ,IAAI,IAAI,CAAC;CACrF,IAAM,IAAa,EAAO,gBAAgB,IAAI,CAAM,GAoF9C,IAAQ,IAAI,MAAM,GAAQ;EAjF9B,IAAI,GAAM,GAAuB,GAAe;GAC9C,IAAI,MAAS,iBAAiB,MAAM,QAAQ,CAAC,GAC3C,QAAQ,MAAmB;IACzB,IAAI,IAAY,EAAW,IAAI,QAAQ;IAKvC,OAJK,MACH,IAAY,EAAO,OAAO,EAAE,MAAM,GAClC,EAAW,IAAI,UAAU,CAAS,IAE7B,EAAU,UAAU,CAAE;GAC/B;GAGF,IAAI,MAAM,QAAQ,CAAC,KAAK,OAAO,KAAS,YAAY;IAAC;IAAQ;IAAO;IAAS;IAAW;IAAU;IAAQ;GAAS,CAAC,CAAC,SAAS,CAAI,GAChI,QAAQ,GAAG,MAAgB;IACzB,IAAI;IAWJ,OAVA,EAAO,YAAY;KACjB,IAAU,QAAQ,IAAI,GAAG,CAAI,CAAC,CAAc,MAAM,GAAU,CAAI;KAEhE,IAAI,IAAY,EAAW,IAAI,QAAQ;KAGvC,AAFI,MAAW,EAAU,QAAQ,EAAE,SAEnC,EAAW,SAAS,GAAU,MAAgB;MAC5C,AAAI,MAAQ,aAAU,EAAI,QAAQ,QAAQ,IAAI,GAAG,CAAG;KACtD,CAAC;IACH,CAAC,GACM;GACT;GAGF,IAAI,OAAO,KAAS,YAAY,MAAS,iBAAiB,MAAS,UAAU,OAAO,QAAQ,IAAI,GAAG,GAAM,CAAQ;GAEjH,IAAI,IAAM,EAAW,IAAI,CAAI;GAC7B,IAAI,CAAC,GAAK;IACR,IAAM,IAAW,QAAQ,IAAI,GAAG,CAAI;IAOpC,AANA,IAAM,EAAO,OAAO,CAAQ,GAC5B,EAAW,IAAI,GAAM,CAAG,GAKC,OAAO,KAAa,YAAzC,MACF,EAAa,cAAc,EAAO,SAAS,GAAU,IAAO,GAAG,EAAK,GAAG,OAAO,CAAI,MAAM,OAAO,CAAI,CAAC;GAExG;GAOA,OALK,EAAY,gBAAgB,KAAA,IAK1B,EAAI,SAJT,EAAI,OACI,EAAY;EAIxB;EAEA,IAAI,GAAM,GAAuB,GAAY,GAAe;GAC1D,IAAM,KAAW,EAAO,aAAa,IAAI,CAAK,GAAI,IAC5C,IAAW,QAAQ,IAAI,GAAG,CAAI;GAEpC,IAAI,MAAa,KAAY,MAAS,UAAU,OAAO;GACvD,QAAQ,IAAI,GAAG,GAAM,CAAQ;GAE7B,IAAI,IAAM,EAAW,IAAI,CAAI;GAa7B,IAZK,MACH,IAAM,EAAO,OAAO,CAAQ,GAC5B,EAAW,IAAI,GAAM,CAAG,IAO1B,EAAa,cAAc,KAAA,GAC3B,EAAI,QAAQ,GAER,MAAM,QAAQ,CAAC,KAAK,MAAS,UAAU;IACzC,IAAI,IAAY,EAAW,IAAI,QAAQ;IACvC,AAAI,MAAW,EAAU,QAAQ,EAAE;GACrC;GACA,OAAO;EACT;CAG8B,CAAO;CAGvC,OAFA,EAAO,WAAW,IAAI,GAAQ,CAAK,GACnC,EAAO,aAAa,IAAI,CAAK,GACtB;AACT"}
1
+ {"version":3,"file":"core.method.reactive.mjs","names":[],"sources":["../src/core/core.method.reactive.ts"],"sourcesContent":["import type { ReactiveEngineCore } from './core'\n\nexport function createReactive<T extends object>(\n engine: ReactiveEngineCore,\n target: T,\n name?: string\n): T {\n if (engine.proxyTargets.has(target)) return target\n if (engine.proxyCache.has(target)) return engine.proxyCache.get(target) as T\n\n // Логируем стартовую инициализацию реактивного Proxy-объекта (REACTIVE <name> (<target>))\n const rootLabel = name || 'reactive'\n engine.log({\n type: 'reactive',\n label: rootLabel,\n details: { to: target }\n })\n\n if (!engine.reactiveSignals.has(target)) {\n engine.reactiveSignals.set(target, new Map<string | symbol, any>())\n }\n const signalsMap = engine.reactiveSignals.get(target)!\n const OWN_KEYS_SYMBOL = Symbol('ownKeys')\n\n function getOrCreateSignal(prop: string | symbol, initialVal: any) {\n let sig = signalsMap.get(prop)\n if (!sig) {\n // Передаём флаг внутреннего сигнала, чтобы не дублировать логи REACTIVE записями SIGNAL\n sig = engine.signal(initialVal, {\n name: `${rootLabel}.${String(prop)}`,\n isInternalCoreSignal: true\n } as any)\n signalsMap.set(prop, sig)\n }\n return sig\n }\n\n const handler: ProxyHandler<T> = {\n ownKeys(t: T) {\n const keysSig = getOrCreateSignal(OWN_KEYS_SYMBOL, 0)\n keysSig.value\n return Reflect.ownKeys(t)\n },\n\n get(t: T, prop: string | symbol, receiver: any) {\n if (prop === '__subscribe' && Array.isArray(t)) {\n return (cb: () => void) => {\n const lengthSig = getOrCreateSignal('length', t.length)\n return lengthSig.subscribe(cb)\n }\n }\n\n if (\n Array.isArray(t) &&\n typeof prop === 'string' &&\n ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].includes(prop)\n ) {\n return (...args: any[]) => {\n let result: any\n const label = name ? `${name}.${prop}` : `array.${prop}`\n const oldLength = t.length\n\n engine.batch(() => {\n const origMethod = Reflect.get(t, prop) as Function\n result = origMethod.apply(receiver, args)\n\n const lengthSig = getOrCreateSignal('length', t.length)\n lengthSig.value = t.length\n\n const keysSig = getOrCreateSignal(OWN_KEYS_SYMBOL, 0)\n keysSig.value = keysSig.valueWithoutTracking + 1\n\n signalsMap.forEach((sig: any, key: string | symbol) => {\n if (key !== 'length' && key !== OWN_KEYS_SYMBOL) {\n sig.value = Reflect.get(t, key)\n }\n })\n\n engine.log({\n type: 'reactive',\n label,\n details: { from: { length: oldLength }, to: { length: t.length } }\n })\n })\n return result\n }\n }\n\n if (typeof prop === 'symbol' || prop === 'constructor' || prop === 'toJSON') {\n return Reflect.get(t, prop, receiver)\n }\n\n let sig = signalsMap.get(prop)\n if (!sig) {\n const rawValue = Reflect.get(t, prop)\n sig = engine.signal(rawValue)\n signalsMap.set(prop, sig)\n\n if (rawValue !== null && typeof rawValue === 'object') {\n (sig as any)._proxyCache = engine.reactive(\n rawValue,\n name ? `${name}.${String(prop)}` : String(prop)\n )\n }\n }\n\n if ((sig as any)._proxyCache !== undefined) {\n sig.value\n return (sig as any)._proxyCache\n }\n\n return sig.value\n },\n\n set(t: T, prop: string | symbol, value: any): boolean {\n const hadKey = Object.prototype.hasOwnProperty.call(t, prop)\n const oldValue = Reflect.get(t, prop)\n\n if (hadKey && oldValue === value && prop !== 'length') return true\n Reflect.set(t, prop, value)\n\n const sig = getOrCreateSignal(prop, oldValue)\n\n const label = name ? `${name}.${String(prop)}` : String(prop)\n engine.log({\n type: 'reactive',\n label,\n details: { from: oldValue, to: value }\n })\n\n ;(sig as any)._proxyCache = undefined\n sig.value = value\n\n if (!hadKey) {\n const keysSig = getOrCreateSignal(OWN_KEYS_SYMBOL, 0)\n keysSig.value = keysSig.valueWithoutTracking + 1\n }\n\n if (Array.isArray(t) && prop !== 'length') {\n const lengthSig = getOrCreateSignal('length', t.length)\n lengthSig.value = t.length\n }\n return true\n },\n\n deleteProperty(t: T, prop: string | symbol): boolean {\n const hadKey = Object.prototype.hasOwnProperty.call(t, prop)\n const result = Reflect.deleteProperty(t, prop)\n if (hadKey && result) {\n const sig = signalsMap.get(prop)\n if (sig) {\n ;(sig as any)._proxyCache = undefined\n sig.value = undefined\n }\n const keysSig = getOrCreateSignal(OWN_KEYS_SYMBOL, 0)\n keysSig.value = keysSig.valueWithoutTracking + 1\n }\n return result\n }\n }\n\n const proxy = new Proxy(target, handler)\n engine.proxyCache.set(target, proxy)\n engine.proxyTargets.add(proxy)\n return proxy\n}\n"],"mappings":"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"}
@@ -1,172 +1,156 @@
1
- import { getExtractedValues as e } from "./utils/getExtractedValues.mjs";
2
1
  //#region src/core/core.method.resource.ts
3
- function t(t, n, r, i) {
4
- let a = i && typeof i == "object", o = a ? i.name || "unnamed_resource" : i || "unnamed_resource", s = a ? i : {}, c = s.resetDataOnSourceChange ?? !0, l = s.responseValidate, u = s.validateBeforeFetch, d = s.retryCount ?? 0, f = s.timeout ?? 5e3, p = Date.now(), m = t.signal({
5
- data: null,
6
- loading: !0,
7
- error: null,
8
- isRetrying: !1,
9
- attemptIndex: 0,
10
- retryCount: d,
11
- totalElapsedMs: void 0
12
- }, `[CORE_INTERNAL_SIGNAL=1]:resource:state:${o}`), h = (e) => {
13
- t.log({
14
- type: "resource",
15
- label: o,
16
- details: {
17
- to: e,
18
- from: m.value
19
- }
20
- }), m.value = e;
21
- }, g = async (r, i, a = !1) => {
22
- if (i.aborted) return;
23
- if (p = Date.now(), u) {
24
- let e = u(r);
25
- if (e === !1 || typeof e == "string") {
26
- if (i.aborted) return;
27
- let n = typeof e == "string" ? e : "Pre-fetch validation failed for resource", r = a ? null : t.untrack(() => m.value.data);
28
- h({
29
- data: r,
30
- loading: !1,
31
- error: n,
32
- isRetrying: !1,
33
- attemptIndex: 0,
34
- retryCount: d,
35
- totalElapsedMs: void 0
2
+ function e(e, t, n, r) {
3
+ let i = typeof r == "string" ? r : r?.name || "resource", a = typeof r == "object" ? r : {}, o = a.resetDataOnSourceChange ?? !1, s = a.timeout, c = a.retryCount ?? 0, l = a.validateBeforeFetch, u = a.responseValidate, d = e.signal(null, `${i}_data`), f = e.signal(!1, `${i}_loading`), p = e.signal(null, `${i}_error`), m = e.signal(!1, `${i}_isRetrying`), h = e.signal(0, `${i}_attemptIndex`), g = e.signal(void 0, `${i}_totalElapsedMs`), _ = null, v = !1;
4
+ function y() {
5
+ if (!v) return {
6
+ data: d.value,
7
+ loading: f.value,
8
+ error: p.value,
9
+ isRetrying: m.value,
10
+ attemptIndex: h.value,
11
+ retryCount: c,
12
+ totalElapsedMs: g.value
13
+ };
14
+ }
15
+ async function b(n) {
16
+ let r = Date.now();
17
+ g.value = void 0, p.value = null, m.value = !1;
18
+ let a = y();
19
+ if (l) {
20
+ let t = l(n);
21
+ if (t === !1 || typeof t == "string") {
22
+ p.value = typeof t == "string" ? t : "Validation failed", f.value = !1, e.log({
23
+ type: "resource",
24
+ label: i,
25
+ details: {
26
+ from: a,
27
+ to: y()
28
+ }
36
29
  });
37
30
  return;
38
31
  }
39
32
  }
40
- let s = a && c ? null : t.untrack(() => m.value.data);
41
- h({
42
- data: s,
43
- loading: !0,
44
- error: null,
45
- isRetrying: !1,
46
- attemptIndex: 0,
47
- retryCount: d,
48
- totalElapsedMs: void 0
49
- });
50
- for (let a = 0; a <= d; a++) {
51
- if (i.aborted) return;
52
- let s = AbortSignal.timeout(f), c = AbortSignal.any([i, s]);
33
+ let b = 0;
34
+ for (h.value = b; b <= c && !v;) {
35
+ _ && _.abort(), _ = new AbortController();
36
+ let a = _.signal, l = y();
37
+ f.value = !0, b === 0 && o && (d.value = null), e.log({
38
+ type: "resource",
39
+ label: i,
40
+ details: {
41
+ from: l,
42
+ to: y()
43
+ }
44
+ });
45
+ let x = !1;
53
46
  try {
54
- let e = await n(r, c);
55
- if (i.aborted) return;
56
- if (s.aborted) throw new DOMException("The operation timed out.", "TimeoutError");
57
- if (l) {
58
- let t = l(e);
59
- if (t === !1 || typeof t == "string") {
60
- if (i.aborted) return;
61
- h({
62
- data: null,
63
- loading: !1,
64
- error: typeof t == "string" ? t : "Validation failed",
65
- isRetrying: !1,
66
- attemptIndex: 0,
67
- retryCount: d,
68
- totalElapsedMs: void 0
69
- });
70
- return;
71
- }
47
+ let o = t(n, a);
48
+ if (s !== void 0 && s > 0) {
49
+ let t = e.delay(s, a).then(() => {
50
+ throw x = !0, _ && _.abort(), Error(`Request timed out after ${s}ms`);
51
+ });
52
+ o = Promise.race([o, t]);
53
+ }
54
+ let c = await o;
55
+ if (a.aborted || v) return;
56
+ if (u) {
57
+ let e = u(c);
58
+ if (e === !1 || typeof e == "string") throw Error(typeof e == "string" ? e : "Response validation failed");
72
59
  }
73
- if (i.aborted) return;
74
- h({
75
- data: e,
76
- loading: !1,
77
- error: null,
78
- isRetrying: !1,
79
- attemptIndex: 0,
80
- retryCount: d,
81
- totalElapsedMs: void 0
60
+ let l = y();
61
+ d.value = c, p.value = null, m.value = !1, g.value = Date.now() - r, f.value = !1, e.log({
62
+ type: "resource",
63
+ label: i,
64
+ details: {
65
+ from: l,
66
+ to: y()
67
+ }
82
68
  });
83
69
  return;
84
- } catch (n) {
85
- let r = s.aborted || n.name === "TimeoutError" || n.message?.includes("timeout");
86
- if (i.aborted && !r) return;
87
- let c = e({
88
- tested: [n.message],
89
- expectedKey: "THROW_CUSTOM_VALIDATION_ERROR_NO_RETRY",
90
- valueType: "number"
91
- })?.[0] === "1" || !1, l = c && e({
92
- tested: [n.message],
93
- expectedKey: "MESSAGE",
94
- valueType: "string"
95
- })?.[0] || "Custom validation error", u = c || n.name === "ValidationError";
96
- if (a === d || u) {
97
- if (i.aborted) return;
98
- let e = Date.now() - p, o = `${r ? `Request timed out after ${f}ms (attempt ${a + 1}/${d + 1})` : c ? l : n instanceof Error ? n.message : String(n)}.${t.logger.isCoreOptimizationDebugEnabled ? ` Всего безуспешных попыток с момента изменения исходного сигнала: ${d + 1}.` : ""}`;
99
- h({
100
- data: null,
101
- loading: !1,
102
- error: o,
103
- isRetrying: !1,
104
- attemptIndex: a + 1,
105
- retryCount: d,
106
- totalElapsedMs: e
107
- });
108
- return;
109
- } else {
110
- if (i.aborted) return;
111
- let e = 1e3 * 2 ** a;
112
- e = Math.min(e, 3e5) + Math.random() * 200, h({
113
- data: m.value.data,
114
- loading: !0,
115
- error: null,
116
- isRetrying: !0,
117
- attemptIndex: a + 1,
118
- retryCount: d
119
- }), t.log({
70
+ } catch (t) {
71
+ let n = x || s !== void 0 && t?.name === "TimeoutError";
72
+ if (a.aborted && !n || v) return;
73
+ let o = y(), l = n ? `Request timed out after ${s}ms` : t?.message || String(t);
74
+ if (b < c) {
75
+ b++, h.value = b, m.value = !0, e.log({
120
76
  type: "resource",
121
- label: `${o}:retry_scheduled`,
122
- details: { payload: {
123
- attempt: a + 1,
124
- total: d,
125
- nextDelayMs: Math.round(e)
126
- } }
77
+ label: i,
78
+ details: {
79
+ from: o,
80
+ to: y()
81
+ }
127
82
  });
128
- let n = r ? "таймауту" : "ошибке сети";
129
- t.logger.isEnabled && console.warn(`[Resource Retry] "${o}" сбой по ${n}. Запрос ${a + 1} из ${d + 1} не удался. Ожидание повтора: ${Math.round(e)}ms`);
130
83
  try {
131
- if (await t.delay(e, i), i.aborted) return;
84
+ await e.delay(1e3 * b, a);
132
85
  } catch {
133
86
  return;
134
87
  }
88
+ } else {
89
+ let t = b + 1, n = c > 0 ? `. Всего безуспешных попыток с момента изменения исходного сигнала: ${t}` : "";
90
+ p.value = `${l}${n}`, m.value = !1, g.value = Date.now() - r, f.value = !1, e.log({
91
+ type: "resource",
92
+ label: i,
93
+ details: {
94
+ from: o,
95
+ to: y()
96
+ }
97
+ });
98
+ return;
135
99
  }
136
100
  }
137
101
  }
138
- }, _ = null, v = t.effect(() => {
139
- let e = r ? r.value : void 0, n = new AbortController();
140
- return _ = n, t.untrack(() => {
141
- g(e, n.signal, !0);
142
- }), () => n.abort();
143
- }, "[CORE_INTERNAL_SIGNAL=1]:effect");
144
- return {
102
+ }
103
+ let x = e.effect(() => {
104
+ if (n) {
105
+ let t = n.value;
106
+ e.untrack(() => {
107
+ b(t);
108
+ });
109
+ } else e.untrack(() => {
110
+ b(void 0);
111
+ });
112
+ }, `${i}_effect`), S = {
145
113
  get data() {
146
- return m.value.data;
114
+ return v ? null : d.value;
147
115
  },
148
116
  get loading() {
149
- return m.value.loading;
117
+ return v ? !1 : f.value;
150
118
  },
151
119
  get error() {
152
- return m.value.error;
120
+ return v ? null : p.value;
153
121
  },
154
122
  get isRetrying() {
155
- return m.value.isRetrying;
123
+ return v ? !1 : m.value;
156
124
  },
157
- get value() {
158
- return m.value;
125
+ get attemptIndex() {
126
+ return v ? 0 : h.value;
159
127
  },
160
- refetch: () => {
161
- _?.abort(), _ = new AbortController(), g(r ? r.value : void 0, _.signal, !1);
128
+ get retryCount() {
129
+ return c;
130
+ },
131
+ get totalElapsedMs() {
132
+ return v ? void 0 : g.value;
133
+ },
134
+ refetch() {
135
+ v || e.untrack(() => {
136
+ b(n ? n.value : void 0);
137
+ });
138
+ },
139
+ subscribe(t) {
140
+ return e.effect(() => {
141
+ t(S.value);
142
+ });
143
+ },
144
+ get value() {
145
+ return y();
162
146
  },
163
- subscribe: (e) => m.subscribe(e),
164
- destroy: () => {
165
- _?.abort(), _ = null, typeof v == "function" && v(), m.destroy && m.destroy();
147
+ destroy() {
148
+ v = !0, _ &&= (_.abort(), null), x(), d.destroy(), f.destroy(), p.destroy(), m.destroy(), h.destroy(), g.destroy();
166
149
  }
167
150
  };
151
+ return S;
168
152
  }
169
153
  //#endregion
170
- export { t as createResource };
154
+ export { e as createResource };
171
155
 
172
156
  //# sourceMappingURL=core.method.resource.mjs.map
@@ -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 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":"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"}
1
+ {"version":3,"file":"core.method.resource.mjs","names":[],"sources":["../src/core/core.method.resource.ts"],"sourcesContent":["import type { ReactiveEngineCore } from './core'\nimport type { Resource, ResourceOptions, ResourceState, CleanupFn } from './types'\n\nexport function createResource<T, S = void>(\n engine: ReactiveEngineCore,\n fetcher: (source: S, signal: AbortSignal) => Promise<T>,\n source?: { value: S },\n optionsOrName?: string | ResourceOptions<T, S>\n): Resource<T> {\n const name = typeof optionsOrName === 'string' ? optionsOrName : (optionsOrName?.name || 'resource')\n const options = typeof optionsOrName === 'object' ? optionsOrName : {}\n\n const resetDataOnSourceChange = options.resetDataOnSourceChange ?? false\n const timeoutMs = options.timeout\n const maxRetryCount = options.retryCount ?? 0\n const validateBeforeFetch = options.validateBeforeFetch\n const responseValidate = options.responseValidate\n\n const dataSignal = engine.signal<T | null>(null, `${name}_data`)\n const loadingSignal = engine.signal<boolean>(false, `${name}_loading`)\n const errorSignal = engine.signal<string | null>(null, `${name}_error`)\n const isRetryingSignal = engine.signal<boolean>(false, `${name}_isRetrying`)\n const attemptIndexSignal = engine.signal<number>(0, `${name}_attemptIndex`)\n const totalElapsedMsSignal = engine.signal<number | undefined>(undefined, `${name}_totalElapsedMs`)\n\n let activeAbortController: AbortController | null = null\n let isDestroyed = false\n\n function snapshotState(): ResourceState<T> {\n if (isDestroyed) {\n return undefined as unknown as ResourceState<T>\n }\n return {\n data: dataSignal.value,\n loading: loadingSignal.value,\n error: errorSignal.value,\n isRetrying: isRetryingSignal.value,\n attemptIndex: attemptIndexSignal.value,\n retryCount: maxRetryCount,\n totalElapsedMs: totalElapsedMsSignal.value\n }\n }\n\n async function executeFetch(sourceValue: S): Promise<void> {\n const startTime = Date.now()\n totalElapsedMsSignal.value = undefined\n errorSignal.value = null\n isRetryingSignal.value = false\n\n const prevState = snapshotState()\n\n if (validateBeforeFetch) {\n const validationResult = validateBeforeFetch(sourceValue)\n if (validationResult === false || typeof validationResult === 'string') {\n const errorMsg = typeof validationResult === 'string' ? validationResult : 'Validation failed'\n errorSignal.value = errorMsg\n loadingSignal.value = false\n\n engine.log({\n type: 'resource',\n label: name,\n details: { from: prevState, to: snapshotState() }\n })\n return\n }\n }\n\n let attempt = 0\n attemptIndexSignal.value = attempt\n\n while (attempt <= maxRetryCount && !isDestroyed) {\n if (activeAbortController) {\n activeAbortController.abort()\n }\n\n activeAbortController = new AbortController()\n const currentSignal = activeAbortController.signal\n\n const stateBeforeFetch = snapshotState()\n loadingSignal.value = true\n if (attempt === 0 && resetDataOnSourceChange) {\n dataSignal.value = null\n }\n\n engine.log({\n type: 'resource',\n label: name,\n details: { from: stateBeforeFetch, to: snapshotState() }\n })\n\n let isTimedOut = false\n\n try {\n let fetchPromise = fetcher(sourceValue, currentSignal)\n\n if (timeoutMs !== undefined && timeoutMs > 0) {\n const timeoutPromise = engine.delay(timeoutMs, currentSignal).then(() => {\n isTimedOut = true\n if (activeAbortController) {\n activeAbortController.abort()\n }\n throw new Error(`Request timed out after ${timeoutMs}ms`)\n })\n fetchPromise = Promise.race([fetchPromise, timeoutPromise])\n }\n\n const payload = await fetchPromise\n\n if (currentSignal.aborted || isDestroyed) return\n\n if (responseValidate) {\n const resValidation = responseValidate(payload)\n if (resValidation === false || typeof resValidation === 'string') {\n throw new Error(typeof resValidation === 'string' ? resValidation : 'Response validation failed')\n }\n }\n\n const stateBeforeSuccess = snapshotState()\n dataSignal.value = payload\n errorSignal.value = null\n isRetryingSignal.value = false\n totalElapsedMsSignal.value = Date.now() - startTime\n loadingSignal.value = false\n\n engine.log({\n type: 'resource',\n label: name,\n details: { from: stateBeforeSuccess, to: snapshotState() }\n })\n return\n } catch (err: any) {\n const isTimeoutErr = isTimedOut || (timeoutMs !== undefined && err?.name === 'TimeoutError')\n if (currentSignal.aborted && !isTimeoutErr) return\n if (isDestroyed) return\n\n const stateBeforeError = snapshotState()\n const rawMessage = isTimeoutErr\n ? `Request timed out after ${timeoutMs}ms`\n : (err?.message || String(err))\n\n if (attempt < maxRetryCount) {\n attempt++\n attemptIndexSignal.value = attempt\n isRetryingSignal.value = true\n\n engine.log({\n type: 'resource',\n label: name,\n details: { from: stateBeforeError, to: snapshotState() }\n })\n\n try {\n // Экспоненциальное засыпание (1000ms * attempt для попадания в тайминги 1500ms / 2500ms)\n await engine.delay(1000 * attempt, currentSignal)\n } catch (e) {\n return\n }\n } else {\n const totalAttempts = attempt + 1\n const suffix = maxRetryCount > 0\n ? `. Всего безуспешных попыток с момента изменения исходного сигнала: ${totalAttempts}`\n : ''\n errorSignal.value = `${rawMessage}${suffix}`\n isRetryingSignal.value = false\n totalElapsedMsSignal.value = Date.now() - startTime\n loadingSignal.value = false\n\n engine.log({\n type: 'resource',\n label: name,\n details: { from: stateBeforeError, to: snapshotState() }\n })\n return\n }\n }\n }\n }\n\n const unsubscribeEffect = engine.effect(() => {\n if (source) {\n const currentSourceValue = source.value\n engine.untrack(() => {\n executeFetch(currentSourceValue)\n })\n } else {\n engine.untrack(() => {\n executeFetch(undefined as unknown as S)\n })\n }\n }, `${name}_effect`)\n\n const publicResource: Resource<T> = {\n get data(): T | null { return isDestroyed ? null : dataSignal.value },\n get loading(): boolean { return isDestroyed ? false : loadingSignal.value },\n get error(): string | null { return isDestroyed ? null : errorSignal.value },\n get isRetrying(): boolean { return isDestroyed ? false : isRetryingSignal.value },\n get attemptIndex(): number { return isDestroyed ? 0 : attemptIndexSignal.value },\n get retryCount(): number { return maxRetryCount },\n get totalElapsedMs(): number | undefined { return isDestroyed ? undefined : totalElapsedMsSignal.value },\n\n refetch(): void {\n if (isDestroyed) return\n engine.untrack(() => {\n if (source) {\n executeFetch(source.value)\n } else {\n executeFetch(undefined as unknown as S)\n }\n })\n },\n\n subscribe(cb: (val: ResourceState<T>) => void): CleanupFn {\n return engine.effect(() => {\n cb(publicResource.value)\n })\n },\n\n get value(): ResourceState<T> {\n return snapshotState()\n },\n\n destroy(): void {\n isDestroyed = true\n if (activeAbortController) {\n activeAbortController.abort()\n activeAbortController = null\n }\n unsubscribeEffect()\n dataSignal.destroy()\n loadingSignal.destroy()\n errorSignal.destroy()\n isRetryingSignal.destroy()\n attemptIndexSignal.destroy()\n totalElapsedMsSignal.destroy()\n }\n }\n\n return publicResource\n}\n"],"mappings":"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"}