react-state-basis 0.6.5 → 0.6.6

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.js CHANGED
@@ -64,33 +64,92 @@ module.exports = __toCommonJS(index_exports);
64
64
  var React = __toESM(require("react"));
65
65
  var import_react = require("react");
66
66
 
67
+ // src/core/constants.ts
68
+ var WINDOW_SIZE = 50;
69
+ var PAIR_RARITY_TARGET = 0.01;
70
+ var RELATIVE_OVERLAP_FLOOR = 0.65;
71
+ var LOOP_THRESHOLD = 150;
72
+ var VOLATILITY_THRESHOLD = 25;
73
+ var INSTANCE_SEP = "##";
74
+
67
75
  // src/core/math.ts
68
- var calculateSimilarityCircular = (bufferA, headA, bufferB, headB, offset) => {
76
+ var choose = (n, k) => {
77
+ if (k < 0 || k > n) return 0;
78
+ k = Math.min(k, n - k);
79
+ let result = 1;
80
+ for (let i = 0; i < k; i++) {
81
+ result = result * (n - i) / (i + 1);
82
+ }
83
+ return result;
84
+ };
85
+ var hypergeomPMF = (k, n1, m1, N) => {
86
+ const total = choose(N, n1);
87
+ if (total === 0) return 0;
88
+ return choose(m1, k) * choose(N - m1, n1 - k) / total;
89
+ };
90
+ var hypergeomUpperTailP = (kStart, n1, m1, N) => {
91
+ const kMax = Math.min(n1, m1);
92
+ let p = 0;
93
+ for (let k = Math.max(0, kStart); k <= kMax; k++) {
94
+ p += hypergeomPMF(k, n1, m1, N);
95
+ }
96
+ return p;
97
+ };
98
+ var minOverlapForRarity = (n1, m1, N, targetP) => {
99
+ const kMax = Math.min(n1, m1);
100
+ for (let k = 0; k <= kMax; k++) {
101
+ if (hypergeomUpperTailP(k, n1, m1, N) <= targetP) return k;
102
+ }
103
+ return kMax + 1;
104
+ };
105
+ var overlapThresholdCache = /* @__PURE__ */ new Map();
106
+ var getMinOverlap = (densityA, densityB, windowSize) => {
107
+ const lo = Math.min(densityA, densityB) | 0;
108
+ const hi = Math.max(densityA, densityB) | 0;
109
+ const key = `${lo}_${hi}_${windowSize}`;
110
+ let cached = overlapThresholdCache.get(key);
111
+ if (cached === void 0) {
112
+ const rare = minOverlapForRarity(lo, hi, windowSize, PAIR_RARITY_TARGET);
113
+ const floor = Math.ceil(RELATIVE_OVERLAP_FLOOR * lo);
114
+ cached = Math.max(rare, floor);
115
+ overlapThresholdCache.set(key, cached);
116
+ }
117
+ return cached;
118
+ };
119
+ var isSignificantOverlap = (overlap, densityA, densityB, windowSize) => {
120
+ if (densityA < 2 || densityB < 2) return false;
121
+ return overlap >= getMinOverlap(densityA, densityB, windowSize);
122
+ };
123
+ var countOverlapsCircular = (bufferA, headA, bufferB, headB) => {
69
124
  const L = bufferA.length;
70
- let dot = 0, magA = 0, magB = 0;
71
- const baseOffset = ((headB - headA + offset) % L + L) % L;
125
+ const offSync = ((headB - headA) % L + L) % L;
126
+ const offALeadsB = ((headB - headA + 1) % L + L) % L;
127
+ const offBLeadsA = ((headB - headA - 1) % L + L) % L;
128
+ let kSync = 0;
129
+ let kALeadsB = 0;
130
+ let kBLeadsA = 0;
131
+ let densityA = 0;
132
+ let densityB = 0;
72
133
  for (let i = 0; i < L; i++) {
73
- const valA = bufferA[i];
74
- let iB = i + baseOffset;
75
- if (iB >= L) {
76
- iB -= L;
77
- }
78
- const valB = bufferB[iB];
79
- dot += valA * valB;
80
- magA += valA * valA;
81
- magB += valB * valB;
134
+ const a = bufferA[i] ? 1 : 0;
135
+ const b = bufferB[i] ? 1 : 0;
136
+ densityA += a;
137
+ densityB += b;
138
+ let iSync = i + offSync;
139
+ if (iSync >= L) iSync -= L;
140
+ let iALeadsB = i + offALeadsB;
141
+ if (iALeadsB >= L) iALeadsB -= L;
142
+ let iBLeadsA = i + offBLeadsA;
143
+ if (iBLeadsA >= L) iBLeadsA -= L;
144
+ if (a && bufferB[iSync]) kSync++;
145
+ if (a && bufferB[iALeadsB]) kALeadsB++;
146
+ if (a && bufferB[iBLeadsA]) kBLeadsA++;
82
147
  }
83
- if (magA === 0 || magB === 0) return 0;
84
- return dot / (Math.sqrt(magA) * Math.sqrt(magB));
148
+ return { kSync, kALeadsB, kBLeadsA, densityA, densityB };
85
149
  };
86
- var calculateCosineSimilarity = (A, B) => {
87
- let dot = 0, magA = 0, magB = 0;
88
- for (let i = 0; i < A.length; i++) {
89
- dot += A[i] * B[i];
90
- magA += A[i] * A[i];
91
- magB += B[i] * B[i];
92
- }
93
- return magA === 0 || magB === 0 ? 0 : dot / (Math.sqrt(magA) * Math.sqrt(magB));
150
+ var cosineFromOverlap = (overlap, densityA, densityB) => {
151
+ if (densityA <= 0 || densityB <= 0) return 0;
152
+ return overlap / Math.sqrt(densityA * densityB);
94
153
  };
95
154
 
96
155
  // src/core/graph.ts
@@ -149,13 +208,6 @@ var groupEventSources = (nodes, edges) => {
149
208
  return groups.sort((a, b) => b.edges.length - a.edges.length || b.occurrences - a.occurrences);
150
209
  };
151
210
 
152
- // src/core/constants.ts
153
- var WINDOW_SIZE = 50;
154
- var SIMILARITY_THRESHOLD = 0.88;
155
- var LOOP_THRESHOLD = 150;
156
- var VOLATILITY_THRESHOLD = 25;
157
- var INSTANCE_SEP = "##";
158
-
159
211
  // src/core/label.ts
160
212
  var stripInstance = (label) => {
161
213
  const idx = label.indexOf(INSTANCE_SEP);
@@ -269,32 +321,22 @@ var LAST_LOG_TIMES = /* @__PURE__ */ new Map();
269
321
  var LOG_COOLDOWN = 3e3;
270
322
  var THEME = {
271
323
  identity: "#6C5CE7",
272
- // Purple (Brand)
273
324
  problem: "#D63031",
274
- // Red (Bugs)
275
325
  solution: "#FBC531",
276
- // Yellow (Fixes)
277
326
  context: "#0984E3",
278
- // Blue (Locations)
279
327
  muted: "#9AA0A6",
280
- // Gray (Metadata)
281
328
  border: "#2E2E35",
282
329
  success: "#00b894"
283
- // Green (Good Score)
284
330
  };
285
331
  var STYLES = {
286
- // Structure
287
332
  basis: `background: ${THEME.identity}; color: white; font-weight: bold; padding: 2px 6px; border-radius: 3px;`,
288
333
  headerIdentity: `background: ${THEME.identity}; color: white; font-weight: bold; padding: 4px 8px; border-radius: 4px;`,
289
334
  headerProblem: `background: ${THEME.problem}; color: white; font-weight: bold; padding: 4px 8px; border-radius: 4px;`,
290
335
  version: `background: #a29bfe; color: #2d3436; padding: 2px 6px; border-radius: 3px; margin-left: -4px;`,
291
- // Actions
292
336
  actionLabel: `color: ${THEME.solution}; font-weight: bold;`,
293
337
  actionPill: `color: ${THEME.solution}; font-weight: bold; border: 1px solid ${THEME.solution}; padding: 0 4px; border-radius: 3px;`,
294
- // Context
295
338
  impactLabel: `color: ${THEME.context}; font-weight: bold;`,
296
339
  location: `color: ${THEME.context}; font-family: monospace; font-weight: bold;`,
297
- // Text
298
340
  subText: `color: ${THEME.muted}; font-size: 11px;`,
299
341
  bold: "font-weight: bold;",
300
342
  label: "background: #dfe6e9; color: #2d3436; padding: 0 4px; border-radius: 3px; font-family: monospace; font-weight: bold; border: 1px solid #b2bec3;"
@@ -309,6 +351,15 @@ var shouldLog = (key) => {
309
351
  return false;
310
352
  };
311
353
  var isBooleanLike = (name) => /^(is|has|can|should|did|will|show|hide)(?=[A-Z_])/.test(name);
354
+ var areSyncSignificant = (metaA, metaB) => {
355
+ const { kSync, densityA, densityB } = countOverlapsCircular(
356
+ metaA.buffer,
357
+ metaA.head,
358
+ metaB.buffer,
359
+ metaB.head
360
+ );
361
+ return isSignificantOverlap(kSync, densityA, densityB, metaA.buffer.length);
362
+ };
312
363
  var getSuggestedFix = (issue, info) => {
313
364
  if (issue.label.includes("Global Event")) {
314
365
  return `These variables update together but live in different hooks/files. Consolidate them into a single %cuseReducer%c or atomic store update.`;
@@ -338,7 +389,7 @@ var getSuggestedFix = (issue, info) => {
338
389
  }
339
390
  return `Check the dependency chain of ${info.name}.`;
340
391
  };
341
- var displayHealthReport = (history2, threshold, violationMap) => {
392
+ var displayHealthReport = (history2, violationMap) => {
342
393
  if (!isWeb) return;
343
394
  const entries = Array.from(history2.entries());
344
395
  if (entries.length === 0) return;
@@ -414,11 +465,10 @@ var displayHealthReport = (history2, threshold, violationMap) => {
414
465
  processed.add(labelA);
415
466
  entries.forEach(([labelB, metaB]) => {
416
467
  if (labelA === labelB || processed.has(labelB)) return;
417
- if (calculateCosineSimilarity(metaA.buffer, metaB.buffer) > threshold) {
418
- if (metaA.role === "context" /* CONTEXT */ && metaB.role === "context" /* CONTEXT */) return;
419
- currentCluster.push(labelB);
420
- processed.add(labelB);
421
- }
468
+ if (!areSyncSignificant(metaA, metaB)) return;
469
+ if (metaA.role === "context" /* CONTEXT */ && metaB.role === "context" /* CONTEXT */) return;
470
+ currentCluster.push(labelB);
471
+ processed.add(labelB);
422
472
  });
423
473
  if (currentCluster.length > 1) clusters.push(currentCluster);
424
474
  else independentCount++;
@@ -494,7 +544,7 @@ var displayRedundancyAlert = (labelA, metaA, labelB, metaB, sim) => {
494
544
  const alertType = isContextMirror ? "CONTEXT MIRRORING" : isStoreMirror ? "STORE MIRRORING" : "DUPLICATE STATE";
495
545
  console.group(`%c \u264A BASIS | ${alertType} `, STYLES.headerProblem);
496
546
  console.log(`%c\u{1F4CD} Location: %c${infoA.file}`, STYLES.bold, STYLES.location);
497
- console.log(`%cIssue:%c ${infoA.name} and ${infoB.name} are synchronized (${(sim * 100).toFixed(0)}%).`, STYLES.bold, "");
547
+ console.log(`%cIssue:%c ${infoA.name} and ${infoB.name} overlapped on ${(sim * 100).toFixed(0)}% of aligned updates.`, STYLES.bold, "");
498
548
  if (isContextMirror || isStoreMirror) {
499
549
  const sourceType = isStoreMirror ? "External Store" : "Global Context";
500
550
  console.log(
@@ -672,7 +722,6 @@ var displayBootLog = (windowSize) => {
672
722
  };
673
723
 
674
724
  // src/core/analysis.ts
675
- var CAUSAL_MARGIN = 0.05;
676
725
  var isEventDriven = (label, graph) => {
677
726
  for (const [parent, targets] of graph.entries()) {
678
727
  if (parent.startsWith("Event_Tick_") && targets.has(label)) {
@@ -682,28 +731,33 @@ var isEventDriven = (label, graph) => {
682
731
  return false;
683
732
  };
684
733
  var calculateAllSimilarities = (entryA, entryB) => {
685
- const sync = calculateSimilarityCircular(
734
+ const { kSync, kALeadsB, kBLeadsA, densityA, densityB } = countOverlapsCircular(
686
735
  entryA.meta.buffer,
687
736
  entryA.meta.head,
688
737
  entryB.meta.buffer,
689
- entryB.meta.head,
690
- 0
738
+ entryB.meta.head
691
739
  );
692
- const bA = calculateSimilarityCircular(
693
- entryA.meta.buffer,
694
- entryA.meta.head,
695
- entryB.meta.buffer,
696
- entryB.meta.head,
697
- 1
698
- );
699
- const aB = calculateSimilarityCircular(
700
- entryA.meta.buffer,
701
- entryA.meta.head,
702
- entryB.meta.buffer,
703
- entryB.meta.head,
704
- -1
705
- );
706
- return { sync, bA, aB, max: Math.max(sync, bA, aB) };
740
+ const sync = cosineFromOverlap(kSync, densityA, densityB);
741
+ const bA = cosineFromOverlap(kALeadsB, densityA, densityB);
742
+ const aB = cosineFromOverlap(kBLeadsA, densityA, densityB);
743
+ const max = Math.max(sync, bA, aB);
744
+ const windowSize = entryA.meta.buffer.length;
745
+ const significantSync = isSignificantOverlap(kSync, densityA, densityB, windowSize);
746
+ const kLead = Math.max(kALeadsB, kBLeadsA);
747
+ const significantLead = isSignificantOverlap(kLead, densityA, densityB, windowSize) && kLead >= kSync + 1;
748
+ return {
749
+ sync,
750
+ bA,
751
+ aB,
752
+ max,
753
+ kSync,
754
+ kALeadsB,
755
+ kBLeadsA,
756
+ densityA,
757
+ densityB,
758
+ significantSync,
759
+ significantLead
760
+ };
707
761
  };
708
762
  var shouldSkipComparison = (entryA, entryB, dirtyLabels2) => {
709
763
  if (entryA.label === entryB.label) return true;
@@ -728,52 +782,51 @@ var detectRedundancy = (entryA, entryB, similarities, redundantSet, violationMap
728
782
  const roleA = entryA.meta.role;
729
783
  const roleB = entryB.meta.role;
730
784
  if (isGlobalSource(roleA) && isGlobalSource(roleB)) return;
731
- if (entryA.meta.density < 2 || entryB.meta.density < 2) return;
785
+ if (similarities.densityA < 2 || similarities.densityB < 2) return;
786
+ const score = similarities.sync;
732
787
  if (roleA === "local" /* LOCAL */ && isGlobalSource(roleB)) {
733
788
  redundantSet.add(entryA.label);
734
- pushViolation(violationMap, entryB.label, { type: "context_mirror", target: entryA.label, similarity: similarities.max });
735
- displayRedundancyAlert(entryA.label, entryA.meta, entryB.label, entryB.meta, similarities.max);
789
+ pushViolation(violationMap, entryB.label, { type: "context_mirror", target: entryA.label, similarity: score });
790
+ displayRedundancyAlert(entryA.label, entryA.meta, entryB.label, entryB.meta, score);
736
791
  } else if (isGlobalSource(roleA) && roleB === "local" /* LOCAL */) {
737
792
  redundantSet.add(entryB.label);
738
- pushViolation(violationMap, entryA.label, { type: "context_mirror", target: entryB.label, similarity: similarities.max });
739
- displayRedundancyAlert(entryB.label, entryB.meta, entryA.label, entryA.meta, similarities.max);
793
+ pushViolation(violationMap, entryA.label, { type: "context_mirror", target: entryB.label, similarity: score });
794
+ displayRedundancyAlert(entryB.label, entryB.meta, entryA.label, entryA.meta, score);
740
795
  } else if (roleA === "local" /* LOCAL */ && roleB === "local" /* LOCAL */) {
741
796
  redundantSet.add(entryA.label);
742
797
  redundantSet.add(entryB.label);
743
- pushViolation(violationMap, entryA.label, { type: "duplicate_state", target: entryB.label, similarity: similarities.max });
744
- pushViolation(violationMap, entryB.label, { type: "duplicate_state", target: entryA.label, similarity: similarities.max });
745
- displayRedundancyAlert(entryA.label, entryA.meta, entryB.label, entryB.meta, similarities.max);
798
+ pushViolation(violationMap, entryA.label, { type: "duplicate_state", target: entryB.label, similarity: score });
799
+ pushViolation(violationMap, entryB.label, { type: "duplicate_state", target: entryA.label, similarity: score });
800
+ displayRedundancyAlert(entryA.label, entryA.meta, entryB.label, entryB.meta, score);
746
801
  }
747
802
  };
748
803
  var detectCausalLeak = (entryA, entryB, similarities, violationMap, graph) => {
749
804
  if (entryA.isVolatile || entryB.isVolatile) return;
750
- if (similarities.max - similarities.sync < CAUSAL_MARGIN) return;
751
805
  const addLeak = (source, target) => {
752
806
  if (isEventDriven(target, graph)) return;
753
- if (!violationMap.has(source)) {
754
- violationMap.set(source, []);
755
- }
756
- violationMap.get(source).push({ type: "causal_leak", target });
807
+ pushViolation(violationMap, source, { type: "causal_leak", target });
757
808
  const sourceEntry = source === entryA.label ? entryA : entryB;
758
809
  const targetEntry = source === entryA.label ? entryB : entryA;
759
810
  displayCausalHint(target, targetEntry.meta, source, sourceEntry.meta);
760
811
  };
761
- if (similarities.bA === similarities.max) {
812
+ if (similarities.kALeadsB >= similarities.kBLeadsA) {
762
813
  addLeak(entryA.label, entryB.label);
763
- } else if (similarities.aB === similarities.max) {
814
+ } else {
764
815
  addLeak(entryB.label, entryA.label);
765
816
  }
766
817
  };
767
818
  var detectSubspaceOverlap = (dirtyEntries, allEntries, redundantSet, dirtyLabels2, graph) => {
768
- let compCount = 0;
769
819
  const violationMap = /* @__PURE__ */ new Map();
820
+ let compCount = 0;
770
821
  for (const entryA of dirtyEntries) {
771
822
  for (const entryB of allEntries) {
772
823
  if (shouldSkipComparison(entryA, entryB, dirtyLabels2)) continue;
773
824
  compCount++;
774
825
  const similarities = calculateAllSimilarities(entryA, entryB);
775
- if (similarities.max > SIMILARITY_THRESHOLD) {
826
+ if (similarities.significantSync) {
776
827
  detectRedundancy(entryA, entryB, similarities, redundantSet, violationMap);
828
+ }
829
+ if (similarities.significantLead) {
777
830
  detectCausalLeak(entryA, entryB, similarities, violationMap, graph);
778
831
  }
779
832
  }
@@ -1032,9 +1085,9 @@ var beginEffectTracking = (l) => {
1032
1085
  var endEffectTracking = () => {
1033
1086
  instance.currentEffectSource = null;
1034
1087
  };
1035
- var printBasisHealthReport = (threshold = 0.5) => {
1088
+ var printBasisHealthReport = () => {
1036
1089
  if (!instance.config.debug) return;
1037
- displayHealthReport(instance.history, threshold, instance.violationMap);
1090
+ displayHealthReport(instance.history, instance.violationMap);
1038
1091
  };
1039
1092
  var getBasisMetrics = () => ({
1040
1093
  engine: "v0.6.x",