UNPKG

@supabase/postgrest-js

Version:
1,887 lines (1,882 loc) 129 kB
Object.defineProperty(exports, '__esModule', { value: true }); //#region src/types/common/common.ts /** * Default number of retry attempts. */ const DEFAULT_MAX_RETRIES = 3; /** * Default exponential backoff delay function. * Delays: 1s, 2s, 4s, 8s, ... (max 30s) * * @param attemptIndex - Zero-based index of the retry attempt * @returns Delay in milliseconds before the next retry */ const getRetryDelay = (attemptIndex) => Math.min(1e3 * 2 ** attemptIndex, 3e4); /** * Status codes that are safe to retry. * 520 = Cloudflare timeout/connection errors (transient) * 503 = PostgREST schema cache not yet loaded (transient, signals retry via Retry-After header) */ const RETRYABLE_STATUS_CODES = [520, 503]; /** * HTTP methods that are safe to retry (idempotent operations). */ const RETRYABLE_METHODS = [ "GET", "HEAD", "OPTIONS" ]; //#endregion //#region src/PostgrestError.ts /** * Error format * * {@link https://postgrest.org/en/stable/api.html?highlight=options#errors-and-http-status-codes} */ var PostgrestError = class extends Error { /** * @example * ```ts * import PostgrestError from '@supabase/postgrest-js' * * throw new PostgrestError({ * message: 'Row level security prevented the request', * details: 'RLS denied the insert', * hint: 'Check your policies', * code: 'PGRST301', * }) * ``` */ constructor(context) { super(context.message); this.name = "PostgrestError"; this.details = context.details; this.hint = context.hint; this.code = context.code; } toJSON() { return { name: this.name, message: this.message, details: this.details, hint: this.hint, code: this.code }; } }; //#endregion //#region src/PostgrestBuilder.ts /** * Sleep for a given number of milliseconds. * If an AbortSignal is provided, the sleep resolves early when the signal is aborted. */ function sleep(ms, signal) { return new Promise((resolve) => { if (signal === null || signal === void 0 ? void 0 : signal.aborted) { resolve(); return; } const id = setTimeout(() => { signal === null || signal === void 0 || signal.removeEventListener("abort", onAbort); resolve(); }, ms); function onAbort() { clearTimeout(id); resolve(); } signal === null || signal === void 0 || signal.addEventListener("abort", onAbort); }); } /** * Check if a request should be retried based on method and status code. */ function shouldRetry(method, status, attemptCount, retryEnabled) { if (!retryEnabled || attemptCount >= DEFAULT_MAX_RETRIES) return false; if (!RETRYABLE_METHODS.includes(method)) return false; if (!RETRYABLE_STATUS_CODES.includes(status)) return false; return true; } var PostgrestBuilder = class { /** * Creates a builder configured for a specific PostgREST request. * * @example Using supabase-js (recommended) * ```ts * import { createClient } from '@supabase/supabase-js' * * const supabase = createClient('https://xyzcompany.supabase.co', 'your-publishable-key') * const { data, error } = await supabase.from('users').select('*') * ``` * * @category Database * * @example Standalone import for bundle-sensitive environments * ```ts * import { PostgrestQueryBuilder } from '@supabase/postgrest-js' * * const builder = new PostgrestQueryBuilder( * new URL('https://xyzcompany.supabase.co/rest/v1/users'), * { headers: new Headers({ apikey: 'your-publishable-key' }) } * ) * ``` */ constructor(builder) { var _builder$shouldThrowO, _builder$isMaybeSingl, _builder$shouldStripN, _builder$urlLengthLim, _builder$retry; this.shouldThrowOnError = false; this.retryEnabled = true; this.method = builder.method; this.url = builder.url; this.headers = new Headers(builder.headers); this.schema = builder.schema; this.body = builder.body; this.shouldThrowOnError = (_builder$shouldThrowO = builder.shouldThrowOnError) !== null && _builder$shouldThrowO !== void 0 ? _builder$shouldThrowO : false; this.signal = builder.signal; this.isMaybeSingle = (_builder$isMaybeSingl = builder.isMaybeSingle) !== null && _builder$isMaybeSingl !== void 0 ? _builder$isMaybeSingl : false; this.shouldStripNulls = (_builder$shouldStripN = builder.shouldStripNulls) !== null && _builder$shouldStripN !== void 0 ? _builder$shouldStripN : false; this.urlLengthLimit = (_builder$urlLengthLim = builder.urlLengthLimit) !== null && _builder$urlLengthLim !== void 0 ? _builder$urlLengthLim : 8e3; this.retryEnabled = (_builder$retry = builder.retry) !== null && _builder$retry !== void 0 ? _builder$retry : true; if (builder.fetch) this.fetch = builder.fetch; else this.fetch = fetch; } /** * If there's an error with the query, throwOnError will reject the promise by * throwing the error instead of returning it as part of a successful response. * * {@link https://github.com/supabase/supabase-js/issues/92} * * @category Database */ throwOnError() { this.shouldThrowOnError = true; return this; } /** * Strip null values from the response data. Properties with `null` values * will be omitted from the returned JSON objects. * * Requires PostgREST 11.2.0+. * * {@link https://docs.postgrest.org/en/stable/references/api/resource_representation.html#stripped-nulls} * * @category Database * @subcategory Using modifiers * * @example With `select()` * ```ts * const { data, error } = await supabase * .from('characters') * .select() * .stripNulls() * ``` * * @exampleSql With `select()` * ```sql * create table * characters (id int8 primary key, name text, bio text); * * insert into * characters (id, name, bio) * values * (1, 'Luke', null), * (2, 'Leia', 'Princess of Alderaan'); * ``` * * @exampleResponse With `select()` * ```json * { * "data": [ * { * "id": 1, * "name": "Luke" * }, * { * "id": 2, * "name": "Leia", * "bio": "Princess of Alderaan" * } * ], * "status": 200, * "statusText": "OK" * } * ``` */ stripNulls() { if (this.headers.get("Accept") === "text/csv") throw new Error("stripNulls() cannot be used with csv()"); this.shouldStripNulls = true; return this; } /** * Set an HTTP header for the request. * * @category Database */ setHeader(name, value) { this.headers = new Headers(this.headers); this.headers.set(name, value); return this; } /** * @category Database * * Configure retry behavior for this request. * * By default, retries are enabled for idempotent requests (GET, HEAD, OPTIONS) * that fail with network errors or specific HTTP status codes (503, 520). * Retries use exponential backoff (1s, 2s, 4s) with a maximum of 3 attempts. * * @param enabled - Whether to enable retries for this request * * @example * ```ts * // Disable retries for a specific query * const { data, error } = await supabase * .from('users') * .select() * .retry(false) * ``` */ retry(enabled) { this.retryEnabled = enabled; return this; } then(onfulfilled, onrejected) { var _this = this; if (this.schema === void 0) {} else if (["GET", "HEAD"].includes(this.method)) this.headers.set("Accept-Profile", this.schema); else this.headers.set("Content-Profile", this.schema); if (this.method !== "GET" && this.method !== "HEAD") this.headers.set("Content-Type", "application/json"); if (this.shouldStripNulls) { const currentAccept = this.headers.get("Accept"); if (currentAccept === "application/vnd.pgrst.object+json") this.headers.set("Accept", "application/vnd.pgrst.object+json;nulls=stripped"); else if (!currentAccept || currentAccept === "application/json") this.headers.set("Accept", "application/vnd.pgrst.array+json;nulls=stripped"); } const _fetch = this.fetch; const executeWithRetry = async () => { let attemptCount = 0; while (true) { const requestHeaders = new Headers(_this.headers); if (attemptCount > 0) requestHeaders.set("X-Retry-Count", String(attemptCount)); let res$1; try { res$1 = await _fetch(_this.url.toString(), { method: _this.method, headers: requestHeaders, body: JSON.stringify(_this.body, (_, value) => typeof value === "bigint" ? value.toString() : value), signal: _this.signal }); } catch (fetchError) { if ((fetchError === null || fetchError === void 0 ? void 0 : fetchError.name) === "AbortError" || (fetchError === null || fetchError === void 0 ? void 0 : fetchError.code) === "ABORT_ERR") throw fetchError; if (!RETRYABLE_METHODS.includes(_this.method)) throw fetchError; if (_this.retryEnabled && attemptCount < DEFAULT_MAX_RETRIES) { const delay = getRetryDelay(attemptCount); attemptCount++; await sleep(delay, _this.signal); continue; } throw fetchError; } if (shouldRetry(_this.method, res$1.status, attemptCount, _this.retryEnabled)) { var _res$headers$get, _res$headers; const retryAfterHeader = (_res$headers$get = (_res$headers = res$1.headers) === null || _res$headers === void 0 ? void 0 : _res$headers.get("Retry-After")) !== null && _res$headers$get !== void 0 ? _res$headers$get : null; const delay = retryAfterHeader !== null ? Math.max(0, parseInt(retryAfterHeader, 10) || 0) * 1e3 : getRetryDelay(attemptCount); await res$1.text(); attemptCount++; await sleep(delay, _this.signal); continue; } return await _this.processResponse(res$1); } }; let res = executeWithRetry(); if (!this.shouldThrowOnError) res = res.catch((fetchError) => { var _fetchError$name2; let errorDetails = ""; let hint = ""; let code = ""; const cause = fetchError === null || fetchError === void 0 ? void 0 : fetchError.cause; if (cause) { var _cause$message, _cause$code, _fetchError$name, _cause$name; const causeMessage = (_cause$message = cause === null || cause === void 0 ? void 0 : cause.message) !== null && _cause$message !== void 0 ? _cause$message : ""; const causeCode = (_cause$code = cause === null || cause === void 0 ? void 0 : cause.code) !== null && _cause$code !== void 0 ? _cause$code : ""; errorDetails = `${(_fetchError$name = fetchError === null || fetchError === void 0 ? void 0 : fetchError.name) !== null && _fetchError$name !== void 0 ? _fetchError$name : "FetchError"}: ${fetchError === null || fetchError === void 0 ? void 0 : fetchError.message}`; errorDetails += `\n\nCaused by: ${(_cause$name = cause === null || cause === void 0 ? void 0 : cause.name) !== null && _cause$name !== void 0 ? _cause$name : "Error"}: ${causeMessage}`; if (causeCode) errorDetails += ` (${causeCode})`; if (cause === null || cause === void 0 ? void 0 : cause.stack) errorDetails += `\n${cause.stack}`; } else { var _fetchError$stack; errorDetails = (_fetchError$stack = fetchError === null || fetchError === void 0 ? void 0 : fetchError.stack) !== null && _fetchError$stack !== void 0 ? _fetchError$stack : ""; } const urlLength = this.url.toString().length; if ((fetchError === null || fetchError === void 0 ? void 0 : fetchError.name) === "AbortError" || (fetchError === null || fetchError === void 0 ? void 0 : fetchError.code) === "ABORT_ERR") { code = ""; hint = "Request was aborted (timeout or manual cancellation)"; if (urlLength > this.urlLengthLimit) 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.`; } else if ((cause === null || cause === void 0 ? void 0 : cause.name) === "HeadersOverflowError" || (cause === null || cause === void 0 ? void 0 : cause.code) === "UND_ERR_HEADERS_OVERFLOW") { code = ""; hint = "HTTP headers exceeded server limits (typically 16KB)"; if (urlLength > this.urlLengthLimit) 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.`; } return { success: false, error: { message: `${(_fetchError$name2 = fetchError === null || fetchError === void 0 ? void 0 : fetchError.name) !== null && _fetchError$name2 !== void 0 ? _fetchError$name2 : "FetchError"}: ${fetchError === null || fetchError === void 0 ? void 0 : fetchError.message}`, details: errorDetails, hint, code }, data: null, count: null, status: 0, statusText: "" }; }); return res.then(onfulfilled, onrejected); } /** * Process a fetch response and return the standardized postgrest response. */ async processResponse(res) { var _this2 = this; let error = null; let data = null; let count = null; let status = res.status; let statusText = res.statusText; if (res.ok) { var _this$headers$get2, _res$headers$get2; if (_this2.method !== "HEAD") { var _this$headers$get; const body = await res.text(); if (body === "") {} else if (_this2.headers.get("Accept") === "text/csv") data = body; else if (_this2.headers.get("Accept") && ((_this$headers$get = _this2.headers.get("Accept")) === null || _this$headers$get === void 0 ? void 0 : _this$headers$get.includes("application/vnd.pgrst.plan+text"))) data = body; else data = JSON.parse(body); } const countHeader = (_this$headers$get2 = _this2.headers.get("Prefer")) === null || _this$headers$get2 === void 0 ? void 0 : _this$headers$get2.match(/count=(exact|planned|estimated)/); const contentRange = (_res$headers$get2 = res.headers.get("content-range")) === null || _res$headers$get2 === void 0 ? void 0 : _res$headers$get2.split("/"); if (countHeader && contentRange && contentRange.length > 1) count = parseInt(contentRange[1]); if (_this2.isMaybeSingle && Array.isArray(data)) if (data.length > 1) { error = { code: "PGRST116", details: `Results contain ${data.length} rows, application/vnd.pgrst.object+json requires 1 row`, hint: null, message: "JSON object requested, multiple (or no) rows returned" }; data = null; count = null; status = 406; statusText = "Not Acceptable"; } else if (data.length === 1) data = data[0]; else data = null; } else { const body = await res.text(); try { error = JSON.parse(body); if (Array.isArray(error) && res.status === 404) { data = []; error = null; status = 200; statusText = "OK"; } } catch (_unused) { if (res.status === 404 && body === "") { status = 204; statusText = "No Content"; } else error = { message: body }; } if (error && _this2.shouldThrowOnError) throw new PostgrestError(error); } return { success: error === null, error, data, count, status, statusText }; } /** * Override the type of the returned `data`. * * @typeParam NewResult - The new result type to override with * @deprecated Use overrideTypes<yourType, { merge: false }>() method at the end of your call chain instead * * @category Database */ returns() { /* istanbul ignore next */ return this; } /** * Override the type of the returned `data` field in the response. * * @typeParam NewResult - The new type to cast the response data to * @typeParam Options - Optional type configuration (defaults to { merge: true }) * @typeParam Options.merge - When true, merges the new type with existing return type. When false, replaces the existing types entirely (defaults to true) * @example * ```typescript * // Merge with existing types (default behavior) * const query = supabase * .from('users') * .select() * .overrideTypes<{ custom_field: string }>() * * // Replace existing types completely * const replaceQuery = supabase * .from('users') * .select() * .overrideTypes<{ id: number; name: string }, { merge: false }>() * ``` * @returns A PostgrestBuilder instance with the new type * * @category Database * @subcategory Using modifiers * * @example Complete Override type of successful response * ```ts * const { data } = await supabase * .from('countries') * .select() * .overrideTypes<Array<MyType>, { merge: false }>() * ``` * * @exampleResponse Complete Override type of successful response * ```ts * let x: typeof data // MyType[] * ``` * * @example Complete Override type of object response * ```ts * const { data } = await supabase * .from('countries') * .select() * .maybeSingle() * .overrideTypes<MyType, { merge: false }>() * ``` * * @exampleResponse Complete Override type of object response * ```ts * let x: typeof data // MyType | null * ``` * * @example Partial Override type of successful response * ```ts * const { data } = await supabase * .from('countries') * .select() * .overrideTypes<Array<{ status: "A" | "B" }>>() * ``` * * @exampleResponse Partial Override type of successful response * ```ts * let x: typeof data // Array<CountryRowProperties & { status: "A" | "B" }> * ``` * * @example Partial Override type of object response * ```ts * const { data } = await supabase * .from('countries') * .select() * .maybeSingle() * .overrideTypes<{ status: "A" | "B" }>() * ``` * * @exampleResponse Partial Override type of object response * ```ts * let x: typeof data // CountryRowProperties & { status: "A" | "B" } | null * ``` * * @example Merge vs replace existing types * ```typescript * // Merge with existing types (default behavior) * const query = supabase * .from('users') * .select() * .overrideTypes<{ custom_field: string }>() * * // Replace existing types completely * const replaceQuery = supabase * .from('users') * .select() * .overrideTypes<{ id: number; name: string }, { merge: false }>() * ``` */ overrideTypes() { return this; } }; //#endregion //#region src/PostgrestTransformBuilder.ts var PostgrestTransformBuilder = class extends PostgrestBuilder { /** * Perform a SELECT on the query result. * * By default, `.insert()`, `.update()`, `.upsert()`, and `.delete()` do not * return modified rows. By calling this method, modified rows are returned in * `data`. * * @param columns - The columns to retrieve, separated by commas * * @category Database * @subcategory Using modifiers * * @example With `upsert()` * ```ts * const { data, error } = await supabase * .from('characters') * .upsert({ id: 1, name: 'Han Solo' }) * .select() * ``` * * @exampleSql With `upsert()` * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Han'); * ``` * * @exampleResponse With `upsert()` * ```json * { * "data": [ * { * "id": 1, * "name": "Han Solo" * } * ], * "status": 201, * "statusText": "Created" * } * ``` */ select(columns) { let quoted = false; const cleanedColumns = (columns !== null && columns !== void 0 ? columns : "*").split("").map((c) => { if (/\s/.test(c) && !quoted) return ""; if (c === "\"") quoted = !quoted; return c; }).join(""); this.url.searchParams.set("select", cleanedColumns); this.headers.append("Prefer", "return=representation"); return this; } /** * Order the query result by `column`. * * You can call this method multiple times to order by multiple columns. * * You can order referenced tables, but it only affects the ordering of the * parent table if you use `!inner` in the query. * * @param column - The column to order by * @param options - Named parameters * @param options.ascending - If `true`, the result will be in ascending order * @param options.nullsFirst - If `true`, `null`s appear first. If `false`, * `null`s appear last. * @param options.referencedTable - Set this to order a referenced table by * its columns * @param options.foreignTable - Deprecated, use `options.referencedTable` * instead * * @category Database * @subcategory Using modifiers * * @example With `select()` * ```ts * const { data, error } = await supabase * .from('characters') * .select('id, name') * .order('id', { ascending: false }) * ``` * * @exampleSql With `select()` * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse With `select()` * ```json * { * "data": [ * { * "id": 3, * "name": "Han" * }, * { * "id": 2, * "name": "Leia" * }, * { * "id": 1, * "name": "Luke" * } * ], * "status": 200, * "statusText": "OK" * } * ``` * * @exampleDescription On a referenced table * Ordering with `referencedTable` doesn't affect the ordering of the * parent table. * * @example On a referenced table * ```ts * const { data, error } = await supabase * .from('orchestral_sections') * .select(` * name, * instruments ( * name * ) * `) * .order('name', { referencedTable: 'instruments', ascending: false }) * * ``` * * @exampleSql On a referenced table * ```sql * create table * orchestral_sections (id int8 primary key, name text); * create table * instruments ( * id int8 primary key, * section_id int8 not null references orchestral_sections, * name text * ); * * insert into * orchestral_sections (id, name) * values * (1, 'strings'), * (2, 'woodwinds'); * insert into * instruments (id, section_id, name) * values * (1, 1, 'harp'), * (2, 1, 'violin'); * ``` * * @exampleResponse On a referenced table * ```json * { * "data": [ * { * "name": "strings", * "instruments": [ * { * "name": "violin" * }, * { * "name": "harp" * } * ] * }, * { * "name": "woodwinds", * "instruments": [] * } * ], * "status": 200, * "statusText": "OK" * } * ``` * * @exampleDescription Order parent table by a referenced table * Ordering with `referenced_table(col)` affects the ordering of the * parent table. * * @example Order parent table by a referenced table * ```ts * const { data, error } = await supabase * .from('instruments') * .select(` * name, * section:orchestral_sections ( * name * ) * `) * .order('section(name)', { ascending: true }) * * ``` * * @exampleSql Order parent table by a referenced table * ```sql * create table * orchestral_sections (id int8 primary key, name text); * create table * instruments ( * id int8 primary key, * section_id int8 not null references orchestral_sections, * name text * ); * * insert into * orchestral_sections (id, name) * values * (1, 'strings'), * (2, 'woodwinds'); * insert into * instruments (id, section_id, name) * values * (1, 2, 'flute'), * (2, 1, 'violin'); * ``` * * @exampleResponse Order parent table by a referenced table * ```json * { * "data": [ * { * "name": "violin", * "orchestral_sections": {"name": "strings"} * }, * { * "name": "flute", * "orchestral_sections": {"name": "woodwinds"} * } * ], * "status": 200, * "statusText": "OK" * } * ``` */ order(column, { ascending = true, nullsFirst, foreignTable, referencedTable = foreignTable } = {}) { const key = referencedTable ? `${referencedTable}.order` : "order"; const existingOrder = this.url.searchParams.get(key); this.url.searchParams.set(key, `${existingOrder ? `${existingOrder},` : ""}${column}.${ascending ? "asc" : "desc"}${nullsFirst === void 0 ? "" : nullsFirst ? ".nullsfirst" : ".nullslast"}`); return this; } /** * Limit the query result by `count`. * * @param count - The maximum number of rows to return * @param options - Named parameters * @param options.referencedTable - Set this to limit rows of referenced * tables instead of the parent table * @param options.foreignTable - Deprecated, use `options.referencedTable` * instead * * @category Database * @subcategory Using modifiers * * @example With `select()` * ```ts * const { data, error } = await supabase * .from('characters') * .select('name') * .limit(1) * ``` * * @exampleSql With `select()` * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse With `select()` * ```json * { * "data": [ * { * "name": "Luke" * } * ], * "status": 200, * "statusText": "OK" * } * ``` * * @example On a referenced table * ```ts * const { data, error } = await supabase * .from('orchestral_sections') * .select(` * name, * instruments ( * name * ) * `) * .limit(1, { referencedTable: 'instruments' }) * ``` * * @exampleSql On a referenced table * ```sql * create table * orchestral_sections (id int8 primary key, name text); * create table * instruments ( * id int8 primary key, * section_id int8 not null references orchestral_sections, * name text * ); * * insert into * orchestral_sections (id, name) * values * (1, 'strings'); * insert into * instruments (id, section_id, name) * values * (1, 1, 'harp'), * (2, 1, 'violin'); * ``` * * @exampleResponse On a referenced table * ```json * { * "data": [ * { * "name": "strings", * "instruments": [ * { * "name": "violin" * } * ] * } * ], * "status": 200, * "statusText": "OK" * } * ``` */ limit(count, { foreignTable, referencedTable = foreignTable } = {}) { const key = typeof referencedTable === "undefined" ? "limit" : `${referencedTable}.limit`; this.url.searchParams.set(key, `${count}`); return this; } /** * Limit the query result by starting at an offset `from` and ending at the offset `to`. * Only records within this range are returned. * This respects the query order and if there is no order clause the range could behave unexpectedly. * The `from` and `to` values are 0-based and inclusive: `range(1, 3)` will include the second, third * and fourth rows of the query. * * @param from - The starting index from which to limit the result * @param to - The last index to which to limit the result * @param options - Named parameters * @param options.referencedTable - Set this to limit rows of referenced * tables instead of the parent table * @param options.foreignTable - Deprecated, use `options.referencedTable` * instead * * @category Database * @subcategory Using modifiers * * @example With `select()` * ```ts * const { data, error } = await supabase * .from('characters') * .select('name') * .range(0, 1) * ``` * * @exampleSql With `select()` * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse With `select()` * ```json * { * "data": [ * { * "name": "Luke" * }, * { * "name": "Leia" * } * ], * "status": 200, * "statusText": "OK" * } * ``` */ range(from, to, { foreignTable, referencedTable = foreignTable } = {}) { const keyOffset = typeof referencedTable === "undefined" ? "offset" : `${referencedTable}.offset`; const keyLimit = typeof referencedTable === "undefined" ? "limit" : `${referencedTable}.limit`; this.url.searchParams.set(keyOffset, `${from}`); this.url.searchParams.set(keyLimit, `${to - from + 1}`); return this; } /** * Set the AbortSignal for the fetch request. * * @param signal - The AbortSignal to use for the fetch request * * @category Database * @subcategory Using modifiers * * @remarks * You can use this to set a timeout for the request. * * @exampleDescription Aborting requests in-flight * You can use an [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) to abort requests. * Note that `status` and `statusText` don't mean anything for aborted requests as the request wasn't fulfilled. * * @example Aborting requests in-flight * ```ts * const ac = new AbortController() * * const { data, error } = await supabase * .from('very_big_table') * .select() * .abortSignal(ac.signal) * * // Abort the request after 100 ms * setTimeout(() => ac.abort(), 100) * ``` * * @exampleResponse Aborting requests in-flight * ```json * { * "error": { * "message": "AbortError: The user aborted a request.", * "details": "", * "hint": "The request was aborted locally via the provided AbortSignal.", * "code": "" * }, * "status": 0, * "statusText": "" * } * * ``` * * @example Set a timeout * ```ts * const { data, error } = await supabase * .from('very_big_table') * .select() * .abortSignal(AbortSignal.timeout(1000 /* ms *\/)) * ``` * * @exampleResponse Set a timeout * ```json * { * "error": { * "message": "FetchError: The user aborted a request.", * "details": "", * "hint": "", * "code": "" * }, * "status": 400, * "statusText": "Bad Request" * } * * ``` */ abortSignal(signal) { this.signal = signal; return this; } /** * Return `data` as a single object instead of an array of objects. * * Query result must be one row (e.g. using `.limit(1)`), otherwise this * returns an error. * * @category Database * @subcategory Using modifiers * * @example With `select()` * ```ts * const { data, error } = await supabase * .from('characters') * .select('name') * .limit(1) * .single() * ``` * * @exampleSql With `select()` * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse With `select()` * ```json * { * "data": { * "name": "Luke" * }, * "status": 200, * "statusText": "OK" * } * ``` */ single() { this.headers.set("Accept", "application/vnd.pgrst.object+json"); return this; } /** * Return `data` as a single object instead of an array of objects. * * Query result must be zero or one row (e.g. using `.limit(1)`), otherwise * this returns an error. * * @category Database * @subcategory Using modifiers * * @example With `select()` * ```ts * const { data, error } = await supabase * .from('characters') * .select() * .eq('name', 'Katniss') * .maybeSingle() * ``` * * @exampleSql With `select()` * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse With `select()` * ```json * { * "status": 200, * "statusText": "OK" * } * ``` */ maybeSingle() { this.isMaybeSingle = true; return this; } /** * Return `data` as a string in CSV format. * * @category Database * @subcategory Using modifiers * * @exampleDescription Return data as CSV * By default, the data is returned in JSON format, but can also be returned as Comma Separated Values. * * @example Return data as CSV * ```ts * const { data, error } = await supabase * .from('characters') * .select() * .csv() * ``` * * @exampleSql Return data as CSV * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse Return data as CSV * ```json * { * "data": "id,name\n1,Luke\n2,Leia\n3,Han", * "status": 200, * "statusText": "OK" * } * ``` */ csv() { this.headers.set("Accept", "text/csv"); return this; } /** * Return `data` as an object in [GeoJSON](https://geojson.org) format. * * @category Database */ geojson() { this.headers.set("Accept", "application/geo+json"); return this; } /** * Return `data` as the EXPLAIN plan for the query. * * You need to enable the * [db_plan_enabled](https://supabase.com/docs/guides/database/debugging-performance#enabling-explain) * setting before using this method. * * @param options - Named parameters * * @param options.analyze - If `true`, the query will be executed and the * actual run time will be returned * * @param options.verbose - If `true`, the query identifier will be returned * and `data` will include the output columns of the query * * @param options.settings - If `true`, include information on configuration * parameters that affect query planning * * @param options.buffers - If `true`, include information on buffer usage * * @param options.wal - If `true`, include information on WAL record generation * * @param options.format - The format of the output, can be `"text"` (default) * or `"json"` * * @category Database * @subcategory Using modifiers * * @exampleDescription Get the execution plan * By default, the data is returned in TEXT format, but can also be returned as JSON by using the `format` parameter. * * @example Get the execution plan * ```ts * const { data, error } = await supabase * .from('characters') * .select() * .explain() * ``` * * @exampleSql Get the execution plan * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse Get the execution plan * ```js * Aggregate (cost=33.34..33.36 rows=1 width=112) * -> Limit (cost=0.00..18.33 rows=1000 width=40) * -> Seq Scan on characters (cost=0.00..22.00 rows=1200 width=40) * ``` * * @exampleDescription Get the execution plan with analyze and verbose * By default, the data is returned in TEXT format, but can also be returned as JSON by using the `format` parameter. * * @example Get the execution plan with analyze and verbose * ```ts * const { data, error } = await supabase * .from('characters') * .select() * .explain({analyze:true,verbose:true}) * ``` * * @exampleSql Get the execution plan with analyze and verbose * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse Get the execution plan with analyze and verbose * ```js * Aggregate (cost=33.34..33.36 rows=1 width=112) (actual time=0.041..0.041 rows=1 loops=1) * 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) * -> Limit (cost=0.00..18.33 rows=1000 width=40) (actual time=0.005..0.006 rows=3 loops=1) * Output: characters.id, characters.name * -> Seq Scan on public.characters (cost=0.00..22.00 rows=1200 width=40) (actual time=0.004..0.005 rows=3 loops=1) * Output: characters.id, characters.name * Query Identifier: -4730654291623321173 * Planning Time: 0.407 ms * Execution Time: 0.119 ms * ``` */ explain({ analyze = false, verbose = false, settings = false, buffers = false, wal = false, format = "text" } = {}) { var _this$headers$get; const options = [ analyze ? "analyze" : null, verbose ? "verbose" : null, settings ? "settings" : null, buffers ? "buffers" : null, wal ? "wal" : null ].filter(Boolean).join("|"); const forMediatype = (_this$headers$get = this.headers.get("Accept")) !== null && _this$headers$get !== void 0 ? _this$headers$get : "application/json"; this.headers.set("Accept", `application/vnd.pgrst.plan+${format}; for="${forMediatype}"; options=${options};`); if (format === "json") return this; else return this; } /** * Rollback the query. * * `data` will still be returned, but the query is not committed. * * @category Database */ rollback() { this.headers.append("Prefer", "tx=rollback"); return this; } /** * Override the type of the returned `data`. * * @typeParam NewResult - The new result type to override with * @deprecated Use overrideTypes<yourType, { merge: false }>() method at the end of your call chain instead * * @category Database * @subcategory Using modifiers * * @remarks * - Deprecated: use overrideTypes method instead * * @example Override type of successful response * ```ts * const { data } = await supabase * .from('countries') * .select() * .returns<Array<MyType>>() * ``` * * @exampleResponse Override type of successful response * ```js * let x: typeof data // MyType[] * ``` * * @example Override type of object response * ```ts * const { data } = await supabase * .from('countries') * .select() * .maybeSingle() * .returns<MyType>() * ``` * * @exampleResponse Override type of object response * ```js * let x: typeof data // MyType | null * ``` */ returns() { return this; } /** * Set the maximum number of rows that can be affected by the query. * Only available in PostgREST v13+ and only works with PATCH and DELETE methods. * * @param value - The maximum number of rows that can be affected * * @category Database */ maxAffected(value) { this.headers.append("Prefer", "handling=strict"); this.headers.append("Prefer", `max-affected=${value}`); return this; } }; //#endregion //#region src/PostgrestFilterBuilder.ts const PostgrestReservedCharsRegexp = /* @__PURE__ */ new RegExp("[,()]"); var PostgrestFilterBuilder = class extends PostgrestTransformBuilder { /** * Match only rows where `column` is equal to `value`. * * To check if the value of `column` is NULL, you should use `.is()` instead. * * @param column - The column to filter on * @param value - The value to filter with * * @category Database * @subcategory Using filters * * @example With `select()` * ```ts * const { data, error } = await supabase * .from('characters') * .select() * .eq('name', 'Leia') * ``` * * @exampleSql With `select()` * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse With `select()` * ```json * { * "data": [ * { * "id": 2, * "name": "Leia" * } * ], * "status": 200, * "statusText": "OK" * } * ``` */ eq(column, value) { this.url.searchParams.append(column, `eq.${value}`); return this; } /** * Match only rows where `column` is not equal to `value`. * * This filter does not include rows where `column` is `NULL`. To match null * values, use `.is(column, null)` instead. * * @param column - The column to filter on * @param value - The value to filter with * * @category Database * @subcategory Using filters * * @example With `select()` * ```ts * const { data, error } = await supabase * .from('characters') * .select() * .neq('name', 'Leia') * ``` * * @exampleSql With `select()` * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse With `select()` * ```json * { * "data": [ * { * "id": 1, * "name": "Luke" * }, * { * "id": 3, * "name": "Han" * } * ], * "status": 200, * "statusText": "OK" * } * ``` */ neq(column, value) { this.url.searchParams.append(column, `neq.${value}`); return this; } /** * Match only rows where `column` is greater than `value`. * * @param column - The column to filter on * @param value - The value to filter with * * @category Database * @subcategory Using filters * * @exampleDescription With `select()` * When using [reserved words](https://www.postgresql.org/docs/current/sql-keywords-appendix.html) for column names you need * to add double quotes e.g. `.gt('"order"', 2)` * * @example With `select()` * ```ts * const { data, error } = await supabase * .from('characters') * .select() * .gt('id', 2) * ``` * * @exampleSql With `select()` * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse With `select()` * ```json * { * "data": [ * { * "id": 3, * "name": "Han" * } * ], * "status": 200, * "statusText": "OK" * } * ``` */ gt(column, value) { this.url.searchParams.append(column, `gt.${value}`); return this; } /** * Match only rows where `column` is greater than or equal to `value`. * * @param column - The column to filter on * @param value - The value to filter with * * @category Database * @subcategory Using filters * * @example With `select()` * ```ts * const { data, error } = await supabase * .from('characters') * .select() * .gte('id', 2) * ``` * * @exampleSql With `select()` * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse With `select()` * ```json * { * "data": [ * { * "id": 2, * "name": "Leia" * }, * { * "id": 3, * "name": "Han" * } * ], * "status": 200, * "statusText": "OK" * } * ``` */ gte(column, value) { this.url.searchParams.append(column, `gte.${value}`); return this; } /** * Match only rows where `column` is less than `value`. * * @param column - The column to filter on * @param value - The value to filter with * * @category Database * @subcategory Using filters * * @example With `select()` * ```ts * const { data, error } = await supabase * .from('characters') * .select() * .lt('id', 2) * ``` * * @exampleSql With `select()` * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse With `select()` * ```json * { * "data": [ * { * "id": 1, * "name": "Luke" * } * ], * "status": 200, * "statusText": "OK" * } * ``` */ lt(column, value) { this.url.searchParams.append(column, `lt.${value}`); return this; } /** * Match only rows where `column` is less than or equal to `value`. * * @param column - The column to filter on * @param value - The value to filter with * * @category Database * @subcategory Using filters * * @example With `select()` * ```ts * const { data, error } = await supabase * .from('characters') * .select() * .lte('id', 2) * ``` * * @exampleSql With `select()` * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse With `select()` * ```json * { * "data": [ * { * "id": 1, * "name": "Luke" * }, * { * "id": 2, * "name": "Leia" * } * ], * "status": 200, * "statusText": "OK" * } * ``` */ lte(column, value) { this.url.searchParams.append(column, `lte.${value}`); return this; } /** * Match only rows where `column` matches `pattern` case-sensitively. * * @param column - The column to filter on * @param pattern - The pattern to match with * * @category Database * @subcategory Using filters * * @example With `select()` * ```ts * const { data, error } = await supabase * .from('characters') * .select() * .like('name', '%Lu%') * ``` * * @exampleSql With `select()` * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse With `select()` * ```json * { * "data": [ * { * "id": 1, * "name": "Luke" * } * ], * "status": 200, * "statusText": "OK" * } * ``` */ like(column, pattern) { this.url.searchParams.append(column, `like.${pattern}`); return this; } /** * Match only rows where `column` matches all of `patterns` case-sensitively. * * @param column - The column to filter on * @param patterns - The patterns to match with * * @category Database * @subcategory Using filters */ likeAllOf(column, patterns) { this.url.searchParams.append(column, `like(all).{${patterns.join(",")}}`); return this; } /** * Match only rows where `column` matches any of `patterns` case-sensitively. * * @param column - The column to filter on * @param patterns - The patterns to match with * * @category Database * @subcategory Using filters */ likeAnyOf(column, patterns) { this.url.searchParams.append(column, `like(any).{${patterns.join(",")}}`); return this; } /** * Match only rows where `column` matches `pattern` case-insensitively. * * @param column - The column to filter on * @param pattern - The pattern to match with * * @category Database * @subcategory Using filters * * @example With `select()` * ```ts * const { data, error } = await supabase * .from('characters') * .select() * .ilike('name', '%lu%') * ``` * * @exampleSql With `select()` * ```sql * create table * characters (id int8 primary key, name text); * * insert into * characters (id, name) * values * (1, 'Luke'), * (2, 'Leia'), * (3, 'Han'); * ``` * * @exampleResponse With `select()` * ```json * { * "data": [ * { * "id": 1, * "name": "Luke" * } * ], * "status": 200, * "statusText": "OK" * } * ``` */ ilike(column, pattern) { this.url.searchParams.append(column, `ilike.${pattern}`); return this; } /** * Match only rows where `column` matches all of `patterns` case-insensitively. * * @param column - The column to filter on * @param patterns - The patterns to match with * * @category Database * @subcategory Using filters */ ilikeAllOf(column, patterns) { this.url.searchParams.append(column, `ilike(all).{${patterns.join(",")}}`); return this; } /** * Match only rows where `column` matches any of `patterns` case-insensitively. * * @param column - The column to filter on * @param patterns - The patterns to match with * * @category Database * @subcategory Using filters */ ilikeAnyOf(column, patterns) { this.url.searchParams.append(column, `ilike(any).{${patterns.join(",")}}`); return this; } /** * Match only rows where `column` matches the PostgreSQL regex `pattern` * case-sensitively (using the `~` operator). * * @param column - The column to filter on * @param pattern - The PostgreSQL regular expression pattern to match with */ regexMatch(column, pattern) { this.url.searchParams.append(column, `match.${pattern}`); return this; } /** * Match only rows where `column` matches the PostgreSQL regex `pattern` * case-insensitively (using the `~*` operator). * * @param column - The column to filter on * @param pattern - The PostgreSQL regular expression pattern to match with */ regexIMatch(column, pattern) { this.url.searchParams.append(column, `imatch.${pattern}`); return this; } /** * Match only rows where `column` IS `value`. * * For non-boolean columns, this is only relevant for checking if the value of * `column` is NULL by setting `value` to `null`. * * For boolean columns, you can also set `value` to `true` or `false` and it * will behave the same way as `.eq()`. * * @param column - The column to filter on * @param value - The value to filter with * * @category Database * @subcategory Using filters * * @exampleDescription Checking for nullness, true or false * Using the `eq()` filter doesn't work when filtering for `null`. * * Instead, you need to use `is()`. * * @example Checking for nullness, true or false * ```ts * const { data, error } = await supabase * .from('countries') * .select() * .is('name', null) * ``` * * @exampleSql Checking for nullness, true or false * ```sql * create table * countries (id int8 primary key, name text); * * insert into * countries (id, name) * values * (1, 'null'), * (2, null); * ``` * * @exampleResponse Checking for nullness, true or false * ```json * { * "data": [ * { * "id": 2, * "name": "null" * } * ], * "status": 200, * "statusText": "OK" * } * ``` */ is(column, value) { this.url.searchParams.append(column, `is.${value}`); return this; } /** * Match only rows where `column` IS DISTINCT FROM `value`. * * Unlike `.neq()`, this treats `NULL` as a comparable value. Two `NULL` values * are considered equal (not distinct), and comparing `NULL` with any non-NULL * value returns true (distinct). * * @param column - The column to filter on * @param value - The value to filter with */ isDistinct(column, value) { this.url.searchParams.append(column, `isdistinct.${value}`); return this; } /** * Match only rows where `column` is included in the `values` array. * * @param column - The column to filter on * @param values - The values array to filter with * * @category Database * @subcategory Using filters * * @example With `select()` * ```ts * const { data, error } = await supabase *