Skip to content

Commit f843f87

Browse files
authored
fix failing api calls due to inactive service worker (#731)
1 parent ee2a2bd commit f843f87

File tree

3 files changed

+124
-4
lines changed

3 files changed

+124
-4
lines changed

.changeset/tender-flies-build.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wallet": patch
3+
---
4+
5+
fix failing api calls due to inactive service worker

apps/wallet/src/data/api.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import { createTRPCProxyClient } from '@trpc/client'
55
import { initTRPC } from '@trpc/server'
66
import superjson from 'superjson'
77
import { createChromeHandler } from 'trpc-chrome/adapter'
8-
import { chromeLink } from 'trpc-chrome/link'
98
import { z } from 'zod'
109

1110
import * as bitcoin from './bitcoin/bitcoin'
11+
import { chromeLinkWithRetries } from './chromeLink'
1212
import * as ethereum from './ethereum/ethereum'
1313
import { getKeystore } from './keystore'
1414
import * as solana from './solana/solana'
@@ -601,10 +601,8 @@ export async function createAPI() {
601601
}
602602

603603
export function createAPIClient() {
604-
const port = chrome.runtime.connect()
605-
606604
return createTRPCProxyClient<APIRouter>({
607-
links: [chromeLink({ port })],
605+
links: [chromeLinkWithRetries()],
608606
transformer: superjson,
609607
})
610608
}

apps/wallet/src/data/chromeLink.ts

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { chromeLink } from 'trpc-chrome/link'
2+
3+
import type { TRPCLink } from '@trpc/client'
4+
import type { AnyRouter } from '@trpc/server'
5+
import type { ChromeLinkOptions } from 'trpc-chrome/link'
6+
7+
const MAX_RETRIES = 3
8+
const RETRY_DELAY = 100
9+
10+
export function chromeLinkWithRetries<TRouter extends AnyRouter>(
11+
options: Omit<ChromeLinkOptions, 'port'> = {},
12+
): TRPCLink<TRouter> {
13+
const chromeLinkOptions = options
14+
15+
let port: chrome.runtime.Port | null = null
16+
let currentLink: TRPCLink<TRouter> | null = null
17+
18+
const createPort = () => {
19+
if (port) {
20+
try {
21+
port.disconnect()
22+
// eslint-disable-next-line no-empty
23+
} catch {}
24+
}
25+
26+
port = chrome.runtime.connect()
27+
port.onDisconnect.addListener(() => {
28+
port = null
29+
currentLink = null
30+
})
31+
32+
currentLink = chromeLink({ ...chromeLinkOptions, port })
33+
34+
return port
35+
}
36+
37+
const checkConnection = () => {
38+
if (!port || !currentLink) {
39+
createPort()
40+
} else {
41+
try {
42+
port.postMessage({ type: 'ping' })
43+
if (chrome.runtime.lastError) {
44+
createPort()
45+
}
46+
} catch {
47+
createPort()
48+
}
49+
}
50+
}
51+
52+
// @ts-expect-error - Custom observable missing pipe method but functionally compatible
53+
return runtime => {
54+
return ctx => {
55+
let retryCount = 0
56+
57+
const isConnectionError = (error: unknown): boolean => {
58+
return (
59+
error instanceof Error &&
60+
(error.message.includes('Attempting to use a disconnected port') ||
61+
error.message.includes('Failed to establish connection') ||
62+
error.message.includes('Extension context invalidated') ||
63+
chrome.runtime.lastError !== undefined)
64+
)
65+
}
66+
67+
const handleConnectionError = (error: unknown) => {
68+
if (isConnectionError(error) && retryCount < MAX_RETRIES) {
69+
retryCount++
70+
port = null
71+
currentLink = null
72+
setTimeout(() => retryOperation(), RETRY_DELAY * retryCount)
73+
} else {
74+
observer.error?.(error)
75+
}
76+
}
77+
78+
return {
79+
subscribe(observer) {
80+
let subscription: { unsubscribe: () => void } | null = null
81+
82+
const retryOperation = () => {
83+
try {
84+
checkConnection()
85+
86+
if (!currentLink) {
87+
throw new Error('Failed to establish connection')
88+
}
89+
90+
subscription = currentLink!(runtime)(ctx).subscribe({
91+
next(value) {
92+
observer.next?.(
93+
value as Parameters<NonNullable<typeof observer.next>>[0],
94+
)
95+
observer.complete?.()
96+
},
97+
error(err) {
98+
handleConnectionError(err)
99+
},
100+
})
101+
} catch (error) {
102+
handleConnectionError(error)
103+
}
104+
}
105+
106+
retryOperation()
107+
108+
return {
109+
unsubscribe: () => {
110+
subscription?.unsubscribe()
111+
},
112+
}
113+
},
114+
}
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)