@deeeed/metamask-harness 0.34.0 → 0.34.1
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 +16 -0
- package/adapters/manifest.json +0 -8
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +14 -3
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +22 -15
- package/adapters/mobile/launch-metro.cjs +6 -4
- package/adapters/mobile/open-device.sh +164 -15
- package/adapters/mobile/start-metro.sh +165 -93
- package/adapters/mobile/stop-metro.sh +1 -0
- package/dist/adapters/mobile/video-recorder.js +12 -10
- package/dist/commands/launch/index.js +9 -0
- package/dist/commands/launch/mobile.js +1 -1
- package/dist/live-adapter-contract.js +7 -0
- package/library/actions/mobile/perps/performance-capture.mjs +83 -1
- package/library/actions/mobile/platform/bridge.mjs +64 -3
- package/library/actions/mobile/wallet/ensure_unlocked.mjs +30 -7
- package/library/manifests/mobile.action-manifest.json +18 -0
- package/library/recipes/mobile/perps/performance.homepage.android-cold-disk-cache.recipe.json +20 -2
- package/library/recipes/mobile/perps/performance.homepage.android-cold-no-cache.recipe.json +2 -0
- package/package.json +1 -1
- package/adapters/mobile/metro-config.cjs +0 -93
|
@@ -39,7 +39,30 @@ function routeName(status, input) {
|
|
|
39
39
|
const selectedStatus = selectBridgeStatusEntry;
|
|
40
40
|
|
|
41
41
|
async function status(input) {
|
|
42
|
-
return bridgeCommand(input, ['status']);
|
|
42
|
+
return bridgeCommand(input, ['status-selected']);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function statusBeforeDeadline(input, deadline) {
|
|
46
|
+
const remainingMs = Math.max(1, deadline - Date.now());
|
|
47
|
+
const configuredMs = Number(
|
|
48
|
+
input.node?.bridge_timeout_ms ??
|
|
49
|
+
input.node?.cdp_timeout_ms ??
|
|
50
|
+
process.env.CDP_TIMEOUT ??
|
|
51
|
+
30000,
|
|
52
|
+
);
|
|
53
|
+
const probeTimeoutMs = Math.min(
|
|
54
|
+
5000,
|
|
55
|
+
remainingMs,
|
|
56
|
+
Number.isFinite(configuredMs) && configuredMs > 0 ? configuredMs : 30000,
|
|
57
|
+
);
|
|
58
|
+
return status({
|
|
59
|
+
...input,
|
|
60
|
+
node: {
|
|
61
|
+
...input.node,
|
|
62
|
+
bridge_timeout_ms: probeTimeoutMs,
|
|
63
|
+
cdp_timeout_ms: probeTimeoutMs,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
43
66
|
}
|
|
44
67
|
|
|
45
68
|
async function waitForTargetStatus(input, timeoutMs = 20000) {
|
|
@@ -48,7 +71,7 @@ async function waitForTargetStatus(input, timeoutMs = 20000) {
|
|
|
48
71
|
let lastError = null;
|
|
49
72
|
while (Date.now() < deadline) {
|
|
50
73
|
try {
|
|
51
|
-
last = await
|
|
74
|
+
last = await statusBeforeDeadline(input, deadline);
|
|
52
75
|
const selected = selectedStatus(last, input);
|
|
53
76
|
if (selected?.agenticPresent === true) return last;
|
|
54
77
|
} catch (error) {
|
|
@@ -69,7 +92,7 @@ async function waitForWalletState(input, initialStatus, timeoutMs) {
|
|
|
69
92
|
if (Date.now() >= deadline) break;
|
|
70
93
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
71
94
|
try {
|
|
72
|
-
last = await
|
|
95
|
+
last = await statusBeforeDeadline(input, deadline);
|
|
73
96
|
} catch {
|
|
74
97
|
// A rotating Hermes target during startup is transient. The target timeout
|
|
75
98
|
// still bounds this loop and the final error reports the last wallet state.
|
|
@@ -80,13 +103,13 @@ async function waitForWalletState(input, initialStatus, timeoutMs) {
|
|
|
80
103
|
);
|
|
81
104
|
}
|
|
82
105
|
|
|
83
|
-
async function waitForUnlocked(input, timeoutMs =
|
|
106
|
+
async function waitForUnlocked(input, timeoutMs = 30000) {
|
|
84
107
|
const deadline = Date.now() + timeoutMs;
|
|
85
108
|
let last = null;
|
|
86
109
|
let lastError = null;
|
|
87
110
|
while (Date.now() < deadline) {
|
|
88
111
|
try {
|
|
89
|
-
last = await
|
|
112
|
+
last = await statusBeforeDeadline(input, deadline);
|
|
90
113
|
if (selectedAccount(last, input) && routeName(last, input) !== 'Login') return last;
|
|
91
114
|
} catch (error) {
|
|
92
115
|
lastError = error;
|
|
@@ -108,7 +131,7 @@ async function waitForStableUnlocked(input, initialStatus, stableMs = 750) {
|
|
|
108
131
|
while (Date.now() < deadline) {
|
|
109
132
|
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
110
133
|
try {
|
|
111
|
-
last = await
|
|
134
|
+
last = await statusBeforeDeadline(input, deadline);
|
|
112
135
|
if (!isUnlockedStatus(last, input)) {
|
|
113
136
|
if (routeName(last, input) === 'Login') return null;
|
|
114
137
|
transientDrops += 1;
|
|
@@ -152,7 +175,7 @@ export async function ensureUnlocked(input) {
|
|
|
152
175
|
const password = input.node?.password ?? await fixturePassword(input.context.projectRoot);
|
|
153
176
|
try {
|
|
154
177
|
const result = await bridgeCommand(input, ['unlock', String(password)]);
|
|
155
|
-
const after = await waitForUnlocked(input, Number(input.node?.unlock_timeout_ms ??
|
|
178
|
+
const after = await waitForUnlocked(input, Number(input.node?.unlock_timeout_ms ?? 30000));
|
|
156
179
|
const stableAfter = await waitForStableUnlocked(input, after, Number(input.node?.stable_unlocked_ms ?? 750)) ?? after;
|
|
157
180
|
return {
|
|
158
181
|
action: input.action,
|
|
@@ -2489,6 +2489,24 @@
|
|
|
2489
2489
|
"items": {
|
|
2490
2490
|
"type": "string"
|
|
2491
2491
|
}
|
|
2492
|
+
},
|
|
2493
|
+
"first_fresh_delivery_requirements": {
|
|
2494
|
+
"type": "array",
|
|
2495
|
+
"items": {
|
|
2496
|
+
"type": "object",
|
|
2497
|
+
"properties": {
|
|
2498
|
+
"stream": { "type": "string" },
|
|
2499
|
+
"lifecycle": { "type": "string" },
|
|
2500
|
+
"subscriber_throttle_ms": { "type": "number", "minimum": 0 },
|
|
2501
|
+
"max_socket_to_subscriber_ms": { "type": "number", "minimum": 0 }
|
|
2502
|
+
},
|
|
2503
|
+
"required": [
|
|
2504
|
+
"stream",
|
|
2505
|
+
"subscriber_throttle_ms",
|
|
2506
|
+
"max_socket_to_subscriber_ms"
|
|
2507
|
+
],
|
|
2508
|
+
"additionalProperties": false
|
|
2509
|
+
}
|
|
2492
2510
|
}
|
|
2493
2511
|
},
|
|
2494
2512
|
"required": ["phase"],
|
package/library/recipes/mobile/perps/performance.homepage.android-cold-disk-cache.recipe.json
CHANGED
|
@@ -57,6 +57,8 @@
|
|
|
57
57
|
},
|
|
58
58
|
"unlock-after-restart": {
|
|
59
59
|
"action": "metamask.wallet.ensure_unlocked",
|
|
60
|
+
"target_timeout_ms": 90000,
|
|
61
|
+
"unlock_timeout_ms": 90000,
|
|
60
62
|
"intent": "Restore the fixture wallet after the cold process launch",
|
|
61
63
|
"next": "open-wallet-home-after"
|
|
62
64
|
},
|
|
@@ -108,17 +110,33 @@
|
|
|
108
110
|
"finish-performance-capture": {
|
|
109
111
|
"action": "metamask.perps.capture_performance",
|
|
110
112
|
"phase": "end",
|
|
113
|
+
"artifact_prefix": "logs/homepage-order-cold-disk-cache",
|
|
111
114
|
"required_stages": [
|
|
112
115
|
"disk_cache_hydrated",
|
|
113
116
|
"viewport_demand",
|
|
114
117
|
"socket_received",
|
|
118
|
+
"subscriber_delivery",
|
|
115
119
|
"react_commit",
|
|
116
120
|
"next_frame_checkpoint"
|
|
117
121
|
],
|
|
118
122
|
"required_lifecycles": ["cold_disk_cache"],
|
|
119
|
-
"required_sources": ["
|
|
123
|
+
"required_sources": ["fresh_socket"],
|
|
120
124
|
"required_fresh_content_variants": ["orders"],
|
|
121
|
-
"
|
|
125
|
+
"first_fresh_delivery_requirements": [
|
|
126
|
+
{
|
|
127
|
+
"stream": "positions",
|
|
128
|
+
"lifecycle": "cold_disk_cache",
|
|
129
|
+
"subscriber_throttle_ms": 5000,
|
|
130
|
+
"max_socket_to_subscriber_ms": 1000
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"stream": "orders",
|
|
134
|
+
"lifecycle": "cold_disk_cache",
|
|
135
|
+
"subscriber_throttle_ms": 5000,
|
|
136
|
+
"max_socket_to_subscriber_ms": 1000
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
"intent": "Require cache hydration, fresh visible order content, and immediate first-fresh positions/orders delivery",
|
|
122
140
|
"next": "cleanup-order"
|
|
123
141
|
},
|
|
124
142
|
"cleanup-order": {
|
package/package.json
CHANGED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('node:fs');
|
|
4
|
-
const { createRequire } = require('node:module');
|
|
5
|
-
const path = require('node:path');
|
|
6
|
-
const { pathToFileURL } = require('node:url');
|
|
7
|
-
|
|
8
|
-
function escapeRegExp(value) {
|
|
9
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function appendBlockList(blockList, pattern) {
|
|
13
|
-
if (Array.isArray(blockList)) return [...blockList, pattern];
|
|
14
|
-
return blockList instanceof RegExp ? [blockList, pattern] : [pattern];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function blockListFlags(blockList) {
|
|
18
|
-
const first = Array.isArray(blockList) ? blockList[0] : blockList;
|
|
19
|
-
return first instanceof RegExp ? first.flags : '';
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function resolveExpoConfigLoader(projectRoot) {
|
|
23
|
-
const requireFromProject = createRequire(path.join(projectRoot, 'package.json'));
|
|
24
|
-
let requireUtilsPath;
|
|
25
|
-
try {
|
|
26
|
-
requireUtilsPath = requireFromProject.resolve('@expo/require-utils');
|
|
27
|
-
} catch (error) {
|
|
28
|
-
if (error?.code !== 'MODULE_NOT_FOUND') throw error;
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
const { loadModuleSync } = requireFromProject(requireUtilsPath);
|
|
32
|
-
return typeof loadModuleSync === 'function' ? loadModuleSync : null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function loadProductConfig(configPath, baseConfig, projectRoot) {
|
|
36
|
-
let loaded;
|
|
37
|
-
const expoConfigLoader = resolveExpoConfigLoader(projectRoot);
|
|
38
|
-
if (expoConfigLoader) {
|
|
39
|
-
loaded = expoConfigLoader(configPath);
|
|
40
|
-
} else {
|
|
41
|
-
try {
|
|
42
|
-
loaded = require(configPath);
|
|
43
|
-
} catch (error) {
|
|
44
|
-
if (error?.code !== 'ERR_REQUIRE_ESM') throw error;
|
|
45
|
-
loaded = await import(pathToFileURL(configPath).href);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const moduleValue = await loaded;
|
|
50
|
-
const exported =
|
|
51
|
-
moduleValue?.__esModule ||
|
|
52
|
-
moduleValue?.[Symbol.toStringTag] === 'Module'
|
|
53
|
-
? moduleValue.default
|
|
54
|
-
: moduleValue;
|
|
55
|
-
const config = await exported;
|
|
56
|
-
return typeof config === 'function' ? config(baseConfig) : config;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
module.exports = async function harnessMetroConfig(baseConfig) {
|
|
60
|
-
const projectRoot = path.resolve(
|
|
61
|
-
process.env.MM_HARNESS_METRO_PROJECT_ROOT || process.cwd(),
|
|
62
|
-
);
|
|
63
|
-
const productConfigPath = path.resolve(
|
|
64
|
-
process.env.MM_HARNESS_METRO_BASE_CONFIG ||
|
|
65
|
-
path.join(projectRoot, 'metro.config.js'),
|
|
66
|
-
);
|
|
67
|
-
let productConfig = baseConfig;
|
|
68
|
-
if (fs.existsSync(productConfigPath)) {
|
|
69
|
-
productConfig = await loadProductConfig(
|
|
70
|
-
productConfigPath,
|
|
71
|
-
baseConfig,
|
|
72
|
-
projectRoot,
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const tempRoot = path.join(projectRoot, 'temp');
|
|
77
|
-
const existingBlockList =
|
|
78
|
-
productConfig.resolver?.blockList ?? baseConfig.resolver?.blockList;
|
|
79
|
-
const tempPattern = new RegExp(
|
|
80
|
-
`^${escapeRegExp(tempRoot)}(?:${escapeRegExp(path.sep)}|$)`,
|
|
81
|
-
blockListFlags(existingBlockList),
|
|
82
|
-
);
|
|
83
|
-
return {
|
|
84
|
-
...productConfig,
|
|
85
|
-
resolver: {
|
|
86
|
-
...productConfig.resolver,
|
|
87
|
-
blockList: appendBlockList(
|
|
88
|
-
existingBlockList,
|
|
89
|
-
tempPattern,
|
|
90
|
-
),
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
};
|