summaryrefslogtreecommitdiffhomepage
path: root/packages/util/src
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-12-20 04:57:39 -0600
committerAdam <[email protected]>2025-12-20 04:57:39 -0600
commit49567fe61aea76871866bb816f27743f57708af2 (patch)
treeee42f7b3d33875a28668575d2a18c95a7cb9e436 /packages/util/src
parente5b3f796e4c2e2ff12939dfc5e523ab9bd042f51 (diff)
downloadopencode-49567fe61aea76871866bb816f27743f57708af2.tar.gz
opencode-49567fe61aea76871866bb816f27743f57708af2.zip
fix(desktop): add retries to init promises
Diffstat (limited to 'packages/util/src')
-rw-r--r--packages/util/src/retry.ts41
1 files changed, 41 insertions, 0 deletions
diff --git a/packages/util/src/retry.ts b/packages/util/src/retry.ts
new file mode 100644
index 000000000..0014a604c
--- /dev/null
+++ b/packages/util/src/retry.ts
@@ -0,0 +1,41 @@
+export interface RetryOptions {
+ attempts?: number
+ delay?: number
+ factor?: number
+ maxDelay?: number
+ retryIf?: (error: unknown) => boolean
+}
+
+const TRANSIENT_MESSAGES = [
+ "load failed",
+ "network connection was lost",
+ "network request failed",
+ "failed to fetch",
+ "econnreset",
+ "econnrefused",
+ "etimedout",
+ "socket hang up",
+]
+
+function isTransientError(error: unknown): boolean {
+ if (!error) return false
+ const message = String(error instanceof Error ? error.message : error).toLowerCase()
+ return TRANSIENT_MESSAGES.some((m) => message.includes(m))
+}
+
+export async function retry<T>(fn: () => Promise<T>, options: RetryOptions = {}): Promise<T> {
+ const { attempts = 3, delay = 500, factor = 2, maxDelay = 10000, retryIf = isTransientError } = options
+
+ let lastError: unknown
+ for (let attempt = 0; attempt < attempts; attempt++) {
+ try {
+ return await fn()
+ } catch (error) {
+ lastError = error
+ if (attempt === attempts - 1 || !retryIf(error)) throw error
+ const wait = Math.min(delay * Math.pow(factor, attempt), maxDelay)
+ await new Promise((resolve) => setTimeout(resolve, wait))
+ }
+ }
+ throw lastError
+}