@camunda8/orchestration-cluster-api 10.0.0-alpha.30 → 10.0.0-alpha.31

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 CHANGED
@@ -1,3 +1,17 @@
1
+ # [10.0.0-alpha.31](https://github.com/camunda/orchestration-cluster-api-js/compare/v10.0.0-alpha.30...v10.0.0-alpha.31) (2026-08-20)
2
+
3
+
4
+ ### Features
5
+
6
+ * **effect:** first-class Effect v4 surface, clean cut from experimental /fp ([#440](https://github.com/camunda/orchestration-cluster-api-js/issues/440)) ([04d2bd1](https://github.com/camunda/orchestration-cluster-api-js/commit/04d2bd1452e9196fb337deb2ecf50b3c5d179bf4)), closes [#437](https://github.com/camunda/orchestration-cluster-api-js/issues/437) [#437](https://github.com/camunda/orchestration-cluster-api-js/issues/437)
7
+
8
+
9
+ ### BREAKING CHANGES
10
+
11
+ * **effect:** the experimental `/fp` subpath and its `fp-ts` optional peer are
12
+ removed. Migrate to `./effect` and install the optional `effect` (v4) peer; the
13
+ main `.` entry and its Promise-based API are unchanged.
14
+
1
15
  # [10.0.0-alpha.30](https://github.com/camunda/orchestration-cluster-api-js/compare/v10.0.0-alpha.29...v10.0.0-alpha.30) (2026-08-20)
2
16
 
3
17
 
package/README.md CHANGED
@@ -1250,67 +1250,92 @@ Notes:
1250
1250
  - Cancellation classification runs first so aborted fetches are never downgraded to generic network errors.
1251
1251
  - Abort is immediate and idempotent; underlying fetch is signalled.
1252
1252
 
1253
- ## Functional (fp-ts style) Surface (Opt-In Subpath)
1253
+ ## Effect Surface (Opt-In Subpath)
1254
1254
 
1255
- @experimental - this feature is not guaranteed to be tested or stable.
1255
+ The main entry stays Promise-based and pulls in **zero** Effect at runtime. Opt in to a
1256
+ first-class [Effect](https://effect.website) surface — a client whose every method returns an
1257
+ `Effect`, tagged domain errors, and Effect-native combinators — by importing the dedicated
1258
+ `./effect` subpath.
1256
1259
 
1257
- > **Peer dependency:** `fp-ts` is an optional peer dependency. If you use real `fp-ts` functions
1258
- > (e.g. `pipe`, `TE.match`) alongside this subpath, install it separately:
1260
+ > **Peer dependency:** `effect` is an **optional peer dependency** (Effect **v4**). The `./effect`
1261
+ > subpath requires it; install it alongside the SDK:
1259
1262
  > ```sh
1260
- > npm install fp-ts
1263
+ > npm install effect
1261
1264
  > ```
1262
- > The `/fp` subpath works without `fp-ts` installed it exposes structurally-compatible
1263
- > `Either`/`TaskEither` shapes that interoperate with `fp-ts` but do not require it at runtime.
1264
-
1265
- The main entry stays minimal. To opt in to a TaskEither-style facade & helper combinators import from the dedicated subpath:
1265
+ > The main `.` entry never imports `effect`, so Promise-first users are never forced to adopt it.
1266
+ >
1267
+ > **Module resolution:** Effect v4 ships as an `exports`-map-only package (no legacy
1268
+ > `main`/`types`), so consuming the `./effect` types requires a modern TypeScript module
1269
+ > resolution — set `"moduleResolution": "bundler"` (or `"node16"`/`"nodenext"`) in your
1270
+ > `tsconfig.json`. The Promise-first `.` entry is unaffected.
1266
1271
 
1267
- <!-- snippet-exempt: uses SDK /fp subpath not available in examples project -->
1272
+ <!-- snippet-exempt: uses SDK /effect subpath + optional effect peer not available in examples project -->
1268
1273
  ```ts
1274
+ import { Effect } from 'effect';
1269
1275
  import {
1270
- createCamundaFpClient,
1271
- retryTE,
1272
- withTimeoutTE,
1273
- eventuallyTE,
1274
- isLeft,
1275
- } from '@camunda8/orchestration-cluster-api/fp';
1276
-
1277
- const fp = createCamundaFpClient();
1278
- const deployTE = fp.deployResourcesFromFiles(['./bpmn/process.bpmn']);
1279
- const deployed = await deployTE();
1280
- if (isLeft(deployed)) throw deployed.left; // DomainError union
1276
+ createCamundaEffectClient,
1277
+ eventually,
1278
+ EventualConsistencyTimeout,
1279
+ } from '@camunda8/orchestration-cluster-api/effect';
1280
+
1281
+ const camunda = createCamundaEffectClient();
1282
+
1283
+ const program = Effect.gen(function* () {
1284
+ const deployment = yield* camunda.deployResourcesFromFiles(['./bpmn/process.bpmn']);
1285
+ const { processInstanceKey } = yield* camunda.createProcessInstance({
1286
+ processDefinitionKey: deployment.processes[0].processDefinitionKey,
1287
+ });
1288
+ // Poll on the Effect Clock until the instance is searchable, timing out deterministically.
1289
+ // waitUpToMs: 0 asks the SDK for the latest available state without its own wall-clock
1290
+ // wait, so the Effect `eventually` combinator owns the predicate + timeout horizon —
1291
+ // making the eventual-consistency wait deterministic under TestClock.
1292
+ const search = yield* eventually(
1293
+ camunda.searchProcessInstances(
1294
+ { filter: { processInstanceKey } },
1295
+ { consistency: { waitUpToMs: 0 } }
1296
+ ),
1297
+ (s) => s.items.some((i) => i.processInstanceKey === processInstanceKey),
1298
+ { waitUpTo: '30 seconds', interval: '750 millis' }
1299
+ );
1300
+ return { processInstanceKey, search };
1301
+ }).pipe(
1302
+ // Tagged errors → discriminate with catchTag / catchTags instead of a manual switch.
1303
+ Effect.catchTag('EventualConsistencyTimeout', (e: EventualConsistencyTimeout) =>
1304
+ Effect.logError(`Timed out: ${e.message}`).pipe(Effect.andThen(Effect.fail(e)))
1305
+ )
1306
+ );
1281
1307
 
1282
- // Chain with fp-ts (optional) – the returned thunks are structurally compatible with TaskEither
1283
- // import { pipe } from 'fp-ts/function'; import * as TE from 'fp-ts/TaskEither';
1308
+ const result = await Effect.runPromise(program);
1284
1309
  ```
1285
1310
 
1286
1311
  Why a subpath?
1287
1312
 
1288
- - Keeps base bundle lean for the 80% use case.
1289
- - No hard dependency on `fp-ts` at runtime; only structural types.
1290
- - Advanced users can compose with real `fp-ts` without pulling the effect model into the default import path.
1313
+ - Keeps the base bundle lean for the Promise-first 80% use case.
1314
+ - No dependency on `effect` at runtime unless you opt in; it is an **optional** peer.
1315
+ - Unlocks the Effect ecosystem (typed errors, `Schedule`, `Layer`/`Context`, `TestClock`).
1291
1316
 
1292
- Exports available from `.../fp`:
1317
+ Exports available from `.../effect`:
1293
1318
 
1294
- - `createCamundaFpClient` – typed facade (methods return `() => Promise<Either<DomainError,T>>`).
1295
- - Type guards: `isLeft`, `isRight`.
1296
- - Error / type aliases: `DomainError`, `TaskEither`, `Either`, `Left`, `Right`, `Fpify`.
1297
- - Combinators: `retryTE`, `withTimeoutTE`, `eventuallyTE`.
1298
-
1299
- DomainError union currently includes:
1300
-
1301
- - `CamundaValidationError`
1302
- - `EventualConsistencyTimeoutError`
1303
- - HTTP-like error objects (status/body/message) produced by transport
1304
- - Generic `Error`
1319
+ - `createCamundaEffectClient(options?)` – a `Proxy` client where every method returns
1320
+ `Effect.Effect<Awaited<R>, DomainError, never>`; the throwing client is reachable via `.inner`.
1321
+ - Tagged errors (`Data.TaggedError`): `CamundaValidationError`, `EventualConsistencyTimeout`,
1322
+ `HttpError`, `CamundaGenericError` — together the `DomainError` union. Discriminate with
1323
+ `Effect.catchTag` / `Effect.catchTags`.
1324
+ - Combinators: `retryWithBackoff` (`Effect.retry` + `Schedule.exponential` + jitter), `withTimeout`
1325
+ (`Effect.timeoutOrElse` with real interruption), `eventually` (a recursive `Effect.sleep` poll on
1326
+ the Effect `Clock`, timing out to `EventualConsistencyTimeout`).
1327
+ - Dependency injection: `CamundaEffect` (`Context.Service`) + `layer(options?)` (`Layer`) so worker /
1328
+ orchestration code composes via `Layer` and swaps a test double trivially.
1305
1329
 
1306
- You can refine left-channel typing later by mapping HTTP status codes or discriminator fields.
1330
+ **Clock-class win:** `eventually` / `withTimeout` run on the Effect `Clock`, so `TestClock.adjust`
1331
+ advances eventual/timeout deterministically in tests — no real-clock burn.
1307
1332
 
1308
1333
  ## Eventual Consistency Polling
1309
1334
 
1310
1335
  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.
1311
1336
 
1312
1337
  To consume eventual polling in a non‑throwing fashion set the client error mode before invoking an eventually consistent method:
1313
- At present the canonical client operates in throwing mode. Non‑throwing adaptation (Result / fp-ts) is achieved via the functional wrappers rather than mutating the base client.
1338
+ At present the canonical client operates in throwing mode. Non‑throwing adaptation (Result / Effect) is achieved via the functional wrappers rather than mutating the base client.
1314
1339
 
1315
1340
  ### Options
1316
1341
 
@@ -1589,43 +1614,32 @@ When to use:
1589
1614
  - Avoiding try/catch nesting in larger orchestration flows.
1590
1615
  - Converting to libraries expecting an Either/Result pattern.
1591
1616
 
1592
- ### fp-ts Adapter (TaskEither / Either) - EXPERIMENTAL
1617
+ ### Effect Adapter
1593
1618
 
1594
- _Note that this feature is experimental and subject to change._
1595
-
1596
- For projects using `fp-ts`, wrap the throwing client in a lazy `TaskEither` facade:
1619
+ For Effect-based projects, wrap the throwing client in an Effect-flavoured facade whose every method
1620
+ returns an `Effect` with a typed `DomainError` channel:
1597
1621
 
1598
- <!-- snippet-exempt: requires external fp-ts dependency -->
1622
+ <!-- snippet-exempt: requires optional effect peer dependency -->
1599
1623
  ```ts
1600
- import { createCamundaFpClient } from '@camunda8/orchestration-cluster-api/fp';
1601
- import { pipe } from 'fp-ts/function';
1602
- import * as TE from 'fp-ts/TaskEither';
1624
+ import { Effect } from 'effect';
1625
+ import { createCamundaEffectClient } from '@camunda8/orchestration-cluster-api/effect';
1603
1626
 
1604
- const fp = createCamundaFpClient();
1627
+ const camunda = createCamundaEffectClient();
1605
1628
 
1606
- const deployTE = fp.createDeployment({ resources: [file] }); // TaskEither<unknown, ExtendedDeploymentResult>
1607
-
1608
- pipe(
1609
- deployTE(), // invoke the task (returns Promise<Either>)
1610
- (then) => then // typical usage would use TE.match / TE.fold; shown expanded for clarity
1629
+ const deployment = await Effect.runPromise(
1630
+ camunda.createDeployment({ resources: [file] })
1611
1631
  );
1612
-
1613
- // With helpers
1614
- const task = fp.createDeployment({ resources: [file] });
1615
- const either = await task();
1616
- if (either._tag === 'Right') {
1617
- console.log(either.right.deployments.length);
1618
- } else {
1619
- console.error('Error', either.left);
1620
- }
1632
+ console.log(deployment.deployments.length);
1621
1633
  ```
1622
1634
 
1635
+ See [Effect Surface (Opt-In Subpath)](#effect-surface-opt-in-subpath) above for the full surface —
1636
+ tagged errors, `retryWithBackoff` / `withTimeout` / `eventually`, and `Layer`/`Context` DI.
1637
+
1623
1638
  Notes:
1624
1639
 
1625
- - No runtime dependency on `fp-ts`; adapter implements a minimal `Either` shape. Structural typing lets you lift into real `fp-ts` functions (`fromEither`, etc.).
1626
- - Each method becomes a function returning `() => Promise<Either<E,A>>` (a `TaskEither` shape). Invoke it later to execute.
1627
- - Cancellation: calling `.cancel()` on the original promise isn’t surfaced; if you need cancellation use the base client directly.
1628
- - For richer interop, you can map the returned factory to `TE.tryCatch` in userland.
1640
+ - `effect` is an **optional peer dependency**; only the `./effect` subpath imports it.
1641
+ - Each method returns `Effect.Effect<Awaited<R>, DomainError, never>`; the throwing client is reachable via `.inner`.
1642
+ - Failures are narrowed into tagged errors so you discriminate with `Effect.catchTag` / `catchTags`.
1629
1643
 
1630
1644
  ## Pagination
1631
1645
 
@@ -1748,7 +1762,7 @@ Generate an HTML API reference site with TypeDoc (public entry points only):
1748
1762
  npm run docs:api
1749
1763
  ```
1750
1764
 
1751
- Output: static site in `docs/api` (open `docs/api/index.html` in a browser or serve the folder, e.g. `npx http-server docs/api`). Entry points: `src/index.ts`, `src/logger.ts`, `src/fp/index.ts`. Internal generated code, scripts, tests are excluded and private / protected members are filtered. Regenerate after changing public exports.
1765
+ Output: static site in `docs/api` (open `docs/api/index.html` in a browser or serve the folder, e.g. `npx http-server docs/api`). Entry points: `src/index.ts`, `src/logger.ts`, `src/effect/index.ts`. Internal generated code, scripts, tests are excluded and private / protected members are filtered. Regenerate after changing public exports.
1752
1766
 
1753
1767
  ## Contributing
1754
1768