@supabase-cache-helpers/postgrest-swr
Version:
A collection of SWR utilities for working with Supabase.
1 lines • 14.2 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.react-server.ts","../src/query/prefetch.ts","../src/lib/constants.ts","../src/lib/create-key-getter.ts","../src/lib/parse-order-by.ts","../src/lib/encode.ts","../src/lib/middleware.ts","../src/lib/use-postgrest-filter-cache.ts","../src/lib/use-queries-for-table-loader.ts"],"sourcesContent":["// Server only flag in case other RSC compatible exports are added here later\nimport 'server-only';\n\nexport * from './query/prefetch';\n","import {\n type AnyPostgrestResponse,\n PostgrestParser,\n isPostgrestBuilder,\n offsetPaginationFetcher,\n offsetPaginationHasMoreFetcher,\n} from '@supabase-cache-helpers/postgrest-core';\nimport type {\n PostgrestMaybeSingleResponse,\n PostgrestResponse,\n PostgrestSingleResponse,\n PostgrestTransformBuilder,\n} from '@supabase/postgrest-js';\nimport { GenericSchema } from '@supabase/postgrest-js/dist/cjs/types';\n\nimport { encode } from '../lib';\n\nfunction fetchQueryFallbackData<Result>(\n query: PromiseLike<PostgrestSingleResponse<Result>>,\n): Promise<[string, PostgrestSingleResponse<Result>]>;\n\nfunction fetchQueryFallbackData<Result>(\n query: PromiseLike<PostgrestMaybeSingleResponse<Result>>,\n): Promise<[string, PostgrestMaybeSingleResponse<Result>]>;\n\nfunction fetchQueryFallbackData<Result>(\n query: PromiseLike<PostgrestResponse<Result>>,\n): Promise<[string, PostgrestResponse<Result>]>;\n\nasync function fetchQueryFallbackData<Result>(\n query: PromiseLike<AnyPostgrestResponse<Result>>,\n): Promise<[string, AnyPostgrestResponse<Result>]> {\n if (!isPostgrestBuilder<Result>(query)) {\n throw new Error('Query is not a PostgrestBuilder');\n }\n\n return [\n encode(new PostgrestParser<Result>(query), false),\n await query.throwOnError(),\n ];\n}\n\nasync function fetchOffsetPaginationHasMoreFallbackData<\n Schema extends GenericSchema,\n Table extends Record<string, unknown>,\n Result extends Record<string, unknown>,\n RelationName = unknown,\n Relationships = unknown,\n>(\n query: PostgrestTransformBuilder<\n Schema,\n Table,\n Result[],\n RelationName,\n Relationships\n >,\n pageSize: number,\n): Promise<\n [\n string,\n [\n {\n data: Result[];\n hasMore: boolean;\n },\n ],\n ]\n> {\n return [\n encode(new PostgrestParser<Result[]>(query.range(0, pageSize)), true),\n [\n await offsetPaginationHasMoreFetcher(query, {\n offset: 0,\n limit: pageSize,\n pageSize,\n }),\n ],\n ];\n}\n\nconst fetchOffsetPaginationFallbackData = async <\n Schema extends GenericSchema,\n Table extends Record<string, unknown>,\n Result extends Record<string, unknown>,\n RelationName = unknown,\n Relationships = unknown,\n>(\n query: PostgrestTransformBuilder<\n Schema,\n Table,\n Result[],\n RelationName,\n Relationships\n >,\n pageSize: number,\n): Promise<[string, [Result[]]]> => {\n return [\n encode(new PostgrestParser<Result[]>(query.range(0, pageSize)), true),\n [\n await offsetPaginationFetcher(query, {\n offset: 0,\n limit: pageSize,\n }),\n ],\n ];\n};\n\nexport {\n fetchQueryFallbackData,\n fetchOffsetPaginationHasMoreFallbackData,\n fetchOffsetPaginationFallbackData,\n};\n","export const KEY_PREFIX = 'postgrest';\nexport const POSTGREST_FILTER_KEY_PREFIX = 'postgrest-filter';\nexport const KEY_SEPARATOR = '$';\nexport const INFINITE_PREFIX = '$inf$';\nexport const INFINITE_KEY_PREFIX = 'page';\n","import {\n type PostgrestHasMorePaginationResponse,\n type PostgrestPaginationResponse,\n get,\n isPlainObject,\n isPostgrestHasMorePaginationResponse,\n isPostgrestPaginationResponse,\n} from '@supabase-cache-helpers/postgrest-core';\nimport type { PostgrestTransformBuilder } from '@supabase/postgrest-js';\nimport { GenericSchema } from '@supabase/postgrest-js/dist/cjs/types';\nimport { parseOrderBy } from './parse-order-by';\n\nexport const createOffsetKeyGetter = <\n Schema extends GenericSchema,\n Table extends Record<string, unknown>,\n Result,\n>(\n queryFactory: (() => PostgrestTransformBuilder<Schema, Table, Result>) | null,\n {\n pageSize,\n rpcArgs,\n }: {\n pageSize: number;\n rpcArgs?: { limit: string; offset: string };\n },\n) => {\n if (!queryFactory) return () => null;\n return (\n pageIndex: number,\n previousPageData: (\n | PostgrestHasMorePaginationResponse<Result>\n | PostgrestPaginationResponse<Result>\n )[],\n ) => {\n if (\n previousPageData &&\n ((isPostgrestHasMorePaginationResponse(previousPageData) &&\n !previousPageData.data.length) ||\n (isPostgrestPaginationResponse(previousPageData) &&\n !previousPageData.length))\n ) {\n return null;\n }\n const cursor = pageIndex * pageSize;\n\n const query = queryFactory();\n\n if (rpcArgs) {\n if (query['method'] === 'GET') {\n query['url'].searchParams.set(rpcArgs.limit, String(pageSize));\n query['url'].searchParams.set(rpcArgs.offset, String(cursor));\n } else {\n query['body'] = {\n ...(isPlainObject(query['body']) ? query['body'] : {}),\n [rpcArgs.limit]: pageSize,\n [rpcArgs.offset]: cursor,\n };\n }\n\n return query;\n }\n\n return query.range(cursor, cursor + pageSize - 1);\n };\n};\n\nexport const createCursorKeyGetter = <\n Schema extends GenericSchema,\n Table extends Record<string, unknown>,\n Result,\n>(\n queryFactory: (() => PostgrestTransformBuilder<Schema, Table, Result>) | null,\n {\n orderBy,\n uqOrderBy: uqColumn,\n rpcArgs,\n }: {\n orderBy: string;\n uqOrderBy?: string;\n rpcArgs?: { orderBy: string; uqOrderBy?: string };\n },\n) => {\n if (!queryFactory) return () => null;\n\n return (\n _pageIndex: number,\n previousPageData: (\n | PostgrestHasMorePaginationResponse<Result>\n | PostgrestPaginationResponse<Result>\n )[],\n ) => {\n // Return null if we've reached the end of the data\n if (previousPageData && isEmptyPreviousPage(previousPageData)) {\n return null;\n }\n\n const query = queryFactory();\n\n // If this is the first page, return the original query\n if (!previousPageData) return query;\n\n // Extract the last values for cursor-based pagination\n const lastItem = getLastItem(previousPageData);\n\n if (!lastItem) return query;\n\n const lastValueOrderBy = get(lastItem, orderBy);\n if (!lastValueOrderBy) return query;\n\n const lastValueUqColumn = uqColumn ? get(lastItem, uqColumn) : null;\n\n if (rpcArgs) {\n if (query['method'] === 'GET') {\n if (lastValueOrderBy) {\n query['url'].searchParams.set(rpcArgs.orderBy, lastValueOrderBy);\n }\n if (lastValueUqColumn && rpcArgs.uqOrderBy) {\n query['url'].searchParams.set(rpcArgs.uqOrderBy, lastValueUqColumn);\n }\n } else {\n query['body'] = {\n ...(isPlainObject(query['body']) ? query['body'] : {}),\n [rpcArgs.orderBy]: lastValueOrderBy,\n ...(lastValueUqColumn && rpcArgs.uqOrderBy\n ? { [rpcArgs.uqOrderBy]: lastValueUqColumn }\n : {}),\n };\n }\n\n return query;\n }\n\n const { orderBy: mainOrderBy, uqOrderBy } = parseOrderBy(\n query['url'].searchParams,\n { orderByPath: orderBy, uqOrderByPath: uqColumn },\n );\n\n // Apply cursor filters\n if (uqColumn && uqOrderBy && lastValueOrderBy && lastValueUqColumn) {\n const operator = mainOrderBy.ascending ? 'gt' : 'lt';\n const uqOperator = uqOrderBy.ascending ? 'gt' : 'lt';\n\n query['url'].searchParams.append(\n 'or',\n `(${orderBy}.${operator}.${lastValueOrderBy},and(${orderBy}.eq.${lastValueOrderBy},${uqColumn}.${uqOperator}.${lastValueUqColumn}))`,\n );\n } else if (lastValueOrderBy) {\n const operator = mainOrderBy.ascending ? 'gt' : 'lt';\n query['url'].searchParams.append(\n orderBy,\n `${operator}.${lastValueOrderBy}`,\n );\n }\n\n return query;\n };\n};\n\n// Helper functions\nfunction isEmptyPreviousPage<Result>(\n previousPageData: (\n | PostgrestHasMorePaginationResponse<Result>\n | PostgrestPaginationResponse<Result>\n )[],\n): boolean {\n return (\n (isPostgrestHasMorePaginationResponse(previousPageData) &&\n !previousPageData.data.length) ||\n (isPostgrestPaginationResponse(previousPageData) &&\n !previousPageData.length)\n );\n}\n\nfunction getLastItem<Result>(\n data: (\n | PostgrestHasMorePaginationResponse<Result>\n | PostgrestPaginationResponse<Result>\n )[],\n): Result | null {\n if (isPostgrestHasMorePaginationResponse(data)) {\n return data.data.length\n ? (data.data[data.data.length - 1] as Result)\n : null;\n } else if (isPostgrestPaginationResponse(data)) {\n return data.length ? (data[data.length - 1] as Result) : null;\n }\n return null;\n}\n","import { parseOrderBy as core } from '@supabase-cache-helpers/postgrest-core';\n\nexport function parseOrderBy(\n searchParams: URLSearchParams,\n {\n orderByPath,\n uqOrderByPath,\n }: { orderByPath: string; uqOrderByPath?: string },\n) {\n const orderByDef = core(searchParams);\n const orderBy = orderByDef.find((o) => o.column === orderByPath);\n\n if (!orderBy) {\n throw new Error(`No ordering key found for path ${orderByPath}`);\n }\n\n const uqOrderBy = uqOrderByPath\n ? orderByDef.find((o) => o.column === uqOrderByPath)\n : null;\n\n return {\n orderBy,\n uqOrderBy,\n };\n}\n","import type { PostgrestParser } from '@supabase-cache-helpers/postgrest-core';\n\nimport { INFINITE_KEY_PREFIX, KEY_PREFIX, KEY_SEPARATOR } from './constants';\n\nexport const encode = <Result>(\n parser: PostgrestParser<Result>,\n isInfinite: boolean,\n) => {\n return [\n KEY_PREFIX,\n isInfinite ? INFINITE_KEY_PREFIX : 'null',\n parser.schema,\n parser.table,\n parser.queryKey,\n parser.bodyKey ?? 'null',\n `count=${parser.count}`,\n `head=${parser.isHead}`,\n parser.orderByKey,\n ].join(KEY_SEPARATOR);\n};\n","import {\n PostgrestParser,\n isPostgrestBuilder,\n} from '@supabase-cache-helpers/postgrest-core';\nimport type {\n SWRInfiniteConfiguration,\n SWRInfiniteFetcher,\n SWRInfiniteHook,\n SWRInfiniteKeyLoader,\n} from 'swr/infinite';\n\nimport { encode } from './encode';\n\nexport const infiniteMiddleware = <Result>(\n useSWRInfiniteNext: SWRInfiniteHook,\n) => {\n return (\n keyFn: SWRInfiniteKeyLoader,\n fetcher: SWRInfiniteFetcher,\n config: SWRInfiniteConfiguration,\n ) => {\n return useSWRInfiniteNext(\n (index, previousPageData) => {\n // todo use type guard\n const query = keyFn(index, previousPageData);\n if (!query) return null;\n if (!isPostgrestBuilder<Result>(query)) {\n throw new Error('Key is not a PostgrestBuilder');\n }\n\n return encode(new PostgrestParser<Result>(query), true);\n },\n typeof fetcher === 'function' ? (query) => fetcher(query) : fetcher,\n config,\n );\n };\n};\n","import {\n PostgrestFilter,\n type PostgrestQueryParserOptions,\n encodeObject,\n} from '@supabase-cache-helpers/postgrest-core';\nimport { useSWRConfig } from 'swr';\n\nimport { KEY_SEPARATOR, POSTGREST_FILTER_KEY_PREFIX } from './constants';\n\nexport const usePostgrestFilterCache = <\n R extends Record<string, unknown>,\n>() => {\n const { cache } = useSWRConfig();\n\n return (query: string, opts?: PostgrestQueryParserOptions) => {\n const key = [\n POSTGREST_FILTER_KEY_PREFIX,\n query,\n opts ? encodeObject(opts) : null,\n ]\n .filter(Boolean)\n .join(KEY_SEPARATOR);\n const cacheData = cache.get(key);\n if (cacheData && cacheData.data instanceof PostgrestFilter) {\n return cacheData.data;\n }\n const filter = PostgrestFilter.fromQuery(query, opts);\n cache.set(key, { data: filter });\n return filter as PostgrestFilter<R>;\n };\n};\n","import type { BuildNormalizedQueryOps } from '@supabase-cache-helpers/postgrest-core';\nimport { useSWRConfig } from 'swr';\n\nimport { decode } from './decode';\nimport { usePostgrestFilterCache } from './use-postgrest-filter-cache';\n\nexport const useQueriesForTableLoader = (table: string) => {\n const { cache } = useSWRConfig();\n const getPostgrestFilter = usePostgrestFilterCache();\n\n return () =>\n Array.from(cache.keys()).reduce<\n ReturnType<BuildNormalizedQueryOps['queriesForTable']>\n >((prev, curr) => {\n const decodedKey = decode(curr);\n if (decodedKey?.table === table) {\n prev.push(getPostgrestFilter(decodedKey.queryKey).params);\n }\n return prev;\n }, []);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,yBAAO;;;ACDP,IAAAA,yBAMO;;;ACNA,IAAM,aAAa;AAEnB,IAAM,gBAAgB;AAEtB,IAAM,sBAAsB;;;ACJnC,IAAAC,yBAOO;;;ACPP,4BAAqC;;;ACI9B,IAAM,SAAS,CACpB,QACA,eACG;AAPL;AAQE,SAAO;AAAA,IACL;AAAA,IACA,aAAa,sBAAsB;AAAA,IACnC,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,KACP,YAAO,YAAP,YAAkB;AAAA,IAClB,SAAS,OAAO,KAAK;AAAA,IACrB,QAAQ,OAAO,MAAM;AAAA,IACrB,OAAO;AAAA,EACT,EAAE,KAAK,aAAa;AACtB;;;ACnBA,IAAAC,yBAGO;;;ACHP,IAAAC,yBAIO;AACP,iBAA6B;;;ACJ7B,IAAAC,cAA6B;;;AP4B7B,SAAe,uBACb,OACiD;AAAA;AACjD,QAAI,KAAC,2CAA2B,KAAK,GAAG;AACtC,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AAEA,WAAO;AAAA,MACL,OAAO,IAAI,uCAAwB,KAAK,GAAG,KAAK;AAAA,MAChD,MAAM,MAAM,aAAa;AAAA,IAC3B;AAAA,EACF;AAAA;AAEA,SAAe,yCAOb,OAOA,UAWA;AAAA;AACA,WAAO;AAAA,MACL,OAAO,IAAI,uCAA0B,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,IAAI;AAAA,MACpE;AAAA,QACE,UAAM,uDAA+B,OAAO;AAAA,UAC1C,QAAQ;AAAA,UACR,OAAO;AAAA,UACP;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAEA,IAAM,oCAAoC,CAOxC,OAOA,aACkC;AAClC,SAAO;AAAA,IACL,OAAO,IAAI,uCAA0B,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,IAAI;AAAA,IACpE;AAAA,MACE,UAAM,gDAAwB,OAAO;AAAA,QACnC,QAAQ;AAAA,QACR,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":["import_postgrest_core","import_postgrest_core","import_postgrest_core","import_postgrest_core","import_swr"]}