BREAKING CHANGE(core): update
This commit is contained in:
		| @@ -3,6 +3,6 @@ | ||||
|  */ | ||||
| export const commitinfo = { | ||||
|   name: '@push.rocks/smartbucket', | ||||
|   version: '2.0.5', | ||||
|   version: '3.0.0', | ||||
|   description: 'A TypeScript library for simple cloud independent object storage with support for buckets, directories, and files.' | ||||
| } | ||||
|   | ||||
| @@ -49,58 +49,63 @@ export class Bucket { | ||||
|   /** | ||||
|    * store file | ||||
|    */ | ||||
|   public async fastPut(pathArg: string, fileContent: string | Buffer): Promise<void> { | ||||
|   public async fastPut(optionsArg: { | ||||
|     path: string; | ||||
|     contents: string | Buffer; | ||||
|   }): Promise<void> { | ||||
|     const streamIntake = new plugins.smartstream.StreamIntake(); | ||||
|     const putPromise = this.smartbucketRef.minioClient | ||||
|       .putObject(this.name, pathArg, streamIntake.getReadable()) | ||||
|       .putObject(this.name, optionsArg.path, streamIntake) | ||||
|       .catch((e) => console.log(e)); | ||||
|     streamIntake.pushData(fileContent); | ||||
|     streamIntake.pushData(optionsArg.contents); | ||||
|     streamIntake.signalEnd(); | ||||
|     await putPromise; | ||||
|     const response = await putPromise; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * get file | ||||
|    */ | ||||
|   public async fastGet(pathArg: string): Promise<Buffer> { | ||||
|   public async fastGet(optionsArg: Parameters<typeof this.fastGetStream>[0]): Promise<Buffer> { | ||||
|     const done = plugins.smartpromise.defer(); | ||||
|     let completeFile: Buffer; | ||||
|     const replaySubject = await this.fastGetStream(pathArg); | ||||
|     const subscription = replaySubject.subscribe( | ||||
|       (chunk) => { | ||||
|     const replaySubject = await this.fastGetStream(optionsArg); | ||||
|     const subscription = replaySubject.subscribe({ | ||||
|       next: (chunk) => { | ||||
|         if (completeFile) { | ||||
|           completeFile = Buffer.concat([completeFile, chunk]); | ||||
|         } else { | ||||
|           completeFile = chunk; | ||||
|         } | ||||
|       }, | ||||
|       (err) => { | ||||
|         console.log(err); | ||||
|       }, | ||||
|       () => { | ||||
|       complete: () => { | ||||
|         done.resolve(); | ||||
|         subscription.unsubscribe(); | ||||
|       } | ||||
|     ); | ||||
|       }, | ||||
|       error: (err) => { | ||||
|         console.log(err); | ||||
|       }, | ||||
|     }); | ||||
|     await done.promise; | ||||
|     return completeFile; | ||||
|   } | ||||
|  | ||||
|   public async fastGetStream(pathArg: string): Promise<plugins.smartrx.rxjs.ReplaySubject<Buffer>> { | ||||
|   public async fastGetStream(optionsArg: { | ||||
|     path: string; | ||||
|   }): Promise<plugins.smartrx.rxjs.ReplaySubject<Buffer>> { | ||||
|     const fileStream = await this.smartbucketRef.minioClient | ||||
|       .getObject(this.name, pathArg) | ||||
|       .getObject(this.name, optionsArg.path) | ||||
|       .catch((e) => console.log(e)); | ||||
|     const replaySubject = new plugins.smartrx.rxjs.ReplaySubject<Buffer>(); | ||||
|     const duplexStream = plugins.smartstream.createDuplexStream<Buffer, Buffer>( | ||||
|       async (chunk) => { | ||||
|     const duplexStream = new plugins.smartstream.SmartDuplex<Buffer, Buffer>({ | ||||
|       writeFunction: async (chunk) => { | ||||
|         replaySubject.next(chunk); | ||||
|         return chunk; | ||||
|       }, | ||||
|       async (cb) => { | ||||
|       finalFunction: async (cb) => { | ||||
|         replaySubject.complete(); | ||||
|         return Buffer.from(''); | ||||
|       } | ||||
|     ); | ||||
|     }); | ||||
|  | ||||
|     if (!fileStream) { | ||||
|       return null; | ||||
| @@ -109,7 +114,6 @@ export class Bucket { | ||||
|     const smartstream = new plugins.smartstream.StreamWrapper([ | ||||
|       fileStream, | ||||
|       duplexStream, | ||||
|       plugins.smartstream.cleanPipe(), | ||||
|     ]); | ||||
|     smartstream.run(); | ||||
|     return replaySubject; | ||||
| @@ -119,16 +123,16 @@ export class Bucket { | ||||
|    * store file as stream | ||||
|    */ | ||||
|   public async fastPutStream(optionsArg: { | ||||
|     pathArg: string; | ||||
|     path: string; | ||||
|     dataStream: plugins.stream.Readable; | ||||
|     metadata?: { [key: string]: string }; | ||||
|     nativeMetadata?: { [key: string]: string }; | ||||
|   }): Promise<void> { | ||||
|     await this.smartbucketRef.minioClient.putObject( | ||||
|       this.name, | ||||
|       optionsArg.pathArg, | ||||
|       optionsArg.path, | ||||
|       optionsArg.dataStream, | ||||
|       null, | ||||
|       ...(optionsArg.metadata | ||||
|       ...(optionsArg.nativeMetadata | ||||
|         ? (() => { | ||||
|             const returnObject: any = {}; | ||||
|             return returnObject; | ||||
| @@ -137,30 +141,47 @@ export class Bucket { | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public async updateMetadata( | ||||
|     bucket: string, | ||||
|     objectKey: string, | ||||
|     metadata: { [key: string]: string } | ||||
|   ): Promise<void> { | ||||
|   public async copyObject(optionsArg: { | ||||
|     /** | ||||
|      * the | ||||
|      */ | ||||
|     objectKey: string; | ||||
|     /** | ||||
|      * in case you want to copy to another bucket specify it here | ||||
|      */ | ||||
|     targetBucket?: Bucket; | ||||
|     targetBucketKey?: string; | ||||
|     /** | ||||
|      * metadata will be merged with existing metadata | ||||
|      */ | ||||
|     nativeMetadata?: { [key: string]: string }; | ||||
|     deleteExistingNativeMetadata?: boolean; | ||||
|   }): Promise<void> { | ||||
|     try { | ||||
|       const targetBucketName = optionsArg.targetBucket ? optionsArg.targetBucket.name : this.name; | ||||
|  | ||||
|       // Retrieve current object information to use in copy conditions | ||||
|       const currentObjInfo = await this.smartbucketRef.minioClient.statObject(bucket, objectKey); | ||||
|    | ||||
|       const currentObjInfo = await this.smartbucketRef.minioClient.statObject( | ||||
|         targetBucketName, | ||||
|         optionsArg.objectKey | ||||
|       ); | ||||
|  | ||||
|       // Setting up copy conditions | ||||
|       const copyConditions = new plugins.minio.CopyConditions(); | ||||
|    | ||||
|       // Prepare new metadata, merging current and new metadata | ||||
|       const newMetadata = { | ||||
|         ...currentObjInfo.metaData, | ||||
|         ...metadata, | ||||
|  | ||||
|       // Prepare new metadata | ||||
|       const newNativeMetadata = { | ||||
|         ...(optionsArg.deleteExistingNativeMetadata ? {} : currentObjInfo.metaData), | ||||
|         ...optionsArg.nativeMetadata, | ||||
|       }; | ||||
|    | ||||
|  | ||||
|       // Define the copy operation as a Promise | ||||
|       // TODO: check on issue here: https://github.com/minio/minio-js/issues/1286 | ||||
|       await this.smartbucketRef.minioClient.copyObject( | ||||
|         bucket, | ||||
|         objectKey, | ||||
|         `/${bucket}/${objectKey}`, | ||||
|         copyConditions, | ||||
|         this.name, | ||||
|         optionsArg.objectKey, | ||||
|         `/${targetBucketName}/${optionsArg.objectKey}`, | ||||
|         copyConditions | ||||
|       ); | ||||
|     } catch (err) { | ||||
|       console.error('Error updating metadata:', err); | ||||
| @@ -171,7 +192,27 @@ export class Bucket { | ||||
|   /** | ||||
|    * removeObject | ||||
|    */ | ||||
|   public async fastRemove(pathArg: string) { | ||||
|     await this.smartbucketRef.minioClient.removeObject(this.name, pathArg); | ||||
|   public async fastRemove(optionsArg: { | ||||
|     path: string; | ||||
|   }) { | ||||
|     await this.smartbucketRef.minioClient.removeObject(this.name, optionsArg.path); | ||||
|   } | ||||
|  | ||||
|   public async doesObjectExist(optionsArg: { | ||||
|     path: string; | ||||
|   }): Promise<boolean> { | ||||
|     try { | ||||
|       await this.smartbucketRef.minioClient.statObject(this.name, optionsArg.path); | ||||
|       console.log(`Object '${optionsArg.path}' exists in bucket '${this.name}'.`); | ||||
|       return true; | ||||
|     } catch (error) { | ||||
|       if (error.code === 'NotFound') { | ||||
|         console.log(`Object '${optionsArg.path}' does not exist in bucket '${this.name}'.`); | ||||
|         return false; | ||||
|       } else { | ||||
|         console.error('Error checking object existence:', error); | ||||
|         throw error; // Rethrow if it's not a NotFound error to handle unexpected issues | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -70,8 +70,8 @@ export class Directory { | ||||
|       false | ||||
|     ); | ||||
|     const fileArray: File[] = []; | ||||
|     const duplexStream = plugins.smartstream.createDuplexStream<plugins.minio.BucketItem, void>( | ||||
|       async (bucketItem) => { | ||||
|     const duplexStream = new plugins.smartstream.SmartDuplex<plugins.minio.BucketItem, void>({ | ||||
|       writeFunction: async (bucketItem) => { | ||||
|         if (bucketItem.prefix) { | ||||
|           return; | ||||
|         } | ||||
| @@ -83,13 +83,18 @@ export class Directory { | ||||
|           subtractedPath = subtractedPath.substr(1); | ||||
|         } | ||||
|         if (!subtractedPath.includes('/')) { | ||||
|           fileArray.push(new File(this, subtractedPath)); | ||||
|           fileArray.push( | ||||
|             new File({ | ||||
|               directoryRefArg: this, | ||||
|               fileName: subtractedPath, | ||||
|             }) | ||||
|           ); | ||||
|         } | ||||
|       }, | ||||
|       async (tools) => { | ||||
|       finalFunction: async (tools) => { | ||||
|         done.resolve(); | ||||
|       } | ||||
|     ); | ||||
|     }); | ||||
|     fileNameStream.pipe(duplexStream); | ||||
|     await done.promise; | ||||
|     return fileArray; | ||||
| @@ -107,8 +112,8 @@ export class Directory { | ||||
|       false | ||||
|     ); | ||||
|     const directoryArray: Directory[] = []; | ||||
|     const duplexStream = plugins.smartstream.createDuplexStream<plugins.minio.BucketItem, void>( | ||||
|       async (bucketItem) => { | ||||
|     const duplexStream = new plugins.smartstream.SmartDuplex<plugins.minio.BucketItem, void>({ | ||||
|       writeFunction: async (bucketItem) => { | ||||
|         if (bucketItem.name) { | ||||
|           return; | ||||
|         } | ||||
| @@ -124,10 +129,10 @@ export class Directory { | ||||
|           directoryArray.push(new Directory(this.bucketRef, this, dirName)); | ||||
|         } | ||||
|       }, | ||||
|       async (tools) => { | ||||
|       finalFunction: async (tools) => { | ||||
|         done.resolve(); | ||||
|       } | ||||
|     ); | ||||
|     }); | ||||
|     completeDirStream.pipe(duplexStream); | ||||
|     await done.promise; | ||||
|     return directoryArray; | ||||
| @@ -177,36 +182,49 @@ export class Directory { | ||||
|    * @param relativePathArg | ||||
|    */ | ||||
|   public async createEmptyFile(relativePathArg: string) { | ||||
|     const emtpyFile = await File.createFileFromString(this, relativePathArg, ''); | ||||
|     const emtpyFile = await File.create({ | ||||
|       directory: this, | ||||
|       name: relativePathArg, | ||||
|       contents: '', | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   // file operations | ||||
|   public async fastStore(pathArg: string, contentArg: string | Buffer) { | ||||
|     const path = plugins.path.join(this.getBasePath(), pathArg); | ||||
|     await this.bucketRef.fastPut(path, contentArg); | ||||
|   public async fastPut(optionsArg: { path: string; contents: string | Buffer }) { | ||||
|     const path = plugins.path.join(this.getBasePath(), optionsArg.path); | ||||
|     await this.bucketRef.fastPut({ | ||||
|       path, | ||||
|       contents: optionsArg.contents, | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   public async fastGet(pathArg: string) { | ||||
|     const path = plugins.path.join(this.getBasePath(), pathArg); | ||||
|     const result = await this.bucketRef.fastGet(path); | ||||
|   public async fastGet(optionsArg: { path: string }) { | ||||
|     const path = plugins.path.join(this.getBasePath(), optionsArg.path); | ||||
|     const result = await this.bucketRef.fastGet({ | ||||
|       path, | ||||
|     }); | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
|   public async fastGetStream(pathArg: string): Promise<plugins.smartrx.rxjs.ReplaySubject<Buffer>> { | ||||
|     const path = plugins.path.join(this.getBasePath(), pathArg); | ||||
|     const result = await this.bucketRef.fastGetStream(path); | ||||
|     const result = await this.bucketRef.fastGetStream({ | ||||
|       path, | ||||
|     }); | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
|   public async fastRemove(pathArg: string) { | ||||
|     const path = plugins.path.join(this.getBasePath(), pathArg); | ||||
|     await this.bucketRef.fastRemove(path); | ||||
|   public async fastRemove(optionsArg: { path: string }) { | ||||
|     const path = plugins.path.join(this.getBasePath(), optionsArg.path); | ||||
|     await this.bucketRef.fastRemove({ | ||||
|       path, | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * deletes the directory with all its contents | ||||
|    */ | ||||
|   public async deleteWithAllContents() { | ||||
|   public async delete() { | ||||
|     const deleteDirectory = async (directoryArg: Directory) => { | ||||
|       const childDirectories = await directoryArg.listDirectories(); | ||||
|       if (childDirectories.length === 0) { | ||||
| @@ -218,7 +236,9 @@ export class Directory { | ||||
|       } | ||||
|       const files = await directoryArg.listFiles(); | ||||
|       for (const file of files) { | ||||
|         await directoryArg.fastRemove(file.name); | ||||
|         await directoryArg.fastRemove({ | ||||
|           path: file.name, | ||||
|         }); | ||||
|       } | ||||
|     }; | ||||
|     await deleteDirectory(this); | ||||
|   | ||||
| @@ -7,29 +7,41 @@ export interface IFileMetaData { | ||||
|   size: string; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * represents a file in a directory | ||||
|  */ | ||||
| export class File { | ||||
|   // STATIC | ||||
|   public static async createFileFromString( | ||||
|     dirArg: Directory, | ||||
|     fileName: string, | ||||
|     fileContent: string | ||||
|   ) { | ||||
|     await this.createFileFromBuffer(dirArg, fileName, Buffer.from(fileContent)); | ||||
|   } | ||||
|  | ||||
|   public static async createFileFromBuffer( | ||||
|     directoryRef: Directory, | ||||
|     fileName: string, | ||||
|     fileContent: Buffer | ||||
|   ) { | ||||
|     const filePath = plugins.path.join(directoryRef.getBasePath(), fileName); | ||||
|     const streamIntake = new plugins.smartstream.StreamIntake(); | ||||
|     const putPromise = directoryRef.bucketRef.smartbucketRef.minioClient | ||||
|       .putObject(this.name, filePath, streamIntake.getReadable()) | ||||
|       .catch((e) => console.log(e)); | ||||
|     streamIntake.pushData(fileContent); | ||||
|     streamIntake.signalEnd(); | ||||
|     await putPromise; | ||||
|   /** | ||||
|    * creates a file in draft mode | ||||
|    * you need to call .save() to store it in s3 | ||||
|    * @param optionsArg | ||||
|    */ | ||||
|   public static async create(optionsArg: { | ||||
|     directory: Directory; | ||||
|     name: string; | ||||
|     contents: Buffer | string | plugins.stream.Readable; | ||||
|     /** | ||||
|      * if contents are of type string, you can specify the encoding here | ||||
|      */ | ||||
|     encoding?: 'utf8' | 'binary'; | ||||
|   }): Promise<File> { | ||||
|     const contents = | ||||
|       typeof optionsArg.contents === 'string' | ||||
|         ? Buffer.from(optionsArg.contents, optionsArg.encoding) | ||||
|         : optionsArg.contents; | ||||
|     const file = new File({ | ||||
|       directoryRefArg: optionsArg.directory, | ||||
|       fileName: optionsArg.name, | ||||
|     }); | ||||
|     if (contents instanceof plugins.stream.Readable) {} else { | ||||
|       await optionsArg.directory.fastPut({ | ||||
|         path: optionsArg.name, | ||||
|         contents: contents, | ||||
|       }); | ||||
|     } | ||||
|     return file; | ||||
|   } | ||||
|  | ||||
|   // INSTANCE | ||||
| @@ -39,9 +51,9 @@ export class File { | ||||
|   public path: string; | ||||
|   public metaData: IFileMetaData; | ||||
|  | ||||
|   constructor(directoryRefArg: Directory, fileName: string) { | ||||
|     this.parentDirectoryRef = directoryRefArg; | ||||
|     this.name = fileName; | ||||
|   constructor(optionsArg: { directoryRefArg: Directory; fileName: string }) { | ||||
|     this.parentDirectoryRef = optionsArg.directoryRefArg; | ||||
|     this.name = optionsArg.fileName; | ||||
|   } | ||||
|  | ||||
|   public async getContentAsString() { | ||||
| @@ -55,14 +67,16 @@ export class File { | ||||
|       .getObject(this.parentDirectoryRef.bucketRef.name, this.path) | ||||
|       .catch((e) => console.log(e)); | ||||
|     let completeFile = Buffer.from(''); | ||||
|     const duplexStream = plugins.smartstream.createDuplexStream<Buffer, Buffer>( | ||||
|       async (chunk) => { | ||||
|         completeFile = Buffer.concat([chunk]); | ||||
|         return chunk; | ||||
|       }, | ||||
|       async (cb) => { | ||||
|         done.resolve(); | ||||
|         return Buffer.from(''); | ||||
|     const duplexStream = new plugins.smartstream.SmartDuplex<Buffer, Buffer>( | ||||
|       { | ||||
|         writeFunction: async (chunk) => { | ||||
|           completeFile = Buffer.concat([chunk]); | ||||
|           return chunk; | ||||
|         }, | ||||
|         finalFunction: async (cb) => { | ||||
|           done.resolve(); | ||||
|           return Buffer.from(''); | ||||
|         }, | ||||
|       } | ||||
|     ); | ||||
|  | ||||
| @@ -75,7 +89,7 @@ export class File { | ||||
|     return completeFile; | ||||
|   } | ||||
|  | ||||
|   public async streamContent() { | ||||
|   public async readStreaming() { | ||||
|     // TODO | ||||
|     throw new Error('not yet implemented'); | ||||
|   } | ||||
| @@ -90,4 +104,37 @@ export class File { | ||||
|     ); | ||||
|     await this.parentDirectoryRef.listFiles(); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * deletes the file | ||||
|    */ | ||||
|   public async delete() {} | ||||
|  | ||||
|   /** | ||||
|    * allows locking the file | ||||
|    * @param optionsArg | ||||
|    */ | ||||
|   public async lock(optionsArg?: { timeoutMillis?: number }) {} | ||||
|  | ||||
|   /** | ||||
|    * actively unlocks a file | ||||
|    * | ||||
|    */ | ||||
|   public async unlock(optionsArg?: { | ||||
|     /** | ||||
|      * unlock the file even if not locked from this instance | ||||
|      */ | ||||
|     force?: boolean; | ||||
|   }) {} | ||||
|  | ||||
|   public async updateWithContents(optionsArg: { | ||||
|     contents: Buffer | string | plugins.stream.Readable; | ||||
|     encoding?: 'utf8' | 'binary'; | ||||
|   }) {} | ||||
|  | ||||
|   /** | ||||
|    * allows updating the metadata of a file | ||||
|    * @param updatedMetadata | ||||
|    */ | ||||
|   public async updateMetaData(updatedMetadata: any) {} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user