multivendor-storage 0.0.1 → 0.0.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.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var clientS3 = require('@aws-sdk/client-s3');
4
+ var s3RequestPresigner = require('@aws-sdk/s3-request-presigner');
4
5
  var storage = require('@google-cloud/storage');
5
6
 
6
7
  // src/s3Client.ts
@@ -21,6 +22,7 @@ var StorageApiError = class extends Error {
21
22
  };
22
23
 
23
24
  // src/s3Client.ts
25
+ var signGetObject = s3RequestPresigner.getSignedUrl;
24
26
  var S3StorageClient = class {
25
27
  constructor(config) {
26
28
  if (!config.bucket?.trim()) {
@@ -64,6 +66,19 @@ var S3StorageClient = class {
64
66
  const byteArray = await response.Body.transformToByteArray();
65
67
  return Buffer.from(byteArray);
66
68
  }
69
+ async getPresignedGetUrl(key, options) {
70
+ const expiresIn = Math.min(Math.max(Math.floor(options.expiresInSeconds), 1), 7 * 24 * 60 * 60);
71
+ return signGetObject(
72
+ this.client,
73
+ new clientS3.GetObjectCommand({
74
+ Bucket: this.bucket,
75
+ Key: key,
76
+ ...options.contentType ? { ResponseContentType: options.contentType } : {},
77
+ ...options.contentDisposition ? { ResponseContentDisposition: options.contentDisposition } : {}
78
+ }),
79
+ { expiresIn }
80
+ );
81
+ }
67
82
  async listObjects(prefix = "", options = {}) {
68
83
  const response = await this.client.send(
69
84
  new clientS3.ListObjectsV2Command({
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/s3Client.ts","../src/keys.ts","../src/gcpClient.ts","../src/publicUrl.ts"],"names":["S3Client","PutObjectCommand","GetObjectCommand","ListObjectsV2Command","DeleteObjectCommand","DeleteObjectsCommand","Storage","base"],"mappings":";;;;;;;;AAAO,IAAM,kBAAA,GAAN,cAAiC,KAAA,CAAM;AAAA,EAC5C,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,oBAAA;AAAA,EACd;AACF;AAEO,IAAM,eAAA,GAAN,cAA8B,KAAA,CAAM;AAAA,EACzC,WAAA,CAAY,SAA0B,KAAA,EAAiB;AACrD,IAAA,KAAA,CAAM,OAAO,CAAA;AADuB,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAEpC,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAAA,EACd;AACF;;;ACiBO,IAAM,kBAAN,MAAsB;AAAA,EAI3B,YAAY,MAAA,EAAyB;AACnC,IAAA,IAAI,CAAC,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAK,EAAG;AAC1B,MAAA,MAAM,IAAI,mBAAmB,6BAA6B,CAAA;AAAA,IAC5D;AACA,IAAA,IAAI,CAAC,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAK,EAAG;AAC1B,MAAA,MAAM,IAAI,mBAAmB,6BAA6B,CAAA;AAAA,IAC5D;AAEA,IAAA,IAAA,CAAK,SAAS,MAAA,CAAO,MAAA;AACrB,IAAA,IAAA,CAAK,MAAA,GAAS,IAAIA,iBAAA,CAAS;AAAA,MACzB,QAAQ,MAAA,CAAO,MAAA;AAAA,MACf,GAAI,MAAA,CAAO,WAAA,IAAe,MAAA,CAAO,eAAA,GAC7B;AAAA,QACE,WAAA,EAAa;AAAA,UACX,aAAa,MAAA,CAAO,WAAA;AAAA,UACpB,iBAAiB,MAAA,CAAO;AAAA;AAC1B,UAEF;AAAC,KACN,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,SAAA,CACJ,GAAA,EACA,IAAA,EACA,aACA,eAAA,EACe;AACf,IAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,MAChB,IAAIC,yBAAA,CAAiB;AAAA,QACnB,QAAQ,IAAA,CAAK,MAAA;AAAA,QACb,GAAA,EAAK,GAAA;AAAA,QACL,IAAA,EAAM,IAAA;AAAA,QACN,GAAI,WAAA,GAAc,EAAE,WAAA,EAAa,WAAA,KAAgB,EAAC;AAAA,QAClD,GAAI,eAAA,GAAkB,EAAE,eAAA,EAAiB,eAAA,KAAoB;AAAC,OAC/D;AAAA,KACH;AAAA,EACF;AAAA,EAEA,MAAM,gBAAgB,GAAA,EAA8B;AAClD,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MACjC,IAAIC,yBAAA,CAAiB;AAAA,QACnB,QAAQ,IAAA,CAAK,MAAA;AAAA,QACb,GAAA,EAAK;AAAA,OACN;AAAA,KACH;AACA,IAAA,IAAI,CAAC,SAAS,IAAA,EAAM;AAClB,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kBAAA,EAAqB,GAAG,CAAA,CAAE,CAAA;AAAA,IAC5C;AACA,IAAA,MAAM,SAAA,GAAY,MAAM,QAAA,CAAS,IAAA,CAAK,oBAAA,EAAqB;AAC3D,IAAA,OAAO,MAAA,CAAO,KAAK,SAAS,CAAA;AAAA,EAC9B;AAAA,EAEA,MAAM,WAAA,CACJ,MAAA,GAAiB,EAAA,EACjB,OAAA,GAAgC,EAAC,EACH;AAC9B,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MACjC,IAAIC,6BAAA,CAAqB;AAAA,QACvB,QAAQ,IAAA,CAAK,MAAA;AAAA,QACb,MAAA,EAAQ,MAAA;AAAA,QACR,WAAW,OAAA,CAAQ,SAAA;AAAA,QACnB,OAAA,EAAS,QAAQ,KAAA,IAAS,MAAA;AAAA,QAC1B,GAAI,QAAQ,MAAA,GAAS,EAAE,YAAY,OAAA,CAAQ,MAAA,KAAW;AAAC,OACxD;AAAA,KACH;AAEA,IAAA,OAAO;AAAA,MACL,MAAM,IAAA,CAAK,MAAA;AAAA,MACX,MAAA,EAAQ,MAAA;AAAA,MACR,WAAW,OAAA,CAAQ,SAAA;AAAA,MACnB,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,YAAY,QAAA,CAAS,qBAAA;AAAA,MACrB,aAAa,QAAA,CAAS,WAAA;AAAA,MACtB,QAAA,EAAA,CAAW,QAAA,CAAS,QAAA,IAAY,EAAC,EAAG,GAAA,CAAI,CAAC,GAAA,MAAkB,EAAE,GAAA,EAAK,GAAA,CAAI,GAAA,EAAI,CAAE,CAAA;AAAA,MAC5E,cAAA,EAAA,CAAiB,QAAA,CAAS,cAAA,IAAkB,EAAC,EAAG,GAAA,CAAI,CAAC,EAAA,MAAQ,EAAE,MAAA,EAAQ,EAAA,CAAG,MAAA,EAAO,CAAE;AAAA,KACrF;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,GAAA,EAA4B;AAC7C,IAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,MAChB,IAAIC,4BAAA,CAAoB;AAAA,QACtB,QAAQ,IAAA,CAAK,MAAA;AAAA,QACb,GAAA,EAAK;AAAA,OACN;AAAA,KACH;AAAA,EACF;AAAA,EAEA,MAAM,sBAAsB,MAAA,EAA+B;AACzD,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,MAC/B,IAAID,6BAAA,CAAqB;AAAA,QACvB,QAAQ,IAAA,CAAK,MAAA;AAAA,QACb,MAAA,EAAQ;AAAA,OACT;AAAA,KACH;AACA,IAAA,MAAM,WAAW,MAAA,CAAO,QAAA,IAAY,EAAC,EAClC,MAAA,CAAO,CAAC,GAAA,KAAQ,GAAA,CAAI,GAAG,CAAA,CACvB,IAAI,CAAC,GAAA,MAAS,EAAE,GAAA,EAAK,GAAA,CAAI,KAAK,CAAE,CAAA;AAEnC,IAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,KAAK,MAAA,CAAO,IAAA;AAAA,MAChB,IAAIE,6BAAA,CAAqB;AAAA,QACvB,QAAQ,IAAA,CAAK,MAAA;AAAA,QACb,MAAA,EAAQ;AAAA,UACN,OAAA,EAAS,OAAA;AAAA,UACT,KAAA,EAAO;AAAA;AACT,OACD;AAAA,KACH;AAAA,EACF;AAAA,EAEA,MAAM,mBAAmB,GAAA,EAA4B;AACnD,IAAA,MAAM,KAAK,SAAA,CAAU,GAAA,EAAK,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA;AAAA,EAC3C;AACF;;;ACnJO,SAAS,iBAAA,CAAkB,WAA+B,YAAA,EAA8B;AAC7F,EAAA,MAAM,cAAA,GAAiB,YAAA,CAAa,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AACtD,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,OAAO,cAAA;AAAA,EACT;AACA,EAAA,MAAM,SAAS,SAAA,CAAU,QAAA,CAAS,GAAG,CAAA,GAAI,SAAA,GAAY,GAAG,SAAS,CAAA,CAAA,CAAA;AACjE,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,cAAc,CAAA,CAAA;AACnC;;;ACMO,IAAM,mBAAN,MAAuB;AAAA,EAK5B,YAAY,MAAA,EAA0B;AACpC,IAAA,IAAI,CAAC,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAK,EAAG;AAC1B,MAAA,MAAM,IAAI,mBAAmB,8BAA8B,CAAA;AAAA,IAC7D;AACA,IAAA,IAAA,CAAK,aAAa,MAAA,CAAO,MAAA;AACzB,IAAA,IAAA,CAAK,SAAA,GAAY,OAAO,SAAA,IAAa,EAAA;AACrC,IAAA,IAAA,CAAK,OAAA,GAAU,IAAIC,eAAA,CAAQ;AAAA,MACzB,aAAa,MAAA,CAAO,WAAA;AAAA,MACpB,WAAW,MAAA,CAAO;AAAA,KACnB,CAAA;AAAA,EACH;AAAA,EAEQ,MAAA,GAAS;AACf,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,MAAA,CAAO,IAAA,CAAK,UAAU,CAAA;AAAA,EAC5C;AAAA,EAEQ,QAAQ,YAAA,EAA8B;AAC5C,IAAA,OAAO,iBAAA,CAAkB,IAAA,CAAK,SAAA,EAAW,YAAY,CAAA;AAAA,EACvD;AAAA,EAEA,MAAM,MAAA,CAAO,GAAA,EAAa,MAAA,EAAgB,WAAA,EAA+D;AACvG,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,OAAA,CAAQ,GAAG,CAAA;AACjC,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,MAAA,EAAO,CAAE,KAAK,QAAQ,CAAA;AACxC,IAAA,MAAM,IAAA,CAAK,IAAA,CAAK,MAAA,EAAQ,EAAE,aAAa,CAAA;AACvC,IAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,OAAA,EAAS,IAAA,EAAK;AAAA,EACzC;AAAA,EAEA,MAAM,UAAA,CAAW,GAAA,EAAa,MAAA,EAAgB,WAAA,EAA+D;AAC3G,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,GAAA,EAAK,MAAA,EAAQ,WAAW,CAAA;AAAA,EAC7C;AAAA,EAEA,MAAM,SAAS,YAAA,EAAuC;AACpD,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,YAAY,CAAA;AACxC,IAAA,MAAM,CAAC,IAAI,CAAA,GAAI,MAAM,IAAA,CAAK,QAAO,CAAE,IAAA,CAAK,MAAM,CAAA,CAAE,QAAA,EAAS;AACzD,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,MAAM,IAAA,CAAK,MAAA,EAAgB,OAAA,EAAgC;AACzD,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,OAAA,CAAQ,MAAM,CAAA;AAC/B,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,OAAO,CAAA;AACjC,IAAA,MAAM,IAAA,CAAK,MAAA,EAAO,CAAE,IAAA,CAAK,GAAG,CAAA,CAAE,IAAA,CAAK,IAAA,CAAK,MAAA,EAAO,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,EAC7D;AAAA,EAEA,MAAM,IAAA,CACJ,UAAA,GAAqB,IACrB,KAAA,GAAgB,CAAA,EAChB,SAAiB,EAAA,EACY;AAC7B,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,UAAU,CAAA;AACtC,IAAA,MAAM,CAAC,KAAK,CAAA,GAAI,MAAM,IAAA,CAAK,MAAA,GAAS,QAAA,CAAS;AAAA,MAC3C,MAAA;AAAA,MACA,YAAY,KAAA,IAAS;AAAA,KACtB,CAAA;AAED,IAAA,MAAM,cAAA,GAAiB,IAAA,CAAK,SAAA,GAAY,CAAA,EAAG,IAAA,CAAK,UAAU,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,CAAA,CAAA,GAAM,EAAA;AAElF,IAAA,MAAM,QAAA,GAAW,MACd,MAAA,CAAO,CAAC,MAAM,EAAE,CAAA,CAAE,KAAK,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA,CAAE,KAAA,CAAM,GAAG,CAAA,CAAE,MAAA,GAAS,EAAE,CAAA,CACjE,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,MACX,GAAA,EAAK,cAAA,GAAiB,CAAA,CAAE,IAAA,CAAK,MAAM,cAAc,CAAA,CAAE,CAAC,CAAA,IAAK,EAAE,IAAA,GAAO,CAAA,CAAE,IAAA,CAAK,OAAA,CAAQ,QAAQ,EAAE;AAAA,KAC7F,CAAE,CAAA;AAEJ,IAAA,MAAM,YAAA,GAAe,MAClB,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,IAAA,CAAK,QAAQ,MAAA,EAAQ,EAAE,EAAE,KAAA,CAAM,GAAG,EAAE,MAAA,GAAS,CAAC,EAC9D,MAAA,CAAkC,CAAC,KAAK,GAAA,KAAQ;AAC/C,MAAA,MAAM,QAAA,GAAW,GAAA,CAAI,IAAA,CAAK,OAAA,CAAQ,QAAQ,EAAE,CAAA;AAC5C,MAAA,MAAM,eAAe,CAAA,EAAG,QAAA,CAAS,MAAM,GAAG,CAAA,CAAE,CAAC,CAAC,CAAA,CAAA,CAAA;AAC9C,MAAA,IAAI,CAAC,IAAI,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,MAAA,KAAW,YAAY,CAAA,EAAG;AAC/C,QAAA,GAAA,CAAI,IAAA,CAAK,EAAE,MAAA,EAAQ,YAAA,EAAc,CAAA;AAAA,MACnC;AACA,MAAA,OAAO,GAAA;AAAA,IACT,CAAA,EAAG,EAAE,CAAA,CACJ,IAAI,CAAC,CAAA,MAAO,EAAE,MAAA,EAAQ,GAAG,UAAU,CAAA,EAAG,CAAA,CAAE,MAAM,IAAG,CAAE,CAAA;AAEtD,IAAA,OAAO;AAAA,MACL,MAAM,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA,IAAK,SAAA;AAAA,MAC3C,MAAA,EAAQ,UAAA;AAAA,MACR,SAAA,EAAW,KAAA;AAAA,MACX,MAAA,EAAQ,MAAA;AAAA,MACR,UAAA,EAAY,EAAA;AAAA,MACZ,WAAA,EAAa,KAAA;AAAA,MACb,QAAA,EAAU,QAAA;AAAA,MACV,cAAA,EAAgB;AAAA,KAClB;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,YAAA,EAAqC;AACpD,IAAA,MAAM,IAAA,CAAK,QAAO,CAAE,IAAA,CAAK,KAAK,OAAA,CAAQ,YAAY,CAAC,CAAA,CAAE,MAAA,EAAO;AAAA,EAC9D;AAAA,EAEA,MAAM,eAAe,cAAA,EAAuC;AAC1D,IAAA,MAAM,IAAA,CAAK,MAAA,EAAO,CAAE,WAAA,CAAY,EAAE,QAAQ,IAAA,CAAK,OAAA,CAAQ,cAAc,CAAA,EAAG,CAAA;AAAA,EAC1E;AAAA;AAAA,EAGA,YAAA,GAAe;AACb,IAAA,OAAO,KAAK,MAAA,EAAO;AAAA,EACrB;AACF;;;ACjHO,SAAS,oBAAA,CAAqB,cAAsB,MAAA,EAAiC;AAC1F,EAAA,MAAM,GAAA,GAAM,YAAA,CAAa,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AAE3C,EAAA,IAAI,MAAA,CAAO,aAAa,IAAA,EAAM;AAC5B,IAAA,MAAM,MAAA,GAAS,OAAO,MAAA,IAAU,EAAA;AAChC,IAAA,MAAM,MAAA,GAAS,OAAO,MAAA,IAAU,WAAA;AAChC,IAAA,MAAMC,KAAAA,GAAO,CAAA,QAAA,EAAW,MAAM,CAAA,IAAA,EAAO,MAAM,CAAA,cAAA,CAAA;AAC3C,IAAA,OAAO,CAAA,EAAGA,KAAI,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA;AAAA,EACvB;AAEA,EAAA,IAAI,MAAA,CAAO,aAAa,KAAA,EAAO;AAC7B,IAAA,MAAM,SAAA,GAAY,OAAO,SAAA,IAAa,EAAA;AACtC,IAAA,MAAM,SAAS,iBAAA,CAAkB,SAAA,CAAU,QAAQ,KAAA,EAAO,EAAE,GAAG,GAAG,CAAA;AAClE,IAAA,MAAMA,SAAQ,MAAA,CAAO,OAAA,IAAW,EAAA,EAAI,OAAA,CAAQ,OAAO,EAAE,CAAA;AACrD,IAAA,IAAIA,KAAAA,EAAM;AACR,MAAA,OAAO,CAAA,EAAGA,KAAI,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAAA,IAC1B;AACA,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,SAAA,IAAa,MAAA,CAAO,MAAA,IAAU,EAAA;AACpD,IAAA,OAAO,CAAA,+BAAA,EAAkC,MAAM,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAAA,EAC3D;AAEA,EAAA,IAAI,UAAU,GAAA,CAAI,UAAA,CAAW,UAAU,CAAA,GAAI,GAAA,GAAM,WAAW,GAAG,CAAA,CAAA;AAC/D,EAAA,MAAM,QAAQ,MAAA,CAAO,OAAA,IAAW,EAAA,EAAI,OAAA,CAAQ,OAAO,EAAE,CAAA;AACrD,EAAA,IAAI,KAAK,QAAA,CAAS,UAAU,KAAK,OAAA,CAAQ,UAAA,CAAW,UAAU,CAAA,EAAG;AAC/D,IAAA,OAAA,GAAU,OAAA,CAAQ,OAAA,CAAQ,YAAA,EAAc,EAAE,CAAA;AAAA,EAC5C;AACA,EAAA,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA;AAC3B","file":"index.cjs","sourcesContent":["export class StorageConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'StorageConfigError';\n }\n}\n\nexport class StorageApiError extends Error {\n constructor(message: string, readonly cause?: unknown) {\n super(message);\n this.name = 'StorageApiError';\n }\n}\n","import {\n DeleteObjectCommand,\n DeleteObjectsCommand,\n GetObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n S3Client,\n type _Object,\n} from '@aws-sdk/client-s3';\nimport type { S3StorageConfig } from './config';\nimport { StorageConfigError } from './errors';\n\nexport interface S3ListObjectsOptions {\n limit?: number;\n marker?: string;\n delimiter?: string;\n}\n\nexport interface S3ListObjectsResult {\n Name?: string;\n Prefix?: string;\n Delimiter?: string;\n Marker?: string;\n NextMarker?: string;\n IsTruncated?: boolean;\n Contents?: Array<{ Key?: string }>;\n CommonPrefixes?: Array<{ Prefix?: string }>;\n}\n\nexport class S3StorageClient {\n private readonly client: S3Client;\n private readonly bucket: string;\n\n constructor(config: S3StorageConfig) {\n if (!config.bucket?.trim()) {\n throw new StorageConfigError('S3 bucket is not configured');\n }\n if (!config.region?.trim()) {\n throw new StorageConfigError('S3 region is not configured');\n }\n\n this.bucket = config.bucket;\n this.client = new S3Client({\n region: config.region,\n ...(config.accessKeyId && config.secretAccessKey\n ? {\n credentials: {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n },\n }\n : {}),\n });\n }\n\n async putObject(\n key: string,\n body: Buffer | Uint8Array | string,\n contentType?: string,\n contentEncoding?: string\n ): Promise<void> {\n await this.client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: key,\n Body: body,\n ...(contentType ? { ContentType: contentType } : {}),\n ...(contentEncoding ? { ContentEncoding: contentEncoding } : {}),\n })\n );\n }\n\n async getObjectBuffer(key: string): Promise<Buffer> {\n const response = await this.client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: key,\n })\n );\n if (!response.Body) {\n throw new Error(`Object not found: ${key}`);\n }\n const byteArray = await response.Body.transformToByteArray();\n return Buffer.from(byteArray);\n }\n\n async listObjects(\n prefix: string = '',\n options: S3ListObjectsOptions = {}\n ): Promise<S3ListObjectsResult> {\n const response = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n Delimiter: options.delimiter,\n MaxKeys: options.limit || undefined,\n ...(options.marker ? { StartAfter: options.marker } : {}),\n })\n );\n\n return {\n Name: this.bucket,\n Prefix: prefix,\n Delimiter: options.delimiter,\n Marker: options.marker,\n NextMarker: response.NextContinuationToken,\n IsTruncated: response.IsTruncated,\n Contents: (response.Contents || []).map((obj: _Object) => ({ Key: obj.Key })),\n CommonPrefixes: (response.CommonPrefixes || []).map((cp) => ({ Prefix: cp.Prefix })),\n };\n }\n\n async deleteObject(key: string): Promise<void> {\n await this.client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: key,\n })\n );\n }\n\n async deleteObjectsByPrefix(prefix: string): Promise<void> {\n const listed = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n })\n );\n const objects = (listed.Contents || [])\n .filter((obj) => obj.Key)\n .map((obj) => ({ Key: obj.Key! }));\n\n if (objects.length === 0) {\n return;\n }\n\n await this.client.send(\n new DeleteObjectsCommand({\n Bucket: this.bucket,\n Delete: {\n Objects: objects,\n Quiet: true,\n },\n })\n );\n }\n\n async createFolderPrefix(key: string): Promise<void> {\n await this.putObject(key, Buffer.alloc(0));\n }\n}\n","/**\n * Join a configurable key prefix with a relative object path.\n */\nexport function resolveStorageKey(keyPrefix: string | undefined, relativePath: string): string {\n const normalizedPath = relativePath.replace(/^\\/+/, '');\n if (!keyPrefix) {\n return normalizedPath;\n }\n const prefix = keyPrefix.endsWith('/') ? keyPrefix : `${keyPrefix}/`;\n return `${prefix}${normalizedPath}`;\n}\n","import { Storage } from '@google-cloud/storage';\nimport type { GcpStorageConfig } from './config';\nimport { StorageConfigError } from './errors';\nimport { resolveStorageKey } from './keys';\n\nexport interface GcpListBlobsResult {\n Name: string;\n Prefix: string;\n Delimiter: number;\n Marker: string;\n NextMarker: string;\n IsTruncated: boolean;\n Contents: Array<{ Key: string }>;\n CommonPrefixes: Array<{ Prefix: string }>;\n}\n\nexport class GcpStorageClient {\n private readonly storage: Storage;\n private readonly bucketName: string;\n private readonly keyPrefix: string;\n\n constructor(config: GcpStorageConfig) {\n if (!config.bucket?.trim()) {\n throw new StorageConfigError('GCP bucket is not configured');\n }\n this.bucketName = config.bucket;\n this.keyPrefix = config.keyPrefix || '';\n this.storage = new Storage({\n keyFilename: config.keyFilename,\n projectId: config.projectId,\n });\n }\n\n private bucket() {\n return this.storage.bucket(this.bucketName);\n }\n\n private fullKey(relativePath: string): string {\n return resolveStorageKey(this.keyPrefix, relativePath);\n }\n\n async upload(key: string, buffer: Buffer, contentType: string): Promise<{ path: string; success: true }> {\n const filePath = this.fullKey(key);\n const file = this.bucket().file(filePath);\n await file.save(buffer, { contentType });\n return { path: filePath, success: true };\n }\n\n async uploadFile(key: string, buffer: Buffer, contentType: string): Promise<{ path: string; success: true }> {\n return this.upload(key, buffer, contentType);\n }\n\n async download(relativePath: string): Promise<Buffer> {\n const prefix = this.fullKey(relativePath);\n const [data] = await this.bucket().file(prefix).download();\n return data;\n }\n\n async copy(srcKey: string, destKey: string): Promise<void> {\n const src = this.fullKey(srcKey);\n const dest = this.fullKey(destKey);\n await this.bucket().file(src).copy(this.bucket().file(dest));\n }\n\n async list(\n folderName: string = '',\n limit: number = 0,\n marker: string = ''\n ): Promise<GcpListBlobsResult> {\n const prefix = this.fullKey(folderName);\n const [files] = await this.bucket().getFiles({\n prefix,\n maxResults: limit || undefined,\n });\n\n const prefixForSplit = this.keyPrefix ? `${this.keyPrefix.replace(/\\/$/, '')}/` : '';\n\n const contents = files\n .filter((t) => !(t.name.replace(prefix, '').split('/').length - 1))\n .map((t) => ({\n Key: prefixForSplit ? t.name.split(prefixForSplit)[1] || t.name : t.name.replace(prefix, ''),\n }));\n\n const commonPrefix = files\n .filter((t) => t.name.replace(prefix, '').split('/').length - 1)\n .reduce<Array<{ Prefix: string }>>((acc, val) => {\n const relative = val.name.replace(prefix, '');\n const folderPrefix = `${relative.split('/')[0]}/`;\n if (!acc.some((t) => t.Prefix === folderPrefix)) {\n acc.push({ Prefix: folderPrefix });\n }\n return acc;\n }, [])\n .map((t) => ({ Prefix: `${folderName}${t.Prefix}` }));\n\n return {\n Name: this.keyPrefix.replace(/\\/$/, '') || 'storage',\n Prefix: folderName,\n Delimiter: limit,\n Marker: marker,\n NextMarker: '',\n IsTruncated: false,\n Contents: contents,\n CommonPrefixes: commonPrefix,\n };\n }\n\n async deleteFile(relativePath: string): Promise<void> {\n await this.bucket().file(this.fullKey(relativePath)).delete();\n }\n\n async deleteByPrefix(relativePrefix: string): Promise<void> {\n await this.bucket().deleteFiles({ prefix: this.fullKey(relativePrefix) });\n }\n\n /** Raw bucket access for app-specific paths outside keyPrefix. */\n getRawBucket() {\n return this.bucket();\n }\n}\n","import type { PublicUrlConfig } from './config';\nimport { resolveStorageKey } from './keys';\n\n/**\n * Build a public URL for an object key after upload.\n */\nexport function buildPublicUploadUrl(relativePath: string, config: PublicUrlConfig): string {\n const key = relativePath.replace(/^\\/+/, '');\n\n if (config.provider === 's3') {\n const bucket = config.bucket || '';\n const region = config.region || 'us-east-1';\n const base = `https://${bucket}.s3.${region}.amazonaws.com`;\n return `${base}/${key}`;\n }\n\n if (config.provider === 'gcp') {\n const gcpPrefix = config.gcpPrefix || '';\n const gcpKey = resolveStorageKey(gcpPrefix.replace(/\\/$/, ''), key);\n const base = (config.baseUrl || '').replace(/\\/$/, '');\n if (base) {\n return `${base}/${gcpKey}`;\n }\n const bucket = config.gcpBucket || config.bucket || '';\n return `https://storage.googleapis.com/${bucket}/${gcpKey}`;\n }\n\n let urlPath = key.startsWith('uploads/') ? key : `uploads/${key}`;\n const base = (config.baseUrl || '').replace(/\\/$/, '');\n if (base.endsWith('/uploads') && urlPath.startsWith('uploads/')) {\n urlPath = urlPath.replace(/^uploads\\//, '');\n }\n return `${base}/${urlPath}`;\n}\n"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/s3Client.ts","../src/keys.ts","../src/gcpClient.ts","../src/publicUrl.ts"],"names":["getSignedUrl","S3Client","PutObjectCommand","GetObjectCommand","ListObjectsV2Command","DeleteObjectCommand","DeleteObjectsCommand","Storage","base"],"mappings":"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","file":"index.cjs","sourcesContent":["export class StorageConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'StorageConfigError';\n }\n}\n\nexport class StorageApiError extends Error {\n constructor(message: string, readonly cause?: unknown) {\n super(message);\n this.name = 'StorageApiError';\n }\n}\n","import {\n DeleteObjectCommand,\n DeleteObjectsCommand,\n GetObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n S3Client,\n type _Object,\n} from '@aws-sdk/client-s3';\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner';\nimport type { S3StorageConfig } from './config';\nimport { StorageConfigError } from './errors';\n\n// Presigner and client-s3 can resolve different @smithy/types copies; the runtime objects are compatible.\nconst signGetObject = getSignedUrl as unknown as (\n client: S3Client,\n command: GetObjectCommand,\n options: { expiresIn: number }\n) => Promise<string>;\n\nexport interface S3PresignedGetOptions {\n /** URL lifetime; S3 caps SigV4 presigned URLs at 7 days. */\n expiresInSeconds: number;\n /** Overrides the response Content-Type header. */\n contentType?: string;\n /** Overrides the response Content-Disposition header, e.g. `attachment; filename=\"x.pdf\"`. */\n contentDisposition?: string;\n}\n\nexport interface S3ListObjectsOptions {\n limit?: number;\n marker?: string;\n delimiter?: string;\n}\n\nexport interface S3ListObjectsResult {\n Name?: string;\n Prefix?: string;\n Delimiter?: string;\n Marker?: string;\n NextMarker?: string;\n IsTruncated?: boolean;\n Contents?: Array<{ Key?: string }>;\n CommonPrefixes?: Array<{ Prefix?: string }>;\n}\n\nexport class S3StorageClient {\n private readonly client: S3Client;\n private readonly bucket: string;\n\n constructor(config: S3StorageConfig) {\n if (!config.bucket?.trim()) {\n throw new StorageConfigError('S3 bucket is not configured');\n }\n if (!config.region?.trim()) {\n throw new StorageConfigError('S3 region is not configured');\n }\n\n this.bucket = config.bucket;\n this.client = new S3Client({\n region: config.region,\n ...(config.accessKeyId && config.secretAccessKey\n ? {\n credentials: {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n },\n }\n : {}),\n });\n }\n\n async putObject(\n key: string,\n body: Buffer | Uint8Array | string,\n contentType?: string,\n contentEncoding?: string\n ): Promise<void> {\n await this.client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: key,\n Body: body,\n ...(contentType ? { ContentType: contentType } : {}),\n ...(contentEncoding ? { ContentEncoding: contentEncoding } : {}),\n })\n );\n }\n\n async getObjectBuffer(key: string): Promise<Buffer> {\n const response = await this.client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: key,\n })\n );\n if (!response.Body) {\n throw new Error(`Object not found: ${key}`);\n }\n const byteArray = await response.Body.transformToByteArray();\n return Buffer.from(byteArray);\n }\n\n async getPresignedGetUrl(key: string, options: S3PresignedGetOptions): Promise<string> {\n const expiresIn = Math.min(Math.max(Math.floor(options.expiresInSeconds), 1), 7 * 24 * 60 * 60);\n return signGetObject(\n this.client,\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: key,\n ...(options.contentType ? { ResponseContentType: options.contentType } : {}),\n ...(options.contentDisposition\n ? { ResponseContentDisposition: options.contentDisposition }\n : {}),\n }),\n { expiresIn }\n );\n }\n\n async listObjects(\n prefix: string = '',\n options: S3ListObjectsOptions = {}\n ): Promise<S3ListObjectsResult> {\n const response = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n Delimiter: options.delimiter,\n MaxKeys: options.limit || undefined,\n ...(options.marker ? { StartAfter: options.marker } : {}),\n })\n );\n\n return {\n Name: this.bucket,\n Prefix: prefix,\n Delimiter: options.delimiter,\n Marker: options.marker,\n NextMarker: response.NextContinuationToken,\n IsTruncated: response.IsTruncated,\n Contents: (response.Contents || []).map((obj: _Object) => ({ Key: obj.Key })),\n CommonPrefixes: (response.CommonPrefixes || []).map((cp) => ({ Prefix: cp.Prefix })),\n };\n }\n\n async deleteObject(key: string): Promise<void> {\n await this.client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: key,\n })\n );\n }\n\n async deleteObjectsByPrefix(prefix: string): Promise<void> {\n const listed = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n })\n );\n const objects = (listed.Contents || [])\n .filter((obj) => obj.Key)\n .map((obj) => ({ Key: obj.Key! }));\n\n if (objects.length === 0) {\n return;\n }\n\n await this.client.send(\n new DeleteObjectsCommand({\n Bucket: this.bucket,\n Delete: {\n Objects: objects,\n Quiet: true,\n },\n })\n );\n }\n\n async createFolderPrefix(key: string): Promise<void> {\n await this.putObject(key, Buffer.alloc(0));\n }\n}\n","/**\n * Join a configurable key prefix with a relative object path.\n */\nexport function resolveStorageKey(keyPrefix: string | undefined, relativePath: string): string {\n const normalizedPath = relativePath.replace(/^\\/+/, '');\n if (!keyPrefix) {\n return normalizedPath;\n }\n const prefix = keyPrefix.endsWith('/') ? keyPrefix : `${keyPrefix}/`;\n return `${prefix}${normalizedPath}`;\n}\n","import { Storage } from '@google-cloud/storage';\nimport type { GcpStorageConfig } from './config';\nimport { StorageConfigError } from './errors';\nimport { resolveStorageKey } from './keys';\n\nexport interface GcpListBlobsResult {\n Name: string;\n Prefix: string;\n Delimiter: number;\n Marker: string;\n NextMarker: string;\n IsTruncated: boolean;\n Contents: Array<{ Key: string }>;\n CommonPrefixes: Array<{ Prefix: string }>;\n}\n\nexport class GcpStorageClient {\n private readonly storage: Storage;\n private readonly bucketName: string;\n private readonly keyPrefix: string;\n\n constructor(config: GcpStorageConfig) {\n if (!config.bucket?.trim()) {\n throw new StorageConfigError('GCP bucket is not configured');\n }\n this.bucketName = config.bucket;\n this.keyPrefix = config.keyPrefix || '';\n this.storage = new Storage({\n keyFilename: config.keyFilename,\n projectId: config.projectId,\n });\n }\n\n private bucket() {\n return this.storage.bucket(this.bucketName);\n }\n\n private fullKey(relativePath: string): string {\n return resolveStorageKey(this.keyPrefix, relativePath);\n }\n\n async upload(key: string, buffer: Buffer, contentType: string): Promise<{ path: string; success: true }> {\n const filePath = this.fullKey(key);\n const file = this.bucket().file(filePath);\n await file.save(buffer, { contentType });\n return { path: filePath, success: true };\n }\n\n async uploadFile(key: string, buffer: Buffer, contentType: string): Promise<{ path: string; success: true }> {\n return this.upload(key, buffer, contentType);\n }\n\n async download(relativePath: string): Promise<Buffer> {\n const prefix = this.fullKey(relativePath);\n const [data] = await this.bucket().file(prefix).download();\n return data;\n }\n\n async copy(srcKey: string, destKey: string): Promise<void> {\n const src = this.fullKey(srcKey);\n const dest = this.fullKey(destKey);\n await this.bucket().file(src).copy(this.bucket().file(dest));\n }\n\n async list(\n folderName: string = '',\n limit: number = 0,\n marker: string = ''\n ): Promise<GcpListBlobsResult> {\n const prefix = this.fullKey(folderName);\n const [files] = await this.bucket().getFiles({\n prefix,\n maxResults: limit || undefined,\n });\n\n const prefixForSplit = this.keyPrefix ? `${this.keyPrefix.replace(/\\/$/, '')}/` : '';\n\n const contents = files\n .filter((t) => !(t.name.replace(prefix, '').split('/').length - 1))\n .map((t) => ({\n Key: prefixForSplit ? t.name.split(prefixForSplit)[1] || t.name : t.name.replace(prefix, ''),\n }));\n\n const commonPrefix = files\n .filter((t) => t.name.replace(prefix, '').split('/').length - 1)\n .reduce<Array<{ Prefix: string }>>((acc, val) => {\n const relative = val.name.replace(prefix, '');\n const folderPrefix = `${relative.split('/')[0]}/`;\n if (!acc.some((t) => t.Prefix === folderPrefix)) {\n acc.push({ Prefix: folderPrefix });\n }\n return acc;\n }, [])\n .map((t) => ({ Prefix: `${folderName}${t.Prefix}` }));\n\n return {\n Name: this.keyPrefix.replace(/\\/$/, '') || 'storage',\n Prefix: folderName,\n Delimiter: limit,\n Marker: marker,\n NextMarker: '',\n IsTruncated: false,\n Contents: contents,\n CommonPrefixes: commonPrefix,\n };\n }\n\n async deleteFile(relativePath: string): Promise<void> {\n await this.bucket().file(this.fullKey(relativePath)).delete();\n }\n\n async deleteByPrefix(relativePrefix: string): Promise<void> {\n await this.bucket().deleteFiles({ prefix: this.fullKey(relativePrefix) });\n }\n\n /** Raw bucket access for app-specific paths outside keyPrefix. */\n getRawBucket() {\n return this.bucket();\n }\n}\n","import type { PublicUrlConfig } from './config';\nimport { resolveStorageKey } from './keys';\n\n/**\n * Build a public URL for an object key after upload.\n */\nexport function buildPublicUploadUrl(relativePath: string, config: PublicUrlConfig): string {\n const key = relativePath.replace(/^\\/+/, '');\n\n if (config.provider === 's3') {\n const bucket = config.bucket || '';\n const region = config.region || 'us-east-1';\n const base = `https://${bucket}.s3.${region}.amazonaws.com`;\n return `${base}/${key}`;\n }\n\n if (config.provider === 'gcp') {\n const gcpPrefix = config.gcpPrefix || '';\n const gcpKey = resolveStorageKey(gcpPrefix.replace(/\\/$/, ''), key);\n const base = (config.baseUrl || '').replace(/\\/$/, '');\n if (base) {\n return `${base}/${gcpKey}`;\n }\n const bucket = config.gcpBucket || config.bucket || '';\n return `https://storage.googleapis.com/${bucket}/${gcpKey}`;\n }\n\n let urlPath = key.startsWith('uploads/') ? key : `uploads/${key}`;\n const base = (config.baseUrl || '').replace(/\\/$/, '');\n if (base.endsWith('/uploads') && urlPath.startsWith('uploads/')) {\n urlPath = urlPath.replace(/^uploads\\//, '');\n }\n return `${base}/${urlPath}`;\n}\n"]}
package/dist/index.d.mts CHANGED
@@ -22,6 +22,14 @@ interface PublicUrlConfig {
22
22
  gcpBucket?: string;
23
23
  }
24
24
 
25
+ interface S3PresignedGetOptions {
26
+ /** URL lifetime; S3 caps SigV4 presigned URLs at 7 days. */
27
+ expiresInSeconds: number;
28
+ /** Overrides the response Content-Type header. */
29
+ contentType?: string;
30
+ /** Overrides the response Content-Disposition header, e.g. `attachment; filename="x.pdf"`. */
31
+ contentDisposition?: string;
32
+ }
25
33
  interface S3ListObjectsOptions {
26
34
  limit?: number;
27
35
  marker?: string;
@@ -47,6 +55,7 @@ declare class S3StorageClient {
47
55
  constructor(config: S3StorageConfig);
48
56
  putObject(key: string, body: Buffer | Uint8Array | string, contentType?: string, contentEncoding?: string): Promise<void>;
49
57
  getObjectBuffer(key: string): Promise<Buffer>;
58
+ getPresignedGetUrl(key: string, options: S3PresignedGetOptions): Promise<string>;
50
59
  listObjects(prefix?: string, options?: S3ListObjectsOptions): Promise<S3ListObjectsResult>;
51
60
  deleteObject(key: string): Promise<void>;
52
61
  deleteObjectsByPrefix(prefix: string): Promise<void>;
@@ -109,4 +118,4 @@ declare function resolveStorageKey(keyPrefix: string | undefined, relativePath:
109
118
  */
110
119
  declare function buildPublicUploadUrl(relativePath: string, config: PublicUrlConfig): string;
111
120
 
112
- export { type GcpListBlobsResult, GcpStorageClient, type GcpStorageConfig, type PublicUrlConfig, type S3ListObjectsOptions, type S3ListObjectsResult, S3StorageClient, type S3StorageConfig, StorageApiError, StorageConfigError, type StorageProvider, buildPublicUploadUrl, resolveStorageKey };
121
+ export { type GcpListBlobsResult, GcpStorageClient, type GcpStorageConfig, type PublicUrlConfig, type S3ListObjectsOptions, type S3ListObjectsResult, type S3PresignedGetOptions, S3StorageClient, type S3StorageConfig, StorageApiError, StorageConfigError, type StorageProvider, buildPublicUploadUrl, resolveStorageKey };
package/dist/index.d.ts CHANGED
@@ -22,6 +22,14 @@ interface PublicUrlConfig {
22
22
  gcpBucket?: string;
23
23
  }
24
24
 
25
+ interface S3PresignedGetOptions {
26
+ /** URL lifetime; S3 caps SigV4 presigned URLs at 7 days. */
27
+ expiresInSeconds: number;
28
+ /** Overrides the response Content-Type header. */
29
+ contentType?: string;
30
+ /** Overrides the response Content-Disposition header, e.g. `attachment; filename="x.pdf"`. */
31
+ contentDisposition?: string;
32
+ }
25
33
  interface S3ListObjectsOptions {
26
34
  limit?: number;
27
35
  marker?: string;
@@ -47,6 +55,7 @@ declare class S3StorageClient {
47
55
  constructor(config: S3StorageConfig);
48
56
  putObject(key: string, body: Buffer | Uint8Array | string, contentType?: string, contentEncoding?: string): Promise<void>;
49
57
  getObjectBuffer(key: string): Promise<Buffer>;
58
+ getPresignedGetUrl(key: string, options: S3PresignedGetOptions): Promise<string>;
50
59
  listObjects(prefix?: string, options?: S3ListObjectsOptions): Promise<S3ListObjectsResult>;
51
60
  deleteObject(key: string): Promise<void>;
52
61
  deleteObjectsByPrefix(prefix: string): Promise<void>;
@@ -109,4 +118,4 @@ declare function resolveStorageKey(keyPrefix: string | undefined, relativePath:
109
118
  */
110
119
  declare function buildPublicUploadUrl(relativePath: string, config: PublicUrlConfig): string;
111
120
 
112
- export { type GcpListBlobsResult, GcpStorageClient, type GcpStorageConfig, type PublicUrlConfig, type S3ListObjectsOptions, type S3ListObjectsResult, S3StorageClient, type S3StorageConfig, StorageApiError, StorageConfigError, type StorageProvider, buildPublicUploadUrl, resolveStorageKey };
121
+ export { type GcpListBlobsResult, GcpStorageClient, type GcpStorageConfig, type PublicUrlConfig, type S3ListObjectsOptions, type S3ListObjectsResult, type S3PresignedGetOptions, S3StorageClient, type S3StorageConfig, StorageApiError, StorageConfigError, type StorageProvider, buildPublicUploadUrl, resolveStorageKey };
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { S3Client, PutObjectCommand, GetObjectCommand, ListObjectsV2Command, DeleteObjectCommand, DeleteObjectsCommand } from '@aws-sdk/client-s3';
2
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
2
3
  import { Storage } from '@google-cloud/storage';
3
4
 
4
5
  // src/s3Client.ts
@@ -19,6 +20,7 @@ var StorageApiError = class extends Error {
19
20
  };
20
21
 
21
22
  // src/s3Client.ts
23
+ var signGetObject = getSignedUrl;
22
24
  var S3StorageClient = class {
23
25
  constructor(config) {
24
26
  if (!config.bucket?.trim()) {
@@ -62,6 +64,19 @@ var S3StorageClient = class {
62
64
  const byteArray = await response.Body.transformToByteArray();
63
65
  return Buffer.from(byteArray);
64
66
  }
67
+ async getPresignedGetUrl(key, options) {
68
+ const expiresIn = Math.min(Math.max(Math.floor(options.expiresInSeconds), 1), 7 * 24 * 60 * 60);
69
+ return signGetObject(
70
+ this.client,
71
+ new GetObjectCommand({
72
+ Bucket: this.bucket,
73
+ Key: key,
74
+ ...options.contentType ? { ResponseContentType: options.contentType } : {},
75
+ ...options.contentDisposition ? { ResponseContentDisposition: options.contentDisposition } : {}
76
+ }),
77
+ { expiresIn }
78
+ );
79
+ }
65
80
  async listObjects(prefix = "", options = {}) {
66
81
  const response = await this.client.send(
67
82
  new ListObjectsV2Command({
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/s3Client.ts","../src/keys.ts","../src/gcpClient.ts","../src/publicUrl.ts"],"names":["base"],"mappings":"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","file":"index.js","sourcesContent":["export class StorageConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'StorageConfigError';\n }\n}\n\nexport class StorageApiError extends Error {\n constructor(message: string, readonly cause?: unknown) {\n super(message);\n this.name = 'StorageApiError';\n }\n}\n","import {\n DeleteObjectCommand,\n DeleteObjectsCommand,\n GetObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n S3Client,\n type _Object,\n} from '@aws-sdk/client-s3';\nimport type { S3StorageConfig } from './config';\nimport { StorageConfigError } from './errors';\n\nexport interface S3ListObjectsOptions {\n limit?: number;\n marker?: string;\n delimiter?: string;\n}\n\nexport interface S3ListObjectsResult {\n Name?: string;\n Prefix?: string;\n Delimiter?: string;\n Marker?: string;\n NextMarker?: string;\n IsTruncated?: boolean;\n Contents?: Array<{ Key?: string }>;\n CommonPrefixes?: Array<{ Prefix?: string }>;\n}\n\nexport class S3StorageClient {\n private readonly client: S3Client;\n private readonly bucket: string;\n\n constructor(config: S3StorageConfig) {\n if (!config.bucket?.trim()) {\n throw new StorageConfigError('S3 bucket is not configured');\n }\n if (!config.region?.trim()) {\n throw new StorageConfigError('S3 region is not configured');\n }\n\n this.bucket = config.bucket;\n this.client = new S3Client({\n region: config.region,\n ...(config.accessKeyId && config.secretAccessKey\n ? {\n credentials: {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n },\n }\n : {}),\n });\n }\n\n async putObject(\n key: string,\n body: Buffer | Uint8Array | string,\n contentType?: string,\n contentEncoding?: string\n ): Promise<void> {\n await this.client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: key,\n Body: body,\n ...(contentType ? { ContentType: contentType } : {}),\n ...(contentEncoding ? { ContentEncoding: contentEncoding } : {}),\n })\n );\n }\n\n async getObjectBuffer(key: string): Promise<Buffer> {\n const response = await this.client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: key,\n })\n );\n if (!response.Body) {\n throw new Error(`Object not found: ${key}`);\n }\n const byteArray = await response.Body.transformToByteArray();\n return Buffer.from(byteArray);\n }\n\n async listObjects(\n prefix: string = '',\n options: S3ListObjectsOptions = {}\n ): Promise<S3ListObjectsResult> {\n const response = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n Delimiter: options.delimiter,\n MaxKeys: options.limit || undefined,\n ...(options.marker ? { StartAfter: options.marker } : {}),\n })\n );\n\n return {\n Name: this.bucket,\n Prefix: prefix,\n Delimiter: options.delimiter,\n Marker: options.marker,\n NextMarker: response.NextContinuationToken,\n IsTruncated: response.IsTruncated,\n Contents: (response.Contents || []).map((obj: _Object) => ({ Key: obj.Key })),\n CommonPrefixes: (response.CommonPrefixes || []).map((cp) => ({ Prefix: cp.Prefix })),\n };\n }\n\n async deleteObject(key: string): Promise<void> {\n await this.client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: key,\n })\n );\n }\n\n async deleteObjectsByPrefix(prefix: string): Promise<void> {\n const listed = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n })\n );\n const objects = (listed.Contents || [])\n .filter((obj) => obj.Key)\n .map((obj) => ({ Key: obj.Key! }));\n\n if (objects.length === 0) {\n return;\n }\n\n await this.client.send(\n new DeleteObjectsCommand({\n Bucket: this.bucket,\n Delete: {\n Objects: objects,\n Quiet: true,\n },\n })\n );\n }\n\n async createFolderPrefix(key: string): Promise<void> {\n await this.putObject(key, Buffer.alloc(0));\n }\n}\n","/**\n * Join a configurable key prefix with a relative object path.\n */\nexport function resolveStorageKey(keyPrefix: string | undefined, relativePath: string): string {\n const normalizedPath = relativePath.replace(/^\\/+/, '');\n if (!keyPrefix) {\n return normalizedPath;\n }\n const prefix = keyPrefix.endsWith('/') ? keyPrefix : `${keyPrefix}/`;\n return `${prefix}${normalizedPath}`;\n}\n","import { Storage } from '@google-cloud/storage';\nimport type { GcpStorageConfig } from './config';\nimport { StorageConfigError } from './errors';\nimport { resolveStorageKey } from './keys';\n\nexport interface GcpListBlobsResult {\n Name: string;\n Prefix: string;\n Delimiter: number;\n Marker: string;\n NextMarker: string;\n IsTruncated: boolean;\n Contents: Array<{ Key: string }>;\n CommonPrefixes: Array<{ Prefix: string }>;\n}\n\nexport class GcpStorageClient {\n private readonly storage: Storage;\n private readonly bucketName: string;\n private readonly keyPrefix: string;\n\n constructor(config: GcpStorageConfig) {\n if (!config.bucket?.trim()) {\n throw new StorageConfigError('GCP bucket is not configured');\n }\n this.bucketName = config.bucket;\n this.keyPrefix = config.keyPrefix || '';\n this.storage = new Storage({\n keyFilename: config.keyFilename,\n projectId: config.projectId,\n });\n }\n\n private bucket() {\n return this.storage.bucket(this.bucketName);\n }\n\n private fullKey(relativePath: string): string {\n return resolveStorageKey(this.keyPrefix, relativePath);\n }\n\n async upload(key: string, buffer: Buffer, contentType: string): Promise<{ path: string; success: true }> {\n const filePath = this.fullKey(key);\n const file = this.bucket().file(filePath);\n await file.save(buffer, { contentType });\n return { path: filePath, success: true };\n }\n\n async uploadFile(key: string, buffer: Buffer, contentType: string): Promise<{ path: string; success: true }> {\n return this.upload(key, buffer, contentType);\n }\n\n async download(relativePath: string): Promise<Buffer> {\n const prefix = this.fullKey(relativePath);\n const [data] = await this.bucket().file(prefix).download();\n return data;\n }\n\n async copy(srcKey: string, destKey: string): Promise<void> {\n const src = this.fullKey(srcKey);\n const dest = this.fullKey(destKey);\n await this.bucket().file(src).copy(this.bucket().file(dest));\n }\n\n async list(\n folderName: string = '',\n limit: number = 0,\n marker: string = ''\n ): Promise<GcpListBlobsResult> {\n const prefix = this.fullKey(folderName);\n const [files] = await this.bucket().getFiles({\n prefix,\n maxResults: limit || undefined,\n });\n\n const prefixForSplit = this.keyPrefix ? `${this.keyPrefix.replace(/\\/$/, '')}/` : '';\n\n const contents = files\n .filter((t) => !(t.name.replace(prefix, '').split('/').length - 1))\n .map((t) => ({\n Key: prefixForSplit ? t.name.split(prefixForSplit)[1] || t.name : t.name.replace(prefix, ''),\n }));\n\n const commonPrefix = files\n .filter((t) => t.name.replace(prefix, '').split('/').length - 1)\n .reduce<Array<{ Prefix: string }>>((acc, val) => {\n const relative = val.name.replace(prefix, '');\n const folderPrefix = `${relative.split('/')[0]}/`;\n if (!acc.some((t) => t.Prefix === folderPrefix)) {\n acc.push({ Prefix: folderPrefix });\n }\n return acc;\n }, [])\n .map((t) => ({ Prefix: `${folderName}${t.Prefix}` }));\n\n return {\n Name: this.keyPrefix.replace(/\\/$/, '') || 'storage',\n Prefix: folderName,\n Delimiter: limit,\n Marker: marker,\n NextMarker: '',\n IsTruncated: false,\n Contents: contents,\n CommonPrefixes: commonPrefix,\n };\n }\n\n async deleteFile(relativePath: string): Promise<void> {\n await this.bucket().file(this.fullKey(relativePath)).delete();\n }\n\n async deleteByPrefix(relativePrefix: string): Promise<void> {\n await this.bucket().deleteFiles({ prefix: this.fullKey(relativePrefix) });\n }\n\n /** Raw bucket access for app-specific paths outside keyPrefix. */\n getRawBucket() {\n return this.bucket();\n }\n}\n","import type { PublicUrlConfig } from './config';\nimport { resolveStorageKey } from './keys';\n\n/**\n * Build a public URL for an object key after upload.\n */\nexport function buildPublicUploadUrl(relativePath: string, config: PublicUrlConfig): string {\n const key = relativePath.replace(/^\\/+/, '');\n\n if (config.provider === 's3') {\n const bucket = config.bucket || '';\n const region = config.region || 'us-east-1';\n const base = `https://${bucket}.s3.${region}.amazonaws.com`;\n return `${base}/${key}`;\n }\n\n if (config.provider === 'gcp') {\n const gcpPrefix = config.gcpPrefix || '';\n const gcpKey = resolveStorageKey(gcpPrefix.replace(/\\/$/, ''), key);\n const base = (config.baseUrl || '').replace(/\\/$/, '');\n if (base) {\n return `${base}/${gcpKey}`;\n }\n const bucket = config.gcpBucket || config.bucket || '';\n return `https://storage.googleapis.com/${bucket}/${gcpKey}`;\n }\n\n let urlPath = key.startsWith('uploads/') ? key : `uploads/${key}`;\n const base = (config.baseUrl || '').replace(/\\/$/, '');\n if (base.endsWith('/uploads') && urlPath.startsWith('uploads/')) {\n urlPath = urlPath.replace(/^uploads\\//, '');\n }\n return `${base}/${urlPath}`;\n}\n"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/s3Client.ts","../src/keys.ts","../src/gcpClient.ts","../src/publicUrl.ts"],"names":["base"],"mappings":"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","file":"index.js","sourcesContent":["export class StorageConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'StorageConfigError';\n }\n}\n\nexport class StorageApiError extends Error {\n constructor(message: string, readonly cause?: unknown) {\n super(message);\n this.name = 'StorageApiError';\n }\n}\n","import {\n DeleteObjectCommand,\n DeleteObjectsCommand,\n GetObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n S3Client,\n type _Object,\n} from '@aws-sdk/client-s3';\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner';\nimport type { S3StorageConfig } from './config';\nimport { StorageConfigError } from './errors';\n\n// Presigner and client-s3 can resolve different @smithy/types copies; the runtime objects are compatible.\nconst signGetObject = getSignedUrl as unknown as (\n client: S3Client,\n command: GetObjectCommand,\n options: { expiresIn: number }\n) => Promise<string>;\n\nexport interface S3PresignedGetOptions {\n /** URL lifetime; S3 caps SigV4 presigned URLs at 7 days. */\n expiresInSeconds: number;\n /** Overrides the response Content-Type header. */\n contentType?: string;\n /** Overrides the response Content-Disposition header, e.g. `attachment; filename=\"x.pdf\"`. */\n contentDisposition?: string;\n}\n\nexport interface S3ListObjectsOptions {\n limit?: number;\n marker?: string;\n delimiter?: string;\n}\n\nexport interface S3ListObjectsResult {\n Name?: string;\n Prefix?: string;\n Delimiter?: string;\n Marker?: string;\n NextMarker?: string;\n IsTruncated?: boolean;\n Contents?: Array<{ Key?: string }>;\n CommonPrefixes?: Array<{ Prefix?: string }>;\n}\n\nexport class S3StorageClient {\n private readonly client: S3Client;\n private readonly bucket: string;\n\n constructor(config: S3StorageConfig) {\n if (!config.bucket?.trim()) {\n throw new StorageConfigError('S3 bucket is not configured');\n }\n if (!config.region?.trim()) {\n throw new StorageConfigError('S3 region is not configured');\n }\n\n this.bucket = config.bucket;\n this.client = new S3Client({\n region: config.region,\n ...(config.accessKeyId && config.secretAccessKey\n ? {\n credentials: {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n },\n }\n : {}),\n });\n }\n\n async putObject(\n key: string,\n body: Buffer | Uint8Array | string,\n contentType?: string,\n contentEncoding?: string\n ): Promise<void> {\n await this.client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: key,\n Body: body,\n ...(contentType ? { ContentType: contentType } : {}),\n ...(contentEncoding ? { ContentEncoding: contentEncoding } : {}),\n })\n );\n }\n\n async getObjectBuffer(key: string): Promise<Buffer> {\n const response = await this.client.send(\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: key,\n })\n );\n if (!response.Body) {\n throw new Error(`Object not found: ${key}`);\n }\n const byteArray = await response.Body.transformToByteArray();\n return Buffer.from(byteArray);\n }\n\n async getPresignedGetUrl(key: string, options: S3PresignedGetOptions): Promise<string> {\n const expiresIn = Math.min(Math.max(Math.floor(options.expiresInSeconds), 1), 7 * 24 * 60 * 60);\n return signGetObject(\n this.client,\n new GetObjectCommand({\n Bucket: this.bucket,\n Key: key,\n ...(options.contentType ? { ResponseContentType: options.contentType } : {}),\n ...(options.contentDisposition\n ? { ResponseContentDisposition: options.contentDisposition }\n : {}),\n }),\n { expiresIn }\n );\n }\n\n async listObjects(\n prefix: string = '',\n options: S3ListObjectsOptions = {}\n ): Promise<S3ListObjectsResult> {\n const response = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n Delimiter: options.delimiter,\n MaxKeys: options.limit || undefined,\n ...(options.marker ? { StartAfter: options.marker } : {}),\n })\n );\n\n return {\n Name: this.bucket,\n Prefix: prefix,\n Delimiter: options.delimiter,\n Marker: options.marker,\n NextMarker: response.NextContinuationToken,\n IsTruncated: response.IsTruncated,\n Contents: (response.Contents || []).map((obj: _Object) => ({ Key: obj.Key })),\n CommonPrefixes: (response.CommonPrefixes || []).map((cp) => ({ Prefix: cp.Prefix })),\n };\n }\n\n async deleteObject(key: string): Promise<void> {\n await this.client.send(\n new DeleteObjectCommand({\n Bucket: this.bucket,\n Key: key,\n })\n );\n }\n\n async deleteObjectsByPrefix(prefix: string): Promise<void> {\n const listed = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.bucket,\n Prefix: prefix,\n })\n );\n const objects = (listed.Contents || [])\n .filter((obj) => obj.Key)\n .map((obj) => ({ Key: obj.Key! }));\n\n if (objects.length === 0) {\n return;\n }\n\n await this.client.send(\n new DeleteObjectsCommand({\n Bucket: this.bucket,\n Delete: {\n Objects: objects,\n Quiet: true,\n },\n })\n );\n }\n\n async createFolderPrefix(key: string): Promise<void> {\n await this.putObject(key, Buffer.alloc(0));\n }\n}\n","/**\n * Join a configurable key prefix with a relative object path.\n */\nexport function resolveStorageKey(keyPrefix: string | undefined, relativePath: string): string {\n const normalizedPath = relativePath.replace(/^\\/+/, '');\n if (!keyPrefix) {\n return normalizedPath;\n }\n const prefix = keyPrefix.endsWith('/') ? keyPrefix : `${keyPrefix}/`;\n return `${prefix}${normalizedPath}`;\n}\n","import { Storage } from '@google-cloud/storage';\nimport type { GcpStorageConfig } from './config';\nimport { StorageConfigError } from './errors';\nimport { resolveStorageKey } from './keys';\n\nexport interface GcpListBlobsResult {\n Name: string;\n Prefix: string;\n Delimiter: number;\n Marker: string;\n NextMarker: string;\n IsTruncated: boolean;\n Contents: Array<{ Key: string }>;\n CommonPrefixes: Array<{ Prefix: string }>;\n}\n\nexport class GcpStorageClient {\n private readonly storage: Storage;\n private readonly bucketName: string;\n private readonly keyPrefix: string;\n\n constructor(config: GcpStorageConfig) {\n if (!config.bucket?.trim()) {\n throw new StorageConfigError('GCP bucket is not configured');\n }\n this.bucketName = config.bucket;\n this.keyPrefix = config.keyPrefix || '';\n this.storage = new Storage({\n keyFilename: config.keyFilename,\n projectId: config.projectId,\n });\n }\n\n private bucket() {\n return this.storage.bucket(this.bucketName);\n }\n\n private fullKey(relativePath: string): string {\n return resolveStorageKey(this.keyPrefix, relativePath);\n }\n\n async upload(key: string, buffer: Buffer, contentType: string): Promise<{ path: string; success: true }> {\n const filePath = this.fullKey(key);\n const file = this.bucket().file(filePath);\n await file.save(buffer, { contentType });\n return { path: filePath, success: true };\n }\n\n async uploadFile(key: string, buffer: Buffer, contentType: string): Promise<{ path: string; success: true }> {\n return this.upload(key, buffer, contentType);\n }\n\n async download(relativePath: string): Promise<Buffer> {\n const prefix = this.fullKey(relativePath);\n const [data] = await this.bucket().file(prefix).download();\n return data;\n }\n\n async copy(srcKey: string, destKey: string): Promise<void> {\n const src = this.fullKey(srcKey);\n const dest = this.fullKey(destKey);\n await this.bucket().file(src).copy(this.bucket().file(dest));\n }\n\n async list(\n folderName: string = '',\n limit: number = 0,\n marker: string = ''\n ): Promise<GcpListBlobsResult> {\n const prefix = this.fullKey(folderName);\n const [files] = await this.bucket().getFiles({\n prefix,\n maxResults: limit || undefined,\n });\n\n const prefixForSplit = this.keyPrefix ? `${this.keyPrefix.replace(/\\/$/, '')}/` : '';\n\n const contents = files\n .filter((t) => !(t.name.replace(prefix, '').split('/').length - 1))\n .map((t) => ({\n Key: prefixForSplit ? t.name.split(prefixForSplit)[1] || t.name : t.name.replace(prefix, ''),\n }));\n\n const commonPrefix = files\n .filter((t) => t.name.replace(prefix, '').split('/').length - 1)\n .reduce<Array<{ Prefix: string }>>((acc, val) => {\n const relative = val.name.replace(prefix, '');\n const folderPrefix = `${relative.split('/')[0]}/`;\n if (!acc.some((t) => t.Prefix === folderPrefix)) {\n acc.push({ Prefix: folderPrefix });\n }\n return acc;\n }, [])\n .map((t) => ({ Prefix: `${folderName}${t.Prefix}` }));\n\n return {\n Name: this.keyPrefix.replace(/\\/$/, '') || 'storage',\n Prefix: folderName,\n Delimiter: limit,\n Marker: marker,\n NextMarker: '',\n IsTruncated: false,\n Contents: contents,\n CommonPrefixes: commonPrefix,\n };\n }\n\n async deleteFile(relativePath: string): Promise<void> {\n await this.bucket().file(this.fullKey(relativePath)).delete();\n }\n\n async deleteByPrefix(relativePrefix: string): Promise<void> {\n await this.bucket().deleteFiles({ prefix: this.fullKey(relativePrefix) });\n }\n\n /** Raw bucket access for app-specific paths outside keyPrefix. */\n getRawBucket() {\n return this.bucket();\n }\n}\n","import type { PublicUrlConfig } from './config';\nimport { resolveStorageKey } from './keys';\n\n/**\n * Build a public URL for an object key after upload.\n */\nexport function buildPublicUploadUrl(relativePath: string, config: PublicUrlConfig): string {\n const key = relativePath.replace(/^\\/+/, '');\n\n if (config.provider === 's3') {\n const bucket = config.bucket || '';\n const region = config.region || 'us-east-1';\n const base = `https://${bucket}.s3.${region}.amazonaws.com`;\n return `${base}/${key}`;\n }\n\n if (config.provider === 'gcp') {\n const gcpPrefix = config.gcpPrefix || '';\n const gcpKey = resolveStorageKey(gcpPrefix.replace(/\\/$/, ''), key);\n const base = (config.baseUrl || '').replace(/\\/$/, '');\n if (base) {\n return `${base}/${gcpKey}`;\n }\n const bucket = config.gcpBucket || config.bucket || '';\n return `https://storage.googleapis.com/${bucket}/${gcpKey}`;\n }\n\n let urlPath = key.startsWith('uploads/') ? key : `uploads/${key}`;\n const base = (config.baseUrl || '').replace(/\\/$/, '');\n if (base.endsWith('/uploads') && urlPath.startsWith('uploads/')) {\n urlPath = urlPath.replace(/^uploads\\//, '');\n }\n return `${base}/${urlPath}`;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multivendor-storage",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Framework-agnostic S3 and GCP storage clients with public URL helpers",
5
5
  "license": "MIT",
6
6
  "author": "SmartByteLabs",
@@ -32,6 +32,7 @@
32
32
  ],
33
33
  "dependencies": {
34
34
  "@aws-sdk/client-s3": "^3.525.0",
35
+ "@aws-sdk/s3-request-presigner": "^3.1066.0",
35
36
  "@google-cloud/storage": "^7.12.0"
36
37
  },
37
38
  "devDependencies": {