Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/supabase

src/table.ts

83 lines · 2.6 KB · TypeScript
import { optionalString, requireString, type JsonRecord } from './types.ts'

const TABLE_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/
const SCHEMA_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/

export function requireTable(value: unknown): string {
	const table = requireString(value, 'table')
	if (!TABLE_PATTERN.test(table)) {
		throw new Error('table must be a simple identifier (letters, digits, underscore).')
	}
	return table
}

export function optionalSchema(value: unknown): string | undefined {
	const schema = optionalString(value, 'schema')
	if (!schema) return undefined
	if (!SCHEMA_PATTERN.test(schema)) {
		throw new Error('schema must be a simple identifier (letters, digits, underscore).')
	}
	return schema
}

export function restPath(table: string): string {
	return `/rest/v1/${encodeURIComponent(table)}`
}

export function profileHeaders(schema?: string): Record<string, string> {
	if (!schema) return {}
	return {
		'Accept-Profile': schema,
		'Content-Profile': schema,
	}
}

export function filterQuery(
	filters?: Record<string, string>,
	extra?: Record<string, string | number | boolean | undefined>,
): Record<string, string | number | boolean | undefined> {
	return {
		...filters,
		...extra,
	}
}

export function readFilters(value: unknown): Record<string, string> | undefined {
	if (value === undefined || value === null) return undefined
	if (typeof value !== 'object' || Array.isArray(value)) {
		throw new Error('filters must be an object of PostgREST operators, like { id: "eq.1" }.')
	}
	const filters: Record<string, string> = {}
	for (const [key, item] of Object.entries(value)) {
		if (typeof item !== 'string' || item.trim().length === 0) {
			throw new Error(`filters.${key} must be a non-empty PostgREST filter string.`)
		}
		if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
			throw new Error(`filters key "${key}" must be a column identifier.`)
		}
		filters[key] = item.trim()
	}
	return Object.keys(filters).length > 0 ? filters : undefined
}

export function readRows(value: unknown): Array<JsonRecord> {
	if (!Array.isArray(value) || value.length === 0) {
		throw new Error('rows must be a non-empty array of objects.')
	}
	return value.map((row, index) => {
		if (row === null || typeof row !== 'object' || Array.isArray(row)) {
			throw new Error(`rows[${index}] must be an object.`)
		}
		return row as JsonRecord
	})
}

export function readPatch(value: unknown): JsonRecord {
	if (value === null || typeof value !== 'object' || Array.isArray(value)) {
		throw new Error('values must be an object of columns to update.')
	}
	if (Object.keys(value).length === 0) {
		throw new Error('values must include at least one column.')
	}
	return value as JsonRecord
}