Skip to content

feat: add standard client headers #1494

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 150 additions & 1 deletion src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ import { RealtimeClientOptions } from '@supabase/realtime-js'
import { SupabaseAuthClientOptions } from './types'
import { version } from './version'

// Type definitions for User-Agent Client Hints API
interface NavigatorUAData {
platform: string
platformVersion?: string
}

interface NavigatorWithUAData extends Navigator {
userAgentData?: NavigatorUAData
}

let JS_ENV = ''
// @ts-ignore
if (typeof Deno !== 'undefined') {
Expand All @@ -15,7 +25,146 @@ if (typeof Deno !== 'undefined') {
JS_ENV = 'node'
}

export const DEFAULT_HEADERS = { 'X-Client-Info': `supabase-js-${JS_ENV}/${version}` }
function getRuntimeName(): string | undefined {
// @ts-ignore
if (typeof Deno !== 'undefined') {
return 'deno'
}

if (typeof process !== 'undefined' && process.versions) {
if (process.versions.bun) {
return 'bun'
}
if (process.versions.node) {
return 'node'
}
}

if (typeof navigator !== 'undefined') {
if (navigator.product === 'ReactNative') {
return 'react-native'
}
return 'web'
}

return undefined
}

function getRuntimeVersion(): string | undefined {
// @ts-ignore
if (typeof Deno !== 'undefined') {
// @ts-ignore
return Deno.version?.deno
}

if (typeof process !== 'undefined' && process.versions) {
if (process.versions.bun) {
return process.versions.bun
}
if (process.versions.node) {
return process.versions.node
}
}

return undefined
}

function getPlatformName(): string | undefined {
if (typeof process !== 'undefined' && process.versions?.node) {
switch (process.platform) {
case 'win32':
return 'windows'
case 'darwin':
return 'macos'
case 'linux':
return detectLinuxPlatform()
default:
return undefined
}
}

if (typeof navigator !== 'undefined') {
const nav = navigator as NavigatorWithUAData
if ('userAgentData' in navigator && nav.userAgentData?.platform) {
return normalizePlatform(nav.userAgentData.platform)
}
}

return undefined
}

function normalizePlatform(platform: string): string | undefined {
switch (platform.toLowerCase()) {
case 'windows':
return 'Windows'
case 'macos':
return 'macOS'
case 'linux':
return 'Linux'
case 'android':
return 'Android'
case 'chrome os':
return 'Chrome OS'
default:
return undefined
}
}

function detectLinuxPlatform(): string | undefined {
try {
const fs = require('fs')
const osRelease = fs.readFileSync('/etc/os-release', 'utf8')
if (osRelease.includes('Chrome OS')) return 'Chrome OS'
if (osRelease.includes('Chromium OS')) return 'Chromium OS'
} catch {
// If the file isn't readable, assume generic Linux
}
return undefined
}

function getPlatformVersion(): string | undefined {
// Check if running in Node.js
if (typeof process !== 'undefined' && process.versions?.node) {
return detectNodeVersion()
}

// Check if running in a browser
if (typeof navigator !== 'undefined') {
return detectBrowserVersion()
}

return undefined
}

// Get OS version in Node.js
function detectNodeVersion(): string | undefined {
try {
const os = require('os')
return os.release() // Returns the OS kernel version
} catch {
return undefined
}
}

// Get OS version in Browsers
function detectBrowserVersion(): string | undefined {
if (typeof navigator !== 'undefined') {
const nav = navigator as NavigatorWithUAData
if ('userAgentData' in navigator && nav.userAgentData?.platformVersion) {
return nav.userAgentData.platformVersion
}
}

return undefined
}

export const DEFAULT_HEADERS = {
'X-Client-Info': `supabase-js-${JS_ENV}/${version}`,
'X-Supabase-Client-Platform': getPlatformName(),
'X-Supabase-Client-Platform-Version': getPlatformVersion(),
'X-Supabase-Client-Runtime': getRuntimeName(),
'X-Supabase-Client-Runtime-Version': getRuntimeVersion(),
}

export const DEFAULT_GLOBAL_OPTIONS = {
headers: DEFAULT_HEADERS,
Expand Down
Loading