arvo-core
Version:
The core Arvo package which provides application tier core primitives and contract system for building production-grade event-driven application. Provides ArvoEvent (CloudEvents-compliant), ArvoContract for type-safe service interfaces, event factories, O
240 lines (239 loc) • 9.57 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var OpenTelemetry_1 = require("../OpenTelemetry");
var schema_1 = require("../schema");
var utils_1 = require("../utils");
var VersionedArvoContract_1 = require("./VersionedArvoContract");
var WildCardArvoSemanticVersion_1 = require("./WildCardArvoSemanticVersion");
var validators_1 = require("./validators");
/**
* Represents a contract with defined input and output schemas for event-driven architectures.
* The ArvoContract class provides type-safe validation and versioning capabilities for event handling,
* ensuring consistency in message passing between different parts of the system.
*
* @example
* ```typescript
* const contract = createArvoContract({
* uri: '#/my/service/data',
* type: 'com.process.data',
* description: 'An example contract',
* metadata: {
* visibility: "public"
* }
* versions: {
* '1.0.0': {
* accepts: z.object({ data: z.string() }),
* emits: {
* 'data.processed': z.object({ result: z.string() })
* }
* },
* '2.0.0': {
* accepts: z.object({ data: z.number() }),
* emits: {
* 'data.processed': z.object({ result: z.number() })
* }
* }
* }
* });
* ```
*/
var ArvoContract = /** @class */ (function () {
/**
* Creates a new ArvoContract instance with validated parameters.
*
* @param params - Contract configuration parameters
*
* @throws {Error} When URI format is invalid
* @throws {Error} When event type format is invalid
* @throws {Error} When version string is not valid semantic version
* @throws {Error} When version is a reserved wildcard version
* @throws {Error} When emit type format is invalid
* @throws {Error} When no versions are provided
* @throws {Error} When domain does not have follow the condition Domain must contain only lowercase letters, numbers, and dots
*/
function ArvoContract(params) {
var _a;
validators_1.ArvoContractValidators.contract.uri.parse(params.uri);
validators_1.ArvoContractValidators.record.type.parse(params.type);
validators_1.ArvoContractValidators.contract.domain.parse(params.domain);
this._uri = params.uri;
this._type = params.type;
this._versions = params.versions;
this._description = (_a = params.description) !== null && _a !== void 0 ? _a : null;
this._metadata = params.metadata;
this._domain = params.domain;
for (var _i = 0, _b = Object.entries(params.versions); _i < _b.length; _i++) {
var _c = _b[_i], version = _c[0], versionContract = _c[1];
schema_1.ArvoSemanticVersionSchema.parse(version);
if ((0, WildCardArvoSemanticVersion_1.isWildCardArvoSematicVersion)(version)) {
throw new Error("For contract (uri=".concat(params.uri, "), the version cannot be '").concat(WildCardArvoSemanticVersion_1.WildCardArvoSemanticVersion, "'. It is a reserved version type."));
}
for (var _d = 0, _e = Object.keys(versionContract.emits); _d < _e.length; _d++) {
var emitType = _e[_d];
validators_1.ArvoContractValidators.record.type.parse(emitType);
}
}
if (!Object.keys(this._versions).length) {
throw new Error("An ArvoContract (uri=".concat(this._uri, ") must have at least one version"));
}
}
Object.defineProperty(ArvoContract.prototype, "uri", {
get: function () {
return this._uri;
},
enumerable: false,
configurable: true
});
Object.defineProperty(ArvoContract.prototype, "type", {
get: function () {
return this._type;
},
enumerable: false,
configurable: true
});
Object.defineProperty(ArvoContract.prototype, "versions", {
get: function () {
return this._versions;
},
enumerable: false,
configurable: true
});
Object.defineProperty(ArvoContract.prototype, "description", {
get: function () {
return this._description;
},
enumerable: false,
configurable: true
});
Object.defineProperty(ArvoContract.prototype, "metadata", {
get: function () {
return this._metadata;
},
enumerable: false,
configurable: true
});
Object.defineProperty(ArvoContract.prototype, "domain", {
get: function () {
return this._domain;
},
enumerable: false,
configurable: true
});
Object.defineProperty(ArvoContract.prototype, "systemError", {
/**
* Gets the system error event specification for this contract.
* System errors follow a standardized format to handle exceptional conditions
* and failures in a consistent way across all contracts.
*
* The error schema includes:
* - errorName: The name/type of the error
* - errorMessage: A descriptive message about what went wrong
* - errorStack: Optional stack trace information (null if not available)
*
* System errors are special events that:
* - Are automatically prefixed with 'sys.' and suffixed with '.error'
* - Use a standardized schema across all contracts
* - Can capture error details, messages, and stack traces
* - Are version-independent (work the same across all contract versions)
*/
get: function () {
return {
type: "sys.".concat(this._type, ".error"),
schema: schema_1.ArvoErrorSchema,
};
},
enumerable: false,
configurable: true
});
/**
* Retrieves a specific version of the contract or resolves special version identifiers.
*
* @param option - Version identifier or special version string
* - Specific version (e.g., "1.0.0")
* - "latest" or "any" for the most recent version
* - "oldest" for the first version
*
* @returns A versioned contract instance with type-safe schemas
*
* @throws {Error} When an invalid or non-existent version is requested
*/
ArvoContract.prototype.version = function (option) {
var resolvedVersion;
if (option === 'any' || option === 'latest') {
resolvedVersion = this.getSortedVersionNumbers('DESC')[0];
}
else if (option === 'oldest') {
resolvedVersion = this.getSortedVersionNumbers('ASC')[0];
// @ts-ignore
}
else if (!this._versions[option]) {
throw new Error("The contract (uri=".concat(this._uri, ") does not have version=").concat(option));
}
else {
resolvedVersion = option;
}
return new VersionedArvoContract_1.VersionedArvoContract({
contract: this,
version: resolvedVersion,
}); // needed due to TypeScript limitations with conditional types
};
/**
* Retrieves version numbers in sorted order based on semantic versioning rules.
* @returns Array of semantic versions sorted according to specified ordering
*/
ArvoContract.prototype.getSortedVersionNumbers = function (ordering) {
var sorted = Object.keys(this._versions).sort(function (a, b) { return (0, utils_1.compareSemanticVersions)(b, a); });
return ordering === 'DESC' ? sorted : sorted.reverse();
};
/**
* Exports the ArvoContract instance as a plain object conforming to the IArvoContract interface.
* This method can be used to serialize the contract or to create a new instance with the same parameters.
*/
ArvoContract.prototype.export = function () {
return {
uri: this._uri,
type: this._type,
domain: this._domain,
description: this._description,
versions: this._versions,
metadata: this._metadata,
};
};
/**
* Converts the ArvoContract instance to a JSON Schema representation.
* This method provides a way to represent the contract's structure and validation rules
* in a format that conforms to the JSON Schema specification.
*/
ArvoContract.prototype.toJsonSchema = function () {
var _this = this;
try {
return {
uri: this._uri,
domain: this._domain,
description: this._description,
metadata: this._metadata,
versions: Object.keys(this._versions).map(function (version) {
var jsonSchema = _this.version(version).toJsonSchema();
return {
domain: jsonSchema.domain,
version: jsonSchema.version,
accepts: jsonSchema.accepts,
systemError: jsonSchema.systemError,
emits: jsonSchema.emits,
metadata: jsonSchema.metadata,
};
}),
};
}
catch (e) {
var errorMessage = "ArvoContract.toJsonSchema failed: ".concat(e.message);
(0, OpenTelemetry_1.logToSpan)({
level: 'ERROR',
message: errorMessage,
});
throw new Error(errorMessage);
}
};
return ArvoContract;
}());
exports.default = ArvoContract;