@deeeed/metamask-harness 0.82.0 → 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.
Files changed (34) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/adapters/core/cleanup.sh +5 -29
  3. package/adapters/core/controller-import-probe.mjs +26 -0
  4. package/adapters/manifest.json +16 -0
  5. package/adapters/web-dapp/lib/policy-fence.mjs +119 -0
  6. package/adapters/web-dapp/lib/policy.mjs +7 -2
  7. package/dist/adapters/core/actions.js +0 -3
  8. package/dist/adapters/core/overlay.js +1 -5
  9. package/dist/adapters/core/readiness.js +133 -2
  10. package/dist/adapters/core/surface.js +53 -41
  11. package/dist/adapters/extension/extension-id.js +1 -0
  12. package/dist/adapters/extension/fixtures.js +60 -1
  13. package/dist/adapters/extension/network-observer.js +19 -206
  14. package/dist/adapters/extension/run.js +1 -1
  15. package/dist/adapters/extension/runtime.js +42 -3
  16. package/dist/adapters/mobile/android-gfxinfo-performance.js +3 -1
  17. package/dist/adapters/mobile/performance-observer.js +9 -6
  18. package/dist/adapters/performance/trace-kinds.js +36 -0
  19. package/dist/adapters/plugin-surface.js +29 -1
  20. package/dist/adapters/web-dapp/run.js +6 -1
  21. package/dist/command-contract.js +34 -0
  22. package/dist/commands/check.js +3 -2
  23. package/dist/commands/core-readiness.js +1 -9
  24. package/dist/performance-observation.js +28 -5
  25. package/library/actions/core/perps/_controller.mjs +10 -0
  26. package/library/actions/extension/wallet/import.mjs +21 -4
  27. package/library/actions/extension/wallet/visible-session.mjs +32 -12
  28. package/package.json +7 -6
  29. package/dist/adapters/core/live-script-env.js +0 -36
  30. package/dist/adapters/extension/browser-cdp.js +0 -174
  31. package/dist/adapters/extension/performance-observer.js +0 -75
  32. package/dist/adapters/performance/cdp-trace.js +0 -406
  33. package/dist/adapters/performance/js-task-metrics.js +0 -35
  34. 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
- };