@deeeed/metamask-harness 0.94.4 → 0.94.5

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 CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.94.5 - 2026-10-28
6
+
7
+ ### Fixed
8
+
9
+ - Extension readiness uses the registered EVM background action and preserves its result when the optional Perps read times out, keeping genuine RPC failures fatal.
10
+
5
11
  ## 0.94.4 - 2026-10-27
6
12
 
7
13
  ### Changed
@@ -328,7 +328,7 @@ async function boundedCdpCall(session, method, params, timeoutMs) {
328
328
  async function evaluateHealth(session, timeoutMs) {
329
329
  const backgroundProbeTimeoutMs = extensionBackgroundProbeTimeoutMs(timeoutMs);
330
330
  const result = await boundedCdpCall(session, "Runtime.evaluate", {
331
- expression: `(() => {
331
+ expression: `(async () => {
332
332
  const hooks = globalThis.stateHooks || {};
333
333
  const bodyText = document.body?.innerText || '';
334
334
  const storeState = hooks.store?.getState?.() || {};
@@ -336,78 +336,80 @@ async function evaluateHealth(session, timeoutMs) {
336
336
  const accountCache = manager?.account?.cache;
337
337
  const networkConnectionBanner = storeState.metamask?.networkConnectionBanner;
338
338
  const networkClientId = storeState.metamask?.selectedNetworkClientId || networkConnectionBanner?.networkClientId;
339
- return Promise.race([
340
- (async () => {
341
- const [backgroundProbe, evmRpcProbe] = await Promise.all([
342
- (async () => {
343
- if (typeof hooks.submitRequestToBackground !== 'function') {
344
- return { ok: false, error: 'submitRequestToBackground is not a function' };
345
- }
346
- try {
347
- const accountState = await hooks.submitRequestToBackground('perpsGetAccountState', []);
348
- const ok = Boolean(accountState && typeof accountState === 'object');
349
- return { ok, error: ok ? null : 'perpsGetAccountState returned an empty result' };
350
- } catch (error) {
351
- return { ok: false, error: String(error?.message || error) };
352
- }
353
- })(),
354
- (async () => {
355
- if (typeof hooks.submitRequestToBackground !== 'function') {
356
- return { ok: false, error: 'submitRequestToBackground is not a function' };
357
- }
358
- if (typeof networkClientId !== 'string' || networkClientId.length === 0) {
359
- return { ok: false, error: 'selected EVM network client is unavailable' };
360
- }
361
- try {
362
- const code = await hooks.submitRequestToBackground('getCode', [
363
- '0x0000000000000000000000000000000000000000',
364
- networkClientId,
365
- ]);
366
- const ok = typeof code === 'string' && code.startsWith('0x');
367
- return { ok, error: ok ? null : 'background EVM RPC returned an invalid result' };
368
- } catch (error) {
369
- return { ok: false, error: String(error?.message || error) };
370
- }
371
- })(),
339
+ async function withDeadline(read, error) {
340
+ let timer;
341
+ try {
342
+ return await Promise.race([
343
+ read,
344
+ new Promise((resolve) => {
345
+ timer = setTimeout(() => resolve({ ok: false, error }), ${backgroundProbeTimeoutMs});
346
+ }),
372
347
  ]);
373
- return {
374
- href: location.href,
375
- title: document.title,
376
- hasPageContent: bodyText.trim().length > 0,
377
- hookKeys: Object.keys(hooks),
378
- hasSubmitRequest: typeof hooks.submitRequestToBackground === 'function',
379
- hasStore: Boolean(hooks.store),
380
- hasPerpsStreamManager: typeof hooks.getPerpsStreamManager === 'function',
381
- backgroundUnresponsive: bodyText.includes('Background connection unresponsive') || bodyText.includes('MetaMask had trouble starting'),
382
- ethereumConnectionUnavailable: bodyText.includes('Unable to connect to Ethereum'),
383
- activeProvider: storeState.metamask?.activeProvider || null,
384
- isTestnet: Boolean(storeState.metamask?.isTestnet),
385
- perpsManagerInitialized: Boolean(manager?.isInitialized?.()),
386
- positionsCacheIsArray: Array.isArray(manager?.positions?.cache),
387
- ordersCacheIsArray: Array.isArray(manager?.orders?.cache),
388
- accountCachePresent: Boolean(accountCache && typeof accountCache === 'object'),
389
- backgroundProbeOk: backgroundProbe.ok,
390
- backgroundProbeError: backgroundProbe.error,
391
- evmRpcProbeOk: evmRpcProbe.ok,
392
- evmRpcProbeError: evmRpcProbe.error,
393
- };
394
- })(),
395
- new Promise((resolve) => setTimeout(() => resolve({
396
- href: location.href,
397
- title: document.title,
398
- hasPageContent: bodyText.trim().length > 0,
399
- hookKeys: Object.keys(hooks),
400
- hasSubmitRequest: typeof hooks.submitRequestToBackground === 'function',
401
- hasStore: Boolean(hooks.store),
402
- hasPerpsStreamManager: typeof hooks.getPerpsStreamManager === 'function',
403
- backgroundUnresponsive: bodyText.includes('Background connection unresponsive') || bodyText.includes('MetaMask had trouble starting'),
404
- ethereumConnectionUnavailable: bodyText.includes('Unable to connect to Ethereum'),
405
- backgroundProbeOk: false,
406
- backgroundProbeError: 'perpsGetAccountState timed out after ${backgroundProbeTimeoutMs}ms',
407
- evmRpcProbeOk: false,
408
- evmRpcProbeError: 'background EVM RPC timed out after ${backgroundProbeTimeoutMs}ms',
409
- }), ${backgroundProbeTimeoutMs})),
348
+ } finally {
349
+ clearTimeout(timer);
350
+ }
351
+ }
352
+ const [backgroundProbe, evmRpcProbe] = await Promise.all([
353
+ withDeadline((async () => {
354
+ if (typeof hooks.submitRequestToBackground !== 'function') {
355
+ return { ok: false, error: 'submitRequestToBackground is not a function' };
356
+ }
357
+ try {
358
+ const accountState = await hooks.submitRequestToBackground('perpsGetAccountState', []);
359
+ const ok = Boolean(accountState && typeof accountState === 'object');
360
+ return { ok, error: ok ? null : 'perpsGetAccountState returned an empty result' };
361
+ } catch (error) {
362
+ return { ok: false, error: String(error?.message || error) };
363
+ }
364
+ })(), 'perpsGetAccountState timed out after ${backgroundProbeTimeoutMs}ms'),
365
+ withDeadline((async () => {
366
+ if (typeof hooks.submitRequestToBackground !== 'function') {
367
+ return { ok: false, error: 'submitRequestToBackground is not a function' };
368
+ }
369
+ if (typeof networkClientId !== 'string' || networkClientId.length === 0) {
370
+ return { ok: false, error: 'selected EVM network client is unavailable' };
371
+ }
372
+ try {
373
+ const args = ['0x0000000000000000000000000000000000000000', networkClientId];
374
+ let code;
375
+ try {
376
+ code = await hooks.submitRequestToBackground('messengerCall', ['LegacyBackgroundApiService:getCode', args]);
377
+ } catch (error) {
378
+ // Older builds may lack the messenger API or its EVM action.
379
+ const message = String(error?.message || error);
380
+ const missingApi = error?.code === -32601 && message === 'messengerCall not found';
381
+ const missingAction = message.includes('A handler for LegacyBackgroundApiService:getCode has not been registered');
382
+ if (!missingApi && !missingAction) throw error;
383
+ code = await hooks.submitRequestToBackground('getCode', args);
384
+ }
385
+ const ok = typeof code === 'string' && code.startsWith('0x');
386
+ return { ok, error: ok ? null : 'background EVM RPC returned an invalid result' };
387
+ } catch (error) {
388
+ return { ok: false, error: String(error?.message || error) };
389
+ }
390
+ })(), 'background EVM RPC timed out after ${backgroundProbeTimeoutMs}ms'),
410
391
  ]);
392
+ return {
393
+ href: location.href,
394
+ title: document.title,
395
+ hasPageContent: bodyText.trim().length > 0,
396
+ hookKeys: Object.keys(hooks),
397
+ hasSubmitRequest: typeof hooks.submitRequestToBackground === 'function',
398
+ hasStore: Boolean(hooks.store),
399
+ hasPerpsStreamManager: typeof hooks.getPerpsStreamManager === 'function',
400
+ backgroundUnresponsive: bodyText.includes('Background connection unresponsive') || bodyText.includes('MetaMask had trouble starting'),
401
+ ethereumConnectionUnavailable: bodyText.includes('Unable to connect to Ethereum'),
402
+ activeProvider: storeState.metamask?.activeProvider || null,
403
+ isTestnet: Boolean(storeState.metamask?.isTestnet),
404
+ perpsManagerInitialized: Boolean(manager?.isInitialized?.()),
405
+ positionsCacheIsArray: Array.isArray(manager?.positions?.cache),
406
+ ordersCacheIsArray: Array.isArray(manager?.orders?.cache),
407
+ accountCachePresent: Boolean(accountCache && typeof accountCache === 'object'),
408
+ backgroundProbeOk: backgroundProbe.ok,
409
+ backgroundProbeError: backgroundProbe.error,
410
+ evmRpcProbeOk: evmRpcProbe.ok,
411
+ evmRpcProbeError: evmRpcProbe.error,
412
+ };
411
413
  })()`,
412
414
  awaitPromise: true,
413
415
  returnByValue: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.94.4",
3
+ "version": "0.94.5",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mm-harness": "bin/mm-harness"