@alepha/bucket-azure 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
  *
@@ -1975,7 +1975,7 @@ declare class AzureFileStorageProvider implements FileStorageProvider {
1975
1975
  protected readonly blobServiceClient: BlobServiceClient;
1976
1976
  readonly options: StoragePipelineOptions;
1977
1977
  constructor();
1978
- protected readonly onStart: HookDescriptor<"start">;
1978
+ protected readonly onStart: HookPrimitive<"start">;
1979
1979
  convertName(name: string): string;
1980
1980
  upload(bucketName: string, file: FileLike, fileId?: string): Promise<string>;
1981
1981
  download(bucketName: string, fileId: string): Promise<FileLike>;
package/dist/index.js CHANGED
@@ -27,7 +27,7 @@ var AzureFileStorageProvider = class {
27
27
  onStart = $hook({
28
28
  on: "start",
29
29
  handler: async () => {
30
- for (const bucket of this.alepha.descriptors($bucket)) {
30
+ for (const bucket of this.alepha.primitives($bucket)) {
31
31
  if (bucket.provider !== this) continue;
32
32
  const containerName = this.convertName(bucket.name);
33
33
  this.log.debug(`Prepare container '${containerName}' ...`);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { Readable } from \"node:stream\";\nimport {\n BlobServiceClient,\n type BlockBlobClient,\n type ContainerClient,\n type StoragePipelineOptions,\n} from \"@azure/storage-blob\";\nimport {\n $env,\n $hook,\n $inject,\n Alepha,\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 { FileSystemProvider } from \"alepha/file\";\nimport { $logger } from \"alepha/logger\";\n\nconst envSchema = t.object({\n AZ_STORAGE_CONNECTION_STRING: t.string(),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\n/**\n * Azure Blog Storage implementation of File Storage Provider.\n */\nexport class AzureFileStorageProvider 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 containers: Record<string, ContainerClient> = {};\n protected readonly blobServiceClient: BlobServiceClient;\n\n public readonly options: StoragePipelineOptions = {};\n\n constructor() {\n this.blobServiceClient = BlobServiceClient.fromConnectionString(\n this.env.AZ_STORAGE_CONNECTION_STRING,\n this.options,\n );\n }\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 containerName = this.convertName(bucket.name);\n\n this.log.debug(`Prepare container '${containerName}' ...`);\n\n if (!this.containers[containerName]) {\n this.containers[containerName] =\n await this.createContainerClient(containerName);\n }\n\n this.log.info(`Container '${bucket.name}' OK`);\n }\n },\n });\n\n public convertName(name: string): string {\n // Azure Blob Storage does not allow uppercase letters in container names\n return name.replaceAll(\"/\", \"-\").toLowerCase();\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 block = this.getBlock(bucketName, fileId);\n\n const metadata = {\n name: file.name,\n type: file.type,\n };\n\n if (file.filepath) {\n await block.uploadFile(file.filepath, {\n metadata,\n blobHTTPHeaders: {\n blobContentType: file.type,\n },\n });\n } else if (file.size > 0) {\n await block.uploadData(await file.arrayBuffer(), {\n metadata,\n blobHTTPHeaders: {\n blobContentType: file.type,\n },\n });\n } else {\n await block.uploadStream(\n Readable.from(file.stream()),\n file.size || undefined,\n 5,\n {\n metadata,\n blobHTTPHeaders: {\n blobContentType: file.type,\n },\n },\n );\n }\n\n return fileId;\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 const block = this.getBlock(bucketName, fileId);\n\n const blob = await block.download().catch((error) => {\n if (error instanceof Error) {\n throw new FileNotFoundError(\"Error downloading file\", { cause: error });\n }\n\n throw error;\n });\n\n if (!blob.readableStreamBody) {\n throw new FileNotFoundError(\"File not found - empty stream body\");\n }\n\n return this.fileSystem.createFile({\n stream: blob.readableStreamBody,\n ...blob.metadata,\n size: blob.contentLength,\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 return await this.getBlock(bucketName, fileId).exists();\n }\n\n public async delete(bucketName: string, fileId: string): Promise<void> {\n this.log.trace(`Deleting file '${fileId}' from bucket '${bucketName}'...`);\n try {\n await this.getBlock(bucketName, fileId).delete();\n } catch (error) {\n if (error instanceof Error) {\n throw new FileNotFoundError(\"Error deleting file\", { cause: error });\n }\n throw error;\n }\n }\n\n public getBlock(container: string, fileId: string): BlockBlobClient {\n const containerName = this.convertName(container);\n\n if (!this.containers[containerName]) {\n throw new FileNotFoundError(\n `File '${fileId}' not found - container '${container}' does not exists`,\n );\n }\n\n return this.containers[containerName].getBlockBlobClient(fileId);\n }\n\n protected async createContainerClient(\n name: string,\n ): Promise<ContainerClient> {\n const container = this.blobServiceClient.getContainerClient(name);\n\n await this.time.deadline(\n (abortSignal) => container.createIfNotExists({ abortSignal }),\n [5, \"seconds\"],\n );\n\n return container;\n }\n\n protected createId(): string {\n return randomUUID();\n }\n}\n","import { $module } from \"alepha\";\nimport { AlephaBucket, FileStorageProvider } from \"alepha/bucket\";\nimport { AzureFileStorageProvider } from \"./providers/AzureFileStorageProvider.ts\";\n\nexport * from \"./providers/AzureFileStorageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Bucket that provides Azure Blob Storage capabilities.\n *\n * @see {@link AzureFileStorageProvider}\n * @module alepha.bucket.azure\n */\nexport const AlephaBucketAzure = $module({\n name: \"alepha.bucket.azure\",\n services: [AzureFileStorageProvider],\n register: (alepha) =>\n alepha\n .with({\n optional: true,\n provide: FileStorageProvider,\n use: AzureFileStorageProvider,\n })\n .with(AlephaBucket),\n});\n"],"mappings":";;;;;;;;;;AA0BA,MAAM,YAAY,EAAE,OAAO,EACzB,8BAA8B,EAAE,QAAQ,EACzC,CAAC;;;;AASF,IAAa,2BAAb,MAAqE;CACnE,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,aAA8C,EAAE;CACnE,AAAmB;CAEnB,AAAgB,UAAkC,EAAE;CAEpD,cAAc;AACZ,OAAK,oBAAoB,kBAAkB,qBACzC,KAAK,IAAI,8BACT,KAAK,QACN;;CAGH,AAAmB,UAAU,MAAM;EACjC,IAAI;EACJ,SAAS,YAAY;AACnB,QAAK,MAAM,UAAU,KAAK,OAAO,YAAY,QAAQ,EAAE;AACrD,QAAI,OAAO,aAAa,KACtB;IAGF,MAAM,gBAAgB,KAAK,YAAY,OAAO,KAAK;AAEnD,SAAK,IAAI,MAAM,sBAAsB,cAAc,OAAO;AAE1D,QAAI,CAAC,KAAK,WAAW,eACnB,MAAK,WAAW,iBACd,MAAM,KAAK,sBAAsB,cAAc;AAGnD,SAAK,IAAI,KAAK,cAAc,OAAO,KAAK,MAAM;;;EAGnD,CAAC;CAEF,AAAO,YAAY,MAAsB;AAEvC,SAAO,KAAK,WAAW,KAAK,IAAI,CAAC,aAAa;;CAGhD,MAAa,OACX,YACA,MACA,QACiB;AACjB,aAAW,KAAK,UAAU;AAE1B,OAAK,IAAI,MACP,mBAAmB,KAAK,KAAK,eAAe,WAAW,aAAa,OAAO,MAC5E;EAED,MAAM,QAAQ,KAAK,SAAS,YAAY,OAAO;EAE/C,MAAM,WAAW;GACf,MAAM,KAAK;GACX,MAAM,KAAK;GACZ;AAED,MAAI,KAAK,SACP,OAAM,MAAM,WAAW,KAAK,UAAU;GACpC;GACA,iBAAiB,EACf,iBAAiB,KAAK,MACvB;GACF,CAAC;WACO,KAAK,OAAO,EACrB,OAAM,MAAM,WAAW,MAAM,KAAK,aAAa,EAAE;GAC/C;GACA,iBAAiB,EACf,iBAAiB,KAAK,MACvB;GACF,CAAC;MAEF,OAAM,MAAM,aACV,SAAS,KAAK,KAAK,QAAQ,CAAC,EAC5B,KAAK,QAAQ,QACb,GACA;GACE;GACA,iBAAiB,EACf,iBAAiB,KAAK,MACvB;GACF,CACF;AAGH,SAAO;;CAGT,MAAa,SAAS,YAAoB,QAAmC;AAC3E,OAAK,IAAI,MACP,qBAAqB,OAAO,iBAAiB,WAAW,MACzD;EAGD,MAAM,OAAO,MAFC,KAAK,SAAS,YAAY,OAAO,CAEtB,UAAU,CAAC,OAAO,UAAU;AACnD,OAAI,iBAAiB,MACnB,OAAM,IAAI,kBAAkB,0BAA0B,EAAE,OAAO,OAAO,CAAC;AAGzE,SAAM;IACN;AAEF,MAAI,CAAC,KAAK,mBACR,OAAM,IAAI,kBAAkB,qCAAqC;AAGnE,SAAO,KAAK,WAAW,WAAW;GAChC,QAAQ,KAAK;GACb,GAAG,KAAK;GACR,MAAM,KAAK;GACZ,CAAC;;CAGJ,MAAa,OAAO,YAAoB,QAAkC;AACxE,OAAK,IAAI,MACP,+BAA+B,OAAO,eAAe,WAAW,MACjE;AACD,SAAO,MAAM,KAAK,SAAS,YAAY,OAAO,CAAC,QAAQ;;CAGzD,MAAa,OAAO,YAAoB,QAA+B;AACrE,OAAK,IAAI,MAAM,kBAAkB,OAAO,iBAAiB,WAAW,MAAM;AAC1E,MAAI;AACF,SAAM,KAAK,SAAS,YAAY,OAAO,CAAC,QAAQ;WACzC,OAAO;AACd,OAAI,iBAAiB,MACnB,OAAM,IAAI,kBAAkB,uBAAuB,EAAE,OAAO,OAAO,CAAC;AAEtE,SAAM;;;CAIV,AAAO,SAAS,WAAmB,QAAiC;EAClE,MAAM,gBAAgB,KAAK,YAAY,UAAU;AAEjD,MAAI,CAAC,KAAK,WAAW,eACnB,OAAM,IAAI,kBACR,SAAS,OAAO,2BAA2B,UAAU,mBACtD;AAGH,SAAO,KAAK,WAAW,eAAe,mBAAmB,OAAO;;CAGlE,MAAgB,sBACd,MAC0B;EAC1B,MAAM,YAAY,KAAK,kBAAkB,mBAAmB,KAAK;AAEjE,QAAM,KAAK,KAAK,UACb,gBAAgB,UAAU,kBAAkB,EAAE,aAAa,CAAC,EAC7D,CAAC,GAAG,UAAU,CACf;AAED,SAAO;;CAGT,AAAU,WAAmB;AAC3B,SAAO,YAAY;;;;;;;;;;;;AC3LvB,MAAa,oBAAoB,QAAQ;CACvC,MAAM;CACN,UAAU,CAAC,yBAAyB;CACpC,WAAW,WACT,OACG,KAAK;EACJ,UAAU;EACV,SAAS;EACT,KAAK;EACN,CAAC,CACD,KAAK,aAAa;CACxB,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { Readable } from \"node:stream\";\nimport {\n BlobServiceClient,\n type BlockBlobClient,\n type ContainerClient,\n type StoragePipelineOptions,\n} from \"@azure/storage-blob\";\nimport {\n $env,\n $hook,\n $inject,\n Alepha,\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 { FileSystemProvider } from \"alepha/file\";\nimport { $logger } from \"alepha/logger\";\n\nconst envSchema = t.object({\n AZ_STORAGE_CONNECTION_STRING: t.string(),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\n/**\n * Azure Blog Storage implementation of File Storage Provider.\n */\nexport class AzureFileStorageProvider 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 containers: Record<string, ContainerClient> = {};\n protected readonly blobServiceClient: BlobServiceClient;\n\n public readonly options: StoragePipelineOptions = {};\n\n constructor() {\n this.blobServiceClient = BlobServiceClient.fromConnectionString(\n this.env.AZ_STORAGE_CONNECTION_STRING,\n this.options,\n );\n }\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 containerName = this.convertName(bucket.name);\n\n this.log.debug(`Prepare container '${containerName}' ...`);\n\n if (!this.containers[containerName]) {\n this.containers[containerName] =\n await this.createContainerClient(containerName);\n }\n\n this.log.info(`Container '${bucket.name}' OK`);\n }\n },\n });\n\n public convertName(name: string): string {\n // Azure Blob Storage does not allow uppercase letters in container names\n return name.replaceAll(\"/\", \"-\").toLowerCase();\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 block = this.getBlock(bucketName, fileId);\n\n const metadata = {\n name: file.name,\n type: file.type,\n };\n\n if (file.filepath) {\n await block.uploadFile(file.filepath, {\n metadata,\n blobHTTPHeaders: {\n blobContentType: file.type,\n },\n });\n } else if (file.size > 0) {\n await block.uploadData(await file.arrayBuffer(), {\n metadata,\n blobHTTPHeaders: {\n blobContentType: file.type,\n },\n });\n } else {\n await block.uploadStream(\n Readable.from(file.stream()),\n file.size || undefined,\n 5,\n {\n metadata,\n blobHTTPHeaders: {\n blobContentType: file.type,\n },\n },\n );\n }\n\n return fileId;\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 const block = this.getBlock(bucketName, fileId);\n\n const blob = await block.download().catch((error) => {\n if (error instanceof Error) {\n throw new FileNotFoundError(\"Error downloading file\", { cause: error });\n }\n\n throw error;\n });\n\n if (!blob.readableStreamBody) {\n throw new FileNotFoundError(\"File not found - empty stream body\");\n }\n\n return this.fileSystem.createFile({\n stream: blob.readableStreamBody,\n ...blob.metadata,\n size: blob.contentLength,\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 return await this.getBlock(bucketName, fileId).exists();\n }\n\n public async delete(bucketName: string, fileId: string): Promise<void> {\n this.log.trace(`Deleting file '${fileId}' from bucket '${bucketName}'...`);\n try {\n await this.getBlock(bucketName, fileId).delete();\n } catch (error) {\n if (error instanceof Error) {\n throw new FileNotFoundError(\"Error deleting file\", { cause: error });\n }\n throw error;\n }\n }\n\n public getBlock(container: string, fileId: string): BlockBlobClient {\n const containerName = this.convertName(container);\n\n if (!this.containers[containerName]) {\n throw new FileNotFoundError(\n `File '${fileId}' not found - container '${container}' does not exists`,\n );\n }\n\n return this.containers[containerName].getBlockBlobClient(fileId);\n }\n\n protected async createContainerClient(\n name: string,\n ): Promise<ContainerClient> {\n const container = this.blobServiceClient.getContainerClient(name);\n\n await this.time.deadline(\n (abortSignal) => container.createIfNotExists({ abortSignal }),\n [5, \"seconds\"],\n );\n\n return container;\n }\n\n protected createId(): string {\n return randomUUID();\n }\n}\n","import { $module } from \"alepha\";\nimport { AlephaBucket, FileStorageProvider } from \"alepha/bucket\";\nimport { AzureFileStorageProvider } from \"./providers/AzureFileStorageProvider.ts\";\n\nexport * from \"./providers/AzureFileStorageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Bucket that provides Azure Blob Storage capabilities.\n *\n * @see {@link AzureFileStorageProvider}\n * @module alepha.bucket.azure\n */\nexport const AlephaBucketAzure = $module({\n name: \"alepha.bucket.azure\",\n services: [AzureFileStorageProvider],\n register: (alepha) =>\n alepha\n .with({\n optional: true,\n provide: FileStorageProvider,\n use: AzureFileStorageProvider,\n })\n .with(AlephaBucket),\n});\n"],"mappings":"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"}
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "storage-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"
@@ -26,12 +26,12 @@
26
26
  "@azure/storage-blob": "^12.29.1"
27
27
  },
28
28
  "devDependencies": {
29
- "alepha": "0.13.1",
29
+ "alepha": "0.13.2",
30
30
  "tsdown": "^0.16.7",
31
31
  "vitest": "^4.0.14"
32
32
  },
33
33
  "peerDependencies": {
34
- "alepha": "0.13.1"
34
+ "alepha": "0.13.2"
35
35
  },
36
36
  "scripts": {
37
37
  "lint": "alepha lint",
@@ -56,7 +56,7 @@ export class AzureFileStorageProvider implements FileStorageProvider {
56
56
  protected readonly onStart = $hook({
57
57
  on: "start",
58
58
  handler: async () => {
59
- for (const bucket of this.alepha.descriptors($bucket)) {
59
+ for (const bucket of this.alepha.primitives($bucket)) {
60
60
  if (bucket.provider !== this) {
61
61
  continue;
62
62
  }