Download node_modules/@supabase/storage-js/src/packages/StorageFileApi.ts from metarm/QMetaRam: direct link, hf CLI and curl.
- Browser
- Download file 48.1 kB
-
https://huggingface.co/metarm/QMetaRam/resolve/main/node_modules/%40supabase/storage-js/src/packages/StorageFileApi.ts
- Command line
-
hf download hf://metarm/QMetaRam/node_modules/@supabase/storage-js/src/packages/StorageFileApi.ts
-
curl -L -o StorageFileApi.ts https://huggingface.co/metarm/QMetaRam/resolve/main/node_modules/%40supabase/storage-js/src/packages/StorageFileApi.ts
48.1 kB
| import { | |
| StorageApiError, | |
| StorageError, | |
| StorageUnknownError, | |
| isStorageError, | |
| } from '../lib/common/errors' | |
| import { get, head, post, put, remove, Fetch } from '../lib/common/fetch' | |
| import { setHeader } from '../lib/common/headers' | |
| import { encodeStoragePath, recursiveToCamel } from '../lib/common/helpers' | |
| import BaseApiClient from '../lib/common/BaseApiClient' | |
| import { | |
| FileObject, | |
| FileOptions, | |
| SearchOptions, | |
| FetchParameters, | |
| TransformOptions, | |
| DestinationOptions, | |
| FileObjectV2, | |
| Camelize, | |
| SearchV2Options, | |
| SearchV2Result, | |
| PurgeCacheOptions, | |
| DeleteObjectEntry, | |
| } from '../lib/types' | |
| import BlobDownloadBuilder from './BlobDownloadBuilder' | |
| const DEFAULT_SEARCH_OPTIONS = { | |
| limit: 100, | |
| offset: 0, | |
| sortBy: { | |
| column: 'name', | |
| order: 'asc', | |
| }, | |
| } | |
| const DEFAULT_FILE_OPTIONS: FileOptions = { | |
| cacheControl: '3600', | |
| contentType: 'text/plain;charset=UTF-8', | |
| upsert: false, | |
| } | |
| type FileBody = | |
| | ArrayBuffer | |
| | ArrayBufferView | |
| | Blob | |
| | Buffer | |
| | File | |
| | FormData | |
| | NodeJS.ReadableStream | |
| | ReadableStream<Uint8Array> | |
| | URLSearchParams | |
| | string | |
| export default class StorageFileApi extends BaseApiClient<StorageError> { | |
| protected bucketId: string | undefined | |
| constructor( | |
| url: string, | |
| headers: { [key: string]: string } = {}, | |
| bucketId?: string, | |
| fetch?: Fetch | |
| ) { | |
| super(url, headers, fetch, 'storage') | |
| this.bucketId = bucketId | |
| } | |
| /** | |
| * Uploads a file to an existing bucket or replaces an existing file at the specified path with a new one. | |
| * | |
| * @param method HTTP method. | |
| * @param path The relative file path. Should be of the format `folder/subfolder/filename.png`. The bucket must already exist before attempting to upload. | |
| * @param fileBody The body of the file to be stored in the bucket. | |
| */ | |
| private async uploadOrUpdate( | |
| method: 'POST' | 'PUT', | |
| path: string, | |
| fileBody: FileBody, | |
| fileOptions?: FileOptions | |
| ): Promise< | |
| | { | |
| data: { id: string; path: string; fullPath: string } | |
| error: null | |
| } | |
| | { | |
| data: null | |
| error: StorageError | |
| } | |
| > { | |
| return this.handleOperation(async () => { | |
| let body | |
| const options = { ...DEFAULT_FILE_OPTIONS, ...fileOptions } | |
| let headers: Record<string, string> = { | |
| ...this.headers, | |
| ...(method === 'POST' && { 'x-upsert': String(options.upsert as boolean) }), | |
| } | |
| const metadata = options.metadata | |
| if (typeof Blob !== 'undefined' && fileBody instanceof Blob) { | |
| body = new FormData() | |
| body.append('cacheControl', options.cacheControl as string) | |
| if (metadata) { | |
| body.append('metadata', this.encodeMetadata(metadata)) | |
| } | |
| body.append('', fileBody) | |
| } else if (typeof FormData !== 'undefined' && fileBody instanceof FormData) { | |
| body = fileBody | |
| // Only append if not already present | |
| if (!body.has('cacheControl')) { | |
| body.append('cacheControl', options.cacheControl as string) | |
| } | |
| if (metadata && !body.has('metadata')) { | |
| body.append('metadata', this.encodeMetadata(metadata)) | |
| } | |
| } else { | |
| body = fileBody | |
| headers['cache-control'] = `max-age=${options.cacheControl}` | |
| headers['content-type'] = options.contentType as string | |
| if (metadata) { | |
| headers['x-metadata'] = this.toBase64(this.encodeMetadata(metadata)) | |
| } | |
| // Node.js streams require duplex option for fetch in Node 20+ | |
| // Check for both web ReadableStream and Node.js streams | |
| const isStream = | |
| (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) || | |
| (body && typeof body === 'object' && 'pipe' in body && typeof body.pipe === 'function') | |
| if (isStream && !options.duplex) { | |
| options.duplex = 'half' | |
| } | |
| } | |
| if (fileOptions?.headers) { | |
| for (const [key, value] of Object.entries(fileOptions.headers)) { | |
| headers = setHeader(headers, key, value) | |
| } | |
| } | |
| const cleanPath = this._removeEmptyFolders(path) | |
| const _path = this._getFinalPath(cleanPath) | |
| const data = await (method == 'PUT' ? put : post)( | |
| this.fetch, | |
| `${this.url}/object/${_path}`, | |
| body as object, | |
| { headers, ...(options?.duplex ? { duplex: options.duplex } : {}) } | |
| ) | |
| return { path: cleanPath, id: data.Id, fullPath: data.Key } | |
| }) | |
| } | |
| /** | |
| * Uploads a file to an existing bucket. | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param path The file path, including the file name. Should be of the format `folder/subfolder/filename.png`. The bucket must already exist before attempting to upload. | |
| * @param fileBody The body of the file to be stored in the bucket. | |
| * @param fileOptions Optional file upload options including cacheControl, contentType, upsert, and metadata. | |
| * @returns Promise with response containing file path, id, and fullPath or error | |
| * | |
| * @example Upload file | |
| * ```js | |
| * const avatarFile = event.target.files[0] | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .upload('public/avatar1.png', avatarFile, { | |
| * cacheControl: '3600', | |
| * upsert: false | |
| * }) | |
| * ``` | |
| * | |
| * Response: | |
| * ```json | |
| * { | |
| * "data": { | |
| * "path": "public/avatar1.png", | |
| * "fullPath": "avatars/public/avatar1.png" | |
| * }, | |
| * "error": null | |
| * } | |
| * ``` | |
| * | |
| * @example Upload file using `ArrayBuffer` from base64 file data | |
| * ```js | |
| * import { decode } from 'base64-arraybuffer' | |
| * | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .upload('public/avatar1.png', decode('base64FileData'), { | |
| * contentType: 'image/png' | |
| * }) | |
| * ``` | |
| * | |
| * @example Handling errors | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .upload('public/avatar1.png', avatarFile) | |
| * | |
| * if (error) { | |
| * // Log the full error so fields like `statusCode` and `error` (the | |
| * // Storage error name, e.g. "Duplicate") aren't hidden behind `error.message`. | |
| * console.error(error) | |
| * return | |
| * } | |
| * ``` | |
| * | |
| * @remarks | |
| * - RLS policy permissions required: | |
| * - `buckets` table permissions: none | |
| * - `objects` table permissions: only `insert` when you are uploading new files and `select`, `insert` and `update` when you are upserting files | |
| * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works | |
| * - For React Native, using either `Blob`, `File` or `FormData` does not work as intended. Upload file using `ArrayBuffer` from base64 file data instead, see example below. | |
| */ | |
| async upload( | |
| path: string, | |
| fileBody: FileBody, | |
| fileOptions?: FileOptions | |
| ): Promise< | |
| | { | |
| data: { id: string; path: string; fullPath: string } | |
| error: null | |
| } | |
| | { | |
| data: null | |
| error: StorageError | |
| } | |
| > { | |
| return this.uploadOrUpdate('POST', path, fileBody, fileOptions) | |
| } | |
| /** | |
| * Upload a file with a token generated from `createSignedUploadUrl`. | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param path The file path, including the file name. Should be of the format `folder/subfolder/filename.png`. The bucket must already exist before attempting to upload. | |
| * @param token The token generated from `createSignedUploadUrl` | |
| * @param fileBody The body of the file to be stored in the bucket. | |
| * @param fileOptions HTTP headers (cacheControl, contentType, etc.). | |
| * **Note:** The `upsert` option has no effect here. To enable upsert behavior, | |
| * pass `{ upsert: true }` when calling `createSignedUploadUrl()` instead. | |
| * @returns Promise with response containing file path and fullPath or error | |
| * | |
| * @example Upload to a signed URL | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .uploadToSignedUrl('folder/cat.jpg', 'token-from-createSignedUploadUrl', file) | |
| * ``` | |
| * | |
| * Response: | |
| * ```json | |
| * { | |
| * "data": { | |
| * "path": "folder/cat.jpg", | |
| * "fullPath": "avatars/folder/cat.jpg" | |
| * }, | |
| * "error": null | |
| * } | |
| * ``` | |
| * | |
| * @remarks | |
| * - RLS policy permissions required: | |
| * - `buckets` table permissions: none | |
| * - `objects` table permissions: none | |
| * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works | |
| */ | |
| async uploadToSignedUrl( | |
| path: string, | |
| token: string, | |
| fileBody: FileBody, | |
| fileOptions?: FileOptions | |
| ) { | |
| const cleanPath = this._removeEmptyFolders(path) | |
| const _path = this._getFinalPath(cleanPath) | |
| const url = new URL(this.url + `/object/upload/sign/${_path}`) | |
| url.searchParams.set('token', token) | |
| return this.handleOperation(async () => { | |
| let body | |
| const options = { ...DEFAULT_FILE_OPTIONS, ...fileOptions } | |
| let headers: Record<string, string> = { | |
| ...this.headers, | |
| ...{ 'x-upsert': String(options.upsert as boolean) }, | |
| } | |
| const metadata = options.metadata | |
| if (typeof Blob !== 'undefined' && fileBody instanceof Blob) { | |
| body = new FormData() | |
| body.append('cacheControl', options.cacheControl as string) | |
| if (metadata) { | |
| body.append('metadata', this.encodeMetadata(metadata)) | |
| } | |
| body.append('', fileBody) | |
| } else if (typeof FormData !== 'undefined' && fileBody instanceof FormData) { | |
| body = fileBody | |
| if (!body.has('cacheControl')) { | |
| body.append('cacheControl', options.cacheControl as string) | |
| } | |
| if (metadata && !body.has('metadata')) { | |
| body.append('metadata', this.encodeMetadata(metadata)) | |
| } | |
| } else { | |
| body = fileBody | |
| headers['cache-control'] = `max-age=${options.cacheControl}` | |
| headers['content-type'] = options.contentType as string | |
| if (metadata) { | |
| headers['x-metadata'] = this.toBase64(this.encodeMetadata(metadata)) | |
| } | |
| const isStream = | |
| (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) || | |
| (body && typeof body === 'object' && 'pipe' in body && typeof body.pipe === 'function') | |
| if (isStream && !options.duplex) { | |
| options.duplex = 'half' | |
| } | |
| } | |
| if (fileOptions?.headers) { | |
| for (const [key, value] of Object.entries(fileOptions.headers)) { | |
| headers = setHeader(headers, key, value) | |
| } | |
| } | |
| const data = await put(this.fetch, url.toString(), body as object, { | |
| headers, | |
| ...(options?.duplex ? { duplex: options.duplex } : {}), | |
| }) | |
| return { path: cleanPath, fullPath: data.Key } | |
| }) | |
| } | |
| /** | |
| * Creates a signed upload URL. | |
| * Signed upload URLs can be used to upload files to the bucket without further authentication. | |
| * They are valid for 2 hours. | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param path The file path, including the current file name. For example `folder/image.png`. | |
| * @param options.upsert If set to true, allows the file to be overwritten if it already exists. | |
| * @returns Promise with response containing signed upload URL, token, and path or error | |
| * | |
| * @example Create Signed Upload URL | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .createSignedUploadUrl('folder/cat.jpg') | |
| * ``` | |
| * | |
| * Response: | |
| * ```json | |
| * { | |
| * "data": { | |
| * "signedUrl": "https://example.supabase.co/storage/v1/object/upload/sign/avatars/folder/cat.jpg?token=<TOKEN>", | |
| * "path": "folder/cat.jpg", | |
| * "token": "<TOKEN>" | |
| * }, | |
| * "error": null | |
| * } | |
| * ``` | |
| * | |
| * @remarks | |
| * - RLS policy permissions required: | |
| * - `buckets` table permissions: none | |
| * - `objects` table permissions: `insert` | |
| * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works | |
| */ | |
| async createSignedUploadUrl( | |
| path: string, | |
| options?: { upsert: boolean } | |
| ): Promise< | |
| | { | |
| data: { signedUrl: string; token: string; path: string } | |
| error: null | |
| } | |
| | { | |
| data: null | |
| error: StorageError | |
| } | |
| > { | |
| return this.handleOperation(async () => { | |
| let _path = this._getFinalPath(path) | |
| const headers = { ...this.headers } | |
| if (options?.upsert) { | |
| headers['x-upsert'] = 'true' | |
| } | |
| const data = await post( | |
| this.fetch, | |
| `${this.url}/object/upload/sign/${_path}`, | |
| {}, | |
| { headers } | |
| ) | |
| const url = new URL(this.url + data.url) | |
| const token = url.searchParams.get('token') | |
| if (!token) { | |
| throw new StorageError('No token returned by API') | |
| } | |
| return { signedUrl: url.toString(), path, token } | |
| }) | |
| } | |
| /** | |
| * Replaces an existing file at the specified path with a new one. | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param path The relative file path. Should be of the format `folder/subfolder/filename.png`. The bucket must already exist before attempting to update. | |
| * @param fileBody The body of the file to be stored in the bucket. | |
| * @param fileOptions Optional file upload options including cacheControl, contentType, and metadata. | |
| * **Note:** The `upsert` option has no effect here. `update()` always replaces the | |
| * file at the given path, so the `x-upsert` header is not sent. To control upsert | |
| * behavior, use `upload()` instead. | |
| * @returns Promise with response containing file path, id, and fullPath or error | |
| * | |
| * @example Update file | |
| * ```js | |
| * const avatarFile = event.target.files[0] | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .update('public/avatar1.png', avatarFile, { | |
| * cacheControl: '3600' | |
| * }) | |
| * ``` | |
| * | |
| * Response: | |
| * ```json | |
| * { | |
| * "data": { | |
| * "path": "public/avatar1.png", | |
| * "fullPath": "avatars/public/avatar1.png" | |
| * }, | |
| * "error": null | |
| * } | |
| * ``` | |
| * | |
| * @example Update file using `ArrayBuffer` from base64 file data | |
| * ```js | |
| * import {decode} from 'base64-arraybuffer' | |
| * | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .update('public/avatar1.png', decode('base64FileData'), { | |
| * contentType: 'image/png' | |
| * }) | |
| * ``` | |
| * | |
| * @remarks | |
| * - RLS policy permissions required: | |
| * - `buckets` table permissions: none | |
| * - `objects` table permissions: `update` and `select` | |
| * - `update()` always replaces the file at the given path regardless of the `upsert` option. | |
| * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works | |
| * - For React Native, using either `Blob`, `File` or `FormData` does not work as intended. Update file using `ArrayBuffer` from base64 file data instead, see example below. | |
| */ | |
| async update( | |
| path: string, | |
| fileBody: | |
| | ArrayBuffer | |
| | ArrayBufferView | |
| | Blob | |
| | Buffer | |
| | File | |
| | FormData | |
| | NodeJS.ReadableStream | |
| | ReadableStream<Uint8Array> | |
| | URLSearchParams | |
| | string, | |
| fileOptions?: FileOptions | |
| ): Promise< | |
| | { | |
| data: { id: string; path: string; fullPath: string } | |
| error: null | |
| } | |
| | { | |
| data: null | |
| error: StorageError | |
| } | |
| > { | |
| return this.uploadOrUpdate('PUT', path, fileBody, fileOptions) | |
| } | |
| /** | |
| * Moves an existing file to a new path in the same bucket. | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param fromPath The original file path, including the current file name. For example `folder/image.png`. | |
| * @param toPath The new file path, including the new file name. For example `folder/image-new.png`. | |
| * @param options The destination options. | |
| * @param options.sourceVersionId The version id of the source object to move. | |
| * @returns Promise with response containing success message or error | |
| * | |
| * @example Move file | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .move('public/avatar1.png', 'private/avatar2.png') | |
| * ``` | |
| * | |
| * Response: | |
| * ```json | |
| * { | |
| * "data": { | |
| * "message": "Successfully moved" | |
| * }, | |
| * "error": null | |
| * } | |
| * ``` | |
| * | |
| * @remarks | |
| * - RLS policy permissions required: | |
| * - `buckets` table permissions: none | |
| * - `objects` table permissions: `update` and `select` | |
| * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works | |
| */ | |
| async move( | |
| fromPath: string, | |
| toPath: string, | |
| options?: DestinationOptions | |
| ): Promise< | |
| | { | |
| data: { message: string } | |
| error: null | |
| } | |
| | { | |
| data: null | |
| error: StorageError | |
| } | |
| > { | |
| return this.handleOperation(async () => { | |
| return await post( | |
| this.fetch, | |
| `${this.url}/object/move`, | |
| { | |
| bucketId: this.bucketId, | |
| sourceKey: fromPath, | |
| destinationKey: toPath, | |
| destinationBucket: options?.destinationBucket, | |
| sourceVersionId: options?.sourceVersionId, | |
| }, | |
| { headers: this.headers } | |
| ) | |
| }) | |
| } | |
| /** | |
| * Copies an existing file to a new path in the same bucket. | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param fromPath The original file path, including the current file name. For example `folder/image.png`. | |
| * @param toPath The new file path, including the new file name. For example `folder/image-copy.png`. | |
| * @param options The destination options. | |
| * @param options.sourceVersionId The version id of the source object to copy. | |
| * @returns Promise with response containing copied file path or error | |
| * | |
| * @example Copy file | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .copy('public/avatar1.png', 'private/avatar2.png') | |
| * ``` | |
| * | |
| * Response: | |
| * ```json | |
| * { | |
| * "data": { | |
| * "path": "avatars/private/avatar2.png" | |
| * }, | |
| * "error": null | |
| * } | |
| * ``` | |
| * | |
| * @remarks | |
| * - RLS policy permissions required: | |
| * - `buckets` table permissions: none | |
| * - `objects` table permissions: `insert` and `select` | |
| * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works | |
| */ | |
| async copy( | |
| fromPath: string, | |
| toPath: string, | |
| options?: DestinationOptions | |
| ): Promise< | |
| | { | |
| data: { path: string } | |
| error: null | |
| } | |
| | { | |
| data: null | |
| error: StorageError | |
| } | |
| > { | |
| return this.handleOperation(async () => { | |
| const data = await post( | |
| this.fetch, | |
| `${this.url}/object/copy`, | |
| { | |
| bucketId: this.bucketId, | |
| sourceKey: fromPath, | |
| destinationKey: toPath, | |
| destinationBucket: options?.destinationBucket, | |
| sourceVersionId: options?.sourceVersionId, | |
| }, | |
| { headers: this.headers } | |
| ) | |
| return { path: data.Key } | |
| }) | |
| } | |
| /** | |
| * Creates a signed URL. Use a signed URL to share a file for a fixed amount of time. | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param path The file path, including the current file name. For example `folder/image.png`. | |
| * @param expiresIn The number of seconds until the signed URL expires. For example, `60` for a URL which is valid for one minute. | |
| * @param options.download triggers the file as a download if set to true. Set this parameter as the name of the file if you want to trigger the download with a different filename. | |
| * @param options.transform Transform the asset before serving it to the client. | |
| * @param options.cacheNonce Append a cache nonce parameter to the URL to invalidate the cache. | |
| * @returns Promise with response containing signed URL or error | |
| * | |
| * @example Create Signed URL | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .createSignedUrl('folder/avatar1.png', 60) | |
| * ``` | |
| * | |
| * Response: | |
| * ```json | |
| * { | |
| * "data": { | |
| * "signedUrl": "https://example.supabase.co/storage/v1/object/sign/avatars/folder/avatar1.png?token=<TOKEN>" | |
| * }, | |
| * "error": null | |
| * } | |
| * ``` | |
| * | |
| * @example Create a signed URL for an asset with transformations | |
| * ```js | |
| * const { data } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .createSignedUrl('folder/avatar1.png', 60, { | |
| * transform: { | |
| * width: 100, | |
| * height: 100, | |
| * } | |
| * }) | |
| * ``` | |
| * | |
| * @example Create a signed URL which triggers the download of the asset | |
| * ```js | |
| * const { data } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .createSignedUrl('folder/avatar1.png', 60, { | |
| * download: true, | |
| * }) | |
| * ``` | |
| * | |
| * @remarks | |
| * - RLS policy permissions required: | |
| * - `buckets` table permissions: none | |
| * - `objects` table permissions: `select` | |
| * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works | |
| */ | |
| async createSignedUrl( | |
| path: string, | |
| expiresIn: number, | |
| options?: { | |
| download?: string | boolean | |
| transform?: TransformOptions | |
| cacheNonce?: string | |
| } | |
| ): Promise< | |
| | { | |
| data: { signedUrl: string } | |
| error: null | |
| } | |
| | { | |
| data: null | |
| error: StorageError | |
| } | |
| > { | |
| return this.handleOperation(async () => { | |
| let _path = this._getFinalPath(path) | |
| const hasTransform = | |
| typeof options?.transform === 'object' && | |
| options.transform !== null && | |
| Object.keys(options.transform).length > 0 | |
| let data = await post( | |
| this.fetch, | |
| `${this.url}/object/sign/${_path}`, | |
| { expiresIn, ...(hasTransform ? { transform: options!.transform } : {}) }, | |
| { headers: this.headers } | |
| ) | |
| const query = new URLSearchParams() | |
| if (options?.download) | |
| query.set('download', options.download === true ? '' : options.download) | |
| if (options?.cacheNonce != null) query.set('cacheNonce', String(options.cacheNonce)) | |
| const queryString = query.toString() | |
| // `data.signedURL` contains a `token` query parameter, so append extra params with `&` | |
| // only when we actually have something to add. | |
| const signedUrl = encodeURI( | |
| `${this.url}${data.signedURL}${queryString ? `&${queryString}` : ''}` | |
| ) | |
| return { signedUrl } | |
| }) | |
| } | |
| /** | |
| * Creates multiple signed URLs. Use a signed URL to share a file for a fixed amount of time. | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param paths The file paths to be downloaded, including the current file names. For example `['folder/image.png', 'folder2/image2.png']`. | |
| * @param expiresIn The number of seconds until the signed URLs expire. For example, `60` for URLs which are valid for one minute. | |
| * @param options.download triggers the file as a download if set to true. Set this parameter as the name of the file if you want to trigger the download with a different filename. | |
| * @param options.cacheNonce Append a cache nonce parameter to the URL to invalidate the cache. | |
| * @returns Promise with response containing array of objects with signedUrl, path, and error or error | |
| * | |
| * @example Create Signed URLs | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .createSignedUrls(['folder/avatar1.png', 'folder/avatar2.png'], 60) | |
| * ``` | |
| * | |
| * Response: | |
| * ```json | |
| * { | |
| * "data": [ | |
| * { | |
| * "error": null, | |
| * "path": "folder/avatar1.png", | |
| * "signedURL": "/object/sign/avatars/folder/avatar1.png?token=<TOKEN>", | |
| * "signedUrl": "https://example.supabase.co/storage/v1/object/sign/avatars/folder/avatar1.png?token=<TOKEN>" | |
| * }, | |
| * { | |
| * "error": null, | |
| * "path": "folder/avatar2.png", | |
| * "signedURL": "/object/sign/avatars/folder/avatar2.png?token=<TOKEN>", | |
| * "signedUrl": "https://example.supabase.co/storage/v1/object/sign/avatars/folder/avatar2.png?token=<TOKEN>" | |
| * } | |
| * ], | |
| * "error": null | |
| * } | |
| * ``` | |
| * | |
| * @remarks | |
| * - RLS policy permissions required: | |
| * - `buckets` table permissions: none | |
| * - `objects` table permissions: `select` | |
| * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works | |
| */ | |
| async createSignedUrls( | |
| paths: string[], | |
| expiresIn: number, | |
| options?: { download?: string | boolean; cacheNonce?: string } | |
| ): Promise< | |
| | { | |
| data: { | |
| error: string | null | |
| path: string | null | |
| signedURL: string | null | |
| signedUrl: string | null | |
| }[] | |
| error: null | |
| } | |
| | { | |
| data: null | |
| error: StorageError | |
| } | |
| > { | |
| return this.handleOperation(async () => { | |
| const data = await post( | |
| this.fetch, | |
| `${this.url}/object/sign/${this.bucketId}`, | |
| { expiresIn, paths }, | |
| { headers: this.headers } | |
| ) | |
| const query = new URLSearchParams() | |
| if (options?.download) | |
| query.set('download', options.download === true ? '' : options.download) | |
| if (options?.cacheNonce != null) query.set('cacheNonce', String(options.cacheNonce)) | |
| const queryString = query.toString() | |
| return data.map( | |
| (datum: { error: string | null; path: string | null; signedURL: string | null }) => ({ | |
| ...datum, | |
| signedUrl: datum.signedURL | |
| ? encodeURI(`${this.url}${datum.signedURL}${queryString ? `&${queryString}` : ''}`) | |
| : null, | |
| }) | |
| ) | |
| }) | |
| } | |
| /** | |
| * Downloads a file from a private bucket. For public buckets, make a request to the URL returned from `getPublicUrl` instead. | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param path The full path and file name of the file to be downloaded. For example `folder/image.png`. | |
| * @param options Optional settings: `transform` to transform the asset before serving it to the client, `cacheNonce` to append a cache nonce parameter to the URL to invalidate the cache, and `versionId` to download a specific object version. | |
| * @param parameters Additional fetch parameters like signal for cancellation. Supports standard fetch options including cache control. | |
| * @returns BlobDownloadBuilder instance for downloading the file | |
| * | |
| * @example Download file | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .download('folder/avatar1.png') | |
| * ``` | |
| * | |
| * Response: | |
| * ```json | |
| * { | |
| * "data": <BLOB>, | |
| * "error": null | |
| * } | |
| * ``` | |
| * | |
| * @example Download file with transformations | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .download('folder/avatar1.png', { | |
| * transform: { | |
| * width: 100, | |
| * height: 100, | |
| * quality: 80 | |
| * } | |
| * }) | |
| * ``` | |
| * | |
| * @example Download with cache control (useful in Edge Functions) | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .download('folder/avatar1.png', {}, { cache: 'no-store' }) | |
| * ``` | |
| * | |
| * @example Download with abort signal | |
| * ```js | |
| * const controller = new AbortController() | |
| * setTimeout(() => controller.abort(), 5000) | |
| * | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .download('folder/avatar1.png', {}, { signal: controller.signal }) | |
| * ``` | |
| * | |
| * @remarks | |
| * - RLS policy permissions required: | |
| * - `buckets` table permissions: none | |
| * - `objects` table permissions: `select` | |
| * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works | |
| */ | |
| download< | |
| Options extends { transform?: TransformOptions; cacheNonce?: string; versionId?: string }, | |
| >(path: string, options?: Options, parameters?: FetchParameters): BlobDownloadBuilder { | |
| const wantsTransformation = | |
| typeof options?.transform === 'object' && | |
| options.transform !== null && | |
| Object.keys(options.transform).length > 0 | |
| const renderPath = wantsTransformation ? 'render/image/authenticated' : 'object' | |
| const query = new URLSearchParams() | |
| if (options?.transform) this.applyTransformOptsToQuery(query, options.transform) | |
| if (options?.cacheNonce != null) query.set('cacheNonce', String(options.cacheNonce)) | |
| if (options?.versionId != null) query.set('versionId', String(options.versionId)) | |
| const queryString = query.toString() | |
| const _path = this._getFinalPath(path) | |
| const downloadFn = () => | |
| get( | |
| this.fetch, | |
| `${this.url}/${renderPath}/${_path}${queryString ? `?${queryString}` : ''}`, | |
| { | |
| headers: this.headers, | |
| noResolveJson: true, | |
| }, | |
| parameters | |
| ) | |
| return new BlobDownloadBuilder(downloadFn, this.shouldThrowOnError) | |
| } | |
| /** | |
| * Retrieves the details of an existing file. | |
| * | |
| * Returns detailed file metadata including size, content type, and timestamps. | |
| * Note: The API returns `last_modified` field, not `updated_at`. | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param path The file path, including the file name. For example `folder/image.png`. | |
| * @param options Optional settings, including `versionId` to retrieve a specific object version. | |
| * @returns Promise with response containing file metadata or error | |
| * | |
| * @example Get file info | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .info('folder/avatar1.png') | |
| * | |
| * if (data) { | |
| * console.log('Last modified:', data.lastModified) | |
| * console.log('Size:', data.size) | |
| * } | |
| * ``` | |
| */ | |
| async info( | |
| path: string, | |
| options?: { versionId?: string } | |
| ): Promise< | |
| | { | |
| data: Camelize<FileObjectV2> | |
| error: null | |
| } | |
| | { | |
| data: null | |
| error: StorageError | |
| } | |
| > { | |
| const _path = this._getFinalPath(path) | |
| const query = new URLSearchParams() | |
| if (options?.versionId != null) query.set('versionId', String(options.versionId)) | |
| const queryString = query.toString() | |
| return this.handleOperation(async () => { | |
| const data = await get( | |
| this.fetch, | |
| `${this.url}/object/info/${_path}${queryString ? `?${queryString}` : ''}`, | |
| { | |
| headers: this.headers, | |
| } | |
| ) | |
| return recursiveToCamel(data) as Camelize<FileObjectV2> | |
| }) | |
| } | |
| /** | |
| * Checks the existence of a file. | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param path The file path, including the file name. For example `folder/image.png`. | |
| * @returns Promise with response containing boolean indicating file existence or error | |
| * | |
| * @example Check file existence | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .exists('folder/avatar1.png') | |
| * ``` | |
| */ | |
| async exists(path: string): Promise< | |
| | { | |
| data: boolean | |
| error: null | |
| } | |
| | { | |
| data: boolean | |
| error: StorageError | |
| } | |
| > { | |
| const _path = this._getFinalPath(path) | |
| try { | |
| await head(this.fetch, `${this.url}/object/${_path}`, { | |
| headers: this.headers, | |
| }) | |
| return { data: true, error: null } | |
| } catch (error) { | |
| if (this.shouldThrowOnError) { | |
| throw error | |
| } | |
| if (isStorageError(error)) { | |
| // HEAD requests produce StorageApiError (via handleError) or StorageUnknownError (legacy) | |
| const status = | |
| error instanceof StorageApiError | |
| ? error.status | |
| : error instanceof StorageUnknownError | |
| ? (error.originalError as { status: number })?.status | |
| : undefined | |
| if (status !== undefined && [400, 404].includes(status)) { | |
| return { data: false, error } | |
| } | |
| } | |
| throw error | |
| } | |
| } | |
| /** | |
| * A simple convenience function to get the URL for an asset in a public bucket. If you do not want to use this function, you can construct the public URL by concatenating the bucket URL with the path to the asset. | |
| * This function does not verify if the bucket is public. If a public URL is created for a bucket which is not public, you will not be able to download the asset. | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param path The path and name of the file to generate the public URL for. For example `folder/image.png`. | |
| * @param options.download Triggers the file as a download if set to true. Set this parameter as the name of the file if you want to trigger the download with a different filename. | |
| * @param options.transform Transform the asset before serving it to the client. | |
| * @param options.cacheNonce Append a cache nonce parameter to the URL to invalidate the cache. | |
| * @returns Object with public URL | |
| * | |
| * @example Returns the URL for an asset in a public bucket | |
| * ```js | |
| * const { data } = supabase | |
| * .storage | |
| * .from('public-bucket') | |
| * .getPublicUrl('folder/avatar1.png') | |
| * ``` | |
| * | |
| * Response: | |
| * ```json | |
| * { | |
| * "data": { | |
| * "publicUrl": "https://example.supabase.co/storage/v1/object/public/public-bucket/folder/avatar1.png" | |
| * } | |
| * } | |
| * ``` | |
| * | |
| * @example Returns the URL for an asset in a public bucket with transformations | |
| * ```js | |
| * const { data } = supabase | |
| * .storage | |
| * .from('public-bucket') | |
| * .getPublicUrl('folder/avatar1.png', { | |
| * transform: { | |
| * width: 100, | |
| * height: 100, | |
| * } | |
| * }) | |
| * ``` | |
| * | |
| * @example Returns the URL which triggers the download of an asset in a public bucket | |
| * ```js | |
| * const { data } = supabase | |
| * .storage | |
| * .from('public-bucket') | |
| * .getPublicUrl('folder/avatar1.png', { | |
| * download: true, | |
| * }) | |
| * ``` | |
| * | |
| * @remarks | |
| * - The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [supabase.com/dashboard](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public" | |
| * - RLS policy permissions required: | |
| * - `buckets` table permissions: none | |
| * - `objects` table permissions: none | |
| * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works | |
| */ | |
| getPublicUrl( | |
| path: string, | |
| options?: { | |
| download?: string | boolean | |
| transform?: TransformOptions | |
| cacheNonce?: string | |
| } | |
| ): { data: { publicUrl: string } } { | |
| const _path = this._getFinalPath(path) | |
| const query = new URLSearchParams() | |
| if (options?.download) query.set('download', options.download === true ? '' : options.download) | |
| if (options?.transform) this.applyTransformOptsToQuery(query, options.transform) | |
| if (options?.cacheNonce != null) query.set('cacheNonce', String(options.cacheNonce)) | |
| const queryString = query.toString() | |
| const wantsTransformation = | |
| typeof options?.transform === 'object' && | |
| options.transform !== null && | |
| Object.keys(options.transform).length > 0 | |
| const renderPath = wantsTransformation ? 'render/image' : 'object' | |
| return { | |
| data: { | |
| publicUrl: | |
| encodeURI(`${this.url}/${renderPath}/public/${_path}`) + | |
| (queryString ? `?${queryString}` : ''), | |
| }, | |
| } | |
| } | |
| /** | |
| * Deletes files within the same bucket | |
| * | |
| * Returns an array of FileObject entries for the deleted files. Note that deprecated | |
| * fields like `bucket_id` may or may not be present in the response - do not rely on them. | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param paths An array of files to delete. Each entry is either a path (deletes whichever | |
| * version is currently at that path, e.g. `'folder/image.png'`), or `{ path, versionId }` to | |
| * delete an exact version current or archived (e.g. `{ path: 'folder/image.png', versionId: '...' }`). | |
| * @returns Promise with response containing array of deleted file objects or error | |
| * | |
| * @example Delete file | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .remove(['folder/avatar1.png']) | |
| * ``` | |
| * | |
| * Response: | |
| * ```json | |
| * { | |
| * "data": [], | |
| * "error": null | |
| * } | |
| * ``` | |
| * | |
| * @example Delete a specific object version | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .remove([{ path: 'folder/avatar1.png', versionId: 'noncurrent-version-id' }]) | |
| * ``` | |
| * | |
| * @remarks | |
| * - RLS policy permissions required: | |
| * - `buckets` table permissions: none | |
| * - `objects` table permissions: `delete` and `select` | |
| * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works | |
| */ | |
| async remove(paths: DeleteObjectEntry[]): Promise< | |
| | { | |
| data: FileObject[] | |
| error: null | |
| } | |
| | { | |
| data: null | |
| error: StorageError | |
| } | |
| > { | |
| return this.handleOperation(async () => { | |
| return await remove( | |
| this.fetch, | |
| `${this.url}/object/${this.bucketId}`, | |
| { prefixes: paths }, | |
| { headers: this.headers } | |
| ) | |
| }) | |
| } | |
| /** | |
| * Purges the CDN cache for a single object in this bucket. | |
| * | |
| * Maps to `DELETE /cdn/{bucket}/{path}` on the Storage API. The server | |
| * issues a CDN invalidation for the object and returns `{ message: 'success' }`. | |
| * | |
| * **Requires the `service_role` key.** The underlying endpoint enforces | |
| * `service_role` JWT — calls made with the anon key or a user JWT will be | |
| * rejected by the server. | |
| * | |
| * **Hosted CDN feature.** On self-hosted Supabase, the Storage service must | |
| * have `CDN_PURGE_ENDPOINT_URL` configured and the `purgeCache` tenant | |
| * feature enabled, otherwise the server returns an error. | |
| * | |
| * Operates on a single object path. There is no wildcard or recursion: pass | |
| * the exact path of the object you want invalidated. | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param path The path (relative to the bucket) of the object to purge, e.g. `folder/avatar.png`. | |
| * @param options Optional purge cache options. | |
| * @param options.transformations If true, purges only transformations (resized/formatted variants), leaving the original cached file intact. | |
| * @param parameters Optional fetch parameters such as an `AbortController` signal. | |
| * @returns Promise with `{ data: { message }, error: null }` on success or `{ data: null, error }` on failure. | |
| * | |
| * @example Purge a single cached object | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .purgeCache('folder/avatar1.png') | |
| * ``` | |
| * | |
| * @example Purge only transformations for a single object | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .purgeCache('folder/avatar1.png', { transformations: true }) | |
| * ``` | |
| */ | |
| async purgeCache( | |
| path: string, | |
| options?: PurgeCacheOptions, | |
| parameters?: FetchParameters | |
| ): Promise< | |
| | { | |
| data: { message: string } | |
| error: null | |
| } | |
| | { | |
| data: null | |
| error: StorageError | |
| } | |
| > { | |
| return this.handleOperation(async () => { | |
| const _path = encodeStoragePath(this._getFinalPath(path)) | |
| const query = new URLSearchParams() | |
| if (options?.transformations) { | |
| query.set('transformations', 'true') | |
| } | |
| const queryString = query.toString() | |
| return await remove( | |
| this.fetch, | |
| `${this.url}/cdn/${_path}${queryString ? `?${queryString}` : ''}`, | |
| {}, | |
| { headers: this.headers }, | |
| parameters | |
| ) | |
| }) | |
| } | |
| /** | |
| * Get file metadata | |
| * @param id the file id to retrieve metadata | |
| */ | |
| // async getMetadata( | |
| // id: string | |
| // ): Promise< | |
| // | { | |
| // data: Metadata | |
| // error: null | |
| // } | |
| // | { | |
| // data: null | |
| // error: StorageError | |
| // } | |
| // > { | |
| // try { | |
| // const data = await get(this.fetch, `${this.url}/metadata/${id}`, { headers: this.headers }) | |
| // return { data, error: null } | |
| // } catch (error) { | |
| // if (isStorageError(error)) { | |
| // return { data: null, error } | |
| // } | |
| // throw error | |
| // } | |
| // } | |
| /** | |
| * Update file metadata | |
| * @param id the file id to update metadata | |
| * @param meta the new file metadata | |
| */ | |
| // async updateMetadata( | |
| // id: string, | |
| // meta: Metadata | |
| // ): Promise< | |
| // | { | |
| // data: Metadata | |
| // error: null | |
| // } | |
| // | { | |
| // data: null | |
| // error: StorageError | |
| // } | |
| // > { | |
| // try { | |
| // const data = await post( | |
| // this.fetch, | |
| // `${this.url}/metadata/${id}`, | |
| // { ...meta }, | |
| // { headers: this.headers } | |
| // ) | |
| // return { data, error: null } | |
| // } catch (error) { | |
| // if (isStorageError(error)) { | |
| // return { data: null, error } | |
| // } | |
| // throw error | |
| // } | |
| // } | |
| /** | |
| * Lists all the files and folders within a path of the bucket. | |
| * | |
| * **Important:** For folder entries, fields like `id`, `updated_at`, `created_at`, | |
| * `last_accessed_at`, and `metadata` will be `null`. Only files have these fields populated. | |
| * Additionally, deprecated fields like `bucket_id`, `owner`, and `buckets` are NOT returned | |
| * by this method. | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param path The folder path. | |
| * @param options Search options including limit (defaults to 100), offset, sortBy, and search | |
| * @param parameters Optional fetch parameters including signal for cancellation | |
| * @returns Promise with response containing array of files/folders or error | |
| * | |
| * @example List files in a bucket | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .list('folder', { | |
| * limit: 100, | |
| * offset: 0, | |
| * sortBy: { column: 'name', order: 'asc' }, | |
| * }) | |
| * | |
| * // Handle files vs folders | |
| * data?.forEach(item => { | |
| * if (item.id !== null) { | |
| * // It's a file | |
| * console.log('File:', item.name, 'Size:', item.metadata?.size) | |
| * } else { | |
| * // It's a folder | |
| * console.log('Folder:', item.name) | |
| * } | |
| * }) | |
| * ``` | |
| * | |
| * Response: | |
| * ```json | |
| * { | |
| * "data": [ | |
| * { | |
| * "name": "avatar1.png", | |
| * "id": "e668cf7f-821b-4a2f-9dce-7dfa5dd1cfd2", | |
| * "updated_at": "2024-05-22T23:06:05.580Z", | |
| * "created_at": "2024-05-22T23:04:34.443Z", | |
| * "last_accessed_at": "2024-05-22T23:04:34.443Z", | |
| * "metadata": { | |
| * "eTag": "\"c5e8c553235d9af30ef4f6e280790b92\"", | |
| * "size": 32175, | |
| * "mimetype": "image/png", | |
| * "cacheControl": "max-age=3600", | |
| * "lastModified": "2024-05-22T23:06:05.574Z", | |
| * "contentLength": 32175, | |
| * "httpStatusCode": 200 | |
| * } | |
| * } | |
| * ], | |
| * "error": null | |
| * } | |
| * ``` | |
| * | |
| * @example Search files in a bucket | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .list('folder', { | |
| * limit: 100, | |
| * offset: 0, | |
| * sortBy: { column: 'name', order: 'asc' }, | |
| * search: 'jon' | |
| * }) | |
| * ``` | |
| * | |
| * @remarks | |
| * - RLS policy permissions required: | |
| * - `buckets` table permissions: none | |
| * - `objects` table permissions: `select` | |
| * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works | |
| */ | |
| async list( | |
| path?: string, | |
| options?: SearchOptions, | |
| parameters?: FetchParameters | |
| ): Promise< | |
| | { | |
| data: FileObject[] | |
| error: null | |
| } | |
| | { | |
| data: null | |
| error: StorageError | |
| } | |
| > { | |
| return this.handleOperation(async () => { | |
| // Deep-merge `sortBy` so overriding only one of its keys (e.g. `column`) keeps the | |
| // default for the other (e.g. `order`), matching how the top-level defaults behave. | |
| const sortBy = options?.sortBy | |
| ? { ...DEFAULT_SEARCH_OPTIONS.sortBy, ...options.sortBy } | |
| : DEFAULT_SEARCH_OPTIONS.sortBy | |
| const body = { ...DEFAULT_SEARCH_OPTIONS, ...options, sortBy, prefix: path || '' } | |
| return await post( | |
| this.fetch, | |
| `${this.url}/object/list/${this.bucketId}`, | |
| body, | |
| { headers: this.headers }, | |
| parameters | |
| ) | |
| }) | |
| } | |
| /** | |
| * Lists all the files and folders within a bucket using the V2 API with pagination support. | |
| * | |
| * **Important:** Folder entries in the `folders` array only contain `name` and optionally `key` — | |
| * they have no `id`, timestamps, or `metadata` fields. Full file metadata is only available | |
| * on entries in the `objects` array. | |
| * | |
| * @experimental this method signature might change in the future | |
| * | |
| * @category Storage | |
| * @subcategory File Buckets | |
| * @param options Search options including prefix, cursor for pagination, limit, with_delimiter | |
| * @param parameters Optional fetch parameters including signal for cancellation | |
| * @returns Promise with response containing folders/objects arrays with pagination info or error | |
| * | |
| * @example List files with pagination | |
| * ```js | |
| * const { data, error } = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .listV2({ | |
| * prefix: 'folder/', | |
| * limit: 100, | |
| * }) | |
| * | |
| * // Handle pagination | |
| * if (data?.hasNext) { | |
| * const nextPage = await supabase | |
| * .storage | |
| * .from('avatars') | |
| * .listV2({ | |
| * prefix: 'folder/', | |
| * cursor: data.nextCursor, | |
| * }) | |
| * } | |
| * | |
| * // Handle files vs folders | |
| * data?.objects.forEach(file => { | |
| * if (file.id !== null) { | |
| * console.log('File:', file.name, 'Size:', file.metadata?.size) | |
| * } | |
| * }) | |
| * data?.folders.forEach(folder => { | |
| * console.log('Folder:', folder.name) | |
| * }) | |
| * ``` | |
| */ | |
| async listV2( | |
| options?: SearchV2Options, | |
| parameters?: FetchParameters | |
| ): Promise< | |
| | { | |
| data: SearchV2Result | |
| error: null | |
| } | |
| | { | |
| data: null | |
| error: StorageError | |
| } | |
| > { | |
| return this.handleOperation(async () => { | |
| const body = { ...options } | |
| return await post( | |
| this.fetch, | |
| `${this.url}/object/list-v2/${this.bucketId}`, | |
| body, | |
| { headers: this.headers }, | |
| parameters | |
| ) | |
| }) | |
| } | |
| protected encodeMetadata(metadata: Record<string, any>) { | |
| return JSON.stringify(metadata) | |
| } | |
| toBase64(data: string) { | |
| if (typeof Buffer !== 'undefined') { | |
| return Buffer.from(data).toString('base64') | |
| } | |
| return btoa(data) | |
| } | |
| private _getFinalPath(path: string) { | |
| return `${this.bucketId}/${path.replace(/^\/+/, '')}` | |
| } | |
| private _removeEmptyFolders(path: string) { | |
| return path.replace(/^\/|\/$/g, '').replace(/\/+/g, '/') | |
| } | |
| /** Modifies the `query`, appending values the from `transform` */ | |
| private applyTransformOptsToQuery( | |
| query: URLSearchParams, | |
| transform: TransformOptions | |
| ): URLSearchParams { | |
| if (transform.width) query.set('width', transform.width.toString()) | |
| if (transform.height) query.set('height', transform.height.toString()) | |
| if (transform.resize) query.set('resize', transform.resize) | |
| if (transform.format) query.set('format', transform.format) | |
| if (transform.quality) query.set('quality', transform.quality.toString()) | |
| return query | |
| } | |
| } | |