@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.
- package/dist/core/core.d.ts +92 -203
- package/dist/core/core.d.ts.map +1 -1
- package/dist/core/core.global.d.ts +64 -8
- package/dist/core/core.global.d.ts.map +1 -1
- package/dist/core/core.logger.d.ts +2 -0
- package/dist/core/core.logger.d.ts.map +1 -1
- package/dist/core/core.method.reactive.d.ts +2 -4
- package/dist/core/core.method.reactive.d.ts.map +1 -1
- package/dist/core/core.method.resource.d.ts +3 -11
- package/dist/core/core.method.resource.d.ts.map +1 -1
- package/dist/core/types.d.ts +1 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core.global.mjs +31 -4
- package/dist/core.global.mjs.map +1 -1
- package/dist/core.logger.mjs +49 -2
- package/dist/core.logger.mjs.map +1 -1
- package/dist/core.method.reactive.mjs +73 -30
- package/dist/core.method.reactive.mjs.map +1 -1
- package/dist/core.method.resource.mjs +122 -138
- package/dist/core.method.resource.mjs.map +1 -1
- package/dist/core.mjs +406 -150
- package/dist/core.mjs.map +1 -1
- package/dist/index.mjs +12 -12
- package/dist/index2.mjs +5 -5
- package/dist/react/hooks/useReactiveSubscription.mjs +13 -13
- package/dist/react/hooks/useReactiveSubscription.mjs.map +1 -1
- package/dist/react/hooks/useReactiveValue.d.ts +0 -4
- package/dist/react/hooks/useReactiveValue.d.ts.map +1 -1
- package/dist/react/hooks/useReactiveValue.mjs +24 -9
- package/dist/react/hooks/useReactiveValue.mjs.map +1 -1
- package/dist/reactive-engine.umd.js +2 -2
- package/dist/reactive-engine.umd.js.map +1 -1
- package/package.json +1 -1
- package/dist/core.method.computed.mjs +0 -167
- package/dist/core.method.computed.mjs.map +0 -1
- package/dist/core.method.effect.mjs +0 -120
- package/dist/core.method.effect.mjs.map +0 -1
- package/dist/core.method.signal.mjs +0 -106
- package/dist/core.method.signal.mjs.map +0 -1
- package/dist/core.method.untrack.mjs +0 -14
- package/dist/core.method.untrack.mjs.map +0 -1
- package/dist/utils/getExtractedValues.mjs +0 -24
- package/dist/utils/getExtractedValues.mjs.map +0 -1
package/dist/core.logger.mjs
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
//#region src/core/core.logger.ts
|
|
2
|
-
var e =
|
|
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 {
|
|
96
|
+
export { a as ReactiveDebounceLogger, i as flushLogs, r as queueLog };
|
|
50
97
|
|
|
51
98
|
//# sourceMappingURL=core.logger.mjs.map
|
package/dist/core.logger.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.logger.mjs","names":[],"sources":["../src/core/core.logger.ts"],"sourcesContent":["
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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(
|
|
21
|
-
let
|
|
33
|
+
].includes(r)) return (...c) => {
|
|
34
|
+
let l, u = n ? `${n}.${r}` : `array.${r}`, d = t.length;
|
|
22
35
|
return e.batch(() => {
|
|
23
|
-
|
|
24
|
-
let
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
}),
|
|
50
|
+
}), l;
|
|
29
51
|
};
|
|
30
|
-
if (typeof
|
|
31
|
-
let
|
|
32
|
-
if (!
|
|
33
|
-
let a = Reflect.get(t,
|
|
34
|
-
|
|
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
|
|
58
|
+
return c._proxyCache === void 0 ? c.value : (c.value, c._proxyCache);
|
|
37
59
|
},
|
|
38
|
-
set(t,
|
|
39
|
-
let
|
|
40
|
-
if (s ===
|
|
41
|
-
Reflect.set(t,
|
|
42
|
-
let c = r
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
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,
|
|
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":["
|
|
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
|
|
4
|
-
let
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
41
|
-
h
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
let
|
|
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
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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 (
|
|
85
|
-
let
|
|
86
|
-
if (
|
|
87
|
-
let
|
|
88
|
-
|
|
89
|
-
|
|
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:
|
|
122
|
-
details: {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
|
114
|
+
return v ? null : d.value;
|
|
147
115
|
},
|
|
148
116
|
get loading() {
|
|
149
|
-
return
|
|
117
|
+
return v ? !1 : f.value;
|
|
150
118
|
},
|
|
151
119
|
get error() {
|
|
152
|
-
return
|
|
120
|
+
return v ? null : p.value;
|
|
153
121
|
},
|
|
154
122
|
get isRetrying() {
|
|
155
|
-
return m.value
|
|
123
|
+
return v ? !1 : m.value;
|
|
156
124
|
},
|
|
157
|
-
get
|
|
158
|
-
return
|
|
125
|
+
get attemptIndex() {
|
|
126
|
+
return v ? 0 : h.value;
|
|
159
127
|
},
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
164
|
-
|
|
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 {
|
|
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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"}
|