@temporalio/common 0.17.2 → 0.19.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -11
- package/lib/converter/data-converter.d.ts +34 -62
- package/lib/converter/data-converter.js +0 -101
- package/lib/converter/data-converter.js.map +1 -1
- package/lib/{encoding.d.ts → converter/encoding.d.ts} +0 -0
- package/lib/{encoding.js → converter/encoding.js} +0 -0
- package/lib/converter/encoding.js.map +1 -0
- package/lib/converter/patch-protobuf-root.d.ts +8 -0
- package/lib/converter/patch-protobuf-root.js +43 -0
- package/lib/converter/patch-protobuf-root.js.map +1 -0
- package/lib/converter/payload-codec.d.ts +27 -0
- package/lib/converter/payload-codec.js +11 -0
- package/lib/converter/payload-codec.js.map +1 -0
- package/lib/converter/payload-converter.d.ts +63 -62
- package/lib/converter/payload-converter.js +113 -68
- package/lib/converter/payload-converter.js.map +1 -1
- package/lib/converter/payload-converters.d.ts +31 -0
- package/lib/converter/payload-converters.js +85 -0
- package/lib/converter/payload-converters.js.map +1 -0
- package/lib/converter/protobuf-payload-converters.d.ts +53 -0
- package/lib/converter/protobuf-payload-converters.js +159 -0
- package/lib/converter/protobuf-payload-converters.js.map +1 -0
- package/lib/converter/types.d.ts +2 -0
- package/lib/converter/types.js +3 -2
- package/lib/converter/types.js.map +1 -1
- package/lib/failure.d.ts +6 -6
- package/lib/failure.js +37 -35
- package/lib/failure.js.map +1 -1
- package/lib/index.d.ts +9 -11
- package/lib/index.js +12 -16
- package/lib/index.js.map +1 -1
- package/lib/protobufs.d.ts +13 -0
- package/lib/protobufs.js +31 -0
- package/lib/protobufs.js.map +1 -0
- package/package.json +11 -8
- package/src/converter/data-converter.ts +35 -176
- package/src/{encoding.ts → converter/encoding.ts} +0 -0
- package/src/converter/patch-protobuf-root.ts +49 -0
- package/src/converter/payload-codec.ts +30 -0
- package/src/converter/payload-converter.ts +122 -102
- package/src/converter/payload-converters.ts +89 -0
- package/src/converter/protobuf-payload-converters.ts +192 -0
- package/src/converter/types.ts +4 -1
- package/src/failure.ts +44 -43
- package/src/index.ts +9 -11
- package/src/protobufs.ts +15 -0
- package/tsconfig.json +2 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/activity-options.d.ts +0 -81
- package/lib/activity-options.js +0 -14
- package/lib/activity-options.js.map +0 -1
- package/lib/encoding.js.map +0 -1
- package/lib/errors.d.ts +0 -16
- package/lib/errors.js +0 -41
- package/lib/errors.js.map +0 -1
- package/lib/interceptors.d.ts +0 -18
- package/lib/interceptors.js +0 -24
- package/lib/interceptors.js.map +0 -1
- package/lib/interfaces.d.ts +0 -31
- package/lib/interfaces.js +0 -3
- package/lib/interfaces.js.map +0 -1
- package/lib/otel.d.ts +0 -26
- package/lib/otel.js +0 -82
- package/lib/otel.js.map +0 -1
- package/lib/retry-policy.d.ts +0 -43
- package/lib/retry-policy.js +0 -36
- package/lib/retry-policy.js.map +0 -1
- package/lib/time.d.ts +0 -16
- package/lib/time.js +0 -73
- package/lib/time.js.map +0 -1
- package/lib/tls-config.d.ts +0 -32
- package/lib/tls-config.js +0 -11
- package/lib/tls-config.js.map +0 -1
- package/lib/type-helpers.d.ts +0 -8
- package/lib/type-helpers.js +0 -9
- package/lib/type-helpers.js.map +0 -1
- package/lib/utils.d.ts +0 -4
- package/lib/utils.js +0 -11
- package/lib/utils.js.map +0 -1
- package/lib/workflow-handle.d.ts +0 -27
- package/lib/workflow-handle.js +0 -3
- package/lib/workflow-handle.js.map +0 -1
- package/lib/workflow-options.d.ts +0 -91
- package/lib/workflow-options.js +0 -26
- package/lib/workflow-options.js.map +0 -1
- package/src/activity-options.ts +0 -97
- package/src/errors.ts +0 -27
- package/src/interceptors.ts +0 -32
- package/src/interfaces.ts +0 -37
- package/src/otel.ts +0 -61
- package/src/retry-policy.ts +0 -73
- package/src/time.ts +0 -72
- package/src/tls-config.ts +0 -35
- package/src/type-helpers.ts +0 -11
- package/src/utils.ts +0 -6
- package/src/workflow-handle.ts +0 -30
- package/src/workflow-options.ts +0 -127
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { isRecord } from '@temporalio/internal-workflow-common';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create a version of `root` with non-nested namespaces to match the generated types.
|
|
5
|
+
* For more information, see:
|
|
6
|
+
* https://github.com/temporalio/sdk-typescript/blob/main/docs/protobuf-libraries.md#current-solution
|
|
7
|
+
* @param root Generated by `pbjs -t json-module -w commonjs -o json-module.js *.proto`
|
|
8
|
+
* @returns A new patched `root`
|
|
9
|
+
*/
|
|
10
|
+
export function patchProtobufRoot<T extends Record<string, unknown>>(root: T): T {
|
|
11
|
+
return _patchProtobufRoot(root);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function _patchProtobufRoot<T extends Record<string, unknown>>(root: T, name?: string): T {
|
|
15
|
+
const newRoot = new (root.constructor as any)(isNamespace(root) ? name : {});
|
|
16
|
+
for (const key in root) {
|
|
17
|
+
newRoot[key] = root[key];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (isRecord(root.nested)) {
|
|
21
|
+
for (const typeOrNamespace in root.nested) {
|
|
22
|
+
const value = root.nested[typeOrNamespace];
|
|
23
|
+
if (typeOrNamespace in root && !(isType(root[typeOrNamespace]) || isNamespace(root[typeOrNamespace]))) {
|
|
24
|
+
console.log(
|
|
25
|
+
`patchRoot warning: overriding property '${typeOrNamespace}' that is used by protobufjs with the '${typeOrNamespace}' protobuf namespace. This may result in protobufjs not working property.`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (isNamespace(value)) {
|
|
30
|
+
newRoot[typeOrNamespace] = _patchProtobufRoot(value, typeOrNamespace);
|
|
31
|
+
} else if (isType(value)) {
|
|
32
|
+
newRoot[typeOrNamespace] = value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return newRoot;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type Type = Record<string, unknown>;
|
|
41
|
+
type Namespace = { nested: Record<string, unknown> };
|
|
42
|
+
|
|
43
|
+
function isType(value: unknown): value is Type {
|
|
44
|
+
return isRecord(value) && value.constructor.name === 'Type';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isNamespace(value: unknown): value is Namespace {
|
|
48
|
+
return isRecord(value) && value.constructor.name === 'Namespace';
|
|
49
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Payload } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `PayloadCodec` is an optional step that happens between the wire and the {@link PayloadConverter}:
|
|
5
|
+
*
|
|
6
|
+
* Temporal Server <--> Wire <--> `PayloadCodec` <--> `PayloadConverter` <--> User code
|
|
7
|
+
*
|
|
8
|
+
* Implement this to transform an array of {@link Payload}s to/from the format sent over the wire and stored by Temporal Server.
|
|
9
|
+
* Common transformations are encryption and compression.
|
|
10
|
+
*/
|
|
11
|
+
export interface PayloadCodec {
|
|
12
|
+
/**
|
|
13
|
+
* Encode an array of {@link Payload}s for sending over the wire.
|
|
14
|
+
* @param payloads May have length 0.
|
|
15
|
+
*/
|
|
16
|
+
encode(payloads: Payload[]): Promise<Payload[]>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Decode an array of {@link Payload}s received from the wire.
|
|
20
|
+
*/
|
|
21
|
+
decode(payloads: Payload[]): Promise<Payload[]>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* No-op implementation of {@link PayloadCodec}.
|
|
26
|
+
*/
|
|
27
|
+
export const defaultPayloadCodec = {
|
|
28
|
+
encode: async (payloads: Payload[]): Promise<Payload[]> => payloads,
|
|
29
|
+
decode: async (payloads: Payload[]): Promise<Payload[]> => payloads,
|
|
30
|
+
};
|
|
@@ -1,142 +1,162 @@
|
|
|
1
|
-
import { ValueError } from '
|
|
2
|
-
import {
|
|
1
|
+
import { PayloadConverterError, ValueError } from '@temporalio/internal-workflow-common';
|
|
2
|
+
import {
|
|
3
|
+
BinaryPayloadConverter,
|
|
4
|
+
JsonPayloadConverter,
|
|
5
|
+
PayloadConverterWithEncoding,
|
|
6
|
+
UndefinedPayloadConverter,
|
|
7
|
+
} from './payload-converters';
|
|
8
|
+
import { METADATA_ENCODING_KEY, Payload, str } from './types';
|
|
3
9
|
|
|
4
10
|
/**
|
|
5
|
-
* Used by the framework to serialize/deserialize
|
|
6
|
-
* wire.
|
|
11
|
+
* Used by the framework to serialize/deserialize parameters and return values.
|
|
7
12
|
*
|
|
8
|
-
*
|
|
13
|
+
* This is called inside the [Workflow isolate](https://docs.temporal.io/docs/typescript/determinism).
|
|
14
|
+
* To write async code or use Node APIs (or use packages that use Node APIs), use a {@link PayloadCodec}.
|
|
9
15
|
*/
|
|
10
16
|
export interface PayloadConverter {
|
|
11
|
-
encodingType: string;
|
|
12
|
-
|
|
13
17
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* @param value JS value to convert.
|
|
18
|
-
* @return converted value
|
|
19
|
-
* @throws DataConverterException if conversion of the value passed as parameter failed for any
|
|
20
|
-
* reason.
|
|
18
|
+
* Converts a value to a {@link Payload}.
|
|
19
|
+
* @param value The value to convert. Example values include the Workflow args sent by the client and the values returned by a Workflow or Activity.
|
|
21
20
|
*/
|
|
22
|
-
|
|
21
|
+
toPayload<T>(value: T): Payload | undefined;
|
|
23
22
|
|
|
24
23
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* @param content Serialized value to convert to a JS value.
|
|
28
|
-
* @return converted JS value
|
|
29
|
-
* @throws DataConverterException if conversion of the data passed as parameter failed for any
|
|
30
|
-
* reason.
|
|
24
|
+
* Converts a {@link Payload} back to a value.
|
|
31
25
|
*/
|
|
32
|
-
|
|
26
|
+
fromPayload<T>(payload: Payload): T;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class CompositePayloadConverter implements PayloadConverter {
|
|
30
|
+
readonly converters: PayloadConverterWithEncoding[];
|
|
31
|
+
readonly converterByEncoding: Map<string, PayloadConverterWithEncoding> = new Map();
|
|
32
|
+
|
|
33
|
+
constructor(...converters: PayloadConverterWithEncoding[]) {
|
|
34
|
+
this.converters = converters;
|
|
35
|
+
for (const converter of converters) {
|
|
36
|
+
this.converterByEncoding.set(converter.encodingType, converter);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
33
39
|
|
|
34
40
|
/**
|
|
35
|
-
*
|
|
36
|
-
* the
|
|
37
|
-
*
|
|
38
|
-
* Implements conversion of value to payload
|
|
41
|
+
* Tries to run `.toPayload(value)` on each converter in the order provided at construction.
|
|
42
|
+
* Returns the first successful result, or `undefined` if there is no converter that can handle the value.
|
|
39
43
|
*
|
|
40
|
-
* @
|
|
41
|
-
* @return converted value
|
|
42
|
-
* @throws DataConverterException if conversion of the value passed as parameter failed for any
|
|
43
|
-
* reason.
|
|
44
|
+
* @throws UnsupportedJsonTypeError
|
|
44
45
|
*/
|
|
45
|
-
|
|
46
|
+
public toPayload<T>(value: T): Payload | undefined {
|
|
47
|
+
for (const converter of this.converters) {
|
|
48
|
+
const result = converter.toPayload(value);
|
|
49
|
+
if (result !== undefined) {
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
46
55
|
|
|
47
56
|
/**
|
|
48
|
-
*
|
|
49
|
-
* the async version limits the functionality of the runtime.
|
|
50
|
-
*
|
|
51
|
-
* Implements conversion of payload to value.
|
|
52
|
-
*
|
|
53
|
-
* @param content Serialized value to convert to a JS value.
|
|
54
|
-
* @return converted JS value
|
|
55
|
-
* @throws DataConverterException if conversion of the data passed as parameter failed for any
|
|
56
|
-
* reason.
|
|
57
|
+
* Run {@link PayloadConverterWithEncoding.fromPayload} based on the {@link encodingTypes | encoding type} of the {@link Payload}.
|
|
57
58
|
*/
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return
|
|
59
|
+
public fromPayload<T>(payload: Payload): T {
|
|
60
|
+
if (payload.metadata === undefined || payload.metadata === null) {
|
|
61
|
+
throw new ValueError('Missing payload metadata');
|
|
62
|
+
}
|
|
63
|
+
const encoding = str(payload.metadata[METADATA_ENCODING_KEY]);
|
|
64
|
+
const converter = this.converterByEncoding.get(encoding);
|
|
65
|
+
if (converter === undefined) {
|
|
66
|
+
throw new ValueError(`Unknown encoding: ${encoding}`);
|
|
67
|
+
}
|
|
68
|
+
return converter.fromPayload(payload);
|
|
68
69
|
}
|
|
70
|
+
}
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Tries to convert `value` to a {@link Payload}. Throws if conversion fails.
|
|
74
|
+
*
|
|
75
|
+
* @throws {@link PayloadConverterError}
|
|
76
|
+
*/
|
|
77
|
+
export function toPayload(converter: PayloadConverter, value: unknown): Payload {
|
|
78
|
+
const payload = converter.toPayload(value);
|
|
79
|
+
if (payload === undefined) {
|
|
80
|
+
throw new PayloadConverterError(`Failed to convert value: ${value}`);
|
|
72
81
|
}
|
|
82
|
+
return payload;
|
|
73
83
|
}
|
|
74
84
|
|
|
75
85
|
/**
|
|
76
|
-
*
|
|
86
|
+
* Implements conversion of a list of values.
|
|
87
|
+
*
|
|
88
|
+
* @param converter
|
|
89
|
+
* @param values JS values to convert to Payloads
|
|
90
|
+
* @return converted values
|
|
91
|
+
* @throws PayloadConverterError if conversion of the value passed as parameter failed for any
|
|
92
|
+
* reason.
|
|
77
93
|
*/
|
|
78
|
-
export
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
public toDataSync(value: unknown): Payload | undefined {
|
|
82
|
-
if (value !== undefined) return undefined; // Can't encode
|
|
83
|
-
return {
|
|
84
|
-
metadata: {
|
|
85
|
-
[METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_NULL,
|
|
86
|
-
},
|
|
87
|
-
};
|
|
94
|
+
export function toPayloads(converter: PayloadConverter, ...values: unknown[]): Payload[] | undefined {
|
|
95
|
+
if (values.length === 0) {
|
|
96
|
+
return undefined;
|
|
88
97
|
}
|
|
89
98
|
|
|
90
|
-
|
|
91
|
-
return undefined as any; // Just return undefined
|
|
92
|
-
}
|
|
99
|
+
return values.map((value) => toPayload(converter, value));
|
|
93
100
|
}
|
|
94
101
|
|
|
95
102
|
/**
|
|
96
|
-
*
|
|
103
|
+
* Run {@link PayloadConverter.toPayload} on each value in the map.
|
|
104
|
+
*
|
|
105
|
+
* @throws {@link PayloadConverterError} if conversion of any value in the map fails
|
|
97
106
|
*/
|
|
98
|
-
export
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
metadata: {
|
|
105
|
-
[METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_JSON,
|
|
106
|
-
},
|
|
107
|
-
data: u8(JSON.stringify(value)),
|
|
108
|
-
};
|
|
109
|
-
}
|
|
107
|
+
export function mapToPayloads<K extends string>(converter: PayloadConverter, map: Record<K, any>): Record<K, Payload> {
|
|
108
|
+
return Object.fromEntries(
|
|
109
|
+
Object.entries(map).map(([k, v]): [K, Payload] => [k as K, toPayload(converter, v)])
|
|
110
|
+
) as Record<K, Payload>;
|
|
111
|
+
}
|
|
110
112
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Implements conversion of an array of values of different types. Useful for deserializing
|
|
115
|
+
* arguments of function invocations.
|
|
116
|
+
*
|
|
117
|
+
* @param converter
|
|
118
|
+
* @param index index of the value in the payloads
|
|
119
|
+
* @param payloads serialized value to convert to JS values.
|
|
120
|
+
* @return converted JS value
|
|
121
|
+
* @throws {@link PayloadConverterError} if conversion of the data passed as parameter failed for any
|
|
122
|
+
* reason.
|
|
123
|
+
*/
|
|
124
|
+
export function fromPayloadsAtIndex<T>(converter: PayloadConverter, index: number, payloads?: Payload[] | null): T {
|
|
125
|
+
// To make adding arguments a backwards compatible change
|
|
126
|
+
if (payloads === undefined || payloads === null || index >= payloads.length) {
|
|
127
|
+
return undefined as any;
|
|
116
128
|
}
|
|
129
|
+
return converter.fromPayload(payloads[index]);
|
|
117
130
|
}
|
|
118
131
|
|
|
119
132
|
/**
|
|
120
|
-
*
|
|
133
|
+
* Run {@link PayloadConverter.fromPayload} on each value in the array.
|
|
121
134
|
*/
|
|
122
|
-
export
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
public toDataSync(value: unknown): Payload | undefined {
|
|
126
|
-
// TODO: support any DataView or ArrayBuffer?
|
|
127
|
-
if (!(value instanceof Uint8Array)) {
|
|
128
|
-
return undefined;
|
|
129
|
-
}
|
|
130
|
-
return {
|
|
131
|
-
metadata: {
|
|
132
|
-
[METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_RAW,
|
|
133
|
-
},
|
|
134
|
-
data: value,
|
|
135
|
-
};
|
|
135
|
+
export function arrayFromPayloads(converter: PayloadConverter, payloads?: Payload[] | null): unknown[] {
|
|
136
|
+
if (!payloads) {
|
|
137
|
+
return [];
|
|
136
138
|
}
|
|
139
|
+
return payloads.map((payload: Payload) => converter.fromPayload(payload));
|
|
140
|
+
}
|
|
137
141
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
export class DefaultPayloadConverter extends CompositePayloadConverter {
|
|
143
|
+
// Match the order used in other SDKs, but exclude Protobuf converters so that the code, including
|
|
144
|
+
// `proto3-json-serializer`, doesn't take space in Workflow bundles that don't use Protobufs. To use Protobufs, use
|
|
145
|
+
// {@link DefaultPayloadConverterWithProtobufs}.
|
|
146
|
+
//
|
|
147
|
+
// Go SDK:
|
|
148
|
+
// https://github.com/temporalio/sdk-go/blob/5e5645f0c550dcf717c095ae32c76a7087d2e985/converter/default_data_converter.go#L28
|
|
149
|
+
constructor() {
|
|
150
|
+
super(new UndefinedPayloadConverter(), new BinaryPayloadConverter(), new JsonPayloadConverter());
|
|
141
151
|
}
|
|
142
152
|
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The default {@link PayloadConverter} used by the SDK.
|
|
156
|
+
* Supports `Uint8Array` and JSON serializables (so if [`JSON.stringify(yourArgOrRetval)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) works, the default payload converter will work).
|
|
157
|
+
*
|
|
158
|
+
* To also support Protobufs, create a custom payload converter with {@link DefaultPayloadConverter}:
|
|
159
|
+
*
|
|
160
|
+
* `const myConverter = new DefaultPayloadConverter({ protobufRoot })`
|
|
161
|
+
*/
|
|
162
|
+
export const defaultPayloadConverter = new DefaultPayloadConverter();
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { errorMessage, UnsupportedJsonTypeError, ValueError } from '@temporalio/internal-workflow-common';
|
|
2
|
+
import { PayloadConverter } from './payload-converter';
|
|
3
|
+
import { encodingKeys, EncodingType, encodingTypes, METADATA_ENCODING_KEY, Payload, str, u8 } from './types';
|
|
4
|
+
|
|
5
|
+
export interface PayloadConverterWithEncoding extends PayloadConverter {
|
|
6
|
+
readonly encodingType: EncodingType;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Converts between JS undefined and NULL Payload
|
|
11
|
+
*/
|
|
12
|
+
export class UndefinedPayloadConverter implements PayloadConverterWithEncoding {
|
|
13
|
+
public encodingType = encodingTypes.METADATA_ENCODING_NULL;
|
|
14
|
+
|
|
15
|
+
public toPayload(value: unknown): Payload | undefined {
|
|
16
|
+
if (value !== undefined) return undefined; // Can't encode
|
|
17
|
+
return {
|
|
18
|
+
metadata: {
|
|
19
|
+
[METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_NULL,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public fromPayload<T>(_content: Payload): T {
|
|
25
|
+
return undefined as any; // Just return undefined
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Converts between non-undefined values and serialized JSON Payload
|
|
31
|
+
*
|
|
32
|
+
* @throws UnsupportedJsonTypeError
|
|
33
|
+
*/
|
|
34
|
+
export class JsonPayloadConverter implements PayloadConverterWithEncoding {
|
|
35
|
+
public encodingType = encodingTypes.METADATA_ENCODING_JSON;
|
|
36
|
+
|
|
37
|
+
public toPayload(value: unknown): Payload | undefined {
|
|
38
|
+
if (value === undefined) return undefined;
|
|
39
|
+
|
|
40
|
+
let json;
|
|
41
|
+
try {
|
|
42
|
+
json = JSON.stringify(value);
|
|
43
|
+
} catch (e) {
|
|
44
|
+
throw new UnsupportedJsonTypeError(
|
|
45
|
+
`Can't run JSON.stringify on this value: ${value}. Either convert it (or its properties) to JSON-serializable values (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description ), or use a custom data converter: https://docs.temporal.io/docs/typescript/data-converters . JSON.stringify error message: ${errorMessage(
|
|
46
|
+
e
|
|
47
|
+
)}`,
|
|
48
|
+
e as Error
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
metadata: {
|
|
54
|
+
[METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_JSON,
|
|
55
|
+
},
|
|
56
|
+
data: u8(json),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public fromPayload<T>(content: Payload): T {
|
|
61
|
+
if (content.data === undefined || content.data === null) {
|
|
62
|
+
throw new ValueError('Got payload with no data');
|
|
63
|
+
}
|
|
64
|
+
return JSON.parse(str(content.data));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Converts between binary data types and RAW Payload
|
|
70
|
+
*/
|
|
71
|
+
export class BinaryPayloadConverter implements PayloadConverterWithEncoding {
|
|
72
|
+
public encodingType = encodingTypes.METADATA_ENCODING_RAW;
|
|
73
|
+
|
|
74
|
+
public toPayload(value: unknown): Payload | undefined {
|
|
75
|
+
// TODO: support any DataView or ArrayBuffer?
|
|
76
|
+
if (!(value instanceof Uint8Array)) return undefined;
|
|
77
|
+
return {
|
|
78
|
+
metadata: {
|
|
79
|
+
[METADATA_ENCODING_KEY]: encodingKeys.METADATA_ENCODING_RAW,
|
|
80
|
+
},
|
|
81
|
+
data: value,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public fromPayload<T>(content: Payload): T {
|
|
86
|
+
// TODO: support any DataView or ArrayBuffer?
|
|
87
|
+
return content.data as any;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import {
|
|
2
|
+
errorMessage,
|
|
3
|
+
hasOwnProperties,
|
|
4
|
+
hasOwnProperty,
|
|
5
|
+
isRecord,
|
|
6
|
+
PayloadConverterError,
|
|
7
|
+
ValueError,
|
|
8
|
+
} from '@temporalio/internal-workflow-common';
|
|
9
|
+
import * as protoJsonSerializer from 'proto3-json-serializer';
|
|
10
|
+
import type { Message, Namespace, Root, Type } from 'protobufjs';
|
|
11
|
+
import { CompositePayloadConverter } from './payload-converter';
|
|
12
|
+
import {
|
|
13
|
+
BinaryPayloadConverter,
|
|
14
|
+
JsonPayloadConverter,
|
|
15
|
+
PayloadConverterWithEncoding,
|
|
16
|
+
UndefinedPayloadConverter,
|
|
17
|
+
} from './payload-converters';
|
|
18
|
+
import {
|
|
19
|
+
EncodingType,
|
|
20
|
+
encodingTypes,
|
|
21
|
+
METADATA_ENCODING_KEY,
|
|
22
|
+
METADATA_MESSAGE_TYPE_KEY,
|
|
23
|
+
Payload,
|
|
24
|
+
str,
|
|
25
|
+
u8,
|
|
26
|
+
} from './types';
|
|
27
|
+
|
|
28
|
+
abstract class ProtobufPayloadConverter implements PayloadConverterWithEncoding {
|
|
29
|
+
protected readonly root: Root | undefined;
|
|
30
|
+
public abstract encodingType: EncodingType;
|
|
31
|
+
|
|
32
|
+
public abstract toPayload<T>(value: T): Payload | undefined;
|
|
33
|
+
public abstract fromPayload<T>(payload: Payload): T;
|
|
34
|
+
|
|
35
|
+
// Don't use type Root here because root.d.ts doesn't export Root, so users would have to type assert
|
|
36
|
+
constructor(root?: unknown) {
|
|
37
|
+
if (root) {
|
|
38
|
+
if (!isRoot(root)) {
|
|
39
|
+
throw new TypeError('root must be an instance of a protobufjs Root');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
this.root = root;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
protected validatePayload(content: Payload): { messageType: Type; data: Uint8Array } {
|
|
47
|
+
if (content.data === undefined || content.data === null) {
|
|
48
|
+
throw new ValueError('Got payload with no data');
|
|
49
|
+
}
|
|
50
|
+
if (!content.metadata || !(METADATA_MESSAGE_TYPE_KEY in content.metadata)) {
|
|
51
|
+
throw new ValueError(`Got protobuf payload without metadata.${METADATA_MESSAGE_TYPE_KEY}`);
|
|
52
|
+
}
|
|
53
|
+
if (!this.root) {
|
|
54
|
+
throw new PayloadConverterError('Unable to deserialize protobuf message without `root` being provided');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const messageTypeName = str(content.metadata[METADATA_MESSAGE_TYPE_KEY]);
|
|
58
|
+
let messageType;
|
|
59
|
+
try {
|
|
60
|
+
messageType = this.root.lookupType(messageTypeName);
|
|
61
|
+
} catch (e) {
|
|
62
|
+
if (errorMessage(e)?.includes('no such type')) {
|
|
63
|
+
throw new PayloadConverterError(
|
|
64
|
+
`Got a \`${messageTypeName}\` protobuf message but cannot find corresponding message class in \`root\``
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
throw e;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return { messageType, data: content.data };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
protected constructPayload({ messageTypeName, message }: { messageTypeName: string; message: Uint8Array }): Payload {
|
|
75
|
+
return {
|
|
76
|
+
metadata: {
|
|
77
|
+
[METADATA_ENCODING_KEY]: u8(this.encodingType),
|
|
78
|
+
[METADATA_MESSAGE_TYPE_KEY]: u8(messageTypeName),
|
|
79
|
+
},
|
|
80
|
+
data: message,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Converts between protobufjs Message instances and serialized Protobuf Payload
|
|
87
|
+
*/
|
|
88
|
+
export class ProtobufBinaryPayloadConverter extends ProtobufPayloadConverter {
|
|
89
|
+
public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @param root The value returned from {@link patchProtobufRoot}
|
|
93
|
+
*/
|
|
94
|
+
constructor(root?: unknown) {
|
|
95
|
+
super(root);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public toPayload(value: unknown): Payload | undefined {
|
|
99
|
+
if (!isProtobufMessage(value)) return undefined;
|
|
100
|
+
|
|
101
|
+
return this.constructPayload({
|
|
102
|
+
messageTypeName: getNamespacedTypeName(value.$type),
|
|
103
|
+
message: value.$type.encode(value).finish(),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public fromPayload<T>(content: Payload): T {
|
|
108
|
+
const { messageType, data } = this.validatePayload(content);
|
|
109
|
+
return messageType.decode(data) as unknown as T;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Converts between protobufjs Message instances and serialized JSON Payload
|
|
115
|
+
*/
|
|
116
|
+
export class ProtobufJsonPayloadConverter extends ProtobufPayloadConverter {
|
|
117
|
+
public encodingType = encodingTypes.METADATA_ENCODING_PROTOBUF_JSON;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @param root The value returned from {@link patchProtobufRoot}
|
|
121
|
+
*/
|
|
122
|
+
constructor(root?: unknown) {
|
|
123
|
+
super(root);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public toPayload(value: unknown): Payload | undefined {
|
|
127
|
+
if (!isProtobufMessage(value)) return undefined;
|
|
128
|
+
|
|
129
|
+
const jsonValue = protoJsonSerializer.toProto3JSON(value);
|
|
130
|
+
|
|
131
|
+
return this.constructPayload({
|
|
132
|
+
messageTypeName: getNamespacedTypeName(value.$type),
|
|
133
|
+
message: u8(JSON.stringify(jsonValue)),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public fromPayload<T>(content: Payload): T {
|
|
138
|
+
const { messageType, data } = this.validatePayload(content);
|
|
139
|
+
return protoJsonSerializer.fromProto3JSON(messageType, JSON.parse(str(data))) as unknown as T;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function isProtobufType(type: unknown): type is Type {
|
|
144
|
+
return (
|
|
145
|
+
isRecord(type) &&
|
|
146
|
+
type.constructor.name === 'Type' &&
|
|
147
|
+
hasOwnProperties(type, ['parent', 'name', 'create', 'encode', 'decode']) &&
|
|
148
|
+
typeof type.name === 'string' &&
|
|
149
|
+
typeof type.create === 'function' &&
|
|
150
|
+
typeof type.encode === 'function' &&
|
|
151
|
+
typeof type.decode === 'function'
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function isProtobufMessage(value: unknown): value is Message {
|
|
156
|
+
return isRecord(value) && hasOwnProperty(value, '$type') && isProtobufType(value.$type);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function getNamespacedTypeName(node: Type | Namespace): string {
|
|
160
|
+
if (node.parent && !isRoot(node.parent)) {
|
|
161
|
+
return getNamespacedTypeName(node.parent) + '.' + node.name;
|
|
162
|
+
} else {
|
|
163
|
+
return node.name;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function isRoot(root: unknown): root is Root {
|
|
168
|
+
return isRecord(root) && root.constructor.name === 'Root';
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export interface DefaultPayloadConverterWithProtobufsOptions {
|
|
172
|
+
/**
|
|
173
|
+
* The `root` provided to {@link ProtobufJsonPayloadConverter} and {@link ProtobufBinaryPayloadConverter}
|
|
174
|
+
*/
|
|
175
|
+
protobufRoot: Record<string, unknown>;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export class DefaultPayloadConverterWithProtobufs extends CompositePayloadConverter {
|
|
179
|
+
// Match the order used in other SDKs.
|
|
180
|
+
//
|
|
181
|
+
// Go SDK:
|
|
182
|
+
// https://github.com/temporalio/sdk-go/blob/5e5645f0c550dcf717c095ae32c76a7087d2e985/converter/default_data_converter.go#L28
|
|
183
|
+
constructor({ protobufRoot }: DefaultPayloadConverterWithProtobufsOptions) {
|
|
184
|
+
super(
|
|
185
|
+
new UndefinedPayloadConverter(),
|
|
186
|
+
new BinaryPayloadConverter(),
|
|
187
|
+
new ProtobufJsonPayloadConverter(protobufRoot),
|
|
188
|
+
new ProtobufBinaryPayloadConverter(protobufRoot),
|
|
189
|
+
new JsonPayloadConverter()
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
}
|
package/src/converter/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type * as iface from '@temporalio/proto/lib/coresdk';
|
|
2
|
-
import { TextEncoder, TextDecoder } from '
|
|
2
|
+
import { TextEncoder, TextDecoder } from './encoding';
|
|
3
3
|
|
|
4
4
|
export type Payload = iface.coresdk.common.IPayload;
|
|
5
5
|
|
|
@@ -22,6 +22,7 @@ export const encodingTypes = {
|
|
|
22
22
|
METADATA_ENCODING_PROTOBUF_JSON: 'json/protobuf',
|
|
23
23
|
METADATA_ENCODING_PROTOBUF: 'binary/protobuf',
|
|
24
24
|
} as const;
|
|
25
|
+
export type EncodingType = typeof encodingTypes[keyof typeof encodingTypes];
|
|
25
26
|
|
|
26
27
|
export const encodingKeys = {
|
|
27
28
|
METADATA_ENCODING_NULL: u8(encodingTypes.METADATA_ENCODING_NULL),
|
|
@@ -30,3 +31,5 @@ export const encodingKeys = {
|
|
|
30
31
|
METADATA_ENCODING_PROTOBUF_JSON: u8(encodingTypes.METADATA_ENCODING_PROTOBUF_JSON),
|
|
31
32
|
METADATA_ENCODING_PROTOBUF: u8(encodingTypes.METADATA_ENCODING_PROTOBUF),
|
|
32
33
|
} as const;
|
|
34
|
+
|
|
35
|
+
export const METADATA_MESSAGE_TYPE_KEY = 'messageType';
|