export type Json = | string | number | boolean | null | { [key: string]: Json | undefined } | Json[] export type JsonRecord = { [key: string]: Json | undefined } export type HttpMethod = | 'GET' | 'HEAD' | 'OPTIONS' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' export type DryRunResult = { dryRun: true method: string path: string url?: string headers?: Record query?: Record body?: T | Json | undefined } export type SupabaseAuthInput = { account?: string secretName?: string patSecretName?: string serviceRoleSecretName?: string projectRef?: string projectUrl?: string } export type MutationFlags = { dryRun?: boolean confirm?: boolean } export class SupabaseRequestError extends Error { readonly status: number readonly method: string readonly path: string readonly code?: string constructor(input: { message: string status: number method: string path: string code?: string }) { super(input.message) this.name = 'SupabaseRequestError' this.status = input.status this.method = input.method this.path = input.path this.code = input.code } } /** * Shared Supabase types and small parsing helpers for this package. * Import from `kody:@kody/supabase/types` when typing other Supabase exports. * * @returns Catalog of exported type/helper names for agent discovery. * * @example * import types from 'kody:@kody/supabase/types' * const catalog = await types() */ export function isRecord(value: unknown): value is JsonRecord { return value !== null && typeof value === 'object' && !Array.isArray(value) } export function requireRecord(value: unknown, label: string): JsonRecord { if (!isRecord(value)) { throw new Error(`${label} must be an object.`) } return value } export function requireString( value: unknown, label: string, options: { allowEmpty?: boolean } = {}, ): string { if (typeof value !== 'string') { throw new Error(`${label} must be a string.`) } const trimmed = value.trim() if (!options.allowEmpty && trimmed.length === 0) { throw new Error(`${label} must not be empty.`) } return trimmed } export function optionalString(value: unknown, label: string): string | undefined { if (value === undefined || value === null) return undefined return requireString(value, label) } export function optionalBoolean(value: unknown, label: string): boolean | undefined { if (value === undefined || value === null) return undefined if (typeof value !== 'boolean') { throw new Error(`${label} must be a boolean.`) } return value } export function optionalStringArray( value: unknown, label: string, ): Array | undefined { if (value === undefined || value === null) return undefined if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) { throw new Error(`${label} must be an array of strings.`) } return value.map((item) => item.trim()).filter((item) => item.length > 0) } export function clampInt( value: unknown, min: number, max: number, fallback: number, label = 'limit', ): number { if (value === undefined || value === null) return fallback if (typeof value !== 'number' || !Number.isFinite(value)) { throw new Error(`${label} must be a finite number.`) } const rounded = Math.trunc(value) return Math.min(max, Math.max(min, rounded)) } export function compactRecord(value: T): T { const next: JsonRecord = {} for (const [key, item] of Object.entries(value)) { if (item !== undefined) next[key] = item } return next as T } export function isReadMethod(method: string): boolean { return method === 'GET' || method === 'HEAD' || method === 'OPTIONS' } export function normalizeHttpMethod(value: unknown, fallback: HttpMethod): HttpMethod { const method = (optionalString(value, 'method') ?? fallback).toUpperCase() switch (method) { case 'GET': case 'HEAD': case 'OPTIONS': case 'POST': case 'PUT': case 'PATCH': case 'DELETE': return method default: throw new Error(`Unsupported HTTP method: ${method}`) } } /** * Shared Supabase types for this package. * @example * import types from 'kody:@kody/supabase/types' * const catalog = await types() */ export default async function typesOverview() { return { package: '@kody/supabase', exports: [ 'DryRunResult', 'SupabaseAuthInput', 'SupabaseRequestError', 'HttpMethod', ], } }