@vercel/blob 2.1.0-371428d-20260119152220 → 2.1.0-454414f-20260204161813

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/helpers.ts","../src/multipart/helpers.ts","../src/bytes.ts","../src/api.ts","../src/debug.ts","../src/dom-exception.ts","../src/is-network-error.ts","../src/fetch.ts","../src/xhr.ts","../src/request.ts","../src/put-helpers.ts","../src/multipart/complete.ts","../src/multipart/create.ts","../src/multipart/upload.ts","../src/multipart/create-uploader.ts","../src/put.ts","../src/multipart/uncontrolled.ts","../src/create-folder.ts"],"sourcesContent":["// common util interface for blob raw commands, not meant to be used directly\n// this is why it's not exported from index/client\n\nimport type { Readable } from 'node:stream';\nimport { isNodeProcess } from 'is-node-process';\nimport type { RequestInit, Response } from 'undici';\nimport { isNodeJsReadableStream } from './multipart/helpers';\nimport type { PutBody } from './put-helpers';\n\nexport { bytes } from './bytes';\n\nconst defaultVercelBlobApiUrl = 'https://vercel.com/api/blob';\n\nexport interface BlobCommandOptions {\n /**\n * Define your blob API token.\n * @defaultvalue process.env.BLOB_READ_WRITE_TOKEN\n */\n token?: string;\n /**\n * `AbortSignal` to cancel the running request. See https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal\n */\n abortSignal?: AbortSignal;\n}\n\n// shared interface for put, copy and multipart upload\nexport interface CommonCreateBlobOptions extends BlobCommandOptions {\n /**\n * Whether the blob should be publicly accessible. The only currently allowed value is `public`.\n */\n access: 'public';\n /**\n * Adds a random suffix to the filename.\n * @defaultvalue false\n */\n addRandomSuffix?: boolean;\n /**\n * Allow overwriting an existing blob. By default this is set to false and will throw an error if the blob already exists.\n * @defaultvalue false\n */\n allowOverwrite?: boolean;\n /**\n * Defines the content type of the blob. By default, this value is inferred from the pathname. Sent as the 'content-type' header when downloading a blob.\n */\n contentType?: string;\n /**\n * Number in seconds to configure the edge and browser cache. The minimum is 1 minute. There's no maximum but keep in mind that browser and edge caches will do a best effort to respect this value.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob#caching\n * @defaultvalue 30 * 24 * 60 * 60 (1 Month)\n */\n cacheControlMaxAge?: number;\n /**\n * Only perform the operation if the blob's current ETag matches this value.\n * Use this for optimistic concurrency control to prevent overwriting changes made by others.\n * If the ETag doesn't match, a `BlobPreconditionFailedError` will be thrown.\n */\n ifMatch?: string;\n}\n\n/**\n * Event object passed to the onUploadProgress callback.\n */\nexport interface UploadProgressEvent {\n /**\n * The number of bytes uploaded.\n */\n loaded: number;\n\n /**\n * The total number of bytes to upload.\n */\n total: number;\n\n /**\n * The percentage of the upload that has been completed.\n */\n percentage: number;\n}\n\n/**\n * Callback type for tracking upload progress.\n */\nexport type OnUploadProgressCallback = (\n progressEvent: UploadProgressEvent,\n) => void;\n\nexport type InternalOnUploadProgressCallback = (loaded: number) => void;\n\nexport type BlobRequestInit = Omit<RequestInit, 'body'> & { body?: PutBody };\n\nexport type BlobRequest = ({\n input,\n init,\n onUploadProgress,\n}: {\n input: string | URL;\n init: BlobRequestInit;\n onUploadProgress?: InternalOnUploadProgressCallback;\n}) => Promise<Response>;\n\n/**\n * Interface for including upload progress tracking capabilities.\n */\nexport interface WithUploadProgress {\n /**\n * Callback to track the upload progress. You will receive an object with the following properties:\n * - `loaded`: The number of bytes uploaded\n * - `total`: The total number of bytes to upload\n * - `percentage`: The percentage of the upload that has been completed\n */\n onUploadProgress?: OnUploadProgressCallback;\n}\n\nexport function getTokenFromOptionsOrEnv(options?: BlobCommandOptions): string {\n if (options?.token) {\n return options.token;\n }\n\n if (process.env.BLOB_READ_WRITE_TOKEN) {\n return process.env.BLOB_READ_WRITE_TOKEN;\n }\n\n throw new BlobError(\n 'No token found. Either configure the `BLOB_READ_WRITE_TOKEN` environment variable, or pass a `token` option to your calls.',\n );\n}\n\nexport class BlobError extends Error {\n constructor(message: string) {\n super(`Vercel Blob: ${message}`);\n }\n}\n\n/**\n * Generates a download URL for a blob.\n * The download URL includes a ?download=1 parameter which causes browsers to download\n * the file instead of displaying it inline.\n *\n * @param blobUrl - The URL of the blob to generate a download URL for\n * @returns A string containing the download URL with the download parameter appended\n */\nexport function getDownloadUrl(blobUrl: string): string {\n const url = new URL(blobUrl);\n\n url.searchParams.set('download', '1');\n\n return url.toString();\n}\n\n// Extracted from https://github.com/sindresorhus/is-plain-obj/blob/main/index.js\n// It's just nearly impossible to use ESM modules with our current setup\nexport function isPlainObject(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const prototype = Object.getPrototypeOf(value);\n return (\n (prototype === null ||\n prototype === Object.prototype ||\n Object.getPrototypeOf(prototype) === null) &&\n !(Symbol.toStringTag in value) &&\n !(Symbol.iterator in value)\n );\n}\n\nexport const disallowedPathnameCharacters = ['//'];\n\n// Chrome: implemented https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests\n// Microsoft Edge: implemented (Chromium)\n// Firefox: not implemented, BOO!! https://bugzilla.mozilla.org/show_bug.cgi?id=1469359\n// Safari: not implemented, BOO!! https://github.com/WebKit/standards-positions/issues/24\nexport const supportsRequestStreams = (() => {\n // The next line is mostly for Node.js 16 to avoid trying to do new Request() as it's not supported\n // TODO: Can be removed when Node.js 16 is no more required internally\n if (isNodeProcess()) {\n return true;\n }\n\n const apiUrl = getApiUrl();\n\n // Localhost generally doesn't work with HTTP 2 so we can stop here\n if (apiUrl.startsWith('http://localhost')) {\n return false;\n }\n\n let duplexAccessed = false;\n\n const hasContentType = new Request(getApiUrl(), {\n body: new ReadableStream(),\n method: 'POST',\n // @ts-expect-error -- TypeScript doesn't yet have duplex but it's in the spec: https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1729\n get duplex() {\n duplexAccessed = true;\n return 'half';\n },\n }).headers.has('Content-Type');\n\n return duplexAccessed && !hasContentType;\n})();\n\nexport function getApiUrl(pathname = ''): string {\n let baseUrl = null;\n try {\n // wrapping this code in a try/catch as this function is used in the browser and Vite doesn't define the process.env.\n // As this varaible is NOT used in production, it will always default to production endpoint\n baseUrl =\n process.env.VERCEL_BLOB_API_URL ||\n process.env.NEXT_PUBLIC_VERCEL_BLOB_API_URL;\n } catch {\n // noop\n }\n\n return `${baseUrl || defaultVercelBlobApiUrl}${pathname}`;\n}\n\nconst TEXT_ENCODER =\n typeof TextEncoder === 'function' ? new TextEncoder() : null;\n\nexport function computeBodyLength(body: PutBody): number {\n if (!body) {\n return 0;\n }\n\n if (typeof body === 'string') {\n if (TEXT_ENCODER) {\n return TEXT_ENCODER.encode(body).byteLength;\n }\n\n // React Native doesn't have TextEncoder\n return new Blob([body]).size;\n }\n\n if ('byteLength' in body && typeof body.byteLength === 'number') {\n // handles Uint8Array, ArrayBuffer, Buffer, and ArrayBufferView\n return body.byteLength;\n }\n\n if ('size' in body && typeof body.size === 'number') {\n // handles Blob and File\n return body.size;\n }\n\n return 0;\n}\n\nexport const createChunkTransformStream = (\n chunkSize: number,\n onProgress?: (bytes: number) => void,\n): TransformStream<ArrayBuffer | Uint8Array> => {\n let buffer = new Uint8Array(0);\n\n return new TransformStream<ArrayBuffer, Uint8Array>({\n transform(chunk, controller) {\n queueMicrotask(() => {\n // Combine the new chunk with any leftover data\n const newBuffer = new Uint8Array(buffer.length + chunk.byteLength);\n newBuffer.set(buffer);\n newBuffer.set(new Uint8Array(chunk), buffer.length);\n buffer = newBuffer;\n\n // Output complete chunks\n while (buffer.length >= chunkSize) {\n const newChunk = buffer.slice(0, chunkSize);\n controller.enqueue(newChunk);\n onProgress?.(newChunk.byteLength);\n buffer = buffer.slice(chunkSize);\n }\n });\n },\n\n flush(controller) {\n queueMicrotask(() => {\n // Send any remaining data\n if (buffer.length > 0) {\n controller.enqueue(buffer);\n onProgress?.(buffer.byteLength);\n }\n });\n },\n });\n};\n\nexport function isReadableStream(value: PutBody): value is ReadableStream {\n return (\n globalThis.ReadableStream && // TODO: Can be removed once Node.js 16 is no more required internally\n value instanceof ReadableStream\n );\n}\n\nexport function isStream(value: PutBody): value is ReadableStream | Readable {\n if (isReadableStream(value)) {\n return true;\n }\n\n if (isNodeJsReadableStream(value)) {\n return true;\n }\n\n return false;\n}\n","import type { Buffer } from 'buffer';\nimport isBuffer from 'is-buffer';\nimport { Readable } from 'stream';\nimport type { PutBody } from '../put-helpers';\n\n/**\n * Input format for a multipart upload part.\n * Used internally for processing multipart uploads.\n */\nexport interface PartInput {\n /**\n * The part number (1-based index).\n */\n partNumber: number;\n\n /**\n * The content of the part.\n */\n blob: PutBody;\n}\n\n/**\n * Represents a single part of a multipart upload.\n * This structure is used when completing a multipart upload to specify the\n * uploaded parts and their order.\n */\nexport interface Part {\n /**\n * The ETag value returned when the part was uploaded.\n * This value is used to verify the integrity of the uploaded part.\n */\n etag: string;\n\n /**\n * The part number of this part (1-based).\n * This number is used to order the parts when completing the multipart upload.\n */\n partNumber: number;\n}\n\nconst supportsNewBlobFromArrayBuffer = new Promise<boolean>((resolve) => {\n // React Native doesn't support creating a Blob from an ArrayBuffer, so we feature detect it\n try {\n const helloAsArrayBuffer = new Uint8Array([104, 101, 108, 108, 111]);\n const blob = new Blob([helloAsArrayBuffer]);\n blob\n .text()\n .then((text) => {\n resolve(text === 'hello');\n })\n .catch(() => {\n resolve(false);\n });\n } catch {\n resolve(false);\n }\n});\n\nexport async function toReadableStream(\n value: PutBody,\n): Promise<ReadableStream<ArrayBuffer | Uint8Array>> {\n // Already a ReadableStream, nothing to do\n if (value instanceof ReadableStream) {\n return value as ReadableStream<ArrayBuffer>;\n }\n\n // In the case of a Blob or File (which inherits from Blob), we could use .slice() to create pointers\n // to the original data instead of loading data in memory gradually.\n // Here's an explanation on this subject: https://stackoverflow.com/a/24834417\n if (value instanceof Blob) {\n return value.stream();\n }\n\n if (isNodeJsReadableStream(value)) {\n return Readable.toWeb(value) as ReadableStream<ArrayBuffer>;\n }\n\n let streamValue: Uint8Array;\n\n // While ArrayBuffer is valid as a fetch body, when used in a ReadableStream it will fail in Node.js with\n // The \"chunk\" argument must be of type string or an instance of Buffer or Uint8Array. Received an instance of ArrayBuffer\n if (value instanceof ArrayBuffer) {\n streamValue = new Uint8Array(value);\n } else if (isNodeJsBuffer(value)) {\n streamValue = value;\n } else {\n // value is a string, we need to convert it to a Uint8Array to get create a stream from it\n streamValue = stringToUint8Array(value as string);\n }\n\n // This line ensures that even when we get a buffer of 70MB, we'll create a stream out of it so we can have\n // better progress indication during uploads\n if (await supportsNewBlobFromArrayBuffer) {\n // @ts-expect-error Uint8Array is compatible with BlobPart at runtime\n return new Blob([streamValue]).stream();\n }\n\n // from https://github.com/sindresorhus/to-readable-stream/blob/main/index.js\n return new ReadableStream<ArrayBuffer | Uint8Array>({\n start(controller) {\n controller.enqueue(streamValue);\n controller.close();\n },\n });\n}\n\n// From https://github.com/sindresorhus/is-stream/\nexport function isNodeJsReadableStream(value: PutBody): value is Readable {\n return (\n typeof value === 'object' &&\n typeof (value as Readable).pipe === 'function' &&\n (value as Readable).readable &&\n typeof (value as Readable)._read === 'function' &&\n // @ts-expect-error _readableState does exists on Readable\n typeof value._readableState === 'object'\n );\n}\n\nfunction stringToUint8Array(s: string): Uint8Array {\n const enc = new TextEncoder();\n return enc.encode(s);\n}\n\nfunction isNodeJsBuffer(value: PutBody): value is Buffer {\n return isBuffer(value);\n}\n","/*!\n * bytes\n * Copyright(c) 2012-2014 TJ Holowaychuk\n * Copyright(c) 2015 Jed Watson\n * MIT Licensed\n */\n\n// from https://github.com/visionmedia/bytes.js/blob/master/index.js\n// had too many issues with bundling: https://github.com/vercel/storage/issues/818\ntype ByteUnit = 'b' | 'kb' | 'mb' | 'gb' | 'tb' | 'pb';\n\ntype ByteUnitMap = {\n readonly [_K in ByteUnit]: number;\n};\n\nconst parseRegExp = /^((-|\\+)?(\\d+(?:\\.\\d+)?)) *(kb|mb|gb|tb|pb)$/i;\n\nconst map: ByteUnitMap = {\n b: 1,\n\n kb: 1 << 10,\n\n mb: 1 << 20,\n\n gb: 1 << 30,\n tb: 1024 ** 4,\n pb: 1024 ** 5,\n};\n\nexport function bytes(val: string | number): number | null {\n if (typeof val === 'number' && !Number.isNaN(val)) {\n return val;\n }\n if (typeof val !== 'string') {\n return null;\n }\n\n const results = parseRegExp.exec(val);\n let floatValue: number;\n let unit: ByteUnit = 'b';\n\n if (!results) {\n floatValue = parseInt(val, 10);\n } else {\n const [, res, , , unitMatch] = results;\n if (!res) {\n return null;\n }\n floatValue = parseFloat(res);\n if (unitMatch) {\n unit = unitMatch.toLowerCase() as ByteUnit;\n }\n }\n\n if (Number.isNaN(floatValue)) {\n return null;\n }\n\n return Math.floor(map[unit] * floatValue);\n}\n","import retry from 'async-retry';\nimport type { Response } from 'undici';\nimport { debug } from './debug';\nimport { DOMException } from './dom-exception';\nimport type {\n BlobCommandOptions,\n BlobRequestInit,\n WithUploadProgress,\n} from './helpers';\nimport {\n BlobError,\n computeBodyLength,\n getApiUrl,\n getTokenFromOptionsOrEnv,\n} from './helpers';\nimport isNetworkError from './is-network-error';\nimport { blobRequest } from './request';\n\n// maximum pathname length is:\n// 1024 (provider limit) - 26 chars (vercel internal suffixes) - 31 chars (blob `-randomId` suffix) = 967\n// we round it to 950 to make it more human friendly, and we apply the limit whatever the value of\n// addRandomSuffix is, to make it consistent\nexport const MAXIMUM_PATHNAME_LENGTH = 950;\n\nexport class BlobAccessError extends BlobError {\n constructor() {\n super('Access denied, please provide a valid token for this resource.');\n }\n}\n\nexport class BlobContentTypeNotAllowedError extends BlobError {\n constructor(message: string) {\n super(`Content type mismatch, ${message}.`);\n }\n}\n\nexport class BlobPathnameMismatchError extends BlobError {\n constructor(message: string) {\n super(\n `Pathname mismatch, ${message}. Check the pathname used in upload() or put() matches the one from the client token.`,\n );\n }\n}\n\nexport class BlobClientTokenExpiredError extends BlobError {\n constructor() {\n super('Client token has expired.');\n }\n}\n\nexport class BlobFileTooLargeError extends BlobError {\n constructor(message: string) {\n super(`File is too large, ${message}.`);\n }\n}\n\nexport class BlobStoreNotFoundError extends BlobError {\n constructor() {\n super('This store does not exist.');\n }\n}\n\nexport class BlobStoreSuspendedError extends BlobError {\n constructor() {\n super('This store has been suspended.');\n }\n}\n\nexport class BlobUnknownError extends BlobError {\n constructor() {\n super('Unknown error, please visit https://vercel.com/help.');\n }\n}\n\nexport class BlobNotFoundError extends BlobError {\n constructor() {\n super('The requested blob does not exist');\n }\n}\n\nexport class BlobServiceNotAvailable extends BlobError {\n constructor() {\n super('The blob service is currently not available. Please try again.');\n }\n}\n\nexport class BlobServiceRateLimited extends BlobError {\n public readonly retryAfter: number;\n\n constructor(seconds?: number) {\n super(\n `Too many requests please lower the number of concurrent requests ${\n seconds ? ` - try again in ${seconds} seconds` : ''\n }.`,\n );\n\n this.retryAfter = seconds ?? 0;\n }\n}\n\nexport class BlobRequestAbortedError extends BlobError {\n constructor() {\n super('The request was aborted.');\n }\n}\n\nexport class BlobPreconditionFailedError extends BlobError {\n constructor() {\n super('Precondition failed: ETag mismatch.');\n }\n}\n\ntype BlobApiErrorCodes =\n | 'store_suspended'\n | 'forbidden'\n | 'not_found'\n | 'unknown_error'\n | 'bad_request'\n | 'store_not_found'\n | 'not_allowed'\n | 'service_unavailable'\n | 'rate_limited'\n | 'content_type_not_allowed'\n | 'client_token_pathname_mismatch'\n | 'client_token_expired'\n | 'file_too_large'\n | 'precondition_failed';\n\nexport interface BlobApiError {\n error?: { code?: BlobApiErrorCodes; message?: string };\n}\n\n// This version is used to ensure that the client and server are compatible\n// The server (Vercel Blob API) uses this information to change its behavior like the\n// response format\nconst BLOB_API_VERSION = 12;\n\nfunction getApiVersion(): string {\n let versionOverride = null;\n try {\n // wrapping this code in a try/catch as this function is used in the browser and Vite doesn't define the process.env.\n // As this varaible is NOT used in production, it will always default to the BLOB_API_VERSION\n versionOverride =\n process.env.VERCEL_BLOB_API_VERSION_OVERRIDE ||\n process.env.NEXT_PUBLIC_VERCEL_BLOB_API_VERSION_OVERRIDE;\n } catch {\n // noop\n }\n\n return `${versionOverride ?? BLOB_API_VERSION}`;\n}\n\nfunction getRetries(): number {\n try {\n const retries = process.env.VERCEL_BLOB_RETRIES || '10';\n\n return parseInt(retries, 10);\n } catch {\n return 10;\n }\n}\n\nfunction createBlobServiceRateLimited(\n response: Response,\n): BlobServiceRateLimited {\n const retryAfter = response.headers.get('retry-after');\n\n return new BlobServiceRateLimited(\n retryAfter ? parseInt(retryAfter, 10) : undefined,\n );\n}\n\n// reads the body of a error response\nasync function getBlobError(\n response: Response,\n): Promise<{ code: string; error: BlobError }> {\n let code: BlobApiErrorCodes;\n let message: string | undefined;\n\n try {\n const data = (await response.json()) as BlobApiError;\n code = data.error?.code ?? 'unknown_error';\n message = data.error?.message;\n } catch {\n code = 'unknown_error';\n }\n\n // Now that we have multiple API clients out in the wild handling errors, we can't just send a different\n // error code for this type of error. We need to add a new field in the API response to handle this correctly,\n // but for now, we can just check the message.\n if (message?.includes('contentType') && message.includes('is not allowed')) {\n code = 'content_type_not_allowed';\n }\n\n if (\n message?.includes('\"pathname\"') &&\n message.includes('does not match the token payload')\n ) {\n code = 'client_token_pathname_mismatch';\n }\n\n if (message === 'Token expired') {\n code = 'client_token_expired';\n }\n\n if (message?.includes('the file length cannot be greater than')) {\n code = 'file_too_large';\n }\n\n let error: BlobError;\n switch (code) {\n case 'store_suspended':\n error = new BlobStoreSuspendedError();\n break;\n case 'forbidden':\n error = new BlobAccessError();\n break;\n case 'content_type_not_allowed':\n error = new BlobContentTypeNotAllowedError(message!);\n break;\n case 'client_token_pathname_mismatch':\n error = new BlobPathnameMismatchError(message!);\n break;\n case 'client_token_expired':\n error = new BlobClientTokenExpiredError();\n break;\n case 'file_too_large':\n error = new BlobFileTooLargeError(message!);\n break;\n case 'not_found':\n error = new BlobNotFoundError();\n break;\n case 'store_not_found':\n error = new BlobStoreNotFoundError();\n break;\n case 'bad_request':\n error = new BlobError(message ?? 'Bad request');\n break;\n case 'service_unavailable':\n error = new BlobServiceNotAvailable();\n break;\n case 'rate_limited':\n error = createBlobServiceRateLimited(response);\n break;\n case 'precondition_failed':\n error = new BlobPreconditionFailedError();\n break;\n case 'unknown_error':\n case 'not_allowed':\n default:\n error = new BlobUnknownError();\n break;\n }\n\n return { code, error };\n}\n\nexport async function requestApi<TResponse>(\n pathname: string,\n init: BlobRequestInit,\n commandOptions: (BlobCommandOptions & WithUploadProgress) | undefined,\n): Promise<TResponse> {\n const apiVersion = getApiVersion();\n const token = getTokenFromOptionsOrEnv(commandOptions);\n const extraHeaders = getProxyThroughAlternativeApiHeaderFromEnv();\n\n const [, , , storeId = ''] = token.split('_');\n const requestId = `${storeId}:${Date.now()}:${Math.random().toString(16).slice(2)}`;\n let retryCount = 0;\n let bodyLength = 0;\n let totalLoaded = 0;\n const sendBodyLength =\n commandOptions?.onUploadProgress || shouldUseXContentLength();\n\n if (\n init.body &&\n // 1. For upload progress we always need to know the total size of the body\n // 2. In development we need the header for put() to work correctly when passing a stream\n sendBodyLength\n ) {\n bodyLength = computeBodyLength(init.body);\n }\n\n if (commandOptions?.onUploadProgress) {\n commandOptions.onUploadProgress({\n loaded: 0,\n total: bodyLength,\n percentage: 0,\n });\n }\n\n const apiResponse = await retry(\n async (bail) => {\n let res: Response;\n\n // try/catch here to treat certain errors as not-retryable\n try {\n res = await blobRequest({\n input: getApiUrl(pathname),\n init: {\n ...init,\n headers: {\n 'x-api-blob-request-id': requestId,\n 'x-api-blob-request-attempt': String(retryCount),\n 'x-api-version': apiVersion,\n ...(sendBodyLength\n ? { 'x-content-length': String(bodyLength) }\n : {}),\n authorization: `Bearer ${token}`,\n ...extraHeaders,\n ...init.headers,\n },\n },\n onUploadProgress: commandOptions?.onUploadProgress\n ? (loaded) => {\n const total = bodyLength !== 0 ? bodyLength : loaded;\n totalLoaded = loaded;\n const percentage =\n bodyLength > 0\n ? Number(((loaded / total) * 100).toFixed(2))\n : 0;\n\n // Leave percentage 100 for the end of request\n if (percentage === 100 && bodyLength > 0) {\n return;\n }\n\n commandOptions.onUploadProgress?.({\n loaded,\n // When passing a stream to put(), we have no way to know the total size of the body.\n // Instead of defining total as total?: number we decided to set the total to the currently\n // loaded number. This is not inaccurate and way more practical for DX.\n // Passing down a stream to put() is very rare\n total,\n percentage,\n });\n }\n : undefined,\n });\n } catch (error) {\n // if the request was aborted, don't retry\n if (error instanceof DOMException && error.name === 'AbortError') {\n bail(new BlobRequestAbortedError());\n return;\n }\n\n // We specifically target network errors because fetch network errors are regular TypeErrors\n // We want to retry for network errors, but not for other TypeErrors\n if (isNetworkError(error)) {\n throw error;\n }\n\n // If we messed up the request part, don't even retry\n if (error instanceof TypeError) {\n bail(error);\n return;\n }\n\n // retry for any other erros thrown by fetch\n throw error;\n }\n\n if (res.ok) {\n return res;\n }\n\n const { code, error } = await getBlobError(res);\n\n // only retry for certain errors\n if (\n code === 'unknown_error' ||\n code === 'service_unavailable' ||\n code === 'internal_server_error'\n ) {\n throw error;\n }\n\n // don't retry for e.g. suspended stores\n bail(error);\n },\n {\n retries: getRetries(),\n onRetry: (error) => {\n if (error instanceof Error) {\n debug(`retrying API request to ${pathname}`, error.message);\n }\n\n retryCount = retryCount + 1;\n },\n },\n );\n\n if (!apiResponse) {\n throw new BlobUnknownError();\n }\n\n // Calling onUploadProgress here has two benefits:\n // 1. It ensures 100% is only reached at the end of the request. While otherwise you can reach 100%\n // before the request is fully done, as we only really measure what gets sent over the wire, not what\n // has been processed by the server.\n // 2. It makes the uploadProgress \"work\" even in rare cases where fetch/xhr onprogress is not working\n // And in the case of multipart uploads it actually provides a simple progress indication (per part)\n if (commandOptions?.onUploadProgress) {\n commandOptions.onUploadProgress({\n loaded: totalLoaded,\n total: totalLoaded,\n percentage: 100,\n });\n }\n\n return (await apiResponse.json()) as TResponse;\n}\n\nfunction getProxyThroughAlternativeApiHeaderFromEnv(): {\n 'x-proxy-through-alternative-api'?: string;\n} {\n const extraHeaders: Record<string, string> = {};\n\n try {\n if (\n 'VERCEL_BLOB_PROXY_THROUGH_ALTERNATIVE_API' in process.env &&\n process.env.VERCEL_BLOB_PROXY_THROUGH_ALTERNATIVE_API !== undefined\n ) {\n extraHeaders['x-proxy-through-alternative-api'] =\n process.env.VERCEL_BLOB_PROXY_THROUGH_ALTERNATIVE_API;\n } else if (\n 'NEXT_PUBLIC_VERCEL_BLOB_PROXY_THROUGH_ALTERNATIVE_API' in process.env &&\n process.env.NEXT_PUBLIC_VERCEL_BLOB_PROXY_THROUGH_ALTERNATIVE_API !==\n undefined\n ) {\n extraHeaders['x-proxy-through-alternative-api'] =\n process.env.NEXT_PUBLIC_VERCEL_BLOB_PROXY_THROUGH_ALTERNATIVE_API;\n }\n } catch {\n // noop\n }\n\n return extraHeaders;\n}\n\nfunction shouldUseXContentLength(): boolean {\n try {\n return process.env.VERCEL_BLOB_USE_X_CONTENT_LENGTH === '1';\n } catch {\n return false;\n }\n}\n","let debugIsActive = false;\n\n// wrapping this code in a try/catch in case some env doesn't support process.env (vite by default)\ntry {\n if (\n process.env.DEBUG?.includes('blob') ||\n process.env.NEXT_PUBLIC_DEBUG?.includes('blob')\n ) {\n debugIsActive = true;\n }\n} catch {\n // noop\n}\n\n// Set process.env.DEBUG = 'blob' to enable debug logging\nexport function debug(message: string, ...args: unknown[]): void {\n if (debugIsActive) {\n console.debug(`vercel-blob: ${message}`, ...args);\n }\n}\n","// TODO: Once Node 16 is no more needed internally, we can remove this file and use the native DOMException type.\nexport const DOMException =\n globalThis.DOMException ??\n (() => {\n // DOMException was only made a global in Node v17.0.0,\n // but fetch supports >= v16.8.\n try {\n atob('~');\n } catch (err) {\n return Object.getPrototypeOf(err).constructor;\n }\n })();\n","// @ts-nocheck -- This file is copy pasted\n\n// Source: https://github.com/sindresorhus/is-network-error/blob/main/index.js\n// Why: Jest + ES6 modules = harder than maintaining a nuclear plant\n\n/**\n * MIT License\n\nCopyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\nconst objectToString = Object.prototype.toString;\n\nconst isError = (value) => objectToString.call(value) === '[object Error]';\n\nconst errorMessages = new Set([\n 'network error', // Chrome\n 'Failed to fetch', // Chrome\n 'NetworkError when attempting to fetch resource.', // Firefox\n 'The Internet connection appears to be offline.', // Safari 16\n 'Load failed', // Safari 17+\n 'Network request failed', // `cross-fetch`\n 'fetch failed', // Undici (Node.js)\n 'terminated', // Undici (Node.js)\n]);\n\nexport default function isNetworkError(error) {\n const isValid =\n error &&\n isError(error) &&\n error.name === 'TypeError' &&\n typeof error.message === 'string';\n\n if (!isValid) {\n return false;\n }\n\n // We do an extra check for Safari 17+ as it has a very generic error message.\n // Network errors in Safari have no stack.\n if (error.message === 'Load failed') {\n return error.stack === undefined;\n }\n\n return errorMessages.has(error.message);\n}\n","import type { BodyInit } from 'undici';\nimport { fetch } from 'undici';\nimport { debug } from './debug';\nimport type { BlobRequest } from './helpers';\nimport {\n createChunkTransformStream,\n isStream,\n supportsRequestStreams,\n} from './helpers';\nimport { toReadableStream } from './multipart/helpers';\nimport type { PutBody } from './put-helpers';\n\nexport const hasFetch = typeof fetch === 'function';\n\nexport const hasFetchWithUploadProgress = hasFetch && supportsRequestStreams;\n\nconst CHUNK_SIZE = 64 * 1024;\n\nexport const blobFetch: BlobRequest = async ({\n input,\n init,\n onUploadProgress,\n}) => {\n debug('using fetch');\n let body: BodyInit | undefined;\n\n if (init.body) {\n if (onUploadProgress) {\n // We transform the body to a stream here instead of at the call site\n // So that on retries we can reuse the original body, otherwise we would not be able to reuse it\n const stream = await toReadableStream(init.body);\n\n let loaded = 0;\n\n const chunkTransformStream = createChunkTransformStream(\n CHUNK_SIZE,\n (newLoaded: number) => {\n loaded += newLoaded;\n onUploadProgress(loaded);\n },\n );\n\n body = stream.pipeThrough(chunkTransformStream);\n } else {\n body = init.body as BodyInit;\n }\n }\n\n // Only set duplex option when supported and dealing with a stream body\n const duplex =\n supportsRequestStreams && body && isStream(body as PutBody)\n ? 'half'\n : undefined;\n\n return fetch(\n input,\n // @ts-expect-error -- Blob and Nodejs Blob are triggering type errors, fine with it\n {\n ...init,\n ...(init.body ? { body } : {}),\n duplex,\n },\n );\n};\n","import type { Response as UndiciResponse } from 'undici';\nimport { debug } from './debug';\nimport { type BlobRequest, isReadableStream } from './helpers';\n\nexport const hasXhr = typeof XMLHttpRequest !== 'undefined';\n\nexport const blobXhr: BlobRequest = async ({\n input,\n init,\n onUploadProgress,\n}) => {\n debug('using xhr');\n let body: XMLHttpRequestBodyInit | null = null;\n\n // xhr.send only support XMLHttpRequestBodyInit types, excluding ReadableStream (web)\n // and Readable (node)\n // We do have to support ReadableStream being sent to xhr as our library allows\n // for Safari to use put(path, ReadableStream, { onUploadProgress }) which would\n // end up here.\n // We do not have to support Readable being sent to xhr as using Node.js you would\n // endup in the fetch implementation by default.\n if (init.body) {\n if (isReadableStream(init.body)) {\n body = await new Response(init.body).blob();\n } else {\n // We \"type lie\" here, what we should do instead:\n // Exclude ReadableStream:\n // body = init.body as Exclude<PutBody, ReadableStream | Readable>;\n // We can't do this because init.body (PutBody) relies on Blob (node:buffer)\n // while XMLHttpRequestBodyInit relies on native Blob type.\n // If we get rid of undici we can remove this trick.\n body = init.body as XMLHttpRequestBodyInit;\n }\n }\n\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.open(init.method || 'GET', input.toString(), true);\n\n // Handle upload progress\n if (onUploadProgress) {\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable) {\n onUploadProgress(event.loaded);\n }\n });\n }\n\n // Handle response\n xhr.onload = () => {\n if (init.signal?.aborted) {\n reject(new DOMException('The user aborted the request.', 'AbortError'));\n return;\n }\n\n const headers = new Headers();\n const rawHeaders = xhr\n .getAllResponseHeaders()\n .trim()\n .split(/[\\r\\n]+/);\n\n // Parse headers\n rawHeaders.forEach((line) => {\n const parts = line.split(': ');\n const key = parts.shift();\n const value = parts.join(': ');\n if (key) headers.set(key.toLowerCase(), value);\n });\n\n // Create response object, api-blob sends back text and api.ts will turn it into json if necessary\n const response = new Response(xhr.response as string, {\n status: xhr.status,\n statusText: xhr.statusText,\n headers,\n }) as unknown as UndiciResponse;\n\n resolve(response);\n };\n\n // Handle network errors\n xhr.onerror = () => {\n reject(new TypeError('Network request failed'));\n };\n\n // Handle timeouts\n xhr.ontimeout = () => {\n reject(new TypeError('Network request timed out'));\n };\n\n // Handle aborts\n xhr.onabort = () => {\n reject(new DOMException('The user aborted a request.', 'AbortError'));\n };\n\n // Set headers\n if (init.headers) {\n const headers = new Headers(init.headers as HeadersInit);\n headers.forEach((value, key) => {\n xhr.setRequestHeader(key, value);\n });\n }\n\n // Handle abort signal\n if (init.signal) {\n init.signal.addEventListener('abort', () => {\n xhr.abort();\n });\n\n // If already aborted, abort xhr immediately\n if (init.signal.aborted) {\n xhr.abort();\n return;\n }\n }\n\n // We're cheating and saying that nobody is gonna use put() with a stream in an environment not supporting\n // fetch with streams. If this ever happens please open an issue and we'll figure it out.\n xhr.send(body);\n });\n};\n","import type { Response } from 'undici';\nimport { blobFetch, hasFetch, hasFetchWithUploadProgress } from './fetch';\nimport type { BlobRequest } from './helpers';\nimport { blobXhr, hasXhr } from './xhr';\n\nexport const blobRequest: BlobRequest = async ({\n input,\n init,\n onUploadProgress,\n}): Promise<Response> => {\n if (onUploadProgress) {\n if (hasFetchWithUploadProgress) {\n return blobFetch({ input, init, onUploadProgress });\n }\n\n if (hasXhr) {\n return blobXhr({ input, init, onUploadProgress });\n }\n }\n\n if (hasFetch) {\n return blobFetch({ input, init });\n }\n\n if (hasXhr) {\n return blobXhr({ input, init });\n }\n\n throw new Error('No request implementation available');\n};\n","import type { Readable } from 'stream';\n// We use the undici types to ensure TS doesn't complain about native types (like ReadableStream) vs\n// undici types fetch expects (like Blob is from node:buffer..)\n// import type { Blob } from 'node:buffer';\nimport type { File } from 'undici';\nimport { MAXIMUM_PATHNAME_LENGTH } from './api';\nimport type { ClientCommonCreateBlobOptions } from './client';\nimport type { CommonCreateBlobOptions } from './helpers';\nimport { BlobError, disallowedPathnameCharacters } from './helpers';\n\nexport const putOptionHeaderMap = {\n cacheControlMaxAge: 'x-cache-control-max-age',\n addRandomSuffix: 'x-add-random-suffix',\n allowOverwrite: 'x-allow-overwrite',\n contentType: 'x-content-type',\n ifMatch: 'x-if-match',\n};\n\n/**\n * Result of a successful put or copy operation.\n */\nexport interface PutBlobResult {\n /**\n * The URL of the blob.\n */\n url: string;\n /**\n * A URL that will cause browsers to download the file instead of displaying it inline.\n */\n downloadUrl: string;\n /**\n * The pathname of the blob within the store.\n */\n pathname: string;\n /**\n * The content-type of the blob.\n */\n contentType: string;\n /**\n * The content disposition header value.\n */\n contentDisposition: string;\n /**\n * The ETag of the blob. Can be used with `ifMatch` for conditional writes.\n */\n etag: string;\n}\n\nexport type PutBlobApiResponse = PutBlobResult;\n\n/**\n * Represents the body content for a put operation.\n * Can be one of several supported types.\n */\nexport type PutBody =\n | string\n | Readable // Node.js streams\n | Buffer // Node.js buffers\n | Blob\n | ArrayBuffer\n | ReadableStream // Streams API (= Web streams in Node.js)\n | File;\n\nexport type CommonPutCommandOptions = CommonCreateBlobOptions &\n ClientCommonCreateBlobOptions;\n\nexport interface CreatePutMethodOptions<TOptions> {\n allowedOptions: (keyof typeof putOptionHeaderMap)[];\n getToken?: (pathname: string, options: TOptions) => Promise<string>;\n extraChecks?: (options: TOptions) => void;\n}\n\nexport function createPutHeaders<TOptions extends CommonPutCommandOptions>(\n allowedOptions: CreatePutMethodOptions<TOptions>['allowedOptions'],\n options: TOptions,\n): Record<string, string> {\n const headers: Record<string, string> = {};\n\n if (allowedOptions.includes('contentType') && options.contentType) {\n headers[putOptionHeaderMap.contentType] = options.contentType;\n }\n\n if (\n allowedOptions.includes('addRandomSuffix') &&\n options.addRandomSuffix !== undefined\n ) {\n headers[putOptionHeaderMap.addRandomSuffix] = options.addRandomSuffix\n ? '1'\n : '0';\n }\n\n if (\n allowedOptions.includes('allowOverwrite') &&\n options.allowOverwrite !== undefined\n ) {\n headers[putOptionHeaderMap.allowOverwrite] = options.allowOverwrite\n ? '1'\n : '0';\n }\n\n if (\n allowedOptions.includes('cacheControlMaxAge') &&\n options.cacheControlMaxAge !== undefined\n ) {\n headers[putOptionHeaderMap.cacheControlMaxAge] =\n options.cacheControlMaxAge.toString();\n }\n\n if (allowedOptions.includes('ifMatch') && options.ifMatch) {\n headers[putOptionHeaderMap.ifMatch] = options.ifMatch;\n }\n\n return headers;\n}\n\nexport async function createPutOptions<\n TOptions extends CommonPutCommandOptions,\n>({\n pathname,\n options,\n extraChecks,\n getToken,\n}: {\n pathname: string;\n options?: TOptions;\n extraChecks?: CreatePutMethodOptions<TOptions>['extraChecks'];\n getToken?: CreatePutMethodOptions<TOptions>['getToken'];\n}): Promise<TOptions> {\n if (!pathname) {\n throw new BlobError('pathname is required');\n }\n\n if (pathname.length > MAXIMUM_PATHNAME_LENGTH) {\n throw new BlobError(\n `pathname is too long, maximum length is ${MAXIMUM_PATHNAME_LENGTH}`,\n );\n }\n\n for (const invalidCharacter of disallowedPathnameCharacters) {\n if (pathname.includes(invalidCharacter)) {\n throw new BlobError(\n `pathname cannot contain \"${invalidCharacter}\", please encode it if needed`,\n );\n }\n }\n\n if (!options) {\n throw new BlobError('missing options, see usage');\n }\n\n if (options.access !== 'public') {\n throw new BlobError('access must be \"public\"');\n }\n\n if (extraChecks) {\n extraChecks(options);\n }\n\n if (getToken) {\n options.token = await getToken(pathname, options);\n }\n\n return options;\n}\n","import { BlobServiceNotAvailable, requestApi } from '../api';\nimport { debug } from '../debug';\nimport type { BlobCommandOptions, CommonCreateBlobOptions } from '../helpers';\nimport type {\n CreatePutMethodOptions,\n PutBlobApiResponse,\n PutBlobResult,\n} from '../put-helpers';\nimport { createPutHeaders, createPutOptions } from '../put-helpers';\nimport type { Part } from './helpers';\n\n/**\n * Options for completing a multipart upload.\n * Used with the completeMultipartUpload method.\n */\nexport interface CommonCompleteMultipartUploadOptions {\n /**\n * Unique upload identifier for the multipart upload, received from createMultipartUpload.\n * This ID is used to identify which multipart upload is being completed.\n */\n uploadId: string;\n\n /**\n * Unique key identifying the blob object, received from createMultipartUpload.\n * This key is used to identify which blob object the parts belong to.\n */\n key: string;\n}\n\nexport type CompleteMultipartUploadCommandOptions =\n CommonCompleteMultipartUploadOptions & CommonCreateBlobOptions;\n\nexport function createCompleteMultipartUploadMethod<\n TOptions extends CompleteMultipartUploadCommandOptions,\n>({ allowedOptions, getToken, extraChecks }: CreatePutMethodOptions<TOptions>) {\n return async (pathname: string, parts: Part[], optionsInput: TOptions) => {\n const options = await createPutOptions({\n pathname,\n options: optionsInput,\n extraChecks,\n getToken,\n });\n\n const headers = createPutHeaders(allowedOptions, options);\n\n return completeMultipartUpload({\n uploadId: options.uploadId,\n key: options.key,\n pathname,\n headers,\n options,\n parts,\n });\n };\n}\n\nexport async function completeMultipartUpload({\n uploadId,\n key,\n pathname,\n parts,\n headers,\n options,\n}: {\n uploadId: string;\n key: string;\n pathname: string;\n parts: Part[];\n headers: Record<string, string>;\n options: BlobCommandOptions;\n}): Promise<PutBlobResult> {\n const params = new URLSearchParams({ pathname });\n\n try {\n const response = await requestApi<PutBlobApiResponse>(\n `/mpu?${params.toString()}`,\n {\n method: 'POST',\n headers: {\n ...headers,\n 'content-type': 'application/json',\n 'x-mpu-action': 'complete',\n 'x-mpu-upload-id': uploadId,\n // key can be any utf8 character so we need to encode it as HTTP headers can only be us-ascii\n // https://www.rfc-editor.org/rfc/rfc7230#swection-3.2.4\n 'x-mpu-key': encodeURIComponent(key),\n },\n body: JSON.stringify(parts),\n signal: options.abortSignal,\n },\n options,\n );\n\n debug('mpu: complete', response);\n\n return response;\n } catch (error: unknown) {\n if (\n error instanceof TypeError &&\n (error.message === 'Failed to fetch' || error.message === 'fetch failed')\n ) {\n throw new BlobServiceNotAvailable();\n } else {\n throw error;\n }\n }\n}\n","import { BlobServiceNotAvailable, requestApi } from '../api';\nimport { debug } from '../debug';\nimport type { BlobCommandOptions, CommonCreateBlobOptions } from '../helpers';\nimport type { CreatePutMethodOptions } from '../put-helpers';\nimport { createPutHeaders, createPutOptions } from '../put-helpers';\n\nexport function createCreateMultipartUploadMethod<\n TOptions extends CommonCreateBlobOptions,\n>({ allowedOptions, getToken, extraChecks }: CreatePutMethodOptions<TOptions>) {\n return async (pathname: string, optionsInput: TOptions) => {\n const options = await createPutOptions({\n pathname,\n options: optionsInput,\n extraChecks,\n getToken,\n });\n\n const headers = createPutHeaders(allowedOptions, options);\n\n const createMultipartUploadResponse = await createMultipartUpload(\n pathname,\n headers,\n options,\n );\n\n return {\n key: createMultipartUploadResponse.key,\n uploadId: createMultipartUploadResponse.uploadId,\n };\n };\n}\n\ninterface CreateMultipartUploadApiResponse {\n uploadId: string;\n key: string;\n}\n\nexport async function createMultipartUpload(\n pathname: string,\n headers: Record<string, string>,\n options: BlobCommandOptions,\n): Promise<CreateMultipartUploadApiResponse> {\n debug('mpu: create', 'pathname:', pathname);\n\n const params = new URLSearchParams({ pathname });\n\n try {\n const response = await requestApi<CreateMultipartUploadApiResponse>(\n `/mpu?${params.toString()}`,\n {\n method: 'POST',\n headers: {\n ...headers,\n 'x-mpu-action': 'create',\n },\n signal: options.abortSignal,\n },\n options,\n );\n\n debug('mpu: create', response);\n\n return response;\n } catch (error: unknown) {\n if (\n error instanceof TypeError &&\n (error.message === 'Failed to fetch' || error.message === 'fetch failed')\n ) {\n throw new BlobServiceNotAvailable();\n }\n\n throw error;\n }\n}\n","import throttle from 'throttleit';\nimport { BlobServiceNotAvailable, requestApi } from '../api';\nimport { debug } from '../debug';\nimport type {\n BlobCommandOptions,\n CommonCreateBlobOptions,\n WithUploadProgress,\n} from '../helpers';\nimport { BlobError, bytes, isPlainObject } from '../helpers';\nimport type { CreatePutMethodOptions, PutBody } from '../put-helpers';\nimport { createPutHeaders, createPutOptions } from '../put-helpers';\nimport type { Part, PartInput } from './helpers';\n\n/**\n * Options for uploading a part in a multipart upload process.\n * Used with the uploadPart method.\n */\nexport interface CommonMultipartUploadOptions {\n /**\n * Unique upload identifier for the multipart upload, received from createMultipartUpload.\n * This ID is used to associate all uploaded parts with the same multipart upload.\n */\n uploadId: string;\n\n /**\n * Unique key identifying the blob object, received from createMultipartUpload.\n * This key is used to identify which blob object the parts belong to.\n */\n key: string;\n\n /**\n * A number identifying which part is being uploaded (1-based).\n * This number is used to order the parts when completing the multipart upload.\n * Parts must be uploaded with consecutive part numbers starting from 1.\n */\n partNumber: number;\n}\n\nexport type UploadPartCommandOptions = CommonMultipartUploadOptions &\n CommonCreateBlobOptions;\n\nexport function createUploadPartMethod<\n TOptions extends UploadPartCommandOptions,\n>({ allowedOptions, getToken, extraChecks }: CreatePutMethodOptions<TOptions>) {\n return async (\n pathname: string,\n body: PutBody,\n optionsInput: TOptions,\n ): Promise<Part> => {\n const options = await createPutOptions({\n pathname,\n options: optionsInput,\n extraChecks,\n getToken,\n });\n\n const headers = createPutHeaders(allowedOptions, options);\n\n if (isPlainObject(body)) {\n throw new BlobError(\n \"Body must be a string, buffer or stream. You sent a plain JavaScript object, double check what you're trying to upload.\",\n );\n }\n\n const result = await uploadPart({\n uploadId: options.uploadId,\n key: options.key,\n pathname,\n part: { blob: body, partNumber: options.partNumber },\n headers,\n options,\n });\n\n return {\n etag: result.etag,\n partNumber: options.partNumber,\n };\n };\n}\n\nexport async function uploadPart({\n uploadId,\n key,\n pathname,\n headers,\n options,\n internalAbortController = new AbortController(),\n part,\n}: {\n uploadId: string;\n key: string;\n pathname: string;\n headers: Record<string, string>;\n options: BlobCommandOptions & WithUploadProgress;\n internalAbortController?: AbortController;\n part: PartInput;\n}): Promise<UploadPartApiResponse> {\n const params = new URLSearchParams({ pathname });\n\n const responsePromise = requestApi<UploadPartApiResponse>(\n `/mpu?${params.toString()}`,\n {\n signal: internalAbortController.signal,\n method: 'POST',\n headers: {\n ...headers,\n 'x-mpu-action': 'upload',\n 'x-mpu-key': encodeURIComponent(key),\n 'x-mpu-upload-id': uploadId,\n 'x-mpu-part-number': part.partNumber.toString(),\n },\n // weird things between undici types and native fetch types\n body: part.blob,\n },\n options,\n );\n\n function handleAbort(): void {\n internalAbortController.abort();\n }\n\n if (options.abortSignal?.aborted) {\n // abort if the signal is already aborted\n handleAbort();\n } else {\n // we connect the internal abort controller to the external abortSignal to allow the user to cancel the upload\n options.abortSignal?.addEventListener('abort', handleAbort);\n }\n\n const response = await responsePromise;\n\n options.abortSignal?.removeEventListener('abort', handleAbort);\n\n return response;\n}\n\n// Most browsers will cap requests at 6 concurrent uploads per domain (Vercel Blob API domain)\n// In other environments, we can afford to be more aggressive\nconst maxConcurrentUploads = typeof window !== 'undefined' ? 6 : 8;\n\n// 5MB is the minimum part size accepted by Vercel Blob, but we set our default part size to 8mb like the aws cli\nconst partSizeInBytes = 8 * 1024 * 1024;\n\nconst maxBytesInMemory = maxConcurrentUploads * partSizeInBytes * 2;\n\ninterface UploadPartApiResponse {\n etag: string;\n}\n\nexport interface BlobUploadPart {\n partNumber: number;\n blob: Blob;\n}\n\n// Can we rewrite this function without new Promise?\nexport function uploadAllParts({\n uploadId,\n key,\n pathname,\n stream,\n headers,\n options,\n totalToLoad,\n}: {\n uploadId: string;\n key: string;\n pathname: string;\n stream: ReadableStream<ArrayBuffer>;\n headers: Record<string, string>;\n options: BlobCommandOptions & WithUploadProgress;\n totalToLoad: number;\n}): Promise<Part[]> {\n debug('mpu: upload init', 'key:', key);\n const internalAbortController = new AbortController();\n\n return new Promise((resolve, reject) => {\n const partsToUpload: BlobUploadPart[] = [];\n const completedParts: Part[] = [];\n const reader = stream.getReader();\n let activeUploads = 0;\n let reading = false;\n let currentPartNumber = 1;\n // this next variable is used to escape the read loop when an error occurs\n let rejected = false;\n let currentBytesInMemory = 0;\n let doneReading = false;\n let bytesSent = 0;\n\n // This must be outside the read loop, in case we reach the maxBytesInMemory and\n // we exit the loop but some bytes are still to be sent on the next read invocation.\n let arrayBuffers: ArrayBuffer[] = [];\n let currentPartBytesRead = 0;\n\n let onUploadProgress: (() => void) | undefined;\n const totalLoadedPerPartNumber: Record<string, number> = {};\n\n if (options.onUploadProgress) {\n onUploadProgress = throttle(() => {\n const loaded = Object.values(totalLoadedPerPartNumber).reduce(\n (acc, cur) => {\n return acc + cur;\n },\n 0,\n );\n const total = totalToLoad || loaded;\n const percentage =\n totalToLoad > 0\n ? Number(((loaded / totalToLoad || loaded) * 100).toFixed(2))\n : 0;\n\n // we call the user's onUploadProgress callback\n options.onUploadProgress?.({ loaded, total, percentage });\n }, 150);\n }\n\n read().catch(cancel);\n\n async function read(): Promise<void> {\n debug(\n 'mpu: upload read start',\n 'activeUploads:',\n activeUploads,\n 'currentBytesInMemory:',\n `${bytes(currentBytesInMemory)}/${bytes(maxBytesInMemory)}`,\n 'bytesSent:',\n bytes(bytesSent),\n );\n\n reading = true;\n\n while (currentBytesInMemory < maxBytesInMemory && !rejected) {\n try {\n const { value, done } = await reader.read();\n\n if (done) {\n doneReading = true;\n debug('mpu: upload read consumed the whole stream');\n // done is sent when the stream is fully consumed. That's why we're not using the value here.\n if (arrayBuffers.length > 0) {\n partsToUpload.push({\n partNumber: currentPartNumber++,\n blob: new Blob(arrayBuffers, {\n type: 'application/octet-stream',\n }),\n });\n\n sendParts();\n }\n reading = false;\n return;\n }\n\n currentBytesInMemory += value.byteLength;\n\n // This code ensures that each part will be exactly of `partSizeInBytes` size\n // Otherwise R2 will refuse it. AWS S3 is fine with parts of different sizes.\n let valueOffset = 0;\n while (valueOffset < value.byteLength) {\n const remainingPartSize = partSizeInBytes - currentPartBytesRead;\n const endOffset = Math.min(\n valueOffset + remainingPartSize,\n value.byteLength,\n );\n\n const chunk = value.slice(valueOffset, endOffset);\n\n arrayBuffers.push(chunk);\n currentPartBytesRead += chunk.byteLength;\n valueOffset = endOffset;\n\n if (currentPartBytesRead === partSizeInBytes) {\n partsToUpload.push({\n partNumber: currentPartNumber++,\n blob: new Blob(arrayBuffers, {\n type: 'application/octet-stream',\n }),\n });\n\n arrayBuffers = [];\n currentPartBytesRead = 0;\n sendParts();\n }\n }\n } catch (error) {\n cancel(error);\n }\n }\n\n debug(\n 'mpu: upload read end',\n 'activeUploads:',\n activeUploads,\n 'currentBytesInMemory:',\n `${bytes(currentBytesInMemory)}/${bytes(maxBytesInMemory)}`,\n 'bytesSent:',\n bytes(bytesSent),\n );\n\n reading = false;\n }\n\n async function sendPart(part: BlobUploadPart): Promise<void> {\n activeUploads++;\n\n debug(\n 'mpu: upload send part start',\n 'partNumber:',\n part.partNumber,\n 'size:',\n part.blob.size,\n 'activeUploads:',\n activeUploads,\n 'currentBytesInMemory:',\n `${bytes(currentBytesInMemory)}/${bytes(maxBytesInMemory)}`,\n 'bytesSent:',\n bytes(bytesSent),\n );\n\n try {\n const uploadProgressForPart: WithUploadProgress['onUploadProgress'] =\n options.onUploadProgress\n ? (event) => {\n totalLoadedPerPartNumber[part.partNumber] = event.loaded;\n if (onUploadProgress) {\n onUploadProgress();\n }\n }\n : undefined;\n\n const completedPart = await uploadPart({\n uploadId,\n key,\n pathname,\n headers,\n options: {\n ...options,\n onUploadProgress: uploadProgressForPart,\n },\n internalAbortController,\n part,\n });\n\n debug(\n 'mpu: upload send part end',\n 'partNumber:',\n part.partNumber,\n 'activeUploads',\n activeUploads,\n 'currentBytesInMemory:',\n `${bytes(currentBytesInMemory)}/${bytes(maxBytesInMemory)}`,\n 'bytesSent:',\n bytes(bytesSent),\n );\n\n if (rejected) {\n return;\n }\n\n completedParts.push({\n partNumber: part.partNumber,\n etag: completedPart.etag,\n });\n\n currentBytesInMemory -= part.blob.size;\n activeUploads--;\n bytesSent += part.blob.size;\n\n if (partsToUpload.length > 0) {\n sendParts();\n }\n\n if (doneReading) {\n if (activeUploads === 0) {\n reader.releaseLock();\n resolve(completedParts);\n }\n return;\n }\n\n if (!reading) {\n read().catch(cancel);\n }\n } catch (error) {\n // cancel if fetch throws an error\n cancel(error);\n }\n }\n\n function sendParts(): void {\n if (rejected) {\n return;\n }\n\n debug(\n 'send parts',\n 'activeUploads',\n activeUploads,\n 'partsToUpload',\n partsToUpload.length,\n );\n\n while (activeUploads < maxConcurrentUploads && partsToUpload.length > 0) {\n const partToSend = partsToUpload.shift();\n if (partToSend) {\n void sendPart(partToSend);\n }\n }\n }\n\n function cancel(error: unknown): void {\n // a previous call already rejected the whole call, ignore\n if (rejected) {\n return;\n }\n rejected = true;\n internalAbortController.abort();\n reader.releaseLock();\n if (\n error instanceof TypeError &&\n (error.message === 'Failed to fetch' ||\n error.message === 'fetch failed')\n ) {\n reject(new BlobServiceNotAvailable());\n } else {\n reject(error as Error);\n }\n }\n });\n}\n","import {\n BlobError,\n type CommonCreateBlobOptions,\n isPlainObject,\n} from '../helpers';\nimport type { CreatePutMethodOptions, PutBody } from '../put-helpers';\nimport { createPutHeaders, createPutOptions } from '../put-helpers';\nimport { completeMultipartUpload } from './complete';\nimport { createMultipartUpload } from './create';\nimport type { Part } from './helpers';\nimport { uploadPart as rawUploadPart } from './upload';\n\nexport function createCreateMultipartUploaderMethod<\n TOptions extends CommonCreateBlobOptions,\n>({ allowedOptions, getToken, extraChecks }: CreatePutMethodOptions<TOptions>) {\n return async (pathname: string, optionsInput: TOptions) => {\n const options = await createPutOptions({\n pathname,\n options: optionsInput,\n extraChecks,\n getToken,\n });\n\n const headers = createPutHeaders(allowedOptions, options);\n\n const createMultipartUploadResponse = await createMultipartUpload(\n pathname,\n headers,\n options,\n );\n\n return {\n key: createMultipartUploadResponse.key,\n uploadId: createMultipartUploadResponse.uploadId,\n\n async uploadPart(partNumber: number, body: PutBody) {\n if (isPlainObject(body)) {\n throw new BlobError(\n \"Body must be a string, buffer or stream. You sent a plain JavaScript object, double check what you're trying to upload.\",\n );\n }\n\n const result = await rawUploadPart({\n uploadId: createMultipartUploadResponse.uploadId,\n key: createMultipartUploadResponse.key,\n pathname,\n part: { partNumber, blob: body },\n headers,\n options,\n });\n\n return {\n etag: result.etag,\n partNumber,\n };\n },\n\n async complete(parts: Part[]) {\n return completeMultipartUpload({\n uploadId: createMultipartUploadResponse.uploadId,\n key: createMultipartUploadResponse.key,\n pathname,\n parts,\n headers,\n options,\n });\n },\n };\n };\n}\n","import throttle from 'throttleit';\nimport { requestApi } from './api';\nimport type { CommonCreateBlobOptions, WithUploadProgress } from './helpers';\nimport { BlobError, isPlainObject } from './helpers';\nimport { uncontrolledMultipartUpload } from './multipart/uncontrolled';\nimport type {\n CreatePutMethodOptions,\n PutBlobApiResponse,\n PutBlobResult,\n PutBody,\n} from './put-helpers';\nimport { createPutHeaders, createPutOptions } from './put-helpers';\n\nexport interface PutCommandOptions\n extends CommonCreateBlobOptions,\n WithUploadProgress {\n /**\n * Whether to use multipart upload. Use this when uploading large files. It will split the file into multiple parts, upload them in parallel and retry failed parts.\n * @defaultvalue false\n */\n multipart?: boolean;\n}\n\nexport function createPutMethod<TOptions extends PutCommandOptions>({\n allowedOptions,\n getToken,\n extraChecks,\n}: CreatePutMethodOptions<TOptions>) {\n return async function put(\n pathname: string,\n body: PutBody,\n optionsInput: TOptions,\n ): Promise<PutBlobResult> {\n if (!body) {\n throw new BlobError('body is required');\n }\n\n if (isPlainObject(body)) {\n throw new BlobError(\n \"Body must be a string, buffer or stream. You sent a plain JavaScript object, double check what you're trying to upload.\",\n );\n }\n\n const options = await createPutOptions({\n pathname,\n options: optionsInput,\n extraChecks,\n getToken,\n });\n\n const headers = createPutHeaders(allowedOptions, options);\n\n if (options.multipart === true) {\n return uncontrolledMultipartUpload(pathname, body, headers, options);\n }\n\n const onUploadProgress = options.onUploadProgress\n ? throttle(options.onUploadProgress, 100)\n : undefined;\n\n const params = new URLSearchParams({ pathname });\n\n const response = await requestApi<PutBlobApiResponse>(\n `/?${params.toString()}`,\n {\n method: 'PUT',\n body,\n headers,\n signal: options.abortSignal,\n },\n {\n ...options,\n onUploadProgress,\n },\n );\n\n return {\n url: response.url,\n downloadUrl: response.downloadUrl,\n pathname: response.pathname,\n contentType: response.contentType,\n contentDisposition: response.contentDisposition,\n etag: response.etag,\n };\n };\n}\n","import { debug } from '../debug';\nimport type { BlobCommandOptions, WithUploadProgress } from '../helpers';\nimport { computeBodyLength } from '../helpers';\nimport type { PutBlobResult, PutBody } from '../put-helpers';\nimport { completeMultipartUpload } from './complete';\nimport { createMultipartUpload } from './create';\nimport { toReadableStream } from './helpers';\nimport { uploadAllParts } from './upload';\n\n// this automatically slices the body into parts and uploads all of them as multiple parts\nexport async function uncontrolledMultipartUpload(\n pathname: string,\n body: PutBody,\n headers: Record<string, string>,\n options: BlobCommandOptions & WithUploadProgress,\n): Promise<PutBlobResult> {\n debug('mpu: init', 'pathname:', pathname, 'headers:', headers);\n\n const optionsWithoutOnUploadProgress = {\n ...options,\n onUploadProgress: undefined,\n };\n\n // Step 1: Start multipart upload\n const createMultipartUploadResponse = await createMultipartUpload(\n pathname,\n headers,\n optionsWithoutOnUploadProgress,\n );\n\n const totalToLoad = computeBodyLength(body);\n const stream = await toReadableStream(body);\n\n // Step 2: Upload parts one by one\n const parts = await uploadAllParts({\n uploadId: createMultipartUploadResponse.uploadId,\n key: createMultipartUploadResponse.key,\n pathname,\n // @ts-expect-error ReadableStream<ArrayBuffer | Uint8Array> is compatible at runtime\n stream,\n headers,\n options,\n totalToLoad,\n });\n\n // Step 3: Complete multipart upload\n const blob = await completeMultipartUpload({\n uploadId: createMultipartUploadResponse.uploadId,\n key: createMultipartUploadResponse.key,\n pathname,\n parts,\n headers,\n options: optionsWithoutOnUploadProgress,\n });\n\n // changes:\n // stream => set percentage to 0% even if loaded/total is valid\n // stream => send onUploadProgress 100% at the end of the request here\n\n return blob;\n}\n","import { requestApi } from './api';\nimport type { BlobCommandOptions } from './helpers';\nimport { type PutBlobApiResponse, putOptionHeaderMap } from './put-helpers';\n\nexport interface CreateFolderResult {\n pathname: string;\n url: string;\n}\n\n/**\n * Creates a folder in your store. Vercel Blob has no real concept of folders, our file browser on Vercel.com displays folders based on the presence of trailing slashes in the pathname. Unless you are building a file browser system, you probably don't need to use this method.\n *\n * Use the resulting `url` to delete the folder, just like you would delete a blob.\n * @param pathname - Can be user1/ or user1/avatars/\n * @param options - Additional options like `token`\n */\nexport async function createFolder(\n pathname: string,\n options: BlobCommandOptions = {},\n): Promise<CreateFolderResult> {\n const folderPathname = pathname.endsWith('/') ? pathname : `${pathname}/`;\n\n const headers: Record<string, string> = {};\n\n headers[putOptionHeaderMap.addRandomSuffix] = '0';\n\n const params = new URLSearchParams({ pathname: folderPathname });\n const response = await requestApi<PutBlobApiResponse>(\n `/?${params.toString()}`,\n {\n method: 'PUT',\n headers,\n signal: options.abortSignal,\n },\n options,\n );\n\n return {\n url: response.url,\n pathname: response.pathname,\n };\n}\n"],"mappings":"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","names":["_a","DOMException","_a","_a","_b","error","DOMException","_a","_b","throttle","throttle"]}
package/dist/client.cjs CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
 
9
9
 
10
- var _chunk4ACRCP3Xcjs = require('./chunk-4ACRCP3X.cjs');
10
+ var _chunk23VLASYPcjs = require('./chunk-23VLASYP.cjs');
11
11
 
12
12
  // src/client.ts
13
13
  var _crypto = require('crypto'); var crypto = _interopRequireWildcard(_crypto);
@@ -15,7 +15,7 @@ var _undici = require('undici');
15
15
  function createPutExtraChecks(methodName) {
16
16
  return function extraChecks(options) {
17
17
  if (!options.token.startsWith("vercel_blob_client_")) {
18
- throw new (0, _chunk4ACRCP3Xcjs.BlobError)(`${methodName} must be called with a client token`);
18
+ throw new (0, _chunk23VLASYPcjs.BlobError)(`${methodName} must be called with a client token`);
19
19
  }
20
20
  if (
21
21
  // @ts-expect-error -- Runtime check for DX.
@@ -23,41 +23,41 @@ function createPutExtraChecks(methodName) {
23
23
  options.allowOverwrite !== void 0 || // @ts-expect-error -- Runtime check for DX.
24
24
  options.cacheControlMaxAge !== void 0
25
25
  ) {
26
- throw new (0, _chunk4ACRCP3Xcjs.BlobError)(
26
+ throw new (0, _chunk23VLASYPcjs.BlobError)(
27
27
  `${methodName} doesn't allow \`addRandomSuffix\`, \`cacheControlMaxAge\` or \`allowOverwrite\`. Configure these options at the server side when generating client tokens.`
28
28
  );
29
29
  }
30
30
  };
31
31
  }
32
- var put = _chunk4ACRCP3Xcjs.createPutMethod.call(void 0, {
32
+ var put = _chunk23VLASYPcjs.createPutMethod.call(void 0, {
33
33
  allowedOptions: ["contentType"],
34
34
  extraChecks: createPutExtraChecks("client/`put`")
35
35
  });
36
- var createMultipartUpload = _chunk4ACRCP3Xcjs.createCreateMultipartUploadMethod.call(void 0, {
36
+ var createMultipartUpload = _chunk23VLASYPcjs.createCreateMultipartUploadMethod.call(void 0, {
37
37
  allowedOptions: ["contentType"],
38
38
  extraChecks: createPutExtraChecks("client/`createMultipartUpload`")
39
39
  });
40
- var createMultipartUploader = _chunk4ACRCP3Xcjs.createCreateMultipartUploaderMethod.call(void 0,
40
+ var createMultipartUploader = _chunk23VLASYPcjs.createCreateMultipartUploaderMethod.call(void 0,
41
41
  {
42
42
  allowedOptions: ["contentType"],
43
43
  extraChecks: createPutExtraChecks("client/`createMultipartUpload`")
44
44
  }
45
45
  );
46
- var uploadPart = _chunk4ACRCP3Xcjs.createUploadPartMethod.call(void 0, {
46
+ var uploadPart = _chunk23VLASYPcjs.createUploadPartMethod.call(void 0, {
47
47
  allowedOptions: ["contentType"],
48
48
  extraChecks: createPutExtraChecks("client/`multipartUpload`")
49
49
  });
50
- var completeMultipartUpload = _chunk4ACRCP3Xcjs.createCompleteMultipartUploadMethod.call(void 0,
50
+ var completeMultipartUpload = _chunk23VLASYPcjs.createCompleteMultipartUploadMethod.call(void 0,
51
51
  {
52
52
  allowedOptions: ["contentType"],
53
53
  extraChecks: createPutExtraChecks("client/`completeMultipartUpload`")
54
54
  }
55
55
  );
56
- var upload = _chunk4ACRCP3Xcjs.createPutMethod.call(void 0, {
56
+ var upload = _chunk23VLASYPcjs.createPutMethod.call(void 0, {
57
57
  allowedOptions: ["contentType"],
58
58
  extraChecks(options) {
59
59
  if (options.handleUploadUrl === void 0) {
60
- throw new (0, _chunk4ACRCP3Xcjs.BlobError)(
60
+ throw new (0, _chunk23VLASYPcjs.BlobError)(
61
61
  "client/`upload` requires the 'handleUploadUrl' parameter"
62
62
  );
63
63
  }
@@ -65,10 +65,11 @@ var upload = _chunk4ACRCP3Xcjs.createPutMethod.call(void 0, {
65
65
  // @ts-expect-error -- Runtime check for DX.
66
66
  options.addRandomSuffix !== void 0 || // @ts-expect-error -- Runtime check for DX.
67
67
  options.createPutExtraChecks !== void 0 || // @ts-expect-error -- Runtime check for DX.
68
- options.cacheControlMaxAge !== void 0
68
+ options.cacheControlMaxAge !== void 0 || // @ts-expect-error -- Runtime check for DX.
69
+ options.ifMatch !== void 0
69
70
  ) {
70
- throw new (0, _chunk4ACRCP3Xcjs.BlobError)(
71
- "client/`upload` doesn't allow `addRandomSuffix`, `cacheControlMaxAge` or `allowOverwrite`. Configure these options at the server side when generating client tokens."
71
+ throw new (0, _chunk23VLASYPcjs.BlobError)(
72
+ "client/`upload` doesn't allow `addRandomSuffix`, `cacheControlMaxAge`, `allowOverwrite` or `ifMatch`. Configure these options at the server side when generating client tokens."
72
73
  );
73
74
  }
74
75
  },
@@ -118,6 +119,7 @@ async function verifyCallbackSignature({
118
119
  const verified = await globalThis.crypto.subtle.verify(
119
120
  "HMAC",
120
121
  await importKey(token),
122
+ // @ts-expect-error Buffer is compatible with BufferSource at runtime
121
123
  hexToArrayByte(signature),
122
124
  new TextEncoder().encode(body)
123
125
  );
@@ -151,7 +153,7 @@ async function handleUpload({
151
153
  onUploadCompleted
152
154
  }) {
153
155
  var _a, _b, _c, _d;
154
- const resolvedToken = _chunk4ACRCP3Xcjs.getTokenFromOptionsOrEnv.call(void 0, { token });
156
+ const resolvedToken = _chunk23VLASYPcjs.getTokenFromOptionsOrEnv.call(void 0, { token });
155
157
  const type = body.type;
156
158
  switch (type) {
157
159
  case "blob.generate-client-token": {
@@ -193,7 +195,7 @@ async function handleUpload({
193
195
  const signatureHeader = "x-vercel-signature";
194
196
  const signature = "credentials" in request ? (_c = request.headers.get(signatureHeader)) != null ? _c : "" : (_d = request.headers[signatureHeader]) != null ? _d : "";
195
197
  if (!signature) {
196
- throw new (0, _chunk4ACRCP3Xcjs.BlobError)("Missing callback signature");
198
+ throw new (0, _chunk23VLASYPcjs.BlobError)("Missing callback signature");
197
199
  }
198
200
  const isVerified = await verifyCallbackSignature({
199
201
  token: resolvedToken,
@@ -201,7 +203,7 @@ async function handleUpload({
201
203
  body: JSON.stringify(body)
202
204
  });
203
205
  if (!isVerified) {
204
- throw new (0, _chunk4ACRCP3Xcjs.BlobError)("Invalid callback signature");
206
+ throw new (0, _chunk23VLASYPcjs.BlobError)("Invalid callback signature");
205
207
  }
206
208
  if (onUploadCompleted) {
207
209
  await onUploadCompleted(body.payload);
@@ -209,7 +211,7 @@ async function handleUpload({
209
211
  return { type, response: "ok" };
210
212
  }
211
213
  default:
212
- throw new (0, _chunk4ACRCP3Xcjs.BlobError)("Invalid event type");
214
+ throw new (0, _chunk23VLASYPcjs.BlobError)("Invalid event type");
213
215
  }
214
216
  }
215
217
  async function retrieveClientToken(options) {
@@ -233,13 +235,13 @@ async function retrieveClientToken(options) {
233
235
  signal: options.abortSignal
234
236
  });
235
237
  if (!res.ok) {
236
- throw new (0, _chunk4ACRCP3Xcjs.BlobError)("Failed to retrieve the client token");
238
+ throw new (0, _chunk23VLASYPcjs.BlobError)("Failed to retrieve the client token");
237
239
  }
238
240
  try {
239
241
  const { clientToken } = await res.json();
240
242
  return clientToken;
241
243
  } catch (e) {
242
- throw new (0, _chunk4ACRCP3Xcjs.BlobError)("Failed to retrieve the client token");
244
+ throw new (0, _chunk23VLASYPcjs.BlobError)("Failed to retrieve the client token");
243
245
  }
244
246
  }
245
247
  function toAbsoluteUrl(url) {
@@ -258,16 +260,16 @@ async function generateClientTokenFromReadWriteToken({
258
260
  }) {
259
261
  var _a;
260
262
  if (typeof window !== "undefined") {
261
- throw new (0, _chunk4ACRCP3Xcjs.BlobError)(
263
+ throw new (0, _chunk23VLASYPcjs.BlobError)(
262
264
  '"generateClientTokenFromReadWriteToken" must be called from a server environment'
263
265
  );
264
266
  }
265
267
  const timestamp = /* @__PURE__ */ new Date();
266
268
  timestamp.setSeconds(timestamp.getSeconds() + 30);
267
- const readWriteToken = _chunk4ACRCP3Xcjs.getTokenFromOptionsOrEnv.call(void 0, { token });
269
+ const readWriteToken = _chunk23VLASYPcjs.getTokenFromOptionsOrEnv.call(void 0, { token });
268
270
  const [, , , storeId = null] = readWriteToken.split("_");
269
271
  if (!storeId) {
270
- throw new (0, _chunk4ACRCP3Xcjs.BlobError)(
272
+ throw new (0, _chunk23VLASYPcjs.BlobError)(
271
273
  token ? "Invalid `token` parameter" : "Invalid `BLOB_READ_WRITE_TOKEN`"
272
274
  );
273
275
  }
@@ -279,7 +281,7 @@ async function generateClientTokenFromReadWriteToken({
279
281
  ).toString("base64");
280
282
  const securedKey = await signPayload(payload, readWriteToken);
281
283
  if (!securedKey) {
282
- throw new (0, _chunk4ACRCP3Xcjs.BlobError)("Unable to sign client token");
284
+ throw new (0, _chunk23VLASYPcjs.BlobError)("Unable to sign client token");
283
285
  }
284
286
  return `vercel_blob_client_${storeId}_${Buffer.from(
285
287
  `${securedKey}.${payload}`
@@ -334,5 +336,5 @@ function getPathFromRequestUrl(url) {
334
336
 
335
337
 
336
338
 
337
- exports.completeMultipartUpload = completeMultipartUpload; exports.createFolder = _chunk4ACRCP3Xcjs.createFolder; exports.createMultipartUpload = createMultipartUpload; exports.createMultipartUploader = createMultipartUploader; exports.generateClientTokenFromReadWriteToken = generateClientTokenFromReadWriteToken; exports.getPayloadFromClientToken = getPayloadFromClientToken; exports.handleUpload = handleUpload; exports.put = put; exports.upload = upload; exports.uploadPart = uploadPart;
339
+ exports.completeMultipartUpload = completeMultipartUpload; exports.createFolder = _chunk23VLASYPcjs.createFolder; exports.createMultipartUpload = createMultipartUpload; exports.createMultipartUploader = createMultipartUploader; exports.generateClientTokenFromReadWriteToken = generateClientTokenFromReadWriteToken; exports.getPayloadFromClientToken = getPayloadFromClientToken; exports.handleUpload = handleUpload; exports.put = put; exports.upload = upload; exports.uploadPart = uploadPart;
338
340
  //# sourceMappingURL=client.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/storage/storage/packages/blob/dist/client.cjs","../src/client.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACA;ACVA,+EAAwB;AAIxB,gCAAsB;AA6DtB,SAAS,oBAAA,CAEP,UAAA,EAAoB;AACpB,EAAA,OAAO,SAAS,WAAA,CAAY,OAAA,EAAmB;AAC7C,IAAA,GAAA,CAAI,CAAC,OAAA,CAAQ,KAAA,CAAM,UAAA,CAAW,qBAAqB,CAAA,EAAG;AACpD,MAAA,MAAM,IAAI,gCAAA,CAAU,CAAA,EAAA;AACtB,IAAA;AAEA,IAAA;AAAA;AAEU,MAAA;AAEA,MAAA;AAEA,MAAA;AACR,IAAA;AACU,MAAA;AACK,QAAA;AACf,MAAA;AACF,IAAA;AACF,EAAA;AACF;AAsB4D;AACzC,EAAA;AACJ,EAAA;AACd;AAqBY;AAEQ,EAAA;AACJ,EAAA;AACd;AAkBU;AAET,EAAA;AACmB,IAAA;AACJ,IAAA;AACf,EAAA;AACF;AA4BA;AACmB,EAAA;AACJ,EAAA;AACd;AAyBU;AAET,EAAA;AACmB,IAAA;AACJ,IAAA;AACf,EAAA;AACF;AA+CoB;AACH,EAAA;AACI,EAAA;AACP,IAAA;AACA,MAAA;AACR,QAAA;AACF,MAAA;AACF,IAAA;AAEA,IAAA;AAAA;AAEU,MAAA;AAEA,MAAA;AAEA,MAAA;AACR,IAAA;AACU,MAAA;AACR,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACyB,EAAA;AApS3B,IAAA;AAqSW,IAAA;AACY,MAAA;AACjB,MAAA;AACe,MAAA;AACJ,MAAA;AACM,MAAA;AAClB,IAAA;AACH,EAAA;AACD;AAKwB;AACE,EAAA;AACvB,IAAA;AACkB,IAAA;AACI,IAAA;AACtB,IAAA;AACiB,IAAA;AACnB,EAAA;AACF;AAME;AAGwB,EAAA;AACR,IAAA;AAChB,EAAA;AAEwB,EAAA;AACtB,IAAA;AACqB,IAAA;AACH,IAAA;AACpB,EAAA;AACuB,EAAA;AACzB;AAKe;AACb,EAAA;AACA,EAAA;AACA,EAAA;AAKmB;AAEJ,EAAA;AAGS,EAAA;AAGnB,IAAA;AAGkB,IAAA;AACf,IAAA;AAGS,IAAA;AAGjB,EAAA;AAEuB,EAAA;AACrB,IAAA;AACqB,IAAA;AACN,IAAA;AACG,IAAA;AACpB,EAAA;AACO,EAAA;AACT;AAKwB;AACG,EAAA;AACF,IAAA;AACvB,EAAA;AACiB,EAAA;AAEG,EAAA;AACG,IAAA;AACvB,EAAA;AAEuB,EAAA;AACzB;AAqBgB;AAGC,EAAA;AACQ,EAAA;AAGA,EAAA;AACL,EAAA;AACpB;AAKmB;AACI,EAAA;AACJ,EAAA;AACnB;AAyIsB;AACpB,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAIA;AA7jBF,EAAA;AA8jBwB,EAAA;AAEJ,EAAA;AACJ,EAAA;AACP,IAAA;AACe,MAAA;AACF,MAAA;AACd,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACM,MAAA;AACe,MAAA;AACH,MAAA;AAGd,MAAA;AACY,QAAA;AAChB,MAAA;AAGK,MAAA;AACK,QAAA;AACN,UAAA;AACF,QAAA;AACF,MAAA;AAGM,MAAA;AACM,MAAA;AAEV,MAAA;AAGK,MAAA;AACL,QAAA;AACmB,QAAA;AACd,UAAA;AACI,UAAA;AACP,UAAA;AACA,UAAA;AAEM,YAAA;AACA,YAAA;AAEF,UAAA;AACJ,UAAA;AACD,QAAA;AACH,MAAA;AACF,IAAA;AACK,IAAA;AACG,MAAA;AAEJ,MAAA;AAKc,MAAA;AACJ,QAAA;AACZ,MAAA;AAEmB,MAAA;AACV,QAAA;AACP,QAAA;AACW,QAAA;AACZ,MAAA;AAEgB,MAAA;AACL,QAAA;AACZ,MAAA;AAEI,MAAA;AACI,QAAA;AACR,MAAA;AACe,MAAA;AACjB,IAAA;AACA,IAAA;AACsB,MAAA;AACxB,EAAA;AACF;AAKe;AAQY,EAAA;AACb,EAAA;AAI4B,EAAA;AACrB,IAAA;AACR,IAAA;AACP,MAAA;AACe,MAAA;AACI,MAAA;AACrB,IAAA;AACF,EAAA;AAEwB,EAAA;AACd,IAAA;AACa,IAAA;AACZ,IAAA;AACS,MAAA;AACL,MAAA;AACb,IAAA;AACgB,IAAA;AACjB,EAAA;AAEY,EAAA;AACS,IAAA;AACtB,EAAA;AAEI,EAAA;AACkB,IAAA;AACb,IAAA;AACD,EAAA;AACc,IAAA;AACtB,EAAA;AACF;AAKuB;AAED,EAAA;AACtB;AAKuB;AACjB,EAAA;AACqB,IAAA;AACjB,EAAA;AACC,IAAA;AACT,EAAA;AACF;AAkBsB;AACpB,EAAA;AACG,EAAA;AAC2C;AAruBhD,EAAA;AAsuBwB,EAAA;AACV,IAAA;AACR,MAAA;AACF,IAAA;AACF,EAAA;AAEkB,EAAA;AACG,EAAA;AACE,EAAA;AAEA,EAAA;AAET,EAAA;AACF,IAAA;AACA,MAAA;AACV,IAAA;AACF,EAAA;AAEuB,EAAA;AACN,IAAA;AACV,MAAA;AACS,MAAA;AACb,IAAA;AACgB,EAAA;AAEM,EAAA;AAER,EAAA;AACK,IAAA;AACtB,EAAA;AACO,EAAA;AACY,IAAA;AACC,EAAA;AACtB;AA2DwB;AACN,EAAA;AAEF,EAAA;AACJ,IAAA;AACN,MAAA;AACF,IAAA;AACO,IAAA;AACT,EAAA;AAGgB,EAAA;AACQ,IAAA;AACxB,EAAA;AAGgB,EAAA;AACN,IAAA;AACN,MAAA;AACF,IAAA;AACO,IAAA;AACT,EAAA;AAIgB,EAAA;AACE,IAAA;AACI,MAAA;AACpB,IAAA;AACgB,IAAA;AACI,MAAA;AACpB,IAAA;AACF,EAAA;AAGc,EAAA;AAGM,IAAA;AACpB,EAAA;AAEO,EAAA;AACT;AAMS;AACH,EAAA;AAEoB,IAAA;AACL,IAAA;AACX,EAAA;AACC,IAAA;AACT,EAAA;AACF;ADrjB2B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/storage/storage/packages/blob/dist/client.cjs","sourcesContent":[null,"import type { IncomingMessage } from 'node:http';\nimport * as crypto from 'crypto';\n// When bundled via a bundler supporting the `browser` field, then\n// the `undici` module will be replaced with https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\n// for browser contexts. See ./undici-browser.js and ./package.json\nimport { fetch } from 'undici';\nimport type { BlobCommandOptions, WithUploadProgress } from './helpers';\nimport { BlobError, getTokenFromOptionsOrEnv } from './helpers';\nimport type { CommonCompleteMultipartUploadOptions } from './multipart/complete';\nimport { createCompleteMultipartUploadMethod } from './multipart/complete';\nimport { createCreateMultipartUploadMethod } from './multipart/create';\nimport { createCreateMultipartUploaderMethod } from './multipart/create-uploader';\nimport type { CommonMultipartUploadOptions } from './multipart/upload';\nimport { createUploadPartMethod } from './multipart/upload';\nimport { createPutMethod } from './put';\nimport type { PutBlobResult } from './put-helpers';\n\n/**\n * Interface for put, upload and multipart upload operations.\n * This type omits all options that are encoded in the client token.\n */\nexport interface ClientCommonCreateBlobOptions {\n /**\n * Whether the blob should be publicly accessible.\n * - 'public': The blob will be publicly accessible via its URL.\n * - 'private': The blob will require authentication to access.\n */\n access: 'public' | 'private';\n /**\n * Defines the content type of the blob. By default, this value is inferred from the pathname.\n * Sent as the 'content-type' header when downloading a blob.\n */\n contentType?: string;\n /**\n * `AbortSignal` to cancel the running request. See https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal\n */\n abortSignal?: AbortSignal;\n}\n\n/**\n * Shared interface for put and multipart operations that use client tokens.\n */\nexport interface ClientTokenOptions {\n /**\n * A client token that was generated by your server using the `generateClientToken` method.\n */\n token: string;\n}\n\n/**\n * Shared interface for put and upload operations.\n * @internal This is an internal interface not intended for direct use by consumers.\n */\ninterface ClientCommonPutOptions\n extends ClientCommonCreateBlobOptions,\n WithUploadProgress {\n /**\n * Whether to use multipart upload. Use this when uploading large files.\n * It will split the file into multiple parts, upload them in parallel and retry failed parts.\n */\n multipart?: boolean;\n}\n\n/**\n * @internal Internal function to validate client token options.\n */\nfunction createPutExtraChecks<\n TOptions extends ClientTokenOptions & ClientCommonCreateBlobOptions,\n>(methodName: string) {\n return function extraChecks(options: TOptions) {\n if (!options.token.startsWith('vercel_blob_client_')) {\n throw new BlobError(`${methodName} must be called with a client token`);\n }\n\n if (\n // @ts-expect-error -- Runtime check for DX.\n options.addRandomSuffix !== undefined ||\n // @ts-expect-error -- Runtime check for DX.\n options.allowOverwrite !== undefined ||\n // @ts-expect-error -- Runtime check for DX.\n options.cacheControlMaxAge !== undefined\n ) {\n throw new BlobError(\n `${methodName} doesn't allow \\`addRandomSuffix\\`, \\`cacheControlMaxAge\\` or \\`allowOverwrite\\`. Configure these options at the server side when generating client tokens.`,\n );\n }\n };\n}\n\n/**\n * Options for the client-side put operation.\n */\nexport type ClientPutCommandOptions = ClientCommonPutOptions &\n ClientTokenOptions;\n\n/**\n * Uploads a file to the blob store using a client token.\n *\n * @param pathname - The pathname to upload the blob to, including the extension. This will influence the URL of your blob.\n * @param body - The content of your blob. Can be a string, File, Blob, Buffer or ReadableStream.\n * @param options - Configuration options including:\n * - access - (Required) Must be 'public' or 'private'. Public blobs are accessible via URL, private blobs require authentication.\n * - token - (Required) A client token generated by your server using the generateClientTokenFromReadWriteToken method.\n * - contentType - (Optional) The media type for the blob. By default, it's derived from the pathname.\n * - multipart - (Optional) Whether to use multipart upload for large files. It will split the file into multiple parts, upload them in parallel and retry failed parts.\n * - abortSignal - (Optional) AbortSignal to cancel the operation.\n * - onUploadProgress - (Optional) Callback to track upload progress: onUploadProgress(\\{loaded: number, total: number, percentage: number\\})\n * @returns A promise that resolves to the blob information, including pathname, contentType, contentDisposition, url, and downloadUrl.\n */\nexport const put = createPutMethod<ClientPutCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`put`'),\n});\n\n/**\n * Options for creating a multipart upload from the client side.\n */\nexport type ClientCreateMultipartUploadCommandOptions =\n ClientCommonCreateBlobOptions & ClientTokenOptions;\n\n/**\n * Creates a multipart upload. This is the first step in the manual multipart upload process.\n *\n * @param pathname - A string specifying the path inside the blob store. This will be the base value of the return URL and includes the filename and extension.\n * @param options - Configuration options including:\n * - access - (Required) Must be 'public' or 'private'. Public blobs are accessible via URL, private blobs require authentication.\n * - token - (Required) A client token generated by your server using the generateClientTokenFromReadWriteToken method.\n * - contentType - (Optional) The media type for the file. If not specified, it's derived from the file extension.\n * - abortSignal - (Optional) AbortSignal to cancel the operation.\n * @returns A promise that resolves to an object containing:\n * - key: A string that identifies the blob object.\n * - uploadId: A string that identifies the multipart upload. Both are needed for subsequent uploadPart calls.\n */\nexport const createMultipartUpload =\n createCreateMultipartUploadMethod<ClientCreateMultipartUploadCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`createMultipartUpload`'),\n });\n\n/**\n * Creates a multipart uploader that simplifies the multipart upload process.\n * This is a wrapper around the manual multipart upload process that provides a more convenient API.\n *\n * @param pathname - A string specifying the path inside the blob store. This will be the base value of the return URL and includes the filename and extension.\n * @param options - Configuration options including:\n * - access - (Required) Must be 'public' or 'private'. Public blobs are accessible via URL, private blobs require authentication.\n * - token - (Required) A client token generated by your server using the generateClientTokenFromReadWriteToken method.\n * - contentType - (Optional) The media type for the file. If not specified, it's derived from the file extension.\n * - abortSignal - (Optional) AbortSignal to cancel the operation.\n * @returns A promise that resolves to an uploader object with the following properties and methods:\n * - key: A string that identifies the blob object.\n * - uploadId: A string that identifies the multipart upload.\n * - uploadPart: A method to upload a part of the file.\n * - complete: A method to complete the multipart upload process.\n */\nexport const createMultipartUploader =\n createCreateMultipartUploaderMethod<ClientCreateMultipartUploadCommandOptions>(\n {\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`createMultipartUpload`'),\n },\n );\n\n/**\n * @internal Internal type for multipart upload options.\n */\ntype ClientMultipartUploadCommandOptions = ClientCommonCreateBlobOptions &\n ClientTokenOptions &\n CommonMultipartUploadOptions &\n WithUploadProgress;\n\n/**\n * Uploads a part of a multipart upload.\n * Used as part of the manual multipart upload process.\n *\n * @param pathname - Same value as the pathname parameter passed to createMultipartUpload. This will influence the final URL of your blob.\n * @param body - A blob object as ReadableStream, String, ArrayBuffer or Blob based on these supported body types. Each part must be a minimum of 5MB, except the last one which can be smaller.\n * @param options - Configuration options including:\n * - access - (Required) Must be 'public' or 'private'. Public blobs are accessible via URL, private blobs require authentication.\n * - token - (Required) A client token generated by your server using the generateClientTokenFromReadWriteToken method.\n * - uploadId - (Required) A string returned from createMultipartUpload which identifies the multipart upload.\n * - key - (Required) A string returned from createMultipartUpload which identifies the blob object.\n * - partNumber - (Required) A number identifying which part is uploaded (1-based index).\n * - contentType - (Optional) The media type for the blob. By default, it's derived from the pathname.\n * - abortSignal - (Optional) AbortSignal to cancel the running request.\n * - onUploadProgress - (Optional) Callback to track upload progress: onUploadProgress(\\{loaded: number, total: number, percentage: number\\})\n * @returns A promise that resolves to the uploaded part information containing etag and partNumber, which will be needed for the completeMultipartUpload call.\n */\nexport const uploadPart =\n createUploadPartMethod<ClientMultipartUploadCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`multipartUpload`'),\n });\n\n/**\n * @internal Internal type for completing multipart uploads.\n */\ntype ClientCompleteMultipartUploadCommandOptions =\n ClientCommonCreateBlobOptions &\n ClientTokenOptions &\n CommonCompleteMultipartUploadOptions;\n\n/**\n * Completes a multipart upload by combining all uploaded parts.\n * This is the final step in the manual multipart upload process.\n *\n * @param pathname - Same value as the pathname parameter passed to createMultipartUpload.\n * @param parts - An array containing all the uploaded parts information from previous uploadPart calls. Each part must have properties etag and partNumber.\n * @param options - Configuration options including:\n * - access - (Required) Must be 'public' or 'private'. Public blobs are accessible via URL, private blobs require authentication.\n * - token - (Required) A client token generated by your server using the generateClientTokenFromReadWriteToken method.\n * - uploadId - (Required) A string returned from createMultipartUpload which identifies the multipart upload.\n * - key - (Required) A string returned from createMultipartUpload which identifies the blob object.\n * - contentType - (Optional) The media type for the file. If not specified, it's derived from the file extension.\n * - abortSignal - (Optional) AbortSignal to cancel the operation.\n * @returns A promise that resolves to the finalized blob information, including pathname, contentType, contentDisposition, url, and downloadUrl.\n */\nexport const completeMultipartUpload =\n createCompleteMultipartUploadMethod<ClientCompleteMultipartUploadCommandOptions>(\n {\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`completeMultipartUpload`'),\n },\n );\n\n/**\n * Options for client-side upload operations.\n */\nexport interface CommonUploadOptions {\n /**\n * A route that implements the `handleUpload` function for generating a client token.\n */\n handleUploadUrl: string;\n /**\n * Additional data which will be sent to your `handleUpload` route.\n */\n clientPayload?: string;\n /**\n * Additional headers to be sent when making the request to your `handleUpload` route.\n * This is useful for sending authorization headers or any other custom headers.\n */\n headers?: Record<string, string>;\n}\n\n/**\n * Options for the upload method, which handles client-side uploads.\n */\nexport type UploadOptions = ClientCommonPutOptions & CommonUploadOptions;\n\n/**\n * Uploads a blob into your store from the client.\n * Detailed documentation can be found here: https://vercel.com/docs/vercel-blob/using-blob-sdk#client-uploads\n *\n * If you want to upload from your server instead, check out the documentation for the put operation: https://vercel.com/docs/vercel-blob/using-blob-sdk#upload-a-blob\n *\n * Unlike the put method, this method does not require a client token as it will fetch one from your server.\n *\n * @param pathname - The pathname to upload the blob to. This includes the filename and extension.\n * @param body - The contents of your blob. This has to be a supported fetch body type (string, Blob, File, ArrayBuffer, etc).\n * @param options - Configuration options including:\n * - access - (Required) Must be 'public' or 'private'. Public blobs are accessible via URL, private blobs require authentication.\n * - handleUploadUrl - (Required) A string specifying the route to call for generating client tokens for client uploads.\n * - clientPayload - (Optional) A string to be sent to your handleUpload server code. Example use-case: attaching the post id an image relates to.\n * - headers - (Optional) An object containing custom headers to be sent with the request to your handleUpload route. Example use-case: sending Authorization headers.\n * - contentType - (Optional) A string indicating the media type. By default, it's extracted from the pathname's extension.\n * - multipart - (Optional) Whether to use multipart upload for large files. It will split the file into multiple parts, upload them in parallel and retry failed parts.\n * - abortSignal - (Optional) AbortSignal to cancel the operation.\n * - onUploadProgress - (Optional) Callback to track upload progress: onUploadProgress(\\{loaded: number, total: number, percentage: number\\})\n * @returns A promise that resolves to the blob information, including pathname, contentType, contentDisposition, url, and downloadUrl.\n */\nexport const upload = createPutMethod<UploadOptions>({\n allowedOptions: ['contentType'],\n extraChecks(options) {\n if (options.handleUploadUrl === undefined) {\n throw new BlobError(\n \"client/`upload` requires the 'handleUploadUrl' parameter\",\n );\n }\n\n if (\n // @ts-expect-error -- Runtime check for DX.\n options.addRandomSuffix !== undefined ||\n // @ts-expect-error -- Runtime check for DX.\n options.createPutExtraChecks !== undefined ||\n // @ts-expect-error -- Runtime check for DX.\n options.cacheControlMaxAge !== undefined\n ) {\n throw new BlobError(\n \"client/`upload` doesn't allow `addRandomSuffix`, `cacheControlMaxAge` or `allowOverwrite`. Configure these options at the server side when generating client tokens.\",\n );\n }\n },\n async getToken(pathname, options) {\n return retrieveClientToken({\n handleUploadUrl: options.handleUploadUrl,\n pathname,\n clientPayload: options.clientPayload ?? null,\n multipart: options.multipart ?? false,\n headers: options.headers,\n });\n },\n});\n\n/**\n * @internal Internal function to import a crypto key.\n */\nasync function importKey(token: string): Promise<CryptoKey> {\n return globalThis.crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(token),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n );\n}\n\n/**\n * @internal Internal function to sign a payload.\n */\nasync function signPayload(\n payload: string,\n token: string,\n): Promise<string | undefined> {\n if (!globalThis.crypto) {\n return crypto.createHmac('sha256', token).update(payload).digest('hex');\n }\n\n const signature = await globalThis.crypto.subtle.sign(\n 'HMAC',\n await importKey(token),\n new TextEncoder().encode(payload),\n );\n return Buffer.from(new Uint8Array(signature)).toString('hex');\n}\n\n/**\n * @internal Internal function to verify a callback signature.\n */\nasync function verifyCallbackSignature({\n token,\n signature,\n body,\n}: {\n token: string;\n signature: string;\n body: string;\n}): Promise<boolean> {\n // callback signature is signed using the server token\n const secret = token;\n // Browsers, Edge runtime and Node >=20 implement the Web Crypto API\n\n if (!globalThis.crypto) {\n // Node <20 falls back to the Node.js crypto module\n const digest = crypto\n .createHmac('sha256', secret)\n .update(body)\n .digest('hex');\n const digestBuffer = Buffer.from(digest);\n const signatureBuffer = Buffer.from(signature);\n\n return (\n digestBuffer.length === signatureBuffer.length &&\n crypto.timingSafeEqual(digestBuffer, signatureBuffer)\n );\n }\n\n const verified = await globalThis.crypto.subtle.verify(\n 'HMAC',\n await importKey(token),\n hexToArrayByte(signature),\n new TextEncoder().encode(body),\n );\n return verified;\n}\n\n/**\n * @internal Internal utility function to convert hex to array byte.\n */\nfunction hexToArrayByte(input: string): Buffer {\n if (input.length % 2 !== 0) {\n throw new RangeError('Expected string to be an even number of characters');\n }\n const view = new Uint8Array(input.length / 2);\n\n for (let i = 0; i < input.length; i += 2) {\n view[i / 2] = Number.parseInt(input.substring(i, i + 2), 16);\n }\n\n return Buffer.from(view);\n}\n\n/**\n * Decoded payload from a client token.\n */\nexport type DecodedClientTokenPayload = Omit<\n GenerateClientTokenOptions,\n 'token'\n> & {\n /**\n * Timestamp in milliseconds when the token will expire.\n */\n validUntil: number;\n};\n\n/**\n * Extracts and decodes the payload from a client token.\n *\n * @param clientToken - The client token string to decode\n * @returns The decoded payload containing token options\n */\nexport function getPayloadFromClientToken(\n clientToken: string,\n): DecodedClientTokenPayload {\n const [, , , , encodedToken] = clientToken.split('_');\n const encodedPayload = Buffer.from(encodedToken ?? '', 'base64')\n .toString()\n .split('.')[1];\n const decodedPayload = Buffer.from(encodedPayload ?? '', 'base64').toString();\n return JSON.parse(decodedPayload) as DecodedClientTokenPayload;\n}\n\n/**\n * @internal Event type constants for internal use.\n */\nconst EventTypes = {\n generateClientToken: 'blob.generate-client-token',\n uploadCompleted: 'blob.upload-completed',\n} as const;\n\n/**\n * Event for generating a client token for blob uploads.\n * @internal This is an internal interface used by the SDK.\n */\ninterface GenerateClientTokenEvent {\n /**\n * Type identifier for the generate client token event.\n */\n type: (typeof EventTypes)['generateClientToken'];\n\n /**\n * Payload containing information needed to generate a client token.\n */\n payload: {\n /**\n * The destination path for the blob.\n */\n pathname: string;\n\n /**\n * Whether the upload will use multipart uploading.\n */\n multipart: boolean;\n\n /**\n * Additional data from the client which will be available in onBeforeGenerateToken.\n */\n clientPayload: string | null;\n };\n}\n\n/**\n * Event that occurs when a client upload has completed.\n * @internal This is an internal interface used by the SDK.\n */\ninterface UploadCompletedEvent {\n /**\n * Type identifier for the upload completed event.\n */\n type: (typeof EventTypes)['uploadCompleted'];\n\n /**\n * Payload containing information about the uploaded blob.\n */\n payload: {\n /**\n * Details about the blob that was uploaded.\n */\n blob: PutBlobResult;\n\n /**\n * Optional payload that was defined during token generation.\n */\n tokenPayload?: string | null;\n };\n}\n\n/**\n * Union type representing either a request to generate a client token or a notification that an upload completed.\n */\nexport type HandleUploadBody = GenerateClientTokenEvent | UploadCompletedEvent;\n\n/**\n * Type representing either a Node.js IncomingMessage or a web standard Request object.\n * @internal This is an internal type used by the SDK.\n */\ntype RequestType = IncomingMessage | Request;\n\n/**\n * Options for the handleUpload function.\n */\nexport interface HandleUploadOptions {\n /**\n * The request body containing upload information.\n */\n body: HandleUploadBody;\n\n /**\n * Function called before generating the client token for uploads.\n *\n * @param pathname - The destination path for the blob\n * @param clientPayload - A string payload specified on the client when calling upload()\n * @param multipart - A boolean specifying whether the file is a multipart upload\n *\n * @returns An object with configuration options for the client token including the optional callbackUrl\n */\n onBeforeGenerateToken: (\n pathname: string,\n clientPayload: string | null,\n multipart: boolean,\n ) => Promise<\n Pick<\n GenerateClientTokenOptions,\n | 'allowedContentTypes'\n | 'maximumSizeInBytes'\n | 'validUntil'\n | 'addRandomSuffix'\n | 'allowOverwrite'\n | 'cacheControlMaxAge'\n > & { tokenPayload?: string | null; callbackUrl?: string }\n >;\n\n /**\n * Function called by Vercel Blob when the client upload finishes.\n * This is useful to update your database with the blob URL that was uploaded.\n *\n * @param body - Contains information about the completed upload including the blob details\n */\n onUploadCompleted?: (body: UploadCompletedEvent['payload']) => Promise<void>;\n\n /**\n * A string specifying the read-write token to use when making requests.\n * It defaults to process.env.BLOB_READ_WRITE_TOKEN when deployed on Vercel.\n */\n token?: string;\n\n /**\n * An IncomingMessage or Request object to be used to determine the action to take.\n */\n request: RequestType;\n}\n\n/**\n * A server-side route helper to manage client uploads. It has two responsibilities:\n * 1. Generate tokens for client uploads\n * 2. Listen for completed client uploads, so you can update your database with the URL of the uploaded file\n *\n * @param options - Configuration options for handling uploads\n * - request - (Required) An IncomingMessage or Request object to be used to determine the action to take.\n * - body - (Required) The request body containing upload information.\n * - onBeforeGenerateToken - (Required) Function called before generating the client token for uploads.\n * - onUploadCompleted - (Optional) Function called by Vercel Blob when the client upload finishes.\n * - token - (Optional) A string specifying the read-write token to use when making requests. Defaults to process.env.BLOB_READ_WRITE_TOKEN.\n * @returns A promise that resolves to either a client token generation result or an upload completion result\n */\nexport async function handleUpload({\n token,\n request,\n body,\n onBeforeGenerateToken,\n onUploadCompleted,\n}: HandleUploadOptions): Promise<\n | { type: 'blob.generate-client-token'; clientToken: string }\n | { type: 'blob.upload-completed'; response: 'ok' }\n> {\n const resolvedToken = getTokenFromOptionsOrEnv({ token });\n\n const type = body.type;\n switch (type) {\n case 'blob.generate-client-token': {\n const { pathname, clientPayload, multipart } = body.payload;\n const payload = await onBeforeGenerateToken(\n pathname,\n clientPayload,\n multipart,\n );\n const tokenPayload = payload.tokenPayload ?? clientPayload;\n const { callbackUrl: providedCallbackUrl, ...tokenOptions } = payload;\n let callbackUrl = providedCallbackUrl;\n\n // If onUploadCompleted is provided but no callbackUrl was provided, try to infer it from environment\n if (onUploadCompleted && !callbackUrl) {\n callbackUrl = getCallbackUrl(request);\n }\n\n // If no onUploadCompleted but callbackUrl was provided, warn about it\n if (!onUploadCompleted && callbackUrl) {\n console.warn(\n 'callbackUrl was provided but onUploadCompleted is not defined. The callback will not be handled.',\n );\n }\n\n // one hour\n const oneHourInSeconds = 60 * 60;\n const now = new Date();\n const validUntil =\n payload.validUntil ??\n now.setSeconds(now.getSeconds() + oneHourInSeconds);\n\n return {\n type,\n clientToken: await generateClientTokenFromReadWriteToken({\n ...tokenOptions,\n token: resolvedToken,\n pathname,\n onUploadCompleted: callbackUrl\n ? {\n callbackUrl,\n tokenPayload,\n }\n : undefined,\n validUntil,\n }),\n };\n }\n case 'blob.upload-completed': {\n const signatureHeader = 'x-vercel-signature';\n const signature = (\n 'credentials' in request\n ? (request.headers.get(signatureHeader) ?? '')\n : (request.headers[signatureHeader] ?? '')\n ) as string;\n\n if (!signature) {\n throw new BlobError('Missing callback signature');\n }\n\n const isVerified = await verifyCallbackSignature({\n token: resolvedToken,\n signature,\n body: JSON.stringify(body),\n });\n\n if (!isVerified) {\n throw new BlobError('Invalid callback signature');\n }\n\n if (onUploadCompleted) {\n await onUploadCompleted(body.payload);\n }\n return { type, response: 'ok' };\n }\n default:\n throw new BlobError('Invalid event type');\n }\n}\n\n/**\n * @internal Internal function to retrieve a client token from server.\n */\nasync function retrieveClientToken(options: {\n pathname: string;\n handleUploadUrl: string;\n clientPayload: string | null;\n multipart: boolean;\n abortSignal?: AbortSignal;\n headers?: Record<string, string>;\n}): Promise<string> {\n const { handleUploadUrl, pathname } = options;\n const url = isAbsoluteUrl(handleUploadUrl)\n ? handleUploadUrl\n : toAbsoluteUrl(handleUploadUrl);\n\n const event: GenerateClientTokenEvent = {\n type: EventTypes.generateClientToken,\n payload: {\n pathname,\n clientPayload: options.clientPayload,\n multipart: options.multipart,\n },\n };\n\n const res = await fetch(url, {\n method: 'POST',\n body: JSON.stringify(event),\n headers: {\n 'content-type': 'application/json',\n ...options.headers,\n },\n signal: options.abortSignal,\n });\n\n if (!res.ok) {\n throw new BlobError('Failed to retrieve the client token');\n }\n\n try {\n const { clientToken } = (await res.json()) as { clientToken: string };\n return clientToken;\n } catch {\n throw new BlobError('Failed to retrieve the client token');\n }\n}\n\n/**\n * @internal Internal utility to convert a relative URL to absolute URL.\n */\nfunction toAbsoluteUrl(url: string): string {\n // location is available in web workers too: https://developer.mozilla.org/en-US/docs/Web/API/Window/location\n return new URL(url, location.href).href;\n}\n\n/**\n * @internal Internal utility to check if a URL is absolute.\n */\nfunction isAbsoluteUrl(url: string): boolean {\n try {\n return Boolean(new URL(url));\n } catch {\n return false;\n }\n}\n\n/**\n * Generates a client token from a read-write token. This function must be called from a server environment.\n * The client token contains permissions and constraints that limit what the client can do.\n *\n * @param options - Options for generating the client token\n * - pathname - (Required) The destination path for the blob.\n * - token - (Optional) A string specifying the read-write token to use. Defaults to process.env.BLOB_READ_WRITE_TOKEN.\n * - onUploadCompleted - (Optional) Configuration for upload completion callback.\n * - maximumSizeInBytes - (Optional) A number specifying the maximum size in bytes that can be uploaded (max 5TB).\n * - allowedContentTypes - (Optional) An array of media types that are allowed to be uploaded. Wildcards are supported (text/*).\n * - validUntil - (Optional) A timestamp in ms when the token will expire. Defaults to one hour from generation.\n * - addRandomSuffix - (Optional) Whether to add a random suffix to the filename. Defaults to false.\n * - allowOverwrite - (Optional) Whether to allow overwriting existing blobs. Defaults to false.\n * - cacheControlMaxAge - (Optional) Number of seconds to configure cache duration. Defaults to one month.\n * @returns A promise that resolves to the generated client token string which can be used in client-side upload operations.\n */\nexport async function generateClientTokenFromReadWriteToken({\n token,\n ...argsWithoutToken\n}: GenerateClientTokenOptions): Promise<string> {\n if (typeof window !== 'undefined') {\n throw new BlobError(\n '\"generateClientTokenFromReadWriteToken\" must be called from a server environment',\n );\n }\n\n const timestamp = new Date();\n timestamp.setSeconds(timestamp.getSeconds() + 30);\n const readWriteToken = getTokenFromOptionsOrEnv({ token });\n\n const [, , , storeId = null] = readWriteToken.split('_');\n\n if (!storeId) {\n throw new BlobError(\n token ? 'Invalid `token` parameter' : 'Invalid `BLOB_READ_WRITE_TOKEN`',\n );\n }\n\n const payload = Buffer.from(\n JSON.stringify({\n ...argsWithoutToken,\n validUntil: argsWithoutToken.validUntil ?? timestamp.getTime(),\n }),\n ).toString('base64');\n\n const securedKey = await signPayload(payload, readWriteToken);\n\n if (!securedKey) {\n throw new BlobError('Unable to sign client token');\n }\n return `vercel_blob_client_${storeId}_${Buffer.from(\n `${securedKey}.${payload}`,\n ).toString('base64')}`;\n}\n\n/**\n * Options for generating a client token.\n */\nexport interface GenerateClientTokenOptions extends BlobCommandOptions {\n /**\n * The destination path for the blob\n */\n pathname: string;\n\n /**\n * Configuration for upload completion callback\n */\n onUploadCompleted?: {\n callbackUrl: string;\n tokenPayload?: string | null;\n };\n\n /**\n * A number specifying the maximum size in bytes that can be uploaded. The maximum is 5TB.\n */\n maximumSizeInBytes?: number;\n\n /**\n * An array of strings specifying the media type that are allowed to be uploaded.\n * By default, it's all content types. Wildcards are supported (text/*)\n */\n allowedContentTypes?: string[];\n\n /**\n * A number specifying the timestamp in ms when the token will expire.\n * By default, it's now + 1 hour.\n */\n validUntil?: number;\n\n /**\n * Adds a random suffix to the filename.\n * @defaultvalue false\n */\n addRandomSuffix?: boolean;\n\n /**\n * Allow overwriting an existing blob. By default this is set to false and will throw an error if the blob already exists.\n * @defaultvalue false\n */\n allowOverwrite?: boolean;\n\n /**\n * Number in seconds to configure how long Blobs are cached. Defaults to one month. Cannot be set to a value lower than 1 minute.\n * @defaultvalue 30 * 24 * 60 * 60 (1 Month)\n */\n cacheControlMaxAge?: number;\n}\n\n/**\n * @internal Helper function to determine the callback URL for client uploads\n * when onUploadCompleted is provided but no callbackUrl was specified\n */\nfunction getCallbackUrl(request: RequestType): string | undefined {\n const reqPath = getPathFromRequestUrl(request.url!);\n\n if (!reqPath) {\n console.warn(\n 'onUploadCompleted provided but no callbackUrl could be determined. Please provide a callbackUrl in onBeforeGenerateToken or set the VERCEL_BLOB_CALLBACK_URL environment variable.',\n );\n return undefined;\n }\n\n // Check if we have VERCEL_BLOB_CALLBACK_URL env var (works on or off Vercel)\n if (process.env.VERCEL_BLOB_CALLBACK_URL) {\n return `${process.env.VERCEL_BLOB_CALLBACK_URL}${reqPath}`;\n }\n\n // Not hosted on Vercel and no VERCEL_BLOB_CALLBACK_URL\n if (process.env.VERCEL !== '1') {\n console.warn(\n 'onUploadCompleted provided but no callbackUrl could be determined. Please provide a callbackUrl in onBeforeGenerateToken or set the VERCEL_BLOB_CALLBACK_URL environment variable.',\n );\n return undefined;\n }\n\n // If hosted on Vercel, generate default callbackUrl\n\n if (process.env.VERCEL_ENV === 'preview') {\n if (process.env.VERCEL_BRANCH_URL) {\n return `https://${process.env.VERCEL_BRANCH_URL}${reqPath}`;\n }\n if (process.env.VERCEL_URL) {\n return `https://${process.env.VERCEL_URL}${reqPath}`;\n }\n }\n\n if (\n process.env.VERCEL_ENV === 'production' &&\n process.env.VERCEL_PROJECT_PRODUCTION_URL\n ) {\n return `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}${reqPath}`;\n }\n\n return undefined;\n}\n\n/**\n * @internal Helper function to safely extract pathname and query string from request URL\n * Handles both full URLs (http://localhost:3000/api/upload?test=1) and relative paths (/api/upload?test=1)\n */\nfunction getPathFromRequestUrl(url: string): string | null {\n try {\n // Using dummy.com as base URL to handle relative paths\n const parsedUrl = new URL(url, 'https://dummy.com');\n return parsedUrl.pathname + parsedUrl.search;\n } catch {\n return null;\n }\n}\n\nexport { createFolder } from './create-folder';\n"]}
1
+ {"version":3,"sources":["/home/runner/work/storage/storage/packages/blob/dist/client.cjs","../src/client.ts"],"names":[],"mappings":"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","file":"/home/runner/work/storage/storage/packages/blob/dist/client.cjs","sourcesContent":[null,"import type { IncomingMessage } from 'node:http';\nimport * as crypto from 'crypto';\n// When bundled via a bundler supporting the `browser` field, then\n// the `undici` module will be replaced with https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\n// for browser contexts. See ./undici-browser.js and ./package.json\nimport { fetch } from 'undici';\nimport type { BlobCommandOptions, WithUploadProgress } from './helpers';\nimport { BlobError, getTokenFromOptionsOrEnv } from './helpers';\nimport type { CommonCompleteMultipartUploadOptions } from './multipart/complete';\nimport { createCompleteMultipartUploadMethod } from './multipart/complete';\nimport { createCreateMultipartUploadMethod } from './multipart/create';\nimport { createCreateMultipartUploaderMethod } from './multipart/create-uploader';\nimport type { CommonMultipartUploadOptions } from './multipart/upload';\nimport { createUploadPartMethod } from './multipart/upload';\nimport { createPutMethod } from './put';\nimport type { PutBlobResult } from './put-helpers';\n\n/**\n * Interface for put, upload and multipart upload operations.\n * This type omits all options that are encoded in the client token.\n */\nexport interface ClientCommonCreateBlobOptions {\n /**\n * Whether the blob should be publicly accessible.\n */\n access: 'public';\n /**\n * Defines the content type of the blob. By default, this value is inferred from the pathname.\n * Sent as the 'content-type' header when downloading a blob.\n */\n contentType?: string;\n /**\n * `AbortSignal` to cancel the running request. See https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal\n */\n abortSignal?: AbortSignal;\n}\n\n/**\n * Shared interface for put and multipart operations that use client tokens.\n */\nexport interface ClientTokenOptions {\n /**\n * A client token that was generated by your server using the `generateClientToken` method.\n */\n token: string;\n}\n\n/**\n * Shared interface for put and upload operations.\n * @internal This is an internal interface not intended for direct use by consumers.\n */\ninterface ClientCommonPutOptions\n extends ClientCommonCreateBlobOptions,\n WithUploadProgress {\n /**\n * Whether to use multipart upload. Use this when uploading large files.\n * It will split the file into multiple parts, upload them in parallel and retry failed parts.\n */\n multipart?: boolean;\n}\n\n/**\n * @internal Internal function to validate client token options.\n */\nfunction createPutExtraChecks<\n TOptions extends ClientTokenOptions & ClientCommonCreateBlobOptions,\n>(methodName: string) {\n return function extraChecks(options: TOptions) {\n if (!options.token.startsWith('vercel_blob_client_')) {\n throw new BlobError(`${methodName} must be called with a client token`);\n }\n\n if (\n // @ts-expect-error -- Runtime check for DX.\n options.addRandomSuffix !== undefined ||\n // @ts-expect-error -- Runtime check for DX.\n options.allowOverwrite !== undefined ||\n // @ts-expect-error -- Runtime check for DX.\n options.cacheControlMaxAge !== undefined\n ) {\n throw new BlobError(\n `${methodName} doesn't allow \\`addRandomSuffix\\`, \\`cacheControlMaxAge\\` or \\`allowOverwrite\\`. Configure these options at the server side when generating client tokens.`,\n );\n }\n };\n}\n\n/**\n * Options for the client-side put operation.\n */\nexport type ClientPutCommandOptions = ClientCommonPutOptions &\n ClientTokenOptions;\n\n/**\n * Uploads a file to the blob store using a client token.\n *\n * @param pathname - The pathname to upload the blob to, including the extension. This will influence the URL of your blob.\n * @param body - The content of your blob. Can be a string, File, Blob, Buffer or ReadableStream.\n * @param options - Configuration options including:\n * - access - (Required) Must be 'public' as blobs are publicly accessible.\n * - token - (Required) A client token generated by your server using the generateClientTokenFromReadWriteToken method.\n * - contentType - (Optional) The media type for the blob. By default, it's derived from the pathname.\n * - multipart - (Optional) Whether to use multipart upload for large files. It will split the file into multiple parts, upload them in parallel and retry failed parts.\n * - abortSignal - (Optional) AbortSignal to cancel the operation.\n * - onUploadProgress - (Optional) Callback to track upload progress: onUploadProgress(\\{loaded: number, total: number, percentage: number\\})\n * @returns A promise that resolves to the blob information, including pathname, contentType, contentDisposition, url, and downloadUrl.\n */\nexport const put = createPutMethod<ClientPutCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`put`'),\n});\n\n/**\n * Options for creating a multipart upload from the client side.\n */\nexport type ClientCreateMultipartUploadCommandOptions =\n ClientCommonCreateBlobOptions & ClientTokenOptions;\n\n/**\n * Creates a multipart upload. This is the first step in the manual multipart upload process.\n *\n * @param pathname - A string specifying the path inside the blob store. This will be the base value of the return URL and includes the filename and extension.\n * @param options - Configuration options including:\n * - access - (Required) Must be 'public' as blobs are publicly accessible.\n * - token - (Required) A client token generated by your server using the generateClientTokenFromReadWriteToken method.\n * - contentType - (Optional) The media type for the file. If not specified, it's derived from the file extension.\n * - abortSignal - (Optional) AbortSignal to cancel the operation.\n * @returns A promise that resolves to an object containing:\n * - key: A string that identifies the blob object.\n * - uploadId: A string that identifies the multipart upload. Both are needed for subsequent uploadPart calls.\n */\nexport const createMultipartUpload =\n createCreateMultipartUploadMethod<ClientCreateMultipartUploadCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`createMultipartUpload`'),\n });\n\n/**\n * Creates a multipart uploader that simplifies the multipart upload process.\n * This is a wrapper around the manual multipart upload process that provides a more convenient API.\n *\n * @param pathname - A string specifying the path inside the blob store. This will be the base value of the return URL and includes the filename and extension.\n * @param options - Configuration options including:\n * - access - (Required) Must be 'public' as blobs are publicly accessible.\n * - token - (Required) A client token generated by your server using the generateClientTokenFromReadWriteToken method.\n * - contentType - (Optional) The media type for the file. If not specified, it's derived from the file extension.\n * - abortSignal - (Optional) AbortSignal to cancel the operation.\n * @returns A promise that resolves to an uploader object with the following properties and methods:\n * - key: A string that identifies the blob object.\n * - uploadId: A string that identifies the multipart upload.\n * - uploadPart: A method to upload a part of the file.\n * - complete: A method to complete the multipart upload process.\n */\nexport const createMultipartUploader =\n createCreateMultipartUploaderMethod<ClientCreateMultipartUploadCommandOptions>(\n {\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`createMultipartUpload`'),\n },\n );\n\n/**\n * @internal Internal type for multipart upload options.\n */\ntype ClientMultipartUploadCommandOptions = ClientCommonCreateBlobOptions &\n ClientTokenOptions &\n CommonMultipartUploadOptions &\n WithUploadProgress;\n\n/**\n * Uploads a part of a multipart upload.\n * Used as part of the manual multipart upload process.\n *\n * @param pathname - Same value as the pathname parameter passed to createMultipartUpload. This will influence the final URL of your blob.\n * @param body - A blob object as ReadableStream, String, ArrayBuffer or Blob based on these supported body types. Each part must be a minimum of 5MB, except the last one which can be smaller.\n * @param options - Configuration options including:\n * - access - (Required) Must be 'public' as blobs are publicly accessible.\n * - token - (Required) A client token generated by your server using the generateClientTokenFromReadWriteToken method.\n * - uploadId - (Required) A string returned from createMultipartUpload which identifies the multipart upload.\n * - key - (Required) A string returned from createMultipartUpload which identifies the blob object.\n * - partNumber - (Required) A number identifying which part is uploaded (1-based index).\n * - contentType - (Optional) The media type for the blob. By default, it's derived from the pathname.\n * - abortSignal - (Optional) AbortSignal to cancel the running request.\n * - onUploadProgress - (Optional) Callback to track upload progress: onUploadProgress(\\{loaded: number, total: number, percentage: number\\})\n * @returns A promise that resolves to the uploaded part information containing etag and partNumber, which will be needed for the completeMultipartUpload call.\n */\nexport const uploadPart =\n createUploadPartMethod<ClientMultipartUploadCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`multipartUpload`'),\n });\n\n/**\n * @internal Internal type for completing multipart uploads.\n */\ntype ClientCompleteMultipartUploadCommandOptions =\n ClientCommonCreateBlobOptions &\n ClientTokenOptions &\n CommonCompleteMultipartUploadOptions;\n\n/**\n * Completes a multipart upload by combining all uploaded parts.\n * This is the final step in the manual multipart upload process.\n *\n * @param pathname - Same value as the pathname parameter passed to createMultipartUpload.\n * @param parts - An array containing all the uploaded parts information from previous uploadPart calls. Each part must have properties etag and partNumber.\n * @param options - Configuration options including:\n * - access - (Required) Must be 'public' as blobs are publicly accessible.\n * - token - (Required) A client token generated by your server using the generateClientTokenFromReadWriteToken method.\n * - uploadId - (Required) A string returned from createMultipartUpload which identifies the multipart upload.\n * - key - (Required) A string returned from createMultipartUpload which identifies the blob object.\n * - contentType - (Optional) The media type for the file. If not specified, it's derived from the file extension.\n * - abortSignal - (Optional) AbortSignal to cancel the operation.\n * @returns A promise that resolves to the finalized blob information, including pathname, contentType, contentDisposition, url, and downloadUrl.\n */\nexport const completeMultipartUpload =\n createCompleteMultipartUploadMethod<ClientCompleteMultipartUploadCommandOptions>(\n {\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`completeMultipartUpload`'),\n },\n );\n\n/**\n * Options for client-side upload operations.\n */\nexport interface CommonUploadOptions {\n /**\n * A route that implements the `handleUpload` function for generating a client token.\n */\n handleUploadUrl: string;\n /**\n * Additional data which will be sent to your `handleUpload` route.\n */\n clientPayload?: string;\n /**\n * Additional headers to be sent when making the request to your `handleUpload` route.\n * This is useful for sending authorization headers or any other custom headers.\n */\n headers?: Record<string, string>;\n}\n\n/**\n * Options for the upload method, which handles client-side uploads.\n */\nexport type UploadOptions = ClientCommonPutOptions & CommonUploadOptions;\n\n/**\n * Uploads a blob into your store from the client.\n * Detailed documentation can be found here: https://vercel.com/docs/vercel-blob/using-blob-sdk#client-uploads\n *\n * If you want to upload from your server instead, check out the documentation for the put operation: https://vercel.com/docs/vercel-blob/using-blob-sdk#upload-a-blob\n *\n * Unlike the put method, this method does not require a client token as it will fetch one from your server.\n *\n * @param pathname - The pathname to upload the blob to. This includes the filename and extension.\n * @param body - The contents of your blob. This has to be a supported fetch body type (string, Blob, File, ArrayBuffer, etc).\n * @param options - Configuration options including:\n * - access - (Required) Must be 'public' as blobs are publicly accessible.\n * - handleUploadUrl - (Required) A string specifying the route to call for generating client tokens for client uploads.\n * - clientPayload - (Optional) A string to be sent to your handleUpload server code. Example use-case: attaching the post id an image relates to.\n * - headers - (Optional) An object containing custom headers to be sent with the request to your handleUpload route. Example use-case: sending Authorization headers.\n * - contentType - (Optional) A string indicating the media type. By default, it's extracted from the pathname's extension.\n * - multipart - (Optional) Whether to use multipart upload for large files. It will split the file into multiple parts, upload them in parallel and retry failed parts.\n * - abortSignal - (Optional) AbortSignal to cancel the operation.\n * - onUploadProgress - (Optional) Callback to track upload progress: onUploadProgress(\\{loaded: number, total: number, percentage: number\\})\n * @returns A promise that resolves to the blob information, including pathname, contentType, contentDisposition, url, and downloadUrl.\n */\nexport const upload = createPutMethod<UploadOptions>({\n allowedOptions: ['contentType'],\n extraChecks(options) {\n if (options.handleUploadUrl === undefined) {\n throw new BlobError(\n \"client/`upload` requires the 'handleUploadUrl' parameter\",\n );\n }\n\n if (\n // @ts-expect-error -- Runtime check for DX.\n options.addRandomSuffix !== undefined ||\n // @ts-expect-error -- Runtime check for DX.\n options.createPutExtraChecks !== undefined ||\n // @ts-expect-error -- Runtime check for DX.\n options.cacheControlMaxAge !== undefined ||\n // @ts-expect-error -- Runtime check for DX.\n options.ifMatch !== undefined\n ) {\n throw new BlobError(\n \"client/`upload` doesn't allow `addRandomSuffix`, `cacheControlMaxAge`, `allowOverwrite` or `ifMatch`. Configure these options at the server side when generating client tokens.\",\n );\n }\n },\n async getToken(pathname, options) {\n return retrieveClientToken({\n handleUploadUrl: options.handleUploadUrl,\n pathname,\n clientPayload: options.clientPayload ?? null,\n multipart: options.multipart ?? false,\n headers: options.headers,\n });\n },\n});\n\n/**\n * @internal Internal function to import a crypto key.\n */\nasync function importKey(token: string): Promise<CryptoKey> {\n return globalThis.crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(token),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n );\n}\n\n/**\n * @internal Internal function to sign a payload.\n */\nasync function signPayload(\n payload: string,\n token: string,\n): Promise<string | undefined> {\n if (!globalThis.crypto) {\n return crypto.createHmac('sha256', token).update(payload).digest('hex');\n }\n\n const signature = await globalThis.crypto.subtle.sign(\n 'HMAC',\n await importKey(token),\n new TextEncoder().encode(payload),\n );\n return Buffer.from(new Uint8Array(signature)).toString('hex');\n}\n\n/**\n * @internal Internal function to verify a callback signature.\n */\nasync function verifyCallbackSignature({\n token,\n signature,\n body,\n}: {\n token: string;\n signature: string;\n body: string;\n}): Promise<boolean> {\n // callback signature is signed using the server token\n const secret = token;\n // Browsers, Edge runtime and Node >=20 implement the Web Crypto API\n\n if (!globalThis.crypto) {\n // Node <20 falls back to the Node.js crypto module\n const digest = crypto\n .createHmac('sha256', secret)\n .update(body)\n .digest('hex');\n const digestBuffer = Buffer.from(digest);\n const signatureBuffer = Buffer.from(signature);\n\n return (\n digestBuffer.length === signatureBuffer.length &&\n crypto.timingSafeEqual(digestBuffer, signatureBuffer)\n );\n }\n\n const verified = await globalThis.crypto.subtle.verify(\n 'HMAC',\n await importKey(token),\n // @ts-expect-error Buffer is compatible with BufferSource at runtime\n hexToArrayByte(signature),\n new TextEncoder().encode(body),\n );\n return verified;\n}\n\n/**\n * @internal Internal utility function to convert hex to array byte.\n */\nfunction hexToArrayByte(input: string): Buffer {\n if (input.length % 2 !== 0) {\n throw new RangeError('Expected string to be an even number of characters');\n }\n const view = new Uint8Array(input.length / 2);\n\n for (let i = 0; i < input.length; i += 2) {\n view[i / 2] = Number.parseInt(input.substring(i, i + 2), 16);\n }\n\n return Buffer.from(view);\n}\n\n/**\n * Decoded payload from a client token.\n */\nexport type DecodedClientTokenPayload = Omit<\n GenerateClientTokenOptions,\n 'token'\n> & {\n /**\n * Timestamp in milliseconds when the token will expire.\n */\n validUntil: number;\n};\n\n/**\n * Extracts and decodes the payload from a client token.\n *\n * @param clientToken - The client token string to decode\n * @returns The decoded payload containing token options\n */\nexport function getPayloadFromClientToken(\n clientToken: string,\n): DecodedClientTokenPayload {\n const [, , , , encodedToken] = clientToken.split('_');\n const encodedPayload = Buffer.from(encodedToken ?? '', 'base64')\n .toString()\n .split('.')[1];\n const decodedPayload = Buffer.from(encodedPayload ?? '', 'base64').toString();\n return JSON.parse(decodedPayload) as DecodedClientTokenPayload;\n}\n\n/**\n * @internal Event type constants for internal use.\n */\nconst EventTypes = {\n generateClientToken: 'blob.generate-client-token',\n uploadCompleted: 'blob.upload-completed',\n} as const;\n\n/**\n * Event for generating a client token for blob uploads.\n * @internal This is an internal interface used by the SDK.\n */\ninterface GenerateClientTokenEvent {\n /**\n * Type identifier for the generate client token event.\n */\n type: (typeof EventTypes)['generateClientToken'];\n\n /**\n * Payload containing information needed to generate a client token.\n */\n payload: {\n /**\n * The destination path for the blob.\n */\n pathname: string;\n\n /**\n * Whether the upload will use multipart uploading.\n */\n multipart: boolean;\n\n /**\n * Additional data from the client which will be available in onBeforeGenerateToken.\n */\n clientPayload: string | null;\n };\n}\n\n/**\n * Event that occurs when a client upload has completed.\n * @internal This is an internal interface used by the SDK.\n */\ninterface UploadCompletedEvent {\n /**\n * Type identifier for the upload completed event.\n */\n type: (typeof EventTypes)['uploadCompleted'];\n\n /**\n * Payload containing information about the uploaded blob.\n */\n payload: {\n /**\n * Details about the blob that was uploaded.\n */\n blob: PutBlobResult;\n\n /**\n * Optional payload that was defined during token generation.\n */\n tokenPayload?: string | null;\n };\n}\n\n/**\n * Union type representing either a request to generate a client token or a notification that an upload completed.\n */\nexport type HandleUploadBody = GenerateClientTokenEvent | UploadCompletedEvent;\n\n/**\n * Type representing either a Node.js IncomingMessage or a web standard Request object.\n * @internal This is an internal type used by the SDK.\n */\ntype RequestType = IncomingMessage | Request;\n\n/**\n * Options for the handleUpload function.\n */\nexport interface HandleUploadOptions {\n /**\n * The request body containing upload information.\n */\n body: HandleUploadBody;\n\n /**\n * Function called before generating the client token for uploads.\n *\n * @param pathname - The destination path for the blob\n * @param clientPayload - A string payload specified on the client when calling upload()\n * @param multipart - A boolean specifying whether the file is a multipart upload\n *\n * @returns An object with configuration options for the client token including the optional callbackUrl\n */\n onBeforeGenerateToken: (\n pathname: string,\n clientPayload: string | null,\n multipart: boolean,\n ) => Promise<\n Pick<\n GenerateClientTokenOptions,\n | 'allowedContentTypes'\n | 'maximumSizeInBytes'\n | 'validUntil'\n | 'addRandomSuffix'\n | 'allowOverwrite'\n | 'cacheControlMaxAge'\n | 'ifMatch'\n > & { tokenPayload?: string | null; callbackUrl?: string }\n >;\n\n /**\n * Function called by Vercel Blob when the client upload finishes.\n * This is useful to update your database with the blob URL that was uploaded.\n *\n * @param body - Contains information about the completed upload including the blob details\n */\n onUploadCompleted?: (body: UploadCompletedEvent['payload']) => Promise<void>;\n\n /**\n * A string specifying the read-write token to use when making requests.\n * It defaults to process.env.BLOB_READ_WRITE_TOKEN when deployed on Vercel.\n */\n token?: string;\n\n /**\n * An IncomingMessage or Request object to be used to determine the action to take.\n */\n request: RequestType;\n}\n\n/**\n * A server-side route helper to manage client uploads. It has two responsibilities:\n * 1. Generate tokens for client uploads\n * 2. Listen for completed client uploads, so you can update your database with the URL of the uploaded file\n *\n * @param options - Configuration options for handling uploads\n * - request - (Required) An IncomingMessage or Request object to be used to determine the action to take.\n * - body - (Required) The request body containing upload information.\n * - onBeforeGenerateToken - (Required) Function called before generating the client token for uploads.\n * - onUploadCompleted - (Optional) Function called by Vercel Blob when the client upload finishes.\n * - token - (Optional) A string specifying the read-write token to use when making requests. Defaults to process.env.BLOB_READ_WRITE_TOKEN.\n * @returns A promise that resolves to either a client token generation result or an upload completion result\n */\nexport async function handleUpload({\n token,\n request,\n body,\n onBeforeGenerateToken,\n onUploadCompleted,\n}: HandleUploadOptions): Promise<\n | { type: 'blob.generate-client-token'; clientToken: string }\n | { type: 'blob.upload-completed'; response: 'ok' }\n> {\n const resolvedToken = getTokenFromOptionsOrEnv({ token });\n\n const type = body.type;\n switch (type) {\n case 'blob.generate-client-token': {\n const { pathname, clientPayload, multipart } = body.payload;\n const payload = await onBeforeGenerateToken(\n pathname,\n clientPayload,\n multipart,\n );\n const tokenPayload = payload.tokenPayload ?? clientPayload;\n const { callbackUrl: providedCallbackUrl, ...tokenOptions } = payload;\n let callbackUrl = providedCallbackUrl;\n\n // If onUploadCompleted is provided but no callbackUrl was provided, try to infer it from environment\n if (onUploadCompleted && !callbackUrl) {\n callbackUrl = getCallbackUrl(request);\n }\n\n // If no onUploadCompleted but callbackUrl was provided, warn about it\n if (!onUploadCompleted && callbackUrl) {\n console.warn(\n 'callbackUrl was provided but onUploadCompleted is not defined. The callback will not be handled.',\n );\n }\n\n // one hour\n const oneHourInSeconds = 60 * 60;\n const now = new Date();\n const validUntil =\n payload.validUntil ??\n now.setSeconds(now.getSeconds() + oneHourInSeconds);\n\n return {\n type,\n clientToken: await generateClientTokenFromReadWriteToken({\n ...tokenOptions,\n token: resolvedToken,\n pathname,\n onUploadCompleted: callbackUrl\n ? {\n callbackUrl,\n tokenPayload,\n }\n : undefined,\n validUntil,\n }),\n };\n }\n case 'blob.upload-completed': {\n const signatureHeader = 'x-vercel-signature';\n const signature = (\n 'credentials' in request\n ? (request.headers.get(signatureHeader) ?? '')\n : (request.headers[signatureHeader] ?? '')\n ) as string;\n\n if (!signature) {\n throw new BlobError('Missing callback signature');\n }\n\n const isVerified = await verifyCallbackSignature({\n token: resolvedToken,\n signature,\n body: JSON.stringify(body),\n });\n\n if (!isVerified) {\n throw new BlobError('Invalid callback signature');\n }\n\n if (onUploadCompleted) {\n await onUploadCompleted(body.payload);\n }\n return { type, response: 'ok' };\n }\n default:\n throw new BlobError('Invalid event type');\n }\n}\n\n/**\n * @internal Internal function to retrieve a client token from server.\n */\nasync function retrieveClientToken(options: {\n pathname: string;\n handleUploadUrl: string;\n clientPayload: string | null;\n multipart: boolean;\n abortSignal?: AbortSignal;\n headers?: Record<string, string>;\n}): Promise<string> {\n const { handleUploadUrl, pathname } = options;\n const url = isAbsoluteUrl(handleUploadUrl)\n ? handleUploadUrl\n : toAbsoluteUrl(handleUploadUrl);\n\n const event: GenerateClientTokenEvent = {\n type: EventTypes.generateClientToken,\n payload: {\n pathname,\n clientPayload: options.clientPayload,\n multipart: options.multipart,\n },\n };\n\n const res = await fetch(url, {\n method: 'POST',\n body: JSON.stringify(event),\n headers: {\n 'content-type': 'application/json',\n ...options.headers,\n },\n signal: options.abortSignal,\n });\n\n if (!res.ok) {\n throw new BlobError('Failed to retrieve the client token');\n }\n\n try {\n const { clientToken } = (await res.json()) as { clientToken: string };\n return clientToken;\n } catch {\n throw new BlobError('Failed to retrieve the client token');\n }\n}\n\n/**\n * @internal Internal utility to convert a relative URL to absolute URL.\n */\nfunction toAbsoluteUrl(url: string): string {\n // location is available in web workers too: https://developer.mozilla.org/en-US/docs/Web/API/Window/location\n return new URL(url, location.href).href;\n}\n\n/**\n * @internal Internal utility to check if a URL is absolute.\n */\nfunction isAbsoluteUrl(url: string): boolean {\n try {\n return Boolean(new URL(url));\n } catch {\n return false;\n }\n}\n\n/**\n * Generates a client token from a read-write token. This function must be called from a server environment.\n * The client token contains permissions and constraints that limit what the client can do.\n *\n * @param options - Options for generating the client token\n * - pathname - (Required) The destination path for the blob.\n * - token - (Optional) A string specifying the read-write token to use. Defaults to process.env.BLOB_READ_WRITE_TOKEN.\n * - onUploadCompleted - (Optional) Configuration for upload completion callback.\n * - maximumSizeInBytes - (Optional) A number specifying the maximum size in bytes that can be uploaded (max 5TB).\n * - allowedContentTypes - (Optional) An array of media types that are allowed to be uploaded. Wildcards are supported (text/*).\n * - validUntil - (Optional) A timestamp in ms when the token will expire. Defaults to one hour from generation.\n * - addRandomSuffix - (Optional) Whether to add a random suffix to the filename. Defaults to false.\n * - allowOverwrite - (Optional) Whether to allow overwriting existing blobs. Defaults to false.\n * - cacheControlMaxAge - (Optional) Number of seconds to configure cache duration. Defaults to one month.\n * - ifMatch - (Optional) Only write if the ETag matches (optimistic concurrency control).\n * @returns A promise that resolves to the generated client token string which can be used in client-side upload operations.\n */\nexport async function generateClientTokenFromReadWriteToken({\n token,\n ...argsWithoutToken\n}: GenerateClientTokenOptions): Promise<string> {\n if (typeof window !== 'undefined') {\n throw new BlobError(\n '\"generateClientTokenFromReadWriteToken\" must be called from a server environment',\n );\n }\n\n const timestamp = new Date();\n timestamp.setSeconds(timestamp.getSeconds() + 30);\n const readWriteToken = getTokenFromOptionsOrEnv({ token });\n\n const [, , , storeId = null] = readWriteToken.split('_');\n\n if (!storeId) {\n throw new BlobError(\n token ? 'Invalid `token` parameter' : 'Invalid `BLOB_READ_WRITE_TOKEN`',\n );\n }\n\n const payload = Buffer.from(\n JSON.stringify({\n ...argsWithoutToken,\n validUntil: argsWithoutToken.validUntil ?? timestamp.getTime(),\n }),\n ).toString('base64');\n\n const securedKey = await signPayload(payload, readWriteToken);\n\n if (!securedKey) {\n throw new BlobError('Unable to sign client token');\n }\n return `vercel_blob_client_${storeId}_${Buffer.from(\n `${securedKey}.${payload}`,\n ).toString('base64')}`;\n}\n\n/**\n * Options for generating a client token.\n */\nexport interface GenerateClientTokenOptions extends BlobCommandOptions {\n /**\n * The destination path for the blob\n */\n pathname: string;\n\n /**\n * Configuration for upload completion callback\n */\n onUploadCompleted?: {\n callbackUrl: string;\n tokenPayload?: string | null;\n };\n\n /**\n * A number specifying the maximum size in bytes that can be uploaded. The maximum is 5TB.\n */\n maximumSizeInBytes?: number;\n\n /**\n * An array of strings specifying the media type that are allowed to be uploaded.\n * By default, it's all content types. Wildcards are supported (text/*)\n */\n allowedContentTypes?: string[];\n\n /**\n * A number specifying the timestamp in ms when the token will expire.\n * By default, it's now + 1 hour.\n */\n validUntil?: number;\n\n /**\n * Adds a random suffix to the filename.\n * @defaultvalue false\n */\n addRandomSuffix?: boolean;\n\n /**\n * Allow overwriting an existing blob. By default this is set to false and will throw an error if the blob already exists.\n * @defaultvalue false\n */\n allowOverwrite?: boolean;\n\n /**\n * Number in seconds to configure how long Blobs are cached. Defaults to one month. Cannot be set to a value lower than 1 minute.\n * @defaultvalue 30 * 24 * 60 * 60 (1 Month)\n */\n cacheControlMaxAge?: number;\n\n /**\n * Only write if the ETag matches (optimistic concurrency control).\n * Use this for conditional writes to prevent overwriting changes made by others.\n * If the ETag doesn't match, a `BlobPreconditionFailedError` will be thrown.\n */\n ifMatch?: string;\n}\n\n/**\n * @internal Helper function to determine the callback URL for client uploads\n * when onUploadCompleted is provided but no callbackUrl was specified\n */\nfunction getCallbackUrl(request: RequestType): string | undefined {\n const reqPath = getPathFromRequestUrl(request.url!);\n\n if (!reqPath) {\n console.warn(\n 'onUploadCompleted provided but no callbackUrl could be determined. Please provide a callbackUrl in onBeforeGenerateToken or set the VERCEL_BLOB_CALLBACK_URL environment variable.',\n );\n return undefined;\n }\n\n // Check if we have VERCEL_BLOB_CALLBACK_URL env var (works on or off Vercel)\n if (process.env.VERCEL_BLOB_CALLBACK_URL) {\n return `${process.env.VERCEL_BLOB_CALLBACK_URL}${reqPath}`;\n }\n\n // Not hosted on Vercel and no VERCEL_BLOB_CALLBACK_URL\n if (process.env.VERCEL !== '1') {\n console.warn(\n 'onUploadCompleted provided but no callbackUrl could be determined. Please provide a callbackUrl in onBeforeGenerateToken or set the VERCEL_BLOB_CALLBACK_URL environment variable.',\n );\n return undefined;\n }\n\n // If hosted on Vercel, generate default callbackUrl\n\n if (process.env.VERCEL_ENV === 'preview') {\n if (process.env.VERCEL_BRANCH_URL) {\n return `https://${process.env.VERCEL_BRANCH_URL}${reqPath}`;\n }\n if (process.env.VERCEL_URL) {\n return `https://${process.env.VERCEL_URL}${reqPath}`;\n }\n }\n\n if (\n process.env.VERCEL_ENV === 'production' &&\n process.env.VERCEL_PROJECT_PRODUCTION_URL\n ) {\n return `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}${reqPath}`;\n }\n\n return undefined;\n}\n\n/**\n * @internal Helper function to safely extract pathname and query string from request URL\n * Handles both full URLs (http://localhost:3000/api/upload?test=1) and relative paths (/api/upload?test=1)\n */\nfunction getPathFromRequestUrl(url: string): string | null {\n try {\n // Using dummy.com as base URL to handle relative paths\n const parsedUrl = new URL(url, 'https://dummy.com');\n return parsedUrl.pathname + parsedUrl.search;\n } catch {\n return null;\n }\n}\n\nexport { createFolder } from './create-folder';\n"]}