@qrvey/object-storage 0.0.10-beta.2 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @qrvey/object-storage
2
2
 
3
3
  ![install size](https://packagephobia.com/badge?p=%40qrvey%2Fobject-storage)
4
- ![coverage](https://img.shields.io/badge/unit_test_coverage-9%25-brightgreen)
4
+ ![coverage](https://img.shields.io/badge/unit_test_coverage-8%25-brightgreen)
5
5
 
6
6
  The `@qrvey/object-storage` package provides a unified interface for ...
7
7
 
package/dist/cjs/index.js CHANGED
@@ -16,6 +16,11 @@ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
16
16
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
17
17
  var __hasOwnProp = Object.prototype.hasOwnProperty;
18
18
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
19
+ var __knownSymbol = (name, symbol) => {
20
+ if (symbol = Symbol[name])
21
+ return symbol;
22
+ throw Error("Symbol." + name + " is not defined");
23
+ };
19
24
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
20
25
  var __spreadValues = (a, b) => {
21
26
  for (var prop in b || (b = {}))
@@ -41,6 +46,7 @@ var __objRest = (source, exclude) => {
41
46
  }
42
47
  return target;
43
48
  };
49
+ var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it);
44
50
 
45
51
  // src/shared/utils/errorHandler.ts
46
52
  var errorMessages = {
@@ -182,16 +188,28 @@ var BlobStorageService = class {
182
188
  * Retrieves an object from the blob storage service.
183
189
  *
184
190
  * @param {string} blobName - The name of the blob to retrieve.
191
+ * @param {Object} [options] - The options that you can pass to the library.
185
192
  * @return {Promise<GetObjectResponse>} A promise that resolves to the object data, including the body, metadata, content type, and content length, or rejects with an error if there was an issue.
186
193
  */
187
- async getObject(blobName) {
194
+ async getObject(blobName, options) {
188
195
  var _a;
189
196
  try {
197
+ let downloadBlockBlobResponse;
190
198
  const containerClient = this.blobServiceClient.getContainerClient(
191
199
  this.containerName
192
200
  );
193
201
  const blockBlobClient = containerClient.getBlockBlobClient(blobName);
194
- const downloadBlockBlobResponse = await blockBlobClient.download(0);
202
+ const expression = (options == null ? void 0 : options.range) || "";
203
+ const matches = expression.match(/\d+/g);
204
+ if (expression && (matches == null ? void 0 : matches.length)) {
205
+ const [start, end] = matches.map(Number);
206
+ downloadBlockBlobResponse = await blockBlobClient.download(
207
+ start,
208
+ end
209
+ );
210
+ } else {
211
+ downloadBlockBlobResponse = await blockBlobClient.download(0);
212
+ }
195
213
  return {
196
214
  body: downloadBlockBlobResponse.readableStreamBody,
197
215
  metadata: downloadBlockBlobResponse.metadata,
@@ -453,6 +471,71 @@ var BlobStorageService = class {
453
471
  );
454
472
  }
455
473
  }
474
+ async listMultipartUploadsForBucket() {
475
+ const containerClient = this.blobServiceClient.getContainerClient(
476
+ this.containerName
477
+ );
478
+ const blobList = containerClient.listBlobsFlat();
479
+ const uploads = [];
480
+ try {
481
+ for (var iter = __forAwait(blobList), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
482
+ const blob = temp.value;
483
+ uploads.push({
484
+ uploadId: blob.name,
485
+ key: blob.name,
486
+ initiated: blob.properties.createdOn || /* @__PURE__ */ new Date()
487
+ });
488
+ }
489
+ } catch (temp) {
490
+ error = [temp];
491
+ } finally {
492
+ try {
493
+ more && (temp = iter.return) && await temp.call(iter);
494
+ } finally {
495
+ if (error)
496
+ throw error[0];
497
+ }
498
+ }
499
+ return {
500
+ bucketName: this.containerName,
501
+ uploads
502
+ };
503
+ }
504
+ /**
505
+ * Retrieves the list of parts for a multipart upload.
506
+ *
507
+ * @param {string} blobName - The name of the blob (file) in Azure Blob Storage.
508
+ * @return {Promise<ListPartsMultipartUploadResponse>} A promise that resolves to the list of parts for the multipart upload.
509
+ */
510
+ async listMultipartUploadsForKey(blobName) {
511
+ var _a;
512
+ const containerClient = this.blobServiceClient.getContainerClient(
513
+ this.containerName
514
+ );
515
+ const blobClient = containerClient.getBlockBlobClient(blobName);
516
+ const listResponse = await blobClient.getBlockList("all");
517
+ const parts = ((_a = listResponse == null ? void 0 : listResponse.uncommittedBlocks) == null ? void 0 : _a.map((block, index) => ({
518
+ PartNumber: index + 1,
519
+ LastModified: /* @__PURE__ */ new Date(),
520
+ // Azure Blob Storage doesn't provide the last modified date for individual parts
521
+ ETag: block.name,
522
+ Size: block.size
523
+ }))) || [];
524
+ return {
525
+ Parts: parts
526
+ };
527
+ }
528
+ /**
529
+ * Generates a unique upload ID for multipart uploads.
530
+ *
531
+ * @return {Promise<string>} A promise that resolves to a unique upload ID.
532
+ */
533
+ async generateUploadIdMultipart() {
534
+ const timestamp = Date.now();
535
+ const randomNum = Math.floor(Math.random() * 100);
536
+ const blockIdBase = (timestamp - randomNum).toString();
537
+ return blockIdBase;
538
+ }
456
539
  /**
457
540
  * Uploads a part of a file as a block to Azure Blob Storage and updates the metadata with the block ID.
458
541
  *
@@ -467,17 +550,10 @@ var BlobStorageService = class {
467
550
  this.containerName
468
551
  );
469
552
  const blobClient = containerClient.getBlockBlobClient(blobName);
470
- let blockIdBase = uploadId;
471
- if (!blockIdBase) {
472
- const timestamp = Date.now();
473
- const randomNum = Math.floor(Math.random() * 100);
474
- blockIdBase = (timestamp - randomNum).toString();
475
- }
553
+ const blockIdBase = uploadId || await this.generateUploadIdMultipart();
476
554
  const partId = partNumber.toString().padStart(6, "0");
477
555
  const partIdBase64 = Buffer.from(partId).toString("base64");
478
556
  await blobClient.stageBlock(partIdBase64, file, file.length);
479
- const blockIdStorage = BlockIdStorage.getInstance();
480
- blockIdStorage.addBlockId(blockIdBase, partIdBase64);
481
557
  return blockIdBase;
482
558
  }
483
559
  /**
@@ -489,17 +565,17 @@ var BlobStorageService = class {
489
565
  * @throws {Error} If no block IDs are found in the metadata.
490
566
  */
491
567
  async completeMultipartUpload(blobName, uploadId) {
568
+ var _a;
492
569
  const containerClient = this.blobServiceClient.getContainerClient(
493
570
  this.containerName
494
571
  );
495
572
  const blobClient = containerClient.getBlockBlobClient(blobName);
496
- const blockIdStorage = BlockIdStorage.getInstance();
497
- const blockIds = blockIdStorage.getBlockIds(uploadId);
573
+ const listMultipartUploads = await this.listMultipartUploadsForKey(blobName);
574
+ const blockIds = ((_a = listMultipartUploads == null ? void 0 : listMultipartUploads.Parts) == null ? void 0 : _a.map((part) => part.ETag)) || [];
498
575
  if (blockIds.length === 0) {
499
576
  throw new Error("No block IDs found in metadata.");
500
577
  }
501
578
  await blobClient.commitBlockList(blockIds);
502
- blockIdStorage.clearBlockIds(uploadId);
503
579
  }
504
580
  /**
505
581
  * Aborts a multipart upload by deleting the specified blob and clearing its block IDs metadata.
@@ -517,6 +593,17 @@ var BlobStorageService = class {
517
593
  blockIdStorage.clearBlockIds(uploadId);
518
594
  await blobClient.delete();
519
595
  }
596
+ async getMultipartUploadPresignedUrl(blobName, uploadId, partNumber, expiresInMinutes = 2) {
597
+ const partId = partNumber.toString().padStart(6, "0");
598
+ const partIdBase64 = Buffer.from(partId).toString("base64");
599
+ const sasUrl = await this.getSignatureUrl(
600
+ blobName,
601
+ expiresInMinutes,
602
+ "w"
603
+ );
604
+ const url = `${sasUrl}&comp=block&blockid=${partIdBase64}`;
605
+ return url;
606
+ }
520
607
  };
521
608
 
522
609
  // src/services/storage/s3/s3Helpers.ts
@@ -773,18 +860,69 @@ var S3StorageService = class {
773
860
  );
774
861
  }
775
862
  }
863
+ /**
864
+ * Generates an upload ID for multipart upload in the S3 bucket.
865
+ *
866
+ * @param {string} key - The key of the object to upload.
867
+ * @return {Promise<string>} A promise that resolves to the generated upload ID.
868
+ * @throws {Error} If no upload ID was generated.
869
+ */
870
+ async generateUploadIdMultipart(key) {
871
+ const params = {
872
+ Bucket: this.bucketName,
873
+ Key: key
874
+ };
875
+ const response = await this.s3.createMultipartUpload(params);
876
+ if (!(response == null ? void 0 : response.UploadId))
877
+ throw new Error("No upload ID was generated");
878
+ return response.UploadId;
879
+ }
880
+ async listMultipartUploadsForBucket() {
881
+ const params = {
882
+ Bucket: this.bucketName
883
+ };
884
+ const response = await this.s3.listMultipartUploads(params);
885
+ return {
886
+ bucketName: this.bucketName,
887
+ uploads: response.Uploads ? response.Uploads.map((upload) => {
888
+ var _a, _b;
889
+ return {
890
+ uploadId: upload.UploadId || "",
891
+ key: upload.Key || "",
892
+ initiator: ((_a = upload == null ? void 0 : upload.Initiator) == null ? void 0 : _a.ID) || "",
893
+ owner: ((_b = upload == null ? void 0 : upload.Owner) == null ? void 0 : _b.ID) || "",
894
+ storageClass: upload.StorageClass || "",
895
+ initiated: upload.Initiated || ""
896
+ };
897
+ }) : []
898
+ };
899
+ }
900
+ /**
901
+ * Retrieves the list of parts for a multipart upload.
902
+ *
903
+ * @param {string} key - The key of the object being uploaded.
904
+ * @param {string} uploadId - The ID of the multipart upload.
905
+ * @return {Promise<ListPartsMultipartUploadResponse>} A promise that resolves to the list of parts for the multipart upload.
906
+ */
907
+ async listMultipartUploadsForKey(key, uploadId) {
908
+ const params = {
909
+ Bucket: this.bucketName,
910
+ Key: key,
911
+ UploadId: uploadId
912
+ };
913
+ const response = await this.s3.listParts(params);
914
+ const parts = response.Parts ? response.Parts.map((part) => ({
915
+ PartNumber: part.PartNumber,
916
+ LastModified: part.LastModified,
917
+ ETag: part.ETag,
918
+ Size: part.Size
919
+ })) : [];
920
+ return {
921
+ Parts: parts
922
+ };
923
+ }
776
924
  async uploadMultipart(key, file, partNumber, uploadId) {
777
- let upId = uploadId;
778
- if (!upId) {
779
- const params2 = {
780
- Bucket: this.bucketName,
781
- Key: key
782
- };
783
- const response = await this.s3.createMultipartUpload(params2);
784
- if (!(response == null ? void 0 : response.UploadId))
785
- throw new Error("No upload ID was generated");
786
- upId = response.UploadId;
787
- }
925
+ const upId = uploadId || await this.generateUploadIdMultipart(key);
788
926
  const params = {
789
927
  Bucket: this.bucketName,
790
928
  Key: key,
@@ -796,12 +934,10 @@ var S3StorageService = class {
796
934
  return upId;
797
935
  }
798
936
  async completeMultipartUpload(key, uploadId) {
799
- const listPartsParams = {
800
- Bucket: this.bucketName,
801
- Key: key,
802
- UploadId: uploadId
803
- };
804
- const partsResponse = await this.s3.listParts(listPartsParams);
937
+ const partsResponse = await this.listMultipartUploadsForKey(
938
+ key,
939
+ uploadId
940
+ );
805
941
  const partsList = (partsResponse == null ? void 0 : partsResponse.Parts) && partsResponse.Parts.map(
806
942
  // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
807
943
  (_a) => {
@@ -809,11 +945,14 @@ var S3StorageService = class {
809
945
  return partList;
810
946
  }
811
947
  );
812
- const completeMultipartParams = __spreadProps(__spreadValues({}, listPartsParams), {
948
+ const completeMultipartParams = {
949
+ Bucket: this.bucketName,
950
+ Key: key,
951
+ UploadId: uploadId,
813
952
  MultipartUpload: {
814
953
  Parts: partsList
815
954
  }
816
- });
955
+ };
817
956
  await this.s3.completeMultipartUpload(completeMultipartParams);
818
957
  }
819
958
  async abortMultipartUpload(key, uploadId) {
@@ -824,6 +963,21 @@ var S3StorageService = class {
824
963
  };
825
964
  await this.s3.abortMultipartUpload(params);
826
965
  }
966
+ async getMultipartUploadPresignedUrl(key, uploadId, partNumber, expiresInMinutes = 60) {
967
+ const uploadPartParams = {
968
+ Bucket: this.bucketName,
969
+ Key: key,
970
+ UploadId: uploadId,
971
+ PartNumber: parseInt(partNumber, 10)
972
+ };
973
+ const expiresIn = expiresInMinutes * 60;
974
+ const presignedUrl = await s3RequestPresigner.getSignedUrl(
975
+ this.s3Client,
976
+ new clientS3.UploadPartCommand(uploadPartParams),
977
+ { expiresIn }
978
+ );
979
+ return presignedUrl;
980
+ }
827
981
  };
828
982
 
829
983
  // src/shared/utils/constants.ts
@@ -893,10 +1047,10 @@ var ObjectStorageService = class _ObjectStorageService {
893
1047
  * @param {string} [bucketName] - The name of the bucket where the object is stored. If not provided, the default bucket name will be used.
894
1048
  * @return {Promise<GetObjectResponse>} A promise that resolves to the retrieved object.
895
1049
  */
896
- static async getObject(key, bucketName) {
1050
+ static async getObject(key, bucketName, options) {
897
1051
  return _ObjectStorageService.getObjectStorageServiceInstance(
898
1052
  bucketName
899
- ).then((instance) => instance.getObject(key));
1053
+ ).then((instance) => instance.getObject(key, options));
900
1054
  }
901
1055
  /**
902
1056
  * Retrieves an object info (without file content) from the object storage service.
@@ -1002,6 +1156,44 @@ var ObjectStorageService = class _ObjectStorageService {
1002
1156
  bucketName
1003
1157
  ).then((instance) => instance.getHeadBucket());
1004
1158
  }
1159
+ /**
1160
+ * Generates a unique upload ID for a multipart upload.
1161
+ *
1162
+ * @param {string} key - The key of the object to upload.
1163
+ * @param {string} [bucketName] - The name of the bucket. If not provided, the default bucket name will be used.
1164
+ * @return {Promise<string>} A promise that resolves to the generated upload ID.
1165
+ */
1166
+ static async generateUploadIdMultipart(key, bucketName) {
1167
+ return _ObjectStorageService.getObjectStorageServiceInstance(
1168
+ bucketName
1169
+ ).then((instance) => instance.generateUploadIdMultipart(key));
1170
+ }
1171
+ /**
1172
+ * Retrieves a list of multipart uploads for a specified key in the object storage service.
1173
+ *
1174
+ * @param {string} key - The key of the object to retrieve uploads for.
1175
+ * @param {string} [bucketName] - The name of the bucket. If not provided, the default bucket name will be used.
1176
+ * @param {string} [uploadId] - The upload ID to filter the results by.
1177
+ * @return {Promise<ListPartsMultipartUploadResponse>} A promise that resolves to the list of multipart uploads for the specified key.
1178
+ */
1179
+ static async listMultipartUploadsForKey(key, bucketName, uploadId) {
1180
+ return _ObjectStorageService.getObjectStorageServiceInstance(
1181
+ bucketName
1182
+ ).then(
1183
+ (instance) => instance.listMultipartUploadsForKey(key, uploadId)
1184
+ );
1185
+ }
1186
+ /**
1187
+ * Retrieves a list of all multipart uploads for a specified bucket in the object storage service.
1188
+ *
1189
+ * @param {string} [bucketName] - The name of the bucket. If not provided, the default bucket name will be used.
1190
+ * @return {Promise<ListMultipartUploadsResponse>} A promise that resolves to the list of multipart uploads for the specified bucket.
1191
+ */
1192
+ static async listMultipartUploadsForBucket(bucketName) {
1193
+ return _ObjectStorageService.getObjectStorageServiceInstance(
1194
+ bucketName
1195
+ ).then((instance) => instance.listMultipartUploadsForBucket());
1196
+ }
1005
1197
  /**
1006
1198
  * Uploads a multipart file to the specified bucket in the object storage service.
1007
1199
  *
@@ -1045,6 +1237,25 @@ var ObjectStorageService = class _ObjectStorageService {
1045
1237
  bucketName
1046
1238
  ).then((instance) => instance.abortMultipartUpload(key, uploadId));
1047
1239
  }
1240
+ /**
1241
+ * Retrieves a presigned URL for a specific part of a multipart upload.
1242
+ *
1243
+ * @param {string} key - The key of the object for which to generate the presigned URL.
1244
+ * @param {string} uploadId - The ID of the multipart upload.
1245
+ * @param {string} partNumber - The number of the part for which to generate the presigned URL.
1246
+ * @param {number} [expiresInMinutes] - The number of minutes until the presigned URL expires. Default is 60 minutes.
1247
+ * @return {Promise<string>} A Promise that resolves to the presigned URL for the specified part of the multipart upload.
1248
+ */
1249
+ static async getMultipartUploadPresignedUrl(key, uploadId, partNumber, expiresInMinutes) {
1250
+ return _ObjectStorageService.getObjectStorageServiceInstance().then(
1251
+ (instance) => instance.getMultipartUploadPresignedUrl(
1252
+ key,
1253
+ uploadId,
1254
+ partNumber,
1255
+ expiresInMinutes
1256
+ )
1257
+ );
1258
+ }
1048
1259
  };
1049
1260
 
1050
1261
  exports.ObjectStorageService = ObjectStorageService;