UNPKG

@squarecloud/blob

Version:
518 lines (498 loc) 17.1 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; // src/index.ts var src_exports = {}; __export(src_exports, { BlobObject: () => BlobObject, MimeTypeUtil: () => MimeTypeUtil, MimeTypes: () => MimeTypes, SquareCloudBlob: () => SquareCloudBlob }); module.exports = __toCommonJS(src_exports); // src/structures/error.ts var SquareCloudBlobError = class _SquareCloudBlobError extends Error { constructor(code, message, cause) { super(message, { cause }); this.name = _SquareCloudBlobError.name; this.message = this.getMessage(code); } getMessage(rawCode) { const code = rawCode.replaceAll("_", " ").toLowerCase().replace(/(^|\s)\S/g, (L) => L.toUpperCase()); const message = this.message ? `: ${this.message}` : ""; return `${code}${message}`; } }; // src/managers/api.ts var APIManager = class { constructor(apiKey) { this.apiKey = apiKey; __publicField(this, "baseUrl"); const { baseUrl, version } = SquareCloudBlob.apiInfo; this.baseUrl = `${baseUrl}/${version}/`; } async request(path, options = {}) { const { init, params } = this.parseOptions(options); const url = new URL(`${path}?${params}`, this.baseUrl); const response = await fetch(url, init); const data = await response.json(); if (data.status === "error") { throw new SquareCloudBlobError(data.code || "UNKNOWN_ERROR"); } return data; } parseOptions(options) { const paramsObject = options.params && Object.fromEntries( Object.entries(options.params).filter(([, value]) => Boolean(value)).map(([key, value]) => [key, String(value)]) ); const params = new URLSearchParams(paramsObject); const { params: _, headers, body, ...rest } = options; const init = { ...rest, headers: { ...headers || {}, Authorization: this.apiKey } }; if (body) { init.body = body instanceof FormData ? body : JSON.stringify(body); } return { init, params }; } }; // src/utils/mimetype/mimetypes.ts var mimeTypesWithExtension = { "video/mp4": ["mp4"], "video/mpeg": ["mpeg"], "video/webm": ["webm"], "video/x-flv": ["flv"], "video/x-m4v": ["m4v"], "image/jpeg": ["jpg", "jpeg"], "image/png": ["png"], "image/apng": ["apng"], "image/tiff": ["tiff"], "image/gif": ["gif"], "image/webp": ["webp"], "image/bmp": ["bmp"], "image/svg+xml": ["svg"], "image/x-icon": ["ico"], "image/ico": ["ico"], "image/cur": ["cur"], "image/heic": ["heic"], "image/heif": ["heif"], "audio/wav": ["wav"], "audio/ogg": ["ogg"], "audio/opus": ["opus"], "audio/mp4": ["mp4"], "audio/mpeg": ["mp3"], "audio/aac": ["aac"], "text/plain": ["txt"], "text/html": ["html"], "text/css": ["css"], "text/csv": ["csv"], "text/x-sql": ["sql"], "application/xml": ["xml"], "application/sql": ["sql"], "application/x-sql": ["sql"], "application/x-sqlite3": ["sqlite3"], "application/x-pkcs12": ["pfx"], "application/pdf": ["pdf"], "application/json": ["json"], "application/javascript": ["js"] }; var mimeTypes = Object.keys(mimeTypesWithExtension); // src/utils/mimetype/enum.ts var MimeTypes = /* @__PURE__ */ ((MimeTypes2) => { MimeTypes2["VIDEO_MP4"] = "video/mp4"; MimeTypes2["VIDEO_MPEG"] = "video/mpeg"; MimeTypes2["VIDEO_WEBM"] = "video/webm"; MimeTypes2["VIDEO_X_FLV"] = "video/x-flv"; MimeTypes2["VIDEO_X_M4V"] = "video/x-m4v"; MimeTypes2["IMAGE_JPEG"] = "image/jpeg"; MimeTypes2["IMAGE_PNG"] = "image/png"; MimeTypes2["IMAGE_APNG"] = "image/apng"; MimeTypes2["IMAGE_TIFF"] = "image/tiff"; MimeTypes2["IMAGE_GIF"] = "image/gif"; MimeTypes2["IMAGE_WEBP"] = "image/webp"; MimeTypes2["IMAGE_BMP"] = "image/bmp"; MimeTypes2["IMAGE_SVG"] = "image/svg+xml"; MimeTypes2["IMAGE_X_ICON"] = "image/x-icon"; MimeTypes2["IMAGE_ICO"] = "image/ico"; MimeTypes2["IMAGE_CUR"] = "image/cur"; MimeTypes2["IMAGE_HEIC"] = "image/heic"; MimeTypes2["IMAGE_HEIF"] = "image/heif"; MimeTypes2["AUDIO_WAV"] = "audio/wav"; MimeTypes2["AUDIO_OGG"] = "audio/ogg"; MimeTypes2["AUDIO_OPUS"] = "audio/opus"; MimeTypes2["AUDIO_MP4"] = "audio/mp4"; MimeTypes2["AUDIO_MPEG"] = "audio/mpeg"; MimeTypes2["AUDIO_AAC"] = "audio/aac"; MimeTypes2["TEXT_PLAIN"] = "text/plain"; MimeTypes2["TEXT_HTML"] = "text/html"; MimeTypes2["TEXT_CSS"] = "text/css"; MimeTypes2["TEXT_CSV"] = "text/csv"; MimeTypes2["TEXT_X_SQL"] = "text/x-sql"; MimeTypes2["APPLICATION_XML"] = "application/xml"; MimeTypes2["APPLICATION_SQL"] = "application/sql"; MimeTypes2["APPLICATION_X_SQL"] = "application/x-sql"; MimeTypes2["APPLICATION_X_SQLITE3"] = "application/x-sqlite3"; MimeTypes2["APPLICATION_X_PKCS12"] = "application/x-pkcs12"; MimeTypes2["APPLICATION_PDF"] = "application/pdf"; MimeTypes2["APPLICATION_JSON"] = "application/json"; MimeTypes2["APPLICATION_JAVASCRIPT"] = "application/javascript"; return MimeTypes2; })(MimeTypes || {}); // src/utils/mimetype/index.ts var MimeTypeUtil = class { /** * Returns the corresponding MIME type for a given file extension. * * @param extension - The file extension to search for. * @return The MIME type associated with the extension, or "text/plain" if not found. * * @example * ```js * MimeTypeUtil.fromExtension("jpeg") // "image/jpeg" | Supported * MimeTypeUtil.fromExtension("json") // "application/json" | Supported * MimeTypeUtil.fromExtension("potato") // "text/plain" | Unsupported, defaults to text/plain * ``` */ static fromExtension(extension) { const entries = Object.entries(mimeTypesWithExtension); const mimeType = entries.find( ([, extensions]) => extensions.includes(extension) )?.[0]; return mimeType || "text/plain"; } }; /** Supported mime types with their extensions */ __publicField(MimeTypeUtil, "mimeTypesWithExtension", mimeTypesWithExtension); /** All supported mime types */ __publicField(MimeTypeUtil, "mimeTypes", mimeTypes); // src/utils/object-url.ts var objectUrlRegex = /^(?<url>https:\/\/public-blob\.squarecloud\.dev)?\/?(?<userId>\d+\/)(?<prefix>[\w\d-_]+\/)?(?<name>[\w\d_]+)-(?<hash>[\w\d]+)\.(?<extension>\w+)$/; function parseObjectUrl(url) { const match = url.match(objectUrlRegex); if (!match?.groups) { throw new SquareCloudBlobError("Invalid object URL"); } const payload = { userId: match.groups.userId.replace("/", ""), prefix: match.groups.prefix?.replace("/", ""), name: match.groups.name, hash: match.groups.hash, extension: match.groups.extension }; return { id: `${payload.userId}/${payload.prefix ? `${payload.prefix}/` : ""}${payload.name}-${payload.hash}.${payload.extension}`, ...payload }; } // src/structures/object.ts var BlobObject = class { constructor(data) { /** The id of the object */ __publicField(this, "id"); /** The url to view or download the object */ __publicField(this, "url"); /** The name of the object */ __publicField(this, "name"); /** The prefix of the object (Optional) */ __publicField(this, "prefix"); /** The hash of the object */ __publicField(this, "hash"); /** The id of the user who created the object */ __publicField(this, "userId"); /** The file extension of the object */ __publicField(this, "extension"); /** The MIME type of the object */ __publicField(this, "mimeType"); /** The size of the object in bytes */ __publicField(this, "size"); /** The expiration date of the object (Only available using `objects.list`) */ __publicField(this, "expiresAt"); /** The creation date of the object (Only available using `objects.list`) */ __publicField(this, "createdAt"); const { id, name, prefix, hash, userId, extension } = parseObjectUrl( data.idOrUrl ); this.id = id; this.url = `https://public-blob.squarecloud.dev/${id}`; this.name = name; this.prefix = prefix; this.hash = hash; this.userId = userId; this.extension = extension; this.mimeType = MimeTypeUtil.fromExtension(extension); this.size = data.size; this.expiresAt = data.expiresAt; this.createdAt = data.createdAt; } }; // src/utils/path-like.ts var import_promises = require("fs/promises"); async function parsePathLike(pathLike) { if (typeof pathLike === "string") { const fileBuffer = await (0, import_promises.readFile)(pathLike).catch(() => void 0); if (!fileBuffer) { throw new SquareCloudBlobError("INVALID_FILE", "File not found"); } return fileBuffer; } return pathLike; } // src/validation/schemas/create.ts var import_zod2 = require("zod"); // src/validation/schemas/common.ts var import_zod = require("zod"); var stringSchema = import_zod.z.string(); var nameLikeSchema = import_zod.z.string().min(3).max(32).regex(/^[a-zA-Z0-9_]{3,32}$/, { message: "Name must contain only letters, numbers and _" }); // src/validation/schemas/create.ts var createObjectSchema = import_zod2.z.object({ /** A string representing the name for the file. */ name: nameLikeSchema, /** Use absolute path, Buffer or Blob */ file: import_zod2.z.string().or(import_zod2.z.instanceof(Buffer)), /** A string representing the MIME type of the file. */ mimeType: import_zod2.z.enum(MimeTypeUtil.mimeTypes).optional(), /** A string representing the prefix for the file. */ prefix: nameLikeSchema.optional(), /** A number indicating the expiration period of the file, ranging from 1 to 365 days. */ expiresIn: import_zod2.z.number().min(1).max(365).optional(), /** Set to true if a security hash is required. */ securityHash: import_zod2.z.boolean().optional(), /** Set to true if the file should be set for automatic download. */ autoDownload: import_zod2.z.boolean().optional() }).refine(({ file, mimeType }) => !(file instanceof Buffer && !mimeType), { message: "mimeType is required if file is a Buffer", path: ["mimeType"] }); var createObjectPayloadSchema = createObjectSchema.transform( ({ file, securityHash, autoDownload, expiresIn, ...rest }) => ({ file, mimeType: typeof file === "string" ? MimeTypeUtil.fromExtension(file.split(".")[1]) : void 0, params: { ...rest, expire: expiresIn, security_hash: securityHash, auto_download: autoDownload } }) ); var createObjectResponseSchema = import_zod2.z.object({ /** The id of the uploaded file. */ id: import_zod2.z.string(), /** The name of the uploaded file. */ name: import_zod2.z.string(), /** The prefix of the uploaded file. */ prefix: import_zod2.z.string().optional(), /** The size of the uploaded file. */ size: import_zod2.z.number(), /** The URL of the uploaded file. (File distributed in Square Cloud CDN) */ url: import_zod2.z.string() }); // src/validation/assertions/handlers.ts function handleAPIObjectAssertion({ schema, value, code }) { const name = code.toLowerCase().replaceAll("_", " "); try { return schema.parse(value); } catch (err) { const cause = err.errors?.map((err2) => ({ ...err2, path: err2.path.join(" > ") })); throw new SquareCloudBlobError( `INVALID_API_${code}`, `Invalid ${name} object received from API`, { cause } ); } } // src/validation/assertions/create.ts function assertCreateObjectResponse(value) { return handleAPIObjectAssertion({ schema: createObjectResponseSchema, code: "CREATE_OBJECT", value }); } // src/validation/schemas/list.ts var import_zod3 = require("zod"); var listObjectsSchema = import_zod3.z.object({ /** Filter by prefix */ prefix: import_zod3.z.string().optional(), /** Return objects after this token */ continuationToken: import_zod3.z.string().optional() }); var listObjectsPayloadSchema = listObjectsSchema.optional().transform((params) => ({ params })); var listObjectResponseSchema = import_zod3.z.object({ id: import_zod3.z.string(), size: import_zod3.z.number(), created_at: import_zod3.z.coerce.date(), expires_at: import_zod3.z.coerce.date().optional() }); var listObjectsResponseSchema = import_zod3.z.object({ objects: import_zod3.z.array(listObjectResponseSchema).default([]) }); // src/validation/assertions/list.ts function assertListObjectsResponse(value) { return handleAPIObjectAssertion({ schema: listObjectsResponseSchema, code: "LIST_OBJECTS", value }); } // src/managers/objects.ts var ObjectsManager = class { constructor(client) { this.client = client; } /** * Lists all objects in the storage. * * @example * ```js * blob.objects.list(); * ``` */ async list(options) { const payload = listObjectsPayloadSchema.parse(options); const { response } = await this.client.api.request( "objects", { params: payload.params } ); const { objects } = assertListObjectsResponse(response); return objects.map((objectData) => { const createdAt = new Date(objectData.created_at); const expiresAt = objectData.expires_at ? new Date(objectData.expires_at) : void 0; return new BlobObject({ idOrUrl: objectData.id, size: objectData.size, createdAt, expiresAt }); }); } /** * Uploads an object to the storage. * * @param object - An object to upload * * @example * ```js * // Basic usage with absolute path * blob.objects.create({ * file: "path/to/file.jpeg", * name: "my_image" * }); * * // Advanced usage with Buffer * blob.objects.create({ * file: Buffer.from("content"), * name: "my_image", * mimeType: "image/jpeg" * }) * ``` */ async create(object) { const payload = createObjectPayloadSchema.parse(object); const file = await parsePathLike(payload.file); const type = payload.mimeType || object.mimeType; const formData = new FormData(); formData.append("file", new Blob([file], { type })); const { response } = await this.client.api.request( "objects", { method: "POST", body: formData, params: payload.params } ); const objectData = assertCreateObjectResponse(response); return new BlobObject({ idOrUrl: objectData.id, size: objectData.size }); } /** * Delete an object from the storage. * * @param object - An array of object IDs * * @example * ```js * blob.object.delete("userId/prefix/name1_xxx-xxx.mp4"); * ``` */ async delete(object) { const { status } = await this.client.api.request("objects", { method: "DELETE", body: { object } }); return status === "success"; } }; // src/validation/schemas/stats.ts var import_zod4 = require("zod"); var statsResponseSchema = import_zod4.z.object({ /** The total number of objects in your account. */ objects: import_zod4.z.number(), /** The total size of all objects in your account, in bytes. */ size: import_zod4.z.number(), /** The total price of storage for all objects in your account, in BRL. */ storagePrice: import_zod4.z.number(), /** The total price of all objects in your account, in BRL. */ objectsPrice: import_zod4.z.number(), /** The total price of all objects in your account, in BRL. */ totalEstimate: import_zod4.z.number() }); // src/validation/assertions/stats.ts function assertStatsResponse(value) { return handleAPIObjectAssertion({ schema: statsResponseSchema, code: "ACCOUNT_STATS", value }); } // src/index.ts var SquareCloudBlob = class { constructor(apiKey) { __publicField(this, "api"); __publicField(this, "objects", new ObjectsManager(this)); this.api = new APIManager(apiKey); } async stats() { const { response } = await this.api.request("account/stats"); return assertStatsResponse(response); } }; __publicField(SquareCloudBlob, "apiInfo", { baseUrl: "https://blob.squarecloud.app", version: "v1" }); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { BlobObject, MimeTypeUtil, MimeTypes, SquareCloudBlob }); //# sourceMappingURL=index.js.map