import { createClient, SupabaseClient } from "@supabase/[email protected]"
/**
* @param on_conflict Comma-separated UNIQUE column(s) to specify how duplicate
* rows are determined. Two rows are duplicates if all the onConflict columns are equal.
*
* @param ignore_duplicates If true, duplicate rows are ignored. If false, duplicate
* rows are merged with existing rows.
*
* @param token Supabase `access_token` and `refresh_token`. `expires_at` (optional) is a UNIX
* timestamp in seconds.
*
* @param count Count algorithm to use to count rows in the table or view.
* `"exact"`: Exact but slow count algorithm. Performs a `COUNT(*)` under the hood.
* `"planned"`: Approximated but fast count algorithm. Uses the Postgres statistics under the hood.
* `"estimated"`: Uses exact count for low numbers and planned count for high numbers.
*/
type Supabase = {
url: string;
key: string;
};
export async function main(
auth: Supabase,
table: string,
values: any,
on_conflict?: string,
ignore_duplicates: boolean = true,
return_updated: boolean = false,
token?: {
access: string;
refresh: string;
expires_at?: number;
},
count?: "exact" | "planned" | "estimated",
) {
return await refreshAndRetryIfExpired(auth, token, async (client) => {
let query: any = client
.from(table)
.upsert(
values,
removeObjectEmptyFields({
onConflict: on_conflict,
ignoreDuplicates: ignore_duplicates,
count,
}),
);
if (return_updated) {
query = query.select();
}
return query;
});
}
function removeObjectEmptyFields(
object?: Record<string, any>,
removeEmptyArraysAndObjects = true,
createNewObject = true,
) {
if (!object || typeof object !== "object") return {}
const obj = createNewObject ? { ...object } : object
const emptyValues = [undefined, null, ""]
for (const key in obj) {
const value = obj[key]
if (emptyValues.includes(value)) {
delete obj[key]
} else if (typeof value === "object") {
if (Object.keys(value).length) {
obj[key] = removeObjectEmptyFields(value, removeEmptyArraysAndObjects, false)
}
if (!Object.keys(value).length && removeEmptyArraysAndObjects) {
delete obj[key]
}
}
}
return obj
}
async function refreshAndRetryIfExpired(
auth: { url: string; key: string },
token: { access: string; refresh: string; expires_at?: number } | undefined,
fn: (client: SupabaseClient) => Promise<{ data: any; error?: any }>,
): Promise<{ data: any; error?: any; token?: { access: string; refresh: string; expires_at?: number } }> {
const makeClient = async (autoRefreshToken: boolean) => {
const client = createClient(auth.url, auth.key, {
auth: { autoRefreshToken, persistSession: false },
})
if (token) {
await client.auth.setSession({ access_token: token.access, refresh_token: token.refresh })
}
return client
}
try {
let result = await fn(await makeClient(false))
if (result?.error?.code === "PGRST301" && token) {
const client = await makeClient(true)
result = await fn(client)
const { data } = await client.auth.getSession()
if (data?.session) {
token = {
access: data.session.access_token,
refresh: data.session.refresh_token,
expires_at: data.session.expires_at,
}
}
}
return { ...result, token }
} catch (error) {
return { data: null, error }
}
}
Submitted by hugo989 7 days ago