@supabase/postgrest-js
Version:
Isomorphic PostgREST client
1 lines • 84 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","names":["count: number | null","res","this","fetch","fetch","method: 'HEAD' | 'GET' | 'POST'","body: unknown | undefined"],"sources":["../src/PostgrestError.ts","../src/PostgrestBuilder.ts","../src/PostgrestTransformBuilder.ts","../src/PostgrestFilterBuilder.ts","../src/PostgrestQueryBuilder.ts","../src/PostgrestClient.ts","../src/index.ts"],"sourcesContent":["/**\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","import type {\n PostgrestSingleResponse,\n PostgrestResponseSuccess,\n CheckMatchingArrayTypes,\n MergePartialResult,\n IsValidResultOverride,\n} from './types/types'\nimport { ClientServerOptions, Fetch } from './types/common/common'\nimport PostgrestError from './PostgrestError'\nimport { ContainsNull } from './select-query-parser/types'\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\n /**\n * Creates a builder configured for a specific PostgREST request.\n *\n * @example\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: 'public-anon-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 }) {\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\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 throwOnError(): this & PostgrestBuilder<ClientOptions, Result, true> {\n this.shouldThrowOnError = true\n return this as this & PostgrestBuilder<ClientOptions, Result, true>\n }\n\n /**\n * Set an HTTP header for the request.\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 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 // 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 let res = _fetch(this.url.toString(), {\n method: this.method,\n headers: this.headers,\n body: JSON.stringify(this.body),\n signal: this.signal,\n }).then(async (res) => {\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 // Temporary partial fix for https://github.com/supabase/postgrest-js/issues/361\n // Issue persists e.g. for `.insert([...]).select().maybeSingle()`\n if (this.isMaybeSingle && this.method === 'GET' && 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.isMaybeSingle && error?.details?.includes('0 rows')) {\n error = null\n status = 200\n statusText = 'OK'\n }\n\n if (error && this.shouldThrowOnError) {\n throw new PostgrestError(error)\n }\n }\n\n const postgrestResponse = {\n error,\n data,\n count,\n status,\n statusText,\n }\n\n return postgrestResponse\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\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 return {\n error: {\n message: `${fetchError?.name ?? 'FetchError'}: ${fetchError?.message}`,\n details: errorDetails,\n hint: '',\n code: '',\n },\n data: null,\n count: null,\n status: 0,\n statusText: '',\n }\n })\n }\n\n return res.then(onfulfilled, onrejected)\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 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 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 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 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 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 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 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 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 maybeSingle<\n ResultOne = Result extends (infer ResultOne)[] ? ResultOne : never,\n >(): PostgrestBuilder<ClientOptions, ResultOne | null> {\n // Temporary partial fix for https://github.com/supabase/postgrest-js/issues/361\n // Issue persists e.g. for `.insert([...]).select().maybeSingle()`\n if (this.method === 'GET') {\n this.headers.set('Accept', 'application/json')\n } else {\n this.headers.set('Accept', 'application/vnd.pgrst.object+json')\n }\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 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 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 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 buffers ? 'buffers' : null,\n wal ? 'wal' : null,\n ]\n .filter(Boolean)\n .join('|')\n // An Accept header can carry multiple media types but postgrest-js always sends one\n const forMediatype = this.headers.get('Accept') ?? 'application/json'\n this.headers.set(\n 'Accept',\n `application/vnd.pgrst.plan+${format}; for=\"${forMediatype}\"; options=${options};`\n )\n if (format === 'json') {\n return this as unknown as PostgrestBuilder<ClientOptions, Record<string, unknown>[]>\n } else {\n return this as unknown as PostgrestBuilder<ClientOptions, string>\n }\n }\n\n /**\n * Rollback the query.\n *\n * `data` will still be returned, but the query is not committed.\n */\n rollback(): this {\n this.headers.append('Prefer', 'tx=rollback')\n return this\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 returns<NewResult>(): PostgrestTransformBuilder<\n ClientOptions,\n Schema,\n Row,\n CheckMatchingArrayTypes<Result, NewResult>,\n RelationName,\n Relationships,\n Method\n > {\n return this as unknown as PostgrestTransformBuilder<\n ClientOptions,\n Schema,\n Row,\n CheckMatchingArrayTypes<Result, NewResult>,\n RelationName,\n Relationships,\n Method\n >\n }\n\n /**\n * Set the maximum number of rows that can be affected by the query.\n * Only available in PostgREST v13+ and only works with PATCH and DELETE methods.\n *\n * @param value - The maximum number of rows that can be affected\n */\n maxAffected(value: number): MaxAffectedEnabled<ClientOptions['PostgrestVersion']> extends true\n ? // TODO: update the RPC case to only work on RPC that returns SETOF rows\n Method extends 'PATCH' | 'DELETE' | 'RPC'\n ? this\n : InvalidMethodError<'maxAffected method only available on update or delete'>\n : InvalidMethodError<'maxAffected method only available on postgrest 13+'> {\n this.headers.append('Prefer', 'handling=strict')\n this.headers.append('Prefer', `max-affected=${value}`)\n return this as unknown as MaxAffectedEnabled<ClientOptions['PostgrestVersion']> extends true\n ? Method extends 'PATCH' | 'DELETE' | 'RPC'\n ? this\n : InvalidMethodError<'maxAffected method only available on update or delete'>\n : InvalidMethodError<'maxAffected method only available on postgrest 13+'>\n }\n}\n","import PostgrestTransformBuilder from './PostgrestTransformBuilder'\nimport { JsonPathToAccessor, JsonPathToType } from './select-query-parser/utils'\nimport { ClientServerOptions, GenericSchema } from './types/common/common'\n\ntype FilterOperator =\n | 'eq'\n | 'neq'\n | 'gt'\n | 'gte'\n | 'lt'\n | 'lte'\n | 'like'\n | 'ilike'\n | 'is'\n | 'isdistinct'\n | 'in'\n | 'cs'\n | 'cd'\n | 'sl'\n | 'sr'\n | 'nxl'\n | 'nxr'\n | 'adj'\n | 'ov'\n | 'fts'\n | 'plfts'\n | 'phfts'\n | 'wfts'\n | 'match'\n | 'imatch'\n\nexport type IsStringOperator<Path extends string> = Path extends `${string}->>${string}`\n ? true\n : false\n\nconst PostgrestReservedCharsRegexp = new RegExp('[,()]')\n\n// Match relationship filters with `table.column` syntax and resolve underlying\n// column value. If not matched, fallback to generic type.\n// TODO: Validate the relationship itself ala select-query-parser. Currently we\n// assume that all tables have valid relationships to each other, despite\n// nonexistent foreign keys.\ntype ResolveFilterValue<\n Schema extends GenericSchema,\n Row extends Record<string, unknown>,\n ColumnName extends string,\n> = ColumnName extends `${infer RelationshipTable}.${infer Remainder}`\n ? Remainder extends `${infer _}.${infer _}`\n ? ResolveFilterValue<Schema, Row, Remainder>\n : ResolveFilterRelationshipValue<Schema, RelationshipTable, Remainder>\n : ColumnName extends keyof Row\n ? Row[ColumnName]\n : // If the column selection is a jsonpath like `data->value` or `data->>value` we attempt to match\n // the expected type with the parsed custom json type\n IsStringOperator<ColumnName> extends true\n ? string\n : JsonPathToType<Row, JsonPathToAccessor<ColumnName>> extends infer JsonPathValue\n ? JsonPathValue extends never\n ? never\n : JsonPathValue\n : never\n\ntype ResolveFilterRelationshipValue<\n Schema extends GenericSchema,\n RelationshipTable extends string,\n RelationshipColumn extends string,\n> = Schema['Tables'] & Schema['Views'] extends infer TablesAndViews\n ? RelationshipTable extends keyof TablesAndViews\n ? 'Row' extends keyof TablesAndViews[RelationshipTable]\n ? RelationshipColumn extends keyof TablesAndViews[RelationshipTable]['Row']\n ? TablesAndViews[RelationshipTable]['Row'][RelationshipColumn]\n : unknown\n : unknown\n : unknown\n : never\n\nexport type InvalidMethodError<S extends string> = { Error: S }\n\nexport default class PostgrestFilterBuilder<\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 PostgrestTransformBuilder<\n ClientOptions,\n Schema,\n Row,\n Result,\n RelationName,\n Relationships,\n Method\n> {\n /**\n * Match only rows where `column` is equal to `value`.\n *\n * To check if the value of `column` is NULL, you should use `.is()` instead.\n *\n * @param column - The column to filter on\n * @param value - The value to filter with\n */\n eq<ColumnName extends string>(\n column: ColumnName,\n value: ResolveFilterValue<Schema, Row, ColumnName> extends never\n ? NonNullable<unknown>\n : // We want to infer the type before wrapping it into a `NonNullable` to avoid too deep\n // type resolution error\n ResolveFilterValue<Schema, Row, ColumnName> extends infer ResolvedFilterValue\n ? NonNullable<ResolvedFilterValue>\n : // We should never enter this case as all the branches are covered above\n never\n ): this {\n this.url.searchParams.append(column, `eq.${value}`)\n return this\n }\n\n /**\n * Match only rows where `column` is not equal to `value`.\n *\n * @param column - The column to filter on\n * @param value - The value to filter with\n */\n neq<ColumnName extends string>(\n column: ColumnName,\n value: ResolveFilterValue<Schema, Row, ColumnName> extends never\n ? unknown\n : ResolveFilterValue<Schema, Row, ColumnName> extends infer ResolvedFilterValue\n ? ResolvedFilterValue\n : never\n ): this {\n this.url.searchParams.append(column, `neq.${value}`)\n return this\n }\n\n gt<ColumnName extends string & keyof Row>(column: ColumnName, value: Row[ColumnName]): this\n gt(column: string, value: unknown): this\n /**\n * Match only rows where `column` is greater than `value`.\n *\n * @param column - The column to filter on\n * @param value - The value to filter with\n */\n gt(column: string, value: unknown): this {\n this.url.searchParams.append(column, `gt.${value}`)\n return this\n }\n\n gte<ColumnName extends string & keyof Row>(column: ColumnName, value: Row[ColumnName]): this\n gte(column: string, value: unknown): this\n /**\n * Match only rows where `column` is greater than or equal to `value`.\n *\n * @param column - The column to filter on\n * @param value - The value to filter with\n */\n gte(column: string, value: unknown): this {\n this.url.searchParams.append(column, `gte.${value}`)\n return this\n }\n\n lt<ColumnName extends string & keyof Row>(column: ColumnName, value: Row[ColumnName]): this\n lt(column: string, value: unknown): this\n /**\n * Match only rows where `column` is less than `value`.\n *\n * @param column - The column to filter on\n * @param value - The value to filter with\n */\n lt(column: string, value: unknown): this {\n this.url.searchParams.append(column, `lt.${value}`)\n return this\n }\n\n lte<ColumnName extends string & keyof Row>(column: ColumnName, value: Row[ColumnName]): this\n lte(column: string, value: unknown): this\n /**\n * Match only rows where `column` is less than or equal to `value`.\n *\n * @param column - The column to filter on\n * @param value - The value to filter with\n */\n lte(column: string, value: unknown): this {\n this.url.searchParams.append(column, `lte.${value}`)\n return this\n }\n\n like<ColumnName extends string & keyof Row>(column: ColumnName, pattern: string): this\n like(column: string, pattern: string): this\n /**\n * Match only rows where `column` matches `pattern` case-sensitively.\n *\n * @param column - The column to filter on\n * @param pattern - The pattern to match with\n */\n like(column: string, pattern: string): this {\n this.url.searchParams.append(column, `like.${pattern}`)\n return this\n }\n\n likeAllOf<ColumnName extends string & keyof Row>(\n column: ColumnName,\n patterns: readonly string[]\n ): this\n likeAllOf(column: string, patterns: readonly string[]): this\n /**\n * Match only rows where `column` matches all of `patterns` case-sensitively.\n *\n * @param column - The column to filter on\n * @param patterns - The patterns to match with\n */\n likeAllOf(column: string, patterns: readonly string[]): this {\n this.url.searchParams.append(column, `like(all).{${patterns.join(',')}}`)\n return this\n }\n\n likeAnyOf<ColumnName extends string & keyof Row>(\n column: ColumnName,\n patterns: readonly string[]\n ): this\n likeAnyOf(column: string, patterns: readonly string[]): this\n /**\n * Match only rows where `column` matches any of `patterns` case-sensitively.\n *\n * @param column - The column to filter on\n * @param patterns - The patterns to match with\n */\n likeAnyOf(column: string, patterns: readonly string[]): this {\n this.url.searchParams.append(column, `like(any).{${patterns.join(',')}}`)\n return this\n }\n\n ilike<ColumnName extends string & keyof Row>(column: ColumnName, pattern: string): this\n ilike(column: string, pattern: string): this\n /**\n * Match only rows where `column` matches `pattern` case-insensitively.\n *\n * @param column - The column to filter on\n * @param pattern - The pattern to match with\n */\n ilike(column: string, pattern: string): this {\n this.url.searchParams.append(column, `ilike.${pattern}`)\n return this\n }\n\n ilikeAllOf<ColumnName extends string & keyof Row>(\n column: ColumnName,\n patterns: readonly string[]\n ): this\n ilikeAllOf(column: string, patterns: readonly string[]): this\n /**\n * Match only rows where `column` matches all of `patterns` case-insensitively.\n *\n * @param column - The column to filter on\n * @param patterns - The patterns to match with\n */\n ilikeAllOf(column: string, patterns: readonly string[]): this {\n this.url.searchParams.append(column, `ilike(all).{${patterns.join(',')}}`)\n return this\n }\n\n ilikeAnyOf<ColumnName extends string & keyof Row>(\n column: ColumnName,\n patterns: readonly string[]\n ): this\n ilikeAnyOf(column: string, patterns: readonly string[]): this\n /**\n * Match only rows where `column` matches any of `patterns` case-insensitively.\n *\n * @param column - The column to filter on\n * @param patterns - The patterns to match with\n */\n ilikeAnyOf(column: string, patterns: readonly string[]): this {\n this.url.searchParams.append(column, `ilike(any).{${patterns.join(',')}}`)\n return this\n }\n\n regexMatch<ColumnName extends string & keyof Row>(column: ColumnName, pattern: string): this\n regexMatch(column: string, pattern: string): this\n /**\n * Match only rows where `column` matches the PostgreSQL regex `pattern`\n * case-sensitively (using the `~` operator).\n *\n * @param column - The column to filter on\n * @param pattern - The PostgreSQL regular expression pattern to match with\n */\n regexMatch(column: string, pattern: string): this {\n this.url.searchParams.append(column, `match.${pattern}`)\n return this\n }\n\n regexIMatch<ColumnName extends string & keyof Row>(column: ColumnName, pattern: string): this\n regexIMatch(column: string, pattern: string): this\n /**\n * Match only rows where `column` matches the PostgreSQL regex `pattern`\n * case-insensitively (using the `~*` operator).\n *\n * @param column - The column to filter on\n * @param pattern - The PostgreSQL regular expression pattern to match with\n */\n regexIMatch(column: string, pattern: string): this {\n this.url.searchParams.append(column, `imatch.${pattern}`)\n return this\n }\n\n is<ColumnName extends string & keyof Row>(\n column: ColumnName,\n value: Row[ColumnName] & (boolean | null)\n ): this\n is(column: string, value: boolean | null): this\n /**\n * Match only rows where `column` IS `value`.\n *\n * For non-boolean columns, this is only relevant for checking if the value of\n * `column` is NULL by setting `value` to `null`.\n *\n * For boolean columns, you can also set `value` to `true` or `false` and it\n * will behave the same way as `.eq()`.\n *\n * @param column - The column to filter on\n * @param value - The value to filter with\n */\n is(column: string, value: boolean | null): this {\n this.url.searchParams.append(column, `is.${value}`)\n return this\n }\n\n /**\n * Match only rows where `column` IS DISTINCT FROM `value`.\n *\n * Unlike `.neq()`, this treats `NULL` as a comparable value. Two `NULL` values\n * are considered equal (not distinct), and comparing `NULL` with any non-NULL\n * value returns true (distinct).\n *\n * @param column - The column to filter on\n * @param value - The value to filter with\n */\n isDistinct<ColumnName extends string>(\n column: ColumnName,\n value: ResolveFilterValue<Schema, Row, ColumnName> extends never\n ? unknown\n : ResolveFilterValue<Schema, Row, ColumnName> extends infer ResolvedFilterValue\n ? ResolvedFilterValue\n : never\n ): this {\n this.url.searchParams.append(column, `isdistinct.${value}`)\n return this\n }\n\n /**\n * Match only rows where `column` is included in the `values` array.\n *\n * @param column - The column to filter on\n * @param values - The values array to filter with\n */\n in<ColumnName extends string>(\n column: ColumnName,\n values: ReadonlyArray<\n ResolveFilterValue<Schema, Row, ColumnName> extends never\n ? unknown\n : // We want to infer the type before wrapping it into a `NonNullable` to avoid too deep\n // type resolution error\n ResolveFilterValue<Schema, Row, ColumnName> extends infer ResolvedFilterValue\n ? ResolvedFilterValue\n : // We should never enter this case as all the branches are covered above\n never\n >\n ): this {\n const cleanedValues = Array.from(new Set(values))\n .map((s) => {\n // handle postgrest reserved characters\n // https://postgrest.org/en/v7.0.0/api.html#reserved-characters\n if (typeof s === 'string' && PostgrestReservedCharsRegexp.test(s)) return `\"${s}\"`\n else return `${s}`\n })\n .join(',')\n this.url.searchParams.append(column, `in.(${cleanedValues})`)\n return this\n }\n\n /**\n * Match only rows where `column` is NOT included in the `values` array.\n *\n * @param column - The column to filter on\n * @param values - The values array to filter with\n */\n notIn<ColumnName extends string>(\n column: ColumnName,\n values: ReadonlyArray<\n ResolveFilterValue<Schema, Row, ColumnName> extends never\n ? unknown\n : ResolveFilterValue<Schema, Row, ColumnName> extends infer ResolvedFilterValue\n ? ResolvedFilterValue\n : never\n >\n ): this {\n const cleanedValues = Array.from(new Set(values))\n .map((s) => {\n // handle postgrest reserved characters\n // https://postgrest.org/en/v7.0.0/api.html#reserved-characters\n if (typeof s === 'string' && PostgrestReservedCharsRegexp.test(s)) return `\"${s}\"`\n else return `${s}`\n })\n .join(',')\n this.url.searchParams.append(column, `not.in.(${cleanedValues})`)\n return this\n }\n\n contains<ColumnName extends string & keyof Row>(\n column: ColumnName,\n value: string | ReadonlyArray<Row[ColumnName]> | Record<string, unknown>\n ): this\n contains(column: string, value: string | readonly unknown[] | Record<string, unknown>): this\n /**\n * Only relevant for jsonb, array, and range columns. Match only rows where\n * `column` contains every element appearing in `value`.\n *\n * @param column - The jsonb, array, or range column to filter on\n * @param value - The jsonb, array, or range value to filter with\n */\n contains(column: string, value: string | readonly unknown[] | Record<string, unknown>): this {\n if (typeof value === 'string') {\n // range types can be inclusive '[', ']' or exclusive '(', ')' so just\n // keep it simple and accept a string\n this.url.searchParams.append(column, `cs.${value}`)\n } else if (Array.isArray(value)) {\n // array\n this.url.searchParams.append(column, `cs.{${value.join(',')}}`)\n } else {\n // json\n this.url.searchParams.append(column, `cs.${JSON.stringify(value)}`)\n }\n return this\n }\n\n containedBy<ColumnName extends string & keyof Row>(\n column: ColumnName,\n value: string | ReadonlyArray<Row[ColumnName]> | Record<string, unknown>\n ): this\n containedBy(column: string, value: string | readonly unknown[] | Record<string, unknown>): this\n /**\n * Only relevant for jsonb, array, and range columns. Match only rows where\n * every element appearing in `column` is contained by `value`.\n *\n * @param column - The jsonb, array, or range column to filter on\n * @param value - The jsonb, array, or range value to filter with\n */\n containedBy(column: string, value: string | readonly unknown[] | Record<string, unknown>): this {\n if (typeof value === 'string') {\n // range\n this.url.searchParams.append(column, `cd.${value}`)\n } else if (Array.isArray(value)) {\n // array\n this.url.searchParams.append(column, `cd.{${value.join(',')}}`)\n } else {\n // json\n this.url.searchParams.append(column, `cd.${JSON.stringify(value)}`)\n }\n return this\n }\n\n rangeGt<ColumnName extends string & keyof Row>(column: ColumnName, range: string): this\n rangeGt(column: string, range: string): this\n /**\n * Only relevant for range columns. Match only rows where every element in\n * `column` is greater than any element in `range`.\n *\n * @param column - The range column to filter on\n * @param range - The range to filter with\n */\n rangeGt(column: string, range: string): this {\n this.url.searchParams.append(column, `sr.${range}`)\n return this\n }\n\n rangeGte<ColumnName extends string & keyof Row>(column: ColumnName, range: string): this\n rangeGte(column: string, range: string): this\n /**\n * Only relevant for range columns. Match only rows where every element in\n * `column` is either contained in `range` or greater than any element in\n * `range`.\n *\n * @param column - The range column to filter on\n * @param range - The range to filter with\n */\n rangeGte(column: string, range: string): this {\n this.url.searchParams.append(column, `nxl.${range}`)\n return this\n }\n\n rangeLt<ColumnName extends string & keyof Row>(column: ColumnName, range: string): this\n rangeLt(column: string, range: string): this\n /**\n * Only relevant for range columns. Match only rows where every element in\n * `column` is less than any element in `range`.\n *\n * @param column - The range column to filter on\n * @param range - The range to filter with\n */\n rangeLt(column: string, range: string): this {\n this.url.searchParams.append(column, `sl.${range}`)\n return this\n }\n\n rangeLte<ColumnName extends string & keyof Row>(column: ColumnName, range: string): this\n rangeLte(column: string, range: string): this\n /**\n * Only relevant for range columns. Match only rows where every element in\n * `column` is either contained in `range` or less than any element in\n * `range`.\n *\n * @param column - The range column to filter on\n * @param range - The range to filter with\n */\n rangeLte(column: string, range: string): this {\n this.url.searchParams.append(column, `nxr.${range}`)\n return this\n }\n\n rangeAdjacent<ColumnName extends string & keyof Row>(column: ColumnName, range: string): this\n rangeAdjacent(column: string, range: string): this\n /**\n * Only relevant for range columns. Match only rows where `column` is\n * mutually exclusive to `range` and there can be no element between the two\n * ranges.\n *\n * @param column - The range column to filter on\n * @param range - The range to filter with\n */\n rangeAdjacent(column: string, range: string): this {\n this.url.searchParams.append(column, `adj.${range}`)\n return this\n }\n\n overlaps<ColumnName extends string & keyof Row>(\n column: ColumnName,\n value: string | ReadonlyArray<Row[ColumnName]>\n ): this\n overlaps(column: string, value: string | readonly unknown[]): this\n /**\n * Only relevant for array and range columns. Match only rows where\n * `column` and `value` have an element in common.\n *\n * @param column - The array or range column to filter on\n * @param value - The array or range value to filter with\n */\n overlaps(column: string, value: string | readonly unknown[]): this {\n if (typeof value === 'string') {\n // range\n this.url.searchParams.append(column, `ov.${value}`)\n } else {\n // array\n this.url.searchParams.append(column, `ov.{${value.join(',')}}`)\n }\n return this\n }\n\n textSearch<ColumnName extends string & keyof Row>(\n column: ColumnName,\n query: string,\n options?: { config?: string; type?: 'plain' | 'phrase' | 'websearch' }\n ): this\n textSearch(\n column: string,\n query: string,\n options?: { config?: string; type?: 'plain' | 'phrase' | 'websearch' }\n ): this\n /**\n * Only relevant for text and tsvector columns. Match only rows where\n * `column` matches the query string in `query`.\n *\n * @param column - The text or tsvector column to filter on\n * @param query - The query text to match with\n * @param options - Named parameters\n * @param options.config - The text search configuration to use\n * @param options.type - Change how the `query` text is interpreted\n */\n textSearch(\n column: string,\n query: string,\n { config, type }: { config?: string; type?: 'plain' | 'phrase' | 'websearch' } = {}\n ): this {\n let typePart = ''\n if (type === 'plain') {\n typePart = 'pl'\n } else if (type === 'phrase') {\n typePart = 'ph'\n } else if (type === 'websearch') {\n typePart = 'w'\n }\n const configPart = config === undefined ? '' : `(${config})`\n this.url.searchParams.append(column, `${typePart}fts${configPart}.${query}`)\n return this\n }\n\n match<ColumnName extends string & keyof Row>(query: Record<ColumnName, Row[ColumnName]>): this\n match(query: Record<string, unknown>): this\n /**\n * Match only rows where each column in `query` keys is equal to its\n * associated value. Shorthand for multiple `.eq()`s.\n *\n * @param query - The object to filter with, with column names as keys mapped\n * to their filter values\n */\n match(query: Record<string, unknown>): this {\n Object.entries(query).forEach(([column, value]) => {\n this.url.searchParams.append(column, `eq.${value}`)\n })\n return this\n }\n\n not<ColumnName extends string & keyof Row>(\n column: ColumnName,\n operator: FilterOperator,\n value: Row[ColumnName]\n ): this\n not(column: string, operator: string, value: unknown): this\n /**\n * Match only rows which doesn't satisfy the filter.\n *\n * Unlike most filters, `opearator` and `value` are used as-is and need to\n * follow [PostgREST\n * syntax](https://postgrest.org/en/stable/api.html#operators). You also need\n * to make sure they are properly sanitized.\n *\n * @param column - The column to filter on\n * @param operator - The operator to be negated to filter with, following\n * PostgREST syntax\n * @param value - The value to filter with, following PostgREST syntax\n */\n not(column: string, operator: string, value: unknown): this {\n this.url.searchParams.append(column, `not.${operator}.${value}`)\n return this\n }\n\n /**\n * Match only rows which satisfy at least one of the filters.\n *\n * Unlike most filters, `filters` is used as-is and needs to follow [PostgREST\n * syntax](https://postgrest.org/en/stable/api.html#operators). You also need\n * to make sure it's properly sanitized.\n *\n * It's currently not possible to do an `.or()` filter across multiple tables.\n *\n * @param filters - The filters to use, following PostgREST syntax\n * @param options - Named parameters\n * @param options.referencedTable - Set this to filter on referenced tables\n * instead of the parent table\n * @param options.foreignTable - Deprecated, use `referencedTable` instead\n */\n or(\n filters: string,\n {\n foreignTable,\n referencedTable = foreignTable,\n }: { foreignTable?: string; referencedTable?: string } = {}\n ): this {\n const key = referencedTable ? `${referencedTable}.or` : 'or'\n this.url.searchParams.append(key, `(${filters})`)\n return this\n }\n\n filter<ColumnName extends string & keyof Row>(\n column: ColumnName,\n operator: `${'' | 'not.'}${FilterOperator}`,\n value: unknown\n ): this\n filter(column: string, operator: string, value: unknown): this\n /**\n * Match only rows which satisfy the filter. This is an escape hatch - you\n * should use the specific filter methods wherever possible.\n *\n * Unlike most filters, `opearator` and `value` are used as-is and need to\n * follow [PostgREST\n * syntax](https://postgrest.org/en/stable/api.html#operators). You also need\n * to make sure they are properly sanitized.\n *\n * @param column - The column to filter on\n * @param operator - The operator to filter with, following PostgREST syntax\n * @param value - The value to filter with, following PostgREST syntax\n */\n filter(column: string, operator: string, value: unknown): this {\n this.url.searchParams.append(column, `${operator}.${value}`)\n return this\n }\n}\n","import PostgrestFilterBuilder from './PostgrestFilterBuilder'\nimport { GetResult } from './select-query-parser/result'\nimport {\n ClientServerOptions,\n Fetch,\n GenericSchema,\n GenericTable,\n GenericView,\n} from './types/common/common'\n\nexport default class PostgrestQueryBuilder<\n Clien