arvo-event-handler 2.3.1 → 3.0.1
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/dist/AbstractArvoEventHandler/index.d.ts +1 -1
- package/dist/ArvoEventHandler/helpers.d.ts +40 -6
- package/dist/ArvoEventHandler/helpers.js +40 -6
- package/dist/ArvoEventHandler/index.d.ts +78 -49
- package/dist/ArvoEventHandler/index.js +151 -81
- package/dist/ArvoEventHandler/types.d.ts +25 -2
- package/dist/ArvoMachine/createMachine.d.ts +208 -0
- package/dist/ArvoMachine/createMachine.js +283 -0
- package/dist/ArvoMachine/index.d.ts +93 -0
- package/dist/ArvoMachine/index.js +160 -0
- package/dist/ArvoMachine/types.d.ts +194 -0
- package/dist/ArvoMachine/utils.d.ts +40 -0
- package/dist/ArvoMachine/utils.js +70 -0
- package/dist/ArvoOrchestrator/error.d.ts +16 -0
- package/dist/ArvoOrchestrator/error.js +43 -0
- package/dist/ArvoOrchestrator/factory.d.ts +28 -0
- package/dist/ArvoOrchestrator/factory.js +56 -0
- package/dist/ArvoOrchestrator/index.d.ts +69 -0
- package/dist/ArvoOrchestrator/index.js +597 -0
- package/dist/ArvoOrchestrator/types.d.ts +98 -0
- package/dist/ArvoResumable/factory.d.ts +50 -0
- package/dist/ArvoResumable/factory.js +70 -0
- package/dist/ArvoResumable/index.d.ts +141 -0
- package/dist/ArvoResumable/index.js +694 -0
- package/dist/ArvoResumable/types.d.ts +147 -0
- package/dist/ArvoResumable/types.js +2 -0
- package/dist/MachineExecutionEngine/index.d.ts +29 -0
- package/dist/MachineExecutionEngine/index.js +132 -0
- package/dist/MachineExecutionEngine/interface.d.ts +14 -0
- package/dist/MachineExecutionEngine/interface.js +2 -0
- package/dist/MachineExecutionEngine/types.d.ts +14 -0
- package/dist/MachineExecutionEngine/types.js +2 -0
- package/dist/MachineMemory/Simple.d.ts +51 -0
- package/dist/MachineMemory/Simple.js +158 -0
- package/dist/MachineMemory/TelemetredSimple.d.ts +51 -0
- package/dist/MachineMemory/TelemetredSimple.js +230 -0
- package/dist/MachineMemory/interface.d.ts +57 -0
- package/dist/MachineMemory/interface.js +2 -0
- package/dist/MachineMemory/utils.d.ts +1 -0
- package/dist/MachineMemory/utils.js +18 -0
- package/dist/MachineRegistry/index.d.ts +37 -0
- package/dist/MachineRegistry/index.js +87 -0
- package/dist/MachineRegistry/interface.d.ts +21 -0
- package/dist/MachineRegistry/interface.js +2 -0
- package/dist/SyncEventResource/index.d.ts +110 -0
- package/dist/SyncEventResource/index.js +280 -0
- package/dist/SyncEventResource/types.d.ts +2 -0
- package/dist/SyncEventResource/types.js +2 -0
- package/dist/index.d.ts +26 -8
- package/dist/index.js +39 -16
- package/dist/utils/SimpleEventBroker/helper.d.ts +166 -0
- package/dist/utils/SimpleEventBroker/helper.js +276 -0
- package/dist/utils/SimpleEventBroker/index.d.ts +96 -0
- package/dist/utils/SimpleEventBroker/index.js +259 -0
- package/dist/utils/SimpleEventBroker/types.d.ts +6 -0
- package/dist/utils/SimpleEventBroker/types.js +2 -0
- package/dist/utils/SimpleEventBroker/utils.d.ts +1 -0
- package/dist/utils/SimpleEventBroker/utils.js +10 -0
- package/dist/{utils.d.ts → utils/index.d.ts} +3 -36
- package/dist/utils/index.js +91 -0
- package/dist/utils/object/index.d.ts +37 -0
- package/dist/utils/object/index.js +63 -0
- package/package.json +6 -12
- package/dist/ArvoEventRouter/helpers.d.ts +0 -19
- package/dist/ArvoEventRouter/helpers.js +0 -22
- package/dist/ArvoEventRouter/index.d.ts +0 -89
- package/dist/ArvoEventRouter/index.js +0 -267
- package/dist/ArvoEventRouter/types.d.ts +0 -36
- package/dist/ArvoEventRouter/utils.d.ts +0 -29
- package/dist/ArvoEventRouter/utils.js +0 -42
- package/dist/MultiArvoEventHandler/helpers.d.ts +0 -48
- package/dist/MultiArvoEventHandler/helpers.js +0 -56
- package/dist/MultiArvoEventHandler/index.d.ts +0 -68
- package/dist/MultiArvoEventHandler/index.js +0 -205
- package/dist/MultiArvoEventHandler/types.d.ts +0 -64
- package/dist/utils.js +0 -190
- /package/dist/{ArvoEventRouter → ArvoMachine}/types.js +0 -0
- /package/dist/{MultiArvoEventHandler → ArvoOrchestrator}/types.js +0 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
24
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.TelemetredSimpleMachineMemory = void 0;
|
|
51
|
+
var api_1 = require("@opentelemetry/api");
|
|
52
|
+
var arvo_core_1 = require("arvo-core");
|
|
53
|
+
var utils_1 = require("./utils");
|
|
54
|
+
/**
|
|
55
|
+
* A telemetred In-memory implementation of machine state storage for single-instance NodeJS apps.
|
|
56
|
+
*
|
|
57
|
+
* Best for: Container apps, request-scoped workflows, testing, demos
|
|
58
|
+
* Not for: Multi-instance deployments, persistent workflows, distributed systems
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* const memory = new TelemetredSimpleMachineMemory();
|
|
62
|
+
* const orchestrator = createArvoOrchestrator({
|
|
63
|
+
* memory,
|
|
64
|
+
* executionunits: 0.1,
|
|
65
|
+
* machines: [workflow]
|
|
66
|
+
* });
|
|
67
|
+
*/
|
|
68
|
+
var TelemetredSimpleMachineMemory = /** @class */ (function () {
|
|
69
|
+
function TelemetredSimpleMachineMemory() {
|
|
70
|
+
this.memoryMap = new Map();
|
|
71
|
+
this.lockMap = new Map();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Gets stored state for a machine instance
|
|
75
|
+
* @param id Machine instance ID
|
|
76
|
+
* @returns State data or null if not found
|
|
77
|
+
* @throws {Error} When id is empty or undefined
|
|
78
|
+
*/
|
|
79
|
+
TelemetredSimpleMachineMemory.prototype.read = function (id) {
|
|
80
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
81
|
+
var _this = this;
|
|
82
|
+
return __generator(this, function (_a) {
|
|
83
|
+
switch (_a.label) {
|
|
84
|
+
case 0: return [4 /*yield*/, arvo_core_1.ArvoOpenTelemetry.getInstance().startActiveSpan({
|
|
85
|
+
name: 'Read Simple Memory',
|
|
86
|
+
spanOptions: {
|
|
87
|
+
kind: api_1.SpanKind.INTERNAL,
|
|
88
|
+
attributes: {
|
|
89
|
+
'arvo.memory.id': id,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
fn: function () { return __awaiter(_this, void 0, void 0, function () {
|
|
93
|
+
var _a;
|
|
94
|
+
return __generator(this, function (_b) {
|
|
95
|
+
if (!id) {
|
|
96
|
+
throw new Error('Machine ID is required for read operation');
|
|
97
|
+
}
|
|
98
|
+
return [2 /*return*/, (_a = this.memoryMap.get(id)) !== null && _a !== void 0 ? _a : null];
|
|
99
|
+
});
|
|
100
|
+
}); },
|
|
101
|
+
})];
|
|
102
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Stores state for a machine instance
|
|
109
|
+
* @param id Machine instance ID
|
|
110
|
+
* @param data State to store
|
|
111
|
+
* @throws {Error} When id is empty/undefined or data is null/undefined
|
|
112
|
+
*/
|
|
113
|
+
TelemetredSimpleMachineMemory.prototype.write = function (id, data) {
|
|
114
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
115
|
+
var _this = this;
|
|
116
|
+
return __generator(this, function (_a) {
|
|
117
|
+
switch (_a.label) {
|
|
118
|
+
case 0: return [4 /*yield*/, arvo_core_1.ArvoOpenTelemetry.getInstance().startActiveSpan({
|
|
119
|
+
name: 'Write Simple Memory',
|
|
120
|
+
spanOptions: {
|
|
121
|
+
kind: api_1.SpanKind.INTERNAL,
|
|
122
|
+
attributes: {
|
|
123
|
+
'arvo.memory.id': id,
|
|
124
|
+
'arvo.memory.record.bytes': data ? (0, utils_1.getJsonSize)(data) : 0,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
fn: function () { return __awaiter(_this, void 0, void 0, function () {
|
|
128
|
+
return __generator(this, function (_a) {
|
|
129
|
+
if (!id) {
|
|
130
|
+
throw new Error('Machine ID is required for write operation');
|
|
131
|
+
}
|
|
132
|
+
if (!data) {
|
|
133
|
+
throw new Error('Data is required for write operation');
|
|
134
|
+
}
|
|
135
|
+
this.memoryMap.set(id, __assign({}, data));
|
|
136
|
+
return [2 /*return*/];
|
|
137
|
+
});
|
|
138
|
+
}); },
|
|
139
|
+
})];
|
|
140
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Attempts to acquire lock for machine instance
|
|
147
|
+
* @param id Machine instance ID
|
|
148
|
+
* @returns Success status of lock acquisition
|
|
149
|
+
* @throws {Error} When id is empty or undefined
|
|
150
|
+
*/
|
|
151
|
+
TelemetredSimpleMachineMemory.prototype.lock = function (id) {
|
|
152
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
153
|
+
var _this = this;
|
|
154
|
+
return __generator(this, function (_a) {
|
|
155
|
+
switch (_a.label) {
|
|
156
|
+
case 0: return [4 /*yield*/, arvo_core_1.ArvoOpenTelemetry.getInstance().startActiveSpan({
|
|
157
|
+
name: 'Lock Simple Memory',
|
|
158
|
+
spanOptions: {
|
|
159
|
+
kind: api_1.SpanKind.INTERNAL,
|
|
160
|
+
attributes: {
|
|
161
|
+
'arvo.memory.id': id,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
fn: function () { return __awaiter(_this, void 0, void 0, function () {
|
|
165
|
+
return __generator(this, function (_a) {
|
|
166
|
+
if (!id) {
|
|
167
|
+
throw new Error('Machine ID is required for lock operation');
|
|
168
|
+
}
|
|
169
|
+
if (this.lockMap.get(id)) {
|
|
170
|
+
return [2 /*return*/, false];
|
|
171
|
+
}
|
|
172
|
+
this.lockMap.set(id, true);
|
|
173
|
+
return [2 /*return*/, true];
|
|
174
|
+
});
|
|
175
|
+
}); },
|
|
176
|
+
})];
|
|
177
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Releases lock for machine instance
|
|
184
|
+
* @param id Machine instance ID
|
|
185
|
+
* @returns True when lock is released
|
|
186
|
+
* @throws {Error} When id is empty or undefined
|
|
187
|
+
*/
|
|
188
|
+
TelemetredSimpleMachineMemory.prototype.unlock = function (id) {
|
|
189
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
190
|
+
var _this = this;
|
|
191
|
+
return __generator(this, function (_a) {
|
|
192
|
+
switch (_a.label) {
|
|
193
|
+
case 0: return [4 /*yield*/, arvo_core_1.ArvoOpenTelemetry.getInstance().startActiveSpan({
|
|
194
|
+
name: 'Unlock Simple Memory',
|
|
195
|
+
spanOptions: {
|
|
196
|
+
kind: api_1.SpanKind.INTERNAL,
|
|
197
|
+
attributes: {
|
|
198
|
+
'arvo.memory.id': id,
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
fn: function () { return __awaiter(_this, void 0, void 0, function () {
|
|
202
|
+
return __generator(this, function (_a) {
|
|
203
|
+
if (!id) {
|
|
204
|
+
throw new Error('Machine ID is required for unlock operation');
|
|
205
|
+
}
|
|
206
|
+
this.lockMap.delete(id);
|
|
207
|
+
return [2 /*return*/, true];
|
|
208
|
+
});
|
|
209
|
+
}); },
|
|
210
|
+
})];
|
|
211
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* Clears all stored data and locks
|
|
218
|
+
*/
|
|
219
|
+
TelemetredSimpleMachineMemory.prototype.clear = function (key) {
|
|
220
|
+
if (key) {
|
|
221
|
+
this.memoryMap.delete(key);
|
|
222
|
+
this.lockMap.delete(key);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
this.memoryMap.clear();
|
|
226
|
+
this.lockMap.clear();
|
|
227
|
+
};
|
|
228
|
+
return TelemetredSimpleMachineMemory;
|
|
229
|
+
}());
|
|
230
|
+
exports.TelemetredSimpleMachineMemory = TelemetredSimpleMachineMemory;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
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
|
|
5
|
+
*/
|
|
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
|
|
31
|
+
* @throws Error if write operation fails
|
|
32
|
+
*/
|
|
33
|
+
write(id: string, data: T, prevData: T | null): Promise<void>;
|
|
34
|
+
/**
|
|
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)
|
|
41
|
+
*/
|
|
42
|
+
lock(id: string): Promise<boolean>;
|
|
43
|
+
/**
|
|
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.
|
|
47
|
+
*
|
|
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
|
|
52
|
+
*
|
|
53
|
+
* @param id - Machine ID
|
|
54
|
+
* @returns True if unlocked successfully
|
|
55
|
+
*/
|
|
56
|
+
unlock(id: string): Promise<boolean>;
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getJsonSize(obj: Record<string, any>): number;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getJsonSize = getJsonSize;
|
|
4
|
+
var arvo_core_1 = require("arvo-core");
|
|
5
|
+
function getJsonSize(obj) {
|
|
6
|
+
try {
|
|
7
|
+
var jsonString = JSON.stringify(obj);
|
|
8
|
+
// Use TextEncoder to get actual UTF-8 byte length
|
|
9
|
+
return new TextEncoder().encode(jsonString).length;
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
(0, arvo_core_1.logToSpan)({
|
|
13
|
+
level: 'WARNING',
|
|
14
|
+
message: "Error while calculating the size of the machine memory record. Error: ".concat(e.message),
|
|
15
|
+
});
|
|
16
|
+
return -1;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type ArvoEvent } from 'arvo-core';
|
|
2
|
+
import type ArvoMachine from '../ArvoMachine';
|
|
3
|
+
import type { IMachineRegistry } from './interface';
|
|
4
|
+
import type { ArvoEventHandlerOpenTelemetryOptions } from '../types';
|
|
5
|
+
/**
|
|
6
|
+
* Registry for managing and resolving ArvoMachine instances.
|
|
7
|
+
* Provides functionality to store multiple machine instances and resolve them based on events.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* The registry must contain at least one machine upon initialization.
|
|
11
|
+
* Each machine in the registry should have a unique combination of version and source.
|
|
12
|
+
*/
|
|
13
|
+
export declare class MachineRegistry implements IMachineRegistry {
|
|
14
|
+
machines: ArvoMachine<any, any, any, any, any>[];
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new MachineRegistry instance with the provided machines.
|
|
17
|
+
*
|
|
18
|
+
* @param args - Variable number of ArvoMachine instances to register
|
|
19
|
+
* @throws {Error} When no machines are provided during initialization
|
|
20
|
+
*/
|
|
21
|
+
constructor(...args: ArvoMachine<any, any, any, any, any>[]);
|
|
22
|
+
/**
|
|
23
|
+
* Resolves and returns a machine instance based on the provided event.
|
|
24
|
+
* The resolution is performed using the orchestrator information in the event's subject.
|
|
25
|
+
*
|
|
26
|
+
* @param event - The event containing orchestration subject information
|
|
27
|
+
* @param opentelemetry Telemetry configuration for tracing
|
|
28
|
+
* @returns The matching ArvoMachine instance or null if not found
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const machine = registry.resolve(incomingEvent);
|
|
33
|
+
* // Use resolved machine for event processing
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
resolve(event: ArvoEvent, opentelemetry?: ArvoEventHandlerOpenTelemetryOptions): ArvoMachine<any, any, any, any, any> | null;
|
|
37
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MachineRegistry = void 0;
|
|
4
|
+
var api_1 = require("@opentelemetry/api");
|
|
5
|
+
var arvo_core_1 = require("arvo-core");
|
|
6
|
+
/**
|
|
7
|
+
* Registry for managing and resolving ArvoMachine instances.
|
|
8
|
+
* Provides functionality to store multiple machine instances and resolve them based on events.
|
|
9
|
+
*
|
|
10
|
+
* @remarks
|
|
11
|
+
* The registry must contain at least one machine upon initialization.
|
|
12
|
+
* Each machine in the registry should have a unique combination of version and source.
|
|
13
|
+
*/
|
|
14
|
+
var MachineRegistry = /** @class */ (function () {
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new MachineRegistry instance with the provided machines.
|
|
17
|
+
*
|
|
18
|
+
* @param args - Variable number of ArvoMachine instances to register
|
|
19
|
+
* @throws {Error} When no machines are provided during initialization
|
|
20
|
+
*/
|
|
21
|
+
function MachineRegistry() {
|
|
22
|
+
var args = [];
|
|
23
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
24
|
+
args[_i] = arguments[_i];
|
|
25
|
+
}
|
|
26
|
+
this.machines = args;
|
|
27
|
+
if (!this.machines.length) {
|
|
28
|
+
throw new Error('Machine registry initialization failed: No machines provided. At least one machine must be registered.');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolves and returns a machine instance based on the provided event.
|
|
33
|
+
* The resolution is performed using the orchestrator information in the event's subject.
|
|
34
|
+
*
|
|
35
|
+
* @param event - The event containing orchestration subject information
|
|
36
|
+
* @param opentelemetry Telemetry configuration for tracing
|
|
37
|
+
* @returns The matching ArvoMachine instance or null if not found
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const machine = registry.resolve(incomingEvent);
|
|
42
|
+
* // Use resolved machine for event processing
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
MachineRegistry.prototype.resolve = function (event, opentelemetry) {
|
|
46
|
+
var _this = this;
|
|
47
|
+
if (opentelemetry === void 0) { opentelemetry = {
|
|
48
|
+
inheritFrom: 'CONTEXT',
|
|
49
|
+
}; }
|
|
50
|
+
return arvo_core_1.ArvoOpenTelemetry.getInstance().startActiveSpan({
|
|
51
|
+
name: 'Resolve Machine',
|
|
52
|
+
spanOptions: {
|
|
53
|
+
kind: api_1.SpanKind.INTERNAL,
|
|
54
|
+
attributes: event.otelAttributes,
|
|
55
|
+
},
|
|
56
|
+
context: opentelemetry.inheritFrom === 'EVENT'
|
|
57
|
+
? {
|
|
58
|
+
inheritFrom: 'TRACE_HEADERS',
|
|
59
|
+
traceHeaders: {
|
|
60
|
+
traceparent: event.traceparent,
|
|
61
|
+
tracestate: event.tracestate,
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
: {
|
|
65
|
+
inheritFrom: 'CONTEXT',
|
|
66
|
+
context: api_1.context.active(),
|
|
67
|
+
},
|
|
68
|
+
fn: function (span) {
|
|
69
|
+
var _a;
|
|
70
|
+
var subject = arvo_core_1.ArvoOrchestrationSubject.parse(event.subject);
|
|
71
|
+
var _b = subject.orchestrator, name = _b.name, version = _b.version;
|
|
72
|
+
span.setAttributes({
|
|
73
|
+
'arvo.parsed.subject.orchestrator.name': name,
|
|
74
|
+
'arvo.parsed.subject.orchestrator.version': version,
|
|
75
|
+
});
|
|
76
|
+
var machine = (_a = _this.machines.find(function (item) { return item.version === version && item.source === name; })) !== null && _a !== void 0 ? _a : null;
|
|
77
|
+
(0, arvo_core_1.logToSpan)({
|
|
78
|
+
level: machine ? 'INFO' : 'WARNING',
|
|
79
|
+
message: machine ? "Resolved machine for type ".concat(name, "@").concat(version) : "No machine found for ".concat(name, "@").concat(version),
|
|
80
|
+
});
|
|
81
|
+
return machine;
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
return MachineRegistry;
|
|
86
|
+
}());
|
|
87
|
+
exports.MachineRegistry = MachineRegistry;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ArvoEvent } from 'arvo-core';
|
|
2
|
+
import type ArvoMachine from '../ArvoMachine';
|
|
3
|
+
import type { ArvoEventHandlerOpenTelemetryOptions } from '../types';
|
|
4
|
+
/**
|
|
5
|
+
* Interface for managing and resolving state machine instances.
|
|
6
|
+
*/
|
|
7
|
+
export interface IMachineRegistry {
|
|
8
|
+
/**
|
|
9
|
+
* Collection of registered machine instances.
|
|
10
|
+
* Each machine should have a unique combination of version and source.
|
|
11
|
+
*/
|
|
12
|
+
machines: ArvoMachine<any, any, any, any, any>[];
|
|
13
|
+
/**
|
|
14
|
+
* Resolves a machine instance based on event information.
|
|
15
|
+
*
|
|
16
|
+
* @param event - Event containing orchestration subject information
|
|
17
|
+
* @param opentelemetry - Configuration for telemetry and tracing
|
|
18
|
+
* @returns Matching ArvoMachine instance for the event or null if not found
|
|
19
|
+
*/
|
|
20
|
+
resolve: (event: ArvoEvent, opentelemetry: ArvoEventHandlerOpenTelemetryOptions) => ArvoMachine<any, any, any, any, any> | null;
|
|
21
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { type ArvoEvent } from 'arvo-core';
|
|
2
|
+
import type { IMachineMemory } from '../MachineMemory/interface';
|
|
3
|
+
import type { AcquiredLockStatusType, ReleasedLockStatusType } from './types';
|
|
4
|
+
import type { Span } from '@opentelemetry/api';
|
|
5
|
+
/**
|
|
6
|
+
* A synchronous event resource that manages machine memory state based on event subjects.
|
|
7
|
+
*
|
|
8
|
+
* This class provides a distributed-system-safe mechanism for persisting and retrieving machine memory
|
|
9
|
+
* objects that are correlated with ArvoEvent subjects. It acts as a key-value store where
|
|
10
|
+
* the event subject serves as the key and the memory object serves as the value.
|
|
11
|
+
*
|
|
12
|
+
* Key features:
|
|
13
|
+
* - JSON serializable memory persistence
|
|
14
|
+
* - Optional resource locking for distributed concurrent access control
|
|
15
|
+
* - Subject-based memory correlation across multiple service instances
|
|
16
|
+
* - Transaction-safe operations with proper error handling
|
|
17
|
+
* - Optional OpenTelemetry span integration for observability
|
|
18
|
+
*
|
|
19
|
+
* @template T - The type of the memory object, must extend Record<string, any> and be JSON serializable
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* type MyMemory = {
|
|
24
|
+
* counter: number;
|
|
25
|
+
* status: string;
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
* class MemoryImplementation implements IMachineMemory<MyMemory> { ... }
|
|
29
|
+
*
|
|
30
|
+
* const resource = new SyncEventResource<MyMemory>(
|
|
31
|
+
* new MemoryImplementation(),
|
|
32
|
+
* true // enable resource locking for distributed systems
|
|
33
|
+
* );
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare class SyncEventResource<T extends Record<string, any>> {
|
|
37
|
+
memory: IMachineMemory<T>;
|
|
38
|
+
requiresResourceLocking: boolean;
|
|
39
|
+
constructor(memory: IMachineMemory<T>, requiresResourceLocking: boolean);
|
|
40
|
+
/**
|
|
41
|
+
* Acquires a lock on the event subject to prevent concurrent access across distributed services.
|
|
42
|
+
*
|
|
43
|
+
* This method ensures distributed-system-safe access to the memory resource by preventing
|
|
44
|
+
* multiple service instances from modifying the same event subject simultaneously. If resource
|
|
45
|
+
* locking is disabled, it will skip the lock acquisition process. The lock is subject-specific,
|
|
46
|
+
* meaning different event subjects can be processed concurrently across services.
|
|
47
|
+
*
|
|
48
|
+
* @returns A promise that resolves to the lock acquisition status:
|
|
49
|
+
* - 'ACQUIRED': Lock was successfully acquired
|
|
50
|
+
* - 'NOT_ACQUIRED': Lock acquisition failed (resource busy by another service)
|
|
51
|
+
* - 'NOOP': Lock acquisition was skipped (locking disabled)
|
|
52
|
+
*
|
|
53
|
+
* @throws {TransactionViolation} When lock acquisition fails due to system errors
|
|
54
|
+
*/
|
|
55
|
+
acquireLock(event: ArvoEvent, span?: Span): Promise<AcquiredLockStatusType>;
|
|
56
|
+
/**
|
|
57
|
+
* Retrieves the current state from memory for the given event subject.
|
|
58
|
+
*
|
|
59
|
+
* This method reads the persisted memory object associated with the event's subject
|
|
60
|
+
* from the distributed storage system. If no memory exists for the subject, it returns null.
|
|
61
|
+
* The operation is wrapped in proper error handling to ensure transaction safety across
|
|
62
|
+
* distributed service instances.
|
|
63
|
+
*
|
|
64
|
+
* @returns A promise that resolves to the memory object if found, or null if no memory exists
|
|
65
|
+
*
|
|
66
|
+
* @throws {TransactionViolation} When the read operation fails due to storage errors
|
|
67
|
+
*/
|
|
68
|
+
acquireState(event: ArvoEvent, span?: Span): Promise<T | null>;
|
|
69
|
+
/**
|
|
70
|
+
* Persists the updated memory state to distributed storage.
|
|
71
|
+
*
|
|
72
|
+
* This method writes the new memory record to the distributed storage system, associating
|
|
73
|
+
* it with the event's subject. It provides both the new record and the previous record for
|
|
74
|
+
* implementations that need to perform atomic updates, maintain audit trails, or handle
|
|
75
|
+
* optimistic concurrency control in distributed environments.
|
|
76
|
+
*
|
|
77
|
+
* @throws {TransactionViolation} When the write operation fails due to storage errors
|
|
78
|
+
*/
|
|
79
|
+
persistState(event: ArvoEvent, record: T, prevRecord: T | null, span?: Span): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Validates that the event subject conforms to the ArvoOrchestrationSubject format.
|
|
82
|
+
*
|
|
83
|
+
* This method ensures that the event subject follows the expected schema format
|
|
84
|
+
* required by the Arvo orchestration system. Invalid subjects will result in
|
|
85
|
+
* execution violations to prevent processing of malformed events across the
|
|
86
|
+
* distributed service architecture.
|
|
87
|
+
*
|
|
88
|
+
* @throws {ExecutionViolation} When the event subject format is invalid
|
|
89
|
+
*
|
|
90
|
+
* @protected
|
|
91
|
+
*/
|
|
92
|
+
validateEventSubject(event: ArvoEvent, span?: Span): void;
|
|
93
|
+
/**
|
|
94
|
+
* Releases a previously acquired lock on the event subject.
|
|
95
|
+
*
|
|
96
|
+
* This method safely releases locks that were acquired during event processing to prevent
|
|
97
|
+
* resource leaks in distributed systems. It handles cases where no lock was acquired
|
|
98
|
+
* (NOOP operations) and provides proper error handling for unlock failures. Failed unlock
|
|
99
|
+
* operations are logged as potential resource leaks but do not throw exceptions to avoid
|
|
100
|
+
* disrupting the main processing flow as it assumes that the lock will have the lifedspan.
|
|
101
|
+
*
|
|
102
|
+
* @returns A promise that resolves to the lock release status:
|
|
103
|
+
* - 'NOOP': No lock was acquired, so no operation was performed
|
|
104
|
+
* - 'RELEASED': Lock was successfully released
|
|
105
|
+
* - 'ERROR': Lock release failed, potential resource leak
|
|
106
|
+
*
|
|
107
|
+
* @protected
|
|
108
|
+
*/
|
|
109
|
+
releaseLock(event: ArvoEvent, acquiredLock: AcquiredLockStatusType | null, span?: Span): Promise<ReleasedLockStatusType>;
|
|
110
|
+
}
|