@tacxou/nestjs_module_factorydrive 1.1.6

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/README.md ADDED
@@ -0,0 +1,245 @@
1
+ <p align="center">
2
+ <a href="http://nestjs.com/" target="blank">
3
+ <img src="https://nestjs.com/img/logo_text.svg" width="320" alt="Nest Logo" />
4
+ </a>
5
+ </p>
6
+
7
+ <p align="center">
8
+ Factory drive module for NestJS framework
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/org/tacxou"><img src="https://img.shields.io/npm/v/@tacxou/nestjs_module_factorydrive.svg" alt="NPM Version" /></a>
13
+ <a href="https://www.npmjs.com/org/tacxou"><img src="https://img.shields.io/npm/l/@tacxou/nestjs_module_factorydrive.svg" alt="Package License" /></a>
14
+ <a href="https://github.com/tacxou/nestjs_module_rcon/actions/workflows/ci.yml"><img src="https://github.com/tacxou/nestjs_module_factorydrive/actions/workflows/ci.yml/badge.svg" alt="Publish Package to npmjs" /></a>
15
+ </p>
16
+ <br>
17
+
18
+ ## `@tacxou/nestjs_module_factorydrive`
19
+
20
+ `nestjs_module_factorydrive` provides a simple storage abstraction for NestJS:
21
+ - configure one or many disks
22
+ - select a default disk
23
+ - use built-in local filesystem driver
24
+ - register custom drivers (S3, Spaces, etc.)
25
+
26
+ ## Maintained Packages
27
+
28
+ Current maintained packages in the Factorydrive ecosystem:
29
+
30
+ - `local`: [`nestjs_module_factorydrive`](https://github.com/tacxou/nestjs_module_factorydrive/blob/main/src/factorydrive/local-file-system.storage.ts)
31
+ - `s3`: [`nestjs_module_factorydrive-s3`](https://github.com/tacxou/nestjs_module_factorydrive-s3)
32
+ - `sftp`: [`nestjs_module_factorydrive-sftp`](https://github.com/tacxou/nestjs_module_factorydrive-sftp)
33
+
34
+ ## Requirements
35
+
36
+ - Node.js `>= 22`
37
+ - Bun `>= 1.0.0` (used for build/test in this repository)
38
+ - NestJS `^6` to `^11` (`@nestjs/common` and `@nestjs/core`)
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ npm install @tacxou/nestjs_module_factorydrive
44
+ ```
45
+
46
+ Or with other package managers:
47
+
48
+ ```bash
49
+ yarn add @tacxou/nestjs_module_factorydrive
50
+ pnpm add @tacxou/nestjs_module_factorydrive
51
+ bun add @tacxou/nestjs_module_factorydrive
52
+ ```
53
+
54
+ ## Quick Start (synchronous config)
55
+
56
+ ```ts
57
+ // app.module.ts
58
+ import { Module } from '@nestjs/common'
59
+ import { FactorydriveModule } from '@tacxou/nestjs_module_factorydrive'
60
+
61
+ @Module({
62
+ imports: [
63
+ FactorydriveModule.forRoot({
64
+ default: 'local',
65
+ disks: {
66
+ local: {
67
+ driver: 'local',
68
+ config: {
69
+ root: `${process.cwd()}/storage`,
70
+ },
71
+ },
72
+ },
73
+ }),
74
+ ],
75
+ })
76
+ export class AppModule {}
77
+ ```
78
+
79
+ ## Async Configuration (`forRootAsync`)
80
+
81
+ ```ts
82
+ // app.module.ts
83
+ import { Module } from '@nestjs/common'
84
+ import { ConfigModule, ConfigService } from '@nestjs/config'
85
+ import { FactorydriveModule } from '@tacxou/nestjs_module_factorydrive'
86
+
87
+ @Module({
88
+ imports: [
89
+ ConfigModule.forRoot({ isGlobal: true }),
90
+ FactorydriveModule.forRootAsync({
91
+ imports: [ConfigModule],
92
+ inject: [ConfigService],
93
+ useFactory: async (config: ConfigService) => ({
94
+ default: config.get<string>('factorydrive.default', 'local'),
95
+ disks: {
96
+ local: {
97
+ driver: 'local',
98
+ config: {
99
+ root: config.get<string>('factorydrive.localRoot', `${process.cwd()}/storage`),
100
+ },
101
+ },
102
+ },
103
+ }),
104
+ }),
105
+ ],
106
+ })
107
+ export class AppModule {}
108
+ ```
109
+
110
+ ## Usage
111
+
112
+ Inject `FactorydriveService` and interact with a disk instance:
113
+
114
+ ```ts
115
+ // file-storage.service.ts
116
+ import { Injectable } from '@nestjs/common'
117
+ import { FactorydriveService } from '@tacxou/nestjs_module_factorydrive'
118
+
119
+ @Injectable()
120
+ export class FileStorageService {
121
+ public constructor(private readonly factorydrive: FactorydriveService) {}
122
+
123
+ public async uploadFile(path: string, buffer: Buffer): Promise<void> {
124
+ await this.factorydrive.getDisk('local').put(path, buffer)
125
+ }
126
+
127
+ public async readFile(path: string): Promise<string> {
128
+ const { content } = await this.factorydrive.getDisk('local').get(path)
129
+ return content
130
+ }
131
+
132
+ public async deleteFile(path: string): Promise<boolean | null> {
133
+ const { wasDeleted } = await this.factorydrive.getDisk('local').delete(path)
134
+ return wasDeleted
135
+ }
136
+ }
137
+ ```
138
+
139
+ If no disk name is provided, the configured `default` disk is used:
140
+
141
+ ```ts
142
+ const disk = this.factorydrive.getDisk()
143
+ ```
144
+
145
+ ## Built-in Local Driver
146
+
147
+ The package includes a `local` driver with the following operations:
148
+
149
+ - `append(location, content)`
150
+ - `copy(src, dest)`
151
+ - `delete(location)`
152
+ - `exists(location)`
153
+ - `get(location, encoding?)`
154
+ - `getBuffer(location)`
155
+ - `getStat(location)`
156
+ - `getStream(location)`
157
+ - `move(src, dest)`
158
+ - `prepend(location, content)`
159
+ - `put(location, content)`
160
+ - `flatList(prefix?)`
161
+
162
+ `content` for `put` accepts `Buffer | ReadableStream | string`.
163
+
164
+ ## Register a Custom Driver
165
+
166
+ Custom drivers must extend `AbstractStorage` and implement the methods you need.
167
+
168
+ ```ts
169
+ // aws-s3.storage.ts
170
+ import { AbstractStorage, DeleteResponse, Response } from '@tacxou/nestjs_module_factorydrive'
171
+
172
+ export class AwsS3Storage extends AbstractStorage {
173
+ public constructor(private readonly config: { bucket: string }) {
174
+ super()
175
+ }
176
+
177
+ public async put(location: string, content: Buffer | NodeJS.ReadableStream | string): Promise<Response> {
178
+ // Upload implementation...
179
+ return { raw: { location, uploaded: true, contentType: typeof content } }
180
+ }
181
+
182
+ public async delete(location: string): Promise<DeleteResponse> {
183
+ // Delete implementation...
184
+ return { raw: { location }, wasDeleted: true }
185
+ }
186
+ }
187
+ ```
188
+
189
+ Then register it at startup:
190
+
191
+ ```ts
192
+ // app.module.ts
193
+ import { Module, OnModuleInit } from '@nestjs/common'
194
+ import { FactorydriveModule, FactorydriveService } from '@tacxou/nestjs_module_factorydrive'
195
+ import { AwsS3Storage } from './aws-s3.storage'
196
+
197
+ @Module({
198
+ imports: [
199
+ FactorydriveModule.forRoot({
200
+ default: 's3',
201
+ disks: {
202
+ s3: {
203
+ driver: 's3',
204
+ config: {
205
+ bucket: 'example',
206
+ },
207
+ },
208
+ },
209
+ }),
210
+ ],
211
+ })
212
+ export class AppModule implements OnModuleInit {
213
+ public constructor(private readonly factorydrive: FactorydriveService) {}
214
+
215
+ public onModuleInit(): void {
216
+ this.factorydrive.registerDriver('s3', AwsS3Storage)
217
+ }
218
+ }
219
+ ```
220
+
221
+ ## Exported API
222
+
223
+ Main exports from this package:
224
+
225
+ - `FactorydriveModule`
226
+ - `FactorydriveService`
227
+ - `AbstractStorage`
228
+ - `StorageManager`
229
+ - storage config/types from `factorydrive/types`
230
+ - exceptions from `exceptions`
231
+
232
+ ## Error Handling
233
+
234
+ The module provides dedicated exceptions (for example):
235
+ - `InvalidConfigException`
236
+ - `DriverNotSupportedException`
237
+ - `FileNotFoundException`
238
+ - `PermissionMissingException`
239
+ - `MethodNotSupportedException`
240
+
241
+ Catch and map them in your service/controller layers as needed.
242
+
243
+ ## License
244
+
245
+ MIT
@@ -0,0 +1,5 @@
1
+ import { RuntimeException } from 'node-exceptions';
2
+ export declare class AuthorizationRequiredException extends RuntimeException {
3
+ raw: Error;
4
+ constructor(err: Error, path: string);
5
+ }
@@ -0,0 +1,5 @@
1
+ import { RuntimeException } from 'node-exceptions';
2
+ export declare class DriverNotSupportedException extends RuntimeException {
3
+ driver: string;
4
+ static driver(name: string): DriverNotSupportedException;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { RuntimeException } from 'node-exceptions';
2
+ export declare class FileNotFoundException extends RuntimeException {
3
+ raw: Error;
4
+ constructor(err: Error, path: string);
5
+ }
@@ -0,0 +1,9 @@
1
+ export * from './authorization-required.exception';
2
+ export * from './driver-not-supported.exception';
3
+ export * from './file-not-found.exception';
4
+ export * from './invalid-config.exception';
5
+ export * from './method-not-supported.exception';
6
+ export * from './no-such-bucket.exception';
7
+ export * from './permission-missing.exception';
8
+ export * from './unknown.exception';
9
+ export * from './wrong-key-path.exception';
@@ -0,0 +1,7 @@
1
+ import { RuntimeException } from 'node-exceptions';
2
+ export declare class InvalidConfigException extends RuntimeException {
3
+ static missingDiskName(): InvalidConfigException;
4
+ static missingDiskConfig(name: string): InvalidConfigException;
5
+ static missingDiskDriver(name: string): InvalidConfigException;
6
+ static duplicateDiskName(name: string): InvalidConfigException;
7
+ }
@@ -0,0 +1,4 @@
1
+ import { RuntimeException } from 'node-exceptions';
2
+ export declare class MethodNotSupportedException extends RuntimeException {
3
+ constructor(name: string, driver: string);
4
+ }
@@ -0,0 +1,5 @@
1
+ import { RuntimeException } from 'node-exceptions';
2
+ export declare class NoSuchBucketException extends RuntimeException {
3
+ raw: Error;
4
+ constructor(err: Error, bucket: string);
5
+ }
@@ -0,0 +1,5 @@
1
+ import { RuntimeException } from 'node-exceptions';
2
+ export declare class PermissionMissingException extends RuntimeException {
3
+ raw: Error;
4
+ constructor(err: Error, path: string);
5
+ }
@@ -0,0 +1,5 @@
1
+ import { RuntimeException } from 'node-exceptions';
2
+ export declare class UnknownException extends RuntimeException {
3
+ raw: Error;
4
+ constructor(err: Error, errorCode: string, path: string);
5
+ }
@@ -0,0 +1,5 @@
1
+ import { RuntimeException } from 'node-exceptions';
2
+ export declare class WrongKeyPathException extends RuntimeException {
3
+ raw: Error;
4
+ constructor(err: Error, path: string);
5
+ }
@@ -0,0 +1,20 @@
1
+ import { ContentResponse, DeleteResponse, ExistsResponse, FileListResponse, Response, SignedUrlOptions, SignedUrlResponse, StatResponse } from './types';
2
+ export default abstract class AbstractStorage {
3
+ constructor();
4
+ onStorageInit(): void | Promise<void>;
5
+ append(_location: string, _content: Buffer | string): Promise<Response>;
6
+ copy(_src: string, _dest: string): Promise<Response>;
7
+ delete(_location: string): Promise<DeleteResponse>;
8
+ driver(): unknown;
9
+ exists(_location: string): Promise<ExistsResponse>;
10
+ get(_location: string, _encoding?: string): Promise<ContentResponse<string>>;
11
+ getBuffer(_location: string): Promise<ContentResponse<Buffer>>;
12
+ getSignedUrl(_location: string, _options?: SignedUrlOptions): Promise<SignedUrlResponse>;
13
+ getStat(_location: string): Promise<StatResponse>;
14
+ getStream(_location: string): Promise<NodeJS.ReadableStream>;
15
+ getUrl(_location: string): string;
16
+ move(_src: string, _dest: string): Promise<Response>;
17
+ put(_location: string, _content: Buffer | NodeJS.ReadableStream | string): Promise<Response>;
18
+ prepend(_location: string, _content: Buffer | string): Promise<Response>;
19
+ flatList(_prefix?: string): AsyncIterable<FileListResponse>;
20
+ }
@@ -0,0 +1,5 @@
1
+ export { default as AbstractStorage } from './abstract.storage';
2
+ export { default as StorageManager } from './storage.manager';
3
+ export { LocalFileSystemStorage } from './local-file-system.storage';
4
+ export * from './utils';
5
+ export * from './types';
@@ -0,0 +1,25 @@
1
+ import * as fse from 'fs-extra';
2
+ import AbstractStorage from './abstract.storage';
3
+ import { ContentResponse, DeleteResponse, ExistsResponse, FileListResponse, Response, StatResponse } from './types';
4
+ export declare class LocalFileSystemStorage extends AbstractStorage {
5
+ private readonly $root;
6
+ constructor(config: LocalFileSystemStorageConfig);
7
+ private _fullPath;
8
+ append(location: string, content: Buffer | string): Promise<Response>;
9
+ copy(src: string, dest: string): Promise<Response>;
10
+ delete(location: string): Promise<DeleteResponse>;
11
+ driver(): typeof fse;
12
+ exists(location: string): Promise<ExistsResponse>;
13
+ get(location: string, encoding?: BufferEncoding): Promise<ContentResponse<string>>;
14
+ getBuffer(location: string): Promise<ContentResponse<Buffer>>;
15
+ getStat(location: string): Promise<StatResponse>;
16
+ getStream(location: string): Promise<NodeJS.ReadableStream>;
17
+ move(src: string, dest: string): Promise<Response>;
18
+ prepend(location: string, content: Buffer | string): Promise<Response>;
19
+ put(location: string, content: Buffer | NodeJS.ReadableStream | string): Promise<Response>;
20
+ flatList(prefix?: string): AsyncIterable<FileListResponse>;
21
+ private _flatDirIterator;
22
+ }
23
+ export type LocalFileSystemStorageConfig = {
24
+ root: string;
25
+ };
@@ -0,0 +1,20 @@
1
+ import AbstractStorage from './abstract.storage';
2
+ import type { StorageManagerConfig, StorageManagerSingleDiskConfig } from './types';
3
+ interface StorageConstructor<T extends AbstractStorage = AbstractStorage> {
4
+ new (...args: any[]): T;
5
+ }
6
+ export default class StorageManager {
7
+ private readonly logger;
8
+ private readonly defaultDisk;
9
+ private readonly disksConfig;
10
+ private _disks;
11
+ private _drivers;
12
+ constructor(config: StorageManagerConfig);
13
+ getDisks(): Map<string, AbstractStorage>;
14
+ getDrivers(): Map<string, StorageConstructor>;
15
+ initDisks(): Promise<void>;
16
+ disk<T extends AbstractStorage = AbstractStorage>(name?: string): T;
17
+ addDisk(name: string, config: StorageManagerSingleDiskConfig): void;
18
+ registerDriver<T extends AbstractStorage>(name: string, driver: StorageConstructor<T>): void;
19
+ }
20
+ export {};
@@ -0,0 +1,42 @@
1
+ import { LocalFileSystemStorageConfig } from './local-file-system.storage';
2
+ export type { LocalFileSystemStorageConfig };
3
+ export type StorageManagerSingleDiskConfig = {
4
+ driver: 'local';
5
+ config: LocalFileSystemStorageConfig;
6
+ } | {
7
+ driver: string;
8
+ config: unknown;
9
+ };
10
+ export interface StorageManagerDiskConfig {
11
+ [key: string]: StorageManagerSingleDiskConfig;
12
+ }
13
+ export interface StorageManagerConfig {
14
+ default?: string;
15
+ disks?: StorageManagerDiskConfig;
16
+ registerLocalDriver?: boolean;
17
+ }
18
+ export interface Response {
19
+ raw: unknown;
20
+ }
21
+ export interface ExistsResponse extends Response {
22
+ exists: boolean;
23
+ }
24
+ export interface ContentResponse<ContentType> extends Response {
25
+ content: ContentType;
26
+ }
27
+ export interface SignedUrlOptions {
28
+ expiresIn?: number;
29
+ }
30
+ export interface SignedUrlResponse extends Response {
31
+ signedUrl: string;
32
+ }
33
+ export interface StatResponse extends Response {
34
+ size: number;
35
+ modified: Date;
36
+ }
37
+ export interface FileListResponse extends Response {
38
+ path: string;
39
+ }
40
+ export interface DeleteResponse extends Response {
41
+ wasDeleted: boolean | null;
42
+ }
@@ -0,0 +1,3 @@
1
+ import { pipeline as nodePipeline } from 'stream';
2
+ export declare function isReadableStream(stream: any): stream is NodeJS.ReadableStream;
3
+ export declare const pipeline: typeof nodePipeline.__promisify__;
@@ -0,0 +1 @@
1
+ export declare const FACTORYDRIVE_MODULE_OPTIONS_TOKEN = "FactorydriveModuleOptionsToken";
@@ -0,0 +1,10 @@
1
+ import { DynamicModule } from '@nestjs/common';
2
+ import type { FactorydriveModuleAsyncOptions } from './factorydrive.interfaces';
3
+ import type { StorageManagerConfig } from './factorydrive';
4
+ export declare class FactorydriveCoreModule {
5
+ constructor();
6
+ static forRoot(options: StorageManagerConfig): DynamicModule;
7
+ static forRootAsync(options: FactorydriveModuleAsyncOptions): DynamicModule;
8
+ private static createAsyncProviders;
9
+ private static createAsyncOptionsProvider;
10
+ }
@@ -0,0 +1,11 @@
1
+ import { ModuleMetadata, Type } from '@nestjs/common';
2
+ import type { StorageManagerConfig } from './factorydrive';
3
+ export interface FactorydriveModuleOptionsFactory {
4
+ createFactorydriveModuleOptions(): Promise<StorageManagerConfig> | StorageManagerConfig;
5
+ }
6
+ export interface FactorydriveModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
7
+ inject?: any[];
8
+ useClass?: Type<FactorydriveModuleOptionsFactory>;
9
+ useExisting?: Type<FactorydriveModuleOptionsFactory>;
10
+ useFactory?: (...args: any[]) => Promise<StorageManagerConfig> | StorageManagerConfig;
11
+ }
@@ -0,0 +1,7 @@
1
+ import { DynamicModule } from '@nestjs/common';
2
+ import type { FactorydriveModuleAsyncOptions } from './factorydrive.interfaces';
3
+ import type { StorageManagerConfig } from './factorydrive';
4
+ export declare class FactorydriveModule {
5
+ static forRoot(options: StorageManagerConfig): DynamicModule;
6
+ static forRootAsync(options: FactorydriveModuleAsyncOptions): DynamicModule;
7
+ }
@@ -0,0 +1,10 @@
1
+ import { AbstractStorage } from './factorydrive';
2
+ import type { StorageManagerConfig } from './factorydrive';
3
+ export declare class FactorydriveService {
4
+ protected options: StorageManagerConfig;
5
+ private storageManager;
6
+ constructor(options: StorageManagerConfig);
7
+ onModuleInit(): Promise<void>;
8
+ getDisk<T extends AbstractStorage>(name?: string): T;
9
+ registerDriver(name: string, driver: new (...args: any[]) => AbstractStorage): void;
10
+ }
@@ -0,0 +1,5 @@
1
+ export * from './factorydrive';
2
+ export * from './exceptions';
3
+ export * from './factorydrive.module';
4
+ export * from './factorydrive.interfaces';
5
+ export * from './factorydrive.service';