Skip to content

Commit 3fb82a1

Browse files
author
lyc
committedNov 9, 2024
chore(lib/fetch): dynamic import @supabase/node-fetch (#1303)
1 parent 4c7f571 commit 3fb82a1

File tree

3 files changed

+100
-26
lines changed

3 files changed

+100
-26
lines changed
 

‎src/lib/fetch.ts

+4-25
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,17 @@
1-
// @ts-ignore
2-
import nodeFetch, { Headers as NodeFetchHeaders } from '@supabase/node-fetch'
3-
4-
type Fetch = typeof fetch
5-
6-
export const resolveFetch = (customFetch?: Fetch): Fetch => {
7-
let _fetch: Fetch
8-
if (customFetch) {
9-
_fetch = customFetch
10-
} else if (typeof fetch === 'undefined') {
11-
_fetch = nodeFetch as unknown as Fetch
12-
} else {
13-
_fetch = fetch
14-
}
15-
return (...args: Parameters<Fetch>) => _fetch(...args)
16-
}
17-
18-
export const resolveHeadersConstructor = () => {
19-
if (typeof Headers === 'undefined') {
20-
return NodeFetchHeaders
21-
}
22-
23-
return Headers
24-
}
1+
import { resolveFetch, resolveHeadersConstructor } from './helpers'
2+
import { Fetch } from './types'
253

264
export const fetchWithAuth = (
275
supabaseKey: string,
286
getAccessToken: () => Promise<string | null>,
297
customFetch?: Fetch
308
): Fetch => {
319
const fetch = resolveFetch(customFetch)
32-
const HeadersConstructor = resolveHeadersConstructor()
3310

3411
return async (input, init) => {
3512
const accessToken = (await getAccessToken()) ?? supabaseKey
13+
14+
const HeadersConstructor = await resolveHeadersConstructor()
3615
let headers = new HeadersConstructor(init?.headers)
3716

3817
if (!headers.has('apikey')) {

‎src/lib/helpers.ts

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// helpers.ts
2-
import { SupabaseClientOptions } from './types'
2+
import { Fetch, SupabaseClientOptions } from './types'
33

44
export function uuid() {
55
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
@@ -66,3 +66,26 @@ export function applySettingDefaults<
6666

6767
return result
6868
}
69+
70+
export const resolveFetch = (customFetch?: Fetch): Fetch => {
71+
let _fetch: Fetch
72+
if (customFetch) {
73+
_fetch = customFetch
74+
} else if (typeof fetch === 'undefined') {
75+
_fetch = (...args) =>
76+
import('@supabase/node-fetch' as any).then(({ default: fetch }) => fetch(...args))
77+
} else {
78+
_fetch = fetch
79+
}
80+
return (...args: Parameters<Fetch>) => _fetch(...args)
81+
}
82+
83+
export const resolveHeadersConstructor = async () => {
84+
if (typeof Headers === 'undefined') {
85+
return import('@supabase/node-fetch' as any).then(
86+
({ Headers: NodeFetchHeaders }) => NodeFetchHeaders as typeof Headers
87+
)
88+
}
89+
90+
return Headers
91+
}

‎test/helpers.test.ts

+72
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,75 @@ test('override setting defaults', async () => {
4040
// Existing property values should remain constant
4141
expect(settings.db.schema).toBe(defaults.db.schema)
4242
})
43+
44+
describe('resolveFetch', () => {
45+
const TEST_URL = 'https://example.com'
46+
const TEST_OPTIONS = { method: 'GET' }
47+
48+
beforeEach(() => {
49+
// Reset any mocks between tests
50+
jest.resetModules()
51+
jest.clearAllMocks()
52+
})
53+
54+
test('should use custom fetch if provided', async () => {
55+
const customFetch = jest.fn()
56+
const resolvedFetch = helpers.resolveFetch(customFetch)
57+
58+
await resolvedFetch(TEST_URL, TEST_OPTIONS)
59+
60+
expect(customFetch).toHaveBeenCalledTimes(1)
61+
expect(customFetch).toHaveBeenCalledWith(TEST_URL, TEST_OPTIONS)
62+
})
63+
64+
test('should use global fetch if no custom fetch is provided', async () => {
65+
const globalFetch = jest.fn()
66+
global.fetch = globalFetch
67+
const resolvedFetch = helpers.resolveFetch()
68+
69+
await resolvedFetch(TEST_URL, TEST_OPTIONS)
70+
71+
expect(globalFetch).toHaveBeenCalledTimes(1)
72+
expect(globalFetch).toHaveBeenCalledWith(TEST_URL, TEST_OPTIONS)
73+
})
74+
75+
test('should use node-fetch if global fetch is not available', async () => {
76+
const nodeFetch = jest.fn()
77+
jest.mock('@supabase/node-fetch', () => nodeFetch)
78+
79+
global.fetch = undefined as any
80+
const resolvedFetch = helpers.resolveFetch()
81+
82+
await resolvedFetch(TEST_URL, TEST_OPTIONS)
83+
84+
expect(nodeFetch).toHaveBeenCalledTimes(1)
85+
expect(nodeFetch).toHaveBeenCalledWith(TEST_URL, TEST_OPTIONS)
86+
})
87+
})
88+
89+
describe('resolveHeadersConstructor', () => {
90+
beforeEach(() => {
91+
// Reset any mocks between tests
92+
jest.resetModules()
93+
jest.clearAllMocks()
94+
})
95+
96+
test('should use Headers if available', async () => {
97+
const resolvedHeadersConstructor = await helpers.resolveHeadersConstructor()
98+
expect(resolvedHeadersConstructor).toBe(Headers)
99+
})
100+
101+
test('should use node-fetch Headers if global Headers is not available', async () => {
102+
const MockHeaders = jest.fn()
103+
jest.mock('@supabase/node-fetch', () => ({
104+
Headers: MockHeaders,
105+
}))
106+
107+
// Cannot assign read-only property, delete is available
108+
// @ts-ignore
109+
delete global.Headers
110+
111+
const resolvedHeadersConstructor = await helpers.resolveHeadersConstructor()
112+
expect(resolvedHeadersConstructor).toBe(MockHeaders)
113+
})
114+
})

0 commit comments

Comments
 (0)
Failed to load comments.