@nextlyhq/storage-vercel-blob 0.0.2-alpha.68 → 0.0.2-alpha.69

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/index.d.ts CHANGED
@@ -9,24 +9,24 @@
9
9
  * - Vercel Blob (via @nextlyhq/storage-vercel-blob)
10
10
  */
11
11
  interface UploadOptions {
12
- /** Original filename from user */
13
- filename: string;
14
- /** MIME type (e.g., 'image/png', 'video/mp4') */
15
- mimeType: string;
16
- /** Optional content type override */
17
- contentType?: string;
18
- /** Optional folder/prefix for organizing uploads */
19
- folder?: string;
20
- /** Collection slug this upload belongs to (for collection-specific storage) */
21
- collection?: string;
22
- /** Optional Content-Disposition header value (e.g., 'attachment' for SVG security) */
23
- contentDisposition?: "inline" | "attachment";
12
+ /** Original filename from user */
13
+ filename: string;
14
+ /** MIME type (e.g., 'image/png', 'video/mp4') */
15
+ mimeType: string;
16
+ /** Optional content type override */
17
+ contentType?: string;
18
+ /** Optional folder/prefix for organizing uploads */
19
+ folder?: string;
20
+ /** Collection slug this upload belongs to (for collection-specific storage) */
21
+ collection?: string;
22
+ /** Optional Content-Disposition header value (e.g., 'attachment' for SVG security) */
23
+ contentDisposition?: "inline" | "attachment";
24
24
  }
25
25
  interface UploadResult {
26
- /** Public URL to access the file */
27
- url: string;
28
- /** Storage path/key (for deletion and metadata retrieval) */
29
- path: string;
26
+ /** Public URL to access the file */
27
+ url: string;
28
+ /** Storage path/key (for deletion and metadata retrieval) */
29
+ path: string;
30
30
  }
31
31
  /**
32
32
  * Extended file metadata returned by getMetadata()
@@ -35,28 +35,28 @@ interface UploadResult {
35
35
  * including dimensions for images and creation timestamps.
36
36
  */
37
37
  interface FileMetadata {
38
- /** Unique identifier (typically the storage path/key) */
39
- id: string;
40
- /** Storage filename (may differ from original) */
41
- filename: string;
42
- /** Original filename as uploaded by user */
43
- originalFilename: string;
44
- /** MIME type (e.g., 'image/jpeg', 'application/pdf') */
45
- mimeType: string;
46
- /** File size in bytes */
47
- size: number;
48
- /** Public URL to access the file */
49
- url: string;
50
- /** Thumbnail URL for images (if generated) */
51
- thumbnailUrl?: string;
52
- /** Image width in pixels (for images only) */
53
- width?: number;
54
- /** Image height in pixels (for images only) */
55
- height?: number;
56
- /** ISO timestamp when file was uploaded */
57
- createdAt: string;
58
- /** ISO timestamp when file was last modified */
59
- updatedAt?: string;
38
+ /** Unique identifier (typically the storage path/key) */
39
+ id: string;
40
+ /** Storage filename (may differ from original) */
41
+ filename: string;
42
+ /** Original filename as uploaded by user */
43
+ originalFilename: string;
44
+ /** MIME type (e.g., 'image/jpeg', 'application/pdf') */
45
+ mimeType: string;
46
+ /** File size in bytes */
47
+ size: number;
48
+ /** Public URL to access the file */
49
+ url: string;
50
+ /** Thumbnail URL for images (if generated) */
51
+ thumbnailUrl?: string;
52
+ /** Image width in pixels (for images only) */
53
+ width?: number;
54
+ /** Image height in pixels (for images only) */
55
+ height?: number;
56
+ /** ISO timestamp when file was uploaded */
57
+ createdAt: string;
58
+ /** ISO timestamp when file was last modified */
59
+ updatedAt?: string;
60
60
  }
61
61
  /**
62
62
  * Storage type identifier.
@@ -85,34 +85,34 @@ type StorageType = "s3" | "vercel-blob" | "local" | "uploadthing";
85
85
  * oversized object fails as a size error rather than after it has been read.
86
86
  */
87
87
  interface StorageReadOptions {
88
- /** Refuse once this many bytes have arrived. Defaults to the fetch cap. */
89
- maxBytes?: number;
90
- /** Give up after this long. Defaults to the fetch deadline. */
91
- timeoutMs?: number;
88
+ /** Refuse once this many bytes have arrived. Defaults to the fetch cap. */
89
+ maxBytes?: number;
90
+ /** Give up after this long. Defaults to the fetch deadline. */
91
+ timeoutMs?: number;
92
92
  }
93
93
  interface StorageAdapterInfo {
94
- /** Storage type identifier */
95
- type: StorageType;
96
- /** Human-readable adapter name */
97
- name: string;
98
- /** Whether this adapter supports signed URLs for private access */
99
- supportsSignedUrls: boolean;
100
- /** Whether this adapter supports client-side (direct) uploads */
101
- supportsClientUploads: boolean;
94
+ /** Storage type identifier */
95
+ type: StorageType;
96
+ /** Human-readable adapter name */
97
+ name: string;
98
+ /** Whether this adapter supports signed URLs for private access */
99
+ supportsSignedUrls: boolean;
100
+ /** Whether this adapter supports client-side (direct) uploads */
101
+ supportsClientUploads: boolean;
102
102
  }
103
103
  /**
104
104
  * Per-collection storage configuration.
105
105
  * Allows customizing storage behavior for specific upload collections.
106
106
  */
107
107
  interface CollectionStorageConfig {
108
- /** Prefix/folder for this collection's uploads */
109
- prefix?: string;
110
- /** Enable client-side uploads (for serverless platforms with body size limits) */
111
- clientUploads?: boolean;
112
- /** Generate signed URLs for downloads (for private buckets) */
113
- signedDownloads?: boolean;
114
- /** Signed URL expiry time in seconds (default: 3600) */
115
- signedUrlExpiresIn?: number;
108
+ /** Prefix/folder for this collection's uploads */
109
+ prefix?: string;
110
+ /** Enable client-side uploads (for serverless platforms with body size limits) */
111
+ clientUploads?: boolean;
112
+ /** Generate signed URLs for downloads (for private buckets) */
113
+ signedDownloads?: boolean;
114
+ /** Signed URL expiry time in seconds (default: 3600) */
115
+ signedUrlExpiresIn?: number;
116
116
  }
117
117
  /**
118
118
  * Collection storage map - maps collection slugs to their config.
@@ -136,10 +136,10 @@ type CollectionStorageMap = Record<string, boolean | CollectionStorageConfig>;
136
136
  * Extended by specific adapter configs (S3StorageConfig, etc.)
137
137
  */
138
138
  interface StoragePluginConfig {
139
- /** Enable/disable the plugin (default: true) */
140
- enabled?: boolean;
141
- /** Collections to apply this storage adapter to */
142
- collections: CollectionStorageMap;
139
+ /** Enable/disable the plugin (default: true) */
140
+ enabled?: boolean;
141
+ /** Collections to apply this storage adapter to */
142
+ collections: CollectionStorageMap;
143
143
  }
144
144
  /**
145
145
  * Storage plugin returned by adapter plugin functions.
@@ -157,24 +157,24 @@ interface StoragePluginConfig {
157
157
  * ```
158
158
  */
159
159
  interface StoragePlugin {
160
- /** Plugin name for identification */
161
- name: string;
162
- /** Storage type */
163
- type: StorageType;
164
- /** Collections this plugin handles */
165
- collections: CollectionStorageMap;
166
- /** The storage adapter instance */
167
- adapter: IStorageAdapter;
168
- /**
169
- * Handler for generating client-side upload URLs.
170
- * Called when clientUploads is enabled for a collection.
171
- */
172
- getClientUploadUrl?: (filename: string, mimeType: string, collection: string) => Promise<ClientUploadData>;
173
- /**
174
- * Handler for generating signed download URLs.
175
- * Called when signedDownloads is enabled for a collection.
176
- */
177
- getSignedDownloadUrl?: (path: string, expiresIn?: number) => Promise<string>;
160
+ /** Plugin name for identification */
161
+ name: string;
162
+ /** Storage type */
163
+ type: StorageType;
164
+ /** Collections this plugin handles */
165
+ collections: CollectionStorageMap;
166
+ /** The storage adapter instance */
167
+ adapter: IStorageAdapter;
168
+ /**
169
+ * Handler for generating client-side upload URLs.
170
+ * Called when clientUploads is enabled for a collection.
171
+ */
172
+ getClientUploadUrl?: (filename: string, mimeType: string, collection: string) => Promise<ClientUploadData>;
173
+ /**
174
+ * Handler for generating signed download URLs.
175
+ * Called when signedDownloads is enabled for a collection.
176
+ */
177
+ getSignedDownloadUrl?: (path: string, expiresIn?: number) => Promise<string>;
178
178
  }
179
179
  /**
180
180
  * Data returned for client-side (direct) uploads.
@@ -197,18 +197,18 @@ interface StoragePlugin {
197
197
  * ```
198
198
  */
199
199
  interface ClientUploadData {
200
- /** Pre-signed URL for direct upload */
201
- uploadUrl: string;
202
- /** Storage path/key that will be used */
203
- path: string;
204
- /** HTTP method to use (usually PUT for S3, POST for some services) */
205
- method: "PUT" | "POST";
206
- /** Headers to include in upload request */
207
- headers?: Record<string, string>;
208
- /** Form fields for multipart uploads (some services require this) */
209
- fields?: Record<string, string>;
210
- /** URL expiry timestamp */
211
- expiresAt: Date;
200
+ /** Pre-signed URL for direct upload */
201
+ uploadUrl: string;
202
+ /** Storage path/key that will be used */
203
+ path: string;
204
+ /** HTTP method to use (usually PUT for S3, POST for some services) */
205
+ method: "PUT" | "POST";
206
+ /** Headers to include in upload request */
207
+ headers?: Record<string, string>;
208
+ /** Form fields for multipart uploads (some services require this) */
209
+ fields?: Record<string, string>;
210
+ /** URL expiry timestamp */
211
+ expiresAt: Date;
212
212
  }
213
213
  /**
214
214
  * Base storage adapter interface.
@@ -227,47 +227,56 @@ interface ClientUploadData {
227
227
  * - getSignedUrl: Generate temporary signed URLs for private access
228
228
  * - getPresignedUploadUrl: Generate pre-signed URL for client uploads
229
229
  */
230
+ /** A file a storage holds, as {@link IStorageAdapter.list} names it. */
231
+ interface StoredFileEntry {
232
+ /** The stored path, as `upload` returned it and `delete` takes it. */
233
+ path: string;
234
+ /** When it was stored, where the storage says. */
235
+ lastModified?: Date;
236
+ /**
237
+ * The name it was uploaded under, where the storage keeps that apart from
238
+ * its path: UploadThing's keys carry no name. Elsewhere the name is the
239
+ * path's last segment.
240
+ */
241
+ name?: string;
242
+ }
230
243
  interface BulkDeleteResult {
231
- successful: string[];
232
- failed: Array<{
233
- filePath: string;
234
- error: string;
235
- }>;
244
+ successful: string[];
245
+ failed: Array<{
246
+ filePath: string;
247
+ error: string;
248
+ }>;
236
249
  }
237
250
  interface IStorageAdapter {
238
- /** Upload file buffer to storage */
239
- upload(buffer: Buffer, options: UploadOptions): Promise<UploadResult>;
240
- /** Delete file from storage */
241
- delete(filePath: string): Promise<void>;
242
- /** Bulk delete files from storage. Optional — adapters that support batch operations should implement this. */
243
- bulkDelete?(filePaths: string[]): Promise<BulkDeleteResult>;
244
- /** Check if file exists in storage */
245
- exists(filePath: string): Promise<boolean>;
246
- /** Get public URL for file */
247
- getPublicUrl(filePath: string): string;
248
- /** Get storage type identifier */
249
- getType(): string;
250
- /** Read file contents from storage (optional - not all adapters support this) */
251
- read?(filePath: string, options?: StorageReadOptions): Promise<Buffer | null>;
252
- /** Get adapter info including capabilities (optional but recommended) */
253
- getInfo?(): StorageAdapterInfo;
254
- /** Get file metadata (optional - not all adapters support this) */
255
- getMetadata?(filePath: string): Promise<FileMetadata | null>;
256
- /** Generate signed URL for temporary private access (optional) */
257
- getSignedUrl?(filePath: string, expiresIn?: number): Promise<string>;
258
- /** Generate pre-signed upload URL for client-side uploads (optional) */
259
- getPresignedUploadUrl?(key: string, mimeType: string, expiresIn?: number): Promise<ClientUploadData>;
251
+ /** Upload file buffer to storage */
252
+ upload(buffer: Buffer, options: UploadOptions): Promise<UploadResult>;
253
+ /** Delete file from storage */
254
+ delete(filePath: string): Promise<void>;
255
+ /** Bulk delete files from storage. Optional — adapters that support batch operations should implement this. */
256
+ bulkDelete?(filePaths: string[]): Promise<BulkDeleteResult>;
257
+ /** Check if file exists in storage */
258
+ exists(filePath: string): Promise<boolean>;
259
+ /** Get public URL for file */
260
+ getPublicUrl(filePath: string): string;
261
+ /** Get storage type identifier */
262
+ getType(): string;
263
+ /** Read file contents from storage (optional - not all adapters support this) */
264
+ read?(filePath: string, options?: StorageReadOptions): Promise<Buffer | null>;
265
+ /** Get adapter info including capabilities (optional but recommended) */
266
+ getInfo?(): StorageAdapterInfo;
267
+ /** Get file metadata (optional - not all adapters support this) */
268
+ getMetadata?(filePath: string): Promise<FileMetadata | null>;
269
+ /** Generate signed URL for temporary private access (optional) */
270
+ getSignedUrl?(filePath: string, expiresIn?: number): Promise<string>;
271
+ /** Generate pre-signed upload URL for client-side uploads (optional) */
272
+ getPresignedUploadUrl?(key: string, mimeType: string, expiresIn?: number): Promise<ClientUploadData>;
273
+ /**
274
+ * Every file the storage holds, a page at a time (optional). Used to find
275
+ * files no record refers to; a storage without it cannot be searched for
276
+ * them, which is said rather than taken as "none".
277
+ */
278
+ list?(): AsyncIterable<StoredFileEntry>;
260
279
  }
261
-
262
- /**
263
- * Vercel Blob Storage Types
264
- *
265
- * Type definitions for the @nextly/storage-vercel-blob package.
266
- * Optimized for Vercel deployments with client-side upload support.
267
- *
268
- * @packageDocumentation
269
- */
270
-
271
280
  /**
272
281
  * Vercel Blob storage adapter configuration.
273
282
  *
@@ -311,70 +320,70 @@ interface IStorageAdapter {
311
320
  * ```
312
321
  */
313
322
  interface VercelBlobStorageConfig extends StoragePluginConfig {
314
- /** Enable/disable this storage plugin (default: true). */
315
- enabled?: boolean;
316
- /** Collections this plugin handles. */
317
- collections: Record<string, boolean | CollectionStorageConfig>;
318
- /**
319
- * Vercel Blob read/write token.
320
- *
321
- * If not provided, falls back to `BLOB_READ_WRITE_TOKEN` environment variable.
322
- * Get your token from: Vercel Dashboard > Storage > Blob > Tokens
323
- *
324
- * @example
325
- * ```typescript
326
- * token: process.env.BLOB_READ_WRITE_TOKEN
327
- * ```
328
- */
329
- token?: string;
330
- /**
331
- * Add a random suffix to uploaded filenames.
332
- * Prevents filename collisions when uploading files with the same name.
333
- *
334
- * When true, `photo.jpg` becomes `photo-abc123.jpg`
335
- *
336
- * @default true
337
- */
338
- addRandomSuffix?: boolean;
339
- /**
340
- * Cache-Control max-age in seconds.
341
- * Controls how long browsers and CDNs cache the files.
342
- *
343
- * Note: Vercel Blob has a minimum cache time of 1 minute.
344
- *
345
- * @default 31536000 (1 year)
346
- */
347
- cacheControlMaxAge?: number;
348
- /**
349
- * Access level for uploaded blobs.
350
- * Vercel Blob only supports 'public' access.
351
- *
352
- * @default 'public'
353
- */
354
- access?: "public";
355
- /**
356
- * Store ID for multi-store setups.
357
- * Required if you have multiple blob stores in your Vercel project.
358
- *
359
- * Get the store ID from: Vercel Dashboard > Storage > Blob > Settings
360
- */
361
- storeId?: string;
362
- /**
363
- * Whether to allow overwriting existing blobs.
364
- * When false, uploading a file with the same path will throw an error.
365
- *
366
- * Note: Only relevant when `addRandomSuffix` is false.
367
- *
368
- * @default false
369
- */
370
- allowOverwrite?: boolean;
371
- /**
372
- * Multipart upload threshold in bytes.
373
- * Files larger than this will be uploaded using multipart upload.
374
- *
375
- * @default 5242880 (5MB)
376
- */
377
- multipartThreshold?: number;
323
+ /** Enable/disable this storage plugin (default: true). */
324
+ enabled?: boolean;
325
+ /** Collections this plugin handles. */
326
+ collections: Record<string, boolean | CollectionStorageConfig>;
327
+ /**
328
+ * Vercel Blob read/write token.
329
+ *
330
+ * If not provided, falls back to `BLOB_READ_WRITE_TOKEN` environment variable.
331
+ * Get your token from: Vercel Dashboard > Storage > Blob > Tokens
332
+ *
333
+ * @example
334
+ * ```typescript
335
+ * token: process.env.BLOB_READ_WRITE_TOKEN
336
+ * ```
337
+ */
338
+ token?: string;
339
+ /**
340
+ * Add a random suffix to uploaded filenames.
341
+ * Prevents filename collisions when uploading files with the same name.
342
+ *
343
+ * When true, `photo.jpg` becomes `photo-abc123.jpg`
344
+ *
345
+ * @default true
346
+ */
347
+ addRandomSuffix?: boolean;
348
+ /**
349
+ * Cache-Control max-age in seconds.
350
+ * Controls how long browsers and CDNs cache the files.
351
+ *
352
+ * Note: Vercel Blob has a minimum cache time of 1 minute.
353
+ *
354
+ * @default 31536000 (1 year)
355
+ */
356
+ cacheControlMaxAge?: number;
357
+ /**
358
+ * Access level for uploaded blobs.
359
+ * Vercel Blob only supports 'public' access.
360
+ *
361
+ * @default 'public'
362
+ */
363
+ access?: "public";
364
+ /**
365
+ * Store ID for multi-store setups.
366
+ * Required if you have multiple blob stores in your Vercel project.
367
+ *
368
+ * Get the store ID from: Vercel Dashboard > Storage > Blob > Settings
369
+ */
370
+ storeId?: string;
371
+ /**
372
+ * Whether to allow overwriting existing blobs.
373
+ * When false, uploading a file with the same path will throw an error.
374
+ *
375
+ * Note: Only relevant when `addRandomSuffix` is false.
376
+ *
377
+ * @default false
378
+ */
379
+ allowOverwrite?: boolean;
380
+ /**
381
+ * Multipart upload threshold in bytes.
382
+ * Files larger than this will be uploaded using multipart upload.
383
+ *
384
+ * @default 5242880 (5MB)
385
+ */
386
+ multipartThreshold?: number;
378
387
  }
379
388
  /**
380
389
  * Vercel Blob-specific collection storage configuration.
@@ -399,16 +408,16 @@ interface VercelBlobStorageConfig extends StoragePluginConfig {
399
408
  * ```
400
409
  */
401
410
  interface VercelBlobCollectionConfig extends CollectionStorageConfig {
402
- /**
403
- * Override addRandomSuffix for this collection.
404
- * If not set, uses the adapter-level setting.
405
- */
406
- addRandomSuffix?: boolean;
407
- /**
408
- * Override allowOverwrite for this collection.
409
- * If not set, uses the adapter-level setting.
410
- */
411
- allowOverwrite?: boolean;
411
+ /**
412
+ * Override addRandomSuffix for this collection.
413
+ * If not set, uses the adapter-level setting.
414
+ */
415
+ addRandomSuffix?: boolean;
416
+ /**
417
+ * Override allowOverwrite for this collection.
418
+ * If not set, uses the adapter-level setting.
419
+ */
420
+ allowOverwrite?: boolean;
412
421
  }
413
422
  /**
414
423
  * Type-safe collection storage map for Vercel Blob.
@@ -422,71 +431,14 @@ type VercelBlobCollectionStorageMap = Record<string, boolean | VercelBlobCollect
422
431
  * @internal
423
432
  */
424
433
  interface ResolvedVercelBlobConfig {
425
- token: string;
426
- addRandomSuffix: boolean;
427
- cacheControlMaxAge: number;
428
- access: "public";
429
- storeId?: string;
430
- allowOverwrite: boolean;
431
- multipartThreshold: number;
434
+ token: string;
435
+ addRandomSuffix: boolean;
436
+ cacheControlMaxAge: number;
437
+ access: "public";
438
+ storeId?: string;
439
+ allowOverwrite: boolean;
440
+ multipartThreshold: number;
432
441
  }
433
-
434
- /**
435
- * Vercel Blob Storage Plugin
436
- *
437
- * Factory function that creates a storage plugin for Vercel Blob Storage.
438
- * Returns a StoragePlugin that can be registered with MediaStorage.
439
- *
440
- * @example Basic usage
441
- * ```typescript
442
- * import { vercelBlobStorage } from '@nextly/storage-vercel-blob'
443
- * import { defineConfig } from 'nextly/config'
444
- *
445
- * export default defineConfig({
446
- * storage: [
447
- * vercelBlobStorage({
448
- * token: process.env.BLOB_READ_WRITE_TOKEN,
449
- * collections: {
450
- * media: true
451
- * }
452
- * })
453
- * ]
454
- * })
455
- * ```
456
- *
457
- * @example With client uploads (recommended for Vercel)
458
- * ```typescript
459
- * vercelBlobStorage({
460
- * collections: {
461
- * media: {
462
- * clientUploads: true // Bypass 4.5MB serverless limit
463
- * }
464
- * }
465
- * })
466
- * ```
467
- *
468
- * @example With collection-specific configuration
469
- * ```typescript
470
- * vercelBlobStorage({
471
- * addRandomSuffix: true,
472
- * cacheControlMaxAge: 86400, // 1 day
473
- * collections: {
474
- * // Simple enable with defaults
475
- * media: true,
476
- *
477
- * // Full configuration
478
- * documents: {
479
- * prefix: 'docs/',
480
- * addRandomSuffix: false,
481
- * allowOverwrite: true
482
- * }
483
- * }
484
- * })
485
- * ```
486
- *
487
- * @packageDocumentation
488
- */
489
-
490
442
  /**
491
443
  * Create a Vercel Blob storage plugin for Nextly.
492
444
  *
@@ -504,264 +456,200 @@ interface ResolvedVercelBlobConfig {
504
456
  *
505
457
  * @throws Error if token is not provided (via adapter)
506
458
  */
507
- declare function vercelBlobStorage(config: VercelBlobStorageConfig): StoragePlugin;
508
-
509
- /**
510
- * Vercel Blob Storage Adapter
511
- *
512
- * Stores files on Vercel Blob Storage, a globally distributed object storage
513
- * service powered by Cloudflare R2. Optimized for Vercel deployments with
514
- * built-in CDN distribution.
515
- *
516
- * Features:
517
- * - Globally distributed CDN-backed storage
518
- * - Automatic file management with unique filenames
519
- * - Token-based authentication
520
- * - Client-side upload support (via handleUpload API)
521
- * - Serverless-friendly (no filesystem access required)
522
- *
523
- * @example Basic usage
524
- * ```typescript
525
- * const adapter = new VercelBlobStorageAdapter({
526
- * token: process.env.BLOB_READ_WRITE_TOKEN,
527
- * collections: { media: true }
528
- * });
529
- *
530
- * const result = await adapter.upload(buffer, {
531
- * filename: 'photo.jpg',
532
- * mimeType: 'image/jpeg'
533
- * });
534
- * // result.url: "https://abc123.public.blob.vercel-storage.com/photo-xyz.jpg"
535
- * ```
536
- */
537
-
459
+ export declare function vercelBlobStorage(config: VercelBlobStorageConfig): StoragePlugin;
538
460
  /**
539
461
  * Vercel Blob Storage Adapter
540
462
  *
541
463
  * Implements the IStorageAdapter interface for Vercel Blob Storage.
542
464
  * Provides file upload, deletion, existence checks, and metadata retrieval.
543
465
  */
544
- declare class VercelBlobStorageAdapter implements IStorageAdapter {
545
- private config;
546
- private resolvedConfig;
547
- /**
548
- * Create a new Vercel Blob storage adapter.
549
- *
550
- * @param config - Vercel Blob storage configuration
551
- * @throws Error if token is not provided
552
- */
553
- constructor(config: VercelBlobStorageConfig);
554
- /**
555
- * Upload file to Vercel Blob.
556
- *
557
- * Files are stored with globally distributed CDN backing.
558
- * By default, a random suffix is added to prevent filename collisions.
559
- *
560
- * @param buffer - File content as Buffer
561
- * @param options - Upload options (filename, mimeType, folder, collection)
562
- * @returns Upload result with URL and storage path
563
- */
564
- upload(buffer: Buffer, options: UploadOptions): Promise<UploadResult>;
565
- /**
566
- * Delete file from Vercel Blob.
567
- *
568
- * @param filePath - Full blob URL to delete
569
- */
570
- delete(filePath: string): Promise<void>;
571
- /**
572
- * Delete multiple files from Vercel Blob in chunked batches.
573
- *
574
- * Processes deletions in chunks of 10 concurrent calls using Promise.allSettled,
575
- * collecting successes and failures without short-circuiting.
576
- *
577
- * @param filePaths - Array of full blob URLs to delete
578
- * @returns BulkDeleteResult with successful and failed arrays
579
- */
580
- bulkDelete(filePaths: string[]): Promise<BulkDeleteResult>;
581
- /**
582
- * Check if file exists in Vercel Blob.
583
- *
584
- * Uses the head() function which is efficient for existence checks.
585
- *
586
- * @param filePath - Full blob URL to check
587
- * @returns true if file exists, false otherwise
588
- */
589
- exists(filePath: string): Promise<boolean>;
590
- /**
591
- * Read a stored blob back as bytes, or `null` when it is not there.
592
- *
593
- * A NETWORK round trip, unlike the local adapter's disk read, because the
594
- * bytes live on Vercel's CDN and there is no other way to reach them. That
595
- * cost is the reason a caller serving these from its own origin has to cache:
596
- * the privacy rule that forces same-origin serving does not also make the
597
- * fetch free, and an uncached proxy pays it on every request.
598
- *
599
- * The URL is taken from `head` rather than assembled from `filePath`,
600
- * because a blob's address is issued by the service — it carries a random
601
- * suffix this adapter never chose — so deriving one here would be guessing at
602
- * a string another system owns.
603
- *
604
- * `null` for a missing blob rather than a throw, matching {@link exists}.
605
- *
606
- * @param filePath - Blob path/key
607
- * @returns The blob's bytes, or `null` when it does not exist
608
- */
609
- read(filePath: string, options?: StorageReadOptions): Promise<Buffer | null>;
610
- /**
611
- * Get public URL for Vercel Blob file.
612
- *
613
- * Vercel Blob paths ARE the public URLs - they're already full HTTPS URLs
614
- * with CDN distribution.
615
- *
616
- * @param filePath - Full blob URL
617
- * @returns The same URL (Vercel Blob paths are already public URLs)
618
- */
619
- getPublicUrl(filePath: string): string;
620
- /**
621
- * Get storage type identifier.
622
- */
623
- getType(): "vercel-blob";
624
- /**
625
- * Get adapter info including capabilities.
626
- *
627
- * @returns Adapter info with type, name, and capability flags
628
- */
629
- getInfo(): StorageAdapterInfo;
630
- /**
631
- * Get file metadata from Vercel Blob.
632
- *
633
- * Retrieves file information including size, content type, and upload date.
634
- *
635
- * @param filePath - Full blob URL
636
- * @returns File metadata or null if file not found
637
- */
638
- getMetadata(filePath: string): Promise<FileMetadata | null>;
639
- /**
640
- * Generate pre-signed URL for client-side uploads.
641
- *
642
- * Vercel Blob uses a different pattern for client uploads - they require
643
- * the handleUpload API which handles token generation server-side.
644
- *
645
- * This method is not implemented because Vercel Blob's client upload flow
646
- * requires server-side route handlers with handleUpload().
647
- *
648
- * @see https://vercel.com/docs/storage/vercel-blob/client-upload
649
- * @throws Error always - client uploads require handleUpload API
650
- */
651
- getPresignedUploadUrl(_key: string, _mimeType: string, _expiresIn?: number): Promise<ClientUploadData>;
652
- /**
653
- * List blobs with optional prefix filter.
654
- *
655
- * Useful for browsing or batch operations on stored files.
656
- *
657
- * @param prefix - Optional prefix to filter results
658
- * @param options - List options (limit, cursor)
659
- * @returns List of blob metadata
660
- */
661
- listBlobs(prefix?: string, options?: {
662
- limit?: number;
663
- cursor?: string;
664
- }): Promise<{
665
- blobs: Array<{
666
- url: string;
667
- pathname: string;
668
- size: number;
669
- uploadedAt: Date;
670
- }>;
671
- hasMore: boolean;
672
- cursor?: string;
466
+ export declare class VercelBlobStorageAdapter implements IStorageAdapter {
467
+ private config;
468
+ private resolvedConfig;
469
+ /**
470
+ * Create a new Vercel Blob storage adapter.
471
+ *
472
+ * @param config - Vercel Blob storage configuration
473
+ * @throws Error if token is not provided
474
+ */
475
+ constructor(config: VercelBlobStorageConfig);
476
+ /**
477
+ * Upload file to Vercel Blob.
478
+ *
479
+ * Files are stored with globally distributed CDN backing.
480
+ * By default, a random suffix is added to prevent filename collisions.
481
+ *
482
+ * @param buffer - File content as Buffer
483
+ * @param options - Upload options (filename, mimeType, folder, collection)
484
+ * @returns Upload result with URL and storage path
485
+ */
486
+ upload(buffer: Buffer, options: UploadOptions): Promise<UploadResult>;
487
+ /**
488
+ * Delete file from Vercel Blob.
489
+ *
490
+ * @param filePath - Full blob URL to delete
491
+ */
492
+ delete(filePath: string): Promise<void>;
493
+ /**
494
+ * Delete multiple files from Vercel Blob in chunked batches.
495
+ *
496
+ * Processes deletions in chunks of 10 concurrent calls using Promise.allSettled,
497
+ * collecting successes and failures without short-circuiting.
498
+ *
499
+ * @param filePaths - Array of full blob URLs to delete
500
+ * @returns BulkDeleteResult with successful and failed arrays
501
+ */
502
+ bulkDelete(filePaths: string[]): Promise<BulkDeleteResult>;
503
+ /**
504
+ * Check if file exists in Vercel Blob.
505
+ *
506
+ * Uses the head() function which is efficient for existence checks.
507
+ *
508
+ * @param filePath - Full blob URL to check
509
+ * @returns true if file exists, false otherwise
510
+ */
511
+ exists(filePath: string): Promise<boolean>;
512
+ /**
513
+ * Read a stored blob back as bytes, or `null` when it is not there.
514
+ *
515
+ * A NETWORK round trip, unlike the local adapter's disk read, because the
516
+ * bytes live on Vercel's CDN and there is no other way to reach them. That
517
+ * cost is the reason a caller serving these from its own origin has to cache:
518
+ * the privacy rule that forces same-origin serving does not also make the
519
+ * fetch free, and an uncached proxy pays it on every request.
520
+ *
521
+ * The URL is taken from `head` rather than assembled from `filePath`,
522
+ * because a blob's address is issued by the service — it carries a random
523
+ * suffix this adapter never chose — so deriving one here would be guessing at
524
+ * a string another system owns.
525
+ *
526
+ * `null` for a missing blob rather than a throw, matching {@link exists}.
527
+ *
528
+ * @param filePath - Blob path/key
529
+ * @returns The blob's bytes, or `null` when it does not exist
530
+ */
531
+ read(filePath: string, options?: StorageReadOptions): Promise<Buffer | null>;
532
+ /**
533
+ * Get public URL for Vercel Blob file.
534
+ *
535
+ * Vercel Blob paths ARE the public URLs - they're already full HTTPS URLs
536
+ * with CDN distribution.
537
+ *
538
+ * @param filePath - Full blob URL
539
+ * @returns The same URL (Vercel Blob paths are already public URLs)
540
+ */
541
+ /**
542
+ * Every blob in the store, by its URL (which is its stored path here), with
543
+ * when it was uploaded, a page at a time.
544
+ */
545
+ list(): AsyncIterable<StoredFileEntry>;
546
+ getPublicUrl(filePath: string): string;
547
+ /**
548
+ * Get storage type identifier.
549
+ */
550
+ getType(): "vercel-blob";
551
+ /**
552
+ * Get adapter info including capabilities.
553
+ *
554
+ * @returns Adapter info with type, name, and capability flags
555
+ */
556
+ getInfo(): StorageAdapterInfo;
557
+ /**
558
+ * Get file metadata from Vercel Blob.
559
+ *
560
+ * Retrieves file information including size, content type, and upload date.
561
+ *
562
+ * @param filePath - Full blob URL
563
+ * @returns File metadata or null if file not found
564
+ */
565
+ getMetadata(filePath: string): Promise<FileMetadata | null>;
566
+ /**
567
+ * Generate pre-signed URL for client-side uploads.
568
+ *
569
+ * Vercel Blob uses a different pattern for client uploads - they require
570
+ * the handleUpload API which handles token generation server-side.
571
+ *
572
+ * This method is not implemented because Vercel Blob's client upload flow
573
+ * requires server-side route handlers with handleUpload().
574
+ *
575
+ * @see https://vercel.com/docs/storage/vercel-blob/client-upload
576
+ * @throws Error always - client uploads require handleUpload API
577
+ */
578
+ getPresignedUploadUrl(_key: string, _mimeType: string, _expiresIn?: number): Promise<ClientUploadData>;
579
+ /**
580
+ * List blobs with optional prefix filter.
581
+ *
582
+ * Useful for browsing or batch operations on stored files.
583
+ *
584
+ * @param prefix - Optional prefix to filter results
585
+ * @param options - List options (limit, cursor)
586
+ * @returns List of blob metadata
587
+ */
588
+ listBlobs(prefix?: string, options?: {
589
+ limit?: number;
590
+ cursor?: string;
591
+ }): Promise<{
592
+ blobs: Array<{
593
+ url: string;
594
+ pathname: string;
595
+ size: number;
596
+ uploadedAt: Date;
673
597
  }>;
674
- /**
675
- * Build pathname for Vercel Blob upload.
676
- *
677
- * Creates pathname in format: {folder}/{filename}
678
- * If no folder is provided, uses the filename directly.
679
- *
680
- * @param filename - Original filename (will be sanitized)
681
- * @param folder - Optional folder/prefix for organizing uploads
682
- * @returns Generated pathname
683
- * @throws Error if folder fails sanitization
684
- */
685
- private buildPathname;
686
- /**
687
- * Sanitize filename to prevent path traversal issues.
688
- *
689
- * @param filename - Original filename
690
- * @returns Sanitized filename
691
- */
692
- private sanitizeFilename;
693
- /**
694
- * Reject folder values that would let a caller
695
- * escape the configured prefix via path traversal or weird
696
- * separators. Vercel Blob is flat-keyed so `/` is just part of the
697
- * pathname, but an attacker who controls `folder` could still:
698
- *
699
- * - Use `..` to chain a relative-traversal-style key.
700
- * - Use `\` (Windows separator) to confuse downstream tooling.
701
- * - Use control chars / null bytes to truncate the key.
702
- * - Use a leading `/` to look like an absolute path.
703
- *
704
- * Internal `/` segments stay allowed because callers legitimately
705
- * use nested folders (`docs/2026/...`); a leading `/`, empty
706
- * segments (`docs//x`), or `..` segments are rejected.
707
- *
708
- * @throws Error with a stable message so the storage facade can
709
- * surface a 400 instead of a 500.
710
- */
711
- private sanitizeFolder;
712
- /**
713
- * Get the configured token (masked for logging).
714
- */
715
- getTokenMasked(): string;
716
- /**
717
- * Check if random suffix is enabled.
718
- */
719
- hasRandomSuffix(): boolean;
720
- /**
721
- * Get configured cache control max age.
722
- */
723
- getCacheControlMaxAge(): number;
598
+ hasMore: boolean;
599
+ cursor?: string;
600
+ }>;
601
+ /**
602
+ * Build pathname for Vercel Blob upload.
603
+ *
604
+ * Creates pathname in format: {folder}/{filename}
605
+ * If no folder is provided, uses the filename directly.
606
+ *
607
+ * @param filename - Original filename (will be sanitized)
608
+ * @param folder - Optional folder/prefix for organizing uploads
609
+ * @returns Generated pathname
610
+ * @throws Error if folder fails sanitization
611
+ */
612
+ private buildPathname;
613
+ /**
614
+ * Sanitize filename to prevent path traversal issues.
615
+ *
616
+ * @param filename - Original filename
617
+ * @returns Sanitized filename
618
+ */
619
+ private sanitizeFilename;
620
+ /**
621
+ * Reject folder values that would let a caller
622
+ * escape the configured prefix via path traversal or weird
623
+ * separators. Vercel Blob is flat-keyed so `/` is just part of the
624
+ * pathname, but an attacker who controls `folder` could still:
625
+ *
626
+ * - Use `..` to chain a relative-traversal-style key.
627
+ * - Use `\` (Windows separator) to confuse downstream tooling.
628
+ * - Use control chars / null bytes to truncate the key.
629
+ * - Use a leading `/` to look like an absolute path.
630
+ *
631
+ * Internal `/` segments stay allowed because callers legitimately
632
+ * use nested folders (`docs/2026/...`); a leading `/`, empty
633
+ * segments (`docs//x`), or `..` segments are rejected.
634
+ *
635
+ * @throws Error with a stable message so the storage facade can
636
+ * surface a 400 instead of a 500.
637
+ */
638
+ private sanitizeFolder;
639
+ /**
640
+ * Get the configured token (masked for logging).
641
+ */
642
+ getTokenMasked(): string;
643
+ /**
644
+ * Check if random suffix is enabled.
645
+ */
646
+ hasRandomSuffix(): boolean;
647
+ /**
648
+ * Get configured cache control max age.
649
+ */
650
+ getCacheControlMaxAge(): number;
724
651
  }
725
-
726
- /**
727
- * @nextly/storage-vercel-blob
728
- *
729
- * Vercel Blob storage adapter for Nextly CMS.
730
- * Optimized for Vercel deployments with client-side upload support
731
- * to bypass serverless function size limits (4.5MB).
732
- *
733
- * @example Basic usage
734
- * ```typescript
735
- * import { vercelBlobStorage } from '@nextly/storage-vercel-blob'
736
- * import { defineConfig } from 'nextly/config'
737
- *
738
- * export default defineConfig({
739
- * storage: [
740
- * vercelBlobStorage({
741
- * token: process.env.BLOB_READ_WRITE_TOKEN,
742
- * collections: {
743
- * media: true
744
- * }
745
- * })
746
- * ]
747
- * })
748
- * ```
749
- *
750
- * @example With client uploads (recommended for Vercel)
751
- * ```typescript
752
- * vercelBlobStorage({
753
- * collections: {
754
- * media: {
755
- * clientUploads: true // Bypass 4.5MB serverless limit
756
- * }
757
- * }
758
- * })
759
- * ```
760
- *
761
- * @packageDocumentation
762
- */
763
-
764
- declare const PACKAGE_NAME = "@nextly/storage-vercel-blob";
765
- declare const PACKAGE_VERSION = "0.1.0";
766
-
767
- export { PACKAGE_NAME, PACKAGE_VERSION, type ResolvedVercelBlobConfig, type VercelBlobCollectionConfig, type VercelBlobCollectionStorageMap, VercelBlobStorageAdapter, type VercelBlobStorageConfig, vercelBlobStorage };
652
+ export declare const PACKAGE_NAME = "@nextly/storage-vercel-blob";
653
+ export declare const PACKAGE_VERSION = "0.1.0";
654
+ export type { ResolvedVercelBlobConfig, VercelBlobCollectionConfig, VercelBlobCollectionStorageMap, VercelBlobStorageConfig };
655
+ //# sourceMappingURL=index.d.ts.map