@vercel/blob 0.27.2 → 1.0.0-907570c7-20250410144756

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.
package/dist/client.cjs CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
 
9
9
 
10
- var _chunk4FOGRTRZcjs = require('./chunk-4FOGRTRZ.cjs');
10
+ var _chunkF6ECO7HScjs = require('./chunk-F6ECO7HS.cjs');
11
11
 
12
12
  // src/client.ts
13
13
  var _crypto = require('crypto'); var crypto = _interopRequireWildcard(_crypto);
@@ -15,58 +15,60 @@ var _undici = require('undici');
15
15
  function createPutExtraChecks(methodName) {
16
16
  return function extraChecks(options) {
17
17
  if (!options.token.startsWith("vercel_blob_client_")) {
18
- throw new (0, _chunk4FOGRTRZcjs.BlobError)(`${methodName} must be called with a client token`);
18
+ throw new (0, _chunkF6ECO7HScjs.BlobError)(`${methodName} must be called with a client token`);
19
19
  }
20
20
  if (
21
21
  // @ts-expect-error -- Runtime check for DX.
22
22
  options.addRandomSuffix !== void 0 || // @ts-expect-error -- Runtime check for DX.
23
+ options.allowOverwrite !== void 0 || // @ts-expect-error -- Runtime check for DX.
23
24
  options.cacheControlMaxAge !== void 0
24
25
  ) {
25
- throw new (0, _chunk4FOGRTRZcjs.BlobError)(
26
- `${methodName} doesn't allow addRandomSuffix and cacheControlMaxAge. Configure these options at the server side when generating client tokens.`
26
+ throw new (0, _chunkF6ECO7HScjs.BlobError)(
27
+ `${methodName} doesn't allow \`addRandomSuffix\`, \`cacheControlMaxAge\` or \`allowOverwrite\`. Configure these options at the server side when generating client tokens.`
27
28
  );
28
29
  }
29
30
  };
30
31
  }
31
- var put = _chunk4FOGRTRZcjs.createPutMethod.call(void 0, {
32
+ var put = _chunkF6ECO7HScjs.createPutMethod.call(void 0, {
32
33
  allowedOptions: ["contentType"],
33
34
  extraChecks: createPutExtraChecks("client/`put`")
34
35
  });
35
- var createMultipartUpload = _chunk4FOGRTRZcjs.createCreateMultipartUploadMethod.call(void 0, {
36
+ var createMultipartUpload = _chunkF6ECO7HScjs.createCreateMultipartUploadMethod.call(void 0, {
36
37
  allowedOptions: ["contentType"],
37
38
  extraChecks: createPutExtraChecks("client/`createMultipartUpload`")
38
39
  });
39
- var createMultipartUploader = _chunk4FOGRTRZcjs.createCreateMultipartUploaderMethod.call(void 0,
40
+ var createMultipartUploader = _chunkF6ECO7HScjs.createCreateMultipartUploaderMethod.call(void 0,
40
41
  {
41
42
  allowedOptions: ["contentType"],
42
43
  extraChecks: createPutExtraChecks("client/`createMultipartUpload`")
43
44
  }
44
45
  );
45
- var uploadPart = _chunk4FOGRTRZcjs.createUploadPartMethod.call(void 0, {
46
+ var uploadPart = _chunkF6ECO7HScjs.createUploadPartMethod.call(void 0, {
46
47
  allowedOptions: ["contentType"],
47
48
  extraChecks: createPutExtraChecks("client/`multipartUpload`")
48
49
  });
49
- var completeMultipartUpload = _chunk4FOGRTRZcjs.createCompleteMultipartUploadMethod.call(void 0,
50
+ var completeMultipartUpload = _chunkF6ECO7HScjs.createCompleteMultipartUploadMethod.call(void 0,
50
51
  {
51
52
  allowedOptions: ["contentType"],
52
53
  extraChecks: createPutExtraChecks("client/`completeMultipartUpload`")
53
54
  }
54
55
  );
55
- var upload = _chunk4FOGRTRZcjs.createPutMethod.call(void 0, {
56
+ var upload = _chunkF6ECO7HScjs.createPutMethod.call(void 0, {
56
57
  allowedOptions: ["contentType"],
57
58
  extraChecks(options) {
58
59
  if (options.handleUploadUrl === void 0) {
59
- throw new (0, _chunk4FOGRTRZcjs.BlobError)(
60
+ throw new (0, _chunkF6ECO7HScjs.BlobError)(
60
61
  "client/`upload` requires the 'handleUploadUrl' parameter"
61
62
  );
62
63
  }
63
64
  if (
64
65
  // @ts-expect-error -- Runtime check for DX.
65
66
  options.addRandomSuffix !== void 0 || // @ts-expect-error -- Runtime check for DX.
67
+ options.createPutExtraChecks !== void 0 || // @ts-expect-error -- Runtime check for DX.
66
68
  options.cacheControlMaxAge !== void 0
67
69
  ) {
68
- throw new (0, _chunk4FOGRTRZcjs.BlobError)(
69
- "client/`upload` doesn't allow addRandomSuffix and cacheControlMaxAge. Configure these options at the server side when generating client tokens."
70
+ throw new (0, _chunkF6ECO7HScjs.BlobError)(
71
+ "client/`upload` doesn't allow `addRandomSuffix`, `cacheControlMaxAge` or `allowOverwrite`. Configure these options at the server side when generating client tokens."
70
72
  );
71
73
  }
72
74
  },
@@ -148,7 +150,7 @@ async function handleUpload({
148
150
  onUploadCompleted
149
151
  }) {
150
152
  var _a, _b, _c, _d;
151
- const resolvedToken = _chunk4FOGRTRZcjs.getTokenFromOptionsOrEnv.call(void 0, { token });
153
+ const resolvedToken = _chunkF6ECO7HScjs.getTokenFromOptionsOrEnv.call(void 0, { token });
152
154
  const type = body.type;
153
155
  switch (type) {
154
156
  case "blob.generate-client-token": {
@@ -180,7 +182,7 @@ async function handleUpload({
180
182
  const signatureHeader = "x-vercel-signature";
181
183
  const signature = "credentials" in request ? (_c = request.headers.get(signatureHeader)) != null ? _c : "" : (_d = request.headers[signatureHeader]) != null ? _d : "";
182
184
  if (!signature) {
183
- throw new (0, _chunk4FOGRTRZcjs.BlobError)("Missing callback signature");
185
+ throw new (0, _chunkF6ECO7HScjs.BlobError)("Missing callback signature");
184
186
  }
185
187
  const isVerified = await verifyCallbackSignature({
186
188
  token: resolvedToken,
@@ -188,13 +190,13 @@ async function handleUpload({
188
190
  body: JSON.stringify(body)
189
191
  });
190
192
  if (!isVerified) {
191
- throw new (0, _chunk4FOGRTRZcjs.BlobError)("Invalid callback signature");
193
+ throw new (0, _chunkF6ECO7HScjs.BlobError)("Invalid callback signature");
192
194
  }
193
195
  await onUploadCompleted(body.payload);
194
196
  return { type, response: "ok" };
195
197
  }
196
198
  default:
197
- throw new (0, _chunk4FOGRTRZcjs.BlobError)("Invalid event type");
199
+ throw new (0, _chunkF6ECO7HScjs.BlobError)("Invalid event type");
198
200
  }
199
201
  }
200
202
  async function retrieveClientToken(options) {
@@ -218,13 +220,13 @@ async function retrieveClientToken(options) {
218
220
  signal: options.abortSignal
219
221
  });
220
222
  if (!res.ok) {
221
- throw new (0, _chunk4FOGRTRZcjs.BlobError)("Failed to retrieve the client token");
223
+ throw new (0, _chunkF6ECO7HScjs.BlobError)("Failed to retrieve the client token");
222
224
  }
223
225
  try {
224
226
  const { clientToken } = await res.json();
225
227
  return clientToken;
226
228
  } catch (e) {
227
- throw new (0, _chunk4FOGRTRZcjs.BlobError)("Failed to retrieve the client token");
229
+ throw new (0, _chunkF6ECO7HScjs.BlobError)("Failed to retrieve the client token");
228
230
  }
229
231
  }
230
232
  function toAbsoluteUrl(url) {
@@ -243,16 +245,16 @@ async function generateClientTokenFromReadWriteToken({
243
245
  }) {
244
246
  var _a;
245
247
  if (typeof window !== "undefined") {
246
- throw new (0, _chunk4FOGRTRZcjs.BlobError)(
248
+ throw new (0, _chunkF6ECO7HScjs.BlobError)(
247
249
  '"generateClientTokenFromReadWriteToken" must be called from a server environment'
248
250
  );
249
251
  }
250
252
  const timestamp = /* @__PURE__ */ new Date();
251
253
  timestamp.setSeconds(timestamp.getSeconds() + 30);
252
- const readWriteToken = _chunk4FOGRTRZcjs.getTokenFromOptionsOrEnv.call(void 0, { token });
254
+ const readWriteToken = _chunkF6ECO7HScjs.getTokenFromOptionsOrEnv.call(void 0, { token });
253
255
  const [, , , storeId = null] = readWriteToken.split("_");
254
256
  if (!storeId) {
255
- throw new (0, _chunk4FOGRTRZcjs.BlobError)(
257
+ throw new (0, _chunkF6ECO7HScjs.BlobError)(
256
258
  token ? "Invalid `token` parameter" : "Invalid `BLOB_READ_WRITE_TOKEN`"
257
259
  );
258
260
  }
@@ -264,7 +266,7 @@ async function generateClientTokenFromReadWriteToken({
264
266
  ).toString("base64");
265
267
  const securedKey = await signPayload(payload, readWriteToken);
266
268
  if (!securedKey) {
267
- throw new (0, _chunk4FOGRTRZcjs.BlobError)("Unable to sign client token");
269
+ throw new (0, _chunkF6ECO7HScjs.BlobError)("Unable to sign client token");
268
270
  }
269
271
  return `vercel_blob_client_${storeId}_${Buffer.from(
270
272
  `${securedKey}.${payload}`
@@ -281,5 +283,5 @@ async function generateClientTokenFromReadWriteToken({
281
283
 
282
284
 
283
285
 
284
- exports.completeMultipartUpload = completeMultipartUpload; exports.createFolder = _chunk4FOGRTRZcjs.createFolder; exports.createMultipartUpload = createMultipartUpload; exports.createMultipartUploader = createMultipartUploader; exports.generateClientTokenFromReadWriteToken = generateClientTokenFromReadWriteToken; exports.getPayloadFromClientToken = getPayloadFromClientToken; exports.handleUpload = handleUpload; exports.put = put; exports.upload = upload; exports.uploadPart = uploadPart;
286
+ exports.completeMultipartUpload = completeMultipartUpload; exports.createFolder = _chunkF6ECO7HScjs.createFolder; exports.createMultipartUpload = createMultipartUpload; exports.createMultipartUploader = createMultipartUploader; exports.generateClientTokenFromReadWriteToken = generateClientTokenFromReadWriteToken; exports.getPayloadFromClientToken = getPayloadFromClientToken; exports.handleUpload = handleUpload; exports.put = put; exports.upload = upload; exports.uploadPart = uploadPart;
285
287
  //# sourceMappingURL=client.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/storage/storage/packages/blob/dist/client.cjs","../src/client.ts"],"names":[],"mappings":"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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 (!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 // 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): Buffer {\n if (input.length % 2 !== 0) {\n throw new RangeError('Expected string to be an even number of characters');\n }\n const view = new Uint8Array(input.length / 2);\n\n for (let i = 0; i < input.length; i += 2) {\n view[i / 2] = 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 // location is available in web workers too: https://developer.mozilla.org/en-US/docs/Web/API/Window/location\n return new URL(url, location.href).href;\n}\n\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":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACA;ACVA,+EAAwB;AAKxB,gCAAsB;AA+CtB,SAAS,oBAAA,CAEP,UAAA,EAAoB;AACpB,EAAA,OAAO,SAAS,WAAA,CAAY,OAAA,EAAmB;AAC7C,IAAA,GAAA,CAAI,CAAC,OAAA,CAAQ,KAAA,CAAM,UAAA,CAAW,qBAAqB,CAAA,EAAG;AACpD,MAAA,MAAM,IAAI,gCAAA,CAAU,CAAA,EAAA;AACtB,IAAA;AAEA,IAAA;AAAA;AAEU,MAAA;AAEA,MAAA;AAEA,MAAA;AACR,IAAA;AACU,MAAA;AACK,QAAA;AACf,MAAA;AACF,IAAA;AACF,EAAA;AACF;AAO4D;AACzC,EAAA;AACJ,EAAA;AACd;AAUY;AAEQ,EAAA;AACJ,EAAA;AACd;AAEU;AAET,EAAA;AACmB,IAAA;AACJ,IAAA;AACf,EAAA;AACF;AAQA;AACmB,EAAA;AACJ,EAAA;AACd;AAOU;AAET,EAAA;AACmB,IAAA;AACJ,IAAA;AACf,EAAA;AACF;AA4BoB;AACH,EAAA;AACI,EAAA;AAEP,IAAA;AACA,MAAA;AACR,QAAA;AACF,MAAA;AACF,IAAA;AAEA,IAAA;AAAA;AAEU,MAAA;AAEA,MAAA;AAEA,MAAA;AACR,IAAA;AACU,MAAA;AACR,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACyB,EAAA;AArL3B,IAAA;AAsLW,IAAA;AACY,MAAA;AACjB,MAAA;AACe,MAAA;AACJ,MAAA;AACZ,IAAA;AACH,EAAA;AACD;AAEwB;AACE,EAAA;AACvB,IAAA;AACkB,IAAA;AACI,IAAA;AACtB,IAAA;AACiB,IAAA;AACnB,EAAA;AACF;AAGE;AAIwB,EAAA;AACR,IAAA;AAChB,EAAA;AAEwB,EAAA;AACtB,IAAA;AACqB,IAAA;AACH,IAAA;AACpB,EAAA;AACuB,EAAA;AACzB;AAEe;AACb,EAAA;AACA,EAAA;AACA,EAAA;AAKmB;AAEJ,EAAA;AAGS,EAAA;AAGnB,IAAA;AAGkB,IAAA;AACf,IAAA;AAGS,IAAA;AAGjB,EAAA;AAEuB,EAAA;AACrB,IAAA;AACqB,IAAA;AACN,IAAA;AACG,IAAA;AACpB,EAAA;AACO,EAAA;AACT;AAEwB;AACG,EAAA;AACF,IAAA;AACvB,EAAA;AACiB,EAAA;AAEG,EAAA;AACK,IAAA;AACzB,EAAA;AAEuB,EAAA;AACzB;AASgB;AAGC,EAAA;AACQ,EAAA;AAGA,EAAA;AACL,EAAA;AACpB;AAEmB;AACI,EAAA;AACJ,EAAA;AACnB;AA6CsB;AACpB,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAIA;AAvVF,EAAA;AAwVwB,EAAA;AAEJ,EAAA;AACJ,EAAA;AACP,IAAA;AACe,MAAA;AACF,MAAA;AACd,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACM,MAAA;AAGA,MAAA;AACM,MAAA;AAEV,MAAA;AAGK,MAAA;AACL,QAAA;AACmB,QAAA;AACd,UAAA;AACI,UAAA;AACP,UAAA;AACA,UAAA;AACE,YAAA;AACA,YAAA;AACF,UAAA;AACA,UAAA;AACD,QAAA;AACH,MAAA;AACF,IAAA;AACK,IAAA;AACG,MAAA;AAEJ,MAAA;AAKc,MAAA;AACJ,QAAA;AACZ,MAAA;AAEmB,MAAA;AACV,QAAA;AACP,QAAA;AACW,QAAA;AACZ,MAAA;AAEgB,MAAA;AACL,QAAA;AACZ,MAAA;AACM,MAAA;AACS,MAAA;AACjB,IAAA;AACA,IAAA;AACsB,MAAA;AACxB,EAAA;AACF;AAEe;AAOY,EAAA;AACb,EAAA;AAI4B,EAAA;AACrB,IAAA;AACR,IAAA;AACP,MAAA;AACa,MAAA;AACE,MAAA;AACI,MAAA;AACrB,IAAA;AACF,EAAA;AAEwB,EAAA;AACd,IAAA;AACa,IAAA;AACZ,IAAA;AACS,MAAA;AAClB,IAAA;AACgB,IAAA;AACjB,EAAA;AAEY,EAAA;AACS,IAAA;AACtB,EAAA;AAEI,EAAA;AACkB,IAAA;AACb,IAAA;AACG,EAAA;AACU,IAAA;AACtB,EAAA;AACF;AAEuB;AAED,EAAA;AACtB;AAEuB;AACjB,EAAA;AACqB,IAAA;AACb,EAAA;AACH,IAAA;AACT,EAAA;AACF;AAEsB;AACpB,EAAA;AACG,EAAA;AAC2C;AAldhD,EAAA;AAmdwB,EAAA;AACV,IAAA;AACR,MAAA;AACF,IAAA;AACF,EAAA;AAEkB,EAAA;AACG,EAAA;AACE,EAAA;AAEA,EAAA;AAET,EAAA;AACF,IAAA;AACA,MAAA;AACV,IAAA;AACF,EAAA;AAEuB,EAAA;AACN,IAAA;AACV,MAAA;AACS,MAAA;AACb,IAAA;AACgB,EAAA;AAEM,EAAA;AAER,EAAA;AACK,IAAA;AACtB,EAAA;AACO,EAAA;AACY,IAAA;AACC,EAAA;AACtB;ADlO2B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","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 (!options.token.startsWith('vercel_blob_client_')) {\n throw new BlobError(`${methodName} must be called with a client token`);\n }\n\n if (\n // @ts-expect-error -- Runtime check for DX.\n options.addRandomSuffix !== undefined ||\n // @ts-expect-error -- Runtime check for DX.\n options.allowOverwrite !== undefined ||\n // @ts-expect-error -- Runtime check for DX.\n options.cacheControlMaxAge !== undefined\n ) {\n throw new BlobError(\n `${methodName} doesn't allow \\`addRandomSuffix\\`, \\`cacheControlMaxAge\\` or \\`allowOverwrite\\`. Configure these options at the server side when generating client tokens.`,\n );\n }\n };\n}\n\n// 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/vercel-blob/using-blob-sdk#client-uploads\n *\n * If you want to upload from your server instead, check out the documentation for the put operation: https://vercel.com/docs/vercel-blob/using-blob-sdk#upload-a-blob\n *\n * @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 // 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.createPutExtraChecks !== undefined ||\n // @ts-expect-error -- Runtime check for DX.\n options.cacheControlMaxAge !== undefined\n ) {\n throw new BlobError(\n \"client/`upload` doesn't allow `addRandomSuffix`, `cacheControlMaxAge` or `allowOverwrite`. Configure these options at the server side when generating client tokens.\",\n );\n }\n },\n async getToken(pathname, options) {\n return retrieveClientToken({\n handleUploadUrl: options.handleUploadUrl,\n pathname,\n clientPayload: options.clientPayload ?? null,\n multipart: options.multipart ?? false,\n });\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): Buffer {\n if (input.length % 2 !== 0) {\n throw new RangeError('Expected string to be an even number of characters');\n }\n const view = new Uint8Array(input.length / 2);\n\n for (let i = 0; i < input.length; i += 2) {\n view[i / 2] = 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 | 'allowOverwrite'\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 // location is available in web workers too: https://developer.mozilla.org/en-US/docs/Web/API/Window/location\n return new URL(url, location.href).href;\n}\n\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 allowOverwrite?: 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-CqdraABG.cjs';
2
- export { d as createFolder } from './create-folder-CqdraABG.cjs';
1
+ import { W as WithUploadProgress, P as PutBody, a as PutBlobResult, b as Part, C as CommonMultipartUploadOptions, c as CommonCompleteMultipartUploadOptions, B as BlobCommandOptions } from './create-folder-Bq7XMIcv.cjs';
2
+ export { d as createFolder } from './create-folder-Bq7XMIcv.cjs';
3
3
  import { IncomingMessage } from 'node:http';
4
4
  import 'stream';
5
5
  import 'undici';
@@ -63,9 +63,9 @@ interface CommonUploadOptions {
63
63
  type UploadOptions = ClientCommonPutOptions & CommonUploadOptions;
64
64
  /**
65
65
  * Uploads a blob into your store from the client.
66
- * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#client-uploads
66
+ * Detailed documentation can be found here: https://vercel.com/docs/vercel-blob/using-blob-sdk#client-uploads
67
67
  *
68
- * 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
68
+ * If you want to upload from your server instead, check out the documentation for the put operation: https://vercel.com/docs/vercel-blob/using-blob-sdk#upload-a-blob
69
69
  *
70
70
  * @param pathname - The pathname to upload the blob to. This includes the filename.
71
71
  * @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.
@@ -100,7 +100,7 @@ type HandleUploadBody = GenerateClientTokenEvent | UploadCompletedEvent;
100
100
  type RequestType = IncomingMessage | Request;
101
101
  interface HandleUploadOptions {
102
102
  body: HandleUploadBody;
103
- onBeforeGenerateToken: (pathname: string, clientPayload: string | null, multipart: boolean) => Promise<Pick<GenerateClientTokenOptions, 'allowedContentTypes' | 'maximumSizeInBytes' | 'validUntil' | 'addRandomSuffix' | 'cacheControlMaxAge'> & {
103
+ onBeforeGenerateToken: (pathname: string, clientPayload: string | null, multipart: boolean) => Promise<Pick<GenerateClientTokenOptions, 'allowedContentTypes' | 'maximumSizeInBytes' | 'validUntil' | 'addRandomSuffix' | 'allowOverwrite' | 'cacheControlMaxAge'> & {
104
104
  tokenPayload?: string | null;
105
105
  }>;
106
106
  onUploadCompleted: (body: UploadCompletedEvent['payload']) => Promise<void>;
@@ -125,6 +125,7 @@ interface GenerateClientTokenOptions extends BlobCommandOptions {
125
125
  allowedContentTypes?: string[];
126
126
  validUntil?: number;
127
127
  addRandomSuffix?: boolean;
128
+ allowOverwrite?: boolean;
128
129
  cacheControlMaxAge?: number;
129
130
  }
130
131
 
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-CqdraABG.js';
2
- export { d as createFolder } from './create-folder-CqdraABG.js';
1
+ import { W as WithUploadProgress, P as PutBody, a as PutBlobResult, b as Part, C as CommonMultipartUploadOptions, c as CommonCompleteMultipartUploadOptions, B as BlobCommandOptions } from './create-folder-Bq7XMIcv.js';
2
+ export { d as createFolder } from './create-folder-Bq7XMIcv.js';
3
3
  import { IncomingMessage } from 'node:http';
4
4
  import 'stream';
5
5
  import 'undici';
@@ -63,9 +63,9 @@ interface CommonUploadOptions {
63
63
  type UploadOptions = ClientCommonPutOptions & CommonUploadOptions;
64
64
  /**
65
65
  * Uploads a blob into your store from the client.
66
- * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#client-uploads
66
+ * Detailed documentation can be found here: https://vercel.com/docs/vercel-blob/using-blob-sdk#client-uploads
67
67
  *
68
- * 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
68
+ * If you want to upload from your server instead, check out the documentation for the put operation: https://vercel.com/docs/vercel-blob/using-blob-sdk#upload-a-blob
69
69
  *
70
70
  * @param pathname - The pathname to upload the blob to. This includes the filename.
71
71
  * @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.
@@ -100,7 +100,7 @@ type HandleUploadBody = GenerateClientTokenEvent | UploadCompletedEvent;
100
100
  type RequestType = IncomingMessage | Request;
101
101
  interface HandleUploadOptions {
102
102
  body: HandleUploadBody;
103
- onBeforeGenerateToken: (pathname: string, clientPayload: string | null, multipart: boolean) => Promise<Pick<GenerateClientTokenOptions, 'allowedContentTypes' | 'maximumSizeInBytes' | 'validUntil' | 'addRandomSuffix' | 'cacheControlMaxAge'> & {
103
+ onBeforeGenerateToken: (pathname: string, clientPayload: string | null, multipart: boolean) => Promise<Pick<GenerateClientTokenOptions, 'allowedContentTypes' | 'maximumSizeInBytes' | 'validUntil' | 'addRandomSuffix' | 'allowOverwrite' | 'cacheControlMaxAge'> & {
104
104
  tokenPayload?: string | null;
105
105
  }>;
106
106
  onUploadCompleted: (body: UploadCompletedEvent['payload']) => Promise<void>;
@@ -125,6 +125,7 @@ interface GenerateClientTokenOptions extends BlobCommandOptions {
125
125
  allowedContentTypes?: string[];
126
126
  validUntil?: number;
127
127
  addRandomSuffix?: boolean;
128
+ allowOverwrite?: boolean;
128
129
  cacheControlMaxAge?: number;
129
130
  }
130
131
 
package/dist/client.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  createPutMethod,
8
8
  createUploadPartMethod,
9
9
  getTokenFromOptionsOrEnv
10
- } from "./chunk-VMBKF2I4.js";
10
+ } from "./chunk-MTKRTZH3.js";
11
11
 
12
12
  // src/client.ts
13
13
  import * as crypto from "crypto";
@@ -20,10 +20,11 @@ function createPutExtraChecks(methodName) {
20
20
  if (
21
21
  // @ts-expect-error -- Runtime check for DX.
22
22
  options.addRandomSuffix !== void 0 || // @ts-expect-error -- Runtime check for DX.
23
+ options.allowOverwrite !== void 0 || // @ts-expect-error -- Runtime check for DX.
23
24
  options.cacheControlMaxAge !== void 0
24
25
  ) {
25
26
  throw new BlobError(
26
- `${methodName} doesn't allow addRandomSuffix and cacheControlMaxAge. Configure these options at the server side when generating client tokens.`
27
+ `${methodName} doesn't allow \`addRandomSuffix\`, \`cacheControlMaxAge\` or \`allowOverwrite\`. Configure these options at the server side when generating client tokens.`
27
28
  );
28
29
  }
29
30
  };
@@ -63,10 +64,11 @@ var upload = createPutMethod({
63
64
  if (
64
65
  // @ts-expect-error -- Runtime check for DX.
65
66
  options.addRandomSuffix !== void 0 || // @ts-expect-error -- Runtime check for DX.
67
+ options.createPutExtraChecks !== void 0 || // @ts-expect-error -- Runtime check for DX.
66
68
  options.cacheControlMaxAge !== void 0
67
69
  ) {
68
70
  throw new BlobError(
69
- "client/`upload` doesn't allow addRandomSuffix and cacheControlMaxAge. Configure these options at the server side when generating client tokens."
71
+ "client/`upload` doesn't allow `addRandomSuffix`, `cacheControlMaxAge` or `allowOverwrite`. Configure these options at the server side when generating client tokens."
70
72
  );
71
73
  }
72
74
  },
@@ -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 (!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 // 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): Buffer {\n if (input.length % 2 !== 0) {\n throw new RangeError('Expected string to be an even number of characters');\n }\n const view = new Uint8Array(input.length / 2);\n\n for (let i = 0; i < input.length; i += 2) {\n view[i / 2] = 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 // location is available in web workers too: https://developer.mozilla.org/en-US/docs/Web/API/Window/location\n return new URL(url, location.href).href;\n}\n\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 (!options.token.startsWith('vercel_blob_client_')) {\n throw new BlobError(`${methodName} must be called with a client token`);\n }\n\n if (\n // @ts-expect-error -- Runtime check for DX.\n options.addRandomSuffix !== undefined ||\n // @ts-expect-error -- Runtime check for DX.\n options.allowOverwrite !== undefined ||\n // @ts-expect-error -- Runtime check for DX.\n options.cacheControlMaxAge !== undefined\n ) {\n throw new BlobError(\n `${methodName} doesn't allow \\`addRandomSuffix\\`, \\`cacheControlMaxAge\\` or \\`allowOverwrite\\`. Configure these options at the server side when generating client tokens.`,\n );\n }\n };\n}\n\n// 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/vercel-blob/using-blob-sdk#client-uploads\n *\n * If you want to upload from your server instead, check out the documentation for the put operation: https://vercel.com/docs/vercel-blob/using-blob-sdk#upload-a-blob\n *\n * @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 // 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.createPutExtraChecks !== undefined ||\n // @ts-expect-error -- Runtime check for DX.\n options.cacheControlMaxAge !== undefined\n ) {\n throw new BlobError(\n \"client/`upload` doesn't allow `addRandomSuffix`, `cacheControlMaxAge` or `allowOverwrite`. Configure these options at the server side when generating client tokens.\",\n );\n }\n },\n async getToken(pathname, options) {\n return retrieveClientToken({\n handleUploadUrl: options.handleUploadUrl,\n pathname,\n clientPayload: options.clientPayload ?? null,\n multipart: options.multipart ?? false,\n });\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): Buffer {\n if (input.length % 2 !== 0) {\n throw new RangeError('Expected string to be an even number of characters');\n }\n const view = new Uint8Array(input.length / 2);\n\n for (let i = 0; i < input.length; i += 2) {\n view[i / 2] = 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 | 'allowOverwrite'\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 // location is available in web workers too: https://developer.mozilla.org/en-US/docs/Web/API/Window/location\n return new URL(url, location.href).href;\n}\n\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 allowOverwrite?: boolean;\n cacheControlMaxAge?: number;\n}\n\nexport { createFolder } from './create-folder';\n"],"mappings":"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","names":[]}
@@ -19,17 +19,22 @@ interface CommonCreateBlobOptions extends BlobCommandOptions {
19
19
  access: 'public';
20
20
  /**
21
21
  * Adds a random suffix to the filename.
22
- * @defaultvalue true
22
+ * @defaultvalue false
23
23
  */
24
24
  addRandomSuffix?: boolean;
25
+ /**
26
+ * Allow overwriting an existing blob. By default this is set to false and will throw an error if the blob already exists.
27
+ * @defaultvalue false
28
+ */
29
+ allowOverwrite?: boolean;
25
30
  /**
26
31
  * 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.
27
32
  */
28
33
  contentType?: string;
29
34
  /**
30
- * Number in seconds to configure the edge and browser cache. The maximum values are 5 minutes for the edge cache and unlimited for the browser cache.
35
+ * Number in seconds to configure the edge and browser cache. The minimum is 1 minute. There's no maximum but keep in mind that browser and edge caches will do a best effort to respect this value.
31
36
  * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob#caching
32
- * @defaultvalue 365 * 24 * 60 * 60 (1 Year)
37
+ * @defaultvalue 30 * 24 * 60 * 60 (1 Month)
33
38
  */
34
39
  cacheControlMaxAge?: number;
35
40
  }
@@ -19,17 +19,22 @@ interface CommonCreateBlobOptions extends BlobCommandOptions {
19
19
  access: 'public';
20
20
  /**
21
21
  * Adds a random suffix to the filename.
22
- * @defaultvalue true
22
+ * @defaultvalue false
23
23
  */
24
24
  addRandomSuffix?: boolean;
25
+ /**
26
+ * Allow overwriting an existing blob. By default this is set to false and will throw an error if the blob already exists.
27
+ * @defaultvalue false
28
+ */
29
+ allowOverwrite?: boolean;
25
30
  /**
26
31
  * 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.
27
32
  */
28
33
  contentType?: string;
29
34
  /**
30
- * Number in seconds to configure the edge and browser cache. The maximum values are 5 minutes for the edge cache and unlimited for the browser cache.
35
+ * Number in seconds to configure the edge and browser cache. The minimum is 1 minute. There's no maximum but keep in mind that browser and edge caches will do a best effort to respect this value.
31
36
  * Detailed documentation can be found here: https://vercel.com/docs/storage/vercel-blob#caching
32
- * @defaultvalue 365 * 24 * 60 * 60 (1 Year)
37
+ * @defaultvalue 30 * 24 * 60 * 60 (1 Month)
33
38
  */
34
39
  cacheControlMaxAge?: number;
35
40
  }