@5minds/processcube_app_sdk 8.2.3-develop-17340a-mlmezagc → 8.2.3-develop-d7a750-mnizx1s1

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 (2) hide show
  1. package/README.md +350 -21
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -48,9 +48,192 @@ import { DynamicLink } from '@5minds/processcube_app_sdk/client';
48
48
 
49
49
  ### External Tasks
50
50
 
51
- External Tasks ermöglichen es, Logik in einer Next.js App auszuführen, die von der ProcessCube Engine als Aufgabe vergeben wird. Dazu wird eine Datei `external_task.ts` (oder `.js`) im `app/`-Verzeichnis der Next.js App angelegt. Der Verzeichnispfad bestimmt das Topic, unter dem die Engine den Task erkennt z.B. wird `app/order/process/external_task.ts` zum Topic `order/process`.
51
+ External Tasks ermöglichen es, eigene Geschäftslogik in einer Next.js App auszuführen, die von der ProcessCube Engine als Aufgabe vergeben wird. Erreicht ein BPMN-Prozess einen External Service Task, veröffentlicht die Engine diesen unter einem **Topic**. Ein passender Worker in der App holt sich den Task ab, verarbeitet ihn und gibt das Ergebnis zurück.
52
52
 
53
- Die External Tasks werden automatisch erkannt und als eigene Worker-Prozesse gestartet, wenn `useExternalTasks: true` in der SDK-Konfiguration gesetzt ist:
53
+ Das App SDK übernimmt dabei die komplette Infrastruktur: Worker-Prozesse werden automatisch gestartet, überwacht und bei Fehlern neu gestartet. Der Entwickler schreibt nur die eigentliche Handler-Funktion.
54
+
55
+ #### Architektur
56
+
57
+ Das folgende Diagramm zeigt die drei beteiligten Schichten und ihre Kommunikation:
58
+
59
+ ```mermaid
60
+ graph LR
61
+ subgraph ProcessCube Engine
62
+ E[Engine]
63
+ end
64
+
65
+ subgraph Next.js App – Hauptprozess
66
+ A[ExternalTaskAdapter]
67
+ W[File Watcher]
68
+ T[Token Management]
69
+ end
70
+
71
+ subgraph Eigener Node.js Prozess je Topic
72
+ WP1[Worker Process<br/>Topic: order/process]
73
+ WP2[Worker Process<br/>Topic: invoice/send]
74
+ end
75
+
76
+ E -- "HTTP: Fetch & Lock /<br/>Finish / Error /<br/>Extend Lock" --> WP1
77
+ E -- "HTTP: Fetch & Lock /<br/>Finish / Error /<br/>Extend Lock" --> WP2
78
+
79
+ A -- "IPC: create / restart /<br/>updateIdentity" --> WP1
80
+ A -- "IPC: create / restart /<br/>updateIdentity" --> WP2
81
+
82
+ W -- "Dateiänderung erkannt" --> A
83
+ T -- "Token-Refresh" --> A
84
+ ```
85
+
86
+ **Hauptkomponenten:**
87
+
88
+ | Komponente | Aufgabe |
89
+ |---|---|
90
+ | **ExternalTaskAdapter** | Läuft im Hauptprozess der Next.js App. Überwacht das Dateisystem, startet Worker-Prozesse, verwaltet Tokens und koordiniert Restarts. |
91
+ | **ExternalTaskWorkerProcess** | Eigenständiger Node.js-Kindprozess (einer pro Topic). Lädt den transpilierten Handler, verbindet sich per HTTP-Long-Polling mit der Engine und verarbeitet Tasks. |
92
+ | **ProcessCube Engine** | Verwaltet BPMN-Prozesse und vergibt External Tasks an Worker über das Fetch-and-Lock-Protokoll. |
93
+
94
+ #### Lebenszyklus eines External Tasks
95
+
96
+ Das folgende Sequenzdiagramm zeigt, wie ein External Task von der Engine zum Worker gelangt, verarbeitet und abgeschlossen wird:
97
+
98
+ ```mermaid
99
+ sequenceDiagram
100
+ participant Engine as ProcessCube Engine
101
+ participant Worker as Worker Process
102
+
103
+ loop Polling-Schleife
104
+ Worker->>Engine: POST /external_tasks/fetch_and_lock<br/>(workerId, topic, maxTasks, lockDuration)
105
+ Note right of Engine: Long-Polling — Anfrage wartet<br/>bis Task verfügbar oder Timeout
106
+ Engine-->>Worker: ExternalTask[] (mit Payload)
107
+ end
108
+
109
+ Note over Worker: Handler-Funktion wird aufgerufen:<br/>handler(payload, task, signal)
110
+
111
+ loop Lock-Verlängerung
112
+ Worker->>Engine: PUT /external_tasks/{id}/extend_lock<br/>(additionalDuration)
113
+ Note right of Engine: Lock wird verlängert,<br/>damit der Task nicht abläuft
114
+ end
115
+
116
+ alt Erfolg
117
+ Worker->>Engine: PUT /external_tasks/{id}/finish<br/>(result)
118
+ Note right of Engine: Ergebnis wird als<br/>Prozessvariable gespeichert
119
+ else Fehler
120
+ Worker->>Engine: PUT /external_tasks/{id}/error<br/>(errorCode, errorMessage)
121
+ Note right of Engine: Fehler wird im<br/>Prozess behandelt
122
+ end
123
+ ```
124
+
125
+ **Ablauf im Detail:**
126
+
127
+ 1. Der Worker pollt die Engine per HTTP-Long-Polling nach neuen Tasks für sein Topic.
128
+ 2. Sobald ein Task verfügbar ist, sperrt die Engine ihn (Lock) und liefert ihn mit dem Payload aus.
129
+ 3. Der Worker ruft die Handler-Funktion auf und übergibt Payload, Task-Metadaten und ein AbortSignal.
130
+ 4. Während der Verarbeitung verlängert der Worker automatisch den Lock, damit die Engine den Task nicht vorzeitig freigibt.
131
+ 5. Nach Abschluss meldet der Worker das Ergebnis (Finish) oder einen Fehler (Error) an die Engine.
132
+
133
+ #### Worker-Startup und IPC-Kommunikation
134
+
135
+ Das App SDK startet pro `external_task.ts`-Datei einen eigenen Node.js-Kindprozess. Die Kommunikation zwischen Hauptprozess (Adapter) und Kindprozess (Worker) läuft über IPC (Inter-Process Communication):
136
+
137
+ ```mermaid
138
+ sequenceDiagram
139
+ participant FS as Dateisystem
140
+ participant Adapter as ExternalTaskAdapter<br/>(Hauptprozess)
141
+ participant Worker as Worker Process<br/>(Kindprozess)
142
+ participant Engine as ProcessCube Engine
143
+
144
+ FS->>Adapter: Neue Datei erkannt:<br/>app/order/process/external_task.ts
145
+
146
+ Adapter->>Adapter: Datei transpilieren (esbuild)
147
+ Adapter->>Adapter: Topic ableiten: order/process
148
+
149
+ Adapter->>Worker: fork() — neuer Node.js-Prozess
150
+
151
+ Adapter->>Worker: IPC: { action: "create",<br/>topic, identity, moduleString }
152
+
153
+ Worker->>Worker: Handler-Modul aus String laden
154
+ Worker->>Engine: Polling starten (Fetch & Lock)
155
+ Note over Worker,Engine: Worker verarbeitet Tasks…
156
+
157
+ Note over FS: Datei wird geändert
158
+
159
+ FS->>Adapter: Dateiänderung erkannt
160
+
161
+ Adapter->>Adapter: Datei neu transpilieren
162
+ Adapter->>Worker: IPC: { action: "restart",<br/>topic, identity, moduleString }
163
+
164
+ Worker->>Worker: Alten Worker stoppen
165
+ Worker->>Worker: Neuen Handler laden
166
+ Worker->>Engine: Polling neu starten
167
+
168
+ Note over Adapter: Token läuft ab (85% Lebensdauer)
169
+
170
+ Adapter->>Adapter: Neuen Token holen (OpenID Connect)
171
+ Adapter->>Worker: IPC: { action: "updateIdentity",<br/>identity }
172
+ ```
173
+
174
+ **IPC-Nachrichten:**
175
+
176
+ | Action | Richtung | Beschreibung |
177
+ |---|---|---|
178
+ | `create` | Adapter → Worker | Initialer Start: Übergibt Topic, Identity und transpilierten Handler-Code |
179
+ | `restart` | Adapter → Worker | Hot-Reload: Stoppt den alten Worker und startet mit neuem Code (gleiche Worker-ID) |
180
+ | `updateIdentity` | Adapter → Worker | Aktualisiert den Auth-Token auf dem laufenden Worker |
181
+
182
+ #### Fehlerbehandlung und Restart-Strategie
183
+
184
+ Das System hat zwei Ebenen der Fehlerbehandlung: im Worker-Prozess selbst und im Adapter (Hauptprozess).
185
+
186
+ ```mermaid
187
+ flowchart TD
188
+ A[Fehler im Worker] --> B{Verbindungsfehler?<br/>ECONNREFUSED / ETIMEDOUT /<br/>ECONNRESET / etc.}
189
+
190
+ B -- Ja --> C{Retries < Max?<br/>Standard: 6}
191
+ C -- Ja --> D[Exponentieller Backoff<br/>1s → 2s → 4s → … → 30s max]
192
+ D --> E[Erneut verbinden]
193
+ E --> F{Verbindung OK?}
194
+ F -- Ja --> G[Weiter arbeiten ✓]
195
+ F -- Nein --> C
196
+
197
+ C -- Nein --> H[Worker beenden<br/>Exit Code 3]
198
+
199
+ B -- Nein --> I{Uncaught Exception?}
200
+ I -- Ja --> J[Worker beenden<br/>Exit Code 4]
201
+ I -- Nein --> K[Fehler loggen,<br/>Worker beenden<br/>Exit Code 3]
202
+
203
+ H --> L[Adapter erkennt Exit]
204
+ J --> L
205
+ K --> L
206
+
207
+ L --> M{Restarts < 6 in<br/>letzten 5 Minuten?}
208
+ M -- Ja --> N[Exponentieller Backoff<br/>1s → 2s → 4s → … → 30s max]
209
+ N --> O[Worker neu starten]
210
+ O --> P{Start OK?}
211
+ P -- Ja --> G
212
+ P -- Nein --> L
213
+
214
+ M -- Nein --> Q[Worker bleibt gestoppt ✗<br/>Manueller Eingriff nötig]
215
+ ```
216
+
217
+ **Worker-Level (Kindprozess):**
218
+
219
+ - Bei Verbindungsfehlern (ECONNREFUSED, ECONNRESET, ETIMEDOUT, ENOTFOUND, EAI_AGAIN, Socket Hang Up) versucht der Worker bis zu **6 Reconnects** mit exponentiellem Backoff (1s → 2s → 4s → 8s → 16s → 30s max).
220
+ - Die Anzahl der Retries ist über die Umgebungsvariable `PROCESSCUBE_APP_SDK_ETW_RETRY` konfigurierbar.
221
+ - Nach Ausschöpfung der Retries beendet sich der Worker mit Exit Code 3.
222
+ - Bei unbehandelten Exceptions beendet sich der Worker mit Exit Code 4.
223
+
224
+ **Adapter-Level (Hauptprozess):**
225
+
226
+ - Erkennt der Adapter einen Worker-Exit mit Code 3 oder 4, wird ein Neustart versucht.
227
+ - Maximal **6 Neustarts** innerhalb eines **5-Minuten-Fensters** sind erlaubt.
228
+ - Die Backoff-Zeiten steigen exponentiell: 1s → 2s → 4s → 8s → 16s → 30s.
229
+ - Wird das Limit erreicht, bleibt der Worker gestoppt — ein manueller Eingriff (z.B. App-Neustart) ist nötig.
230
+ - Nach Ablauf des 5-Minuten-Fensters wird der Zähler zurückgesetzt.
231
+
232
+ #### Setup und Konfiguration
233
+
234
+ ##### 1. Next.js Plugin aktivieren
235
+
236
+ In der `next.config.js` wird das SDK-Plugin eingebunden und External Tasks aktiviert:
54
237
 
55
238
  ```javascript
56
239
  // next.config.js
@@ -59,53 +242,91 @@ const { withApplicationSdk } = require('@5minds/processcube_app_sdk/server');
59
242
  module.exports = withApplicationSdk({
60
243
  applicationSdk: {
61
244
  useExternalTasks: true,
245
+ // Optional: Eigenes Verzeichnis für External Tasks
246
+ // customExternalTasksDirPath: './my-tasks',
62
247
  },
63
248
  });
64
249
  ```
65
250
 
251
+ Das Plugin erkennt automatisch, ob die App im Development- oder Production-Modus läuft, und startet die Worker entsprechend. Während des Build-Prozesses (`next build`) werden keine Worker gestartet.
252
+
253
+ ##### 2. Handler-Datei anlegen
254
+
255
+ External Tasks werden durch Dateien mit dem Namen `external_task.ts` (oder `.js`) definiert. Das Verzeichnis, in dem die Datei liegt, bestimmt automatisch das **Topic**, unter dem sich der Worker bei der Engine registriert.
256
+
257
+ ```
258
+ app/
259
+ ├── order/
260
+ │ └── process/
261
+ │ └── external_task.ts → Topic: order/process
262
+ ├── invoice/
263
+ │ └── send/
264
+ │ └── external_task.ts → Topic: invoice/send
265
+ └── notification/
266
+ └── email/
267
+ └── external_task.ts → Topic: notification/email
268
+ ```
269
+
270
+ Das SDK sucht Handler-Dateien standardmäßig in `./app` oder `./src/app`. Ein eigenes Verzeichnis kann über `customExternalTasksDirPath` konfiguriert werden.
271
+
272
+ > **Wichtig:** Pro Verzeichnis darf nur eine `external_task.ts` oder `external_task.js` existieren. Beide Dateien im selben Verzeichnis führen zu einem Fehler.
273
+
66
274
  #### Handler-Signatur
67
275
 
68
- Der Handler wird als `default export` exportiert und erhält bis zu drei Parameter:
276
+ Der Handler wird als **Default-Export** der Datei definiert. Er erhält bis zu drei Parameter:
69
277
 
70
278
  ```typescript
71
279
  export default async function handleExternalTask(
72
- payload: any, // Prozess-Variablen
73
- task: ExternalTask<any>, // Task-Metadaten (optional)
74
- signal: AbortSignal, // Abort-Signal (optional)
280
+ payload: any,
281
+ task: ExternalTask<any>,
282
+ signal: AbortSignal,
75
283
  ) {
76
- // Aufgabe bearbeiten
284
+ // Geschäftslogik hier
77
285
  return { result: 'done' };
78
286
  }
79
287
  ```
80
288
 
81
- Der Rückgabewert wird als Ergebnis an die Engine zurückgegeben und steht im Prozess als Variable zur Verfügung.
289
+ | Parameter | Typ | Beschreibung |
290
+ |---|---|---|
291
+ | `payload` | `any` | Die Prozessvariablen, die der BPMN-Prozess dem External Task mitgibt. Enthält die im Prozessmodell definierte Payload-Expression. |
292
+ | `task` | `ExternalTask<any>` | Metadaten des Tasks: `id`, `workerId`, `topic`, `correlationId`, `processInstanceId`, `processDefinitionId`, `flowNodeInstanceId`, `lockExpirationTime`, `state`, `createdAt`. Optional. |
293
+ | `signal` | `AbortSignal` | Wird ausgelöst, wenn ein Boundary Event (z.B. Timer) den Task abbricht. Optional. |
82
294
 
83
- #### Konfiguration
295
+ **Rückgabewert:** Das zurückgegebene Objekt wird als Ergebnis an die Engine gemeldet und steht im BPMN-Prozess als Variable zur Verfügung.
84
296
 
85
- Über einen benannten `config`-Export können Worker-Einstellungen angepasst werden:
297
+ #### Worker-Konfiguration
298
+
299
+ Über einen benannten `config`-Export können Worker-Einstellungen pro Handler angepasst werden:
86
300
 
87
301
  ```typescript
88
302
  import { ExternalTaskConfig } from '@5minds/processcube_app_sdk/server';
89
303
 
90
304
  export const config: ExternalTaskConfig = {
91
- lockDuration: 5000, // Lock-Dauer in ms (default: 30000)
92
- maxTasks: 5, // Gleichzeitige Tasks (default: 10)
305
+ lockDuration: 5000, // Lock-Dauer in ms (Standard: 30000)
306
+ maxTasks: 5, // Gleichzeitige Tasks pro Polling-Zyklus (Standard: 10)
93
307
  };
94
308
  ```
95
309
 
96
- Die `lockDuration` bestimmt, wie oft der Worker der Engine signalisiert, dass er noch aktiv ist. Der Standardwert ist 30 Sekunden.
310
+ | Option | Typ | Standard | Beschreibung |
311
+ |---|---|---|---|
312
+ | `lockDuration` | `number` | `30000` | Dauer in Millisekunden, für die ein Task gesperrt wird. Bestimmt auch das Intervall der Lock-Verlängerung und die maximale Verzögerung bei Abort-Signalen. |
313
+ | `maxTasks` | `number` | `10` | Maximale Anzahl gleichzeitig abgeholter Tasks pro Polling-Zyklus. |
97
314
 
98
315
  #### Abort-Handling bei Boundary Events
99
316
 
100
- Wenn ein Boundary Event (z.B. ein Timer) einen External Task abbricht, wird das `AbortSignal` ausgelöst. Damit der Worker schnell auf den Abbruch reagiert, sollte die `lockDuration` reduziert werden — die Engine kann den Abbruch erst beim nächsten Lock-Renewal mitteilen.
317
+ Wenn ein BPMN Boundary Event (z.B. ein Timer oder Signal) einen External Task abbricht, löst die Engine den Abbruch beim nächsten Lock-Renewal aus. Das `AbortSignal` im Handler wird daraufhin ausgelöst.
318
+
319
+ **Wichtig:** Die `lockDuration` bestimmt die maximale Verzögerung bis zum Abort, da die Engine den Abbruch erst beim nächsten Lock-Renewal mitteilen kann:
101
320
 
102
- | lockDuration | Max. Verzögerung bis Abort |
103
- | --------------- | -------------------------- |
104
- | 30000 (default) | bis zu 30 Sekunden |
105
- | 5000 | bis zu 5 Sekunden |
106
- | 1000 | bis zu 1 Sekunde |
321
+ | lockDuration | Max. Verzögerung bis Abort |
322
+ |---|---|
323
+ | `30000` (Standard) | bis zu 30 Sekunden |
324
+ | `5000` | bis zu 5 Sekunden |
325
+ | `1000` | bis zu 1 Sekunde |
107
326
 
108
- Vollständiges Beispiel mit Abort-Handling:
327
+ Für zeitkritische Abbrüche sollte die `lockDuration` entsprechend reduziert werden.
328
+
329
+ **Beispiel mit Abort-Handling:**
109
330
 
110
331
  ```typescript
111
332
  import { ExternalTaskConfig } from '@5minds/processcube_app_sdk/server';
@@ -115,25 +336,133 @@ export const config: ExternalTaskConfig = {
115
336
  };
116
337
 
117
338
  export default async function handleExternalTask(payload: any, _task: any, signal: AbortSignal) {
339
+ // Listener für Cleanup-Aktionen bei Abbruch
118
340
  signal.addEventListener(
119
341
  'abort',
120
342
  () => {
121
343
  console.log('Task wurde durch Boundary Event abgebrochen');
344
+ // Hier ggf. Ressourcen freigeben
122
345
  },
123
346
  { once: true },
124
347
  );
125
348
 
126
- // Signal vor und nach asynchronen Operationen prüfen
349
+ // Signal vor asynchronen Operationen prüfen
127
350
  if (signal.aborted) return;
128
351
 
129
352
  const result = await doWork(payload);
130
353
 
354
+ // Signal nach asynchronen Operationen prüfen
131
355
  if (signal.aborted) return;
132
356
 
133
357
  return result;
134
358
  }
135
359
  ```
136
360
 
361
+ #### Authentifizierung und Token-Management
362
+
363
+ Ist eine ProcessCube Authority konfiguriert, holt der Adapter automatisch Tokens per **OpenID Connect Client Credentials Grant** und verteilt sie an alle Worker.
364
+
365
+ ```mermaid
366
+ sequenceDiagram
367
+ participant Authority as OpenID Authority
368
+ participant Adapter as ExternalTaskAdapter
369
+ participant W1 as Worker 1
370
+ participant W2 as Worker 2
371
+
372
+ Adapter->>Authority: Client Credentials Grant<br/>(client_id, client_secret, scope: engine_etw)
373
+ Authority-->>Adapter: TokenSet (access_token, expires_in)
374
+
375
+ Adapter->>W1: IPC: create (mit Identity)
376
+ Adapter->>W2: IPC: create (mit Identity)
377
+
378
+ Note over Adapter: Token-Refresh bei 85%<br/>der Lebensdauer
379
+
380
+ Adapter->>Authority: Client Credentials Grant (Refresh)
381
+ Authority-->>Adapter: Neues TokenSet
382
+
383
+ Adapter->>W1: IPC: updateIdentity
384
+ Adapter->>W2: IPC: updateIdentity
385
+ ```
386
+
387
+ - Der Token wird bei **85% seiner Lebensdauer** automatisch erneuert.
388
+ - Alle aktiven Worker erhalten den neuen Token per IPC-Nachricht.
389
+ - Der initiale Token-Abruf hat **10 Versuche** mit exponentiellem Backoff (max. 30s).
390
+ - Der periodische Token-Refresh versucht es **unbegrenzt** mit Backoff (max. 60s).
391
+ - Ist keine Authority konfiguriert, wird eine Dummy-Identity verwendet (für lokale Entwicklung ohne Auth).
392
+
393
+ #### Umgebungsvariablen
394
+
395
+ | Variable | Pflicht | Standard | Beschreibung |
396
+ |---|---|---|---|
397
+ | `PROCESSCUBE_ENGINE_URL` | Nein | `http://localhost:10560` | URL der ProcessCube Engine |
398
+ | `PROCESSCUBE_AUTHORITY_URL` | Nein | — | URL des OpenID-Providers. Wenn gesetzt, wird Token-basierte Authentifizierung aktiviert. |
399
+ | `PROCESSCUBE_EXTERNAL_TASK_WORKER_CLIENT_ID` | Wenn Authority | — | Client-ID für den OpenID Client Credentials Grant |
400
+ | `PROCESSCUBE_EXTERNAL_TASK_WORKER_CLIENT_SECRET` | Wenn Authority | — | Client-Secret für den OpenID Client Credentials Grant |
401
+ | `PROCESSCUBE_APP_SDK_ETW_RETRY` | Nein | `6` | Maximale Anzahl der Reconnect-Versuche im Worker-Prozess bei Verbindungsfehlern |
402
+
403
+ #### Vollständiges Beispiel
404
+
405
+ Eine `external_task.ts` mit allen Features — Konfiguration, typisiertem Payload, Fehlerbehandlung und Abort-Support:
406
+
407
+ ```typescript
408
+ import { ExternalTaskConfig } from '@5minds/processcube_app_sdk/server';
409
+
410
+ // Worker-Konfiguration
411
+ export const config: ExternalTaskConfig = {
412
+ lockDuration: 5000, // 5s Lock für schnelle Abort-Reaktion
413
+ maxTasks: 3, // Maximal 3 Tasks gleichzeitig
414
+ };
415
+
416
+ // Typen für Payload und Ergebnis
417
+ interface OrderPayload {
418
+ orderId: string;
419
+ customerEmail: string;
420
+ items: Array<{ productId: string; quantity: number }>;
421
+ }
422
+
423
+ interface OrderResult {
424
+ confirmationId: string;
425
+ processedAt: string;
426
+ }
427
+
428
+ export default async function handleExternalTask(
429
+ payload: OrderPayload,
430
+ task: any,
431
+ signal: AbortSignal,
432
+ ): Promise<OrderResult | undefined> {
433
+ console.log(`Verarbeite Bestellung ${payload.orderId} (Task: ${task.id})`);
434
+
435
+ // Abort-Handler für Cleanup
436
+ signal.addEventListener(
437
+ 'abort',
438
+ () => {
439
+ console.log(`Bestellung ${payload.orderId} wurde abgebrochen`);
440
+ },
441
+ { once: true },
442
+ );
443
+
444
+ if (signal.aborted) return;
445
+
446
+ // Geschäftslogik
447
+ const confirmation = await processOrder(payload);
448
+
449
+ if (signal.aborted) return;
450
+
451
+ await sendConfirmationEmail(payload.customerEmail, confirmation);
452
+
453
+ if (signal.aborted) return;
454
+
455
+ return {
456
+ confirmationId: confirmation.id,
457
+ processedAt: new Date().toISOString(),
458
+ };
459
+ }
460
+ ```
461
+
462
+ #### Hot-Reload
463
+
464
+ Im Development-Modus überwacht das SDK die Handler-Dateien per File-Watcher. Änderungen an einer `external_task.ts` werden automatisch erkannt: Die Datei wird neu transpiliert und der Worker per IPC-Nachricht (`restart`) mit dem neuen Code neu gestartet — ohne die App neu starten zu müssen. Die Worker-ID bleibt dabei erhalten.
465
+
137
466
  ## Wie kann ich das Projekt aufsetzen?
138
467
 
139
468
  ### Setup/Installation
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@5minds/processcube_app_sdk",
3
- "version": "8.2.3-develop-17340a-mlmezagc",
3
+ "version": "8.2.3-develop-d7a750-mnizx1s1",
4
4
  "description": "The SDK for ProcessCube Apps",
5
5
  "type": "module",
6
6
  "main": "build/common/index.cjs",