@5minds/processcube_engine_client 6.2.1 → 6.3.0-develop.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.
- package/README.md +611 -100
- package/package.json +7 -10
- package/scripts/add-box-release-tag.sh +0 -44
package/README.md
CHANGED
|
@@ -1,201 +1,712 @@
|
|
|
1
|
-
# Engine Client
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# ProcessCube Engine Client
|
|
2
|
+
|
|
3
|
+
TypeScript-Client zur Kommunikation mit der ProcessCube Engine via REST und Socket.IO.
|
|
4
|
+
|
|
5
|
+
- **Paketname:** `@5minds/processcube_engine_client`
|
|
6
|
+
- **Lizenz:** MIT
|
|
7
|
+
- **Plattformen:** Node.js, Browser
|
|
8
|
+
|
|
9
|
+
## Inhaltsverzeichnis
|
|
10
|
+
|
|
11
|
+
- [Installation](#installation)
|
|
12
|
+
- [Schnelleinstieg](#schnelleinstieg)
|
|
13
|
+
- [Architektur](#architektur)
|
|
14
|
+
- [EngineClient](#engineclient)
|
|
15
|
+
- [ClientFactory](#clientfactory)
|
|
16
|
+
- [Authentifizierung](#authentifizierung)
|
|
17
|
+
- [Prozesse starten](#prozesse-starten)
|
|
18
|
+
- [UserTasks](#usertasks)
|
|
19
|
+
- [External Tasks](#external-tasks)
|
|
20
|
+
- [Konzept](#konzept)
|
|
21
|
+
- [Lebenszyklus eines ExternalTasks](#lebenszyklus-eines-externaltasks)
|
|
22
|
+
- [ExternalTaskWorker](#externaltaskworker)
|
|
23
|
+
- [Worker-Konfiguration](#worker-konfiguration)
|
|
24
|
+
- [Processing Function](#processing-function)
|
|
25
|
+
- [Fehlerbehandlung](#fehlerbehandlung)
|
|
26
|
+
- [Lock-Verlängerung](#lock-verlängerung)
|
|
27
|
+
- [Health Monitoring](#health-monitoring)
|
|
28
|
+
- [Heartbeat Monitoring](#heartbeat-monitoring)
|
|
29
|
+
- [Graceful Shutdown](#graceful-shutdown)
|
|
30
|
+
- [Multi-Topic Worker](#multi-topic-worker)
|
|
31
|
+
- [Payload-Filter](#payload-filter)
|
|
32
|
+
- [ExternalTask REST API](#externaltask-rest-api)
|
|
33
|
+
- [Echtzeit-Events via Socket.IO](#echtzeit-events-via-socketio)
|
|
34
|
+
- [Alle verfügbaren Clients](#alle-verfügbaren-clients)
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install @5minds/processcube_engine_client
|
|
40
|
+
```
|
|
4
41
|
|
|
5
42
|
## Schnelleinstieg
|
|
6
43
|
|
|
7
44
|
```ts
|
|
8
45
|
import { EngineClient } from '@5minds/processcube_engine_client';
|
|
9
46
|
|
|
10
|
-
const
|
|
47
|
+
const client = new EngineClient('http://localhost:10560');
|
|
48
|
+
|
|
49
|
+
const processInstances = await client.processInstances.query({
|
|
50
|
+
correlationId: 'my-correlation-id',
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Architektur
|
|
55
|
+
|
|
56
|
+
Der Client ist modular aufgebaut. Der `EngineClient` bündelt spezialisierte Sub-Clients, die jeweils einen fachlichen Bereich der Engine abdecken. Die Kommunikation erfolgt über REST (HTTP) und Socket.IO (WebSocket).
|
|
57
|
+
|
|
58
|
+
```mermaid
|
|
59
|
+
graph TB
|
|
60
|
+
App[Anwendung]
|
|
61
|
+
EC[EngineClient]
|
|
62
|
+
|
|
63
|
+
subgraph "Sub-Clients"
|
|
64
|
+
PM[ProcessModels]
|
|
65
|
+
PI[ProcessInstances]
|
|
66
|
+
UT[UserTasks]
|
|
67
|
+
ET[ExternalTasks]
|
|
68
|
+
MT[ManualTasks]
|
|
69
|
+
CO[Correlations]
|
|
70
|
+
EV[Events]
|
|
71
|
+
NO[Notifications]
|
|
72
|
+
PD[ProcessDefinitions]
|
|
73
|
+
CR[Cronjobs]
|
|
74
|
+
FN[FlowNodeInstances]
|
|
75
|
+
DO[DataObjectInstances]
|
|
76
|
+
AI[ApplicationInfo]
|
|
77
|
+
UNT[UntypedTasks]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
subgraph "Kommunikation"
|
|
81
|
+
HTTP[HttpClient<br/>REST / fetch]
|
|
82
|
+
SIO[SocketIoManager<br/>Socket.IO]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
Engine[ProcessCube Engine]
|
|
86
|
+
|
|
87
|
+
App --> EC
|
|
88
|
+
EC --> PM & PI & UT & ET & MT & CO & EV & NO & PD & CR & FN & DO & AI & UNT
|
|
89
|
+
PM & PI & UT & ET & MT & CO & CR & FN & DO & AI & UNT & PD --> HTTP
|
|
90
|
+
EV & NO --> SIO
|
|
91
|
+
HTTP --> Engine
|
|
92
|
+
SIO --> Engine
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## EngineClient
|
|
96
|
+
|
|
97
|
+
Der `EngineClient` ist der zentrale Einstiegspunkt. Er erstellt alle Sub-Clients und verwaltet die Socket.IO-Verbindung.
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { EngineClient } from '@5minds/processcube_engine_client';
|
|
101
|
+
|
|
102
|
+
const client = new EngineClient('http://localhost:10560');
|
|
103
|
+
|
|
104
|
+
// Sub-Clients als Properties
|
|
105
|
+
client.processModels;
|
|
106
|
+
client.processInstances;
|
|
107
|
+
client.userTasks;
|
|
108
|
+
client.externalTasks;
|
|
109
|
+
client.manualTasks;
|
|
110
|
+
client.correlations;
|
|
111
|
+
client.events;
|
|
112
|
+
client.notification;
|
|
113
|
+
client.processDefinitions;
|
|
114
|
+
client.cronjobs;
|
|
115
|
+
client.flowNodeInstances;
|
|
116
|
+
client.dataObjectInstances;
|
|
117
|
+
client.applicationInfo;
|
|
118
|
+
client.untypedTasks;
|
|
119
|
+
|
|
120
|
+
// Aufräumen
|
|
121
|
+
client.dispose();
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## ClientFactory
|
|
125
|
+
|
|
126
|
+
Alternativ lassen sich einzelne Clients gezielt erstellen, wenn nur ein bestimmter fachlicher Bereich benötigt wird:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import { ClientFactory } from '@5minds/processcube_engine_client';
|
|
130
|
+
|
|
131
|
+
const engineUrl = 'http://localhost:10560';
|
|
11
132
|
|
|
12
|
-
const
|
|
133
|
+
const processInstanceClient = ClientFactory.createProcessInstanceClient(engineUrl);
|
|
13
134
|
|
|
14
|
-
const
|
|
15
|
-
|
|
135
|
+
const processInstances = await processInstanceClient.query({
|
|
136
|
+
correlationId: 'my-correlation-id',
|
|
16
137
|
});
|
|
17
138
|
```
|
|
18
139
|
|
|
19
|
-
|
|
140
|
+
Verfügbare Factory-Methoden:
|
|
141
|
+
|
|
142
|
+
| Methode | Beschreibung |
|
|
143
|
+
| ---------------------------------- | ------------------------------------------ |
|
|
144
|
+
| `createProcessModelClient()` | Prozessmodelle deployen, starten, abfragen |
|
|
145
|
+
| `createProcessInstanceClient()` | Prozessinstanzen abfragen und verwalten |
|
|
146
|
+
| `createProcessDefinitionClient()` | Prozessdefinitionen verwalten |
|
|
147
|
+
| `createUserTaskClient()` | UserTasks abfragen und abschließen |
|
|
148
|
+
| `createExternalTaskClient()` | ExternalTasks abonnieren und verarbeiten |
|
|
149
|
+
| `createManualTaskClient()` | ManualTasks abfragen und abschließen |
|
|
150
|
+
| `createUntypedTaskClient()` | Untypisierte Tasks verwalten |
|
|
151
|
+
| `createCorrelationClient()` | Correlations abfragen |
|
|
152
|
+
| `createEventClient()` | BPMN-Events senden und empfangen |
|
|
153
|
+
| `createNotificationClient()` | Echtzeit-Benachrichtigungen |
|
|
154
|
+
| `createCronJobClient()` | Cronjobs verwalten |
|
|
155
|
+
| `createFlowNodeInstanceClient()` | FlowNode-Instanzen abfragen |
|
|
156
|
+
| `createDataObjectInstanceClient()` | DataObjects abfragen |
|
|
157
|
+
| `createApplicationInfoClient()` | Engine-Metadaten abfragen |
|
|
158
|
+
|
|
159
|
+
## Authentifizierung
|
|
20
160
|
|
|
21
|
-
|
|
22
|
-
Mit dieser lässt sich eine Instanz des Clients anlegen, die direkt verwendbar ist.
|
|
161
|
+
Der Client unterstützt verschiedene Authentifizierungsarten:
|
|
23
162
|
|
|
24
|
-
|
|
163
|
+
### Statischer Token
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
import { EngineClient, Identity } from '@5minds/processcube_engine_client';
|
|
25
167
|
|
|
26
|
-
|
|
168
|
+
const identity: Identity = { token: 'Bearer my-jwt-token', userId: 'user123' };
|
|
169
|
+
const client = new EngineClient('http://localhost:10560', identity);
|
|
170
|
+
```
|
|
27
171
|
|
|
28
|
-
|
|
172
|
+
### Dynamischer Token (Callback)
|
|
29
173
|
|
|
30
174
|
```ts
|
|
31
175
|
import { EngineClient } from '@5minds/processcube_engine_client';
|
|
32
176
|
|
|
33
|
-
const
|
|
177
|
+
const client = new EngineClient('http://localhost:10560', async () => {
|
|
178
|
+
const token = await fetchTokenFromAuthProvider();
|
|
179
|
+
return { token, userId: 'user123' };
|
|
180
|
+
});
|
|
181
|
+
```
|
|
34
182
|
|
|
35
|
-
|
|
183
|
+
### Client Credentials (OAuth2)
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
import { EngineClient, ClientCredentialsConfig } from '@5minds/processcube_engine_client';
|
|
36
187
|
|
|
37
|
-
const
|
|
38
|
-
|
|
188
|
+
const credentials: ClientCredentialsConfig = {
|
|
189
|
+
clientId: 'my-client-id',
|
|
190
|
+
clientSecret: 'my-client-secret',
|
|
191
|
+
authority: 'https://auth.example.com',
|
|
192
|
+
scope: 'engine',
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const client = new EngineClient('http://localhost:10560', credentials);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Bei dynamischen Tokens und Client Credentials wird der Token automatisch vor Ablauf erneuert.
|
|
199
|
+
|
|
200
|
+
## Prozesse starten
|
|
201
|
+
|
|
202
|
+
### Einfacher Prozessstart
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
await client.processModels.startProcessInstance({
|
|
206
|
+
processModelId: 'myProcessModelId',
|
|
39
207
|
});
|
|
40
208
|
```
|
|
41
209
|
|
|
42
|
-
###
|
|
210
|
+
### Konfigurierter Prozessstart
|
|
43
211
|
|
|
44
|
-
|
|
212
|
+
```ts
|
|
213
|
+
await client.processModels.startProcessInstance({
|
|
214
|
+
processModelId: 'myProcessModelId',
|
|
215
|
+
startEventId: 'StartEvent_1',
|
|
216
|
+
correlationId: 'my-correlation-id',
|
|
217
|
+
initialToken: { hello: 'world' },
|
|
218
|
+
});
|
|
219
|
+
```
|
|
45
220
|
|
|
46
|
-
|
|
221
|
+
### Prozess starten und auf Ende warten
|
|
47
222
|
|
|
48
223
|
```ts
|
|
49
|
-
|
|
224
|
+
const result = await client.processModels.startProcessInstanceAndAwaitEndEvent({
|
|
225
|
+
processModelId: 'myProcessModelId',
|
|
226
|
+
});
|
|
227
|
+
```
|
|
50
228
|
|
|
51
|
-
|
|
229
|
+
## UserTasks
|
|
52
230
|
|
|
53
|
-
|
|
231
|
+
```ts
|
|
232
|
+
import { EngineClient, DataModels } from '@5minds/processcube_engine_client';
|
|
54
233
|
|
|
55
|
-
const
|
|
56
|
-
|
|
234
|
+
const client = new EngineClient('http://localhost:10560');
|
|
235
|
+
|
|
236
|
+
const userTasks = await client.userTasks.query({
|
|
237
|
+
processModelId: 'myProcessModelId',
|
|
238
|
+
state: DataModels.FlowNodeInstances.FlowNodeInstanceState.suspended,
|
|
57
239
|
});
|
|
58
240
|
```
|
|
59
241
|
|
|
60
|
-
|
|
242
|
+
---
|
|
61
243
|
|
|
62
|
-
##
|
|
244
|
+
## External Tasks
|
|
63
245
|
|
|
64
|
-
|
|
246
|
+
### Konzept
|
|
65
247
|
|
|
66
|
-
|
|
248
|
+
External Tasks sind ein zentrales Muster in BPMN 2.0 zur Auslagerung von Arbeitsschritten an externe Worker. Wenn die ProcessCube Engine einen **External Service Task** in einem Prozess erreicht, erstellt sie einen ExternalTask-Eintrag und wartet, bis ein externer Worker diesen abholt, verarbeitet und das Ergebnis zurückmeldet.
|
|
67
249
|
|
|
68
|
-
|
|
250
|
+
```mermaid
|
|
251
|
+
sequenceDiagram
|
|
252
|
+
participant P as BPMN-Prozess
|
|
253
|
+
participant E as ProcessCube Engine
|
|
254
|
+
participant W as ExternalTask Worker
|
|
69
255
|
|
|
70
|
-
|
|
256
|
+
P->>E: Erreicht External Service Task
|
|
257
|
+
E->>E: ExternalTask erstellen (state: pending)
|
|
258
|
+
Note over E: Task wartet auf Worker
|
|
71
259
|
|
|
72
|
-
|
|
260
|
+
loop Polling-Schleife
|
|
261
|
+
W->>E: Fetch & Lock (Topic, maxTasks, lockDuration)
|
|
262
|
+
E-->>W: Gesperrte ExternalTasks
|
|
263
|
+
end
|
|
73
264
|
|
|
74
|
-
|
|
75
|
-
import { EngineClient } from '@5minds/processcube_engine_client';
|
|
265
|
+
W->>W: Payload verarbeiten
|
|
76
266
|
|
|
77
|
-
|
|
267
|
+
alt Erfolg
|
|
268
|
+
W->>E: Finish (Result)
|
|
269
|
+
E->>P: Prozess fortsetzen mit Result
|
|
270
|
+
else Fehler
|
|
271
|
+
W->>E: HandleError (ErrorCode, Message)
|
|
272
|
+
E->>P: Fehler im Prozess auslösen
|
|
273
|
+
end
|
|
274
|
+
```
|
|
78
275
|
|
|
79
|
-
|
|
276
|
+
Dieses Muster entkoppelt die Prozessausführung von der eigentlichen Geschäftslogik und ermöglicht:
|
|
80
277
|
|
|
81
|
-
|
|
278
|
+
- **Skalierung:** Beliebig viele Worker können dieselben Topics verarbeiten
|
|
279
|
+
- **Sprachunabhängigkeit:** Worker können in jeder Sprache implementiert werden
|
|
280
|
+
- **Fehlertoleranz:** Locks verfallen automatisch, sodass fehlgeschlagene Tasks erneut verarbeitet werden
|
|
281
|
+
- **Langläufige Aufgaben:** Locks werden automatisch verlängert
|
|
282
|
+
|
|
283
|
+
### Lebenszyklus eines ExternalTasks
|
|
284
|
+
|
|
285
|
+
```mermaid
|
|
286
|
+
stateDiagram-v2
|
|
287
|
+
[*] --> pending: Engine erstellt Task
|
|
288
|
+
pending --> pending: Lock abgelaufen<br/>(Multi-Try)
|
|
289
|
+
pending --> expired: Lock abgelaufen<br/>(Single-Try)
|
|
290
|
+
pending --> finished: Worker meldet Ergebnis<br/>oder Fehler
|
|
291
|
+
finished --> [*]
|
|
292
|
+
expired --> [*]
|
|
82
293
|
```
|
|
83
294
|
|
|
84
|
-
|
|
295
|
+
| State | Beschreibung |
|
|
296
|
+
| ---------- | ------------------------------------------------------------ |
|
|
297
|
+
| `pending` | Task wartet auf Verarbeitung oder wird gerade verarbeitet |
|
|
298
|
+
| `finished` | Task wurde erfolgreich abgeschlossen oder mit Fehler beendet |
|
|
299
|
+
| `expired` | Lock ist abgelaufen (nur bei Single-Try Tasks) |
|
|
300
|
+
|
|
301
|
+
**Single-Try vs. Multi-Try:**
|
|
85
302
|
|
|
86
|
-
|
|
303
|
+
- **Multi-Try** (Standard): Wenn der Lock abläuft, geht der Task zurück in `pending` und kann von einem anderen Worker abgeholt werden.
|
|
304
|
+
- **Single-Try**: Wenn der Lock abläuft, geht der Task in `expired` und wird nicht erneut angeboten.
|
|
87
305
|
|
|
88
|
-
|
|
306
|
+
### ExternalTaskWorker
|
|
89
307
|
|
|
90
|
-
|
|
308
|
+
Der `ExternalTaskWorker` ist die zentrale Klasse zur Verarbeitung von External Tasks. Er implementiert eine Polling-Schleife mit Long-Polling, automatischer Lock-Verlängerung und robuster Fehlerbehandlung.
|
|
91
309
|
|
|
92
|
-
|
|
93
|
-
- **Hinweis:** Bei Prozessen mit mehreren Start Events ist dieser Parameter ebenfalls erforderlich!
|
|
94
|
-
- `correlationId`: Gibt an, in welcher Correlation die Prozessinstanz laufen soll
|
|
95
|
-
- `initialToken`: Der JSON-formatierte Prozess-Token, den die Prozessinstanz zu beginn besitzen soll
|
|
310
|
+
#### Minimalbeispiel
|
|
96
311
|
|
|
97
312
|
```ts
|
|
98
|
-
import {
|
|
313
|
+
import { ExternalTaskWorker } from '@5minds/processcube_engine_client';
|
|
99
314
|
|
|
100
|
-
const
|
|
315
|
+
const worker = new ExternalTaskWorker<MyPayload, MyResult>('http://localhost:10560', 'my_topic', async (payload) => {
|
|
316
|
+
return { result: payload.value * 2 };
|
|
317
|
+
});
|
|
101
318
|
|
|
102
|
-
|
|
319
|
+
worker.start();
|
|
320
|
+
```
|
|
103
321
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
322
|
+
#### Vollständiges Beispiel mit Typen
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
import { ExternalTaskWorker, DataModels } from '@5minds/processcube_engine_client';
|
|
326
|
+
|
|
327
|
+
interface OrderPayload {
|
|
328
|
+
orderId: string;
|
|
329
|
+
items: Array<{ productId: string; quantity: number }>;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
interface OrderResult {
|
|
333
|
+
confirmationNumber: string;
|
|
334
|
+
totalPrice: number;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const worker = new ExternalTaskWorker<OrderPayload, OrderResult>(
|
|
338
|
+
'http://localhost:10560',
|
|
339
|
+
'process_order',
|
|
340
|
+
async (payload, externalTask, abortSignal) => {
|
|
341
|
+
const order = await processOrder(payload.orderId, payload.items);
|
|
342
|
+
|
|
343
|
+
if (abortSignal?.aborted) {
|
|
344
|
+
throw new Error('Task wurde abgebrochen');
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return {
|
|
348
|
+
confirmationNumber: order.confirmation,
|
|
349
|
+
totalPrice: order.total,
|
|
350
|
+
};
|
|
110
351
|
},
|
|
352
|
+
{
|
|
353
|
+
maxTasks: 5,
|
|
354
|
+
lockDuration: 60000,
|
|
355
|
+
longpollingTimeout: 15000,
|
|
356
|
+
},
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
worker.start();
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
#### Interner Ablauf des Workers
|
|
363
|
+
|
|
364
|
+
```mermaid
|
|
365
|
+
flowchart TD
|
|
366
|
+
Start([worker.start]) --> CheckAuth{Identity<br/>gültig?}
|
|
367
|
+
CheckAuth -->|Ja| FetchLock[Fetch & Lock<br/>Long-Polling]
|
|
368
|
+
CheckAuth -->|Nein| RefreshToken[Token erneuern]
|
|
369
|
+
RefreshToken -->|Erfolg| FetchLock
|
|
370
|
+
RefreshToken -->|Fehlgeschlagen| CritErr[Kritischer Fehler<br/>Zähler erhöhen]
|
|
371
|
+
CritErr --> MaxErr{> 5 kritische<br/>Fehler?}
|
|
372
|
+
MaxErr -->|Ja| StopWorker([Worker stoppt])
|
|
373
|
+
MaxErr -->|Nein| Backoff[Exponentielles<br/>Backoff]
|
|
374
|
+
Backoff --> CheckAuth
|
|
375
|
+
|
|
376
|
+
FetchLock -->|Tasks erhalten| Process[Tasks parallel<br/>verarbeiten]
|
|
377
|
+
FetchLock -->|Keine Tasks| Idle[Warten<br/>idleTimeout]
|
|
378
|
+
FetchLock -->|Fehler| ErrHandle[Fehlerbehandlung]
|
|
379
|
+
ErrHandle -->|401/403| PermErr[Permanenter<br/>Auth-Fehler]
|
|
380
|
+
PermErr --> StopWorker
|
|
381
|
+
ErrHandle -->|Transient| Backoff
|
|
382
|
+
Idle --> CheckAuth
|
|
383
|
+
Process --> CheckAuth
|
|
384
|
+
|
|
385
|
+
subgraph "Pro Task (parallel)"
|
|
386
|
+
Process --> Exec[ExternalTaskExecution]
|
|
387
|
+
Exec --> LockExt[Lock-Verlängerung<br/>planen]
|
|
388
|
+
Exec --> RunFn[Processing Function<br/>ausführen]
|
|
389
|
+
RunFn -->|Result| Finish[finishExternalTask]
|
|
390
|
+
RunFn -->|Error| HandleErr[handleError]
|
|
391
|
+
RunFn -->|Aborted| Cleanup[Aufräumen]
|
|
392
|
+
end
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Worker-Konfiguration
|
|
396
|
+
|
|
397
|
+
```ts
|
|
398
|
+
import { ExternalTaskWorker, IExternalTaskWorkerConfig } from '@5minds/processcube_engine_client';
|
|
399
|
+
|
|
400
|
+
const config: IExternalTaskWorkerConfig = {
|
|
401
|
+
workerId: 'order-worker-1',
|
|
402
|
+
lockDuration: 30000,
|
|
403
|
+
maxTasks: 10,
|
|
404
|
+
idleTimeout: 500,
|
|
405
|
+
longpollingTimeout: 10000,
|
|
406
|
+
identity: { token: 'Bearer ...', userId: 'worker-user' },
|
|
407
|
+
payloadFilter: /urgent/,
|
|
408
|
+
};
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
| Option | Typ | Standard | Beschreibung |
|
|
412
|
+
| -------------------- | -------------- | -------- | ------------------------------------------- |
|
|
413
|
+
| `workerId` | `string` | UUID | Eindeutige Worker-ID |
|
|
414
|
+
| `lockDuration` | `number` | `30000` | Dauer des Locks in ms |
|
|
415
|
+
| `maxTasks` | `number` | `10` | Max. Tasks pro Fetch-and-Lock |
|
|
416
|
+
| `idleTimeout` | `number` | `500` | Wartezeit in ms, wenn keine Tasks vorhanden |
|
|
417
|
+
| `longpollingTimeout` | `number` | `10000` | Long-Polling-Timeout in ms |
|
|
418
|
+
| `identity` | `IdentityLike` | - | Authentifizierung |
|
|
419
|
+
| `payloadFilter` | `RegExp` | - | Regex-Filter auf Task-Payload |
|
|
420
|
+
| `logger` | `ILogger` | - | Eigener Logger |
|
|
421
|
+
|
|
422
|
+
### Processing Function
|
|
423
|
+
|
|
424
|
+
Die Processing Function erhält drei Parameter:
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
type HandleExternalTaskAction<TPayload, TResult> = (payload: TPayload, externalTask?: ExternalTask<TPayload>, signal?: AbortSignal) => TResult | Promise<TResult>;
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
| Parameter | Beschreibung |
|
|
431
|
+
| -------------- | -------------------------------------------------- |
|
|
432
|
+
| `payload` | Die Nutzdaten des Tasks (generisch typisiert) |
|
|
433
|
+
| `externalTask` | Das vollständige ExternalTask-Objekt mit Metadaten |
|
|
434
|
+
| `signal` | AbortSignal zum Reagieren auf Abbruch |
|
|
435
|
+
|
|
436
|
+
**Rückgabewerte:**
|
|
437
|
+
|
|
438
|
+
| Rückgabe | Verhalten |
|
|
439
|
+
| ------------------- | ----------------------------------------------------------- |
|
|
440
|
+
| Objekt | Task wird mit diesem Ergebnis als erfolgreich abgeschlossen |
|
|
441
|
+
| `ExternalTaskError` | Task wird als fehlgeschlagen gemeldet |
|
|
442
|
+
| `throw Error` | Task wird als fehlgeschlagen gemeldet |
|
|
443
|
+
|
|
444
|
+
#### Fehler gezielt melden
|
|
445
|
+
|
|
446
|
+
```ts
|
|
447
|
+
import { ExternalTaskWorker, DataModels } from '@5minds/processcube_engine_client';
|
|
448
|
+
|
|
449
|
+
const worker = new ExternalTaskWorker<MyPayload, MyResult>('http://localhost:10560', 'validate_data', async (payload) => {
|
|
450
|
+
if (!payload.email) {
|
|
451
|
+
return new DataModels.ExternalTasks.ExternalTaskError('VALIDATION_ERROR', 'E-Mail-Adresse fehlt', { field: 'email' });
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return { valid: true };
|
|
111
455
|
});
|
|
112
456
|
```
|
|
113
457
|
|
|
114
|
-
|
|
458
|
+
### Fehlerbehandlung
|
|
459
|
+
|
|
460
|
+
Der Worker unterscheidet zwischen verschiedenen Fehlertypen und reagiert entsprechend:
|
|
461
|
+
|
|
462
|
+
```mermaid
|
|
463
|
+
flowchart LR
|
|
464
|
+
Error[Fehler tritt auf]
|
|
465
|
+
Error --> Type{Fehlertyp}
|
|
466
|
+
|
|
467
|
+
Type -->|Netzwerk / Timeout| Retry[Retry mit<br/>exponentiellem Backoff]
|
|
468
|
+
Type -->|Token abgelaufen| Refresh[Token erneuern<br/>und Retry]
|
|
469
|
+
Type -->|401 / 403| Stop[Worker stoppt<br/>nach 5 Versuchen]
|
|
470
|
+
Type -->|409 Conflict| Abort[Task bereits<br/>abgeschlossen - Skip]
|
|
471
|
+
Type -->|Unbekannt| Report[Fehler melden<br/>und weiter]
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
#### Error-Handler registrieren
|
|
115
475
|
|
|
116
476
|
```ts
|
|
117
|
-
|
|
477
|
+
worker.onWorkerError((errorType, error, externalTask) => {
|
|
478
|
+
console.error(`[${errorType}]`, error.message);
|
|
479
|
+
});
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
| ErrorType | Beschreibung |
|
|
483
|
+
| ----------------------------- | ------------------------------------------- |
|
|
484
|
+
| `fetchAndLock` | Fehler beim Abholen von Tasks |
|
|
485
|
+
| `extendLock` | Fehler bei der Lock-Verlängerung |
|
|
486
|
+
| `processExternalTask` | Fehler in der Processing Function |
|
|
487
|
+
| `finishExternalTask` | Fehler beim Abschließen des Tasks |
|
|
488
|
+
| `unprocessableExternalTask` | Task konnte nicht verarbeitet werden |
|
|
489
|
+
| `identityRefresh` | Fehler bei der Token-Erneuerung |
|
|
490
|
+
| `criticalIdentityFailure` | Token-Erneuerung endgültig fehlgeschlagen |
|
|
491
|
+
| `permanentAuthorizationError` | Dauerhafter 401/403-Fehler |
|
|
492
|
+
| `unexpectedLoopError` | Unerwarteter Fehler in der Polling-Schleife |
|
|
493
|
+
|
|
494
|
+
### Lock-Verlängerung
|
|
495
|
+
|
|
496
|
+
Wenn eine Task-Verarbeitung länger dauert als die `lockDuration`, verlängert der Worker den Lock automatisch im Hintergrund.
|
|
497
|
+
|
|
498
|
+
```mermaid
|
|
499
|
+
sequenceDiagram
|
|
500
|
+
participant W as Worker
|
|
501
|
+
participant E as Engine
|
|
502
|
+
|
|
503
|
+
W->>E: Fetch & Lock (lockDuration: 30s)
|
|
504
|
+
E-->>W: Task (Lock bis T+30s)
|
|
505
|
+
|
|
506
|
+
Note over W: Verarbeitung startet...
|
|
507
|
+
|
|
508
|
+
W->>E: Extend Lock (bei T+25s)
|
|
509
|
+
E-->>W: OK (Lock bis T+55s)
|
|
510
|
+
|
|
511
|
+
Note over W: Verarbeitung läuft weiter...
|
|
118
512
|
|
|
119
|
-
|
|
513
|
+
W->>E: Extend Lock (bei T+50s)
|
|
514
|
+
E-->>W: OK (Lock bis T+80s)
|
|
120
515
|
|
|
121
|
-
|
|
516
|
+
Note over W: Verarbeitung abgeschlossen
|
|
122
517
|
|
|
123
|
-
|
|
518
|
+
W->>E: Finish Task (Result)
|
|
124
519
|
```
|
|
125
520
|
|
|
126
|
-
|
|
521
|
+
Die Lock-Verlängerung wird 5 Sekunden vor Ablauf des aktuellen Locks ausgelöst und verlängert jeweils um die konfigurierte `lockDuration`. Dies geschieht automatisch und transparent.
|
|
127
522
|
|
|
128
|
-
|
|
523
|
+
### Health Monitoring
|
|
129
524
|
|
|
130
|
-
|
|
525
|
+
```ts
|
|
526
|
+
const health = worker.getHealth();
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
Das Health-Objekt enthält:
|
|
131
530
|
|
|
132
|
-
|
|
531
|
+
| Eigenschaft | Typ | Beschreibung |
|
|
532
|
+
| --------------------------------- | --------- | --------------------------------------------- |
|
|
533
|
+
| `isPollingActive` | `boolean` | Ob die Polling-Schleife aktiv ist |
|
|
534
|
+
| `consecutiveCriticalErrors` | `number` | Anzahl aufeinanderfolgender kritischer Fehler |
|
|
535
|
+
| `maxConsecutiveCriticalErrors` | `number` | Maximum (5), danach stoppt der Worker |
|
|
536
|
+
| `activeTaskCount` | `number` | Anzahl aktuell verarbeiteter Tasks |
|
|
537
|
+
| `identityRefreshFailedCritically` | `boolean` | Ob die Token-Erneuerung fehlgeschlagen ist |
|
|
133
538
|
|
|
134
539
|
```ts
|
|
135
|
-
|
|
540
|
+
if (!worker.isHealthy()) {
|
|
541
|
+
// Worker neu starten oder Alarm auslösen
|
|
542
|
+
}
|
|
543
|
+
```
|
|
136
544
|
|
|
137
|
-
|
|
545
|
+
### Heartbeat Monitoring
|
|
138
546
|
|
|
139
|
-
|
|
547
|
+
Für detailliertes Monitoring kann ein Heartbeat-Callback registriert werden:
|
|
140
548
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
549
|
+
```ts
|
|
550
|
+
worker.onHeartbeat((operation, externalTaskId) => {
|
|
551
|
+
metrics.recordHeartbeat(operation, externalTaskId);
|
|
144
552
|
});
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
Gemeldete Operationen: `fetchAndLock`, `processExternalTask`, `extendLock`, `finishExternalTask`, `handleError`
|
|
556
|
+
|
|
557
|
+
### Graceful Shutdown
|
|
145
558
|
|
|
146
|
-
|
|
559
|
+
```ts
|
|
560
|
+
process.on('SIGTERM', () => {
|
|
561
|
+
worker.stop();
|
|
562
|
+
});
|
|
147
563
|
```
|
|
148
564
|
|
|
149
|
-
|
|
565
|
+
`stop()` bewirkt:
|
|
150
566
|
|
|
151
|
-
|
|
567
|
+
1. Polling-Schleife wird beendet
|
|
568
|
+
2. Alle laufenden Tasks erhalten ein Abort-Signal
|
|
569
|
+
3. Lock-Verlängerungen werden gestoppt
|
|
152
570
|
|
|
153
|
-
|
|
571
|
+
Für vollständiges Aufräumen:
|
|
154
572
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
573
|
+
```ts
|
|
574
|
+
worker.dispose();
|
|
575
|
+
```
|
|
158
576
|
|
|
159
|
-
|
|
577
|
+
### Multi-Topic Worker
|
|
578
|
+
|
|
579
|
+
Über die `ExternalTaskApiClient`-Klasse lassen sich Tasks aus mehreren Topics gleichzeitig abholen:
|
|
160
580
|
|
|
161
581
|
```ts
|
|
162
|
-
import {
|
|
582
|
+
import { ClientFactory } from '@5minds/processcube_engine_client';
|
|
163
583
|
|
|
164
|
-
|
|
165
|
-
number1: number;
|
|
166
|
-
number2: number;
|
|
167
|
-
}
|
|
584
|
+
const externalTaskApiClient = ClientFactory.createExternalTaskClient('http://localhost:10560');
|
|
168
585
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
586
|
+
const tasks = await externalTaskApiClient.fetchAndLockExternalTasks('my-worker-id', ['topic_a', 'topic_b', 'topic_c'], 10, 10000, 30000);
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### Payload-Filter
|
|
172
590
|
|
|
173
|
-
|
|
174
|
-
const topic = 'sum_numbers';
|
|
591
|
+
Mit einem Payload-Filter können nur Tasks abgeholt werden, deren Payload einem bestimmten Muster entspricht:
|
|
175
592
|
|
|
176
|
-
|
|
593
|
+
```ts
|
|
594
|
+
const worker = new ExternalTaskWorker<MyPayload, MyResult>('http://localhost:10560', 'notifications', handleNotification, {
|
|
595
|
+
payloadFilter: /priority.*high/,
|
|
596
|
+
});
|
|
597
|
+
```
|
|
177
598
|
|
|
178
|
-
|
|
599
|
+
### ExternalTask REST API
|
|
179
600
|
|
|
180
|
-
|
|
181
|
-
const result: AddResult = {
|
|
182
|
-
sum: payload.number1 + payload.number2,
|
|
183
|
-
};
|
|
601
|
+
Der Client kommuniziert mit folgenden Engine-Endpunkten:
|
|
184
602
|
|
|
185
|
-
|
|
186
|
-
|
|
603
|
+
| Methode | Endpunkt | Beschreibung |
|
|
604
|
+
| ------- | ------------------------------------------------------ | --------------------------- |
|
|
605
|
+
| `GET` | `/atlas_engine/api/v1/external_tasks/deployed_topics` | Alle aktiven Topics abrufen |
|
|
606
|
+
| `POST` | `/atlas_engine/api/v1/external_tasks/fetch_and_lock` | Tasks abholen und sperren |
|
|
607
|
+
| `PUT` | `/atlas_engine/api/v1/external_tasks/{id}/extend_lock` | Lock verlängern |
|
|
608
|
+
| `PUT` | `/atlas_engine/api/v1/external_tasks/{id}/finish` | Task abschließen |
|
|
609
|
+
| `PUT` | `/atlas_engine/api/v1/external_tasks/{id}/error` | Fehler melden |
|
|
610
|
+
|
|
611
|
+
### ExternalTask-Datenmodell
|
|
612
|
+
|
|
613
|
+
```ts
|
|
614
|
+
type ExternalTask<TPayload> = {
|
|
615
|
+
id: string;
|
|
616
|
+
workerId: string;
|
|
617
|
+
topic: string;
|
|
618
|
+
isSingleTry: boolean;
|
|
619
|
+
flowNodeInstanceId: string;
|
|
620
|
+
correlationId: string;
|
|
621
|
+
processDefinitionId: string;
|
|
622
|
+
processModelId?: string;
|
|
623
|
+
embeddedProcessModelId?: string;
|
|
624
|
+
processInstanceId: string;
|
|
625
|
+
ownerId: string;
|
|
626
|
+
payload: TPayload;
|
|
627
|
+
lockExpirationTime: Date;
|
|
628
|
+
state: ExternalTaskState; // 'pending' | 'finished' | 'expired'
|
|
629
|
+
finishedAt?: Date;
|
|
630
|
+
result?: any;
|
|
631
|
+
error?: any;
|
|
632
|
+
createdAt?: Date;
|
|
633
|
+
};
|
|
187
634
|
```
|
|
188
635
|
|
|
189
|
-
|
|
636
|
+
### ExternalTaskError
|
|
190
637
|
|
|
191
638
|
```ts
|
|
192
|
-
|
|
639
|
+
class ExternalTaskError {
|
|
640
|
+
errorCode: string;
|
|
641
|
+
errorMessage?: string;
|
|
642
|
+
errorDetails?: any;
|
|
643
|
+
|
|
644
|
+
constructor(errorCode: string, errorMessage?: string, errorDetails?: any);
|
|
645
|
+
}
|
|
193
646
|
```
|
|
194
647
|
|
|
195
|
-
|
|
648
|
+
---
|
|
649
|
+
|
|
650
|
+
## Echtzeit-Events via Socket.IO
|
|
651
|
+
|
|
652
|
+
Der Client kann sich über Socket.IO für Echtzeit-Benachrichtigungen der Engine registrieren.
|
|
653
|
+
|
|
654
|
+
### Notification-Events
|
|
196
655
|
|
|
197
656
|
```ts
|
|
198
|
-
|
|
657
|
+
const subscription = await client.notification.onProcessStarted((event) => {
|
|
658
|
+
// Prozess wurde gestartet
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
// Später abmelden
|
|
662
|
+
client.notification.removeSubscription(subscription);
|
|
199
663
|
```
|
|
200
664
|
|
|
201
|
-
|
|
665
|
+
### ExternalTask-Events
|
|
666
|
+
|
|
667
|
+
| Event | Beschreibung |
|
|
668
|
+
| ------------------------ | ------------------------------------------------ |
|
|
669
|
+
| `external_task_created` | Ein neuer ExternalTask wurde erstellt |
|
|
670
|
+
| `external_task_locked` | Ein ExternalTask wurde von einem Worker gesperrt |
|
|
671
|
+
| `external_task_unlocked` | Ein Lock ist abgelaufen (Multi-Try) |
|
|
672
|
+
| `external_task_expired` | Ein Lock ist abgelaufen (Single-Try) |
|
|
673
|
+
|
|
674
|
+
### Prozess-Events
|
|
675
|
+
|
|
676
|
+
| Event | Beschreibung |
|
|
677
|
+
| --------------------- | ---------------------------------- |
|
|
678
|
+
| `OnProcessStarting` | Prozessinstanz wird gestartet |
|
|
679
|
+
| `OnProcessStarted` | Prozessinstanz wurde gestartet |
|
|
680
|
+
| `OnProcessFinished` | Prozessinstanz wurde abgeschlossen |
|
|
681
|
+
| `OnProcessError` | Fehler in einer Prozessinstanz |
|
|
682
|
+
| `OnProcessTerminated` | Prozessinstanz wurde abgebrochen |
|
|
683
|
+
|
|
684
|
+
### Task-Events
|
|
685
|
+
|
|
686
|
+
| Event | Beschreibung |
|
|
687
|
+
| ---------------------- | --------------------------------- |
|
|
688
|
+
| `user_task_waiting` | UserTask wartet auf Bearbeitung |
|
|
689
|
+
| `user_task_finished` | UserTask wurde abgeschlossen |
|
|
690
|
+
| `manual_task_waiting` | ManualTask wartet auf Bearbeitung |
|
|
691
|
+
| `manual_task_finished` | ManualTask wurde abgeschlossen |
|
|
692
|
+
|
|
693
|
+
---
|
|
694
|
+
|
|
695
|
+
## Alle verfügbaren Clients
|
|
696
|
+
|
|
697
|
+
| Client | Property | Beschreibung |
|
|
698
|
+
| -------------------------- | ---------------------------- | -------------------------------------------- |
|
|
699
|
+
| `ProcessModelClient` | `client.processModels` | Prozessmodelle deployen, starten, abfragen |
|
|
700
|
+
| `ProcessInstanceClient` | `client.processInstances` | Laufende und abgeschlossene Prozessinstanzen |
|
|
701
|
+
| `ProcessDefinitionClient` | `client.processDefinitions` | Prozessdefinitionen (BPMN-XML) verwalten |
|
|
702
|
+
| `UserTaskClient` | `client.userTasks` | UserTasks abfragen und abschließen |
|
|
703
|
+
| `ExternalTaskClient` | `client.externalTasks` | ExternalTasks abonnieren und verarbeiten |
|
|
704
|
+
| `ManualTaskClient` | `client.manualTasks` | ManualTasks abfragen und abschließen |
|
|
705
|
+
| `UntypedTaskClient` | `client.untypedTasks` | Untypisierte Tasks verwalten |
|
|
706
|
+
| `CorrelationClient` | `client.correlations` | Correlations abfragen |
|
|
707
|
+
| `EventClient` | `client.events` | BPMN-Message/Signal-Events senden |
|
|
708
|
+
| `NotificationClient` | `client.notification` | Echtzeit-Events über Socket.IO |
|
|
709
|
+
| `CronjobClient` | `client.cronjobs` | Zeitgesteuerte Prozesse verwalten |
|
|
710
|
+
| `FlowNodeInstanceClient` | `client.flowNodeInstances` | FlowNode-Instanzen abfragen |
|
|
711
|
+
| `DataObjectInstanceClient` | `client.dataObjectInstances` | Prozess-Datenobjekte abfragen |
|
|
712
|
+
| `ApplicationInfoClient` | `client.applicationInfo` | Engine-Version und Metadaten |
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"utf-8-validate": "Not used by the client directly, but a requirement of the 'ws' sub-dependency. Since it is not available in the browser, the client would be unusable for the APP SDK, if we did not install it explicitly here"
|
|
5
5
|
},
|
|
6
6
|
"name": "@5minds/processcube_engine_client",
|
|
7
|
-
"version": "6.2
|
|
7
|
+
"version": "6.3.0-develop.2",
|
|
8
8
|
"description": "Contains a typescript based client for accessing the Engine.",
|
|
9
9
|
"main": "dist/commonjs/index.js",
|
|
10
10
|
"types": "dist/index.d.ts",
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"async-lock": "^1.4.1",
|
|
35
|
-
"bufferutil": "4.0
|
|
35
|
+
"bufferutil": "4.1.0",
|
|
36
36
|
"cross-fetch": "4.1.0",
|
|
37
|
-
"dayjs": "^1.11.
|
|
38
|
-
"openid-client": "^6.8.
|
|
39
|
-
"socket.io-client": "4.8.
|
|
37
|
+
"dayjs": "^1.11.20",
|
|
38
|
+
"openid-client": "^6.8.2",
|
|
39
|
+
"socket.io-client": "4.8.3",
|
|
40
40
|
"utf-8-validate": "5.0.10",
|
|
41
41
|
"uuid": "^13.0.0",
|
|
42
|
-
"@5minds/processcube_engine_sdk": "7.
|
|
42
|
+
"@5minds/processcube_engine_sdk": "7.3.0-develop.2"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^24.7.2",
|
|
@@ -50,9 +50,6 @@
|
|
|
50
50
|
"build": "tsc",
|
|
51
51
|
"build:test": "tsc --project tsconfig.tests.json",
|
|
52
52
|
"test": "node ./dist_test/commonjs/test/smoke-test.js",
|
|
53
|
-
"
|
|
54
|
-
"commit-and-tag-version": "CI_TOOLS_GIT_TAG_PREFIX=ProcessCube.Engine.Client.ts- ci_tools commit-and-tag-version --only-on-primary-branches && CI_TOOLS_GIT_TAG_PREFIX=ProcessCube.Engine.Client.ts- ci_tools update-github-release --only-on-primary-branches --use-title-and-text-from-git-tag",
|
|
55
|
-
"publish-npm-package": "CI_TOOLS_GIT_TAG_PREFIX=ProcessCube.Engine.Client.ts- ci_tools publish-npm-package --create-tag-from-branch-name",
|
|
56
|
-
"add-box-release-tag": "./scripts/add-box-release-tag.sh"
|
|
53
|
+
"test:unit": "node ./dist_test/commonjs/test/smoke-test.js"
|
|
57
54
|
}
|
|
58
55
|
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
set -e
|
|
4
|
-
|
|
5
|
-
echo "=== Add Box Release Tag Script ==="
|
|
6
|
-
echo "Timestamp: $(date)"
|
|
7
|
-
echo ""
|
|
8
|
-
|
|
9
|
-
echo "=== Environment Variables ==="
|
|
10
|
-
echo "BOX_RELEASE_TAG: ${BOX_RELEASE_TAG:-'(not set)'}"
|
|
11
|
-
echo "CI_TOOLS_VERSION: ${CI_TOOLS_VERSION:-'(not set)'}"
|
|
12
|
-
echo "NODE_ENV: ${NODE_ENV:-'(not set)'}"
|
|
13
|
-
echo "npm_config_registry: ${npm_config_registry:-'(not set)'}"
|
|
14
|
-
echo ""
|
|
15
|
-
|
|
16
|
-
VERSION="$(ci_tools get-version)"
|
|
17
|
-
echo "=== Version Information ==="
|
|
18
|
-
echo "Detected version: $VERSION"
|
|
19
|
-
echo ""
|
|
20
|
-
|
|
21
|
-
PACKAGE_NAME="@5minds/processcube_engine_client"
|
|
22
|
-
|
|
23
|
-
echo "=== Current NPM Package Information ==="
|
|
24
|
-
pnpm view "$PACKAGE_NAME@$VERSION" --json || echo "Package version not found in registry"
|
|
25
|
-
echo ""
|
|
26
|
-
|
|
27
|
-
echo "=== Adding dist-tag ==="
|
|
28
|
-
echo "Package: $PACKAGE_NAME"
|
|
29
|
-
echo "Version: $VERSION"
|
|
30
|
-
echo "Tag: ${BOX_RELEASE_TAG}"
|
|
31
|
-
echo ""
|
|
32
|
-
|
|
33
|
-
if [ -z "$BOX_RELEASE_TAG" ]; then
|
|
34
|
-
echo "Error: BOX_RELEASE_TAG environment variable is not set"
|
|
35
|
-
exit 1
|
|
36
|
-
fi
|
|
37
|
-
|
|
38
|
-
pnpm dist-tag add "$PACKAGE_NAME@$VERSION" "$BOX_RELEASE_TAG"
|
|
39
|
-
|
|
40
|
-
echo ""
|
|
41
|
-
echo "=== Verification ==="
|
|
42
|
-
pnpm dist-tag ls "$PACKAGE_NAME"
|
|
43
|
-
echo ""
|
|
44
|
-
echo "✅ Successfully added dist-tag '$BOX_RELEASE_TAG' to $PACKAGE_NAME@$VERSION"
|