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/{chunk-EJXGN76H.mjs → chunk-WMHY2C6D.mjs} +138 -85
- package/dist/chunk-WMHY2C6D.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +137 -84
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/integrations/zustand.js +137 -84
- package/dist/integrations/zustand.js.map +1 -1
- package/dist/integrations/zustand.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-EJXGN76H.mjs.map +0 -1
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
|
|
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
|
-
|
|
71
|
-
const
|
|
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
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
84
|
-
return dot / (Math.sqrt(magA) * Math.sqrt(magB));
|
|
148
|
+
return { kSync, kALeadsB, kBLeadsA, densityA, densityB };
|
|
85
149
|
};
|
|
86
|
-
var
|
|
87
|
-
|
|
88
|
-
|
|
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,
|
|
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 (
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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}
|
|
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
|
|
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
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
);
|
|
699
|
-
const
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
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 (
|
|
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:
|
|
735
|
-
displayRedundancyAlert(entryA.label, entryA.meta, entryB.label, entryB.meta,
|
|
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:
|
|
739
|
-
displayRedundancyAlert(entryB.label, entryB.meta, entryA.label, entryA.meta,
|
|
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:
|
|
744
|
-
pushViolation(violationMap, entryB.label, { type: "duplicate_state", target: entryA.label, similarity:
|
|
745
|
-
displayRedundancyAlert(entryA.label, entryA.meta, entryB.label, entryB.meta,
|
|
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
|
-
|
|
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.
|
|
812
|
+
if (similarities.kALeadsB >= similarities.kBLeadsA) {
|
|
762
813
|
addLeak(entryA.label, entryB.label);
|
|
763
|
-
} else
|
|
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.
|
|
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 = (
|
|
1088
|
+
var printBasisHealthReport = () => {
|
|
1036
1089
|
if (!instance.config.debug) return;
|
|
1037
|
-
displayHealthReport(instance.history,
|
|
1090
|
+
displayHealthReport(instance.history, instance.violationMap);
|
|
1038
1091
|
};
|
|
1039
1092
|
var getBasisMetrics = () => ({
|
|
1040
1093
|
engine: "v0.6.x",
|