UNPKG

@temporalio/common

Version:

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

276 lines 11.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.defaultPayloadConverter = exports.DefaultPayloadConverter = exports.searchAttributePayloadConverter = exports.SearchAttributePayloadConverter = exports.JsonPayloadConverter = exports.BinaryPayloadConverter = exports.UndefinedPayloadConverter = exports.CompositePayloadConverter = void 0; exports.toPayloads = toPayloads; exports.mapToPayloads = mapToPayloads; exports.fromPayloadsAtIndex = fromPayloadsAtIndex; exports.arrayFromPayloads = arrayFromPayloads; exports.mapFromPayloads = mapFromPayloads; const encoding_1 = require("../encoding"); const errors_1 = require("../errors"); const types_1 = require("./types"); /** * Implements conversion of a list of values. * * @param converter * @param values JS values to convert to Payloads * @return list of {@link Payload}s * @throws {@link ValueError} if conversion of the value passed as parameter failed for any * reason. */ function toPayloads(converter, ...values) { if (values.length === 0) { return undefined; } return values.map((value) => converter.toPayload(value)); } /** * Run {@link PayloadConverter.toPayload} on each value in the map. * * @throws {@link ValueError} if conversion of any value in the map fails */ function mapToPayloads(converter, map) { return Object.fromEntries(Object.entries(map).map(([k, v]) => [k, converter.toPayload(v)])); } /** * Implements conversion of an array of values of different types. Useful for deserializing * arguments of function invocations. * * @param converter * @param index index of the value in the payloads * @param payloads serialized value to convert to JS values. * @return converted JS value * @throws {@link PayloadConverterError} if conversion of the data passed as parameter failed for any * reason. */ function fromPayloadsAtIndex(converter, index, payloads) { // To make adding arguments a backwards compatible change if (payloads === undefined || payloads === null || index >= payloads.length) { return undefined; } return converter.fromPayload(payloads[index]); } /** * Run {@link PayloadConverter.fromPayload} on each value in the array. */ function arrayFromPayloads(converter, payloads) { if (!payloads) { return []; } return payloads.map((payload) => converter.fromPayload(payload)); } function mapFromPayloads(converter, map) { if (map == null) return undefined; return Object.fromEntries(Object.entries(map).map(([k, payload]) => { const value = converter.fromPayload(payload); return [k, value]; })); } /** * Tries to convert values to {@link Payload}s using the {@link PayloadConverterWithEncoding}s provided to the constructor, in the order provided. * * Converts Payloads to values based on the `Payload.metadata.encoding` field, which matches the {@link PayloadConverterWithEncoding.encodingType} * of the converter that created the Payload. */ class CompositePayloadConverter { constructor(...converters) { this.converterByEncoding = new Map(); if (converters.length === 0) { throw new errors_1.PayloadConverterError('Must provide at least one PayloadConverterWithEncoding'); } this.converters = converters; for (const converter of converters) { this.converterByEncoding.set(converter.encodingType, converter); } } /** * Tries to run `.toPayload(value)` on each converter in the order provided at construction. * Returns the first successful result, throws {@link ValueError} if there is no converter that can handle the value. */ toPayload(value) { for (const converter of this.converters) { const result = converter.toPayload(value); if (result !== undefined) { return result; } } throw new errors_1.ValueError(`Unable to convert ${value} to payload`); } /** * Run {@link PayloadConverterWithEncoding.fromPayload} based on the `encoding` metadata of the {@link Payload}. */ fromPayload(payload) { if (payload.metadata === undefined || payload.metadata === null) { throw new errors_1.ValueError('Missing payload metadata'); } const encoding = (0, encoding_1.decode)(payload.metadata[types_1.METADATA_ENCODING_KEY]); const converter = this.converterByEncoding.get(encoding); if (converter === undefined) { throw new errors_1.ValueError(`Unknown encoding: ${encoding}`); } return converter.fromPayload(payload); } } exports.CompositePayloadConverter = CompositePayloadConverter; /** * Converts between JS undefined and NULL Payload */ class UndefinedPayloadConverter { constructor() { this.encodingType = types_1.encodingTypes.METADATA_ENCODING_NULL; } toPayload(value) { if (value !== undefined) { return undefined; } return { metadata: { [types_1.METADATA_ENCODING_KEY]: types_1.encodingKeys.METADATA_ENCODING_NULL, }, }; } fromPayload(_content) { return undefined; // Just return undefined } } exports.UndefinedPayloadConverter = UndefinedPayloadConverter; /** * Converts between binary data types and RAW Payload */ class BinaryPayloadConverter { constructor() { this.encodingType = types_1.encodingTypes.METADATA_ENCODING_RAW; } toPayload(value) { if (!(value instanceof Uint8Array)) { return undefined; } return { metadata: { [types_1.METADATA_ENCODING_KEY]: types_1.encodingKeys.METADATA_ENCODING_RAW, }, data: value, }; } fromPayload(content) { return ( // Wrap with Uint8Array from this context to ensure `instanceof` works (content.data ? new Uint8Array(content.data.buffer, content.data.byteOffset, content.data.length) : content.data)); } } exports.BinaryPayloadConverter = BinaryPayloadConverter; /** * Converts between non-undefined values and serialized JSON Payload */ class JsonPayloadConverter { constructor() { this.encodingType = types_1.encodingTypes.METADATA_ENCODING_JSON; } toPayload(value) { if (value === undefined) { return undefined; } let json; try { json = JSON.stringify(value); } catch (_err) { return undefined; } return { metadata: { [types_1.METADATA_ENCODING_KEY]: types_1.encodingKeys.METADATA_ENCODING_JSON, }, data: (0, encoding_1.encode)(json), }; } fromPayload(content) { if (content.data === undefined || content.data === null) { throw new errors_1.ValueError('Got payload with no data'); } return JSON.parse((0, encoding_1.decode)(content.data)); } } exports.JsonPayloadConverter = JsonPayloadConverter; /** * Converts Search Attribute values using JsonPayloadConverter */ class SearchAttributePayloadConverter { constructor() { this.jsonConverter = new JsonPayloadConverter(); this.validNonDateTypes = ['string', 'number', 'boolean']; } toPayload(values) { if (!Array.isArray(values)) { throw new errors_1.ValueError(`SearchAttribute value must be an array`); } if (values.length > 0) { const firstValue = values[0]; const firstType = typeof firstValue; if (firstType === 'object') { for (const [idx, value] of values.entries()) { if (!(value instanceof Date)) { throw new errors_1.ValueError(`SearchAttribute values must arrays of strings, numbers, booleans, or Dates. The value ${value} at index ${idx} is of type ${typeof value}`); } } } else { if (!this.validNonDateTypes.includes(firstType)) { throw new errors_1.ValueError(`SearchAttribute array values must be: string | number | boolean | Date`); } for (const [idx, value] of values.entries()) { if (typeof value !== firstType) { throw new errors_1.ValueError(`All SearchAttribute array values must be of the same type. The first value ${firstValue} of type ${firstType} doesn't match value ${value} of type ${typeof value} at index ${idx}`); } } } } // JSON.stringify takes care of converting Dates to ISO strings const ret = this.jsonConverter.toPayload(values); if (ret === undefined) { throw new errors_1.ValueError('Could not convert search attributes to payloads'); } return ret; } /** * Datetime Search Attribute values are converted to `Date`s */ fromPayload(payload) { if (payload.metadata === undefined || payload.metadata === null) { throw new errors_1.ValueError('Missing payload metadata'); } const value = this.jsonConverter.fromPayload(payload); let arrayWrappedValue = Array.isArray(value) ? value : [value]; const searchAttributeType = (0, encoding_1.decode)(payload.metadata.type); if (searchAttributeType === 'Datetime') { arrayWrappedValue = arrayWrappedValue.map((dateString) => new Date(dateString)); } return arrayWrappedValue; } } exports.SearchAttributePayloadConverter = SearchAttributePayloadConverter; exports.searchAttributePayloadConverter = new SearchAttributePayloadConverter(); class DefaultPayloadConverter extends CompositePayloadConverter { // Match the order used in other SDKs, but exclude Protobuf converters so that the code, including // `proto3-json-serializer`, doesn't take space in Workflow bundles that don't use Protobufs. To use Protobufs, use // {@link DefaultPayloadConverterWithProtobufs}. // // Go SDK: // https://github.com/temporalio/sdk-go/blob/5e5645f0c550dcf717c095ae32c76a7087d2e985/converter/default_data_converter.go#L28 constructor() { super(new UndefinedPayloadConverter(), new BinaryPayloadConverter(), new JsonPayloadConverter()); } } exports.DefaultPayloadConverter = DefaultPayloadConverter; /** * The default {@link PayloadConverter} used by the SDK. Supports `Uint8Array` and JSON serializables (so if * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description | `JSON.stringify(yourArgOrRetval)`} * works, the default payload converter will work). * * To also support Protobufs, create a custom payload converter with {@link DefaultPayloadConverter}: * * `const myConverter = new DefaultPayloadConverter({ protobufRoot })` */ exports.defaultPayloadConverter = new DefaultPayloadConverter(); //# sourceMappingURL=payload-converter.js.map