supastash
Version:
Offline-first sync engine for Supabase in React Native using SQLite
171 lines (150 loc) • 4.29 kB
TypeScript
import { PostgrestError, PostgrestSingleResponse } from "@supabase/supabase-js";
type SupabaseResult<T> = PostgrestSingleResponse<T>;
export type PayloadData = any;
export type SupabaseQueryReturn<U extends boolean, Z> = U extends true
? PostgrestSingleResponse<Z>
: PostgrestSingleResponse<Z[]>;
type FilterOperations =
| "="
| "!="
| ">"
| "<"
| ">="
| "<="
| "LIKE"
| "IS"
| "IN";
export type CrudMethods =
| "select"
| "insert"
| "update"
| "delete"
| "upsert"
| "none";
export type FilterCalls = {
column: string;
operator: FilterOperations;
value: any;
};
export interface SupastashQuery<
T extends CrudMethods,
U extends boolean,
R = PayloadData
> {
id: string;
table: string;
method: T;
payload: R | R[] | null;
filters: FilterCalls[] | null;
limit: number | null;
select: string | null;
isSingle: U;
type: SyncMode;
runSelected: boolean;
viewRemoteResult: boolean;
onConflictKeys?: string[];
preserveTimestamp: boolean;
}
export interface CrudReturnValue {
error;
}
export type SupastashResult<R = PayloadData> = {
data: R | null;
error: {
message: string;
name: string;
supabaseError?: PostgrestError;
} | null;
};
export type PayloadResult<R = PayloadData> = SupastashResult<R>;
export type PayloadListResult<R = PayloadData> = SupastashResult<R[]>;
export type SupatashDeleteResult<R = PayloadData> = {
data?: R[] | null;
error: {
message: string;
name: string;
supabaseError?: PostgrestError;
} | null;
};
export type MethodReturnTypeMap<U extends boolean, R = PayloadData> = {
select: U extends true ? SupastashResult<R> : SupastashResult<R[]>;
insert: U extends true ? SupastashResult<R> : SupastashResult<R[]>;
update: U extends true ? SupastashResult<R> : SupastashResult<R[]>;
delete: SupatashDeleteResult<R>;
upsert: U extends true ? SupastashResult<R> : SupastashResult<R[]>;
none: null;
};
export type HandlerMap<U extends boolean, R = PayloadData> = {
[K in CrudMethods]: () => Promise<MethodReturnTypeMap<U, R>[K]>;
};
export type SyncMode =
| "localFirst"
| "remoteFirst"
| "localOnly"
| "remoteOnly";
export type SupastashQueryResult<
T extends CrudMethods,
U extends boolean,
V extends boolean,
R = PayloadData
> = V extends true
? Promise<{
remote: SupabaseQueryReturn<U, R> | null;
local: MethodReturnTypeMap<U, R>[T] | null;
success: boolean;
}>
: MethodReturnTypeMap<U, R>[T] extends { data: infer D; error: infer E }
? Promise<{ data: D; error: E; success: boolean }>
: Promise<{
data: null;
error: { message: string } | null;
success: boolean;
}>;
export type QueryBuilder<
T extends CrudMethods,
U extends boolean,
R,
Z = any
> = {
eq(column: string, value: any): QueryBuilder<T, U, R, Z>;
neq(column: string, value: any): QueryBuilder<T, U, R, Z>;
gt(column: string, value: any): QueryBuilder<T, U, R, Z>;
lt(column: string, value: any): QueryBuilder<T, U, R, Z>;
gte(column: string, value: any): QueryBuilder<T, U, R, Z>;
lte(column: string, value: any): QueryBuilder<T, U, R, Z>;
like(column: string, value: any): QueryBuilder<T, U, R, Z>;
is(column: string, value: any): QueryBuilder<T, U, R, Z>;
in(column: string, value: any[]): QueryBuilder<T, U, R, Z>;
limit(n: number): QueryBuilder<T, U, R, Z>;
syncMode(mode: SyncMode): QueryBuilder<T, U, R, Z>;
// Transitions to U = true when called
single(): QueryBuilder<T, true, R, Z>;
// Executes the query
execute<V extends boolean = false>(
options?: ExecuteOptions & { viewRemoteResult?: V }
): SupastashQueryResult<T, U, V, Z>;
run<V extends boolean = false>(
options?: ExecuteOptions & { viewRemoteResult?: V }
): SupastashQueryResult<T, U, V, Z>;
go<V extends boolean = false>(
options?: ExecuteOptions & { viewRemoteResult?: V }
): SupastashQueryResult<T, U, V, Z>;
};
export interface ExecuteOptions {
/**
* The number of times to retry the remote query if it fails
*/
remoteRetry?: number;
/**
* Whether to view the remote result
*/
viewRemoteResult?: boolean;
/**
* The delay between remote retries
*/
remoteRetryDelay?: number;
/**
* Whether to enable debug logging
*/
debug?: boolean;
}