UNPKG

@nuxthub/core

Version:

Build full-stack Nuxt applications, with zero configuration.

185 lines (184 loc) • 6.08 kB
import { put as vercelPut, del as vercelDel, head as vercelHead, list as vercelList, createMultipartUpload as vercelCreateMultipartUpload, uploadPart as vercelUploadPart, completeMultipartUpload as vercelCompleteMultipartUpload } from "@vercel/blob"; import { handleUpload } from "@vercel/blob/client"; import { createError, readBody } from "h3"; import { getContentType } from "../utils.mjs"; function mapVercelBlobToBlob(blob) { return { pathname: blob.pathname, contentType: ("contentType" in blob ? blob.contentType : void 0) || getContentType(blob.pathname), size: "size" in blob ? blob.size : void 0, httpEtag: void 0, uploadedAt: "uploadedAt" in blob ? blob.uploadedAt : /* @__PURE__ */ new Date(), httpMetadata: {}, customMetadata: {}, url: blob.url }; } export function createDriver(options = {}) { const token = options.token || process.env.BLOB_READ_WRITE_TOKEN; const access = options.access || "public"; return { name: "vercel-blob", options, async list(listOptions) { const result = await vercelList({ token, limit: listOptions?.limit ?? 1e3, prefix: listOptions?.prefix, cursor: listOptions?.cursor, mode: listOptions?.folded ? "folded" : "expanded" }); return { blobs: result.blobs.map(mapVercelBlobToBlob), hasMore: result.hasMore, cursor: result.cursor, folders: "folders" in result ? result.folders : void 0 }; }, async get(pathname) { try { const headResult = await vercelHead(decodeURIComponent(pathname), { token }); if (!headResult) return null; const res = await fetch(headResult.url); if (!res.ok) return null; return res.blob(); } catch { return null; } }, async getArrayBuffer(pathname) { try { const headResult = await vercelHead(decodeURIComponent(pathname), { token }); if (!headResult) return null; const res = await fetch(headResult.url); if (!res.ok) return null; return res.arrayBuffer(); } catch { return null; } }, async put(pathname, body, putOptions) { const contentType = putOptions?.contentType || (body instanceof Blob ? body.type : void 0) || getContentType(pathname); if (putOptions?.access === "private") { throw createError({ statusCode: 400, statusMessage: "Private access is not yet supported for Vercel Blob" }); } const result = await vercelPut(pathname, body, { token, access, contentType // Note: Vercel Blob doesn't support custom metadata in the same way }); return mapVercelBlobToBlob(result); }, async head(pathname) { try { const result = await vercelHead(decodeURIComponent(pathname), { token }); if (!result) return null; return mapVercelBlobToBlob(result); } catch { return null; } }, async hasItem(pathname) { try { const result = await vercelHead(decodeURIComponent(pathname), { token }); return result !== null; } catch { return false; } }, async delete(pathnames) { const paths = Array.isArray(pathnames) ? pathnames : [pathnames]; for (const pathname of paths) { try { const headResult = await vercelHead(decodeURIComponent(pathname), { token }); if (headResult) { await vercelDel(headResult.url, { token }); } } catch { } } }, async createMultipartUpload(pathname, mpuOptions) { const { key, uploadId } = await vercelCreateMultipartUpload(pathname, { access, token, contentType: mpuOptions?.contentType || getContentType(pathname) }); return { pathname, uploadId, async uploadPart(partNumber, value) { return vercelUploadPart(pathname, value, { access, token, contentType: mpuOptions?.contentType || getContentType(pathname), uploadId, key, partNumber }); }, async abort() { }, async complete(uploadedParts) { const result = await vercelCompleteMultipartUpload(pathname, uploadedParts, { access, token, contentType: mpuOptions?.contentType || getContentType(pathname), uploadId, key }); return mapVercelBlobToBlob(result); } }; }, async resumeMultipartUpload(pathname, uploadId) { return { pathname, uploadId, async uploadPart(partNumber, value) { return vercelUploadPart(pathname, value, { access, token, uploadId, partNumber, key: pathname }); }, async abort() { }, async complete(uploadedParts) { const result = await vercelCompleteMultipartUpload(pathname, uploadedParts, { access, token, uploadId, key: pathname }); return mapVercelBlobToBlob(result); } }; }, /** * Vercel-specific multipart upload handler using @vercel/blob/client */ async handleMultipartUpload(event, mpuOptions) { const body = await readBody(event); if (!body || typeof body.type !== "string") { throw createError({ statusCode: 400, message: "Invalid multipart upload request body" }); } const json = await handleUpload({ body, request: event.node.req, onBeforeGenerateToken: async (pathname, clientPayload) => { const result = mpuOptions?.onBeforeGenerateToken ? mpuOptions?.onBeforeGenerateToken?.(pathname, clientPayload) : {}; return { ...mpuOptions, ...result }; }, onUploadCompleted: mpuOptions?.onUploadCompleted || void 0 }); return json; } }; }