@alepha/bucket-vercel 0.11.10 → 0.11.12

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/README.md CHANGED
@@ -14,25 +14,13 @@ npm install alepha
14
14
 
15
15
  Plugin for Alepha Bucket that provides Vercel Blob Storage capabilities.
16
16
 
17
- This module can be imported and used as follows:
18
-
19
- ```typescript
20
- import { Alepha, run } from "alepha";
21
- import { AlephaBucketVercel } from "alepha/bucket/vercel";
22
-
23
- const alepha = Alepha.create()
24
- .with(AlephaBucketVercel);
25
-
26
- run(alepha);
27
- ```
28
-
29
17
  ## API Reference
30
18
 
31
19
  ### Providers
32
20
 
33
21
  Providers are classes that encapsulate specific functionality and can be injected into your application. They handle initialization, configuration, and lifecycle management.
34
22
 
35
- For more details, see the [Providers documentation](/docs/providers).
23
+ For more details, see the [Providers documentation](https://feunard.github.io/alepha/).
36
24
 
37
25
  #### VercelFileStorageProvider
38
26
 
package/dist/index.cjs CHANGED
@@ -1,9 +1,8 @@
1
- let __alepha_bucket = require("@alepha/bucket");
2
- let __alepha_core = require("@alepha/core");
3
- let node_crypto = require("node:crypto");
4
- let __alepha_datetime = require("@alepha/datetime");
5
- let __alepha_file = require("@alepha/file");
6
- let __alepha_logger = require("@alepha/logger");
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");
7
6
  let __vercel_blob = require("@vercel/blob");
8
7
 
9
8
  //#region src/providers/VercelBlobProvider.ts
@@ -15,23 +14,23 @@ var VercelBlobApi = class {
15
14
 
16
15
  //#endregion
17
16
  //#region src/providers/VercelFileStorageProvider.ts
18
- const envSchema = __alepha_core.t.object({ BLOB_READ_WRITE_TOKEN: __alepha_core.t.text({ size: "long" }) });
17
+ const envSchema = alepha.t.object({ BLOB_READ_WRITE_TOKEN: alepha.t.text({ size: "long" }) });
19
18
  /**
20
19
  * Vercel Blob Storage implementation of File Storage Provider.
21
20
  */
22
21
  var VercelFileStorageProvider = class {
23
- log = (0, __alepha_logger.$logger)();
24
- env = (0, __alepha_core.$env)(envSchema);
25
- alepha = (0, __alepha_core.$inject)(__alepha_core.Alepha);
26
- time = (0, __alepha_core.$inject)(__alepha_datetime.DateTimeProvider);
27
- fileSystem = (0, __alepha_core.$inject)(__alepha_file.FileSystem);
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);
28
27
  stores = /* @__PURE__ */ new Set();
29
- vercelBlobApi = (0, __alepha_core.$inject)(VercelBlobApi);
30
- metadataService = (0, __alepha_core.$inject)(__alepha_bucket.FileMetadataService);
31
- onStart = (0, __alepha_core.$hook)({
28
+ vercelBlobApi = (0, alepha.$inject)(VercelBlobApi);
29
+ metadataService = (0, alepha.$inject)(alepha_bucket.FileMetadataService);
30
+ onStart = (0, alepha.$hook)({
32
31
  on: "start",
33
32
  handler: async () => {
34
- for (const bucket of this.alepha.descriptors(__alepha_bucket.$bucket)) {
33
+ for (const bucket of this.alepha.descriptors(alepha_bucket.$bucket)) {
35
34
  if (bucket.provider !== this) continue;
36
35
  const storeName = this.convertName(bucket.name);
37
36
  this.log.debug(`Prepare store '${storeName}' ...`);
@@ -44,7 +43,7 @@ var VercelFileStorageProvider = class {
44
43
  return name.replaceAll("/", "-").toLowerCase();
45
44
  }
46
45
  createId() {
47
- return (0, node_crypto.randomUUID)();
46
+ return crypto.randomUUID();
48
47
  }
49
48
  async upload(bucketName, file, fileId) {
50
49
  fileId ??= this.createId();
@@ -63,7 +62,7 @@ var VercelFileStorageProvider = class {
63
62
  return fileId;
64
63
  } catch (error) {
65
64
  this.log.error(`Failed to upload file: ${error}`);
66
- if (error instanceof Error) throw new __alepha_core.AlephaError(`Upload failed: ${error.message}`, { cause: error });
65
+ if (error instanceof Error) throw new alepha.AlephaError(`Upload failed: ${error.message}`, { cause: error });
67
66
  throw error;
68
67
  }
69
68
  }
@@ -72,11 +71,11 @@ var VercelFileStorageProvider = class {
72
71
  const pathname = `${this.convertName(bucketName)}/${fileId}`;
73
72
  try {
74
73
  const headResult = await this.vercelBlobApi.head(pathname, { token: this.env.BLOB_READ_WRITE_TOKEN });
75
- if (!headResult) throw new __alepha_bucket.FileNotFoundError(`File '${fileId}' not found in bucket '${bucketName}'`);
74
+ if (!headResult) throw new alepha_bucket.FileNotFoundError(`File '${fileId}' not found in bucket '${bucketName}'`);
76
75
  const response = await fetch(headResult.url);
77
- if (!response.ok) throw new __alepha_bucket.FileNotFoundError(`Failed to fetch file: ${response.statusText}`);
76
+ if (!response.ok) throw new alepha_bucket.FileNotFoundError(`Failed to fetch file: ${response.statusText}`);
78
77
  const arrayBuffer = await response.arrayBuffer();
79
- if (!arrayBuffer) throw new __alepha_bucket.FileNotFoundError("File not found - empty response body");
78
+ if (!arrayBuffer) throw new alepha_bucket.FileNotFoundError("File not found - empty response body");
80
79
  const buffer = Buffer.from(arrayBuffer);
81
80
  const { metadata, contentStart } = this.metadataService.decodeMetadataFromBuffer(buffer);
82
81
  const content = buffer.subarray(contentStart);
@@ -86,9 +85,9 @@ var VercelFileStorageProvider = class {
86
85
  type: metadata.type
87
86
  });
88
87
  } catch (error) {
89
- if (error instanceof __alepha_bucket.FileNotFoundError) throw error;
88
+ if (error instanceof alepha_bucket.FileNotFoundError) throw error;
90
89
  this.log.error(`Failed to download file: ${error}`);
91
- if (error instanceof Error) throw new __alepha_bucket.FileNotFoundError("Error downloading file", { cause: error });
90
+ if (error instanceof Error) throw new alepha_bucket.FileNotFoundError("Error downloading file", { cause: error });
92
91
  throw error;
93
92
  }
94
93
  }
@@ -108,7 +107,7 @@ var VercelFileStorageProvider = class {
108
107
  await this.vercelBlobApi.del(pathname, { token: this.env.BLOB_READ_WRITE_TOKEN });
109
108
  } catch (error) {
110
109
  this.log.error(`Failed to delete file: ${error}`);
111
- if (error instanceof Error) throw new __alepha_bucket.FileNotFoundError("Error deleting file", { cause: error });
110
+ if (error instanceof Error) throw new alepha_bucket.FileNotFoundError("Error deleting file", { cause: error });
112
111
  throw error;
113
112
  }
114
113
  }
@@ -122,14 +121,14 @@ var VercelFileStorageProvider = class {
122
121
  * @see {@link VercelFileStorageProvider}
123
122
  * @module alepha.bucket.vercel
124
123
  */
125
- const AlephaBucketVercel = (0, __alepha_core.$module)({
124
+ const AlephaBucketVercel = (0, alepha.$module)({
126
125
  name: "alepha.bucket.vercel",
127
126
  services: [VercelFileStorageProvider],
128
- register: (alepha) => alepha.with({
127
+ register: (alepha$1) => alepha$1.with({
129
128
  optional: true,
130
- provide: __alepha_bucket.FileStorageProvider,
129
+ provide: alepha_bucket.FileStorageProvider,
131
130
  use: VercelFileStorageProvider
132
- }).with(__alepha_bucket.AlephaBucket)
131
+ }).with(alepha_bucket.AlephaBucket)
133
132
  });
134
133
 
135
134
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["put","head","del","t","Alepha","DateTimeProvider","FileSystem","FileMetadataService","$bucket","AlephaError","FileNotFoundError","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 { randomUUID } from \"node:crypto\";\nimport type { Readable } from \"node:stream\";\nimport {\n $bucket,\n FileMetadataService,\n FileNotFoundError,\n type FileStorageProvider,\n} from \"@alepha/bucket\";\nimport {\n $env,\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type FileLike,\n type Static,\n t,\n} from \"@alepha/core\";\nimport { DateTimeProvider } from \"@alepha/datetime\";\nimport { FileSystem } 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/core\" {\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(FileSystem);\n protected readonly stores: Set<string> = new Set();\n protected readonly vercelBlobApi = $inject(VercelBlobApi);\n protected readonly metadataService = $inject(FileMetadataService);\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(): string {\n return randomUUID();\n }\n\n public async upload(\n bucketName: string,\n file: FileLike,\n fileId?: string,\n ): Promise<string> {\n fileId ??= this.createId();\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 // Create a buffer with metadata and content\n const contentBuffer = Buffer.from(await file.arrayBuffer());\n const fileBuffer = this.metadataService.createFileBuffer(\n file,\n contentBuffer,\n );\n\n // Upload the complete buffer (metadata + content) to Vercel Blob\n const result = await this.vercelBlobApi.put(\n pathname,\n fileBuffer 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 // check if the file exists and get metadata\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 // fetch the actual file content (with metadata)\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 // Decode metadata from the buffer\n const buffer = Buffer.from(arrayBuffer);\n const { metadata, contentStart } =\n this.metadataService.decodeMetadataFromBuffer(buffer);\n\n // Extract the actual content\n const content = buffer.subarray(contentStart);\n\n return this.fileSystem.createFile({\n buffer: content,\n name: metadata.name,\n type: metadata.type,\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 { AlephaBucket, FileStorageProvider } from \"@alepha/bucket\";\nimport { $module } from \"@alepha/core\";\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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"}
1
+ {"version":3,"file":"index.cjs","names":["put","head","del","t","Alepha","DateTimeProvider","FileSystemProvider","FileMetadataService","$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 FileMetadataService,\n FileNotFoundError,\n type FileStorageProvider,\n} from \"alepha/bucket\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { 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 stores: Set<string> = new Set();\n protected readonly vercelBlobApi = $inject(VercelBlobApi);\n protected readonly metadataService = $inject(FileMetadataService);\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(): string {\n return crypto.randomUUID();\n }\n\n public async upload(\n bucketName: string,\n file: FileLike,\n fileId?: string,\n ): Promise<string> {\n fileId ??= this.createId();\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 // Create a buffer with metadata and content\n const contentBuffer = Buffer.from(await file.arrayBuffer());\n const fileBuffer = this.metadataService.createFileBuffer(\n file,\n contentBuffer,\n );\n\n // Upload the complete buffer (metadata + content) to Vercel Blob\n const result = await this.vercelBlobApi.put(\n pathname,\n fileBuffer 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 // check if the file exists and get metadata\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 // fetch the actual file content (with metadata)\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 // Decode metadata from the buffer\n const buffer = Buffer.from(arrayBuffer);\n const { metadata, contentStart } =\n this.metadataService.decodeMetadataFromBuffer(buffer);\n\n // Extract the actual content\n const content = buffer.subarray(contentStart);\n\n return this.fileSystem.createFile({\n buffer: content,\n name: metadata.name,\n type: metadata.type,\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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"}
package/dist/index.d.cts CHANGED
@@ -1,9 +1,9 @@
1
- import * as _alepha_core1 from "@alepha/core";
2
- import { Alepha, FileLike, Static } from "@alepha/core";
3
- import * as _alepha_logger0 from "@alepha/logger";
4
- import { FileMetadataService, FileStorageProvider } from "@alepha/bucket";
5
- import { DateTimeProvider } from "@alepha/datetime";
6
- import { FileSystem } from "@alepha/file";
1
+ import * as alepha1 from "alepha";
2
+ import { Alepha, FileLike, Static } from "alepha";
3
+ import * as alepha_logger0 from "alepha/logger";
4
+ import { FileMetadataService, FileStorageProvider } from "alepha/bucket";
5
+ import { DateTimeProvider } from "alepha/datetime";
6
+ import { FileSystemProvider } from "alepha/file";
7
7
  import { del, head, put } from "@vercel/blob";
8
8
 
9
9
  //#region src/providers/VercelBlobProvider.d.ts
@@ -14,27 +14,27 @@ declare class VercelBlobApi {
14
14
  }
15
15
  //#endregion
16
16
  //#region src/providers/VercelFileStorageProvider.d.ts
17
- declare const envSchema: _alepha_core1.TObject<{
18
- BLOB_READ_WRITE_TOKEN: _alepha_core1.TString;
17
+ declare const envSchema: alepha1.TObject<{
18
+ BLOB_READ_WRITE_TOKEN: alepha1.TString;
19
19
  }>;
20
- declare module "@alepha/core" {
20
+ declare module "alepha" {
21
21
  interface Env extends Partial<Static<typeof envSchema>> {}
22
22
  }
23
23
  /**
24
24
  * Vercel Blob Storage implementation of File Storage Provider.
25
25
  */
26
26
  declare class VercelFileStorageProvider implements FileStorageProvider {
27
- protected readonly log: _alepha_logger0.Logger;
27
+ protected readonly log: alepha_logger0.Logger;
28
28
  protected readonly env: {
29
29
  BLOB_READ_WRITE_TOKEN: string;
30
30
  };
31
31
  protected readonly alepha: Alepha;
32
32
  protected readonly time: DateTimeProvider;
33
- protected readonly fileSystem: FileSystem;
33
+ protected readonly fileSystem: FileSystemProvider;
34
34
  protected readonly stores: Set<string>;
35
35
  protected readonly vercelBlobApi: VercelBlobApi;
36
36
  protected readonly metadataService: FileMetadataService;
37
- protected readonly onStart: _alepha_core1.HookDescriptor<"start">;
37
+ protected readonly onStart: alepha1.HookDescriptor<"start">;
38
38
  convertName(name: string): string;
39
39
  protected createId(): string;
40
40
  upload(bucketName: string, file: FileLike, fileId?: string): Promise<string>;
@@ -50,7 +50,7 @@ declare class VercelFileStorageProvider implements FileStorageProvider {
50
50
  * @see {@link VercelFileStorageProvider}
51
51
  * @module alepha.bucket.vercel
52
52
  */
53
- declare const AlephaBucketVercel: _alepha_core1.Service<_alepha_core1.Module>;
53
+ declare const AlephaBucketVercel: alepha1.Service<alepha1.Module>;
54
54
  //#endregion
55
55
  export { AlephaBucketVercel, VercelFileStorageProvider };
56
56
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/providers/VercelBlobProvider.ts","../src/providers/VercelFileStorageProvider.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;cAEa,aAAA;cACC;eACC;cACD;;;;cCkBR,WAIJ,aAAA,CAJa;yBAIb,aAAA,CAAA;;;EDzBW,UAAA,GAAA,SC4BW,OD5BE,CC4BM,MD5BN,CAAA,OC4BoB,SD5BpB,CAAA,CAAA,CAAA,CACZ;;;;;cCiCD,yBAAA,YAAqC;0BAAX,eAAA,CACf;EAdlB,mBAIJ,GAAA,EAAA;IAJa,qBAAA,EAAA,MAAA;EAAA,CAAA;qBAAA,MAAA,EAgBY,MAhBZ;EAO+B,mBAAA,IAAA,EAUrB,gBAVqB;EAAd,mBAAA,UAAA,EAWD,UAXC;EAAR,mBAAA,MAAA,EAYK,GAZL,CAAA,MAAA,CAAA;EAAO,mBAAA,aAAA,EAaG,aAbH;EAAA,mBAAA,eAAA,EAcK,mBAdL;EAAA,mBAAA,OAAA,EAcK,aAAA,CAER,cAhBG,CAAA,OAAA,CAAA;EAMlB,WAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAA0B;EAAA,UAAA,QACf,CAAA,CAAA,EAAA,MAAA;EAEG,MAAA,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EAuCjB,QAvCiB,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAyCtB,OAzCsB,CAAA,MAAA,CAAA;EACF,QAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAoFoC,OApFpC,CAoF4C,QApF5C,CAAA;EACM,MAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAgJ4B,OAhJ5B,CAAA,OAAA,CAAA;EACF,MAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAkK8B,OAlK9B,CAAA,IAAA,CAAA;;;;;;;;;;cC1BhB,oBAAkB,aAAA,CAAA,QAW7B,aAAA,CAX6B,MAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/providers/VercelBlobProvider.ts","../src/providers/VercelFileStorageProvider.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;cAEa,aAAA;cACC;eACC;cACD;;;;cCiBR,WAIJ,OAAA,CAJa;yBAIb,OAAA,CAAA;;;EDxBW,UAAA,GAAA,SC2BW,OD3BE,CC2BM,MD3BN,CAAA,OC2BoB,SD3BpB,CAAA,CAAA,CAAA,CACZ;;;;;cCgCD,yBAAA,YAAqC;0BAAX,cAAA,CACf;EAdlB,mBAIJ,GAAA,EAAA;IAJa,qBAAA,EAAA,MAAA;EAAA,CAAA;qBAAA,MAAA,EAgBY,MAhBZ;EAO+B,mBAAA,IAAA,EAUrB,gBAVqB;EAAd,mBAAA,UAAA,EAWD,kBAXC;EAAR,mBAAA,MAAA,EAYK,GAZL,CAAA,MAAA,CAAA;EAAO,mBAAA,aAAA,EAaG,aAbH;EAAA,mBAAA,eAAA,EAcK,mBAdL;EAAA,mBAAA,OAAA,EAcK,OAAA,CAER,cAhBG,CAAA,OAAA,CAAA;EAMlB,WAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAA0B;EAAA,UAAA,QACf,CAAA,CAAA,EAAA,MAAA;EAEG,MAAA,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EAuCjB,QAvCiB,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAyCtB,OAzCsB,CAAA,MAAA,CAAA;EACF,QAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAoFoC,OApFpC,CAoF4C,QApF5C,CAAA;EACM,MAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAgJ4B,OAhJ5B,CAAA,OAAA,CAAA;EACF,MAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAkK8B,OAlK9B,CAAA,IAAA,CAAA;;;;;;;;;;cCzBhB,oBAAkB,OAAA,CAAA,QAW7B,OAAA,CAX6B,MAAA"}
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { FileMetadataService, FileStorageProvider } from "@alepha/bucket";
2
- import * as _alepha_core1 from "@alepha/core";
3
- import { Alepha, FileLike, Static } from "@alepha/core";
4
- import { DateTimeProvider } from "@alepha/datetime";
5
- import { FileSystem } from "@alepha/file";
6
- import * as _alepha_logger0 from "@alepha/logger";
1
+ import * as alepha1 from "alepha";
2
+ import { Alepha, FileLike, Static } from "alepha";
3
+ import { FileMetadataService, FileStorageProvider } from "alepha/bucket";
4
+ import { DateTimeProvider } from "alepha/datetime";
5
+ import { FileSystemProvider } from "alepha/file";
6
+ import * as alepha_logger0 from "alepha/logger";
7
7
  import { del, head, put } from "@vercel/blob";
8
8
 
9
9
  //#region src/providers/VercelBlobProvider.d.ts
@@ -14,27 +14,27 @@ declare class VercelBlobApi {
14
14
  }
15
15
  //#endregion
16
16
  //#region src/providers/VercelFileStorageProvider.d.ts
17
- declare const envSchema: _alepha_core1.TObject<{
18
- BLOB_READ_WRITE_TOKEN: _alepha_core1.TString;
17
+ declare const envSchema: alepha1.TObject<{
18
+ BLOB_READ_WRITE_TOKEN: alepha1.TString;
19
19
  }>;
20
- declare module "@alepha/core" {
20
+ declare module "alepha" {
21
21
  interface Env extends Partial<Static<typeof envSchema>> {}
22
22
  }
23
23
  /**
24
24
  * Vercel Blob Storage implementation of File Storage Provider.
25
25
  */
26
26
  declare class VercelFileStorageProvider implements FileStorageProvider {
27
- protected readonly log: _alepha_logger0.Logger;
27
+ protected readonly log: alepha_logger0.Logger;
28
28
  protected readonly env: {
29
29
  BLOB_READ_WRITE_TOKEN: string;
30
30
  };
31
31
  protected readonly alepha: Alepha;
32
32
  protected readonly time: DateTimeProvider;
33
- protected readonly fileSystem: FileSystem;
33
+ protected readonly fileSystem: FileSystemProvider;
34
34
  protected readonly stores: Set<string>;
35
35
  protected readonly vercelBlobApi: VercelBlobApi;
36
36
  protected readonly metadataService: FileMetadataService;
37
- protected readonly onStart: _alepha_core1.HookDescriptor<"start">;
37
+ protected readonly onStart: alepha1.HookDescriptor<"start">;
38
38
  convertName(name: string): string;
39
39
  protected createId(): string;
40
40
  upload(bucketName: string, file: FileLike, fileId?: string): Promise<string>;
@@ -50,7 +50,7 @@ declare class VercelFileStorageProvider implements FileStorageProvider {
50
50
  * @see {@link VercelFileStorageProvider}
51
51
  * @module alepha.bucket.vercel
52
52
  */
53
- declare const AlephaBucketVercel: _alepha_core1.Service<_alepha_core1.Module>;
53
+ declare const AlephaBucketVercel: alepha1.Service<alepha1.Module>;
54
54
  //#endregion
55
55
  export { AlephaBucketVercel, VercelFileStorageProvider };
56
56
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/providers/VercelBlobProvider.ts","../src/providers/VercelFileStorageProvider.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;cAEa,aAAA;cACC;eACC;cACD;;;;cCkBR,WAIJ,aAAA,CAJa;yBAIb,aAAA,CAAA;;;EDzBW,UAAA,GAAA,SC4BW,OD5BE,CC4BM,MD5BN,CAAA,OC4BoB,SD5BpB,CAAA,CAAA,CAAA,CACZ;;;;;cCiCD,yBAAA,YAAqC;0BAAX,eAAA,CACf;EAdlB,mBAIJ,GAAA,EAAA;IAJa,qBAAA,EAAA,MAAA;EAAA,CAAA;qBAAA,MAAA,EAgBY,MAhBZ;EAO+B,mBAAA,IAAA,EAUrB,gBAVqB;EAAd,mBAAA,UAAA,EAWD,UAXC;EAAR,mBAAA,MAAA,EAYK,GAZL,CAAA,MAAA,CAAA;EAAO,mBAAA,aAAA,EAaG,aAbH;EAAA,mBAAA,eAAA,EAcK,mBAdL;EAAA,mBAAA,OAAA,EAcK,aAAA,CAER,cAhBG,CAAA,OAAA,CAAA;EAMlB,WAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAA0B;EAAA,UAAA,QACf,CAAA,CAAA,EAAA,MAAA;EAEG,MAAA,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EAuCjB,QAvCiB,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAyCtB,OAzCsB,CAAA,MAAA,CAAA;EACF,QAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAoFoC,OApFpC,CAoF4C,QApF5C,CAAA;EACM,MAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAgJ4B,OAhJ5B,CAAA,OAAA,CAAA;EACF,MAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAkK8B,OAlK9B,CAAA,IAAA,CAAA;;;;;;;;;;cC1BhB,oBAAkB,aAAA,CAAA,QAW7B,aAAA,CAX6B,MAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/providers/VercelBlobProvider.ts","../src/providers/VercelFileStorageProvider.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;cAEa,aAAA;cACC;eACC;cACD;;;;cCiBR,WAIJ,OAAA,CAJa;yBAIb,OAAA,CAAA;;;EDxBW,UAAA,GAAA,SC2BW,OD3BE,CC2BM,MD3BN,CAAA,OC2BoB,SD3BpB,CAAA,CAAA,CAAA,CACZ;;;;;cCgCD,yBAAA,YAAqC;0BAAX,cAAA,CACf;EAdlB,mBAIJ,GAAA,EAAA;IAJa,qBAAA,EAAA,MAAA;EAAA,CAAA;qBAAA,MAAA,EAgBY,MAhBZ;EAO+B,mBAAA,IAAA,EAUrB,gBAVqB;EAAd,mBAAA,UAAA,EAWD,kBAXC;EAAR,mBAAA,MAAA,EAYK,GAZL,CAAA,MAAA,CAAA;EAAO,mBAAA,aAAA,EAaG,aAbH;EAAA,mBAAA,eAAA,EAcK,mBAdL;EAAA,mBAAA,OAAA,EAcK,OAAA,CAER,cAhBG,CAAA,OAAA,CAAA;EAMlB,WAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAAA,MAA0B;EAAA,UAAA,QACf,CAAA,CAAA,EAAA,MAAA;EAEG,MAAA,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EAuCjB,QAvCiB,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAyCtB,OAzCsB,CAAA,MAAA,CAAA;EACF,QAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAoFoC,OApFpC,CAoF4C,QApF5C,CAAA;EACM,MAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAgJ4B,OAhJ5B,CAAA,OAAA,CAAA;EACF,MAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAkK8B,OAlK9B,CAAA,IAAA,CAAA;;;;;;;;;;cCzBhB,oBAAkB,OAAA,CAAA,QAW7B,OAAA,CAX6B,MAAA"}
package/dist/index.js CHANGED
@@ -1,9 +1,8 @@
1
- import { $bucket, AlephaBucket, FileMetadataService, FileNotFoundError, FileStorageProvider } from "@alepha/bucket";
2
- import { $env, $hook, $inject, $module, Alepha, AlephaError, t } from "@alepha/core";
3
- import { randomUUID } from "node:crypto";
4
- import { DateTimeProvider } from "@alepha/datetime";
5
- import { FileSystem } from "@alepha/file";
6
- import { $logger } from "@alepha/logger";
1
+ import { $env, $hook, $inject, $module, Alepha, AlephaError, t } from "alepha";
2
+ import { $bucket, AlephaBucket, FileMetadataService, FileNotFoundError, FileStorageProvider } from "alepha/bucket";
3
+ import { DateTimeProvider } from "alepha/datetime";
4
+ import { FileSystemProvider } from "alepha/file";
5
+ import { $logger } from "alepha/logger";
7
6
  import { del, head, put } from "@vercel/blob";
8
7
 
9
8
  //#region src/providers/VercelBlobProvider.ts
@@ -24,7 +23,7 @@ var VercelFileStorageProvider = class {
24
23
  env = $env(envSchema);
25
24
  alepha = $inject(Alepha);
26
25
  time = $inject(DateTimeProvider);
27
- fileSystem = $inject(FileSystem);
26
+ fileSystem = $inject(FileSystemProvider);
28
27
  stores = /* @__PURE__ */ new Set();
29
28
  vercelBlobApi = $inject(VercelBlobApi);
30
29
  metadataService = $inject(FileMetadataService);
@@ -44,7 +43,7 @@ var VercelFileStorageProvider = class {
44
43
  return name.replaceAll("/", "-").toLowerCase();
45
44
  }
46
45
  createId() {
47
- return randomUUID();
46
+ return crypto.randomUUID();
48
47
  }
49
48
  async upload(bucketName, file, fileId) {
50
49
  fileId ??= this.createId();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"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 { randomUUID } from \"node:crypto\";\nimport type { Readable } from \"node:stream\";\nimport {\n $bucket,\n FileMetadataService,\n FileNotFoundError,\n type FileStorageProvider,\n} from \"@alepha/bucket\";\nimport {\n $env,\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type FileLike,\n type Static,\n t,\n} from \"@alepha/core\";\nimport { DateTimeProvider } from \"@alepha/datetime\";\nimport { FileSystem } 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/core\" {\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(FileSystem);\n protected readonly stores: Set<string> = new Set();\n protected readonly vercelBlobApi = $inject(VercelBlobApi);\n protected readonly metadataService = $inject(FileMetadataService);\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(): string {\n return randomUUID();\n }\n\n public async upload(\n bucketName: string,\n file: FileLike,\n fileId?: string,\n ): Promise<string> {\n fileId ??= this.createId();\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 // Create a buffer with metadata and content\n const contentBuffer = Buffer.from(await file.arrayBuffer());\n const fileBuffer = this.metadataService.createFileBuffer(\n file,\n contentBuffer,\n );\n\n // Upload the complete buffer (metadata + content) to Vercel Blob\n const result = await this.vercelBlobApi.put(\n pathname,\n fileBuffer 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 // check if the file exists and get metadata\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 // fetch the actual file content (with metadata)\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 // Decode metadata from the buffer\n const buffer = Buffer.from(arrayBuffer);\n const { metadata, contentStart } =\n this.metadataService.decodeMetadataFromBuffer(buffer);\n\n // Extract the actual content\n const content = buffer.subarray(contentStart);\n\n return this.fileSystem.createFile({\n buffer: content,\n name: metadata.name,\n type: metadata.type,\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 { AlephaBucket, FileStorageProvider } from \"@alepha/bucket\";\nimport { $module } from \"@alepha/core\";\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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"}
1
+ {"version":3,"file":"index.js","names":[],"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 FileMetadataService,\n FileNotFoundError,\n type FileStorageProvider,\n} from \"alepha/bucket\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { 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 stores: Set<string> = new Set();\n protected readonly vercelBlobApi = $inject(VercelBlobApi);\n protected readonly metadataService = $inject(FileMetadataService);\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(): string {\n return crypto.randomUUID();\n }\n\n public async upload(\n bucketName: string,\n file: FileLike,\n fileId?: string,\n ): Promise<string> {\n fileId ??= this.createId();\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 // Create a buffer with metadata and content\n const contentBuffer = Buffer.from(await file.arrayBuffer());\n const fileBuffer = this.metadataService.createFileBuffer(\n file,\n contentBuffer,\n );\n\n // Upload the complete buffer (metadata + content) to Vercel Blob\n const result = await this.vercelBlobApi.put(\n pathname,\n fileBuffer 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 // check if the file exists and get metadata\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 // fetch the actual file content (with metadata)\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 // Decode metadata from the buffer\n const buffer = Buffer.from(arrayBuffer);\n const { metadata, contentStart } =\n this.metadataService.decodeMetadataFromBuffer(buffer);\n\n // Extract the actual content\n const content = buffer.subarray(contentStart);\n\n return this.fileSystem.createFile({\n buffer: content,\n name: metadata.name,\n type: metadata.type,\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":";;;;;;;;AAEA,IAAa,gBAAb,MAA2B;CACzB,MAAkB;CAClB,OAAoB;CACpB,MAAkB;;;;;ACiBpB,MAAM,YAAY,EAAE,OAAO,EACzB,uBAAuB,EAAE,KAAK,EAC5B,MAAM,QACP,CAAC,EACH,CAAC;;;;AASF,IAAa,4BAAb,MAAsE;CACpE,AAAmB,MAAM,SAAS;CAClC,AAAmB,MAAM,KAAK,UAAU;CACxC,AAAmB,SAAS,QAAQ,OAAO;CAC3C,AAAmB,OAAO,QAAQ,iBAAiB;CACnD,AAAmB,aAAa,QAAQ,mBAAmB;CAC3D,AAAmB,yBAAsB,IAAI,KAAK;CAClD,AAAmB,gBAAgB,QAAQ,cAAc;CACzD,AAAmB,kBAAkB,QAAQ,oBAAoB;CAEjE,AAAmB,UAAU,MAAM;EACjC,IAAI;EACJ,SAAS,YAAY;AACnB,QAAK,MAAM,UAAU,KAAK,OAAO,YAAY,QAAQ,EAAE;AACrD,QAAI,OAAO,aAAa,KACtB;IAGF,MAAM,YAAY,KAAK,YAAY,OAAO,KAAK;AAE/C,SAAK,IAAI,MAAM,kBAAkB,UAAU,OAAO;AAIlD,SAAK,OAAO,IAAI,UAAU;AAE1B,SAAK,IAAI,KAAK,iBAAiB,OAAO,KAAK,MAAM;;;EAGtD,CAAC;CAEF,AAAO,YAAY,MAAsB;AAEvC,SAAO,KAAK,WAAW,KAAK,IAAI,CAAC,aAAa;;CAGhD,AAAU,WAAmB;AAC3B,SAAO,OAAO,YAAY;;CAG5B,MAAa,OACX,YACA,MACA,QACiB;AACjB,aAAW,KAAK,UAAU;AAE1B,OAAK,IAAI,MACP,mBAAmB,KAAK,KAAK,eAAe,WAAW,aAAa,OAAO,MAC5E;EAGD,MAAM,WAAW,GADC,KAAK,YAAY,WAAW,CAChB,GAAG;AAEjC,MAAI;GAEF,MAAM,gBAAgB,OAAO,KAAK,MAAM,KAAK,aAAa,CAAC;GAC3D,MAAM,aAAa,KAAK,gBAAgB,iBACtC,MACA,cACD;GAGD,MAAM,SAAS,MAAM,KAAK,cAAc,IACtC,UACA,YACA;IACE,QAAQ;IACR,aAAa,KAAK,QAAQ;IAC1B,OAAO,KAAK,IAAI;IAChB,gBAAgB;IACjB,CACF;AAED,QAAK,IAAI,MAAM,+BAA+B,OAAO,MAAM;AAC3D,UAAO;WACA,OAAO;AACd,QAAK,IAAI,MAAM,0BAA0B,QAAQ;AACjD,OAAI,iBAAiB,MACnB,OAAM,IAAI,YAAY,kBAAkB,MAAM,WAAW,EACvD,OAAO,OACR,CAAC;AAGJ,SAAM;;;CAIV,MAAa,SAAS,YAAoB,QAAmC;AAC3E,OAAK,IAAI,MACP,qBAAqB,OAAO,iBAAiB,WAAW,MACzD;EAGD,MAAM,WAAW,GADC,KAAK,YAAY,WAAW,CAChB,GAAG;AAEjC,MAAI;GAEF,MAAM,aAAa,MAAM,KAAK,cAAc,KAAK,UAAU,EACzD,OAAO,KAAK,IAAI,uBACjB,CAAC;AAEF,OAAI,CAAC,WACH,OAAM,IAAI,kBACR,SAAS,OAAO,yBAAyB,WAAW,GACrD;GAIH,MAAM,WAAW,MAAM,MAAM,WAAW,IAAI;AAE5C,OAAI,CAAC,SAAS,GACZ,OAAM,IAAI,kBACR,yBAAyB,SAAS,aACnC;GAGH,MAAM,cAAc,MAAM,SAAS,aAAa;AAChD,OAAI,CAAC,YACH,OAAM,IAAI,kBAAkB,uCAAuC;GAIrE,MAAM,SAAS,OAAO,KAAK,YAAY;GACvC,MAAM,EAAE,UAAU,iBAChB,KAAK,gBAAgB,yBAAyB,OAAO;GAGvD,MAAM,UAAU,OAAO,SAAS,aAAa;AAE7C,UAAO,KAAK,WAAW,WAAW;IAChC,QAAQ;IACR,MAAM,SAAS;IACf,MAAM,SAAS;IAChB,CAAC;WACK,OAAO;AACd,OAAI,iBAAiB,kBACnB,OAAM;AAGR,QAAK,IAAI,MAAM,4BAA4B,QAAQ;AACnD,OAAI,iBAAiB,MACnB,OAAM,IAAI,kBAAkB,0BAA0B,EAAE,OAAO,OAAO,CAAC;AAGzE,SAAM;;;CAIV,MAAa,OAAO,YAAoB,QAAkC;AACxE,OAAK,IAAI,MACP,+BAA+B,OAAO,eAAe,WAAW,MACjE;EAGD,MAAM,WAAW,GADC,KAAK,YAAY,WAAW,CAChB,GAAG;AAEjC,MAAI;AAIF,UAHe,MAAM,KAAK,cAAc,KAAK,UAAU,EACrD,OAAO,KAAK,IAAI,uBACjB,CAAC,KACgB;WACX,OAAO;AAEd,UAAO;;;CAIX,MAAa,OAAO,YAAoB,QAA+B;AACrE,OAAK,IAAI,MAAM,kBAAkB,OAAO,iBAAiB,WAAW,MAAM;EAG1E,MAAM,WAAW,GADC,KAAK,YAAY,WAAW,CAChB,GAAG;AAEjC,MAAI;AACF,SAAM,KAAK,cAAc,IAAI,UAAU,EACrC,OAAO,KAAK,IAAI,uBACjB,CAAC;WACK,OAAO;AACd,QAAK,IAAI,MAAM,0BAA0B,QAAQ;AACjD,OAAI,iBAAiB,MACnB,OAAM,IAAI,kBAAkB,uBAAuB,EAAE,OAAO,OAAO,CAAC;AAEtE,SAAM;;;;;;;;;;;;;AC1MZ,MAAa,qBAAqB,QAAQ;CACxC,MAAM;CACN,UAAU,CAAC,0BAA0B;CACrC,WAAW,WACT,OACG,KAAK;EACJ,UAAU;EACV,SAAS;EACT,KAAK;EACN,CAAC,CACD,KAAK,aAAa;CACxB,CAAC"}
package/package.json CHANGED
@@ -1,59 +1,49 @@
1
1
  {
2
- "name": "@alepha/bucket-vercel",
3
- "description": "Vercel Blob Storage implementation for the bucket file storage.",
4
- "keywords": [
5
- "alepha",
6
- "bucket",
7
- "file-storage",
8
- "file",
9
- "vercel",
10
- "blob"
11
- ],
12
- "author": "Feunard",
13
- "version": "0.11.10",
14
- "type": "module",
15
- "engines": {
16
- "node": ">=22.0.0"
17
- },
18
- "license": "MIT",
19
- "main": "./dist/index.js",
20
- "types": "./dist/index.d.ts",
21
- "files": [
22
- "dist",
23
- "src"
24
- ],
25
- "dependencies": {
26
- "@alepha/bucket": "0.11.10",
27
- "@alepha/core": "0.11.10",
28
- "@alepha/datetime": "0.11.10",
29
- "@alepha/file": "0.11.10",
30
- "@alepha/logger": "0.11.10",
31
- "@vercel/blob": "^2.0.0"
32
- },
33
- "devDependencies": {
34
- "@biomejs/biome": "^2.3.5",
35
- "@types/node": "^24.10.1",
36
- "tsdown": "^0.16.4",
37
- "typescript": "^5.9.3",
38
- "vitest": "^4.0.9"
39
- },
40
- "scripts": {
41
- "test": "vitest run",
42
- "lint": "biome check --fix",
43
- "build": "tsdown -c=../../tsdown.config.ts",
44
- "typecheck": "tsc --noEmit"
45
- },
46
- "repository": {
47
- "type": "git",
48
- "url": "git+https://github.com/feunard/alepha.git"
49
- },
50
- "homepage": "https://github.com/feunard/alepha",
51
- "module": "./dist/index.js",
52
- "exports": {
53
- ".": {
54
- "types": "./dist/index.d.ts",
55
- "import": "./dist/index.js",
56
- "require": "./dist/index.cjs"
57
- }
58
- }
2
+ "name": "@alepha/bucket-vercel",
3
+ "description": "Vercel Blob Storage implementation for the bucket file storage.",
4
+ "keywords": [
5
+ "alepha",
6
+ "bucket",
7
+ "file-storage",
8
+ "file",
9
+ "vercel",
10
+ "blob"
11
+ ],
12
+ "author": "Feunard",
13
+ "version": "0.11.12",
14
+ "type": "module",
15
+ "engines": {
16
+ "node": ">=22.0.0"
17
+ },
18
+ "license": "MIT",
19
+ "main": "./dist/index.js",
20
+ "types": "./dist/index.d.ts",
21
+ "files": [
22
+ "dist",
23
+ "src"
24
+ ],
25
+ "dependencies": {
26
+ "@vercel/blob": "^2.0.0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^24.10.1",
30
+ "alepha": "0.11.12",
31
+ "tsdown": "^0.16.4",
32
+ "vitest": "^4.0.9"
33
+ },
34
+ "peerDependencies": {
35
+ "alepha": "0.11.12"
36
+ },
37
+ "scripts": {
38
+ "lint": "alepha lint",
39
+ "typecheck": "alepha typecheck",
40
+ "test": "alepha test",
41
+ "build": "tsdown -c=../../tsdown.config.ts"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/feunard/alepha.git"
46
+ },
47
+ "homepage": "https://github.com/feunard/alepha",
48
+ "module": "./dist/index.js"
59
49
  }
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { AlephaBucket, FileStorageProvider } from "@alepha/bucket";
2
- import { $module } from "@alepha/core";
1
+ import { $module } from "alepha";
2
+ import { AlephaBucket, FileStorageProvider } from "alepha/bucket";
3
3
  import { VercelFileStorageProvider } from "./providers/VercelFileStorageProvider.ts";
4
4
 
5
5
  // ---------------------------------------------------------------------------------------------------------------------
@@ -1,11 +1,4 @@
1
- import { randomUUID } from "node:crypto";
2
1
  import type { Readable } from "node:stream";
3
- import {
4
- $bucket,
5
- FileMetadataService,
6
- FileNotFoundError,
7
- type FileStorageProvider,
8
- } from "@alepha/bucket";
9
2
  import {
10
3
  $env,
11
4
  $hook,
@@ -15,10 +8,16 @@ import {
15
8
  type FileLike,
16
9
  type Static,
17
10
  t,
18
- } from "@alepha/core";
19
- import { DateTimeProvider } from "@alepha/datetime";
20
- import { FileSystem } from "@alepha/file";
21
- import { $logger } from "@alepha/logger";
11
+ } from "alepha";
12
+ import {
13
+ $bucket,
14
+ FileMetadataService,
15
+ FileNotFoundError,
16
+ type FileStorageProvider,
17
+ } from "alepha/bucket";
18
+ import { DateTimeProvider } from "alepha/datetime";
19
+ import { FileSystemProvider } from "alepha/file";
20
+ import { $logger } from "alepha/logger";
22
21
  import { VercelBlobApi } from "./VercelBlobProvider.ts";
23
22
 
24
23
  const envSchema = t.object({
@@ -27,7 +26,7 @@ const envSchema = t.object({
27
26
  }),
28
27
  });
29
28
 
30
- declare module "@alepha/core" {
29
+ declare module "alepha" {
31
30
  interface Env extends Partial<Static<typeof envSchema>> {}
32
31
  }
33
32
 
@@ -39,7 +38,7 @@ export class VercelFileStorageProvider implements FileStorageProvider {
39
38
  protected readonly env = $env(envSchema);
40
39
  protected readonly alepha = $inject(Alepha);
41
40
  protected readonly time = $inject(DateTimeProvider);
42
- protected readonly fileSystem = $inject(FileSystem);
41
+ protected readonly fileSystem = $inject(FileSystemProvider);
43
42
  protected readonly stores: Set<string> = new Set();
44
43
  protected readonly vercelBlobApi = $inject(VercelBlobApi);
45
44
  protected readonly metadataService = $inject(FileMetadataService);
@@ -71,7 +70,7 @@ export class VercelFileStorageProvider implements FileStorageProvider {
71
70
  }
72
71
 
73
72
  protected createId(): string {
74
- return randomUUID();
73
+ return crypto.randomUUID();
75
74
  }
76
75
 
77
76
  public async upload(