@vercel/blob 0.27.0 → 0.27.1
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/{chunk-XTBGGUVN.cjs → chunk-A3XDFQYY.cjs} +56 -10
- package/dist/chunk-A3XDFQYY.cjs.map +1 -0
- package/dist/{chunk-KN6WT5GP.js → chunk-JGHUXF3X.js} +48 -2
- package/dist/chunk-JGHUXF3X.js.map +1 -0
- package/dist/client.cjs +23 -33
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +2 -12
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +18 -18
- package/dist/index.js +1 -1
- package/package.json +2 -4
- package/dist/chunk-KN6WT5GP.js.map +0 -1
- package/dist/chunk-XTBGGUVN.cjs.map +0 -1
package/dist/client.cjs
CHANGED
|
@@ -7,66 +7,56 @@
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var _chunkA3XDFQYYcjs = require('./chunk-A3XDFQYY.cjs');
|
|
11
11
|
|
|
12
12
|
// src/client.ts
|
|
13
13
|
var _crypto = require('crypto'); var crypto = _interopRequireWildcard(_crypto);
|
|
14
14
|
var _undici = require('undici');
|
|
15
15
|
function createPutExtraChecks(methodName) {
|
|
16
16
|
return function extraChecks(options) {
|
|
17
|
-
if (typeof window === "undefined") {
|
|
18
|
-
throw new (0, _chunkXTBGGUVNcjs.BlobError)(
|
|
19
|
-
`${methodName} must be called from a client environment`
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
17
|
if (!options.token.startsWith("vercel_blob_client_")) {
|
|
23
|
-
throw new (0,
|
|
18
|
+
throw new (0, _chunkA3XDFQYYcjs.BlobError)(`${methodName} must be called with a client token`);
|
|
24
19
|
}
|
|
25
20
|
if (
|
|
26
21
|
// @ts-expect-error -- Runtime check for DX.
|
|
27
22
|
options.addRandomSuffix !== void 0 || // @ts-expect-error -- Runtime check for DX.
|
|
28
23
|
options.cacheControlMaxAge !== void 0
|
|
29
24
|
) {
|
|
30
|
-
throw new (0,
|
|
25
|
+
throw new (0, _chunkA3XDFQYYcjs.BlobError)(
|
|
31
26
|
`${methodName} doesn't allow addRandomSuffix and cacheControlMaxAge. Configure these options at the server side when generating client tokens.`
|
|
32
27
|
);
|
|
33
28
|
}
|
|
34
29
|
};
|
|
35
30
|
}
|
|
36
|
-
var put =
|
|
31
|
+
var put = _chunkA3XDFQYYcjs.createPutMethod.call(void 0, {
|
|
37
32
|
allowedOptions: ["contentType"],
|
|
38
33
|
extraChecks: createPutExtraChecks("client/`put`")
|
|
39
34
|
});
|
|
40
|
-
var createMultipartUpload =
|
|
35
|
+
var createMultipartUpload = _chunkA3XDFQYYcjs.createCreateMultipartUploadMethod.call(void 0, {
|
|
41
36
|
allowedOptions: ["contentType"],
|
|
42
37
|
extraChecks: createPutExtraChecks("client/`createMultipartUpload`")
|
|
43
38
|
});
|
|
44
|
-
var createMultipartUploader =
|
|
39
|
+
var createMultipartUploader = _chunkA3XDFQYYcjs.createCreateMultipartUploaderMethod.call(void 0,
|
|
45
40
|
{
|
|
46
41
|
allowedOptions: ["contentType"],
|
|
47
42
|
extraChecks: createPutExtraChecks("client/`createMultipartUpload`")
|
|
48
43
|
}
|
|
49
44
|
);
|
|
50
|
-
var uploadPart =
|
|
45
|
+
var uploadPart = _chunkA3XDFQYYcjs.createUploadPartMethod.call(void 0, {
|
|
51
46
|
allowedOptions: ["contentType"],
|
|
52
47
|
extraChecks: createPutExtraChecks("client/`multipartUpload`")
|
|
53
48
|
});
|
|
54
|
-
var completeMultipartUpload =
|
|
49
|
+
var completeMultipartUpload = _chunkA3XDFQYYcjs.createCompleteMultipartUploadMethod.call(void 0,
|
|
55
50
|
{
|
|
56
51
|
allowedOptions: ["contentType"],
|
|
57
52
|
extraChecks: createPutExtraChecks("client/`completeMultipartUpload`")
|
|
58
53
|
}
|
|
59
54
|
);
|
|
60
|
-
var upload =
|
|
55
|
+
var upload = _chunkA3XDFQYYcjs.createPutMethod.call(void 0, {
|
|
61
56
|
allowedOptions: ["contentType"],
|
|
62
57
|
extraChecks(options) {
|
|
63
|
-
if (typeof window === "undefined") {
|
|
64
|
-
throw new (0, _chunkXTBGGUVNcjs.BlobError)(
|
|
65
|
-
"client/`upload` must be called from a client environment"
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
58
|
if (options.handleUploadUrl === void 0) {
|
|
69
|
-
throw new (0,
|
|
59
|
+
throw new (0, _chunkA3XDFQYYcjs.BlobError)(
|
|
70
60
|
"client/`upload` requires the 'handleUploadUrl' parameter"
|
|
71
61
|
);
|
|
72
62
|
}
|
|
@@ -75,7 +65,7 @@ var upload = _chunkXTBGGUVNcjs.createPutMethod.call(void 0, {
|
|
|
75
65
|
options.addRandomSuffix !== void 0 || // @ts-expect-error -- Runtime check for DX.
|
|
76
66
|
options.cacheControlMaxAge !== void 0
|
|
77
67
|
) {
|
|
78
|
-
throw new (0,
|
|
68
|
+
throw new (0, _chunkA3XDFQYYcjs.BlobError)(
|
|
79
69
|
"client/`upload` doesn't allow addRandomSuffix and cacheControlMaxAge. Configure these options at the server side when generating client tokens."
|
|
80
70
|
);
|
|
81
71
|
}
|
|
@@ -158,7 +148,7 @@ async function handleUpload({
|
|
|
158
148
|
onUploadCompleted
|
|
159
149
|
}) {
|
|
160
150
|
var _a, _b, _c, _d;
|
|
161
|
-
const resolvedToken =
|
|
151
|
+
const resolvedToken = _chunkA3XDFQYYcjs.getTokenFromOptionsOrEnv.call(void 0, { token });
|
|
162
152
|
const type = body.type;
|
|
163
153
|
switch (type) {
|
|
164
154
|
case "blob.generate-client-token": {
|
|
@@ -190,7 +180,7 @@ async function handleUpload({
|
|
|
190
180
|
const signatureHeader = "x-vercel-signature";
|
|
191
181
|
const signature = "credentials" in request ? (_c = request.headers.get(signatureHeader)) != null ? _c : "" : (_d = request.headers[signatureHeader]) != null ? _d : "";
|
|
192
182
|
if (!signature) {
|
|
193
|
-
throw new (0,
|
|
183
|
+
throw new (0, _chunkA3XDFQYYcjs.BlobError)("Missing callback signature");
|
|
194
184
|
}
|
|
195
185
|
const isVerified = await verifyCallbackSignature({
|
|
196
186
|
token: resolvedToken,
|
|
@@ -198,13 +188,13 @@ async function handleUpload({
|
|
|
198
188
|
body: JSON.stringify(body)
|
|
199
189
|
});
|
|
200
190
|
if (!isVerified) {
|
|
201
|
-
throw new (0,
|
|
191
|
+
throw new (0, _chunkA3XDFQYYcjs.BlobError)("Invalid callback signature");
|
|
202
192
|
}
|
|
203
193
|
await onUploadCompleted(body.payload);
|
|
204
194
|
return { type, response: "ok" };
|
|
205
195
|
}
|
|
206
196
|
default:
|
|
207
|
-
throw new (0,
|
|
197
|
+
throw new (0, _chunkA3XDFQYYcjs.BlobError)("Invalid event type");
|
|
208
198
|
}
|
|
209
199
|
}
|
|
210
200
|
async function retrieveClientToken(options) {
|
|
@@ -228,17 +218,17 @@ async function retrieveClientToken(options) {
|
|
|
228
218
|
signal: options.abortSignal
|
|
229
219
|
});
|
|
230
220
|
if (!res.ok) {
|
|
231
|
-
throw new (0,
|
|
221
|
+
throw new (0, _chunkA3XDFQYYcjs.BlobError)("Failed to retrieve the client token");
|
|
232
222
|
}
|
|
233
223
|
try {
|
|
234
224
|
const { clientToken } = await res.json();
|
|
235
225
|
return clientToken;
|
|
236
226
|
} catch (e) {
|
|
237
|
-
throw new (0,
|
|
227
|
+
throw new (0, _chunkA3XDFQYYcjs.BlobError)("Failed to retrieve the client token");
|
|
238
228
|
}
|
|
239
229
|
}
|
|
240
230
|
function toAbsoluteUrl(url) {
|
|
241
|
-
return new URL(url,
|
|
231
|
+
return new URL(url, location.href).href;
|
|
242
232
|
}
|
|
243
233
|
function isAbsoluteUrl(url) {
|
|
244
234
|
try {
|
|
@@ -253,16 +243,16 @@ async function generateClientTokenFromReadWriteToken({
|
|
|
253
243
|
}) {
|
|
254
244
|
var _a;
|
|
255
245
|
if (typeof window !== "undefined") {
|
|
256
|
-
throw new (0,
|
|
246
|
+
throw new (0, _chunkA3XDFQYYcjs.BlobError)(
|
|
257
247
|
'"generateClientTokenFromReadWriteToken" must be called from a server environment'
|
|
258
248
|
);
|
|
259
249
|
}
|
|
260
250
|
const timestamp = /* @__PURE__ */ new Date();
|
|
261
251
|
timestamp.setSeconds(timestamp.getSeconds() + 30);
|
|
262
|
-
const readWriteToken =
|
|
252
|
+
const readWriteToken = _chunkA3XDFQYYcjs.getTokenFromOptionsOrEnv.call(void 0, { token });
|
|
263
253
|
const [, , , storeId = null] = readWriteToken.split("_");
|
|
264
254
|
if (!storeId) {
|
|
265
|
-
throw new (0,
|
|
255
|
+
throw new (0, _chunkA3XDFQYYcjs.BlobError)(
|
|
266
256
|
token ? "Invalid `token` parameter" : "Invalid `BLOB_READ_WRITE_TOKEN`"
|
|
267
257
|
);
|
|
268
258
|
}
|
|
@@ -274,7 +264,7 @@ async function generateClientTokenFromReadWriteToken({
|
|
|
274
264
|
).toString("base64");
|
|
275
265
|
const securedKey = await signPayload(payload, readWriteToken);
|
|
276
266
|
if (!securedKey) {
|
|
277
|
-
throw new (0,
|
|
267
|
+
throw new (0, _chunkA3XDFQYYcjs.BlobError)("Unable to sign client token");
|
|
278
268
|
}
|
|
279
269
|
return `vercel_blob_client_${storeId}_${Buffer.from(
|
|
280
270
|
`${securedKey}.${payload}`
|
|
@@ -291,5 +281,5 @@ async function generateClientTokenFromReadWriteToken({
|
|
|
291
281
|
|
|
292
282
|
|
|
293
283
|
|
|
294
|
-
exports.completeMultipartUpload = completeMultipartUpload; exports.createFolder =
|
|
284
|
+
exports.completeMultipartUpload = completeMultipartUpload; exports.createFolder = _chunkA3XDFQYYcjs.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;
|
|
295
285
|
//# sourceMappingURL=client.cjs.map
|
package/dist/client.cjs.map
CHANGED
|
@@ -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 (!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"]}
|
package/dist/client.js
CHANGED
|
@@ -7,18 +7,13 @@ import {
|
|
|
7
7
|
createPutMethod,
|
|
8
8
|
createUploadPartMethod,
|
|
9
9
|
getTokenFromOptionsOrEnv
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-JGHUXF3X.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"
|
|
@@ -238,7 +228,7 @@ async function retrieveClientToken(options) {
|
|
|
238
228
|
}
|
|
239
229
|
}
|
|
240
230
|
function toAbsoluteUrl(url) {
|
|
241
|
-
return new URL(url,
|
|
231
|
+
return new URL(url, location.href).href;
|
|
242
232
|
}
|
|
243
233
|
function isAbsoluteUrl(url) {
|
|
244
234
|
try {
|
package/dist/client.js.map
CHANGED
|
@@ -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 (!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":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
var
|
|
25
|
+
var _chunkA3XDFQYYcjs = require('./chunk-A3XDFQYY.cjs');
|
|
26
26
|
|
|
27
27
|
// src/del.ts
|
|
28
28
|
async function del(url, options) {
|
|
29
|
-
await
|
|
29
|
+
await _chunkA3XDFQYYcjs.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
|
|
44
|
+
const response = await _chunkA3XDFQYYcjs.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
|
|
81
|
+
const response = await _chunkA3XDFQYYcjs.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,
|
|
116
|
+
throw new (0, _chunkA3XDFQYYcjs.BlobError)("missing options, see usage");
|
|
117
117
|
}
|
|
118
118
|
if (options.access !== "public") {
|
|
119
|
-
throw new (0,
|
|
119
|
+
throw new (0, _chunkA3XDFQYYcjs.BlobError)('access must be "public"');
|
|
120
120
|
}
|
|
121
|
-
if (toPathname.length >
|
|
122
|
-
throw new (0,
|
|
123
|
-
`pathname is too long, maximum length is ${
|
|
121
|
+
if (toPathname.length > _chunkA3XDFQYYcjs.MAXIMUM_PATHNAME_LENGTH) {
|
|
122
|
+
throw new (0, _chunkA3XDFQYYcjs.BlobError)(
|
|
123
|
+
`pathname is too long, maximum length is ${_chunkA3XDFQYYcjs.MAXIMUM_PATHNAME_LENGTH}`
|
|
124
124
|
);
|
|
125
125
|
}
|
|
126
|
-
for (const invalidCharacter of
|
|
126
|
+
for (const invalidCharacter of _chunkA3XDFQYYcjs.disallowedPathnameCharacters) {
|
|
127
127
|
if (toPathname.includes(invalidCharacter)) {
|
|
128
|
-
throw new (0,
|
|
128
|
+
throw new (0, _chunkA3XDFQYYcjs.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
|
|
143
|
+
const response = await _chunkA3XDFQYYcjs.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 =
|
|
162
|
+
var put = _chunkA3XDFQYYcjs.createPutMethod.call(void 0, {
|
|
163
163
|
allowedOptions: ["cacheControlMaxAge", "addRandomSuffix", "contentType"]
|
|
164
164
|
});
|
|
165
|
-
var createMultipartUpload =
|
|
165
|
+
var createMultipartUpload = _chunkA3XDFQYYcjs.createCreateMultipartUploadMethod.call(void 0, {
|
|
166
166
|
allowedOptions: ["cacheControlMaxAge", "addRandomSuffix", "contentType"]
|
|
167
167
|
});
|
|
168
|
-
var createMultipartUploader =
|
|
168
|
+
var createMultipartUploader = _chunkA3XDFQYYcjs.createCreateMultipartUploaderMethod.call(void 0, {
|
|
169
169
|
allowedOptions: ["cacheControlMaxAge", "addRandomSuffix", "contentType"]
|
|
170
170
|
});
|
|
171
|
-
var uploadPart =
|
|
171
|
+
var uploadPart = _chunkA3XDFQYYcjs.createUploadPartMethod.call(void 0, {
|
|
172
172
|
allowedOptions: ["cacheControlMaxAge", "addRandomSuffix", "contentType"]
|
|
173
173
|
});
|
|
174
|
-
var completeMultipartUpload =
|
|
174
|
+
var completeMultipartUpload = _chunkA3XDFQYYcjs.createCompleteMultipartUploadMethod.call(void 0, {
|
|
175
175
|
allowedOptions: ["cacheControlMaxAge", "addRandomSuffix", "contentType"]
|
|
176
176
|
});
|
|
177
177
|
|
|
@@ -199,5 +199,5 @@ var completeMultipartUpload = _chunkXTBGGUVNcjs.createCompleteMultipartUploadMet
|
|
|
199
199
|
|
|
200
200
|
|
|
201
201
|
|
|
202
|
-
exports.BlobAccessError =
|
|
202
|
+
exports.BlobAccessError = _chunkA3XDFQYYcjs.BlobAccessError; exports.BlobClientTokenExpiredError = _chunkA3XDFQYYcjs.BlobClientTokenExpiredError; exports.BlobContentTypeNotAllowedError = _chunkA3XDFQYYcjs.BlobContentTypeNotAllowedError; exports.BlobError = _chunkA3XDFQYYcjs.BlobError; exports.BlobFileTooLargeError = _chunkA3XDFQYYcjs.BlobFileTooLargeError; exports.BlobNotFoundError = _chunkA3XDFQYYcjs.BlobNotFoundError; exports.BlobPathnameMismatchError = _chunkA3XDFQYYcjs.BlobPathnameMismatchError; exports.BlobRequestAbortedError = _chunkA3XDFQYYcjs.BlobRequestAbortedError; exports.BlobServiceNotAvailable = _chunkA3XDFQYYcjs.BlobServiceNotAvailable; exports.BlobServiceRateLimited = _chunkA3XDFQYYcjs.BlobServiceRateLimited; exports.BlobStoreNotFoundError = _chunkA3XDFQYYcjs.BlobStoreNotFoundError; exports.BlobStoreSuspendedError = _chunkA3XDFQYYcjs.BlobStoreSuspendedError; exports.BlobUnknownError = _chunkA3XDFQYYcjs.BlobUnknownError; exports.completeMultipartUpload = completeMultipartUpload; exports.copy = copy; exports.createFolder = _chunkA3XDFQYYcjs.createFolder; exports.createMultipartUpload = createMultipartUpload; exports.createMultipartUploader = createMultipartUploader; exports.del = del; exports.getDownloadUrl = _chunkA3XDFQYYcjs.getDownloadUrl; exports.head = head; exports.list = list; exports.put = put; exports.uploadPart = uploadPart;
|
|
203
203
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercel/blob",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.1",
|
|
4
4
|
"description": "The Vercel Blob JavaScript API client",
|
|
5
5
|
"homepage": "https://vercel.com/storage/blob",
|
|
6
6
|
"repository": {
|
|
@@ -47,7 +47,6 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"async-retry": "^1.3.3",
|
|
50
|
-
"bytes": "^3.1.2",
|
|
51
50
|
"is-buffer": "^2.0.5",
|
|
52
51
|
"is-node-process": "^1.2.0",
|
|
53
52
|
"throttleit": "^2.1.0",
|
|
@@ -57,9 +56,8 @@
|
|
|
57
56
|
"@edge-runtime/jest-environment": "2.3.10",
|
|
58
57
|
"@edge-runtime/types": "2.2.9",
|
|
59
58
|
"@types/async-retry": "1.4.9",
|
|
60
|
-
"@types/bytes": "3.1.4",
|
|
61
59
|
"@types/jest": "29.5.14",
|
|
62
|
-
"@types/node": "22.
|
|
60
|
+
"@types/node": "22.10.7",
|
|
63
61
|
"eslint": "8.56.0",
|
|
64
62
|
"jest": "29.7.0",
|
|
65
63
|
"jest-environment-jsdom": "29.7.0",
|