@react-perfscope/core 0.7.0 → 1.0.0
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.cjs +105 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -8
- package/dist/index.d.ts +0 -8
- package/dist/index.js +105 -47
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.cjs
CHANGED
|
@@ -129,8 +129,14 @@ var import_trace_mapping = require("@jridgewell/trace-mapping");
|
|
|
129
129
|
|
|
130
130
|
// src/self-requests.ts
|
|
131
131
|
var selfRequests = /* @__PURE__ */ new Set();
|
|
132
|
+
var MAX_SELF_REQUESTS = 2e3;
|
|
132
133
|
function markSelfRequest(url) {
|
|
134
|
+
selfRequests.delete(url);
|
|
133
135
|
selfRequests.add(url);
|
|
136
|
+
if (selfRequests.size > MAX_SELF_REQUESTS) {
|
|
137
|
+
const oldest = selfRequests.values().next().value;
|
|
138
|
+
if (oldest !== void 0) selfRequests.delete(oldest);
|
|
139
|
+
}
|
|
134
140
|
}
|
|
135
141
|
function isSelfRequest(url) {
|
|
136
142
|
return selfRequests.has(url);
|
|
@@ -139,23 +145,25 @@ function isSelfRequest(url) {
|
|
|
139
145
|
// src/sourcemap.ts
|
|
140
146
|
var CHROME_FRAME = /^\s*at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?$/;
|
|
141
147
|
var FIREFOX_FRAME = /^(.*?)@(.+?):(\d+):(\d+)$/;
|
|
148
|
+
function lookupFrame(tracer, frame) {
|
|
149
|
+
const pos = (0, import_trace_mapping.originalPositionFor)(tracer, { line: frame.line, column: frame.col - 1 });
|
|
150
|
+
if (pos.source == null || pos.line == null || pos.column == null) {
|
|
151
|
+
return frame;
|
|
152
|
+
}
|
|
153
|
+
const resolved = {
|
|
154
|
+
file: pos.source,
|
|
155
|
+
line: pos.line,
|
|
156
|
+
col: pos.column + 1
|
|
157
|
+
};
|
|
158
|
+
if (pos.name) resolved.fnName = pos.name;
|
|
159
|
+
else if (frame.fnName) resolved.fnName = frame.fnName;
|
|
160
|
+
return resolved;
|
|
161
|
+
}
|
|
142
162
|
async function resolveFrame(frame, fetchMap) {
|
|
143
163
|
try {
|
|
144
164
|
const map = await fetchMap(frame.file);
|
|
145
165
|
if (!map) return frame;
|
|
146
|
-
|
|
147
|
-
const pos = (0, import_trace_mapping.originalPositionFor)(tracer, { line: frame.line, column: frame.col });
|
|
148
|
-
if (pos.source == null || pos.line == null || pos.column == null) {
|
|
149
|
-
return frame;
|
|
150
|
-
}
|
|
151
|
-
const resolved = {
|
|
152
|
-
file: pos.source,
|
|
153
|
-
line: pos.line,
|
|
154
|
-
col: pos.column
|
|
155
|
-
};
|
|
156
|
-
if (pos.name) resolved.fnName = pos.name;
|
|
157
|
-
else if (frame.fnName) resolved.fnName = frame.fnName;
|
|
158
|
-
return resolved;
|
|
166
|
+
return lookupFrame(new import_trace_mapping.TraceMap(map), frame);
|
|
159
167
|
} catch (err) {
|
|
160
168
|
console.warn("[react-perfscope] resolveFrame failed:", err);
|
|
161
169
|
return frame;
|
|
@@ -175,13 +183,25 @@ function attachLazyStack(target, raw, skipTopFrames = 0) {
|
|
|
175
183
|
}
|
|
176
184
|
});
|
|
177
185
|
}
|
|
186
|
+
var MAX_CACHED_MAPS = 50;
|
|
187
|
+
function sourceCacheKey(url) {
|
|
188
|
+
const q = url.indexOf("?");
|
|
189
|
+
const h = url.indexOf("#");
|
|
190
|
+
const end = Math.min(q === -1 ? url.length : q, h === -1 ? url.length : h);
|
|
191
|
+
return url.slice(0, end);
|
|
192
|
+
}
|
|
178
193
|
function createSourceMapResolver(opts = {}) {
|
|
179
194
|
const f = opts.fetch ?? (typeof fetch !== "undefined" ? fetch.bind(globalThis) : null);
|
|
180
195
|
const cache = /* @__PURE__ */ new Map();
|
|
181
|
-
function
|
|
196
|
+
function fetchTracerFor(sourceUrl) {
|
|
182
197
|
if (!f) return Promise.resolve(null);
|
|
183
|
-
const
|
|
184
|
-
|
|
198
|
+
const key = sourceCacheKey(sourceUrl);
|
|
199
|
+
const existing = cache.get(key);
|
|
200
|
+
if (existing) {
|
|
201
|
+
cache.delete(key);
|
|
202
|
+
cache.set(key, existing);
|
|
203
|
+
return existing;
|
|
204
|
+
}
|
|
185
205
|
const promise = (async () => {
|
|
186
206
|
try {
|
|
187
207
|
markSelfRequest(sourceUrl);
|
|
@@ -197,24 +217,31 @@ function createSourceMapResolver(opts = {}) {
|
|
|
197
217
|
const header = ref.slice(0, commaIdx);
|
|
198
218
|
const payload = ref.slice(commaIdx + 1);
|
|
199
219
|
const decoded = header.includes("base64") ? typeof atob === "function" ? atob(payload) : globalThis.Buffer.from(payload, "base64").toString("utf8") : decodeURIComponent(payload);
|
|
200
|
-
return JSON.parse(decoded);
|
|
220
|
+
return new import_trace_mapping.TraceMap(JSON.parse(decoded));
|
|
201
221
|
}
|
|
202
222
|
const mapUrl = new URL(ref, sourceUrl).href;
|
|
203
223
|
markSelfRequest(mapUrl);
|
|
204
224
|
const mapRes = await f(mapUrl);
|
|
205
225
|
if (!mapRes || !mapRes.ok) return null;
|
|
206
|
-
return await mapRes.json();
|
|
226
|
+
return new import_trace_mapping.TraceMap(await mapRes.json());
|
|
207
227
|
} catch {
|
|
228
|
+
cache.delete(key);
|
|
208
229
|
return null;
|
|
209
230
|
}
|
|
210
231
|
})();
|
|
211
|
-
cache.set(
|
|
232
|
+
cache.set(key, promise);
|
|
233
|
+
if (cache.size > MAX_CACHED_MAPS) {
|
|
234
|
+
const oldest = cache.keys().next().value;
|
|
235
|
+
if (oldest !== void 0) cache.delete(oldest);
|
|
236
|
+
}
|
|
212
237
|
return promise;
|
|
213
238
|
}
|
|
214
239
|
return {
|
|
215
240
|
async resolve(frame) {
|
|
216
241
|
try {
|
|
217
|
-
|
|
242
|
+
const tracer = await fetchTracerFor(frame.file);
|
|
243
|
+
if (!tracer) return frame;
|
|
244
|
+
return lookupFrame(tracer, frame);
|
|
218
245
|
} catch {
|
|
219
246
|
return frame;
|
|
220
247
|
}
|
|
@@ -405,6 +432,7 @@ function createLongTasksCollector() {
|
|
|
405
432
|
return {
|
|
406
433
|
kind: "long-task",
|
|
407
434
|
activate(emit) {
|
|
435
|
+
if (active) return;
|
|
408
436
|
if (typeof PerformanceObserver === "undefined") {
|
|
409
437
|
console.warn("[react-perfscope] PerformanceObserver not supported; long-tasks disabled");
|
|
410
438
|
return;
|
|
@@ -456,6 +484,20 @@ function createLongTasksCollector() {
|
|
|
456
484
|
};
|
|
457
485
|
}
|
|
458
486
|
|
|
487
|
+
// src/self-measurement.ts
|
|
488
|
+
var depth = 0;
|
|
489
|
+
function withSelfMeasurement(fn) {
|
|
490
|
+
depth++;
|
|
491
|
+
try {
|
|
492
|
+
return fn();
|
|
493
|
+
} finally {
|
|
494
|
+
depth--;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
function inSelfMeasurement() {
|
|
498
|
+
return depth > 0;
|
|
499
|
+
}
|
|
500
|
+
|
|
459
501
|
// src/collectors/forced-reflow.ts
|
|
460
502
|
var LAYOUT_GETTERS = [
|
|
461
503
|
"offsetWidth",
|
|
@@ -518,7 +560,7 @@ function createForcedReflowCollector() {
|
|
|
518
560
|
Object.defineProperty(proto, key, {
|
|
519
561
|
configurable: true,
|
|
520
562
|
get() {
|
|
521
|
-
if (active) {
|
|
563
|
+
if (active && !inSelfMeasurement()) {
|
|
522
564
|
if (!consumePendingMutations()) {
|
|
523
565
|
return originalGet.call(this);
|
|
524
566
|
}
|
|
@@ -543,7 +585,7 @@ function createForcedReflowCollector() {
|
|
|
543
585
|
configurable: true,
|
|
544
586
|
writable: true,
|
|
545
587
|
value: function patchedLayoutMethod(...args) {
|
|
546
|
-
if (active) {
|
|
588
|
+
if (active && !inSelfMeasurement()) {
|
|
547
589
|
if (!consumePendingMutations()) {
|
|
548
590
|
return original.apply(this, args);
|
|
549
591
|
}
|
|
@@ -633,7 +675,7 @@ function collectPerfscopeRects() {
|
|
|
633
675
|
function entryIsOnlyFromPerfscope(sources) {
|
|
634
676
|
if (sources.length === 0) return false;
|
|
635
677
|
if (typeof document === "undefined") return false;
|
|
636
|
-
const hostRects = collectPerfscopeRects
|
|
678
|
+
const hostRects = withSelfMeasurement(collectPerfscopeRects);
|
|
637
679
|
if (hostRects.length === 0) return false;
|
|
638
680
|
for (const src of sources) {
|
|
639
681
|
let matched = false;
|
|
@@ -670,6 +712,7 @@ function createLayoutShiftCollector() {
|
|
|
670
712
|
return {
|
|
671
713
|
kind: "layout-shift",
|
|
672
714
|
activate(emit) {
|
|
715
|
+
if (active) return;
|
|
673
716
|
if (typeof PerformanceObserver === "undefined") {
|
|
674
717
|
console.warn("[react-perfscope] PerformanceObserver not supported; layout-shift disabled");
|
|
675
718
|
return;
|
|
@@ -728,6 +771,7 @@ function createNetworkCollector() {
|
|
|
728
771
|
return {
|
|
729
772
|
kind: "network",
|
|
730
773
|
activate(emit) {
|
|
774
|
+
if (active) return;
|
|
731
775
|
if (typeof PerformanceObserver === "undefined") {
|
|
732
776
|
console.warn("[react-perfscope] PerformanceObserver not supported; network disabled");
|
|
733
777
|
return;
|
|
@@ -771,37 +815,40 @@ function createNetworkCollector() {
|
|
|
771
815
|
|
|
772
816
|
// src/collectors/web-vitals.ts
|
|
773
817
|
var import_web_vitals = require("web-vitals");
|
|
818
|
+
var subscribed = false;
|
|
819
|
+
var sinks = /* @__PURE__ */ new Set();
|
|
820
|
+
function ensureSubscribed() {
|
|
821
|
+
if (subscribed) return true;
|
|
822
|
+
try {
|
|
823
|
+
const fan = (name) => (metric) => {
|
|
824
|
+
for (const sink of sinks) sink(name, metric.value);
|
|
825
|
+
};
|
|
826
|
+
(0, import_web_vitals.onLCP)(fan("LCP"));
|
|
827
|
+
(0, import_web_vitals.onINP)(fan("INP"));
|
|
828
|
+
(0, import_web_vitals.onCLS)(fan("CLS"));
|
|
829
|
+
(0, import_web_vitals.onFCP)(fan("FCP"));
|
|
830
|
+
(0, import_web_vitals.onTTFB)(fan("TTFB"));
|
|
831
|
+
subscribed = true;
|
|
832
|
+
} catch (err) {
|
|
833
|
+
console.warn("[react-perfscope] web-vitals collector failed to subscribe:", err);
|
|
834
|
+
}
|
|
835
|
+
return subscribed;
|
|
836
|
+
}
|
|
774
837
|
function createWebVitalsCollector() {
|
|
775
|
-
let active = false;
|
|
776
|
-
let subscribed = false;
|
|
777
838
|
let emit = () => {
|
|
778
839
|
};
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
emit({ kind: "web-vital", name, value: metric.value });
|
|
783
|
-
};
|
|
784
|
-
}
|
|
840
|
+
const sink = (name, value) => {
|
|
841
|
+
emit({ kind: "web-vital", name, value });
|
|
842
|
+
};
|
|
785
843
|
return {
|
|
786
844
|
kind: "web-vital",
|
|
787
845
|
activate(emitFn) {
|
|
846
|
+
if (!ensureSubscribed()) return;
|
|
788
847
|
emit = emitFn;
|
|
789
|
-
|
|
790
|
-
if (subscribed) return;
|
|
791
|
-
try {
|
|
792
|
-
(0, import_web_vitals.onLCP)(makeHandler("LCP"));
|
|
793
|
-
(0, import_web_vitals.onINP)(makeHandler("INP"));
|
|
794
|
-
(0, import_web_vitals.onCLS)(makeHandler("CLS"));
|
|
795
|
-
(0, import_web_vitals.onFCP)(makeHandler("FCP"));
|
|
796
|
-
(0, import_web_vitals.onTTFB)(makeHandler("TTFB"));
|
|
797
|
-
subscribed = true;
|
|
798
|
-
} catch (err) {
|
|
799
|
-
console.warn("[react-perfscope] web-vitals collector failed to subscribe:", err);
|
|
800
|
-
active = false;
|
|
801
|
-
}
|
|
848
|
+
sinks.add(sink);
|
|
802
849
|
},
|
|
803
850
|
deactivate() {
|
|
804
|
-
|
|
851
|
+
sinks.delete(sink);
|
|
805
852
|
}
|
|
806
853
|
};
|
|
807
854
|
}
|
|
@@ -874,6 +921,7 @@ function createHeapCollector() {
|
|
|
874
921
|
return {
|
|
875
922
|
kind: "heap",
|
|
876
923
|
activate() {
|
|
924
|
+
if (timer != null) return;
|
|
877
925
|
samples = [];
|
|
878
926
|
if (!readMemory()) return;
|
|
879
927
|
sample();
|
|
@@ -894,6 +942,7 @@ function createHeapCollector() {
|
|
|
894
942
|
|
|
895
943
|
// src/collectors/interaction.ts
|
|
896
944
|
var DURATION_THRESHOLD = 40;
|
|
945
|
+
var MAX_ENTRIES = 5e3;
|
|
897
946
|
function selectorFor(target) {
|
|
898
947
|
const el = target;
|
|
899
948
|
if (!el || typeof el.tagName !== "string") return void 0;
|
|
@@ -916,13 +965,17 @@ function createInteractionCollector() {
|
|
|
916
965
|
return {
|
|
917
966
|
kind: "interaction",
|
|
918
967
|
activate() {
|
|
968
|
+
if (active) return;
|
|
919
969
|
if (typeof PerformanceObserver === "undefined" || !supportsEventTiming()) return;
|
|
920
970
|
active = true;
|
|
921
971
|
entries = [];
|
|
922
972
|
try {
|
|
923
973
|
observer = new PerformanceObserver((list) => {
|
|
924
974
|
if (!active) return;
|
|
925
|
-
for (const e of list.getEntries())
|
|
975
|
+
for (const e of list.getEntries()) {
|
|
976
|
+
if (entries.length >= MAX_ENTRIES) break;
|
|
977
|
+
entries.push(e);
|
|
978
|
+
}
|
|
926
979
|
});
|
|
927
980
|
observer.observe({ type: "event", buffered: false, durationThreshold: DURATION_THRESHOLD });
|
|
928
981
|
try {
|
|
@@ -946,8 +999,10 @@ function createInteractionCollector() {
|
|
|
946
999
|
}
|
|
947
1000
|
},
|
|
948
1001
|
finalize(result) {
|
|
1002
|
+
const buffered = entries;
|
|
1003
|
+
entries = [];
|
|
949
1004
|
const byId = /* @__PURE__ */ new Map();
|
|
950
|
-
for (const e of
|
|
1005
|
+
for (const e of buffered) {
|
|
951
1006
|
const id = e.interactionId;
|
|
952
1007
|
if (!id) continue;
|
|
953
1008
|
const group = byId.get(id);
|
|
@@ -1146,6 +1201,7 @@ function createSelfProfilingCollector() {
|
|
|
1146
1201
|
return {
|
|
1147
1202
|
kind: "long-task",
|
|
1148
1203
|
activate() {
|
|
1204
|
+
if (profiler) return;
|
|
1149
1205
|
tracePromise = null;
|
|
1150
1206
|
const Ctor = globalThis.Profiler;
|
|
1151
1207
|
if (typeof Ctor !== "function") return;
|
|
@@ -1163,6 +1219,8 @@ function createSelfProfilingCollector() {
|
|
|
1163
1219
|
if (!profiler) return;
|
|
1164
1220
|
try {
|
|
1165
1221
|
tracePromise = profiler.stop();
|
|
1222
|
+
tracePromise.catch(() => {
|
|
1223
|
+
});
|
|
1166
1224
|
} catch (err) {
|
|
1167
1225
|
console.warn("[react-perfscope] self-profiling stop failed:", err);
|
|
1168
1226
|
tracePromise = null;
|