UNPKG

@temporalio/common

Version:

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

218 lines 8.81 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DefaultPayloadConverterWithProtobufs = exports.ProtobufJsonPayloadConverter = exports.ProtobufBinaryPayloadConverter = void 0; const protoJsonSerializer = __importStar(require("proto3-json-serializer")); const encoding_1 = require("../encoding"); const errors_1 = require("../errors"); const type_helpers_1 = require("../type-helpers"); const payload_converter_1 = require("./payload-converter"); const types_1 = require("./types"); const GLOBAL_BUFFER = globalThis.constructor.constructor('return globalThis.Buffer')(); class ProtobufPayloadConverter { root; // Don't use type Root here because root.d.ts doesn't export Root, so users would have to type assert constructor(root) { if (root) { if (!isRoot(root)) { throw new TypeError('root must be an instance of a protobufjs Root'); } this.root = root; } } validatePayload(content) { if (content.data === undefined || content.data === null) { throw new errors_1.ValueError('Got payload with no data'); } if (!content.metadata || !(types_1.METADATA_MESSAGE_TYPE_KEY in content.metadata)) { throw new errors_1.ValueError(`Got protobuf payload without metadata.${types_1.METADATA_MESSAGE_TYPE_KEY}`); } if (!this.root) { throw new errors_1.PayloadConverterError('Unable to deserialize protobuf message without `root` being provided'); } const messageTypeName = (0, encoding_1.decode)(content.metadata[types_1.METADATA_MESSAGE_TYPE_KEY]); let messageType; try { messageType = this.root.lookupType(messageTypeName); } catch (e) { if ((0, type_helpers_1.errorMessage)(e)?.includes('no such type')) { throw new errors_1.PayloadConverterError(`Got a \`${messageTypeName}\` protobuf message but cannot find corresponding message class in \`root\``); } throw e; } return { messageType, data: content.data }; } constructPayload({ messageTypeName, message }) { return { metadata: { [types_1.METADATA_ENCODING_KEY]: (0, encoding_1.encode)(this.encodingType), [types_1.METADATA_MESSAGE_TYPE_KEY]: (0, encoding_1.encode)(messageTypeName), }, data: message, }; } } /** * Converts between protobufjs Message instances and serialized Protobuf Payload */ class ProtobufBinaryPayloadConverter extends ProtobufPayloadConverter { encodingType = types_1.encodingTypes.METADATA_ENCODING_PROTOBUF; /** * @param root The value returned from {@link patchProtobufRoot} */ constructor(root) { super(root); } toPayload(value) { if (!isProtobufMessage(value)) { return undefined; } return this.constructPayload({ messageTypeName: getNamespacedTypeName(value.$type), message: value.$type.encode(value).finish(), }); } fromPayload(content) { const { messageType, data } = this.validatePayload(content); // Wrap with Uint8Array from this context to ensure `instanceof` works const localData = data ? new Uint8Array(data.buffer, data.byteOffset, data.length) : data; return messageType.decode(localData); } } exports.ProtobufBinaryPayloadConverter = ProtobufBinaryPayloadConverter; /** * Converts between protobufjs Message instances and serialized JSON Payload */ class ProtobufJsonPayloadConverter extends ProtobufPayloadConverter { encodingType = types_1.encodingTypes.METADATA_ENCODING_PROTOBUF_JSON; /** * @param root The value returned from {@link patchProtobufRoot} */ constructor(root) { super(root); } toPayload(value) { if (!isProtobufMessage(value)) { return undefined; } const hasBufferChanged = setBufferInGlobal(); try { const jsonValue = protoJsonSerializer.toProto3JSON(value); return this.constructPayload({ messageTypeName: getNamespacedTypeName(value.$type), message: (0, encoding_1.encode)(JSON.stringify(jsonValue)), }); } finally { resetBufferInGlobal(hasBufferChanged); } } fromPayload(content) { const hasBufferChanged = setBufferInGlobal(); try { const { messageType, data } = this.validatePayload(content); const res = protoJsonSerializer.fromProto3JSON(messageType, JSON.parse((0, encoding_1.decode)(data))); if (Buffer.isBuffer(res)) { return new Uint8Array(res); } replaceBuffers(res); return res; } finally { resetBufferInGlobal(hasBufferChanged); } } } exports.ProtobufJsonPayloadConverter = ProtobufJsonPayloadConverter; function replaceBuffers(obj) { const replaceBuffersImpl = (value, key, target) => { if (Buffer.isBuffer(value)) { target[key] = new Uint8Array(value); } else { replaceBuffers(value); } }; if (obj != null && typeof obj === 'object') { // Performance optimization for large arrays if (Array.isArray(obj)) { obj.forEach(replaceBuffersImpl); } else { for (const [key, value] of Object.entries(obj)) { replaceBuffersImpl(value, key, obj); } } } } function setBufferInGlobal() { if (typeof globalThis.Buffer === 'undefined') { globalThis.Buffer = GLOBAL_BUFFER; return true; } return false; } function resetBufferInGlobal(hasChanged) { if (hasChanged) { delete globalThis.Buffer; } } function isProtobufType(type) { return ((0, type_helpers_1.isRecord)(type) && // constructor.name may get mangled by minifiers; thanksfuly protobufjs also sets a className property type.constructor.className === 'Type' && (0, type_helpers_1.hasOwnProperties)(type, ['parent', 'name', 'create', 'encode', 'decode']) && typeof type.name === 'string' && typeof type.create === 'function' && typeof type.encode === 'function' && typeof type.decode === 'function'); } function isProtobufMessage(value) { return (0, type_helpers_1.isRecord)(value) && (0, type_helpers_1.hasOwnProperty)(value, '$type') && isProtobufType(value.$type); } function getNamespacedTypeName(node) { if (node.parent && !isRoot(node.parent)) { return getNamespacedTypeName(node.parent) + '.' + node.name; } else { return node.name; } } function isRoot(root) { // constructor.name may get mangled by minifiers; thanksfuly protobufjs also sets a className property return (0, type_helpers_1.isRecord)(root) && root.constructor.className === 'Root'; } class DefaultPayloadConverterWithProtobufs extends payload_converter_1.CompositePayloadConverter { // Match the order used in other SDKs. // // Go SDK: // https://github.com/temporalio/sdk-go/blob/5e5645f0c550dcf717c095ae32c76a7087d2e985/converter/default_data_converter.go#L28 constructor({ protobufRoot }) { super(new payload_converter_1.UndefinedPayloadConverter(), new payload_converter_1.BinaryPayloadConverter(), new ProtobufJsonPayloadConverter(protobufRoot), new ProtobufBinaryPayloadConverter(protobufRoot), new payload_converter_1.JsonPayloadConverter()); } } exports.DefaultPayloadConverterWithProtobufs = DefaultPayloadConverterWithProtobufs; //# sourceMappingURL=protobuf-payload-converters.js.map