@temporalio/common
Version:
Common library for code that's used across the Client, Worker, and/or Workflow
136 lines • 5.68 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkExtends = checkExtends;
exports.isRecord = isRecord;
exports.hasOwnProperty = hasOwnProperty;
exports.hasOwnProperties = hasOwnProperties;
exports.isError = isError;
exports.isAbortError = isAbortError;
exports.errorMessage = errorMessage;
exports.errorCode = errorCode;
exports.assertNever = assertNever;
exports.SymbolBasedInstanceOfError = SymbolBasedInstanceOfError;
exports.deepFreeze = deepFreeze;
/** Verify that an type _Copy extends _Orig */
function checkExtends() {
// noop, just type check
}
function isRecord(value) {
return typeof value === 'object' && value !== null;
}
function hasOwnProperty(record, prop) {
return prop in record;
}
function hasOwnProperties(record, props) {
return props.every((prop) => prop in record);
}
function isError(error) {
return (isRecord(error) &&
typeof error.name === 'string' &&
typeof error.message === 'string' &&
(error.stack == null || typeof error.stack === 'string'));
}
function isAbortError(error) {
return isError(error) && error.name === 'AbortError';
}
/**
* Get `error.message` (or `undefined` if not present)
*/
function errorMessage(error) {
if (isError(error)) {
return error.message;
}
else if (typeof error === 'string') {
return error;
}
return undefined;
}
function isErrorWithCode(error) {
return isRecord(error) && typeof error.code === 'string';
}
/**
* Get `error.code` (or `undefined` if not present)
*/
function errorCode(error) {
if (isErrorWithCode(error)) {
return error.code;
}
return undefined;
}
/**
* Asserts that some type is the never type
*/
function assertNever(msg, x) {
throw new TypeError(msg + ': ' + x);
}
/**
* A decorator to be used on error classes. It adds the 'name' property AND provides a custom
* 'instanceof' handler that works correctly across execution contexts.
*
* ### Details ###
*
* According to the EcmaScript's spec, the default behavior of JavaScript's `x instanceof Y` operator is to walk up the
* prototype chain of object 'x', checking if any constructor in that hierarchy is _exactly the same object_ as the
* constructor function 'Y'.
*
* Unfortunately, it happens in various situations that different constructor function objects get created for what
* appears to be the very same class. This leads to surprising behavior where `instanceof` returns false though it is
* known that the object is indeed an instance of that class. One particular case where this happens is when constructor
* 'Y' belongs to a different realm than the constuctor with which 'x' was instantiated. Another case is when two copies
* of the same library gets loaded in the same realm.
*
* In practice, this tends to cause issues when crossing the workflow-sandboxing boundary (since Node's vm module
* really creates new execution realms), as well as when running tests using Jest (see https://github.com/jestjs/jest/issues/2549
* for some details on that one).
*
* This function injects a custom 'instanceof' handler into the prototype of 'clazz', which is both cross-realm safe and
* cross-copies-of-the-same-lib safe. It works by adding a special symbol property to the prototype of 'clazz', and then
* checking for the presence of that symbol.
*/
function SymbolBasedInstanceOfError(markerName) {
return (clazz) => {
const marker = Symbol.for(`__temporal_is${markerName}`);
Object.defineProperty(clazz.prototype, 'name', { value: markerName, enumerable: true });
Object.defineProperty(clazz.prototype, marker, { value: true, enumerable: false });
Object.defineProperty(clazz, Symbol.hasInstance, {
// eslint-disable-next-line object-shorthand
value: function (error) {
if (this === clazz) {
return isRecord(error) && error[marker] === true;
}
else {
// 'this' must be a _subclass_ of clazz that doesn't redefined [Symbol.hasInstance], so that it inherited
// from clazz's [Symbol.hasInstance]. If we don't handle this particular situation, then
// `x instanceof SubclassOfParent` would return true for any instance of 'Parent', which is clearly wrong.
//
// Ideally, it'd be preferable to avoid this case entirely, by making sure that all subclasses of 'clazz'
// redefine [Symbol.hasInstance], but we can't enforce that. We therefore fallback to the default instanceof
// behavior (which is NOT cross-realm safe).
return this.prototype.isPrototypeOf(error); // eslint-disable-line no-prototype-builtins
}
},
});
};
}
// Thanks MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
function deepFreeze(object) {
// Retrieve the property names defined on object
const propNames = Object.getOwnPropertyNames(object);
// Freeze properties before freezing self
for (const name of propNames) {
const value = object[name];
if (value && typeof value === 'object') {
try {
deepFreeze(value);
}
catch (_err) {
// This is okay, there are some typed arrays that cannot be frozen (encodingKeys)
}
}
else if (typeof value === 'function') {
Object.freeze(value);
}
}
return Object.freeze(object);
}
//# sourceMappingURL=type-helpers.js.map