diff --git a/src/sync-plugins/supabase.ts b/src/sync-plugins/supabase.ts index 55cfaf20..1783cd22 100644 --- a/src/sync-plugins/supabase.ts +++ b/src/sync-plugins/supabase.ts @@ -18,7 +18,9 @@ import { syncedCrud, } from '@legendapp/state/sync-plugins/crud'; import type { PostgrestFilterBuilder, PostgrestQueryBuilder } from '@supabase/postgrest-js'; -import type { SupabaseClient } from '@supabase/supabase-js'; +import type { PostgrestSingleResponse, SupabaseClient } from '@supabase/supabase-js'; +import type { FunctionsResponse } from '@supabase/functions-js'; + // Unused types but maybe useful in the future so keeping them for now type DatabaseOf = Client extends SupabaseClient ? TDB : never; @@ -77,7 +79,7 @@ interface SyncedSupabaseProps< TRemote extends SupabaseRowOf = SupabaseRowOf, TLocal = TRemote, > extends SyncedSupabaseConfig, - SyncedCrudPropsMany { + Omit, 'list'> { supabase: Client; collection: Collection; schema?: SchemaName; @@ -95,6 +97,18 @@ interface SyncedSupabaseProps< actions?: ('create' | 'read' | 'update' | 'delete')[]; realtime?: boolean | { schema?: string; filter?: string }; stringifyDates?: boolean; + list?: ( + ...params: Parameters>['list']> + ) => PromiseLike> | Promise[]>>; + create?: ( + ...params: Parameters>['create']> + ) => PromiseLike> | Promise>>; + update?: ( + ...params: Parameters>['update']> + ) => PromiseLike> | Promise>>; + delete?: ( + ...params: Parameters>['delete']> + ) => PromiseLike> | Promise>>; } let channelNum = 1; @@ -112,6 +126,16 @@ export function configureSyncedSupabase(config: SyncedSupabaseConfiguration) { Object.assign(supabaseConfig, removeNullUndefined(rest)); } +function wrapSupabaseFn(fn: (...args: any) => PromiseLike) { + return async (...args: any) => { + const { data, error } = await fn(...args); + if (error) { + throw new Error(error.message); + } + return data; + }; +} + export function syncedSupabase< Client extends SupabaseClient, Collection extends SupabaseCollectionOf & string, @@ -141,6 +165,10 @@ export function syncedSupabase< waitForSet, generateId, mode, + list: listParam, + create: createParam, + update: updateParam, + delete: deleteParam, ...rest } = props; @@ -151,70 +179,77 @@ export function syncedSupabase< const list = !actions || actions.includes('read') - ? async (params: SyncedGetParams) => { - const { lastSync } = params; - const clientSchema = schema ? client.schema(schema as string) : client; - const from = clientSchema.from(collection); - let select = selectFn ? selectFn(from) : from.select(); + ? listParam + ? wrapSupabaseFn(listParam) + : async (params: SyncedGetParams) => { + const { lastSync } = params; + const clientSchema = schema ? client.schema(schema as string) : client; + const from = clientSchema.from(collection); + let select = selectFn ? selectFn(from) : from.select(); - // in last-sync mode, filter for rows updated more recently than the last sync - if (changesSince === 'last-sync' && lastSync) { - const date = new Date(lastSync).toISOString(); - select = select.gt(fieldUpdatedAt!, date); - } - // filter with filter parameter - if (filter) { - select = filter(select, params); + // in last-sync mode, filter for rows updated more recently than the last sync + if (changesSince === 'last-sync' && lastSync) { + const date = new Date(lastSync).toISOString(); + select = select.gt(fieldUpdatedAt!, date); + } + // filter with filter parameter + if (filter) { + select = filter(select, params); + } + const { data, error } = await select; + if (error) { + throw new Error(error?.message); + } + return (data! || []) as SupabaseRowOf[]; } - const { data, error } = await select; - if (error) { - throw new Error(error?.message); - } - return (data! || []) as SupabaseRowOf[]; - } : undefined; - const create = - !actions || actions.includes('create') - ? async (input: SupabaseRowOf) => { - const res = await client.from(collection).insert(input).select(); - const { data, error } = res; - if (data) { - const created = data[0]; - return created; - } else { - throw new Error(error?.message); - } - } - : undefined; + const create = createParam + ? wrapSupabaseFn(createParam) + : !actions || actions.includes('create') + ? async (input: SupabaseRowOf) => { + const res = await client.from(collection).insert(input).select(); + const { data, error } = res; + if (data) { + const created = data[0]; + return created; + } else { + throw new Error(error?.message); + } + } + : undefined; const update = !actions || actions.includes('update') - ? async (input: SupabaseRowOf) => { - const res = await client.from(collection).update(input).eq('id', input.id).select(); - const { data, error } = res; - if (data) { - const created = data[0]; - return created; - } else { - throw new Error(error?.message); + ? updateParam + ? wrapSupabaseFn(updateParam) + : async (input: SupabaseRowOf) => { + const res = await client.from(collection).update(input).eq('id', input.id).select(); + const { data, error } = res; + if (data) { + const created = data[0]; + return created; + } else { + throw new Error(error?.message); + } } - } : undefined; const deleteFn = !fieldDeleted && (!actions || actions.includes('delete')) - ? async (input: { id: SupabaseRowOf['id'] }) => { - const id = input.id; - const res = await client.from(collection).delete().eq('id', id).select(); - const { data, error } = res; - if (data) { - const created = data[0]; - return created; - } else { - throw new Error(error?.message); + ? deleteParam + ? wrapSupabaseFn(deleteParam) + : async (input: { id: SupabaseRowOf['id'] }) => { + const id = input.id; + const res = await client.from(collection).delete().eq('id', id).select(); + const { data, error } = res; + if (data) { + const created = data[0]; + return created; + } else { + throw new Error(error?.message); + } } - } : undefined; const subscribe = realtime