@vercel/blob 0.26.0 → 0.27.1-0372ed96-20250113161609

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.
@@ -1 +1 @@
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,"// eslint-disable-next-line unicorn/prefer-node-protocol -- node:crypto does not resolve correctly in browser and edge runtime\nimport * as crypto from 'crypto';\nimport type { IncomingMessage } from 'node:http';\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 { createPutMethod } from './put';\nimport type { PutBlobResult } from './put-helpers';\nimport type { CommonCompleteMultipartUploadOptions } from './multipart/complete';\nimport { createCompleteMultipartUploadMethod } from './multipart/complete';\nimport { createCreateMultipartUploadMethod } from './multipart/create';\nimport { createUploadPartMethod } from './multipart/upload';\nimport type { CommonMultipartUploadOptions } from './multipart/upload';\nimport { createCreateMultipartUploaderMethod } from './multipart/create-uploader';\n\n// interface for put, upload and multipartUpload.\n// This types omits all options that are encoded in the client token.\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. 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// shared interface for put and multipartUpload\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// shared interface for put and upload\ninterface ClientCommonPutOptions\n extends ClientCommonCreateBlobOptions,\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 */\n multipart?: boolean;\n}\n\nfunction createPutExtraChecks<\n TOptions extends ClientTokenOptions & ClientCommonCreateBlobOptions,\n>(methodName: string) {\n return function extraChecks(options: TOptions) {\n if (typeof window === 'undefined') {\n throw new BlobError(\n `${methodName} must be called from a client environment`,\n );\n }\n\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.cacheControlMaxAge !== undefined\n ) {\n throw new BlobError(\n `${methodName} doesn't allow addRandomSuffix and cacheControlMaxAge. Configure these options at the server side when generating client tokens.`,\n );\n }\n };\n}\n\n// client.put()\n\nexport type ClientPutCommandOptions = ClientCommonPutOptions &\n ClientTokenOptions;\n\nexport const put = createPutMethod<ClientPutCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`put`'),\n});\n\n// vercelBlob. createMultipartUpload()\n// vercelBlob. uploadPart()\n// vercelBlob. completeMultipartUpload()\n// vercelBlob. createMultipartUploader()\n\nexport type ClientCreateMultipartUploadCommandOptions =\n ClientCommonCreateBlobOptions & ClientTokenOptions;\n\nexport const createMultipartUpload =\n createCreateMultipartUploadMethod<ClientCreateMultipartUploadCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`createMultipartUpload`'),\n });\n\nexport const createMultipartUploader =\n createCreateMultipartUploaderMethod<ClientCreateMultipartUploadCommandOptions>(\n {\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`createMultipartUpload`'),\n },\n );\n\ntype ClientMultipartUploadCommandOptions = ClientCommonCreateBlobOptions &\n ClientTokenOptions &\n CommonMultipartUploadOptions &\n WithUploadProgress;\n\nexport const uploadPart =\n createUploadPartMethod<ClientMultipartUploadCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`multipartUpload`'),\n });\n\ntype ClientCompleteMultipartUploadCommandOptions =\n ClientCommonCreateBlobOptions &\n ClientTokenOptions &\n CommonCompleteMultipartUploadOptions;\n\nexport const completeMultipartUpload =\n createCompleteMultipartUploadMethod<ClientCompleteMultipartUploadCommandOptions>(\n {\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`completeMultipartUpload`'),\n },\n );\n\n// upload methods\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\n// client.upload()\n// This is a client-side wrapper that will fetch the client token for you and then upload the file\nexport type UploadOptions = ClientCommonPutOptions & CommonUploadOptions;\n/**\n * Uploads a blob into your store from the client.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/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/storage/vercel-blob/using-blob-sdk#upload-a-blob\n *\n * @param pathname - The pathname to upload the blob to. This includes the filename.\n * @param body - The contents of your blob. This has to be a supported fetch body type https://developer.mozilla.org/en-US/docs/Web/API/fetch#body.\n * @param options - Additional options.\n */\nexport const upload = createPutMethod<UploadOptions>({\n allowedOptions: ['contentType'],\n extraChecks(options) {\n if (typeof window === 'undefined') {\n throw new BlobError(\n 'client/`upload` must be called from a client environment',\n );\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Runtime check for DX.\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.cacheControlMaxAge !== undefined\n ) {\n throw new BlobError(\n \"client/`upload` doesn't allow addRandomSuffix and cacheControlMaxAge. 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 });\n },\n});\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\nasync function signPayload(\n payload: string,\n token: string,\n): Promise<string | undefined> {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Node.js < 20: globalThis.crypto is undefined (in a real script.js, because the REPL has it linked to the crypto module). Node.js >= 20, Browsers and Cloudflare workers: globalThis.crypto is defined and is the Web Crypto API.\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\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 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Node.js < 20: globalThis.crypto is undefined (in a real script.js, because the REPL has it linked to the crypto module). Node.js >= 20, Browsers and Cloudflare workers: globalThis.crypto is defined and is the Web Crypto API.\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\nfunction hexToArrayByte(input: string): ArrayBuffer {\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] = parseInt(input.substring(i, i + 2), 16);\n }\n\n return Buffer.from(view);\n}\n\nexport type DecodedClientTokenPayload = Omit<\n GenerateClientTokenOptions,\n 'token'\n> & {\n validUntil: number;\n};\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\nconst EventTypes = {\n generateClientToken: 'blob.generate-client-token',\n uploadCompleted: 'blob.upload-completed',\n} as const;\n\ninterface GenerateClientTokenEvent {\n type: (typeof EventTypes)['generateClientToken'];\n payload: {\n pathname: string;\n callbackUrl: string;\n multipart: boolean;\n clientPayload: string | null;\n };\n}\ninterface UploadCompletedEvent {\n type: (typeof EventTypes)['uploadCompleted'];\n payload: {\n blob: PutBlobResult;\n tokenPayload?: string | null;\n };\n}\n\nexport type HandleUploadBody = GenerateClientTokenEvent | UploadCompletedEvent;\n\ntype RequestType = IncomingMessage | Request;\n\nexport interface HandleUploadOptions {\n body: HandleUploadBody;\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 | 'cacheControlMaxAge'\n > & { tokenPayload?: string | null }\n >;\n onUploadCompleted: (body: UploadCompletedEvent['payload']) => Promise<void>;\n token?: string;\n request: RequestType;\n}\n\nexport async function handleUpload({\n token,\n request,\n body,\n onBeforeGenerateToken,\n onUploadCompleted,\n}: HandleUploadOptions): Promise<\n | { type: GenerateClientTokenEvent['type']; clientToken: string }\n | { type: UploadCompletedEvent['type']; 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, callbackUrl, clientPayload, multipart } = body.payload;\n const payload = await onBeforeGenerateToken(\n pathname,\n clientPayload,\n multipart,\n );\n const tokenPayload = payload.tokenPayload ?? clientPayload;\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 ...payload,\n token: resolvedToken,\n pathname,\n onUploadCompleted: {\n callbackUrl,\n tokenPayload,\n },\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 await onUploadCompleted(body.payload);\n return { type, response: 'ok' };\n }\n default:\n throw new BlobError('Invalid event type');\n }\n}\n\nasync function retrieveClientToken(options: {\n pathname: string;\n handleUploadUrl: string;\n clientPayload: string | null;\n multipart: boolean;\n abortSignal?: AbortSignal;\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 callbackUrl: url,\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 },\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 (e) {\n throw new BlobError('Failed to retrieve the client token');\n }\n}\n\nfunction toAbsoluteUrl(url: string): string {\n return new URL(url, window.location.href).href;\n}\n\nfunction isAbsoluteUrl(url: string): boolean {\n try {\n return Boolean(new URL(url));\n } catch (e) {\n return false;\n }\n}\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\nexport interface GenerateClientTokenOptions extends BlobCommandOptions {\n pathname: string;\n onUploadCompleted?: {\n callbackUrl: string;\n tokenPayload?: string | null;\n };\n maximumSizeInBytes?: number;\n allowedContentTypes?: string[];\n validUntil?: number;\n addRandomSuffix?: boolean;\n cacheControlMaxAge?: number;\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,"// eslint-disable-next-line unicorn/prefer-node-protocol -- node:crypto does not resolve correctly in browser and edge runtime\nimport * as crypto from 'crypto';\nimport type { IncomingMessage } from 'node:http';\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 { createPutMethod } from './put';\nimport type { PutBlobResult } from './put-helpers';\nimport type { CommonCompleteMultipartUploadOptions } from './multipart/complete';\nimport { createCompleteMultipartUploadMethod } from './multipart/complete';\nimport { createCreateMultipartUploadMethod } from './multipart/create';\nimport { createUploadPartMethod } from './multipart/upload';\nimport type { CommonMultipartUploadOptions } from './multipart/upload';\nimport { createCreateMultipartUploaderMethod } from './multipart/create-uploader';\n\n// interface for put, upload and multipartUpload.\n// This types omits all options that are encoded in the client token.\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. 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// shared interface for put and multipartUpload\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// shared interface for put and upload\ninterface ClientCommonPutOptions\n extends ClientCommonCreateBlobOptions,\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 */\n multipart?: boolean;\n}\n\nfunction createPutExtraChecks<\n TOptions extends ClientTokenOptions & ClientCommonCreateBlobOptions,\n>(methodName: string) {\n return function extraChecks(options: TOptions) {\n // if (typeof window === 'undefined') {\n // throw new BlobError(\n // `${methodName} must be called from a client environment`,\n // );\n // }\n\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.cacheControlMaxAge !== undefined\n ) {\n throw new BlobError(\n `${methodName} doesn't allow addRandomSuffix and cacheControlMaxAge. Configure these options at the server side when generating client tokens.`,\n );\n }\n };\n}\n\n// client.put()\n\nexport type ClientPutCommandOptions = ClientCommonPutOptions &\n ClientTokenOptions;\n\nexport const put = createPutMethod<ClientPutCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`put`'),\n});\n\n// vercelBlob. createMultipartUpload()\n// vercelBlob. uploadPart()\n// vercelBlob. completeMultipartUpload()\n// vercelBlob. createMultipartUploader()\n\nexport type ClientCreateMultipartUploadCommandOptions =\n ClientCommonCreateBlobOptions & ClientTokenOptions;\n\nexport const createMultipartUpload =\n createCreateMultipartUploadMethod<ClientCreateMultipartUploadCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`createMultipartUpload`'),\n });\n\nexport const createMultipartUploader =\n createCreateMultipartUploaderMethod<ClientCreateMultipartUploadCommandOptions>(\n {\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`createMultipartUpload`'),\n },\n );\n\ntype ClientMultipartUploadCommandOptions = ClientCommonCreateBlobOptions &\n ClientTokenOptions &\n CommonMultipartUploadOptions &\n WithUploadProgress;\n\nexport const uploadPart =\n createUploadPartMethod<ClientMultipartUploadCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`multipartUpload`'),\n });\n\ntype ClientCompleteMultipartUploadCommandOptions =\n ClientCommonCreateBlobOptions &\n ClientTokenOptions &\n CommonCompleteMultipartUploadOptions;\n\nexport const completeMultipartUpload =\n createCompleteMultipartUploadMethod<ClientCompleteMultipartUploadCommandOptions>(\n {\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`completeMultipartUpload`'),\n },\n );\n\n// upload methods\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\n// client.upload()\n// This is a client-side wrapper that will fetch the client token for you and then upload the file\nexport type UploadOptions = ClientCommonPutOptions & CommonUploadOptions;\n/**\n * Uploads a blob into your store from the client.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/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/storage/vercel-blob/using-blob-sdk#upload-a-blob\n *\n * @param pathname - The pathname to upload the blob to. This includes the filename.\n * @param body - The contents of your blob. This has to be a supported fetch body type https://developer.mozilla.org/en-US/docs/Web/API/fetch#body.\n * @param options - Additional options.\n */\nexport const upload = createPutMethod<UploadOptions>({\n allowedOptions: ['contentType'],\n extraChecks(options) {\n // if (typeof window === 'undefined') {\n // throw new BlobError(\n // 'client/`upload` must be called from a client environment',\n // );\n // }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Runtime check for DX.\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.cacheControlMaxAge !== undefined\n ) {\n throw new BlobError(\n \"client/`upload` doesn't allow addRandomSuffix and cacheControlMaxAge. 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 });\n },\n});\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\nasync function signPayload(\n payload: string,\n token: string,\n): Promise<string | undefined> {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Node.js < 20: globalThis.crypto is undefined (in a real script.js, because the REPL has it linked to the crypto module). Node.js >= 20, Browsers and Cloudflare workers: globalThis.crypto is defined and is the Web Crypto API.\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\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 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Node.js < 20: globalThis.crypto is undefined (in a real script.js, because the REPL has it linked to the crypto module). Node.js >= 20, Browsers and Cloudflare workers: globalThis.crypto is defined and is the Web Crypto API.\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\nfunction hexToArrayByte(input: string): ArrayBuffer {\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] = parseInt(input.substring(i, i + 2), 16);\n }\n\n return Buffer.from(view);\n}\n\nexport type DecodedClientTokenPayload = Omit<\n GenerateClientTokenOptions,\n 'token'\n> & {\n validUntil: number;\n};\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\nconst EventTypes = {\n generateClientToken: 'blob.generate-client-token',\n uploadCompleted: 'blob.upload-completed',\n} as const;\n\ninterface GenerateClientTokenEvent {\n type: (typeof EventTypes)['generateClientToken'];\n payload: {\n pathname: string;\n callbackUrl: string;\n multipart: boolean;\n clientPayload: string | null;\n };\n}\ninterface UploadCompletedEvent {\n type: (typeof EventTypes)['uploadCompleted'];\n payload: {\n blob: PutBlobResult;\n tokenPayload?: string | null;\n };\n}\n\nexport type HandleUploadBody = GenerateClientTokenEvent | UploadCompletedEvent;\n\ntype RequestType = IncomingMessage | Request;\n\nexport interface HandleUploadOptions {\n body: HandleUploadBody;\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 | 'cacheControlMaxAge'\n > & { tokenPayload?: string | null }\n >;\n onUploadCompleted: (body: UploadCompletedEvent['payload']) => Promise<void>;\n token?: string;\n request: RequestType;\n}\n\nexport async function handleUpload({\n token,\n request,\n body,\n onBeforeGenerateToken,\n onUploadCompleted,\n}: HandleUploadOptions): Promise<\n | { type: GenerateClientTokenEvent['type']; clientToken: string }\n | { type: UploadCompletedEvent['type']; 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, callbackUrl, clientPayload, multipart } = body.payload;\n const payload = await onBeforeGenerateToken(\n pathname,\n clientPayload,\n multipart,\n );\n const tokenPayload = payload.tokenPayload ?? clientPayload;\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 ...payload,\n token: resolvedToken,\n pathname,\n onUploadCompleted: {\n callbackUrl,\n tokenPayload,\n },\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 await onUploadCompleted(body.payload);\n return { type, response: 'ok' };\n }\n default:\n throw new BlobError('Invalid event type');\n }\n}\n\nasync function retrieveClientToken(options: {\n pathname: string;\n handleUploadUrl: string;\n clientPayload: string | null;\n multipart: boolean;\n abortSignal?: AbortSignal;\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 callbackUrl: url,\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 },\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 (e) {\n throw new BlobError('Failed to retrieve the client token');\n }\n}\n\nfunction toAbsoluteUrl(url: string): string {\n return new URL(url, window.location.href).href;\n}\n\nfunction isAbsoluteUrl(url: string): boolean {\n try {\n return Boolean(new URL(url));\n } catch (e) {\n return false;\n }\n}\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\nexport interface GenerateClientTokenOptions extends BlobCommandOptions {\n pathname: string;\n onUploadCompleted?: {\n callbackUrl: string;\n tokenPayload?: string | null;\n };\n maximumSizeInBytes?: number;\n allowedContentTypes?: string[];\n validUntil?: number;\n addRandomSuffix?: boolean;\n cacheControlMaxAge?: number;\n}\n\nexport { createFolder } from './create-folder';\n"]}
package/dist/client.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as PutBody, a as PutBlobResult, b as Part, B as BlobCommandOptions, W as WithUploadProgress, C as CommonMultipartUploadOptions, c as CommonCompleteMultipartUploadOptions } from './create-folder-DGBfYPet.cjs';
2
- export { d as createFolder } from './create-folder-DGBfYPet.cjs';
1
+ import { P as PutBody, a as PutBlobResult, b as Part, B as BlobCommandOptions, W as WithUploadProgress, C as CommonMultipartUploadOptions, c as CommonCompleteMultipartUploadOptions } from './create-folder-CqdraABG.cjs';
2
+ export { d as createFolder } from './create-folder-CqdraABG.cjs';
3
3
  import { IncomingMessage } from 'node:http';
4
4
  import 'stream';
5
5
  import 'undici';
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as PutBody, a as PutBlobResult, b as Part, B as BlobCommandOptions, W as WithUploadProgress, C as CommonMultipartUploadOptions, c as CommonCompleteMultipartUploadOptions } from './create-folder-DGBfYPet.js';
2
- export { d as createFolder } from './create-folder-DGBfYPet.js';
1
+ import { P as PutBody, a as PutBlobResult, b as Part, B as BlobCommandOptions, W as WithUploadProgress, C as CommonMultipartUploadOptions, c as CommonCompleteMultipartUploadOptions } from './create-folder-CqdraABG.js';
2
+ export { d as createFolder } from './create-folder-CqdraABG.js';
3
3
  import { IncomingMessage } from 'node:http';
4
4
  import 'stream';
5
5
  import 'undici';
package/dist/client.js CHANGED
@@ -7,18 +7,13 @@ import {
7
7
  createPutMethod,
8
8
  createUploadPartMethod,
9
9
  getTokenFromOptionsOrEnv
10
- } from "./chunk-QXAUX3LT.js";
10
+ } from "./chunk-KN6WT5GP.js";
11
11
 
12
12
  // src/client.ts
13
13
  import * as crypto from "crypto";
14
14
  import { fetch } from "undici";
15
15
  function createPutExtraChecks(methodName) {
16
16
  return function extraChecks(options) {
17
- if (typeof window === "undefined") {
18
- throw new BlobError(
19
- `${methodName} must be called from a client environment`
20
- );
21
- }
22
17
  if (!options.token.startsWith("vercel_blob_client_")) {
23
18
  throw new BlobError(`${methodName} must be called with a client token`);
24
19
  }
@@ -60,11 +55,6 @@ var completeMultipartUpload = createCompleteMultipartUploadMethod(
60
55
  var upload = createPutMethod({
61
56
  allowedOptions: ["contentType"],
62
57
  extraChecks(options) {
63
- if (typeof window === "undefined") {
64
- throw new BlobError(
65
- "client/`upload` must be called from a client environment"
66
- );
67
- }
68
58
  if (options.handleUploadUrl === void 0) {
69
59
  throw new BlobError(
70
60
  "client/`upload` requires the 'handleUploadUrl' parameter"
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"sourcesContent":["// eslint-disable-next-line unicorn/prefer-node-protocol -- node:crypto does not resolve correctly in browser and edge runtime\nimport * as crypto from 'crypto';\nimport type { IncomingMessage } from 'node:http';\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 { createPutMethod } from './put';\nimport type { PutBlobResult } from './put-helpers';\nimport type { CommonCompleteMultipartUploadOptions } from './multipart/complete';\nimport { createCompleteMultipartUploadMethod } from './multipart/complete';\nimport { createCreateMultipartUploadMethod } from './multipart/create';\nimport { createUploadPartMethod } from './multipart/upload';\nimport type { CommonMultipartUploadOptions } from './multipart/upload';\nimport { createCreateMultipartUploaderMethod } from './multipart/create-uploader';\n\n// interface for put, upload and multipartUpload.\n// This types omits all options that are encoded in the client token.\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. 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// shared interface for put and multipartUpload\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// shared interface for put and upload\ninterface ClientCommonPutOptions\n extends ClientCommonCreateBlobOptions,\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 */\n multipart?: boolean;\n}\n\nfunction createPutExtraChecks<\n TOptions extends ClientTokenOptions & ClientCommonCreateBlobOptions,\n>(methodName: string) {\n return function extraChecks(options: TOptions) {\n if (typeof window === 'undefined') {\n throw new BlobError(\n `${methodName} must be called from a client environment`,\n );\n }\n\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.cacheControlMaxAge !== undefined\n ) {\n throw new BlobError(\n `${methodName} doesn't allow addRandomSuffix and cacheControlMaxAge. Configure these options at the server side when generating client tokens.`,\n );\n }\n };\n}\n\n// client.put()\n\nexport type ClientPutCommandOptions = ClientCommonPutOptions &\n ClientTokenOptions;\n\nexport const put = createPutMethod<ClientPutCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`put`'),\n});\n\n// vercelBlob. createMultipartUpload()\n// vercelBlob. uploadPart()\n// vercelBlob. completeMultipartUpload()\n// vercelBlob. createMultipartUploader()\n\nexport type ClientCreateMultipartUploadCommandOptions =\n ClientCommonCreateBlobOptions & ClientTokenOptions;\n\nexport const createMultipartUpload =\n createCreateMultipartUploadMethod<ClientCreateMultipartUploadCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`createMultipartUpload`'),\n });\n\nexport const createMultipartUploader =\n createCreateMultipartUploaderMethod<ClientCreateMultipartUploadCommandOptions>(\n {\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`createMultipartUpload`'),\n },\n );\n\ntype ClientMultipartUploadCommandOptions = ClientCommonCreateBlobOptions &\n ClientTokenOptions &\n CommonMultipartUploadOptions &\n WithUploadProgress;\n\nexport const uploadPart =\n createUploadPartMethod<ClientMultipartUploadCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`multipartUpload`'),\n });\n\ntype ClientCompleteMultipartUploadCommandOptions =\n ClientCommonCreateBlobOptions &\n ClientTokenOptions &\n CommonCompleteMultipartUploadOptions;\n\nexport const completeMultipartUpload =\n createCompleteMultipartUploadMethod<ClientCompleteMultipartUploadCommandOptions>(\n {\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`completeMultipartUpload`'),\n },\n );\n\n// upload methods\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\n// client.upload()\n// This is a client-side wrapper that will fetch the client token for you and then upload the file\nexport type UploadOptions = ClientCommonPutOptions & CommonUploadOptions;\n/**\n * Uploads a blob into your store from the client.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/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/storage/vercel-blob/using-blob-sdk#upload-a-blob\n *\n * @param pathname - The pathname to upload the blob to. This includes the filename.\n * @param body - The contents of your blob. This has to be a supported fetch body type https://developer.mozilla.org/en-US/docs/Web/API/fetch#body.\n * @param options - Additional options.\n */\nexport const upload = createPutMethod<UploadOptions>({\n allowedOptions: ['contentType'],\n extraChecks(options) {\n if (typeof window === 'undefined') {\n throw new BlobError(\n 'client/`upload` must be called from a client environment',\n );\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Runtime check for DX.\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.cacheControlMaxAge !== undefined\n ) {\n throw new BlobError(\n \"client/`upload` doesn't allow addRandomSuffix and cacheControlMaxAge. 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 });\n },\n});\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\nasync function signPayload(\n payload: string,\n token: string,\n): Promise<string | undefined> {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Node.js < 20: globalThis.crypto is undefined (in a real script.js, because the REPL has it linked to the crypto module). Node.js >= 20, Browsers and Cloudflare workers: globalThis.crypto is defined and is the Web Crypto API.\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\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 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Node.js < 20: globalThis.crypto is undefined (in a real script.js, because the REPL has it linked to the crypto module). Node.js >= 20, Browsers and Cloudflare workers: globalThis.crypto is defined and is the Web Crypto API.\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\nfunction hexToArrayByte(input: string): ArrayBuffer {\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] = parseInt(input.substring(i, i + 2), 16);\n }\n\n return Buffer.from(view);\n}\n\nexport type DecodedClientTokenPayload = Omit<\n GenerateClientTokenOptions,\n 'token'\n> & {\n validUntil: number;\n};\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\nconst EventTypes = {\n generateClientToken: 'blob.generate-client-token',\n uploadCompleted: 'blob.upload-completed',\n} as const;\n\ninterface GenerateClientTokenEvent {\n type: (typeof EventTypes)['generateClientToken'];\n payload: {\n pathname: string;\n callbackUrl: string;\n multipart: boolean;\n clientPayload: string | null;\n };\n}\ninterface UploadCompletedEvent {\n type: (typeof EventTypes)['uploadCompleted'];\n payload: {\n blob: PutBlobResult;\n tokenPayload?: string | null;\n };\n}\n\nexport type HandleUploadBody = GenerateClientTokenEvent | UploadCompletedEvent;\n\ntype RequestType = IncomingMessage | Request;\n\nexport interface HandleUploadOptions {\n body: HandleUploadBody;\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 | 'cacheControlMaxAge'\n > & { tokenPayload?: string | null }\n >;\n onUploadCompleted: (body: UploadCompletedEvent['payload']) => Promise<void>;\n token?: string;\n request: RequestType;\n}\n\nexport async function handleUpload({\n token,\n request,\n body,\n onBeforeGenerateToken,\n onUploadCompleted,\n}: HandleUploadOptions): Promise<\n | { type: GenerateClientTokenEvent['type']; clientToken: string }\n | { type: UploadCompletedEvent['type']; 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, callbackUrl, clientPayload, multipart } = body.payload;\n const payload = await onBeforeGenerateToken(\n pathname,\n clientPayload,\n multipart,\n );\n const tokenPayload = payload.tokenPayload ?? clientPayload;\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 ...payload,\n token: resolvedToken,\n pathname,\n onUploadCompleted: {\n callbackUrl,\n tokenPayload,\n },\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 await onUploadCompleted(body.payload);\n return { type, response: 'ok' };\n }\n default:\n throw new BlobError('Invalid event type');\n }\n}\n\nasync function retrieveClientToken(options: {\n pathname: string;\n handleUploadUrl: string;\n clientPayload: string | null;\n multipart: boolean;\n abortSignal?: AbortSignal;\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 callbackUrl: url,\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 },\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 (e) {\n throw new BlobError('Failed to retrieve the client token');\n }\n}\n\nfunction toAbsoluteUrl(url: string): string {\n return new URL(url, window.location.href).href;\n}\n\nfunction isAbsoluteUrl(url: string): boolean {\n try {\n return Boolean(new URL(url));\n } catch (e) {\n return false;\n }\n}\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\nexport interface GenerateClientTokenOptions extends BlobCommandOptions {\n pathname: string;\n onUploadCompleted?: {\n callbackUrl: string;\n tokenPayload?: string | null;\n };\n maximumSizeInBytes?: number;\n allowedContentTypes?: string[];\n validUntil?: number;\n addRandomSuffix?: boolean;\n cacheControlMaxAge?: number;\n}\n\nexport { createFolder } from './create-folder';\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/client.ts"],"sourcesContent":["// eslint-disable-next-line unicorn/prefer-node-protocol -- node:crypto does not resolve correctly in browser and edge runtime\nimport * as crypto from 'crypto';\nimport type { IncomingMessage } from 'node:http';\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 { createPutMethod } from './put';\nimport type { PutBlobResult } from './put-helpers';\nimport type { CommonCompleteMultipartUploadOptions } from './multipart/complete';\nimport { createCompleteMultipartUploadMethod } from './multipart/complete';\nimport { createCreateMultipartUploadMethod } from './multipart/create';\nimport { createUploadPartMethod } from './multipart/upload';\nimport type { CommonMultipartUploadOptions } from './multipart/upload';\nimport { createCreateMultipartUploaderMethod } from './multipart/create-uploader';\n\n// interface for put, upload and multipartUpload.\n// This types omits all options that are encoded in the client token.\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. 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// shared interface for put and multipartUpload\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// shared interface for put and upload\ninterface ClientCommonPutOptions\n extends ClientCommonCreateBlobOptions,\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 */\n multipart?: boolean;\n}\n\nfunction createPutExtraChecks<\n TOptions extends ClientTokenOptions & ClientCommonCreateBlobOptions,\n>(methodName: string) {\n return function extraChecks(options: TOptions) {\n // if (typeof window === 'undefined') {\n // throw new BlobError(\n // `${methodName} must be called from a client environment`,\n // );\n // }\n\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.cacheControlMaxAge !== undefined\n ) {\n throw new BlobError(\n `${methodName} doesn't allow addRandomSuffix and cacheControlMaxAge. Configure these options at the server side when generating client tokens.`,\n );\n }\n };\n}\n\n// client.put()\n\nexport type ClientPutCommandOptions = ClientCommonPutOptions &\n ClientTokenOptions;\n\nexport const put = createPutMethod<ClientPutCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`put`'),\n});\n\n// vercelBlob. createMultipartUpload()\n// vercelBlob. uploadPart()\n// vercelBlob. completeMultipartUpload()\n// vercelBlob. createMultipartUploader()\n\nexport type ClientCreateMultipartUploadCommandOptions =\n ClientCommonCreateBlobOptions & ClientTokenOptions;\n\nexport const createMultipartUpload =\n createCreateMultipartUploadMethod<ClientCreateMultipartUploadCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`createMultipartUpload`'),\n });\n\nexport const createMultipartUploader =\n createCreateMultipartUploaderMethod<ClientCreateMultipartUploadCommandOptions>(\n {\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`createMultipartUpload`'),\n },\n );\n\ntype ClientMultipartUploadCommandOptions = ClientCommonCreateBlobOptions &\n ClientTokenOptions &\n CommonMultipartUploadOptions &\n WithUploadProgress;\n\nexport const uploadPart =\n createUploadPartMethod<ClientMultipartUploadCommandOptions>({\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`multipartUpload`'),\n });\n\ntype ClientCompleteMultipartUploadCommandOptions =\n ClientCommonCreateBlobOptions &\n ClientTokenOptions &\n CommonCompleteMultipartUploadOptions;\n\nexport const completeMultipartUpload =\n createCompleteMultipartUploadMethod<ClientCompleteMultipartUploadCommandOptions>(\n {\n allowedOptions: ['contentType'],\n extraChecks: createPutExtraChecks('client/`completeMultipartUpload`'),\n },\n );\n\n// upload methods\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\n// client.upload()\n// This is a client-side wrapper that will fetch the client token for you and then upload the file\nexport type UploadOptions = ClientCommonPutOptions & CommonUploadOptions;\n/**\n * Uploads a blob into your store from the client.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/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/storage/vercel-blob/using-blob-sdk#upload-a-blob\n *\n * @param pathname - The pathname to upload the blob to. This includes the filename.\n * @param body - The contents of your blob. This has to be a supported fetch body type https://developer.mozilla.org/en-US/docs/Web/API/fetch#body.\n * @param options - Additional options.\n */\nexport const upload = createPutMethod<UploadOptions>({\n allowedOptions: ['contentType'],\n extraChecks(options) {\n // if (typeof window === 'undefined') {\n // throw new BlobError(\n // 'client/`upload` must be called from a client environment',\n // );\n // }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Runtime check for DX.\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.cacheControlMaxAge !== undefined\n ) {\n throw new BlobError(\n \"client/`upload` doesn't allow addRandomSuffix and cacheControlMaxAge. 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 });\n },\n});\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\nasync function signPayload(\n payload: string,\n token: string,\n): Promise<string | undefined> {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Node.js < 20: globalThis.crypto is undefined (in a real script.js, because the REPL has it linked to the crypto module). Node.js >= 20, Browsers and Cloudflare workers: globalThis.crypto is defined and is the Web Crypto API.\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\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 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Node.js < 20: globalThis.crypto is undefined (in a real script.js, because the REPL has it linked to the crypto module). Node.js >= 20, Browsers and Cloudflare workers: globalThis.crypto is defined and is the Web Crypto API.\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\nfunction hexToArrayByte(input: string): ArrayBuffer {\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] = parseInt(input.substring(i, i + 2), 16);\n }\n\n return Buffer.from(view);\n}\n\nexport type DecodedClientTokenPayload = Omit<\n GenerateClientTokenOptions,\n 'token'\n> & {\n validUntil: number;\n};\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\nconst EventTypes = {\n generateClientToken: 'blob.generate-client-token',\n uploadCompleted: 'blob.upload-completed',\n} as const;\n\ninterface GenerateClientTokenEvent {\n type: (typeof EventTypes)['generateClientToken'];\n payload: {\n pathname: string;\n callbackUrl: string;\n multipart: boolean;\n clientPayload: string | null;\n };\n}\ninterface UploadCompletedEvent {\n type: (typeof EventTypes)['uploadCompleted'];\n payload: {\n blob: PutBlobResult;\n tokenPayload?: string | null;\n };\n}\n\nexport type HandleUploadBody = GenerateClientTokenEvent | UploadCompletedEvent;\n\ntype RequestType = IncomingMessage | Request;\n\nexport interface HandleUploadOptions {\n body: HandleUploadBody;\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 | 'cacheControlMaxAge'\n > & { tokenPayload?: string | null }\n >;\n onUploadCompleted: (body: UploadCompletedEvent['payload']) => Promise<void>;\n token?: string;\n request: RequestType;\n}\n\nexport async function handleUpload({\n token,\n request,\n body,\n onBeforeGenerateToken,\n onUploadCompleted,\n}: HandleUploadOptions): Promise<\n | { type: GenerateClientTokenEvent['type']; clientToken: string }\n | { type: UploadCompletedEvent['type']; 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, callbackUrl, clientPayload, multipart } = body.payload;\n const payload = await onBeforeGenerateToken(\n pathname,\n clientPayload,\n multipart,\n );\n const tokenPayload = payload.tokenPayload ?? clientPayload;\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 ...payload,\n token: resolvedToken,\n pathname,\n onUploadCompleted: {\n callbackUrl,\n tokenPayload,\n },\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 await onUploadCompleted(body.payload);\n return { type, response: 'ok' };\n }\n default:\n throw new BlobError('Invalid event type');\n }\n}\n\nasync function retrieveClientToken(options: {\n pathname: string;\n handleUploadUrl: string;\n clientPayload: string | null;\n multipart: boolean;\n abortSignal?: AbortSignal;\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 callbackUrl: url,\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 },\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 (e) {\n throw new BlobError('Failed to retrieve the client token');\n }\n}\n\nfunction toAbsoluteUrl(url: string): string {\n return new URL(url, window.location.href).href;\n}\n\nfunction isAbsoluteUrl(url: string): boolean {\n try {\n return Boolean(new URL(url));\n } catch (e) {\n return false;\n }\n}\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\nexport interface GenerateClientTokenOptions extends BlobCommandOptions {\n pathname: string;\n onUploadCompleted?: {\n callbackUrl: string;\n tokenPayload?: string | null;\n };\n maximumSizeInBytes?: number;\n allowedContentTypes?: string[];\n validUntil?: number;\n addRandomSuffix?: boolean;\n cacheControlMaxAge?: number;\n}\n\nexport { createFolder } from './create-folder';\n"],"mappings":"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","names":[]}
@@ -57,7 +57,7 @@ interface PutBlobResult {
57
57
  url: string;
58
58
  downloadUrl: string;
59
59
  pathname: string;
60
- contentType?: string;
60
+ contentType: string;
61
61
  contentDisposition: string;
62
62
  }
63
63
  type PutBody = string | Readable | Buffer | Blob | ArrayBuffer | ReadableStream | File;
@@ -57,7 +57,7 @@ interface PutBlobResult {
57
57
  url: string;
58
58
  downloadUrl: string;
59
59
  pathname: string;
60
- contentType?: string;
60
+ contentType: string;
61
61
  contentDisposition: string;
62
62
  }
63
63
  type PutBody = string | Readable | Buffer | Blob | ArrayBuffer | ReadableStream | File;
package/dist/index.cjs CHANGED
@@ -22,11 +22,11 @@
22
22
 
23
23
 
24
24
 
25
- var _chunkWOCAUB3Mcjs = require('./chunk-WOCAUB3M.cjs');
25
+ var _chunkXTBGGUVNcjs = require('./chunk-XTBGGUVN.cjs');
26
26
 
27
27
  // src/del.ts
28
28
  async function del(url, options) {
29
- await _chunkWOCAUB3Mcjs.requestApi.call(void 0,
29
+ await _chunkXTBGGUVNcjs.requestApi.call(void 0,
30
30
  "/delete",
31
31
  {
32
32
  method: "POST",
@@ -41,7 +41,7 @@ async function del(url, options) {
41
41
  // src/head.ts
42
42
  async function head(url, options) {
43
43
  const searchParams = new URLSearchParams({ url });
44
- const response = await _chunkWOCAUB3Mcjs.requestApi.call(void 0,
44
+ const response = await _chunkXTBGGUVNcjs.requestApi.call(void 0,
45
45
  `?${searchParams.toString()}`,
46
46
  // HEAD can't have body as a response, so we use GET
47
47
  {
@@ -78,7 +78,7 @@ async function list(options) {
78
78
  if (options == null ? void 0 : options.mode) {
79
79
  searchParams.set("mode", options.mode);
80
80
  }
81
- const response = await _chunkWOCAUB3Mcjs.requestApi.call(void 0,
81
+ const response = await _chunkXTBGGUVNcjs.requestApi.call(void 0,
82
82
  `?${searchParams.toString()}`,
83
83
  {
84
84
  method: "GET",
@@ -113,19 +113,19 @@ function mapBlobResult(blobResult) {
113
113
  // src/copy.ts
114
114
  async function copy(fromUrl, toPathname, options) {
115
115
  if (!options) {
116
- throw new (0, _chunkWOCAUB3Mcjs.BlobError)("missing options, see usage");
116
+ throw new (0, _chunkXTBGGUVNcjs.BlobError)("missing options, see usage");
117
117
  }
118
118
  if (options.access !== "public") {
119
- throw new (0, _chunkWOCAUB3Mcjs.BlobError)('access must be "public"');
119
+ throw new (0, _chunkXTBGGUVNcjs.BlobError)('access must be "public"');
120
120
  }
121
- if (toPathname.length > _chunkWOCAUB3Mcjs.MAXIMUM_PATHNAME_LENGTH) {
122
- throw new (0, _chunkWOCAUB3Mcjs.BlobError)(
123
- `pathname is too long, maximum length is ${_chunkWOCAUB3Mcjs.MAXIMUM_PATHNAME_LENGTH}`
121
+ if (toPathname.length > _chunkXTBGGUVNcjs.MAXIMUM_PATHNAME_LENGTH) {
122
+ throw new (0, _chunkXTBGGUVNcjs.BlobError)(
123
+ `pathname is too long, maximum length is ${_chunkXTBGGUVNcjs.MAXIMUM_PATHNAME_LENGTH}`
124
124
  );
125
125
  }
126
- for (const invalidCharacter of _chunkWOCAUB3Mcjs.disallowedPathnameCharacters) {
126
+ for (const invalidCharacter of _chunkXTBGGUVNcjs.disallowedPathnameCharacters) {
127
127
  if (toPathname.includes(invalidCharacter)) {
128
- throw new (0, _chunkWOCAUB3Mcjs.BlobError)(
128
+ throw new (0, _chunkXTBGGUVNcjs.BlobError)(
129
129
  `pathname cannot contain "${invalidCharacter}", please encode it if needed`
130
130
  );
131
131
  }
@@ -140,7 +140,7 @@ async function copy(fromUrl, toPathname, options) {
140
140
  if (options.cacheControlMaxAge !== void 0) {
141
141
  headers["x-cache-control-max-age"] = options.cacheControlMaxAge.toString();
142
142
  }
143
- const response = await _chunkWOCAUB3Mcjs.requestApi.call(void 0,
143
+ const response = await _chunkXTBGGUVNcjs.requestApi.call(void 0,
144
144
  `/${toPathname}?fromUrl=${fromUrl}`,
145
145
  {
146
146
  method: "PUT",
@@ -159,19 +159,19 @@ async function copy(fromUrl, toPathname, options) {
159
159
  }
160
160
 
161
161
  // src/index.ts
162
- var put = _chunkWOCAUB3Mcjs.createPutMethod.call(void 0, {
162
+ var put = _chunkXTBGGUVNcjs.createPutMethod.call(void 0, {
163
163
  allowedOptions: ["cacheControlMaxAge", "addRandomSuffix", "contentType"]
164
164
  });
165
- var createMultipartUpload = _chunkWOCAUB3Mcjs.createCreateMultipartUploadMethod.call(void 0, {
165
+ var createMultipartUpload = _chunkXTBGGUVNcjs.createCreateMultipartUploadMethod.call(void 0, {
166
166
  allowedOptions: ["cacheControlMaxAge", "addRandomSuffix", "contentType"]
167
167
  });
168
- var createMultipartUploader = _chunkWOCAUB3Mcjs.createCreateMultipartUploaderMethod.call(void 0, {
168
+ var createMultipartUploader = _chunkXTBGGUVNcjs.createCreateMultipartUploaderMethod.call(void 0, {
169
169
  allowedOptions: ["cacheControlMaxAge", "addRandomSuffix", "contentType"]
170
170
  });
171
- var uploadPart = _chunkWOCAUB3Mcjs.createUploadPartMethod.call(void 0, {
171
+ var uploadPart = _chunkXTBGGUVNcjs.createUploadPartMethod.call(void 0, {
172
172
  allowedOptions: ["cacheControlMaxAge", "addRandomSuffix", "contentType"]
173
173
  });
174
- var completeMultipartUpload = _chunkWOCAUB3Mcjs.createCompleteMultipartUploadMethod.call(void 0, {
174
+ var completeMultipartUpload = _chunkXTBGGUVNcjs.createCompleteMultipartUploadMethod.call(void 0, {
175
175
  allowedOptions: ["cacheControlMaxAge", "addRandomSuffix", "contentType"]
176
176
  });
177
177
 
@@ -199,5 +199,5 @@ var completeMultipartUpload = _chunkWOCAUB3Mcjs.createCompleteMultipartUploadMet
199
199
 
200
200
 
201
201
 
202
- exports.BlobAccessError = _chunkWOCAUB3Mcjs.BlobAccessError; exports.BlobClientTokenExpiredError = _chunkWOCAUB3Mcjs.BlobClientTokenExpiredError; exports.BlobContentTypeNotAllowedError = _chunkWOCAUB3Mcjs.BlobContentTypeNotAllowedError; exports.BlobError = _chunkWOCAUB3Mcjs.BlobError; exports.BlobFileTooLargeError = _chunkWOCAUB3Mcjs.BlobFileTooLargeError; exports.BlobNotFoundError = _chunkWOCAUB3Mcjs.BlobNotFoundError; exports.BlobPathnameMismatchError = _chunkWOCAUB3Mcjs.BlobPathnameMismatchError; exports.BlobRequestAbortedError = _chunkWOCAUB3Mcjs.BlobRequestAbortedError; exports.BlobServiceNotAvailable = _chunkWOCAUB3Mcjs.BlobServiceNotAvailable; exports.BlobServiceRateLimited = _chunkWOCAUB3Mcjs.BlobServiceRateLimited; exports.BlobStoreNotFoundError = _chunkWOCAUB3Mcjs.BlobStoreNotFoundError; exports.BlobStoreSuspendedError = _chunkWOCAUB3Mcjs.BlobStoreSuspendedError; exports.BlobUnknownError = _chunkWOCAUB3Mcjs.BlobUnknownError; exports.completeMultipartUpload = completeMultipartUpload; exports.copy = copy; exports.createFolder = _chunkWOCAUB3Mcjs.createFolder; exports.createMultipartUpload = createMultipartUpload; exports.createMultipartUploader = createMultipartUploader; exports.del = del; exports.getDownloadUrl = _chunkWOCAUB3Mcjs.getDownloadUrl; exports.head = head; exports.list = list; exports.put = put; exports.uploadPart = uploadPart;
202
+ exports.BlobAccessError = _chunkXTBGGUVNcjs.BlobAccessError; exports.BlobClientTokenExpiredError = _chunkXTBGGUVNcjs.BlobClientTokenExpiredError; exports.BlobContentTypeNotAllowedError = _chunkXTBGGUVNcjs.BlobContentTypeNotAllowedError; exports.BlobError = _chunkXTBGGUVNcjs.BlobError; exports.BlobFileTooLargeError = _chunkXTBGGUVNcjs.BlobFileTooLargeError; exports.BlobNotFoundError = _chunkXTBGGUVNcjs.BlobNotFoundError; exports.BlobPathnameMismatchError = _chunkXTBGGUVNcjs.BlobPathnameMismatchError; exports.BlobRequestAbortedError = _chunkXTBGGUVNcjs.BlobRequestAbortedError; exports.BlobServiceNotAvailable = _chunkXTBGGUVNcjs.BlobServiceNotAvailable; exports.BlobServiceRateLimited = _chunkXTBGGUVNcjs.BlobServiceRateLimited; exports.BlobStoreNotFoundError = _chunkXTBGGUVNcjs.BlobStoreNotFoundError; exports.BlobStoreSuspendedError = _chunkXTBGGUVNcjs.BlobStoreSuspendedError; exports.BlobUnknownError = _chunkXTBGGUVNcjs.BlobUnknownError; exports.completeMultipartUpload = completeMultipartUpload; exports.copy = copy; exports.createFolder = _chunkXTBGGUVNcjs.createFolder; exports.createMultipartUpload = createMultipartUpload; exports.createMultipartUploader = createMultipartUploader; exports.del = del; exports.getDownloadUrl = _chunkXTBGGUVNcjs.getDownloadUrl; exports.head = head; exports.list = list; exports.put = put; exports.uploadPart = uploadPart;
203
203
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/storage/storage/packages/blob/dist/index.cjs","../src/del.ts","../src/head.ts","../src/list.ts","../src/copy.ts","../src/index.ts"],"names":[],"mappings":"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","file":"/home/runner/work/storage/storage/packages/blob/dist/index.cjs","sourcesContent":[null,"import { requestApi } from './api';\nimport type { BlobCommandOptions } from './helpers';\n\n/**\n * Deletes one or multiple blobs from your store.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#delete-a-blob\n *\n * @param url - Blob url or array of blob urls that identify the blobs to be deleted. You can only delete blobs that are located in a store, that your 'BLOB_READ_WRITE_TOKEN' has access to.\n * @param options - Additional options for the request.\n */\nexport async function del(\n url: string[] | string,\n options?: BlobCommandOptions,\n): Promise<void> {\n await requestApi(\n '/delete',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ urls: Array.isArray(url) ? url : [url] }),\n signal: options?.abortSignal,\n },\n options,\n );\n}\n","import { requestApi } from './api';\nimport type { BlobCommandOptions } from './helpers';\n\nexport interface HeadBlobResult {\n url: string;\n downloadUrl: string;\n size: number;\n uploadedAt: Date;\n pathname: string;\n contentType: string;\n contentDisposition: string;\n cacheControl: string;\n}\n\ninterface HeadBlobApiResponse extends Omit<HeadBlobResult, 'uploadedAt'> {\n uploadedAt: string; // when receiving data from our API, uploadedAt is a string\n}\n\n/**\n * Fetches metadata of a blob object.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#get-blob-metadata\n *\n * @param url - Blob url to lookup.\n * @param options - Additional options for the request.\n */\nexport async function head(\n url: string,\n options?: BlobCommandOptions,\n): Promise<HeadBlobResult> {\n const searchParams = new URLSearchParams({ url });\n\n const response = await requestApi<HeadBlobApiResponse>(\n `?${searchParams.toString()}`,\n // HEAD can't have body as a response, so we use GET\n {\n method: 'GET',\n signal: options?.abortSignal,\n },\n options,\n );\n\n return {\n url: response.url,\n downloadUrl: response.downloadUrl,\n pathname: response.pathname,\n size: response.size,\n contentType: response.contentType,\n contentDisposition: response.contentDisposition,\n cacheControl: response.cacheControl,\n uploadedAt: new Date(response.uploadedAt),\n };\n}\n","import { requestApi } from './api';\nimport type { BlobCommandOptions } from './helpers';\n\nexport interface ListBlobResultBlob {\n url: string;\n downloadUrl: string;\n pathname: string;\n size: number;\n uploadedAt: Date;\n}\n\nexport interface ListBlobResult {\n blobs: ListBlobResultBlob[];\n cursor?: string;\n hasMore: boolean;\n}\n\nexport interface ListFoldedBlobResult extends ListBlobResult {\n folders: string[];\n}\n\ninterface ListBlobApiResponseBlob\n extends Omit<ListBlobResultBlob, 'uploadedAt'> {\n uploadedAt: string;\n}\n\ninterface ListBlobApiResponse extends Omit<ListBlobResult, 'blobs'> {\n blobs: ListBlobApiResponseBlob[];\n folders?: string[];\n}\n\nexport interface ListCommandOptions<\n M extends 'expanded' | 'folded' | undefined = undefined,\n> extends BlobCommandOptions {\n /**\n * The maximum number of blobs to return.\n * @defaultvalue 1000\n */\n limit?: number;\n /**\n * Filters the result to only include blobs that start with this prefix.\n * If used together with `mode: 'folded'`, make sure to include a trailing slash after the foldername.\n */\n prefix?: string;\n /**\n * The cursor to use for pagination. Can be obtained from the response of a previous `list` request.\n */\n cursor?: string;\n /**\n * Defines how the blobs are listed\n * - `expanded` the blobs property contains all blobs.\n * - `folded` the blobs property contains only the blobs at the root level of your store. Blobs that are located inside a folder get merged into a single entry in the folder response property.\n * @defaultvalue 'expanded'\n */\n mode?: M;\n}\n\ntype ListCommandResult<\n M extends 'expanded' | 'folded' | undefined = undefined,\n> = M extends 'folded' ? ListFoldedBlobResult : ListBlobResult;\n\n/**\n * Fetches a paginated list of blob objects from your store.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#list-blobs\n *\n * @param options - Additional options for the request.\n */\nexport async function list<\n M extends 'expanded' | 'folded' | undefined = undefined,\n>(options?: ListCommandOptions<M>): Promise<ListCommandResult<M>> {\n const searchParams = new URLSearchParams();\n\n if (options?.limit) {\n searchParams.set('limit', options.limit.toString());\n }\n if (options?.prefix) {\n searchParams.set('prefix', options.prefix);\n }\n if (options?.cursor) {\n searchParams.set('cursor', options.cursor);\n }\n if (options?.mode) {\n searchParams.set('mode', options.mode);\n }\n\n const response = await requestApi<ListBlobApiResponse>(\n `?${searchParams.toString()}`,\n {\n method: 'GET',\n signal: options?.abortSignal,\n },\n options,\n );\n\n if (options?.mode === 'folded') {\n return {\n folders: response.folders ?? [],\n cursor: response.cursor,\n hasMore: response.hasMore,\n blobs: response.blobs.map(mapBlobResult),\n } as ListCommandResult<M>;\n }\n\n return {\n cursor: response.cursor,\n hasMore: response.hasMore,\n blobs: response.blobs.map(mapBlobResult),\n } as ListCommandResult<M>;\n}\n\nfunction mapBlobResult(\n blobResult: ListBlobApiResponseBlob,\n): ListBlobResultBlob {\n return {\n url: blobResult.url,\n downloadUrl: blobResult.downloadUrl,\n pathname: blobResult.pathname,\n size: blobResult.size,\n uploadedAt: new Date(blobResult.uploadedAt),\n };\n}\n","import { MAXIMUM_PATHNAME_LENGTH, requestApi } from './api';\nimport type { CommonCreateBlobOptions } from './helpers';\nimport { BlobError, disallowedPathnameCharacters } from './helpers';\n\nexport type CopyCommandOptions = CommonCreateBlobOptions;\n\nexport interface CopyBlobResult {\n url: string;\n downloadUrl: string;\n pathname: string;\n contentType?: string;\n contentDisposition: string;\n}\n\n/**\n * Copies a blob to another location in your store.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#copy-a-blob\n *\n * @param fromUrl - The blob URL to copy. You can only copy blobs that are in the store, that your 'BLOB_READ_WRITE_TOKEN' has access to.\n * @param toPathname - The pathname to copy the blob to. This includes the filename.\n * @param options - Additional options. The copy method will not preserve any metadata configuration (e.g.: 'cacheControlMaxAge') of the source blob. If you want to copy the metadata, you need to define it here again.\n */\nexport async function copy(\n fromUrl: string,\n toPathname: string,\n options: CopyCommandOptions,\n): Promise<CopyBlobResult> {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Runtime check for DX.\n if (!options) {\n throw new BlobError('missing options, see usage');\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Runtime check for DX.\n if (options.access !== 'public') {\n throw new BlobError('access must be \"public\"');\n }\n\n if (toPathname.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 (toPathname.includes(invalidCharacter)) {\n throw new BlobError(\n `pathname cannot contain \"${invalidCharacter}\", please encode it if needed`,\n );\n }\n }\n\n const headers: Record<string, string> = {};\n\n if (options.addRandomSuffix !== undefined) {\n headers['x-add-random-suffix'] = options.addRandomSuffix ? '1' : '0';\n }\n\n if (options.contentType) {\n headers['x-content-type'] = options.contentType;\n }\n\n if (options.cacheControlMaxAge !== undefined) {\n headers['x-cache-control-max-age'] = options.cacheControlMaxAge.toString();\n }\n\n const response = await requestApi<CopyBlobResult>(\n `/${toPathname}?fromUrl=${fromUrl}`,\n {\n method: 'PUT',\n headers,\n signal: options.abortSignal,\n },\n options,\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 };\n}\n","import type { PutCommandOptions } from './put';\nimport { createPutMethod } from './put';\nimport { createCreateMultipartUploadMethod } from './multipart/create';\nimport type { UploadPartCommandOptions } from './multipart/upload';\nimport { createUploadPartMethod } from './multipart/upload';\nimport type { CompleteMultipartUploadCommandOptions } from './multipart/complete';\nimport { createCompleteMultipartUploadMethod } from './multipart/complete';\nimport type { CommonCreateBlobOptions } from './helpers';\nimport { createCreateMultipartUploaderMethod } from './multipart/create-uploader';\n\n// expose generic BlobError and download url util\nexport {\n BlobError,\n getDownloadUrl,\n type OnUploadProgressCallback,\n type UploadProgressEvent,\n} from './helpers';\n\n// expose api BlobErrors\nexport {\n BlobAccessError,\n BlobNotFoundError,\n BlobStoreNotFoundError,\n BlobStoreSuspendedError,\n BlobUnknownError,\n BlobServiceNotAvailable,\n BlobRequestAbortedError,\n BlobServiceRateLimited,\n BlobContentTypeNotAllowedError,\n BlobPathnameMismatchError,\n BlobClientTokenExpiredError,\n BlobFileTooLargeError,\n} from './api';\n\n// vercelBlob.put()\n\nexport type { PutBlobResult } from './put-helpers';\nexport type { PutCommandOptions };\n\n/**\n * Uploads a blob into your store from your server.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#upload-a-blob\n *\n * If you want to upload from the browser directly, check out the documentation forAclient uploads: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#client-uploads\n *\n * @param pathname - The pathname to upload the blob to, including the extension. This will influence the url of your blob like https://$storeId.public.blob.vercel-storage.com/$pathname.\n * @param body - The content of your blob, can be a: string, File, Blob, Buffer or Stream. We support almost everything fetch supports: https://developer.mozilla.org/en-US/docs/Web/API/RequestInit#body.\n * @param options - Additional options like `token` or `contentType`.\n */\nexport const put = createPutMethod<PutCommandOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n});\n\n// vercelBlob.del()\n\nexport { del } from './del';\n\n// vercelBlob.head()\n\nexport type { HeadBlobResult } from './head';\nexport { head } from './head';\n\n// vercelBlob.list()\n\nexport type {\n ListBlobResultBlob,\n ListBlobResult,\n ListCommandOptions,\n ListFoldedBlobResult,\n} from './list';\nexport { list } from './list';\n\n// vercelBlob.copy()\n\nexport type { CopyBlobResult, CopyCommandOptions } from './copy';\nexport { copy } from './copy';\n\n// vercelBlob. createMultipartUpload()\n// vercelBlob. uploadPart()\n// vercelBlob. completeMultipartUpload()\n// vercelBlob. createMultipartUploader()\n\nexport const createMultipartUpload =\n createCreateMultipartUploadMethod<CommonCreateBlobOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n });\n\nexport const createMultipartUploader =\n createCreateMultipartUploaderMethod<CommonCreateBlobOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n });\n\nexport type { UploadPartCommandOptions };\nexport const uploadPart = createUploadPartMethod<UploadPartCommandOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n});\n\nexport type { CompleteMultipartUploadCommandOptions };\nexport const completeMultipartUpload =\n createCompleteMultipartUploadMethod<CompleteMultipartUploadCommandOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n });\n\nexport type { Part, PartInput } from './multipart/helpers';\n\nexport { createFolder } from './create-folder';\n"]}
1
+ {"version":3,"sources":["/home/runner/work/storage/storage/packages/blob/dist/index.cjs","../src/del.ts","../src/head.ts","../src/list.ts","../src/copy.ts","../src/index.ts"],"names":[],"mappings":"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","file":"/home/runner/work/storage/storage/packages/blob/dist/index.cjs","sourcesContent":[null,"import { requestApi } from './api';\nimport type { BlobCommandOptions } from './helpers';\n\n/**\n * Deletes one or multiple blobs from your store.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#delete-a-blob\n *\n * @param url - Blob url or array of blob urls that identify the blobs to be deleted. You can only delete blobs that are located in a store, that your 'BLOB_READ_WRITE_TOKEN' has access to.\n * @param options - Additional options for the request.\n */\nexport async function del(\n url: string[] | string,\n options?: BlobCommandOptions,\n): Promise<void> {\n await requestApi(\n '/delete',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ urls: Array.isArray(url) ? url : [url] }),\n signal: options?.abortSignal,\n },\n options,\n );\n}\n","import { requestApi } from './api';\nimport type { BlobCommandOptions } from './helpers';\n\nexport interface HeadBlobResult {\n url: string;\n downloadUrl: string;\n size: number;\n uploadedAt: Date;\n pathname: string;\n contentType: string;\n contentDisposition: string;\n cacheControl: string;\n}\n\ninterface HeadBlobApiResponse extends Omit<HeadBlobResult, 'uploadedAt'> {\n uploadedAt: string; // when receiving data from our API, uploadedAt is a string\n}\n\n/**\n * Fetches metadata of a blob object.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#get-blob-metadata\n *\n * @param url - Blob url to lookup.\n * @param options - Additional options for the request.\n */\nexport async function head(\n url: string,\n options?: BlobCommandOptions,\n): Promise<HeadBlobResult> {\n const searchParams = new URLSearchParams({ url });\n\n const response = await requestApi<HeadBlobApiResponse>(\n `?${searchParams.toString()}`,\n // HEAD can't have body as a response, so we use GET\n {\n method: 'GET',\n signal: options?.abortSignal,\n },\n options,\n );\n\n return {\n url: response.url,\n downloadUrl: response.downloadUrl,\n pathname: response.pathname,\n size: response.size,\n contentType: response.contentType,\n contentDisposition: response.contentDisposition,\n cacheControl: response.cacheControl,\n uploadedAt: new Date(response.uploadedAt),\n };\n}\n","import { requestApi } from './api';\nimport type { BlobCommandOptions } from './helpers';\n\nexport interface ListBlobResultBlob {\n url: string;\n downloadUrl: string;\n pathname: string;\n size: number;\n uploadedAt: Date;\n}\n\nexport interface ListBlobResult {\n blobs: ListBlobResultBlob[];\n cursor?: string;\n hasMore: boolean;\n}\n\nexport interface ListFoldedBlobResult extends ListBlobResult {\n folders: string[];\n}\n\ninterface ListBlobApiResponseBlob\n extends Omit<ListBlobResultBlob, 'uploadedAt'> {\n uploadedAt: string;\n}\n\ninterface ListBlobApiResponse extends Omit<ListBlobResult, 'blobs'> {\n blobs: ListBlobApiResponseBlob[];\n folders?: string[];\n}\n\nexport interface ListCommandOptions<\n M extends 'expanded' | 'folded' | undefined = undefined,\n> extends BlobCommandOptions {\n /**\n * The maximum number of blobs to return.\n * @defaultvalue 1000\n */\n limit?: number;\n /**\n * Filters the result to only include blobs that start with this prefix.\n * If used together with `mode: 'folded'`, make sure to include a trailing slash after the foldername.\n */\n prefix?: string;\n /**\n * The cursor to use for pagination. Can be obtained from the response of a previous `list` request.\n */\n cursor?: string;\n /**\n * Defines how the blobs are listed\n * - `expanded` the blobs property contains all blobs.\n * - `folded` the blobs property contains only the blobs at the root level of your store. Blobs that are located inside a folder get merged into a single entry in the folder response property.\n * @defaultvalue 'expanded'\n */\n mode?: M;\n}\n\ntype ListCommandResult<\n M extends 'expanded' | 'folded' | undefined = undefined,\n> = M extends 'folded' ? ListFoldedBlobResult : ListBlobResult;\n\n/**\n * Fetches a paginated list of blob objects from your store.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#list-blobs\n *\n * @param options - Additional options for the request.\n */\nexport async function list<\n M extends 'expanded' | 'folded' | undefined = undefined,\n>(options?: ListCommandOptions<M>): Promise<ListCommandResult<M>> {\n const searchParams = new URLSearchParams();\n\n if (options?.limit) {\n searchParams.set('limit', options.limit.toString());\n }\n if (options?.prefix) {\n searchParams.set('prefix', options.prefix);\n }\n if (options?.cursor) {\n searchParams.set('cursor', options.cursor);\n }\n if (options?.mode) {\n searchParams.set('mode', options.mode);\n }\n\n const response = await requestApi<ListBlobApiResponse>(\n `?${searchParams.toString()}`,\n {\n method: 'GET',\n signal: options?.abortSignal,\n },\n options,\n );\n\n if (options?.mode === 'folded') {\n return {\n folders: response.folders ?? [],\n cursor: response.cursor,\n hasMore: response.hasMore,\n blobs: response.blobs.map(mapBlobResult),\n } as ListCommandResult<M>;\n }\n\n return {\n cursor: response.cursor,\n hasMore: response.hasMore,\n blobs: response.blobs.map(mapBlobResult),\n } as ListCommandResult<M>;\n}\n\nfunction mapBlobResult(\n blobResult: ListBlobApiResponseBlob,\n): ListBlobResultBlob {\n return {\n url: blobResult.url,\n downloadUrl: blobResult.downloadUrl,\n pathname: blobResult.pathname,\n size: blobResult.size,\n uploadedAt: new Date(blobResult.uploadedAt),\n };\n}\n","import { MAXIMUM_PATHNAME_LENGTH, requestApi } from './api';\nimport type { CommonCreateBlobOptions } from './helpers';\nimport { BlobError, disallowedPathnameCharacters } from './helpers';\n\nexport type CopyCommandOptions = CommonCreateBlobOptions;\n\nexport interface CopyBlobResult {\n url: string;\n downloadUrl: string;\n pathname: string;\n contentType: string;\n contentDisposition: string;\n}\n\n/**\n * Copies a blob to another location in your store.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#copy-a-blob\n *\n * @param fromUrl - The blob URL to copy. You can only copy blobs that are in the store, that your 'BLOB_READ_WRITE_TOKEN' has access to.\n * @param toPathname - The pathname to copy the blob to. This includes the filename.\n * @param options - Additional options. The copy method will not preserve any metadata configuration (e.g.: 'cacheControlMaxAge') of the source blob. If you want to copy the metadata, you need to define it here again.\n */\nexport async function copy(\n fromUrl: string,\n toPathname: string,\n options: CopyCommandOptions,\n): Promise<CopyBlobResult> {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Runtime check for DX.\n if (!options) {\n throw new BlobError('missing options, see usage');\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Runtime check for DX.\n if (options.access !== 'public') {\n throw new BlobError('access must be \"public\"');\n }\n\n if (toPathname.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 (toPathname.includes(invalidCharacter)) {\n throw new BlobError(\n `pathname cannot contain \"${invalidCharacter}\", please encode it if needed`,\n );\n }\n }\n\n const headers: Record<string, string> = {};\n\n if (options.addRandomSuffix !== undefined) {\n headers['x-add-random-suffix'] = options.addRandomSuffix ? '1' : '0';\n }\n\n if (options.contentType) {\n headers['x-content-type'] = options.contentType;\n }\n\n if (options.cacheControlMaxAge !== undefined) {\n headers['x-cache-control-max-age'] = options.cacheControlMaxAge.toString();\n }\n\n const response = await requestApi<CopyBlobResult>(\n `/${toPathname}?fromUrl=${fromUrl}`,\n {\n method: 'PUT',\n headers,\n signal: options.abortSignal,\n },\n options,\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 };\n}\n","import type { PutCommandOptions } from './put';\nimport { createPutMethod } from './put';\nimport { createCreateMultipartUploadMethod } from './multipart/create';\nimport type { UploadPartCommandOptions } from './multipart/upload';\nimport { createUploadPartMethod } from './multipart/upload';\nimport type { CompleteMultipartUploadCommandOptions } from './multipart/complete';\nimport { createCompleteMultipartUploadMethod } from './multipart/complete';\nimport type { CommonCreateBlobOptions } from './helpers';\nimport { createCreateMultipartUploaderMethod } from './multipart/create-uploader';\n\n// expose generic BlobError and download url util\nexport {\n BlobError,\n getDownloadUrl,\n type OnUploadProgressCallback,\n type UploadProgressEvent,\n} from './helpers';\n\n// expose api BlobErrors\nexport {\n BlobAccessError,\n BlobNotFoundError,\n BlobStoreNotFoundError,\n BlobStoreSuspendedError,\n BlobUnknownError,\n BlobServiceNotAvailable,\n BlobRequestAbortedError,\n BlobServiceRateLimited,\n BlobContentTypeNotAllowedError,\n BlobPathnameMismatchError,\n BlobClientTokenExpiredError,\n BlobFileTooLargeError,\n} from './api';\n\n// vercelBlob.put()\n\nexport type { PutBlobResult } from './put-helpers';\nexport type { PutCommandOptions };\n\n/**\n * Uploads a blob into your store from your server.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#upload-a-blob\n *\n * If you want to upload from the browser directly, check out the documentation forAclient uploads: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#client-uploads\n *\n * @param pathname - The pathname to upload the blob to, including the extension. This will influence the url of your blob like https://$storeId.public.blob.vercel-storage.com/$pathname.\n * @param body - The content of your blob, can be a: string, File, Blob, Buffer or Stream. We support almost everything fetch supports: https://developer.mozilla.org/en-US/docs/Web/API/RequestInit#body.\n * @param options - Additional options like `token` or `contentType`.\n */\nexport const put = createPutMethod<PutCommandOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n});\n\n// vercelBlob.del()\n\nexport { del } from './del';\n\n// vercelBlob.head()\n\nexport type { HeadBlobResult } from './head';\nexport { head } from './head';\n\n// vercelBlob.list()\n\nexport type {\n ListBlobResultBlob,\n ListBlobResult,\n ListCommandOptions,\n ListFoldedBlobResult,\n} from './list';\nexport { list } from './list';\n\n// vercelBlob.copy()\n\nexport type { CopyBlobResult, CopyCommandOptions } from './copy';\nexport { copy } from './copy';\n\n// vercelBlob. createMultipartUpload()\n// vercelBlob. uploadPart()\n// vercelBlob. completeMultipartUpload()\n// vercelBlob. createMultipartUploader()\n\nexport const createMultipartUpload =\n createCreateMultipartUploadMethod<CommonCreateBlobOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n });\n\nexport const createMultipartUploader =\n createCreateMultipartUploaderMethod<CommonCreateBlobOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n });\n\nexport type { UploadPartCommandOptions };\nexport const uploadPart = createUploadPartMethod<UploadPartCommandOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n});\n\nexport type { CompleteMultipartUploadCommandOptions };\nexport const completeMultipartUpload =\n createCompleteMultipartUploadMethod<CompleteMultipartUploadCommandOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n });\n\nexport type { Part, PartInput } from './multipart/helpers';\n\nexport { createFolder } from './create-folder';\n"]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { e as CommonCreateBlobOptions, W as WithUploadProgress, f as BlobError, B as BlobCommandOptions, P as PutBody, a as PutBlobResult, b as Part, U as UploadPartCommandOptions, g as CompleteMultipartUploadCommandOptions } from './create-folder-DGBfYPet.cjs';
2
- export { O as OnUploadProgressCallback, j as PartInput, i as UploadProgressEvent, d as createFolder, h as getDownloadUrl } from './create-folder-DGBfYPet.cjs';
1
+ import { e as CommonCreateBlobOptions, W as WithUploadProgress, f as BlobError, B as BlobCommandOptions, P as PutBody, a as PutBlobResult, b as Part, U as UploadPartCommandOptions, g as CompleteMultipartUploadCommandOptions } from './create-folder-CqdraABG.cjs';
2
+ export { O as OnUploadProgressCallback, j as PartInput, i as UploadProgressEvent, d as createFolder, h as getDownloadUrl } from './create-folder-CqdraABG.cjs';
3
3
  import 'stream';
4
4
  import 'undici';
5
5
 
@@ -129,7 +129,7 @@ interface CopyBlobResult {
129
129
  url: string;
130
130
  downloadUrl: string;
131
131
  pathname: string;
132
- contentType?: string;
132
+ contentType: string;
133
133
  contentDisposition: string;
134
134
  }
135
135
  /**
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { e as CommonCreateBlobOptions, W as WithUploadProgress, f as BlobError, B as BlobCommandOptions, P as PutBody, a as PutBlobResult, b as Part, U as UploadPartCommandOptions, g as CompleteMultipartUploadCommandOptions } from './create-folder-DGBfYPet.js';
2
- export { O as OnUploadProgressCallback, j as PartInput, i as UploadProgressEvent, d as createFolder, h as getDownloadUrl } from './create-folder-DGBfYPet.js';
1
+ import { e as CommonCreateBlobOptions, W as WithUploadProgress, f as BlobError, B as BlobCommandOptions, P as PutBody, a as PutBlobResult, b as Part, U as UploadPartCommandOptions, g as CompleteMultipartUploadCommandOptions } from './create-folder-CqdraABG.js';
2
+ export { O as OnUploadProgressCallback, j as PartInput, i as UploadProgressEvent, d as createFolder, h as getDownloadUrl } from './create-folder-CqdraABG.js';
3
3
  import 'stream';
4
4
  import 'undici';
5
5
 
@@ -129,7 +129,7 @@ interface CopyBlobResult {
129
129
  url: string;
130
130
  downloadUrl: string;
131
131
  pathname: string;
132
- contentType?: string;
132
+ contentType: string;
133
133
  contentDisposition: string;
134
134
  }
135
135
  /**
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@ import {
22
22
  disallowedPathnameCharacters,
23
23
  getDownloadUrl,
24
24
  requestApi
25
- } from "./chunk-QXAUX3LT.js";
25
+ } from "./chunk-KN6WT5GP.js";
26
26
 
27
27
  // src/del.ts
28
28
  async function del(url, options) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/del.ts","../src/head.ts","../src/list.ts","../src/copy.ts","../src/index.ts"],"sourcesContent":["import { requestApi } from './api';\nimport type { BlobCommandOptions } from './helpers';\n\n/**\n * Deletes one or multiple blobs from your store.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#delete-a-blob\n *\n * @param url - Blob url or array of blob urls that identify the blobs to be deleted. You can only delete blobs that are located in a store, that your 'BLOB_READ_WRITE_TOKEN' has access to.\n * @param options - Additional options for the request.\n */\nexport async function del(\n url: string[] | string,\n options?: BlobCommandOptions,\n): Promise<void> {\n await requestApi(\n '/delete',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ urls: Array.isArray(url) ? url : [url] }),\n signal: options?.abortSignal,\n },\n options,\n );\n}\n","import { requestApi } from './api';\nimport type { BlobCommandOptions } from './helpers';\n\nexport interface HeadBlobResult {\n url: string;\n downloadUrl: string;\n size: number;\n uploadedAt: Date;\n pathname: string;\n contentType: string;\n contentDisposition: string;\n cacheControl: string;\n}\n\ninterface HeadBlobApiResponse extends Omit<HeadBlobResult, 'uploadedAt'> {\n uploadedAt: string; // when receiving data from our API, uploadedAt is a string\n}\n\n/**\n * Fetches metadata of a blob object.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#get-blob-metadata\n *\n * @param url - Blob url to lookup.\n * @param options - Additional options for the request.\n */\nexport async function head(\n url: string,\n options?: BlobCommandOptions,\n): Promise<HeadBlobResult> {\n const searchParams = new URLSearchParams({ url });\n\n const response = await requestApi<HeadBlobApiResponse>(\n `?${searchParams.toString()}`,\n // HEAD can't have body as a response, so we use GET\n {\n method: 'GET',\n signal: options?.abortSignal,\n },\n options,\n );\n\n return {\n url: response.url,\n downloadUrl: response.downloadUrl,\n pathname: response.pathname,\n size: response.size,\n contentType: response.contentType,\n contentDisposition: response.contentDisposition,\n cacheControl: response.cacheControl,\n uploadedAt: new Date(response.uploadedAt),\n };\n}\n","import { requestApi } from './api';\nimport type { BlobCommandOptions } from './helpers';\n\nexport interface ListBlobResultBlob {\n url: string;\n downloadUrl: string;\n pathname: string;\n size: number;\n uploadedAt: Date;\n}\n\nexport interface ListBlobResult {\n blobs: ListBlobResultBlob[];\n cursor?: string;\n hasMore: boolean;\n}\n\nexport interface ListFoldedBlobResult extends ListBlobResult {\n folders: string[];\n}\n\ninterface ListBlobApiResponseBlob\n extends Omit<ListBlobResultBlob, 'uploadedAt'> {\n uploadedAt: string;\n}\n\ninterface ListBlobApiResponse extends Omit<ListBlobResult, 'blobs'> {\n blobs: ListBlobApiResponseBlob[];\n folders?: string[];\n}\n\nexport interface ListCommandOptions<\n M extends 'expanded' | 'folded' | undefined = undefined,\n> extends BlobCommandOptions {\n /**\n * The maximum number of blobs to return.\n * @defaultvalue 1000\n */\n limit?: number;\n /**\n * Filters the result to only include blobs that start with this prefix.\n * If used together with `mode: 'folded'`, make sure to include a trailing slash after the foldername.\n */\n prefix?: string;\n /**\n * The cursor to use for pagination. Can be obtained from the response of a previous `list` request.\n */\n cursor?: string;\n /**\n * Defines how the blobs are listed\n * - `expanded` the blobs property contains all blobs.\n * - `folded` the blobs property contains only the blobs at the root level of your store. Blobs that are located inside a folder get merged into a single entry in the folder response property.\n * @defaultvalue 'expanded'\n */\n mode?: M;\n}\n\ntype ListCommandResult<\n M extends 'expanded' | 'folded' | undefined = undefined,\n> = M extends 'folded' ? ListFoldedBlobResult : ListBlobResult;\n\n/**\n * Fetches a paginated list of blob objects from your store.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#list-blobs\n *\n * @param options - Additional options for the request.\n */\nexport async function list<\n M extends 'expanded' | 'folded' | undefined = undefined,\n>(options?: ListCommandOptions<M>): Promise<ListCommandResult<M>> {\n const searchParams = new URLSearchParams();\n\n if (options?.limit) {\n searchParams.set('limit', options.limit.toString());\n }\n if (options?.prefix) {\n searchParams.set('prefix', options.prefix);\n }\n if (options?.cursor) {\n searchParams.set('cursor', options.cursor);\n }\n if (options?.mode) {\n searchParams.set('mode', options.mode);\n }\n\n const response = await requestApi<ListBlobApiResponse>(\n `?${searchParams.toString()}`,\n {\n method: 'GET',\n signal: options?.abortSignal,\n },\n options,\n );\n\n if (options?.mode === 'folded') {\n return {\n folders: response.folders ?? [],\n cursor: response.cursor,\n hasMore: response.hasMore,\n blobs: response.blobs.map(mapBlobResult),\n } as ListCommandResult<M>;\n }\n\n return {\n cursor: response.cursor,\n hasMore: response.hasMore,\n blobs: response.blobs.map(mapBlobResult),\n } as ListCommandResult<M>;\n}\n\nfunction mapBlobResult(\n blobResult: ListBlobApiResponseBlob,\n): ListBlobResultBlob {\n return {\n url: blobResult.url,\n downloadUrl: blobResult.downloadUrl,\n pathname: blobResult.pathname,\n size: blobResult.size,\n uploadedAt: new Date(blobResult.uploadedAt),\n };\n}\n","import { MAXIMUM_PATHNAME_LENGTH, requestApi } from './api';\nimport type { CommonCreateBlobOptions } from './helpers';\nimport { BlobError, disallowedPathnameCharacters } from './helpers';\n\nexport type CopyCommandOptions = CommonCreateBlobOptions;\n\nexport interface CopyBlobResult {\n url: string;\n downloadUrl: string;\n pathname: string;\n contentType?: string;\n contentDisposition: string;\n}\n\n/**\n * Copies a blob to another location in your store.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#copy-a-blob\n *\n * @param fromUrl - The blob URL to copy. You can only copy blobs that are in the store, that your 'BLOB_READ_WRITE_TOKEN' has access to.\n * @param toPathname - The pathname to copy the blob to. This includes the filename.\n * @param options - Additional options. The copy method will not preserve any metadata configuration (e.g.: 'cacheControlMaxAge') of the source blob. If you want to copy the metadata, you need to define it here again.\n */\nexport async function copy(\n fromUrl: string,\n toPathname: string,\n options: CopyCommandOptions,\n): Promise<CopyBlobResult> {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Runtime check for DX.\n if (!options) {\n throw new BlobError('missing options, see usage');\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Runtime check for DX.\n if (options.access !== 'public') {\n throw new BlobError('access must be \"public\"');\n }\n\n if (toPathname.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 (toPathname.includes(invalidCharacter)) {\n throw new BlobError(\n `pathname cannot contain \"${invalidCharacter}\", please encode it if needed`,\n );\n }\n }\n\n const headers: Record<string, string> = {};\n\n if (options.addRandomSuffix !== undefined) {\n headers['x-add-random-suffix'] = options.addRandomSuffix ? '1' : '0';\n }\n\n if (options.contentType) {\n headers['x-content-type'] = options.contentType;\n }\n\n if (options.cacheControlMaxAge !== undefined) {\n headers['x-cache-control-max-age'] = options.cacheControlMaxAge.toString();\n }\n\n const response = await requestApi<CopyBlobResult>(\n `/${toPathname}?fromUrl=${fromUrl}`,\n {\n method: 'PUT',\n headers,\n signal: options.abortSignal,\n },\n options,\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 };\n}\n","import type { PutCommandOptions } from './put';\nimport { createPutMethod } from './put';\nimport { createCreateMultipartUploadMethod } from './multipart/create';\nimport type { UploadPartCommandOptions } from './multipart/upload';\nimport { createUploadPartMethod } from './multipart/upload';\nimport type { CompleteMultipartUploadCommandOptions } from './multipart/complete';\nimport { createCompleteMultipartUploadMethod } from './multipart/complete';\nimport type { CommonCreateBlobOptions } from './helpers';\nimport { createCreateMultipartUploaderMethod } from './multipart/create-uploader';\n\n// expose generic BlobError and download url util\nexport {\n BlobError,\n getDownloadUrl,\n type OnUploadProgressCallback,\n type UploadProgressEvent,\n} from './helpers';\n\n// expose api BlobErrors\nexport {\n BlobAccessError,\n BlobNotFoundError,\n BlobStoreNotFoundError,\n BlobStoreSuspendedError,\n BlobUnknownError,\n BlobServiceNotAvailable,\n BlobRequestAbortedError,\n BlobServiceRateLimited,\n BlobContentTypeNotAllowedError,\n BlobPathnameMismatchError,\n BlobClientTokenExpiredError,\n BlobFileTooLargeError,\n} from './api';\n\n// vercelBlob.put()\n\nexport type { PutBlobResult } from './put-helpers';\nexport type { PutCommandOptions };\n\n/**\n * Uploads a blob into your store from your server.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#upload-a-blob\n *\n * If you want to upload from the browser directly, check out the documentation forAclient uploads: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#client-uploads\n *\n * @param pathname - The pathname to upload the blob to, including the extension. This will influence the url of your blob like https://$storeId.public.blob.vercel-storage.com/$pathname.\n * @param body - The content of your blob, can be a: string, File, Blob, Buffer or Stream. We support almost everything fetch supports: https://developer.mozilla.org/en-US/docs/Web/API/RequestInit#body.\n * @param options - Additional options like `token` or `contentType`.\n */\nexport const put = createPutMethod<PutCommandOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n});\n\n// vercelBlob.del()\n\nexport { del } from './del';\n\n// vercelBlob.head()\n\nexport type { HeadBlobResult } from './head';\nexport { head } from './head';\n\n// vercelBlob.list()\n\nexport type {\n ListBlobResultBlob,\n ListBlobResult,\n ListCommandOptions,\n ListFoldedBlobResult,\n} from './list';\nexport { list } from './list';\n\n// vercelBlob.copy()\n\nexport type { CopyBlobResult, CopyCommandOptions } from './copy';\nexport { copy } from './copy';\n\n// vercelBlob. createMultipartUpload()\n// vercelBlob. uploadPart()\n// vercelBlob. completeMultipartUpload()\n// vercelBlob. createMultipartUploader()\n\nexport const createMultipartUpload =\n createCreateMultipartUploadMethod<CommonCreateBlobOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n });\n\nexport const createMultipartUploader =\n createCreateMultipartUploaderMethod<CommonCreateBlobOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n });\n\nexport type { UploadPartCommandOptions };\nexport const uploadPart = createUploadPartMethod<UploadPartCommandOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n});\n\nexport type { CompleteMultipartUploadCommandOptions };\nexport const completeMultipartUpload =\n createCompleteMultipartUploadMethod<CompleteMultipartUploadCommandOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n });\n\nexport type { Part, PartInput } from './multipart/helpers';\n\nexport { createFolder } from './create-folder';\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/del.ts","../src/head.ts","../src/list.ts","../src/copy.ts","../src/index.ts"],"sourcesContent":["import { requestApi } from './api';\nimport type { BlobCommandOptions } from './helpers';\n\n/**\n * Deletes one or multiple blobs from your store.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#delete-a-blob\n *\n * @param url - Blob url or array of blob urls that identify the blobs to be deleted. You can only delete blobs that are located in a store, that your 'BLOB_READ_WRITE_TOKEN' has access to.\n * @param options - Additional options for the request.\n */\nexport async function del(\n url: string[] | string,\n options?: BlobCommandOptions,\n): Promise<void> {\n await requestApi(\n '/delete',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ urls: Array.isArray(url) ? url : [url] }),\n signal: options?.abortSignal,\n },\n options,\n );\n}\n","import { requestApi } from './api';\nimport type { BlobCommandOptions } from './helpers';\n\nexport interface HeadBlobResult {\n url: string;\n downloadUrl: string;\n size: number;\n uploadedAt: Date;\n pathname: string;\n contentType: string;\n contentDisposition: string;\n cacheControl: string;\n}\n\ninterface HeadBlobApiResponse extends Omit<HeadBlobResult, 'uploadedAt'> {\n uploadedAt: string; // when receiving data from our API, uploadedAt is a string\n}\n\n/**\n * Fetches metadata of a blob object.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#get-blob-metadata\n *\n * @param url - Blob url to lookup.\n * @param options - Additional options for the request.\n */\nexport async function head(\n url: string,\n options?: BlobCommandOptions,\n): Promise<HeadBlobResult> {\n const searchParams = new URLSearchParams({ url });\n\n const response = await requestApi<HeadBlobApiResponse>(\n `?${searchParams.toString()}`,\n // HEAD can't have body as a response, so we use GET\n {\n method: 'GET',\n signal: options?.abortSignal,\n },\n options,\n );\n\n return {\n url: response.url,\n downloadUrl: response.downloadUrl,\n pathname: response.pathname,\n size: response.size,\n contentType: response.contentType,\n contentDisposition: response.contentDisposition,\n cacheControl: response.cacheControl,\n uploadedAt: new Date(response.uploadedAt),\n };\n}\n","import { requestApi } from './api';\nimport type { BlobCommandOptions } from './helpers';\n\nexport interface ListBlobResultBlob {\n url: string;\n downloadUrl: string;\n pathname: string;\n size: number;\n uploadedAt: Date;\n}\n\nexport interface ListBlobResult {\n blobs: ListBlobResultBlob[];\n cursor?: string;\n hasMore: boolean;\n}\n\nexport interface ListFoldedBlobResult extends ListBlobResult {\n folders: string[];\n}\n\ninterface ListBlobApiResponseBlob\n extends Omit<ListBlobResultBlob, 'uploadedAt'> {\n uploadedAt: string;\n}\n\ninterface ListBlobApiResponse extends Omit<ListBlobResult, 'blobs'> {\n blobs: ListBlobApiResponseBlob[];\n folders?: string[];\n}\n\nexport interface ListCommandOptions<\n M extends 'expanded' | 'folded' | undefined = undefined,\n> extends BlobCommandOptions {\n /**\n * The maximum number of blobs to return.\n * @defaultvalue 1000\n */\n limit?: number;\n /**\n * Filters the result to only include blobs that start with this prefix.\n * If used together with `mode: 'folded'`, make sure to include a trailing slash after the foldername.\n */\n prefix?: string;\n /**\n * The cursor to use for pagination. Can be obtained from the response of a previous `list` request.\n */\n cursor?: string;\n /**\n * Defines how the blobs are listed\n * - `expanded` the blobs property contains all blobs.\n * - `folded` the blobs property contains only the blobs at the root level of your store. Blobs that are located inside a folder get merged into a single entry in the folder response property.\n * @defaultvalue 'expanded'\n */\n mode?: M;\n}\n\ntype ListCommandResult<\n M extends 'expanded' | 'folded' | undefined = undefined,\n> = M extends 'folded' ? ListFoldedBlobResult : ListBlobResult;\n\n/**\n * Fetches a paginated list of blob objects from your store.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#list-blobs\n *\n * @param options - Additional options for the request.\n */\nexport async function list<\n M extends 'expanded' | 'folded' | undefined = undefined,\n>(options?: ListCommandOptions<M>): Promise<ListCommandResult<M>> {\n const searchParams = new URLSearchParams();\n\n if (options?.limit) {\n searchParams.set('limit', options.limit.toString());\n }\n if (options?.prefix) {\n searchParams.set('prefix', options.prefix);\n }\n if (options?.cursor) {\n searchParams.set('cursor', options.cursor);\n }\n if (options?.mode) {\n searchParams.set('mode', options.mode);\n }\n\n const response = await requestApi<ListBlobApiResponse>(\n `?${searchParams.toString()}`,\n {\n method: 'GET',\n signal: options?.abortSignal,\n },\n options,\n );\n\n if (options?.mode === 'folded') {\n return {\n folders: response.folders ?? [],\n cursor: response.cursor,\n hasMore: response.hasMore,\n blobs: response.blobs.map(mapBlobResult),\n } as ListCommandResult<M>;\n }\n\n return {\n cursor: response.cursor,\n hasMore: response.hasMore,\n blobs: response.blobs.map(mapBlobResult),\n } as ListCommandResult<M>;\n}\n\nfunction mapBlobResult(\n blobResult: ListBlobApiResponseBlob,\n): ListBlobResultBlob {\n return {\n url: blobResult.url,\n downloadUrl: blobResult.downloadUrl,\n pathname: blobResult.pathname,\n size: blobResult.size,\n uploadedAt: new Date(blobResult.uploadedAt),\n };\n}\n","import { MAXIMUM_PATHNAME_LENGTH, requestApi } from './api';\nimport type { CommonCreateBlobOptions } from './helpers';\nimport { BlobError, disallowedPathnameCharacters } from './helpers';\n\nexport type CopyCommandOptions = CommonCreateBlobOptions;\n\nexport interface CopyBlobResult {\n url: string;\n downloadUrl: string;\n pathname: string;\n contentType: string;\n contentDisposition: string;\n}\n\n/**\n * Copies a blob to another location in your store.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#copy-a-blob\n *\n * @param fromUrl - The blob URL to copy. You can only copy blobs that are in the store, that your 'BLOB_READ_WRITE_TOKEN' has access to.\n * @param toPathname - The pathname to copy the blob to. This includes the filename.\n * @param options - Additional options. The copy method will not preserve any metadata configuration (e.g.: 'cacheControlMaxAge') of the source blob. If you want to copy the metadata, you need to define it here again.\n */\nexport async function copy(\n fromUrl: string,\n toPathname: string,\n options: CopyCommandOptions,\n): Promise<CopyBlobResult> {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Runtime check for DX.\n if (!options) {\n throw new BlobError('missing options, see usage');\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Runtime check for DX.\n if (options.access !== 'public') {\n throw new BlobError('access must be \"public\"');\n }\n\n if (toPathname.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 (toPathname.includes(invalidCharacter)) {\n throw new BlobError(\n `pathname cannot contain \"${invalidCharacter}\", please encode it if needed`,\n );\n }\n }\n\n const headers: Record<string, string> = {};\n\n if (options.addRandomSuffix !== undefined) {\n headers['x-add-random-suffix'] = options.addRandomSuffix ? '1' : '0';\n }\n\n if (options.contentType) {\n headers['x-content-type'] = options.contentType;\n }\n\n if (options.cacheControlMaxAge !== undefined) {\n headers['x-cache-control-max-age'] = options.cacheControlMaxAge.toString();\n }\n\n const response = await requestApi<CopyBlobResult>(\n `/${toPathname}?fromUrl=${fromUrl}`,\n {\n method: 'PUT',\n headers,\n signal: options.abortSignal,\n },\n options,\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 };\n}\n","import type { PutCommandOptions } from './put';\nimport { createPutMethod } from './put';\nimport { createCreateMultipartUploadMethod } from './multipart/create';\nimport type { UploadPartCommandOptions } from './multipart/upload';\nimport { createUploadPartMethod } from './multipart/upload';\nimport type { CompleteMultipartUploadCommandOptions } from './multipart/complete';\nimport { createCompleteMultipartUploadMethod } from './multipart/complete';\nimport type { CommonCreateBlobOptions } from './helpers';\nimport { createCreateMultipartUploaderMethod } from './multipart/create-uploader';\n\n// expose generic BlobError and download url util\nexport {\n BlobError,\n getDownloadUrl,\n type OnUploadProgressCallback,\n type UploadProgressEvent,\n} from './helpers';\n\n// expose api BlobErrors\nexport {\n BlobAccessError,\n BlobNotFoundError,\n BlobStoreNotFoundError,\n BlobStoreSuspendedError,\n BlobUnknownError,\n BlobServiceNotAvailable,\n BlobRequestAbortedError,\n BlobServiceRateLimited,\n BlobContentTypeNotAllowedError,\n BlobPathnameMismatchError,\n BlobClientTokenExpiredError,\n BlobFileTooLargeError,\n} from './api';\n\n// vercelBlob.put()\n\nexport type { PutBlobResult } from './put-helpers';\nexport type { PutCommandOptions };\n\n/**\n * Uploads a blob into your store from your server.\n * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#upload-a-blob\n *\n * If you want to upload from the browser directly, check out the documentation forAclient uploads: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#client-uploads\n *\n * @param pathname - The pathname to upload the blob to, including the extension. This will influence the url of your blob like https://$storeId.public.blob.vercel-storage.com/$pathname.\n * @param body - The content of your blob, can be a: string, File, Blob, Buffer or Stream. We support almost everything fetch supports: https://developer.mozilla.org/en-US/docs/Web/API/RequestInit#body.\n * @param options - Additional options like `token` or `contentType`.\n */\nexport const put = createPutMethod<PutCommandOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n});\n\n// vercelBlob.del()\n\nexport { del } from './del';\n\n// vercelBlob.head()\n\nexport type { HeadBlobResult } from './head';\nexport { head } from './head';\n\n// vercelBlob.list()\n\nexport type {\n ListBlobResultBlob,\n ListBlobResult,\n ListCommandOptions,\n ListFoldedBlobResult,\n} from './list';\nexport { list } from './list';\n\n// vercelBlob.copy()\n\nexport type { CopyBlobResult, CopyCommandOptions } from './copy';\nexport { copy } from './copy';\n\n// vercelBlob. createMultipartUpload()\n// vercelBlob. uploadPart()\n// vercelBlob. completeMultipartUpload()\n// vercelBlob. createMultipartUploader()\n\nexport const createMultipartUpload =\n createCreateMultipartUploadMethod<CommonCreateBlobOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n });\n\nexport const createMultipartUploader =\n createCreateMultipartUploaderMethod<CommonCreateBlobOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n });\n\nexport type { UploadPartCommandOptions };\nexport const uploadPart = createUploadPartMethod<UploadPartCommandOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n});\n\nexport type { CompleteMultipartUploadCommandOptions };\nexport const completeMultipartUpload =\n createCompleteMultipartUploadMethod<CompleteMultipartUploadCommandOptions>({\n allowedOptions: ['cacheControlMaxAge', 'addRandomSuffix', 'contentType'],\n });\n\nexport type { Part, PartInput } from './multipart/helpers';\n\nexport { createFolder } from './create-folder';\n"],"mappings":"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","names":[]}