nest-hex 0.2.0 → 0.3.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.
Files changed (51) hide show
  1. package/README.md +155 -267
  2. package/dist/src/cli/bin.js +2644 -0
  3. package/dist/src/cli/commands/index.d.ts +23 -0
  4. package/dist/src/cli/commands/index.js +2609 -0
  5. package/dist/src/cli/config/defaults.d.ts +74 -0
  6. package/dist/src/cli/config/defaults.js +76 -0
  7. package/dist/src/cli/config/define-config.d.ts +74 -0
  8. package/dist/src/cli/config/define-config.js +62 -0
  9. package/dist/src/cli/config/loader.d.ts +74 -0
  10. package/dist/src/cli/config/loader.js +106 -0
  11. package/dist/src/cli/config/validator.d.ts +81 -0
  12. package/dist/src/cli/config/validator.js +108 -0
  13. package/dist/src/cli/generators/adapter.generator.d.ts +235 -0
  14. package/dist/src/cli/generators/adapter.generator.js +377 -0
  15. package/dist/src/cli/generators/base.generator.d.ts +190 -0
  16. package/dist/src/cli/generators/base.generator.js +312 -0
  17. package/dist/src/cli/generators/index.d.ts +264 -0
  18. package/dist/src/cli/generators/index.js +467 -0
  19. package/dist/src/cli/generators/port.generator.d.ts +211 -0
  20. package/dist/src/cli/generators/port.generator.js +364 -0
  21. package/dist/src/cli/generators/service.generator.d.ts +208 -0
  22. package/dist/src/cli/generators/service.generator.js +340 -0
  23. package/dist/src/cli/index.d.ts +74 -0
  24. package/dist/src/cli/index.js +69 -0
  25. package/dist/src/cli/types/config.types.d.ts +73 -0
  26. package/dist/src/cli/types/config.types.js +56 -0
  27. package/dist/src/cli/types/generator.types.d.ts +46 -0
  28. package/dist/src/cli/types/generator.types.js +56 -0
  29. package/dist/src/cli/types/index.d.ts +121 -0
  30. package/dist/src/cli/types/index.js +56 -0
  31. package/dist/src/cli/types/template.types.d.ts +28 -0
  32. package/dist/src/cli/types/template.types.js +56 -0
  33. package/dist/src/cli/ui/components/index.d.ts +97 -0
  34. package/dist/src/cli/ui/components/index.js +1507 -0
  35. package/dist/src/cli/utils/file-writer.d.ts +17 -0
  36. package/dist/src/cli/utils/file-writer.js +100 -0
  37. package/dist/src/cli/utils/linter-detector.d.ts +12 -0
  38. package/dist/src/cli/utils/linter-detector.js +128 -0
  39. package/dist/src/cli/utils/linter-runner.d.ts +17 -0
  40. package/dist/src/cli/utils/linter-runner.js +101 -0
  41. package/dist/src/cli/utils/name-transformer.d.ts +18 -0
  42. package/dist/src/cli/utils/name-transformer.js +90 -0
  43. package/dist/src/cli/utils/path-resolver.d.ts +5 -0
  44. package/dist/src/cli/utils/path-resolver.js +78 -0
  45. package/dist/src/cli/utils/port-scanner.d.ts +93 -0
  46. package/dist/src/cli/utils/port-scanner.js +104 -0
  47. package/dist/src/cli/utils/template-renderer.d.ts +30 -0
  48. package/dist/src/cli/utils/template-renderer.js +95 -0
  49. package/dist/{index.d.ts → src/index.d.ts} +60 -30
  50. package/dist/{index.js → src/index.js} +45 -13
  51. package/package.json +10 -10
package/README.md CHANGED
@@ -1,29 +1,28 @@
1
1
  # nest-hex
2
2
 
3
- > A tiny, **class-based**, **NestJS-native** helper library for building **pluggable adapters** following the Ports & Adapters (Hexagonal Architecture) pattern with minimal boilerplate and great developer experience.
3
+ > A tiny, **class-based**, **NestJS-native** library for building **pluggable adapters** following the Ports & Adapters (Hexagonal Architecture) pattern with minimal boilerplate.
4
4
 
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
- ## Why?
7
+ ## What is nest-hex?
8
8
 
9
- Building NestJS applications with the Ports & Adapters pattern involves repetitive boilerplate:
9
+ **nest-hex** eliminates boilerplate when building NestJS applications with the Ports & Adapters (Hexagonal Architecture) pattern. It provides decorators and base classes that handle all the repetitive wiring, letting you focus on business logic.
10
10
 
11
- - Registering concrete implementation classes
12
- - Aliasing port tokens to implementations (`useExisting`)
13
- - Exporting only the port token (not provider objects)
14
- - Supporting both `register()` and `registerAsync()` patterns
15
- - Keeping the app responsible for configuration (no `process.env` in libraries)
11
+ ### Why Hexagonal Architecture?
16
12
 
17
- This library provides base classes and decorators to eliminate this boilerplate while maintaining compile-time type safety.
13
+ - 🧪 **Testable** - Mock infrastructure easily, test business logic in isolation
14
+ - 🔌 **Swappable** - Switch from S3 to Azure Blob Storage without touching domain code
15
+ - 🎯 **Clean** - Keep business logic free of infrastructure concerns
16
+ - 🌍 **Flexible** - Use different adapters for dev, test, and production
18
17
 
19
18
  ## Features
20
19
 
21
- - 🎯 **Declarative**: Declare port tokens and implementations once using `@Port({ token, implementation })`
22
- - 🏗️ **Class-based**: Use standard NestJS dynamic modules, no function factories required
23
- - 🔒 **Type-safe**: `AdapterModule<TToken>` carries compile-time proof of which token it provides
24
- - ⚡ **Zero runtime overhead**: Uses TypeScript decorators and metadata, minimal abstraction
25
- - 📦 **Tiny**: Core library is under 1KB minified
26
- - 🧪 **Testable**: Easily mock adapters for testing
20
+ - 🎯 **Declarative** - Declare port tokens and implementations once using `@Adapter({ portToken, implementation })`
21
+ - 🏗️ **Class-based** - Standard NestJS dynamic modules, no function factories
22
+ - 🔒 **Type-safe** - Compile-time proof that adapters provide the correct port tokens
23
+ - ⚡ **Zero runtime overhead** - Uses TypeScript decorators and metadata
24
+ - 📦 **Tiny** - Core library under 1KB minified
25
+ - 🛠️ **Powerful CLI** - Generate ports, adapters, and services instantly
27
26
 
28
27
  ## Installation
29
28
 
@@ -37,8 +36,7 @@ pnpm add nest-hex
37
36
  bun add nest-hex
38
37
  ```
39
38
 
40
- ### Peer Dependencies
41
-
39
+ **Peer dependencies:**
42
40
  ```bash
43
41
  npm install @nestjs/common @nestjs/core reflect-metadata
44
42
  ```
@@ -49,11 +47,11 @@ npm install @nestjs/common @nestjs/core reflect-metadata
49
47
 
50
48
  ```typescript
51
49
  // storage.port.ts
52
- export const STORAGE_PORT = Symbol('STORAGE_PORT');
50
+ export const STORAGE_PORT = Symbol('STORAGE_PORT')
53
51
 
54
52
  export interface StoragePort {
55
- upload(file: Buffer, key: string): Promise<{ url: string }>;
56
- download(key: string): Promise<Buffer>;
53
+ upload(key: string, data: Buffer): Promise<string>
54
+ download(key: string): Promise<Buffer>
57
55
  }
58
56
  ```
59
57
 
@@ -61,103 +59,116 @@ export interface StoragePort {
61
59
 
62
60
  ```typescript
63
61
  // s3.adapter.ts
64
- import { Injectable } from '@nestjs/common';
65
- import { Adapter, Port } from 'nest-hex';
66
- import { STORAGE_PORT, type StoragePort } from './storage.port';
62
+ import { Injectable } from '@nestjs/common'
63
+ import { Adapter } from 'nest-hex'
64
+ import { STORAGE_PORT, type StoragePort } from './storage.port'
67
65
 
68
66
  // Implementation service
69
67
  @Injectable()
70
- class S3StorageService implements StoragePort {
71
- async upload(file: Buffer, key: string) {
68
+ class S3Service implements StoragePort {
69
+ constructor(private options: { bucket: string; region: string }) {}
70
+
71
+ async upload(key: string, data: Buffer): Promise<string> {
72
72
  // AWS S3 upload logic here
73
- return { url: `https://s3.amazonaws.com/bucket/${key}` };
73
+ return `https://s3.amazonaws.com/${this.options.bucket}/${key}`
74
74
  }
75
75
 
76
- async download(key: string) {
76
+ async download(key: string): Promise<Buffer> {
77
77
  // AWS S3 download logic here
78
- return Buffer.from('file contents');
78
+ return Buffer.from('file contents')
79
79
  }
80
80
  }
81
81
 
82
- // Adapter configuration
83
- interface S3Options {
84
- bucket: string;
85
- region: string;
86
- accessKeyId?: string;
87
- secretAccessKey?: string;
88
- }
89
-
90
82
  // Adapter module - single decorator declares everything!
91
- @Port({
92
- token: STORAGE_PORT,
93
- implementation: S3StorageService,
83
+ @Adapter({
84
+ portToken: STORAGE_PORT,
85
+ implementation: S3Service
94
86
  })
95
- export class S3Adapter extends Adapter<S3Options> {}
87
+ export class S3Adapter extends AdapterBase<{ bucket: string; region: string }> {}
96
88
  ```
97
89
 
98
- ### 3. Create a Port Module (Domain Service)
90
+ ### 3. Create a Domain Service
99
91
 
100
92
  ```typescript
101
- // storage.module.ts
102
- import { Injectable, Module } from '@nestjs/common';
103
- import { InjectPort, PortModule } from 'nest-hex';
104
- import { STORAGE_PORT, type StoragePort } from './storage.port';
93
+ // file.service.ts
94
+ import { Injectable } from '@nestjs/common'
95
+ import { InjectPort } from 'nest-hex'
96
+ import { STORAGE_PORT, type StoragePort } from './storage.port'
105
97
 
106
- // Domain service that uses the port
107
98
  @Injectable()
108
- export class StorageService {
99
+ export class FileService {
109
100
  constructor(
110
101
  @InjectPort(STORAGE_PORT)
111
- private readonly storage: StoragePort,
102
+ private readonly storage: StoragePort
112
103
  ) {}
113
104
 
114
- async uploadUserAvatar(userId: string, image: Buffer) {
115
- const key = `avatars/${userId}.jpg`;
116
- return this.storage.upload(image, key);
117
- }
118
-
119
- async downloadUserAvatar(userId: string) {
120
- const key = `avatars/${userId}.jpg`;
121
- return this.storage.download(key);
105
+ async uploadUserAvatar(userId: string, image: Buffer): Promise<string> {
106
+ const key = `avatars/${userId}.jpg`
107
+ return this.storage.upload(key, image)
122
108
  }
123
109
  }
110
+ ```
111
+
112
+ ### 4. Create a Port Module
113
+
114
+ ```typescript
115
+ // file.module.ts
116
+ import { Module } from '@nestjs/common'
117
+ import { PortModule } from 'nest-hex'
118
+ import { FileService } from './file.service'
124
119
 
125
- // Port module that accepts any adapter
126
120
  @Module({})
127
- export class StorageModule extends PortModule<typeof StorageService> {}
121
+ export class FileModule extends PortModule<typeof FileService> {}
128
122
  ```
129
123
 
130
- ### 4. Wire It Up in Your App
124
+ ### 5. Wire It Up
131
125
 
132
126
  ```typescript
133
127
  // app.module.ts
134
- import { Module } from '@nestjs/common';
135
- import { StorageModule } from './storage/storage.module';
136
- import S3Adapter from './storage/adapters/s3.adapter';
128
+ import { Module } from '@nestjs/common'
129
+ import { FileModule } from './file.module'
130
+ import { S3Adapter } from './s3.adapter'
137
131
 
138
132
  @Module({
139
133
  imports: [
140
- StorageModule.register({
134
+ FileModule.register({
141
135
  adapter: S3Adapter.register({
142
- bucket: 'my-app-uploads',
143
- region: 'us-east-1',
144
- accessKeyId: process.env.AWS_ACCESS_KEY_ID,
145
- secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
146
- }),
147
- }),
148
- ],
136
+ bucket: process.env.S3_BUCKET || 'my-bucket',
137
+ region: process.env.AWS_REGION || 'us-east-1'
138
+ })
139
+ })
140
+ ]
149
141
  })
150
142
  export class AppModule {}
151
143
  ```
152
144
 
153
145
  That's it! You now have a fully type-safe, pluggable storage adapter. 🎉
154
146
 
147
+ ## CLI
148
+
149
+ Generate ports, adapters, and services instantly with the built-in CLI:
150
+
151
+ ```bash
152
+ # Initialize configuration
153
+ npx nest-hex init
154
+
155
+ # Generate a port (domain interface)
156
+ npx nest-hex generate port ObjectStorage
157
+
158
+ # Generate an adapter for the port
159
+ npx nest-hex generate adapter S3 --port ObjectStorage
160
+
161
+ # Or generate both at once
162
+ npx nest-hex generate full ObjectStorage S3
163
+ ```
164
+
165
+ **See [CLI Documentation](./docs/cli.md) for complete command reference, configuration options, and template customization.**
166
+
155
167
  ## Key Benefits
156
168
 
157
169
  ### Before (Manual Boilerplate)
158
170
 
159
171
  ```typescript
160
- // Lots of manual wiring...
161
172
  @Module({})
162
173
  export class S3StorageModule {
163
174
  static register(options: S3Options): DynamicModule {
@@ -169,7 +180,7 @@ export class S3StorageModule {
169
180
  // More boilerplate...
170
181
  ],
171
182
  exports: [STORAGE_PORT],
172
- };
183
+ }
173
184
  }
174
185
  }
175
186
  ```
@@ -177,231 +188,109 @@ export class S3StorageModule {
177
188
  ### After (With nest-hex)
178
189
 
179
190
  ```typescript
180
- // Clean and declarative!
181
- @Port({
182
- token: STORAGE_PORT,
183
- implementation: S3StorageService,
191
+ @Adapter({
192
+ portToken: STORAGE_PORT,
193
+ implementation: S3StorageService
184
194
  })
185
- export class S3Adapter extends Adapter<S3Options> {}
195
+ export class S3Adapter extends AdapterBase<S3Options> {}
186
196
  ```
187
197
 
188
- ## Advanced Usage
198
+ ## Swappable Infrastructure
189
199
 
190
- ### Async Registration with Dependency Injection
200
+ The real power: swap infrastructure without touching business logic.
191
201
 
192
202
  ```typescript
193
- import { ConfigModule, ConfigService } from '@nestjs/config';
203
+ // Development: Use local filesystem
204
+ const adapter = process.env.NODE_ENV === 'production'
205
+ ? S3Adapter.register({ bucket: 'prod-bucket', region: 'us-east-1' })
206
+ : LocalStorageAdapter.register({ basePath: './uploads' })
194
207
 
195
208
  @Module({
196
- imports: [
197
- StorageModule.register({
198
- adapter: S3Adapter.registerAsync({
199
- imports: [ConfigModule],
200
- inject: [ConfigService],
201
- useFactory: (config: ConfigService) => ({
202
- bucket: config.get('S3_BUCKET'),
203
- region: config.get('AWS_REGION'),
204
- accessKeyId: config.get('AWS_ACCESS_KEY_ID'),
205
- secretAccessKey: config.get('AWS_SECRET_ACCESS_KEY'),
206
- }),
207
- }),
208
- }),
209
- ],
209
+ imports: [FileModule.register({ adapter })]
210
210
  })
211
211
  export class AppModule {}
212
212
  ```
213
213
 
214
- ### Custom Imports and Extra Ports
215
-
216
- ```typescript
217
- @Port({
218
- token: HTTP_CLIENT_PORT,
219
- implementation: AxiosHttpClient,
220
- })
221
- class AxiosAdapterClass extends Adapter<AxiosOptions> {
222
- protected override imports(options: AxiosOptions) {
223
- return [
224
- HttpModule.register({
225
- baseURL: options.baseUrl,
226
- timeout: options.timeout,
227
- }),
228
- ];
229
- }
230
-
231
- protected override extraPoviders(options: AxiosOptions) {
232
- return [
233
- {
234
- provide: 'HTTP_CLIENT_CONFIG',
235
- useValue: options,
236
- },
237
- ];
238
- }
239
- }
240
- ```
214
+ Your `FileService` business logic **never changes**. Only the adapter changes.
241
215
 
242
- ### Swapping Adapters
216
+ ## Advanced Features
243
217
 
244
- The beauty of the Ports & Adapters pattern is that you can easily swap implementations:
218
+ ### Async Configuration with Dependency Injection
245
219
 
246
220
  ```typescript
247
- // Development: Use filesystem storage
248
- import FilesystemAdapter from './storage/adapters/filesystem.adapter';
249
-
250
- // Production: Use AWS S3
251
- import S3Adapter from './storage/adapters/s3.adapter';
252
-
253
- const adapter = process.env.NODE_ENV === 'production'
254
- ? S3Adapter.register({ bucket: 'prod-bucket', region: 'us-east-1' })
255
- : FilesystemAdapter.register({ basePath: './uploads' });
256
-
257
221
  @Module({
258
222
  imports: [
259
- StorageModule.register({ adapter }),
260
- ],
223
+ FileModule.register({
224
+ adapter: S3Adapter.registerAsync({
225
+ imports: [ConfigModule],
226
+ inject: [ConfigService],
227
+ useFactory: (config: ConfigService) => ({
228
+ bucket: config.get('S3_BUCKET')!,
229
+ region: config.get('AWS_REGION')!
230
+ })
231
+ })
232
+ })
233
+ ]
261
234
  })
262
235
  export class AppModule {}
263
236
  ```
264
237
 
265
- ### Testing with Mock Adapters
238
+ ### Adapters with Dependencies
266
239
 
267
240
  ```typescript
268
- import { Adapter, Port } from 'nest-hex';
241
+ @Adapter({
242
+ portToken: HTTP_CLIENT_PORT,
243
+ implementation: AxiosHttpClient,
244
+ imports: [HttpModule],
245
+ providers: [
246
+ { provide: 'HTTP_CONFIG', useValue: { timeout: 5000 } }
247
+ ]
248
+ })
249
+ export class AxiosAdapter extends AdapterBase<AxiosOptions> {}
250
+ ```
269
251
 
270
- class MockStorageService {
271
- async upload(file: Buffer, key: string) {
272
- return { url: `mock://storage/${key}` };
273
- }
252
+ ### Mock Adapters for Testing
274
253
 
275
- async download(key: string) {
276
- return Buffer.from('mock file contents');
254
+ ```typescript
255
+ @Injectable()
256
+ class MockStorageService implements StoragePort {
257
+ async upload(key: string, data: Buffer): Promise<string> {
258
+ return `mock://storage/${key}`
259
+ }
260
+ async download(key: string): Promise<Buffer> {
261
+ return Buffer.from('mock data')
277
262
  }
278
263
  }
279
264
 
280
- @Port({
281
- token: STORAGE_PORT,
282
- implementation: MockStorageService,
265
+ @Adapter({
266
+ portToken: STORAGE_PORT,
267
+ implementation: MockStorageService
283
268
  })
284
- export class MockStorageAdapter extends Adapter<void> {}
269
+ export class MockStorageAdapter extends AdapterBase<{}> {}
285
270
 
286
271
  // Use in tests
287
272
  const module = await Test.createTestingModule({
288
273
  imports: [
289
- StorageModule.register({
290
- adapter: MockStorageAdapter.register(undefined),
291
- }),
292
- ],
293
- }).compile();
294
- ```
295
-
296
- ## API Reference
297
-
298
- ### Core Classes
299
-
300
- #### `Adapter<TOptions>`
301
-
302
- Abstract base class for building adapter modules.
303
-
304
- **Methods:**
305
- - `static register<TToken, TOptions>(options: TOptions): AdapterModule<TToken>` - Synchronous registration
306
- - `static registerAsync<TToken, TOptions>(config: AsyncConfig): AdapterModule<TToken>` - Async registration with DI
307
-
308
- **Protected Hooks:**
309
- - `protected imports(options?: TOptions): unknown[]` - Override to import other NestJS modules
310
- - `protected extraPoviders(options: TOptions): Port[]` - Override to add additional providers
311
-
312
- #### `PortModule<TService>`
313
-
314
- Abstract base class for building port modules that consume adapters.
315
-
316
- **Methods:**
317
- - `static register<TToken>({ adapter }: { adapter?: AdapterModule<TToken> }): DynamicModule`
318
-
319
- ### Decorators
320
-
321
- #### `@Port({ token, implementation })`
322
-
323
- Class decorator that declares which port token an adapter provides and its implementation class.
324
-
325
- **Parameters:**
326
- - `token: TToken` - The port token (usually a Symbol)
327
- - `implementation: Type<unknown>` - The concrete implementation class
328
-
329
- **Example:**
330
- ```typescript
331
- @Port({
332
- token: STORAGE_PORT,
333
- implementation: S3StorageService,
334
- })
335
- class S3Adapter extends Adapter<S3Options> {}
336
- ```
337
-
338
- #### `@InjectPort(token)`
339
-
340
- Parameter decorator for injecting a port token into service constructors.
341
-
342
- **Example:**
343
- ```typescript
344
- constructor(
345
- @InjectPort(STORAGE_PORT)
346
- private readonly storage: StoragePort,
347
- ) {}
348
- ```
349
-
350
- ### Types
351
-
352
- #### `AdapterModule<TToken>`
353
-
354
- A DynamicModule that carries compile-time proof it provides `TToken`.
355
-
356
- ```typescript
357
- type AdapterModule<TToken> = DynamicModule & {
358
- __provides: TToken;
359
- };
274
+ FileModule.register({
275
+ adapter: MockStorageAdapter.register({})
276
+ })
277
+ ]
278
+ }).compile()
360
279
  ```
361
280
 
362
- ## Best Practices
363
-
364
- ### ✅ Do's
365
-
366
- - **Export port tokens, not provider objects**
367
- ```typescript
368
- exports: [STORAGE_PORT] // ✅ Correct
369
- ```
370
-
371
- - **Keep configuration in the app layer**
372
- ```typescript
373
- // ✅ Good: App provides config
374
- S3Adapter.register({
375
- bucket: process.env.S3_BUCKET,
376
- })
377
- ```
378
-
379
- - **Use `@InjectPort` for clarity**
380
- ```typescript
381
- @InjectPort(STORAGE_PORT) // ✅ Clear intent
382
- ```
383
-
384
- - **Create small, focused adapters**
385
- - One adapter = one infrastructure concern
386
-
387
- ### ❌ Don'ts
388
-
389
- - **Don't export provider objects**
390
- ```typescript
391
- exports: [{ provide: STORAGE_PORT, useExisting: S3Service }] // ❌ Wrong
392
- ```
281
+ ## Documentation
393
282
 
394
- - **Don't use `process.env` in adapters**
395
- ```typescript
396
- // Bad: Config hard-coded in adapter
397
- class S3Adapter {
398
- bucket = process.env.S3_BUCKET;
399
- }
400
- ```
283
+ 📚 **Complete Documentation:**
284
+ - **[Library Documentation](./docs/library.md)** - Full API reference, architecture guide, advanced patterns, and examples
285
+ - **[CLI Documentation](./docs/cli.md)** - Complete CLI reference, configuration, templates, and best practices
401
286
 
402
- - **Don't mix domain logic with adapters**
403
- - Adapters = infrastructure only
404
- - Domain logic = port modules/services
287
+ 📖 **Quick Links:**
288
+ - [Core Concepts](./docs/library.md#core-concepts) - Understand ports, adapters, and services
289
+ - [Why Hexagonal Architecture?](./docs/library.md#why-hexagonal-architecture) - Benefits with code examples
290
+ - [Architecture Overview](./docs/library.md#architecture-overview) - Visual diagrams
291
+ - [API Reference](./docs/library.md#api-reference) - Complete API documentation
292
+ - [Testing Guide](./docs/library.md#testing) - Mock adapters and integration testing
293
+ - [Migration Guide](./docs/library.md#migration-guide) - Upgrading from @Port to @Adapter
405
294
 
406
295
  ## Examples
407
296
 
@@ -409,17 +298,16 @@ See the [`examples/`](./examples) directory for complete working examples:
409
298
 
410
299
  - **Object Storage** - S3 adapter with file upload/download
411
300
  - **Currency Rates** - HTTP API adapter with rate conversion
412
- - **Basic Examples** - Decorator usage patterns
413
-
414
- ## Documentation
415
-
416
- - 📖 [Full Specification](./spec/spec.md) - Complete implementation guide with AWS S3 and HTTP API examples
417
- - 🔧 [API Reference](#api-reference) - Detailed API documentation
301
+ - **Mock Patterns** - Testing with mock adapters
418
302
 
419
303
  ## License
420
304
 
421
- MIT
305
+ MIT © [Your Name]
422
306
 
423
307
  ## Contributing
424
308
 
425
- Contributions are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for contribution guidelines.
309
+ Contributions are welcome! Please read our [Contributing Guide](./CONTRIBUTING.md) for details on our code of conduct and development process.
310
+
311
+ ---
312
+
313
+ **Built with ❤️ for the NestJS community**