@camunda8/orchestration-cluster-api 10.0.0-alpha.3 → 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 +242 -0
- package/README.md +160 -69
- package/dist/{index-CtFmBFXM.d.ts → CamundaClient-D95Qr9pC.d.ts} +19081 -11059
- package/dist/{index-CvA10E3U.d.cts → CamundaClient-esvUSLtB.d.cts} +19081 -11059
- package/dist/{chunk-JP44BUAM.js → chunk-M5UHGQJ3.js} +11314 -7358
- package/dist/chunk-M5UHGQJ3.js.map +1 -0
- package/dist/{fp → effect}/index.cjs +17343 -12448
- package/dist/effect/index.cjs.map +1 -0
- package/dist/effect/index.d.cts +119 -0
- package/dist/effect/index.d.ts +119 -0
- package/dist/effect/index.js +155 -0
- package/dist/effect/index.js.map +1 -0
- package/dist/index.cjs +17559 -12419
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +85 -21
- package/dist/index.d.ts +85 -21
- package/dist/index.js +308 -15
- package/dist/index.js.map +1 -1
- package/dist/zod.gen-CGU4FBRF.js +9184 -0
- package/dist/zod.gen-CGU4FBRF.js.map +1 -0
- package/package.json +43 -26
- package/dist/chunk-JP44BUAM.js.map +0 -1
- package/dist/fp/index.cjs.map +0 -1
- package/dist/fp/index.d.cts +0 -4
- package/dist/fp/index.d.ts +0 -4
- package/dist/fp/index.js +0 -23
- package/dist/fp/index.js.map +0 -1
- package/dist/zod.gen-WZT74U4Q.js +0 -8387
- package/dist/zod.gen-WZT74U4Q.js.map +0 -1
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as effect_Cause from 'effect/Cause';
|
|
2
|
+
import * as effect_Types from 'effect/Types';
|
|
3
|
+
import { Context, Effect, Duration, Layer } from 'effect';
|
|
4
|
+
import { C as CamundaClient, a as CamundaOptions } from '../CamundaClient-esvUSLtB.cjs';
|
|
5
|
+
import 'zod';
|
|
6
|
+
import '../logger-D-p21VHo.cjs';
|
|
7
|
+
|
|
8
|
+
declare const CamundaValidationError_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => effect_Cause.YieldableError & {
|
|
9
|
+
readonly _tag: "CamundaValidationError";
|
|
10
|
+
} & Readonly<A>;
|
|
11
|
+
/** A request/response validation failure surfaced by the SDK. */
|
|
12
|
+
declare class CamundaValidationError extends CamundaValidationError_base<{
|
|
13
|
+
readonly side: 'request' | 'response';
|
|
14
|
+
readonly operationId?: string;
|
|
15
|
+
readonly summary: string;
|
|
16
|
+
readonly issues: string[];
|
|
17
|
+
readonly message: string;
|
|
18
|
+
readonly cause?: unknown;
|
|
19
|
+
}> {
|
|
20
|
+
}
|
|
21
|
+
declare const EventualConsistencyTimeout_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => effect_Cause.YieldableError & {
|
|
22
|
+
readonly _tag: "EventualConsistencyTimeout";
|
|
23
|
+
} & Readonly<A>;
|
|
24
|
+
/** An eventual-consistency poll that did not converge within its budget. */
|
|
25
|
+
declare class EventualConsistencyTimeout extends EventualConsistencyTimeout_base<{
|
|
26
|
+
readonly attempts?: number;
|
|
27
|
+
readonly elapsedMs?: number;
|
|
28
|
+
readonly message: string;
|
|
29
|
+
readonly cause?: unknown;
|
|
30
|
+
}> {
|
|
31
|
+
}
|
|
32
|
+
declare const HttpError_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => effect_Cause.YieldableError & {
|
|
33
|
+
readonly _tag: "HttpError";
|
|
34
|
+
} & Readonly<A>;
|
|
35
|
+
/** An HTTP-level failure (non-2xx / transport carrying a status). */
|
|
36
|
+
declare class HttpError extends HttpError_base<{
|
|
37
|
+
readonly status?: number;
|
|
38
|
+
readonly body?: unknown;
|
|
39
|
+
readonly message: string;
|
|
40
|
+
readonly cause?: unknown;
|
|
41
|
+
}> {
|
|
42
|
+
}
|
|
43
|
+
declare const CamundaGenericError_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => effect_Cause.YieldableError & {
|
|
44
|
+
readonly _tag: "CamundaGenericError";
|
|
45
|
+
} & Readonly<A>;
|
|
46
|
+
/** Any other thrown value that does not map to a more specific tag. */
|
|
47
|
+
declare class CamundaGenericError extends CamundaGenericError_base<{
|
|
48
|
+
readonly message: string;
|
|
49
|
+
readonly cause?: unknown;
|
|
50
|
+
}> {
|
|
51
|
+
}
|
|
52
|
+
/** The typed error channel for every Effect the client produces. */
|
|
53
|
+
type DomainError = CamundaValidationError | EventualConsistencyTimeout | HttpError | CamundaGenericError;
|
|
54
|
+
/** The tag literals of the {@link DomainError} union. */
|
|
55
|
+
type DomainErrorTag = DomainError['_tag'];
|
|
56
|
+
/** Keys of `C` whose values are callable. */
|
|
57
|
+
type FnKeys<C> = {
|
|
58
|
+
[K in keyof C]: C[K] extends (...a: any) => any ? K : never;
|
|
59
|
+
}[keyof C];
|
|
60
|
+
/** Maps every method of `C` to an Effect-returning method, preserving non-fn members. */
|
|
61
|
+
type Effectify<C> = {
|
|
62
|
+
[K in FnKeys<C>]: C[K] extends (...a: infer A) => infer R ? (...a: A) => Effect.Effect<Awaited<R>, DomainError, never> : never;
|
|
63
|
+
} & {
|
|
64
|
+
inner: C;
|
|
65
|
+
} & {
|
|
66
|
+
[K in Exclude<keyof C, FnKeys<C>>]: C[K];
|
|
67
|
+
};
|
|
68
|
+
/** The Effect-flavoured Camunda client. Every operation returns an `Effect`. */
|
|
69
|
+
type CamundaEffectClient = Effectify<CamundaClient>;
|
|
70
|
+
/**
|
|
71
|
+
* Create an Effect-flavoured Camunda client.
|
|
72
|
+
*
|
|
73
|
+
* Every `CamundaClient` method becomes `(...args) => Effect.Effect<Awaited<R>,
|
|
74
|
+
* DomainError, never>`. Failures are narrowed into the tagged {@link DomainError}
|
|
75
|
+
* union so callers use `Effect.catchTag`/`catchTags`. The underlying throwing
|
|
76
|
+
* client is reachable via the `.inner` escape hatch.
|
|
77
|
+
*
|
|
78
|
+
* @description Camunda Effect Client. See the README and [this test](https://github.com/camunda/orchestration-cluster-api-js/blob/main/tests-integration/effect.test.ts) for example usage.
|
|
79
|
+
*/
|
|
80
|
+
declare function createCamundaEffectClient(options?: CamundaOptions): CamundaEffectClient;
|
|
81
|
+
/**
|
|
82
|
+
* Retry an effect with exponential backoff (+ jitter), capped attempts, and an
|
|
83
|
+
* optional predicate over the error.
|
|
84
|
+
*/
|
|
85
|
+
declare function retryWithBackoff<A, E, R>(effect: Effect.Effect<A, E, R>, opts: {
|
|
86
|
+
max: number;
|
|
87
|
+
baseDelay?: Duration.Input;
|
|
88
|
+
while?: (e: E) => boolean;
|
|
89
|
+
}): Effect.Effect<A, E, R>;
|
|
90
|
+
/**
|
|
91
|
+
* Fail an effect with a real interruption if it does not settle within `duration`
|
|
92
|
+
* (true interruption, not a best-effort `Promise.race`).
|
|
93
|
+
*/
|
|
94
|
+
declare function withTimeout<A, E, R>(effect: Effect.Effect<A, E, R>, duration: Duration.Input, onTimeout?: () => E | EventualConsistencyTimeout): Effect.Effect<A, E | EventualConsistencyTimeout, R>;
|
|
95
|
+
/**
|
|
96
|
+
* Poll `effect` on the Effect `Clock` until `predicate` holds, timing out to
|
|
97
|
+
* {@link EventualConsistencyTimeout} once `waitUpTo` elapses. Because it uses the
|
|
98
|
+
* Effect `Clock` (not `Date.now`/`setTimeout`), `TestClock.adjust` advances it
|
|
99
|
+
* deterministically in tests — no real-clock burn.
|
|
100
|
+
*/
|
|
101
|
+
declare function eventually<A, E, R>(effect: Effect.Effect<A, E, R>, predicate: (a: A) => boolean, opts: {
|
|
102
|
+
waitUpTo: Duration.Input;
|
|
103
|
+
interval?: Duration.Input;
|
|
104
|
+
}): Effect.Effect<A, E | EventualConsistencyTimeout, R>;
|
|
105
|
+
declare const CamundaEffect_base: Context.ServiceClass<CamundaEffect, "CamundaEffect", CamundaEffectClient>;
|
|
106
|
+
/**
|
|
107
|
+
* `Context` service key for the Effect Camunda client. Compose worker/orchestration
|
|
108
|
+
* code against this tag and provide {@link layer} (or a test double) via `Layer`.
|
|
109
|
+
*/
|
|
110
|
+
declare class CamundaEffect extends CamundaEffect_base {
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* A `Layer` that constructs a {@link CamundaEffectClient} and provides it as the
|
|
114
|
+
* {@link CamundaEffect} service. Swap in a test double by providing a different
|
|
115
|
+
* `Layer` for the same tag.
|
|
116
|
+
*/
|
|
117
|
+
declare function layer(options?: CamundaOptions): Layer.Layer<CamundaEffect>;
|
|
118
|
+
|
|
119
|
+
export { CamundaEffect, type CamundaEffectClient, CamundaGenericError, CamundaValidationError, type DomainError, type DomainErrorTag, type Effectify, EventualConsistencyTimeout, type FnKeys, HttpError, createCamundaEffectClient, eventually, layer, retryWithBackoff, withTimeout };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as effect_Cause from 'effect/Cause';
|
|
2
|
+
import * as effect_Types from 'effect/Types';
|
|
3
|
+
import { Context, Effect, Duration, Layer } from 'effect';
|
|
4
|
+
import { C as CamundaClient, a as CamundaOptions } from '../CamundaClient-D95Qr9pC.js';
|
|
5
|
+
import 'zod';
|
|
6
|
+
import '../logger-D-p21VHo.js';
|
|
7
|
+
|
|
8
|
+
declare const CamundaValidationError_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => effect_Cause.YieldableError & {
|
|
9
|
+
readonly _tag: "CamundaValidationError";
|
|
10
|
+
} & Readonly<A>;
|
|
11
|
+
/** A request/response validation failure surfaced by the SDK. */
|
|
12
|
+
declare class CamundaValidationError extends CamundaValidationError_base<{
|
|
13
|
+
readonly side: 'request' | 'response';
|
|
14
|
+
readonly operationId?: string;
|
|
15
|
+
readonly summary: string;
|
|
16
|
+
readonly issues: string[];
|
|
17
|
+
readonly message: string;
|
|
18
|
+
readonly cause?: unknown;
|
|
19
|
+
}> {
|
|
20
|
+
}
|
|
21
|
+
declare const EventualConsistencyTimeout_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => effect_Cause.YieldableError & {
|
|
22
|
+
readonly _tag: "EventualConsistencyTimeout";
|
|
23
|
+
} & Readonly<A>;
|
|
24
|
+
/** An eventual-consistency poll that did not converge within its budget. */
|
|
25
|
+
declare class EventualConsistencyTimeout extends EventualConsistencyTimeout_base<{
|
|
26
|
+
readonly attempts?: number;
|
|
27
|
+
readonly elapsedMs?: number;
|
|
28
|
+
readonly message: string;
|
|
29
|
+
readonly cause?: unknown;
|
|
30
|
+
}> {
|
|
31
|
+
}
|
|
32
|
+
declare const HttpError_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => effect_Cause.YieldableError & {
|
|
33
|
+
readonly _tag: "HttpError";
|
|
34
|
+
} & Readonly<A>;
|
|
35
|
+
/** An HTTP-level failure (non-2xx / transport carrying a status). */
|
|
36
|
+
declare class HttpError extends HttpError_base<{
|
|
37
|
+
readonly status?: number;
|
|
38
|
+
readonly body?: unknown;
|
|
39
|
+
readonly message: string;
|
|
40
|
+
readonly cause?: unknown;
|
|
41
|
+
}> {
|
|
42
|
+
}
|
|
43
|
+
declare const CamundaGenericError_base: new <A extends Record<string, any> = {}>(args: effect_Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => effect_Cause.YieldableError & {
|
|
44
|
+
readonly _tag: "CamundaGenericError";
|
|
45
|
+
} & Readonly<A>;
|
|
46
|
+
/** Any other thrown value that does not map to a more specific tag. */
|
|
47
|
+
declare class CamundaGenericError extends CamundaGenericError_base<{
|
|
48
|
+
readonly message: string;
|
|
49
|
+
readonly cause?: unknown;
|
|
50
|
+
}> {
|
|
51
|
+
}
|
|
52
|
+
/** The typed error channel for every Effect the client produces. */
|
|
53
|
+
type DomainError = CamundaValidationError | EventualConsistencyTimeout | HttpError | CamundaGenericError;
|
|
54
|
+
/** The tag literals of the {@link DomainError} union. */
|
|
55
|
+
type DomainErrorTag = DomainError['_tag'];
|
|
56
|
+
/** Keys of `C` whose values are callable. */
|
|
57
|
+
type FnKeys<C> = {
|
|
58
|
+
[K in keyof C]: C[K] extends (...a: any) => any ? K : never;
|
|
59
|
+
}[keyof C];
|
|
60
|
+
/** Maps every method of `C` to an Effect-returning method, preserving non-fn members. */
|
|
61
|
+
type Effectify<C> = {
|
|
62
|
+
[K in FnKeys<C>]: C[K] extends (...a: infer A) => infer R ? (...a: A) => Effect.Effect<Awaited<R>, DomainError, never> : never;
|
|
63
|
+
} & {
|
|
64
|
+
inner: C;
|
|
65
|
+
} & {
|
|
66
|
+
[K in Exclude<keyof C, FnKeys<C>>]: C[K];
|
|
67
|
+
};
|
|
68
|
+
/** The Effect-flavoured Camunda client. Every operation returns an `Effect`. */
|
|
69
|
+
type CamundaEffectClient = Effectify<CamundaClient>;
|
|
70
|
+
/**
|
|
71
|
+
* Create an Effect-flavoured Camunda client.
|
|
72
|
+
*
|
|
73
|
+
* Every `CamundaClient` method becomes `(...args) => Effect.Effect<Awaited<R>,
|
|
74
|
+
* DomainError, never>`. Failures are narrowed into the tagged {@link DomainError}
|
|
75
|
+
* union so callers use `Effect.catchTag`/`catchTags`. The underlying throwing
|
|
76
|
+
* client is reachable via the `.inner` escape hatch.
|
|
77
|
+
*
|
|
78
|
+
* @description Camunda Effect Client. See the README and [this test](https://github.com/camunda/orchestration-cluster-api-js/blob/main/tests-integration/effect.test.ts) for example usage.
|
|
79
|
+
*/
|
|
80
|
+
declare function createCamundaEffectClient(options?: CamundaOptions): CamundaEffectClient;
|
|
81
|
+
/**
|
|
82
|
+
* Retry an effect with exponential backoff (+ jitter), capped attempts, and an
|
|
83
|
+
* optional predicate over the error.
|
|
84
|
+
*/
|
|
85
|
+
declare function retryWithBackoff<A, E, R>(effect: Effect.Effect<A, E, R>, opts: {
|
|
86
|
+
max: number;
|
|
87
|
+
baseDelay?: Duration.Input;
|
|
88
|
+
while?: (e: E) => boolean;
|
|
89
|
+
}): Effect.Effect<A, E, R>;
|
|
90
|
+
/**
|
|
91
|
+
* Fail an effect with a real interruption if it does not settle within `duration`
|
|
92
|
+
* (true interruption, not a best-effort `Promise.race`).
|
|
93
|
+
*/
|
|
94
|
+
declare function withTimeout<A, E, R>(effect: Effect.Effect<A, E, R>, duration: Duration.Input, onTimeout?: () => E | EventualConsistencyTimeout): Effect.Effect<A, E | EventualConsistencyTimeout, R>;
|
|
95
|
+
/**
|
|
96
|
+
* Poll `effect` on the Effect `Clock` until `predicate` holds, timing out to
|
|
97
|
+
* {@link EventualConsistencyTimeout} once `waitUpTo` elapses. Because it uses the
|
|
98
|
+
* Effect `Clock` (not `Date.now`/`setTimeout`), `TestClock.adjust` advances it
|
|
99
|
+
* deterministically in tests — no real-clock burn.
|
|
100
|
+
*/
|
|
101
|
+
declare function eventually<A, E, R>(effect: Effect.Effect<A, E, R>, predicate: (a: A) => boolean, opts: {
|
|
102
|
+
waitUpTo: Duration.Input;
|
|
103
|
+
interval?: Duration.Input;
|
|
104
|
+
}): Effect.Effect<A, E | EventualConsistencyTimeout, R>;
|
|
105
|
+
declare const CamundaEffect_base: Context.ServiceClass<CamundaEffect, "CamundaEffect", CamundaEffectClient>;
|
|
106
|
+
/**
|
|
107
|
+
* `Context` service key for the Effect Camunda client. Compose worker/orchestration
|
|
108
|
+
* code against this tag and provide {@link layer} (or a test double) via `Layer`.
|
|
109
|
+
*/
|
|
110
|
+
declare class CamundaEffect extends CamundaEffect_base {
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* A `Layer` that constructs a {@link CamundaEffectClient} and provides it as the
|
|
114
|
+
* {@link CamundaEffect} service. Swap in a test double by providing a different
|
|
115
|
+
* `Layer` for the same tag.
|
|
116
|
+
*/
|
|
117
|
+
declare function layer(options?: CamundaOptions): Layer.Layer<CamundaEffect>;
|
|
118
|
+
|
|
119
|
+
export { CamundaEffect, type CamundaEffectClient, CamundaGenericError, CamundaValidationError, type DomainError, type DomainErrorTag, type Effectify, EventualConsistencyTimeout, type FnKeys, HttpError, createCamundaEffectClient, eventually, layer, retryWithBackoff, withTimeout };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CamundaValidationError,
|
|
3
|
+
EventualConsistencyTimeoutError,
|
|
4
|
+
createCamundaClient
|
|
5
|
+
} from "../chunk-M5UHGQJ3.js";
|
|
6
|
+
import "../chunk-KQ4UL2WX.js";
|
|
7
|
+
import "../chunk-DGUM43GV.js";
|
|
8
|
+
|
|
9
|
+
// src/effect.ts
|
|
10
|
+
import { Context, Data, Duration, Effect, Layer, Schedule } from "effect";
|
|
11
|
+
var CamundaValidationError2 = class extends Data.TaggedError("CamundaValidationError") {
|
|
12
|
+
};
|
|
13
|
+
var EventualConsistencyTimeout = class extends Data.TaggedError("EventualConsistencyTimeout") {
|
|
14
|
+
};
|
|
15
|
+
var HttpError = class extends Data.TaggedError("HttpError") {
|
|
16
|
+
};
|
|
17
|
+
var CamundaGenericError = class extends Data.TaggedError("CamundaGenericError") {
|
|
18
|
+
};
|
|
19
|
+
function toDomainError(err) {
|
|
20
|
+
if (err instanceof CamundaValidationError) {
|
|
21
|
+
return new CamundaValidationError2({
|
|
22
|
+
side: err.side,
|
|
23
|
+
operationId: err.operationId,
|
|
24
|
+
summary: err.summary,
|
|
25
|
+
issues: err.issues,
|
|
26
|
+
message: err.message,
|
|
27
|
+
cause: err
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (err instanceof EventualConsistencyTimeoutError) {
|
|
31
|
+
return new EventualConsistencyTimeout({
|
|
32
|
+
attempts: err.attempts,
|
|
33
|
+
elapsedMs: err.elapsedMs,
|
|
34
|
+
message: err.message,
|
|
35
|
+
cause: err
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const anyErr = err;
|
|
39
|
+
if (anyErr && typeof anyErr === "object" && typeof anyErr.status === "number") {
|
|
40
|
+
return new HttpError({
|
|
41
|
+
status: anyErr.status,
|
|
42
|
+
body: anyErr.body,
|
|
43
|
+
message: typeof anyErr.message === "string" ? anyErr.message : `HTTP ${anyErr.status}`,
|
|
44
|
+
cause: err
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (err instanceof Error) {
|
|
48
|
+
return new CamundaGenericError({ message: err.message, cause: err });
|
|
49
|
+
}
|
|
50
|
+
return new CamundaGenericError({ message: String(err), cause: err });
|
|
51
|
+
}
|
|
52
|
+
function isPromiseLike(v) {
|
|
53
|
+
return !!v && typeof v.then === "function";
|
|
54
|
+
}
|
|
55
|
+
function createCamundaEffectClient(options) {
|
|
56
|
+
const base = createCamundaClient(options);
|
|
57
|
+
const cache = /* @__PURE__ */ new Map();
|
|
58
|
+
function wrap(fn) {
|
|
59
|
+
return (...args) => Effect.suspend(() => {
|
|
60
|
+
let r;
|
|
61
|
+
try {
|
|
62
|
+
r = fn.apply(base, args);
|
|
63
|
+
} catch (e) {
|
|
64
|
+
return Effect.fail(toDomainError(e));
|
|
65
|
+
}
|
|
66
|
+
if (!isPromiseLike(r)) return Effect.succeed(r);
|
|
67
|
+
const promise = r;
|
|
68
|
+
return Effect.callback((resume) => {
|
|
69
|
+
promise.then(
|
|
70
|
+
(a) => resume(Effect.succeed(a)),
|
|
71
|
+
(e) => resume(Effect.fail(toDomainError(e)))
|
|
72
|
+
);
|
|
73
|
+
const cancel = r.cancel;
|
|
74
|
+
return typeof cancel === "function" ? Effect.sync(() => cancel.call(r)) : void 0;
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const handler = {
|
|
79
|
+
get(_t, prop) {
|
|
80
|
+
if (prop === "inner") return base;
|
|
81
|
+
if (cache.has(prop)) return cache.get(prop);
|
|
82
|
+
const value = base[prop];
|
|
83
|
+
if (typeof value === "function") {
|
|
84
|
+
const w = wrap(value);
|
|
85
|
+
cache.set(prop, w);
|
|
86
|
+
return w;
|
|
87
|
+
}
|
|
88
|
+
cache.set(prop, value);
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
return new Proxy({}, handler);
|
|
93
|
+
}
|
|
94
|
+
function formatDuration(d) {
|
|
95
|
+
try {
|
|
96
|
+
return Duration.format(Duration.fromInputUnsafe(d));
|
|
97
|
+
} catch {
|
|
98
|
+
return String(d);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function retryWithBackoff(effect, opts) {
|
|
102
|
+
const backoff = Schedule.exponential(opts.baseDelay ?? "100 millis").pipe(Schedule.jittered);
|
|
103
|
+
return Effect.retry(effect, {
|
|
104
|
+
schedule: backoff,
|
|
105
|
+
times: Math.max(0, opts.max - 1),
|
|
106
|
+
while: opts.while
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function withTimeout(effect, duration, onTimeout) {
|
|
110
|
+
return Effect.timeoutOrElse(effect, {
|
|
111
|
+
duration,
|
|
112
|
+
orElse: () => Effect.fail(
|
|
113
|
+
onTimeout ? onTimeout() : new EventualConsistencyTimeout({
|
|
114
|
+
message: `Timed out after ${formatDuration(duration)}`
|
|
115
|
+
})
|
|
116
|
+
)
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function eventually(effect, predicate, opts) {
|
|
120
|
+
const interval = opts.interval ?? "500 millis";
|
|
121
|
+
const poll = Effect.suspend(
|
|
122
|
+
() => effect.pipe(
|
|
123
|
+
Effect.flatMap(
|
|
124
|
+
(a) => predicate(a) ? Effect.succeed(a) : Effect.sleep(interval).pipe(Effect.andThen(poll))
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
);
|
|
128
|
+
return withTimeout(
|
|
129
|
+
poll,
|
|
130
|
+
opts.waitUpTo,
|
|
131
|
+
() => new EventualConsistencyTimeout({
|
|
132
|
+
message: `Eventual consistency timeout after ${formatDuration(opts.waitUpTo)}`
|
|
133
|
+
})
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
var CamundaEffect = class extends Context.Service()(
|
|
137
|
+
"CamundaEffect"
|
|
138
|
+
) {
|
|
139
|
+
};
|
|
140
|
+
function layer(options) {
|
|
141
|
+
return Layer.sync(CamundaEffect, () => createCamundaEffectClient(options));
|
|
142
|
+
}
|
|
143
|
+
export {
|
|
144
|
+
CamundaEffect,
|
|
145
|
+
CamundaGenericError,
|
|
146
|
+
CamundaValidationError2 as CamundaValidationError,
|
|
147
|
+
EventualConsistencyTimeout,
|
|
148
|
+
HttpError,
|
|
149
|
+
createCamundaEffectClient,
|
|
150
|
+
eventually,
|
|
151
|
+
layer,
|
|
152
|
+
retryWithBackoff,
|
|
153
|
+
withTimeout
|
|
154
|
+
};
|
|
155
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/effect.ts"],"sourcesContent":["// First-class Effect adapter for the Camunda client.\n//\n// Mirrors the Promise-based `CamundaClient` surface, mapping every method to an\n// `Effect.Effect<Awaited<R>, DomainError, never>` via the same `Proxy` +\n// method-memoization trick the former functional adapter used (it already handles\n// `CancelablePromise`/sync returns via `isPromiseLike`).\n//\n// `effect` is an OPTIONAL peer dependency: importing this module (the `./effect`\n// subpath) is the only way to pull `effect` into the runtime graph. The main `.`\n// entry never imports it, so Promise-first users are never forced to adopt Effect.\n\nimport { Context, Data, Duration, Effect, Layer, Schedule } from 'effect';\nimport { type CamundaClient, type CamundaOptions, createCamundaClient } from './gen/CamundaClient';\nimport {\n EventualConsistencyTimeoutError as RuntimeEventualConsistencyTimeoutError,\n CamundaValidationError as RuntimeValidationError,\n} from './runtime/errors';\n\n// --- Tagged domain errors -------------------------------------------------------\n//\n// Modeled as `Data.TaggedError` classes so callers discriminate with\n// `Effect.catchTag` / `Effect.catchTags` instead of a hand-rolled classification\n// switch. Each carries a stable `_tag` literal.\n\n/** A request/response validation failure surfaced by the SDK. */\nexport class CamundaValidationError extends Data.TaggedError('CamundaValidationError')<{\n readonly side: 'request' | 'response';\n readonly operationId?: string;\n readonly summary: string;\n readonly issues: string[];\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\n/** An eventual-consistency poll that did not converge within its budget. */\nexport class EventualConsistencyTimeout extends Data.TaggedError('EventualConsistencyTimeout')<{\n readonly attempts?: number;\n readonly elapsedMs?: number;\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\n/** An HTTP-level failure (non-2xx / transport carrying a status). */\nexport class HttpError extends Data.TaggedError('HttpError')<{\n readonly status?: number;\n readonly body?: unknown;\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\n/** Any other thrown value that does not map to a more specific tag. */\nexport class CamundaGenericError extends Data.TaggedError('CamundaGenericError')<{\n readonly message: string;\n readonly cause?: unknown;\n}> {}\n\n/** The typed error channel for every Effect the client produces. */\nexport type DomainError =\n | CamundaValidationError\n | EventualConsistencyTimeout\n | HttpError\n | CamundaGenericError;\n\n/** The tag literals of the {@link DomainError} union. */\nexport type DomainErrorTag = DomainError['_tag'];\n\n// Narrow an arbitrary thrown value into the tagged `DomainError` union.\nfunction toDomainError(err: unknown): DomainError {\n if (err instanceof RuntimeValidationError) {\n return new CamundaValidationError({\n side: err.side,\n operationId: err.operationId,\n summary: err.summary,\n issues: err.issues,\n message: err.message,\n cause: err,\n });\n }\n if (err instanceof RuntimeEventualConsistencyTimeoutError) {\n return new EventualConsistencyTimeout({\n attempts: err.attempts,\n elapsedMs: err.elapsedMs,\n message: err.message,\n cause: err,\n });\n }\n const anyErr = err as { status?: unknown; body?: unknown; message?: unknown } | null | undefined;\n if (anyErr && typeof anyErr === 'object' && typeof anyErr.status === 'number') {\n return new HttpError({\n status: anyErr.status,\n body: anyErr.body,\n message: typeof anyErr.message === 'string' ? anyErr.message : `HTTP ${anyErr.status}`,\n cause: err,\n });\n }\n if (err instanceof Error) {\n return new CamundaGenericError({ message: err.message, cause: err });\n }\n return new CamundaGenericError({ message: String(err), cause: err });\n}\n\n// --- Client shape ---------------------------------------------------------------\n\n/** Keys of `C` whose values are callable. */\nexport type FnKeys<C> = { [K in keyof C]: C[K] extends (...a: any) => any ? K : never }[keyof C];\n\n/** Maps every method of `C` to an Effect-returning method, preserving non-fn members. */\nexport type Effectify<C> = {\n [K in FnKeys<C>]: C[K] extends (...a: infer A) => infer R\n ? (...a: A) => Effect.Effect<Awaited<R>, DomainError, never>\n : never;\n} & { inner: C } & { [K in Exclude<keyof C, FnKeys<C>>]: C[K] };\n\n/** The Effect-flavoured Camunda client. Every operation returns an `Effect`. */\nexport type CamundaEffectClient = Effectify<CamundaClient>;\n\n// Runtime detection of promise-like (includes CancelablePromise).\nfunction isPromiseLike<T>(v: unknown): v is Promise<T> {\n return !!v && typeof (v as { then?: unknown }).then === 'function';\n}\n\n/**\n * Create an Effect-flavoured Camunda client.\n *\n * Every `CamundaClient` method becomes `(...args) => Effect.Effect<Awaited<R>,\n * DomainError, never>`. Failures are narrowed into the tagged {@link DomainError}\n * union so callers use `Effect.catchTag`/`catchTags`. The underlying throwing\n * client is reachable via the `.inner` escape hatch.\n *\n * @description Camunda Effect Client. See the README and [this test](https://github.com/camunda/orchestration-cluster-api-js/blob/main/tests-integration/effect.test.ts) for example usage.\n */\nexport function createCamundaEffectClient(options?: CamundaOptions): CamundaEffectClient {\n const base = createCamundaClient(options);\n const cache = new Map<string | symbol, unknown>();\n\n function wrap(fn: (...a: any[]) => any) {\n return (...args: any[]) =>\n Effect.suspend(() => {\n // The generated client can throw *synchronously* (e.g. an eventual\n // endpoint invoked without `consistencyManagement`). Map those to the\n // tagged error channel too, rather than letting them escape as a defect.\n let r: unknown;\n try {\n r = fn.apply(base, args);\n } catch (e) {\n return Effect.fail(toDomainError(e));\n }\n if (!isPromiseLike(r)) return Effect.succeed(r);\n const promise = r;\n return Effect.callback<any, DomainError>((resume) => {\n promise.then(\n (a: unknown) => resume(Effect.succeed(a)),\n (e: unknown) => resume(Effect.fail(toDomainError(e)))\n );\n // Honour Effect interruption: if the fiber is interrupted (e.g. by\n // `withTimeout` or a parent), cancel the underlying `CancelablePromise`\n // so the in-flight HTTP request is aborted instead of leaking.\n const cancel = (r as { cancel?: () => void }).cancel;\n return typeof cancel === 'function' ? Effect.sync(() => cancel.call(r)) : undefined;\n });\n });\n }\n\n const handler: ProxyHandler<any> = {\n get(_t, prop: string | symbol) {\n if (prop === 'inner') return base;\n if (cache.has(prop)) return cache.get(prop);\n const value = (base as any)[prop];\n if (typeof value === 'function') {\n const w = wrap(value);\n cache.set(prop, w);\n return w;\n }\n cache.set(prop, value);\n return value;\n },\n };\n\n return new Proxy({}, handler) as CamundaEffectClient;\n}\n\n// --- Combinators (Effect-native) ------------------------------------------------\n\n/**\n * Human-readable rendering of a `Duration.Input` for error messages. `String()`\n * degrades to `[object Object]` for non-string inputs (millis-numbers, `Duration`\n * values, `[seconds, nanos]` tuples); this normalises them via `Duration.format`.\n */\nfunction formatDuration(d: Duration.Input): string {\n // Only used to build error messages. `fromInputUnsafe` throws on a malformed\n // (but statically typed) Duration.Input, so guard it — a formatting failure\n // must never turn a typed timeout failure into an unexpected defect.\n try {\n return Duration.format(Duration.fromInputUnsafe(d));\n } catch {\n return String(d);\n }\n}\n\n/**\n * Retry an effect with exponential backoff (+ jitter), capped attempts, and an\n * optional predicate over the error.\n */\nexport function retryWithBackoff<A, E, R>(\n effect: Effect.Effect<A, E, R>,\n opts: {\n max: number;\n baseDelay?: Duration.Input;\n while?: (e: E) => boolean;\n }\n): Effect.Effect<A, E, R> {\n const backoff = Schedule.exponential(opts.baseDelay ?? '100 millis').pipe(Schedule.jittered);\n // `times: n` caps at n retries after the initial attempt → n+1 total attempts.\n // (v4 dropped `Schedule.intersect`; the retry options object composes the\n // exponential-backoff schedule with the attempt cap and the error predicate.)\n return Effect.retry(effect, {\n schedule: backoff,\n times: Math.max(0, opts.max - 1),\n while: opts.while,\n });\n}\n\n/**\n * Fail an effect with a real interruption if it does not settle within `duration`\n * (true interruption, not a best-effort `Promise.race`).\n */\nexport function withTimeout<A, E, R>(\n effect: Effect.Effect<A, E, R>,\n duration: Duration.Input,\n onTimeout?: () => E | EventualConsistencyTimeout\n): Effect.Effect<A, E | EventualConsistencyTimeout, R> {\n return Effect.timeoutOrElse(effect, {\n duration,\n orElse: () =>\n Effect.fail(\n onTimeout\n ? onTimeout()\n : new EventualConsistencyTimeout({\n message: `Timed out after ${formatDuration(duration)}`,\n })\n ),\n });\n}\n\n/**\n * Poll `effect` on the Effect `Clock` until `predicate` holds, timing out to\n * {@link EventualConsistencyTimeout} once `waitUpTo` elapses. Because it uses the\n * Effect `Clock` (not `Date.now`/`setTimeout`), `TestClock.adjust` advances it\n * deterministically in tests — no real-clock burn.\n */\nexport function eventually<A, E, R>(\n effect: Effect.Effect<A, E, R>,\n predicate: (a: A) => boolean,\n opts: {\n waitUpTo: Duration.Input;\n interval?: Duration.Input;\n }\n): Effect.Effect<A, E | EventualConsistencyTimeout, R> {\n const interval = opts.interval ?? '500 millis';\n // Poll on the Effect `Clock` (Effect.sleep), returning the value A once the\n // predicate holds. Kept as an Effect-valued loop (not `Effect.repeat` with a\n // schedule, whose success channel is the schedule output, not A).\n const poll: Effect.Effect<A, E, R> = Effect.suspend(() =>\n effect.pipe(\n Effect.flatMap((a) =>\n predicate(a) ? Effect.succeed(a) : Effect.sleep(interval).pipe(Effect.andThen(poll))\n )\n )\n );\n return withTimeout(\n poll,\n opts.waitUpTo,\n () =>\n new EventualConsistencyTimeout({\n message: `Eventual consistency timeout after ${formatDuration(opts.waitUpTo)}`,\n })\n );\n}\n\n// --- Dependency injection -------------------------------------------------------\n\n/**\n * `Context` service key for the Effect Camunda client. Compose worker/orchestration\n * code against this tag and provide {@link layer} (or a test double) via `Layer`.\n */\nexport class CamundaEffect extends Context.Service<CamundaEffect, CamundaEffectClient>()(\n 'CamundaEffect'\n) {}\n\n/**\n * A `Layer` that constructs a {@link CamundaEffectClient} and provides it as the\n * {@link CamundaEffect} service. Swap in a test double by providing a different\n * `Layer` for the same tag.\n */\nexport function layer(options?: CamundaOptions): Layer.Layer<CamundaEffect> {\n return Layer.sync(CamundaEffect, () => createCamundaEffectClient(options));\n}\n"],"mappings":"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","names":["CamundaValidationError"]}
|