@alepha/bucket-azure 0.8.1 → 0.9.0

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.cjs CHANGED
@@ -22,8 +22,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
 
23
23
  //#endregion
24
24
  const __alepha_bucket = __toESM(require("@alepha/bucket"));
25
- const node_crypto = __toESM(require("node:crypto"));
26
25
  const __alepha_core = __toESM(require("@alepha/core"));
26
+ const node_crypto = __toESM(require("node:crypto"));
27
+ const node_stream = __toESM(require("node:stream"));
27
28
  const __alepha_datetime = __toESM(require("@alepha/datetime"));
28
29
  const __alepha_file = __toESM(require("@alepha/file"));
29
30
  const __azure_storage_blob = __toESM(require("@azure/storage-blob"));
@@ -35,8 +36,8 @@ const envSchema = __alepha_core.t.object({ AZ_STORAGE_CONNECTION_STRING: __aleph
35
36
  */
36
37
  var AzureFileStorageProvider = class {
37
38
  log = (0, __alepha_core.$logger)();
38
- env = (0, __alepha_core.$inject)(envSchema);
39
- bucket = (0, __alepha_core.$inject)(__alepha_bucket.BucketDescriptorProvider);
39
+ env = (0, __alepha_core.$env)(envSchema);
40
+ alepha = (0, __alepha_core.$inject)(__alepha_core.Alepha);
40
41
  time = (0, __alepha_core.$inject)(__alepha_datetime.DateTimeProvider);
41
42
  containers = {};
42
43
  blobServiceClient;
@@ -44,6 +45,18 @@ var AzureFileStorageProvider = class {
44
45
  constructor() {
45
46
  this.blobServiceClient = __azure_storage_blob.BlobServiceClient.fromConnectionString(this.env.AZ_STORAGE_CONNECTION_STRING, this.options);
46
47
  }
48
+ onStart = (0, __alepha_core.$hook)({
49
+ on: "start",
50
+ handler: async () => {
51
+ for (const bucket of this.alepha.descriptors(__alepha_bucket.$bucket)) {
52
+ if (bucket.provider !== this) continue;
53
+ const containerName = bucket.name.replaceAll("/", "-").toLowerCase();
54
+ this.log.debug(`Prepare container ${containerName}...`);
55
+ if (!this.containers[containerName]) this.containers[containerName] = await this.createContainerClient(containerName);
56
+ this.log.info(`Container ${bucket} OK`);
57
+ }
58
+ }
59
+ });
47
60
  async createContainer(containerName) {
48
61
  if (this.containers[containerName]) return this.containers[containerName];
49
62
  const container = await this.createContainerClient(containerName);
@@ -65,7 +78,10 @@ var AzureFileStorageProvider = class {
65
78
  metadata,
66
79
  blobHTTPHeaders: { blobContentType: file.type }
67
80
  });
68
- else throw new Error("Raw stream upload is not supported yet");
81
+ else await block.uploadStream(node_stream.Readable.from(file.stream()), file.size || void 0, 5, {
82
+ metadata,
83
+ blobHTTPHeaders: { blobContentType: file.type }
84
+ });
69
85
  return fileId;
70
86
  }
71
87
  async download(bucketName, fileId) {
@@ -75,7 +91,10 @@ var AzureFileStorageProvider = class {
75
91
  throw error;
76
92
  });
77
93
  if (!blob.readableStreamBody) throw new __alepha_bucket.FileNotFoundError("File not found - empty stream body");
78
- return (0, __alepha_file.createFile)(blob.readableStreamBody, blob.metadata);
94
+ return (0, __alepha_file.createFile)(blob.readableStreamBody, {
95
+ ...blob.metadata,
96
+ size: blob.contentLength
97
+ });
79
98
  }
80
99
  async exists(bucketName, fileId) {
81
100
  return await this.getBlock(bucketName, fileId).exists();
@@ -92,17 +111,6 @@ var AzureFileStorageProvider = class {
92
111
  if (!this.containers[container]) throw new __alepha_bucket.FileNotFoundError(`File '${fileId}' not found - container '${container}' does not exists`);
93
112
  return this.containers[container].getBlockBlobClient(fileId);
94
113
  }
95
- onStart = (0, __alepha_core.$hook)({
96
- on: "start",
97
- handler: async () => {
98
- for (const bucket of this.bucket.getBuckets()) {
99
- const containerName = bucket.name.replaceAll("/", "-").toLowerCase();
100
- this.log.debug(`Prepare container ${containerName}...`);
101
- if (!this.containers[containerName]) this.containers[containerName] = await this.createContainerClient(containerName);
102
- this.log.info(`Container ${bucket} OK`);
103
- }
104
- }
105
- });
106
114
  async createContainerClient(name) {
107
115
  const container = this.blobServiceClient.getContainerClient(name);
108
116
  await this.time.deadline((abortSignal) => container.createIfNotExists({ abortSignal }), [5, "seconds"]);
@@ -121,16 +129,15 @@ var AzureFileStorageProvider = class {
121
129
  * @see {@link AzureFileStorageProvider}
122
130
  * @module alepha.bucket.azure
123
131
  */
124
- var AlephaBucketAzure = class {
125
- name = "alepha.bucket.azure";
126
- $services = (alepha) => {
127
- alepha.with({
128
- provide: __alepha_bucket.FileStorageProvider,
129
- use: AzureFileStorageProvider,
130
- optional: true
131
- }).with(__alepha_bucket.AlephaBucket);
132
- };
133
- };
132
+ const AlephaBucketAzure = (0, __alepha_core.$module)({
133
+ name: "alepha.bucket.azure",
134
+ services: [AzureFileStorageProvider],
135
+ register: (alepha) => alepha.with({
136
+ optional: true,
137
+ provide: __alepha_bucket.FileStorageProvider,
138
+ use: AzureFileStorageProvider
139
+ }).with(__alepha_bucket.AlephaBucket)
140
+ });
134
141
 
135
142
  //#endregion
136
143
  exports.AlephaBucketAzure = AlephaBucketAzure;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["envSchema: TObject<{\n\tAZ_STORAGE_CONNECTION_STRING: TString;\n}>","BucketDescriptorProvider","DateTimeProvider","containerName: string","bucketName: string","file: FileLike","fileId?: string","fileId: string","FileNotFoundError","container: string","name: string","alepha: Alepha","FileStorageProvider","AlephaBucket"],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n\tBucketDescriptorProvider,\n\tFileNotFoundError,\n\ttype FileStorageProvider,\n} from \"@alepha/bucket\";\nimport {\n\t$hook,\n\t$inject,\n\t$logger,\n\ttype FileLike,\n\ttype HookDescriptor,\n\ttype Logger,\n\ttype Static,\n\ttype TObject,\n\ttype TString,\n\tt,\n} from \"@alepha/core\";\nimport { DateTimeProvider } from \"@alepha/datetime\";\nimport { createFile } from \"@alepha/file\";\nimport {\n\tBlobServiceClient,\n\ttype BlockBlobClient,\n\ttype ContainerClient,\n\ttype StoragePipelineOptions,\n} from \"@azure/storage-blob\";\n\nconst envSchema: TObject<{\n\tAZ_STORAGE_CONNECTION_STRING: TString;\n}> = t.object({\n\tAZ_STORAGE_CONNECTION_STRING: t.string({\n\t\tsize: \"long\",\n\t}),\n});\n\ndeclare module \"@alepha/core\" {\n\tinterface 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\tprotected readonly log: Logger = $logger();\n\tprotected readonly env: Static<typeof envSchema> = $inject(envSchema);\n\tprotected readonly bucket: BucketDescriptorProvider = $inject(\n\t\tBucketDescriptorProvider,\n\t);\n\tprotected readonly time: DateTimeProvider = $inject(DateTimeProvider);\n\tprotected readonly containers: Record<string, ContainerClient> = {};\n\tprotected readonly blobServiceClient: BlobServiceClient;\n\n\tpublic readonly options: StoragePipelineOptions = {};\n\n\tconstructor() {\n\t\tthis.blobServiceClient = BlobServiceClient.fromConnectionString(\n\t\t\tthis.env.AZ_STORAGE_CONNECTION_STRING,\n\t\t\tthis.options,\n\t\t);\n\t}\n\n\tpublic async createContainer(\n\t\tcontainerName: string,\n\t): Promise<ContainerClient> {\n\t\tif (this.containers[containerName]) {\n\t\t\treturn this.containers[containerName];\n\t\t}\n\t\tconst container = await this.createContainerClient(containerName);\n\t\tthis.containers[containerName] = container;\n\t\treturn container;\n\t}\n\n\tpublic async upload(\n\t\tbucketName: string,\n\t\tfile: FileLike,\n\t\tfileId?: string,\n\t): Promise<string> {\n\t\tfileId ??= this.createId();\n\t\tconst block = this.getBlock(bucketName, fileId);\n\n\t\tconst metadata = {\n\t\t\tname: file.name,\n\t\t\ttype: file.type,\n\t\t};\n\n\t\tif (file.filepath) {\n\t\t\tawait block.uploadFile(file.filepath, {\n\t\t\t\tmetadata,\n\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t},\n\t\t\t});\n\t\t} else if (file.size > 0) {\n\t\t\tawait block.uploadData(await file.arrayBuffer(), {\n\t\t\t\tmetadata,\n\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t},\n\t\t\t});\n\t\t} else {\n\t\t\tthrow new Error(\"Raw stream upload is not supported yet\");\n\t\t}\n\n\t\treturn fileId;\n\t}\n\n\tpublic async download(bucketName: string, fileId: string): Promise<FileLike> {\n\t\tconst block = this.getBlock(bucketName, fileId);\n\n\t\tconst blob = await block.download().catch((error) => {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new FileNotFoundError(\"Error downloading file\", { cause: error });\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t});\n\n\t\tif (!blob.readableStreamBody) {\n\t\t\tthrow new FileNotFoundError(\"File not found - empty stream body\");\n\t\t}\n\n\t\treturn createFile(blob.readableStreamBody, blob.metadata);\n\t}\n\n\tpublic async exists(bucketName: string, fileId: string): Promise<boolean> {\n\t\treturn await this.getBlock(bucketName, fileId).exists();\n\t}\n\n\tpublic async delete(bucketName: string, fileId: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.getBlock(bucketName, fileId).delete();\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new FileNotFoundError(\"Error deleting file\", { cause: error });\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic getBlock(container: string, fileId: string): BlockBlobClient {\n\t\tif (!this.containers[container]) {\n\t\t\tthrow new FileNotFoundError(\n\t\t\t\t`File '${fileId}' not found - container '${container}' does not exists`,\n\t\t\t);\n\t\t}\n\n\t\treturn this.containers[container].getBlockBlobClient(fileId);\n\t}\n\n\tpublic readonly onStart: HookDescriptor<\"start\"> = $hook({\n\t\ton: \"start\",\n\t\thandler: async () => {\n\t\t\tfor (const bucket of this.bucket.getBuckets()) {\n\t\t\t\tconst containerName = bucket.name.replaceAll(\"/\", \"-\").toLowerCase();\n\t\t\t\tthis.log.debug(`Prepare container ${containerName}...`);\n\n\t\t\t\tif (!this.containers[containerName]) {\n\t\t\t\t\tthis.containers[containerName] =\n\t\t\t\t\t\tawait this.createContainerClient(containerName);\n\t\t\t\t}\n\n\t\t\t\tthis.log.info(`Container ${bucket} OK`);\n\t\t\t}\n\t\t},\n\t});\n\n\tprotected async createContainerClient(\n\t\tname: string,\n\t): Promise<ContainerClient> {\n\t\tconst container = this.blobServiceClient.getContainerClient(name);\n\n\t\tawait this.time.deadline(\n\t\t\t(abortSignal) => container.createIfNotExists({ abortSignal }),\n\t\t\t[5, \"seconds\"],\n\t\t);\n\n\t\treturn container;\n\t}\n\n\tprotected createId(): string {\n\t\treturn randomUUID();\n\t}\n}\n","import { AlephaBucket, FileStorageProvider } from \"@alepha/bucket\";\nimport type { Alepha, Module } from \"@alepha/core\";\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 class AlephaBucketAzure implements Module {\n\tpublic readonly name = \"alepha.bucket.azure\";\n\tpublic readonly $services = (alepha: Alepha): void => {\n\t\talepha\n\t\t\t.with({\n\t\t\t\tprovide: FileStorageProvider,\n\t\t\t\tuse: AzureFileStorageProvider,\n\t\t\t\toptional: true,\n\t\t\t})\n\t\t\t.with(AlephaBucket);\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["Alepha","DateTimeProvider","$bucket","containerName: string","bucketName: string","file: FileLike","fileId?: string","fileId: string","FileNotFoundError","container: string","name: string","FileStorageProvider","AlephaBucket"],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { Readable } from \"node:stream\";\nimport {\n\t$bucket,\n\tFileNotFoundError,\n\ttype FileStorageProvider,\n} from \"@alepha/bucket\";\nimport {\n\t$env,\n\t$hook,\n\t$inject,\n\t$logger,\n\tAlepha,\n\ttype FileLike,\n\ttype Static,\n\tt,\n} from \"@alepha/core\";\nimport { DateTimeProvider } from \"@alepha/datetime\";\nimport { createFile } from \"@alepha/file\";\nimport {\n\tBlobServiceClient,\n\ttype BlockBlobClient,\n\ttype ContainerClient,\n\ttype StoragePipelineOptions,\n} from \"@azure/storage-blob\";\n\nconst envSchema = t.object({\n\tAZ_STORAGE_CONNECTION_STRING: t.string({\n\t\tsize: \"long\",\n\t}),\n});\n\ndeclare module \"@alepha/core\" {\n\tinterface 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\tprotected readonly log = $logger();\n\tprotected readonly env = $env(envSchema);\n\tprotected readonly alepha = $inject(Alepha);\n\tprotected readonly time = $inject(DateTimeProvider);\n\tprotected readonly containers: Record<string, ContainerClient> = {};\n\tprotected readonly blobServiceClient: BlobServiceClient;\n\n\tpublic readonly options: StoragePipelineOptions = {};\n\n\tconstructor() {\n\t\tthis.blobServiceClient = BlobServiceClient.fromConnectionString(\n\t\t\tthis.env.AZ_STORAGE_CONNECTION_STRING,\n\t\t\tthis.options,\n\t\t);\n\t}\n\n\tprotected readonly onStart = $hook({\n\t\ton: \"start\",\n\t\thandler: async () => {\n\t\t\tfor (const bucket of this.alepha.descriptors($bucket)) {\n\t\t\t\tif (bucket.provider !== this) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst containerName = bucket.name.replaceAll(\"/\", \"-\").toLowerCase();\n\t\t\t\tthis.log.debug(`Prepare container ${containerName}...`);\n\n\t\t\t\tif (!this.containers[containerName]) {\n\t\t\t\t\tthis.containers[containerName] =\n\t\t\t\t\t\tawait this.createContainerClient(containerName);\n\t\t\t\t}\n\n\t\t\t\tthis.log.info(`Container ${bucket} OK`);\n\t\t\t}\n\t\t},\n\t});\n\n\tpublic async createContainer(\n\t\tcontainerName: string,\n\t): Promise<ContainerClient> {\n\t\tif (this.containers[containerName]) {\n\t\t\treturn this.containers[containerName];\n\t\t}\n\t\tconst container = await this.createContainerClient(containerName);\n\t\tthis.containers[containerName] = container;\n\t\treturn container;\n\t}\n\n\tpublic async upload(\n\t\tbucketName: string,\n\t\tfile: FileLike,\n\t\tfileId?: string,\n\t): Promise<string> {\n\t\tfileId ??= this.createId();\n\t\tconst block = this.getBlock(bucketName, fileId);\n\n\t\tconst metadata = {\n\t\t\tname: file.name,\n\t\t\ttype: file.type,\n\t\t};\n\n\t\tif (file.filepath) {\n\t\t\tawait block.uploadFile(file.filepath, {\n\t\t\t\tmetadata,\n\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t},\n\t\t\t});\n\t\t} else if (file.size > 0) {\n\t\t\tawait block.uploadData(await file.arrayBuffer(), {\n\t\t\t\tmetadata,\n\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t},\n\t\t\t});\n\t\t} else {\n\t\t\tawait block.uploadStream(\n\t\t\t\tReadable.from(file.stream()),\n\t\t\t\tfile.size || undefined,\n\t\t\t\t5,\n\t\t\t\t{\n\t\t\t\t\tmetadata,\n\t\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\treturn fileId;\n\t}\n\n\tpublic async download(bucketName: string, fileId: string): Promise<FileLike> {\n\t\tconst block = this.getBlock(bucketName, fileId);\n\n\t\tconst blob = await block.download().catch((error) => {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new FileNotFoundError(\"Error downloading file\", { cause: error });\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t});\n\n\t\tif (!blob.readableStreamBody) {\n\t\t\tthrow new FileNotFoundError(\"File not found - empty stream body\");\n\t\t}\n\n\t\treturn createFile(blob.readableStreamBody, {\n\t\t\t...blob.metadata,\n\t\t\tsize: blob.contentLength,\n\t\t});\n\t}\n\n\tpublic async exists(bucketName: string, fileId: string): Promise<boolean> {\n\t\treturn await this.getBlock(bucketName, fileId).exists();\n\t}\n\n\tpublic async delete(bucketName: string, fileId: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.getBlock(bucketName, fileId).delete();\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new FileNotFoundError(\"Error deleting file\", { cause: error });\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic getBlock(container: string, fileId: string): BlockBlobClient {\n\t\tif (!this.containers[container]) {\n\t\t\tthrow new FileNotFoundError(\n\t\t\t\t`File '${fileId}' not found - container '${container}' does not exists`,\n\t\t\t);\n\t\t}\n\n\t\treturn this.containers[container].getBlockBlobClient(fileId);\n\t}\n\n\tprotected async createContainerClient(\n\t\tname: string,\n\t): Promise<ContainerClient> {\n\t\tconst container = this.blobServiceClient.getContainerClient(name);\n\n\t\tawait this.time.deadline(\n\t\t\t(abortSignal) => container.createIfNotExists({ abortSignal }),\n\t\t\t[5, \"seconds\"],\n\t\t);\n\n\t\treturn container;\n\t}\n\n\tprotected createId(): string {\n\t\treturn randomUUID();\n\t}\n}\n","import { AlephaBucket, FileStorageProvider } from \"@alepha/bucket\";\nimport { $module } from \"@alepha/core\";\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\tname: \"alepha.bucket.azure\",\n\tservices: [AzureFileStorageProvider],\n\tregister: (alepha) =>\n\t\talepha\n\t\t\t.with({\n\t\t\t\toptional: true,\n\t\t\t\tprovide: FileStorageProvider,\n\t\t\t\tuse: AzureFileStorageProvider,\n\t\t\t})\n\t\t\t.with(AlephaBucket),\n});\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,50 +1,52 @@
1
- import { Alepha, FileLike, HookDescriptor, Logger, Module, Static, TObject, TString } from "@alepha/core";
2
- import { BucketDescriptorProvider, FileStorageProvider } from "@alepha/bucket";
1
+ import * as _alepha_core0$1 from "@alepha/core";
2
+ import * as _alepha_core0 from "@alepha/core";
3
+ import { Alepha, FileLike, Static } from "@alepha/core";
4
+ import { FileStorageProvider } from "@alepha/bucket";
3
5
  import { DateTimeProvider } from "@alepha/datetime";
4
6
  import { BlobServiceClient, BlockBlobClient, ContainerClient, StoragePipelineOptions } from "@azure/storage-blob";
5
7
 
6
8
  //#region src/providers/AzureFileStorageProvider.d.ts
7
- declare const envSchema: TObject<{
8
- AZ_STORAGE_CONNECTION_STRING: TString;
9
+ declare const envSchema: _alepha_core0$1.TObject<{
10
+ AZ_STORAGE_CONNECTION_STRING: _alepha_core0$1.TString;
9
11
  }>;
10
12
  declare module "@alepha/core" {
11
13
  interface Env extends Partial<Static<typeof envSchema>> {}
12
14
  }
13
15
  /**
14
- * Azure Blog Storage implementation of File Storage Provider.
15
- */
16
+ * Azure Blog Storage implementation of File Storage Provider.
17
+ */
16
18
  declare class AzureFileStorageProvider implements FileStorageProvider {
17
- protected readonly log: Logger;
18
- protected readonly env: Static<typeof envSchema>;
19
- protected readonly bucket: BucketDescriptorProvider;
19
+ protected readonly log: _alepha_core0$1.Logger;
20
+ protected readonly env: {
21
+ AZ_STORAGE_CONNECTION_STRING: string;
22
+ };
23
+ protected readonly alepha: Alepha;
20
24
  protected readonly time: DateTimeProvider;
21
25
  protected readonly containers: Record<string, ContainerClient>;
22
26
  protected readonly blobServiceClient: BlobServiceClient;
23
27
  readonly options: StoragePipelineOptions;
24
28
  constructor();
29
+ protected readonly onStart: _alepha_core0$1.HookDescriptor<"start">;
25
30
  createContainer(containerName: string): Promise<ContainerClient>;
26
31
  upload(bucketName: string, file: FileLike, fileId?: string): Promise<string>;
27
32
  download(bucketName: string, fileId: string): Promise<FileLike>;
28
33
  exists(bucketName: string, fileId: string): Promise<boolean>;
29
34
  delete(bucketName: string, fileId: string): Promise<void>;
30
35
  getBlock(container: string, fileId: string): BlockBlobClient;
31
- readonly onStart: HookDescriptor<"start">;
32
36
  protected createContainerClient(name: string): Promise<ContainerClient>;
33
37
  protected createId(): string;
34
38
  }
35
39
  //#endregion
36
40
  //#region src/index.d.ts
37
- // ---------------------------------------------------------------------------------------------------------------------
38
41
  /**
39
- * Plugin for Alepha Bucket that provides Azure Blob Storage capabilities.
40
- *
41
- * @see {@link AzureFileStorageProvider}
42
- * @module alepha.bucket.azure
43
- */
44
- declare class AlephaBucketAzure implements Module {
45
- readonly name = "alepha.bucket.azure";
46
- readonly $services: (alepha: Alepha) => void;
47
- }
42
+ * Plugin for Alepha Bucket that provides Azure Blob Storage capabilities.
43
+ *
44
+ * @see {@link AzureFileStorageProvider}
45
+ * @module alepha.bucket.azure
46
+ */
47
+ declare const AlephaBucketAzure: _alepha_core0.ModuleDescriptor;
48
+ //# sourceMappingURL=index.d.ts.map
49
+
48
50
  //#endregion
49
51
  export { AlephaBucketAzure, AzureFileStorageProvider };
50
52
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":["envSchema: TObject<{\n\tAZ_STORAGE_CONNECTION_STRING: TString;\n}>","containerName: string","bucketName: string","file: FileLike","fileId?: string","fileId: string","container: string","name: string","alepha: Alepha"],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;cA2BMA,WAAW;gCACc;AAHF,CAAA,CAAA;eAI5B,cAAA,CAAA;EAAA,UAD8B,GAAA,SAQR,OARQ,CAQA,MARA,CAAA,OAQc,SARd,CAAA,CAAA,CAAA,CAAA;;AADd;AAAA;;AAS4B,cAMhC,wBAAA,YAAoC,mBANJ,CAAA;EAAA,mBAAd,GAAA,EAON,MAPM;EAAA,mBAAR,GAAA,EAQE,MARF,CAAA,OAQgB,SARhB,CAAA;EAAA,mBAAA,MAAA,EASK,wBATL;EAAA,mBAAA,IAAA,EAYG,gBAZH;EAMvB,mBAAa,UAAA,EAOmB,MAPnB,CAAA,MAAA,EAOkC,eAPlC,CAAA;EAAA,mBAAA,iBAAA,EAQ0B,iBAR1B;EAAA,SACY,OAAA,EASC,sBATD;EAAA,WACc,CAAA;EAAA,eAAd,CAAA,aAAA,EAAA,MAAA,CAAA,EAmBrB,OAnBqB,CAmBb,eAnBa,CAAA;EAAA,MACG,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EA6BpB,QA7BoB,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EA+BxB,OA/BwB,CAAA,MAAA,CAAA;EAAA,QAGF,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EA0DkC,OA1DlC,CA0D0C,QA1D1C,CAAA;EAAA,MACqB,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EA2EW,OA3EX,CAAA,OAAA,CAAA;EAAA,MAAf,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EA+E0B,OA/E1B,CAAA,IAAA,CAAA;EAAA,QACO,CAAA,SAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAyFc,eAzFd;EAAA,SAEb,OAAA,EAiGA,cAjGA,CAAA,OAAA,CAAA;EAAA,UAWd,qBAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAyGR,OAzGQ,CAyGA,eAzGA,CAAA;EAAA,UAAR,QAAA,CAAA,CAAA,EAAA,MAAA;;;;;;;;AAtCyB;;;AAEZ,cCbJ,iBAAA,YAA6B,MDazB,CAAA;EAAA,SAAA,IAAA,GAAA,qBAAA;EAAA,SAAA,SAAA,EAAA,CAAA,MAAA,ECXqB,MDWrB,EAAA,GAAA,IAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;cA0BM,2BAAS;gCAIb,eAAA,CAAA;;;EAJI,UAAA,GAIJ,SAGqB,OAHrB,CAG6B,MAH7B,CAAA,OAG2C,SAH3C,CAAA,CAAA,CAAA,CAAA;;;AAJa;AAAA;AAAA,cAaF,wBAAA,YAAoC,mBAblC,CAAA;EAAA,mBAO8B,GAAA,EAMP,eAAA,CACf,MAPsB;EAAS,mBAAvB,GAAA,EAAA;IAAR,4BAAA,EAAA,MAAA;EAAO,CAAA;EAAA,mBAAA,MAAA,EASJ,MATI;EAMjB,mBAAA,IAAA,EAIW,gBAJc;EAAA,mBAAA,UAAA,EAKN,MALM,CAAA,MAAA,EAKS,eALT,CAAA;EAAA,mBACf,iBAAA,EAKgB,iBALhB;EAAA,SAEG,OAAA,EAKA,sBALA;EAAA,WACF,CAAA;EAAA,mBACuB,OAAA,EAGC,eAAA,CASrB,cAZoB,CAAA,OAAA,CAAA;EAAe,eAA9B,CAAA,aAAA,EAAA,MAAA,CAAA,EAmC5B,OAnC4B,CAmCpB,eAnCoB,CAAA;EAAM,MACC,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EA6C/B,QA7C+B,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EA+CnC,OA/CmC,CAAA,MAAA,CAAA;EAAiB,QAE9B,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAqFkC,OArFlC,CAqF0C,QArF1C,CAAA;EAAsB,MAAA,CAAA,UAAA,EASrB,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAiG+B,OAjG/B,CAAA,OAAA,CAAA;EAAA,MAuBf,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EA8E8C,OA9E9C,CAAA,IAAA,CAAA;EAAe,QAAvB,CAAA,SAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAyFiD,eAzFjD;EAAO,UAWH,qBAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EA0FJ,OA1FI,CA0FI,eA1FJ,CAAA;EAAQ,UAEZ,QAAA,CAAA,CAAA,EAAA,MAAA;;;;;;;;;;cC9ES,mBAWX,aAAA,CAX4B;ADUD"}
package/dist/index.d.ts CHANGED
@@ -1,50 +1,52 @@
1
- import { BucketDescriptorProvider, FileStorageProvider } from "@alepha/bucket";
2
- import { Alepha, FileLike, HookDescriptor, Logger, Module, Static, TObject, TString } from "@alepha/core";
1
+ import { FileStorageProvider } from "@alepha/bucket";
2
+ import * as _alepha_core0$1 from "@alepha/core";
3
+ import * as _alepha_core0 from "@alepha/core";
4
+ import { Alepha, FileLike, Static } from "@alepha/core";
3
5
  import { DateTimeProvider } from "@alepha/datetime";
4
6
  import { BlobServiceClient, BlockBlobClient, ContainerClient, StoragePipelineOptions } from "@azure/storage-blob";
5
7
 
6
8
  //#region src/providers/AzureFileStorageProvider.d.ts
7
- declare const envSchema: TObject<{
8
- AZ_STORAGE_CONNECTION_STRING: TString;
9
+ declare const envSchema: _alepha_core0$1.TObject<{
10
+ AZ_STORAGE_CONNECTION_STRING: _alepha_core0$1.TString;
9
11
  }>;
10
12
  declare module "@alepha/core" {
11
13
  interface Env extends Partial<Static<typeof envSchema>> {}
12
14
  }
13
15
  /**
14
- * Azure Blog Storage implementation of File Storage Provider.
15
- */
16
+ * Azure Blog Storage implementation of File Storage Provider.
17
+ */
16
18
  declare class AzureFileStorageProvider implements FileStorageProvider {
17
- protected readonly log: Logger;
18
- protected readonly env: Static<typeof envSchema>;
19
- protected readonly bucket: BucketDescriptorProvider;
19
+ protected readonly log: _alepha_core0$1.Logger;
20
+ protected readonly env: {
21
+ AZ_STORAGE_CONNECTION_STRING: string;
22
+ };
23
+ protected readonly alepha: Alepha;
20
24
  protected readonly time: DateTimeProvider;
21
25
  protected readonly containers: Record<string, ContainerClient>;
22
26
  protected readonly blobServiceClient: BlobServiceClient;
23
27
  readonly options: StoragePipelineOptions;
24
28
  constructor();
29
+ protected readonly onStart: _alepha_core0$1.HookDescriptor<"start">;
25
30
  createContainer(containerName: string): Promise<ContainerClient>;
26
31
  upload(bucketName: string, file: FileLike, fileId?: string): Promise<string>;
27
32
  download(bucketName: string, fileId: string): Promise<FileLike>;
28
33
  exists(bucketName: string, fileId: string): Promise<boolean>;
29
34
  delete(bucketName: string, fileId: string): Promise<void>;
30
35
  getBlock(container: string, fileId: string): BlockBlobClient;
31
- readonly onStart: HookDescriptor<"start">;
32
36
  protected createContainerClient(name: string): Promise<ContainerClient>;
33
37
  protected createId(): string;
34
38
  }
35
39
  //#endregion
36
40
  //#region src/index.d.ts
37
- // ---------------------------------------------------------------------------------------------------------------------
38
41
  /**
39
- * Plugin for Alepha Bucket that provides Azure Blob Storage capabilities.
40
- *
41
- * @see {@link AzureFileStorageProvider}
42
- * @module alepha.bucket.azure
43
- */
44
- declare class AlephaBucketAzure implements Module {
45
- readonly name = "alepha.bucket.azure";
46
- readonly $services: (alepha: Alepha) => void;
47
- }
42
+ * Plugin for Alepha Bucket that provides Azure Blob Storage capabilities.
43
+ *
44
+ * @see {@link AzureFileStorageProvider}
45
+ * @module alepha.bucket.azure
46
+ */
47
+ declare const AlephaBucketAzure: _alepha_core0.ModuleDescriptor;
48
+ //# sourceMappingURL=index.d.ts.map
49
+
48
50
  //#endregion
49
51
  export { AlephaBucketAzure, AzureFileStorageProvider };
50
52
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":["envSchema: TObject<{\n\tAZ_STORAGE_CONNECTION_STRING: TString;\n}>","containerName: string","bucketName: string","file: FileLike","fileId?: string","fileId: string","container: string","name: string","alepha: Alepha"],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;cA2BMA,WAAW;gCACc;AAHF,CAAA,CAAA;eAI5B,cAAA,CAAA;EAAA,UAD8B,GAAA,SAQR,OARQ,CAQA,MARA,CAAA,OAQc,SARd,CAAA,CAAA,CAAA,CAAA;;AADd;AAAA;;AAS4B,cAMhC,wBAAA,YAAoC,mBANJ,CAAA;EAAA,mBAAd,GAAA,EAON,MAPM;EAAA,mBAAR,GAAA,EAQE,MARF,CAAA,OAQgB,SARhB,CAAA;EAAA,mBAAA,MAAA,EASK,wBATL;EAAA,mBAAA,IAAA,EAYG,gBAZH;EAMvB,mBAAa,UAAA,EAOmB,MAPnB,CAAA,MAAA,EAOkC,eAPlC,CAAA;EAAA,mBAAA,iBAAA,EAQ0B,iBAR1B;EAAA,SACY,OAAA,EASC,sBATD;EAAA,WACc,CAAA;EAAA,eAAd,CAAA,aAAA,EAAA,MAAA,CAAA,EAmBrB,OAnBqB,CAmBb,eAnBa,CAAA;EAAA,MACG,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EA6BpB,QA7BoB,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EA+BxB,OA/BwB,CAAA,MAAA,CAAA;EAAA,QAGF,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EA0DkC,OA1DlC,CA0D0C,QA1D1C,CAAA;EAAA,MACqB,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EA2EW,OA3EX,CAAA,OAAA,CAAA;EAAA,MAAf,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EA+E0B,OA/E1B,CAAA,IAAA,CAAA;EAAA,QACO,CAAA,SAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAyFc,eAzFd;EAAA,SAEb,OAAA,EAiGA,cAjGA,CAAA,OAAA,CAAA;EAAA,UAWd,qBAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EAyGR,OAzGQ,CAyGA,eAzGA,CAAA;EAAA,UAAR,QAAA,CAAA,CAAA,EAAA,MAAA;;;;;;;;AAtCyB;;;AAEZ,cCbJ,iBAAA,YAA6B,MDazB,CAAA;EAAA,SAAA,IAAA,GAAA,qBAAA;EAAA,SAAA,SAAA,EAAA,CAAA,MAAA,ECXqB,MDWrB,EAAA,GAAA,IAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;cA0BM,2BAAS;gCAIb,eAAA,CAAA;;;EAJI,UAAA,GAIJ,SAGqB,OAHrB,CAG6B,MAH7B,CAAA,OAG2C,SAH3C,CAAA,CAAA,CAAA,CAAA;;;AAJa;AAAA;AAAA,cAaF,wBAAA,YAAoC,mBAblC,CAAA;EAAA,mBAO8B,GAAA,EAMP,eAAA,CACf,MAPsB;EAAS,mBAAvB,GAAA,EAAA;IAAR,4BAAA,EAAA,MAAA;EAAO,CAAA;EAAA,mBAAA,MAAA,EASJ,MATI;EAMjB,mBAAA,IAAA,EAIW,gBAJc;EAAA,mBAAA,UAAA,EAKN,MALM,CAAA,MAAA,EAKS,eALT,CAAA;EAAA,mBACf,iBAAA,EAKgB,iBALhB;EAAA,SAEG,OAAA,EAKA,sBALA;EAAA,WACF,CAAA;EAAA,mBACuB,OAAA,EAGC,eAAA,CASrB,cAZoB,CAAA,OAAA,CAAA;EAAe,eAA9B,CAAA,aAAA,EAAA,MAAA,CAAA,EAmC5B,OAnC4B,CAmCpB,eAnCoB,CAAA;EAAM,MACC,CAAA,UAAA,EAAA,MAAA,EAAA,IAAA,EA6C/B,QA7C+B,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EA+CnC,OA/CmC,CAAA,MAAA,CAAA;EAAiB,QAE9B,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAqFkC,OArFlC,CAqF0C,QArF1C,CAAA;EAAsB,MAAA,CAAA,UAAA,EASrB,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAiG+B,OAjG/B,CAAA,OAAA,CAAA;EAAA,MAuBf,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EA8E8C,OA9E9C,CAAA,IAAA,CAAA;EAAe,QAAvB,CAAA,SAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA,EAyFiD,eAzFjD;EAAO,UAWH,qBAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EA0FJ,OA1FI,CA0FI,eA1FJ,CAAA;EAAQ,UAEZ,QAAA,CAAA,CAAA,EAAA,MAAA;;;;;;;;;;cC9ES,mBAWX,aAAA,CAX4B;ADUD"}
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
- import { AlephaBucket, BucketDescriptorProvider, FileNotFoundError, FileStorageProvider } from "@alepha/bucket";
1
+ import { $bucket, AlephaBucket, FileNotFoundError, FileStorageProvider } from "@alepha/bucket";
2
+ import { $env, $hook, $inject, $logger, $module, Alepha, t } from "@alepha/core";
2
3
  import { randomUUID } from "node:crypto";
3
- import { $hook, $inject, $logger, t } from "@alepha/core";
4
+ import { Readable } from "node:stream";
4
5
  import { DateTimeProvider } from "@alepha/datetime";
5
6
  import { createFile } from "@alepha/file";
6
7
  import { BlobServiceClient } from "@azure/storage-blob";
@@ -12,8 +13,8 @@ const envSchema = t.object({ AZ_STORAGE_CONNECTION_STRING: t.string({ size: "lon
12
13
  */
13
14
  var AzureFileStorageProvider = class {
14
15
  log = $logger();
15
- env = $inject(envSchema);
16
- bucket = $inject(BucketDescriptorProvider);
16
+ env = $env(envSchema);
17
+ alepha = $inject(Alepha);
17
18
  time = $inject(DateTimeProvider);
18
19
  containers = {};
19
20
  blobServiceClient;
@@ -21,6 +22,18 @@ var AzureFileStorageProvider = class {
21
22
  constructor() {
22
23
  this.blobServiceClient = BlobServiceClient.fromConnectionString(this.env.AZ_STORAGE_CONNECTION_STRING, this.options);
23
24
  }
25
+ onStart = $hook({
26
+ on: "start",
27
+ handler: async () => {
28
+ for (const bucket of this.alepha.descriptors($bucket)) {
29
+ if (bucket.provider !== this) continue;
30
+ const containerName = bucket.name.replaceAll("/", "-").toLowerCase();
31
+ this.log.debug(`Prepare container ${containerName}...`);
32
+ if (!this.containers[containerName]) this.containers[containerName] = await this.createContainerClient(containerName);
33
+ this.log.info(`Container ${bucket} OK`);
34
+ }
35
+ }
36
+ });
24
37
  async createContainer(containerName) {
25
38
  if (this.containers[containerName]) return this.containers[containerName];
26
39
  const container = await this.createContainerClient(containerName);
@@ -42,7 +55,10 @@ var AzureFileStorageProvider = class {
42
55
  metadata,
43
56
  blobHTTPHeaders: { blobContentType: file.type }
44
57
  });
45
- else throw new Error("Raw stream upload is not supported yet");
58
+ else await block.uploadStream(Readable.from(file.stream()), file.size || void 0, 5, {
59
+ metadata,
60
+ blobHTTPHeaders: { blobContentType: file.type }
61
+ });
46
62
  return fileId;
47
63
  }
48
64
  async download(bucketName, fileId) {
@@ -52,7 +68,10 @@ var AzureFileStorageProvider = class {
52
68
  throw error;
53
69
  });
54
70
  if (!blob.readableStreamBody) throw new FileNotFoundError("File not found - empty stream body");
55
- return createFile(blob.readableStreamBody, blob.metadata);
71
+ return createFile(blob.readableStreamBody, {
72
+ ...blob.metadata,
73
+ size: blob.contentLength
74
+ });
56
75
  }
57
76
  async exists(bucketName, fileId) {
58
77
  return await this.getBlock(bucketName, fileId).exists();
@@ -69,17 +88,6 @@ var AzureFileStorageProvider = class {
69
88
  if (!this.containers[container]) throw new FileNotFoundError(`File '${fileId}' not found - container '${container}' does not exists`);
70
89
  return this.containers[container].getBlockBlobClient(fileId);
71
90
  }
72
- onStart = $hook({
73
- on: "start",
74
- handler: async () => {
75
- for (const bucket of this.bucket.getBuckets()) {
76
- const containerName = bucket.name.replaceAll("/", "-").toLowerCase();
77
- this.log.debug(`Prepare container ${containerName}...`);
78
- if (!this.containers[containerName]) this.containers[containerName] = await this.createContainerClient(containerName);
79
- this.log.info(`Container ${bucket} OK`);
80
- }
81
- }
82
- });
83
91
  async createContainerClient(name) {
84
92
  const container = this.blobServiceClient.getContainerClient(name);
85
93
  await this.time.deadline((abortSignal) => container.createIfNotExists({ abortSignal }), [5, "seconds"]);
@@ -98,16 +106,15 @@ var AzureFileStorageProvider = class {
98
106
  * @see {@link AzureFileStorageProvider}
99
107
  * @module alepha.bucket.azure
100
108
  */
101
- var AlephaBucketAzure = class {
102
- name = "alepha.bucket.azure";
103
- $services = (alepha) => {
104
- alepha.with({
105
- provide: FileStorageProvider,
106
- use: AzureFileStorageProvider,
107
- optional: true
108
- }).with(AlephaBucket);
109
- };
110
- };
109
+ const AlephaBucketAzure = $module({
110
+ name: "alepha.bucket.azure",
111
+ services: [AzureFileStorageProvider],
112
+ register: (alepha) => alepha.with({
113
+ optional: true,
114
+ provide: FileStorageProvider,
115
+ use: AzureFileStorageProvider
116
+ }).with(AlephaBucket)
117
+ });
111
118
 
112
119
  //#endregion
113
120
  export { AlephaBucketAzure, AzureFileStorageProvider };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["envSchema: TObject<{\n\tAZ_STORAGE_CONNECTION_STRING: TString;\n}>","containerName: string","bucketName: string","file: FileLike","fileId?: string","fileId: string","container: string","name: string","alepha: Alepha"],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n\tBucketDescriptorProvider,\n\tFileNotFoundError,\n\ttype FileStorageProvider,\n} from \"@alepha/bucket\";\nimport {\n\t$hook,\n\t$inject,\n\t$logger,\n\ttype FileLike,\n\ttype HookDescriptor,\n\ttype Logger,\n\ttype Static,\n\ttype TObject,\n\ttype TString,\n\tt,\n} from \"@alepha/core\";\nimport { DateTimeProvider } from \"@alepha/datetime\";\nimport { createFile } from \"@alepha/file\";\nimport {\n\tBlobServiceClient,\n\ttype BlockBlobClient,\n\ttype ContainerClient,\n\ttype StoragePipelineOptions,\n} from \"@azure/storage-blob\";\n\nconst envSchema: TObject<{\n\tAZ_STORAGE_CONNECTION_STRING: TString;\n}> = t.object({\n\tAZ_STORAGE_CONNECTION_STRING: t.string({\n\t\tsize: \"long\",\n\t}),\n});\n\ndeclare module \"@alepha/core\" {\n\tinterface 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\tprotected readonly log: Logger = $logger();\n\tprotected readonly env: Static<typeof envSchema> = $inject(envSchema);\n\tprotected readonly bucket: BucketDescriptorProvider = $inject(\n\t\tBucketDescriptorProvider,\n\t);\n\tprotected readonly time: DateTimeProvider = $inject(DateTimeProvider);\n\tprotected readonly containers: Record<string, ContainerClient> = {};\n\tprotected readonly blobServiceClient: BlobServiceClient;\n\n\tpublic readonly options: StoragePipelineOptions = {};\n\n\tconstructor() {\n\t\tthis.blobServiceClient = BlobServiceClient.fromConnectionString(\n\t\t\tthis.env.AZ_STORAGE_CONNECTION_STRING,\n\t\t\tthis.options,\n\t\t);\n\t}\n\n\tpublic async createContainer(\n\t\tcontainerName: string,\n\t): Promise<ContainerClient> {\n\t\tif (this.containers[containerName]) {\n\t\t\treturn this.containers[containerName];\n\t\t}\n\t\tconst container = await this.createContainerClient(containerName);\n\t\tthis.containers[containerName] = container;\n\t\treturn container;\n\t}\n\n\tpublic async upload(\n\t\tbucketName: string,\n\t\tfile: FileLike,\n\t\tfileId?: string,\n\t): Promise<string> {\n\t\tfileId ??= this.createId();\n\t\tconst block = this.getBlock(bucketName, fileId);\n\n\t\tconst metadata = {\n\t\t\tname: file.name,\n\t\t\ttype: file.type,\n\t\t};\n\n\t\tif (file.filepath) {\n\t\t\tawait block.uploadFile(file.filepath, {\n\t\t\t\tmetadata,\n\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t},\n\t\t\t});\n\t\t} else if (file.size > 0) {\n\t\t\tawait block.uploadData(await file.arrayBuffer(), {\n\t\t\t\tmetadata,\n\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t},\n\t\t\t});\n\t\t} else {\n\t\t\tthrow new Error(\"Raw stream upload is not supported yet\");\n\t\t}\n\n\t\treturn fileId;\n\t}\n\n\tpublic async download(bucketName: string, fileId: string): Promise<FileLike> {\n\t\tconst block = this.getBlock(bucketName, fileId);\n\n\t\tconst blob = await block.download().catch((error) => {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new FileNotFoundError(\"Error downloading file\", { cause: error });\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t});\n\n\t\tif (!blob.readableStreamBody) {\n\t\t\tthrow new FileNotFoundError(\"File not found - empty stream body\");\n\t\t}\n\n\t\treturn createFile(blob.readableStreamBody, blob.metadata);\n\t}\n\n\tpublic async exists(bucketName: string, fileId: string): Promise<boolean> {\n\t\treturn await this.getBlock(bucketName, fileId).exists();\n\t}\n\n\tpublic async delete(bucketName: string, fileId: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.getBlock(bucketName, fileId).delete();\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new FileNotFoundError(\"Error deleting file\", { cause: error });\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic getBlock(container: string, fileId: string): BlockBlobClient {\n\t\tif (!this.containers[container]) {\n\t\t\tthrow new FileNotFoundError(\n\t\t\t\t`File '${fileId}' not found - container '${container}' does not exists`,\n\t\t\t);\n\t\t}\n\n\t\treturn this.containers[container].getBlockBlobClient(fileId);\n\t}\n\n\tpublic readonly onStart: HookDescriptor<\"start\"> = $hook({\n\t\ton: \"start\",\n\t\thandler: async () => {\n\t\t\tfor (const bucket of this.bucket.getBuckets()) {\n\t\t\t\tconst containerName = bucket.name.replaceAll(\"/\", \"-\").toLowerCase();\n\t\t\t\tthis.log.debug(`Prepare container ${containerName}...`);\n\n\t\t\t\tif (!this.containers[containerName]) {\n\t\t\t\t\tthis.containers[containerName] =\n\t\t\t\t\t\tawait this.createContainerClient(containerName);\n\t\t\t\t}\n\n\t\t\t\tthis.log.info(`Container ${bucket} OK`);\n\t\t\t}\n\t\t},\n\t});\n\n\tprotected async createContainerClient(\n\t\tname: string,\n\t): Promise<ContainerClient> {\n\t\tconst container = this.blobServiceClient.getContainerClient(name);\n\n\t\tawait this.time.deadline(\n\t\t\t(abortSignal) => container.createIfNotExists({ abortSignal }),\n\t\t\t[5, \"seconds\"],\n\t\t);\n\n\t\treturn container;\n\t}\n\n\tprotected createId(): string {\n\t\treturn randomUUID();\n\t}\n}\n","import { AlephaBucket, FileStorageProvider } from \"@alepha/bucket\";\nimport type { Alepha, Module } from \"@alepha/core\";\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 class AlephaBucketAzure implements Module {\n\tpublic readonly name = \"alepha.bucket.azure\";\n\tpublic readonly $services = (alepha: Alepha): void => {\n\t\talepha\n\t\t\t.with({\n\t\t\t\tprovide: FileStorageProvider,\n\t\t\t\tuse: AzureFileStorageProvider,\n\t\t\t\toptional: true,\n\t\t\t})\n\t\t\t.with(AlephaBucket);\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["containerName: string","bucketName: string","file: FileLike","fileId?: string","fileId: string","container: string","name: string"],"sources":["../src/providers/AzureFileStorageProvider.ts","../src/index.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { Readable } from \"node:stream\";\nimport {\n\t$bucket,\n\tFileNotFoundError,\n\ttype FileStorageProvider,\n} from \"@alepha/bucket\";\nimport {\n\t$env,\n\t$hook,\n\t$inject,\n\t$logger,\n\tAlepha,\n\ttype FileLike,\n\ttype Static,\n\tt,\n} from \"@alepha/core\";\nimport { DateTimeProvider } from \"@alepha/datetime\";\nimport { createFile } from \"@alepha/file\";\nimport {\n\tBlobServiceClient,\n\ttype BlockBlobClient,\n\ttype ContainerClient,\n\ttype StoragePipelineOptions,\n} from \"@azure/storage-blob\";\n\nconst envSchema = t.object({\n\tAZ_STORAGE_CONNECTION_STRING: t.string({\n\t\tsize: \"long\",\n\t}),\n});\n\ndeclare module \"@alepha/core\" {\n\tinterface 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\tprotected readonly log = $logger();\n\tprotected readonly env = $env(envSchema);\n\tprotected readonly alepha = $inject(Alepha);\n\tprotected readonly time = $inject(DateTimeProvider);\n\tprotected readonly containers: Record<string, ContainerClient> = {};\n\tprotected readonly blobServiceClient: BlobServiceClient;\n\n\tpublic readonly options: StoragePipelineOptions = {};\n\n\tconstructor() {\n\t\tthis.blobServiceClient = BlobServiceClient.fromConnectionString(\n\t\t\tthis.env.AZ_STORAGE_CONNECTION_STRING,\n\t\t\tthis.options,\n\t\t);\n\t}\n\n\tprotected readonly onStart = $hook({\n\t\ton: \"start\",\n\t\thandler: async () => {\n\t\t\tfor (const bucket of this.alepha.descriptors($bucket)) {\n\t\t\t\tif (bucket.provider !== this) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst containerName = bucket.name.replaceAll(\"/\", \"-\").toLowerCase();\n\t\t\t\tthis.log.debug(`Prepare container ${containerName}...`);\n\n\t\t\t\tif (!this.containers[containerName]) {\n\t\t\t\t\tthis.containers[containerName] =\n\t\t\t\t\t\tawait this.createContainerClient(containerName);\n\t\t\t\t}\n\n\t\t\t\tthis.log.info(`Container ${bucket} OK`);\n\t\t\t}\n\t\t},\n\t});\n\n\tpublic async createContainer(\n\t\tcontainerName: string,\n\t): Promise<ContainerClient> {\n\t\tif (this.containers[containerName]) {\n\t\t\treturn this.containers[containerName];\n\t\t}\n\t\tconst container = await this.createContainerClient(containerName);\n\t\tthis.containers[containerName] = container;\n\t\treturn container;\n\t}\n\n\tpublic async upload(\n\t\tbucketName: string,\n\t\tfile: FileLike,\n\t\tfileId?: string,\n\t): Promise<string> {\n\t\tfileId ??= this.createId();\n\t\tconst block = this.getBlock(bucketName, fileId);\n\n\t\tconst metadata = {\n\t\t\tname: file.name,\n\t\t\ttype: file.type,\n\t\t};\n\n\t\tif (file.filepath) {\n\t\t\tawait block.uploadFile(file.filepath, {\n\t\t\t\tmetadata,\n\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t},\n\t\t\t});\n\t\t} else if (file.size > 0) {\n\t\t\tawait block.uploadData(await file.arrayBuffer(), {\n\t\t\t\tmetadata,\n\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t},\n\t\t\t});\n\t\t} else {\n\t\t\tawait block.uploadStream(\n\t\t\t\tReadable.from(file.stream()),\n\t\t\t\tfile.size || undefined,\n\t\t\t\t5,\n\t\t\t\t{\n\t\t\t\t\tmetadata,\n\t\t\t\t\tblobHTTPHeaders: {\n\t\t\t\t\t\tblobContentType: file.type,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\treturn fileId;\n\t}\n\n\tpublic async download(bucketName: string, fileId: string): Promise<FileLike> {\n\t\tconst block = this.getBlock(bucketName, fileId);\n\n\t\tconst blob = await block.download().catch((error) => {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new FileNotFoundError(\"Error downloading file\", { cause: error });\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t});\n\n\t\tif (!blob.readableStreamBody) {\n\t\t\tthrow new FileNotFoundError(\"File not found - empty stream body\");\n\t\t}\n\n\t\treturn createFile(blob.readableStreamBody, {\n\t\t\t...blob.metadata,\n\t\t\tsize: blob.contentLength,\n\t\t});\n\t}\n\n\tpublic async exists(bucketName: string, fileId: string): Promise<boolean> {\n\t\treturn await this.getBlock(bucketName, fileId).exists();\n\t}\n\n\tpublic async delete(bucketName: string, fileId: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.getBlock(bucketName, fileId).delete();\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new FileNotFoundError(\"Error deleting file\", { cause: error });\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic getBlock(container: string, fileId: string): BlockBlobClient {\n\t\tif (!this.containers[container]) {\n\t\t\tthrow new FileNotFoundError(\n\t\t\t\t`File '${fileId}' not found - container '${container}' does not exists`,\n\t\t\t);\n\t\t}\n\n\t\treturn this.containers[container].getBlockBlobClient(fileId);\n\t}\n\n\tprotected async createContainerClient(\n\t\tname: string,\n\t): Promise<ContainerClient> {\n\t\tconst container = this.blobServiceClient.getContainerClient(name);\n\n\t\tawait this.time.deadline(\n\t\t\t(abortSignal) => container.createIfNotExists({ abortSignal }),\n\t\t\t[5, \"seconds\"],\n\t\t);\n\n\t\treturn container;\n\t}\n\n\tprotected createId(): string {\n\t\treturn randomUUID();\n\t}\n}\n","import { AlephaBucket, FileStorageProvider } from \"@alepha/bucket\";\nimport { $module } from \"@alepha/core\";\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\tname: \"alepha.bucket.azure\",\n\tservices: [AzureFileStorageProvider],\n\tregister: (alepha) =>\n\t\talepha\n\t\t\t.with({\n\t\t\t\toptional: true,\n\t\t\t\tprovide: FileStorageProvider,\n\t\t\t\tuse: AzureFileStorageProvider,\n\t\t\t})\n\t\t\t.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": "Feunard",
13
- "version": "0.8.1",
13
+ "version": "0.9.0",
14
14
  "type": "module",
15
15
  "engines": {
16
16
  "node": ">=22.0.0"
@@ -23,14 +23,14 @@
23
23
  "src"
24
24
  ],
25
25
  "dependencies": {
26
- "@alepha/bucket": "0.8.1",
27
- "@alepha/core": "0.8.1",
28
- "@alepha/datetime": "0.8.1",
29
- "@alepha/file": "0.8.1",
30
- "@azure/storage-blob": "^12.27.0"
26
+ "@alepha/bucket": "0.9.0",
27
+ "@alepha/core": "0.9.0",
28
+ "@alepha/datetime": "0.9.0",
29
+ "@alepha/file": "0.9.0",
30
+ "@azure/storage-blob": "12.27.0"
31
31
  },
32
32
  "devDependencies": {
33
- "tsdown": "^0.12.9",
33
+ "tsdown": "^0.13.0",
34
34
  "vitest": "^3.2.4"
35
35
  },
36
36
  "scripts": {
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { AlephaBucket, FileStorageProvider } from "@alepha/bucket";
2
- import type { Alepha, Module } from "@alepha/core";
2
+ import { $module } from "@alepha/core";
3
3
  import { AzureFileStorageProvider } from "./providers/AzureFileStorageProvider.ts";
4
4
 
5
5
  export * from "./providers/AzureFileStorageProvider.ts";
@@ -12,15 +12,15 @@ export * from "./providers/AzureFileStorageProvider.ts";
12
12
  * @see {@link AzureFileStorageProvider}
13
13
  * @module alepha.bucket.azure
14
14
  */
15
- export class AlephaBucketAzure implements Module {
16
- public readonly name = "alepha.bucket.azure";
17
- public readonly $services = (alepha: Alepha): void => {
15
+ export const AlephaBucketAzure = $module({
16
+ name: "alepha.bucket.azure",
17
+ services: [AzureFileStorageProvider],
18
+ register: (alepha) =>
18
19
  alepha
19
20
  .with({
21
+ optional: true,
20
22
  provide: FileStorageProvider,
21
23
  use: AzureFileStorageProvider,
22
- optional: true,
23
24
  })
24
- .with(AlephaBucket);
25
- };
26
- }
25
+ .with(AlephaBucket),
26
+ });
@@ -1,19 +1,18 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ import { Readable } from "node:stream";
2
3
  import {
3
- BucketDescriptorProvider,
4
+ $bucket,
4
5
  FileNotFoundError,
5
6
  type FileStorageProvider,
6
7
  } from "@alepha/bucket";
7
8
  import {
9
+ $env,
8
10
  $hook,
9
11
  $inject,
10
12
  $logger,
13
+ Alepha,
11
14
  type FileLike,
12
- type HookDescriptor,
13
- type Logger,
14
15
  type Static,
15
- type TObject,
16
- type TString,
17
16
  t,
18
17
  } from "@alepha/core";
19
18
  import { DateTimeProvider } from "@alepha/datetime";
@@ -25,9 +24,7 @@ import {
25
24
  type StoragePipelineOptions,
26
25
  } from "@azure/storage-blob";
27
26
 
28
- const envSchema: TObject<{
29
- AZ_STORAGE_CONNECTION_STRING: TString;
30
- }> = t.object({
27
+ const envSchema = t.object({
31
28
  AZ_STORAGE_CONNECTION_STRING: t.string({
32
29
  size: "long",
33
30
  }),
@@ -41,12 +38,10 @@ declare module "@alepha/core" {
41
38
  * Azure Blog Storage implementation of File Storage Provider.
42
39
  */
43
40
  export class AzureFileStorageProvider implements FileStorageProvider {
44
- protected readonly log: Logger = $logger();
45
- protected readonly env: Static<typeof envSchema> = $inject(envSchema);
46
- protected readonly bucket: BucketDescriptorProvider = $inject(
47
- BucketDescriptorProvider,
48
- );
49
- protected readonly time: DateTimeProvider = $inject(DateTimeProvider);
41
+ protected readonly log = $logger();
42
+ protected readonly env = $env(envSchema);
43
+ protected readonly alepha = $inject(Alepha);
44
+ protected readonly time = $inject(DateTimeProvider);
50
45
  protected readonly containers: Record<string, ContainerClient> = {};
51
46
  protected readonly blobServiceClient: BlobServiceClient;
52
47
 
@@ -59,6 +54,27 @@ export class AzureFileStorageProvider implements FileStorageProvider {
59
54
  );
60
55
  }
61
56
 
57
+ protected readonly onStart = $hook({
58
+ on: "start",
59
+ handler: async () => {
60
+ for (const bucket of this.alepha.descriptors($bucket)) {
61
+ if (bucket.provider !== this) {
62
+ continue;
63
+ }
64
+
65
+ const containerName = bucket.name.replaceAll("/", "-").toLowerCase();
66
+ this.log.debug(`Prepare container ${containerName}...`);
67
+
68
+ if (!this.containers[containerName]) {
69
+ this.containers[containerName] =
70
+ await this.createContainerClient(containerName);
71
+ }
72
+
73
+ this.log.info(`Container ${bucket} OK`);
74
+ }
75
+ },
76
+ });
77
+
62
78
  public async createContainer(
63
79
  containerName: string,
64
80
  ): Promise<ContainerClient> {
@@ -98,7 +114,17 @@ export class AzureFileStorageProvider implements FileStorageProvider {
98
114
  },
99
115
  });
100
116
  } else {
101
- throw new Error("Raw stream upload is not supported yet");
117
+ await block.uploadStream(
118
+ Readable.from(file.stream()),
119
+ file.size || undefined,
120
+ 5,
121
+ {
122
+ metadata,
123
+ blobHTTPHeaders: {
124
+ blobContentType: file.type,
125
+ },
126
+ },
127
+ );
102
128
  }
103
129
 
104
130
  return fileId;
@@ -119,7 +145,10 @@ export class AzureFileStorageProvider implements FileStorageProvider {
119
145
  throw new FileNotFoundError("File not found - empty stream body");
120
146
  }
121
147
 
122
- return createFile(blob.readableStreamBody, blob.metadata);
148
+ return createFile(blob.readableStreamBody, {
149
+ ...blob.metadata,
150
+ size: blob.contentLength,
151
+ });
123
152
  }
124
153
 
125
154
  public async exists(bucketName: string, fileId: string): Promise<boolean> {
@@ -147,23 +176,6 @@ export class AzureFileStorageProvider implements FileStorageProvider {
147
176
  return this.containers[container].getBlockBlobClient(fileId);
148
177
  }
149
178
 
150
- public readonly onStart: HookDescriptor<"start"> = $hook({
151
- on: "start",
152
- handler: async () => {
153
- for (const bucket of this.bucket.getBuckets()) {
154
- const containerName = bucket.name.replaceAll("/", "-").toLowerCase();
155
- this.log.debug(`Prepare container ${containerName}...`);
156
-
157
- if (!this.containers[containerName]) {
158
- this.containers[containerName] =
159
- await this.createContainerClient(containerName);
160
- }
161
-
162
- this.log.info(`Container ${bucket} OK`);
163
- }
164
- },
165
- });
166
-
167
179
  protected async createContainerClient(
168
180
  name: string,
169
181
  ): Promise<ContainerClient> {