@alepha/bucket-vercel 0.12.1 → 0.13.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/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "blob"
11
11
  ],
12
12
  "author": "Nicolas Foures",
13
- "version": "0.12.1",
13
+ "version": "0.13.1",
14
14
  "type": "module",
15
15
  "engines": {
16
16
  "node": ">=22.0.0"
@@ -27,12 +27,12 @@
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^24.10.1",
30
- "alepha": "0.12.1",
30
+ "alepha": "0.13.1",
31
31
  "tsdown": "^0.16.7",
32
32
  "vitest": "^4.0.14"
33
33
  },
34
34
  "peerDependencies": {
35
- "alepha": "0.12.1"
35
+ "alepha": "0.13.1"
36
36
  },
37
37
  "scripts": {
38
38
  "lint": "alepha lint",
@@ -1,27 +0,0 @@
1
- //#region rolldown:runtime
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") {
10
- for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) {
13
- __defProp(to, key, {
14
- get: ((k) => from[k]).bind(null, key),
15
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
- });
17
- }
18
- }
19
- }
20
- return to;
21
- };
22
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
- value: mod,
24
- enumerable: true
25
- }) : target, mod));
26
-
27
- //#endregion
package/dist/index.cjs DELETED
@@ -1,135 +0,0 @@
1
- let alepha = require("alepha");
2
- let alepha_bucket = require("alepha/bucket");
3
- let alepha_datetime = require("alepha/datetime");
4
- let alepha_file = require("alepha/file");
5
- let alepha_logger = require("alepha/logger");
6
- let __vercel_blob = require("@vercel/blob");
7
-
8
- //#region src/providers/VercelBlobProvider.ts
9
- var VercelBlobApi = class {
10
- put = __vercel_blob.put;
11
- head = __vercel_blob.head;
12
- del = __vercel_blob.del;
13
- };
14
-
15
- //#endregion
16
- //#region src/providers/VercelFileStorageProvider.ts
17
- const envSchema = alepha.t.object({ BLOB_READ_WRITE_TOKEN: alepha.t.text({ size: "long" }) });
18
- /**
19
- * Vercel Blob Storage implementation of File Storage Provider.
20
- */
21
- var VercelFileStorageProvider = class {
22
- log = (0, alepha_logger.$logger)();
23
- env = (0, alepha.$env)(envSchema);
24
- alepha = (0, alepha.$inject)(alepha.Alepha);
25
- time = (0, alepha.$inject)(alepha_datetime.DateTimeProvider);
26
- fileSystem = (0, alepha.$inject)(alepha_file.FileSystemProvider);
27
- fileDetector = (0, alepha.$inject)(alepha_file.FileDetector);
28
- stores = /* @__PURE__ */ new Set();
29
- vercelBlobApi = (0, alepha.$inject)(VercelBlobApi);
30
- onStart = (0, alepha.$hook)({
31
- on: "start",
32
- handler: async () => {
33
- for (const bucket of this.alepha.descriptors(alepha_bucket.$bucket)) {
34
- if (bucket.provider !== this) continue;
35
- const storeName = this.convertName(bucket.name);
36
- this.log.debug(`Prepare store '${storeName}' ...`);
37
- this.stores.add(storeName);
38
- this.log.info(`Blob storage '${bucket.name}' OK`);
39
- }
40
- }
41
- });
42
- convertName(name) {
43
- return name.replaceAll("/", "-").toLowerCase();
44
- }
45
- createId(mimeType) {
46
- const ext = this.fileDetector.getExtensionFromMimeType(mimeType);
47
- return `${crypto.randomUUID()}.${ext}`;
48
- }
49
- async upload(bucketName, file, fileId) {
50
- fileId ??= this.createId(file.type);
51
- this.log.trace(`Uploading file '${file.name}' to bucket '${bucketName}' with id '${fileId}'...`);
52
- const pathname = `${this.convertName(bucketName)}/${fileId}`;
53
- try {
54
- const contentBuffer = Buffer.from(await file.arrayBuffer());
55
- const result = await this.vercelBlobApi.put(pathname, contentBuffer, {
56
- access: "public",
57
- contentType: file.type || "application/octet-stream",
58
- token: this.env.BLOB_READ_WRITE_TOKEN,
59
- allowOverwrite: true
60
- });
61
- this.log.trace(`File uploaded successfully: ${result.url}`);
62
- return fileId;
63
- } catch (error) {
64
- this.log.error(`Failed to upload file: ${error}`);
65
- if (error instanceof Error) throw new alepha.AlephaError(`Upload failed: ${error.message}`, { cause: error });
66
- throw error;
67
- }
68
- }
69
- async download(bucketName, fileId) {
70
- this.log.trace(`Downloading file '${fileId}' from bucket '${bucketName}'...`);
71
- const pathname = `${this.convertName(bucketName)}/${fileId}`;
72
- try {
73
- const headResult = await this.vercelBlobApi.head(pathname, { token: this.env.BLOB_READ_WRITE_TOKEN });
74
- if (!headResult) throw new alepha_bucket.FileNotFoundError(`File '${fileId}' not found in bucket '${bucketName}'`);
75
- const response = await fetch(headResult.url);
76
- if (!response.ok) throw new alepha_bucket.FileNotFoundError(`Failed to fetch file: ${response.statusText}`);
77
- const arrayBuffer = await response.arrayBuffer();
78
- if (!arrayBuffer) throw new alepha_bucket.FileNotFoundError("File not found - empty response body");
79
- const mimeType = this.fileDetector.getContentType(fileId);
80
- return this.fileSystem.createFile({
81
- buffer: Buffer.from(arrayBuffer),
82
- name: fileId,
83
- type: mimeType
84
- });
85
- } catch (error) {
86
- if (error instanceof alepha_bucket.FileNotFoundError) throw error;
87
- this.log.error(`Failed to download file: ${error}`);
88
- if (error instanceof Error) throw new alepha_bucket.FileNotFoundError("Error downloading file", { cause: error });
89
- throw error;
90
- }
91
- }
92
- async exists(bucketName, fileId) {
93
- this.log.trace(`Checking existence of file '${fileId}' in bucket '${bucketName}'...`);
94
- const pathname = `${this.convertName(bucketName)}/${fileId}`;
95
- try {
96
- return await this.vercelBlobApi.head(pathname, { token: this.env.BLOB_READ_WRITE_TOKEN }) !== null;
97
- } catch (error) {
98
- return false;
99
- }
100
- }
101
- async delete(bucketName, fileId) {
102
- this.log.trace(`Deleting file '${fileId}' from bucket '${bucketName}'...`);
103
- const pathname = `${this.convertName(bucketName)}/${fileId}`;
104
- try {
105
- await this.vercelBlobApi.del(pathname, { token: this.env.BLOB_READ_WRITE_TOKEN });
106
- } catch (error) {
107
- this.log.error(`Failed to delete file: ${error}`);
108
- if (error instanceof Error) throw new alepha_bucket.FileNotFoundError("Error deleting file", { cause: error });
109
- throw error;
110
- }
111
- }
112
- };
113
-
114
- //#endregion
115
- //#region src/index.ts
116
- /**
117
- * Plugin for Alepha Bucket that provides Vercel Blob Storage capabilities.
118
- *
119
- * @see {@link VercelFileStorageProvider}
120
- * @module alepha.bucket.vercel
121
- */
122
- const AlephaBucketVercel = (0, alepha.$module)({
123
- name: "alepha.bucket.vercel",
124
- services: [VercelFileStorageProvider],
125
- register: (alepha$1) => alepha$1.with({
126
- optional: true,
127
- provide: alepha_bucket.FileStorageProvider,
128
- use: VercelFileStorageProvider
129
- }).with(alepha_bucket.AlephaBucket)
130
- });
131
-
132
- //#endregion
133
- exports.AlephaBucketVercel = AlephaBucketVercel;
134
- exports.VercelFileStorageProvider = VercelFileStorageProvider;
135
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":["put","head","del","t","Alepha","DateTimeProvider","FileSystemProvider","FileDetector","$bucket","AlephaError","FileNotFoundError","alepha","FileStorageProvider","AlephaBucket"],"sources":["../src/providers/VercelBlobProvider.ts","../src/providers/VercelFileStorageProvider.ts","../src/index.ts"],"sourcesContent":["import { del, head, put } from \"@vercel/blob\";\n\nexport class VercelBlobApi {\n put: typeof put = put;\n head: typeof head = head;\n del: typeof del = del;\n}\n","import type { Readable } from \"node:stream\";\nimport {\n $env,\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type FileLike,\n type Static,\n t,\n} from \"alepha\";\nimport {\n $bucket,\n FileNotFoundError,\n type FileStorageProvider,\n} from \"alepha/bucket\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { FileDetector, FileSystemProvider } from \"alepha/file\";\nimport { $logger } from \"alepha/logger\";\nimport { VercelBlobApi } from \"./VercelBlobProvider.ts\";\n\nconst envSchema = t.object({\n BLOB_READ_WRITE_TOKEN: t.text({\n size: \"long\",\n }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\n/**\n * Vercel Blob Storage implementation of File Storage Provider.\n */\nexport class VercelFileStorageProvider implements FileStorageProvider {\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly alepha = $inject(Alepha);\n protected readonly time = $inject(DateTimeProvider);\n protected readonly fileSystem = $inject(FileSystemProvider);\n protected readonly fileDetector = $inject(FileDetector);\n protected readonly stores: Set<string> = new Set();\n protected readonly vercelBlobApi = $inject(VercelBlobApi);\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n for (const bucket of this.alepha.descriptors($bucket)) {\n if (bucket.provider !== this) {\n continue;\n }\n\n const storeName = this.convertName(bucket.name);\n\n this.log.debug(`Prepare store '${storeName}' ...`);\n\n // Vercel Blob doesn't require explicit store/container creation\n // We just track the store names for reference\n this.stores.add(storeName);\n\n this.log.info(`Blob storage '${bucket.name}' OK`);\n }\n },\n });\n\n public convertName(name: string): string {\n // Convert to a valid path-like name for Vercel Blob\n return name.replaceAll(\"/\", \"-\").toLowerCase();\n }\n\n protected createId(mimeType: string): string {\n const ext = this.fileDetector.getExtensionFromMimeType(mimeType);\n return `${crypto.randomUUID()}.${ext}`;\n }\n\n public async upload(\n bucketName: string,\n file: FileLike,\n fileId?: string,\n ): Promise<string> {\n fileId ??= this.createId(file.type);\n\n this.log.trace(\n `Uploading file '${file.name}' to bucket '${bucketName}' with id '${fileId}'...`,\n );\n\n const storeName = this.convertName(bucketName);\n const pathname = `${storeName}/${fileId}`;\n\n try {\n const contentBuffer = Buffer.from(await file.arrayBuffer());\n\n const result = await this.vercelBlobApi.put(\n pathname,\n contentBuffer as unknown as Readable,\n {\n access: \"public\",\n contentType: file.type || \"application/octet-stream\",\n token: this.env.BLOB_READ_WRITE_TOKEN,\n allowOverwrite: true,\n },\n );\n\n this.log.trace(`File uploaded successfully: ${result.url}`);\n return fileId;\n } catch (error) {\n this.log.error(`Failed to upload file: ${error}`);\n if (error instanceof Error) {\n throw new AlephaError(`Upload failed: ${error.message}`, {\n cause: error,\n });\n }\n\n throw error;\n }\n }\n\n public async download(bucketName: string, fileId: string): Promise<FileLike> {\n this.log.trace(\n `Downloading file '${fileId}' from bucket '${bucketName}'...`,\n );\n\n const storeName = this.convertName(bucketName);\n const pathname = `${storeName}/${fileId}`;\n\n try {\n const headResult = await this.vercelBlobApi.head(pathname, {\n token: this.env.BLOB_READ_WRITE_TOKEN,\n });\n\n if (!headResult) {\n throw new FileNotFoundError(\n `File '${fileId}' not found in bucket '${bucketName}'`,\n );\n }\n\n const response = await fetch(headResult.url);\n\n if (!response.ok) {\n throw new FileNotFoundError(\n `Failed to fetch file: ${response.statusText}`,\n );\n }\n\n const arrayBuffer = await response.arrayBuffer();\n if (!arrayBuffer) {\n throw new FileNotFoundError(\"File not found - empty response body\");\n }\n\n const mimeType = this.fileDetector.getContentType(fileId);\n\n return this.fileSystem.createFile({\n buffer: Buffer.from(arrayBuffer),\n name: fileId,\n type: mimeType,\n });\n } catch (error) {\n if (error instanceof FileNotFoundError) {\n throw error;\n }\n\n this.log.error(`Failed to download file: ${error}`);\n if (error instanceof Error) {\n throw new FileNotFoundError(\"Error downloading file\", { cause: error });\n }\n\n throw error;\n }\n }\n\n public async exists(bucketName: string, fileId: string): Promise<boolean> {\n this.log.trace(\n `Checking existence of file '${fileId}' in bucket '${bucketName}'...`,\n );\n\n const storeName = this.convertName(bucketName);\n const pathname = `${storeName}/${fileId}`;\n\n try {\n const result = await this.vercelBlobApi.head(pathname, {\n token: this.env.BLOB_READ_WRITE_TOKEN,\n });\n return result !== null;\n } catch (error) {\n // Vercel Blob head() throws for non-existent files\n return false;\n }\n }\n\n public async delete(bucketName: string, fileId: string): Promise<void> {\n this.log.trace(`Deleting file '${fileId}' from bucket '${bucketName}'...`);\n\n const storeName = this.convertName(bucketName);\n const pathname = `${storeName}/${fileId}`;\n\n try {\n await this.vercelBlobApi.del(pathname, {\n token: this.env.BLOB_READ_WRITE_TOKEN,\n });\n } catch (error) {\n this.log.error(`Failed to delete file: ${error}`);\n if (error instanceof Error) {\n throw new FileNotFoundError(\"Error deleting file\", { cause: error });\n }\n throw error;\n }\n }\n}\n","import { $module } from \"alepha\";\nimport { AlephaBucket, FileStorageProvider } from \"alepha/bucket\";\nimport { VercelFileStorageProvider } from \"./providers/VercelFileStorageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/VercelFileStorageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Bucket that provides Vercel Blob Storage capabilities.\n *\n * @see {@link VercelFileStorageProvider}\n * @module alepha.bucket.vercel\n */\nexport const AlephaBucketVercel = $module({\n name: \"alepha.bucket.vercel\",\n services: [VercelFileStorageProvider],\n register: (alepha) =>\n alepha\n .with({\n optional: true,\n provide: FileStorageProvider,\n use: VercelFileStorageProvider,\n })\n .with(AlephaBucket),\n});\n"],"mappings":"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"}