UNPKG

@temporalio/common

Version:

Common library for code that's used across the Client, Worker, and/or Workflow

136 lines 5.68 kB
"use strict"; 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