@temporalio/common
Version:
Common library for code that's used across the Client, Worker, and/or Workflow
276 lines • 11.1 kB
JavaScript
;
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