@camunda8/orchestration-cluster-api 10.0.0-alpha.38 → 10.0.0-alpha.39
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/CHANGELOG.md +7 -0
- package/README.md +125 -5
- package/dist/{CamundaClient-BMOpGKIo.d.ts → CamundaClient-Crt5W0EI.d.ts} +8 -9
- package/dist/{CamundaClient-CvdIL8uM.d.cts → CamundaClient-VlYVveFG.d.cts} +8 -9
- package/dist/{chunk-LPNUMNEM.js → chunk-LNMC2FKC.js} +73 -74
- package/dist/chunk-LNMC2FKC.js.map +1 -0
- package/dist/effect/index.cjs +109 -74
- package/dist/effect/index.cjs.map +1 -1
- package/dist/effect/index.d.cts +48 -4
- package/dist/effect/index.d.ts +48 -4
- package/dist/effect/index.js +45 -8
- package/dist/effect/index.js.map +1 -1
- package/dist/index.cjs +72 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +4 -3
- package/dist/chunk-LPNUMNEM.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [10.0.0-alpha.39](https://github.com/camunda/orchestration-cluster-api-js/compare/v10.0.0-alpha.38...v10.0.0-alpha.39) (2026-08-27)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **effect:** keep .paginate on the Effect client, as Streams ([#468](https://github.com/camunda/orchestration-cluster-api-js/issues/468)) ([b61cfbe](https://github.com/camunda/orchestration-cluster-api-js/commit/b61cfbe1b324b0a97bb1a77858c53c4de7156e37))
|
|
7
|
+
|
|
1
8
|
# [10.0.0-alpha.38](https://github.com/camunda/orchestration-cluster-api-js/compare/v10.0.0-alpha.37...v10.0.0-alpha.38) (2026-08-27)
|
|
2
9
|
|
|
3
10
|
|
package/README.md
CHANGED
|
@@ -1269,7 +1269,7 @@ first-class [Effect](https://effect.website) surface — a client whose every me
|
|
|
1269
1269
|
> resolution — set `"moduleResolution": "bundler"` (or `"node16"`/`"nodenext"`) in your
|
|
1270
1270
|
> `tsconfig.json`. The Promise-first `.` entry is unaffected.
|
|
1271
1271
|
|
|
1272
|
-
<!-- snippet-
|
|
1272
|
+
<!-- snippet-source: examples/effect.ts,examples/readme-imports.txt | regions: ReadmeEffectClientImport+ReadmeEffectClient -->
|
|
1273
1273
|
```ts
|
|
1274
1274
|
import { Effect } from 'effect';
|
|
1275
1275
|
import {
|
|
@@ -1326,10 +1326,44 @@ Exports available from `.../effect`:
|
|
|
1326
1326
|
the Effect `Clock`, timing out to `EventualConsistencyTimeout`).
|
|
1327
1327
|
- Dependency injection: `CamundaEffect` (`Context.Service`) + `layer(options?)` (`Layer`) so worker /
|
|
1328
1328
|
orchestration code composes via `Layer` and swaps a test double trivially.
|
|
1329
|
+
- Pagination: `.paginate(body, opts?)` on every `search*` method, returning an `EffectPaginator`
|
|
1330
|
+
(`pages()` / `items()` → `Stream`, `toArray()` → `Effect`). See below.
|
|
1329
1331
|
|
|
1330
1332
|
**Clock-class win:** `eventually` / `withTimeout` run on the Effect `Clock`, so `TestClock.adjust`
|
|
1331
1333
|
advances eventual/timeout deterministically in tests — no real-clock burn.
|
|
1332
1334
|
|
|
1335
|
+
### Paginated Search as a `Stream`
|
|
1336
|
+
|
|
1337
|
+
Every `search*` operation on the Effect client carries the same `.paginate` helper the
|
|
1338
|
+
Promise client installs, re-expressed in Effect terms: `pages()` and `items()` are
|
|
1339
|
+
`Stream`s and `toArray()` is an `Effect`. Pages are fetched lazily as they are pulled,
|
|
1340
|
+
and interrupting the fiber cancels the in-flight page request.
|
|
1341
|
+
|
|
1342
|
+
<!-- snippet-source: examples/effect.ts,examples/readme-imports.txt | regions: ReadmeEffectPaginateImport+ReadmeEffectPaginate -->
|
|
1343
|
+
```ts
|
|
1344
|
+
import { Effect, Stream } from 'effect';
|
|
1345
|
+
import { createCamundaEffectClient } from '@camunda8/orchestration-cluster-api/effect';
|
|
1346
|
+
|
|
1347
|
+
const camunda = createCamundaEffectClient();
|
|
1348
|
+
|
|
1349
|
+
// Walk every ACTIVE process instance, 100 per request, without ever holding more
|
|
1350
|
+
// than one page in memory. `Stream.take` stops pulling — and so stops fetching.
|
|
1351
|
+
const activeKeys = await Effect.runPromise(
|
|
1352
|
+
camunda.searchProcessInstances
|
|
1353
|
+
.paginate({ filter: { state: 'ACTIVE' }, page: { limit: 100 } })
|
|
1354
|
+
.items()
|
|
1355
|
+
.pipe(
|
|
1356
|
+
Stream.map((instance) => instance.processInstanceKey),
|
|
1357
|
+
Stream.take(500),
|
|
1358
|
+
Stream.runCollect
|
|
1359
|
+
)
|
|
1360
|
+
);
|
|
1361
|
+
```
|
|
1362
|
+
|
|
1363
|
+
Options: `maxPages` (safety cap), `mode` (`auto` | `cursor` | `offset`), and `consistency`
|
|
1364
|
+
(forwarded to the first page only — once paging is under way an empty page is
|
|
1365
|
+
end-of-results, not a stale read).
|
|
1366
|
+
|
|
1333
1367
|
### Effect Job Workers
|
|
1334
1368
|
|
|
1335
1369
|
The same subpath also exposes an **Effect-native job worker** — the long-running
|
|
@@ -1340,7 +1374,7 @@ and a `TerminalJobError` becomes `throwJobError` (caught by a BPMN error boundar
|
|
|
1340
1374
|
uncaught). Success completes the job with the returned variables. It composes over the same
|
|
1341
1375
|
activation/backpressure runtime the Promise worker uses — it does not reimplement activation.
|
|
1342
1376
|
|
|
1343
|
-
<!-- snippet-
|
|
1377
|
+
<!-- snippet-source: examples/effect.ts,examples/readme-imports.txt | regions: ReadmeEffectWorkerImport+ReadmeEffectWorker -->
|
|
1344
1378
|
```ts
|
|
1345
1379
|
import { Effect, Schedule } from 'effect';
|
|
1346
1380
|
import {
|
|
@@ -1352,8 +1386,12 @@ import {
|
|
|
1352
1386
|
|
|
1353
1387
|
const program = Effect.gen(function* () {
|
|
1354
1388
|
// Forked into the current Scope: interrupted (with a best-effort lease release) when
|
|
1355
|
-
// the scope closes.
|
|
1356
|
-
|
|
1389
|
+
// the scope closes. Let both type parameters infer — supplying only the completion-
|
|
1390
|
+
// variable type (`createCamundaEffectWorker<{ ok: boolean }>(…)`) makes TypeScript
|
|
1391
|
+
// fall back to the *default* for the handler's requirements (`R = never`) rather
|
|
1392
|
+
// than inferring it, so a handler with dependencies would stop compiling. See
|
|
1393
|
+
// "Injecting Services into a Handler".
|
|
1394
|
+
yield* createCamundaEffectWorker({
|
|
1357
1395
|
type: 'payment-processing',
|
|
1358
1396
|
maxJobsToActivate: 10, // activation batch size
|
|
1359
1397
|
concurrency: 10, // max jobs handled in parallel (backpressure)
|
|
@@ -1371,7 +1409,10 @@ const program = Effect.gen(function* () {
|
|
|
1371
1409
|
if (yield* isServiceDown()) {
|
|
1372
1410
|
// Retryable → failJob(retries - 1) with a re-activation backoff.
|
|
1373
1411
|
return yield* Effect.fail(
|
|
1374
|
-
new RetryableJobError({
|
|
1412
|
+
new RetryableJobError({
|
|
1413
|
+
message: 'downstream unavailable',
|
|
1414
|
+
retryBackoff: '5 seconds',
|
|
1415
|
+
})
|
|
1375
1416
|
);
|
|
1376
1417
|
}
|
|
1377
1418
|
return { ok: true }; // success → completeJob(variables)
|
|
@@ -1402,6 +1443,85 @@ Worker exports from `.../effect`:
|
|
|
1402
1443
|
`Clock`, so `TestClock.adjust` bounds activation/retry timing in virtual time — the whole loop is
|
|
1403
1444
|
deterministic in tests, with no real-clock burn.
|
|
1404
1445
|
|
|
1446
|
+
### Injecting Services into a Handler
|
|
1447
|
+
|
|
1448
|
+
A handler is `(job) => Effect.Effect<A, JobError, R>`, and `R` — whatever services the handler
|
|
1449
|
+
depends on — is threaded out through `createCamundaEffectWorker` / `workerLayer` into the worker's
|
|
1450
|
+
own requirements. So a handler's dependencies are provided, and swapped for mocks, exactly like
|
|
1451
|
+
any other `Layer`.
|
|
1452
|
+
|
|
1453
|
+
<!-- snippet-source: examples/effect.ts,examples/readme-imports.txt | regions: ReadmeEffectWorkerServicesImport+ReadmeEffectWorkerServices -->
|
|
1454
|
+
```ts
|
|
1455
|
+
import { Context, Effect, Layer } from 'effect';
|
|
1456
|
+
import {
|
|
1457
|
+
CamundaEffect,
|
|
1458
|
+
type CamundaEffectClient,
|
|
1459
|
+
layer,
|
|
1460
|
+
workerLayer,
|
|
1461
|
+
} from '@camunda8/orchestration-cluster-api/effect';
|
|
1462
|
+
|
|
1463
|
+
// A service the handler depends on. Nothing about it is Camunda-specific — it is an
|
|
1464
|
+
// ordinary Effect service.
|
|
1465
|
+
class PaymentGateway extends Context.Service<
|
|
1466
|
+
PaymentGateway,
|
|
1467
|
+
{ readonly charge: (amount: number) => Effect.Effect<string> }
|
|
1468
|
+
>()('PaymentGateway') {}
|
|
1469
|
+
|
|
1470
|
+
// The handler's requirements flow out through the worker's own requirements, so the
|
|
1471
|
+
// worker layer asks for `PaymentGateway` just like it asks for the Camunda client.
|
|
1472
|
+
const paymentWorker = workerLayer({
|
|
1473
|
+
type: 'payment-processing',
|
|
1474
|
+
handler: (job) =>
|
|
1475
|
+
Effect.gen(function* () {
|
|
1476
|
+
const gateway = yield* PaymentGateway;
|
|
1477
|
+
return { receipt: yield* gateway.charge(Number(job.variables.amount)) };
|
|
1478
|
+
}),
|
|
1479
|
+
});
|
|
1480
|
+
// paymentWorker: Layer<never, never, CamundaEffect | PaymentGateway>
|
|
1481
|
+
|
|
1482
|
+
// Production: the real gateway and a real client.
|
|
1483
|
+
const liveWorker = paymentWorker.pipe(
|
|
1484
|
+
Layer.provide(
|
|
1485
|
+
Layer.succeed(PaymentGateway, {
|
|
1486
|
+
charge: (amount) => Effect.succeed(`live-receipt-${amount}`),
|
|
1487
|
+
})
|
|
1488
|
+
),
|
|
1489
|
+
Layer.provide(layer())
|
|
1490
|
+
);
|
|
1491
|
+
|
|
1492
|
+
// Tests: the same worker with *both* dependencies swapped. `CamundaEffect` is a service
|
|
1493
|
+
// too, so the broker is mocked exactly like the gateway — the worker runs end-to-end
|
|
1494
|
+
// with neither a payment provider nor a broker.
|
|
1495
|
+
const fakeClient = {
|
|
1496
|
+
activateJobs: () => Effect.succeed({ jobs: [] }),
|
|
1497
|
+
completeJob: () => Effect.void,
|
|
1498
|
+
failJob: () => Effect.void,
|
|
1499
|
+
throwJobError: () => Effect.void,
|
|
1500
|
+
} as unknown as CamundaEffectClient;
|
|
1501
|
+
|
|
1502
|
+
const mockedWorker = paymentWorker.pipe(
|
|
1503
|
+
Layer.provide(Layer.succeed(PaymentGateway, { charge: () => Effect.succeed('mock-receipt') })),
|
|
1504
|
+
Layer.provide(Layer.succeed(CamundaEffect, fakeClient))
|
|
1505
|
+
);
|
|
1506
|
+
```
|
|
1507
|
+
|
|
1508
|
+
`Layer.succeed(CamundaEffect, fakeClient)` is what the SDK's own worker tests use; see
|
|
1509
|
+
[tests/effect-worker-di.test.ts](tests/effect-worker-di.test.ts) for worked examples that mock both
|
|
1510
|
+
dependencies and drive the loop to a `completeJob` / `failJob` under `TestClock`.
|
|
1511
|
+
|
|
1512
|
+
> **Gotcha — let both type parameters infer.** `createCamundaEffectWorker<A, R>` has `R = never`
|
|
1513
|
+
> as its default, and TypeScript does not infer a type parameter when only *some* are supplied.
|
|
1514
|
+
> So `createCamundaEffectWorker<{ ok: boolean }>({ ... })` pins `R` to `never`, and a handler with
|
|
1515
|
+
> dependencies fails to compile with an error pointing at the handler rather than at the missing
|
|
1516
|
+
> type argument:
|
|
1517
|
+
>
|
|
1518
|
+
> ```
|
|
1519
|
+
> Type 'PaymentGateway' is not assignable to type 'never'.
|
|
1520
|
+
> ```
|
|
1521
|
+
>
|
|
1522
|
+
> Omit both — `A` is inferred from the handler's success value — or supply both
|
|
1523
|
+
> (`createCamundaEffectWorker<{ receipt: string }, PaymentGateway>({ ... })`).
|
|
1524
|
+
|
|
1405
1525
|
## Eventual Consistency Polling
|
|
1406
1526
|
|
|
1407
1527
|
Some endpoints accept consistency management options. Pass a `consistency` block (where supported) with `waitUpToMs` and optional `pollIntervalMs` (default 500). If the condition is not met within timeout an `EventualConsistencyTimeoutError` is thrown.
|