react-state-basis 0.6.5 → 0.6.7
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 +2 -2
- package/dist/{chunk-EJXGN76H.mjs → chunk-KIXM6YRX.mjs} +307 -212
- package/dist/chunk-KIXM6YRX.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +306 -211
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/integrations/zustand.js +301 -210
- 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
|
@@ -1,30 +1,89 @@
|
|
|
1
|
+
// src/core/constants.ts
|
|
2
|
+
var WINDOW_SIZE = 50;
|
|
3
|
+
var PAIR_RARITY_TARGET = 0.01;
|
|
4
|
+
var RELATIVE_OVERLAP_FLOOR = 0.65;
|
|
5
|
+
var LOOP_THRESHOLD = 150;
|
|
6
|
+
var VOLATILITY_THRESHOLD = 25;
|
|
7
|
+
var INSTANCE_SEP = "##";
|
|
8
|
+
|
|
1
9
|
// src/core/math.ts
|
|
2
|
-
var
|
|
10
|
+
var choose = (n, k) => {
|
|
11
|
+
if (k < 0 || k > n) return 0;
|
|
12
|
+
k = Math.min(k, n - k);
|
|
13
|
+
let result = 1;
|
|
14
|
+
for (let i = 0; i < k; i++) {
|
|
15
|
+
result = result * (n - i) / (i + 1);
|
|
16
|
+
}
|
|
17
|
+
return result;
|
|
18
|
+
};
|
|
19
|
+
var hypergeomPMF = (k, n1, m1, N) => {
|
|
20
|
+
const total = choose(N, n1);
|
|
21
|
+
if (total === 0) return 0;
|
|
22
|
+
return choose(m1, k) * choose(N - m1, n1 - k) / total;
|
|
23
|
+
};
|
|
24
|
+
var hypergeomUpperTailP = (kStart, n1, m1, N) => {
|
|
25
|
+
const kMax = Math.min(n1, m1);
|
|
26
|
+
let p = 0;
|
|
27
|
+
for (let k = Math.max(0, kStart); k <= kMax; k++) {
|
|
28
|
+
p += hypergeomPMF(k, n1, m1, N);
|
|
29
|
+
}
|
|
30
|
+
return p;
|
|
31
|
+
};
|
|
32
|
+
var minOverlapForRarity = (n1, m1, N, targetP) => {
|
|
33
|
+
const kMax = Math.min(n1, m1);
|
|
34
|
+
for (let k = 0; k <= kMax; k++) {
|
|
35
|
+
if (hypergeomUpperTailP(k, n1, m1, N) <= targetP) return k;
|
|
36
|
+
}
|
|
37
|
+
return kMax + 1;
|
|
38
|
+
};
|
|
39
|
+
var overlapThresholdCache = /* @__PURE__ */ new Map();
|
|
40
|
+
var getMinOverlap = (densityA, densityB, windowSize) => {
|
|
41
|
+
const lo = Math.min(densityA, densityB) | 0;
|
|
42
|
+
const hi = Math.max(densityA, densityB) | 0;
|
|
43
|
+
const key = `${lo}_${hi}_${windowSize}`;
|
|
44
|
+
let cached = overlapThresholdCache.get(key);
|
|
45
|
+
if (cached === void 0) {
|
|
46
|
+
const rare = minOverlapForRarity(lo, hi, windowSize, PAIR_RARITY_TARGET);
|
|
47
|
+
const floor = Math.ceil(RELATIVE_OVERLAP_FLOOR * lo);
|
|
48
|
+
cached = Math.max(rare, floor);
|
|
49
|
+
overlapThresholdCache.set(key, cached);
|
|
50
|
+
}
|
|
51
|
+
return cached;
|
|
52
|
+
};
|
|
53
|
+
var isSignificantOverlap = (overlap, densityA, densityB, windowSize) => {
|
|
54
|
+
if (densityA < 2 || densityB < 2) return false;
|
|
55
|
+
return overlap >= getMinOverlap(densityA, densityB, windowSize);
|
|
56
|
+
};
|
|
57
|
+
var countOverlapsCircular = (bufferA, headA, bufferB, headB) => {
|
|
3
58
|
const L = bufferA.length;
|
|
4
|
-
|
|
5
|
-
const
|
|
59
|
+
const offSync = ((headB - headA) % L + L) % L;
|
|
60
|
+
const offALeadsB = ((headB - headA + 1) % L + L) % L;
|
|
61
|
+
const offBLeadsA = ((headB - headA - 1) % L + L) % L;
|
|
62
|
+
let kSync = 0;
|
|
63
|
+
let kALeadsB = 0;
|
|
64
|
+
let kBLeadsA = 0;
|
|
65
|
+
let densityA = 0;
|
|
66
|
+
let densityB = 0;
|
|
6
67
|
for (let i = 0; i < L; i++) {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
68
|
+
const a = bufferA[i] ? 1 : 0;
|
|
69
|
+
const b = bufferB[i] ? 1 : 0;
|
|
70
|
+
densityA += a;
|
|
71
|
+
densityB += b;
|
|
72
|
+
let iSync = i + offSync;
|
|
73
|
+
if (iSync >= L) iSync -= L;
|
|
74
|
+
let iALeadsB = i + offALeadsB;
|
|
75
|
+
if (iALeadsB >= L) iALeadsB -= L;
|
|
76
|
+
let iBLeadsA = i + offBLeadsA;
|
|
77
|
+
if (iBLeadsA >= L) iBLeadsA -= L;
|
|
78
|
+
if (a && bufferB[iSync]) kSync++;
|
|
79
|
+
if (a && bufferB[iALeadsB]) kALeadsB++;
|
|
80
|
+
if (a && bufferB[iBLeadsA]) kBLeadsA++;
|
|
16
81
|
}
|
|
17
|
-
|
|
18
|
-
return dot / (Math.sqrt(magA) * Math.sqrt(magB));
|
|
82
|
+
return { kSync, kALeadsB, kBLeadsA, densityA, densityB };
|
|
19
83
|
};
|
|
20
|
-
var
|
|
21
|
-
|
|
22
|
-
|
|
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));
|
|
84
|
+
var cosineFromOverlap = (overlap, densityA, densityB) => {
|
|
85
|
+
if (densityA <= 0 || densityB <= 0) return 0;
|
|
86
|
+
return overlap / Math.sqrt(densityA * densityB);
|
|
28
87
|
};
|
|
29
88
|
|
|
30
89
|
// src/core/graph.ts
|
|
@@ -83,13 +142,6 @@ var groupEventSources = (nodes, edges) => {
|
|
|
83
142
|
return groups.sort((a, b) => b.edges.length - a.edges.length || b.occurrences - a.occurrences);
|
|
84
143
|
};
|
|
85
144
|
|
|
86
|
-
// src/core/constants.ts
|
|
87
|
-
var WINDOW_SIZE = 50;
|
|
88
|
-
var SIMILARITY_THRESHOLD = 0.88;
|
|
89
|
-
var LOOP_THRESHOLD = 150;
|
|
90
|
-
var VOLATILITY_THRESHOLD = 25;
|
|
91
|
-
var INSTANCE_SEP = "##";
|
|
92
|
-
|
|
93
145
|
// src/core/label.ts
|
|
94
146
|
var stripInstance = (label) => {
|
|
95
147
|
const idx = label.indexOf(INSTANCE_SEP);
|
|
@@ -203,32 +255,22 @@ var LAST_LOG_TIMES = /* @__PURE__ */ new Map();
|
|
|
203
255
|
var LOG_COOLDOWN = 3e3;
|
|
204
256
|
var THEME = {
|
|
205
257
|
identity: "#6C5CE7",
|
|
206
|
-
// Purple (Brand)
|
|
207
258
|
problem: "#D63031",
|
|
208
|
-
// Red (Bugs)
|
|
209
259
|
solution: "#FBC531",
|
|
210
|
-
// Yellow (Fixes)
|
|
211
260
|
context: "#0984E3",
|
|
212
|
-
// Blue (Locations)
|
|
213
261
|
muted: "#9AA0A6",
|
|
214
|
-
// Gray (Metadata)
|
|
215
262
|
border: "#2E2E35",
|
|
216
263
|
success: "#00b894"
|
|
217
|
-
// Green (Good Score)
|
|
218
264
|
};
|
|
219
265
|
var STYLES = {
|
|
220
|
-
// Structure
|
|
221
266
|
basis: `background: ${THEME.identity}; color: white; font-weight: bold; padding: 2px 6px; border-radius: 3px;`,
|
|
222
267
|
headerIdentity: `background: ${THEME.identity}; color: white; font-weight: bold; padding: 4px 8px; border-radius: 4px;`,
|
|
223
268
|
headerProblem: `background: ${THEME.problem}; color: white; font-weight: bold; padding: 4px 8px; border-radius: 4px;`,
|
|
224
269
|
version: `background: #a29bfe; color: #2d3436; padding: 2px 6px; border-radius: 3px; margin-left: -4px;`,
|
|
225
|
-
// Actions
|
|
226
270
|
actionLabel: `color: ${THEME.solution}; font-weight: bold;`,
|
|
227
271
|
actionPill: `color: ${THEME.solution}; font-weight: bold; border: 1px solid ${THEME.solution}; padding: 0 4px; border-radius: 3px;`,
|
|
228
|
-
// Context
|
|
229
272
|
impactLabel: `color: ${THEME.context}; font-weight: bold;`,
|
|
230
273
|
location: `color: ${THEME.context}; font-family: monospace; font-weight: bold;`,
|
|
231
|
-
// Text
|
|
232
274
|
subText: `color: ${THEME.muted}; font-size: 11px;`,
|
|
233
275
|
bold: "font-weight: bold;",
|
|
234
276
|
label: "background: #dfe6e9; color: #2d3436; padding: 0 4px; border-radius: 3px; font-family: monospace; font-weight: bold; border: 1px solid #b2bec3;"
|
|
@@ -243,97 +285,95 @@ var shouldLog = (key) => {
|
|
|
243
285
|
return false;
|
|
244
286
|
};
|
|
245
287
|
var isBooleanLike = (name) => /^(is|has|can|should|did|will|show|hide)(?=[A-Z_])/.test(name);
|
|
288
|
+
var displayName = (raw) => {
|
|
289
|
+
const { name } = parseLabel(raw);
|
|
290
|
+
return name.replace(/:\d+$/, "");
|
|
291
|
+
};
|
|
292
|
+
var areSyncSignificant = (metaA, metaB) => {
|
|
293
|
+
const { kSync, densityA, densityB } = countOverlapsCircular(
|
|
294
|
+
metaA.buffer,
|
|
295
|
+
metaA.head,
|
|
296
|
+
metaB.buffer,
|
|
297
|
+
metaB.head
|
|
298
|
+
);
|
|
299
|
+
return isSignificantOverlap(kSync, densityA, densityB, metaA.buffer.length);
|
|
300
|
+
};
|
|
246
301
|
var getSuggestedFix = (issue, info) => {
|
|
247
302
|
if (issue.label.includes("Global Event")) {
|
|
248
|
-
return `
|
|
303
|
+
return `One interaction is updating state in several places. If that is really one transition, put it in one %cstore / reducer%c. If it is intentional, ignore this.`;
|
|
249
304
|
}
|
|
250
305
|
const violations = issue.violations || [];
|
|
251
306
|
const leaks = violations.filter((v) => v.type === "causal_leak");
|
|
252
307
|
const mirrors = violations.filter((v) => v.type === "context_mirror");
|
|
253
308
|
const duplicates = violations.filter((v) => v.type === "duplicate_state");
|
|
254
309
|
if (mirrors.length > 0) {
|
|
255
|
-
return `
|
|
310
|
+
return `A local hook is only tracking context or a store. If it is not a draft, delete it and read the %ccontext / store%c in render.`;
|
|
256
311
|
}
|
|
257
312
|
if (leaks.length > 0) {
|
|
258
|
-
const targetName =
|
|
313
|
+
const targetName = displayName(leaks[0].target);
|
|
259
314
|
if (issue.label.includes("effect")) {
|
|
260
|
-
return `
|
|
315
|
+
return `An effect is calling setState on ${targetName}, which paints again. If you can compute ${targetName} while rendering, drop the %ceffect%c.`;
|
|
261
316
|
}
|
|
262
|
-
return
|
|
317
|
+
return `${info.name} updates, then ${targetName} updates on the next frame. If they are one fact, write them in the same %csetState%c.`;
|
|
263
318
|
}
|
|
264
319
|
if (duplicates.length > 0) {
|
|
265
320
|
if (isBooleanLike(info.name)) {
|
|
266
|
-
return `
|
|
321
|
+
return `Several flags move together. One %cstatus%c value avoids impossible combinations.`;
|
|
267
322
|
}
|
|
268
|
-
return `
|
|
323
|
+
return `These hooks move together. If one is just the other in another shape, compute it while %crendering%c.`;
|
|
269
324
|
}
|
|
270
325
|
if (issue.metric === "density") {
|
|
271
|
-
return `
|
|
326
|
+
return `This hook updates faster than a frame. %cDebounce%c it or keep it in a ref if the UI does not need every pulse.`;
|
|
272
327
|
}
|
|
273
|
-
return `
|
|
328
|
+
return `Inspect ${info.name} and what updates with it.`;
|
|
274
329
|
};
|
|
275
|
-
var displayHealthReport = (history2,
|
|
330
|
+
var displayHealthReport = (history2, violationMap) => {
|
|
276
331
|
if (!isWeb) return;
|
|
277
332
|
const entries = Array.from(history2.entries());
|
|
278
333
|
if (entries.length === 0) return;
|
|
279
334
|
const topIssues = identifyTopIssues(instance.graph, history2, instance.redundantLabels, violationMap);
|
|
280
|
-
console.group(`%c
|
|
335
|
+
console.group(`%c BASIS | report `, STYLES.headerIdentity);
|
|
281
336
|
if (topIssues.length > 0) {
|
|
282
|
-
console.log(
|
|
283
|
-
`%c\u{1F3AF} REFACTOR PRIORITIES %c(PRIME MOVERS)`,
|
|
284
|
-
`font-weight: bold; color: ${THEME.identity}; margin-top: 10px;`,
|
|
285
|
-
`font-weight: normal; color: ${THEME.muted}; font-style: italic;`
|
|
286
|
-
);
|
|
337
|
+
console.log(`%cStart here`, `font-weight: bold; color: ${THEME.identity}; margin-top: 10px;`);
|
|
287
338
|
topIssues.forEach((issue, idx) => {
|
|
288
339
|
const info = parseLabel(issue.label);
|
|
289
|
-
const icon = issue.metric === "influence" ? "\
|
|
340
|
+
const icon = issue.metric === "influence" ? "\u2192" : "\u2022";
|
|
290
341
|
const pColor = idx === 0 ? THEME.problem : idx === 1 ? THEME.solution : THEME.identity;
|
|
291
|
-
let displayName = info.name;
|
|
292
|
-
let displayFile = info.file;
|
|
293
|
-
if (issue.label.includes("Global Event")) {
|
|
294
|
-
displayName = info.name;
|
|
295
|
-
displayFile = info.file;
|
|
296
|
-
}
|
|
297
342
|
console.group(
|
|
298
|
-
` %c${idx + 1}%c ${icon} ${displayName} %c(${
|
|
343
|
+
` %c${idx + 1}%c ${icon} ${displayName(issue.label)} %c(${info.file})`,
|
|
299
344
|
`background: ${pColor}; color: ${idx === 1 ? "black" : "white"}; border-radius: 50%; padding: 0 5px;`,
|
|
300
345
|
"font-family: monospace; font-weight: 700;",
|
|
301
|
-
`color: ${THEME.muted}; font-size: 10px; font-weight: normal
|
|
346
|
+
`color: ${THEME.muted}; font-size: 10px; font-weight: normal;`
|
|
302
347
|
);
|
|
303
|
-
console.log(`%c${issue.reason}`, `color: ${THEME.muted}
|
|
348
|
+
console.log(`%c${issue.reason}`, `color: ${THEME.muted};`);
|
|
304
349
|
if (issue.violations.length > 0) {
|
|
305
350
|
const byFile = /* @__PURE__ */ new Map();
|
|
306
351
|
issue.violations.forEach((v) => {
|
|
307
352
|
if (issue.label.includes("Global Event") && v.type === "context_mirror") return;
|
|
308
353
|
const { file, name } = parseLabel(v.target);
|
|
309
354
|
if (!byFile.has(file)) byFile.set(file, []);
|
|
310
|
-
byFile.get(file).push(name);
|
|
355
|
+
byFile.get(file).push(name.replace(/:\d+$/, ""));
|
|
311
356
|
});
|
|
312
357
|
const impactParts = [];
|
|
313
358
|
byFile.forEach((vars, file) => {
|
|
314
|
-
|
|
315
|
-
impactParts.push(`${file} (${varList})`);
|
|
359
|
+
impactParts.push(`${file} (${vars.join(", ")})`);
|
|
316
360
|
});
|
|
317
361
|
if (impactParts.length > 0) {
|
|
318
|
-
console.log(`%
|
|
362
|
+
console.log(`%cAlso updates: %c${impactParts.join(" \xB7 ")}`, STYLES.impactLabel, "");
|
|
319
363
|
}
|
|
320
364
|
}
|
|
321
365
|
const fix = getSuggestedFix(issue, info);
|
|
322
366
|
const fixParts = fix.split("%c");
|
|
323
367
|
if (fixParts.length === 3) {
|
|
324
368
|
console.log(
|
|
325
|
-
`%
|
|
369
|
+
`%cTry: %c${fixParts[0]}%c${fixParts[1]}%c${fixParts[2]}`,
|
|
326
370
|
STYLES.actionLabel,
|
|
327
371
|
"",
|
|
328
372
|
STYLES.actionPill,
|
|
329
373
|
""
|
|
330
374
|
);
|
|
331
375
|
} else {
|
|
332
|
-
console.log(
|
|
333
|
-
`%cSolution: %c${fix}`,
|
|
334
|
-
STYLES.actionLabel,
|
|
335
|
-
""
|
|
336
|
-
);
|
|
376
|
+
console.log(`%cTry: %c${fix}`, STYLES.actionLabel, "");
|
|
337
377
|
}
|
|
338
378
|
console.groupEnd();
|
|
339
379
|
});
|
|
@@ -348,145 +388,179 @@ var displayHealthReport = (history2, threshold, violationMap) => {
|
|
|
348
388
|
processed.add(labelA);
|
|
349
389
|
entries.forEach(([labelB, metaB]) => {
|
|
350
390
|
if (labelA === labelB || processed.has(labelB)) return;
|
|
351
|
-
if (
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
391
|
+
if (!areSyncSignificant(metaA, metaB)) return;
|
|
392
|
+
if (metaA.role === "context" /* CONTEXT */ && metaB.role === "context" /* CONTEXT */) return;
|
|
393
|
+
currentCluster.push(labelB);
|
|
394
|
+
processed.add(labelB);
|
|
356
395
|
});
|
|
357
396
|
if (currentCluster.length > 1) clusters.push(currentCluster);
|
|
358
397
|
else independentCount++;
|
|
359
398
|
});
|
|
360
399
|
const totalVars = entries.length;
|
|
361
|
-
const redundancyScore = (independentCount + clusters.length) / totalVars * 100;
|
|
362
|
-
let internalEdges = 0;
|
|
363
|
-
instance.graph.forEach((targets, source) => {
|
|
364
|
-
if (source.startsWith("Event_Tick_")) return;
|
|
365
|
-
internalEdges += targets.size;
|
|
366
|
-
});
|
|
367
|
-
const causalPenalty = internalEdges / totalVars * 100;
|
|
368
|
-
let healthScore = redundancyScore - causalPenalty;
|
|
369
|
-
if (healthScore < 0) healthScore = 0;
|
|
370
|
-
const scoreColor = healthScore > 85 ? THEME.success : THEME.problem;
|
|
371
400
|
console.log(
|
|
372
|
-
`%
|
|
373
|
-
STYLES.
|
|
374
|
-
`color: ${scoreColor}; font-weight: bold;`
|
|
401
|
+
`%c${independentCount + clusters.length} of ${totalVars} instrumented hooks look independent in this window.`,
|
|
402
|
+
STYLES.subText
|
|
375
403
|
);
|
|
376
|
-
console.log(`%cSources of Truth: ${independentCount + clusters.length}/${totalVars} | Causal Leaks: ${internalEdges}`, STYLES.subText);
|
|
377
404
|
if (clusters.length > 0) {
|
|
378
|
-
console.log(
|
|
405
|
+
console.log(
|
|
406
|
+
`%c${clusters.length} group${clusters.length === 1 ? "" : "s"} that keep updating together:`,
|
|
407
|
+
`font-weight: bold; color: ${THEME.problem}; margin-top: 10px;`
|
|
408
|
+
);
|
|
379
409
|
clusters.forEach((cluster, idx) => {
|
|
380
410
|
const clusterMetas = cluster.map((l) => ({
|
|
381
411
|
label: l,
|
|
382
412
|
meta: history2.get(l),
|
|
383
|
-
name:
|
|
413
|
+
name: displayName(l)
|
|
384
414
|
}));
|
|
385
415
|
const hasCtx = clusterMetas.some(
|
|
386
416
|
(c) => c.meta.role === "context" /* CONTEXT */ || c.meta.role === "store" /* STORE */
|
|
387
417
|
);
|
|
388
|
-
const names = clusterMetas.map((c) =>
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
418
|
+
const names = clusterMetas.map((c) => c.name).join(", ");
|
|
419
|
+
console.group(
|
|
420
|
+
` %c${idx + 1}%c ${names}`,
|
|
421
|
+
`background: ${THEME.problem}; color: white; border-radius: 50%; padding: 0 5px;`,
|
|
422
|
+
"font-family: monospace; font-weight: bold;"
|
|
423
|
+
);
|
|
393
424
|
if (hasCtx) {
|
|
394
425
|
const hasStore = clusterMetas.some((c) => c.meta.role === "store" /* STORE */);
|
|
395
|
-
const sourceType = hasStore ? "
|
|
396
|
-
console.log(
|
|
397
|
-
console.log(
|
|
426
|
+
const sourceType = hasStore ? "a store" : "context";
|
|
427
|
+
console.log(`A local hook is only following ${sourceType}.`);
|
|
428
|
+
console.log(
|
|
429
|
+
`%cTry:%c Read ${sourceType} in render if the local value is not a draft.`,
|
|
430
|
+
STYLES.actionLabel,
|
|
431
|
+
""
|
|
432
|
+
);
|
|
398
433
|
} else {
|
|
399
|
-
const boolKeywords = [
|
|
434
|
+
const boolKeywords = [
|
|
435
|
+
"is",
|
|
436
|
+
"has",
|
|
437
|
+
"can",
|
|
438
|
+
"should",
|
|
439
|
+
"loading",
|
|
440
|
+
"success",
|
|
441
|
+
"error",
|
|
442
|
+
"active",
|
|
443
|
+
"enabled",
|
|
444
|
+
"open",
|
|
445
|
+
"visible"
|
|
446
|
+
];
|
|
400
447
|
const boolCount = clusterMetas.filter(
|
|
401
448
|
(c) => boolKeywords.some((kw) => c.name.toLowerCase().startsWith(kw))
|
|
402
449
|
).length;
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
console.log(
|
|
406
|
-
|
|
450
|
+
if (cluster.length > 2 && boolCount / cluster.length > 0.5) {
|
|
451
|
+
console.log(`These flags move together.`);
|
|
452
|
+
console.log(
|
|
453
|
+
`%cTry:%c One %cstatus%c instead of several booleans.`,
|
|
454
|
+
STYLES.actionLabel,
|
|
455
|
+
"",
|
|
456
|
+
STYLES.actionPill,
|
|
457
|
+
""
|
|
458
|
+
);
|
|
407
459
|
} else if (cluster.length > 2) {
|
|
408
|
-
console.log(
|
|
409
|
-
console.log(
|
|
460
|
+
console.log(`These hooks move on the same frames. Often the same click or fetch.`);
|
|
461
|
+
console.log(
|
|
462
|
+
`%cTry:%c Leave it if that is intentional. Otherwise one %creducer%c.`,
|
|
463
|
+
STYLES.actionLabel,
|
|
464
|
+
"",
|
|
465
|
+
STYLES.actionPill,
|
|
466
|
+
""
|
|
467
|
+
);
|
|
410
468
|
} else {
|
|
411
|
-
console.log(
|
|
412
|
-
console.log(
|
|
469
|
+
console.log(`These two hooks keep updating in the same frame.`);
|
|
470
|
+
console.log(
|
|
471
|
+
`%cTry:%c If one is derived, compute it while %crendering%c.`,
|
|
472
|
+
STYLES.actionLabel,
|
|
473
|
+
"",
|
|
474
|
+
STYLES.actionPill,
|
|
475
|
+
""
|
|
476
|
+
);
|
|
413
477
|
}
|
|
414
478
|
}
|
|
415
479
|
console.groupEnd();
|
|
416
480
|
});
|
|
417
481
|
} else {
|
|
418
|
-
console.log(
|
|
482
|
+
console.log(
|
|
483
|
+
"%cNo hooks were updating in lockstep in this window.",
|
|
484
|
+
`color: ${THEME.success}; font-weight: bold;`
|
|
485
|
+
);
|
|
419
486
|
}
|
|
420
487
|
console.groupEnd();
|
|
421
488
|
};
|
|
422
|
-
var displayRedundancyAlert = (labelA, metaA, labelB, metaB,
|
|
489
|
+
var displayRedundancyAlert = (labelA, metaA, labelB, metaB, overlap) => {
|
|
423
490
|
if (!isWeb || !shouldLog(`redundant-${labelA}-${labelB}`)) return;
|
|
424
491
|
const infoA = parseLabel(labelA);
|
|
425
|
-
const
|
|
492
|
+
const nameA = displayName(labelA);
|
|
493
|
+
const nameB = displayName(labelB);
|
|
426
494
|
const isContextMirror = metaA.role === "local" /* LOCAL */ && metaB.role === "context" /* CONTEXT */ || metaB.role === "local" /* LOCAL */ && metaA.role === "context" /* CONTEXT */;
|
|
427
495
|
const isStoreMirror = metaA.role === "local" /* LOCAL */ && metaB.role === "store" /* STORE */ || metaB.role === "local" /* LOCAL */ && metaA.role === "store" /* STORE */;
|
|
428
|
-
const alertType = isContextMirror ? "
|
|
429
|
-
|
|
430
|
-
console.
|
|
431
|
-
console.log(`%
|
|
496
|
+
const alertType = isContextMirror ? "local state follows context" : isStoreMirror ? "local state follows a store" : "hooks moving together";
|
|
497
|
+
const times = overlap.kSync === 1 ? "time" : "times";
|
|
498
|
+
console.group(`%c BASIS | ${alertType} `, STYLES.headerProblem);
|
|
499
|
+
console.log(`%c${infoA.file}`, STYLES.location);
|
|
500
|
+
console.log(
|
|
501
|
+
`%c${nameA}%c and %c${nameB}%c updated in the same frame ${overlap.kSync} ${times}.`,
|
|
502
|
+
STYLES.label,
|
|
503
|
+
"",
|
|
504
|
+
STYLES.label,
|
|
505
|
+
""
|
|
506
|
+
);
|
|
432
507
|
if (isContextMirror || isStoreMirror) {
|
|
433
|
-
const sourceType = isStoreMirror ? "
|
|
508
|
+
const sourceType = isStoreMirror ? "store" : "context";
|
|
509
|
+
console.log(
|
|
510
|
+
`%cTry:%c If this is not a draft, delete the local hook and read the %c${sourceType}%c in render.`,
|
|
511
|
+
STYLES.bold,
|
|
512
|
+
"",
|
|
513
|
+
STYLES.actionPill,
|
|
514
|
+
""
|
|
515
|
+
);
|
|
516
|
+
} else if (isBooleanLike(nameA) || isBooleanLike(nameB)) {
|
|
434
517
|
console.log(
|
|
435
|
-
`%
|
|
518
|
+
`%cTry:%c One %cstatus%c instead of several flags.`,
|
|
436
519
|
STYLES.bold,
|
|
437
520
|
"",
|
|
438
521
|
STYLES.actionPill,
|
|
439
522
|
""
|
|
440
523
|
);
|
|
441
524
|
} else {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
);
|
|
452
|
-
} else {
|
|
453
|
-
console.log(
|
|
454
|
-
`%cFix:%c Redundant State detected. Derive %c${infoB.name}%c from %c${infoA.name}%c during render, or use %cuseMemo%c.`,
|
|
455
|
-
STYLES.bold,
|
|
456
|
-
"",
|
|
457
|
-
STYLES.label,
|
|
458
|
-
"",
|
|
459
|
-
STYLES.label,
|
|
460
|
-
"",
|
|
461
|
-
STYLES.actionPill,
|
|
462
|
-
""
|
|
463
|
-
);
|
|
464
|
-
}
|
|
525
|
+
console.log(
|
|
526
|
+
`%cTry:%c If %c${nameB}%c is just %c${nameA}%c in another shape, compute it while rendering.`,
|
|
527
|
+
STYLES.bold,
|
|
528
|
+
"",
|
|
529
|
+
STYLES.label,
|
|
530
|
+
"",
|
|
531
|
+
STYLES.label,
|
|
532
|
+
""
|
|
533
|
+
);
|
|
465
534
|
}
|
|
466
535
|
console.groupEnd();
|
|
467
536
|
};
|
|
468
|
-
var displayCausalHint = (targetLabel,
|
|
537
|
+
var displayCausalHint = (targetLabel, _targetMeta, sourceLabel, sourceMeta) => {
|
|
469
538
|
if (!isWeb || !shouldLog(`causal-${sourceLabel}-${targetLabel}`)) return;
|
|
470
539
|
const target = parseLabel(targetLabel);
|
|
471
|
-
const
|
|
472
|
-
const
|
|
540
|
+
const sourceName = displayName(sourceLabel);
|
|
541
|
+
const targetName = displayName(targetLabel);
|
|
542
|
+
const headerType = sourceMeta.role === "context" /* CONTEXT */ ? "extra render from context" : sourceMeta.role === "store" /* STORE */ ? "extra render from a store" : "extra render";
|
|
473
543
|
const isEffect = sourceLabel.includes("effect") || sourceLabel.includes("useLayoutEffect");
|
|
474
|
-
console.groupCollapsed(`%c
|
|
475
|
-
console.log(`%c
|
|
476
|
-
console.log(
|
|
544
|
+
console.groupCollapsed(`%c BASIS | ${headerType} `, STYLES.headerProblem);
|
|
545
|
+
console.log(`%c${target.file}`, STYLES.location);
|
|
546
|
+
console.log(
|
|
547
|
+
`%c${sourceName}%c updates %c${targetName}%c on the next frame.`,
|
|
548
|
+
STYLES.label,
|
|
549
|
+
"",
|
|
550
|
+
STYLES.label,
|
|
551
|
+
""
|
|
552
|
+
);
|
|
477
553
|
if (isEffect) {
|
|
478
554
|
console.log(
|
|
479
|
-
`%
|
|
555
|
+
`%cTry:%c If %c${targetName}%c can be computed while rendering, drop the extra setState.`,
|
|
480
556
|
STYLES.bold,
|
|
481
557
|
"",
|
|
482
558
|
STYLES.label,
|
|
483
|
-
"",
|
|
484
|
-
STYLES.actionPill,
|
|
485
559
|
""
|
|
486
560
|
);
|
|
487
561
|
} else {
|
|
488
562
|
console.log(
|
|
489
|
-
`%
|
|
563
|
+
`%cTry:%c Write %c${targetName}%c in the same update as %c${sourceName}%c if they are one fact.`,
|
|
490
564
|
STYLES.bold,
|
|
491
565
|
"",
|
|
492
566
|
STYLES.label,
|
|
@@ -519,7 +593,7 @@ var displayGraphReport = (graph) => {
|
|
|
519
593
|
if (!isWeb) return;
|
|
520
594
|
if (graph.nodes.length === 0) {
|
|
521
595
|
console.log(
|
|
522
|
-
`%c
|
|
596
|
+
`%c BASIS | update graph %c(nothing recorded yet)`,
|
|
523
597
|
STYLES.headerIdentity,
|
|
524
598
|
`color: ${THEME.muted}; font-style: italic;`
|
|
525
599
|
);
|
|
@@ -538,17 +612,22 @@ var displayGraphReport = (graph) => {
|
|
|
538
612
|
occurrences: g.occurrences
|
|
539
613
|
}));
|
|
540
614
|
const groupedSourceIds = new Set(graph.eventGroups.flatMap((g) => g.sourceIds));
|
|
541
|
-
const nonEventGroups = Array.from(outgoing.keys()).filter((id) => !groupedSourceIds.has(id)).map((id) => ({
|
|
615
|
+
const nonEventGroups = Array.from(outgoing.keys()).filter((id) => !groupedSourceIds.has(id)).map((id) => ({
|
|
616
|
+
sourceIds: [id],
|
|
617
|
+
sourceNode: nodeById.get(id),
|
|
618
|
+
edges: outgoing.get(id),
|
|
619
|
+
occurrences: 1
|
|
620
|
+
}));
|
|
542
621
|
const groups = [...eventGroups, ...nonEventGroups].sort(
|
|
543
622
|
(a, b) => b.edges.length - a.edges.length || b.occurrences - a.occurrences
|
|
544
623
|
);
|
|
545
624
|
console.group(
|
|
546
|
-
`%c
|
|
625
|
+
`%c BASIS | update graph %c${graph.nodes.length} nodes \xB7 ${graph.edges.length} edges \xB7 ${groups.length} sources \xB7 last ${graph.bufferWindowSize} frames`,
|
|
547
626
|
STYLES.headerIdentity,
|
|
548
|
-
`color: ${THEME.muted}; font-weight: normal
|
|
627
|
+
`color: ${THEME.muted}; font-weight: normal;`
|
|
549
628
|
);
|
|
550
629
|
console.log(
|
|
551
|
-
`%cparent \u2192 child =
|
|
630
|
+
`%cparent \u2192 child = what we saw cause an update. (\xD7N) = times in this window. Repeat clicks with the same targets are grouped.`,
|
|
552
631
|
STYLES.subText
|
|
553
632
|
);
|
|
554
633
|
groups.forEach((group) => {
|
|
@@ -556,12 +635,12 @@ var displayGraphReport = (graph) => {
|
|
|
556
635
|
const isCtx = group.sourceNode?.role === "context" /* CONTEXT */;
|
|
557
636
|
const isFx = group.sourceNode?.role === "effect";
|
|
558
637
|
const isUnknown = group.sourceNode?.role === "unknown";
|
|
559
|
-
const icon = isEvent ? "\
|
|
638
|
+
const icon = isEvent ? "\u2022" : isCtx ? "ctx" : isFx ? "fx" : isUnknown ? "?" : "\u2022";
|
|
560
639
|
const color = isEvent ? THEME.solution : isCtx ? THEME.context : THEME.identity;
|
|
561
640
|
const fanout = group.edges.length;
|
|
562
641
|
const hits = group.occurrences;
|
|
563
642
|
const hitLabel = hits > 1 ? ` \xB7 \xD7${hits}` : "";
|
|
564
|
-
const title = isEvent ? `
|
|
643
|
+
const title = isEvent ? `click / event \xB7 ${fanout} update${fanout === 1 ? "" : "s"}${hitLabel}` : formatNode(group.sourceNode, group.sourceIds[0]);
|
|
565
644
|
console.groupCollapsed(
|
|
566
645
|
`%c${icon} %c${title}`,
|
|
567
646
|
`color: ${color};`,
|
|
@@ -573,16 +652,16 @@ var displayGraphReport = (graph) => {
|
|
|
573
652
|
const weight = edge.weight > 1 ? ` (\xD7${edge.weight})` : "";
|
|
574
653
|
if (target?.redundant) {
|
|
575
654
|
console.log(
|
|
576
|
-
`%c ${label}%c${weight} %
|
|
655
|
+
`%c ${label}%c${weight} %cmoving with another hook`,
|
|
577
656
|
`color: ${THEME.muted}; font-family: monospace;`,
|
|
578
|
-
`color: ${THEME.muted}
|
|
657
|
+
`color: ${THEME.muted};`,
|
|
579
658
|
`color: ${THEME.problem}; font-weight: bold;`
|
|
580
659
|
);
|
|
581
660
|
} else {
|
|
582
661
|
console.log(
|
|
583
662
|
`%c ${label}%c${weight}`,
|
|
584
663
|
`color: ${THEME.muted}; font-family: monospace;`,
|
|
585
|
-
`color: ${THEME.muted}
|
|
664
|
+
`color: ${THEME.muted};`
|
|
586
665
|
);
|
|
587
666
|
}
|
|
588
667
|
});
|
|
@@ -590,23 +669,29 @@ var displayGraphReport = (graph) => {
|
|
|
590
669
|
});
|
|
591
670
|
console.groupEnd();
|
|
592
671
|
};
|
|
593
|
-
var displayViolentBreaker = (label, count,
|
|
672
|
+
var displayViolentBreaker = (label, count, _threshold) => {
|
|
594
673
|
if (!isWeb) return;
|
|
595
|
-
const
|
|
596
|
-
console.group(`%c
|
|
597
|
-
console.error(
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
console.log(
|
|
674
|
+
const name = displayName(label);
|
|
675
|
+
console.group(`%c BASIS | loop guard `, STYLES.headerProblem);
|
|
676
|
+
console.error(
|
|
677
|
+
`${name} updated ${count} times in one second. Basis stopped recording this path so the tab stays usable.`
|
|
678
|
+
);
|
|
679
|
+
console.log(
|
|
680
|
+
`%cReact may still error on its own. Fix the effect that writes a value it also lists as a dependency.`,
|
|
681
|
+
`color: ${THEME.muted};`
|
|
682
|
+
);
|
|
601
683
|
console.groupEnd();
|
|
602
684
|
};
|
|
603
685
|
var displayBootLog = (windowSize) => {
|
|
604
686
|
if (!isWeb) return;
|
|
605
|
-
console.log(
|
|
687
|
+
console.log(
|
|
688
|
+
`%cBasis%c watching updates (${windowSize}-frame window)`,
|
|
689
|
+
STYLES.basis,
|
|
690
|
+
`color: ${THEME.muted}; margin-left: 8px;`
|
|
691
|
+
);
|
|
606
692
|
};
|
|
607
693
|
|
|
608
694
|
// src/core/analysis.ts
|
|
609
|
-
var CAUSAL_MARGIN = 0.05;
|
|
610
695
|
var isEventDriven = (label, graph) => {
|
|
611
696
|
for (const [parent, targets] of graph.entries()) {
|
|
612
697
|
if (parent.startsWith("Event_Tick_") && targets.has(label)) {
|
|
@@ -616,29 +701,40 @@ var isEventDriven = (label, graph) => {
|
|
|
616
701
|
return false;
|
|
617
702
|
};
|
|
618
703
|
var calculateAllSimilarities = (entryA, entryB) => {
|
|
619
|
-
const
|
|
620
|
-
entryA.meta.buffer,
|
|
621
|
-
entryA.meta.head,
|
|
622
|
-
entryB.meta.buffer,
|
|
623
|
-
entryB.meta.head,
|
|
624
|
-
0
|
|
625
|
-
);
|
|
626
|
-
const bA = calculateSimilarityCircular(
|
|
704
|
+
const { kSync, kALeadsB, kBLeadsA, densityA, densityB } = countOverlapsCircular(
|
|
627
705
|
entryA.meta.buffer,
|
|
628
706
|
entryA.meta.head,
|
|
629
707
|
entryB.meta.buffer,
|
|
630
|
-
entryB.meta.head
|
|
631
|
-
1
|
|
708
|
+
entryB.meta.head
|
|
632
709
|
);
|
|
633
|
-
const
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
);
|
|
640
|
-
|
|
710
|
+
const sync = cosineFromOverlap(kSync, densityA, densityB);
|
|
711
|
+
const bA = cosineFromOverlap(kALeadsB, densityA, densityB);
|
|
712
|
+
const aB = cosineFromOverlap(kBLeadsA, densityA, densityB);
|
|
713
|
+
const max = Math.max(sync, bA, aB);
|
|
714
|
+
const windowSize = entryA.meta.buffer.length;
|
|
715
|
+
const significantSync = isSignificantOverlap(kSync, densityA, densityB, windowSize);
|
|
716
|
+
const kLead = Math.max(kALeadsB, kBLeadsA);
|
|
717
|
+
const significantLead = isSignificantOverlap(kLead, densityA, densityB, windowSize) && kLead >= kSync + 1;
|
|
718
|
+
return {
|
|
719
|
+
sync,
|
|
720
|
+
bA,
|
|
721
|
+
aB,
|
|
722
|
+
max,
|
|
723
|
+
kSync,
|
|
724
|
+
kALeadsB,
|
|
725
|
+
kBLeadsA,
|
|
726
|
+
densityA,
|
|
727
|
+
densityB,
|
|
728
|
+
significantSync,
|
|
729
|
+
significantLead
|
|
730
|
+
};
|
|
641
731
|
};
|
|
732
|
+
var overlapFrom = (s) => ({
|
|
733
|
+
kSync: s.kSync,
|
|
734
|
+
densityA: s.densityA,
|
|
735
|
+
densityB: s.densityB,
|
|
736
|
+
cosine: s.sync
|
|
737
|
+
});
|
|
642
738
|
var shouldSkipComparison = (entryA, entryB, dirtyLabels2) => {
|
|
643
739
|
if (entryA.label === entryB.label) return true;
|
|
644
740
|
if (isSameField(entryA.label, entryB.label)) return true;
|
|
@@ -662,52 +758,51 @@ var detectRedundancy = (entryA, entryB, similarities, redundantSet, violationMap
|
|
|
662
758
|
const roleA = entryA.meta.role;
|
|
663
759
|
const roleB = entryB.meta.role;
|
|
664
760
|
if (isGlobalSource(roleA) && isGlobalSource(roleB)) return;
|
|
665
|
-
if (
|
|
761
|
+
if (similarities.densityA < 2 || similarities.densityB < 2) return;
|
|
762
|
+
const overlap = overlapFrom(similarities);
|
|
666
763
|
if (roleA === "local" /* LOCAL */ && isGlobalSource(roleB)) {
|
|
667
764
|
redundantSet.add(entryA.label);
|
|
668
|
-
pushViolation(violationMap, entryB.label, { type: "context_mirror", target: entryA.label,
|
|
669
|
-
displayRedundancyAlert(entryA.label, entryA.meta, entryB.label, entryB.meta,
|
|
765
|
+
pushViolation(violationMap, entryB.label, { type: "context_mirror", target: entryA.label, overlap });
|
|
766
|
+
displayRedundancyAlert(entryA.label, entryA.meta, entryB.label, entryB.meta, overlap);
|
|
670
767
|
} else if (isGlobalSource(roleA) && roleB === "local" /* LOCAL */) {
|
|
671
768
|
redundantSet.add(entryB.label);
|
|
672
|
-
pushViolation(violationMap, entryA.label, { type: "context_mirror", target: entryB.label,
|
|
673
|
-
displayRedundancyAlert(entryB.label, entryB.meta, entryA.label, entryA.meta,
|
|
769
|
+
pushViolation(violationMap, entryA.label, { type: "context_mirror", target: entryB.label, overlap });
|
|
770
|
+
displayRedundancyAlert(entryB.label, entryB.meta, entryA.label, entryA.meta, overlap);
|
|
674
771
|
} else if (roleA === "local" /* LOCAL */ && roleB === "local" /* LOCAL */) {
|
|
675
772
|
redundantSet.add(entryA.label);
|
|
676
773
|
redundantSet.add(entryB.label);
|
|
677
|
-
pushViolation(violationMap, entryA.label, { type: "duplicate_state", target: entryB.label,
|
|
678
|
-
pushViolation(violationMap, entryB.label, { type: "duplicate_state", target: entryA.label,
|
|
679
|
-
displayRedundancyAlert(entryA.label, entryA.meta, entryB.label, entryB.meta,
|
|
774
|
+
pushViolation(violationMap, entryA.label, { type: "duplicate_state", target: entryB.label, overlap });
|
|
775
|
+
pushViolation(violationMap, entryB.label, { type: "duplicate_state", target: entryA.label, overlap });
|
|
776
|
+
displayRedundancyAlert(entryA.label, entryA.meta, entryB.label, entryB.meta, overlap);
|
|
680
777
|
}
|
|
681
778
|
};
|
|
682
779
|
var detectCausalLeak = (entryA, entryB, similarities, violationMap, graph) => {
|
|
683
780
|
if (entryA.isVolatile || entryB.isVolatile) return;
|
|
684
|
-
if (similarities.max - similarities.sync < CAUSAL_MARGIN) return;
|
|
685
781
|
const addLeak = (source, target) => {
|
|
686
782
|
if (isEventDriven(target, graph)) return;
|
|
687
|
-
|
|
688
|
-
violationMap.set(source, []);
|
|
689
|
-
}
|
|
690
|
-
violationMap.get(source).push({ type: "causal_leak", target });
|
|
783
|
+
pushViolation(violationMap, source, { type: "causal_leak", target });
|
|
691
784
|
const sourceEntry = source === entryA.label ? entryA : entryB;
|
|
692
785
|
const targetEntry = source === entryA.label ? entryB : entryA;
|
|
693
786
|
displayCausalHint(target, targetEntry.meta, source, sourceEntry.meta);
|
|
694
787
|
};
|
|
695
|
-
if (similarities.
|
|
788
|
+
if (similarities.kALeadsB >= similarities.kBLeadsA) {
|
|
696
789
|
addLeak(entryA.label, entryB.label);
|
|
697
|
-
} else
|
|
790
|
+
} else {
|
|
698
791
|
addLeak(entryB.label, entryA.label);
|
|
699
792
|
}
|
|
700
793
|
};
|
|
701
794
|
var detectSubspaceOverlap = (dirtyEntries, allEntries, redundantSet, dirtyLabels2, graph) => {
|
|
702
|
-
let compCount = 0;
|
|
703
795
|
const violationMap = /* @__PURE__ */ new Map();
|
|
796
|
+
let compCount = 0;
|
|
704
797
|
for (const entryA of dirtyEntries) {
|
|
705
798
|
for (const entryB of allEntries) {
|
|
706
799
|
if (shouldSkipComparison(entryA, entryB, dirtyLabels2)) continue;
|
|
707
800
|
compCount++;
|
|
708
801
|
const similarities = calculateAllSimilarities(entryA, entryB);
|
|
709
|
-
if (similarities.
|
|
802
|
+
if (similarities.significantSync) {
|
|
710
803
|
detectRedundancy(entryA, entryB, similarities, redundantSet, violationMap);
|
|
804
|
+
}
|
|
805
|
+
if (similarities.significantLead) {
|
|
711
806
|
detectCausalLeak(entryA, entryB, similarities, violationMap, graph);
|
|
712
807
|
}
|
|
713
808
|
}
|
|
@@ -966,9 +1061,9 @@ var beginEffectTracking = (l) => {
|
|
|
966
1061
|
var endEffectTracking = () => {
|
|
967
1062
|
instance.currentEffectSource = null;
|
|
968
1063
|
};
|
|
969
|
-
var printBasisHealthReport = (
|
|
1064
|
+
var printBasisHealthReport = () => {
|
|
970
1065
|
if (!instance.config.debug) return;
|
|
971
|
-
displayHealthReport(instance.history,
|
|
1066
|
+
displayHealthReport(instance.history, instance.violationMap);
|
|
972
1067
|
};
|
|
973
1068
|
var getBasisMetrics = () => ({
|
|
974
1069
|
engine: "v0.6.x",
|
|
@@ -1042,4 +1137,4 @@ export {
|
|
|
1042
1137
|
getBasisGraph,
|
|
1043
1138
|
printBasisGraph
|
|
1044
1139
|
};
|
|
1045
|
-
//# sourceMappingURL=chunk-
|
|
1140
|
+
//# sourceMappingURL=chunk-KIXM6YRX.mjs.map
|