@supabase/supabase-js
Version:
Isomorphic Javascript SDK for Supabase
101 lines (83 loc) • 2.9 kB
text/typescript
import {
extractTraceContext,
parseTraceParent,
shouldPropagateToTarget,
getDefaultPropagationTargets,
type TraceContext,
type TracePropagationTarget,
} from '@supabase/tracing'
import type { TracePropagationOptions } from './types'
type Fetch = typeof fetch
export const resolveFetch = (customFetch?: Fetch): Fetch => {
if (customFetch) {
return (...args: Parameters<Fetch>) => customFetch(...args)
}
return (...args: Parameters<Fetch>) => fetch(...args)
}
export const resolveHeadersConstructor = () => {
return Headers
}
export const fetchWithAuth = (
supabaseKey: string,
supabaseUrl: string,
getAccessToken: () => Promise<string | null>,
customFetch?: Fetch,
tracePropagationOptions?: TracePropagationOptions
): Fetch => {
const fetch = resolveFetch(customFetch)
const HeadersConstructor = resolveHeadersConstructor()
// Pre-compute trace propagation state once. When disabled, the per-request
// path skips all tracing work with a single truthy check.
const traceEnabled = tracePropagationOptions?.enabled === true
const respectSampling = tracePropagationOptions?.respectSamplingDecision !== false
const traceTargets: TracePropagationTarget[] | null = traceEnabled
? getDefaultPropagationTargets(supabaseUrl)
: null
return async (input, init) => {
const accessToken = (await getAccessToken()) ?? supabaseKey
let headers = new HeadersConstructor(init?.headers)
if (!headers.has('apikey')) {
headers.set('apikey', supabaseKey)
}
if (!headers.has('Authorization')) {
headers.set('Authorization', `Bearer ${accessToken}`)
}
if (traceTargets) {
const traceHeaders = await getTraceHeaders(input, traceTargets, respectSampling)
if (traceHeaders) {
if (traceHeaders.traceparent && !headers.has('traceparent')) {
headers.set('traceparent', traceHeaders.traceparent)
}
if (traceHeaders.tracestate && !headers.has('tracestate')) {
headers.set('tracestate', traceHeaders.tracestate)
}
if (traceHeaders.baggage && !headers.has('baggage')) {
headers.set('baggage', traceHeaders.baggage)
}
}
}
return fetch(input, { ...init, headers })
}
}
async function getTraceHeaders(
input: RequestInfo | URL,
targets: TracePropagationTarget[],
respectSampling: boolean
): Promise<TraceContext | null> {
const targetUrl: string | URL =
typeof input === 'string' ? input : input instanceof URL ? input : input.url
if (!shouldPropagateToTarget(targetUrl, targets)) {
return null
}
const traceContext = await extractTraceContext()
if (!traceContext || !traceContext.traceparent) {
return null
}
if (respectSampling) {
const parsed = parseTraceParent(traceContext.traceparent)
if (parsed && !parsed.isSampled) {
return null
}
}
return traceContext
}