UNPKG

@supabase/postgrest-js

Version:
1 lines 195 kB
{"version":3,"file":"index.cjs","names":["this","res: Response","fetchError: any","res","count: number | null","fetch","fetch","method: 'HEAD' | 'GET' | 'POST'","body: unknown | undefined"],"sources":["../src/types/common/common.ts","../src/PostgrestError.ts","../src/PostgrestBuilder.ts","../src/PostgrestTransformBuilder.ts","../src/PostgrestFilterBuilder.ts","../src/PostgrestQueryBuilder.ts","../src/PostgrestClient.ts","../src/index.ts"],"sourcesContent":["// Types that are shared between supabase-js and postgrest-js\n\nexport type Fetch = typeof fetch\n\n/**\n * Default number of retry attempts.\n */\nexport const DEFAULT_MAX_RETRIES = 3\n\n/**\n * Default exponential backoff delay function.\n * Delays: 1s, 2s, 4s, 8s, ... (max 30s)\n *\n * @param attemptIndex - Zero-based index of the retry attempt\n * @returns Delay in milliseconds before the next retry\n */\nexport const getRetryDelay = (attemptIndex: number): number =>\n Math.min(1000 * 2 ** attemptIndex, 30000)\n\n/**\n * Status codes that are safe to retry.\n * 520 = Cloudflare timeout/connection errors (transient)\n * 503 = PostgREST schema cache not yet loaded (transient, signals retry via Retry-After header)\n */\nexport const RETRYABLE_STATUS_CODES = [520, 503] as const\n\n/**\n * HTTP methods that are safe to retry (idempotent operations).\n */\nexport const RETRYABLE_METHODS = ['GET', 'HEAD', 'OPTIONS'] as const\n\nexport type GenericRelationship = {\n foreignKeyName: string\n columns: string[]\n isOneToOne?: boolean\n referencedRelation: string\n referencedColumns: string[]\n}\n\nexport type GenericTable = {\n Row: Record<string, unknown>\n Insert: Record<string, unknown>\n Update: Record<string, unknown>\n Relationships: GenericRelationship[]\n}\n\nexport type GenericUpdatableView = {\n Row: Record<string, unknown>\n Insert: Record<string, unknown>\n Update: Record<string, unknown>\n Relationships: GenericRelationship[]\n}\n\nexport type GenericNonUpdatableView = {\n Row: Record<string, unknown>\n Relationships: GenericRelationship[]\n}\n\nexport type GenericView = GenericUpdatableView | GenericNonUpdatableView\n\nexport type GenericSetofOption = {\n isSetofReturn?: boolean | undefined\n isOneToOne?: boolean | undefined\n isNotNullable?: boolean | undefined\n to: string\n from: string\n}\n\nexport type GenericFunction = {\n Args: Record<string, unknown> | never\n Returns: unknown\n SetofOptions?: GenericSetofOption\n}\n\nexport type GenericSchema = {\n Tables: Record<string, GenericTable>\n Views: Record<string, GenericView>\n Functions: Record<string, GenericFunction>\n}\n\nexport type ClientServerOptions = {\n PostgrestVersion?: string\n}\n","/**\n * Error format\n *\n * {@link https://postgrest.org/en/stable/api.html?highlight=options#errors-and-http-status-codes}\n */\nexport default class PostgrestError extends Error {\n details: string\n hint: string\n code: string\n\n /**\n * @example\n * ```ts\n * import PostgrestError from '@supabase/postgrest-js'\n *\n * throw new PostgrestError({\n * message: 'Row level security prevented the request',\n * details: 'RLS denied the insert',\n * hint: 'Check your policies',\n * code: 'PGRST301',\n * })\n * ```\n */\n constructor(context: { message: string; details: string; hint: string; code: string }) {\n super(context.message)\n this.name = 'PostgrestError'\n this.details = context.details\n this.hint = context.hint\n this.code = context.code\n }\n\n toJSON(): { name: string; message: string; details: string; hint: string; code: string } {\n return {\n name: this.name,\n message: this.message,\n details: this.details,\n hint: this.hint,\n code: this.code,\n }\n }\n}\n","import type {\n PostgrestSingleResponse,\n PostgrestResponseSuccess,\n CheckMatchingArrayTypes,\n MergePartialResult,\n IsValidResultOverride,\n} from './types/types'\nimport {\n ClientServerOptions,\n Fetch,\n DEFAULT_MAX_RETRIES,\n getRetryDelay,\n RETRYABLE_STATUS_CODES,\n RETRYABLE_METHODS,\n} from './types/common/common'\nimport PostgrestError from './PostgrestError'\nimport { ContainsNull } from './select-query-parser/types'\n\n/**\n * Sleep for a given number of milliseconds.\n * If an AbortSignal is provided, the sleep resolves early when the signal is aborted.\n */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal?.aborted) {\n resolve()\n return\n }\n const id = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n function onAbort() {\n clearTimeout(id)\n resolve()\n }\n signal?.addEventListener('abort', onAbort)\n })\n}\n\n/**\n * Check if a request should be retried based on method and status code.\n */\nfunction shouldRetry(\n method: string,\n status: number,\n attemptCount: number,\n retryEnabled: boolean\n): boolean {\n // Don't retry if retries are disabled or we've exhausted attempts\n if (!retryEnabled || attemptCount >= DEFAULT_MAX_RETRIES) {\n return false\n }\n\n // Only retry idempotent methods (GET, HEAD, OPTIONS)\n if (!RETRYABLE_METHODS.includes(method as (typeof RETRYABLE_METHODS)[number])) {\n return false\n }\n\n // Only retry on specific status codes (520 - Cloudflare errors)\n if (!RETRYABLE_STATUS_CODES.includes(status as (typeof RETRYABLE_STATUS_CODES)[number])) {\n return false\n }\n\n return true\n}\n\nexport default abstract class PostgrestBuilder<\n ClientOptions extends ClientServerOptions,\n Result,\n ThrowOnError extends boolean = false,\n> implements\n PromiseLike<\n ThrowOnError extends true ? PostgrestResponseSuccess<Result> : PostgrestSingleResponse<Result>\n >\n{\n protected method: 'GET' | 'HEAD' | 'POST' | 'PATCH' | 'DELETE'\n protected url: URL\n protected headers: Headers\n protected schema?: string\n protected body?: unknown\n protected shouldThrowOnError = false\n protected signal?: AbortSignal\n protected fetch: Fetch\n protected isMaybeSingle: boolean\n protected shouldStripNulls: boolean\n protected urlLengthLimit: number\n\n // Retry configuration - enabled by default\n protected retryEnabled: boolean = true\n\n /**\n * Creates a builder configured for a specific PostgREST request.\n *\n * @example Using supabase-js (recommended)\n * ```ts\n * import { createClient } from '@supabase/supabase-js'\n *\n * const supabase = createClient('https://xyzcompany.supabase.co', 'your-publishable-key')\n * const { data, error } = await supabase.from('users').select('*')\n * ```\n *\n * @category Database\n *\n * @example Standalone import for bundle-sensitive environments\n * ```ts\n * import { PostgrestQueryBuilder } from '@supabase/postgrest-js'\n *\n * const builder = new PostgrestQueryBuilder(\n * new URL('https://xyzcompany.supabase.co/rest/v1/users'),\n * { headers: new Headers({ apikey: 'your-publishable-key' }) }\n * )\n * ```\n */\n constructor(builder: {\n method: 'GET' | 'HEAD' | 'POST' | 'PATCH' | 'DELETE'\n url: URL\n headers: HeadersInit\n schema?: string\n body?: unknown\n shouldThrowOnError?: boolean\n signal?: AbortSignal\n fetch?: Fetch\n isMaybeSingle?: boolean\n shouldStripNulls?: boolean\n urlLengthLimit?: number\n // Retry option\n retry?: boolean\n }) {\n this.method = builder.method\n this.url = builder.url\n this.headers = new Headers(builder.headers)\n this.schema = builder.schema\n this.body = builder.body\n this.shouldThrowOnError = builder.shouldThrowOnError ?? false\n this.signal = builder.signal\n this.isMaybeSingle = builder.isMaybeSingle ?? false\n this.shouldStripNulls = builder.shouldStripNulls ?? false\n this.urlLengthLimit = builder.urlLengthLimit ?? 8000\n this.retryEnabled = builder.retry ?? true\n\n if (builder.fetch) {\n this.fetch = builder.fetch\n } else {\n this.fetch = fetch\n }\n }\n\n /**\n * If there's an error with the query, throwOnError will reject the promise by\n * throwing the error instead of returning it as part of a successful response.\n *\n * {@link https://github.com/supabase/supabase-js/issues/92}\n *\n * @category Database\n */\n throwOnError(): this & PostgrestBuilder<ClientOptions, Result, true> {\n this.shouldThrowOnError = true\n return this as this & PostgrestBuilder<ClientOptions, Result, true>\n }\n\n /**\n * Strip null values from the response data. Properties with `null` values\n * will be omitted from the returned JSON objects.\n *\n * Requires PostgREST 11.2.0+.\n *\n * {@link https://docs.postgrest.org/en/stable/references/api/resource_representation.html#stripped-nulls}\n *\n * @category Database\n * @subcategory Using modifiers\n *\n * @example With `select()`\n * ```ts\n * const { data, error } = await supabase\n * .from('characters')\n * .select()\n * .stripNulls()\n * ```\n *\n * @exampleSql With `select()`\n * ```sql\n * create table\n * characters (id int8 primary key, name text, bio text);\n *\n * insert into\n * characters (id, name, bio)\n * values\n * (1, 'Luke', null),\n * (2, 'Leia', 'Princess of Alderaan');\n * ```\n *\n * @exampleResponse With `select()`\n * ```json\n * {\n * \"data\": [\n * {\n * \"id\": 1,\n * \"name\": \"Luke\"\n * },\n * {\n * \"id\": 2,\n * \"name\": \"Leia\",\n * \"bio\": \"Princess of Alderaan\"\n * }\n * ],\n * \"status\": 200,\n * \"statusText\": \"OK\"\n * }\n * ```\n */\n stripNulls(): this {\n if (this.headers.get('Accept') === 'text/csv') {\n throw new Error('stripNulls() cannot be used with csv()')\n }\n this.shouldStripNulls = true\n return this\n }\n\n /**\n * Set an HTTP header for the request.\n *\n * @category Database\n */\n setHeader(name: string, value: string): this {\n this.headers = new Headers(this.headers)\n this.headers.set(name, value)\n return this\n }\n\n /**\n * @category Database\n *\n * Configure retry behavior for this request.\n *\n * By default, retries are enabled for idempotent requests (GET, HEAD, OPTIONS)\n * that fail with network errors or specific HTTP status codes (503, 520).\n * Retries use exponential backoff (1s, 2s, 4s) with a maximum of 3 attempts.\n *\n * @param enabled - Whether to enable retries for this request\n *\n * @example\n * ```ts\n * // Disable retries for a specific query\n * const { data, error } = await supabase\n * .from('users')\n * .select()\n * .retry(false)\n * ```\n */\n retry(enabled: boolean): this {\n this.retryEnabled = enabled\n return this\n }\n\n then<\n TResult1 = ThrowOnError extends true\n ? PostgrestResponseSuccess<Result>\n : PostgrestSingleResponse<Result>,\n TResult2 = never,\n >(\n onfulfilled?:\n | ((\n value: ThrowOnError extends true\n ? PostgrestResponseSuccess<Result>\n : PostgrestSingleResponse<Result>\n ) => TResult1 | PromiseLike<TResult1>)\n | undefined\n | null,\n onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null\n ): PromiseLike<TResult1 | TResult2> {\n // https://postgrest.org/en/stable/api.html#switching-schemas\n if (this.schema === undefined) {\n // skip\n } else if (['GET', 'HEAD'].includes(this.method)) {\n this.headers.set('Accept-Profile', this.schema)\n } else {\n this.headers.set('Content-Profile', this.schema)\n }\n if (this.method !== 'GET' && this.method !== 'HEAD') {\n this.headers.set('Content-Type', 'application/json')\n }\n\n // https://docs.postgrest.org/en/stable/references/api/resource_representation.html#stripped-nulls\n if (this.shouldStripNulls) {\n const currentAccept = this.headers.get('Accept')\n if (currentAccept === 'application/vnd.pgrst.object+json') {\n this.headers.set('Accept', 'application/vnd.pgrst.object+json;nulls=stripped')\n } else if (!currentAccept || currentAccept === 'application/json') {\n this.headers.set('Accept', 'application/vnd.pgrst.array+json;nulls=stripped')\n }\n }\n\n // NOTE: Invoke w/o `this` to avoid illegal invocation error.\n // https://github.com/supabase/postgrest-js/pull/247\n const _fetch = this.fetch\n\n // Execute fetch with retry logic\n const executeWithRetry = async (): Promise<{\n error: any\n data: any\n count: number | null\n status: number\n statusText: string\n }> => {\n let attemptCount = 0\n\n while (true) {\n const requestHeaders = new Headers(this.headers)\n if (attemptCount > 0) {\n requestHeaders.set('X-Retry-Count', String(attemptCount))\n }\n\n // Only wrap the fetch call itself — processResponse errors must never trigger retries\n let res: Response\n try {\n res = await _fetch(this.url.toString(), {\n method: this.method,\n headers: requestHeaders,\n body: JSON.stringify(this.body, (_, value) =>\n typeof value === 'bigint' ? value.toString() : value\n ),\n signal: this.signal,\n })\n // JS allows throwing any value, and serverless or realm-crossing fetch\n // implementations can reject with non-Error objects. `instanceof Error`\n // is too narrow here; narrow at the use site with optional chaining.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (fetchError: any) {\n // Never retry aborted requests\n if (fetchError?.name === 'AbortError' || fetchError?.code === 'ABORT_ERR') {\n throw fetchError\n }\n\n // Don't retry network errors for non-idempotent methods\n if (!RETRYABLE_METHODS.includes(this.method as (typeof RETRYABLE_METHODS)[number])) {\n throw fetchError\n }\n\n // Check if we should retry network errors\n if (this.retryEnabled && attemptCount < DEFAULT_MAX_RETRIES) {\n const delay = getRetryDelay(attemptCount)\n attemptCount++\n await sleep(delay, this.signal)\n continue\n }\n\n // Exhausted retries or retries disabled, throw the last error\n throw fetchError\n }\n\n // Check if we should retry this HTTP response\n if (shouldRetry(this.method, res.status, attemptCount, this.retryEnabled)) {\n const retryAfterHeader = res.headers?.get('Retry-After') ?? null\n const delay =\n retryAfterHeader !== null\n ? Math.max(0, parseInt(retryAfterHeader, 10) || 0) * 1000\n : getRetryDelay(attemptCount)\n await res.text()\n attemptCount++\n await sleep(delay, this.signal)\n continue\n }\n\n return await this.processResponse(res)\n }\n }\n\n let res = executeWithRetry()\n\n if (!this.shouldThrowOnError) {\n res = res.catch((fetchError) => {\n // Build detailed error information including cause if available\n // Note: We don't populate code/hint for client-side network errors since those\n // fields are meant for upstream service errors (PostgREST/PostgreSQL)\n let errorDetails = ''\n let hint = ''\n let code = ''\n\n // Add cause information if available (e.g., DNS errors, network failures)\n const cause = fetchError?.cause\n if (cause) {\n const causeMessage = cause?.message ?? ''\n const causeCode = cause?.code ?? ''\n\n errorDetails = `${fetchError?.name ?? 'FetchError'}: ${fetchError?.message}`\n errorDetails += `\\n\\nCaused by: ${cause?.name ?? 'Error'}: ${causeMessage}`\n if (causeCode) {\n errorDetails += ` (${causeCode})`\n }\n if (cause?.stack) {\n errorDetails += `\\n${cause.stack}`\n }\n } else {\n // No cause available, just include the error stack\n errorDetails = fetchError?.stack ?? ''\n }\n\n // Get URL length for potential hints\n const urlLength = this.url.toString().length\n\n // Handle AbortError specially with helpful hints\n if (fetchError?.name === 'AbortError' || fetchError?.code === 'ABORT_ERR') {\n code = ''\n hint = 'Request was aborted (timeout or manual cancellation)'\n\n if (urlLength > this.urlLengthLimit) {\n hint += `. Note: Your request URL is ${urlLength} characters, which may exceed server limits. If selecting many fields, consider using views. If filtering with large arrays (e.g., .in('id', [many IDs])), consider using an RPC function to pass values server-side.`\n }\n }\n // Handle HeadersOverflowError from undici (Node.js fetch implementation)\n else if (\n cause?.name === 'HeadersOverflowError' ||\n cause?.code === 'UND_ERR_HEADERS_OVERFLOW'\n ) {\n code = ''\n hint = 'HTTP headers exceeded server limits (typically 16KB)'\n\n if (urlLength > this.urlLengthLimit) {\n hint += `. Your request URL is ${urlLength} characters. If selecting many fields, consider using views. If filtering with large arrays (e.g., .in('id', [200+ IDs])), consider using an RPC function instead.`\n }\n }\n\n return {\n success: false as const,\n error: {\n message: `${fetchError?.name ?? 'FetchError'}: ${fetchError?.message}`,\n details: errorDetails,\n hint: hint,\n code: code,\n },\n data: null,\n count: null,\n status: 0,\n statusText: '',\n }\n })\n }\n\n return (\n res as Promise<\n ThrowOnError extends true\n ? PostgrestResponseSuccess<Result>\n : PostgrestSingleResponse<Result>\n >\n ).then(onfulfilled, onrejected)\n }\n\n /**\n * Process a fetch response and return the standardized postgrest response.\n */\n private async processResponse(res: Response): Promise<{\n success: boolean\n error: any\n data: any\n count: number | null\n status: number\n statusText: string\n }> {\n let error = null\n let data = null\n let count: number | null = null\n let status = res.status\n let statusText = res.statusText\n\n if (res.ok) {\n if (this.method !== 'HEAD') {\n const body = await res.text()\n if (body === '') {\n // Prefer: return=minimal\n } else if (this.headers.get('Accept') === 'text/csv') {\n data = body\n } else if (\n this.headers.get('Accept') &&\n this.headers.get('Accept')?.includes('application/vnd.pgrst.plan+text')\n ) {\n data = body\n } else {\n data = JSON.parse(body)\n }\n }\n\n const countHeader = this.headers.get('Prefer')?.match(/count=(exact|planned|estimated)/)\n const contentRange = res.headers.get('content-range')?.split('/')\n if (countHeader && contentRange && contentRange.length > 1) {\n count = parseInt(contentRange[1])\n }\n\n // Fix for https://github.com/supabase/postgrest-js/issues/361 — applies to all methods.\n if (this.isMaybeSingle && Array.isArray(data)) {\n if (data.length > 1) {\n error = {\n // https://github.com/PostgREST/postgrest/blob/a867d79c42419af16c18c3fb019eba8df992626f/src/PostgREST/Error.hs#L553\n code: 'PGRST116',\n details: `Results contain ${data.length} rows, application/vnd.pgrst.object+json requires 1 row`,\n hint: null,\n message: 'JSON object requested, multiple (or no) rows returned',\n }\n data = null\n count = null\n status = 406\n statusText = 'Not Acceptable'\n } else if (data.length === 1) {\n data = data[0]\n } else {\n data = null\n }\n }\n } else {\n const body = await res.text()\n\n try {\n error = JSON.parse(body)\n\n // Workaround for https://github.com/supabase/postgrest-js/issues/295\n if (Array.isArray(error) && res.status === 404) {\n data = []\n error = null\n status = 200\n statusText = 'OK'\n }\n } catch {\n // Workaround for https://github.com/supabase/postgrest-js/issues/295\n if (res.status === 404 && body === '') {\n status = 204\n statusText = 'No Content'\n } else {\n error = {\n message: body,\n }\n }\n }\n\n if (error && this.shouldThrowOnError) {\n throw new PostgrestError(error)\n }\n }\n\n return {\n success: error === null,\n error,\n data,\n count,\n status,\n statusText,\n }\n }\n\n /**\n * Override the type of the returned `data`.\n *\n * @typeParam NewResult - The new result type to override with\n * @deprecated Use overrideTypes<yourType, { merge: false }>() method at the end of your call chain instead\n *\n * @category Database\n */\n returns<NewResult>(): PostgrestBuilder<\n ClientOptions,\n CheckMatchingArrayTypes<Result, NewResult>,\n ThrowOnError\n > {\n /* istanbul ignore next */\n return this as unknown as PostgrestBuilder<\n ClientOptions,\n CheckMatchingArrayTypes<Result, NewResult>,\n ThrowOnError\n >\n }\n\n /**\n * Override the type of the returned `data` field in the response.\n *\n * @typeParam NewResult - The new type to cast the response data to\n * @typeParam Options - Optional type configuration (defaults to { merge: true })\n * @typeParam Options.merge - When true, merges the new type with existing return type. When false, replaces the existing types entirely (defaults to true)\n * @example\n * ```typescript\n * // Merge with existing types (default behavior)\n * const query = supabase\n * .from('users')\n * .select()\n * .overrideTypes<{ custom_field: string }>()\n *\n * // Replace existing types completely\n * const replaceQuery = supabase\n * .from('users')\n * .select()\n * .overrideTypes<{ id: number; name: string }, { merge: false }>()\n * ```\n * @returns A PostgrestBuilder instance with the new type\n *\n * @category Database\n * @subcategory Using modifiers\n *\n * @example Complete Override type of successful response\n * ```ts\n * const { data } = await supabase\n * .from('countries')\n * .select()\n * .overrideTypes<Array<MyType>, { merge: false }>()\n * ```\n *\n * @exampleResponse Complete Override type of successful response\n * ```ts\n * let x: typeof data // MyType[]\n * ```\n *\n * @example Complete Override type of object response\n * ```ts\n * const { data } = await supabase\n * .from('countries')\n * .select()\n * .maybeSingle()\n * .overrideTypes<MyType, { merge: false }>()\n * ```\n *\n * @exampleResponse Complete Override type of object response\n * ```ts\n * let x: typeof data // MyType | null\n * ```\n *\n * @example Partial Override type of successful response\n * ```ts\n * const { data } = await supabase\n * .from('countries')\n * .select()\n * .overrideTypes<Array<{ status: \"A\" | \"B\" }>>()\n * ```\n *\n * @exampleResponse Partial Override type of successful response\n * ```ts\n * let x: typeof data // Array<CountryRowProperties & { status: \"A\" | \"B\" }>\n * ```\n *\n * @example Partial Override type of object response\n * ```ts\n * const { data } = await supabase\n * .from('countries')\n * .select()\n * .maybeSingle()\n * .overrideTypes<{ status: \"A\" | \"B\" }>()\n * ```\n *\n * @exampleResponse Partial Override type of object response\n * ```ts\n * let x: typeof data // CountryRowProperties & { status: \"A\" | \"B\" } | null\n * ```\n *\n * @example Merge vs replace existing types\n * ```typescript\n * // Merge with existing types (default behavior)\n * const query = supabase\n * .from('users')\n * .select()\n * .overrideTypes<{ custom_field: string }>()\n *\n * // Replace existing types completely\n * const replaceQuery = supabase\n * .from('users')\n * .select()\n * .overrideTypes<{ id: number; name: string }, { merge: false }>()\n * ```\n */\n overrideTypes<\n NewResult,\n Options extends { merge?: boolean } = { merge: true },\n >(): PostgrestBuilder<\n ClientOptions,\n IsValidResultOverride<Result, NewResult, false, false> extends true\n ? // Preserve the optionality of the result if the overriden type is an object (case of chaining with `maybeSingle`)\n ContainsNull<Result> extends true\n ? MergePartialResult<NewResult, NonNullable<Result>, Options> | null\n : MergePartialResult<NewResult, Result, Options>\n : CheckMatchingArrayTypes<Result, NewResult>,\n ThrowOnError\n > {\n return this as unknown as PostgrestBuilder<\n ClientOptions,\n IsValidResultOverride<Result, NewResult, false, false> extends true\n ? // Preserve the optionality of the result if the overriden type is an object (case of chaining with `maybeSingle`)\n ContainsNull<Result> extends true\n ? MergePartialResult<NewResult, NonNullable<Result>, Options> | null\n : MergePartialResult<NewResult, Result, Options>\n : CheckMatchingArrayTypes<Result, NewResult>,\n ThrowOnError\n >\n }\n}\n","import PostgrestBuilder from './PostgrestBuilder'\nimport PostgrestFilterBuilder, { InvalidMethodError } from './PostgrestFilterBuilder'\nimport { GetResult } from './select-query-parser/result'\nimport { CheckMatchingArrayTypes } from './types/types'\nimport { ClientServerOptions, GenericSchema } from './types/common/common'\nimport type { MaxAffectedEnabled } from './types/feature-flags'\n\nexport default class PostgrestTransformBuilder<\n ClientOptions extends ClientServerOptions,\n Schema extends GenericSchema,\n Row extends Record<string, unknown>,\n Result,\n RelationName = unknown,\n Relationships = unknown,\n Method = unknown,\n> extends PostgrestBuilder<ClientOptions, Result> {\n /**\n * Perform a SELECT on the query result.\n *\n * By default, `.insert()`, `.update()`, `.upsert()`, and `.delete()` do not\n * return modified rows. By calling this method, modified rows are returned in\n * `data`.\n *\n * @param columns - The columns to retrieve, separated by commas\n *\n * @category Database\n * @subcategory Using modifiers\n *\n * @example With `upsert()`\n * ```ts\n * const { data, error } = await supabase\n * .from('characters')\n * .upsert({ id: 1, name: 'Han Solo' })\n * .select()\n * ```\n *\n * @exampleSql With `upsert()`\n * ```sql\n * create table\n * characters (id int8 primary key, name text);\n *\n * insert into\n * characters (id, name)\n * values\n * (1, 'Han');\n * ```\n *\n * @exampleResponse With `upsert()`\n * ```json\n * {\n * \"data\": [\n * {\n * \"id\": 1,\n * \"name\": \"Han Solo\"\n * }\n * ],\n * \"status\": 201,\n * \"statusText\": \"Created\"\n * }\n * ```\n */\n select<\n Query extends string = '*',\n NewResultOne = GetResult<Schema, Row, RelationName, Relationships, Query, ClientOptions>,\n >(\n columns?: Query\n ): PostgrestFilterBuilder<\n ClientOptions,\n Schema,\n Row,\n Method extends 'RPC'\n ? Result extends unknown[]\n ? NewResultOne[]\n : NewResultOne\n : NewResultOne[],\n RelationName,\n Relationships,\n Method\n > {\n // Remove whitespaces except when quoted\n let quoted = false\n const cleanedColumns = (columns ?? '*')\n .split('')\n .map((c) => {\n if (/\\s/.test(c) && !quoted) {\n return ''\n }\n if (c === '\"') {\n quoted = !quoted\n }\n return c\n })\n .join('')\n this.url.searchParams.set('select', cleanedColumns)\n this.headers.append('Prefer', 'return=representation')\n return this as unknown as PostgrestFilterBuilder<\n ClientOptions,\n Schema,\n Row,\n Method extends 'RPC'\n ? Result extends unknown[]\n ? NewResultOne[]\n : NewResultOne\n : NewResultOne[],\n RelationName,\n Relationships,\n Method\n >\n }\n\n order<ColumnName extends string & keyof Row>(\n column: ColumnName,\n options?: { ascending?: boolean; nullsFirst?: boolean; referencedTable?: undefined }\n ): this\n order(\n column: string,\n options?: { ascending?: boolean; nullsFirst?: boolean; referencedTable?: string }\n ): this\n /**\n * @deprecated Use `options.referencedTable` instead of `options.foreignTable`\n */\n order<ColumnName extends string & keyof Row>(\n column: ColumnName,\n options?: { ascending?: boolean; nullsFirst?: boolean; foreignTable?: undefined }\n ): this\n /**\n * @deprecated Use `options.referencedTable` instead of `options.foreignTable`\n */\n order(\n column: string,\n options?: { ascending?: boolean; nullsFirst?: boolean; foreignTable?: string }\n ): this\n /**\n * Order the query result by `column`.\n *\n * You can call this method multiple times to order by multiple columns.\n *\n * You can order referenced tables, but it only affects the ordering of the\n * parent table if you use `!inner` in the query.\n *\n * @param column - The column to order by\n * @param options - Named parameters\n * @param options.ascending - If `true`, the result will be in ascending order\n * @param options.nullsFirst - If `true`, `null`s appear first. If `false`,\n * `null`s appear last.\n * @param options.referencedTable - Set this to order a referenced table by\n * its columns\n * @param options.foreignTable - Deprecated, use `options.referencedTable`\n * instead\n *\n * @category Database\n * @subcategory Using modifiers\n *\n * @example With `select()`\n * ```ts\n * const { data, error } = await supabase\n * .from('characters')\n * .select('id, name')\n * .order('id', { ascending: false })\n * ```\n *\n * @exampleSql With `select()`\n * ```sql\n * create table\n * characters (id int8 primary key, name text);\n *\n * insert into\n * characters (id, name)\n * values\n * (1, 'Luke'),\n * (2, 'Leia'),\n * (3, 'Han');\n * ```\n *\n * @exampleResponse With `select()`\n * ```json\n * {\n * \"data\": [\n * {\n * \"id\": 3,\n * \"name\": \"Han\"\n * },\n * {\n * \"id\": 2,\n * \"name\": \"Leia\"\n * },\n * {\n * \"id\": 1,\n * \"name\": \"Luke\"\n * }\n * ],\n * \"status\": 200,\n * \"statusText\": \"OK\"\n * }\n * ```\n *\n * @exampleDescription On a referenced table\n * Ordering with `referencedTable` doesn't affect the ordering of the\n * parent table.\n *\n * @example On a referenced table\n * ```ts\n * const { data, error } = await supabase\n * .from('orchestral_sections')\n * .select(`\n * name,\n * instruments (\n * name\n * )\n * `)\n * .order('name', { referencedTable: 'instruments', ascending: false })\n *\n * ```\n *\n * @exampleSql On a referenced table\n * ```sql\n * create table\n * orchestral_sections (id int8 primary key, name text);\n * create table\n * instruments (\n * id int8 primary key,\n * section_id int8 not null references orchestral_sections,\n * name text\n * );\n *\n * insert into\n * orchestral_sections (id, name)\n * values\n * (1, 'strings'),\n * (2, 'woodwinds');\n * insert into\n * instruments (id, section_id, name)\n * values\n * (1, 1, 'harp'),\n * (2, 1, 'violin');\n * ```\n *\n * @exampleResponse On a referenced table\n * ```json\n * {\n * \"data\": [\n * {\n * \"name\": \"strings\",\n * \"instruments\": [\n * {\n * \"name\": \"violin\"\n * },\n * {\n * \"name\": \"harp\"\n * }\n * ]\n * },\n * {\n * \"name\": \"woodwinds\",\n * \"instruments\": []\n * }\n * ],\n * \"status\": 200,\n * \"statusText\": \"OK\"\n * }\n * ```\n *\n * @exampleDescription Order parent table by a referenced table\n * Ordering with `referenced_table(col)` affects the ordering of the\n * parent table.\n *\n * @example Order parent table by a referenced table\n * ```ts\n * const { data, error } = await supabase\n * .from('instruments')\n * .select(`\n * name,\n * section:orchestral_sections (\n * name\n * )\n * `)\n * .order('section(name)', { ascending: true })\n *\n * ```\n *\n * @exampleSql Order parent table by a referenced table\n * ```sql\n * create table\n * orchestral_sections (id int8 primary key, name text);\n * create table\n * instruments (\n * id int8 primary key,\n * section_id int8 not null references orchestral_sections,\n * name text\n * );\n *\n * insert into\n * orchestral_sections (id, name)\n * values\n * (1, 'strings'),\n * (2, 'woodwinds');\n * insert into\n * instruments (id, section_id, name)\n * values\n * (1, 2, 'flute'),\n * (2, 1, 'violin');\n * ```\n *\n * @exampleResponse Order parent table by a referenced table\n * ```json\n * {\n * \"data\": [\n * {\n * \"name\": \"violin\",\n * \"orchestral_sections\": {\"name\": \"strings\"}\n * },\n * {\n * \"name\": \"flute\",\n * \"orchestral_sections\": {\"name\": \"woodwinds\"}\n * }\n * ],\n * \"status\": 200,\n * \"statusText\": \"OK\"\n * }\n * ```\n */\n order(\n column: string,\n {\n ascending = true,\n nullsFirst,\n foreignTable,\n referencedTable = foreignTable,\n }: {\n ascending?: boolean\n nullsFirst?: boolean\n foreignTable?: string\n referencedTable?: string\n } = {}\n ): this {\n const key = referencedTable ? `${referencedTable}.order` : 'order'\n const existingOrder = this.url.searchParams.get(key)\n\n this.url.searchParams.set(\n key,\n `${existingOrder ? `${existingOrder},` : ''}${column}.${ascending ? 'asc' : 'desc'}${\n nullsFirst === undefined ? '' : nullsFirst ? '.nullsfirst' : '.nullslast'\n }`\n )\n return this\n }\n\n /**\n * Limit the query result by `count`.\n *\n * @param count - The maximum number of rows to return\n * @param options - Named parameters\n * @param options.referencedTable - Set this to limit rows of referenced\n * tables instead of the parent table\n * @param options.foreignTable - Deprecated, use `options.referencedTable`\n * instead\n *\n * @category Database\n * @subcategory Using modifiers\n *\n * @example With `select()`\n * ```ts\n * const { data, error } = await supabase\n * .from('characters')\n * .select('name')\n * .limit(1)\n * ```\n *\n * @exampleSql With `select()`\n * ```sql\n * create table\n * characters (id int8 primary key, name text);\n *\n * insert into\n * characters (id, name)\n * values\n * (1, 'Luke'),\n * (2, 'Leia'),\n * (3, 'Han');\n * ```\n *\n * @exampleResponse With `select()`\n * ```json\n * {\n * \"data\": [\n * {\n * \"name\": \"Luke\"\n * }\n * ],\n * \"status\": 200,\n * \"statusText\": \"OK\"\n * }\n * ```\n *\n * @example On a referenced table\n * ```ts\n * const { data, error } = await supabase\n * .from('orchestral_sections')\n * .select(`\n * name,\n * instruments (\n * name\n * )\n * `)\n * .limit(1, { referencedTable: 'instruments' })\n * ```\n *\n * @exampleSql On a referenced table\n * ```sql\n * create table\n * orchestral_sections (id int8 primary key, name text);\n * create table\n * instruments (\n * id int8 primary key,\n * section_id int8 not null references orchestral_sections,\n * name text\n * );\n *\n * insert into\n * orchestral_sections (id, name)\n * values\n * (1, 'strings');\n * insert into\n * instruments (id, section_id, name)\n * values\n * (1, 1, 'harp'),\n * (2, 1, 'violin');\n * ```\n *\n * @exampleResponse On a referenced table\n * ```json\n * {\n * \"data\": [\n * {\n * \"name\": \"strings\",\n * \"instruments\": [\n * {\n * \"name\": \"violin\"\n * }\n * ]\n * }\n * ],\n * \"status\": 200,\n * \"statusText\": \"OK\"\n * }\n * ```\n */\n limit(\n count: number,\n {\n foreignTable,\n referencedTable = foreignTable,\n }: { foreignTable?: string; referencedTable?: string } = {}\n ): this {\n const key = typeof referencedTable === 'undefined' ? 'limit' : `${referencedTable}.limit`\n this.url.searchParams.set(key, `${count}`)\n return this\n }\n\n /**\n * Limit the query result by starting at an offset `from` and ending at the offset `to`.\n * Only records within this range are returned.\n * This respects the query order and if there is no order clause the range could behave unexpectedly.\n * The `from` and `to` values are 0-based and inclusive: `range(1, 3)` will include the second, third\n * and fourth rows of the query.\n *\n * @param from - The starting index from which to limit the result\n * @param to - The last index to which to limit the result\n * @param options - Named parameters\n * @param options.referencedTable - Set this to limit rows of referenced\n * tables instead of the parent table\n * @param options.foreignTable - Deprecated, use `options.referencedTable`\n * instead\n *\n * @category Database\n * @subcategory Using modifiers\n *\n * @example With `select()`\n * ```ts\n * const { data, error } = await supabase\n * .from('characters')\n * .select('name')\n * .range(0, 1)\n * ```\n *\n * @exampleSql With `select()`\n * ```sql\n * create table\n * characters (id int8 primary key, name text);\n *\n * insert into\n * characters (id, name)\n * values\n * (1, 'Luke'),\n * (2, 'Leia'),\n * (3, 'Han');\n * ```\n *\n * @exampleResponse With `select()`\n * ```json\n * {\n * \"data\": [\n * {\n * \"name\": \"Luke\"\n * },\n * {\n * \"name\": \"Leia\"\n * }\n * ],\n * \"status\": 200,\n * \"statusText\": \"OK\"\n * }\n * ```\n */\n range(\n from: number,\n to: number,\n {\n foreignTable,\n referencedTable = foreignTable,\n }: { foreignTable?: string; referencedTable?: string } = {}\n ): this {\n const keyOffset =\n typeof referencedTable === 'undefined' ? 'offset' : `${referencedTable}.offset`\n const keyLimit = typeof referencedTable === 'undefined' ? 'limit' : `${referencedTable}.limit`\n this.url.searchParams.set(keyOffset, `${from}`)\n // Range is inclusive, so add 1\n this.url.searchParams.set(keyLimit, `${to - from + 1}`)\n return this\n }\n\n /**\n * Set the AbortSignal for the fetch request.\n *\n * @param signal - The AbortSignal to use for the fetch request\n *\n * @category Database\n * @subcategory Using modifiers\n *\n * @remarks\n * You can use this to set a timeout for the request.\n *\n * @exampleDescription Aborting requests in-flight\n * You can use an [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) to abort requests.\n * Note that `status` and `statusText` don't mean anything for aborted requests as the request wasn't fulfilled.\n *\n * @example Aborting requests in-flight\n * ```ts\n * const ac = new AbortController()\n *\n * const { data, error } = await supabase\n * .from('very_big_table')\n * .select()\n * .abortSignal(ac.signal)\n *\n * // Abort the request after 100 ms\n * setTimeout(() => ac.abort(), 100)\n * ```\n *\n * @exampleResponse Aborting requests in-flight\n * ```json\n * {\n * \"error\": {\n * \"message\": \"AbortError: The user aborted a request.\",\n * \"details\": \"\",\n * \"hint\": \"The request was aborted locally via the provided AbortSignal.\",\n * \"code\": \"\"\n * },\n * \"status\": 0,\n * \"statusText\": \"\"\n * }\n *\n * ```\n *\n * @example Set a timeout\n * ```ts\n * const { data, error } = await supabase\n * .from('very_big_table')\n * .select()\n * .abortSignal(AbortSignal.timeout(1000 /* ms *\\/))\n * ```\n *\n * @exampleResponse Set a timeout\n * ```json\n * {\n * \"error\": {\n * \"message\": \"FetchError: The user aborted a request.\",\n * \"details\": \"\",\n * \"hint\": \"\",\n * \"code\": \"\"\n * },\n * \"status\": 400,\n * \"statusText\": \"Bad Request\"\n * }\n *\n * ```\n */\n abortSignal(signal: AbortSignal): this {\n this.signal = signal\n return this\n }\n\n /**\n * Return `data` as a single object instead of an array of objects.\n *\n * Query result must be one row (e.g. using `.limit(1)`), otherwise this\n * returns an error.\n *\n * @category Database\n * @subcategory Using modifiers\n *\n * @example With `select()`\n * ```ts\n * const { data, error } = await supabase\n * .from('characters')\n * .select('name')\n * .limit(1)\n * .single()\n * ```\n *\n * @exampleSql With `select()`\n * ```sql\n * create table\n * characters (id int8 primary key, name text);\n *\n * insert into\n * characters (id, name)\n * values\n * (1, 'Luke'),\n * (2, 'Leia'),\n * (3, 'Han');\n * ```\n *\n * @exampleResponse With `select()`\n * ```json\n * {\n * \"data\": {\n * \"name\": \"Luke\"\n * },\n * \"status\": 200,\n * \"statusText\": \"OK\"\n * }\n * ```\n */\n single<ResultOne = Result extends (infer ResultOne)[] ? ResultOne : never>(): PostgrestBuilder<\n ClientOptions,\n ResultOne\n > {\n this.headers.set('Accept', 'application/vnd.pgrst.object+json')\n return this as unknown as PostgrestBuilder<ClientOptions, ResultOne>\n }\n\n /**\n * Return `data` as a single object instead of an array of objects.\n *\n * Query result must be zero or one row (e.g. using `.limit(1)`), otherwise\n * this returns an error.\n *\n * @category Database\n * @subcategory Using modifiers\n *\n * @example With `select()`\n * ```ts\n * const { data, error } = await supabase\n * .from('characters')\n * .select()\n * .eq('name', 'Katniss')\n * .maybeSingle()\n * ```\n *\n * @exampleSql With `select()`\n * ```sql\n * create table\n * characters (id int8 primary key, name text);\n *\n * insert into\n * characters (id, name)\n * values\n * (1, 'Luke'),\n * (2, 'Leia'),\n * (3, 'Han');\n * ```\n *\n * @exampleResponse With `select()`\n * ```json\n * {\n * \"status\": 200,\n * \"statusText\": \"OK\"\n * }\n * ```\n */\n maybeSingle<\n ResultOne = Result extends (infer ResultOne)[] ? ResultOne : never,\n >(): PostgrestBuilder<ClientOptions, ResultOne | null> {\n // No Accept header override — we fetch as a list and enforce cardinality client-side.\n // Fixes https://github.com/supabase/postgrest-js/issues/361 for all request methods.\n this.isMaybeSingle = true\n return this as unknown as PostgrestBuilder<ClientOptions, ResultOne | null>\n }\n\n /**\n * Return `data` as a string in CSV format.\n *\n * @category Database\n * @subcategory Using modifiers\n *\n * @exampleDescription Return data as CSV\n * By default, the data is returned in JSON format, but can also be returned as Comma Separated Values.\n *\n * @example Return data as CSV\n * ```ts\n * const { data, error } = await supabase\n * .from('characters')\n * .select()\n * .csv()\n * ```\n *\n * @exampleSql Return data as CSV\n * ```sql\n * create table\n * characters (id int8 primary key, name text);\n *\n * insert into\n * characters (id, name)\n * values\n * (1, 'Luke'),\n * (2, 'Leia'),\n * (3, 'Han');\n * ```\n *\n * @exampleResponse Return data as CSV\n * ```json\n * {\n * \"data\": \"id,name\\n1,Luke\\n2,Leia\\n3,Han\",\n * \"status\": 200,\n * \"statusText\": \"OK\"\n * }\n * ```\n */\n csv(): PostgrestBuilder<ClientOptions, string> {\n this.headers.set('Accept', 'text/csv')\n return this as unknown as PostgrestBuilder<ClientOptions, string>\n }\n\n /**\n * Return `data` as an object in [GeoJSON](https://geojson.org) format.\n *\n * @category Database\n */\n geojson(): PostgrestBuilder<ClientOptions, Record<string, unknown>> {\n this.headers.set('Accept', 'application/geo+json')\n return this as unknown as PostgrestBuilder<ClientOptions, Record<string, unknown>>\n }\n\n /**\n * Return `data` as the EXPLAIN plan for the query.\n *\n * You need to enable the\n * [db_plan_enabled](https://supabase.com/docs/guides/database/debugging-performance#enabling-explain)\n * setting before using this method.\n *\n * @param options - Named parameters\n *\n * @param options.analyze - If `true`, the query will be executed and the\n * actual run time will be returned\n *\n * @param options.verbose - If `true`, the query identifier will be returned\n * and `data` will include the output columns of the query\n *\n * @param options.settings - If `true`, include information on configuration\n * parameters that affect query planning\n *\n * @param options.buffers - If `true`, include information on buffer usage\n *\n * @param options.wal - If `true`, include information on WAL record generation\n *\n * @param options.format - The format of the output, can be `\"text\"` (default)\n * or `\"json\"`\n *\n * @category Database\n * @subcategory Using modifiers\n *\n * @exampleDescription Get the execution plan\n * By default, the data is returned in TEXT format, but can also be returned as JSON by using the `format` parameter.\n *\n * @example Get the execution plan\n * ```ts\n * const { data, error } = await supabase\n * .from('characters')\n * .select()\n * .explain()\n * ```\n *\n * @exampleSql Get the execution plan\n * ```sql\n * create table\n * characters (id int8 primary key, name text);\n *\n * insert into\n * characters (id, name)\n * values\n * (1, 'Luke'),\n * (2, 'Leia'),\n * (3, 'Han');\n * ```\n *\n * @exampleResponse Get the execution plan\n * ```js\n * Aggregate (cost=33.34..33.36 rows=1 width=112)\n * -> Limit (cost=0.00..18.33 rows=1000 width=40)\n * -> Seq Scan on characters (cost=0.00..22.00 rows=1200 width=40)\n * ```\n *\n * @exampleDescription Get the execution plan with analyze and verbose\n * By default, the data is returned in TEXT format, but can also be returned as JSON by using the `format` parameter.\n *\n * @example Get the execution plan with analyze and verbose\n * ```ts\n * const { data, error } = await supabase\n * .from('characters')\n * .select()\n * .explain({analyze:true,verbose:true})\n * ```\n *\n * @exampleSql Get the execution plan with analyze and verbose\n * ```sql\n * create table\n * characters (id int8 primary key, name text);\n *\n * insert into\n * characters (id, name)\n * values\n * (1, 'Luke'),\n * (2, 'Leia'),\n * (3, 'Han');\n * ```\n *\n * @exampleResponse Get the execution plan with analyze and verbose\n * ```js\n * Aggregate (cost=33.34..33.36 rows=1 width=112) (actual time=0.041..0.041 rows=1 loops=1)\n * Output: NULL::bigint, count(ROW(characters.id, characters.name)), COALESCE(json_agg(ROW(characters.id, characters.name)), '[]'::json), NULLIF(current_setting('response.headers'::text, true), ''::text), NULLIF(current_setting('response.status'::text, true), ''::text)\n * -> Limit (cost=0.00..18.33 rows=1000 width=40) (actual time=0.005..0.006 rows=3 loops=1)\n * Output: characters.id, characters.name\n * -> Seq Scan on public.characters (cost=0.00..22.00 rows=1200 width=40) (actual time=0.004..0.005 rows=3 loops=1)\n * Output: characters.id, characters.name\n * Query Identifier: -4730654291623321173\n * Planning Time: 0.407 ms\n * Execution Time: 0.119 ms\n * ```\n */\n explain({\n analyze = false,\n verbose = false,\n settings = false,\n buffers = false,\n wal = false,\n format = 'text',\n }: {\n analyze?: boolean\n verbose?: boolean\n settings?: boolean\n buffers?: boolean\n wal?: boolean\n format?: 'json' | 'text'\n } = {}) {\n const options = [\n analyze ? 'analyze' : null,\n verbose ? 'verbose' : null,\n settings ? 'settings' : null,\n buffer