@2702rebels/wpidata 1.0.0

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.
Files changed (80) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +5 -0
  3. package/dist/abstractions.cjs +0 -0
  4. package/dist/abstractions.d.cts +246 -0
  5. package/dist/abstractions.d.cts.map +1 -0
  6. package/dist/abstractions.d.mts +246 -0
  7. package/dist/abstractions.d.mts.map +1 -0
  8. package/dist/abstractions.mjs +1 -0
  9. package/dist/formats/json.cjs +32 -0
  10. package/dist/formats/json.d.cts +14 -0
  11. package/dist/formats/json.d.cts.map +1 -0
  12. package/dist/formats/json.d.mts +14 -0
  13. package/dist/formats/json.d.mts.map +1 -0
  14. package/dist/formats/json.mjs +33 -0
  15. package/dist/formats/json.mjs.map +1 -0
  16. package/dist/formats/msgpack.cjs +30 -0
  17. package/dist/formats/msgpack.d.cts +14 -0
  18. package/dist/formats/msgpack.d.cts.map +1 -0
  19. package/dist/formats/msgpack.d.mts +14 -0
  20. package/dist/formats/msgpack.d.mts.map +1 -0
  21. package/dist/formats/msgpack.mjs +31 -0
  22. package/dist/formats/msgpack.mjs.map +1 -0
  23. package/dist/formats/protobuf.cjs +130 -0
  24. package/dist/formats/protobuf.d.cts +68 -0
  25. package/dist/formats/protobuf.d.cts.map +1 -0
  26. package/dist/formats/protobuf.d.mts +68 -0
  27. package/dist/formats/protobuf.d.mts.map +1 -0
  28. package/dist/formats/protobuf.mjs +128 -0
  29. package/dist/formats/protobuf.mjs.map +1 -0
  30. package/dist/formats/struct.cjs +593 -0
  31. package/dist/formats/struct.d.cts +134 -0
  32. package/dist/formats/struct.d.cts.map +1 -0
  33. package/dist/formats/struct.d.mts +134 -0
  34. package/dist/formats/struct.d.mts.map +1 -0
  35. package/dist/formats/struct.mjs +591 -0
  36. package/dist/formats/struct.mjs.map +1 -0
  37. package/dist/sink.cjs +360 -0
  38. package/dist/sink.d.cts +93 -0
  39. package/dist/sink.d.cts.map +1 -0
  40. package/dist/sink.d.mts +93 -0
  41. package/dist/sink.d.mts.map +1 -0
  42. package/dist/sink.mjs +361 -0
  43. package/dist/sink.mjs.map +1 -0
  44. package/dist/types/protobuf.cjs +0 -0
  45. package/dist/types/protobuf.d.cts +302 -0
  46. package/dist/types/protobuf.d.cts.map +1 -0
  47. package/dist/types/protobuf.d.mts +302 -0
  48. package/dist/types/protobuf.d.mts.map +1 -0
  49. package/dist/types/protobuf.mjs +1 -0
  50. package/dist/types/sendable.cjs +0 -0
  51. package/dist/types/sendable.d.cts +225 -0
  52. package/dist/types/sendable.d.cts.map +1 -0
  53. package/dist/types/sendable.d.mts +225 -0
  54. package/dist/types/sendable.d.mts.map +1 -0
  55. package/dist/types/sendable.mjs +1 -0
  56. package/dist/types/struct.cjs +0 -0
  57. package/dist/types/struct.d.cts +304 -0
  58. package/dist/types/struct.d.cts.map +1 -0
  59. package/dist/types/struct.d.mts +304 -0
  60. package/dist/types/struct.d.mts.map +1 -0
  61. package/dist/types/struct.mjs +1 -0
  62. package/dist/utils.cjs +140 -0
  63. package/dist/utils.d.cts +40 -0
  64. package/dist/utils.d.cts.map +1 -0
  65. package/dist/utils.d.mts +40 -0
  66. package/dist/utils.d.mts.map +1 -0
  67. package/dist/utils.mjs +135 -0
  68. package/dist/utils.mjs.map +1 -0
  69. package/package.json +51 -0
  70. package/src/abstractions.ts +308 -0
  71. package/src/formats/json.ts +53 -0
  72. package/src/formats/msgpack.ts +42 -0
  73. package/src/formats/protobuf.ts +213 -0
  74. package/src/formats/struct.test.ts +814 -0
  75. package/src/formats/struct.ts +992 -0
  76. package/src/sink.ts +611 -0
  77. package/src/types/protobuf.ts +334 -0
  78. package/src/types/sendable.ts +244 -0
  79. package/src/types/struct.ts +333 -0
  80. package/src/utils.ts +241 -0
@@ -0,0 +1,308 @@
1
+ export type DataType =
2
+ | "boolean"
3
+ | "string"
4
+ | "number"
5
+ | "booleanArray"
6
+ | "stringArray"
7
+ | "numberArray"
8
+ | "json"
9
+ | "binary"
10
+ | "composite";
11
+
12
+ export type DataTypeImpl =
13
+ | boolean
14
+ | string
15
+ | number
16
+ | Array<boolean>
17
+ | Array<string>
18
+ | Array<number>
19
+ | Record<string, unknown>
20
+ | Array<Record<string, unknown>>
21
+ | Uint8Array;
22
+
23
+ export interface StructuredTypeDescriptor {
24
+ /**
25
+ * **Type name**
26
+ *
27
+ * Typically the name represents the original type name without any adorners.
28
+ * For example, `struct:Pose3d`, `struct:Pose3d[]` would be represented
29
+ * as `Pose3d`, and `proto:wpi.proto.ProtobufPose3d` as `wpi.proto.ProtobufPose3d`.
30
+ *
31
+ * To determine if the parsed value is an array, consult `array` indicator and
32
+ * use `Array.isArray` method.
33
+ */
34
+ readonly name: string;
35
+
36
+ /**
37
+ * **Type format**
38
+ *
39
+ * Represents the origin (protocol) of the deserialized data.
40
+ *
41
+ * Due to WPILIB idiosyncrasies deserialized JSON representation of the same
42
+ * structured type may differ across various serialization protocols.
43
+ */
44
+ readonly format: "protobuf" | "struct" | "composite";
45
+
46
+ /**
47
+ * **Array indicator**
48
+ *
49
+ * Indicates that the value contains a top-level array of type.
50
+ */
51
+ readonly isArray?: boolean;
52
+ }
53
+
54
+ export interface DataChannelRecord<T extends DataTypeImpl = DataTypeImpl> {
55
+ /**
56
+ * **Timestamp (microseconds)**
57
+ *
58
+ * Timestamp of the record in microseconds. Typically represents robot time.
59
+ */
60
+ readonly timestamp: number;
61
+
62
+ /**
63
+ * **Value**
64
+ *
65
+ * Record value. Depends on the {@link DataChannel.dataType}.
66
+ */
67
+ value: T;
68
+ }
69
+
70
+ export interface DataChannelBase<T extends DataTypeImpl = DataTypeImpl> {
71
+ /**
72
+ * **Channel source**
73
+ *
74
+ * For example, `nt` or `wpilog`. This field is considered opaque.
75
+ */
76
+ readonly source: string;
77
+
78
+ /**
79
+ * **Channel identifier**
80
+ *
81
+ * Must be unique within the data source.
82
+ * NT4 — topic name
83
+ * WPILOG — data channel identifier
84
+ */
85
+ readonly id: string;
86
+
87
+ /**
88
+ * **Data representation type**
89
+ *
90
+ * This type indicates the processed data representation in the Javascript world.
91
+ * For example, NT4 source may differentiate between numeric types, such as
92
+ * 32-bit integer and 64-bit float, yet both are stored as `number`.
93
+ *
94
+ * Complex types are typically serialized using JSON, msgpack, struct or protobuf
95
+ * protocols and require a corresponding data transformer to be registered in
96
+ * the pipeline to unpack them. If no transformer is present the raw `binary`
97
+ * representation is used where data is stored in a byte array buffer.
98
+ */
99
+ readonly dataType: DataType;
100
+
101
+ /**
102
+ * **Raw data type**
103
+ *
104
+ * Raw data type string to be used for publishing. Retains original type specifics
105
+ * that cannot be represented in the JavaScript world, such as differences between
106
+ * various numeric types.
107
+ */
108
+ readonly publishedDataType: string;
109
+
110
+ /**
111
+ * **Protocol transformer**
112
+ *
113
+ * Unpacks raw binary data into JSON object according to the serialization protocol.
114
+ */
115
+ readonly transformer?: DataTransformer;
116
+
117
+ /**
118
+ * **Structured type descriptor**
119
+ *
120
+ * Applicable to complex types, serialized as struct or protobuf. Must be set
121
+ * by the corresponding transformer.
122
+ */
123
+ structuredType?: StructuredTypeDescriptor;
124
+
125
+ /**
126
+ * **Channel metadata**
127
+ *
128
+ * For NT4 metadata is extracted from topic properties beyond the default set.
129
+ * For WPILOG metadata is read and attempted to be parsed as JSON object first
130
+ * and stored as string otherwise.
131
+ *
132
+ * This field represents the most recently seen values as published by the source.
133
+ */
134
+ metadata?: Record<string, unknown> | string;
135
+
136
+ /**
137
+ * **Timestamped records**
138
+ *
139
+ * Timestamps are provided by the data source. The array is sorted by the timestamp
140
+ * in ascending order, earlier records have smaller indices.
141
+ */
142
+ records?: Array<DataChannelRecord<T>>;
143
+
144
+ /**
145
+ * **Timestamped dead-letter queue**
146
+ *
147
+ * Records that cannot be yet unpacked due to missing type descriptors.
148
+ *
149
+ * Timestamps are provided by the data source. The array is sorted by the timestamp
150
+ * in ascending order, earlier records have smaller indices.
151
+ */
152
+ dlq?: Array<DataChannelRecord<Uint8Array>>;
153
+
154
+ /**
155
+ * **Composite channel container**
156
+ *
157
+ * Returns the container composite channel for sub-channels that are part of it.
158
+ */
159
+ composite?: CompositeDataChannel;
160
+
161
+ /**
162
+ * **Publishes value to the channel**
163
+ *
164
+ * @param value value to publish
165
+ * @param path optional path to the sub-channel
166
+ * @param options additional publisher options
167
+ */
168
+ publish?: (value: unknown, path?: ReadonlyArray<string>, options?: DataChannelPublisherOptions) => void;
169
+ }
170
+
171
+ export interface BooleanDataChannel extends DataChannelBase<boolean> {
172
+ readonly dataType: "boolean";
173
+ }
174
+
175
+ export interface StringDataChannel extends DataChannelBase<string> {
176
+ readonly dataType: "string";
177
+ }
178
+
179
+ export interface NumberDataChannel extends DataChannelBase<number> {
180
+ readonly dataType: "number";
181
+ }
182
+
183
+ export interface BooleanArrayDataChannel extends DataChannelBase<Array<boolean>> {
184
+ readonly dataType: "booleanArray";
185
+ }
186
+
187
+ export interface StringArrayDataChannel extends DataChannelBase<Array<string>> {
188
+ readonly dataType: "stringArray";
189
+ }
190
+
191
+ export interface NumberArrayDataChannel extends DataChannelBase<Array<number>> {
192
+ readonly dataType: "numberArray";
193
+ }
194
+
195
+ export interface JsonDataChannel extends DataChannelBase<Record<string, unknown>> {
196
+ readonly dataType: "json";
197
+ }
198
+
199
+ export interface BinaryDataChannel extends DataChannelBase<Uint8Array> {
200
+ readonly dataType: "binary";
201
+ }
202
+
203
+ export interface CompositeDataChannel extends DataChannelBase<Record<string, unknown>> {
204
+ readonly dataType: "composite";
205
+
206
+ /**
207
+ * **Sub-channel identifiers and record paths**
208
+ *
209
+ * Identifiers of individual data channels that comprise this composite channel
210
+ * and their respective paths for the data to be written in the JSON record.
211
+ */
212
+ channels: Map<string, Array<string>>;
213
+ }
214
+
215
+ /**
216
+ * Represents an abstract data channel extracted from one of the supported data sources,
217
+ * such as NetworkTables or WPILOG.
218
+ *
219
+ * A channel tracks one or more data values as a set of timestamped records.
220
+ */
221
+ export type DataChannel =
222
+ | BooleanDataChannel
223
+ | StringDataChannel
224
+ | NumberDataChannel
225
+ | BooleanArrayDataChannel
226
+ | StringArrayDataChannel
227
+ | NumberArrayDataChannel
228
+ | JsonDataChannel
229
+ | BinaryDataChannel
230
+ | CompositeDataChannel;
231
+
232
+ /**
233
+ * Represents a transformer in the data pipeline.
234
+ *
235
+ * Transformers enable conversion of raw binary data into one of the supported
236
+ * {@link DataType} formats. For example, unpacking `struct`, `protobuf` or `msgpack`
237
+ * binary data requires a corresponding transformer.
238
+ *
239
+ * Some transforms, e.g. `struct` or `protobuf` may require schema, transported
240
+ * separately from the data block entries.
241
+ */
242
+ export interface DataTransformer {
243
+ /**
244
+ * Inspects a channel to determine whether it represents a schema or a data channel
245
+ * that must run through this transformer.
246
+ *
247
+ * This method must return either a {@link DataChannel} instance indicating a data
248
+ * channel, which must have {@link DataChannel.transformer} field set, or a string
249
+ * containing the schema type name indicating a schema channel. Otherwise,
250
+ * a value of `undefined` must be returned.
251
+ *
252
+ * @param source source
253
+ * @param name channel name
254
+ * @param type channel type
255
+ * @param metadata channel metadata
256
+ * @returns data channel, schema type name or `undefined`
257
+ */
258
+ inspect(
259
+ source: string,
260
+ name: string,
261
+ type: string,
262
+ metadata?: Record<string, unknown> | string
263
+ ): DataChannel | string | undefined;
264
+
265
+ /**
266
+ * Ingests raw schema data for the specified type name. This method will be invoked
267
+ * when the data arrives on a schema channel previously identified by {@link inspect}.
268
+ *
269
+ * @param typeName schema type name previously returned by {@link inspect}
270
+ * @param value raw schema data
271
+ */
272
+ schema(typeName: string, value: unknown): void;
273
+
274
+ /**
275
+ * Deserializes raw data. If the data cannot be transformed yet due to missing
276
+ * schema dependencies, this method must return `undefined` and the data will
277
+ * stored in the dead-letter queue to retry automatically at a later time.
278
+ *
279
+ * @param value raw value to deserialize
280
+ * @param type an optional {@link DataChannel.structuredType} descriptor
281
+ * @returns a deserialized value on success or `undefined` otherwise
282
+ */
283
+ deserialize(value: unknown, type?: StructuredTypeDescriptor): DataTypeImpl | undefined;
284
+
285
+ /**
286
+ * Serializes data into a raw format for publishing.
287
+ *
288
+ * @param value value to serialize
289
+ * @param type an optional {@link DataChannel.structuredType} descriptor
290
+ * @returns a serialized representation of the value
291
+ */
292
+ serialize(value: unknown, type?: StructuredTypeDescriptor): string | Uint8Array;
293
+
294
+ /**
295
+ * Determines whether data with the specified `type` can be transformed.
296
+ *
297
+ * The transformer may not be able to transform certain types if it is missing
298
+ * corresponding type descriptors yet to arrive via schema channel.
299
+ *
300
+ * @param type type name
301
+ */
302
+ canTransform(type: string): boolean;
303
+ }
304
+
305
+ export interface DataChannelPublisherOptions {
306
+ /** Published structured type */
307
+ structuredType?: StructuredTypeDescriptor;
308
+ }
@@ -0,0 +1,53 @@
1
+ import { toUint8Array } from "../utils";
2
+
3
+ import type { DataChannel, DataTransformer, DataTypeImpl } from "../abstractions";
4
+
5
+ const utf8decoder = new TextDecoder("utf-8", {
6
+ // throw TypeError on invalid data instead of silent substitution
7
+ fatal: true,
8
+ });
9
+
10
+ /** Implements {@link DataTransformer} interface for the `json` serialization protocol. */
11
+ export class JsonDataTransformer implements DataTransformer {
12
+ public inspect(
13
+ source: string,
14
+ name: string,
15
+ type: string,
16
+ metadata?: string | Record<string, unknown>
17
+ ): DataChannel | string | undefined {
18
+ if (type === "json") {
19
+ return {
20
+ source,
21
+ id: name,
22
+ dataType: "json",
23
+ publishedDataType: type,
24
+ transformer: this,
25
+ metadata,
26
+ };
27
+ }
28
+
29
+ return undefined;
30
+ }
31
+
32
+ public schema() {}
33
+
34
+ public deserialize(value: unknown): DataTypeImpl | undefined {
35
+ if (typeof value === "string") {
36
+ return value ? JSON.parse(value) : {};
37
+ }
38
+
39
+ return JSON.parse(utf8decoder.decode(toUint8Array(value)));
40
+ }
41
+
42
+ public serialize(value: unknown): string | Uint8Array {
43
+ if (value == null || typeof value !== "object") {
44
+ throw new Error("Only JSON objects can be serialized");
45
+ }
46
+
47
+ return JSON.stringify(value);
48
+ }
49
+
50
+ public canTransform(): boolean {
51
+ return true;
52
+ }
53
+ }
@@ -0,0 +1,42 @@
1
+ import { decode, encode } from "@msgpack/msgpack";
2
+
3
+ import { toUint8Array } from "../utils";
4
+
5
+ import type { DataChannel, DataTransformer, DataTypeImpl } from "../abstractions";
6
+
7
+ /** Implements {@link DataTransformer} interface for the `msgpack` serialization protocol. */
8
+ export class MsgpackDataTransformer implements DataTransformer {
9
+ public inspect(
10
+ source: string,
11
+ name: string,
12
+ type: string,
13
+ metadata?: string | Record<string, unknown>
14
+ ): DataChannel | string | undefined {
15
+ if (type === "msgpack") {
16
+ return {
17
+ source,
18
+ id: name,
19
+ dataType: "json",
20
+ publishedDataType: type,
21
+ transformer: this,
22
+ metadata,
23
+ };
24
+ }
25
+
26
+ return undefined;
27
+ }
28
+
29
+ public schema() {}
30
+
31
+ public deserialize(value: unknown): DataTypeImpl | undefined {
32
+ return decode(toUint8Array(value)) as DataTypeImpl;
33
+ }
34
+
35
+ public serialize(value: unknown): Uint8Array {
36
+ return encode(value);
37
+ }
38
+
39
+ public canTransform(): boolean {
40
+ return true;
41
+ }
42
+ }
@@ -0,0 +1,213 @@
1
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
2
+
3
+ import { Enum, Field, Root, Service, Type } from "protobufjs";
4
+ import { FileDescriptorProto } from "protobufjs/ext/descriptor";
5
+
6
+ import { toUint8Array } from "../utils";
7
+
8
+ import type { IFileDescriptorProto } from "protobufjs/ext/descriptor";
9
+ import type { DataChannel, DataTransformer, DataTypeImpl, StructuredTypeDescriptor } from "../abstractions";
10
+
11
+ const error = (message: string) => {
12
+ return new Error(message);
13
+ };
14
+
15
+ /**
16
+ * Unpacks protobuf serialized data into JSON object.
17
+ *
18
+ * @param name protobuf descriptor name
19
+ * @param data serialized binary data
20
+ * @param repository repository of available descriptors
21
+ */
22
+ export function unpack(
23
+ name: string,
24
+ data: Uint8Array<ArrayBufferLike>,
25
+ repository: ProtobufRepository
26
+ ): Record<string, unknown> {
27
+ const type = repository.root.lookupType(name); // throws on error
28
+ return type.decode(data) as unknown as Record<string, unknown>;
29
+ }
30
+ /**
31
+ * Packs JSON object into serialized protobuf data.
32
+ *
33
+ * @param name protobuf descriptor name
34
+ * @param value JSON object to pack
35
+ * @param repository repository of available descriptors
36
+ * @returns ArrayBuffer containing serialized data
37
+ */
38
+ export function pack(name: string, value: Record<string, unknown>, repository: ProtobufRepository): Uint8Array {
39
+ const type = repository.root.lookupType(name); // throws on error
40
+ return type.encode(value).finish();
41
+ }
42
+
43
+ /** Repository of protobuf descriptors. */
44
+ export class ProtobufRepository {
45
+ /**
46
+ * Root namespace.
47
+ */
48
+ public root = new Root();
49
+
50
+ /**
51
+ * Determines whether type can be transformed, indicating that
52
+ * the parsed type descriptor is present.
53
+ *
54
+ * @param name protobuf descriptor name
55
+ */
56
+ public canTransform(name: string) {
57
+ try {
58
+ return this.root.lookupType(name) != null;
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Unpacks protobuf serialized data into JSON object.
66
+ *
67
+ * @param name protobuf descriptor name
68
+ * @param data serialized binary data
69
+ */
70
+ public unpack(name: string, data: Uint8Array<ArrayBufferLike>) {
71
+ return unpack(name, data, this);
72
+ }
73
+
74
+ /**
75
+ * Packs JSON object into serialized protobuf data.
76
+ *
77
+ * @param name protobuf descriptor name
78
+ * @param value JSON object to pack
79
+ */
80
+ public pack(name: string, value: Record<string, unknown>) {
81
+ return pack(name, value, this);
82
+ }
83
+
84
+ /**
85
+ * Parses protobuf file descriptor and adds it to the repository.
86
+ *
87
+ * @param data descriptor binary data
88
+ */
89
+ public add(data: Uint8Array<ArrayBufferLike>): void {
90
+ const fd = FileDescriptorProto.decode(data) as IFileDescriptorProto;
91
+ if (fd.name == null) {
92
+ throw error(`Failed to parse protobuf file descriptor: missing name`);
93
+ }
94
+
95
+ // ignore duplicates
96
+ if (this.root.files.includes(fd.name)) {
97
+ return;
98
+ // throw error(`Failed to parse protobuf file descriptor: duplicate '${fd.name}' filename`);
99
+ }
100
+
101
+ const filePackage = fd.package ? this.root.define(fd.package) : this.root;
102
+
103
+ if (fd.name) {
104
+ filePackage.filename = fd.name;
105
+ this.root.files.push(fd.name);
106
+ }
107
+
108
+ if (fd.messageType) {
109
+ for (const messageType of fd.messageType) {
110
+ // @ts-expect-error
111
+ filePackage.add(Type.fromDescriptor(messageType, fd.syntax));
112
+ }
113
+ }
114
+
115
+ if (fd.enumType) {
116
+ for (const enumType of fd.enumType) {
117
+ // @ts-expect-error
118
+ filePackage.add(Enum.fromDescriptor(enumType));
119
+ }
120
+ }
121
+
122
+ if (fd.extension) {
123
+ for (const extension of fd.extension) {
124
+ // @ts-expect-error
125
+ filePackage.add(Field.fromDescriptor(extension));
126
+ }
127
+ }
128
+
129
+ if (fd.service) {
130
+ for (const service of fd.service) {
131
+ // @ts-expect-error
132
+ filePackage.add(Service.fromDescriptor(service));
133
+ }
134
+ }
135
+ }
136
+ }
137
+
138
+ /** Implements {@link DataTransformer} interface for the `protobuf` serialization protocol. */
139
+ export class ProtobufDataTransformer implements DataTransformer {
140
+ private readonly repo = new ProtobufRepository();
141
+
142
+ public inspect(
143
+ source: string,
144
+ name: string,
145
+ type: string,
146
+ metadata?: string | Record<string, unknown>
147
+ ): DataChannel | string | undefined {
148
+ if (name.startsWith("/.schema/proto:")) {
149
+ if (type !== "proto:FileDescriptorProto") {
150
+ throw new Error(`Unexpected type '${type}' for protobuf schema entry`);
151
+ }
152
+
153
+ return name.substring(15);
154
+ }
155
+
156
+ if (type.startsWith("proto:")) {
157
+ return {
158
+ source,
159
+ id: name,
160
+ dataType: "json",
161
+ publishedDataType: type,
162
+ transformer: this,
163
+ structuredType: {
164
+ name: type.slice(6),
165
+ format: "protobuf",
166
+ },
167
+ metadata,
168
+ };
169
+ }
170
+
171
+ return undefined;
172
+ }
173
+
174
+ public schema(typeName: string, value: unknown) {
175
+ this.repo.add(toUint8Array(value));
176
+ }
177
+
178
+ public deserialize(value: unknown, type?: StructuredTypeDescriptor): DataTypeImpl | undefined {
179
+ if (type == null) {
180
+ throw new Error(
181
+ `Transformation requires type to be specified. This situation should not be possible if the transformer is wired correctly.`
182
+ );
183
+ }
184
+
185
+ if (this.repo.canTransform(type.name)) {
186
+ return this.repo.unpack(type.name, toUint8Array(value));
187
+ }
188
+
189
+ return undefined;
190
+ }
191
+
192
+ public serialize(value: unknown, type?: StructuredTypeDescriptor): Uint8Array {
193
+ if (type == null) {
194
+ throw new Error(
195
+ `Transformation requires type to be specified. This situation should not be possible if the transformer is wired correctly.`
196
+ );
197
+ }
198
+
199
+ if (value == null || typeof value !== "object") {
200
+ throw new Error("Only JSON objects can be serialized");
201
+ }
202
+
203
+ if (this.repo.canTransform(type.name)) {
204
+ return this.repo.pack(type.name, value as Record<string, unknown>);
205
+ }
206
+
207
+ throw new Error(`Protobuf serialization is not supported for '${type.name}'`);
208
+ }
209
+
210
+ public canTransform(type: string): boolean {
211
+ return this.repo.canTransform(type);
212
+ }
213
+ }