arvo-event-handler 3.0.25 → 3.0.27

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 (30) hide show
  1. package/dist/ArvoEventHandler/helpers.d.ts +0 -1
  2. package/dist/ArvoEventHandler/helpers.js +0 -1
  3. package/dist/ArvoEventHandler/index.js +6 -6
  4. package/dist/ArvoEventHandler/types.d.ts +1 -1
  5. package/dist/ArvoMachine/createMachine.d.ts +19 -10
  6. package/dist/ArvoOrchestrationUtils/handlerErrors.d.ts +3 -2
  7. package/dist/ArvoOrchestrationUtils/handlerErrors.js +5 -3
  8. package/dist/ArvoOrchestrationUtils/orchestrationExecutionState.d.ts +5 -1
  9. package/dist/ArvoOrchestrationUtils/orchestrationExecutionState.js +2 -0
  10. package/dist/ArvoOrchestrationUtils/orchestrationExecutionWrapper/acquireLockWithValidation.d.ts +2 -1
  11. package/dist/ArvoOrchestrationUtils/orchestrationExecutionWrapper/acquireLockWithValidation.js +2 -2
  12. package/dist/ArvoOrchestrationUtils/orchestrationExecutionWrapper/index.d.ts +2 -2
  13. package/dist/ArvoOrchestrationUtils/orchestrationExecutionWrapper/index.js +31 -15
  14. package/dist/ArvoOrchestrator/factory.d.ts +0 -1
  15. package/dist/ArvoOrchestrator/factory.js +4 -5
  16. package/dist/ArvoOrchestrator/index.js +6 -2
  17. package/dist/ArvoOrchestrator/types.d.ts +3 -2
  18. package/dist/ArvoResumable/factory.d.ts +0 -1
  19. package/dist/ArvoResumable/factory.js +0 -1
  20. package/dist/ArvoResumable/index.js +5 -1
  21. package/dist/MachineMemory/Simple.d.ts +33 -9
  22. package/dist/MachineMemory/Simple.js +51 -16
  23. package/dist/MachineMemory/interface.d.ts +145 -46
  24. package/dist/SyncEventResource/index.d.ts +22 -8
  25. package/dist/SyncEventResource/index.js +65 -12
  26. package/dist/index.d.ts +3 -3
  27. package/dist/index.js +3 -1
  28. package/dist/types.d.ts +40 -0
  29. package/dist/types.js +11 -0
  30. package/package.json +6 -5
@@ -14,7 +14,6 @@ import type { ArvoEventHandlerParam } from './types';
14
14
  * ```ts
15
15
  * const handler = createArvoEventHandler({
16
16
  * contract: userContract,
17
- * executionunits: 1,
18
17
  * handler: {
19
18
  * '1.0.0': async ({ event, domain, span }) => {
20
19
  * if (domain.event !== domain.self) {
@@ -18,7 +18,6 @@ var _1 = __importDefault(require("."));
18
18
  * ```ts
19
19
  * const handler = createArvoEventHandler({
20
20
  * contract: userContract,
21
- * executionunits: 1,
22
21
  * handler: {
23
22
  * '1.0.0': async ({ event, domain, span }) => {
24
23
  * if (domain.event !== domain.self) {
@@ -72,18 +72,18 @@ var utils_1 = require("../utils");
72
72
  var ArvoEventHandler = /** @class */ (function () {
73
73
  function ArvoEventHandler(param) {
74
74
  var _a;
75
- var _b, _c, _d;
75
+ var _b, _c, _d, _e;
76
76
  this.contract = param.contract;
77
- this.executionunits = param.executionunits;
77
+ this.executionunits = (_b = param.executionunits) !== null && _b !== void 0 ? _b : 0;
78
78
  this.handler = param.handler;
79
- this.defaultEventEmissionDomains = __assign({ systemError: [ArvoDomain_1.ArvoDomain.ORCHESTRATION_CONTEXT], emits: [ArvoDomain_1.ArvoDomain.ORCHESTRATION_CONTEXT] }, ((_b = param.defaultEventEmissionDomains) !== null && _b !== void 0 ? _b : {}));
80
- for (var _i = 0, _e = Object.keys(this.contract.versions); _i < _e.length; _i++) {
81
- var contractVersions = _e[_i];
79
+ this.defaultEventEmissionDomains = __assign({ systemError: [ArvoDomain_1.ArvoDomain.ORCHESTRATION_CONTEXT], emits: [ArvoDomain_1.ArvoDomain.ORCHESTRATION_CONTEXT] }, ((_c = param.defaultEventEmissionDomains) !== null && _c !== void 0 ? _c : {}));
80
+ for (var _i = 0, _f = Object.keys(this.contract.versions); _i < _f.length; _i++) {
81
+ var contractVersions = _f[_i];
82
82
  if (!this.handler[contractVersions]) {
83
83
  throw new Error("Contract ".concat(this.contract.uri, " requires handler implementation for version ").concat(contractVersions));
84
84
  }
85
85
  }
86
- this.spanOptions = __assign(__assign({ kind: api_1.SpanKind.CONSUMER }, param.spanOptions), { attributes: __assign(__assign((_a = {}, _a[arvo_core_1.ArvoExecution.ATTR_SPAN_KIND] = arvo_core_1.ArvoExecutionSpanKind.EVENT_HANDLER, _a[arvo_core_1.OpenInference.ATTR_SPAN_KIND] = arvo_core_1.OpenInferenceSpanKind.CHAIN, _a), ((_d = (_c = param.spanOptions) === null || _c === void 0 ? void 0 : _c.attributes) !== null && _d !== void 0 ? _d : {})), { 'arvo.handler.source': this.source, 'arvo.contract.uri': this.contract.uri }) });
86
+ this.spanOptions = __assign(__assign({ kind: api_1.SpanKind.CONSUMER }, param.spanOptions), { attributes: __assign(__assign((_a = {}, _a[arvo_core_1.ArvoExecution.ATTR_SPAN_KIND] = arvo_core_1.ArvoExecutionSpanKind.EVENT_HANDLER, _a[arvo_core_1.OpenInference.ATTR_SPAN_KIND] = arvo_core_1.OpenInferenceSpanKind.CHAIN, _a), ((_e = (_d = param.spanOptions) === null || _d === void 0 ? void 0 : _d.attributes) !== null && _e !== void 0 ? _e : {})), { 'arvo.handler.source': this.source, 'arvo.contract.uri': this.contract.uri }) });
87
87
  }
88
88
  Object.defineProperty(ArvoEventHandler.prototype, "source", {
89
89
  /** The source identifier for events produced by this handler */
@@ -70,7 +70,7 @@ export type ArvoEventHandlerParam<TContract extends ArvoContract> = {
70
70
  * The default execution cost of the function.
71
71
  * This can represent a dollar value or some other number with a rate card.
72
72
  */
73
- executionunits: number;
73
+ executionunits?: number;
74
74
  /**
75
75
  * The functional handler of the event which takes the input, performs an action, and returns the result.
76
76
  * @param params - The input parameters for the handler function.
@@ -138,17 +138,23 @@ export declare function setupArvoMachine<TContext extends MachineContext, TSelfC
138
138
  }, InferServiceContract<TServiceContracts>["emitted"], TMeta>>(config: TConfig & {
139
139
  id: string;
140
140
  version?: TSelfContract["version"];
141
- }) => ArvoMachine<string, TSelfContract["version"], TSelfContract, TServiceContracts, import("xstate").StateMachine<TContext, { [K in keyof TServiceContracts]: import("./types").InferEmittableEventsFromVersionedArvoContract<TServiceContracts[K]>; }[keyof TServiceContracts], {}, never, import("xstate").IsNever<TActions & {
141
+ }) => ArvoMachine<string, TSelfContract["version"], TSelfContract, TServiceContracts, import("xstate").StateMachine<TContext, { [K in keyof TServiceContracts]: import("./types").InferEmittableEventsFromVersionedArvoContract<TServiceContracts[K]>; }[keyof TServiceContracts], {}, never, (TActions & {
142
142
  enqueueArvoEvent: EnqueueArvoEventActionParam;
143
- }> extends true ? never : import("xstate").Values<{ [K_1 in (keyof TActions & string) | "enqueueArvoEvent"]: {
144
- type: K_1;
145
- params: (TActions & {
146
- enqueueArvoEvent: EnqueueArvoEventActionParam;
147
- })[K_1];
148
- }; }>, import("xstate").IsNever<TGuards> extends true ? never : import("xstate").Values<{ [K_2 in keyof TGuards & string]: {
149
- type: K_2;
143
+ } extends infer T extends Record<string, import("xstate").NonReducibleUnknown> ? { [K_1 in keyof T as K_1 & string]: {
144
+ type: K_1 & string;
145
+ params: T[K_1];
146
+ }; } : never)[keyof (TActions & {
147
+ enqueueArvoEvent: EnqueueArvoEventActionParam;
148
+ } extends infer T_1 extends Record<string, import("xstate").NonReducibleUnknown> ? { [K_1 in keyof T_1 as K_1 & string]: {
149
+ type: K_1 & string;
150
+ params: T_1[K_1];
151
+ }; } : never)], { [K_2 in keyof TGuards as K_2 & string]: {
152
+ type: K_2 & string;
153
+ params: TGuards[K_2];
154
+ }; }[keyof { [K_2 in keyof TGuards as K_2 & string]: {
155
+ type: K_2 & string;
150
156
  params: TGuards[K_2];
151
- }; }>, never, {} | {
157
+ }; }], never, {} | {
152
158
  [x: string]: {} | /*elided*/ any | {
153
159
  [x: string]: {} | /*elided*/ any | /*elided*/ any;
154
160
  };
@@ -156,5 +162,8 @@ export declare function setupArvoMachine<TContext extends MachineContext, TSelfC
156
162
  [x: string]: {} | {
157
163
  [x: string]: {} | /*elided*/ any | /*elided*/ any;
158
164
  } | /*elided*/ any;
159
- }, TTag, TSelfContract["accepts"]["schema"]["_output"], { [K_3 in string & keyof TSelfContract["emits"]]: import("arvo-core").InferArvoEvent<import("arvo-core").ArvoEvent<TSelfContract["emits"][K_3]["_output"], Record<string, any>, K_3>>; }[`arvo.orc.${ExtractOrchestratorType<TSelfContract["accepts"]["type"]>}.done`]["data"], { [K_4 in keyof TServiceContracts]: EnqueueArvoEventActionParam<z.input<TServiceContracts[K_4]["accepts"]["schema"]>, TServiceContracts[K_4]["accepts"]["type"], Record<string, string | number | boolean | null>>; }[keyof TServiceContracts], TMeta, any>>;
165
+ }, TTag, TSelfContract["accepts"]["schema"]["_output"], { [K_3 in string & keyof TSelfContract["emits"]]: import("arvo-core").InferArvoEvent<import("arvo-core").ArvoEvent<TSelfContract["emits"][K_3]["_output"], Record<string, any>, K_3>>; }[`arvo.orc.${ExtractOrchestratorType<TSelfContract["accepts"]["type"]>}.done`]["data"], { [K_4 in keyof TServiceContracts]: EnqueueArvoEventActionParam<z.input<TServiceContracts[K_4]["accepts"]["schema"]>, TServiceContracts[K_4]["accepts"]["type"], Record<string, string | number | boolean | null>>; }[keyof TServiceContracts], TMeta, {
166
+ id: any;
167
+ states: any;
168
+ }>>;
160
169
  };
@@ -1,9 +1,10 @@
1
1
  import { type Span } from '@opentelemetry/api';
2
2
  import { type ArvoContract, type ArvoEvent, type ArvoSemanticVersion, type OpenTelemetryHeaders, type VersionedArvoContract, type ViolationError } from 'arvo-core';
3
3
  import type { SyncEventResource } from '../SyncEventResource';
4
- import type { OrchestrationExecutionMemoryRecord } from './orchestrationExecutionState';
4
+ import { type OrchestrationExecutionMemoryRecord } from './orchestrationExecutionState';
5
5
  import { type ArvoOrchestrationHandlerType } from './types';
6
6
  import type { NonEmptyArray } from '../types';
7
+ import { MachineMemoryMetadata } from '../MachineMemory/interface';
7
8
  /**
8
9
  * Parameters for creating system error events during orchestration failures.
9
10
  */
@@ -54,7 +55,7 @@ export declare const createSystemErrorEvents: ({ error, event, otelHeaders, orch
54
55
  */
55
56
  export declare const handleOrchestrationErrors: (_handlerType: ArvoOrchestrationHandlerType, param: CreateSystemErrorEventsParams & {
56
57
  syncEventResource: SyncEventResource<OrchestrationExecutionMemoryRecord<Record<string, any>>>;
57
- }, span: Span) => Promise<{
58
+ }, metadata: MachineMemoryMetadata, span: Span) => Promise<{
58
59
  errorToThrow: ViolationError;
59
60
  events: null;
60
61
  } | {
@@ -53,6 +53,7 @@ var arvo_core_1 = require("arvo-core");
53
53
  var ArvoDomain_1 = require("../ArvoDomain");
54
54
  var errors_1 = require("../errors");
55
55
  var utils_1 = require("../utils");
56
+ var orchestrationExecutionState_1 = require("./orchestrationExecutionState");
56
57
  var types_1 = require("./types");
57
58
  /**
58
59
  * Creates standardized system error events for orchestration failures.
@@ -136,7 +137,7 @@ exports.createSystemErrorEvents = createSystemErrorEvents;
136
137
  *
137
138
  * @returns Either the violation error to throw or system error events to emit
138
139
  */
139
- var handleOrchestrationErrors = function (_handlerType, param, span) { return __awaiter(void 0, void 0, void 0, function () {
140
+ var handleOrchestrationErrors = function (_handlerType, param, metadata, span) { return __awaiter(void 0, void 0, void 0, function () {
140
141
  var handlerType, error, errorEvents, _i, _a, _b, errEvtIdx, errEvt, _c, _d, _e, key, value;
141
142
  return __generator(this, function (_f) {
142
143
  switch (_f.label) {
@@ -170,10 +171,11 @@ var handleOrchestrationErrors = function (_handlerType, param, span) { return __
170
171
  }
171
172
  return [4 /*yield*/, param.syncEventResource
172
173
  .persistState(param.event, {
173
- executionStatus: 'failure',
174
+ __type: 'OrchestrationExecutionMemoryRecord',
175
+ executionStatus: orchestrationExecutionState_1.OrchestrationExecutionStatus.FAILURE,
174
176
  subject: param.event.subject,
175
177
  error: param.error,
176
- }, null, span)
178
+ }, null, metadata, span)
177
179
  .catch(function (e) {
178
180
  (0, arvo_core_1.logToSpan)({
179
181
  level: 'CRITICAL',
@@ -9,6 +9,8 @@ export declare const OrchestrationExecutionStatus: {
9
9
  readonly NORMAL: "normal";
10
10
  /** Orchestration has failed and entered terminal error state */
11
11
  readonly FAILURE: "failure";
12
+ /** Orchestration is marked as done */
13
+ readonly DONE: "done";
12
14
  };
13
15
  /**
14
16
  * Discriminated union representing persisted orchestration state.
@@ -26,8 +28,10 @@ export declare const OrchestrationExecutionStatus: {
26
28
  * @template T - Custom state fields specific to the orchestration type
27
29
  */
28
30
  export type OrchestrationExecutionMemoryRecord<T extends Record<string, unknown>> = (T & {
29
- executionStatus: typeof OrchestrationExecutionStatus.NORMAL;
31
+ __type: 'OrchestrationExecutionMemoryRecord';
32
+ executionStatus: typeof OrchestrationExecutionStatus.NORMAL | typeof OrchestrationExecutionStatus.DONE;
30
33
  }) | (Partial<T> & {
34
+ __type: 'OrchestrationExecutionMemoryRecord';
31
35
  executionStatus: typeof OrchestrationExecutionStatus.FAILURE;
32
36
  error: Error;
33
37
  subject: string;
@@ -12,4 +12,6 @@ exports.OrchestrationExecutionStatus = {
12
12
  NORMAL: 'normal',
13
13
  /** Orchestration has failed and entered terminal error state */
14
14
  FAILURE: 'failure',
15
+ /** Orchestration is marked as done */
16
+ DONE: 'done',
15
17
  };
@@ -2,6 +2,7 @@ import type { Span } from '@opentelemetry/api';
2
2
  import { type ArvoEvent } from 'arvo-core';
3
3
  import type { SyncEventResource } from '../../SyncEventResource';
4
4
  import type { AcquiredLockStatusType } from '../../SyncEventResource/types';
5
+ import { MachineMemoryMetadata } from '../../MachineMemory/interface';
5
6
  /**
6
7
  * Acquires an exclusive lock for event processing with validation.
7
8
  *
@@ -9,4 +10,4 @@ import type { AcquiredLockStatusType } from '../../SyncEventResource/types';
9
10
  * processing. Throws if lock cannot be acquired, preventing concurrent modifications.
10
11
  * @throws {TransactionViolation} When lock cannot be acquired
11
12
  */
12
- export declare const acquireLockWithValidation: (syncEventResource: SyncEventResource<Record<string, any>>, event: ArvoEvent, span: Span) => Promise<AcquiredLockStatusType>;
13
+ export declare const acquireLockWithValidation: (syncEventResource: SyncEventResource<Record<string, any>>, event: ArvoEvent, metadata: MachineMemoryMetadata, span: Span) => Promise<AcquiredLockStatusType>;
@@ -46,11 +46,11 @@ var error_1 = require("../error");
46
46
  * processing. Throws if lock cannot be acquired, preventing concurrent modifications.
47
47
  * @throws {TransactionViolation} When lock cannot be acquired
48
48
  */
49
- var acquireLockWithValidation = function (syncEventResource, event, span) { return __awaiter(void 0, void 0, void 0, function () {
49
+ var acquireLockWithValidation = function (syncEventResource, event, metadata, span) { return __awaiter(void 0, void 0, void 0, function () {
50
50
  var acquiredLock;
51
51
  return __generator(this, function (_a) {
52
52
  switch (_a.label) {
53
- case 0: return [4 /*yield*/, syncEventResource.acquireLock(event, span)];
53
+ case 0: return [4 /*yield*/, syncEventResource.acquireLock(event, metadata, span)];
54
54
  case 1:
55
55
  acquiredLock = _a.sent();
56
56
  if (acquiredLock === 'NOT_ACQUIRED') {
@@ -2,8 +2,8 @@ import { type Span } from '@opentelemetry/api';
2
2
  import { type ArvoEvent, type ArvoOrchestrationSubjectContent, type ArvoOrchestratorContract, type ArvoSemanticVersion, type OpenTelemetryHeaders, type VersionedArvoContract } from 'arvo-core';
3
3
  import type IArvoEventHandler from '../../IArvoEventHandler';
4
4
  import type { SyncEventResource } from '../../SyncEventResource';
5
- import type { ArvoEventHandlerOpenTelemetryOptions, ArvoEventHandlerOtelSpanOptions, NonEmptyArray } from '../../types';
6
- import type { OrchestrationExecutionMemoryRecord } from '../orchestrationExecutionState';
5
+ import { type ArvoEventHandlerOpenTelemetryOptions, type ArvoEventHandlerOtelSpanOptions, type NonEmptyArray } from '../../types';
6
+ import { type OrchestrationExecutionMemoryRecord } from '../orchestrationExecutionState';
7
7
  import type { ArvoOrchestrationHandlerType } from '../types';
8
8
  /**
9
9
  * Configuration context for orchestration execution.
@@ -50,8 +50,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
50
50
  exports.executeWithOrchestrationWrapper = exports.returnEventsWithLogging = void 0;
51
51
  var api_1 = require("@opentelemetry/api");
52
52
  var arvo_core_1 = require("arvo-core");
53
+ var types_1 = require("../../types");
53
54
  var utils_1 = require("../../utils");
54
55
  var handlerErrors_1 = require("../handlerErrors");
56
+ var orchestrationExecutionState_1 = require("../orchestrationExecutionState");
55
57
  var acquireLockWithValidation_1 = require("./acquireLockWithValidation");
56
58
  var validateAndParseSubject_1 = require("./validateAndParseSubject");
57
59
  /**
@@ -90,7 +92,7 @@ var executeWithOrchestrationWrapper = function (_a, coreExecutionFn_1) { return
90
92
  case 0:
91
93
  otelConfig = (0, utils_1.createEventHandlerTelemetryConfig)(spanOptions.spanName({ selfContractUri: selfContract.uri, consumedEvent: event }), spanOptions, opentelemetry, event);
92
94
  return [4 /*yield*/, arvo_core_1.ArvoOpenTelemetry.getInstance().startActiveSpan(__assign(__assign({}, otelConfig), { fn: function (span) { return __awaiter(void 0, void 0, void 0, function () {
93
- var _i, _a, _b, key, value, otelHeaders, orchestrationParentSubject, initEventId, acquiredLock, parsedEventSubject, state, _c, emittables, newState, i, _d, _e, _f, key, value, error_1, _g, errorToThrow, errorEvents;
95
+ var _i, _a, _b, key, value, otelHeaders, orchestrationParentSubject, initEventId, acquiredLock, machineMemoryMetadata, parsedEventSubject, state, _c, emittables, newState, i, _d, _e, _f, key, value, error_1, _g, errorToThrow, errorEvents;
94
96
  var _h, _j, _k, _l;
95
97
  return __generator(this, function (_m) {
96
98
  switch (_m.label) {
@@ -110,34 +112,42 @@ var executeWithOrchestrationWrapper = function (_a, coreExecutionFn_1) { return
110
112
  orchestrationParentSubject = null;
111
113
  initEventId = null;
112
114
  acquiredLock = null;
115
+ machineMemoryMetadata = {
116
+ subject: event.subject,
117
+ source: source,
118
+ initiator: types_1.Materialized.pending(),
119
+ parentSubject: types_1.Materialized.pending(),
120
+ };
113
121
  _m.label = 1;
114
122
  case 1:
115
- _m.trys.push([1, 6, 8, 10]);
123
+ _m.trys.push([1, 8, 10, 12]);
116
124
  parsedEventSubject = (0, validateAndParseSubject_1.validateAndParseSubject)(event, source, syncEventResource, span, 'orchestrator');
117
125
  if (!parsedEventSubject) {
118
126
  return [2 /*return*/, (0, exports.returnEventsWithLogging)({ events: [] }, span)];
119
127
  }
120
- return [4 /*yield*/, (0, acquireLockWithValidation_1.acquireLockWithValidation)(syncEventResource, event, span)];
128
+ return [4 /*yield*/, (0, acquireLockWithValidation_1.acquireLockWithValidation)(syncEventResource, event, machineMemoryMetadata, span)];
121
129
  case 2:
122
130
  // Lock acquisition
123
131
  acquiredLock = _m.sent();
124
- return [4 /*yield*/, syncEventResource.acquireState(event, span)];
132
+ return [4 /*yield*/, syncEventResource.acquireState(event, machineMemoryMetadata, span)];
125
133
  case 3:
126
134
  state = _m.sent();
127
- if ((state === null || state === void 0 ? void 0 : state.executionStatus) === 'failure') {
135
+ if ((state === null || state === void 0 ? void 0 : state.executionStatus) === orchestrationExecutionState_1.OrchestrationExecutionStatus.FAILURE ||
136
+ (state === null || state === void 0 ? void 0 : state.executionStatus) === orchestrationExecutionState_1.OrchestrationExecutionStatus.DONE) {
128
137
  span.setAttribute('arvo.handler.execution.status', state.executionStatus);
129
138
  (0, arvo_core_1.logToSpan)({
130
139
  level: 'WARNING',
131
- message: "The orchestration has failed in a previous event. Ignoring event id: ".concat(event.id, " with event subject: ").concat(event.subject),
140
+ message: "The orchestration is in terminal state '".concat(state.executionStatus, "'. Ignoring event id: ").concat(event.id, " with event subject: ").concat(event.subject),
132
141
  }, span);
133
142
  span.setStatus({
134
- code: api_1.SpanStatusCode.ERROR,
135
- message: "The orchestration has failed in a previous event. Ignoring event id: ".concat(event.id, " with event subject: ").concat(event.subject),
143
+ code: state.executionStatus === orchestrationExecutionState_1.OrchestrationExecutionStatus.FAILURE ? api_1.SpanStatusCode.ERROR : api_1.SpanStatusCode.OK,
144
+ message: "The orchestration is in terminal state '".concat(state.executionStatus, "'. Ignoring event id: ").concat(event.id, " with event subject: ").concat(event.subject),
136
145
  });
137
146
  return [2 /*return*/, (0, exports.returnEventsWithLogging)({ events: [] }, span)];
138
147
  }
139
148
  orchestrationParentSubject = (_h = state === null || state === void 0 ? void 0 : state.parentSubject) !== null && _h !== void 0 ? _h : null;
140
149
  initEventId = (_j = state === null || state === void 0 ? void 0 : state.initEventId) !== null && _j !== void 0 ? _j : null;
150
+ machineMemoryMetadata = __assign(__assign({}, machineMemoryMetadata), { initiator: types_1.Materialized.resolved(parsedEventSubject.execution.initiator), parentSubject: types_1.Materialized.resolved(orchestrationParentSubject) });
141
151
  if (!state) {
142
152
  (0, arvo_core_1.logToSpan)({
143
153
  level: 'INFO',
@@ -180,10 +190,16 @@ var executeWithOrchestrationWrapper = function (_a, coreExecutionFn_1) { return
180
190
  }
181
191
  }
182
192
  // Persist state
183
- return [4 /*yield*/, syncEventResource.persistState(event, newState, state, span)];
193
+ return [4 /*yield*/, syncEventResource.persistState(event, newState, state, machineMemoryMetadata, span)];
184
194
  case 5:
185
195
  // Persist state
186
196
  _m.sent();
197
+ if (!(newState.executionStatus === orchestrationExecutionState_1.OrchestrationExecutionStatus.DONE)) return [3 /*break*/, 7];
198
+ return [4 /*yield*/, syncEventResource.cleanup(event, newState, state, machineMemoryMetadata, span)];
199
+ case 6:
200
+ _m.sent();
201
+ _m.label = 7;
202
+ case 7:
187
203
  (0, arvo_core_1.logToSpan)({
188
204
  level: 'INFO',
189
205
  message: "State update persisted in memory for subject ".concat(event.subject),
@@ -193,7 +209,7 @@ var executeWithOrchestrationWrapper = function (_a, coreExecutionFn_1) { return
193
209
  message: "Execution successfully completed and emitted ".concat(emittables.length, " events"),
194
210
  });
195
211
  return [2 /*return*/, (0, exports.returnEventsWithLogging)({ events: emittables }, span)];
196
- case 6:
212
+ case 8:
197
213
  error_1 = _m.sent();
198
214
  span.setAttribute('arvo.handler.execution.status', 'failure');
199
215
  return [4 /*yield*/, (0, handlerErrors_1.handleOrchestrationErrors)(_handlerType, {
@@ -208,20 +224,20 @@ var executeWithOrchestrationWrapper = function (_a, coreExecutionFn_1) { return
208
224
  source: source,
209
225
  syncEventResource: syncEventResource,
210
226
  handlerType: _handlerType,
211
- }, span)];
212
- case 7:
227
+ }, machineMemoryMetadata, span)];
228
+ case 9:
213
229
  _g = _m.sent(), errorToThrow = _g.errorToThrow, errorEvents = _g.events;
214
230
  if (errorToThrow)
215
231
  throw errorToThrow;
216
232
  return [2 /*return*/, {
217
233
  events: errorEvents,
218
234
  }];
219
- case 8: return [4 /*yield*/, syncEventResource.releaseLock(event, acquiredLock, span)];
220
- case 9:
235
+ case 10: return [4 /*yield*/, syncEventResource.releaseLock(event, acquiredLock, machineMemoryMetadata, span)];
236
+ case 11:
221
237
  _m.sent();
222
238
  span.end();
223
239
  return [7 /*endfinally*/];
224
- case 10: return [2 /*return*/];
240
+ case 12: return [2 /*return*/];
225
241
  }
226
242
  });
227
243
  }); } }))];
@@ -18,7 +18,6 @@ import type { CreateArvoOrchestratorParam } from './types';
18
18
  * ```typescript
19
19
  * const orchestrator = createArvoOrchestrator({
20
20
  * memory: new SimpleMachineMemory(),
21
- * executionunits: 1,
22
21
  * machines: [userOnboardingMachine, paymentMachine]
23
22
  * });
24
23
  *
@@ -32,7 +32,6 @@ var errors_1 = require("../errors");
32
32
  * ```typescript
33
33
  * const orchestrator = createArvoOrchestrator({
34
34
  * memory: new SimpleMachineMemory(),
35
- * executionunits: 1,
36
35
  * machines: [userOnboardingMachine, paymentMachine]
37
36
  * });
38
37
  *
@@ -51,16 +50,16 @@ var createArvoOrchestrator = function (_a) {
51
50
  var registry = new (MachineRegistry_1.MachineRegistry.bind.apply(MachineRegistry_1.MachineRegistry, __spreadArray([void 0], machines, false)))();
52
51
  var requiresResourceLocking = _locking !== null && _locking !== void 0 ? _locking : machines.some(function (machine) { return machine.requiresResourceLocking; });
53
52
  var representativeMachine = registry.machines[0];
54
- var lastSeenVersions = [];
53
+ var lastSeenVersions = new Set();
55
54
  for (var _i = 0, _b = registry.machines; _i < _b.length; _i++) {
56
55
  var machine = _b[_i];
57
56
  if (representativeMachine.source !== machine.source) {
58
- throw new errors_1.ConfigViolation("All the machines in the orchestrator must have type '".concat(representativeMachine.source, "'"));
57
+ throw new errors_1.ConfigViolation("All the machines in the orchestrator must have source '".concat(representativeMachine.source, "'. Make sure all machine are implementing versions of the same self contract"));
59
58
  }
60
- if (lastSeenVersions.includes(machine.version)) {
59
+ if (lastSeenVersions.has(machine.version)) {
61
60
  throw new errors_1.ConfigViolation("An orchestrator must have unique machine versions. Machine ID:".concat(machine.id, " has duplicate version ").concat(machine.version, "."));
62
61
  }
63
- lastSeenVersions.push(machine.version);
62
+ lastSeenVersions.add(machine.version);
64
63
  }
65
64
  return new _1.ArvoOrchestrator({
66
65
  executionunits: executionunits,
@@ -55,6 +55,7 @@ var orchestrationExecutionWrapper_1 = require("../ArvoOrchestrationUtils/orchest
55
55
  var SyncEventResource_1 = require("../SyncEventResource");
56
56
  var errors_1 = require("../errors");
57
57
  var ArvoDomain_1 = require("../ArvoDomain");
58
+ var orchestrationExecutionState_1 = require("../ArvoOrchestrationUtils/orchestrationExecutionState");
58
59
  /**
59
60
  * Orchestrates state machine execution and lifecycle management.
60
61
  *
@@ -67,7 +68,7 @@ var ArvoOrchestrator = /** @class */ (function () {
67
68
  var _b;
68
69
  var executionunits = _a.executionunits, memory = _a.memory, registry = _a.registry, executionEngine = _a.executionEngine, requiresResourceLocking = _a.requiresResourceLocking, defaultEventEmissionDomains = _a.defaultEventEmissionDomains, spanOptions = _a.spanOptions;
69
70
  var _c, _d, _e, _f, _g, _h, _j;
70
- this.executionunits = executionunits;
71
+ this.executionunits = executionunits !== null && executionunits !== void 0 ? executionunits : 0;
71
72
  this.registry = registry;
72
73
  this.executionEngine = executionEngine;
73
74
  this.syncEventResource = new SyncEventResource_1.SyncEventResource(memory, requiresResourceLocking);
@@ -206,7 +207,10 @@ var ArvoOrchestrator = /** @class */ (function () {
206
207
  message: "Machine execution completed - Status: ".concat(executionResult.state.status, ", Generated events: ").concat(emittables.length),
207
208
  }, span);
208
209
  newState = {
209
- executionStatus: 'normal',
210
+ __type: 'OrchestrationExecutionMemoryRecord',
211
+ executionStatus: executionResult.finalOutput
212
+ ? orchestrationExecutionState_1.OrchestrationExecutionStatus.DONE
213
+ : orchestrationExecutionState_1.OrchestrationExecutionStatus.NORMAL,
210
214
  initEventId: initEventId,
211
215
  subject: event.subject,
212
216
  parentSubject: orchestrationParentSubject,
@@ -84,7 +84,7 @@ export type MachineMemoryRecord = OrchestrationExecutionMemoryRecord<{
84
84
  */
85
85
  export type ArvoOrchestratorParam = {
86
86
  /** Computational cost metric assigned to orchestrator operations */
87
- executionunits: number;
87
+ executionunits?: number;
88
88
  /** Memory interface for state persistence and retrieval */
89
89
  memory: IMachineMemory<MachineMemoryRecord>;
90
90
  /** Registry for managing and resolving machine instances */
@@ -135,7 +135,8 @@ export type ArvoOrchestratorParam = {
135
135
  * Simplified interface for {@link createArvoOrchestrator} that automatically
136
136
  * constructs default registry and execution engine components.
137
137
  */
138
- export type CreateArvoOrchestratorParam = Pick<ArvoOrchestratorParam, 'memory' | 'executionunits' | 'spanOptions' | 'defaultEventEmissionDomains'> & {
138
+ export type CreateArvoOrchestratorParam = Pick<ArvoOrchestratorParam, 'executionunits' | 'spanOptions' | 'defaultEventEmissionDomains'> & {
139
+ memory: IMachineMemory<Record<string, any>>;
139
140
  /**
140
141
  * Optional override for resource locking requirement.
141
142
  *
@@ -29,7 +29,6 @@ import type { CreateArvoResumableParam } from './types';
29
29
  * // Handler implementation
30
30
  * }
31
31
  * },
32
- * executionunits: 1
33
32
  * });
34
33
  * ```
35
34
  *
@@ -31,7 +31,6 @@ var servicesValidation_1 = require("../ArvoOrchestrationUtils/servicesValidation
31
31
  * // Handler implementation
32
32
  * }
33
33
  * },
34
- * executionunits: 1
35
34
  * });
36
35
  * ```
37
36
  *
@@ -56,6 +56,7 @@ var orchestrationExecutionWrapper_1 = require("../ArvoOrchestrationUtils/orchest
56
56
  var index_1 = require("../SyncEventResource/index");
57
57
  var errors_1 = require("../errors");
58
58
  var ArvoDomain_1 = require("../ArvoDomain");
59
+ var orchestrationExecutionState_1 = require("../ArvoOrchestrationUtils/orchestrationExecutionState");
59
60
  /**
60
61
  * ArvoResumable complements {@link ArvoOrchestrator} by providing imperative
61
62
  * handler functions for orchestration logic instead of declarative state machines.
@@ -266,7 +267,10 @@ var ArvoResumable = /** @class */ (function () {
266
267
  produced: emittables.map(function (item) { return item.toJSON(); }),
267
268
  };
268
269
  newState = {
269
- executionStatus: 'normal',
270
+ __type: 'OrchestrationExecutionMemoryRecord',
271
+ executionStatus: (executionResult === null || executionResult === void 0 ? void 0 : executionResult.output)
272
+ ? orchestrationExecutionState_1.OrchestrationExecutionStatus.DONE
273
+ : orchestrationExecutionState_1.OrchestrationExecutionStatus.NORMAL,
270
274
  status: (executionResult === null || executionResult === void 0 ? void 0 : executionResult.output) ? 'done' : 'active',
271
275
  initEventId: initEventId,
272
276
  parentSubject: orchestrationParentSubject,
@@ -1,21 +1,47 @@
1
1
  import type { IMachineMemory } from './interface';
2
2
  /**
3
- * In-memory implementation of machine state storage for single-instance NodeJS apps.
4
- *
5
- * Best for: Container apps, request-scoped workflows, testing, demos
6
- * Not for: Multi-instance deployments, persistent workflows, distributed systems
3
+ * In-memory implementation of machine state storage for single-instance NodeJS applications.
7
4
  *
8
5
  * @example
9
6
  * const memory = new SimpleMachineMemory();
10
7
  * const orchestrator = createArvoOrchestrator({
11
8
  * memory,
12
- * executionunits: 1,
13
9
  * machines: [workflow]
14
10
  * });
11
+ *
12
+ * @warning Concurrency Limitations
13
+ *
14
+ * This implementation is NOT concurrency-safe. Lock acquisition is not atomic,
15
+ * making it unsuitable for parallel event processing where multiple handlers
16
+ * might process events for the same workflow simultaneously.
17
+ *
18
+ * **Safe Usage:**
19
+ * - {@link SimpleEventBroker} (sequential event processing)
20
+ * - Isolated handlers without shared durable state requirements
21
+ *
22
+ * **Unsafe Usage:**
23
+ * - In-memory parallel/concurrent event brokers (e.g., p-queue with prefetch > 1)
24
+ *
25
+ * For concurrent in-process event processing, use the concurrency-safe memory
26
+ * backend from the `@arvo-tools/concurrent` package, which provides atomic lock
27
+ * operations suitable for parallel execution within a single process.
28
+ *
29
+ * **Unsuitable For:**
30
+ * - Multi-instance deployments
31
+ * - Distributed systems requiring shared state across processes
32
+ *
33
+ * For these scenarios, implement or use a database-backed memory backend
34
+ * (Redis, PostgreSQL, DynamoDB, etc.) that provides distributed state
35
+ * persistence and atomic locking across instances. You can also explore the
36
+ * Arvo tool eco-system `@arvo-tools`
15
37
  */
16
38
  export declare class SimpleMachineMemory<T extends Record<string, any> = Record<string, any>> implements IMachineMemory<T> {
17
39
  private readonly memoryMap;
18
40
  private readonly lockMap;
41
+ private readonly enableCleanup;
42
+ constructor(config?: {
43
+ enableCleanup?: boolean;
44
+ });
19
45
  /**
20
46
  * Gets stored state for a machine instance
21
47
  * @param id Machine instance ID
@@ -44,8 +70,6 @@ export declare class SimpleMachineMemory<T extends Record<string, any> = Record<
44
70
  * @throws {Error} When id is empty or undefined
45
71
  */
46
72
  unlock(id: string): Promise<boolean>;
47
- /**
48
- * Clears all stored data and locks
49
- */
50
- clear(key?: string): void;
73
+ cleanup(id: string): Promise<void>;
74
+ clear(): void;
51
75
  }
@@ -48,24 +48,50 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
48
48
  };
49
49
  Object.defineProperty(exports, "__esModule", { value: true });
50
50
  exports.SimpleMachineMemory = void 0;
51
+ var arvo_core_1 = require("arvo-core");
51
52
  /**
52
- * In-memory implementation of machine state storage for single-instance NodeJS apps.
53
- *
54
- * Best for: Container apps, request-scoped workflows, testing, demos
55
- * Not for: Multi-instance deployments, persistent workflows, distributed systems
53
+ * In-memory implementation of machine state storage for single-instance NodeJS applications.
56
54
  *
57
55
  * @example
58
56
  * const memory = new SimpleMachineMemory();
59
57
  * const orchestrator = createArvoOrchestrator({
60
58
  * memory,
61
- * executionunits: 1,
62
59
  * machines: [workflow]
63
60
  * });
61
+ *
62
+ * @warning Concurrency Limitations
63
+ *
64
+ * This implementation is NOT concurrency-safe. Lock acquisition is not atomic,
65
+ * making it unsuitable for parallel event processing where multiple handlers
66
+ * might process events for the same workflow simultaneously.
67
+ *
68
+ * **Safe Usage:**
69
+ * - {@link SimpleEventBroker} (sequential event processing)
70
+ * - Isolated handlers without shared durable state requirements
71
+ *
72
+ * **Unsafe Usage:**
73
+ * - In-memory parallel/concurrent event brokers (e.g., p-queue with prefetch > 1)
74
+ *
75
+ * For concurrent in-process event processing, use the concurrency-safe memory
76
+ * backend from the `@arvo-tools/concurrent` package, which provides atomic lock
77
+ * operations suitable for parallel execution within a single process.
78
+ *
79
+ * **Unsuitable For:**
80
+ * - Multi-instance deployments
81
+ * - Distributed systems requiring shared state across processes
82
+ *
83
+ * For these scenarios, implement or use a database-backed memory backend
84
+ * (Redis, PostgreSQL, DynamoDB, etc.) that provides distributed state
85
+ * persistence and atomic locking across instances. You can also explore the
86
+ * Arvo tool eco-system `@arvo-tools`
64
87
  */
65
88
  var SimpleMachineMemory = /** @class */ (function () {
66
- function SimpleMachineMemory() {
89
+ function SimpleMachineMemory(config) {
90
+ var _a;
67
91
  this.memoryMap = new Map();
68
92
  this.lockMap = new Map();
93
+ this.enableCleanup = true;
94
+ this.enableCleanup = (_a = config === null || config === void 0 ? void 0 : config.enableCleanup) !== null && _a !== void 0 ? _a : true;
69
95
  }
70
96
  /**
71
97
  * Gets stored state for a machine instance
@@ -141,17 +167,26 @@ var SimpleMachineMemory = /** @class */ (function () {
141
167
  });
142
168
  });
143
169
  };
144
- /**
145
- * Clears all stored data and locks
146
- */
147
- SimpleMachineMemory.prototype.clear = function (key) {
148
- if (key) {
149
- this.memoryMap.delete(key);
150
- this.lockMap.delete(key);
151
- return;
152
- }
153
- this.memoryMap.clear();
170
+ SimpleMachineMemory.prototype.cleanup = function (id) {
171
+ return __awaiter(this, void 0, void 0, function () {
172
+ return __generator(this, function (_a) {
173
+ if (!this.enableCleanup) {
174
+ (0, arvo_core_1.logToSpan)({
175
+ level: 'INFO',
176
+ message: 'Skipping cleanup due to config setting',
177
+ });
178
+ return [2 /*return*/];
179
+ }
180
+ this.memoryMap.delete(id);
181
+ this.lockMap.delete(id);
182
+ return [2 /*return*/];
183
+ });
184
+ });
185
+ };
186
+ // Clears all stored data and locks
187
+ SimpleMachineMemory.prototype.clear = function () {
154
188
  this.lockMap.clear();
189
+ this.memoryMap.clear();
155
190
  };
156
191
  return SimpleMachineMemory;
157
192
  }());
@@ -1,57 +1,156 @@
1
+ import { Materializable } from '../types';
2
+ export type MachineMemoryMetadata = {
3
+ /**
4
+ * Workflow instance identifier (same as event.subject).
5
+ * This unique identifier remains constant throughout the workflow's entire lifecycle.
6
+ */
7
+ subject: string;
8
+ /**
9
+ * Parent workflow's subject for tracking orchestration hierarchies.
10
+ *
11
+ * When materialized, a null value indicates this is a root workflow with no parent.
12
+ * A string value indicates this is a child workflow with the specified parent subject.
13
+ * A pending state means the parent context has not yet been determined.
14
+ *
15
+ * This enables hierarchical workflow tracking and cleanup strategies that differentiate
16
+ * between root workflows, child workflows, and workflows with pending parent determination.
17
+ */
18
+ parentSubject: Materializable<string | null>;
19
+ /**
20
+ * Identifier of the entity or process that initiated this workflow.
21
+ *
22
+ * When materialized, contains the initiator's identifier.
23
+ * A pending state means the initiator has not yet been determined.
24
+ */
25
+ initiator: Materializable<string>;
26
+ /**
27
+ * Orchestrator's source identifier (handler.source).
28
+ * Identifies which orchestrator type is managing this workflow instance.
29
+ */
30
+ source: string;
31
+ };
1
32
  /**
2
- * Manages machine state memory operations with optimistic locking strategy.
3
- * Implements a "fail fast on acquire, be tolerant on release" approach for resource management.
4
- * @template T - Structure of stored data
33
+ * Manages workflow instance state with optimistic locking for orchestration handlers.
34
+ *
35
+ * Each workflow execution has a unique identifier (event.subject) that remains constant
36
+ * throughout its entire lifecycle. This interface uses that subject as the key for all
37
+ * state operations, enabling multiple concurrent executions of the same orchestrator
38
+ * to maintain isolated state.
39
+ *
40
+ * Implements "fail fast on acquire, be tolerant on release" locking philosophy:
41
+ * - Lock acquisition fails quickly after reasonable retries to prevent duplicate execution
42
+ * - Lock release tolerates failures since TTL-based expiry provides automatic recovery
43
+ *
44
+ * @template T - Structure of the workflow state data stored per instance
5
45
  */
6
- export interface IMachineMemory<T extends Record<string, any>> {
7
- /**
8
- * Gets state data for machine ID (event.subject).
9
- * Should implement minimal retries (e.g. 2-3 attempts) with backoff for transient failures.
10
- * Must distinguish between:
11
- * - No data exists: Returns null (normal case for new machines)
12
- * - Operation failed: Throws error after all retries exhausted
13
- *
14
- * Retry strategy should be quick with reasonable timeout to avoid blocking:
15
- * - Few retry attempts (2-3)
16
- * - Short backoff delays (e.g. 100ms with exponential backoff)
17
- * - Total operation time under 1s
18
- *
19
- * @param id - Machine ID
20
- * @returns null if no data exists, T if data found
21
- * @throws Error if read operation fails after retries (e.g. connection error, permission denied)
22
- */
23
- read(id: string): Promise<T | null>;
24
- /**
25
- * Saves state data for machine ID (event.subject).
26
- * Should fail fast - if write fails, throw error immediately.
27
- * No retry logic as consistency is critical and caller handles failures.
28
- * @param id - Machine ID
29
- * @param data - State to save
30
- * @param prevData - The previous snapshot of the data
46
+ export interface IMachineMemory<T extends Record<string, any> = Record<string, any>> {
47
+ /**
48
+ * Retrieves persisted state for a specific workflow instance.
49
+ *
50
+ * The orchestrator calls this when resuming a workflow to load the context
51
+ * needed to continue from where it previously stopped. Returns null for
52
+ * new workflow instances that have no persisted state yet.
53
+ *
54
+ * Should implement minimal retries (2-3 attempts) with short backoff for
55
+ * transient failures to avoid blocking event processing. Total operation
56
+ * time should stay under 1 second.
57
+ *
58
+ * @param id - Workflow instance identifier (event.subject)
59
+ * @param metadata - Workflow context (subject, parent subject, source)
60
+ * @returns null if no state exists for this workflow instance, T if state found
61
+ * @throws Error if read operation fails after retries (connection error, permission denied, etc.)
62
+ */
63
+ read(id: string, metadata: MachineMemoryMetadata | null): Promise<T | null>;
64
+ /**
65
+ * Persists updated state for a specific workflow instance.
66
+ *
67
+ * Called after the orchestrator processes an event and needs to save
68
+ * the workflow's current context before terminating. The next event
69
+ * for this workflow will load this saved state to resume execution.
70
+ *
71
+ * Should fail fast without retries - if persistence fails, the orchestrator
72
+ * must know immediately to avoid state inconsistency. The caller handles
73
+ * failures through retry mechanisms or dead letter queues.
74
+ *
75
+ * @param id - Workflow instance identifier (event.subject)
76
+ * @param data - Current workflow state to persist
77
+ * @param prevData - Previous state snapshot (can be used for compare-and-swap or audit trails)
78
+ * @param metadata - Workflow context (subject, parent subject, source)
31
79
  * @throws Error if write operation fails
32
80
  */
33
- write(id: string, data: T, prevData: T | null): Promise<void>;
81
+ write(id: string, data: T, prevData: T | null, metadata: MachineMemoryMetadata | null): Promise<void>;
34
82
  /**
35
- * Acquires execution lock for machine ID (event.subject).
36
- * Should implement reasonable retries with backoff for transient lock conflicts.
37
- * Must fail fast after retry attempts exhausted - no long polling.
38
- * @param id - Machine ID
39
- * @returns True if lock acquired successfully
40
- * @throws Error if lock operation fails (not same as lock denial)
83
+ * Acquires exclusive execution lock for a workflow instance.
84
+ *
85
+ * Prevents concurrent processing of the same workflow instance across
86
+ * distributed orchestrator instances. The orchestrator acquires this lock
87
+ * before reading/modifying state to ensure only one instance processes
88
+ * events for this workflow at any given time.
89
+ *
90
+ * Should implement reasonable retries (2-3 attempts) with backoff for
91
+ * transient lock conflicts, then fail fast. Returns false if another
92
+ * instance holds the lock after retries exhausted.
93
+ *
94
+ * CRITICAL: Should set TTL when acquiring lock (typically 30s-5m based on
95
+ * expected execution duration) to prevent permanent deadlocks if unlock fails.
96
+ *
97
+ * @param id - Workflow instance identifier (event.subject)
98
+ * @param metadata - Workflow context (subject, parent subject, source)
99
+ * @returns true if lock acquired successfully, false if lock held by another instance
100
+ * @throws Error if lock operation itself fails (not same as lock being unavailable)
41
101
  */
42
- lock(id: string): Promise<boolean>;
102
+ lock(id: string, metadata: MachineMemoryMetadata | null): Promise<boolean>;
43
103
  /**
44
- * Releases execution lock for machine ID (event.subject).
45
- * Can retry a few times on failure but should not over-engineer.
46
- * System will eventually recover even if unlock fails.
104
+ * Releases execution lock for a workflow instance.
105
+ *
106
+ * Called after the orchestrator finishes processing an event and persisting
107
+ * state. Can retry a few times on transient failures but should not block
108
+ * execution - the TTL-based expiry ensures eventual recovery even if unlock fails.
109
+ *
110
+ * The "be tolerant on release" philosophy means unlock failures don't cascade
111
+ * into workflow failures. The lock will auto-expire via TTL, allowing the
112
+ * workflow to resume after the timeout period.
113
+ *
114
+ * @param id - Workflow instance identifier (event.subject)
115
+ * @param metadata - Workflow context (subject, parent subject, source)
116
+ * @returns true if unlocked successfully, false if unlock failed (non-critical)
117
+ */
118
+ unlock(id: string, metadata: MachineMemoryMetadata | null): Promise<boolean>;
119
+ /**
120
+ * Optional cleanup hook invoked during successful workflow completion.
121
+ *
122
+ * The orchestrator calls this automatically when a workflow instance:
123
+ * 1. Reaches a final completion state (terminal state or returns output)
124
+ * 2. Successfully persists the final workflow state to storage
125
+ *
126
+ * This executes AFTER final state persistence but BEFORE the completion
127
+ * event is emitted back to the initiator. This ordering ensures the final
128
+ * state is durably saved before cleanup runs, while allowing cleanup logic
129
+ * to complete before the workflow signals completion to external systems.
130
+ *
131
+ * IMPORTANT: This hook is NOT called when orchestration execution fails
132
+ * (e.g., lock acquisition failure, state persistence failure, handler errors).
133
+ * It only executes for workflows that successfully reach their final state.
134
+ *
135
+ * Receives the same state transition data as write() to enable intelligent
136
+ * cleanup decisions based on how the workflow completed and what changed
137
+ * in the final step.
138
+ *
139
+ * This hook enables custom memory management strategies:
140
+ * - Mark state as eligible for garbage collection based on final state values
141
+ * - Archive completed workflows to cold storage with state-dependent retention
142
+ * - Implement conditional retention policies (e.g., keep failures longer than successes)
143
+ * - Extract specific data from final state for long-term analytics storage
144
+ * - Compare final vs previous state to determine appropriate storage tier
145
+ * - Trigger external cleanup processes with workflow completion context
47
146
  *
48
- * Implementation MUST include lock expiry mechanism (TTL):
49
- * - Set reasonable TTL when acquiring lock (e.g. 30s-5m based on execution patterns)
50
- * - Ensure locks auto-expire to prevent deadlocks from unlock failures
51
- * - Consider execution patterns when setting TTL to avoid premature expiry
147
+ * Implementations are not required to delete state immediately - they can
148
+ * implement whatever retention/archival strategy suits their operational needs.
52
149
  *
53
- * @param id - Machine ID
54
- * @returns True if unlocked successfully
150
+ * @param id - Workflow instance identifier (event.subject)
151
+ * @param data - Final workflow state that was just persisted
152
+ * @param prevData - Previous state before final persistence (null if this was first/only state)
153
+ * @param metadata - Workflow context (subject, parent subject, source)
55
154
  */
56
- unlock(id: string): Promise<boolean>;
155
+ cleanup?(id: string, data: T, prevData: T | null, metadata: MachineMemoryMetadata | null): Promise<void>;
57
156
  }
@@ -1,6 +1,6 @@
1
1
  import type { Span } from '@opentelemetry/api';
2
2
  import { type ArvoEvent } from 'arvo-core';
3
- import type { IMachineMemory } from '../MachineMemory/interface';
3
+ import type { IMachineMemory, MachineMemoryMetadata } from '../MachineMemory/interface';
4
4
  import type { AcquiredLockStatusType, ReleasedLockStatusType } from './types';
5
5
  /**
6
6
  * A synchronous event resource that manages machine memory state based on event subjects.
@@ -52,7 +52,7 @@ export declare class SyncEventResource<T extends Record<string, any>> {
52
52
  *
53
53
  * @throws {TransactionViolation} When lock acquisition fails due to system errors
54
54
  */
55
- acquireLock(event: ArvoEvent, span?: Span): Promise<AcquiredLockStatusType>;
55
+ acquireLock(event: ArvoEvent, metadata: MachineMemoryMetadata, span?: Span): Promise<AcquiredLockStatusType>;
56
56
  /**
57
57
  * Retrieves the current state from memory for the given event subject.
58
58
  *
@@ -65,7 +65,7 @@ export declare class SyncEventResource<T extends Record<string, any>> {
65
65
  *
66
66
  * @throws {TransactionViolation} When the read operation fails due to storage errors
67
67
  */
68
- acquireState(event: ArvoEvent, span?: Span): Promise<T | null>;
68
+ acquireState(event: ArvoEvent, metadata: MachineMemoryMetadata, span?: Span): Promise<T | null>;
69
69
  /**
70
70
  * Persists the updated memory state to distributed storage.
71
71
  *
@@ -76,7 +76,7 @@ export declare class SyncEventResource<T extends Record<string, any>> {
76
76
  *
77
77
  * @throws {TransactionViolation} When the write operation fails due to storage errors
78
78
  */
79
- persistState(event: ArvoEvent, record: T, prevRecord: T | null, span?: Span): Promise<void>;
79
+ persistState(event: ArvoEvent, record: T, prevRecord: T | null, metadata: MachineMemoryMetadata, span?: Span): Promise<void>;
80
80
  /**
81
81
  * Validates that the event subject conforms to the ArvoOrchestrationSubject format.
82
82
  *
@@ -86,8 +86,6 @@ export declare class SyncEventResource<T extends Record<string, any>> {
86
86
  * distributed service architecture.
87
87
  *
88
88
  * @throws {ExecutionViolation} When the event subject format is invalid
89
- *
90
- * @protected
91
89
  */
92
90
  validateEventSubject(event: ArvoEvent, span?: Span): void;
93
91
  /**
@@ -103,8 +101,24 @@ export declare class SyncEventResource<T extends Record<string, any>> {
103
101
  * - 'NOOP': No lock was acquired, so no operation was performed
104
102
  * - 'RELEASED': Lock was successfully released
105
103
  * - 'ERROR': Lock release failed, potential resource leak
104
+ */
105
+ releaseLock(event: ArvoEvent, acquiredLock: AcquiredLockStatusType | null, metadata: MachineMemoryMetadata, span?: Span): Promise<ReleasedLockStatusType>;
106
+ /**
107
+ * Invokes the optional cleanup hook after successful workflow completion.
108
+ *
109
+ * This method calls the memory implementation's cleanup hook if it exists, allowing
110
+ * implementations to perform custom memory management operations like archiving,
111
+ * marking for garbage collection, or implementing retention policies.
112
+ *
113
+ * Cleanup is a non-critical operation - failures are logged but do not throw exceptions
114
+ * or disrupt the workflow completion process. The assumption is that cleanup operations
115
+ * can be retried later or handled through separate maintenance processes.
106
116
  *
107
- * @protected
117
+ * @param event - The ArvoEvent that triggered workflow completion
118
+ * @param record - Final workflow state that was just persisted
119
+ * @param prevRecord - Previous state before final persistence
120
+ * @param metadata - Workflow context (subject, parent subject, source)
121
+ * @param span - Optional OpenTelemetry span for observability
108
122
  */
109
- releaseLock(event: ArvoEvent, acquiredLock: AcquiredLockStatusType | null, span?: Span): Promise<ReleasedLockStatusType>;
123
+ cleanup(event: ArvoEvent, record: T, prevRecord: T | null, metadata: MachineMemoryMetadata, span?: Span): Promise<void>;
110
124
  }
@@ -91,7 +91,7 @@ var SyncEventResource = /** @class */ (function () {
91
91
  *
92
92
  * @throws {TransactionViolation} When lock acquisition fails due to system errors
93
93
  */
94
- SyncEventResource.prototype.acquireLock = function (event, span) {
94
+ SyncEventResource.prototype.acquireLock = function (event, metadata, span) {
95
95
  return __awaiter(this, void 0, void 0, function () {
96
96
  var acquired, e_1;
97
97
  return __generator(this, function (_a) {
@@ -111,7 +111,7 @@ var SyncEventResource = /** @class */ (function () {
111
111
  level: 'INFO',
112
112
  message: 'Acquiring lock for the event',
113
113
  });
114
- return [4 /*yield*/, this.memory.lock(event.subject)];
114
+ return [4 /*yield*/, this.memory.lock(event.subject, metadata)];
115
115
  case 2:
116
116
  acquired = _a.sent();
117
117
  return [2 /*return*/, acquired ? 'ACQUIRED' : 'NOT_ACQUIRED'];
@@ -139,7 +139,7 @@ var SyncEventResource = /** @class */ (function () {
139
139
  *
140
140
  * @throws {TransactionViolation} When the read operation fails due to storage errors
141
141
  */
142
- SyncEventResource.prototype.acquireState = function (event, span) {
142
+ SyncEventResource.prototype.acquireState = function (event, metadata, span) {
143
143
  return __awaiter(this, void 0, void 0, function () {
144
144
  var e_2;
145
145
  return __generator(this, function (_a) {
@@ -150,7 +150,7 @@ var SyncEventResource = /** @class */ (function () {
150
150
  level: 'INFO',
151
151
  message: 'Reading machine state for the event',
152
152
  }, span);
153
- return [4 /*yield*/, this.memory.read(event.subject)];
153
+ return [4 /*yield*/, this.memory.read(event.subject, metadata)];
154
154
  case 1: return [2 /*return*/, _a.sent()];
155
155
  case 2:
156
156
  e_2 = _a.sent();
@@ -174,7 +174,7 @@ var SyncEventResource = /** @class */ (function () {
174
174
  *
175
175
  * @throws {TransactionViolation} When the write operation fails due to storage errors
176
176
  */
177
- SyncEventResource.prototype.persistState = function (event, record, prevRecord, span) {
177
+ SyncEventResource.prototype.persistState = function (event, record, prevRecord, metadata, span) {
178
178
  return __awaiter(this, void 0, void 0, function () {
179
179
  var e_3;
180
180
  return __generator(this, function (_a) {
@@ -185,7 +185,7 @@ var SyncEventResource = /** @class */ (function () {
185
185
  level: 'INFO',
186
186
  message: 'Persisting machine state to the storage',
187
187
  }, span);
188
- return [4 /*yield*/, this.memory.write(event.subject, record, prevRecord)];
188
+ return [4 /*yield*/, this.memory.write(event.subject, record, prevRecord, metadata)];
189
189
  case 1:
190
190
  _a.sent();
191
191
  return [3 /*break*/, 3];
@@ -210,8 +210,6 @@ var SyncEventResource = /** @class */ (function () {
210
210
  * distributed service architecture.
211
211
  *
212
212
  * @throws {ExecutionViolation} When the event subject format is invalid
213
- *
214
- * @protected
215
213
  */
216
214
  SyncEventResource.prototype.validateEventSubject = function (event, span) {
217
215
  (0, arvo_core_1.logToSpan)({
@@ -236,10 +234,8 @@ var SyncEventResource = /** @class */ (function () {
236
234
  * - 'NOOP': No lock was acquired, so no operation was performed
237
235
  * - 'RELEASED': Lock was successfully released
238
236
  * - 'ERROR': Lock release failed, potential resource leak
239
- *
240
- * @protected
241
237
  */
242
- SyncEventResource.prototype.releaseLock = function (event, acquiredLock, span) {
238
+ SyncEventResource.prototype.releaseLock = function (event, acquiredLock, metadata, span) {
243
239
  return __awaiter(this, void 0, void 0, function () {
244
240
  var err_1;
245
241
  return __generator(this, function (_a) {
@@ -255,7 +251,7 @@ var SyncEventResource = /** @class */ (function () {
255
251
  _a.label = 1;
256
252
  case 1:
257
253
  _a.trys.push([1, 3, , 4]);
258
- return [4 /*yield*/, this.memory.unlock(event.subject)];
254
+ return [4 /*yield*/, this.memory.unlock(event.subject, metadata)];
259
255
  case 2:
260
256
  _a.sent();
261
257
  (0, arvo_core_1.logToSpan)({
@@ -275,6 +271,63 @@ var SyncEventResource = /** @class */ (function () {
275
271
  });
276
272
  });
277
273
  };
274
+ /**
275
+ * Invokes the optional cleanup hook after successful workflow completion.
276
+ *
277
+ * This method calls the memory implementation's cleanup hook if it exists, allowing
278
+ * implementations to perform custom memory management operations like archiving,
279
+ * marking for garbage collection, or implementing retention policies.
280
+ *
281
+ * Cleanup is a non-critical operation - failures are logged but do not throw exceptions
282
+ * or disrupt the workflow completion process. The assumption is that cleanup operations
283
+ * can be retried later or handled through separate maintenance processes.
284
+ *
285
+ * @param event - The ArvoEvent that triggered workflow completion
286
+ * @param record - Final workflow state that was just persisted
287
+ * @param prevRecord - Previous state before final persistence
288
+ * @param metadata - Workflow context (subject, parent subject, source)
289
+ * @param span - Optional OpenTelemetry span for observability
290
+ */
291
+ SyncEventResource.prototype.cleanup = function (event, record, prevRecord, metadata, span) {
292
+ return __awaiter(this, void 0, void 0, function () {
293
+ var err_2;
294
+ return __generator(this, function (_a) {
295
+ switch (_a.label) {
296
+ case 0:
297
+ if (!this.memory.cleanup) {
298
+ (0, arvo_core_1.logToSpan)({
299
+ level: 'INFO',
300
+ message: 'Cleanup hook not implemented by memory backend, skipping cleanup operation',
301
+ }, span);
302
+ return [2 /*return*/];
303
+ }
304
+ _a.label = 1;
305
+ case 1:
306
+ _a.trys.push([1, 3, , 4]);
307
+ (0, arvo_core_1.logToSpan)({
308
+ level: 'INFO',
309
+ message: 'Invoking cleanup hook for completed workflow',
310
+ }, span);
311
+ return [4 /*yield*/, this.memory.cleanup(event.subject, record, prevRecord, metadata)];
312
+ case 2:
313
+ _a.sent();
314
+ (0, arvo_core_1.logToSpan)({
315
+ level: 'INFO',
316
+ message: 'Cleanup hook completed successfully',
317
+ }, span);
318
+ return [3 /*break*/, 4];
319
+ case 3:
320
+ err_2 = _a.sent();
321
+ (0, arvo_core_1.logToSpan)({
322
+ level: 'ERROR',
323
+ message: "Cleanup operation failed (non-critical): ".concat(err_2.message),
324
+ }, span);
325
+ return [3 /*break*/, 4];
326
+ case 4: return [2 /*return*/];
327
+ }
328
+ });
329
+ });
330
+ };
278
331
  return SyncEventResource;
279
332
  }());
280
333
  exports.SyncEventResource = SyncEventResource;
package/dist/index.d.ts CHANGED
@@ -20,11 +20,11 @@ import { IMachineExectionEngine } from './MachineExecutionEngine/interface';
20
20
  import { ExecuteMachineInput, ExecuteMachineOutput } from './MachineExecutionEngine/types';
21
21
  import { SimpleMachineMemory } from './MachineMemory/Simple';
22
22
  import { TelemetredSimpleMachineMemory } from './MachineMemory/TelemetredSimple';
23
- import { IMachineMemory } from './MachineMemory/interface';
23
+ import { IMachineMemory, MachineMemoryMetadata } from './MachineMemory/interface';
24
24
  import { MachineRegistry } from './MachineRegistry';
25
25
  import { IMachineRegistry } from './MachineRegistry/interface';
26
26
  import { ConfigViolation, ContractViolation, ExecutionViolation } from './errors';
27
- import { ArvoEventHandlerOpenTelemetryOptions, ArvoEventHandlerOtelSpanOptions, EventHandlerFactory, PartialExcept } from './types';
27
+ import { ArvoEventHandlerOpenTelemetryOptions, ArvoEventHandlerOtelSpanOptions, EventHandlerFactory, Materializable, Materialized, PartialExcept } from './types';
28
28
  import { coalesce, coalesceOrDefault, getValueOrDefault, isNullOrUndefined } from './utils';
29
29
  import { SimpleEventBroker } from './utils/SimpleEventBroker';
30
30
  import { createSimpleEventBroker } from './utils/SimpleEventBroker/helper';
@@ -34,4 +34,4 @@ declare const xstate: {
34
34
  emit: typeof emit;
35
35
  assign: typeof assign;
36
36
  };
37
- export { ArvoEventHandler, createArvoEventHandler, IArvoEventHandler, ArvoEventHandlerFunctionOutput, ArvoEventHandlerFunctionInput, ArvoEventHandlerFunction, PartialExcept, isNullOrUndefined, getValueOrDefault, coalesce, coalesceOrDefault, ArvoEventHandlerOpenTelemetryOptions, EventHandlerFactory, ContractViolation, ConfigViolation, ExecutionViolation, ArvoMachine, setupArvoMachine, ArvoMachineContext, EnqueueArvoEventActionParam, IMachineRegistry, MachineRegistry, MachineExecutionEngine, IMachineExectionEngine, ExecuteMachineInput, ExecuteMachineOutput, IMachineMemory, SimpleMachineMemory, MachineMemoryRecord, ArvoOrchestratorParam, TransactionViolation, TransactionViolationCause, ArvoOrchestrator, createArvoOrchestrator, SimpleEventBroker, createSimpleEventBroker, TelemetredSimpleMachineMemory, xstate, ArvoResumable, createArvoResumable, ArvoResumableHandler, ArvoResumableState, ArvoDomain, resolveEventDomain, isTransactionViolationError, OrchestrationExecutionStatus, ArvoEventHandlerOtelSpanOptions, runArvoTestSuites, ArvoTestStep, ArvoTestCase, ArvoTestConfig, ArvoTestSuite, ArvoTestResult, IArvoTestFramework, };
37
+ export { ArvoEventHandler, createArvoEventHandler, IArvoEventHandler, ArvoEventHandlerFunctionOutput, ArvoEventHandlerFunctionInput, ArvoEventHandlerFunction, PartialExcept, isNullOrUndefined, getValueOrDefault, coalesce, coalesceOrDefault, ArvoEventHandlerOpenTelemetryOptions, EventHandlerFactory, ContractViolation, ConfigViolation, ExecutionViolation, ArvoMachine, setupArvoMachine, ArvoMachineContext, EnqueueArvoEventActionParam, IMachineRegistry, MachineRegistry, MachineExecutionEngine, IMachineExectionEngine, ExecuteMachineInput, ExecuteMachineOutput, IMachineMemory, SimpleMachineMemory, MachineMemoryRecord, ArvoOrchestratorParam, TransactionViolation, TransactionViolationCause, ArvoOrchestrator, createArvoOrchestrator, SimpleEventBroker, createSimpleEventBroker, TelemetredSimpleMachineMemory, xstate, ArvoResumable, createArvoResumable, ArvoResumableHandler, ArvoResumableState, ArvoDomain, resolveEventDomain, isTransactionViolationError, OrchestrationExecutionStatus, ArvoEventHandlerOtelSpanOptions, runArvoTestSuites, ArvoTestStep, ArvoTestCase, ArvoTestConfig, ArvoTestSuite, ArvoTestResult, IArvoTestFramework, Materializable, Materialized, MachineMemoryMetadata, };
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.runArvoTestSuites = exports.OrchestrationExecutionStatus = exports.isTransactionViolationError = exports.resolveEventDomain = exports.ArvoDomain = exports.createArvoResumable = exports.ArvoResumable = exports.xstate = exports.TelemetredSimpleMachineMemory = exports.createSimpleEventBroker = exports.SimpleEventBroker = exports.createArvoOrchestrator = exports.ArvoOrchestrator = exports.TransactionViolationCause = exports.TransactionViolation = exports.SimpleMachineMemory = exports.MachineExecutionEngine = exports.MachineRegistry = exports.setupArvoMachine = exports.ArvoMachine = exports.ExecutionViolation = exports.ConfigViolation = exports.ContractViolation = exports.coalesceOrDefault = exports.coalesce = exports.getValueOrDefault = exports.isNullOrUndefined = exports.createArvoEventHandler = exports.ArvoEventHandler = void 0;
6
+ exports.Materialized = exports.runArvoTestSuites = exports.OrchestrationExecutionStatus = exports.isTransactionViolationError = exports.resolveEventDomain = exports.ArvoDomain = exports.createArvoResumable = exports.ArvoResumable = exports.xstate = exports.TelemetredSimpleMachineMemory = exports.createSimpleEventBroker = exports.SimpleEventBroker = exports.createArvoOrchestrator = exports.ArvoOrchestrator = exports.TransactionViolationCause = exports.TransactionViolation = exports.SimpleMachineMemory = exports.MachineExecutionEngine = exports.MachineRegistry = exports.setupArvoMachine = exports.ArvoMachine = exports.ExecutionViolation = exports.ConfigViolation = exports.ContractViolation = exports.coalesceOrDefault = exports.coalesce = exports.getValueOrDefault = exports.isNullOrUndefined = exports.createArvoEventHandler = exports.ArvoEventHandler = void 0;
7
7
  var xstate_1 = require("xstate");
8
8
  var ArvoDomain_1 = require("./ArvoDomain");
9
9
  Object.defineProperty(exports, "ArvoDomain", { enumerable: true, get: function () { return ArvoDomain_1.ArvoDomain; } });
@@ -42,6 +42,8 @@ var errors_1 = require("./errors");
42
42
  Object.defineProperty(exports, "ConfigViolation", { enumerable: true, get: function () { return errors_1.ConfigViolation; } });
43
43
  Object.defineProperty(exports, "ContractViolation", { enumerable: true, get: function () { return errors_1.ContractViolation; } });
44
44
  Object.defineProperty(exports, "ExecutionViolation", { enumerable: true, get: function () { return errors_1.ExecutionViolation; } });
45
+ var types_1 = require("./types");
46
+ Object.defineProperty(exports, "Materialized", { enumerable: true, get: function () { return types_1.Materialized; } });
45
47
  var utils_1 = require("./utils");
46
48
  Object.defineProperty(exports, "coalesce", { enumerable: true, get: function () { return utils_1.coalesce; } });
47
49
  Object.defineProperty(exports, "coalesceOrDefault", { enumerable: true, get: function () { return utils_1.coalesceOrDefault; } });
package/dist/types.d.ts CHANGED
@@ -2,6 +2,46 @@ import type { SpanOptions } from '@opentelemetry/api';
2
2
  import type { ArvoEvent } from 'arvo-core';
3
3
  import type IArvoEventHandler from './IArvoEventHandler';
4
4
  export type NonEmptyArray<T> = [T, ...T[]];
5
+ /**
6
+ * Represents a value that may or may not have been materialized yet.
7
+ *
8
+ * This type models the distinction between a value whose information is still pending
9
+ * and one that has been fully materialized with its concrete value. The materialized
10
+ * value itself can be of any type, including null or undefined.
11
+ *
12
+ * @template T The type of the value once materialized
13
+ *
14
+ * @remarks
15
+ * Use this type when you need to explicitly track whether information has been
16
+ * acquired or computed, distinguishing between "we haven't obtained this yet"
17
+ * and "we have obtained this, and here is the value".
18
+ *
19
+ * The 'pending' state indicates materialization has not yet occurred.
20
+ * The 'materialized' state indicates the value has been obtained and is available.
21
+ *
22
+ * This is distinct from optional or nullable types, which represent whether a value
23
+ * exists, rather than whether the information about that value has been determined.
24
+ */
25
+ export type Materializable<T> = {
26
+ state: 'pending';
27
+ } | {
28
+ state: 'resolved';
29
+ value: T;
30
+ };
31
+ export declare const Materialized: {
32
+ pending: <T>() => Extract<Materializable<T>, {
33
+ state: "pending";
34
+ }>;
35
+ resolved: <T>(value: T) => Extract<Materializable<T>, {
36
+ state: "resolved";
37
+ }>;
38
+ isPending: <T extends Materializable<any>>(value: T) => value is Extract<T, {
39
+ state: "pending";
40
+ }>;
41
+ isResolved: <T extends Materializable<any>>(value: T) => value is Extract<T, {
42
+ state: "resolved";
43
+ }>;
44
+ };
5
45
  /**
6
46
  * Makes properties optional except specified keys
7
47
  *
package/dist/types.js CHANGED
@@ -1,2 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Materialized = void 0;
4
+ exports.Materialized = {
5
+ pending: function () { return ({ state: 'pending' }); },
6
+ resolved: function (value) { return ({ state: 'resolved', value: value }); },
7
+ isPending: function (value) {
8
+ return value.state === 'pending';
9
+ },
10
+ isResolved: function (value) {
11
+ return value.state === 'resolved';
12
+ },
13
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arvo-event-handler",
3
- "version": "3.0.25",
3
+ "version": "3.0.27",
4
4
  "description": "A complete set of orthogonal event handler and orchestration primitives for Arvo based applications, featuring declarative state machines (XState), imperative resumables for agentic workflows, contract-based routing, OpenTelemetry observability, and in-memory event broker for building composable event-driven architectures.",
5
5
  "main": "dist/index.js",
6
6
  "repository": {
@@ -36,10 +36,12 @@
36
36
  "observability"
37
37
  ],
38
38
  "author": "Saad Ahmad <saadkwi12@hotmail.com>",
39
+ "homepage": "https://www.arvo.land",
39
40
  "license": "MIT",
40
41
  "devDependencies": {
41
42
  "@biomejs/biome": "1.9.4",
42
43
  "@jest/globals": "29.7.0",
44
+ "@opentelemetry/core": "2.2.0",
43
45
  "@opentelemetry/auto-instrumentations-node": "0.49.1",
44
46
  "@opentelemetry/exporter-metrics-otlp-proto": "0.52.1",
45
47
  "@opentelemetry/exporter-trace-otlp-grpc": "0.53.0",
@@ -64,11 +66,10 @@
64
66
  },
65
67
  "dependencies": {
66
68
  "@opentelemetry/api": "1.9.0",
67
- "@opentelemetry/core": "1.30.1",
68
- "arvo-core": "3.0.25",
69
+ "arvo-core": "3.0.27",
69
70
  "uuid": "11.1.0",
70
- "xstate": "5.24.0",
71
- "zod": "3.25.74",
71
+ "xstate": "5.25.0",
72
+ "zod": "3.25.76",
72
73
  "zod-to-json-schema": "3.25.0"
73
74
  },
74
75
  "engines": {