@russ-b/nestjs-common-tools 1.13.1 → 1.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,4 @@
1
1
  # nestjs-common-tools
2
- NestJS Common Tools
3
2
 
4
3
  A small toolbox for NestJS with helpers that often come up in day-to-day development.
5
4
 
@@ -15,6 +14,239 @@ npm install @russ-b/nestjs-common-tools
15
14
 
16
15
  Depending on which features you use, make sure the relevant peer dependencies are also installed in your project.
17
16
 
17
+ ## Public entrypoints
18
+
19
+ This package uses subpath exports for most features.
20
+
21
+ | Import path | What it contains |
22
+ |-------------|------------------|
23
+ | `@russ-b/nestjs-common-tools` | root exports such as `services` |
24
+ | `@russ-b/nestjs-common-tools/class-transformer` | reusable `class-transformer` decorators and helpers |
25
+ | `@russ-b/nestjs-common-tools/modules` | NestJS modules such as `S3Module` |
26
+ | `@russ-b/nestjs-common-tools/validators` | validation decorators and constraints |
27
+ | `@russ-b/nestjs-common-tools/typeorm` | TypeORM filters, helpers, transformers, and types |
28
+ | `@russ-b/nestjs-common-tools/logger` | logger builder and logger-related interfaces/types |
29
+ | `@russ-b/nestjs-common-tools/common/util` | generic utility helpers |
30
+ | `@russ-b/nestjs-common-tools/common/pagination` | pagination DTOs and helpers |
31
+ | `@russ-b/nestjs-common-tools/common/filters` | shared filter exports |
32
+
33
+ ## Class Transformer Helpers
34
+
35
+ This package also exposes small reusable decorators for `class-transformer`.
36
+
37
+ ### `ToStringArray`
38
+
39
+ `ToStringArray()` is useful for DTO query fields that may arrive as a comma-separated string such as `"cars, bikes, boats"` or as an array.
40
+
41
+ ```typescript
42
+ import { ToStringArray } from '@russ-b/nestjs-common-tools/class-transformer';
43
+
44
+ export class SearchDto {
45
+ @ToStringArray()
46
+ tags?: string[];
47
+ }
48
+ ```
49
+
50
+ It will:
51
+
52
+ - split string values by comma
53
+ - trim extra spaces around items
54
+ - remove empty values
55
+ - normalize a string like `'cars, bikes, boats'` into `['cars', 'bikes', 'boats']`
56
+
57
+ ### `ToBooleanFromString`
58
+
59
+ `ToBooleanFromString()` is useful for DTO fields that may arrive as `'true'` or `'false'` strings and should become real booleans.
60
+
61
+ ```typescript
62
+ import { ToBooleanFromString } from '@russ-b/nestjs-common-tools/class-transformer';
63
+
64
+ export class SearchDto {
65
+ @ToBooleanFromString()
66
+ archived?: boolean;
67
+ }
68
+ ```
69
+
70
+ It will:
71
+
72
+ - convert `'true'` to `true`
73
+ - convert `'false'` to `false`
74
+ - trim extra spaces and ignore case
75
+ - leave unsupported values unchanged
76
+
77
+ ## S3 Module
78
+
79
+ `S3Module` is a small NestJS wrapper around the AWS SDK v3 S3 client. It gives you a reusable `S3Service` with a simple Nest-friendly setup for uploads, downloads, deletes, and signed URLs.
80
+
81
+ ### Install S3 peer dependencies
82
+
83
+ If you want to use the S3 module, install the required AWS peer dependencies in your application:
84
+
85
+ ```bash
86
+ npm install @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner
87
+ ```
88
+
89
+ ### Register the module
90
+
91
+ Import the module from `@russ-b/nestjs-common-tools/modules` and configure it with `forRootAsync`.
92
+
93
+ ```typescript
94
+ // app.module.ts
95
+ import { Module } from '@nestjs/common';
96
+ import { ConfigModule, ConfigService } from '@nestjs/config';
97
+ import { S3Module } from '@russ-b/nestjs-common-tools/modules';
98
+
99
+ @Module({
100
+ imports: [
101
+ ConfigModule.forRoot({
102
+ isGlobal: true,
103
+ }),
104
+ S3Module.forRootAsync({
105
+ global: true,
106
+ imports: [ConfigModule],
107
+ inject: [ConfigService],
108
+ useFactory: (config: ConfigService) => ({
109
+ region: config.get<string>('AWS_REGION') ?? 'eu-central-1',
110
+ bucket: config.get<string>('S3_BUCKET'),
111
+ endpoint: config.get<string>('S3_ENDPOINT'),
112
+ logger: config.get<string>('S3_DEBUG') === 'true',
113
+ }),
114
+ }),
115
+ ],
116
+ })
117
+ export class AppModule {}
118
+ ```
119
+
120
+ ### Credentials
121
+
122
+ The module does not accept `accessKeyId` or `secretAccessKey` directly. It relies on the AWS SDK default credential chain instead, which means credentials can come from:
123
+
124
+ - `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`
125
+ - local AWS profiles and shared config
126
+ - IAM roles in AWS environments
127
+
128
+ Example environment variables:
129
+
130
+ ```env
131
+ AWS_REGION=eu-central-1
132
+ AWS_ACCESS_KEY_ID=your-access-key
133
+ AWS_SECRET_ACCESS_KEY=your-secret-key
134
+ S3_BUCKET=my-app-bucket
135
+ ```
136
+
137
+ The AWS SDK reads values from process environment, not directly from a `.env` file, so make sure your application loads those variables before creating the client.
138
+
139
+ `endpoint` is optional and is mostly useful for S3-compatible providers such as MinIO or LocalStack.
140
+
141
+ `forcePathStyle` defaults to `true` in this module. That is usually convenient for MinIO and LocalStack. If you want standard AWS virtual-hosted URLs, set `forcePathStyle: false`.
142
+
143
+ ### Optional logging
144
+
145
+ By default, the module stays silent and does not write S3 operation logs.
146
+
147
+ If you want extra visibility while testing connectivity with S3 or MinIO, set `logger: true` in the module options. That enables the standard Nest logger for this service.
148
+
149
+ ```typescript
150
+ S3Module.forRootAsync({
151
+ useFactory: () => ({
152
+ bucket: 'my-app-bucket',
153
+ endpoint: 'http://localhost:9000',
154
+ forcePathStyle: true,
155
+ logger: true,
156
+ }),
157
+ });
158
+ ```
159
+
160
+ You can also pass your own Nest-compatible logger object if you want to redirect those logs elsewhere.
161
+
162
+ ### Inject and use the service
163
+
164
+ ```typescript
165
+ // files.service.ts
166
+ import { Injectable } from '@nestjs/common';
167
+ import { Readable } from 'stream';
168
+ import { S3Service } from '@russ-b/nestjs-common-tools/modules';
169
+
170
+ @Injectable()
171
+ export class FilesService {
172
+ constructor(private readonly s3Service: S3Service) {}
173
+
174
+ async putAvatar(key: string, file: Buffer) {
175
+ return this.s3Service.putObject(key, file, {
176
+ contentType: 'image/png',
177
+ cacheControl: 'public, max-age=31536000, immutable',
178
+ metadata: {
179
+ source: 'avatar-service',
180
+ },
181
+ });
182
+ }
183
+
184
+ async uploadLargeFile(key: string, stream: Readable) {
185
+ return this.s3Service.upload(key, stream, {
186
+ contentType: 'application/pdf',
187
+ metadata: {
188
+ source: 'document-service',
189
+ },
190
+ });
191
+ }
192
+
193
+ async getAvatar(key: string) {
194
+ const object = await this.s3Service.getObject(key);
195
+
196
+ return {
197
+ stream: object.body,
198
+ contentType: object.contentType,
199
+ cacheControl: object.cacheControl,
200
+ metadata: object.metadata,
201
+ };
202
+ }
203
+
204
+ async deleteAvatar(key: string) {
205
+ return this.s3Service.deleteObject(key);
206
+ }
207
+ }
208
+ ```
209
+
210
+ ### Signed URLs
211
+
212
+ Use `getSignedUrl` when the client should upload or download directly from S3.
213
+
214
+ ```typescript
215
+ // files.service.ts
216
+ async getAvatarUploadUrl(key: string) {
217
+ return this.s3Service.getSignedUrl(key, {
218
+ operation: 'putObject',
219
+ expiresIn: 300,
220
+ contentType: 'image/png',
221
+ cacheControl: 'public, max-age=31536000, immutable',
222
+ metadata: {
223
+ source: 'avatar-upload',
224
+ },
225
+ });
226
+ }
227
+
228
+ async getAvatarDownloadUrl(key: string) {
229
+ return this.s3Service.getSignedUrl(key, {
230
+ operation: 'getObject',
231
+ expiresIn: 300,
232
+ });
233
+ }
234
+ ```
235
+
236
+ When generating a signed `putObject` URL, make sure the client sends the same headers you used during signing, especially `Content-Type` and any custom metadata headers.
237
+
238
+ ### Available methods
239
+
240
+ | Method | Description |
241
+ |--------|-------------|
242
+ | `putObject(key, body, options)` | Simple upload using `PutObjectCommand` |
243
+ | `upload(key, body, options)` | Managed upload using `@aws-sdk/lib-storage`, useful for larger or streaming payloads |
244
+ | `getObject(key, options)` | Returns the readable stream together with object metadata |
245
+ | `deleteObject(key, options)` | Deletes an object from the configured bucket |
246
+ | `getSignedUrl(key, options)` | Creates a presigned URL for `getObject` or `putObject` |
247
+
248
+ `uploadObject` is still available as a compatibility alias, but `putObject` is the preferred method name going forward.
249
+
18
250
  ## Entity Validator
19
251
 
20
252
  A custom validator for NestJS that validates if an entity exists in the database using TypeORM.
@@ -25,7 +257,9 @@ A custom validator for NestJS that validates if an entity exists in the database
25
257
 
26
258
  ```typescript
27
259
  // main.ts
260
+ import { NestFactory } from '@nestjs/core';
28
261
  import { useContainer } from 'class-validator';
262
+ import { AppModule } from './app.module';
29
263
 
30
264
  async function bootstrap() {
31
265
  const app = await NestFactory.create(AppModule);
@@ -43,7 +277,6 @@ async function bootstrap() {
43
277
  ```typescript
44
278
  // app.module.ts
45
279
  import { Module } from '@nestjs/common';
46
- import { TypeOrmModule } from '@nestjs/typeorm';
47
280
  import { EntityConstraint } from '@russ-b/nestjs-common-tools/validators';
48
281
 
49
282
  @Module({
@@ -0,0 +1,2 @@
1
+ export * from './to-string-array.decorator';
2
+ export * from './to-boolean-from-string.decorator';
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./to-string-array.decorator"), exports);
18
+ __exportStar(require("./to-boolean-from-string.decorator"), exports);
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/class-transformer/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8DAA4C;AAC5C,qEAAmD"}
@@ -0,0 +1,3 @@
1
+ import { TransformFnParams } from 'class-transformer';
2
+ export declare function ToBooleanFromString(): PropertyDecorator;
3
+ export declare const parseBooleanFromStringTransformer: ({ value, }: TransformFnParams) => unknown;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseBooleanFromStringTransformer = void 0;
4
+ exports.ToBooleanFromString = ToBooleanFromString;
5
+ const class_transformer_1 = require("class-transformer");
6
+ function ToBooleanFromString() {
7
+ return (0, class_transformer_1.Transform)(exports.parseBooleanFromStringTransformer);
8
+ }
9
+ const parseBooleanFromStringTransformer = ({ value, }) => normalizeBooleanFromString(value);
10
+ exports.parseBooleanFromStringTransformer = parseBooleanFromStringTransformer;
11
+ function normalizeBooleanFromString(value) {
12
+ if (value === undefined) {
13
+ return undefined;
14
+ }
15
+ if (typeof value === 'boolean') {
16
+ return value;
17
+ }
18
+ if (typeof value === 'string') {
19
+ const normalizedValue = value.trim().toLowerCase();
20
+ if (normalizedValue === 'true') {
21
+ return true;
22
+ }
23
+ if (normalizedValue === 'false') {
24
+ return false;
25
+ }
26
+ }
27
+ return value;
28
+ }
29
+ //# sourceMappingURL=to-boolean-from-string.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-boolean-from-string.decorator.js","sourceRoot":"","sources":["../../src/class-transformer/to-boolean-from-string.decorator.ts"],"names":[],"mappings":";;;AAEA,kDAEC;AAJD,yDAAiE;AAEjE,SAAgB,mBAAmB;IACjC,OAAO,IAAA,6BAAS,EAAC,yCAAiC,CAAC,CAAC;AACtD,CAAC;AAEM,MAAM,iCAAiC,GAAG,CAAC,EAChD,KAAK,GACa,EAAW,EAAE,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC;AAFvD,QAAA,iCAAiC,qCAEsB;AAEpE,SAAS,0BAA0B,CAAC,KAAc;IAChD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,eAAe,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAEnD,IAAI,eAAe,KAAK,MAAM,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,eAAe,KAAK,OAAO,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function ToStringArray(): PropertyDecorator;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ToStringArray = ToStringArray;
4
+ const class_transformer_1 = require("class-transformer");
5
+ function ToStringArray() {
6
+ return (0, class_transformer_1.Transform)(({ value }) => normalizeToStringArray(value));
7
+ }
8
+ function normalizeToStringArray(value) {
9
+ if (typeof value === 'string') {
10
+ return splitAndTrim(value);
11
+ }
12
+ if (Array.isArray(value)) {
13
+ return value.flatMap((item) => typeof item === 'string' ? splitAndTrim(item) : []);
14
+ }
15
+ return value;
16
+ }
17
+ function splitAndTrim(value) {
18
+ return value
19
+ .split(',')
20
+ .map((item) => item.trim())
21
+ .filter(Boolean);
22
+ }
23
+ //# sourceMappingURL=to-string-array.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-string-array.decorator.js","sourceRoot":"","sources":["../../src/class-transformer/to-string-array.decorator.ts"],"names":[],"mappings":";;AAEA,sCAEC;AAJD,yDAAiE;AAEjE,SAAgB,aAAa;IAC3B,OAAO,IAAA,6BAAS,EAAC,CAAC,EAAE,KAAK,EAAqB,EAAE,EAAE,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,CAAC;AACpF,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAc;IAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAC5B,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CACnD,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK;SACT,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export * from './s3.module';
2
2
  export * from './s3.service';
3
+ export * from './s3.interface';
@@ -16,4 +16,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./s3.module"), exports);
18
18
  __exportStar(require("./s3.service"), exports);
19
+ __exportStar(require("./s3.interface"), exports);
19
20
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/s3/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA4B;AAC5B,+CAA6B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/s3/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA4B;AAC5B,+CAA6B;AAC7B,iDAA+B"}
@@ -1,19 +1,44 @@
1
- import { ModuleMetadata } from '@nestjs/common';
2
- import { ObjectCannedACL } from '@aws-sdk/client-s3';
1
+ import type { LoggerService, ModuleMetadata } from '@nestjs/common';
2
+ import type { DeleteObjectCommandOutput, GetObjectCommandOutput, ObjectCannedACL, PutObjectCommandInput, PutObjectCommandOutput } from '@aws-sdk/client-s3';
3
+ import type { CompleteMultipartUploadCommandOutput } from '@aws-sdk/client-s3';
4
+ import type { Readable } from 'stream';
5
+ export type S3ModuleLogger = Pick<LoggerService, 'log' | 'error'>;
3
6
  export interface S3ModuleOptions {
4
7
  region?: string;
5
- endpoint: string;
6
- accessKeyId: string;
7
- secretAccessKey: string;
8
+ endpoint?: string;
8
9
  forcePathStyle?: boolean;
9
10
  bucket?: string;
11
+ logger?: boolean | S3ModuleLogger;
10
12
  }
11
13
  export interface S3ModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
12
14
  useFactory: (...args: any[]) => Promise<S3ModuleOptions> | S3ModuleOptions;
13
15
  global?: boolean;
14
16
  inject?: any[];
15
17
  }
16
- export interface UploadParams {
18
+ export interface S3ObjectOptions {
17
19
  bucket?: string;
20
+ }
21
+ export interface S3UploadOptions extends S3ObjectOptions {
18
22
  acl?: ObjectCannedACL;
23
+ cacheControl?: PutObjectCommandInput['CacheControl'];
24
+ contentType?: NonNullable<PutObjectCommandInput['ContentType']>;
25
+ metadata?: PutObjectCommandInput['Metadata'];
26
+ }
27
+ export interface S3SignedUrlOptions extends S3UploadOptions {
28
+ expiresIn?: number;
29
+ operation?: 'getObject' | 'putObject';
30
+ }
31
+ export interface S3GetObjectResult {
32
+ body: Readable;
33
+ cacheControl?: GetObjectCommandOutput['CacheControl'];
34
+ contentLength?: GetObjectCommandOutput['ContentLength'];
35
+ contentType?: GetObjectCommandOutput['ContentType'];
36
+ eTag?: GetObjectCommandOutput['ETag'];
37
+ lastModified?: GetObjectCommandOutput['LastModified'];
38
+ metadata?: GetObjectCommandOutput['Metadata'];
19
39
  }
40
+ export type S3UploadResult = CompleteMultipartUploadCommandOutput;
41
+ export type S3PutObjectResult = PutObjectCommandOutput;
42
+ export type S3DeleteObjectResult = DeleteObjectCommandOutput;
43
+ export type S3Body = NonNullable<PutObjectCommandInput['Body']>;
44
+ export type UploadParams = S3UploadOptions;
@@ -9,9 +9,9 @@ var S3Module_1;
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.S3Module = void 0;
11
11
  const common_1 = require("@nestjs/common");
12
- const s3_service_1 = require("./s3.service");
13
- const s3_constants_1 = require("./s3.constants");
14
12
  const client_s3_1 = require("@aws-sdk/client-s3");
13
+ const s3_constants_1 = require("./s3.constants");
14
+ const s3_service_1 = require("./s3.service");
15
15
  let S3Module = S3Module_1 = class S3Module {
16
16
  static forRootAsync(options) {
17
17
  return {
@@ -28,15 +28,12 @@ let S3Module = S3Module_1 = class S3Module {
28
28
  provide: s3_constants_1.S3_CLIENT,
29
29
  inject: [s3_constants_1.S3_MODULE_OPTIONS],
30
30
  useFactory: (opts) => {
31
- return new client_s3_1.S3Client({
31
+ const clientConfig = {
32
32
  region: opts.region || 'us-east-1',
33
33
  endpoint: opts.endpoint,
34
- credentials: {
35
- accessKeyId: opts.accessKeyId,
36
- secretAccessKey: opts.secretAccessKey,
37
- },
38
34
  forcePathStyle: opts.forcePathStyle ?? true,
39
- });
35
+ };
36
+ return new client_s3_1.S3Client(clientConfig);
40
37
  },
41
38
  },
42
39
  s3_service_1.S3Service,
@@ -1 +1 @@
1
- {"version":3,"file":"s3.module.js","sourceRoot":"","sources":["../../../src/modules/s3/s3.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAuD;AACvD,6CAAyC;AACzC,iDAA8D;AAC9D,kDAA8C;AAIvC,IAAM,QAAQ,gBAAd,MAAM,QAAQ;IACnB,MAAM,CAAC,YAAY,CAAC,OAA6B;QAC/C,OAAO;YACL,MAAM,EAAE,UAAQ;YAChB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;YAC9B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,gCAAiB;oBAC1B,UAAU,EAAE,OAAO,CAAC,UAAU;oBAC9B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;iBAC7B;gBACD;oBACE,OAAO,EAAE,wBAAS;oBAClB,MAAM,EAAE,CAAC,gCAAiB,CAAC;oBAC3B,UAAU,EAAE,CAAC,IAAqB,EAAE,EAAE;wBACpC,OAAO,IAAI,oBAAQ,CAAC;4BAClB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,WAAW;4BAClC,QAAQ,EAAE,IAAI,CAAC,QAAQ;4BACvB,WAAW,EAAE;gCACX,WAAW,EAAE,IAAI,CAAC,WAAW;gCAC7B,eAAe,EAAE,IAAI,CAAC,eAAe;6BACtC;4BACD,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,IAAI;yBAC5C,CAAC,CAAC;oBACL,CAAC;iBACF;gBACD,sBAAS;aACV;YACD,OAAO,EAAE,CAAC,sBAAS,CAAC;SACrB,CAAC;IACJ,CAAC;CACF,CAAA;AAhCY,4BAAQ;mBAAR,QAAQ;IADpB,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,QAAQ,CAgCpB"}
1
+ {"version":3,"file":"s3.module.js","sourceRoot":"","sources":["../../../src/modules/s3/s3.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAuD;AACvD,kDAA8D;AAE9D,iDAA8D;AAC9D,6CAAyC;AAGlC,IAAM,QAAQ,gBAAd,MAAM,QAAQ;IACnB,MAAM,CAAC,YAAY,CAAC,OAA6B;QAC/C,OAAO;YACL,MAAM,EAAE,UAAQ;YAChB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;YAC9B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,gCAAiB;oBAC1B,UAAU,EAAE,OAAO,CAAC,UAAU;oBAC9B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;iBAC7B;gBACD;oBACE,OAAO,EAAE,wBAAS;oBAClB,MAAM,EAAE,CAAC,gCAAiB,CAAC;oBAC3B,UAAU,EAAE,CAAC,IAAqB,EAAE,EAAE;wBACpC,MAAM,YAAY,GAAmB;4BACnC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,WAAW;4BAClC,QAAQ,EAAE,IAAI,CAAC,QAAQ;4BACvB,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,IAAI;yBAC5C,CAAC;wBAEF,OAAO,IAAI,oBAAQ,CAAC,YAAY,CAAC,CAAC;oBACpC,CAAC;iBACF;gBACD,sBAAS;aACV;YACD,OAAO,EAAE,CAAC,sBAAS,CAAC;SACrB,CAAC;IACJ,CAAC;CACF,CAAA;AA9BY,4BAAQ;mBAAR,QAAQ;IADpB,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,QAAQ,CA8BpB"}
@@ -1,15 +1,18 @@
1
1
  import { S3Client } from '@aws-sdk/client-s3';
2
- import { Readable } from 'stream';
3
- import { StreamingBlobPayloadInputTypes } from '@smithy/types/dist-types/streaming-payload/streaming-blob-payload-input-types';
4
- import { UploadParams } from './s3.interface';
2
+ import { S3Body, S3DeleteObjectResult, S3GetObjectResult, S3ModuleOptions, S3ObjectOptions, S3PutObjectResult, S3SignedUrlOptions, S3UploadOptions, S3UploadResult } from './s3.interface';
5
3
  export declare class S3Service {
6
4
  private readonly options;
7
5
  private readonly s3Client;
8
- private readonly logger;
9
- private readonly bucket;
10
- constructor(options: Record<string, string>, s3Client: S3Client);
11
- upload(key: string, body: StreamingBlobPayloadInputTypes, contentType?: string, params?: UploadParams): Promise<void>;
12
- uploadObject(key: string, body: StreamingBlobPayloadInputTypes, contentType?: string, params?: UploadParams): Promise<void>;
13
- deleteObject(key: string, bucket?: string): Promise<void>;
14
- getObject(key: string, bucket?: string): Promise<Readable>;
6
+ private readonly logger?;
7
+ constructor(options: S3ModuleOptions, s3Client: S3Client);
8
+ upload(key: string, body: S3Body, options?: S3UploadOptions): Promise<S3UploadResult>;
9
+ putObject(key: string, body: S3Body, options?: S3UploadOptions): Promise<S3PutObjectResult>;
10
+ uploadObject(key: string, body: S3Body, options?: S3UploadOptions): Promise<S3PutObjectResult>;
11
+ deleteObject(key: string, options?: S3ObjectOptions): Promise<S3DeleteObjectResult>;
12
+ getObject(key: string, options?: S3ObjectOptions): Promise<S3GetObjectResult>;
13
+ getSignedUrl(key: string, options?: S3SignedUrlOptions): Promise<string>;
14
+ private createPutObjectParams;
15
+ private resolveBucket;
16
+ private log;
17
+ private logError;
15
18
  }
@@ -11,81 +11,137 @@ var __metadata = (this && this.__metadata) || function (k, v) {
11
11
  var __param = (this && this.__param) || function (paramIndex, decorator) {
12
12
  return function (target, key) { decorator(target, key, paramIndex); }
13
13
  };
14
+ var S3Service_1;
14
15
  Object.defineProperty(exports, "__esModule", { value: true });
15
16
  exports.S3Service = void 0;
16
17
  const common_1 = require("@nestjs/common");
17
18
  const client_s3_1 = require("@aws-sdk/client-s3");
18
- const s3_constants_1 = require("./s3.constants");
19
+ const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner");
20
+ const stream_1 = require("stream");
19
21
  const lib_storage_1 = require("@aws-sdk/lib-storage");
20
- let S3Service = class S3Service {
22
+ const s3_constants_1 = require("./s3.constants");
23
+ let S3Service = S3Service_1 = class S3Service {
21
24
  constructor(options, s3Client) {
22
25
  this.options = options;
23
26
  this.s3Client = s3Client;
24
- this.logger = new common_1.Logger(this.constructor.name);
25
- this.bucket = this.options.bucket;
27
+ this.logger =
28
+ options.logger === true ? new common_1.Logger(S3Service_1.name) : options.logger || undefined;
26
29
  }
27
- async upload(key, body, contentType = 'application/octet-stream', params) {
30
+ async upload(key, body, options) {
28
31
  const upload = new lib_storage_1.Upload({
29
32
  client: this.s3Client,
30
- params: {
31
- Bucket: params?.bucket || this.bucket,
32
- Key: key,
33
- Body: body,
34
- ContentType: contentType,
35
- ACL: params?.acl || 'public-read',
36
- },
33
+ params: this.createPutObjectParams(key, body, options, true),
37
34
  });
38
- await upload.done();
35
+ const response = await upload.done();
36
+ this.log(`Uploaded object with multipart upload: ${key}`);
37
+ return response;
39
38
  }
40
- async uploadObject(key, body, contentType = 'application/octet-stream', params) {
41
- const command = new client_s3_1.PutObjectCommand({
42
- Bucket: params?.bucket || this.bucket,
43
- Key: key,
44
- Body: body,
45
- ContentType: contentType,
46
- ACL: params?.acl || 'public-read',
47
- });
39
+ async putObject(key, body, options) {
40
+ const command = new client_s3_1.PutObjectCommand(this.createPutObjectParams(key, body, options, true));
48
41
  try {
49
- await this.s3Client.send(command);
50
- this.logger.log(`Uploaded object: ${key}`);
42
+ const response = await this.s3Client.send(command);
43
+ this.log(`Uploaded object: ${key}`);
44
+ return response;
51
45
  }
52
46
  catch (error) {
53
- this.logger.error(`Failed to upload object: ${key}`, error);
47
+ this.logError(`Failed to upload object: ${key}`, error);
54
48
  throw error;
55
49
  }
56
50
  }
57
- async deleteObject(key, bucket) {
51
+ async uploadObject(key, body, options) {
52
+ return this.putObject(key, body, options);
53
+ }
54
+ async deleteObject(key, options) {
58
55
  const command = new client_s3_1.DeleteObjectCommand({
59
- Bucket: bucket || this.bucket,
56
+ Bucket: this.resolveBucket(options?.bucket),
60
57
  Key: key,
61
58
  });
62
59
  try {
63
- await this.s3Client.send(command);
64
- this.logger.log(`Deleted object: ${key}`);
60
+ const response = await this.s3Client.send(command);
61
+ this.log(`Deleted object: ${key}`);
62
+ return response;
65
63
  }
66
64
  catch (error) {
67
- this.logger.error(`Failed to delete object: ${key}`, error);
65
+ this.logError(`Failed to delete object: ${key}`, error);
68
66
  throw error;
69
67
  }
70
68
  }
71
- async getObject(key, bucket) {
69
+ async getObject(key, options) {
72
70
  const command = new client_s3_1.GetObjectCommand({
73
- Bucket: bucket || this.bucket,
71
+ Bucket: this.resolveBucket(options?.bucket),
74
72
  Key: key,
75
73
  });
76
74
  try {
77
75
  const response = await this.s3Client.send(command);
78
- this.logger.log(`Fetched object: ${key}`);
79
- return response.Body;
76
+ this.log(`Fetched object: ${key}`);
77
+ if (!response.Body) {
78
+ throw new Error(`S3 object "${key}" returned an empty body.`);
79
+ }
80
+ if (!(response.Body instanceof stream_1.Readable)) {
81
+ throw new Error(`S3 object "${key}" did not return a Node.js readable stream.`);
82
+ }
83
+ return {
84
+ body: response.Body,
85
+ cacheControl: response.CacheControl,
86
+ contentLength: response.ContentLength,
87
+ contentType: response.ContentType,
88
+ eTag: response.ETag,
89
+ lastModified: response.LastModified,
90
+ metadata: response.Metadata,
91
+ };
80
92
  }
81
93
  catch (error) {
82
- this.logger.error(`Failed to get object: ${key}`, error);
94
+ this.logError(`Failed to get object: ${key}`, error);
83
95
  throw error;
84
96
  }
85
97
  }
98
+ async getSignedUrl(key, options) {
99
+ const expiresIn = options?.expiresIn ?? 900;
100
+ const operation = options?.operation ?? 'getObject';
101
+ const command = operation === 'putObject'
102
+ ? new client_s3_1.PutObjectCommand(this.createPutObjectParams(key, undefined, options))
103
+ : new client_s3_1.GetObjectCommand({
104
+ Bucket: this.resolveBucket(options?.bucket),
105
+ Key: key,
106
+ });
107
+ return (0, s3_request_presigner_1.getSignedUrl)(this.s3Client, command, { expiresIn });
108
+ }
109
+ createPutObjectParams(key, body, options, useDefaultContentType = false) {
110
+ const contentType = options?.contentType ??
111
+ (useDefaultContentType ? 'application/octet-stream' : undefined);
112
+ return {
113
+ Bucket: this.resolveBucket(options?.bucket),
114
+ Key: key,
115
+ ...(body !== undefined ? { Body: body } : {}),
116
+ ...(contentType ? { ContentType: contentType } : {}),
117
+ ...(options?.acl ? { ACL: options.acl } : {}),
118
+ ...(options?.cacheControl ? { CacheControl: options.cacheControl } : {}),
119
+ ...(options?.metadata ? { Metadata: options.metadata } : {}),
120
+ };
121
+ }
122
+ resolveBucket(bucket) {
123
+ const resolvedBucket = bucket ?? this.options.bucket;
124
+ if (!resolvedBucket) {
125
+ throw new Error('S3 bucket is not configured. Pass a bucket to the method call or set S3ModuleOptions.bucket.');
126
+ }
127
+ return resolvedBucket;
128
+ }
129
+ log(message) {
130
+ this.logger?.log(message);
131
+ }
132
+ logError(message, error) {
133
+ if (!this.logger) {
134
+ return;
135
+ }
136
+ if (error instanceof Error) {
137
+ this.logger.error(message, error.stack ?? error.message);
138
+ return;
139
+ }
140
+ this.logger.error(message, error);
141
+ }
86
142
  };
87
143
  exports.S3Service = S3Service;
88
- exports.S3Service = S3Service = __decorate([
144
+ exports.S3Service = S3Service = S3Service_1 = __decorate([
89
145
  (0, common_1.Injectable)(),
90
146
  __param(0, (0, common_1.Inject)(s3_constants_1.S3_MODULE_OPTIONS)),
91
147
  __param(1, (0, common_1.Inject)(s3_constants_1.S3_CLIENT)),
@@ -1 +1 @@
1
- {"version":3,"file":"s3.service.js","sourceRoot":"","sources":["../../../src/modules/s3/s3.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"s3.service.js","sourceRoot":"","sources":["../../../src/modules/s3/s3.service.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@russ-b/nestjs-common-tools",
3
- "version": "1.13.1",
3
+ "version": "1.14.1",
4
4
  "description": "NestJS utility tools",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -47,6 +47,10 @@
47
47
  "types": "./dist/common/filters/index.d.ts",
48
48
  "default": "./dist/common/filters/index.js"
49
49
  },
50
+ "./class-transformer": {
51
+ "types": "./dist/class-transformer/index.d.ts",
52
+ "default": "./dist/class-transformer/index.js"
53
+ },
50
54
  "./validators": {
51
55
  "types": "./dist/validators/index.d.ts",
52
56
  "default": "./dist/validators/index.js"
@@ -82,6 +86,9 @@
82
86
  "common/filters": [
83
87
  "dist/common/filters/index.d.ts"
84
88
  ],
89
+ "class-transformer": [
90
+ "dist/class-transformer/index.d.ts"
91
+ ],
85
92
  "modules": [
86
93
  "dist/modules/index.d.ts"
87
94
  ],
@@ -99,6 +106,7 @@
99
106
  "peerDependencies": {
100
107
  "@aws-sdk/client-s3": "^3.0.0",
101
108
  "@aws-sdk/lib-storage": "^3.0.0",
109
+ "@aws-sdk/s3-request-presigner": "^3.1019.0",
102
110
  "@nestjs/common": "^10.0.0 || ^11.0.0",
103
111
  "@nestjs/config": "^3.0.0 || ^4.0.0",
104
112
  "nest-winston": "^1.10.2",