react-state-basis 0.6.0 → 0.6.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/README.md +32 -0
- package/dist/chunk-O2ZOR3L2.mjs +869 -0
- package/dist/chunk-O2ZOR3L2.mjs.map +1 -0
- package/dist/index.js +38 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +28 -855
- package/dist/index.mjs.map +1 -1
- package/dist/integrations/zustand-production.d.mts +3 -0
- package/dist/integrations/zustand-production.d.ts +3 -0
- package/dist/integrations/zustand-production.js +31 -0
- package/dist/integrations/zustand-production.js.map +1 -0
- package/dist/integrations/zustand-production.mjs +6 -0
- package/dist/integrations/zustand-production.mjs.map +1 -0
- package/dist/integrations/zustand.d.mts +6 -0
- package/dist/integrations/zustand.d.ts +6 -0
- package/dist/integrations/zustand.js +887 -0
- package/dist/integrations/zustand.js.map +1 -0
- package/dist/integrations/zustand.mjs +33 -0
- package/dist/integrations/zustand.mjs.map +1 -0
- package/package.json +22 -3
|
@@ -0,0 +1,869 @@
|
|
|
1
|
+
// src/core/math.ts
|
|
2
|
+
var calculateSimilarityCircular = (bufferA, headA, bufferB, headB, offset) => {
|
|
3
|
+
const L = bufferA.length;
|
|
4
|
+
let dot = 0, magA = 0, magB = 0;
|
|
5
|
+
const baseOffset = ((headB - headA + offset) % L + L) % L;
|
|
6
|
+
for (let i = 0; i < L; i++) {
|
|
7
|
+
const valA = bufferA[i];
|
|
8
|
+
let iB = i + baseOffset;
|
|
9
|
+
if (iB >= L) {
|
|
10
|
+
iB -= L;
|
|
11
|
+
}
|
|
12
|
+
const valB = bufferB[iB];
|
|
13
|
+
dot += valA * valB;
|
|
14
|
+
magA += valA * valA;
|
|
15
|
+
magB += valB * valB;
|
|
16
|
+
}
|
|
17
|
+
if (magA === 0 || magB === 0) return 0;
|
|
18
|
+
return dot / (Math.sqrt(magA) * Math.sqrt(magB));
|
|
19
|
+
};
|
|
20
|
+
var calculateCosineSimilarity = (A, B) => {
|
|
21
|
+
let dot = 0, magA = 0, magB = 0;
|
|
22
|
+
for (let i = 0; i < A.length; i++) {
|
|
23
|
+
dot += A[i] * B[i];
|
|
24
|
+
magA += A[i] * A[i];
|
|
25
|
+
magB += B[i] * B[i];
|
|
26
|
+
}
|
|
27
|
+
return magA === 0 || magB === 0 ? 0 : dot / (Math.sqrt(magA) * Math.sqrt(magB));
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/core/graph.ts
|
|
31
|
+
var calculateSpectralInfluence = (graph, maxIterations = 20, tolerance = 1e-3) => {
|
|
32
|
+
const nodes = Array.from(/* @__PURE__ */ new Set([...graph.keys(), ...Array.from(graph.values()).flatMap((m) => [...m.keys()])]));
|
|
33
|
+
if (nodes.length === 0) return /* @__PURE__ */ new Map();
|
|
34
|
+
let scores = /* @__PURE__ */ new Map();
|
|
35
|
+
nodes.forEach((n) => scores.set(n, 1 / nodes.length));
|
|
36
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
37
|
+
const nextScores = /* @__PURE__ */ new Map();
|
|
38
|
+
let totalWeight = 0;
|
|
39
|
+
nodes.forEach((source) => {
|
|
40
|
+
let influence = 0;
|
|
41
|
+
const outgoing = graph.get(source);
|
|
42
|
+
if (outgoing) {
|
|
43
|
+
outgoing.forEach((weight, target) => {
|
|
44
|
+
if (source !== target) {
|
|
45
|
+
influence += (scores.get(target) || 0) * weight;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
nextScores.set(source, influence + 0.01);
|
|
50
|
+
totalWeight += influence + 0.01;
|
|
51
|
+
});
|
|
52
|
+
let delta = 0;
|
|
53
|
+
nextScores.forEach((val, key) => {
|
|
54
|
+
const normalized = val / totalWeight;
|
|
55
|
+
const diff = normalized - (scores.get(key) || 0);
|
|
56
|
+
delta += diff * diff;
|
|
57
|
+
nextScores.set(key, normalized);
|
|
58
|
+
});
|
|
59
|
+
scores = nextScores;
|
|
60
|
+
if (Math.sqrt(delta) < tolerance) break;
|
|
61
|
+
}
|
|
62
|
+
return scores;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// src/core/ranker.ts
|
|
66
|
+
var parseLabel = (label) => {
|
|
67
|
+
const parts = label.split(" -> ");
|
|
68
|
+
return { file: parts[0] || "Unknown", name: parts[1] || label };
|
|
69
|
+
};
|
|
70
|
+
var identifyTopIssues = (graph, history2, redundantLabels2, violationMap) => {
|
|
71
|
+
const results = [];
|
|
72
|
+
const influence = calculateSpectralInfluence(graph);
|
|
73
|
+
const isEffect = (label) => label.includes("effect_L") || label.includes("useLayoutEffect") || label.includes("useInsertionEffect");
|
|
74
|
+
const isEvent = (label) => label.startsWith("Event_Tick_");
|
|
75
|
+
const eventSignatures = /* @__PURE__ */ new Map();
|
|
76
|
+
const drivers = Array.from(graph.entries()).filter(([label, targets]) => {
|
|
77
|
+
if (targets.size === 0) return false;
|
|
78
|
+
if (isEvent(label)) {
|
|
79
|
+
const validTargets = Array.from(targets.keys()).filter((t) => {
|
|
80
|
+
const meta2 = history2.get(t);
|
|
81
|
+
return meta2 && meta2.role !== "context" /* CONTEXT */;
|
|
82
|
+
});
|
|
83
|
+
if (validTargets.length > 1) {
|
|
84
|
+
const signature = validTargets.sort().join("|");
|
|
85
|
+
const existing = eventSignatures.get(signature);
|
|
86
|
+
if (existing) {
|
|
87
|
+
existing.count++;
|
|
88
|
+
existing.score = Math.max(existing.score, influence.get(label) || 0);
|
|
89
|
+
} else {
|
|
90
|
+
eventSignatures.set(signature, {
|
|
91
|
+
count: 1,
|
|
92
|
+
score: influence.get(label) || 0,
|
|
93
|
+
targets: validTargets
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const meta = history2.get(label);
|
|
100
|
+
if (meta?.role === "context" /* CONTEXT */) return false;
|
|
101
|
+
if (meta?.role === "proj" /* PROJECTION */) return false;
|
|
102
|
+
const score = influence.get(label) || 0;
|
|
103
|
+
if (isEffect(label) && targets.size > 0) return true;
|
|
104
|
+
if (targets.size < 2 && score < 0.05) return false;
|
|
105
|
+
return true;
|
|
106
|
+
}).sort((a, b) => {
|
|
107
|
+
const scoreA = influence.get(a[0]) || 0;
|
|
108
|
+
const scoreB = influence.get(b[0]) || 0;
|
|
109
|
+
return scoreB - scoreA;
|
|
110
|
+
});
|
|
111
|
+
const sortedEvents = Array.from(eventSignatures.entries()).sort((a, b) => b[1].targets.length - a[1].targets.length);
|
|
112
|
+
sortedEvents.slice(0, 2).forEach(([sig, data]) => {
|
|
113
|
+
const primaryVictim = parseLabel(data.targets[0]);
|
|
114
|
+
const smartLabel = `${primaryVictim.file} -> Global Event (${primaryVictim.name})`;
|
|
115
|
+
results.push({
|
|
116
|
+
label: smartLabel,
|
|
117
|
+
metric: "influence",
|
|
118
|
+
score: 1,
|
|
119
|
+
reason: `Global Sync Event: An external trigger is updating ${data.targets.length} roots simultaneously. Occurred ${data.count} times.`,
|
|
120
|
+
violations: data.targets.map((t) => ({
|
|
121
|
+
type: "causal_leak",
|
|
122
|
+
target: t
|
|
123
|
+
}))
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
drivers.slice(0, 3 - results.length).forEach(([label, targets]) => {
|
|
127
|
+
const targetNames = Array.from(targets.keys());
|
|
128
|
+
if (isEffect(label)) {
|
|
129
|
+
results.push({
|
|
130
|
+
label,
|
|
131
|
+
metric: "influence",
|
|
132
|
+
score: influence.get(label) || 0,
|
|
133
|
+
reason: `Side-Effect Driver: Hook writes to state during render.`,
|
|
134
|
+
violations: targetNames.map((t) => ({ type: "causal_leak", target: t }))
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
results.push({
|
|
139
|
+
label,
|
|
140
|
+
metric: "influence",
|
|
141
|
+
score: influence.get(label) || targets.size,
|
|
142
|
+
reason: `Sync Driver: Acts as a "Prime Mover" for ${targets.size} downstream signals.`,
|
|
143
|
+
violations: targetNames.map((t) => ({ type: "causal_leak", target: t }))
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
if (results.length === 0) {
|
|
147
|
+
const sortedDensity = Array.from(history2.entries()).filter(
|
|
148
|
+
([label, meta]) => meta.role === "local" /* LOCAL */ && !redundantLabels2.has(label) && meta.density > 25
|
|
149
|
+
).sort((a, b) => b[1].density - a[1].density);
|
|
150
|
+
sortedDensity.slice(0, 3).forEach(([label, meta]) => {
|
|
151
|
+
results.push({
|
|
152
|
+
label,
|
|
153
|
+
metric: "density",
|
|
154
|
+
score: meta.density,
|
|
155
|
+
reason: `High Frequency: potential main-thread saturation.`,
|
|
156
|
+
violations: []
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return results;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// src/core/logger.ts
|
|
164
|
+
var isWeb = typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
165
|
+
var LAST_LOG_TIMES = /* @__PURE__ */ new Map();
|
|
166
|
+
var LOG_COOLDOWN = 3e3;
|
|
167
|
+
var THEME = {
|
|
168
|
+
identity: "#6C5CE7",
|
|
169
|
+
// Purple (Brand)
|
|
170
|
+
problem: "#D63031",
|
|
171
|
+
// Red (Bugs)
|
|
172
|
+
solution: "#FBC531",
|
|
173
|
+
// Yellow (Fixes)
|
|
174
|
+
context: "#0984E3",
|
|
175
|
+
// Blue (Locations)
|
|
176
|
+
muted: "#9AA0A6",
|
|
177
|
+
// Gray (Metadata)
|
|
178
|
+
border: "#2E2E35",
|
|
179
|
+
success: "#00b894"
|
|
180
|
+
// Green (Good Score)
|
|
181
|
+
};
|
|
182
|
+
var STYLES = {
|
|
183
|
+
// Structure
|
|
184
|
+
basis: `background: ${THEME.identity}; color: white; font-weight: bold; padding: 2px 6px; border-radius: 3px;`,
|
|
185
|
+
headerIdentity: `background: ${THEME.identity}; color: white; font-weight: bold; padding: 4px 8px; border-radius: 4px;`,
|
|
186
|
+
headerProblem: `background: ${THEME.problem}; color: white; font-weight: bold; padding: 4px 8px; border-radius: 4px;`,
|
|
187
|
+
version: `background: #a29bfe; color: #2d3436; padding: 2px 6px; border-radius: 3px; margin-left: -4px;`,
|
|
188
|
+
// Actions
|
|
189
|
+
actionLabel: `color: ${THEME.solution}; font-weight: bold;`,
|
|
190
|
+
actionPill: `color: ${THEME.solution}; font-weight: bold; border: 1px solid ${THEME.solution}; padding: 0 4px; border-radius: 3px;`,
|
|
191
|
+
// Context
|
|
192
|
+
impactLabel: `color: ${THEME.context}; font-weight: bold;`,
|
|
193
|
+
location: `color: ${THEME.context}; font-family: monospace; font-weight: bold;`,
|
|
194
|
+
// Text
|
|
195
|
+
subText: `color: ${THEME.muted}; font-size: 11px;`,
|
|
196
|
+
bold: "font-weight: bold;",
|
|
197
|
+
label: "background: #dfe6e9; color: #2d3436; padding: 0 4px; border-radius: 3px; font-family: monospace; font-weight: bold; border: 1px solid #b2bec3;"
|
|
198
|
+
};
|
|
199
|
+
var parseLabel2 = (label) => {
|
|
200
|
+
const parts = label.split(" -> ");
|
|
201
|
+
return { file: parts[0] || "Unknown", name: parts[1] || label };
|
|
202
|
+
};
|
|
203
|
+
var shouldLog = (key) => {
|
|
204
|
+
const now = Date.now();
|
|
205
|
+
const last = LAST_LOG_TIMES.get(key) || 0;
|
|
206
|
+
if (now - last > LOG_COOLDOWN) {
|
|
207
|
+
LAST_LOG_TIMES.set(key, now);
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
return false;
|
|
211
|
+
};
|
|
212
|
+
var isBooleanLike = (name) => /^(is|has|can|should|did|will|show|hide)(?=[A-Z_])/.test(name);
|
|
213
|
+
var getSuggestedFix = (issue, info) => {
|
|
214
|
+
if (issue.label.includes("Global Event")) {
|
|
215
|
+
return `These variables update together but live in different hooks/files. Consolidate them into a single %cuseReducer%c or atomic store update.`;
|
|
216
|
+
}
|
|
217
|
+
const violations = issue.violations || [];
|
|
218
|
+
const leaks = violations.filter((v) => v.type === "causal_leak");
|
|
219
|
+
const mirrors = violations.filter((v) => v.type === "context_mirror");
|
|
220
|
+
const duplicates = violations.filter((v) => v.type === "duplicate_state");
|
|
221
|
+
if (mirrors.length > 0) {
|
|
222
|
+
return `Local state is 'shadowing' Global Context. This creates two sources of truth. Delete the local state and consume the %cContext%c value directly.`;
|
|
223
|
+
}
|
|
224
|
+
if (leaks.length > 0) {
|
|
225
|
+
const targetName = parseLabel2(leaks[0].target).name;
|
|
226
|
+
if (issue.label.includes("effect")) {
|
|
227
|
+
return `This Effect triggers a synchronous re-render of ${targetName}. Calculate ${targetName} during the render phase (Derived State) or wrap in %cuseMemo%c if expensive.`;
|
|
228
|
+
}
|
|
229
|
+
return `State cascading detected. ${info.name} triggers ${targetName} in a separate frame. Merge them into one object to update simultaneously.`;
|
|
230
|
+
}
|
|
231
|
+
if (duplicates.length > 0) {
|
|
232
|
+
if (isBooleanLike(info.name)) {
|
|
233
|
+
return `Boolean Explosion detected. Multiple flags are toggling in sync. Replace impossible states with a single %cstatus%c string ('idle' | 'loading' | 'success').`;
|
|
234
|
+
}
|
|
235
|
+
return `Redundant State detected. This variable carries no unique information. Derive it from the source variable during render, or use %cuseMemo%c to cache the result.`;
|
|
236
|
+
}
|
|
237
|
+
if (issue.metric === "density") {
|
|
238
|
+
return `High-Frequency Update. This variable updates faster than the frame rate. Apply %cdebounce%c or move to a Ref to unblock the main thread.`;
|
|
239
|
+
}
|
|
240
|
+
return `Check the dependency chain of ${info.name}.`;
|
|
241
|
+
};
|
|
242
|
+
var displayHealthReport = (history2, threshold, violationMap) => {
|
|
243
|
+
if (!isWeb) return;
|
|
244
|
+
const entries = Array.from(history2.entries());
|
|
245
|
+
if (entries.length === 0) return;
|
|
246
|
+
const topIssues = identifyTopIssues(instance.graph, history2, instance.redundantLabels, violationMap);
|
|
247
|
+
console.group(`%c \u{1F4CA} BASIS | ARCHITECTURAL HEALTH REPORT `, STYLES.headerIdentity);
|
|
248
|
+
if (topIssues.length > 0) {
|
|
249
|
+
console.log(
|
|
250
|
+
`%c\u{1F3AF} REFACTOR PRIORITIES %c(PRIME MOVERS)`,
|
|
251
|
+
`font-weight: bold; color: ${THEME.identity}; margin-top: 10px;`,
|
|
252
|
+
`font-weight: normal; color: ${THEME.muted}; font-style: italic;`
|
|
253
|
+
);
|
|
254
|
+
topIssues.forEach((issue, idx) => {
|
|
255
|
+
const info = parseLabel2(issue.label);
|
|
256
|
+
const icon = issue.metric === "influence" ? "\u26A1" : "\u{1F4C8}";
|
|
257
|
+
const pColor = idx === 0 ? THEME.problem : idx === 1 ? THEME.solution : THEME.identity;
|
|
258
|
+
let displayName = info.name;
|
|
259
|
+
let displayFile = info.file;
|
|
260
|
+
if (issue.label.includes("Global Event")) {
|
|
261
|
+
displayName = info.name;
|
|
262
|
+
displayFile = info.file;
|
|
263
|
+
}
|
|
264
|
+
console.group(
|
|
265
|
+
` %c${idx + 1}%c ${icon} ${displayName} %c(${displayFile})`,
|
|
266
|
+
`background: ${pColor}; color: ${idx === 1 ? "black" : "white"}; border-radius: 50%; padding: 0 5px;`,
|
|
267
|
+
"font-family: monospace; font-weight: 700;",
|
|
268
|
+
`color: ${THEME.muted}; font-size: 10px; font-weight: normal; font-style: italic;`
|
|
269
|
+
);
|
|
270
|
+
console.log(`%c${issue.reason}`, `color: ${THEME.muted}; font-style: italic;`);
|
|
271
|
+
if (issue.violations.length > 0) {
|
|
272
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
273
|
+
issue.violations.forEach((v) => {
|
|
274
|
+
if (issue.label.includes("Global Event") && v.type === "context_mirror") return;
|
|
275
|
+
const { file, name } = parseLabel2(v.target);
|
|
276
|
+
if (!byFile.has(file)) byFile.set(file, []);
|
|
277
|
+
byFile.get(file).push(name);
|
|
278
|
+
});
|
|
279
|
+
const impactParts = [];
|
|
280
|
+
byFile.forEach((vars, file) => {
|
|
281
|
+
const varList = vars.join(", ");
|
|
282
|
+
impactParts.push(`${file} (${varList})`);
|
|
283
|
+
});
|
|
284
|
+
if (impactParts.length > 0) {
|
|
285
|
+
console.log(`%cImpacts: %c${impactParts.join(" + ")}`, STYLES.impactLabel, "");
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const fix = getSuggestedFix(issue, info);
|
|
289
|
+
const fixParts = fix.split("%c");
|
|
290
|
+
if (fixParts.length === 3) {
|
|
291
|
+
console.log(
|
|
292
|
+
`%cSolution: %c${fixParts[0]}%c${fixParts[1]}%c${fixParts[2]}`,
|
|
293
|
+
STYLES.actionLabel,
|
|
294
|
+
"",
|
|
295
|
+
STYLES.actionPill,
|
|
296
|
+
""
|
|
297
|
+
);
|
|
298
|
+
} else {
|
|
299
|
+
console.log(
|
|
300
|
+
`%cSolution: %c${fix}`,
|
|
301
|
+
STYLES.actionLabel,
|
|
302
|
+
""
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
console.groupEnd();
|
|
306
|
+
});
|
|
307
|
+
console.log("\n");
|
|
308
|
+
}
|
|
309
|
+
const clusters = [];
|
|
310
|
+
const processed = /* @__PURE__ */ new Set();
|
|
311
|
+
let independentCount = 0;
|
|
312
|
+
entries.forEach(([labelA, metaA]) => {
|
|
313
|
+
if (processed.has(labelA)) return;
|
|
314
|
+
const currentCluster = [labelA];
|
|
315
|
+
processed.add(labelA);
|
|
316
|
+
entries.forEach(([labelB, metaB]) => {
|
|
317
|
+
if (labelA === labelB || processed.has(labelB)) return;
|
|
318
|
+
if (calculateCosineSimilarity(metaA.buffer, metaB.buffer) > threshold) {
|
|
319
|
+
if (metaA.role === "context" /* CONTEXT */ && metaB.role === "context" /* CONTEXT */) return;
|
|
320
|
+
currentCluster.push(labelB);
|
|
321
|
+
processed.add(labelB);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
if (currentCluster.length > 1) clusters.push(currentCluster);
|
|
325
|
+
else independentCount++;
|
|
326
|
+
});
|
|
327
|
+
const totalVars = entries.length;
|
|
328
|
+
const redundancyScore = (independentCount + clusters.length) / totalVars * 100;
|
|
329
|
+
let internalEdges = 0;
|
|
330
|
+
instance.graph.forEach((targets, source) => {
|
|
331
|
+
if (source.startsWith("Event_Tick_")) return;
|
|
332
|
+
internalEdges += targets.size;
|
|
333
|
+
});
|
|
334
|
+
const causalPenalty = internalEdges / totalVars * 100;
|
|
335
|
+
let healthScore = redundancyScore - causalPenalty;
|
|
336
|
+
if (healthScore < 0) healthScore = 0;
|
|
337
|
+
const scoreColor = healthScore > 85 ? THEME.success : THEME.problem;
|
|
338
|
+
console.log(
|
|
339
|
+
`%cSystem Efficiency: %c${healthScore.toFixed(1)}%`,
|
|
340
|
+
STYLES.bold,
|
|
341
|
+
`color: ${scoreColor}; font-weight: bold;`
|
|
342
|
+
);
|
|
343
|
+
console.log(`%cSources of Truth: ${independentCount + clusters.length}/${totalVars} | Causal Leaks: ${internalEdges}`, STYLES.subText);
|
|
344
|
+
if (clusters.length > 0) {
|
|
345
|
+
console.log(`%cDetected ${clusters.length} Sync Issues:`, `font-weight: bold; color: ${THEME.problem}; margin-top: 10px;`);
|
|
346
|
+
clusters.forEach((cluster, idx) => {
|
|
347
|
+
const clusterMetas = cluster.map((l) => ({
|
|
348
|
+
label: l,
|
|
349
|
+
meta: history2.get(l),
|
|
350
|
+
name: parseLabel2(l).name
|
|
351
|
+
}));
|
|
352
|
+
const hasCtx = clusterMetas.some(
|
|
353
|
+
(c) => c.meta.role === "context" /* CONTEXT */ || c.meta.role === "store" /* STORE */
|
|
354
|
+
);
|
|
355
|
+
const names = clusterMetas.map((c) => {
|
|
356
|
+
const prefix = c.meta.role === "store" /* STORE */ ? "\u03A3 " : c.meta.role === "context" /* CONTEXT */ ? "\u03A9 " : "";
|
|
357
|
+
return `${prefix}${c.name}`;
|
|
358
|
+
}).join(" \u27F7 ");
|
|
359
|
+
console.group(` %c${idx + 1}%c ${names}`, `background: ${THEME.problem}; color: white; border-radius: 50%; padding: 0 5px;`, "font-family: monospace; font-weight: bold;");
|
|
360
|
+
if (hasCtx) {
|
|
361
|
+
const hasStore = clusterMetas.some((c) => c.meta.role === "store" /* STORE */);
|
|
362
|
+
const sourceType = hasStore ? "External Store" : "global context";
|
|
363
|
+
console.log(`%cDiagnosis: ${hasStore ? "Store" : "Context"} Mirroring. Local state is shadowing ${sourceType}.`, `color: ${THEME.problem};`);
|
|
364
|
+
console.log(`%cSolution: Use ${sourceType} directly to avoid state drift.`, STYLES.actionLabel);
|
|
365
|
+
} else {
|
|
366
|
+
const boolKeywords = ["is", "has", "can", "should", "loading", "success", "error", "active", "enabled", "open", "visible"];
|
|
367
|
+
const boolCount = clusterMetas.filter(
|
|
368
|
+
(c) => boolKeywords.some((kw) => c.name.toLowerCase().startsWith(kw))
|
|
369
|
+
).length;
|
|
370
|
+
const isBoolExplosion = cluster.length > 2 && boolCount / cluster.length > 0.5;
|
|
371
|
+
if (isBoolExplosion) {
|
|
372
|
+
console.log(`%cDiagnosis:%c Boolean Explosion. Multiple booleans updating in sync.`, STYLES.bold, "");
|
|
373
|
+
console.log(`%cSolution:%c Combine into a single %cstatus%c string or a %creducer%c.`, STYLES.actionLabel, "", STYLES.actionPill, "", STYLES.actionPill, "");
|
|
374
|
+
} else if (cluster.length > 2) {
|
|
375
|
+
console.log(`%cDiagnosis:%c Sibling Updates. These states respond to the same event.`, STYLES.bold, "");
|
|
376
|
+
console.log(`%cSolution:%c This may be intentional. If not, consolidate into a %creducer%c.`, STYLES.actionLabel, "", STYLES.actionPill, "");
|
|
377
|
+
} else {
|
|
378
|
+
console.log(`%cDiagnosis:%c Redundant State. Variables always change together.`, STYLES.bold, "");
|
|
379
|
+
console.log(`%cSolution:%c Derive one from the other via %cuseMemo%c.`, STYLES.actionLabel, "", STYLES.actionPill, "");
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
console.groupEnd();
|
|
383
|
+
});
|
|
384
|
+
} else {
|
|
385
|
+
console.log("%c\u2728 Your architecture is clean. No redundant state detected.", `color: ${THEME.success}; font-weight: bold;`);
|
|
386
|
+
}
|
|
387
|
+
console.groupEnd();
|
|
388
|
+
};
|
|
389
|
+
var displayRedundancyAlert = (labelA, metaA, labelB, metaB, sim) => {
|
|
390
|
+
if (!isWeb || !shouldLog(`redundant-${labelA}-${labelB}`)) return;
|
|
391
|
+
const infoA = parseLabel2(labelA);
|
|
392
|
+
const infoB = parseLabel2(labelB);
|
|
393
|
+
const isContextMirror = metaA.role === "local" /* LOCAL */ && metaB.role === "context" /* CONTEXT */ || metaB.role === "local" /* LOCAL */ && metaA.role === "context" /* CONTEXT */;
|
|
394
|
+
const isStoreMirror = metaA.role === "local" /* LOCAL */ && metaB.role === "store" /* STORE */ || metaB.role === "local" /* LOCAL */ && metaA.role === "store" /* STORE */;
|
|
395
|
+
const alertType = isContextMirror ? "CONTEXT MIRRORING" : isStoreMirror ? "STORE MIRRORING" : "DUPLICATE STATE";
|
|
396
|
+
console.group(`%c \u264A BASIS | ${alertType} `, STYLES.headerProblem);
|
|
397
|
+
console.log(`%c\u{1F4CD} Location: %c${infoA.file}`, STYLES.bold, STYLES.location);
|
|
398
|
+
console.log(`%cIssue:%c ${infoA.name} and ${infoB.name} are synchronized (${(sim * 100).toFixed(0)}%).`, STYLES.bold, "");
|
|
399
|
+
if (isContextMirror || isStoreMirror) {
|
|
400
|
+
const sourceType = isStoreMirror ? "External Store" : "Global Context";
|
|
401
|
+
console.log(
|
|
402
|
+
`%cFix:%c Local state is 'shadowing' ${sourceType}. Delete the local state and consume the %c${sourceType}%c value directly.`,
|
|
403
|
+
STYLES.bold,
|
|
404
|
+
"",
|
|
405
|
+
STYLES.actionPill,
|
|
406
|
+
""
|
|
407
|
+
);
|
|
408
|
+
} else {
|
|
409
|
+
if (isBooleanLike(infoA.name) || isBooleanLike(infoB.name)) {
|
|
410
|
+
console.log(
|
|
411
|
+
`%cFix:%c Boolean Explosion detected. Merge flags into a single %cstatus%c string or %cuseReducer%c.`,
|
|
412
|
+
STYLES.bold,
|
|
413
|
+
"",
|
|
414
|
+
STYLES.actionPill,
|
|
415
|
+
"",
|
|
416
|
+
STYLES.actionPill,
|
|
417
|
+
""
|
|
418
|
+
);
|
|
419
|
+
} else {
|
|
420
|
+
console.log(
|
|
421
|
+
`%cFix:%c Redundant State detected. Derive %c${infoB.name}%c from %c${infoA.name}%c during render, or use %cuseMemo%c.`,
|
|
422
|
+
STYLES.bold,
|
|
423
|
+
"",
|
|
424
|
+
STYLES.label,
|
|
425
|
+
"",
|
|
426
|
+
STYLES.label,
|
|
427
|
+
"",
|
|
428
|
+
STYLES.actionPill,
|
|
429
|
+
""
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
console.groupEnd();
|
|
434
|
+
};
|
|
435
|
+
var displayCausalHint = (targetLabel, targetMeta, sourceLabel, sourceMeta) => {
|
|
436
|
+
if (!isWeb || !shouldLog(`causal-${sourceLabel}-${targetLabel}`)) return;
|
|
437
|
+
const target = parseLabel2(targetLabel);
|
|
438
|
+
const source = parseLabel2(sourceLabel);
|
|
439
|
+
const headerType = sourceMeta.role === "context" /* CONTEXT */ ? "CONTEXT SYNC LEAK" : sourceMeta.role === "store" /* STORE */ ? "STORE SYNC LEAK" : "DOUBLE RENDER";
|
|
440
|
+
const isEffect = sourceLabel.includes("effect") || sourceLabel.includes("useLayoutEffect");
|
|
441
|
+
console.groupCollapsed(`%c \u26A1 BASIS | ${headerType} `, STYLES.headerProblem);
|
|
442
|
+
console.log(`%c\u{1F4CD} Location: %c${target.file}`, STYLES.bold, STYLES.location);
|
|
443
|
+
console.log(`%cIssue:%c ${source.name} triggers ${target.name} in separate frames.`, STYLES.bold, "");
|
|
444
|
+
if (isEffect) {
|
|
445
|
+
console.log(
|
|
446
|
+
`%cFix:%c Derive %c${target.name}%c during the render phase (remove effect) or wrap in %cuseMemo%c.`,
|
|
447
|
+
STYLES.bold,
|
|
448
|
+
"",
|
|
449
|
+
STYLES.label,
|
|
450
|
+
"",
|
|
451
|
+
STYLES.actionPill,
|
|
452
|
+
""
|
|
453
|
+
);
|
|
454
|
+
} else {
|
|
455
|
+
console.log(
|
|
456
|
+
`%cFix:%c Merge %c${target.name}%c with %c${source.name}%c into a single state update.`,
|
|
457
|
+
STYLES.bold,
|
|
458
|
+
"",
|
|
459
|
+
STYLES.label,
|
|
460
|
+
"",
|
|
461
|
+
STYLES.label,
|
|
462
|
+
""
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
console.groupEnd();
|
|
466
|
+
};
|
|
467
|
+
var displayViolentBreaker = (label, count, threshold) => {
|
|
468
|
+
if (!isWeb) return;
|
|
469
|
+
const parts = label.split(" -> ");
|
|
470
|
+
console.group(`%c \u{1F6D1} BASIS CRITICAL | CIRCUIT BREAKER `, STYLES.headerProblem);
|
|
471
|
+
console.error(`INFINITE LOOP DETECTED
|
|
472
|
+
Variable: ${parts[1] || label}
|
|
473
|
+
Frequency: ${count} updates/sec`);
|
|
474
|
+
console.log(`%cACTION: Update BLOCKED to prevent browser freeze.`, `color: ${THEME.problem}; font-weight: bold;`);
|
|
475
|
+
console.groupEnd();
|
|
476
|
+
};
|
|
477
|
+
var displayBootLog = (windowSize) => {
|
|
478
|
+
if (!isWeb) return;
|
|
479
|
+
console.log(`%cBasis%cAuditor%c "Graph Era" (Window: ${windowSize})`, STYLES.basis, STYLES.version, `color: ${THEME.muted}; font-style: italic; margin-left: 8px;`);
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
// src/core/constants.ts
|
|
483
|
+
var WINDOW_SIZE = 50;
|
|
484
|
+
var SIMILARITY_THRESHOLD = 0.88;
|
|
485
|
+
var LOOP_THRESHOLD = 150;
|
|
486
|
+
var VOLATILITY_THRESHOLD = 25;
|
|
487
|
+
|
|
488
|
+
// src/core/analysis.ts
|
|
489
|
+
var CAUSAL_MARGIN = 0.05;
|
|
490
|
+
var isEventDriven = (label, graph) => {
|
|
491
|
+
for (const [parent, targets] of graph.entries()) {
|
|
492
|
+
if (parent.startsWith("Event_Tick_") && targets.has(label)) {
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return false;
|
|
497
|
+
};
|
|
498
|
+
var calculateAllSimilarities = (entryA, entryB) => {
|
|
499
|
+
const sync = calculateSimilarityCircular(
|
|
500
|
+
entryA.meta.buffer,
|
|
501
|
+
entryA.meta.head,
|
|
502
|
+
entryB.meta.buffer,
|
|
503
|
+
entryB.meta.head,
|
|
504
|
+
0
|
|
505
|
+
);
|
|
506
|
+
const bA = calculateSimilarityCircular(
|
|
507
|
+
entryA.meta.buffer,
|
|
508
|
+
entryA.meta.head,
|
|
509
|
+
entryB.meta.buffer,
|
|
510
|
+
entryB.meta.head,
|
|
511
|
+
1
|
|
512
|
+
);
|
|
513
|
+
const aB = calculateSimilarityCircular(
|
|
514
|
+
entryA.meta.buffer,
|
|
515
|
+
entryA.meta.head,
|
|
516
|
+
entryB.meta.buffer,
|
|
517
|
+
entryB.meta.head,
|
|
518
|
+
-1
|
|
519
|
+
);
|
|
520
|
+
return { sync, bA, aB, max: Math.max(sync, bA, aB) };
|
|
521
|
+
};
|
|
522
|
+
var shouldSkipComparison = (entryA, entryB, dirtyLabels2) => {
|
|
523
|
+
if (entryA.label === entryB.label) return true;
|
|
524
|
+
if (dirtyLabels2.has(entryB.label) && entryA.label > entryB.label) return true;
|
|
525
|
+
return false;
|
|
526
|
+
};
|
|
527
|
+
var pushViolation = (map, source, detail) => {
|
|
528
|
+
if (!map.has(source)) {
|
|
529
|
+
map.set(source, []);
|
|
530
|
+
}
|
|
531
|
+
const list = map.get(source);
|
|
532
|
+
const exists = list.some(
|
|
533
|
+
(v) => v.type === detail.type && v.target === detail.target
|
|
534
|
+
);
|
|
535
|
+
if (!exists) {
|
|
536
|
+
list.push(detail);
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
var isGlobalSource = (role) => role === "context" /* CONTEXT */ || role === "store" /* STORE */;
|
|
540
|
+
var detectRedundancy = (entryA, entryB, similarities, redundantSet, violationMap) => {
|
|
541
|
+
const roleA = entryA.meta.role;
|
|
542
|
+
const roleB = entryB.meta.role;
|
|
543
|
+
if (isGlobalSource(roleA) && isGlobalSource(roleB)) return;
|
|
544
|
+
if (entryA.meta.density < 2 || entryB.meta.density < 2) return;
|
|
545
|
+
if (roleA === "local" /* LOCAL */ && isGlobalSource(roleB)) {
|
|
546
|
+
redundantSet.add(entryA.label);
|
|
547
|
+
pushViolation(violationMap, entryB.label, { type: "context_mirror", target: entryA.label, similarity: similarities.max });
|
|
548
|
+
displayRedundancyAlert(entryA.label, entryA.meta, entryB.label, entryB.meta, similarities.max);
|
|
549
|
+
} else if (isGlobalSource(roleA) && roleB === "local" /* LOCAL */) {
|
|
550
|
+
redundantSet.add(entryB.label);
|
|
551
|
+
pushViolation(violationMap, entryA.label, { type: "context_mirror", target: entryB.label, similarity: similarities.max });
|
|
552
|
+
displayRedundancyAlert(entryB.label, entryB.meta, entryA.label, entryA.meta, similarities.max);
|
|
553
|
+
} else if (roleA === "local" /* LOCAL */ && roleB === "local" /* LOCAL */) {
|
|
554
|
+
redundantSet.add(entryA.label);
|
|
555
|
+
redundantSet.add(entryB.label);
|
|
556
|
+
pushViolation(violationMap, entryA.label, { type: "duplicate_state", target: entryB.label, similarity: similarities.max });
|
|
557
|
+
pushViolation(violationMap, entryB.label, { type: "duplicate_state", target: entryA.label, similarity: similarities.max });
|
|
558
|
+
displayRedundancyAlert(entryA.label, entryA.meta, entryB.label, entryB.meta, similarities.max);
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
var detectCausalLeak = (entryA, entryB, similarities, violationMap, graph) => {
|
|
562
|
+
if (entryA.isVolatile || entryB.isVolatile) return;
|
|
563
|
+
if (similarities.max - similarities.sync < CAUSAL_MARGIN) return;
|
|
564
|
+
const addLeak = (source, target) => {
|
|
565
|
+
if (isEventDriven(target, graph)) return;
|
|
566
|
+
if (!violationMap.has(source)) {
|
|
567
|
+
violationMap.set(source, []);
|
|
568
|
+
}
|
|
569
|
+
violationMap.get(source).push({ type: "causal_leak", target });
|
|
570
|
+
const sourceEntry = source === entryA.label ? entryA : entryB;
|
|
571
|
+
const targetEntry = source === entryA.label ? entryB : entryA;
|
|
572
|
+
displayCausalHint(target, targetEntry.meta, source, sourceEntry.meta);
|
|
573
|
+
};
|
|
574
|
+
if (similarities.bA === similarities.max) {
|
|
575
|
+
addLeak(entryA.label, entryB.label);
|
|
576
|
+
} else if (similarities.aB === similarities.max) {
|
|
577
|
+
addLeak(entryB.label, entryA.label);
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
var detectSubspaceOverlap = (dirtyEntries, allEntries, redundantSet, dirtyLabels2, graph) => {
|
|
581
|
+
let compCount = 0;
|
|
582
|
+
const violationMap = /* @__PURE__ */ new Map();
|
|
583
|
+
for (const entryA of dirtyEntries) {
|
|
584
|
+
for (const entryB of allEntries) {
|
|
585
|
+
if (shouldSkipComparison(entryA, entryB, dirtyLabels2)) continue;
|
|
586
|
+
compCount++;
|
|
587
|
+
const similarities = calculateAllSimilarities(entryA, entryB);
|
|
588
|
+
if (similarities.max > SIMILARITY_THRESHOLD) {
|
|
589
|
+
detectRedundancy(entryA, entryB, similarities, redundantSet, violationMap);
|
|
590
|
+
detectCausalLeak(entryA, entryB, similarities, violationMap, graph);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return { compCount, violationMap };
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
// src/engine.ts
|
|
598
|
+
var BASIS_INSTANCE_KEY = /* @__PURE__ */ Symbol.for("__basis_engine_instance__");
|
|
599
|
+
var EVENT_TTL = 1e4;
|
|
600
|
+
var NULL_SIGNAL = {
|
|
601
|
+
role: "proj" /* PROJECTION */,
|
|
602
|
+
buffer: new Uint8Array(0),
|
|
603
|
+
head: 0,
|
|
604
|
+
density: 0,
|
|
605
|
+
options: {}
|
|
606
|
+
};
|
|
607
|
+
var activeEventId = null;
|
|
608
|
+
var pruneGraph = () => {
|
|
609
|
+
const now = Date.now();
|
|
610
|
+
for (const source of instance.graph.keys()) {
|
|
611
|
+
if (source.startsWith("Event_Tick_")) {
|
|
612
|
+
const parts = source.split("_");
|
|
613
|
+
const timestamp = parseInt(parts[2], 10);
|
|
614
|
+
if (now - timestamp > EVENT_TTL) {
|
|
615
|
+
instance.graph.delete(source);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
var getEventId = () => {
|
|
621
|
+
if (!activeEventId) {
|
|
622
|
+
activeEventId = `Event_Tick_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`;
|
|
623
|
+
requestAnimationFrame(() => {
|
|
624
|
+
activeEventId = null;
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
return activeEventId;
|
|
628
|
+
};
|
|
629
|
+
var getGlobalInstance = () => {
|
|
630
|
+
const root = globalThis;
|
|
631
|
+
if (!root[BASIS_INSTANCE_KEY]) {
|
|
632
|
+
root[BASIS_INSTANCE_KEY] = {
|
|
633
|
+
config: { debug: false },
|
|
634
|
+
history: /* @__PURE__ */ new Map(),
|
|
635
|
+
currentTickBatch: /* @__PURE__ */ new Set(),
|
|
636
|
+
redundantLabels: /* @__PURE__ */ new Set(),
|
|
637
|
+
booted: false,
|
|
638
|
+
tick: 0,
|
|
639
|
+
isBatching: false,
|
|
640
|
+
currentEffectSource: null,
|
|
641
|
+
lastStateUpdate: null,
|
|
642
|
+
pausedVariables: /* @__PURE__ */ new Set(),
|
|
643
|
+
graph: /* @__PURE__ */ new Map(),
|
|
644
|
+
violationMap: /* @__PURE__ */ new Map(),
|
|
645
|
+
metrics: {
|
|
646
|
+
lastAnalysisTimeMs: 0,
|
|
647
|
+
comparisonCount: 0,
|
|
648
|
+
lastAnalysisTimestamp: 0,
|
|
649
|
+
systemEntropy: 0
|
|
650
|
+
},
|
|
651
|
+
alertCount: 0,
|
|
652
|
+
loopCounters: /* @__PURE__ */ new Map(),
|
|
653
|
+
lastCleanup: Date.now()
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
return root[BASIS_INSTANCE_KEY];
|
|
657
|
+
};
|
|
658
|
+
var instance = getGlobalInstance();
|
|
659
|
+
var config = instance.config;
|
|
660
|
+
var history = instance.history;
|
|
661
|
+
var redundantLabels = instance.redundantLabels;
|
|
662
|
+
var currentTickBatch = instance.currentTickBatch;
|
|
663
|
+
var currentTickRegistry = {};
|
|
664
|
+
var dirtyLabels = /* @__PURE__ */ new Set();
|
|
665
|
+
var calculateTickEntropy = (tickIdx) => {
|
|
666
|
+
let activeCount = 0;
|
|
667
|
+
const total = instance.history.size;
|
|
668
|
+
if (total === 0) return 1;
|
|
669
|
+
instance.history.forEach((meta) => {
|
|
670
|
+
if (meta.buffer[tickIdx] === 1) activeCount++;
|
|
671
|
+
});
|
|
672
|
+
return 1 - activeCount / total;
|
|
673
|
+
};
|
|
674
|
+
var recordEdge = (source, target) => {
|
|
675
|
+
if (!source || !target || source === target) return;
|
|
676
|
+
if (!instance.graph.has(source)) {
|
|
677
|
+
instance.graph.set(source, /* @__PURE__ */ new Map());
|
|
678
|
+
}
|
|
679
|
+
const targets = instance.graph.get(source);
|
|
680
|
+
targets.set(target, (targets.get(target) || 0) + 1);
|
|
681
|
+
};
|
|
682
|
+
var analyzeBasis = () => {
|
|
683
|
+
if (!instance.config.debug || dirtyLabels.size === 0) {
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
const scheduler = globalThis.requestIdleCallback || ((cb) => setTimeout(cb, 1));
|
|
687
|
+
const snapshot = new Set(dirtyLabels);
|
|
688
|
+
dirtyLabels.clear();
|
|
689
|
+
scheduler(() => {
|
|
690
|
+
const analysisStart = performance.now();
|
|
691
|
+
const allEntries = [];
|
|
692
|
+
const dirtyEntries = [];
|
|
693
|
+
instance.history.forEach((meta, label) => {
|
|
694
|
+
if (meta.options.suppressAll || meta.density === 0) return;
|
|
695
|
+
const entry = {
|
|
696
|
+
label,
|
|
697
|
+
meta,
|
|
698
|
+
isVolatile: meta.density > VOLATILITY_THRESHOLD
|
|
699
|
+
};
|
|
700
|
+
allEntries.push(entry);
|
|
701
|
+
if (snapshot.has(label)) {
|
|
702
|
+
dirtyEntries.push(entry);
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
if (dirtyEntries.length === 0 || allEntries.length < 2) return;
|
|
706
|
+
const nextRedundant = /* @__PURE__ */ new Set();
|
|
707
|
+
instance.redundantLabels.forEach((l) => {
|
|
708
|
+
if (!snapshot.has(l)) {
|
|
709
|
+
nextRedundant.add(l);
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
const { compCount, violationMap } = detectSubspaceOverlap(
|
|
713
|
+
dirtyEntries,
|
|
714
|
+
allEntries,
|
|
715
|
+
nextRedundant,
|
|
716
|
+
snapshot,
|
|
717
|
+
instance.graph
|
|
718
|
+
);
|
|
719
|
+
instance.redundantLabels.clear();
|
|
720
|
+
nextRedundant.forEach((l) => {
|
|
721
|
+
instance.redundantLabels.add(l);
|
|
722
|
+
});
|
|
723
|
+
violationMap.forEach((newList, label) => {
|
|
724
|
+
const existing = instance.violationMap.get(label) || [];
|
|
725
|
+
newList.forEach((detail) => {
|
|
726
|
+
const alreadyExists = existing.some(
|
|
727
|
+
(v) => v.type === detail.type && v.target === detail.target
|
|
728
|
+
);
|
|
729
|
+
if (!alreadyExists) {
|
|
730
|
+
existing.push(detail);
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
instance.violationMap.set(label, existing);
|
|
734
|
+
});
|
|
735
|
+
if (instance.violationMap.size > 500) {
|
|
736
|
+
const keys = Array.from(instance.violationMap.keys()).slice(0, 200);
|
|
737
|
+
keys.forEach((k) => instance.violationMap.delete(k));
|
|
738
|
+
}
|
|
739
|
+
instance.metrics.lastAnalysisTimeMs = performance.now() - analysisStart;
|
|
740
|
+
instance.metrics.comparisonCount = compCount;
|
|
741
|
+
instance.metrics.lastAnalysisTimestamp = Date.now();
|
|
742
|
+
});
|
|
743
|
+
};
|
|
744
|
+
var processHeartbeat = () => {
|
|
745
|
+
instance.tick++;
|
|
746
|
+
instance.history.forEach((meta, label) => {
|
|
747
|
+
const oldValue = meta.buffer[meta.head];
|
|
748
|
+
const newValue = instance.currentTickBatch.has(label) ? 1 : 0;
|
|
749
|
+
meta.buffer[meta.head] = newValue;
|
|
750
|
+
if (oldValue !== newValue) {
|
|
751
|
+
meta.density += newValue - oldValue;
|
|
752
|
+
}
|
|
753
|
+
meta.head = (meta.head + 1) % WINDOW_SIZE;
|
|
754
|
+
});
|
|
755
|
+
const lastHead = instance.history.size > 0 ? instance.tick % WINDOW_SIZE : 0;
|
|
756
|
+
instance.metrics.systemEntropy = calculateTickEntropy(lastHead);
|
|
757
|
+
instance.currentTickBatch.clear();
|
|
758
|
+
currentTickRegistry = {};
|
|
759
|
+
instance.isBatching = false;
|
|
760
|
+
instance.lastStateUpdate = null;
|
|
761
|
+
if (dirtyLabels.size > 0) {
|
|
762
|
+
analyzeBasis();
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
var recordUpdate = (label) => {
|
|
766
|
+
if (!instance.config.debug) return true;
|
|
767
|
+
if (instance.pausedVariables.has(label)) return false;
|
|
768
|
+
const now = Date.now();
|
|
769
|
+
if (now - instance.lastCleanup > 1e3) {
|
|
770
|
+
instance.loopCounters.clear();
|
|
771
|
+
instance.lastCleanup = now;
|
|
772
|
+
pruneGraph();
|
|
773
|
+
}
|
|
774
|
+
const count = (instance.loopCounters.get(label) || 0) + 1;
|
|
775
|
+
instance.loopCounters.set(label, count);
|
|
776
|
+
if (count > LOOP_THRESHOLD) {
|
|
777
|
+
displayViolentBreaker(label, count, LOOP_THRESHOLD);
|
|
778
|
+
instance.pausedVariables.add(label);
|
|
779
|
+
return false;
|
|
780
|
+
}
|
|
781
|
+
const meta = instance.history.get(label);
|
|
782
|
+
let edgeSource = null;
|
|
783
|
+
if (instance.currentEffectSource) {
|
|
784
|
+
edgeSource = instance.currentEffectSource;
|
|
785
|
+
} else {
|
|
786
|
+
edgeSource = getEventId();
|
|
787
|
+
}
|
|
788
|
+
if (edgeSource && edgeSource !== label) {
|
|
789
|
+
recordEdge(edgeSource, label);
|
|
790
|
+
if (instance.currentEffectSource && instance.currentEffectSource !== label) {
|
|
791
|
+
const sourceMeta = instance.history.get(instance.currentEffectSource) || NULL_SIGNAL;
|
|
792
|
+
if (meta && sourceMeta && meta.density < VOLATILITY_THRESHOLD && sourceMeta.density < VOLATILITY_THRESHOLD) {
|
|
793
|
+
displayCausalHint(label, meta, instance.currentEffectSource, sourceMeta);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
if (meta && meta.role === "local" /* LOCAL */ && !instance.currentEffectSource) {
|
|
798
|
+
instance.lastStateUpdate = label;
|
|
799
|
+
}
|
|
800
|
+
if (currentTickRegistry[label]) return true;
|
|
801
|
+
currentTickRegistry[label] = true;
|
|
802
|
+
instance.currentTickBatch.add(label);
|
|
803
|
+
dirtyLabels.add(label);
|
|
804
|
+
if (!instance.isBatching) {
|
|
805
|
+
instance.isBatching = true;
|
|
806
|
+
requestAnimationFrame(processHeartbeat);
|
|
807
|
+
}
|
|
808
|
+
return true;
|
|
809
|
+
};
|
|
810
|
+
var configureBasis = (c) => {
|
|
811
|
+
Object.assign(instance.config, c);
|
|
812
|
+
if (instance.config.debug && !instance.booted) {
|
|
813
|
+
displayBootLog(WINDOW_SIZE);
|
|
814
|
+
instance.booted = true;
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
var registerVariable = (l, o = {}) => {
|
|
818
|
+
if (o.suppressAll) return;
|
|
819
|
+
if (!instance.history.has(l)) {
|
|
820
|
+
instance.history.set(l, {
|
|
821
|
+
buffer: new Uint8Array(WINDOW_SIZE),
|
|
822
|
+
head: 0,
|
|
823
|
+
density: 0,
|
|
824
|
+
options: o,
|
|
825
|
+
role: o.role || "local" /* LOCAL */
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
var unregisterVariable = (l) => {
|
|
830
|
+
instance.history.delete(l);
|
|
831
|
+
instance.loopCounters.delete(l);
|
|
832
|
+
instance.pausedVariables.delete(l);
|
|
833
|
+
instance.redundantLabels.delete(l);
|
|
834
|
+
};
|
|
835
|
+
var beginEffectTracking = (l) => {
|
|
836
|
+
if (instance.config.debug) instance.currentEffectSource = l;
|
|
837
|
+
};
|
|
838
|
+
var endEffectTracking = () => {
|
|
839
|
+
instance.currentEffectSource = null;
|
|
840
|
+
};
|
|
841
|
+
var printBasisHealthReport = (threshold = 0.5) => {
|
|
842
|
+
if (!instance.config.debug) return;
|
|
843
|
+
displayHealthReport(instance.history, threshold, instance.violationMap);
|
|
844
|
+
};
|
|
845
|
+
var getBasisMetrics = () => ({
|
|
846
|
+
engine: "v0.6.x",
|
|
847
|
+
hooks: instance.history.size,
|
|
848
|
+
analysis_ms: instance.metrics.lastAnalysisTimeMs.toFixed(3),
|
|
849
|
+
entropy: instance.metrics.systemEntropy.toFixed(3)
|
|
850
|
+
});
|
|
851
|
+
if (typeof window !== "undefined") {
|
|
852
|
+
window.printBasisReport = printBasisHealthReport;
|
|
853
|
+
window.getBasisMetrics = getBasisMetrics;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
export {
|
|
857
|
+
WINDOW_SIZE,
|
|
858
|
+
history,
|
|
859
|
+
redundantLabels,
|
|
860
|
+
recordUpdate,
|
|
861
|
+
configureBasis,
|
|
862
|
+
registerVariable,
|
|
863
|
+
unregisterVariable,
|
|
864
|
+
beginEffectTracking,
|
|
865
|
+
endEffectTracking,
|
|
866
|
+
printBasisHealthReport,
|
|
867
|
+
getBasisMetrics
|
|
868
|
+
};
|
|
869
|
+
//# sourceMappingURL=chunk-O2ZOR3L2.mjs.map
|