nestjs-temporal-core 3.0.4 → 3.0.5

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
@@ -23,7 +23,8 @@ NestJS Temporal Core brings Temporal's durable execution to NestJS with familiar
23
23
  - **⚙️ Flexible Setup** - Client-only, worker-only, or unified deployments
24
24
  - **🏥 Health Monitoring** - Comprehensive status monitoring and health checks
25
25
  - **🔧 Production Ready** - TLS, connection management, graceful shutdowns
26
- - **📊 Complete Integration** - Full-featured module architecture
26
+ - **📊 Modular Architecture** - Individual modules for specific needs
27
+ - **🔐 Enterprise Ready** - Temporal Cloud support with TLS and API keys
27
28
 
28
29
  ## 📦 Installation
29
30
 
@@ -33,7 +34,9 @@ npm install nestjs-temporal-core @temporalio/client @temporalio/worker @temporal
33
34
 
34
35
  ## 🚀 Quick Start
35
36
 
36
- ### 1. Module Setup
37
+ ### 1. Complete Integration (Recommended)
38
+
39
+ For applications that need full Temporal functionality:
37
40
 
38
41
  ```typescript
39
42
  // app.module.ts
@@ -48,294 +51,505 @@ import { EmailActivities } from './activities/email.activities';
48
51
  address: 'localhost:7233',
49
52
  namespace: 'default',
50
53
  },
51
- taskQueue: 'my-app',
54
+ taskQueue: 'main-queue',
52
55
  worker: {
53
56
  workflowsPath: './dist/workflows',
54
- activityClasses: [EmailActivities], // Auto-discovered
55
- },
56
- }),
57
+ activityClasses: [EmailActivities],
58
+ autoStart: true
59
+ }
60
+ })
57
61
  ],
58
- providers: [EmailActivities],
62
+ providers: [EmailActivities], // Auto-discovered
59
63
  })
60
64
  export class AppModule {}
61
65
  ```
62
66
 
63
- ### 2. Create Activities
67
+ ### 2. Define Activities
64
68
 
65
69
  ```typescript
66
70
  // activities/email.activities.ts
67
71
  import { Injectable } from '@nestjs/common';
68
72
  import { Activity, ActivityMethod } from 'nestjs-temporal-core';
69
73
 
70
- @Injectable()
71
74
  @Activity()
75
+ @Injectable()
72
76
  export class EmailActivities {
73
- @ActivityMethod()
74
- async sendWelcomeEmail(email: string, name: string): Promise<boolean> {
75
- console.log(`Sending welcome email to ${email}`);
76
- // Your email logic here
77
- return true;
77
+
78
+ @ActivityMethod({
79
+ name: 'sendEmail',
80
+ timeout: '30s',
81
+ maxRetries: 3
82
+ })
83
+ async sendEmail(to: string, subject: string, body: string): Promise<void> {
84
+ console.log(`Sending email to ${to}: ${subject}`);
85
+ // Your email sending logic here
78
86
  }
79
87
 
80
- @ActivityMethod()
81
- async sendNotification(email: string, message: string): Promise<void> {
82
- console.log(`Notification to ${email}: ${message}`);
88
+ @ActivityMethod('sendNotification')
89
+ async sendNotification(userId: string, message: string): Promise<void> {
90
+ console.log(`Notifying user ${userId}: ${message}`);
83
91
  // Your notification logic here
84
92
  }
85
93
  }
86
94
  ```
87
95
 
88
- ### 3. Create Scheduled Workflows
96
+ ### 3. Create Workflows
97
+
98
+ ```typescript
99
+ // workflows/email.workflow.ts
100
+ import { proxyActivities } from '@temporalio/workflow';
101
+ import type { EmailActivities } from '../activities/email.activities';
102
+
103
+ const { sendEmail, sendNotification } = proxyActivities<EmailActivities>({
104
+ startToCloseTimeout: '1 minute',
105
+ });
106
+
107
+ export async function processEmailWorkflow(
108
+ userId: string,
109
+ emailData: { to: string; subject: string; body: string }
110
+ ): Promise<void> {
111
+ // Send email
112
+ await sendEmail(emailData.to, emailData.subject, emailData.body);
113
+
114
+ // Send notification
115
+ await sendNotification(userId, 'Email sent successfully');
116
+ }
117
+ ```
118
+
119
+ ### 4. Schedule Workflows
89
120
 
90
121
  ```typescript
91
122
  // services/scheduled.service.ts
92
123
  import { Injectable } from '@nestjs/common';
93
- import { Cron, Interval } from 'nestjs-temporal-core';
124
+ import { Scheduled, Cron, Interval, CRON_EXPRESSIONS } from 'nestjs-temporal-core';
94
125
 
95
126
  @Injectable()
96
127
  export class ScheduledService {
97
- // Automatic scheduling - runs at 9 AM daily
98
- @Cron('0 9 * * *', {
99
- scheduleId: 'daily-user-report',
100
- description: 'Generate daily user report'
128
+
129
+ @Scheduled({
130
+ scheduleId: 'daily-report',
131
+ cron: CRON_EXPRESSIONS.DAILY_8AM,
132
+ description: 'Generate daily sales report',
133
+ taskQueue: 'reports'
101
134
  })
102
135
  async generateDailyReport(): Promise<void> {
103
- console.log('Generating daily user report...');
104
- // Report generation logic
136
+ console.log('Generating daily report...');
137
+ // Your report generation logic
105
138
  }
106
139
 
107
- // Interval-based scheduling - runs every hour
108
- @Interval('1h', {
109
- scheduleId: 'hourly-cleanup',
110
- description: 'Hourly cleanup task'
140
+ @Cron(CRON_EXPRESSIONS.WEEKLY_MONDAY_9AM, {
141
+ scheduleId: 'weekly-cleanup'
111
142
  })
112
- async cleanupTask(): Promise<void> {
113
- console.log('Running cleanup task...');
114
- // Cleanup logic
143
+ async performWeeklyCleanup(): Promise<void> {
144
+ console.log('Performing weekly cleanup...');
145
+ // Your cleanup logic
146
+ }
147
+
148
+ @Interval('5m', {
149
+ scheduleId: 'health-check'
150
+ })
151
+ async performHealthCheck(): Promise<void> {
152
+ console.log('Performing health check...');
153
+ // Your health check logic
115
154
  }
116
155
  }
117
156
  ```
118
157
 
119
- ### 4. Use in Services
158
+ ### 5. Use in Services
120
159
 
121
160
  ```typescript
122
- // services/user.service.ts
161
+ // services/order.service.ts
123
162
  import { Injectable } from '@nestjs/common';
124
163
  import { TemporalService } from 'nestjs-temporal-core';
125
164
 
126
165
  @Injectable()
127
- export class UserService {
166
+ export class OrderService {
128
167
  constructor(private readonly temporal: TemporalService) {}
129
168
 
130
- async processUser(email: string, name: string): Promise<string> {
131
- // Start workflow directly with client
169
+ async createOrder(orderData: any) {
132
170
  const { workflowId } = await this.temporal.startWorkflow(
133
- 'processUser',
134
- [email, name],
135
- {
136
- taskQueue: 'user-processing',
137
- workflowId: `user-${email}-${Date.now()}`
171
+ 'processOrder',
172
+ [orderData],
173
+ {
174
+ taskQueue: 'orders',
175
+ workflowId: `order-${orderData.id}`,
176
+ searchAttributes: {
177
+ 'customer-id': orderData.customerId
178
+ }
138
179
  }
139
180
  );
140
181
 
141
- return workflowId;
182
+ return { workflowId };
142
183
  }
143
184
 
144
- async getUserStatus(workflowId: string): Promise<string> {
145
- return await this.temporal.queryWorkflow(workflowId, 'getStatus');
185
+ async cancelOrder(orderId: string) {
186
+ await this.temporal.signalWorkflow(`order-${orderId}`, 'cancel');
187
+ return { cancelled: true };
146
188
  }
147
189
 
148
- async updateUserStatus(workflowId: string, status: string): Promise<void> {
149
- await this.temporal.signalWorkflow(workflowId, 'updateStatus', [status]);
150
- }
151
-
152
- // Schedule management
153
- async pauseDailyReport(): Promise<void> {
154
- await this.temporal.pauseSchedule('daily-user-report', 'Maintenance mode');
155
- }
156
-
157
- async resumeDailyReport(): Promise<void> {
158
- await this.temporal.resumeSchedule('daily-user-report');
190
+ async getOrderStatus(orderId: string) {
191
+ const status = await this.temporal.queryWorkflow(`order-${orderId}`, 'getStatus');
192
+ return status;
159
193
  }
160
194
  }
161
195
  ```
162
196
 
163
- ## ⚙️ Configuration Options
197
+ ## 🏗️ Integration Patterns
164
198
 
165
- ### Basic Configuration
199
+ ### Client-Only Integration
166
200
 
167
- ```typescript
168
- TemporalModule.register({
169
- connection: {
170
- address: 'localhost:7233',
171
- namespace: 'default',
172
- },
173
- taskQueue: 'my-app',
174
- worker: {
175
- workflowsPath: './dist/workflows',
176
- activityClasses: [EmailActivities, PaymentActivities],
177
- },
178
- });
179
- ```
180
-
181
- ### Client-Only Mode
201
+ For applications that only start workflows (e.g., web APIs):
182
202
 
183
203
  ```typescript
184
- TemporalModule.forClient({
185
- connection: {
186
- address: 'temporal.company.com:7233',
187
- namespace: 'production',
188
- tls: true,
189
- },
190
- });
204
+ import { TemporalClientModule } from 'nestjs-temporal-core';
205
+
206
+ @Module({
207
+ imports: [
208
+ TemporalClientModule.forRoot({
209
+ connection: {
210
+ address: 'localhost:7233',
211
+ namespace: 'production'
212
+ }
213
+ })
214
+ ],
215
+ providers: [ApiService],
216
+ })
217
+ export class ClientOnlyModule {}
191
218
  ```
192
219
 
193
- ### Worker-Only Mode
220
+ ### Worker-Only Integration
221
+
222
+ For dedicated worker processes:
194
223
 
195
224
  ```typescript
196
- TemporalModule.forWorker({
197
- connection: {
198
- address: 'localhost:7233',
199
- namespace: 'development',
200
- },
201
- taskQueue: 'worker-queue',
202
- workflowsPath: './dist/workflows',
203
- activityClasses: [ProcessingActivities],
204
- });
225
+ import { TemporalWorkerModule, WORKER_PRESETS } from 'nestjs-temporal-core';
226
+
227
+ @Module({
228
+ imports: [
229
+ TemporalWorkerModule.forRoot({
230
+ connection: {
231
+ address: 'localhost:7233',
232
+ namespace: 'production'
233
+ },
234
+ taskQueue: 'worker-queue',
235
+ workflowsPath: './dist/workflows',
236
+ activityClasses: [ProcessingActivities],
237
+ workerOptions: WORKER_PRESETS.PRODUCTION_HIGH_THROUGHPUT
238
+ })
239
+ ],
240
+ providers: [ProcessingActivities],
241
+ })
242
+ export class WorkerOnlyModule {}
205
243
  ```
206
244
 
207
- ### Async Configuration
245
+ ### Modular Integration
246
+
247
+ Using individual modules for specific needs:
208
248
 
209
249
  ```typescript
210
- TemporalModule.registerAsync({
211
- imports: [ConfigModule],
212
- useFactory: (config: ConfigService) => ({
213
- connection: {
214
- address: config.get('TEMPORAL_ADDRESS'),
215
- namespace: config.get('TEMPORAL_NAMESPACE'),
216
- },
217
- taskQueue: config.get('TEMPORAL_TASK_QUEUE'),
218
- worker: {
219
- workflowsPath: './dist/workflows',
220
- activityClasses: [EmailActivities],
221
- },
222
- }),
223
- inject: [ConfigService],
224
- });
250
+ import {
251
+ TemporalClientModule,
252
+ TemporalActivityModule,
253
+ TemporalSchedulesModule
254
+ } from 'nestjs-temporal-core';
255
+
256
+ @Module({
257
+ imports: [
258
+ // Client for workflow operations
259
+ TemporalClientModule.forRoot({
260
+ connection: { address: 'localhost:7233' }
261
+ }),
262
+
263
+ // Activities management
264
+ TemporalActivityModule.forRoot({
265
+ activityClasses: [EmailActivities, PaymentActivities]
266
+ }),
267
+
268
+ // Schedule management
269
+ TemporalSchedulesModule.forRoot({
270
+ autoStart: true,
271
+ defaultTimezone: 'UTC'
272
+ }),
273
+ ],
274
+ providers: [EmailActivities, PaymentActivities, ScheduledService],
275
+ })
276
+ export class ModularIntegrationModule {}
225
277
  ```
226
278
 
227
- ## 📋 Core Concepts
279
+ ## ⚙️ Configuration
228
280
 
229
- ### Auto-Discovery
230
- The module automatically discovers and registers:
231
- - **Activity Classes** marked with `@Activity`
232
- - **Scheduled Workflows** marked with `@Cron` or `@Interval`
233
- - **Signals and Queries** within classes
281
+ ### Async Configuration
234
282
 
235
- ### Scheduling Made Simple
236
283
  ```typescript
237
- // Just add the decorator - schedule is created automatically!
238
- @Cron('0 8 * * *', { scheduleId: 'daily-report' })
239
- async generateReport(): Promise<void> {
240
- // This will run every day at 8 AM
241
- }
284
+ import { ConfigModule, ConfigService } from '@nestjs/config';
285
+
286
+ @Module({
287
+ imports: [
288
+ ConfigModule.forRoot(),
289
+ TemporalModule.registerAsync({
290
+ imports: [ConfigModule],
291
+ useFactory: async (config: ConfigService) => ({
292
+ connection: {
293
+ address: config.get('TEMPORAL_ADDRESS'),
294
+ namespace: config.get('TEMPORAL_NAMESPACE'),
295
+ tls: config.get('TEMPORAL_TLS_ENABLED') === 'true',
296
+ apiKey: config.get('TEMPORAL_API_KEY'),
297
+ },
298
+ taskQueue: config.get('TEMPORAL_TASK_QUEUE'),
299
+ worker: {
300
+ workflowsPath: config.get('WORKFLOWS_PATH'),
301
+ activityClasses: [EmailActivities, PaymentActivities],
302
+ autoStart: config.get('WORKER_AUTO_START') !== 'false',
303
+ }
304
+ }),
305
+ inject: [ConfigService],
306
+ })
307
+ ],
308
+ })
309
+ export class AppModule {}
242
310
  ```
243
311
 
244
- ## 🔧 Common Use Cases
312
+ ### Environment-Specific Configurations
245
313
 
246
- ### Scheduled Reports
247
314
  ```typescript
248
- @Injectable()
249
- export class ReportService {
250
- @Cron('0 0 * * 0', { scheduleId: 'weekly-sales-report' })
251
- async generateWeeklySalesReport(): Promise<void> {
252
- // Automatically runs every Sunday at midnight
253
- console.log('Generating weekly sales report...');
315
+ // Development
316
+ const developmentConfig = {
317
+ connection: {
318
+ address: 'localhost:7233',
319
+ namespace: 'development'
320
+ },
321
+ taskQueue: 'dev-queue',
322
+ worker: {
323
+ workflowsPath: './dist/workflows',
324
+ workerOptions: WORKER_PRESETS.DEVELOPMENT
254
325
  }
255
- }
326
+ };
327
+
328
+ // Production
329
+ const productionConfig = {
330
+ connection: {
331
+ address: process.env.TEMPORAL_ADDRESS!,
332
+ namespace: process.env.TEMPORAL_NAMESPACE!,
333
+ tls: true,
334
+ apiKey: process.env.TEMPORAL_API_KEY
335
+ },
336
+ taskQueue: process.env.TEMPORAL_TASK_QUEUE!,
337
+ worker: {
338
+ workflowBundle: require('../workflows/bundle'), // Pre-bundled
339
+ workerOptions: WORKER_PRESETS.PRODUCTION_BALANCED
340
+ }
341
+ };
256
342
  ```
257
343
 
258
- ### Data Processing
344
+ ## 📊 Health Monitoring
345
+
346
+ Built-in health monitoring for production environments:
347
+
259
348
  ```typescript
260
- @Injectable()
261
- @Activity()
262
- export class DataProcessingActivities {
263
- @ActivityMethod()
264
- async processFile(filePath: string): Promise<string> {
265
- console.log(`Processing file: ${filePath}`);
266
- // File processing logic
267
- return 'processed';
349
+ @Controller('health')
350
+ export class HealthController {
351
+ constructor(private readonly temporal: TemporalService) {}
352
+
353
+ @Get('temporal')
354
+ async getTemporalHealth() {
355
+ const health = await this.temporal.getOverallHealth();
356
+ return {
357
+ status: health.status,
358
+ components: health.components,
359
+ timestamp: new Date().toISOString()
360
+ };
268
361
  }
269
362
 
270
- @ActivityMethod()
271
- async sendNotification(message: string): Promise<void> {
272
- console.log(`Sending notification: ${message}`);
273
- // Notification logic
363
+ @Get('temporal/detailed')
364
+ async getDetailedStatus() {
365
+ const systemStatus = await this.temporal.getSystemStatus();
366
+ const stats = this.temporal.getDiscoveryStats();
367
+
368
+ return {
369
+ system: systemStatus,
370
+ discovery: stats,
371
+ schedules: this.temporal.getScheduleStats()
372
+ };
274
373
  }
275
374
  }
276
375
  ```
277
376
 
278
- ### Monitoring Tasks
377
+ ## 🔧 Advanced Features
378
+
379
+ ### Activity Options
380
+
279
381
  ```typescript
382
+ @Activity()
280
383
  @Injectable()
281
- export class MonitoringService {
282
- @Interval('5m', {
283
- scheduleId: 'health-check',
284
- description: 'Health check every 5 minutes'
384
+ export class PaymentActivities {
385
+
386
+ @ActivityMethod({
387
+ name: 'processPayment',
388
+ timeout: '2m',
389
+ maxRetries: 5,
390
+ retryPolicy: {
391
+ maximumAttempts: 5,
392
+ initialInterval: '1s',
393
+ maximumInterval: '60s',
394
+ backoffCoefficient: 2.0
395
+ }
285
396
  })
286
- async healthCheck(): Promise<void> {
287
- console.log('Running health check...');
288
- // Health check logic
397
+ async processPayment(orderId: string, amount: number) {
398
+ // Complex payment processing with retries
289
399
  }
290
400
  }
291
401
  ```
292
402
 
293
- ## 📊 Monitoring & Health Checks
403
+ ### Schedule Management
294
404
 
295
405
  ```typescript
296
406
  @Injectable()
297
- export class MonitoringService {
407
+ export class ScheduleManagementService {
298
408
  constructor(private readonly temporal: TemporalService) {}
299
409
 
300
- async getSystemHealth() {
301
- // Comprehensive health status
302
- const health = await this.temporal.getOverallHealth();
303
- return {
304
- status: health.status, // 'healthy' | 'degraded' | 'unhealthy'
305
- components: health.components,
306
- };
410
+ async pauseSchedule(scheduleId: string) {
411
+ await this.temporal.pauseSchedule(scheduleId, 'Maintenance mode');
307
412
  }
308
413
 
309
- async getDiscoveryInfo() {
310
- // What was discovered
311
- const schedules = this.temporal.getManagedSchedules();
312
- const stats = this.temporal.getDiscoveryStats();
313
-
314
- return { schedules, stats };
414
+ async resumeSchedule(scheduleId: string) {
415
+ await this.temporal.resumeSchedule(scheduleId);
315
416
  }
316
417
 
317
- async manageSchedules() {
318
- // Schedule management
319
- await this.temporal.pauseSchedule('daily-report', 'Maintenance');
320
- await this.temporal.resumeSchedule('daily-report');
321
- await this.temporal.triggerSchedule('daily-report'); // Run now
418
+ async triggerScheduleNow(scheduleId: string) {
419
+ await this.temporal.triggerSchedule(scheduleId);
420
+ }
421
+
422
+ async getScheduleInfo(scheduleId: string) {
423
+ return this.temporal.getScheduleInfo(scheduleId);
322
424
  }
323
425
  }
324
426
  ```
325
427
 
326
- ## 🌟 Why This Package?
428
+ ### Workflow Signals and Queries
429
+
430
+ ```typescript
431
+ // In your workflow
432
+ import { defineSignal, defineQuery, setHandler } from '@temporalio/workflow';
433
+
434
+ export const cancelSignal = defineSignal('cancel');
435
+ export const getStatusQuery = defineQuery<string>('getStatus');
436
+
437
+ export async function orderWorkflow(orderData: any) {
438
+ let status = 'processing';
439
+ let cancelled = false;
440
+
441
+ // Handle cancel signal
442
+ setHandler(cancelSignal, () => {
443
+ cancelled = true;
444
+ status = 'cancelled';
445
+ });
446
+
447
+ // Handle status query
448
+ setHandler(getStatusQuery, () => status);
449
+
450
+ // Workflow logic with cancellation support
451
+ if (cancelled) return;
452
+
453
+ // Process order...
454
+ status = 'completed';
455
+ }
456
+ ```
457
+
458
+ ## 🌐 Temporal Cloud Integration
459
+
460
+ For Temporal Cloud deployments:
461
+
462
+ ```typescript
463
+ TemporalModule.register({
464
+ connection: {
465
+ address: 'your-namespace.account.tmprl.cloud:7233',
466
+ namespace: 'your-namespace.account',
467
+ tls: true,
468
+ apiKey: process.env.TEMPORAL_API_KEY,
469
+ metadata: {
470
+ 'temporal-namespace': 'your-namespace.account'
471
+ }
472
+ },
473
+ taskQueue: 'production-queue',
474
+ worker: {
475
+ workflowBundle: require('../workflows/bundle'),
476
+ workerOptions: WORKER_PRESETS.PRODUCTION_BALANCED
477
+ }
478
+ })
479
+ ```
480
+
481
+ ## 📋 Best Practices
482
+
483
+ ### 1. **Activity Design**
484
+ - Keep activities idempotent
485
+ - Use proper timeouts and retry policies
486
+ - Handle errors gracefully
487
+ - Use dependency injection for testability
488
+
489
+ ### 2. **Workflow Organization**
490
+ - Separate workflow files from activities
491
+ - Use TypeScript for type safety
492
+ - Keep workflows deterministic
493
+ - Bundle workflows for production
494
+
495
+ ### 3. **Configuration Management**
496
+ - Use environment variables for connection settings
497
+ - Separate configs for different environments
498
+ - Use async configuration for dynamic settings
499
+ - Validate configuration at startup
500
+
501
+ ### 4. **Monitoring & Observability**
502
+ - Implement health checks
503
+ - Monitor worker status
504
+ - Track schedule execution
505
+ - Use structured logging
506
+
507
+ ### 5. **Production Deployment**
508
+ - Use pre-bundled workflows
509
+ - Configure appropriate worker limits
510
+ - Enable TLS for security
511
+ - Implement graceful shutdowns
512
+
513
+ ## 📚 API Reference
327
514
 
328
- - **🎯 NestJS First** - Built specifically for NestJS with familiar patterns
329
- - **🔄 Auto-Discovery** - No manual registration, just use decorators
330
- - **📅 Built-in Scheduling** - Cron jobs that integrate with workflows
331
- - **🔧 Production Ready** - Health checks, monitoring, graceful shutdowns
332
- - **📚 Easy to Learn** - Familiar NestJS service patterns
333
- - **🚀 Scalable** - Client-only, worker-only, or unified deployments
515
+ ### Decorators
516
+
517
+ - `@Activity()` - Mark a class as containing activities
518
+ - `@ActivityMethod(options?)` - Define an activity method
519
+ - `@Scheduled(options)` - Schedule a workflow with full options
520
+ - `@Cron(expression, options?)` - Schedule using cron expression
521
+ - `@Interval(interval, options?)` - Schedule using interval
522
+
523
+ ### Services
524
+
525
+ - `TemporalService` - Main service for all operations
526
+ - `TemporalClientService` - Client-only operations
527
+ - `TemporalActivityService` - Activity management
528
+ - `TemporalSchedulesService` - Schedule management
529
+ - `TemporalWorkerManagerService` - Worker lifecycle
530
+
531
+ ### Constants
532
+
533
+ - `CRON_EXPRESSIONS` - Common cron patterns
534
+ - `INTERVAL_EXPRESSIONS` - Common interval patterns
535
+ - `WORKER_PRESETS` - Environment-specific worker configs
536
+ - `RETRY_POLICIES` - Common retry patterns
537
+ - `TIMEOUTS` - Common timeout values
334
538
 
335
539
  ## 🤝 Contributing
336
540
 
337
- Contributions welcome! Please read our [Contributing Guide](./CONTRIBUTING.md) for details.
541
+ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
338
542
 
339
543
  ## 📄 License
340
544
 
341
- MIT License - see [LICENSE](LICENSE) file for details.
545
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
546
+
547
+ ## 🙏 Acknowledgments
548
+
549
+ - [Temporal.io](https://temporal.io/) for the amazing workflow engine
550
+ - [NestJS](https://nestjs.com/) for the fantastic framework
551
+ - The TypeScript community for excellent tooling
552
+
553
+ ---
554
+
555
+ Built with ❤️ for the NestJS and Temporal communities