Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/supabase

src/list-objects.ts

98 lines · 3.0 KB · TypeScript
import { projectAuth } from './setup.ts'
import { asLive, projectRequest } from './client.ts'
import {
	clampInt,
	isRecord,
	optionalString,
	requireString,
	type SupabaseAuthInput,
} from './types.ts'

export type StorageObjectSummary = {
	name: string
	id?: string
	updatedAt?: string
	createdAt?: string
	size?: number
	mimeType?: string
}

/**
 * List objects in a Storage bucket. This Storage endpoint is POST, but it is a read.
 * @example
 * import listObjects from 'kody:@kody/supabase/list-objects'
 * const { items } = await listObjects({
 *   projectRef: 'abcdefghijklmnop',
 *   bucket: 'avatars',
 *   prefix: 'public/',
 * })
 */
export async function listObjects(
	input: SupabaseAuthInput & {
		bucket: string
		prefix?: string
		search?: string
		limit?: number
		offset?: number
	},
): Promise<{ items: Array<StorageObjectSummary>; count: number }> {
	const bucket = requireString(input.bucket, 'bucket')
	const live = asLive<{ data: unknown }>(
		await projectRequest({
			auth: projectAuth(input),
			method: 'POST',
			path: `/storage/v1/object/list/${encodeURIComponent(bucket)}`,
			body: {
				prefix: optionalString(input.prefix, 'prefix') ?? '',
				search: optionalString(input.search, 'search'),
				limit: clampInt(input.limit, 1, 1000, 100),
				offset: clampInt(input.offset, 0, 1_000_000, 0, 'offset'),
				sortBy: { column: 'name', order: 'asc' },
			},
		}),
	)
	const rows = Array.isArray(live.data) ? live.data : []
	const items = rows
		.map((row) => {
			if (!isRecord(row)) return null
			const name = optionalString(row.name, 'name')
			if (!name) return null
			const metadata = isRecord(row.metadata) ? row.metadata : undefined
			return {
				name,
				id: optionalString(row.id, 'id'),
				updatedAt: optionalString(row.updated_at, 'updated_at'),
				createdAt: optionalString(row.created_at, 'created_at'),
				size: typeof metadata?.size === 'number' ? metadata.size : undefined,
				mimeType: optionalString(metadata?.mimetype, 'mimetype'),
			}
		})
		.filter((item): item is StorageObjectSummary => item !== null)
	return { items, count: items.length }
}

/**
 * List objects in a Storage bucket.
 * @example
 * import listObjects from 'kody:@kody/supabase/list-objects'
 * const result = await listObjects({ projectRef: 'abcdefghijklmnop', bucket: 'avatars' })
 */
export default async function listObjectsEntrypoint(
	params: Partial<SupabaseAuthInput> & Record<string, unknown> = {},
) {
	return await listObjects({
		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'),
		prefix: optionalString(params.prefix, 'prefix'),
		search: optionalString(params.search, 'search'),
		limit: typeof params.limit === 'number' ? params.limit : undefined,
		offset: typeof params.offset === 'number' ? params.offset : undefined,
	})
}