spacetimedb 2.5.0 → 2.6.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.
- package/LICENSE.txt +759 -759
- package/README.md +211 -211
- package/dist/angular/index.cjs.map +1 -1
- package/dist/angular/index.mjs.map +1 -1
- package/dist/browser/angular/index.mjs.map +1 -1
- package/dist/browser/react/index.mjs +129 -57
- package/dist/browser/react/index.mjs.map +1 -1
- package/dist/browser/solid/index.mjs +120 -50
- package/dist/browser/solid/index.mjs.map +1 -1
- package/dist/browser/svelte/index.mjs.map +1 -1
- package/dist/browser/vue/index.mjs.map +1 -1
- package/dist/index.browser.mjs +10 -2
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.cjs +10 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +10 -2
- package/dist/index.mjs.map +1 -1
- package/dist/min/index.browser.mjs +1 -1
- package/dist/min/index.browser.mjs.map +1 -1
- package/dist/min/react/index.mjs +1 -1
- package/dist/min/react/index.mjs.map +1 -1
- package/dist/min/sdk/index.browser.mjs +1 -1
- package/dist/min/sdk/index.browser.mjs.map +1 -1
- package/dist/react/index.cjs +129 -57
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.mjs +129 -57
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/useTable.d.ts.map +1 -1
- package/dist/sdk/connection_manager.d.ts +8 -0
- package/dist/sdk/connection_manager.d.ts.map +1 -1
- package/dist/sdk/db_connection_impl.d.ts +7 -0
- package/dist/sdk/db_connection_impl.d.ts.map +1 -1
- package/dist/sdk/index.browser.mjs +10 -2
- package/dist/sdk/index.browser.mjs.map +1 -1
- package/dist/sdk/index.cjs +10 -2
- package/dist/sdk/index.cjs.map +1 -1
- package/dist/sdk/index.mjs +10 -2
- package/dist/sdk/index.mjs.map +1 -1
- package/dist/sdk/websocket_test_adapter.d.ts +2 -1
- package/dist/sdk/websocket_test_adapter.d.ts.map +1 -1
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/runtime.d.ts.map +1 -1
- package/dist/solid/index.cjs +120 -50
- package/dist/solid/index.cjs.map +1 -1
- package/dist/solid/index.mjs +120 -50
- package/dist/solid/index.mjs.map +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.mjs.map +1 -1
- package/dist/tanstack/index.cjs +120 -50
- package/dist/tanstack/index.cjs.map +1 -1
- package/dist/tanstack/index.mjs +120 -50
- package/dist/tanstack/index.mjs.map +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/angular/connection_state.ts +19 -19
- package/src/angular/index.ts +3 -3
- package/src/angular/injectors/index.ts +4 -4
- package/src/angular/injectors/inject-reducer.ts +62 -62
- package/src/angular/injectors/inject-spacetimedb-connected.ts +13 -13
- package/src/angular/injectors/inject-spacetimedb.ts +10 -10
- package/src/angular/injectors/inject-table.ts +234 -234
- package/src/angular/providers/index.ts +1 -1
- package/src/angular/providers/provide-spacetimedb.ts +96 -96
- package/src/index.ts +16 -16
- package/src/lib/algebraic_type.ts +819 -819
- package/src/lib/algebraic_type_variants.ts +26 -26
- package/src/lib/algebraic_value.ts +10 -10
- package/src/lib/autogen/types.ts +746 -746
- package/src/lib/binary_reader.ts +188 -188
- package/src/lib/binary_writer.ts +213 -213
- package/src/lib/connection_id.ts +102 -102
- package/src/lib/constraints.ts +48 -48
- package/src/lib/errors.ts +26 -26
- package/src/lib/filter.ts +195 -195
- package/src/lib/identity.ts +83 -83
- package/src/lib/indexes.ts +251 -251
- package/src/lib/option.ts +34 -34
- package/src/lib/query.ts +1019 -1019
- package/src/lib/reducer_schema.ts +38 -38
- package/src/lib/reducers.ts +116 -116
- package/src/lib/result.ts +36 -36
- package/src/lib/schedule_at.ts +86 -86
- package/src/lib/schema.ts +420 -420
- package/src/lib/table.ts +548 -548
- package/src/lib/table_schema.ts +64 -64
- package/src/lib/time_duration.ts +77 -77
- package/src/lib/timestamp.ts +148 -148
- package/src/lib/type_builders.test-d.ts +128 -128
- package/src/lib/type_builders.ts +4014 -4014
- package/src/lib/type_util.ts +124 -124
- package/src/lib/util.ts +196 -196
- package/src/lib/uuid.ts +337 -337
- package/src/react/SpacetimeDBProvider.ts +84 -84
- package/src/react/connection_state.ts +6 -6
- package/src/react/index.ts +5 -5
- package/src/react/useProcedure.ts +60 -60
- package/src/react/useReducer.ts +53 -53
- package/src/react/useSpacetimeDB.ts +18 -18
- package/src/react/useTable.ts +256 -251
- package/src/sdk/client_api/index.ts +114 -114
- package/src/sdk/client_api/types/procedures.ts +8 -8
- package/src/sdk/client_api/types/reducers.ts +8 -8
- package/src/sdk/client_api/types.ts +288 -288
- package/src/sdk/client_cache.ts +129 -129
- package/src/sdk/client_table.ts +179 -179
- package/src/sdk/connection_manager.ts +352 -237
- package/src/sdk/db_connection_builder.ts +290 -290
- package/src/sdk/db_connection_impl.ts +1356 -1347
- package/src/sdk/db_context.ts +28 -28
- package/src/sdk/db_view.ts +12 -12
- package/src/sdk/decompress.ts +51 -51
- package/src/sdk/event.ts +18 -18
- package/src/sdk/event_context.ts +51 -51
- package/src/sdk/event_emitter.ts +32 -32
- package/src/sdk/index.ts +14 -14
- package/src/sdk/internal.ts +2 -2
- package/src/sdk/json_api.ts +46 -46
- package/src/sdk/logger.ts +134 -134
- package/src/sdk/message_types.ts +46 -46
- package/src/sdk/procedures.ts +83 -83
- package/src/sdk/reducer_event.ts +20 -20
- package/src/sdk/reducer_handle.ts +12 -12
- package/src/sdk/reducers.ts +159 -159
- package/src/sdk/schema.ts +45 -45
- package/src/sdk/spacetime_module.ts +28 -28
- package/src/sdk/subscription_builder_impl.ts +275 -275
- package/src/sdk/table_cache.ts +581 -581
- package/src/sdk/type_utils.ts +19 -19
- package/src/sdk/version.ts +133 -133
- package/src/sdk/websocket_decompress_adapter.ts +63 -63
- package/src/sdk/websocket_protocols.ts +25 -25
- package/src/sdk/websocket_test_adapter.ts +107 -100
- package/src/sdk/websocket_v3_frames.ts +126 -126
- package/src/sdk/ws.ts +105 -105
- package/src/server/console.ts +81 -81
- package/src/server/db_view.ts +21 -21
- package/src/server/errors.ts +138 -138
- package/src/server/http.test-d.ts +80 -80
- package/src/server/http.ts +14 -14
- package/src/server/http_handlers.ts +413 -413
- package/src/server/http_internal.ts +79 -79
- package/src/server/http_shared.ts +186 -186
- package/src/server/index.ts +37 -37
- package/src/server/polyfills.ts +4 -4
- package/src/server/procedures.ts +239 -239
- package/src/server/query.ts +1 -1
- package/src/server/range.ts +53 -53
- package/src/server/reducers.ts +113 -113
- package/src/server/rng.ts +113 -113
- package/src/server/runtime.ts +1102 -1102
- package/src/server/schema.test-d.ts +99 -99
- package/src/server/schema.ts +663 -663
- package/src/server/sys.d.ts +125 -125
- package/src/server/view.test-d.ts +194 -194
- package/src/server/views.ts +340 -340
- package/src/solid/SpacetimeDBProvider.ts +97 -97
- package/src/solid/connection_state.ts +6 -6
- package/src/solid/index.ts +5 -5
- package/src/solid/useProcedure.ts +57 -57
- package/src/solid/useReducer.ts +50 -50
- package/src/solid/useSpacetimeDB.ts +18 -18
- package/src/solid/useTable.ts +203 -203
- package/src/svelte/SpacetimeDBProvider.ts +101 -101
- package/src/svelte/connection_state.ts +16 -16
- package/src/svelte/index.ts +4 -4
- package/src/svelte/useReducer.ts +61 -61
- package/src/svelte/useSpacetimeDB.ts +22 -22
- package/src/svelte/useTable.ts +218 -218
- package/src/tanstack/SpacetimeDBQueryClient.ts +330 -330
- package/src/tanstack/hooks.ts +83 -83
- package/src/tanstack/index.ts +16 -16
- package/src/util-stub.ts +1 -1
- package/src/vue/SpacetimeDBProvider.ts +157 -157
- package/src/vue/connection_state.ts +19 -19
- package/src/vue/index.ts +5 -5
- package/src/vue/useProcedure.ts +62 -62
- package/src/vue/useReducer.ts +55 -55
- package/src/vue/useSpacetimeDB.ts +18 -18
- package/src/vue/useTable.ts +229 -229
|
@@ -1,819 +1,819 @@
|
|
|
1
|
-
import { TimeDuration } from './time_duration';
|
|
2
|
-
import { Timestamp } from './timestamp';
|
|
3
|
-
import { Uuid } from './uuid';
|
|
4
|
-
import { ConnectionId } from './connection_id';
|
|
5
|
-
import BinaryReader from './binary_reader';
|
|
6
|
-
import BinaryWriter from './binary_writer';
|
|
7
|
-
import { Identity } from './identity';
|
|
8
|
-
import * as AlgebraicTypeVariants from './algebraic_type_variants';
|
|
9
|
-
import { hasOwn } from './util';
|
|
10
|
-
|
|
11
|
-
type TypespaceType = {
|
|
12
|
-
types: AlgebraicTypeType[];
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export type ProductTypeType = {
|
|
16
|
-
elements: ProductTypeElement[];
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* A factor / element of a product type.
|
|
21
|
-
*
|
|
22
|
-
* An element consist of an optional name and a type.
|
|
23
|
-
*
|
|
24
|
-
* NOTE: Each element has an implicit element tag based on its order.
|
|
25
|
-
* Uniquely identifies an element similarly to protobuf tags.
|
|
26
|
-
*/
|
|
27
|
-
export type ProductTypeElement = {
|
|
28
|
-
name: string | undefined;
|
|
29
|
-
algebraicType: AlgebraicTypeType;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export type SumTypeType = {
|
|
33
|
-
variants: SumTypeVariant[];
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* A variant of a sum type.
|
|
38
|
-
*
|
|
39
|
-
* NOTE: Each element has an implicit element tag based on its order.
|
|
40
|
-
* Uniquely identifies an element similarly to protobuf tags.
|
|
41
|
-
*/
|
|
42
|
-
export type SumTypeVariant = {
|
|
43
|
-
name: string | undefined;
|
|
44
|
-
algebraicType: AlgebraicTypeType;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export type AlgebraicTypeType =
|
|
48
|
-
| AlgebraicTypeVariants.Ref
|
|
49
|
-
| AlgebraicTypeVariants.Sum
|
|
50
|
-
| AlgebraicTypeVariants.Product
|
|
51
|
-
| AlgebraicTypeVariants.Array
|
|
52
|
-
| AlgebraicTypeVariants.String
|
|
53
|
-
| AlgebraicTypeVariants.Bool
|
|
54
|
-
| AlgebraicTypeVariants.I8
|
|
55
|
-
| AlgebraicTypeVariants.U8
|
|
56
|
-
| AlgebraicTypeVariants.I16
|
|
57
|
-
| AlgebraicTypeVariants.U16
|
|
58
|
-
| AlgebraicTypeVariants.I32
|
|
59
|
-
| AlgebraicTypeVariants.U32
|
|
60
|
-
| AlgebraicTypeVariants.I64
|
|
61
|
-
| AlgebraicTypeVariants.U64
|
|
62
|
-
| AlgebraicTypeVariants.I128
|
|
63
|
-
| AlgebraicTypeVariants.U128
|
|
64
|
-
| AlgebraicTypeVariants.I256
|
|
65
|
-
| AlgebraicTypeVariants.U256
|
|
66
|
-
| AlgebraicTypeVariants.F32
|
|
67
|
-
| AlgebraicTypeVariants.F64;
|
|
68
|
-
|
|
69
|
-
export type AlgebraicType = AlgebraicTypeType;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* The variant types of the Algebraic Type tagged union.
|
|
73
|
-
*/
|
|
74
|
-
export { AlgebraicTypeVariants };
|
|
75
|
-
|
|
76
|
-
export type Serializer<T> = (writer: BinaryWriter, value: T) => void;
|
|
77
|
-
|
|
78
|
-
export type Deserializer<T> = (reader: BinaryReader) => T;
|
|
79
|
-
|
|
80
|
-
// Caches to prevent `makeSerializer`/`makeDeserializer` from recursing
|
|
81
|
-
// infinitely when called on recursive types.
|
|
82
|
-
//
|
|
83
|
-
// We check for recursion in `{Product,Sum}Type.make{Deser,Ser}ializer` rather
|
|
84
|
-
// than in `AlgebraciType.make{Deser,Ser}ializer` because we need to store the
|
|
85
|
-
// [de]serializer in the cache before recursing into its fields/variants, and
|
|
86
|
-
// we wouldn't be able to do that in the `AlgebraicType` functions.
|
|
87
|
-
const SERIALIZERS = new Map<ProductType | SumType, Serializer<any>>();
|
|
88
|
-
const DESERIALIZERS = new Map<ProductType | SumType, Deserializer<any>>();
|
|
89
|
-
|
|
90
|
-
// A value with helper functions to construct the type.
|
|
91
|
-
export const AlgebraicType = {
|
|
92
|
-
Ref: (value: number): AlgebraicTypeVariants.Ref => ({ tag: 'Ref', value }),
|
|
93
|
-
Sum: <T extends SumTypeType>(value: T): { tag: 'Sum'; value: T } => ({
|
|
94
|
-
tag: 'Sum',
|
|
95
|
-
value,
|
|
96
|
-
}),
|
|
97
|
-
Product: <T extends ProductTypeType>(
|
|
98
|
-
value: T
|
|
99
|
-
): { tag: 'Product'; value: T } => ({
|
|
100
|
-
tag: 'Product',
|
|
101
|
-
value,
|
|
102
|
-
}),
|
|
103
|
-
Array: <T extends AlgebraicTypeType>(
|
|
104
|
-
value: T
|
|
105
|
-
): { tag: 'Array'; value: T } => ({
|
|
106
|
-
tag: 'Array',
|
|
107
|
-
value,
|
|
108
|
-
}),
|
|
109
|
-
String: { tag: 'String' } as const,
|
|
110
|
-
Bool: { tag: 'Bool' } as const,
|
|
111
|
-
I8: { tag: 'I8' } as const,
|
|
112
|
-
U8: { tag: 'U8' } as const,
|
|
113
|
-
I16: { tag: 'I16' } as const,
|
|
114
|
-
U16: { tag: 'U16' } as const,
|
|
115
|
-
I32: { tag: 'I32' } as const,
|
|
116
|
-
U32: { tag: 'U32' } as const,
|
|
117
|
-
I64: { tag: 'I64' } as const,
|
|
118
|
-
U64: { tag: 'U64' } as const,
|
|
119
|
-
I128: { tag: 'I128' } as const,
|
|
120
|
-
U128: { tag: 'U128' } as const,
|
|
121
|
-
I256: { tag: 'I256' } as const,
|
|
122
|
-
U256: { tag: 'U256' } as const,
|
|
123
|
-
F32: { tag: 'F32' } as const,
|
|
124
|
-
F64: { tag: 'F64' } as const,
|
|
125
|
-
makeSerializer(
|
|
126
|
-
ty: AlgebraicTypeType,
|
|
127
|
-
typespace?: TypespaceType
|
|
128
|
-
): Serializer<any> {
|
|
129
|
-
if (ty.tag === 'Ref') {
|
|
130
|
-
if (!typespace)
|
|
131
|
-
throw new Error('cannot serialize refs without a typespace');
|
|
132
|
-
while (ty.tag === 'Ref') ty = typespace.types[ty.value];
|
|
133
|
-
}
|
|
134
|
-
switch (ty.tag) {
|
|
135
|
-
case 'Product':
|
|
136
|
-
return ProductType.makeSerializer(ty.value, typespace);
|
|
137
|
-
case 'Sum':
|
|
138
|
-
return SumType.makeSerializer(ty.value, typespace);
|
|
139
|
-
case 'Array':
|
|
140
|
-
if (ty.value.tag === 'U8') {
|
|
141
|
-
return serializeUint8Array;
|
|
142
|
-
} else {
|
|
143
|
-
const serialize = AlgebraicType.makeSerializer(ty.value, typespace);
|
|
144
|
-
return (writer, value) => {
|
|
145
|
-
writer.writeU32(value.length);
|
|
146
|
-
for (const elem of value) {
|
|
147
|
-
serialize(writer, elem);
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
default:
|
|
152
|
-
return primitiveSerializers[ty.tag];
|
|
153
|
-
}
|
|
154
|
-
},
|
|
155
|
-
/** @deprecated Use `makeSerializer` instead. */
|
|
156
|
-
serializeValue(
|
|
157
|
-
writer: BinaryWriter,
|
|
158
|
-
ty: AlgebraicTypeType,
|
|
159
|
-
value: any,
|
|
160
|
-
typespace?: TypespaceType
|
|
161
|
-
) {
|
|
162
|
-
AlgebraicType.makeSerializer(ty, typespace)(writer, value);
|
|
163
|
-
},
|
|
164
|
-
makeDeserializer(
|
|
165
|
-
ty: AlgebraicTypeType,
|
|
166
|
-
typespace?: TypespaceType
|
|
167
|
-
): Deserializer<any> {
|
|
168
|
-
if (ty.tag === 'Ref') {
|
|
169
|
-
if (!typespace)
|
|
170
|
-
throw new Error('cannot deserialize refs without a typespace');
|
|
171
|
-
while (ty.tag === 'Ref') ty = typespace.types[ty.value];
|
|
172
|
-
}
|
|
173
|
-
switch (ty.tag) {
|
|
174
|
-
case 'Product':
|
|
175
|
-
return ProductType.makeDeserializer(ty.value, typespace);
|
|
176
|
-
case 'Sum':
|
|
177
|
-
return SumType.makeDeserializer(ty.value, typespace);
|
|
178
|
-
case 'Array':
|
|
179
|
-
if (ty.value.tag === 'U8') {
|
|
180
|
-
return deserializeUint8Array;
|
|
181
|
-
} else {
|
|
182
|
-
const deserialize = AlgebraicType.makeDeserializer(
|
|
183
|
-
ty.value,
|
|
184
|
-
typespace
|
|
185
|
-
);
|
|
186
|
-
return reader => {
|
|
187
|
-
const length = reader.readU32();
|
|
188
|
-
const result: any[] = Array(length);
|
|
189
|
-
for (let i = 0; i < length; i++) {
|
|
190
|
-
result[i] = deserialize(reader);
|
|
191
|
-
}
|
|
192
|
-
return result;
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
default:
|
|
196
|
-
return primitiveDeserializers[ty.tag];
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
/** @deprecated Use `makeDeserializer` instead. */
|
|
200
|
-
deserializeValue(
|
|
201
|
-
reader: BinaryReader,
|
|
202
|
-
ty: AlgebraicTypeType,
|
|
203
|
-
typespace?: TypespaceType
|
|
204
|
-
): any {
|
|
205
|
-
return AlgebraicType.makeDeserializer(ty, typespace)(reader);
|
|
206
|
-
},
|
|
207
|
-
/**
|
|
208
|
-
* Convert a value of the algebraic type into something that can be used as a key in a map.
|
|
209
|
-
* There are no guarantees about being able to order it.
|
|
210
|
-
* This is only guaranteed to be comparable to other values of the same type.
|
|
211
|
-
* @param value A value of the algebraic type
|
|
212
|
-
* @returns Something that can be used as a key in a map.
|
|
213
|
-
*/
|
|
214
|
-
intoMapKey: function (
|
|
215
|
-
ty: AlgebraicTypeType,
|
|
216
|
-
value: any
|
|
217
|
-
): ComparablePrimitive {
|
|
218
|
-
switch (ty.tag) {
|
|
219
|
-
case 'U8':
|
|
220
|
-
case 'U16':
|
|
221
|
-
case 'U32':
|
|
222
|
-
case 'U64':
|
|
223
|
-
case 'U128':
|
|
224
|
-
case 'U256':
|
|
225
|
-
case 'I8':
|
|
226
|
-
case 'I16':
|
|
227
|
-
case 'I32':
|
|
228
|
-
case 'I64':
|
|
229
|
-
case 'I128':
|
|
230
|
-
case 'I256':
|
|
231
|
-
case 'F32':
|
|
232
|
-
case 'F64':
|
|
233
|
-
case 'String':
|
|
234
|
-
case 'Bool':
|
|
235
|
-
return value;
|
|
236
|
-
case 'Product':
|
|
237
|
-
return ProductType.intoMapKey(ty.value, value);
|
|
238
|
-
default: {
|
|
239
|
-
// The fallback is to serialize and base64 encode the bytes.
|
|
240
|
-
const writer = new BinaryWriter(10);
|
|
241
|
-
AlgebraicType.serializeValue(writer, ty, value);
|
|
242
|
-
return writer.toBase64();
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
},
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
function bindCall<F extends (this: any, ...args: any[]) => any>(
|
|
249
|
-
f: F
|
|
250
|
-
): (recv: ThisParameterType<F>, ...args: Parameters<F>) => ReturnType<F> {
|
|
251
|
-
return Function.prototype.call.bind(f);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
type Primitives = Exclude<
|
|
255
|
-
AlgebraicType['tag'],
|
|
256
|
-
'Ref' | 'Sum' | 'Product' | 'Array'
|
|
257
|
-
>;
|
|
258
|
-
|
|
259
|
-
const primitiveSerializers: Record<Primitives, Serializer<any>> = {
|
|
260
|
-
Bool: bindCall(BinaryWriter.prototype.writeBool),
|
|
261
|
-
I8: bindCall(BinaryWriter.prototype.writeI8),
|
|
262
|
-
U8: bindCall(BinaryWriter.prototype.writeU8),
|
|
263
|
-
I16: bindCall(BinaryWriter.prototype.writeI16),
|
|
264
|
-
U16: bindCall(BinaryWriter.prototype.writeU16),
|
|
265
|
-
I32: bindCall(BinaryWriter.prototype.writeI32),
|
|
266
|
-
U32: bindCall(BinaryWriter.prototype.writeU32),
|
|
267
|
-
I64: bindCall(BinaryWriter.prototype.writeI64),
|
|
268
|
-
U64: bindCall(BinaryWriter.prototype.writeU64),
|
|
269
|
-
I128: bindCall(BinaryWriter.prototype.writeI128),
|
|
270
|
-
U128: bindCall(BinaryWriter.prototype.writeU128),
|
|
271
|
-
I256: bindCall(BinaryWriter.prototype.writeI256),
|
|
272
|
-
U256: bindCall(BinaryWriter.prototype.writeU256),
|
|
273
|
-
F32: bindCall(BinaryWriter.prototype.writeF32),
|
|
274
|
-
F64: bindCall(BinaryWriter.prototype.writeF64),
|
|
275
|
-
String: bindCall(BinaryWriter.prototype.writeString),
|
|
276
|
-
};
|
|
277
|
-
Object.freeze(primitiveSerializers);
|
|
278
|
-
|
|
279
|
-
const serializeUint8Array = bindCall(BinaryWriter.prototype.writeUInt8Array);
|
|
280
|
-
|
|
281
|
-
const primitiveDeserializers: Record<Primitives, Deserializer<any>> = {
|
|
282
|
-
Bool: bindCall(BinaryReader.prototype.readBool),
|
|
283
|
-
I8: bindCall(BinaryReader.prototype.readI8),
|
|
284
|
-
U8: bindCall(BinaryReader.prototype.readU8),
|
|
285
|
-
I16: bindCall(BinaryReader.prototype.readI16),
|
|
286
|
-
U16: bindCall(BinaryReader.prototype.readU16),
|
|
287
|
-
I32: bindCall(BinaryReader.prototype.readI32),
|
|
288
|
-
U32: bindCall(BinaryReader.prototype.readU32),
|
|
289
|
-
I64: bindCall(BinaryReader.prototype.readI64),
|
|
290
|
-
U64: bindCall(BinaryReader.prototype.readU64),
|
|
291
|
-
I128: bindCall(BinaryReader.prototype.readI128),
|
|
292
|
-
U128: bindCall(BinaryReader.prototype.readU128),
|
|
293
|
-
I256: bindCall(BinaryReader.prototype.readI256),
|
|
294
|
-
U256: bindCall(BinaryReader.prototype.readU256),
|
|
295
|
-
F32: bindCall(BinaryReader.prototype.readF32),
|
|
296
|
-
F64: bindCall(BinaryReader.prototype.readF64),
|
|
297
|
-
String: bindCall(BinaryReader.prototype.readString),
|
|
298
|
-
};
|
|
299
|
-
Object.freeze(primitiveDeserializers);
|
|
300
|
-
|
|
301
|
-
const deserializeUint8Array = bindCall(BinaryReader.prototype.readUInt8Array);
|
|
302
|
-
|
|
303
|
-
type FixedSizePrimitives = Exclude<Primitives, 'String'>;
|
|
304
|
-
|
|
305
|
-
const primitiveSizes: Record<FixedSizePrimitives, number> = {
|
|
306
|
-
Bool: 1,
|
|
307
|
-
I8: 1,
|
|
308
|
-
U8: 1,
|
|
309
|
-
I16: 2,
|
|
310
|
-
U16: 2,
|
|
311
|
-
I32: 4,
|
|
312
|
-
U32: 4,
|
|
313
|
-
I64: 8,
|
|
314
|
-
U64: 8,
|
|
315
|
-
I128: 16,
|
|
316
|
-
U128: 16,
|
|
317
|
-
I256: 32,
|
|
318
|
-
U256: 32,
|
|
319
|
-
F32: 4,
|
|
320
|
-
F64: 8,
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
const fixedSizePrimitives = new Set(Object.keys(primitiveSizes));
|
|
324
|
-
|
|
325
|
-
type FixedSizeProductType = {
|
|
326
|
-
elements: { name: string; algebraicType: { tag: FixedSizePrimitives } }[];
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
const isFixedSizeProduct = (ty: ProductType): ty is FixedSizeProductType =>
|
|
330
|
-
ty.elements.every(({ algebraicType }) =>
|
|
331
|
-
fixedSizePrimitives.has(algebraicType.tag)
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
const productSize = (ty: FixedSizeProductType): number =>
|
|
335
|
-
ty.elements.reduce(
|
|
336
|
-
(acc, { algebraicType }) => acc + primitiveSizes[algebraicType.tag],
|
|
337
|
-
0
|
|
338
|
-
);
|
|
339
|
-
|
|
340
|
-
type JSPrimitives = Exclude<
|
|
341
|
-
FixedSizePrimitives,
|
|
342
|
-
'I128' | 'U128' | 'I256' | 'U256'
|
|
343
|
-
>;
|
|
344
|
-
|
|
345
|
-
const primitiveJSName: Record<JSPrimitives, string> = {
|
|
346
|
-
Bool: 'Uint8',
|
|
347
|
-
I8: 'Int8',
|
|
348
|
-
U8: 'Uint8',
|
|
349
|
-
I16: 'Int16',
|
|
350
|
-
U16: 'Uint16',
|
|
351
|
-
I32: 'Int32',
|
|
352
|
-
U32: 'Uint32',
|
|
353
|
-
I64: 'BigInt64',
|
|
354
|
-
U64: 'BigUint64',
|
|
355
|
-
F32: 'Float32',
|
|
356
|
-
F64: 'Float64',
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
type SpecialProducts = {
|
|
360
|
-
__time_duration_micros__: TimeDuration;
|
|
361
|
-
__timestamp_micros_since_unix_epoch__: Timestamp;
|
|
362
|
-
__identity__: Identity;
|
|
363
|
-
__connection_id__: ConnectionId;
|
|
364
|
-
__uuid__: Uuid;
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
const specialProductDeserializers: {
|
|
368
|
-
[k in keyof SpecialProducts]: Deserializer<SpecialProducts[k]>;
|
|
369
|
-
} = {
|
|
370
|
-
__time_duration_micros__: reader => new TimeDuration(reader.readI64()),
|
|
371
|
-
__timestamp_micros_since_unix_epoch__: reader =>
|
|
372
|
-
new Timestamp(reader.readI64()),
|
|
373
|
-
__identity__: reader => new Identity(reader.readU256()),
|
|
374
|
-
__connection_id__: reader => new ConnectionId(reader.readU128()),
|
|
375
|
-
__uuid__: reader => new Uuid(reader.readU128()),
|
|
376
|
-
};
|
|
377
|
-
Object.freeze(specialProductDeserializers);
|
|
378
|
-
|
|
379
|
-
const unitDeserializer: Deserializer<{}> = () => ({});
|
|
380
|
-
|
|
381
|
-
const getElementInitializer = (element: ProductTypeElement) => {
|
|
382
|
-
let init: string;
|
|
383
|
-
switch (element.algebraicType.tag) {
|
|
384
|
-
case 'String':
|
|
385
|
-
init = "''";
|
|
386
|
-
break;
|
|
387
|
-
case 'Bool':
|
|
388
|
-
init = 'false';
|
|
389
|
-
break;
|
|
390
|
-
case 'I8':
|
|
391
|
-
case 'U8':
|
|
392
|
-
case 'I16':
|
|
393
|
-
case 'U16':
|
|
394
|
-
case 'I32':
|
|
395
|
-
case 'U32':
|
|
396
|
-
init = '0';
|
|
397
|
-
break;
|
|
398
|
-
case 'I64':
|
|
399
|
-
case 'U64':
|
|
400
|
-
case 'I128':
|
|
401
|
-
case 'U128':
|
|
402
|
-
case 'I256':
|
|
403
|
-
case 'U256':
|
|
404
|
-
init = '0n';
|
|
405
|
-
break;
|
|
406
|
-
case 'F32':
|
|
407
|
-
case 'F64':
|
|
408
|
-
init = '0.0';
|
|
409
|
-
break;
|
|
410
|
-
default:
|
|
411
|
-
init = 'undefined';
|
|
412
|
-
}
|
|
413
|
-
return `${element.name!}: ${init}`;
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* A structural product type of the factors given by `elements`.
|
|
418
|
-
*
|
|
419
|
-
* This is also known as `struct` and `tuple` in many languages,
|
|
420
|
-
* but note that unlike most languages, products in SATs are *[structural]* and not nominal.
|
|
421
|
-
* When checking whether two nominal types are the same,
|
|
422
|
-
* their names and/or declaration sites (e.g., module / namespace) are considered.
|
|
423
|
-
* Meanwhile, a structural type system would only check the structure of the type itself,
|
|
424
|
-
* e.g., the names of its fields and their types in the case of a record.
|
|
425
|
-
* The name "product" comes from category theory.
|
|
426
|
-
*
|
|
427
|
-
* See also: https://ncatlab.org/nlab/show/product+type.
|
|
428
|
-
*
|
|
429
|
-
* These structures are known as product types because the number of possible values in product
|
|
430
|
-
* ```ignore
|
|
431
|
-
* { N_0: T_0, N_1: T_1, ..., N_n: T_n }
|
|
432
|
-
* ```
|
|
433
|
-
* is:
|
|
434
|
-
* ```ignore
|
|
435
|
-
* Π (i ∈ 0..n). values(T_i)
|
|
436
|
-
* ```
|
|
437
|
-
* so for example, `values({ A: U64, B: Bool }) = values(U64) * values(Bool)`.
|
|
438
|
-
*
|
|
439
|
-
* [structural]: https://en.wikipedia.org/wiki/Structural_type_system
|
|
440
|
-
*/
|
|
441
|
-
export type ProductType = ProductTypeType;
|
|
442
|
-
|
|
443
|
-
export const ProductType = {
|
|
444
|
-
makeSerializer(
|
|
445
|
-
ty: ProductTypeType,
|
|
446
|
-
typespace?: TypespaceType
|
|
447
|
-
): Serializer<any> {
|
|
448
|
-
let serializer = SERIALIZERS.get(ty);
|
|
449
|
-
if (serializer != null) return serializer;
|
|
450
|
-
|
|
451
|
-
if (isFixedSizeProduct(ty)) {
|
|
452
|
-
const size = productSize(ty);
|
|
453
|
-
const body = `\
|
|
454
|
-
"use strict";
|
|
455
|
-
writer.expandBuffer(${size});
|
|
456
|
-
const view = writer.view;
|
|
457
|
-
${ty.elements
|
|
458
|
-
.map(({ name, algebraicType: { tag } }) =>
|
|
459
|
-
tag in primitiveJSName
|
|
460
|
-
? `\
|
|
461
|
-
view.set${primitiveJSName[tag as JSPrimitives]}(writer.offset, value.${name!}, ${primitiveSizes[tag] > 1 ? 'true' : ''});
|
|
462
|
-
writer.offset += ${primitiveSizes[tag]};`
|
|
463
|
-
: `writer.write${tag}(value.${name});`
|
|
464
|
-
)
|
|
465
|
-
.join('\n')}`;
|
|
466
|
-
serializer = Function('writer', 'value', body) as Serializer<any>;
|
|
467
|
-
SERIALIZERS.set(ty, serializer);
|
|
468
|
-
return serializer;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// Because V8 forces us to generate our code as a string, rather than a proper syntax tree,
|
|
472
|
-
// we can't have our `body` close over values.
|
|
473
|
-
// Instead, we construct an object with the values we'd otherwise "close over" in `serializers`,
|
|
474
|
-
// and use `Function.prototype.bind` to pass it as the `this` argument.
|
|
475
|
-
//
|
|
476
|
-
// We populate `serializers` after constructing this type's `serializer`
|
|
477
|
-
// so that it can close over itself, in the case that `ty` is recursive.
|
|
478
|
-
const serializers: Record<string, Serializer<any>> = {};
|
|
479
|
-
const body =
|
|
480
|
-
'"use strict";\n' +
|
|
481
|
-
ty.elements
|
|
482
|
-
.map(
|
|
483
|
-
element => `this.${element.name!}(writer, value.${element.name!});`
|
|
484
|
-
)
|
|
485
|
-
.join('\n');
|
|
486
|
-
serializer = Function('writer', 'value', body).bind(
|
|
487
|
-
serializers
|
|
488
|
-
) as Serializer<any>;
|
|
489
|
-
// In case `ty` is recursive, we cache the function *before* before computing
|
|
490
|
-
// `serializers`, so that a recursive `makeSerializer` with the same `ty` has
|
|
491
|
-
// an exit condition.
|
|
492
|
-
SERIALIZERS.set(ty, serializer);
|
|
493
|
-
for (const { name, algebraicType } of ty.elements) {
|
|
494
|
-
serializers[name!] = AlgebraicType.makeSerializer(
|
|
495
|
-
algebraicType,
|
|
496
|
-
typespace
|
|
497
|
-
);
|
|
498
|
-
}
|
|
499
|
-
Object.freeze(serializers);
|
|
500
|
-
return serializer;
|
|
501
|
-
},
|
|
502
|
-
/** @deprecated Use `makeSerializer` instead. */
|
|
503
|
-
serializeValue(
|
|
504
|
-
writer: BinaryWriter,
|
|
505
|
-
ty: ProductTypeType,
|
|
506
|
-
value: any,
|
|
507
|
-
typespace?: TypespaceType
|
|
508
|
-
): void {
|
|
509
|
-
ProductType.makeSerializer(ty, typespace)(writer, value);
|
|
510
|
-
},
|
|
511
|
-
makeDeserializer(
|
|
512
|
-
ty: ProductTypeType,
|
|
513
|
-
typespace?: TypespaceType
|
|
514
|
-
): Deserializer<any> {
|
|
515
|
-
switch (ty.elements.length) {
|
|
516
|
-
case 0:
|
|
517
|
-
return unitDeserializer;
|
|
518
|
-
case 1: {
|
|
519
|
-
const fieldName = ty.elements[0].name!;
|
|
520
|
-
if (hasOwn(specialProductDeserializers, fieldName))
|
|
521
|
-
return specialProductDeserializers[
|
|
522
|
-
fieldName as keyof SpecialProducts
|
|
523
|
-
];
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
let deserializer = DESERIALIZERS.get(ty);
|
|
528
|
-
if (deserializer != null) return deserializer;
|
|
529
|
-
|
|
530
|
-
if (isFixedSizeProduct(ty)) {
|
|
531
|
-
const body = `\
|
|
532
|
-
"use strict";
|
|
533
|
-
const result = { ${ty.elements.map(getElementInitializer).join(', ')} };
|
|
534
|
-
const view = reader.view;
|
|
535
|
-
${ty.elements
|
|
536
|
-
.map(({ name, algebraicType: { tag } }) =>
|
|
537
|
-
tag in primitiveJSName
|
|
538
|
-
? tag === 'Bool'
|
|
539
|
-
? `\
|
|
540
|
-
result.${name} = view.getUint8(reader.offset) !== 0;
|
|
541
|
-
reader.offset += 1;`
|
|
542
|
-
: `\
|
|
543
|
-
result.${name} = view.get${primitiveJSName[tag as JSPrimitives]}(reader.offset, ${primitiveSizes[tag] > 1 ? 'true' : ''});
|
|
544
|
-
reader.offset += ${primitiveSizes[tag]};`
|
|
545
|
-
: `result.${name} = reader.read${tag}();`
|
|
546
|
-
)
|
|
547
|
-
.join('\n')}
|
|
548
|
-
return result;`;
|
|
549
|
-
deserializer = Function('reader', body) as Deserializer<any>;
|
|
550
|
-
DESERIALIZERS.set(ty, deserializer);
|
|
551
|
-
return deserializer;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
// Because V8 forces us to generate our code as a string, rather than a proper syntax tree,
|
|
555
|
-
// we can't have our `body` close over values.
|
|
556
|
-
// Instead, we construct an object with the values we'd otherwise "close over" in `deserializers`,
|
|
557
|
-
// and use `Function.prototype.bind` to pass it as the `this` argument.
|
|
558
|
-
//
|
|
559
|
-
// We populate `deserializers` after constructing this type's `deserializer`
|
|
560
|
-
// so that it can close over itself, in the case that `ty` is recursive.
|
|
561
|
-
const deserializers: Record<string, Deserializer<any>> = {};
|
|
562
|
-
deserializer = Function(
|
|
563
|
-
'reader',
|
|
564
|
-
`\
|
|
565
|
-
"use strict";
|
|
566
|
-
const result = { ${ty.elements.map(getElementInitializer).join(', ')} };
|
|
567
|
-
${ty.elements.map(({ name }) => `result.${name!} = this.${name!}(reader);`).join('\n')}
|
|
568
|
-
return result;`
|
|
569
|
-
).bind(deserializers) as Deserializer<any>;
|
|
570
|
-
// In case `ty` is recursive, we cache the function *before* before computing
|
|
571
|
-
// `deserializers`, so that a recursive `makeDeserializer` with the same `ty` has
|
|
572
|
-
// an exit condition.
|
|
573
|
-
DESERIALIZERS.set(ty, deserializer);
|
|
574
|
-
for (const { name, algebraicType } of ty.elements) {
|
|
575
|
-
deserializers[name!] = AlgebraicType.makeDeserializer(
|
|
576
|
-
algebraicType,
|
|
577
|
-
typespace
|
|
578
|
-
);
|
|
579
|
-
}
|
|
580
|
-
Object.freeze(deserializers);
|
|
581
|
-
return deserializer;
|
|
582
|
-
},
|
|
583
|
-
/** @deprecated Use `makeDeserializer` instead. */
|
|
584
|
-
deserializeValue(
|
|
585
|
-
reader: BinaryReader,
|
|
586
|
-
ty: ProductTypeType,
|
|
587
|
-
typespace?: TypespaceType
|
|
588
|
-
): any {
|
|
589
|
-
return ProductType.makeDeserializer(ty, typespace)(reader);
|
|
590
|
-
},
|
|
591
|
-
intoMapKey(ty: ProductTypeType, value: any): ComparablePrimitive {
|
|
592
|
-
if (ty.elements.length === 1) {
|
|
593
|
-
const fieldName = ty.elements[0].name!;
|
|
594
|
-
if (hasOwn(specialProductDeserializers, fieldName)) {
|
|
595
|
-
return value[fieldName];
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
// The fallback is to serialize and base64 encode the bytes.
|
|
599
|
-
const writer = new BinaryWriter(10);
|
|
600
|
-
AlgebraicType.serializeValue(writer, AlgebraicType.Product(ty), value);
|
|
601
|
-
return writer.toBase64();
|
|
602
|
-
},
|
|
603
|
-
};
|
|
604
|
-
|
|
605
|
-
export type SumType = SumTypeType;
|
|
606
|
-
|
|
607
|
-
/**
|
|
608
|
-
* Unlike most languages, sums in SATS are *[structural]* and not nominal.
|
|
609
|
-
* When checking whether two nominal types are the same,
|
|
610
|
-
* their names and/or declaration sites (e.g., module / namespace) are considered.
|
|
611
|
-
* Meanwhile, a structural type system would only check the structure of the type itself,
|
|
612
|
-
* e.g., the names of its variants and their inner data types in the case of a sum.
|
|
613
|
-
*
|
|
614
|
-
* This is also known as a discriminated union (implementation) or disjoint union.
|
|
615
|
-
* Another name is [coproduct (category theory)](https://ncatlab.org/nlab/show/coproduct).
|
|
616
|
-
*
|
|
617
|
-
* These structures are known as sum types because the number of possible values a sum
|
|
618
|
-
* ```ignore
|
|
619
|
-
* { N_0(T_0), N_1(T_1), ..., N_n(T_n) }
|
|
620
|
-
* ```
|
|
621
|
-
* is:
|
|
622
|
-
* ```ignore
|
|
623
|
-
* Σ (i ∈ 0..n). values(T_i)
|
|
624
|
-
* ```
|
|
625
|
-
* so for example, `values({ A(U64), B(Bool) }) = values(U64) + values(Bool)`.
|
|
626
|
-
*
|
|
627
|
-
* See also: https://ncatlab.org/nlab/show/sum+type.
|
|
628
|
-
*
|
|
629
|
-
* [structural]: https://en.wikipedia.org/wiki/Structural_type_system
|
|
630
|
-
*/
|
|
631
|
-
export const SumType = {
|
|
632
|
-
makeSerializer(ty: SumTypeType, typespace?: TypespaceType): Serializer<any> {
|
|
633
|
-
if (
|
|
634
|
-
ty.variants.length == 2 &&
|
|
635
|
-
ty.variants[0].name === 'some' &&
|
|
636
|
-
ty.variants[1].name === 'none'
|
|
637
|
-
) {
|
|
638
|
-
const serialize = AlgebraicType.makeSerializer(
|
|
639
|
-
ty.variants[0].algebraicType,
|
|
640
|
-
typespace
|
|
641
|
-
);
|
|
642
|
-
return (writer, value) => {
|
|
643
|
-
if (value !== null && value !== undefined) {
|
|
644
|
-
writer.writeByte(0);
|
|
645
|
-
serialize(writer, value);
|
|
646
|
-
} else {
|
|
647
|
-
writer.writeByte(1);
|
|
648
|
-
}
|
|
649
|
-
};
|
|
650
|
-
} else if (
|
|
651
|
-
ty.variants.length == 2 &&
|
|
652
|
-
ty.variants[0].name === 'ok' &&
|
|
653
|
-
ty.variants[1].name === 'err'
|
|
654
|
-
) {
|
|
655
|
-
const serializeOk = AlgebraicType.makeSerializer(
|
|
656
|
-
ty.variants[0].algebraicType,
|
|
657
|
-
typespace
|
|
658
|
-
);
|
|
659
|
-
const serializeErr = AlgebraicType.makeSerializer(
|
|
660
|
-
ty.variants[0].algebraicType,
|
|
661
|
-
typespace
|
|
662
|
-
);
|
|
663
|
-
|
|
664
|
-
return (writer, value) => {
|
|
665
|
-
if ('ok' in value) {
|
|
666
|
-
writer.writeU8(0);
|
|
667
|
-
serializeOk(writer, value.ok);
|
|
668
|
-
} else if ('err' in value) {
|
|
669
|
-
writer.writeU8(1);
|
|
670
|
-
serializeErr(writer, value.err);
|
|
671
|
-
} else {
|
|
672
|
-
throw new TypeError(
|
|
673
|
-
'could not serialize result: object had neither a `ok` nor an `err` field'
|
|
674
|
-
);
|
|
675
|
-
}
|
|
676
|
-
};
|
|
677
|
-
} else {
|
|
678
|
-
let serializer = SERIALIZERS.get(ty);
|
|
679
|
-
if (serializer != null) return serializer;
|
|
680
|
-
|
|
681
|
-
const serializers: Record<string, Serializer<any>> = {};
|
|
682
|
-
|
|
683
|
-
const body = `\
|
|
684
|
-
switch (value.tag) {
|
|
685
|
-
${ty.variants
|
|
686
|
-
.map(
|
|
687
|
-
({ name }, i) => `\
|
|
688
|
-
case ${JSON.stringify(name!)}:
|
|
689
|
-
writer.writeByte(${i});
|
|
690
|
-
return this.${name!}(writer, value.value);`
|
|
691
|
-
)
|
|
692
|
-
.join('\n')}
|
|
693
|
-
default:
|
|
694
|
-
throw new TypeError(
|
|
695
|
-
\`Could not serialize sum type; unknown tag \${value.tag}\`
|
|
696
|
-
)
|
|
697
|
-
}
|
|
698
|
-
`;
|
|
699
|
-
|
|
700
|
-
serializer = Function('writer', 'value', body).bind(
|
|
701
|
-
serializers
|
|
702
|
-
) as Serializer<any>;
|
|
703
|
-
|
|
704
|
-
// In case `ty` is recursive, we cache the function *before* before computing
|
|
705
|
-
// `variants`, so that a recursive `makeSerializer` with the same `ty` has
|
|
706
|
-
// an exit condition.
|
|
707
|
-
SERIALIZERS.set(ty, serializer);
|
|
708
|
-
|
|
709
|
-
for (const { name, algebraicType } of ty.variants) {
|
|
710
|
-
serializers[name!] = AlgebraicType.makeSerializer(
|
|
711
|
-
algebraicType,
|
|
712
|
-
typespace
|
|
713
|
-
);
|
|
714
|
-
}
|
|
715
|
-
Object.freeze(serializers);
|
|
716
|
-
return serializer;
|
|
717
|
-
}
|
|
718
|
-
},
|
|
719
|
-
/** @deprecated Use `makeSerializer` instead. */
|
|
720
|
-
serializeValue(
|
|
721
|
-
writer: BinaryWriter,
|
|
722
|
-
ty: SumTypeType,
|
|
723
|
-
value: any,
|
|
724
|
-
typespace?: TypespaceType
|
|
725
|
-
): void {
|
|
726
|
-
SumType.makeSerializer(ty, typespace)(writer, value);
|
|
727
|
-
},
|
|
728
|
-
makeDeserializer(
|
|
729
|
-
ty: SumTypeType,
|
|
730
|
-
typespace?: TypespaceType
|
|
731
|
-
): Deserializer<any> {
|
|
732
|
-
// In TypeScript we handle Option values as a special case
|
|
733
|
-
// we don't represent the some and none variants, but instead
|
|
734
|
-
// we represent the value directly.
|
|
735
|
-
//
|
|
736
|
-
// For these special cases, we don't do dynamic codegen, since that has the
|
|
737
|
-
// most benefit in cases where the object has a different shape. Since
|
|
738
|
-
// option/result always have the same number of variants, there's not as
|
|
739
|
-
// much benefit for the amount of work.
|
|
740
|
-
if (
|
|
741
|
-
ty.variants.length == 2 &&
|
|
742
|
-
ty.variants[0].name === 'some' &&
|
|
743
|
-
ty.variants[1].name === 'none'
|
|
744
|
-
) {
|
|
745
|
-
const deserialize = AlgebraicType.makeDeserializer(
|
|
746
|
-
ty.variants[0].algebraicType,
|
|
747
|
-
typespace
|
|
748
|
-
);
|
|
749
|
-
return reader => {
|
|
750
|
-
const tag = reader.readU8();
|
|
751
|
-
if (tag === 0) {
|
|
752
|
-
return deserialize(reader);
|
|
753
|
-
} else if (tag === 1) {
|
|
754
|
-
return undefined;
|
|
755
|
-
} else {
|
|
756
|
-
throw `Can't deserialize an option type, couldn't find ${tag} tag`;
|
|
757
|
-
}
|
|
758
|
-
};
|
|
759
|
-
} else if (
|
|
760
|
-
ty.variants.length == 2 &&
|
|
761
|
-
ty.variants[0].name === 'ok' &&
|
|
762
|
-
ty.variants[1].name === 'err'
|
|
763
|
-
) {
|
|
764
|
-
const deserializeOk = AlgebraicType.makeDeserializer(
|
|
765
|
-
ty.variants[0].algebraicType,
|
|
766
|
-
typespace
|
|
767
|
-
);
|
|
768
|
-
const deserializeErr = AlgebraicType.makeDeserializer(
|
|
769
|
-
ty.variants[1].algebraicType,
|
|
770
|
-
typespace
|
|
771
|
-
);
|
|
772
|
-
return reader => {
|
|
773
|
-
const tag = reader.readByte();
|
|
774
|
-
if (tag === 0) {
|
|
775
|
-
return { ok: deserializeOk(reader) };
|
|
776
|
-
} else if (tag === 1) {
|
|
777
|
-
return { err: deserializeErr(reader) };
|
|
778
|
-
} else {
|
|
779
|
-
throw `Can't deserialize a result type, couldn't find ${tag} tag`;
|
|
780
|
-
}
|
|
781
|
-
};
|
|
782
|
-
} else {
|
|
783
|
-
let deserializer = DESERIALIZERS.get(ty);
|
|
784
|
-
if (deserializer != null) return deserializer;
|
|
785
|
-
const deserializers: Record<string, Deserializer<any>> = {};
|
|
786
|
-
deserializer = Function(
|
|
787
|
-
'reader',
|
|
788
|
-
`switch (reader.readU8()) {\n${ty.variants
|
|
789
|
-
.map(
|
|
790
|
-
({ name }, i) =>
|
|
791
|
-
`case ${i}: return { tag: ${JSON.stringify(name!)}, value: this.${name!}(reader) };`
|
|
792
|
-
)
|
|
793
|
-
.join('\n')} }`
|
|
794
|
-
).bind(deserializers) as Deserializer<any>;
|
|
795
|
-
// In case `ty` is recursive, we cache the function *before* before computing
|
|
796
|
-
// `deserializers`, so that a recursive `makeDeserializer` with the same `ty` has
|
|
797
|
-
// an exit condition.
|
|
798
|
-
DESERIALIZERS.set(ty, deserializer);
|
|
799
|
-
for (const { name, algebraicType } of ty.variants) {
|
|
800
|
-
deserializers[name!] = AlgebraicType.makeDeserializer(
|
|
801
|
-
algebraicType,
|
|
802
|
-
typespace
|
|
803
|
-
);
|
|
804
|
-
}
|
|
805
|
-
Object.freeze(deserializers);
|
|
806
|
-
return deserializer;
|
|
807
|
-
}
|
|
808
|
-
},
|
|
809
|
-
/** @deprecated Use `makeDeserializer` instead. */
|
|
810
|
-
deserializeValue(
|
|
811
|
-
reader: BinaryReader,
|
|
812
|
-
ty: SumTypeType,
|
|
813
|
-
typespace?: TypespaceType
|
|
814
|
-
): any {
|
|
815
|
-
return SumType.makeDeserializer(ty, typespace)(reader);
|
|
816
|
-
},
|
|
817
|
-
};
|
|
818
|
-
|
|
819
|
-
export type ComparablePrimitive = number | string | boolean | bigint;
|
|
1
|
+
import { TimeDuration } from './time_duration';
|
|
2
|
+
import { Timestamp } from './timestamp';
|
|
3
|
+
import { Uuid } from './uuid';
|
|
4
|
+
import { ConnectionId } from './connection_id';
|
|
5
|
+
import BinaryReader from './binary_reader';
|
|
6
|
+
import BinaryWriter from './binary_writer';
|
|
7
|
+
import { Identity } from './identity';
|
|
8
|
+
import * as AlgebraicTypeVariants from './algebraic_type_variants';
|
|
9
|
+
import { hasOwn } from './util';
|
|
10
|
+
|
|
11
|
+
type TypespaceType = {
|
|
12
|
+
types: AlgebraicTypeType[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type ProductTypeType = {
|
|
16
|
+
elements: ProductTypeElement[];
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A factor / element of a product type.
|
|
21
|
+
*
|
|
22
|
+
* An element consist of an optional name and a type.
|
|
23
|
+
*
|
|
24
|
+
* NOTE: Each element has an implicit element tag based on its order.
|
|
25
|
+
* Uniquely identifies an element similarly to protobuf tags.
|
|
26
|
+
*/
|
|
27
|
+
export type ProductTypeElement = {
|
|
28
|
+
name: string | undefined;
|
|
29
|
+
algebraicType: AlgebraicTypeType;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type SumTypeType = {
|
|
33
|
+
variants: SumTypeVariant[];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A variant of a sum type.
|
|
38
|
+
*
|
|
39
|
+
* NOTE: Each element has an implicit element tag based on its order.
|
|
40
|
+
* Uniquely identifies an element similarly to protobuf tags.
|
|
41
|
+
*/
|
|
42
|
+
export type SumTypeVariant = {
|
|
43
|
+
name: string | undefined;
|
|
44
|
+
algebraicType: AlgebraicTypeType;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type AlgebraicTypeType =
|
|
48
|
+
| AlgebraicTypeVariants.Ref
|
|
49
|
+
| AlgebraicTypeVariants.Sum
|
|
50
|
+
| AlgebraicTypeVariants.Product
|
|
51
|
+
| AlgebraicTypeVariants.Array
|
|
52
|
+
| AlgebraicTypeVariants.String
|
|
53
|
+
| AlgebraicTypeVariants.Bool
|
|
54
|
+
| AlgebraicTypeVariants.I8
|
|
55
|
+
| AlgebraicTypeVariants.U8
|
|
56
|
+
| AlgebraicTypeVariants.I16
|
|
57
|
+
| AlgebraicTypeVariants.U16
|
|
58
|
+
| AlgebraicTypeVariants.I32
|
|
59
|
+
| AlgebraicTypeVariants.U32
|
|
60
|
+
| AlgebraicTypeVariants.I64
|
|
61
|
+
| AlgebraicTypeVariants.U64
|
|
62
|
+
| AlgebraicTypeVariants.I128
|
|
63
|
+
| AlgebraicTypeVariants.U128
|
|
64
|
+
| AlgebraicTypeVariants.I256
|
|
65
|
+
| AlgebraicTypeVariants.U256
|
|
66
|
+
| AlgebraicTypeVariants.F32
|
|
67
|
+
| AlgebraicTypeVariants.F64;
|
|
68
|
+
|
|
69
|
+
export type AlgebraicType = AlgebraicTypeType;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The variant types of the Algebraic Type tagged union.
|
|
73
|
+
*/
|
|
74
|
+
export { AlgebraicTypeVariants };
|
|
75
|
+
|
|
76
|
+
export type Serializer<T> = (writer: BinaryWriter, value: T) => void;
|
|
77
|
+
|
|
78
|
+
export type Deserializer<T> = (reader: BinaryReader) => T;
|
|
79
|
+
|
|
80
|
+
// Caches to prevent `makeSerializer`/`makeDeserializer` from recursing
|
|
81
|
+
// infinitely when called on recursive types.
|
|
82
|
+
//
|
|
83
|
+
// We check for recursion in `{Product,Sum}Type.make{Deser,Ser}ializer` rather
|
|
84
|
+
// than in `AlgebraciType.make{Deser,Ser}ializer` because we need to store the
|
|
85
|
+
// [de]serializer in the cache before recursing into its fields/variants, and
|
|
86
|
+
// we wouldn't be able to do that in the `AlgebraicType` functions.
|
|
87
|
+
const SERIALIZERS = new Map<ProductType | SumType, Serializer<any>>();
|
|
88
|
+
const DESERIALIZERS = new Map<ProductType | SumType, Deserializer<any>>();
|
|
89
|
+
|
|
90
|
+
// A value with helper functions to construct the type.
|
|
91
|
+
export const AlgebraicType = {
|
|
92
|
+
Ref: (value: number): AlgebraicTypeVariants.Ref => ({ tag: 'Ref', value }),
|
|
93
|
+
Sum: <T extends SumTypeType>(value: T): { tag: 'Sum'; value: T } => ({
|
|
94
|
+
tag: 'Sum',
|
|
95
|
+
value,
|
|
96
|
+
}),
|
|
97
|
+
Product: <T extends ProductTypeType>(
|
|
98
|
+
value: T
|
|
99
|
+
): { tag: 'Product'; value: T } => ({
|
|
100
|
+
tag: 'Product',
|
|
101
|
+
value,
|
|
102
|
+
}),
|
|
103
|
+
Array: <T extends AlgebraicTypeType>(
|
|
104
|
+
value: T
|
|
105
|
+
): { tag: 'Array'; value: T } => ({
|
|
106
|
+
tag: 'Array',
|
|
107
|
+
value,
|
|
108
|
+
}),
|
|
109
|
+
String: { tag: 'String' } as const,
|
|
110
|
+
Bool: { tag: 'Bool' } as const,
|
|
111
|
+
I8: { tag: 'I8' } as const,
|
|
112
|
+
U8: { tag: 'U8' } as const,
|
|
113
|
+
I16: { tag: 'I16' } as const,
|
|
114
|
+
U16: { tag: 'U16' } as const,
|
|
115
|
+
I32: { tag: 'I32' } as const,
|
|
116
|
+
U32: { tag: 'U32' } as const,
|
|
117
|
+
I64: { tag: 'I64' } as const,
|
|
118
|
+
U64: { tag: 'U64' } as const,
|
|
119
|
+
I128: { tag: 'I128' } as const,
|
|
120
|
+
U128: { tag: 'U128' } as const,
|
|
121
|
+
I256: { tag: 'I256' } as const,
|
|
122
|
+
U256: { tag: 'U256' } as const,
|
|
123
|
+
F32: { tag: 'F32' } as const,
|
|
124
|
+
F64: { tag: 'F64' } as const,
|
|
125
|
+
makeSerializer(
|
|
126
|
+
ty: AlgebraicTypeType,
|
|
127
|
+
typespace?: TypespaceType
|
|
128
|
+
): Serializer<any> {
|
|
129
|
+
if (ty.tag === 'Ref') {
|
|
130
|
+
if (!typespace)
|
|
131
|
+
throw new Error('cannot serialize refs without a typespace');
|
|
132
|
+
while (ty.tag === 'Ref') ty = typespace.types[ty.value];
|
|
133
|
+
}
|
|
134
|
+
switch (ty.tag) {
|
|
135
|
+
case 'Product':
|
|
136
|
+
return ProductType.makeSerializer(ty.value, typespace);
|
|
137
|
+
case 'Sum':
|
|
138
|
+
return SumType.makeSerializer(ty.value, typespace);
|
|
139
|
+
case 'Array':
|
|
140
|
+
if (ty.value.tag === 'U8') {
|
|
141
|
+
return serializeUint8Array;
|
|
142
|
+
} else {
|
|
143
|
+
const serialize = AlgebraicType.makeSerializer(ty.value, typespace);
|
|
144
|
+
return (writer, value) => {
|
|
145
|
+
writer.writeU32(value.length);
|
|
146
|
+
for (const elem of value) {
|
|
147
|
+
serialize(writer, elem);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
default:
|
|
152
|
+
return primitiveSerializers[ty.tag];
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
/** @deprecated Use `makeSerializer` instead. */
|
|
156
|
+
serializeValue(
|
|
157
|
+
writer: BinaryWriter,
|
|
158
|
+
ty: AlgebraicTypeType,
|
|
159
|
+
value: any,
|
|
160
|
+
typespace?: TypespaceType
|
|
161
|
+
) {
|
|
162
|
+
AlgebraicType.makeSerializer(ty, typespace)(writer, value);
|
|
163
|
+
},
|
|
164
|
+
makeDeserializer(
|
|
165
|
+
ty: AlgebraicTypeType,
|
|
166
|
+
typespace?: TypespaceType
|
|
167
|
+
): Deserializer<any> {
|
|
168
|
+
if (ty.tag === 'Ref') {
|
|
169
|
+
if (!typespace)
|
|
170
|
+
throw new Error('cannot deserialize refs without a typespace');
|
|
171
|
+
while (ty.tag === 'Ref') ty = typespace.types[ty.value];
|
|
172
|
+
}
|
|
173
|
+
switch (ty.tag) {
|
|
174
|
+
case 'Product':
|
|
175
|
+
return ProductType.makeDeserializer(ty.value, typespace);
|
|
176
|
+
case 'Sum':
|
|
177
|
+
return SumType.makeDeserializer(ty.value, typespace);
|
|
178
|
+
case 'Array':
|
|
179
|
+
if (ty.value.tag === 'U8') {
|
|
180
|
+
return deserializeUint8Array;
|
|
181
|
+
} else {
|
|
182
|
+
const deserialize = AlgebraicType.makeDeserializer(
|
|
183
|
+
ty.value,
|
|
184
|
+
typespace
|
|
185
|
+
);
|
|
186
|
+
return reader => {
|
|
187
|
+
const length = reader.readU32();
|
|
188
|
+
const result: any[] = Array(length);
|
|
189
|
+
for (let i = 0; i < length; i++) {
|
|
190
|
+
result[i] = deserialize(reader);
|
|
191
|
+
}
|
|
192
|
+
return result;
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
default:
|
|
196
|
+
return primitiveDeserializers[ty.tag];
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
/** @deprecated Use `makeDeserializer` instead. */
|
|
200
|
+
deserializeValue(
|
|
201
|
+
reader: BinaryReader,
|
|
202
|
+
ty: AlgebraicTypeType,
|
|
203
|
+
typespace?: TypespaceType
|
|
204
|
+
): any {
|
|
205
|
+
return AlgebraicType.makeDeserializer(ty, typespace)(reader);
|
|
206
|
+
},
|
|
207
|
+
/**
|
|
208
|
+
* Convert a value of the algebraic type into something that can be used as a key in a map.
|
|
209
|
+
* There are no guarantees about being able to order it.
|
|
210
|
+
* This is only guaranteed to be comparable to other values of the same type.
|
|
211
|
+
* @param value A value of the algebraic type
|
|
212
|
+
* @returns Something that can be used as a key in a map.
|
|
213
|
+
*/
|
|
214
|
+
intoMapKey: function (
|
|
215
|
+
ty: AlgebraicTypeType,
|
|
216
|
+
value: any
|
|
217
|
+
): ComparablePrimitive {
|
|
218
|
+
switch (ty.tag) {
|
|
219
|
+
case 'U8':
|
|
220
|
+
case 'U16':
|
|
221
|
+
case 'U32':
|
|
222
|
+
case 'U64':
|
|
223
|
+
case 'U128':
|
|
224
|
+
case 'U256':
|
|
225
|
+
case 'I8':
|
|
226
|
+
case 'I16':
|
|
227
|
+
case 'I32':
|
|
228
|
+
case 'I64':
|
|
229
|
+
case 'I128':
|
|
230
|
+
case 'I256':
|
|
231
|
+
case 'F32':
|
|
232
|
+
case 'F64':
|
|
233
|
+
case 'String':
|
|
234
|
+
case 'Bool':
|
|
235
|
+
return value;
|
|
236
|
+
case 'Product':
|
|
237
|
+
return ProductType.intoMapKey(ty.value, value);
|
|
238
|
+
default: {
|
|
239
|
+
// The fallback is to serialize and base64 encode the bytes.
|
|
240
|
+
const writer = new BinaryWriter(10);
|
|
241
|
+
AlgebraicType.serializeValue(writer, ty, value);
|
|
242
|
+
return writer.toBase64();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
function bindCall<F extends (this: any, ...args: any[]) => any>(
|
|
249
|
+
f: F
|
|
250
|
+
): (recv: ThisParameterType<F>, ...args: Parameters<F>) => ReturnType<F> {
|
|
251
|
+
return Function.prototype.call.bind(f);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
type Primitives = Exclude<
|
|
255
|
+
AlgebraicType['tag'],
|
|
256
|
+
'Ref' | 'Sum' | 'Product' | 'Array'
|
|
257
|
+
>;
|
|
258
|
+
|
|
259
|
+
const primitiveSerializers: Record<Primitives, Serializer<any>> = {
|
|
260
|
+
Bool: bindCall(BinaryWriter.prototype.writeBool),
|
|
261
|
+
I8: bindCall(BinaryWriter.prototype.writeI8),
|
|
262
|
+
U8: bindCall(BinaryWriter.prototype.writeU8),
|
|
263
|
+
I16: bindCall(BinaryWriter.prototype.writeI16),
|
|
264
|
+
U16: bindCall(BinaryWriter.prototype.writeU16),
|
|
265
|
+
I32: bindCall(BinaryWriter.prototype.writeI32),
|
|
266
|
+
U32: bindCall(BinaryWriter.prototype.writeU32),
|
|
267
|
+
I64: bindCall(BinaryWriter.prototype.writeI64),
|
|
268
|
+
U64: bindCall(BinaryWriter.prototype.writeU64),
|
|
269
|
+
I128: bindCall(BinaryWriter.prototype.writeI128),
|
|
270
|
+
U128: bindCall(BinaryWriter.prototype.writeU128),
|
|
271
|
+
I256: bindCall(BinaryWriter.prototype.writeI256),
|
|
272
|
+
U256: bindCall(BinaryWriter.prototype.writeU256),
|
|
273
|
+
F32: bindCall(BinaryWriter.prototype.writeF32),
|
|
274
|
+
F64: bindCall(BinaryWriter.prototype.writeF64),
|
|
275
|
+
String: bindCall(BinaryWriter.prototype.writeString),
|
|
276
|
+
};
|
|
277
|
+
Object.freeze(primitiveSerializers);
|
|
278
|
+
|
|
279
|
+
const serializeUint8Array = bindCall(BinaryWriter.prototype.writeUInt8Array);
|
|
280
|
+
|
|
281
|
+
const primitiveDeserializers: Record<Primitives, Deserializer<any>> = {
|
|
282
|
+
Bool: bindCall(BinaryReader.prototype.readBool),
|
|
283
|
+
I8: bindCall(BinaryReader.prototype.readI8),
|
|
284
|
+
U8: bindCall(BinaryReader.prototype.readU8),
|
|
285
|
+
I16: bindCall(BinaryReader.prototype.readI16),
|
|
286
|
+
U16: bindCall(BinaryReader.prototype.readU16),
|
|
287
|
+
I32: bindCall(BinaryReader.prototype.readI32),
|
|
288
|
+
U32: bindCall(BinaryReader.prototype.readU32),
|
|
289
|
+
I64: bindCall(BinaryReader.prototype.readI64),
|
|
290
|
+
U64: bindCall(BinaryReader.prototype.readU64),
|
|
291
|
+
I128: bindCall(BinaryReader.prototype.readI128),
|
|
292
|
+
U128: bindCall(BinaryReader.prototype.readU128),
|
|
293
|
+
I256: bindCall(BinaryReader.prototype.readI256),
|
|
294
|
+
U256: bindCall(BinaryReader.prototype.readU256),
|
|
295
|
+
F32: bindCall(BinaryReader.prototype.readF32),
|
|
296
|
+
F64: bindCall(BinaryReader.prototype.readF64),
|
|
297
|
+
String: bindCall(BinaryReader.prototype.readString),
|
|
298
|
+
};
|
|
299
|
+
Object.freeze(primitiveDeserializers);
|
|
300
|
+
|
|
301
|
+
const deserializeUint8Array = bindCall(BinaryReader.prototype.readUInt8Array);
|
|
302
|
+
|
|
303
|
+
type FixedSizePrimitives = Exclude<Primitives, 'String'>;
|
|
304
|
+
|
|
305
|
+
const primitiveSizes: Record<FixedSizePrimitives, number> = {
|
|
306
|
+
Bool: 1,
|
|
307
|
+
I8: 1,
|
|
308
|
+
U8: 1,
|
|
309
|
+
I16: 2,
|
|
310
|
+
U16: 2,
|
|
311
|
+
I32: 4,
|
|
312
|
+
U32: 4,
|
|
313
|
+
I64: 8,
|
|
314
|
+
U64: 8,
|
|
315
|
+
I128: 16,
|
|
316
|
+
U128: 16,
|
|
317
|
+
I256: 32,
|
|
318
|
+
U256: 32,
|
|
319
|
+
F32: 4,
|
|
320
|
+
F64: 8,
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const fixedSizePrimitives = new Set(Object.keys(primitiveSizes));
|
|
324
|
+
|
|
325
|
+
type FixedSizeProductType = {
|
|
326
|
+
elements: { name: string; algebraicType: { tag: FixedSizePrimitives } }[];
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const isFixedSizeProduct = (ty: ProductType): ty is FixedSizeProductType =>
|
|
330
|
+
ty.elements.every(({ algebraicType }) =>
|
|
331
|
+
fixedSizePrimitives.has(algebraicType.tag)
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
const productSize = (ty: FixedSizeProductType): number =>
|
|
335
|
+
ty.elements.reduce(
|
|
336
|
+
(acc, { algebraicType }) => acc + primitiveSizes[algebraicType.tag],
|
|
337
|
+
0
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
type JSPrimitives = Exclude<
|
|
341
|
+
FixedSizePrimitives,
|
|
342
|
+
'I128' | 'U128' | 'I256' | 'U256'
|
|
343
|
+
>;
|
|
344
|
+
|
|
345
|
+
const primitiveJSName: Record<JSPrimitives, string> = {
|
|
346
|
+
Bool: 'Uint8',
|
|
347
|
+
I8: 'Int8',
|
|
348
|
+
U8: 'Uint8',
|
|
349
|
+
I16: 'Int16',
|
|
350
|
+
U16: 'Uint16',
|
|
351
|
+
I32: 'Int32',
|
|
352
|
+
U32: 'Uint32',
|
|
353
|
+
I64: 'BigInt64',
|
|
354
|
+
U64: 'BigUint64',
|
|
355
|
+
F32: 'Float32',
|
|
356
|
+
F64: 'Float64',
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
type SpecialProducts = {
|
|
360
|
+
__time_duration_micros__: TimeDuration;
|
|
361
|
+
__timestamp_micros_since_unix_epoch__: Timestamp;
|
|
362
|
+
__identity__: Identity;
|
|
363
|
+
__connection_id__: ConnectionId;
|
|
364
|
+
__uuid__: Uuid;
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
const specialProductDeserializers: {
|
|
368
|
+
[k in keyof SpecialProducts]: Deserializer<SpecialProducts[k]>;
|
|
369
|
+
} = {
|
|
370
|
+
__time_duration_micros__: reader => new TimeDuration(reader.readI64()),
|
|
371
|
+
__timestamp_micros_since_unix_epoch__: reader =>
|
|
372
|
+
new Timestamp(reader.readI64()),
|
|
373
|
+
__identity__: reader => new Identity(reader.readU256()),
|
|
374
|
+
__connection_id__: reader => new ConnectionId(reader.readU128()),
|
|
375
|
+
__uuid__: reader => new Uuid(reader.readU128()),
|
|
376
|
+
};
|
|
377
|
+
Object.freeze(specialProductDeserializers);
|
|
378
|
+
|
|
379
|
+
const unitDeserializer: Deserializer<{}> = () => ({});
|
|
380
|
+
|
|
381
|
+
const getElementInitializer = (element: ProductTypeElement) => {
|
|
382
|
+
let init: string;
|
|
383
|
+
switch (element.algebraicType.tag) {
|
|
384
|
+
case 'String':
|
|
385
|
+
init = "''";
|
|
386
|
+
break;
|
|
387
|
+
case 'Bool':
|
|
388
|
+
init = 'false';
|
|
389
|
+
break;
|
|
390
|
+
case 'I8':
|
|
391
|
+
case 'U8':
|
|
392
|
+
case 'I16':
|
|
393
|
+
case 'U16':
|
|
394
|
+
case 'I32':
|
|
395
|
+
case 'U32':
|
|
396
|
+
init = '0';
|
|
397
|
+
break;
|
|
398
|
+
case 'I64':
|
|
399
|
+
case 'U64':
|
|
400
|
+
case 'I128':
|
|
401
|
+
case 'U128':
|
|
402
|
+
case 'I256':
|
|
403
|
+
case 'U256':
|
|
404
|
+
init = '0n';
|
|
405
|
+
break;
|
|
406
|
+
case 'F32':
|
|
407
|
+
case 'F64':
|
|
408
|
+
init = '0.0';
|
|
409
|
+
break;
|
|
410
|
+
default:
|
|
411
|
+
init = 'undefined';
|
|
412
|
+
}
|
|
413
|
+
return `${element.name!}: ${init}`;
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* A structural product type of the factors given by `elements`.
|
|
418
|
+
*
|
|
419
|
+
* This is also known as `struct` and `tuple` in many languages,
|
|
420
|
+
* but note that unlike most languages, products in SATs are *[structural]* and not nominal.
|
|
421
|
+
* When checking whether two nominal types are the same,
|
|
422
|
+
* their names and/or declaration sites (e.g., module / namespace) are considered.
|
|
423
|
+
* Meanwhile, a structural type system would only check the structure of the type itself,
|
|
424
|
+
* e.g., the names of its fields and their types in the case of a record.
|
|
425
|
+
* The name "product" comes from category theory.
|
|
426
|
+
*
|
|
427
|
+
* See also: https://ncatlab.org/nlab/show/product+type.
|
|
428
|
+
*
|
|
429
|
+
* These structures are known as product types because the number of possible values in product
|
|
430
|
+
* ```ignore
|
|
431
|
+
* { N_0: T_0, N_1: T_1, ..., N_n: T_n }
|
|
432
|
+
* ```
|
|
433
|
+
* is:
|
|
434
|
+
* ```ignore
|
|
435
|
+
* Π (i ∈ 0..n). values(T_i)
|
|
436
|
+
* ```
|
|
437
|
+
* so for example, `values({ A: U64, B: Bool }) = values(U64) * values(Bool)`.
|
|
438
|
+
*
|
|
439
|
+
* [structural]: https://en.wikipedia.org/wiki/Structural_type_system
|
|
440
|
+
*/
|
|
441
|
+
export type ProductType = ProductTypeType;
|
|
442
|
+
|
|
443
|
+
export const ProductType = {
|
|
444
|
+
makeSerializer(
|
|
445
|
+
ty: ProductTypeType,
|
|
446
|
+
typespace?: TypespaceType
|
|
447
|
+
): Serializer<any> {
|
|
448
|
+
let serializer = SERIALIZERS.get(ty);
|
|
449
|
+
if (serializer != null) return serializer;
|
|
450
|
+
|
|
451
|
+
if (isFixedSizeProduct(ty)) {
|
|
452
|
+
const size = productSize(ty);
|
|
453
|
+
const body = `\
|
|
454
|
+
"use strict";
|
|
455
|
+
writer.expandBuffer(${size});
|
|
456
|
+
const view = writer.view;
|
|
457
|
+
${ty.elements
|
|
458
|
+
.map(({ name, algebraicType: { tag } }) =>
|
|
459
|
+
tag in primitiveJSName
|
|
460
|
+
? `\
|
|
461
|
+
view.set${primitiveJSName[tag as JSPrimitives]}(writer.offset, value.${name!}, ${primitiveSizes[tag] > 1 ? 'true' : ''});
|
|
462
|
+
writer.offset += ${primitiveSizes[tag]};`
|
|
463
|
+
: `writer.write${tag}(value.${name});`
|
|
464
|
+
)
|
|
465
|
+
.join('\n')}`;
|
|
466
|
+
serializer = Function('writer', 'value', body) as Serializer<any>;
|
|
467
|
+
SERIALIZERS.set(ty, serializer);
|
|
468
|
+
return serializer;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Because V8 forces us to generate our code as a string, rather than a proper syntax tree,
|
|
472
|
+
// we can't have our `body` close over values.
|
|
473
|
+
// Instead, we construct an object with the values we'd otherwise "close over" in `serializers`,
|
|
474
|
+
// and use `Function.prototype.bind` to pass it as the `this` argument.
|
|
475
|
+
//
|
|
476
|
+
// We populate `serializers` after constructing this type's `serializer`
|
|
477
|
+
// so that it can close over itself, in the case that `ty` is recursive.
|
|
478
|
+
const serializers: Record<string, Serializer<any>> = {};
|
|
479
|
+
const body =
|
|
480
|
+
'"use strict";\n' +
|
|
481
|
+
ty.elements
|
|
482
|
+
.map(
|
|
483
|
+
element => `this.${element.name!}(writer, value.${element.name!});`
|
|
484
|
+
)
|
|
485
|
+
.join('\n');
|
|
486
|
+
serializer = Function('writer', 'value', body).bind(
|
|
487
|
+
serializers
|
|
488
|
+
) as Serializer<any>;
|
|
489
|
+
// In case `ty` is recursive, we cache the function *before* before computing
|
|
490
|
+
// `serializers`, so that a recursive `makeSerializer` with the same `ty` has
|
|
491
|
+
// an exit condition.
|
|
492
|
+
SERIALIZERS.set(ty, serializer);
|
|
493
|
+
for (const { name, algebraicType } of ty.elements) {
|
|
494
|
+
serializers[name!] = AlgebraicType.makeSerializer(
|
|
495
|
+
algebraicType,
|
|
496
|
+
typespace
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
Object.freeze(serializers);
|
|
500
|
+
return serializer;
|
|
501
|
+
},
|
|
502
|
+
/** @deprecated Use `makeSerializer` instead. */
|
|
503
|
+
serializeValue(
|
|
504
|
+
writer: BinaryWriter,
|
|
505
|
+
ty: ProductTypeType,
|
|
506
|
+
value: any,
|
|
507
|
+
typespace?: TypespaceType
|
|
508
|
+
): void {
|
|
509
|
+
ProductType.makeSerializer(ty, typespace)(writer, value);
|
|
510
|
+
},
|
|
511
|
+
makeDeserializer(
|
|
512
|
+
ty: ProductTypeType,
|
|
513
|
+
typespace?: TypespaceType
|
|
514
|
+
): Deserializer<any> {
|
|
515
|
+
switch (ty.elements.length) {
|
|
516
|
+
case 0:
|
|
517
|
+
return unitDeserializer;
|
|
518
|
+
case 1: {
|
|
519
|
+
const fieldName = ty.elements[0].name!;
|
|
520
|
+
if (hasOwn(specialProductDeserializers, fieldName))
|
|
521
|
+
return specialProductDeserializers[
|
|
522
|
+
fieldName as keyof SpecialProducts
|
|
523
|
+
];
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
let deserializer = DESERIALIZERS.get(ty);
|
|
528
|
+
if (deserializer != null) return deserializer;
|
|
529
|
+
|
|
530
|
+
if (isFixedSizeProduct(ty)) {
|
|
531
|
+
const body = `\
|
|
532
|
+
"use strict";
|
|
533
|
+
const result = { ${ty.elements.map(getElementInitializer).join(', ')} };
|
|
534
|
+
const view = reader.view;
|
|
535
|
+
${ty.elements
|
|
536
|
+
.map(({ name, algebraicType: { tag } }) =>
|
|
537
|
+
tag in primitiveJSName
|
|
538
|
+
? tag === 'Bool'
|
|
539
|
+
? `\
|
|
540
|
+
result.${name} = view.getUint8(reader.offset) !== 0;
|
|
541
|
+
reader.offset += 1;`
|
|
542
|
+
: `\
|
|
543
|
+
result.${name} = view.get${primitiveJSName[tag as JSPrimitives]}(reader.offset, ${primitiveSizes[tag] > 1 ? 'true' : ''});
|
|
544
|
+
reader.offset += ${primitiveSizes[tag]};`
|
|
545
|
+
: `result.${name} = reader.read${tag}();`
|
|
546
|
+
)
|
|
547
|
+
.join('\n')}
|
|
548
|
+
return result;`;
|
|
549
|
+
deserializer = Function('reader', body) as Deserializer<any>;
|
|
550
|
+
DESERIALIZERS.set(ty, deserializer);
|
|
551
|
+
return deserializer;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Because V8 forces us to generate our code as a string, rather than a proper syntax tree,
|
|
555
|
+
// we can't have our `body` close over values.
|
|
556
|
+
// Instead, we construct an object with the values we'd otherwise "close over" in `deserializers`,
|
|
557
|
+
// and use `Function.prototype.bind` to pass it as the `this` argument.
|
|
558
|
+
//
|
|
559
|
+
// We populate `deserializers` after constructing this type's `deserializer`
|
|
560
|
+
// so that it can close over itself, in the case that `ty` is recursive.
|
|
561
|
+
const deserializers: Record<string, Deserializer<any>> = {};
|
|
562
|
+
deserializer = Function(
|
|
563
|
+
'reader',
|
|
564
|
+
`\
|
|
565
|
+
"use strict";
|
|
566
|
+
const result = { ${ty.elements.map(getElementInitializer).join(', ')} };
|
|
567
|
+
${ty.elements.map(({ name }) => `result.${name!} = this.${name!}(reader);`).join('\n')}
|
|
568
|
+
return result;`
|
|
569
|
+
).bind(deserializers) as Deserializer<any>;
|
|
570
|
+
// In case `ty` is recursive, we cache the function *before* before computing
|
|
571
|
+
// `deserializers`, so that a recursive `makeDeserializer` with the same `ty` has
|
|
572
|
+
// an exit condition.
|
|
573
|
+
DESERIALIZERS.set(ty, deserializer);
|
|
574
|
+
for (const { name, algebraicType } of ty.elements) {
|
|
575
|
+
deserializers[name!] = AlgebraicType.makeDeserializer(
|
|
576
|
+
algebraicType,
|
|
577
|
+
typespace
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
Object.freeze(deserializers);
|
|
581
|
+
return deserializer;
|
|
582
|
+
},
|
|
583
|
+
/** @deprecated Use `makeDeserializer` instead. */
|
|
584
|
+
deserializeValue(
|
|
585
|
+
reader: BinaryReader,
|
|
586
|
+
ty: ProductTypeType,
|
|
587
|
+
typespace?: TypespaceType
|
|
588
|
+
): any {
|
|
589
|
+
return ProductType.makeDeserializer(ty, typespace)(reader);
|
|
590
|
+
},
|
|
591
|
+
intoMapKey(ty: ProductTypeType, value: any): ComparablePrimitive {
|
|
592
|
+
if (ty.elements.length === 1) {
|
|
593
|
+
const fieldName = ty.elements[0].name!;
|
|
594
|
+
if (hasOwn(specialProductDeserializers, fieldName)) {
|
|
595
|
+
return value[fieldName];
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
// The fallback is to serialize and base64 encode the bytes.
|
|
599
|
+
const writer = new BinaryWriter(10);
|
|
600
|
+
AlgebraicType.serializeValue(writer, AlgebraicType.Product(ty), value);
|
|
601
|
+
return writer.toBase64();
|
|
602
|
+
},
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
export type SumType = SumTypeType;
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Unlike most languages, sums in SATS are *[structural]* and not nominal.
|
|
609
|
+
* When checking whether two nominal types are the same,
|
|
610
|
+
* their names and/or declaration sites (e.g., module / namespace) are considered.
|
|
611
|
+
* Meanwhile, a structural type system would only check the structure of the type itself,
|
|
612
|
+
* e.g., the names of its variants and their inner data types in the case of a sum.
|
|
613
|
+
*
|
|
614
|
+
* This is also known as a discriminated union (implementation) or disjoint union.
|
|
615
|
+
* Another name is [coproduct (category theory)](https://ncatlab.org/nlab/show/coproduct).
|
|
616
|
+
*
|
|
617
|
+
* These structures are known as sum types because the number of possible values a sum
|
|
618
|
+
* ```ignore
|
|
619
|
+
* { N_0(T_0), N_1(T_1), ..., N_n(T_n) }
|
|
620
|
+
* ```
|
|
621
|
+
* is:
|
|
622
|
+
* ```ignore
|
|
623
|
+
* Σ (i ∈ 0..n). values(T_i)
|
|
624
|
+
* ```
|
|
625
|
+
* so for example, `values({ A(U64), B(Bool) }) = values(U64) + values(Bool)`.
|
|
626
|
+
*
|
|
627
|
+
* See also: https://ncatlab.org/nlab/show/sum+type.
|
|
628
|
+
*
|
|
629
|
+
* [structural]: https://en.wikipedia.org/wiki/Structural_type_system
|
|
630
|
+
*/
|
|
631
|
+
export const SumType = {
|
|
632
|
+
makeSerializer(ty: SumTypeType, typespace?: TypespaceType): Serializer<any> {
|
|
633
|
+
if (
|
|
634
|
+
ty.variants.length == 2 &&
|
|
635
|
+
ty.variants[0].name === 'some' &&
|
|
636
|
+
ty.variants[1].name === 'none'
|
|
637
|
+
) {
|
|
638
|
+
const serialize = AlgebraicType.makeSerializer(
|
|
639
|
+
ty.variants[0].algebraicType,
|
|
640
|
+
typespace
|
|
641
|
+
);
|
|
642
|
+
return (writer, value) => {
|
|
643
|
+
if (value !== null && value !== undefined) {
|
|
644
|
+
writer.writeByte(0);
|
|
645
|
+
serialize(writer, value);
|
|
646
|
+
} else {
|
|
647
|
+
writer.writeByte(1);
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
} else if (
|
|
651
|
+
ty.variants.length == 2 &&
|
|
652
|
+
ty.variants[0].name === 'ok' &&
|
|
653
|
+
ty.variants[1].name === 'err'
|
|
654
|
+
) {
|
|
655
|
+
const serializeOk = AlgebraicType.makeSerializer(
|
|
656
|
+
ty.variants[0].algebraicType,
|
|
657
|
+
typespace
|
|
658
|
+
);
|
|
659
|
+
const serializeErr = AlgebraicType.makeSerializer(
|
|
660
|
+
ty.variants[0].algebraicType,
|
|
661
|
+
typespace
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
return (writer, value) => {
|
|
665
|
+
if ('ok' in value) {
|
|
666
|
+
writer.writeU8(0);
|
|
667
|
+
serializeOk(writer, value.ok);
|
|
668
|
+
} else if ('err' in value) {
|
|
669
|
+
writer.writeU8(1);
|
|
670
|
+
serializeErr(writer, value.err);
|
|
671
|
+
} else {
|
|
672
|
+
throw new TypeError(
|
|
673
|
+
'could not serialize result: object had neither a `ok` nor an `err` field'
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
} else {
|
|
678
|
+
let serializer = SERIALIZERS.get(ty);
|
|
679
|
+
if (serializer != null) return serializer;
|
|
680
|
+
|
|
681
|
+
const serializers: Record<string, Serializer<any>> = {};
|
|
682
|
+
|
|
683
|
+
const body = `\
|
|
684
|
+
switch (value.tag) {
|
|
685
|
+
${ty.variants
|
|
686
|
+
.map(
|
|
687
|
+
({ name }, i) => `\
|
|
688
|
+
case ${JSON.stringify(name!)}:
|
|
689
|
+
writer.writeByte(${i});
|
|
690
|
+
return this.${name!}(writer, value.value);`
|
|
691
|
+
)
|
|
692
|
+
.join('\n')}
|
|
693
|
+
default:
|
|
694
|
+
throw new TypeError(
|
|
695
|
+
\`Could not serialize sum type; unknown tag \${value.tag}\`
|
|
696
|
+
)
|
|
697
|
+
}
|
|
698
|
+
`;
|
|
699
|
+
|
|
700
|
+
serializer = Function('writer', 'value', body).bind(
|
|
701
|
+
serializers
|
|
702
|
+
) as Serializer<any>;
|
|
703
|
+
|
|
704
|
+
// In case `ty` is recursive, we cache the function *before* before computing
|
|
705
|
+
// `variants`, so that a recursive `makeSerializer` with the same `ty` has
|
|
706
|
+
// an exit condition.
|
|
707
|
+
SERIALIZERS.set(ty, serializer);
|
|
708
|
+
|
|
709
|
+
for (const { name, algebraicType } of ty.variants) {
|
|
710
|
+
serializers[name!] = AlgebraicType.makeSerializer(
|
|
711
|
+
algebraicType,
|
|
712
|
+
typespace
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
Object.freeze(serializers);
|
|
716
|
+
return serializer;
|
|
717
|
+
}
|
|
718
|
+
},
|
|
719
|
+
/** @deprecated Use `makeSerializer` instead. */
|
|
720
|
+
serializeValue(
|
|
721
|
+
writer: BinaryWriter,
|
|
722
|
+
ty: SumTypeType,
|
|
723
|
+
value: any,
|
|
724
|
+
typespace?: TypespaceType
|
|
725
|
+
): void {
|
|
726
|
+
SumType.makeSerializer(ty, typespace)(writer, value);
|
|
727
|
+
},
|
|
728
|
+
makeDeserializer(
|
|
729
|
+
ty: SumTypeType,
|
|
730
|
+
typespace?: TypespaceType
|
|
731
|
+
): Deserializer<any> {
|
|
732
|
+
// In TypeScript we handle Option values as a special case
|
|
733
|
+
// we don't represent the some and none variants, but instead
|
|
734
|
+
// we represent the value directly.
|
|
735
|
+
//
|
|
736
|
+
// For these special cases, we don't do dynamic codegen, since that has the
|
|
737
|
+
// most benefit in cases where the object has a different shape. Since
|
|
738
|
+
// option/result always have the same number of variants, there's not as
|
|
739
|
+
// much benefit for the amount of work.
|
|
740
|
+
if (
|
|
741
|
+
ty.variants.length == 2 &&
|
|
742
|
+
ty.variants[0].name === 'some' &&
|
|
743
|
+
ty.variants[1].name === 'none'
|
|
744
|
+
) {
|
|
745
|
+
const deserialize = AlgebraicType.makeDeserializer(
|
|
746
|
+
ty.variants[0].algebraicType,
|
|
747
|
+
typespace
|
|
748
|
+
);
|
|
749
|
+
return reader => {
|
|
750
|
+
const tag = reader.readU8();
|
|
751
|
+
if (tag === 0) {
|
|
752
|
+
return deserialize(reader);
|
|
753
|
+
} else if (tag === 1) {
|
|
754
|
+
return undefined;
|
|
755
|
+
} else {
|
|
756
|
+
throw `Can't deserialize an option type, couldn't find ${tag} tag`;
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
} else if (
|
|
760
|
+
ty.variants.length == 2 &&
|
|
761
|
+
ty.variants[0].name === 'ok' &&
|
|
762
|
+
ty.variants[1].name === 'err'
|
|
763
|
+
) {
|
|
764
|
+
const deserializeOk = AlgebraicType.makeDeserializer(
|
|
765
|
+
ty.variants[0].algebraicType,
|
|
766
|
+
typespace
|
|
767
|
+
);
|
|
768
|
+
const deserializeErr = AlgebraicType.makeDeserializer(
|
|
769
|
+
ty.variants[1].algebraicType,
|
|
770
|
+
typespace
|
|
771
|
+
);
|
|
772
|
+
return reader => {
|
|
773
|
+
const tag = reader.readByte();
|
|
774
|
+
if (tag === 0) {
|
|
775
|
+
return { ok: deserializeOk(reader) };
|
|
776
|
+
} else if (tag === 1) {
|
|
777
|
+
return { err: deserializeErr(reader) };
|
|
778
|
+
} else {
|
|
779
|
+
throw `Can't deserialize a result type, couldn't find ${tag} tag`;
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
} else {
|
|
783
|
+
let deserializer = DESERIALIZERS.get(ty);
|
|
784
|
+
if (deserializer != null) return deserializer;
|
|
785
|
+
const deserializers: Record<string, Deserializer<any>> = {};
|
|
786
|
+
deserializer = Function(
|
|
787
|
+
'reader',
|
|
788
|
+
`switch (reader.readU8()) {\n${ty.variants
|
|
789
|
+
.map(
|
|
790
|
+
({ name }, i) =>
|
|
791
|
+
`case ${i}: return { tag: ${JSON.stringify(name!)}, value: this.${name!}(reader) };`
|
|
792
|
+
)
|
|
793
|
+
.join('\n')} }`
|
|
794
|
+
).bind(deserializers) as Deserializer<any>;
|
|
795
|
+
// In case `ty` is recursive, we cache the function *before* before computing
|
|
796
|
+
// `deserializers`, so that a recursive `makeDeserializer` with the same `ty` has
|
|
797
|
+
// an exit condition.
|
|
798
|
+
DESERIALIZERS.set(ty, deserializer);
|
|
799
|
+
for (const { name, algebraicType } of ty.variants) {
|
|
800
|
+
deserializers[name!] = AlgebraicType.makeDeserializer(
|
|
801
|
+
algebraicType,
|
|
802
|
+
typespace
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
Object.freeze(deserializers);
|
|
806
|
+
return deserializer;
|
|
807
|
+
}
|
|
808
|
+
},
|
|
809
|
+
/** @deprecated Use `makeDeserializer` instead. */
|
|
810
|
+
deserializeValue(
|
|
811
|
+
reader: BinaryReader,
|
|
812
|
+
ty: SumTypeType,
|
|
813
|
+
typespace?: TypespaceType
|
|
814
|
+
): any {
|
|
815
|
+
return SumType.makeDeserializer(ty, typespace)(reader);
|
|
816
|
+
},
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
export type ComparablePrimitive = number | string | boolean | bigint;
|