UNPKG

@supabase/storage-js

Version:
1,603 lines (1,588 loc) 94.7 kB
let iceberg_js = require("iceberg-js"); //#region src/lib/errors.ts var StorageError = class extends Error { constructor(message) { super(message); this.__isStorageError = true; this.name = "StorageError"; } }; function isStorageError(error) { return typeof error === "object" && error !== null && "__isStorageError" in error; } var StorageApiError = class extends StorageError { constructor(message, status, statusCode) { super(message); this.name = "StorageApiError"; this.status = status; this.statusCode = statusCode; } toJSON() { return { name: this.name, message: this.message, status: this.status, statusCode: this.statusCode }; } }; var StorageUnknownError = class extends StorageError { constructor(message, originalError) { super(message); this.name = "StorageUnknownError"; this.originalError = originalError; } }; //#endregion //#region src/lib/helpers.ts const resolveFetch$1 = (customFetch) => { if (customFetch) return (...args) => customFetch(...args); return (...args) => fetch(...args); }; const resolveResponse$1 = () => { return Response; }; const recursiveToCamel = (item) => { if (Array.isArray(item)) return item.map((el) => recursiveToCamel(el)); else if (typeof item === "function" || item !== Object(item)) return item; const result = {}; Object.entries(item).forEach(([key, value]) => { const newKey = key.replace(/([-_][a-z])/gi, (c) => c.toUpperCase().replace(/[-_]/g, "")); result[newKey] = recursiveToCamel(value); }); return result; }; /** * Determine if input is a plain object * An object is plain if it's created by either {}, new Object(), or Object.create(null) * source: https://github.com/sindresorhus/is-plain-obj */ const isPlainObject$1 = (value) => { if (typeof value !== "object" || value === null) return false; const prototype = Object.getPrototypeOf(value); return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value); }; /** * Validates if a given bucket name is valid according to Supabase Storage API rules * Mirrors backend validation from: storage/src/storage/limits.ts:isValidBucketName() * * Rules: * - Length: 1-100 characters * - Allowed characters: alphanumeric (a-z, A-Z, 0-9), underscore (_), and safe special characters * - Safe special characters: ! - . * ' ( ) space & $ @ = ; : + , ? * - Forbidden: path separators (/, \), path traversal (..), leading/trailing whitespace * * AWS S3 Reference: https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html * * @param bucketName - The bucket name to validate * @returns true if valid, false otherwise */ const isValidBucketName = (bucketName) => { if (!bucketName || typeof bucketName !== "string") return false; if (bucketName.length === 0 || bucketName.length > 100) return false; if (bucketName.trim() !== bucketName) return false; if (bucketName.includes("/") || bucketName.includes("\\")) return false; return /^[\w!.\*'() &$@=;:+,?-]+$/.test(bucketName); }; //#endregion //#region \0@oxc-project+runtime@0.101.0/helpers/typeof.js function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) { return typeof o$1; } : function(o$1) { return o$1 && "function" == typeof Symbol && o$1.constructor === Symbol && o$1 !== Symbol.prototype ? "symbol" : typeof o$1; }, _typeof(o); } //#endregion //#region \0@oxc-project+runtime@0.101.0/helpers/toPrimitive.js function toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } //#endregion //#region \0@oxc-project+runtime@0.101.0/helpers/toPropertyKey.js function toPropertyKey(t) { var i = toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } //#endregion //#region \0@oxc-project+runtime@0.101.0/helpers/defineProperty.js function _defineProperty(e, r, t) { return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } //#endregion //#region \0@oxc-project+runtime@0.101.0/helpers/objectSpread2.js function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function(r$1) { return Object.getOwnPropertyDescriptor(e, r$1).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread2(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function(r$1) { _defineProperty(e, r$1, t[r$1]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r$1) { Object.defineProperty(e, r$1, Object.getOwnPropertyDescriptor(t, r$1)); }); } return e; } //#endregion //#region src/lib/fetch.ts const _getErrorMessage$1 = (err) => { var _err$error; return err.msg || err.message || err.error_description || (typeof err.error === "string" ? err.error : (_err$error = err.error) === null || _err$error === void 0 ? void 0 : _err$error.message) || JSON.stringify(err); }; const handleError$1 = async (error, reject, options) => { if (error instanceof await resolveResponse$1() && !(options === null || options === void 0 ? void 0 : options.noResolveJson)) error.json().then((err) => { const status = error.status || 500; const statusCode = (err === null || err === void 0 ? void 0 : err.statusCode) || status + ""; reject(new StorageApiError(_getErrorMessage$1(err), status, statusCode)); }).catch((err) => { reject(new StorageUnknownError(_getErrorMessage$1(err), err)); }); else reject(new StorageUnknownError(_getErrorMessage$1(error), error)); }; const _getRequestParams$1 = (method, options, parameters, body) => { const params = { method, headers: (options === null || options === void 0 ? void 0 : options.headers) || {} }; if (method === "GET" || !body) return params; if (isPlainObject$1(body)) { params.headers = _objectSpread2({ "Content-Type": "application/json" }, options === null || options === void 0 ? void 0 : options.headers); params.body = JSON.stringify(body); } else params.body = body; if (options === null || options === void 0 ? void 0 : options.duplex) params.duplex = options.duplex; return _objectSpread2(_objectSpread2({}, params), parameters); }; async function _handleRequest$1(fetcher, method, url, options, parameters, body) { return new Promise((resolve, reject) => { fetcher(url, _getRequestParams$1(method, options, parameters, body)).then((result) => { if (!result.ok) throw result; if (options === null || options === void 0 ? void 0 : options.noResolveJson) return result; return result.json(); }).then((data) => resolve(data)).catch((error) => handleError$1(error, reject, options)); }); } async function get(fetcher, url, options, parameters) { return _handleRequest$1(fetcher, "GET", url, options, parameters); } async function post$1(fetcher, url, body, options, parameters) { return _handleRequest$1(fetcher, "POST", url, options, parameters, body); } async function put(fetcher, url, body, options, parameters) { return _handleRequest$1(fetcher, "PUT", url, options, parameters, body); } async function head(fetcher, url, options, parameters) { return _handleRequest$1(fetcher, "HEAD", url, _objectSpread2(_objectSpread2({}, options), {}, { noResolveJson: true }), parameters); } async function remove(fetcher, url, body, options, parameters) { return _handleRequest$1(fetcher, "DELETE", url, options, parameters, body); } //#endregion //#region src/packages/StreamDownloadBuilder.ts var StreamDownloadBuilder = class { constructor(downloadFn, shouldThrowOnError) { this.downloadFn = downloadFn; this.shouldThrowOnError = shouldThrowOnError; } then(onfulfilled, onrejected) { return this.execute().then(onfulfilled, onrejected); } async execute() { var _this = this; try { return { data: (await _this.downloadFn()).body, error: null }; } catch (error) { if (_this.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } }; //#endregion //#region src/packages/BlobDownloadBuilder.ts let _Symbol$toStringTag; _Symbol$toStringTag = Symbol.toStringTag; var BlobDownloadBuilder = class { constructor(downloadFn, shouldThrowOnError) { this.downloadFn = downloadFn; this.shouldThrowOnError = shouldThrowOnError; this[_Symbol$toStringTag] = "BlobDownloadBuilder"; this.promise = null; } asStream() { return new StreamDownloadBuilder(this.downloadFn, this.shouldThrowOnError); } then(onfulfilled, onrejected) { return this.getPromise().then(onfulfilled, onrejected); } catch(onrejected) { return this.getPromise().catch(onrejected); } finally(onfinally) { return this.getPromise().finally(onfinally); } getPromise() { if (!this.promise) this.promise = this.execute(); return this.promise; } async execute() { var _this = this; try { return { data: await (await _this.downloadFn()).blob(), error: null }; } catch (error) { if (_this.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } }; //#endregion //#region src/packages/StorageFileApi.ts const DEFAULT_SEARCH_OPTIONS = { limit: 100, offset: 0, sortBy: { column: "name", order: "asc" } }; const DEFAULT_FILE_OPTIONS = { cacheControl: "3600", contentType: "text/plain;charset=UTF-8", upsert: false }; var StorageFileApi = class { constructor(url, headers = {}, bucketId, fetch$1) { this.shouldThrowOnError = false; this.url = url; this.headers = headers; this.bucketId = bucketId; this.fetch = resolveFetch$1(fetch$1); } /** * Enable throwing errors instead of returning them. * * @category File Buckets */ throwOnError() { this.shouldThrowOnError = true; return this; } /** * 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. */ async uploadOrUpdate(method, path, fileBody, fileOptions) { var _this = this; try { let body; const options = _objectSpread2(_objectSpread2({}, DEFAULT_FILE_OPTIONS), fileOptions); let headers = _objectSpread2(_objectSpread2({}, _this.headers), method === "POST" && { "x-upsert": String(options.upsert) }); const metadata = options.metadata; if (typeof Blob !== "undefined" && fileBody instanceof Blob) { body = new FormData(); body.append("cacheControl", options.cacheControl); 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); 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; if (metadata) headers["x-metadata"] = _this.toBase64(_this.encodeMetadata(metadata)); if ((typeof ReadableStream !== "undefined" && body instanceof ReadableStream || body && typeof body === "object" && "pipe" in body && typeof body.pipe === "function") && !options.duplex) options.duplex = "half"; } if (fileOptions === null || fileOptions === void 0 ? void 0 : fileOptions.headers) headers = _objectSpread2(_objectSpread2({}, headers), fileOptions.headers); const cleanPath = _this._removeEmptyFolders(path); const _path = _this._getFinalPath(cleanPath); const data = await (method == "PUT" ? put : post$1)(_this.fetch, `${_this.url}/object/${_path}`, body, _objectSpread2({ headers }, (options === null || options === void 0 ? void 0 : options.duplex) ? { duplex: options.duplex } : {})); return { data: { path: cleanPath, id: data.Id, fullPath: data.Key }, error: null }; } catch (error) { if (_this.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * Uploads a file to an existing bucket. * * @category 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' * }) * ``` */ async upload(path, fileBody, fileOptions) { return this.uploadOrUpdate("POST", path, fileBody, fileOptions); } /** * Upload a file with a token generated from `createSignedUploadUrl`. * * @category 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 * } * ``` */ async uploadToSignedUrl(path, token, fileBody, fileOptions) { var _this3 = this; const cleanPath = _this3._removeEmptyFolders(path); const _path = _this3._getFinalPath(cleanPath); const url = new URL(_this3.url + `/object/upload/sign/${_path}`); url.searchParams.set("token", token); try { let body; const options = _objectSpread2({ upsert: DEFAULT_FILE_OPTIONS.upsert }, fileOptions); const headers = _objectSpread2(_objectSpread2({}, _this3.headers), { "x-upsert": String(options.upsert) }); if (typeof Blob !== "undefined" && fileBody instanceof Blob) { body = new FormData(); body.append("cacheControl", options.cacheControl); body.append("", fileBody); } else if (typeof FormData !== "undefined" && fileBody instanceof FormData) { body = fileBody; body.append("cacheControl", options.cacheControl); } else { body = fileBody; headers["cache-control"] = `max-age=${options.cacheControl}`; headers["content-type"] = options.contentType; } return { data: { path: cleanPath, fullPath: (await put(_this3.fetch, url.toString(), body, { headers })).Key }, error: null }; } catch (error) { if (_this3.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * 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 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 * } * ``` */ async createSignedUploadUrl(path, options) { var _this4 = this; try { let _path = _this4._getFinalPath(path); const headers = _objectSpread2({}, _this4.headers); if (options === null || options === void 0 ? void 0 : options.upsert) headers["x-upsert"] = "true"; const data = await post$1(_this4.fetch, `${_this4.url}/object/upload/sign/${_path}`, {}, { headers }); const url = new URL(_this4.url + data.url); const token = url.searchParams.get("token"); if (!token) throw new StorageError("No token returned by API"); return { data: { signedUrl: url.toString(), path, token }, error: null }; } catch (error) { if (_this4.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * Replaces an existing file at the specified path with a new one. * * @category 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, upsert, and metadata. * @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', * upsert: true * }) * ``` * * 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' * }) * ``` */ async update(path, fileBody, fileOptions) { return this.uploadOrUpdate("PUT", path, fileBody, fileOptions); } /** * Moves an existing file to a new path in the same bucket. * * @category 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. * @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 * } * ``` */ async move(fromPath, toPath, options) { var _this6 = this; try { return { data: await post$1(_this6.fetch, `${_this6.url}/object/move`, { bucketId: _this6.bucketId, sourceKey: fromPath, destinationKey: toPath, destinationBucket: options === null || options === void 0 ? void 0 : options.destinationBucket }, { headers: _this6.headers }), error: null }; } catch (error) { if (_this6.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * Copies an existing file to a new path in the same bucket. * * @category 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. * @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 * } * ``` */ async copy(fromPath, toPath, options) { var _this7 = this; try { return { data: { path: (await post$1(_this7.fetch, `${_this7.url}/object/copy`, { bucketId: _this7.bucketId, sourceKey: fromPath, destinationKey: toPath, destinationBucket: options === null || options === void 0 ? void 0 : options.destinationBucket }, { headers: _this7.headers })).Key }, error: null }; } catch (error) { if (_this7.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * Creates a signed URL. Use a signed URL to share a file for a fixed amount of time. * * @category 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. * @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, * }) * ``` */ async createSignedUrl(path, expiresIn, options) { var _this8 = this; try { let _path = _this8._getFinalPath(path); let data = await post$1(_this8.fetch, `${_this8.url}/object/sign/${_path}`, _objectSpread2({ expiresIn }, (options === null || options === void 0 ? void 0 : options.transform) ? { transform: options.transform } : {}), { headers: _this8.headers }); const downloadQueryParam = (options === null || options === void 0 ? void 0 : options.download) ? `&download=${options.download === true ? "" : options.download}` : ""; data = { signedUrl: encodeURI(`${_this8.url}${data.signedURL}${downloadQueryParam}`) }; return { data, error: null }; } catch (error) { if (_this8.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * Creates multiple signed URLs. Use a signed URL to share a file for a fixed amount of time. * * @category 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. * @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 * } * ``` */ async createSignedUrls(paths, expiresIn, options) { var _this9 = this; try { const data = await post$1(_this9.fetch, `${_this9.url}/object/sign/${_this9.bucketId}`, { expiresIn, paths }, { headers: _this9.headers }); const downloadQueryParam = (options === null || options === void 0 ? void 0 : options.download) ? `&download=${options.download === true ? "" : options.download}` : ""; return { data: data.map((datum) => _objectSpread2(_objectSpread2({}, datum), {}, { signedUrl: datum.signedURL ? encodeURI(`${_this9.url}${datum.signedURL}${downloadQueryParam}`) : null })), error: null }; } catch (error) { if (_this9.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * Downloads a file from a private bucket. For public buckets, make a request to the URL returned from `getPublicUrl` instead. * * @category File Buckets * @param path The full path and file name of the file to be downloaded. For example `folder/image.png`. * @param options.transform Transform the asset before serving it to the client. * @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 * } * }) * ``` */ download(path, options) { const renderPath = typeof (options === null || options === void 0 ? void 0 : options.transform) !== "undefined" ? "render/image/authenticated" : "object"; const transformationQuery = this.transformOptsToQueryString((options === null || options === void 0 ? void 0 : options.transform) || {}); const queryString = transformationQuery ? `?${transformationQuery}` : ""; const _path = this._getFinalPath(path); const downloadFn = () => get(this.fetch, `${this.url}/${renderPath}/${_path}${queryString}`, { headers: this.headers, noResolveJson: true }); return new BlobDownloadBuilder(downloadFn, this.shouldThrowOnError); } /** * Retrieves the details of an existing file. * * @category File Buckets * @param path The file path, including the file name. For example `folder/image.png`. * @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') * ``` */ async info(path) { var _this10 = this; const _path = _this10._getFinalPath(path); try { return { data: recursiveToCamel(await get(_this10.fetch, `${_this10.url}/object/info/${_path}`, { headers: _this10.headers })), error: null }; } catch (error) { if (_this10.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * Checks the existence of a file. * * @category 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) { var _this11 = this; const _path = _this11._getFinalPath(path); try { await head(_this11.fetch, `${_this11.url}/object/${_path}`, { headers: _this11.headers }); return { data: true, error: null }; } catch (error) { if (_this11.shouldThrowOnError) throw error; if (isStorageError(error) && error instanceof StorageUnknownError) { const originalError = error.originalError; if ([400, 404].includes(originalError === null || originalError === void 0 ? void 0 : originalError.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 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. * @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, * }) * ``` */ getPublicUrl(path, options) { const _path = this._getFinalPath(path); const _queryString = []; const downloadQueryParam = (options === null || options === void 0 ? void 0 : options.download) ? `download=${options.download === true ? "" : options.download}` : ""; if (downloadQueryParam !== "") _queryString.push(downloadQueryParam); const renderPath = typeof (options === null || options === void 0 ? void 0 : options.transform) !== "undefined" ? "render/image" : "object"; const transformationQuery = this.transformOptsToQueryString((options === null || options === void 0 ? void 0 : options.transform) || {}); if (transformationQuery !== "") _queryString.push(transformationQuery); let queryString = _queryString.join("&"); if (queryString !== "") queryString = `?${queryString}`; return { data: { publicUrl: encodeURI(`${this.url}/${renderPath}/public/${_path}${queryString}`) } }; } /** * Deletes files within the same bucket * * @category File Buckets * @param paths An array of files to delete, including the path and file name. For example [`'folder/image.png'`]. * @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 * } * ``` */ async remove(paths) { var _this12 = this; try { return { data: await remove(_this12.fetch, `${_this12.url}/object/${_this12.bucketId}`, { prefixes: paths }, { headers: _this12.headers }), error: null }; } catch (error) { if (_this12.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * Get file metadata * @param id the file id to retrieve metadata */ /** * Update file metadata * @param id the file id to update metadata * @param meta the new file metadata */ /** * Lists all the files and folders within a path of the bucket. * * @category 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 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' }, * }) * ``` * * 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' * }) * ``` */ async list(path, options, parameters) { var _this13 = this; try { const body = _objectSpread2(_objectSpread2(_objectSpread2({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path || "" }); return { data: await post$1(_this13.fetch, `${_this13.url}/object/list/${_this13.bucketId}`, body, { headers: _this13.headers }, parameters), error: null }; } catch (error) { if (_this13.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * @experimental this method signature might change in the future * * @category File Buckets * @param options search options * @param parameters */ async listV2(options, parameters) { var _this14 = this; try { const body = _objectSpread2({}, options); return { data: await post$1(_this14.fetch, `${_this14.url}/object/list-v2/${_this14.bucketId}`, body, { headers: _this14.headers }, parameters), error: null }; } catch (error) { if (_this14.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } encodeMetadata(metadata) { return JSON.stringify(metadata); } toBase64(data) { if (typeof Buffer !== "undefined") return Buffer.from(data).toString("base64"); return btoa(data); } _getFinalPath(path) { return `${this.bucketId}/${path.replace(/^\/+/, "")}`; } _removeEmptyFolders(path) { return path.replace(/^\/|\/$/g, "").replace(/\/+/g, "/"); } transformOptsToQueryString(transform) { const params = []; if (transform.width) params.push(`width=${transform.width}`); if (transform.height) params.push(`height=${transform.height}`); if (transform.resize) params.push(`resize=${transform.resize}`); if (transform.format) params.push(`format=${transform.format}`); if (transform.quality) params.push(`quality=${transform.quality}`); return params.join("&"); } }; //#endregion //#region src/lib/version.ts const version = "2.89.0"; //#endregion //#region src/lib/constants.ts const DEFAULT_HEADERS$1 = { "X-Client-Info": `storage-js/${version}` }; //#endregion //#region src/packages/StorageBucketApi.ts var StorageBucketApi = class { constructor(url, headers = {}, fetch$1, opts) { this.shouldThrowOnError = false; const baseUrl = new URL(url); if (opts === null || opts === void 0 ? void 0 : opts.useNewHostname) { if (/supabase\.(co|in|red)$/.test(baseUrl.hostname) && !baseUrl.hostname.includes("storage.supabase.")) baseUrl.hostname = baseUrl.hostname.replace("supabase.", "storage.supabase."); } this.url = baseUrl.href.replace(/\/$/, ""); this.headers = _objectSpread2(_objectSpread2({}, DEFAULT_HEADERS$1), headers); this.fetch = resolveFetch$1(fetch$1); } /** * Enable throwing errors instead of returning them. * * @category File Buckets */ throwOnError() { this.shouldThrowOnError = true; return this; } /** * Retrieves the details of all Storage buckets within an existing project. * * @category File Buckets * @param options Query parameters for listing buckets * @param options.limit Maximum number of buckets to return * @param options.offset Number of buckets to skip * @param options.sortColumn Column to sort by ('id', 'name', 'created_at', 'updated_at') * @param options.sortOrder Sort order ('asc' or 'desc') * @param options.search Search term to filter bucket names * @returns Promise with response containing array of buckets or error * * @example List buckets * ```js * const { data, error } = await supabase * .storage * .listBuckets() * ``` * * @example List buckets with options * ```js * const { data, error } = await supabase * .storage * .listBuckets({ * limit: 10, * offset: 0, * sortColumn: 'created_at', * sortOrder: 'desc', * search: 'prod' * }) * ``` */ async listBuckets(options) { var _this = this; try { const queryString = _this.listBucketOptionsToQueryString(options); return { data: await get(_this.fetch, `${_this.url}/bucket${queryString}`, { headers: _this.headers }), error: null }; } catch (error) { if (_this.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * Retrieves the details of an existing Storage bucket. * * @category File Buckets * @param id The unique identifier of the bucket you would like to retrieve. * @returns Promise with response containing bucket details or error * * @example Get bucket * ```js * const { data, error } = await supabase * .storage * .getBucket('avatars') * ``` * * Response: * ```json * { * "data": { * "id": "avatars", * "name": "avatars", * "owner": "", * "public": false, * "file_size_limit": 1024, * "allowed_mime_types": [ * "image/png" * ], * "created_at": "2024-05-22T22:26:05.100Z", * "updated_at": "2024-05-22T22:26:05.100Z" * }, * "error": null * } * ``` */ async getBucket(id) { var _this2 = this; try { return { data: await get(_this2.fetch, `${_this2.url}/bucket/${id}`, { headers: _this2.headers }), error: null }; } catch (error) { if (_this2.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * Creates a new Storage bucket * * @category File Buckets * @param id A unique identifier for the bucket you are creating. * @param options.public The visibility of the bucket. Public buckets don't require an authorization token to download objects, but still require a valid token for all other operations. By default, buckets are private. * @param options.fileSizeLimit specifies the max file size in bytes that can be uploaded to this bucket. * The global file size limit takes precedence over this value. * The default value is null, which doesn't set a per bucket file size limit. * @param options.allowedMimeTypes specifies the allowed mime types that this bucket can accept during upload. * The default value is null, which allows files with all mime types to be uploaded. * Each mime type specified can be a wildcard, e.g. image/*, or a specific mime type, e.g. image/png. * @param options.type (private-beta) specifies the bucket type. see `BucketType` for more details. * - default bucket type is `STANDARD` * @returns Promise with response containing newly created bucket name or error * * @example Create bucket * ```js * const { data, error } = await supabase * .storage * .createBucket('avatars', { * public: false, * allowedMimeTypes: ['image/png'], * fileSizeLimit: 1024 * }) * ``` * * Response: * ```json * { * "data": { * "name": "avatars" * }, * "error": null * } * ``` */ async createBucket(id, options = { public: false }) { var _this3 = this; try { return { data: await post$1(_this3.fetch, `${_this3.url}/bucket`, { id, name: id, type: options.type, public: options.public, file_size_limit: options.fileSizeLimit, allowed_mime_types: options.allowedMimeTypes }, { headers: _this3.headers }), error: null }; } catch (error) { if (_this3.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * Updates a Storage bucket * * @category File Buckets * @param id A unique identifier for the bucket you are updating. * @param options.public The visibility of the bucket. Public buckets don't require an authorization token to download objects, but still require a valid token for all other operations. * @param options.fileSizeLimit specifies the max file size in bytes that can be uploaded to this bucket. * The global file size limit takes precedence over this value. * The default value is null, which doesn't set a per bucket file size limit. * @param options.allowedMimeTypes specifies the allowed mime types that this bucket can accept during upload. * The default value is null, which allows files with all mime types to be uploaded. * Each mime type specified can be a wildcard, e.g. image/*, or a specific mime type, e.g. image/png. * @returns Promise with response containing success message or error * * @example Update bucket * ```js * const { data, error } = await supabase * .storage * .updateBucket('avatars', { * public: false, * allowedMimeTypes: ['image/png'], * fileSizeLimit: 1024 * }) * ``` * * Response: * ```json * { * "data": { * "message": "Successfully updated" * }, * "error": null * } * ``` */ async updateBucket(id, options) { var _this4 = this; try { return { data: await put(_this4.fetch, `${_this4.url}/bucket/${id}`, { id, name: id, public: options.public, file_size_limit: options.fileSizeLimit, allowed_mime_types: options.allowedMimeTypes }, { headers: _this4.headers }), error: null }; } catch (error) { if (_this4.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * Removes all objects inside a single bucket. * * @category File Buckets * @param id The unique identifier of the bucket you would like to empty. * @returns Promise with success message or error * * @example Empty bucket * ```js * const { data, error } = await supabase * .storage * .emptyBucket('avatars') * ``` * * Response: * ```json * { * "data": { * "message": "Successfully emptied" * }, * "error": null * } * ``` */ async emptyBucket(id) { var _this5 = this; try { return { data: await post$1(_this5.fetch, `${_this5.url}/bucket/${id}/empty`, {}, { headers: _this5.headers }), error: null }; } catch (error) { if (_this5.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * Deletes an existing bucket. A bucket can't be deleted with existing objects inside it. * You must first `empty()` the bucket. * * @category File Buckets * @param id The unique identifier of the bucket you would like to delete. * @returns Promise with success message or error * * @example Delete bucket * ```js * const { data, error } = await supabase * .storage * .deleteBucket('avatars') * ``` * * Response: * ```json * { * "data": { * "message": "Successfully deleted" * }, * "error": null * } * ``` */ async deleteBucket(id) { var _this6 = this; try { return { data: await remove(_this6.fetch, `${_this6.url}/bucket/${id}`, {}, { headers: _this6.headers }), error: null }; } catch (error) { if (_this6.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } listBucketOptionsToQueryString(options) { const params = {}; if (options) { if ("limit" in options) params.limit = String(options.limit); if ("offset" in options) params.offset = String(options.offset); if (options.search) params.search = options.search; if (options.sortColumn) params.sortColumn = options.sortColumn; if (options.sortOrder) params.sortOrder = options.sortOrder; } return Object.keys(params).length > 0 ? "?" + new URLSearchParams(params).toString() : ""; } }; //#endregion //#region src/packages/StorageAnalyticsClient.ts /** * Client class for managing Analytics Buckets using Iceberg tables * Provides methods for creating, listing, and deleting analytics buckets */ var StorageAnalyticsClient = class { /** * @alpha * * Creates a new StorageAnalyticsClient instance * * **Public alpha:** This API is part of a public alpha release and may not be available to your account type. * * @category Analytics Buckets * @param url - The base URL for the storage API * @param headers - HTTP headers to include in requests * @param fetch - Optional custom fetch implementation * * @example * ```typescript * const client = new StorageAnalyticsClient(url, headers) * ``` */ constructor(url, headers = {}, fetch$1) { this.shouldThrowOnError = false; this.url = url.replace(/\/$/, ""); this.headers = _objectSpread2(_objectSpread2({}, DEFAULT_HEADERS$1), headers); this.fetch = resolveFetch$1(fetch$1); } /** * @alpha * * Enable throwing errors instead of returning them in the response * When enabled, failed operations will throw instead of returning { data: null, error } * * **Public alpha:** This API is part of a public alpha release and may not be available to your account type. * * @category Analytics Buckets * @returns This instance for method chaining */ throwOnError() { this.shouldThrowOnError = true; return this; } /** * @alpha * * Creates a new analytics bucket using Iceberg tables * Analytics buckets are optimized for analytical queries and data processing * * **Public alpha:** This API is part of a public alpha release and may not be available to your account type. * * @category Analytics Buckets * @param name A unique name for the bucket you are creating * @returns Promise with response containing newly created analytics bucket or error * * @example Create analytics bucket * ```js * const { data, error } = await supabase * .storage * .analytics * .createBucket('analytics-data') * ``` * * Response: * ```json * { * "data": { * "name": "analytics-data", * "type": "ANALYTICS", * "format": "iceberg", * "created_at": "2024-05-22T22:26:05.100Z", * "updated_at": "2024-05-22T22:26:05.100Z" * }, * "error": null * } * ``` */ async createBucket(name) { var _this = this; try { return { data: await post$1(_this.fetch, `${_this.url}/bucket`, { name }, { headers: _this.headers }), error: null }; } catch (error) { if (_this.shouldThrowOnError) throw error; if (isStorageError(error)) return { data: null, error }; throw error; } } /** * @alpha * * Retrieves the details of all Analytics Storage buckets within an existing project * Only returns buckets of type 'ANALYTICS' * * **Public alpha:** This