react-state-basis 0.6.0 → 0.6.2

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