nestjs-temporal-core 3.0.0 → 3.0.3
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 +271 -150
- package/dist/client/temporal-client.module.d.ts +9 -3
- package/dist/client/temporal-client.module.js +43 -13
- package/dist/client/temporal-client.module.js.map +1 -1
- package/dist/client/temporal-client.service.d.ts +10 -3
- package/dist/client/temporal-client.service.js +15 -6
- package/dist/client/temporal-client.service.js.map +1 -1
- package/dist/client/temporal-schedule.service.d.ts +21 -5
- package/dist/client/temporal-schedule.service.js +42 -10
- package/dist/client/temporal-schedule.service.js.map +1 -1
- package/dist/constants.d.ts +47 -70
- package/dist/constants.js +51 -74
- package/dist/constants.js.map +1 -1
- package/dist/decorators/activity.decorator.d.ts +3 -0
- package/dist/decorators/activity.decorator.js +39 -0
- package/dist/decorators/activity.decorator.js.map +1 -0
- package/dist/decorators/index.d.ts +4 -5
- package/dist/decorators/index.js +17 -19
- package/dist/decorators/index.js.map +1 -1
- package/dist/decorators/parameter.decorator.d.ts +7 -0
- package/dist/decorators/parameter.decorator.js +69 -0
- package/dist/decorators/parameter.decorator.js.map +1 -0
- package/dist/decorators/{scheduling.decorators.d.ts → scheduling.decorator.d.ts} +1 -1
- package/dist/decorators/{scheduling.decorators.js → scheduling.decorator.js} +24 -1
- package/dist/decorators/scheduling.decorator.js.map +1 -0
- package/dist/decorators/workflow.decorator.d.ts +3 -0
- package/dist/decorators/workflow.decorator.js +58 -0
- package/dist/decorators/workflow.decorator.js.map +1 -0
- package/dist/discovery/index.d.ts +2 -2
- package/dist/discovery/index.js +2 -2
- package/dist/discovery/index.js.map +1 -1
- package/dist/discovery/{workflow-discovery.service.d.ts → temporal-discovery.service.d.ts} +21 -18
- package/dist/discovery/temporal-discovery.service.js +190 -0
- package/dist/discovery/temporal-discovery.service.js.map +1 -0
- package/dist/discovery/{schedule-manager.service.d.ts → temporal-schedule-manager.service.d.ts} +20 -15
- package/dist/discovery/{schedule-manager.service.js → temporal-schedule-manager.service.js} +96 -76
- package/dist/discovery/temporal-schedule-manager.service.js.map +1 -0
- package/dist/index.d.ts +6 -11
- package/dist/index.js +16 -16
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +193 -0
- package/dist/interfaces.js +9 -0
- package/dist/interfaces.js.map +1 -0
- package/dist/temporal.module.d.ts +2 -1
- package/dist/temporal.module.js +84 -49
- package/dist/temporal.module.js.map +1 -1
- package/dist/temporal.service.d.ts +34 -26
- package/dist/temporal.service.js +76 -47
- package/dist/temporal.service.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/worker/index.d.ts +1 -1
- package/dist/worker/index.js +1 -1
- package/dist/worker/index.js.map +1 -1
- package/dist/worker/temporal-metadata.accessor.d.ts +13 -5
- package/dist/worker/temporal-metadata.accessor.js +38 -26
- package/dist/worker/temporal-metadata.accessor.js.map +1 -1
- package/dist/worker/{worker-manager.service.d.ts → temporal-worker-manager.service.d.ts} +13 -4
- package/dist/worker/{worker-manager.service.js → temporal-worker-manager.service.js} +58 -22
- package/dist/worker/temporal-worker-manager.service.js.map +1 -0
- package/dist/worker/temporal-worker.module.d.ts +14 -4
- package/dist/worker/temporal-worker.module.js +81 -15
- package/dist/worker/temporal-worker.module.js.map +1 -1
- package/package.json +13 -7
- package/dist/decorators/communication.decorators.d.ts +0 -5
- package/dist/decorators/communication.decorators.js +0 -66
- package/dist/decorators/communication.decorators.js.map +0 -1
- package/dist/decorators/core.decorators.d.ts +0 -6
- package/dist/decorators/core.decorators.js +0 -87
- package/dist/decorators/core.decorators.js.map +0 -1
- package/dist/decorators/parameter.decorators.d.ts +0 -2
- package/dist/decorators/parameter.decorators.js +0 -29
- package/dist/decorators/parameter.decorators.js.map +0 -1
- package/dist/decorators/scheduling.decorators.js.map +0 -1
- package/dist/decorators/workflow-starter.decorator.d.ts +0 -2
- package/dist/decorators/workflow-starter.decorator.js +0 -14
- package/dist/decorators/workflow-starter.decorator.js.map +0 -1
- package/dist/discovery/schedule-manager.service.js.map +0 -1
- package/dist/discovery/workflow-discovery.service.js +0 -216
- package/dist/discovery/workflow-discovery.service.js.map +0 -1
- package/dist/interfaces/activity.interface.d.ts +0 -8
- package/dist/interfaces/activity.interface.js +0 -3
- package/dist/interfaces/activity.interface.js.map +0 -1
- package/dist/interfaces/core.interface.d.ts +0 -112
- package/dist/interfaces/core.interface.js +0 -3
- package/dist/interfaces/core.interface.js.map +0 -1
- package/dist/interfaces/discovery.interface.d.ts +0 -61
- package/dist/interfaces/discovery.interface.js +0 -3
- package/dist/interfaces/discovery.interface.js.map +0 -1
- package/dist/interfaces/index.d.ts +0 -9
- package/dist/interfaces/index.js +0 -29
- package/dist/interfaces/index.js.map +0 -1
- package/dist/interfaces/scheduling.interface.d.ts +0 -17
- package/dist/interfaces/scheduling.interface.js +0 -3
- package/dist/interfaces/scheduling.interface.js.map +0 -1
- package/dist/interfaces/worker.interface.d.ts +0 -23
- package/dist/interfaces/worker.interface.js +0 -3
- package/dist/interfaces/worker.interface.js.map +0 -1
- package/dist/interfaces/workflow.interface.d.ts +0 -55
- package/dist/interfaces/workflow.interface.js +0 -3
- package/dist/interfaces/workflow.interface.js.map +0 -1
- package/dist/worker/worker-manager.service.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,216 +1,337 @@
|
|
|
1
1
|
# NestJS Temporal Core
|
|
2
2
|
|
|
3
|
-
A comprehensive NestJS integration for [Temporal.io](https://temporal.io/) that provides seamless
|
|
3
|
+
A comprehensive NestJS integration for [Temporal.io](https://temporal.io/) that provides seamless workflow orchestration with auto-discovery, declarative scheduling, and production-ready features.
|
|
4
4
|
|
|
5
5
|
[](https://badge.fury.io/js/nestjs-temporal-core)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
[](http://www.typescriptlang.org/)
|
|
8
8
|
|
|
9
|
-
## 📚 Documentation
|
|
10
|
-
|
|
11
|
-
### Quick Links
|
|
12
|
-
- **[🚀 Getting Started](./docs/getting-started.md)** - Installation, basic setup, and first workflow
|
|
13
|
-
- **[⚙️ Configuration](./docs/configuration.md)** - Complete configuration reference and examples
|
|
14
|
-
- **[📖 API Reference](./docs/api-reference.md)** - Detailed API documentation for all services and decorators
|
|
15
|
-
- **[🍳 Examples & Recipes](./docs/examples.md)** - Practical examples and common patterns
|
|
16
|
-
- **[🏗️ Best Practices](./docs/best-practices.md)** - Production guidelines and optimization tips
|
|
17
|
-
- **[🔄 Migration Guide](./docs/migration.md)** - Upgrading from previous versions
|
|
18
|
-
- **[🔧 Troubleshooting](./docs/troubleshooting.md)** - Common issues and solutions
|
|
19
|
-
|
|
20
|
-
### Advanced Topics
|
|
21
|
-
- **[🏢 Enterprise Features](./docs/enterprise.md)** - Advanced features for production deployments
|
|
22
|
-
- **[📊 Monitoring & Health](./docs/monitoring.md)** - Health checks, metrics, and observability
|
|
23
|
-
- **[🔐 Security](./docs/security.md)** - TLS, authentication, and security best practices
|
|
24
|
-
- **[🧪 Testing](./docs/testing.md)** - Testing workflows and activities
|
|
25
|
-
- **[🐳 Deployment](./docs/deployment.md)** - Docker, Kubernetes, and cloud deployment guides
|
|
26
|
-
|
|
27
9
|
## Overview
|
|
28
10
|
|
|
29
|
-
NestJS Temporal Core
|
|
11
|
+
NestJS Temporal Core brings Temporal's durable execution to NestJS with familiar decorator patterns and automatic discovery. Build reliable distributed systems with activities and scheduled tasks using native NestJS conventions.
|
|
30
12
|
|
|
31
|
-
##
|
|
13
|
+
## 🚀 Key Features
|
|
32
14
|
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
- 📊 **Worker Management** - Advanced worker lifecycle control, monitoring, and health checks
|
|
42
|
-
- 🏭 **Production Ready** - Environment-aware configuration, health monitoring, and graceful degradation
|
|
15
|
+
- **🎯 NestJS-Native** - Familiar patterns: `@Activity`, `@Cron`, `@Interval`
|
|
16
|
+
- **🔍 Auto-Discovery** - Automatically finds and registers activities and schedules
|
|
17
|
+
- **📅 Declarative Scheduling** - Built-in cron and interval scheduling that just works
|
|
18
|
+
- **🔄 Unified Service** - Single `TemporalService` for all operations
|
|
19
|
+
- **⚙️ Flexible Setup** - Client-only, worker-only, or unified deployments
|
|
20
|
+
- **🏥 Health Monitoring** - Comprehensive status monitoring and health checks
|
|
21
|
+
- **🔧 Production Ready** - TLS, connection management, graceful shutdowns
|
|
22
|
+
- **📊 Complete Integration** - Full-featured module architecture
|
|
43
23
|
|
|
44
|
-
##
|
|
45
|
-
|
|
46
|
-
### Installation
|
|
24
|
+
## 📦 Installation
|
|
47
25
|
|
|
48
26
|
```bash
|
|
49
27
|
npm install nestjs-temporal-core @temporalio/client @temporalio/worker @temporalio/workflow
|
|
50
28
|
```
|
|
51
29
|
|
|
52
|
-
|
|
30
|
+
## 🚀 Quick Start
|
|
31
|
+
|
|
32
|
+
### 1. Module Setup
|
|
53
33
|
|
|
54
34
|
```typescript
|
|
55
35
|
// app.module.ts
|
|
56
36
|
import { Module } from '@nestjs/common';
|
|
57
37
|
import { TemporalModule } from 'nestjs-temporal-core';
|
|
38
|
+
import { EmailActivities } from './activities/email.activities';
|
|
58
39
|
|
|
59
40
|
@Module({
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
],
|
|
41
|
+
imports: [
|
|
42
|
+
TemporalModule.register({
|
|
43
|
+
connection: {
|
|
44
|
+
address: 'localhost:7233',
|
|
45
|
+
namespace: 'default',
|
|
46
|
+
},
|
|
47
|
+
taskQueue: 'my-app',
|
|
48
|
+
worker: {
|
|
49
|
+
workflowsPath: './dist/workflows',
|
|
50
|
+
activityClasses: [EmailActivities], // Auto-discovered
|
|
51
|
+
},
|
|
52
|
+
}),
|
|
53
|
+
],
|
|
54
|
+
providers: [EmailActivities],
|
|
75
55
|
})
|
|
76
56
|
export class AppModule {}
|
|
77
57
|
```
|
|
78
58
|
|
|
79
|
-
### Create
|
|
59
|
+
### 2. Create Activities
|
|
80
60
|
|
|
81
61
|
```typescript
|
|
82
|
-
|
|
62
|
+
// activities/email.activities.ts
|
|
63
|
+
import { Injectable } from '@nestjs/common';
|
|
64
|
+
import { Activity, ActivityMethod } from 'nestjs-temporal-core';
|
|
83
65
|
|
|
84
|
-
@
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
})
|
|
100
|
-
@WorkflowMethod()
|
|
101
|
-
async generateDailyReport(): Promise<void> {
|
|
102
|
-
console.log('Generating daily order report...');
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
@Signal('addItem')
|
|
106
|
-
async addItemToOrder(item: any): Promise<void> {
|
|
107
|
-
console.log('Item added to order:', item);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
@Query('getStatus')
|
|
111
|
-
getOrderStatus(): string {
|
|
112
|
-
return this.status;
|
|
113
|
-
}
|
|
66
|
+
@Injectable()
|
|
67
|
+
@Activity()
|
|
68
|
+
export class EmailActivities {
|
|
69
|
+
@ActivityMethod()
|
|
70
|
+
async sendWelcomeEmail(email: string, name: string): Promise<boolean> {
|
|
71
|
+
console.log(`Sending welcome email to ${email}`);
|
|
72
|
+
// Your email logic here
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@ActivityMethod()
|
|
77
|
+
async sendNotification(email: string, message: string): Promise<void> {
|
|
78
|
+
console.log(`Notification to ${email}: ${message}`);
|
|
79
|
+
// Your notification logic here
|
|
80
|
+
}
|
|
114
81
|
}
|
|
115
82
|
```
|
|
116
83
|
|
|
117
|
-
###
|
|
84
|
+
### 3. Create Scheduled Workflows
|
|
118
85
|
|
|
119
86
|
```typescript
|
|
87
|
+
// services/scheduled.service.ts
|
|
120
88
|
import { Injectable } from '@nestjs/common';
|
|
121
|
-
import {
|
|
89
|
+
import { Cron, Interval } from 'nestjs-temporal-core';
|
|
122
90
|
|
|
123
91
|
@Injectable()
|
|
124
|
-
export class
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
92
|
+
export class ScheduledService {
|
|
93
|
+
// Automatic scheduling - runs at 9 AM daily
|
|
94
|
+
@Cron('0 9 * * *', {
|
|
95
|
+
scheduleId: 'daily-user-report',
|
|
96
|
+
description: 'Generate daily user report'
|
|
97
|
+
})
|
|
98
|
+
async generateDailyReport(): Promise<void> {
|
|
99
|
+
console.log('Generating daily user report...');
|
|
100
|
+
// Report generation logic
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Interval-based scheduling - runs every hour
|
|
104
|
+
@Interval('1h', {
|
|
105
|
+
scheduleId: 'hourly-cleanup',
|
|
106
|
+
description: 'Hourly cleanup task'
|
|
107
|
+
})
|
|
108
|
+
async cleanupTask(): Promise<void> {
|
|
109
|
+
console.log('Running cleanup task...');
|
|
110
|
+
// Cleanup logic
|
|
111
|
+
}
|
|
136
112
|
}
|
|
137
113
|
```
|
|
138
114
|
|
|
139
|
-
|
|
115
|
+
### 4. Use in Services
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// services/user.service.ts
|
|
119
|
+
import { Injectable } from '@nestjs/common';
|
|
120
|
+
import { TemporalService } from 'nestjs-temporal-core';
|
|
140
121
|
|
|
141
|
-
|
|
142
|
-
|
|
122
|
+
@Injectable()
|
|
123
|
+
export class UserService {
|
|
124
|
+
constructor(private readonly temporal: TemporalService) {}
|
|
125
|
+
|
|
126
|
+
async processUser(email: string, name: string): Promise<string> {
|
|
127
|
+
// Start workflow directly with client
|
|
128
|
+
const { workflowId } = await this.temporal.startWorkflow(
|
|
129
|
+
'processUser',
|
|
130
|
+
[email, name],
|
|
131
|
+
{
|
|
132
|
+
taskQueue: 'user-processing',
|
|
133
|
+
workflowId: `user-${email}-${Date.now()}`
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
return workflowId;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async getUserStatus(workflowId: string): Promise<string> {
|
|
141
|
+
return await this.temporal.queryWorkflow(workflowId, 'getStatus');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async updateUserStatus(workflowId: string, status: string): Promise<void> {
|
|
145
|
+
await this.temporal.signalWorkflow(workflowId, 'updateStatus', [status]);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Schedule management
|
|
149
|
+
async pauseDailyReport(): Promise<void> {
|
|
150
|
+
await this.temporal.pauseSchedule('daily-user-report', 'Maintenance mode');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async resumeDailyReport(): Promise<void> {
|
|
154
|
+
await this.temporal.resumeSchedule('daily-user-report');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
143
158
|
|
|
144
|
-
|
|
145
|
-
Reusable business logic components that can be called from workflows.
|
|
159
|
+
## ⚙️ Configuration Options
|
|
146
160
|
|
|
147
|
-
###
|
|
148
|
-
Built-in support for cron and interval-based workflow scheduling using decorators.
|
|
161
|
+
### Basic Configuration
|
|
149
162
|
|
|
150
|
-
|
|
151
|
-
|
|
163
|
+
```typescript
|
|
164
|
+
TemporalModule.register({
|
|
165
|
+
connection: {
|
|
166
|
+
address: 'localhost:7233',
|
|
167
|
+
namespace: 'default',
|
|
168
|
+
},
|
|
169
|
+
taskQueue: 'my-app',
|
|
170
|
+
worker: {
|
|
171
|
+
workflowsPath: './dist/workflows',
|
|
172
|
+
activityClasses: [EmailActivities, PaymentActivities],
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
```
|
|
152
176
|
|
|
153
|
-
|
|
177
|
+
### Client-Only Mode
|
|
154
178
|
|
|
179
|
+
```typescript
|
|
180
|
+
TemporalModule.forClient({
|
|
181
|
+
connection: {
|
|
182
|
+
address: 'temporal.company.com:7233',
|
|
183
|
+
namespace: 'production',
|
|
184
|
+
tls: true,
|
|
185
|
+
},
|
|
186
|
+
});
|
|
155
187
|
```
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
188
|
+
|
|
189
|
+
### Worker-Only Mode
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
TemporalModule.forWorker({
|
|
193
|
+
connection: {
|
|
194
|
+
address: 'localhost:7233',
|
|
195
|
+
namespace: 'development',
|
|
196
|
+
},
|
|
197
|
+
taskQueue: 'worker-queue',
|
|
198
|
+
workflowsPath: './dist/workflows',
|
|
199
|
+
activityClasses: [ProcessingActivities],
|
|
200
|
+
});
|
|
164
201
|
```
|
|
165
202
|
|
|
166
|
-
|
|
203
|
+
### Async Configuration
|
|
167
204
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
205
|
+
```typescript
|
|
206
|
+
TemporalModule.registerAsync({
|
|
207
|
+
imports: [ConfigModule],
|
|
208
|
+
useFactory: (config: ConfigService) => ({
|
|
209
|
+
connection: {
|
|
210
|
+
address: config.get('TEMPORAL_ADDRESS'),
|
|
211
|
+
namespace: config.get('TEMPORAL_NAMESPACE'),
|
|
212
|
+
},
|
|
213
|
+
taskQueue: config.get('TEMPORAL_TASK_QUEUE'),
|
|
214
|
+
worker: {
|
|
215
|
+
workflowsPath: './dist/workflows',
|
|
216
|
+
activityClasses: [EmailActivities],
|
|
217
|
+
},
|
|
218
|
+
}),
|
|
219
|
+
inject: [ConfigService],
|
|
220
|
+
});
|
|
221
|
+
```
|
|
175
222
|
|
|
176
|
-
##
|
|
223
|
+
## 📋 Core Concepts
|
|
177
224
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
-
|
|
181
|
-
-
|
|
182
|
-
-
|
|
183
|
-
- **🔧 Better Developer Experience** - Improved APIs and TypeScript support
|
|
225
|
+
### Auto-Discovery
|
|
226
|
+
The module automatically discovers and registers:
|
|
227
|
+
- **Activity Classes** marked with `@Activity`
|
|
228
|
+
- **Scheduled Workflows** marked with `@Cron` or `@Interval`
|
|
229
|
+
- **Signals and Queries** within classes
|
|
184
230
|
|
|
185
|
-
|
|
231
|
+
### Scheduling Made Simple
|
|
232
|
+
```typescript
|
|
233
|
+
// Just add the decorator - schedule is created automatically!
|
|
234
|
+
@Cron('0 8 * * *', { scheduleId: 'daily-report' })
|
|
235
|
+
async generateReport(): Promise<void> {
|
|
236
|
+
// This will run every day at 8 AM
|
|
237
|
+
}
|
|
238
|
+
```
|
|
186
239
|
|
|
187
|
-
|
|
188
|
-
| ---------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------ |
|
|
189
|
-
| `nestjs-temporal-core` | [](https://www.npmjs.com/package/nestjs-temporal-core) | Main integration package |
|
|
190
|
-
| `@temporalio/client` | [](https://www.npmjs.com/package/@temporalio/client) | Temporal client library |
|
|
191
|
-
| `@temporalio/worker` | [](https://www.npmjs.com/package/@temporalio/worker) | Temporal worker library |
|
|
192
|
-
| `@temporalio/workflow` | [](https://www.npmjs.com/package/@temporalio/workflow) | Workflow runtime library |
|
|
240
|
+
## 🔧 Common Use Cases
|
|
193
241
|
|
|
194
|
-
|
|
242
|
+
### Scheduled Reports
|
|
243
|
+
```typescript
|
|
244
|
+
@Injectable()
|
|
245
|
+
export class ReportService {
|
|
246
|
+
@Cron('0 0 * * 0', { scheduleId: 'weekly-sales-report' })
|
|
247
|
+
async generateWeeklySalesReport(): Promise<void> {
|
|
248
|
+
// Automatically runs every Sunday at midnight
|
|
249
|
+
console.log('Generating weekly sales report...');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
195
253
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
254
|
+
### Data Processing
|
|
255
|
+
```typescript
|
|
256
|
+
@Injectable()
|
|
257
|
+
@Activity()
|
|
258
|
+
export class DataProcessingActivities {
|
|
259
|
+
@ActivityMethod()
|
|
260
|
+
async processFile(filePath: string): Promise<string> {
|
|
261
|
+
console.log(`Processing file: ${filePath}`);
|
|
262
|
+
// File processing logic
|
|
263
|
+
return 'processed';
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
@ActivityMethod()
|
|
267
|
+
async sendNotification(message: string): Promise<void> {
|
|
268
|
+
console.log(`Sending notification: ${message}`);
|
|
269
|
+
// Notification logic
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
199
273
|
|
|
200
|
-
|
|
274
|
+
### Monitoring Tasks
|
|
275
|
+
```typescript
|
|
276
|
+
@Injectable()
|
|
277
|
+
export class MonitoringService {
|
|
278
|
+
@Interval('5m', {
|
|
279
|
+
scheduleId: 'health-check',
|
|
280
|
+
description: 'Health check every 5 minutes'
|
|
281
|
+
})
|
|
282
|
+
async healthCheck(): Promise<void> {
|
|
283
|
+
console.log('Running health check...');
|
|
284
|
+
// Health check logic
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## 📊 Monitoring & Health Checks
|
|
201
290
|
|
|
202
|
-
|
|
291
|
+
```typescript
|
|
292
|
+
@Injectable()
|
|
293
|
+
export class MonitoringService {
|
|
294
|
+
constructor(private readonly temporal: TemporalService) {}
|
|
295
|
+
|
|
296
|
+
async getSystemHealth() {
|
|
297
|
+
// Comprehensive health status
|
|
298
|
+
const health = await this.temporal.getOverallHealth();
|
|
299
|
+
return {
|
|
300
|
+
status: health.status, // 'healthy' | 'degraded' | 'unhealthy'
|
|
301
|
+
components: health.components,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async getDiscoveryInfo() {
|
|
306
|
+
// What was discovered
|
|
307
|
+
const schedules = this.temporal.getManagedSchedules();
|
|
308
|
+
const stats = this.temporal.getDiscoveryStats();
|
|
309
|
+
|
|
310
|
+
return { schedules, stats };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async manageSchedules() {
|
|
314
|
+
// Schedule management
|
|
315
|
+
await this.temporal.pauseSchedule('daily-report', 'Maintenance');
|
|
316
|
+
await this.temporal.resumeSchedule('daily-report');
|
|
317
|
+
await this.temporal.triggerSchedule('daily-report'); // Run now
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
```
|
|
203
321
|
|
|
204
|
-
##
|
|
322
|
+
## 🌟 Why This Package?
|
|
205
323
|
|
|
206
|
-
|
|
324
|
+
- **🎯 NestJS First** - Built specifically for NestJS with familiar patterns
|
|
325
|
+
- **🔄 Auto-Discovery** - No manual registration, just use decorators
|
|
326
|
+
- **📅 Built-in Scheduling** - Cron jobs that integrate with workflows
|
|
327
|
+
- **🔧 Production Ready** - Health checks, monitoring, graceful shutdowns
|
|
328
|
+
- **📚 Easy to Learn** - Familiar NestJS service patterns
|
|
329
|
+
- **🚀 Scalable** - Client-only, worker-only, or unified deployments
|
|
207
330
|
|
|
208
|
-
##
|
|
331
|
+
## 🤝 Contributing
|
|
209
332
|
|
|
210
|
-
|
|
211
|
-
- [NestJS](https://nestjs.com/) - For the incredible framework
|
|
212
|
-
- [TypeScript](https://www.typescriptlang.org/) - For making JavaScript enjoyable
|
|
333
|
+
Contributions welcome! Please read our [Contributing Guide](./CONTRIBUTING.md) for details.
|
|
213
334
|
|
|
214
|
-
|
|
335
|
+
## 📄 License
|
|
215
336
|
|
|
216
|
-
|
|
337
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { DynamicModule } from '@nestjs/common';
|
|
2
|
-
import {
|
|
2
|
+
import { TemporalAsyncOptions, TemporalOptions } from '../interfaces';
|
|
3
3
|
export declare class TemporalClientModule {
|
|
4
4
|
private static readonly logger;
|
|
5
|
-
static register(options:
|
|
6
|
-
static registerAsync(options:
|
|
5
|
+
static register(options: TemporalOptions): DynamicModule;
|
|
6
|
+
static registerAsync(options: TemporalAsyncOptions): DynamicModule;
|
|
7
|
+
static forClient(options: {
|
|
8
|
+
connection: TemporalOptions['connection'];
|
|
9
|
+
isGlobal?: boolean;
|
|
10
|
+
}): DynamicModule;
|
|
7
11
|
private static createClientProvider;
|
|
8
12
|
private static createAsyncClientProvider;
|
|
9
13
|
private static createAsyncProviders;
|
|
10
14
|
private static createClientInstance;
|
|
11
15
|
private static enhanceClientWithShutdown;
|
|
16
|
+
private static extractClientOptions;
|
|
17
|
+
private static validateOptions;
|
|
12
18
|
}
|
|
@@ -15,18 +15,19 @@ const temporal_client_service_1 = require("./temporal-client.service");
|
|
|
15
15
|
const temporal_schedule_service_1 = require("./temporal-schedule.service");
|
|
16
16
|
let TemporalClientModule = TemporalClientModule_1 = class TemporalClientModule {
|
|
17
17
|
static register(options) {
|
|
18
|
+
const clientOptions = this.extractClientOptions(options);
|
|
18
19
|
return {
|
|
19
20
|
module: TemporalClientModule_1,
|
|
20
21
|
providers: [
|
|
21
22
|
{
|
|
22
|
-
provide: constants_1.
|
|
23
|
-
useValue:
|
|
23
|
+
provide: constants_1.TEMPORAL_MODULE_OPTIONS,
|
|
24
|
+
useValue: clientOptions,
|
|
24
25
|
},
|
|
25
|
-
this.createClientProvider(
|
|
26
|
+
this.createClientProvider(clientOptions),
|
|
26
27
|
temporal_client_service_1.TemporalClientService,
|
|
27
28
|
temporal_schedule_service_1.TemporalScheduleService,
|
|
28
29
|
],
|
|
29
|
-
exports: [temporal_client_service_1.TemporalClientService, temporal_schedule_service_1.TemporalScheduleService],
|
|
30
|
+
exports: [temporal_client_service_1.TemporalClientService, temporal_schedule_service_1.TemporalScheduleService, constants_1.TEMPORAL_CLIENT],
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
33
|
static registerAsync(options) {
|
|
@@ -39,9 +40,15 @@ let TemporalClientModule = TemporalClientModule_1 = class TemporalClientModule {
|
|
|
39
40
|
temporal_client_service_1.TemporalClientService,
|
|
40
41
|
temporal_schedule_service_1.TemporalScheduleService,
|
|
41
42
|
],
|
|
42
|
-
exports: [temporal_client_service_1.TemporalClientService, temporal_schedule_service_1.TemporalScheduleService],
|
|
43
|
+
exports: [temporal_client_service_1.TemporalClientService, temporal_schedule_service_1.TemporalScheduleService, constants_1.TEMPORAL_CLIENT],
|
|
43
44
|
};
|
|
44
45
|
}
|
|
46
|
+
static forClient(options) {
|
|
47
|
+
return this.register({
|
|
48
|
+
connection: options.connection,
|
|
49
|
+
isGlobal: options.isGlobal,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
45
52
|
static createClientProvider(options) {
|
|
46
53
|
return {
|
|
47
54
|
provide: constants_1.TEMPORAL_CLIENT,
|
|
@@ -51,15 +58,18 @@ let TemporalClientModule = TemporalClientModule_1 = class TemporalClientModule {
|
|
|
51
58
|
static createAsyncClientProvider() {
|
|
52
59
|
return {
|
|
53
60
|
provide: constants_1.TEMPORAL_CLIENT,
|
|
54
|
-
useFactory: async (
|
|
55
|
-
|
|
61
|
+
useFactory: async (temporalOptions) => {
|
|
62
|
+
const clientOptions = this.extractClientOptions(temporalOptions);
|
|
63
|
+
return this.createClientInstance(clientOptions);
|
|
64
|
+
},
|
|
65
|
+
inject: [constants_1.TEMPORAL_MODULE_OPTIONS],
|
|
56
66
|
};
|
|
57
67
|
}
|
|
58
68
|
static createAsyncProviders(options) {
|
|
59
69
|
if (options.useFactory) {
|
|
60
70
|
return [
|
|
61
71
|
{
|
|
62
|
-
provide: constants_1.
|
|
72
|
+
provide: constants_1.TEMPORAL_MODULE_OPTIONS,
|
|
63
73
|
useFactory: options.useFactory,
|
|
64
74
|
inject: options.inject || [],
|
|
65
75
|
},
|
|
@@ -68,8 +78,8 @@ let TemporalClientModule = TemporalClientModule_1 = class TemporalClientModule {
|
|
|
68
78
|
if (options.useClass) {
|
|
69
79
|
return [
|
|
70
80
|
{
|
|
71
|
-
provide: constants_1.
|
|
72
|
-
useFactory: async (optionsFactory) => optionsFactory.
|
|
81
|
+
provide: constants_1.TEMPORAL_MODULE_OPTIONS,
|
|
82
|
+
useFactory: async (optionsFactory) => optionsFactory.createTemporalOptions(),
|
|
73
83
|
inject: [options.useClass],
|
|
74
84
|
},
|
|
75
85
|
{
|
|
@@ -81,8 +91,8 @@ let TemporalClientModule = TemporalClientModule_1 = class TemporalClientModule {
|
|
|
81
91
|
if (options.useExisting) {
|
|
82
92
|
return [
|
|
83
93
|
{
|
|
84
|
-
provide: constants_1.
|
|
85
|
-
useFactory: async (optionsFactory) => optionsFactory.
|
|
94
|
+
provide: constants_1.TEMPORAL_MODULE_OPTIONS,
|
|
95
|
+
useFactory: async (optionsFactory) => optionsFactory.createTemporalOptions(),
|
|
86
96
|
inject: [options.useExisting],
|
|
87
97
|
},
|
|
88
98
|
];
|
|
@@ -105,7 +115,7 @@ let TemporalClientModule = TemporalClientModule_1 = class TemporalClientModule {
|
|
|
105
115
|
};
|
|
106
116
|
}
|
|
107
117
|
connection = await client_1.Connection.connect(connectionConfig);
|
|
108
|
-
const namespace = options.namespace || constants_1.DEFAULT_NAMESPACE;
|
|
118
|
+
const namespace = options.connection.namespace || constants_1.DEFAULT_NAMESPACE;
|
|
109
119
|
this.logger.log(`Connected to Temporal server, using namespace "${namespace}"`);
|
|
110
120
|
const client = new client_1.Client({ connection, namespace });
|
|
111
121
|
return this.enhanceClientWithShutdown(client);
|
|
@@ -141,6 +151,26 @@ let TemporalClientModule = TemporalClientModule_1 = class TemporalClientModule {
|
|
|
141
151
|
};
|
|
142
152
|
return enhancedClient;
|
|
143
153
|
}
|
|
154
|
+
static extractClientOptions(options) {
|
|
155
|
+
return {
|
|
156
|
+
connection: {
|
|
157
|
+
address: options.connection.address,
|
|
158
|
+
namespace: options.connection.namespace,
|
|
159
|
+
tls: options.connection.tls,
|
|
160
|
+
apiKey: options.connection.apiKey,
|
|
161
|
+
metadata: options.connection.metadata,
|
|
162
|
+
},
|
|
163
|
+
allowConnectionFailure: true,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
static validateOptions(options) {
|
|
167
|
+
if (!options.connection) {
|
|
168
|
+
throw new Error('Connection configuration is required');
|
|
169
|
+
}
|
|
170
|
+
if (!options.connection.address) {
|
|
171
|
+
throw new Error('Connection address is required');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
144
174
|
};
|
|
145
175
|
exports.TemporalClientModule = TemporalClientModule;
|
|
146
176
|
TemporalClientModule.logger = new common_1.Logger(TemporalClientModule_1.name);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temporal-client.module.js","sourceRoot":"","sources":["../../src/client/temporal-client.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAOwB;AACxB,+CAAwD;
|
|
1
|
+
{"version":3,"file":"temporal-client.module.js","sourceRoot":"","sources":["../../src/client/temporal-client.module.ts"],"names":[],"mappings":"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"}
|