@deeeed/metamask-harness 0.82.1 → 0.83.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/CHANGELOG.md +20 -0
- package/adapters/core/cleanup.sh +5 -29
- package/adapters/core/controller-import-probe.mjs +26 -0
- package/adapters/manifest.json +16 -0
- package/adapters/web-dapp/lib/policy-fence.mjs +119 -0
- package/adapters/web-dapp/lib/policy.mjs +7 -2
- package/dist/adapters/core/actions.js +0 -3
- package/dist/adapters/core/overlay.js +1 -5
- package/dist/adapters/core/readiness.js +133 -2
- package/dist/adapters/core/surface.js +53 -41
- package/dist/adapters/extension/extension-id.js +1 -0
- package/dist/adapters/extension/fixtures.js +60 -1
- package/dist/adapters/extension/network-observer.js +19 -206
- package/dist/adapters/extension/run.js +1 -1
- package/dist/adapters/extension/runtime.js +42 -3
- package/dist/adapters/mobile/android-gfxinfo-performance.js +3 -1
- package/dist/adapters/mobile/performance-observer.js +9 -6
- package/dist/adapters/performance/trace-kinds.js +36 -0
- package/dist/adapters/plugin-surface.js +29 -1
- package/dist/adapters/web-dapp/run.js +6 -1
- package/dist/command-contract.js +34 -0
- package/dist/commands/core-readiness.js +1 -9
- package/dist/performance-observation.js +28 -5
- package/library/actions/core/perps/_controller.mjs +10 -0
- package/library/actions/extension/wallet/import.mjs +21 -4
- package/library/actions/extension/wallet/visible-session.mjs +32 -12
- package/package.json +7 -6
- package/dist/adapters/core/live-script-env.js +0 -36
- package/dist/adapters/extension/browser-cdp.js +0 -174
- package/dist/adapters/extension/performance-observer.js +0 -75
- package/dist/adapters/performance/cdp-trace.js +0 -406
- package/dist/adapters/performance/js-task-metrics.js +0 -35
- package/dist/async.js +0 -19
|
@@ -1,406 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
summarizeFrames
|
|
3
|
-
} from "@farmslot/adapter-rn";
|
|
4
|
-
import { withTimeout } from "../../async.js";
|
|
5
|
-
import {
|
|
6
|
-
summarizeJavaScriptTasks
|
|
7
|
-
} from "./js-task-metrics.js";
|
|
8
|
-
const MAX_TRACE_BYTES = 16 * 1024 * 1024;
|
|
9
|
-
const MAX_TRACE_EVENTS = 25e4;
|
|
10
|
-
const MOBILE_TRACE_CATEGORIES = [
|
|
11
|
-
"blink.user_timing",
|
|
12
|
-
"disabled-by-default-devtools.timeline.frame"
|
|
13
|
-
];
|
|
14
|
-
const EXTENSION_TRACE_CATEGORIES = [
|
|
15
|
-
"blink.user_timing",
|
|
16
|
-
"devtools.timeline",
|
|
17
|
-
"disabled-by-default-devtools.timeline",
|
|
18
|
-
"disabled-by-default-devtools.timeline.frame",
|
|
19
|
-
"v8.execute"
|
|
20
|
-
];
|
|
21
|
-
const MOBILE_TRACE_KIND = {
|
|
22
|
-
categories: MOBILE_TRACE_CATEGORIES,
|
|
23
|
-
rendererScoped: false,
|
|
24
|
-
nativeSource: "react-native-cdp-frame-timings",
|
|
25
|
-
javascriptTasks: false
|
|
26
|
-
};
|
|
27
|
-
const TRACE_KINDS = {
|
|
28
|
-
extension: {
|
|
29
|
-
categories: EXTENSION_TRACE_CATEGORIES,
|
|
30
|
-
rendererScoped: true,
|
|
31
|
-
nativeSource: "chromium-cdp-frame-timings",
|
|
32
|
-
javascriptTasks: true
|
|
33
|
-
},
|
|
34
|
-
ios: MOBILE_TRACE_KIND,
|
|
35
|
-
android: MOBILE_TRACE_KIND
|
|
36
|
-
};
|
|
37
|
-
function createCdpTraceCollector(client, platform, marker) {
|
|
38
|
-
let active;
|
|
39
|
-
const offData = client.on("Tracing.dataCollected", (params) => {
|
|
40
|
-
if (!active || !Array.isArray(params.value)) return;
|
|
41
|
-
for (const value of params.value) {
|
|
42
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
43
|
-
const event = value;
|
|
44
|
-
const bytes = Buffer.byteLength(JSON.stringify(event));
|
|
45
|
-
if (active.events.length >= MAX_TRACE_EVENTS || active.bytes + bytes > MAX_TRACE_BYTES) {
|
|
46
|
-
active.overflow = true;
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
active.events.push(event);
|
|
50
|
-
active.bytes += bytes;
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
const offComplete = client.on("Tracing.tracingComplete", (params) => {
|
|
54
|
-
if (!active) return;
|
|
55
|
-
active.dataLossOccurred = params.dataLossOccurred === true;
|
|
56
|
-
active.complete?.();
|
|
57
|
-
});
|
|
58
|
-
return {
|
|
59
|
-
async start(id) {
|
|
60
|
-
if (active) throw new Error("Only one CDP performance trace may be active.");
|
|
61
|
-
let complete;
|
|
62
|
-
const completePromise = new Promise((resolve) => {
|
|
63
|
-
complete = resolve;
|
|
64
|
-
});
|
|
65
|
-
active = {
|
|
66
|
-
id,
|
|
67
|
-
events: [],
|
|
68
|
-
bytes: 0,
|
|
69
|
-
overflow: false,
|
|
70
|
-
dataLossOccurred: false,
|
|
71
|
-
complete,
|
|
72
|
-
completePromise
|
|
73
|
-
};
|
|
74
|
-
let tracingStarted = false;
|
|
75
|
-
try {
|
|
76
|
-
await client.send(
|
|
77
|
-
"Tracing.start",
|
|
78
|
-
{
|
|
79
|
-
categories: TRACE_KINDS[platform].categories.join(","),
|
|
80
|
-
transferMode: "ReportEvents"
|
|
81
|
-
},
|
|
82
|
-
15e3
|
|
83
|
-
);
|
|
84
|
-
tracingStarted = true;
|
|
85
|
-
const markerName = `mmh-clock-${id}-${Date.now()}`;
|
|
86
|
-
const sync = await marker(markerName);
|
|
87
|
-
active.markerName = markerName;
|
|
88
|
-
active.markerHostEpochMs = sync.hostEpochMs;
|
|
89
|
-
active.markerUncertaintyMs = sync.uncertaintyMs;
|
|
90
|
-
} catch (error) {
|
|
91
|
-
if (tracingStarted) {
|
|
92
|
-
await client.send("Tracing.end", {}, 5e3).catch(() => void 0);
|
|
93
|
-
await withTimeout(active.completePromise, {
|
|
94
|
-
message: "CDP tracing cleanup did not report tracingComplete.",
|
|
95
|
-
timeoutMs: 1e3
|
|
96
|
-
}).catch(() => void 0);
|
|
97
|
-
}
|
|
98
|
-
active = void 0;
|
|
99
|
-
throw error;
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
async end(id) {
|
|
103
|
-
const capture = active;
|
|
104
|
-
if (!capture || capture.id !== id) {
|
|
105
|
-
throw new Error(`Performance capture is not active: ${id}`);
|
|
106
|
-
}
|
|
107
|
-
try {
|
|
108
|
-
await client.send("Tracing.end", {}, 15e3);
|
|
109
|
-
await withTimeout(capture.completePromise, {
|
|
110
|
-
message: "CDP tracing did not report tracingComplete.",
|
|
111
|
-
timeoutMs: 3e4
|
|
112
|
-
});
|
|
113
|
-
} finally {
|
|
114
|
-
active = void 0;
|
|
115
|
-
}
|
|
116
|
-
return parseTraceCapture({
|
|
117
|
-
platform,
|
|
118
|
-
events: capture.events,
|
|
119
|
-
markerName: capture.markerName,
|
|
120
|
-
markerHostEpochMs: capture.markerHostEpochMs,
|
|
121
|
-
markerUncertaintyMs: capture.markerUncertaintyMs,
|
|
122
|
-
dataLossOccurred: capture.dataLossOccurred,
|
|
123
|
-
overflow: capture.overflow,
|
|
124
|
-
unavailableReasons: [],
|
|
125
|
-
coverageGapReasons: []
|
|
126
|
-
});
|
|
127
|
-
},
|
|
128
|
-
async close() {
|
|
129
|
-
if (active) {
|
|
130
|
-
await client.send("Tracing.end", {}, 5e3).catch(() => void 0);
|
|
131
|
-
await withTimeout(active.completePromise, {
|
|
132
|
-
message: "CDP tracing cleanup did not report tracingComplete.",
|
|
133
|
-
timeoutMs: 1e3
|
|
134
|
-
}).catch(() => void 0);
|
|
135
|
-
active = void 0;
|
|
136
|
-
}
|
|
137
|
-
offData();
|
|
138
|
-
offComplete();
|
|
139
|
-
client.close();
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
function parseTraceCapture(capture) {
|
|
144
|
-
const marker = findClockMarker(capture.events, capture.markerName);
|
|
145
|
-
const kind = TRACE_KINDS[capture.platform];
|
|
146
|
-
if (kind.rendererScoped && !finite(marker?.pid)) {
|
|
147
|
-
const reason = "Extension trace marker did not identify a renderer process.";
|
|
148
|
-
return {
|
|
149
|
-
platform: capture.platform,
|
|
150
|
-
javascript: unavailableJavaScriptSource("cdp-js-runtime-tasks", reason),
|
|
151
|
-
nativeUi: unavailableNativeUiSource("cdp-native-frame-timings", reason),
|
|
152
|
-
trace: traceEvidence(capture, "unresolved")
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
const scopedEvents = kind.rendererScoped && finite(marker?.pid) ? capture.events.filter((event) => event.pid === marker.pid) : capture.events;
|
|
156
|
-
const scope = !kind.rendererScoped ? "mobile-host" : finite(marker?.pid) ? "extension-renderer" : "unresolved";
|
|
157
|
-
const trace = traceEvidence(
|
|
158
|
-
{ ...capture, events: scopedEvents },
|
|
159
|
-
scope,
|
|
160
|
-
kind.rendererScoped && finite(marker?.pid) ? marker.pid : void 0
|
|
161
|
-
);
|
|
162
|
-
if (!finite(marker?.ts) || capture.markerHostEpochMs === void 0 || capture.markerUncertaintyMs === void 0) {
|
|
163
|
-
const reason = "CDP trace clock marker was unavailable.";
|
|
164
|
-
return {
|
|
165
|
-
platform: capture.platform,
|
|
166
|
-
javascript: unavailableJavaScriptSource("cdp-js-runtime-tasks", reason),
|
|
167
|
-
nativeUi: unavailableNativeUiSource(
|
|
168
|
-
"cdp-native-frame-timings",
|
|
169
|
-
reason
|
|
170
|
-
),
|
|
171
|
-
trace
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
const traceToEpochMs = (timestampUs) => capture.markerHostEpochMs + (timestampUs - marker.ts) / 1e3;
|
|
175
|
-
const gapReasons = [
|
|
176
|
-
...capture.coverageGapReasons,
|
|
177
|
-
...capture.overflow ? ["CDP trace retention limit was reached."] : [],
|
|
178
|
-
...capture.dataLossOccurred ? ["CDP reported trace data loss."] : []
|
|
179
|
-
];
|
|
180
|
-
const nativeSamples = nativeFrameSamples(
|
|
181
|
-
scopedEvents,
|
|
182
|
-
capture.platform,
|
|
183
|
-
traceToEpochMs
|
|
184
|
-
);
|
|
185
|
-
const javascriptSamples = javascriptTaskSamples(
|
|
186
|
-
scopedEvents,
|
|
187
|
-
traceToEpochMs
|
|
188
|
-
);
|
|
189
|
-
const status = capture.overflow || capture.dataLossOccurred ? "partial" : "complete";
|
|
190
|
-
return {
|
|
191
|
-
platform: capture.platform,
|
|
192
|
-
javascript: javascriptSourceResult(
|
|
193
|
-
"cdp-js-runtime-tasks",
|
|
194
|
-
javascriptSamples,
|
|
195
|
-
status,
|
|
196
|
-
gapReasons,
|
|
197
|
-
traceEventNames(scopedEvents),
|
|
198
|
-
capture.markerUncertaintyMs
|
|
199
|
-
),
|
|
200
|
-
nativeUi: nativeUiSourceResult(
|
|
201
|
-
kind.nativeSource,
|
|
202
|
-
nativeSamples,
|
|
203
|
-
status,
|
|
204
|
-
gapReasons,
|
|
205
|
-
traceEventNames(scopedEvents),
|
|
206
|
-
capture.markerUncertaintyMs
|
|
207
|
-
),
|
|
208
|
-
trace,
|
|
209
|
-
rawTraceEvents: normalizeTraceTimestamps(scopedEvents, traceToEpochMs)
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
function normalizeTraceTimestamps(events, traceToEpochMs) {
|
|
213
|
-
return events.map(
|
|
214
|
-
(event) => finite(event.ts) ? { ...event, ts: traceToEpochMs(event.ts) * 1e3 } : { ...event }
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
function traceEvidence(capture, scope, rendererProcessId) {
|
|
218
|
-
const counts = /* @__PURE__ */ new Map();
|
|
219
|
-
for (const event of capture.events) {
|
|
220
|
-
const name = String(event.name ?? "");
|
|
221
|
-
counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
222
|
-
}
|
|
223
|
-
return {
|
|
224
|
-
beginFrameCount: counts.get("BeginFrame") ?? 0,
|
|
225
|
-
dataLossOccurred: capture.dataLossOccurred,
|
|
226
|
-
drawFrameCount: counts.get("DrawFrame") ?? 0,
|
|
227
|
-
overflow: capture.overflow,
|
|
228
|
-
profileChunkCount: counts.get("ProfileChunk") ?? 0,
|
|
229
|
-
...rendererProcessId === void 0 ? {} : { rendererProcessId },
|
|
230
|
-
runTaskCount: counts.get("RunTask") ?? 0,
|
|
231
|
-
scope,
|
|
232
|
-
totalEventCount: capture.events.length
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
function nativeFrameSamples(events, platform, traceToEpochMs) {
|
|
236
|
-
const begins = /* @__PURE__ */ new Map();
|
|
237
|
-
const frames = [];
|
|
238
|
-
for (const event of events) {
|
|
239
|
-
if (event.name !== "BeginFrame" && event.name !== "DrawFrame") continue;
|
|
240
|
-
const sequence = frameSequence(event);
|
|
241
|
-
if (!sequence || !finite(event.ts)) continue;
|
|
242
|
-
if (event.name === "BeginFrame") {
|
|
243
|
-
begins.set(sequence, event);
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
const begin = begins.get(sequence);
|
|
247
|
-
if (!begin || !finite(begin.ts)) continue;
|
|
248
|
-
frames.push({ beginUs: begin.ts, endUs: event.ts });
|
|
249
|
-
}
|
|
250
|
-
frames.sort((first, second) => first.beginUs - second.beginUs);
|
|
251
|
-
const frameBudgetMs = estimateFrameBudgetMs(frames);
|
|
252
|
-
if (platform === "ios") {
|
|
253
|
-
const samples = [];
|
|
254
|
-
for (let index = 1; index < frames.length; index += 1) {
|
|
255
|
-
const previous = frames[index - 1];
|
|
256
|
-
const current = frames[index];
|
|
257
|
-
if (!previous || !current) continue;
|
|
258
|
-
const durationMs = (current.beginUs - previous.beginUs) / 1e3;
|
|
259
|
-
if (durationMs <= 0) continue;
|
|
260
|
-
samples.push({
|
|
261
|
-
cadenceInterval: true,
|
|
262
|
-
completedAtEpochMs: traceToEpochMs(current.beginUs),
|
|
263
|
-
durationMs,
|
|
264
|
-
...frameBudgetMs ? {
|
|
265
|
-
frameBudgetMs,
|
|
266
|
-
overBudget: durationMs > frameBudgetMs * 1.01
|
|
267
|
-
} : {},
|
|
268
|
-
startedAtEpochMs: traceToEpochMs(previous.beginUs)
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
return samples;
|
|
272
|
-
}
|
|
273
|
-
return frames.map((frame) => {
|
|
274
|
-
const durationMs = (frame.endUs - frame.beginUs) / 1e3;
|
|
275
|
-
return {
|
|
276
|
-
completedAtEpochMs: traceToEpochMs(frame.endUs),
|
|
277
|
-
durationMs,
|
|
278
|
-
...frameBudgetMs ? {
|
|
279
|
-
frameBudgetMs,
|
|
280
|
-
overBudget: durationMs > frameBudgetMs * 1.01
|
|
281
|
-
} : {},
|
|
282
|
-
startedAtEpochMs: traceToEpochMs(frame.beginUs)
|
|
283
|
-
};
|
|
284
|
-
}).filter((sample) => sample.durationMs > 0);
|
|
285
|
-
}
|
|
286
|
-
function estimateFrameBudgetMs(frames) {
|
|
287
|
-
const cadenceMs = [];
|
|
288
|
-
for (let index = 1; index < frames.length; index += 1) {
|
|
289
|
-
const previous = frames[index - 1];
|
|
290
|
-
const current = frames[index];
|
|
291
|
-
if (!previous || !current) continue;
|
|
292
|
-
const intervalMs = (current.beginUs - previous.beginUs) / 1e3;
|
|
293
|
-
if (intervalMs >= 3.5 && intervalMs <= 40) cadenceMs.push(intervalMs);
|
|
294
|
-
}
|
|
295
|
-
if (cadenceMs.length < 5) return void 0;
|
|
296
|
-
cadenceMs.sort((first, second) => first - second);
|
|
297
|
-
const observedBudgetMs = percentile(cadenceMs, 0.1);
|
|
298
|
-
const refreshRateHz = Math.round(1e3 / observedBudgetMs);
|
|
299
|
-
return refreshRateHz >= 24 && refreshRateHz <= 240 ? 1e3 / refreshRateHz : void 0;
|
|
300
|
-
}
|
|
301
|
-
function percentile(sorted, quantile) {
|
|
302
|
-
if (sorted.length === 1) return sorted[0] ?? 0;
|
|
303
|
-
const position = (sorted.length - 1) * quantile;
|
|
304
|
-
const lower = Math.floor(position);
|
|
305
|
-
const upper = Math.ceil(position);
|
|
306
|
-
const lowerValue = sorted[lower] ?? 0;
|
|
307
|
-
const upperValue = sorted[upper] ?? lowerValue;
|
|
308
|
-
return lowerValue + (upperValue - lowerValue) * (position - lower);
|
|
309
|
-
}
|
|
310
|
-
function javascriptTaskSamples(events, traceToEpochMs) {
|
|
311
|
-
return events.filter(
|
|
312
|
-
(event) => event.name === "RunTask" && event.ph === "X" && finite(event.ts) && finite(event.dur) && event.dur > 0
|
|
313
|
-
).map((event) => ({
|
|
314
|
-
completedAtEpochMs: traceToEpochMs(event.ts + event.dur),
|
|
315
|
-
durationMs: event.dur / 1e3
|
|
316
|
-
}));
|
|
317
|
-
}
|
|
318
|
-
function findClockMarker(events, markerName) {
|
|
319
|
-
if (!markerName) return void 0;
|
|
320
|
-
return events.find(
|
|
321
|
-
(candidate) => candidate.name === markerName || asRecord(candidate.args).sync_id === markerName || asRecord(asRecord(candidate.args).data).sync_id === markerName
|
|
322
|
-
);
|
|
323
|
-
}
|
|
324
|
-
function frameSequence(event) {
|
|
325
|
-
const args = asRecord(event.args);
|
|
326
|
-
const value = args.frameSeqId ?? asRecord(args.data).frameSeqId;
|
|
327
|
-
return value === void 0 ? void 0 : String(value);
|
|
328
|
-
}
|
|
329
|
-
function javascriptSourceResult(kind, samples, status, coverageGapReasons, observedEventNames, clockSyncUncertaintyMs) {
|
|
330
|
-
return {
|
|
331
|
-
applicability: "applicable",
|
|
332
|
-
status: samples.length > 0 ? status : "partial",
|
|
333
|
-
kind,
|
|
334
|
-
unavailableReasons: [],
|
|
335
|
-
coverageGapReasons: [
|
|
336
|
-
...coverageGapReasons,
|
|
337
|
-
...samples.length > 0 ? [] : [
|
|
338
|
-
`No ${kind} samples were recorded. Observed trace events: ${observedEventNames.join(", ") || "none"}.`
|
|
339
|
-
]
|
|
340
|
-
],
|
|
341
|
-
samples,
|
|
342
|
-
summary: summarizeJavaScriptTasks(samples),
|
|
343
|
-
clockSyncUncertaintyMs
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
function nativeUiSourceResult(kind, samples, status, coverageGapReasons, observedEventNames, clockSyncUncertaintyMs) {
|
|
347
|
-
const summary = summarizeFrames(samples);
|
|
348
|
-
const hasUnclassifiedCadenceGap = (summary.cadenceGapCount ?? 0) > 0;
|
|
349
|
-
return {
|
|
350
|
-
status: samples.length === 0 ? "partial" : hasUnclassifiedCadenceGap ? "partial" : status,
|
|
351
|
-
kind,
|
|
352
|
-
unavailableReasons: [],
|
|
353
|
-
coverageGapReasons: [
|
|
354
|
-
...coverageGapReasons,
|
|
355
|
-
...samples.length > 0 ? [] : [
|
|
356
|
-
`No ${kind} samples were recorded. Observed trace events: ${observedEventNames.join(", ") || "none"}.`
|
|
357
|
-
],
|
|
358
|
-
...hasUnclassifiedCadenceGap ? [
|
|
359
|
-
"Unclassified native cadence gap prevents a complete FPS result."
|
|
360
|
-
] : []
|
|
361
|
-
],
|
|
362
|
-
samples,
|
|
363
|
-
summary,
|
|
364
|
-
clockSyncUncertaintyMs
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
function traceEventNames(events) {
|
|
368
|
-
const counts = /* @__PURE__ */ new Map();
|
|
369
|
-
for (const event of events) {
|
|
370
|
-
const name = String(event.name ?? "unknown");
|
|
371
|
-
counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
372
|
-
}
|
|
373
|
-
return [...counts.entries()].sort((first, second) => second[1] - first[1]).slice(0, 20).map(([name, count]) => `${name}(${count})`);
|
|
374
|
-
}
|
|
375
|
-
function unavailableJavaScriptSource(kind, reason) {
|
|
376
|
-
return {
|
|
377
|
-
applicability: "applicable",
|
|
378
|
-
status: "unavailable",
|
|
379
|
-
kind,
|
|
380
|
-
unavailableReasons: [reason],
|
|
381
|
-
coverageGapReasons: [],
|
|
382
|
-
samples: [],
|
|
383
|
-
summary: { taskCount: 0 }
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
function unavailableNativeUiSource(kind, reason) {
|
|
387
|
-
return {
|
|
388
|
-
status: "unavailable",
|
|
389
|
-
kind,
|
|
390
|
-
unavailableReasons: [reason],
|
|
391
|
-
coverageGapReasons: [],
|
|
392
|
-
samples: [],
|
|
393
|
-
summary: { frameCount: 0 }
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
function finite(value) {
|
|
397
|
-
return typeof value === "number" && Number.isFinite(value);
|
|
398
|
-
}
|
|
399
|
-
function asRecord(value) {
|
|
400
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
401
|
-
}
|
|
402
|
-
export {
|
|
403
|
-
TRACE_KINDS,
|
|
404
|
-
createCdpTraceCollector,
|
|
405
|
-
parseTraceCapture
|
|
406
|
-
};
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
const LONG_TASK_MS = 50;
|
|
2
|
-
function summarizeJavaScriptTasks(samples) {
|
|
3
|
-
const durations = samples.map((sample) => sample.durationMs).filter((duration) => Number.isFinite(duration) && duration > 0).sort((first, second) => first - second);
|
|
4
|
-
if (durations.length === 0) return { taskCount: 0 };
|
|
5
|
-
const longTaskCount = durations.filter(
|
|
6
|
-
(duration) => duration >= LONG_TASK_MS
|
|
7
|
-
).length;
|
|
8
|
-
return {
|
|
9
|
-
longestTaskMs: round(durations.at(-1) ?? 0),
|
|
10
|
-
longTaskCount,
|
|
11
|
-
longTaskPercent: round(longTaskCount / durations.length * 100),
|
|
12
|
-
p50TaskMs: round(percentile(durations, 0.5)),
|
|
13
|
-
p95TaskMs: round(percentile(durations, 0.95)),
|
|
14
|
-
p99TaskMs: round(percentile(durations, 0.99)),
|
|
15
|
-
taskCount: durations.length,
|
|
16
|
-
totalTaskTimeMs: round(
|
|
17
|
-
durations.reduce((total, duration) => total + duration, 0)
|
|
18
|
-
)
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
function percentile(sorted, quantile) {
|
|
22
|
-
if (sorted.length === 1) return sorted[0] ?? 0;
|
|
23
|
-
const position = (sorted.length - 1) * quantile;
|
|
24
|
-
const lower = Math.floor(position);
|
|
25
|
-
const upper = Math.ceil(position);
|
|
26
|
-
const lowerValue = sorted[lower] ?? 0;
|
|
27
|
-
const upperValue = sorted[upper] ?? lowerValue;
|
|
28
|
-
return lowerValue + (upperValue - lowerValue) * (position - lower);
|
|
29
|
-
}
|
|
30
|
-
function round(value) {
|
|
31
|
-
return Math.round(value * 1e3) / 1e3;
|
|
32
|
-
}
|
|
33
|
-
export {
|
|
34
|
-
summarizeJavaScriptTasks
|
|
35
|
-
};
|
package/dist/async.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
async function withTimeout(promise, options) {
|
|
2
|
-
let timer;
|
|
3
|
-
try {
|
|
4
|
-
return await Promise.race([
|
|
5
|
-
promise,
|
|
6
|
-
new Promise((_resolve, reject) => {
|
|
7
|
-
timer = setTimeout(() => {
|
|
8
|
-
options.onTimeout?.();
|
|
9
|
-
reject(new Error(options.message));
|
|
10
|
-
}, options.timeoutMs);
|
|
11
|
-
})
|
|
12
|
-
]);
|
|
13
|
-
} finally {
|
|
14
|
-
if (timer) clearTimeout(timer);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
export {
|
|
18
|
-
withTimeout
|
|
19
|
-
};
|