@alepha/bucket-vercel 0.13.1 → 0.13.2

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
@@ -11,7 +11,7 @@ import "node:fs";
11
11
 
12
12
  //#region ../alepha/src/core/constants/KIND.d.ts
13
13
  /**
14
- * Used for identifying descriptors.
14
+ * Used for identifying primitives.
15
15
  *
16
16
  * @internal
17
17
  */
@@ -64,34 +64,50 @@ interface ServiceSubstitution<T extends object = any> {
64
64
  */
65
65
  type ServiceEntry<T extends object = any> = Service<T> | ServiceSubstitution<T>;
66
66
  //#endregion
67
- //#region ../alepha/src/core/helpers/descriptor.d.ts
68
- interface DescriptorArgs<T extends object = {}> {
67
+ //#region ../alepha/src/core/helpers/primitive.d.ts
68
+ interface PrimitiveArgs<T extends object = {}> {
69
69
  options: T;
70
70
  alepha: Alepha;
71
71
  service: InstantiableClass<Service>;
72
72
  module?: Service;
73
73
  }
74
- interface DescriptorConfig {
74
+ interface PrimitiveConfig {
75
75
  propertyKey: string;
76
76
  service: InstantiableClass<Service>;
77
77
  module?: Service;
78
78
  }
79
- declare abstract class Descriptor<T extends object = {}> {
79
+ declare abstract class Primitive<T extends object = {}> {
80
80
  protected readonly alepha: Alepha;
81
81
  readonly options: T;
82
- readonly config: DescriptorConfig;
83
- constructor(args: DescriptorArgs<T>);
82
+ readonly config: PrimitiveConfig;
83
+ constructor(args: PrimitiveArgs<T>);
84
84
  /**
85
- * Called automatically by Alepha after the descriptor is created.
85
+ * Called automatically by Alepha after the primitive is created.
86
86
  */
87
87
  protected onInit(): void;
88
88
  }
89
- type DescriptorFactoryLike<T extends object = any> = {
89
+ type PrimitiveFactoryLike<T extends object = any> = {
90
90
  (options: T): any;
91
91
  [KIND]: any;
92
92
  };
93
93
  //#endregion
94
- //#region ../alepha/src/core/descriptors/$inject.d.ts
94
+ //#region ../alepha/src/core/interfaces/Async.d.ts
95
+ /**
96
+ * Represents a value that can be either a value or a promise of value.
97
+ */
98
+ type Async<T> = T | Promise<T>;
99
+ //#endregion
100
+ //#region ../alepha/src/core/interfaces/LoggerInterface.d.ts
101
+ type LogLevel = "ERROR" | "WARN" | "INFO" | "DEBUG" | "TRACE" | "SILENT";
102
+ interface LoggerInterface {
103
+ trace(message: string, data?: unknown): void;
104
+ debug(message: string, data?: unknown): void;
105
+ info(message: string, data?: unknown): void;
106
+ warn(message: string, data?: unknown): void;
107
+ error(message: string, data?: unknown): void;
108
+ }
109
+ //#endregion
110
+ //#region ../alepha/src/core/primitives/$inject.d.ts
95
111
  interface InjectOptions<T extends object = any> {
96
112
  /**
97
113
  * - 'transient' → Always a new instance on every inject. Zero caching.
@@ -116,8 +132,8 @@ interface InjectOptions<T extends object = any> {
116
132
  parent?: Service | null;
117
133
  }
118
134
  //#endregion
119
- //#region ../alepha/src/core/descriptors/$module.d.ts
120
- interface ModuleDescriptorOptions {
135
+ //#region ../alepha/src/core/primitives/$module.d.ts
136
+ interface ModulePrimitiveOptions {
121
137
  /**
122
138
  * Name of the module.
123
139
  *
@@ -132,9 +148,9 @@ interface ModuleDescriptorOptions {
132
148
  */
133
149
  services?: Array<Service>;
134
150
  /**
135
- * List of $descriptors to register in the module.
151
+ * List of $primitives to register in the module.
136
152
  */
137
- descriptors?: Array<DescriptorFactoryLike>;
153
+ primitives?: Array<PrimitiveFactoryLike>;
138
154
  /**
139
155
  * By default, module will register ALL services.
140
156
  * You can override this behavior by providing a register function.
@@ -148,7 +164,7 @@ interface ModuleDescriptorOptions {
148
164
  * Base class for all modules.
149
165
  */
150
166
  declare abstract class Module {
151
- abstract readonly options: ModuleDescriptorOptions;
167
+ abstract readonly options: ModulePrimitiveOptions;
152
168
  abstract register(alepha: Alepha): void;
153
169
  static NAME_REGEX: RegExp;
154
170
  /**
@@ -163,22 +179,6 @@ declare abstract class Module {
163
179
  static of(ctor: Service): Service<Module> | undefined;
164
180
  }
165
181
  //#endregion
166
- //#region ../alepha/src/core/interfaces/Async.d.ts
167
- /**
168
- * Represents a value that can be either a value or a promise of value.
169
- */
170
- type Async<T> = T | Promise<T>;
171
- //#endregion
172
- //#region ../alepha/src/core/interfaces/LoggerInterface.d.ts
173
- type LogLevel = "ERROR" | "WARN" | "INFO" | "DEBUG" | "TRACE" | "SILENT";
174
- interface LoggerInterface {
175
- trace(message: string, data?: unknown): void;
176
- debug(message: string, data?: unknown): void;
177
- info(message: string, data?: unknown): void;
178
- warn(message: string, data?: unknown): void;
179
- error(message: string, data?: unknown): void;
180
- }
181
- //#endregion
182
182
  //#region ../alepha/src/core/providers/AlsProvider.d.ts
183
183
  type AsyncLocalStorageData = any;
184
184
  declare class AlsProvider {
@@ -440,7 +440,7 @@ declare class EventManager {
440
440
  }): Promise<void>;
441
441
  }
442
442
  //#endregion
443
- //#region ../alepha/src/core/descriptors/$atom.d.ts
443
+ //#region ../alepha/src/core/primitives/$atom.d.ts
444
444
  type AtomOptions<T extends TAtomObject, N extends string> = {
445
445
  name: N;
446
446
  schema: T;
@@ -569,7 +569,7 @@ type OnlyArray<T extends object> = { [K in keyof T]: NonNullable<T[K]> extends A
569
569
  * // You can access the environment variables using alepha.env
570
570
  * console.log(alepha.env.MY_VAR); // "value"
571
571
  *
572
- * // But you should use $env() descriptor to get typed values from the environment.
572
+ * // But you should use $env() primitive to get typed values from the environment.
573
573
  * class App {
574
574
  * env = $env(
575
575
  * t.object({
@@ -582,7 +582,7 @@ type OnlyArray<T extends object> = { [K in keyof T]: NonNullable<T[K]> extends A
582
582
  * ### Modules
583
583
  *
584
584
  * Modules are a way to group services together.
585
- * You can register a module using the `$module` descriptor.
585
+ * You can register a module using the `$module` primitive.
586
586
  *
587
587
  * ```ts
588
588
  * import { $module } from "alepha";
@@ -600,7 +600,7 @@ type OnlyArray<T extends object> = { [K in keyof T]: NonNullable<T[K]> extends A
600
600
  * ### Hooks
601
601
  *
602
602
  * Hooks are a way to run async functions from all registered providers/services.
603
- * You can register a hook using the `$hook` descriptor.
603
+ * You can register a hook using the `$hook` primitive.
604
604
  *
605
605
  * ```ts
606
606
  * import { $hook } from "alepha";
@@ -697,9 +697,9 @@ declare class Alepha {
697
697
  use: Service;
698
698
  }>;
699
699
  /**
700
- * Registry of descriptors.
700
+ * Registry of primitives.
701
701
  */
702
- protected descriptorRegistry: Map<Service<Descriptor<{}>>, Descriptor<{}>[]>;
702
+ protected primitiveRegistry: Map<Service<Primitive<{}>>, Primitive<{}>[]>;
703
703
  /**
704
704
  * List of all services + how they are provided.
705
705
  */
@@ -719,7 +719,7 @@ declare class Alepha {
719
719
  /**
720
720
  * State manager to store arbitrary values.
721
721
  */
722
- get state(): StateManager<State>;
722
+ get store(): StateManager<State>;
723
723
  /**
724
724
  * Codec manager for encoding and decoding data with different formats.
725
725
  *
@@ -792,7 +792,7 @@ declare class Alepha {
792
792
  * Starts the App.
793
793
  *
794
794
  * - Lock any further changes to the container.
795
- * - Run "configure" hook for all services. Descriptors will be processed.
795
+ * - Run "configure" hook for all services. Primitives will be processed.
796
796
  * - Run "start" hook for all services. Providers will connect/listen/...
797
797
  * - Run "ready" hook for all services. This is the point where the App is ready to serve requests.
798
798
  *
@@ -897,13 +897,13 @@ declare class Alepha {
897
897
  }>;
898
898
  services<T extends object>(base: Service<T>): Array<T>;
899
899
  /**
900
- * Get all descriptors of the specified type.
900
+ * Get all primitives of the specified type.
901
901
  */
902
- descriptors<TDescriptor extends Descriptor>(factory: {
903
- [KIND]: InstantiableClass<TDescriptor>;
904
- } | string): Array<TDescriptor>;
902
+ primitives<TPrimitive extends Primitive>(factory: {
903
+ [KIND]: InstantiableClass<TPrimitive>;
904
+ } | string): Array<TPrimitive>;
905
905
  protected new<T extends object>(service: Service<T>, args?: any[]): T;
906
- protected processDescriptor(value: Descriptor, propertyKey?: string): void;
906
+ protected processPrimitive(value: Primitive, propertyKey?: string): void;
907
907
  }
908
908
  interface Hook<T extends keyof Hooks = any> {
909
909
  caller?: Service;
@@ -1030,34 +1030,6 @@ interface Hooks {
1030
1030
  };
1031
1031
  }
1032
1032
  //#endregion
1033
- //#region ../alepha/src/core/descriptors/$hook.d.ts
1034
- interface HookOptions<T extends keyof Hooks> {
1035
- /**
1036
- * The name of the hook. "configure", "start", "ready", "stop", ...
1037
- */
1038
- on: T;
1039
- /**
1040
- * The handler to run when the hook is triggered.
1041
- */
1042
- handler: (args: Hooks[T]) => Async<any>;
1043
- /**
1044
- * Force the hook to run first or last on the list of hooks.
1045
- */
1046
- priority?: "first" | "last";
1047
- /**
1048
- * Empty placeholder, not implemented yet. :-)
1049
- */
1050
- before?: object | Array<object>;
1051
- /**
1052
- * Empty placeholder, not implemented yet. :-)
1053
- */
1054
- after?: object | Array<object>;
1055
- }
1056
- declare class HookDescriptor<T extends keyof Hooks> extends Descriptor<HookOptions<T>> {
1057
- called: number;
1058
- protected onInit(): void;
1059
- }
1060
- //#endregion
1061
1033
  //#region ../alepha/src/core/schemas/pageSchema.d.ts
1062
1034
  declare const pageMetadataSchema: TObject$1<{
1063
1035
  number: TInteger;
@@ -1090,6 +1062,34 @@ declare module "alepha" {
1090
1062
  }
1091
1063
  }
1092
1064
  //#endregion
1065
+ //#region ../alepha/src/core/primitives/$hook.d.ts
1066
+ interface HookOptions<T extends keyof Hooks> {
1067
+ /**
1068
+ * The name of the hook. "configure", "start", "ready", "stop", ...
1069
+ */
1070
+ on: T;
1071
+ /**
1072
+ * The handler to run when the hook is triggered.
1073
+ */
1074
+ handler: (args: Hooks[T]) => Async<any>;
1075
+ /**
1076
+ * Force the hook to run first or last on the list of hooks.
1077
+ */
1078
+ priority?: "first" | "last";
1079
+ /**
1080
+ * Empty placeholder, not implemented yet. :-)
1081
+ */
1082
+ before?: object | Array<object>;
1083
+ /**
1084
+ * Empty placeholder, not implemented yet. :-)
1085
+ */
1086
+ after?: object | Array<object>;
1087
+ }
1088
+ declare class HookPrimitive<T extends keyof Hooks> extends Primitive<HookOptions<T>> {
1089
+ called: number;
1090
+ protected onInit(): void;
1091
+ }
1092
+ //#endregion
1093
1093
  //#region ../alepha/src/logger/schemas/logEntrySchema.d.ts
1094
1094
  declare const logEntrySchema: TObject$1<{
1095
1095
  level: TUnsafe<"SILENT" | "TRACE" | "DEBUG" | "INFO" | "WARN" | "ERROR">;
@@ -1114,8 +1114,8 @@ declare class DateTimeProvider {
1114
1114
  protected readonly timeouts: Timeout[];
1115
1115
  protected readonly intervals: Interval[];
1116
1116
  constructor();
1117
- protected readonly onStart: HookDescriptor<"start">;
1118
- protected readonly onStop: HookDescriptor<"stop">;
1117
+ protected readonly onStart: HookPrimitive<"start">;
1118
+ protected readonly onStop: HookPrimitive<"stop">;
1119
1119
  setLocale(locale: string): void;
1120
1120
  isDateTime(value: unknown): value is DateTime;
1121
1121
  /**
@@ -1733,8 +1733,8 @@ declare class MemoryFileStorageProvider implements FileStorageProvider {
1733
1733
  protected createId(): string;
1734
1734
  }
1735
1735
  //#endregion
1736
- //#region ../alepha/src/bucket/descriptors/$bucket.d.ts
1737
- interface BucketDescriptorOptions extends BucketFileOptions {
1736
+ //#region ../alepha/src/bucket/primitives/$bucket.d.ts
1737
+ interface BucketPrimitiveOptions extends BucketFileOptions {
1738
1738
  /**
1739
1739
  * File storage provider configuration for the bucket.
1740
1740
  *
@@ -1866,7 +1866,7 @@ interface BucketFileOptions {
1866
1866
  */
1867
1867
  maxSize?: number;
1868
1868
  }
1869
- declare class BucketDescriptor extends Descriptor<BucketDescriptorOptions> {
1869
+ declare class BucketPrimitive extends Primitive<BucketPrimitiveOptions> {
1870
1870
  readonly provider: FileStorageProvider | MemoryFileStorageProvider;
1871
1871
  private readonly fileSystem;
1872
1872
  get name(): string;
@@ -1929,7 +1929,7 @@ declare module "alepha" {
1929
1929
  "bucket:file:uploaded": {
1930
1930
  id: string;
1931
1931
  file: FileLike;
1932
- bucket: BucketDescriptor;
1932
+ bucket: BucketPrimitive;
1933
1933
  options: BucketFileOptions;
1934
1934
  };
1935
1935
  /**
@@ -1937,14 +1937,14 @@ declare module "alepha" {
1937
1937
  */
1938
1938
  "bucket:file:deleted": {
1939
1939
  id: string;
1940
- bucket: BucketDescriptor;
1940
+ bucket: BucketPrimitive;
1941
1941
  };
1942
1942
  }
1943
1943
  }
1944
1944
  /**
1945
- * Provides file storage capabilities through declarative bucket descriptors with support for multiple storage backends.
1945
+ * Provides file storage capabilities through declarative bucket primitives with support for multiple storage backends.
1946
1946
  *
1947
- * The bucket module enables unified file operations across different storage systems using the `$bucket` descriptor
1947
+ * The bucket module enables unified file operations across different storage systems using the `$bucket` primitive
1948
1948
  * on class properties. It abstracts storage provider differences, offering consistent APIs for local filesystem,
1949
1949
  * cloud storage, or in-memory storage for testing environments.
1950
1950
  *
@@ -1981,7 +1981,7 @@ declare class VercelFileStorageProvider implements FileStorageProvider {
1981
1981
  protected readonly fileDetector: FileDetector;
1982
1982
  protected readonly stores: Set<string>;
1983
1983
  protected readonly vercelBlobApi: VercelBlobApi;
1984
- protected readonly onStart: HookDescriptor<"start">;
1984
+ protected readonly onStart: HookPrimitive<"start">;
1985
1985
  convertName(name: string): string;
1986
1986
  protected createId(mimeType: string): string;
1987
1987
  upload(bucketName: string, file: FileLike, fileId?: string): Promise<string>;
package/dist/index.js CHANGED
@@ -30,7 +30,7 @@ var VercelFileStorageProvider = class {
30
30
  onStart = $hook({
31
31
  on: "start",
32
32
  handler: async () => {
33
- for (const bucket of this.alepha.descriptors($bucket)) {
33
+ for (const bucket of this.alepha.primitives($bucket)) {
34
34
  if (bucket.provider !== this) continue;
35
35
  const storeName = this.convertName(bucket.name);
36
36
  this.log.debug(`Prepare store '${storeName}' ...`);
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 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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"}
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 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.primitives($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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"}
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "blob"
11
11
  ],
12
12
  "author": "Nicolas Foures",
13
- "version": "0.13.1",
13
+ "version": "0.13.2",
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.13.1",
30
+ "alepha": "0.13.2",
31
31
  "tsdown": "^0.16.7",
32
32
  "vitest": "^4.0.14"
33
33
  },
34
34
  "peerDependencies": {
35
- "alepha": "0.13.1"
35
+ "alepha": "0.13.2"
36
36
  },
37
37
  "scripts": {
38
38
  "lint": "alepha lint",
@@ -45,7 +45,7 @@ export class VercelFileStorageProvider implements FileStorageProvider {
45
45
  protected readonly onStart = $hook({
46
46
  on: "start",
47
47
  handler: async () => {
48
- for (const bucket of this.alepha.descriptors($bucket)) {
48
+ for (const bucket of this.alepha.primitives($bucket)) {
49
49
  if (bucket.provider !== this) {
50
50
  continue;
51
51
  }