import { projectAuth } from './setup.ts' import { assertMutationAllowed, readMutationFlags } from './safety.ts' import { asLive, projectRequest } from './client.ts' import { optionalBoolean, optionalString, requireString, type DryRunResult, type SupabaseAuthInput, } from './types.ts' /** * Upload an object to Storage. Requires `confirm: true`, or use `dryRun: true`. * Pass `content` for UTF-8 text or `contentBase64` for binary. * @example * import uploadObject from 'kody:@kody/supabase/upload-object' * const preview = await uploadObject({ * projectRef: 'abcdefghijklmnop', * bucket: 'docs', * path: 'notes/hello.txt', * content: 'hello', * dryRun: true, * }) */ export async function uploadObject( input: SupabaseAuthInput & { bucket: string path: string content?: string contentBase64?: string contentType?: string upsert?: boolean dryRun?: boolean confirm?: boolean }, ) { const bucket = requireString(input.bucket, 'bucket') const objectPath = requireString(input.path, 'path').replace(/^\/+/, '') const allowed = assertMutationAllowed(readMutationFlags(input), 'upload-object') const apiPath = `/storage/v1/object/${encodeURIComponent(bucket)}/${objectPath .split('/') .map((part) => encodeURIComponent(part)) .join('/')}` if (allowed.dryRun) { const preview: DryRunResult = { dryRun: true, method: 'POST', path: apiPath, headers: { 'Content-Type': input.contentType ?? 'application/octet-stream', 'x-upsert': input.upsert ? 'true' : 'false', }, body: { bytes: input.contentBase64 ? Math.ceil((input.contentBase64.length * 3) / 4) : (input.content ?? '').length, }, } return preview } if (input.content === undefined && input.contentBase64 === undefined) { throw new Error('upload-object requires content or contentBase64.') } const rawBody = input.contentBase64 !== undefined ? Uint8Array.from(atob(input.contentBase64), (char) => char.charCodeAt(0)) : input.content const live = asLive<{ data: unknown }>( await projectRequest({ auth: projectAuth(input), method: 'POST', path: apiPath, headers: { 'Content-Type': input.contentType ?? 'application/octet-stream', 'x-upsert': input.upsert ? 'true' : 'false', }, rawBody, }), ) return { uploaded: true, bucket, path: objectPath, result: live.data } } /** * Upload an object to Storage. Requires `confirm: true`, or use `dryRun: true`. * @example * import uploadObject from 'kody:@kody/supabase/upload-object' * const preview = await uploadObject({ projectRef: 'abcdefghijklmnop', bucket: 'docs', path: 'notes/hello.txt', content: 'hello', dryRun: true }) */ export default async function uploadObjectEntrypoint( params: Partial & Record = {}, ) { return await uploadObject({ account: optionalString(params.account, 'account'), secretName: optionalString(params.secretName, 'secretName'), serviceRoleSecretName: optionalString( params.serviceRoleSecretName, 'serviceRoleSecretName', ), projectRef: optionalString(params.projectRef, 'projectRef'), projectUrl: optionalString(params.projectUrl, 'projectUrl'), bucket: requireString(params.bucket, 'bucket'), path: requireString(params.path, 'path'), content: optionalString(params.content, 'content'), contentBase64: optionalString(params.contentBase64, 'contentBase64'), contentType: optionalString(params.contentType, 'contentType'), upsert: optionalBoolean(params.upsert, 'upsert'), dryRun: optionalBoolean(params.dryRun, 'dryRun'), confirm: optionalBoolean(params.confirm, 'confirm'), }) }