arvo-event-handler 2.2.1 → 2.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -35,7 +35,11 @@
35
35
  ## [2.1.4] - 2024-12-19
36
36
 
37
37
  - Updated the opentelemetry core version and arvo core versions
38
+
38
39
  ## [2.2.0] - 2024-12-25
39
40
 
40
41
  - Stable release for arvo event handler version 2
41
42
 
43
+ ## [2.2.5] - 2024-12-25
44
+
45
+ - Added better error boundaries for the handlers
package/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  [![SonarCloud](https://sonarcloud.io/images/project_badges/sonarcloud-white.svg)](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
2
2
  [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=SaadAhmad123_arvo-event-handler&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=SaadAhmad123_arvo-event-handler)
3
+
3
4
  # Arvo
4
5
 
5
6
  ## What is Arvo
@@ -69,6 +69,7 @@ var arvo_core_1 = require("arvo-core");
69
69
  var api_1 = require("@opentelemetry/api");
70
70
  var utils_1 = require("../utils");
71
71
  var AbstractArvoEventHandler_1 = __importDefault(require("../AbstractArvoEventHandler"));
72
+ var errors_1 = require("../errors");
72
73
  /**
73
74
  * ArvoEventHandler manages the execution and processing of events in accordance with
74
75
  * Arvo contracts. This class serves as the cornerstone for event handling operations,
@@ -169,7 +170,7 @@ var ArvoEventHandler = /** @class */ (function (_super) {
169
170
  return span.setAttribute("to_process.0.".concat(key), value);
170
171
  });
171
172
  if (this.contract.type !== event.type) {
172
- throw new Error("Event type mismatch: Received '".concat(event.type, "', expected '").concat(this.contract.type, "'"));
173
+ throw new errors_1.ContractViolation("Event type mismatch: Received '".concat(event.type, "', expected '").concat(this.contract.type, "'"));
173
174
  }
174
175
  (0, arvo_core_1.logToSpan)({
175
176
  level: 'INFO',
@@ -178,7 +179,7 @@ var ArvoEventHandler = /** @class */ (function (_super) {
178
179
  parsedDataSchema = arvo_core_1.EventDataschemaUtil.parse(event);
179
180
  if ((parsedDataSchema === null || parsedDataSchema === void 0 ? void 0 : parsedDataSchema.uri) &&
180
181
  (parsedDataSchema === null || parsedDataSchema === void 0 ? void 0 : parsedDataSchema.uri) !== this.contract.uri) {
181
- throw new Error("Contract URI mismatch: Handler expects '".concat(this.contract.uri, "' but event dataschema specifies '").concat(event.dataschema, "'. Events must reference the same contract URI as their handler."));
182
+ throw new errors_1.ContractViolation("Contract URI mismatch: Handler expects '".concat(this.contract.uri, "' but event dataschema specifies '").concat(event.dataschema, "'. Events must reference the same contract URI as their handler."));
182
183
  }
183
184
  if (!(parsedDataSchema === null || parsedDataSchema === void 0 ? void 0 : parsedDataSchema.version)) {
184
185
  (0, arvo_core_1.logToSpan)({
@@ -193,7 +194,7 @@ var ArvoEventHandler = /** @class */ (function (_super) {
193
194
  });
194
195
  inputEventValidation = handlerContract.accepts.schema.safeParse(event.data);
195
196
  if (inputEventValidation.error) {
196
- throw new Error("Event payload validation failed: ".concat(inputEventValidation.error));
197
+ throw new errors_1.ContractViolation("Input event payload validation failed: ".concat(inputEventValidation.error));
197
198
  }
198
199
  (0, arvo_core_1.logToSpan)({
199
200
  level: 'INFO',
@@ -220,7 +221,14 @@ var ArvoEventHandler = /** @class */ (function (_super) {
220
221
  outputs = [_handleOutput];
221
222
  }
222
223
  eventFactory_1 = (0, arvo_core_1.createArvoEventFactory)(handlerContract);
223
- result = (0, utils_1.eventHandlerOutputEventCreator)(outputs, otelSpanHeaders, this.source, event, this.executionunits, function (param, extensions) { return eventFactory_1.emits(param, extensions); });
224
+ result = (0, utils_1.eventHandlerOutputEventCreator)(outputs, otelSpanHeaders, this.source, event, this.executionunits, function (param, extensions) {
225
+ try {
226
+ return eventFactory_1.emits(param, extensions);
227
+ }
228
+ catch (e) {
229
+ throw new errors_1.ContractViolation(e.message);
230
+ }
231
+ });
224
232
  (0, arvo_core_1.logToSpan)({
225
233
  level: 'INFO',
226
234
  message: "Event processing completed successfully. Generated ".concat(result.length, " event(s)"),
@@ -232,12 +240,15 @@ var ArvoEventHandler = /** @class */ (function (_super) {
232
240
  return [2 /*return*/, result];
233
241
  case 3:
234
242
  error_1 = _e.sent();
235
- eventFactory = (0, arvo_core_1.createArvoEventFactory)(this.contract.version('latest'));
236
243
  (0, arvo_core_1.exceptionToSpan)(error_1);
237
244
  span.setStatus({
238
245
  code: api_1.SpanStatusCode.ERROR,
239
246
  message: "Event processing failed: ".concat(error_1.message),
240
247
  });
248
+ if (error_1.name.includes('ViolationError')) {
249
+ throw error_1;
250
+ }
251
+ eventFactory = (0, arvo_core_1.createArvoEventFactory)(this.contract.version('latest'));
241
252
  result = eventFactory.systemError({
242
253
  source: this.source,
243
254
  subject: event.subject,
@@ -71,6 +71,7 @@ var utils_1 = require("../utils");
71
71
  var api_1 = require("@opentelemetry/api");
72
72
  var utils_2 = require("./utils");
73
73
  var AbstractArvoEventHandler_1 = __importDefault(require("../AbstractArvoEventHandler"));
74
+ var errors_1 = require("../errors");
74
75
  /**
75
76
  * ArvoEventRouter manages event routing and execution within the Arvo event system. It directs
76
77
  * incoming events to appropriate handlers based on event type while maintaining telemetry
@@ -181,11 +182,11 @@ var ArvoEventRouter = /** @class */ (function (_super) {
181
182
  message: "Initiating event resolution - Type: ".concat(newEvent.type, ", Source: ").concat(newEvent.source, ", Destination: ").concat(newEvent.to),
182
183
  });
183
184
  if (newEvent.to !== this.source) {
184
- throw new Error("Event destination mismatch: Received destination '".concat(newEvent.to, "', ") +
185
+ throw new errors_1.ConfigViolation("Event destination mismatch: Received destination '".concat(newEvent.to, "', ") +
185
186
  "but router accepts only '".concat(this.source, "'"));
186
187
  }
187
188
  if (!this.handlersMap[newEvent.type]) {
188
- throw new Error("No registered handler found for event type '".concat(newEvent.type, "'"));
189
+ throw new errors_1.ConfigViolation("No registered handler found for event type '".concat(newEvent.type, "'"));
189
190
  }
190
191
  (0, arvo_core_1.logToSpan)({
191
192
  level: 'INFO',
@@ -228,7 +229,7 @@ var ArvoEventRouter = /** @class */ (function (_super) {
228
229
  return [2 /*return*/, resultingEvents];
229
230
  case 3:
230
231
  error_1 = _a.sent();
231
- return [2 /*return*/, (0, utils_1.createHandlerErrorOutputEvent)(error_1, otelSpanHeaders, this.systemErrorSchema.type, this.source, event, this.executionunits, function (param, extensions) { return (0, arvo_core_1.createArvoEvent)(param, extensions); })];
232
+ return [2 /*return*/, (0, utils_1.handleArvoEventHandlerCommonError)(error_1, otelSpanHeaders, this.systemErrorSchema.type, this.source, event, this.executionunits, function (param, extensions) { return (0, arvo_core_1.createArvoEvent)(param, extensions); })];
232
233
  case 4:
233
234
  span.end();
234
235
  return [7 /*endfinally*/];
@@ -69,6 +69,7 @@ var api_1 = require("@opentelemetry/api");
69
69
  var arvo_core_1 = require("arvo-core");
70
70
  var utils_1 = require("../utils");
71
71
  var AbstractArvoEventHandler_1 = __importDefault(require("../AbstractArvoEventHandler"));
72
+ var errors_1 = require("../errors");
72
73
  /**
73
74
  * MultiArvoEventHandler processes multiple event types without being bound to specific contracts.
74
75
  * Manages event execution, telemetry tracking, and error handling for diverse event streams.
@@ -140,7 +141,7 @@ var MultiArvoEventHandler = /** @class */ (function (_super) {
140
141
  message: "Initiating event resolution - Type: ".concat(event.type, ", Source: ").concat(event.source, ", Destination: ").concat(event.to),
141
142
  });
142
143
  if (event.to !== this.source) {
143
- throw new Error("Event destination mismatch: Expected '".concat(this.source, "', received '").concat(event.to, "'"));
144
+ throw new errors_1.ConfigViolation("Event destination mismatch: Expected '".concat(this.source, "', received '").concat(event.to, "'"));
144
145
  }
145
146
  return [4 /*yield*/, this.handler({
146
147
  event: event,
@@ -166,7 +167,7 @@ var MultiArvoEventHandler = /** @class */ (function (_super) {
166
167
  return [2 /*return*/, resultingEvents];
167
168
  case 3:
168
169
  error_1 = _a.sent();
169
- return [2 /*return*/, (0, utils_1.createHandlerErrorOutputEvent)(error_1, otelSpanHeaders, "sys.".concat(this.source, ".error"), this.source, event, this.executionunits, function (param, extensions) { return (0, arvo_core_1.createArvoEvent)(param, extensions); })];
170
+ return [2 /*return*/, (0, utils_1.handleArvoEventHandlerCommonError)(error_1, otelSpanHeaders, "sys.".concat(this.source, ".error"), this.source, event, this.executionunits, function (param, extensions) { return (0, arvo_core_1.createArvoEvent)(param, extensions); })];
170
171
  case 4:
171
172
  span.end();
172
173
  return [7 /*endfinally*/];
@@ -0,0 +1,38 @@
1
+ import { ViolationError } from 'arvo-core';
2
+ /**
3
+ * Represents violations of service contracts, typically involving invalid inputs,
4
+ * outputs, or state transitions that break expected invariants.
5
+ */
6
+ export declare class ContractViolation extends ViolationError<'Contract'> {
7
+ constructor(message: string, metadata?: Record<string, any>);
8
+ }
9
+ /**
10
+ * Represents violations related to system configuration, typically involving
11
+ * missing, invalid, or conflicting configuration requirement by the event
12
+ * being processed.
13
+ */
14
+ export declare class ConfigViolation extends ViolationError<'Config'> {
15
+ constructor(message: string, metadata?: Record<string, any>);
16
+ }
17
+ /**
18
+ * Represents violations that occur during system execution, typically involving
19
+ * runtime failures that require explicit handling or intervention. This allow
20
+ * the developer to throw an error which must be handled explicity at `.execute`
21
+ * of an Arvo event handler. Otherwise, the the error throw during exection are
22
+ * converted to system error event and require to be handled by the workflow
23
+ * orchestration.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * throw new ExecutionViolation(
28
+ * 'API rate limit exceeded',
29
+ * {
30
+ * rateLimitRemaining: 0,
31
+ * resetAfterSeconds: 60
32
+ * }
33
+ * );
34
+ * ```
35
+ */
36
+ export declare class ExecutionViolation extends ViolationError<'Execution'> {
37
+ constructor(message: string, metadata?: Record<string, any>);
38
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.ExecutionViolation = exports.ConfigViolation = exports.ContractViolation = void 0;
19
+ var arvo_core_1 = require("arvo-core");
20
+ /**
21
+ * Represents violations of service contracts, typically involving invalid inputs,
22
+ * outputs, or state transitions that break expected invariants.
23
+ */
24
+ var ContractViolation = /** @class */ (function (_super) {
25
+ __extends(ContractViolation, _super);
26
+ function ContractViolation(message, metadata) {
27
+ return _super.call(this, {
28
+ type: 'Contract',
29
+ message: message,
30
+ metadata: metadata,
31
+ }) || this;
32
+ }
33
+ return ContractViolation;
34
+ }(arvo_core_1.ViolationError));
35
+ exports.ContractViolation = ContractViolation;
36
+ /**
37
+ * Represents violations related to system configuration, typically involving
38
+ * missing, invalid, or conflicting configuration requirement by the event
39
+ * being processed.
40
+ */
41
+ var ConfigViolation = /** @class */ (function (_super) {
42
+ __extends(ConfigViolation, _super);
43
+ function ConfigViolation(message, metadata) {
44
+ return _super.call(this, {
45
+ type: 'Config',
46
+ message: message,
47
+ metadata: metadata,
48
+ }) || this;
49
+ }
50
+ return ConfigViolation;
51
+ }(arvo_core_1.ViolationError));
52
+ exports.ConfigViolation = ConfigViolation;
53
+ /**
54
+ * Represents violations that occur during system execution, typically involving
55
+ * runtime failures that require explicit handling or intervention. This allow
56
+ * the developer to throw an error which must be handled explicity at `.execute`
57
+ * of an Arvo event handler. Otherwise, the the error throw during exection are
58
+ * converted to system error event and require to be handled by the workflow
59
+ * orchestration.
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * throw new ExecutionViolation(
64
+ * 'API rate limit exceeded',
65
+ * {
66
+ * rateLimitRemaining: 0,
67
+ * resetAfterSeconds: 60
68
+ * }
69
+ * );
70
+ * ```
71
+ */
72
+ var ExecutionViolation = /** @class */ (function (_super) {
73
+ __extends(ExecutionViolation, _super);
74
+ function ExecutionViolation(message, metadata) {
75
+ return _super.call(this, {
76
+ type: 'Execution',
77
+ message: message,
78
+ metadata: metadata,
79
+ }) || this;
80
+ }
81
+ return ExecutionViolation;
82
+ }(arvo_core_1.ViolationError));
83
+ exports.ExecutionViolation = ExecutionViolation;
package/dist/index.d.ts CHANGED
@@ -11,4 +11,5 @@ import { ArvoEventRouter } from './ArvoEventRouter';
11
11
  import { createArvoEventRouter } from './ArvoEventRouter/helpers';
12
12
  import AbstractArvoEventHandler from './AbstractArvoEventHandler';
13
13
  import { deleteOtelHeaders } from './ArvoEventRouter/utils';
14
- export { ArvoEventHandler, createArvoEventHandler, IArvoEventHandler, ArvoEventHandlerFunctionOutput, ArvoEventHandlerFunctionInput, ArvoEventHandlerFunction, PartialExcept, MultiArvoEventHandler, MultiArvoEventHandlerFunctionInput, MultiArvoEventHandlerFunctionOutput, MultiArvoEventHandlerFunction, IMultiArvoEventHandler, createMultiArvoEventHandler, isNullOrUndefined, getValueOrDefault, coalesce, coalesceOrDefault, IArvoEventRouter, ArvoEventRouter, createArvoEventRouter, AbstractArvoEventHandler, deleteOtelHeaders, ArvoEventHandlerOpenTelemetryOptions, EventHandlerFactory, };
14
+ import { ContractViolation, ConfigViolation, ExecutionViolation } from './errors';
15
+ export { ArvoEventHandler, createArvoEventHandler, IArvoEventHandler, ArvoEventHandlerFunctionOutput, ArvoEventHandlerFunctionInput, ArvoEventHandlerFunction, PartialExcept, MultiArvoEventHandler, MultiArvoEventHandlerFunctionInput, MultiArvoEventHandlerFunctionOutput, MultiArvoEventHandlerFunction, IMultiArvoEventHandler, createMultiArvoEventHandler, isNullOrUndefined, getValueOrDefault, coalesce, coalesceOrDefault, IArvoEventRouter, ArvoEventRouter, createArvoEventRouter, AbstractArvoEventHandler, deleteOtelHeaders, ArvoEventHandlerOpenTelemetryOptions, EventHandlerFactory, ContractViolation, ConfigViolation, ExecutionViolation, };
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.deleteOtelHeaders = exports.AbstractArvoEventHandler = exports.createArvoEventRouter = exports.ArvoEventRouter = exports.coalesceOrDefault = exports.coalesce = exports.getValueOrDefault = exports.isNullOrUndefined = exports.createMultiArvoEventHandler = exports.MultiArvoEventHandler = exports.createArvoEventHandler = exports.ArvoEventHandler = void 0;
6
+ exports.ExecutionViolation = exports.ConfigViolation = exports.ContractViolation = exports.deleteOtelHeaders = exports.AbstractArvoEventHandler = exports.createArvoEventRouter = exports.ArvoEventRouter = exports.coalesceOrDefault = exports.coalesce = exports.getValueOrDefault = exports.isNullOrUndefined = exports.createMultiArvoEventHandler = exports.MultiArvoEventHandler = exports.createArvoEventHandler = exports.ArvoEventHandler = void 0;
7
7
  var ArvoEventHandler_1 = __importDefault(require("./ArvoEventHandler"));
8
8
  exports.ArvoEventHandler = ArvoEventHandler_1.default;
9
9
  var helpers_1 = require("./ArvoEventHandler/helpers");
@@ -25,3 +25,7 @@ var AbstractArvoEventHandler_1 = __importDefault(require("./AbstractArvoEventHan
25
25
  exports.AbstractArvoEventHandler = AbstractArvoEventHandler_1.default;
26
26
  var utils_2 = require("./ArvoEventRouter/utils");
27
27
  Object.defineProperty(exports, "deleteOtelHeaders", { enumerable: true, get: function () { return utils_2.deleteOtelHeaders; } });
28
+ var errors_1 = require("./errors");
29
+ Object.defineProperty(exports, "ContractViolation", { enumerable: true, get: function () { return errors_1.ContractViolation; } });
30
+ Object.defineProperty(exports, "ConfigViolation", { enumerable: true, get: function () { return errors_1.ConfigViolation; } });
31
+ Object.defineProperty(exports, "ExecutionViolation", { enumerable: true, get: function () { return errors_1.ExecutionViolation; } });
package/dist/utils.d.ts CHANGED
@@ -61,7 +61,7 @@ export declare function coalesceOrDefault<T>(values: (T | null | undefined)[], _
61
61
  export declare const eventHandlerOutputEventCreator: (events: Array<ArvoEventHandlerFunctionOutput<any> | MultiArvoEventHandlerFunctionOutput>, otelSpanHeaders: OpenTelemetryHeaders, source: string, originalEvent: ArvoEvent, handlerExectionUnits: number, factory: (param: CreateArvoEvent<any, any> & {
62
62
  to: string;
63
63
  }, extensions?: Record<string, string | number | boolean>) => ArvoEvent<any, any, any>) => ArvoEvent<any, any, any>[];
64
- export declare const createHandlerErrorOutputEvent: (error: Error, otelSpanHeaders: OpenTelemetryHeaders, type: string, source: string, originalEvent: ArvoEvent, handlerExectionUnits: number, factory: (param: CreateArvoEvent<any, any> & {
64
+ export declare const handleArvoEventHandlerCommonError: (error: Error, otelSpanHeaders: OpenTelemetryHeaders, type: string, source: string, originalEvent: ArvoEvent, handlerExectionUnits: number, factory: (param: CreateArvoEvent<any, any> & {
65
65
  to: string;
66
66
  }, extensions?: Record<string, string | number | boolean>) => ArvoEvent<any, any, any>) => ArvoEvent<any, any, any>[];
67
67
  /**
package/dist/utils.js CHANGED
@@ -22,7 +22,7 @@ var __rest = (this && this.__rest) || function (s, e) {
22
22
  return t;
23
23
  };
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.createEventHandlerTelemetryConfig = exports.createHandlerErrorOutputEvent = exports.eventHandlerOutputEventCreator = void 0;
25
+ exports.createEventHandlerTelemetryConfig = exports.handleArvoEventHandlerCommonError = exports.eventHandlerOutputEventCreator = void 0;
26
26
  exports.isNullOrUndefined = isNullOrUndefined;
27
27
  exports.getValueOrDefault = getValueOrDefault;
28
28
  exports.coalesce = coalesce;
@@ -120,13 +120,16 @@ var eventHandlerOutputEventCreator = function (events, otelSpanHeaders, source,
120
120
  });
121
121
  };
122
122
  exports.eventHandlerOutputEventCreator = eventHandlerOutputEventCreator;
123
- var createHandlerErrorOutputEvent = function (error, otelSpanHeaders, type, source, originalEvent, handlerExectionUnits, factory) {
123
+ var handleArvoEventHandlerCommonError = function (error, otelSpanHeaders, type, source, originalEvent, handlerExectionUnits, factory) {
124
124
  var _a, _b, _c, _d, _e;
125
125
  (0, arvo_core_1.exceptionToSpan)(error);
126
126
  (_a = api_1.trace.getActiveSpan()) === null || _a === void 0 ? void 0 : _a.setStatus({
127
127
  code: api_1.SpanStatusCode.ERROR,
128
128
  message: error.message,
129
129
  });
130
+ if (error.name.includes('ViolationError')) {
131
+ throw error;
132
+ }
130
133
  var result = factory({
131
134
  type: type,
132
135
  source: source,
@@ -149,7 +152,7 @@ var createHandlerErrorOutputEvent = function (error, otelSpanHeaders, type, sour
149
152
  });
150
153
  return [result];
151
154
  };
152
- exports.createHandlerErrorOutputEvent = createHandlerErrorOutputEvent;
155
+ exports.handleArvoEventHandlerCommonError = handleArvoEventHandlerCommonError;
153
156
  /**
154
157
  * Validates if a string contains only uppercase or lowercase alphanumeric characters.
155
158
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arvo-event-handler",
3
- "version": "2.2.1",
3
+ "version": "2.2.6",
4
4
  "description": "Type-safe event handler system with versioning, telemetry, and contract validation for distributed Arvo event-driven architectures, featuring routing and multi-handler support.",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -51,7 +51,7 @@
51
51
  "dependencies": {
52
52
  "@opentelemetry/api": "^1.9.0",
53
53
  "@opentelemetry/core": "^1.28.0",
54
- "arvo-core": "^2.2.2",
54
+ "arvo-core": "^2.2.6",
55
55
  "uuid": "^10.0.0",
56
56
  "zod": "^3.23.8"
57
57
  }