skir-client 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/serializer_tester.d.ts +18 -0
- package/dist/cjs/serializer_tester.d.ts.map +1 -0
- package/dist/cjs/serializer_tester.js +124 -0
- package/dist/cjs/serializer_tester.js.map +1 -0
- package/dist/cjs/skir-client.d.ts +697 -0
- package/dist/cjs/skir-client.d.ts.map +1 -0
- package/dist/cjs/skir-client.js +2392 -0
- package/dist/cjs/skir-client.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/serializer_tester.d.ts +18 -0
- package/dist/esm/serializer_tester.d.ts.map +1 -0
- package/dist/esm/serializer_tester.js +119 -0
- package/dist/esm/serializer_tester.js.map +1 -0
- package/dist/esm/skir-client.d.ts +697 -0
- package/dist/esm/skir-client.d.ts.map +1 -0
- package/dist/esm/skir-client.js +2374 -0
- package/dist/esm/skir-client.js.map +1 -0
- package/package.json +58 -0
- package/src/serializer_tester.ts +185 -0
- package/src/skir-client.ts +3492 -0
|
@@ -0,0 +1,2392 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports._initModuleClasses = exports.installServiceOnExpressApp = exports.Service = exports.RawResponse = exports.ServiceClient = exports._EnumBase = exports._FrozenBase = exports._toFrozenArray = exports._EMPTY_ARRAY = exports.parseTypeDescriptorFromJsonCode = exports.parseTypeDescriptorFromJson = exports.optionalSerializer = exports.arraySerializer = exports.primitiveSerializer = exports.ByteString = exports.Timestamp = void 0;
|
|
13
|
+
/**
|
|
14
|
+
* A single moment in time represented in a platform-independent format, with a
|
|
15
|
+
* precision of one millisecond.
|
|
16
|
+
*
|
|
17
|
+
* A `Timestamp` object can represent a maximum of ±8,640,000,000,000,000
|
|
18
|
+
* milliseconds, or ±100,000,000 (one hundred million) days, relative to the
|
|
19
|
+
* Unix epoch. This is the range from April 20, 271821 BC to September 13,
|
|
20
|
+
* 275760 AD.
|
|
21
|
+
*
|
|
22
|
+
* Unlike the Javascript built-in `Date` type, a `Timestamp` is immutable.
|
|
23
|
+
* Like a `Date`, a `Timestamp` object does not contain a timezone.
|
|
24
|
+
*/
|
|
25
|
+
class Timestamp {
|
|
26
|
+
/**
|
|
27
|
+
* Returns a `Timestamp` representing the same moment in time as the given
|
|
28
|
+
* Javascript `Date` object.
|
|
29
|
+
*
|
|
30
|
+
* @throws if the given `Date` object has a timestamp value of NaN
|
|
31
|
+
*/
|
|
32
|
+
static from(date) {
|
|
33
|
+
return this.fromUnixMillis(date.getTime());
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Creates a `Timestamp` object from a number of milliseconds from the Unix
|
|
37
|
+
* epoch.
|
|
38
|
+
*
|
|
39
|
+
* If the given number if outside the valid range (±8,640,000,000,000,000),
|
|
40
|
+
* this function will return `Timestamp.MAX` or `Timestamp.MIN` depending on
|
|
41
|
+
* the sign of the number.
|
|
42
|
+
*
|
|
43
|
+
* @throws if the given number is NaN
|
|
44
|
+
*/
|
|
45
|
+
static fromUnixMillis(unixMillis) {
|
|
46
|
+
if (unixMillis <= this.MIN.unixMillis) {
|
|
47
|
+
return Timestamp.MIN;
|
|
48
|
+
}
|
|
49
|
+
else if (unixMillis < Timestamp.MAX.unixMillis) {
|
|
50
|
+
return new Timestamp(Math.round(unixMillis));
|
|
51
|
+
}
|
|
52
|
+
else if (Number.isNaN(unixMillis)) {
|
|
53
|
+
throw new Error("Cannot construct Timestamp from NaN");
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return Timestamp.MAX;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Creates a `Timestamp` object from a number of seconds from the Unix epoch.
|
|
61
|
+
*
|
|
62
|
+
* If the given number if outside the valid range (±8,640,000,000,000), this
|
|
63
|
+
* function will return `Timestamp.MAX` or `Timestamp.MIN` depending on the
|
|
64
|
+
* sign of the number.
|
|
65
|
+
*
|
|
66
|
+
* @throws if the given number is NaN
|
|
67
|
+
*/
|
|
68
|
+
static fromUnixSeconds(unixSeconds) {
|
|
69
|
+
return this.fromUnixMillis(unixSeconds * 1000);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Parses a date in the date time string format.
|
|
73
|
+
*
|
|
74
|
+
* @throws if the given string is not a date in the date time string format
|
|
75
|
+
* @see https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-date-time-string-format
|
|
76
|
+
*/
|
|
77
|
+
static parse(date) {
|
|
78
|
+
return this.fromUnixMillis(Date.parse(date));
|
|
79
|
+
}
|
|
80
|
+
/** Returns a `Timestamp` representing the current moment in time. */
|
|
81
|
+
static now() {
|
|
82
|
+
return this.fromUnixMillis(Date.now());
|
|
83
|
+
}
|
|
84
|
+
constructor(
|
|
85
|
+
/** Number of milliseconds ellapsed since the Unix epoch. */
|
|
86
|
+
unixMillis) {
|
|
87
|
+
this.unixMillis = unixMillis;
|
|
88
|
+
Object.freeze(this);
|
|
89
|
+
}
|
|
90
|
+
/** Number of seconds ellapsed since the Unix epoch. */
|
|
91
|
+
get unixSeconds() {
|
|
92
|
+
return this.unixMillis / 1000.0;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Returns a `Date` object representing the same moment in time as this
|
|
96
|
+
* `Timestamp`.
|
|
97
|
+
*/
|
|
98
|
+
toDate() {
|
|
99
|
+
return new Date(this.unixMillis);
|
|
100
|
+
}
|
|
101
|
+
toString() {
|
|
102
|
+
return this.toDate().toISOString();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.Timestamp = Timestamp;
|
|
106
|
+
/** Thursday, 1 January 1970. */
|
|
107
|
+
Timestamp.UNIX_EPOCH = new Timestamp(0);
|
|
108
|
+
/**
|
|
109
|
+
* Earliest moment in time representable as a `Timestamp`, namely April 20,
|
|
110
|
+
* 271821 BC.
|
|
111
|
+
*
|
|
112
|
+
* @see https://262.ecma-international.org/5.1/#sec-15.9.1.1
|
|
113
|
+
*/
|
|
114
|
+
Timestamp.MIN = new Timestamp(-8640000000000000);
|
|
115
|
+
/**
|
|
116
|
+
* Latest moment in time representable as a `Timestamp`, namely September 13,
|
|
117
|
+
* 275760 AD.
|
|
118
|
+
*
|
|
119
|
+
* @see https://262.ecma-international.org/5.1/#sec-15.9.1.1
|
|
120
|
+
*/
|
|
121
|
+
Timestamp.MAX = new Timestamp(8640000000000000);
|
|
122
|
+
/** An immutable array of bytes. */
|
|
123
|
+
class ByteString {
|
|
124
|
+
/**
|
|
125
|
+
* Returns an immutable byte string containing all the bytes of `input` from
|
|
126
|
+
* `start`, inclusive, up to `end`, exclusive.
|
|
127
|
+
*
|
|
128
|
+
* If `input` is an `ArrayBuffer`, this function copies the bytes. Otherwise
|
|
129
|
+
* this function returns a sliced view of the input `ByteString`.
|
|
130
|
+
*
|
|
131
|
+
* @example <caption>Copy an array buffer into a byte string</caption>
|
|
132
|
+
* const byteString = ByteString.sliceOf(arrayBuffer);
|
|
133
|
+
*/
|
|
134
|
+
static sliceOf(input, start = 0, end) {
|
|
135
|
+
const { byteLength } = input;
|
|
136
|
+
if (start < 0) {
|
|
137
|
+
start = 0;
|
|
138
|
+
}
|
|
139
|
+
if (end === undefined || end > byteLength) {
|
|
140
|
+
end = byteLength;
|
|
141
|
+
}
|
|
142
|
+
if (end <= start) {
|
|
143
|
+
return ByteString.EMPTY;
|
|
144
|
+
}
|
|
145
|
+
if (input instanceof ByteString) {
|
|
146
|
+
if (start <= 0 && byteLength <= end) {
|
|
147
|
+
// Don't copy the ByteString itself.
|
|
148
|
+
return input;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
// Don't copy the ArrayBuffer.
|
|
152
|
+
const newByteOffset = input.byteOffset + start;
|
|
153
|
+
const newByteLength = end - start;
|
|
154
|
+
return new ByteString(input.arrayBuffer, newByteOffset, newByteLength);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else if (input instanceof ArrayBuffer) {
|
|
158
|
+
return new ByteString(input.slice(start, end));
|
|
159
|
+
}
|
|
160
|
+
else if (input instanceof SharedArrayBuffer) {
|
|
161
|
+
const slice = input.slice(start, end);
|
|
162
|
+
const newBuffer = new ArrayBuffer(slice.byteLength);
|
|
163
|
+
new Uint8Array(newBuffer).set(new Uint8Array(slice));
|
|
164
|
+
return new ByteString(newBuffer);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const _ = input;
|
|
168
|
+
throw new TypeError(_);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Decodes a Base64 string, which can be obtained by calling `toBase64()`.
|
|
173
|
+
*
|
|
174
|
+
* @throws if the given string is not a valid Base64 string.
|
|
175
|
+
* @see https://en.wikipedia.org/wiki/Base64
|
|
176
|
+
*/
|
|
177
|
+
static fromBase64(base64) {
|
|
178
|
+
// See https://developer.mozilla.org/en-US/docs/Glossary/Base64
|
|
179
|
+
const binaryString = atob(base64);
|
|
180
|
+
const array = Uint8Array.from(binaryString, (m) => m.codePointAt(0));
|
|
181
|
+
return new this(array.buffer);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Decodes a hexadecimal string, which can be obtained by calling
|
|
185
|
+
* `toBase16()`.
|
|
186
|
+
*
|
|
187
|
+
* @throws if the given string is not a valid Base64 string.
|
|
188
|
+
*/
|
|
189
|
+
static fromBase16(base16) {
|
|
190
|
+
const bytes = new Uint8Array(base16.length / 2);
|
|
191
|
+
for (let i = 0; i < bytes.length; ++i) {
|
|
192
|
+
const byte = parseInt(base16.substring(i * 2, i * 2 + 2), 16);
|
|
193
|
+
if (Number.isNaN(byte)) {
|
|
194
|
+
throw new Error("Not a valid Base64 string");
|
|
195
|
+
}
|
|
196
|
+
bytes[i] = byte;
|
|
197
|
+
}
|
|
198
|
+
return new ByteString(bytes.buffer);
|
|
199
|
+
}
|
|
200
|
+
/** Copies the contents of this byte string into the given array buffer. */
|
|
201
|
+
copyTo(target, targetOffset = 0) {
|
|
202
|
+
new Uint8Array(target).set(this.uint8Array, targetOffset);
|
|
203
|
+
}
|
|
204
|
+
/** Copies the contents of this byte string into a new array buffer. */
|
|
205
|
+
toBuffer() {
|
|
206
|
+
return this.arrayBuffer.slice(this.byteOffset, this.byteOffset + this.byteLength);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Encodes this byte string into a Base64 string.
|
|
210
|
+
*
|
|
211
|
+
* @see https://en.wikipedia.org/wiki/Base64
|
|
212
|
+
*/
|
|
213
|
+
toBase64() {
|
|
214
|
+
// See https://developer.mozilla.org/en-US/docs/Glossary/Base64
|
|
215
|
+
const binaryString = Array.from(this.uint8Array, (x) => String.fromCodePoint(x)).join("");
|
|
216
|
+
return btoa(binaryString);
|
|
217
|
+
}
|
|
218
|
+
/** Encodes this byte string into a hexadecimal string. */
|
|
219
|
+
toBase16() {
|
|
220
|
+
return [...this.uint8Array]
|
|
221
|
+
.map((x) => x.toString(16).padStart(2, "0"))
|
|
222
|
+
.join("");
|
|
223
|
+
}
|
|
224
|
+
at(index) {
|
|
225
|
+
return this.uint8Array[index < 0 ? index + this.byteLength : index];
|
|
226
|
+
}
|
|
227
|
+
toString() {
|
|
228
|
+
return `ByteString(${this.byteLength})`;
|
|
229
|
+
}
|
|
230
|
+
constructor(arrayBuffer, byteOffset = 0,
|
|
231
|
+
/** The length of this byte string. */
|
|
232
|
+
byteLength = arrayBuffer.byteLength) {
|
|
233
|
+
this.arrayBuffer = arrayBuffer;
|
|
234
|
+
this.byteOffset = byteOffset;
|
|
235
|
+
this.byteLength = byteLength;
|
|
236
|
+
this.uint8Array = new Uint8Array(arrayBuffer, byteOffset, byteLength);
|
|
237
|
+
Object.freeze(this);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
exports.ByteString = ByteString;
|
|
241
|
+
/** An empty byte string. */
|
|
242
|
+
ByteString.EMPTY = new ByteString(new ArrayBuffer(0));
|
|
243
|
+
/**
|
|
244
|
+
* Returns a serializer of instances of the given Skir primitive type.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* expect(
|
|
248
|
+
* primitiveSerializer("string").toJsonCode("foo")
|
|
249
|
+
* ).toBe(
|
|
250
|
+
* '"foo"'
|
|
251
|
+
* );
|
|
252
|
+
*/
|
|
253
|
+
function primitiveSerializer(primitiveType) {
|
|
254
|
+
return primitiveSerializers[primitiveType];
|
|
255
|
+
}
|
|
256
|
+
exports.primitiveSerializer = primitiveSerializer;
|
|
257
|
+
/**
|
|
258
|
+
* Returns a serializer of arrays of `Item`s.
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* expect(
|
|
262
|
+
* arraySerializer(User.serializer).toJsonCode([JANE, JOE])
|
|
263
|
+
* ).toBe(
|
|
264
|
+
* '[["jane"],["joe"]]'
|
|
265
|
+
* );
|
|
266
|
+
*/
|
|
267
|
+
function arraySerializer(item, keyChain) {
|
|
268
|
+
if (keyChain !== undefined &&
|
|
269
|
+
!/^[a-z_][a-z0-9_]*(\.[a-z_][a-z0-9_]*)*$/.test(keyChain)) {
|
|
270
|
+
throw new Error(`Invalid keyChain "${keyChain}"`);
|
|
271
|
+
}
|
|
272
|
+
return new ArraySerializerImpl(item, keyChain);
|
|
273
|
+
}
|
|
274
|
+
exports.arraySerializer = arraySerializer;
|
|
275
|
+
/** Returns a serializer of nullable `T`s. */
|
|
276
|
+
function optionalSerializer(other) {
|
|
277
|
+
return other instanceof OptionalSerializerImpl
|
|
278
|
+
? other
|
|
279
|
+
: new OptionalSerializerImpl(other);
|
|
280
|
+
}
|
|
281
|
+
exports.optionalSerializer = optionalSerializer;
|
|
282
|
+
/** Parameter of the {@link InternalSerializer.decode} method. */
|
|
283
|
+
class InputStream {
|
|
284
|
+
constructor(buffer, keep) {
|
|
285
|
+
this.buffer = buffer;
|
|
286
|
+
this.offset = 0;
|
|
287
|
+
this.dataView = new DataView(buffer);
|
|
288
|
+
this.keepUnrecognizedValues = !!keep;
|
|
289
|
+
}
|
|
290
|
+
readUint8() {
|
|
291
|
+
return this.dataView.getUint8(this.offset++);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// For wires [232, 241]
|
|
295
|
+
const DECODE_NUMBER_FNS = [
|
|
296
|
+
(s) => s.dataView.getUint16((s.offset += 2) - 2, true),
|
|
297
|
+
(s) => s.dataView.getUint32((s.offset += 4) - 4, true),
|
|
298
|
+
(s) => s.dataView.getBigUint64((s.offset += 8) - 8, true),
|
|
299
|
+
(stream) => stream.readUint8() - 256,
|
|
300
|
+
(s) => s.dataView.getUint16((s.offset += 2) - 2, true) - 65536,
|
|
301
|
+
(s) => s.dataView.getInt32((s.offset += 4) - 4, true),
|
|
302
|
+
(s) => s.dataView.getBigInt64((s.offset += 8) - 8, true),
|
|
303
|
+
(s) => s.dataView.getBigInt64((s.offset += 8) - 8, true),
|
|
304
|
+
(s) => s.dataView.getFloat32((s.offset += 4) - 4, true),
|
|
305
|
+
(s) => s.dataView.getFloat64((s.offset += 8) - 8, true),
|
|
306
|
+
];
|
|
307
|
+
function decodeNumber(stream) {
|
|
308
|
+
const wire = stream.readUint8();
|
|
309
|
+
return wire < 232 ? wire : DECODE_NUMBER_FNS[wire - 232](stream);
|
|
310
|
+
}
|
|
311
|
+
function decodeBigInt(stream) {
|
|
312
|
+
const number = decodeNumber(stream);
|
|
313
|
+
return typeof number === "bigint" ? number : BigInt(Math.round(number));
|
|
314
|
+
}
|
|
315
|
+
/** Parameter of the {@link InternalSerializer.encode} method. */
|
|
316
|
+
class OutputStream {
|
|
317
|
+
constructor() {
|
|
318
|
+
this.buffer = new ArrayBuffer(128);
|
|
319
|
+
this.dataView = new DataView(this.buffer);
|
|
320
|
+
this.offset = 0;
|
|
321
|
+
// The final binary form is the result of concatenating these arrays.
|
|
322
|
+
// The length of each array is approximately twice the length of the previous
|
|
323
|
+
// array.
|
|
324
|
+
this.pieces = [];
|
|
325
|
+
// Updated each time `flush()` is called.
|
|
326
|
+
this.byteLength = 0;
|
|
327
|
+
}
|
|
328
|
+
writeUint8(value) {
|
|
329
|
+
const dataView = this.reserve(1);
|
|
330
|
+
dataView.setUint8(++this.offset - 1, value);
|
|
331
|
+
}
|
|
332
|
+
writeUint16(value) {
|
|
333
|
+
const dataView = this.reserve(2);
|
|
334
|
+
dataView.setUint16((this.offset += 2) - 2, value, true);
|
|
335
|
+
}
|
|
336
|
+
writeUint32(value) {
|
|
337
|
+
const dataView = this.reserve(4);
|
|
338
|
+
dataView.setUint32((this.offset += 4) - 4, value, true);
|
|
339
|
+
}
|
|
340
|
+
writeInt32(value) {
|
|
341
|
+
const dataView = this.reserve(4);
|
|
342
|
+
dataView.setInt32((this.offset += 4) - 4, value, true);
|
|
343
|
+
}
|
|
344
|
+
writeUint64(value) {
|
|
345
|
+
const dataView = this.reserve(8);
|
|
346
|
+
dataView.setBigUint64((this.offset += 8) - 8, value, true);
|
|
347
|
+
}
|
|
348
|
+
writeInt64(value) {
|
|
349
|
+
const dataView = this.reserve(8);
|
|
350
|
+
dataView.setBigInt64((this.offset += 8) - 8, value, true);
|
|
351
|
+
}
|
|
352
|
+
writeFloat32(value) {
|
|
353
|
+
const dataView = this.reserve(4);
|
|
354
|
+
dataView.setFloat32((this.offset += 4) - 4, value, true);
|
|
355
|
+
}
|
|
356
|
+
writeFloat64(value) {
|
|
357
|
+
const dataView = this.reserve(8);
|
|
358
|
+
dataView.setFloat64((this.offset += 8) - 8, value, true);
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Encodes the given string to UTF-8 and writes the bytes to this stream.
|
|
362
|
+
* Returns the number of bytes written.
|
|
363
|
+
*/
|
|
364
|
+
putUtf8String(string) {
|
|
365
|
+
// We do at most 3 writes:
|
|
366
|
+
// - First, fill the current buffer as much as possible
|
|
367
|
+
// - If there is not enough room, allocate a new buffer of N bytes, where
|
|
368
|
+
// N is twice the number of remaining UTF-16 characters in the string,
|
|
369
|
+
// and write to it. This new buffer is very likely to have enough
|
|
370
|
+
// room.
|
|
371
|
+
// - If there was not enough room, try again one last time.
|
|
372
|
+
//
|
|
373
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder
|
|
374
|
+
let dataView = this.dataView;
|
|
375
|
+
let result = 0;
|
|
376
|
+
while (string) {
|
|
377
|
+
const encodeResult = textEncoder.encodeInto(string, new Uint8Array(dataView.buffer, this.offset));
|
|
378
|
+
this.offset += encodeResult.written;
|
|
379
|
+
result += encodeResult.written;
|
|
380
|
+
string = string.substring(encodeResult.read);
|
|
381
|
+
if (string) {
|
|
382
|
+
dataView = this.reserve(string.length * 2);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return result;
|
|
386
|
+
}
|
|
387
|
+
putBytes(bytes) {
|
|
388
|
+
// We do at most 2 writes:
|
|
389
|
+
// - First, fill the current buffer as much as possible
|
|
390
|
+
// - If there is not enough room, allocate a new buffer of N bytes, where
|
|
391
|
+
// N is at least the number of bytes left in the byte string.
|
|
392
|
+
const { buffer } = this;
|
|
393
|
+
const bytesLeftInCurrentBuffer = buffer.byteLength - this.offset;
|
|
394
|
+
const head = ByteString.sliceOf(bytes, 0, bytesLeftInCurrentBuffer);
|
|
395
|
+
head.copyTo(buffer, this.offset);
|
|
396
|
+
this.offset += head.byteLength;
|
|
397
|
+
const remainingBytes = bytes.byteLength - head.byteLength;
|
|
398
|
+
if (remainingBytes <= 0) {
|
|
399
|
+
// Everything was written.
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const tail = ByteString.sliceOf(bytes, remainingBytes);
|
|
403
|
+
this.reserve(remainingBytes);
|
|
404
|
+
tail.copyTo(buffer, this.offset);
|
|
405
|
+
this.offset += remainingBytes;
|
|
406
|
+
}
|
|
407
|
+
finalize() {
|
|
408
|
+
this.flush();
|
|
409
|
+
Object.freeze(this.pieces);
|
|
410
|
+
Object.freeze(this);
|
|
411
|
+
return this;
|
|
412
|
+
}
|
|
413
|
+
copyTo(target, offset = 0) {
|
|
414
|
+
const targetArea = new Uint8Array(target);
|
|
415
|
+
for (const piece of this.pieces) {
|
|
416
|
+
targetArea.set(piece, offset);
|
|
417
|
+
offset += piece.length;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
toBuffer() {
|
|
421
|
+
const result = new ArrayBuffer(this.byteLength);
|
|
422
|
+
this.copyTo(result);
|
|
423
|
+
return result;
|
|
424
|
+
}
|
|
425
|
+
/** Returns a data view with enough capacity for `bytes` more bytes. */
|
|
426
|
+
reserve(bytes) {
|
|
427
|
+
if (this.offset < this.buffer.byteLength - bytes) {
|
|
428
|
+
// Enough room in the current data view.
|
|
429
|
+
return this.dataView;
|
|
430
|
+
}
|
|
431
|
+
this.flush();
|
|
432
|
+
const lengthInBytes = Math.max(this.byteLength, bytes);
|
|
433
|
+
this.offset = 0;
|
|
434
|
+
this.buffer = new ArrayBuffer(lengthInBytes);
|
|
435
|
+
return (this.dataView = new DataView(this.buffer));
|
|
436
|
+
}
|
|
437
|
+
/** Adds the current buffer to `pieces`. Updates `byteLength` accordingly. */
|
|
438
|
+
flush() {
|
|
439
|
+
const { offset } = this;
|
|
440
|
+
this.pieces.push(new Uint8Array(this.dataView.buffer, 0, offset));
|
|
441
|
+
this.byteLength += offset;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
function encodeUint32(length, stream) {
|
|
445
|
+
if (length < 232) {
|
|
446
|
+
stream.writeUint8(length);
|
|
447
|
+
}
|
|
448
|
+
else if (length < 65536) {
|
|
449
|
+
stream.writeUint8(232);
|
|
450
|
+
stream.writeUint16(length);
|
|
451
|
+
}
|
|
452
|
+
else if (length < 4294967296) {
|
|
453
|
+
stream.writeUint8(233);
|
|
454
|
+
stream.writeUint32(length);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
throw new Error(`max length exceeded: ${length}`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
class AbstractSerializer {
|
|
461
|
+
fromJsonCode(code, keep) {
|
|
462
|
+
return this.fromJson(JSON.parse(code), keep);
|
|
463
|
+
}
|
|
464
|
+
fromBytes(bytes, keep) {
|
|
465
|
+
const inputStream = new InputStream(bytes, keep);
|
|
466
|
+
inputStream.offset = 4; // Skip the "skir" header.
|
|
467
|
+
return this.decode(inputStream);
|
|
468
|
+
}
|
|
469
|
+
toJsonCode(input, flavor) {
|
|
470
|
+
const indent = flavor === "readable" ? " " : undefined;
|
|
471
|
+
return JSON.stringify(this.toJson(input, flavor), undefined, indent);
|
|
472
|
+
}
|
|
473
|
+
toBytes(input) {
|
|
474
|
+
const stream = new OutputStream();
|
|
475
|
+
stream.putUtf8String("skir");
|
|
476
|
+
this.encode(input, stream);
|
|
477
|
+
return stream.finalize();
|
|
478
|
+
}
|
|
479
|
+
// Default implementation; this behavior is not correct for all subclasses.
|
|
480
|
+
isDefault(input) {
|
|
481
|
+
return !input;
|
|
482
|
+
}
|
|
483
|
+
get typeDescriptor() {
|
|
484
|
+
return this;
|
|
485
|
+
}
|
|
486
|
+
asJson() {
|
|
487
|
+
const recordDefinitions = {};
|
|
488
|
+
this.addRecordDefinitionsTo(recordDefinitions);
|
|
489
|
+
const result = {
|
|
490
|
+
type: this.typeSignature,
|
|
491
|
+
records: Object.values(recordDefinitions),
|
|
492
|
+
};
|
|
493
|
+
return result;
|
|
494
|
+
}
|
|
495
|
+
asJsonCode() {
|
|
496
|
+
return JSON.stringify(this.asJson(), undefined, " ");
|
|
497
|
+
}
|
|
498
|
+
transform(json_or_bytes, out) {
|
|
499
|
+
const decoded = json_or_bytes instanceof ArrayBuffer
|
|
500
|
+
? this.fromBytes(json_or_bytes)
|
|
501
|
+
: this.fromJson(json_or_bytes);
|
|
502
|
+
return out === "bytes"
|
|
503
|
+
? this.toBytes(decoded).toBuffer()
|
|
504
|
+
: this.toJson(decoded, out);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
// The UNKNOWN variant is common to all enums.
|
|
508
|
+
const UNKNOWN_VARIANT_DEFINITION = {
|
|
509
|
+
name: "?",
|
|
510
|
+
number: 0,
|
|
511
|
+
};
|
|
512
|
+
/**
|
|
513
|
+
* Returns a `TypeDescriptor` from its JSON representation as returned by
|
|
514
|
+
* `asJson()`.
|
|
515
|
+
*/
|
|
516
|
+
function parseTypeDescriptorFromJson(json) {
|
|
517
|
+
var _a;
|
|
518
|
+
const typeDefinition = json;
|
|
519
|
+
const recordBundles = {};
|
|
520
|
+
// First loop: create the serializer for each record.
|
|
521
|
+
// It's not yet initialized.
|
|
522
|
+
for (const record of typeDefinition.records) {
|
|
523
|
+
let serializer;
|
|
524
|
+
switch (record.kind) {
|
|
525
|
+
case "struct":
|
|
526
|
+
serializer = new StructSerializerImpl({}, (initializer) => Object.freeze(Object.assign({}, initializer)), (() => ({})));
|
|
527
|
+
break;
|
|
528
|
+
case "enum":
|
|
529
|
+
serializer = new EnumSerializerImpl((o) => o instanceof UnrecognizedEnum
|
|
530
|
+
? Object.freeze({ kind: "?" })
|
|
531
|
+
: Object.freeze({
|
|
532
|
+
kind: o.kind,
|
|
533
|
+
value: o.value,
|
|
534
|
+
}));
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
const recordBundle = {
|
|
538
|
+
definition: record,
|
|
539
|
+
serializer: serializer,
|
|
540
|
+
};
|
|
541
|
+
recordBundles[record.id] = recordBundle;
|
|
542
|
+
}
|
|
543
|
+
function parse(ts) {
|
|
544
|
+
switch (ts.kind) {
|
|
545
|
+
case "array": {
|
|
546
|
+
const { item, key_extractor } = ts.value;
|
|
547
|
+
return new ArraySerializerImpl(parse(item), key_extractor);
|
|
548
|
+
}
|
|
549
|
+
case "optional":
|
|
550
|
+
return new OptionalSerializerImpl(parse(ts.value));
|
|
551
|
+
case "primitive":
|
|
552
|
+
return primitiveSerializer(ts.value);
|
|
553
|
+
case "record": {
|
|
554
|
+
const recordId = ts.value;
|
|
555
|
+
return recordBundles[recordId].serializer;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
// Second loop: initialize each serializer.
|
|
560
|
+
const initOps = [];
|
|
561
|
+
for (const recordBundle of Object.values(recordBundles)) {
|
|
562
|
+
const { definition, serializer } = recordBundle;
|
|
563
|
+
const { defaultValue } = serializer;
|
|
564
|
+
const { id, removed_numbers } = definition;
|
|
565
|
+
const idParts = id.split(":");
|
|
566
|
+
const module = idParts[0];
|
|
567
|
+
const qualifiedName = idParts[1];
|
|
568
|
+
const nameParts = qualifiedName.split(".");
|
|
569
|
+
const name = nameParts[nameParts.length - 1];
|
|
570
|
+
const parentId = module + ":" + nameParts.slice(0, -1).join(".");
|
|
571
|
+
const parentType = (_a = recordBundles[parentId]) === null || _a === void 0 ? void 0 : _a.serializer;
|
|
572
|
+
switch (definition.kind) {
|
|
573
|
+
case "struct": {
|
|
574
|
+
const fields = [];
|
|
575
|
+
for (const f of definition.fields) {
|
|
576
|
+
const fieldSerializer = parse(f.type);
|
|
577
|
+
fields.push(new StructFieldImpl(f.name, f.name, f.number, fieldSerializer));
|
|
578
|
+
defaultValue[f.name] = fieldSerializer.defaultValue;
|
|
579
|
+
}
|
|
580
|
+
const s = serializer;
|
|
581
|
+
initOps.push(() => s.init(name, module, parentType, fields, removed_numbers !== null && removed_numbers !== void 0 ? removed_numbers : []));
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
case "enum": {
|
|
585
|
+
const s = serializer;
|
|
586
|
+
const variants = [UNKNOWN_VARIANT_DEFINITION]
|
|
587
|
+
.concat(definition.variants)
|
|
588
|
+
.map((f) => f.type
|
|
589
|
+
? new EnumWrapperVariantImpl(f.name, f.number, parse(f.type), serializer.createFn)
|
|
590
|
+
: {
|
|
591
|
+
name: f.name,
|
|
592
|
+
number: f.number,
|
|
593
|
+
constant: Object.freeze({ kind: f.name }),
|
|
594
|
+
});
|
|
595
|
+
initOps.push(() => s.init(name, module, parentType, variants, removed_numbers !== null && removed_numbers !== void 0 ? removed_numbers : []));
|
|
596
|
+
break;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
// We need to actually initialize the serializers *after* the default values
|
|
601
|
+
// were constructed, because `init` calls `freezeDeeply` and this might result
|
|
602
|
+
// in freezing the default of another serializer.
|
|
603
|
+
initOps.forEach((op) => op());
|
|
604
|
+
return parse(typeDefinition.type).typeDescriptor;
|
|
605
|
+
}
|
|
606
|
+
exports.parseTypeDescriptorFromJson = parseTypeDescriptorFromJson;
|
|
607
|
+
/**
|
|
608
|
+
* Returns a `TypeDescriptor` from its JSON code representation as returned by
|
|
609
|
+
* `asJsonCode()`.
|
|
610
|
+
*/
|
|
611
|
+
function parseTypeDescriptorFromJsonCode(code) {
|
|
612
|
+
return parseTypeDescriptorFromJson(JSON.parse(code));
|
|
613
|
+
}
|
|
614
|
+
exports.parseTypeDescriptorFromJsonCode = parseTypeDescriptorFromJsonCode;
|
|
615
|
+
class AbstractPrimitiveSerializer extends AbstractSerializer {
|
|
616
|
+
constructor() {
|
|
617
|
+
super(...arguments);
|
|
618
|
+
this.kind = "primitive";
|
|
619
|
+
}
|
|
620
|
+
get typeSignature() {
|
|
621
|
+
return {
|
|
622
|
+
kind: "primitive",
|
|
623
|
+
value: this.primitive,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
addRecordDefinitionsTo(_out) { }
|
|
627
|
+
}
|
|
628
|
+
class BoolSerializer extends AbstractPrimitiveSerializer {
|
|
629
|
+
constructor() {
|
|
630
|
+
super(...arguments);
|
|
631
|
+
this.primitive = "bool";
|
|
632
|
+
this.defaultValue = false;
|
|
633
|
+
}
|
|
634
|
+
toJson(input, flavor) {
|
|
635
|
+
return flavor === "readable" ? !!input : input ? 1 : 0;
|
|
636
|
+
}
|
|
637
|
+
fromJson(json) {
|
|
638
|
+
return !!json && json !== "0";
|
|
639
|
+
}
|
|
640
|
+
encode(input, stream) {
|
|
641
|
+
stream.writeUint8(input ? 1 : 0);
|
|
642
|
+
}
|
|
643
|
+
decode(stream) {
|
|
644
|
+
return !!decodeNumber(stream);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
class Int32Serializer extends AbstractPrimitiveSerializer {
|
|
648
|
+
constructor() {
|
|
649
|
+
super(...arguments);
|
|
650
|
+
this.primitive = "int32";
|
|
651
|
+
this.defaultValue = 0;
|
|
652
|
+
}
|
|
653
|
+
toJson(input) {
|
|
654
|
+
return input | 0;
|
|
655
|
+
}
|
|
656
|
+
fromJson(json) {
|
|
657
|
+
// `+value` will work if the input JSON value is a string, which is
|
|
658
|
+
// what the int64 serializer produces.
|
|
659
|
+
return +json | 0;
|
|
660
|
+
}
|
|
661
|
+
encode(input, stream) {
|
|
662
|
+
if (input < 0) {
|
|
663
|
+
if (input >= -256) {
|
|
664
|
+
stream.writeUint8(235);
|
|
665
|
+
stream.writeUint8(input + 256);
|
|
666
|
+
}
|
|
667
|
+
else if (input >= -65536) {
|
|
668
|
+
stream.writeUint8(236);
|
|
669
|
+
stream.writeUint16(input + 65536);
|
|
670
|
+
}
|
|
671
|
+
else {
|
|
672
|
+
stream.writeUint8(237);
|
|
673
|
+
stream.writeInt32(input >= -2147483648 ? input : -2147483648);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
else if (input < 232) {
|
|
677
|
+
stream.writeUint8(input);
|
|
678
|
+
}
|
|
679
|
+
else if (input < 65536) {
|
|
680
|
+
stream.writeUint8(232);
|
|
681
|
+
stream.writeUint16(input);
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
stream.writeUint8(233);
|
|
685
|
+
stream.writeUint32(input <= 2147483647 ? input : 2147483647);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
decode(stream) {
|
|
689
|
+
return Number(decodeNumber(stream)) | 0;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
const int32_Serializer = new Int32Serializer();
|
|
693
|
+
class FloatSerializer extends AbstractPrimitiveSerializer {
|
|
694
|
+
constructor() {
|
|
695
|
+
super(...arguments);
|
|
696
|
+
this.defaultValue = 0;
|
|
697
|
+
}
|
|
698
|
+
toJson(input) {
|
|
699
|
+
if (Number.isFinite(input)) {
|
|
700
|
+
return input;
|
|
701
|
+
}
|
|
702
|
+
else if (typeof input === "number") {
|
|
703
|
+
// If the number is NaN or +/- Infinity, return a JSON string.
|
|
704
|
+
return input.toString();
|
|
705
|
+
}
|
|
706
|
+
throw new TypeError();
|
|
707
|
+
}
|
|
708
|
+
fromJson(json) {
|
|
709
|
+
return +json;
|
|
710
|
+
}
|
|
711
|
+
decode(stream) {
|
|
712
|
+
return Number(decodeNumber(stream));
|
|
713
|
+
}
|
|
714
|
+
isDefault(input) {
|
|
715
|
+
// Needs to work for NaN.
|
|
716
|
+
return input === 0;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
class Float32Serializer extends FloatSerializer {
|
|
720
|
+
constructor() {
|
|
721
|
+
super(...arguments);
|
|
722
|
+
this.primitive = "float32";
|
|
723
|
+
}
|
|
724
|
+
encode(input, stream) {
|
|
725
|
+
if (input === 0) {
|
|
726
|
+
stream.writeUint8(0);
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
stream.writeUint8(240);
|
|
730
|
+
stream.writeFloat32(input);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
class Float64Serializer extends FloatSerializer {
|
|
735
|
+
constructor() {
|
|
736
|
+
super(...arguments);
|
|
737
|
+
this.primitive = "float64";
|
|
738
|
+
}
|
|
739
|
+
encode(input, stream) {
|
|
740
|
+
if (input === 0) {
|
|
741
|
+
stream.writeUint8(0);
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
stream.writeUint8(241);
|
|
745
|
+
stream.writeFloat64(input);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
class AbstractBigIntSerializer extends AbstractPrimitiveSerializer {
|
|
750
|
+
constructor() {
|
|
751
|
+
super(...arguments);
|
|
752
|
+
this.defaultValue = BigInt(0);
|
|
753
|
+
}
|
|
754
|
+
fromJson(json) {
|
|
755
|
+
try {
|
|
756
|
+
return BigInt(json);
|
|
757
|
+
}
|
|
758
|
+
catch (e) {
|
|
759
|
+
if (typeof json === "number") {
|
|
760
|
+
return BigInt(Math.round(json));
|
|
761
|
+
}
|
|
762
|
+
else {
|
|
763
|
+
throw e;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
const MIN_INT64 = BigInt("-9223372036854775808");
|
|
769
|
+
const MAX_INT64 = BigInt("9223372036854775807");
|
|
770
|
+
class Int64Serializer extends AbstractBigIntSerializer {
|
|
771
|
+
constructor() {
|
|
772
|
+
super(...arguments);
|
|
773
|
+
this.primitive = "int64";
|
|
774
|
+
}
|
|
775
|
+
toJson(input) {
|
|
776
|
+
// 9007199254740991 == Number.MAX_SAFE_INTEGER
|
|
777
|
+
if (-9007199254740991 <= input && input <= 9007199254740991) {
|
|
778
|
+
return Number(input);
|
|
779
|
+
}
|
|
780
|
+
const s = BigInt(input).toString();
|
|
781
|
+
// Clamp the number if it's out of bounds.
|
|
782
|
+
return s.length <= 18
|
|
783
|
+
? // Small optimization for "small" numbers. The max int64 has 19 digits.
|
|
784
|
+
s
|
|
785
|
+
: input < MIN_INT64
|
|
786
|
+
? MIN_INT64.toString()
|
|
787
|
+
: input < MAX_INT64
|
|
788
|
+
? s
|
|
789
|
+
: MAX_INT64.toString();
|
|
790
|
+
}
|
|
791
|
+
encode(input, stream) {
|
|
792
|
+
if (input) {
|
|
793
|
+
if (-2147483648 <= input && input <= 2147483647) {
|
|
794
|
+
int32_Serializer.encode(Number(input), stream);
|
|
795
|
+
}
|
|
796
|
+
else {
|
|
797
|
+
stream.writeUint8(238);
|
|
798
|
+
// Clamp the number if it's out of bounds.
|
|
799
|
+
stream.writeInt64(input < MIN_INT64 ? MIN_INT64 : input < MAX_INT64 ? input : MAX_INT64);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
else {
|
|
803
|
+
stream.writeUint8(0);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
decode(stream) {
|
|
807
|
+
return decodeBigInt(stream);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
const MAX_UINT64 = BigInt("18446744073709551615");
|
|
811
|
+
class Uint64Serializer extends AbstractBigIntSerializer {
|
|
812
|
+
constructor() {
|
|
813
|
+
super(...arguments);
|
|
814
|
+
this.primitive = "uint64";
|
|
815
|
+
}
|
|
816
|
+
toJson(input) {
|
|
817
|
+
if (input <= 9007199254740991) {
|
|
818
|
+
return input <= 0 ? 0 : Number(input);
|
|
819
|
+
}
|
|
820
|
+
input = BigInt(input);
|
|
821
|
+
return MAX_UINT64 < input ? MAX_UINT64.toString() : input.toString();
|
|
822
|
+
}
|
|
823
|
+
encode(input, stream) {
|
|
824
|
+
if (input < 232) {
|
|
825
|
+
stream.writeUint8(input <= 0 ? 0 : Number(input));
|
|
826
|
+
}
|
|
827
|
+
else if (input < 4294967296) {
|
|
828
|
+
if (input < 65536) {
|
|
829
|
+
stream.writeUint8(232);
|
|
830
|
+
stream.writeUint16(Number(input));
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
stream.writeUint8(233);
|
|
834
|
+
stream.writeUint32(Number(input));
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
else {
|
|
838
|
+
stream.writeUint8(234);
|
|
839
|
+
stream.writeUint64(input <= MAX_UINT64 ? input : MAX_UINT64);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
decode(stream) {
|
|
843
|
+
return decodeBigInt(stream);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
class TimestampSerializer extends AbstractPrimitiveSerializer {
|
|
847
|
+
constructor() {
|
|
848
|
+
super(...arguments);
|
|
849
|
+
this.primitive = "timestamp";
|
|
850
|
+
this.defaultValue = Timestamp.UNIX_EPOCH;
|
|
851
|
+
}
|
|
852
|
+
toJson(input, flavor) {
|
|
853
|
+
return flavor === "readable"
|
|
854
|
+
? {
|
|
855
|
+
unix_millis: input.unixMillis,
|
|
856
|
+
formatted: input.toDate().toISOString(),
|
|
857
|
+
}
|
|
858
|
+
: input.unixMillis;
|
|
859
|
+
}
|
|
860
|
+
fromJson(json) {
|
|
861
|
+
return Timestamp.fromUnixMillis(typeof json === "number"
|
|
862
|
+
? json
|
|
863
|
+
: typeof json === "string"
|
|
864
|
+
? +json
|
|
865
|
+
: json["unix_millis"]);
|
|
866
|
+
}
|
|
867
|
+
encode(input, stream) {
|
|
868
|
+
const { unixMillis } = input;
|
|
869
|
+
if (unixMillis) {
|
|
870
|
+
stream.writeUint8(239);
|
|
871
|
+
stream.writeInt64(BigInt(unixMillis));
|
|
872
|
+
}
|
|
873
|
+
else {
|
|
874
|
+
stream.writeUint8(0);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
decode(stream) {
|
|
878
|
+
const unixMillis = decodeNumber(stream);
|
|
879
|
+
return Timestamp.fromUnixMillis(Number(unixMillis));
|
|
880
|
+
}
|
|
881
|
+
isDefault(input) {
|
|
882
|
+
return !input.unixMillis;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
class StringSerializer extends AbstractPrimitiveSerializer {
|
|
886
|
+
constructor() {
|
|
887
|
+
super(...arguments);
|
|
888
|
+
this.primitive = "string";
|
|
889
|
+
this.defaultValue = "";
|
|
890
|
+
}
|
|
891
|
+
toJson(input) {
|
|
892
|
+
if (typeof input === "string") {
|
|
893
|
+
return input;
|
|
894
|
+
}
|
|
895
|
+
throw this.newTypeError(input);
|
|
896
|
+
}
|
|
897
|
+
fromJson(json) {
|
|
898
|
+
if (typeof json === "string") {
|
|
899
|
+
return json;
|
|
900
|
+
}
|
|
901
|
+
if (json === 0) {
|
|
902
|
+
return "";
|
|
903
|
+
}
|
|
904
|
+
throw this.newTypeError(json);
|
|
905
|
+
}
|
|
906
|
+
encode(input, stream) {
|
|
907
|
+
if (!input) {
|
|
908
|
+
stream.writeUint8(242);
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
stream.writeUint8(243);
|
|
912
|
+
// We don't know the length of the UTF-8 string until we actually encode the
|
|
913
|
+
// string. We just know that it's at most 3 times the length of the input
|
|
914
|
+
// string.
|
|
915
|
+
const maxEncodedLength = input.length * 3;
|
|
916
|
+
// Write zero in place of the UTF-8 sequence length. We will override this
|
|
917
|
+
// number later.
|
|
918
|
+
if (maxEncodedLength < 232) {
|
|
919
|
+
stream.writeUint8(0);
|
|
920
|
+
}
|
|
921
|
+
else if (maxEncodedLength < 65536) {
|
|
922
|
+
stream.writeUint8(232);
|
|
923
|
+
stream.writeUint16(0);
|
|
924
|
+
}
|
|
925
|
+
else {
|
|
926
|
+
stream.writeUint8(233);
|
|
927
|
+
stream.writeUint32(0);
|
|
928
|
+
}
|
|
929
|
+
const { dataView, offset } = stream;
|
|
930
|
+
// Write the UTF-8 string and record the number of bytes written.
|
|
931
|
+
const encodedLength = stream.putUtf8String(input);
|
|
932
|
+
// Write the length of the UTF-8 string where we wrote 0.
|
|
933
|
+
if (maxEncodedLength < 232) {
|
|
934
|
+
dataView.setUint8(offset - 1, encodedLength);
|
|
935
|
+
}
|
|
936
|
+
else if (maxEncodedLength < 65536) {
|
|
937
|
+
dataView.setUint16(offset - 2, encodedLength, true);
|
|
938
|
+
}
|
|
939
|
+
else {
|
|
940
|
+
dataView.setUint32(offset - 4, encodedLength, true);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
decode(stream) {
|
|
944
|
+
const wire = stream.readUint8();
|
|
945
|
+
if (wire === 0 || wire === 242) {
|
|
946
|
+
return "";
|
|
947
|
+
}
|
|
948
|
+
const encodedLength = decodeNumber(stream);
|
|
949
|
+
return textDecoder.decode(new Uint8Array(stream.buffer, (stream.offset += encodedLength) - encodedLength, encodedLength));
|
|
950
|
+
}
|
|
951
|
+
newTypeError(actual) {
|
|
952
|
+
return new TypeError(`expected: string; actual: ${typeof actual}`);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
class ByteStringSerializer extends AbstractPrimitiveSerializer {
|
|
956
|
+
constructor() {
|
|
957
|
+
super(...arguments);
|
|
958
|
+
this.primitive = "bytes";
|
|
959
|
+
this.defaultValue = ByteString.EMPTY;
|
|
960
|
+
}
|
|
961
|
+
toJson(input, flavor) {
|
|
962
|
+
return flavor === "readable" ? "hex:" + input.toBase16() : input.toBase64();
|
|
963
|
+
}
|
|
964
|
+
fromJson(json) {
|
|
965
|
+
if (json === 0) {
|
|
966
|
+
return ByteString.EMPTY;
|
|
967
|
+
}
|
|
968
|
+
const string = json;
|
|
969
|
+
return string.startsWith("hex:")
|
|
970
|
+
? ByteString.fromBase16(string.substring(4))
|
|
971
|
+
: ByteString.fromBase64(string);
|
|
972
|
+
}
|
|
973
|
+
encode(input, stream) {
|
|
974
|
+
const { byteLength } = input;
|
|
975
|
+
if (byteLength) {
|
|
976
|
+
stream.writeUint8(245);
|
|
977
|
+
encodeUint32(byteLength, stream);
|
|
978
|
+
stream.putBytes(input);
|
|
979
|
+
}
|
|
980
|
+
else {
|
|
981
|
+
stream.writeUint8(244);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
decode(stream) {
|
|
985
|
+
const wire = stream.readUint8();
|
|
986
|
+
if (wire === 0 || wire === 244) {
|
|
987
|
+
return ByteString.EMPTY;
|
|
988
|
+
}
|
|
989
|
+
const lengthInBytes = decodeNumber(stream);
|
|
990
|
+
return ByteString.sliceOf(stream.buffer, stream.offset, (stream.offset += lengthInBytes));
|
|
991
|
+
}
|
|
992
|
+
isDefault(input) {
|
|
993
|
+
return !input.byteLength;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
class StructFieldImpl {
|
|
997
|
+
constructor(name, property, number, serializer) {
|
|
998
|
+
this.name = name;
|
|
999
|
+
this.property = property;
|
|
1000
|
+
this.number = number;
|
|
1001
|
+
this.serializer = serializer;
|
|
1002
|
+
}
|
|
1003
|
+
get type() {
|
|
1004
|
+
return this.serializer.typeDescriptor;
|
|
1005
|
+
}
|
|
1006
|
+
get(struct) {
|
|
1007
|
+
return Reflect.get(struct, this.property);
|
|
1008
|
+
}
|
|
1009
|
+
set(struct, value) {
|
|
1010
|
+
Reflect.set(struct, this.property, value);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
const textEncoder = new TextEncoder();
|
|
1014
|
+
const textDecoder = new TextDecoder();
|
|
1015
|
+
class ArraySerializerImpl extends AbstractSerializer {
|
|
1016
|
+
constructor(itemSerializer, keyExtractor) {
|
|
1017
|
+
super();
|
|
1018
|
+
this.itemSerializer = itemSerializer;
|
|
1019
|
+
this.keyExtractor = keyExtractor;
|
|
1020
|
+
this.kind = "array";
|
|
1021
|
+
this.defaultValue = exports._EMPTY_ARRAY;
|
|
1022
|
+
}
|
|
1023
|
+
toJson(input, flavor) {
|
|
1024
|
+
return input.map((e) => this.itemSerializer.toJson(e, flavor));
|
|
1025
|
+
}
|
|
1026
|
+
fromJson(json, keep) {
|
|
1027
|
+
if (json === 0) {
|
|
1028
|
+
return exports._EMPTY_ARRAY;
|
|
1029
|
+
}
|
|
1030
|
+
return freezeArray(json.map((e) => this.itemSerializer.fromJson(e, keep)));
|
|
1031
|
+
}
|
|
1032
|
+
encode(input, stream) {
|
|
1033
|
+
const { length } = input;
|
|
1034
|
+
if (length <= 3) {
|
|
1035
|
+
stream.writeUint8(246 + length);
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
stream.writeUint8(250);
|
|
1039
|
+
encodeUint32(length, stream);
|
|
1040
|
+
}
|
|
1041
|
+
const { itemSerializer } = this;
|
|
1042
|
+
for (let i = 0; i < input.length; ++i) {
|
|
1043
|
+
itemSerializer.encode(input[i], stream);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
decode(stream) {
|
|
1047
|
+
const wire = stream.readUint8();
|
|
1048
|
+
if (wire === 0 || wire === 246) {
|
|
1049
|
+
return exports._EMPTY_ARRAY;
|
|
1050
|
+
}
|
|
1051
|
+
const length = wire === 250 ? decodeNumber(stream) : wire - 246;
|
|
1052
|
+
const { itemSerializer } = this;
|
|
1053
|
+
const result = new Array(length);
|
|
1054
|
+
for (let i = 0; i < length; ++i) {
|
|
1055
|
+
result[i] = itemSerializer.decode(stream);
|
|
1056
|
+
}
|
|
1057
|
+
return freezeArray(result);
|
|
1058
|
+
}
|
|
1059
|
+
isDefault(input) {
|
|
1060
|
+
return !input.length;
|
|
1061
|
+
}
|
|
1062
|
+
get itemType() {
|
|
1063
|
+
return this.itemSerializer.typeDescriptor;
|
|
1064
|
+
}
|
|
1065
|
+
get typeSignature() {
|
|
1066
|
+
return {
|
|
1067
|
+
kind: "array",
|
|
1068
|
+
value: {
|
|
1069
|
+
item: this.itemSerializer.typeSignature,
|
|
1070
|
+
key_extractor: this.keyExtractor,
|
|
1071
|
+
},
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
addRecordDefinitionsTo(out) {
|
|
1075
|
+
this.itemSerializer.addRecordDefinitionsTo(out);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
class OptionalSerializerImpl extends AbstractSerializer {
|
|
1079
|
+
constructor(otherSerializer) {
|
|
1080
|
+
super();
|
|
1081
|
+
this.otherSerializer = otherSerializer;
|
|
1082
|
+
this.kind = "optional";
|
|
1083
|
+
this.defaultValue = null;
|
|
1084
|
+
}
|
|
1085
|
+
toJson(input, flavor) {
|
|
1086
|
+
return input !== null ? this.otherSerializer.toJson(input, flavor) : null;
|
|
1087
|
+
}
|
|
1088
|
+
fromJson(json, keep) {
|
|
1089
|
+
return json !== null ? this.otherSerializer.fromJson(json, keep) : null;
|
|
1090
|
+
}
|
|
1091
|
+
encode(input, stream) {
|
|
1092
|
+
if (input === null) {
|
|
1093
|
+
stream.writeUint8(255);
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
this.otherSerializer.encode(input, stream);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
decode(stream) {
|
|
1100
|
+
const wire = stream.dataView.getUint8(stream.offset);
|
|
1101
|
+
if (wire === 255) {
|
|
1102
|
+
++stream.offset;
|
|
1103
|
+
return null;
|
|
1104
|
+
}
|
|
1105
|
+
return this.otherSerializer.decode(stream);
|
|
1106
|
+
}
|
|
1107
|
+
isDefault(input) {
|
|
1108
|
+
return input === null;
|
|
1109
|
+
}
|
|
1110
|
+
get otherType() {
|
|
1111
|
+
return this.otherSerializer.typeDescriptor;
|
|
1112
|
+
}
|
|
1113
|
+
get typeSignature() {
|
|
1114
|
+
return {
|
|
1115
|
+
kind: "optional",
|
|
1116
|
+
value: this.otherSerializer.typeSignature,
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
addRecordDefinitionsTo(out) {
|
|
1120
|
+
this.otherSerializer.addRecordDefinitionsTo(out);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
const primitiveSerializers = {
|
|
1124
|
+
bool: new BoolSerializer(),
|
|
1125
|
+
int32: int32_Serializer,
|
|
1126
|
+
int64: new Int64Serializer(),
|
|
1127
|
+
uint64: new Uint64Serializer(),
|
|
1128
|
+
float32: new Float32Serializer(),
|
|
1129
|
+
float64: new Float64Serializer(),
|
|
1130
|
+
timestamp: new TimestampSerializer(),
|
|
1131
|
+
string: new StringSerializer(),
|
|
1132
|
+
bytes: new ByteStringSerializer(),
|
|
1133
|
+
};
|
|
1134
|
+
function decodeUnused(stream) {
|
|
1135
|
+
const wire = stream.readUint8();
|
|
1136
|
+
if (wire < 232) {
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
switch (wire - 232) {
|
|
1140
|
+
case 0: // uint16
|
|
1141
|
+
case 4: // uint16 - 65536
|
|
1142
|
+
stream.offset += 2;
|
|
1143
|
+
break;
|
|
1144
|
+
case 1: // uint32
|
|
1145
|
+
case 5: // int32
|
|
1146
|
+
case 8: // float32
|
|
1147
|
+
stream.offset += 4;
|
|
1148
|
+
break;
|
|
1149
|
+
case 2: // uint64
|
|
1150
|
+
case 6: // int64
|
|
1151
|
+
case 7: // uint64 timestamp
|
|
1152
|
+
case 9: // float64
|
|
1153
|
+
stream.offset += 8;
|
|
1154
|
+
break;
|
|
1155
|
+
case 3: // uint8 - 256
|
|
1156
|
+
++stream.offset;
|
|
1157
|
+
break;
|
|
1158
|
+
case 11: // string
|
|
1159
|
+
case 13: {
|
|
1160
|
+
// bytes
|
|
1161
|
+
const length = decodeNumber(stream);
|
|
1162
|
+
stream.offset += length;
|
|
1163
|
+
break;
|
|
1164
|
+
}
|
|
1165
|
+
case 15: // array length==1
|
|
1166
|
+
case 19: // enum value kind==1
|
|
1167
|
+
case 20: // enum value kind==2
|
|
1168
|
+
case 21: // enum value kind==3
|
|
1169
|
+
case 22: // enum value kind==4
|
|
1170
|
+
decodeUnused(stream);
|
|
1171
|
+
break;
|
|
1172
|
+
case 16: // array length==2
|
|
1173
|
+
decodeUnused(stream);
|
|
1174
|
+
decodeUnused(stream);
|
|
1175
|
+
break;
|
|
1176
|
+
case 17: // array length==3
|
|
1177
|
+
decodeUnused(stream);
|
|
1178
|
+
decodeUnused(stream);
|
|
1179
|
+
decodeUnused(stream);
|
|
1180
|
+
break;
|
|
1181
|
+
case 18: {
|
|
1182
|
+
// array length==N
|
|
1183
|
+
const length = decodeNumber(stream);
|
|
1184
|
+
for (let i = 0; i < length; ++i) {
|
|
1185
|
+
decodeUnused(stream);
|
|
1186
|
+
}
|
|
1187
|
+
break;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
class AbstractRecordSerializer extends AbstractSerializer {
|
|
1192
|
+
constructor() {
|
|
1193
|
+
super(...arguments);
|
|
1194
|
+
/** Uniquely identifies this record serializer. */
|
|
1195
|
+
this.token = Symbol();
|
|
1196
|
+
this.name = "";
|
|
1197
|
+
this.modulePath = "";
|
|
1198
|
+
this.removedNumbers = new Set();
|
|
1199
|
+
}
|
|
1200
|
+
init(name, modulePath, parentType, fieldsOrVariants, removedNumbers) {
|
|
1201
|
+
this.name = name;
|
|
1202
|
+
this.modulePath = modulePath;
|
|
1203
|
+
this.parentType = parentType;
|
|
1204
|
+
this.removedNumbers = new Set(removedNumbers);
|
|
1205
|
+
this.registerFieldsOrVariants(fieldsOrVariants);
|
|
1206
|
+
this.initialized = true;
|
|
1207
|
+
freezeDeeply(this);
|
|
1208
|
+
}
|
|
1209
|
+
get qualifiedName() {
|
|
1210
|
+
const { name, parentType } = this;
|
|
1211
|
+
return parentType ? `${parentType.name}.${name}` : name;
|
|
1212
|
+
}
|
|
1213
|
+
addRecordDefinitionsTo(out) {
|
|
1214
|
+
const recordId = `${this.modulePath}:${this.qualifiedName}`;
|
|
1215
|
+
if (out[recordId]) {
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
const recordDefinition = this.makeRecordDefinition(recordId);
|
|
1219
|
+
if (this.removedNumbers.size) {
|
|
1220
|
+
recordDefinition.removed_numbers = [...this.removedNumbers];
|
|
1221
|
+
}
|
|
1222
|
+
out[recordId] = recordDefinition;
|
|
1223
|
+
for (const dependency of this.dependencies()) {
|
|
1224
|
+
dependency.addRecordDefinitionsTo(out);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
/** Unrecognized fields found when deserializing a struct. */
|
|
1229
|
+
class UnrecognizedFields {
|
|
1230
|
+
constructor(
|
|
1231
|
+
/** Uniquely identifies the struct. */
|
|
1232
|
+
token,
|
|
1233
|
+
/** Total number of fields in the struct. */
|
|
1234
|
+
totalSlots, json, bytes) {
|
|
1235
|
+
this.token = token;
|
|
1236
|
+
this.totalSlots = totalSlots;
|
|
1237
|
+
this.json = json;
|
|
1238
|
+
this.bytes = bytes;
|
|
1239
|
+
Object.freeze(this);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
class StructSerializerImpl extends AbstractRecordSerializer {
|
|
1243
|
+
constructor(defaultValue, createFn, newMutableFn) {
|
|
1244
|
+
super();
|
|
1245
|
+
this.defaultValue = defaultValue;
|
|
1246
|
+
this.createFn = createFn;
|
|
1247
|
+
this.newMutableFn = newMutableFn;
|
|
1248
|
+
this.kind = "struct";
|
|
1249
|
+
// Fields in the order they appear in the `.skir` file.
|
|
1250
|
+
this.fields = [];
|
|
1251
|
+
this.fieldMapping = {};
|
|
1252
|
+
// Fields sorted by number in descending order.
|
|
1253
|
+
this.reversedFields = [];
|
|
1254
|
+
// This is *not* a dense array, missing slots correspond to removed fields.
|
|
1255
|
+
this.slots = [];
|
|
1256
|
+
this.recognizedSlots = 0;
|
|
1257
|
+
// Contains one zero for every field number.
|
|
1258
|
+
this.zeros = [];
|
|
1259
|
+
this.initializerTemplate = {};
|
|
1260
|
+
}
|
|
1261
|
+
toJson(input, flavor) {
|
|
1262
|
+
if (input === this.defaultValue) {
|
|
1263
|
+
return flavor === "readable" ? {} : [];
|
|
1264
|
+
}
|
|
1265
|
+
if (flavor === "readable") {
|
|
1266
|
+
const { fields } = this;
|
|
1267
|
+
const result = {};
|
|
1268
|
+
for (const field of fields) {
|
|
1269
|
+
const { serializer } = field;
|
|
1270
|
+
const value = input[field.property];
|
|
1271
|
+
if (field.serializer.isDefault(value)) {
|
|
1272
|
+
continue;
|
|
1273
|
+
}
|
|
1274
|
+
result[field.name] = serializer.toJson(value, flavor);
|
|
1275
|
+
}
|
|
1276
|
+
return result;
|
|
1277
|
+
}
|
|
1278
|
+
else {
|
|
1279
|
+
// Dense flavor.
|
|
1280
|
+
const { slots } = this;
|
|
1281
|
+
let result;
|
|
1282
|
+
const unrecognizedFields = //
|
|
1283
|
+
input["^"];
|
|
1284
|
+
if (unrecognizedFields &&
|
|
1285
|
+
unrecognizedFields.json &&
|
|
1286
|
+
unrecognizedFields.token === this.token) {
|
|
1287
|
+
// We'll need to copy the unrecognized fields to the JSON.
|
|
1288
|
+
result = this.zeros.concat(unrecognizedFields.json);
|
|
1289
|
+
for (const field of this.fields) {
|
|
1290
|
+
result[field.number] = field.serializer.toJson(input[field.property], flavor);
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
else {
|
|
1294
|
+
result = [];
|
|
1295
|
+
const arrayLength = this.getArrayLength(input);
|
|
1296
|
+
for (let i = 0; i < arrayLength; ++i) {
|
|
1297
|
+
const field = slots[i];
|
|
1298
|
+
result[i] = field
|
|
1299
|
+
? field.serializer.toJson(input[field.property], flavor)
|
|
1300
|
+
: 0;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
return result;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
fromJson(json, keep) {
|
|
1307
|
+
if (!json) {
|
|
1308
|
+
return this.defaultValue;
|
|
1309
|
+
}
|
|
1310
|
+
const initializer = Object.assign({}, this.initializerTemplate);
|
|
1311
|
+
if (json instanceof Array) {
|
|
1312
|
+
const { slots, recognizedSlots } = this;
|
|
1313
|
+
// Dense flavor.
|
|
1314
|
+
if (json.length > recognizedSlots) {
|
|
1315
|
+
// We have some unrecognized fields.
|
|
1316
|
+
if (keep) {
|
|
1317
|
+
const unrecognizedFields = new UnrecognizedFields(this.token, json.length, copyJson(json.slice(recognizedSlots)));
|
|
1318
|
+
initializer["^"] = unrecognizedFields;
|
|
1319
|
+
}
|
|
1320
|
+
// Now that we have stored the unrecognized fields in `initializer`, we
|
|
1321
|
+
// can remove them from `json`.
|
|
1322
|
+
json = json.slice(0, recognizedSlots);
|
|
1323
|
+
}
|
|
1324
|
+
for (let i = 0; i < json.length && i < slots.length; ++i) {
|
|
1325
|
+
const field = slots[i];
|
|
1326
|
+
if (field) {
|
|
1327
|
+
initializer[field.property] = field.serializer.fromJson(json[i], keep);
|
|
1328
|
+
}
|
|
1329
|
+
// Else the field was removed.
|
|
1330
|
+
}
|
|
1331
|
+
return this.createFn(initializer);
|
|
1332
|
+
}
|
|
1333
|
+
else if (json instanceof Object) {
|
|
1334
|
+
// Readable flavor.
|
|
1335
|
+
const { fieldMapping } = this;
|
|
1336
|
+
for (const name in json) {
|
|
1337
|
+
const field = fieldMapping[name];
|
|
1338
|
+
if (field) {
|
|
1339
|
+
initializer[field.property] = field.serializer.fromJson(json[name], keep);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
return this.createFn(initializer);
|
|
1343
|
+
}
|
|
1344
|
+
throw TypeError();
|
|
1345
|
+
}
|
|
1346
|
+
encode(input, stream) {
|
|
1347
|
+
// Total number of slots to write. Includes removed and unrecognized fields.
|
|
1348
|
+
let totalSlots;
|
|
1349
|
+
let recognizedSlots;
|
|
1350
|
+
let unrecognizedBytes;
|
|
1351
|
+
const unrecognizedFields = input["^"];
|
|
1352
|
+
if (unrecognizedFields &&
|
|
1353
|
+
unrecognizedFields.bytes &&
|
|
1354
|
+
unrecognizedFields.token === this.token) {
|
|
1355
|
+
totalSlots = unrecognizedFields.totalSlots;
|
|
1356
|
+
recognizedSlots = this.recognizedSlots;
|
|
1357
|
+
unrecognizedBytes = unrecognizedFields.bytes;
|
|
1358
|
+
}
|
|
1359
|
+
else {
|
|
1360
|
+
// No unrecognized fields.
|
|
1361
|
+
totalSlots = recognizedSlots = this.getArrayLength(input);
|
|
1362
|
+
}
|
|
1363
|
+
if (totalSlots <= 3) {
|
|
1364
|
+
stream.writeUint8(246 + totalSlots);
|
|
1365
|
+
}
|
|
1366
|
+
else {
|
|
1367
|
+
stream.writeUint8(250);
|
|
1368
|
+
encodeUint32(totalSlots, stream);
|
|
1369
|
+
}
|
|
1370
|
+
const { slots } = this;
|
|
1371
|
+
for (let i = 0; i < recognizedSlots; ++i) {
|
|
1372
|
+
const field = slots[i];
|
|
1373
|
+
if (field) {
|
|
1374
|
+
field.serializer.encode(input[field.property], stream);
|
|
1375
|
+
}
|
|
1376
|
+
else {
|
|
1377
|
+
// Append '0' if the field was removed.
|
|
1378
|
+
stream.writeUint8(0);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
if (unrecognizedBytes) {
|
|
1382
|
+
// Copy the unrecognized fields.
|
|
1383
|
+
stream.putBytes(unrecognizedBytes);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
decode(stream) {
|
|
1387
|
+
const wire = stream.readUint8();
|
|
1388
|
+
if (wire === 0 || wire === 246) {
|
|
1389
|
+
return this.defaultValue;
|
|
1390
|
+
}
|
|
1391
|
+
const initializer = Object.assign({}, this.initializerTemplate);
|
|
1392
|
+
const encodedSlots = wire === 250 ? decodeNumber(stream) : wire - 246;
|
|
1393
|
+
const { slots, recognizedSlots } = this;
|
|
1394
|
+
// Do not read more slots than the number of recognized slots.
|
|
1395
|
+
for (let i = 0; i < encodedSlots && i < recognizedSlots; ++i) {
|
|
1396
|
+
const field = slots[i];
|
|
1397
|
+
if (field) {
|
|
1398
|
+
initializer[field.property] = field.serializer.decode(stream);
|
|
1399
|
+
}
|
|
1400
|
+
else {
|
|
1401
|
+
// The field was removed.
|
|
1402
|
+
decodeUnused(stream);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
if (encodedSlots > recognizedSlots) {
|
|
1406
|
+
// We have some unrecognized fields.
|
|
1407
|
+
const start = stream.offset;
|
|
1408
|
+
for (let i = recognizedSlots; i < encodedSlots; ++i) {
|
|
1409
|
+
decodeUnused(stream);
|
|
1410
|
+
}
|
|
1411
|
+
if (stream.keepUnrecognizedValues) {
|
|
1412
|
+
const end = stream.offset;
|
|
1413
|
+
const unrecognizedBytes = ByteString.sliceOf(stream.buffer, start, end);
|
|
1414
|
+
const unrecognizedFields = new UnrecognizedFields(this.token, encodedSlots, undefined, unrecognizedBytes);
|
|
1415
|
+
initializer["^"] = unrecognizedFields;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
return this.createFn(initializer);
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Returns the length of the JSON array for the given input, which is also the
|
|
1422
|
+
* number of slots and includes removed fields.
|
|
1423
|
+
* Assumes that `input` does not contain unrecognized fields.
|
|
1424
|
+
*/
|
|
1425
|
+
getArrayLength(input) {
|
|
1426
|
+
const { reversedFields } = this;
|
|
1427
|
+
for (let i = 0; i < reversedFields.length; ++i) {
|
|
1428
|
+
const field = reversedFields[i];
|
|
1429
|
+
const isDefault = //
|
|
1430
|
+
field.serializer.isDefault(input[field.property]);
|
|
1431
|
+
if (!isDefault) {
|
|
1432
|
+
return field.number + 1;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
return 0;
|
|
1436
|
+
}
|
|
1437
|
+
isDefault(input) {
|
|
1438
|
+
if (input === this.defaultValue) {
|
|
1439
|
+
return true;
|
|
1440
|
+
}
|
|
1441
|
+
// It's possible for a value of type T to be equal to T.DEFAULT but to not
|
|
1442
|
+
// be the reference to T.DEFAULT.
|
|
1443
|
+
if (input["^"]) {
|
|
1444
|
+
return false;
|
|
1445
|
+
}
|
|
1446
|
+
return this.fields.every((f) => f.serializer.isDefault(input[f.property]));
|
|
1447
|
+
}
|
|
1448
|
+
get typeSignature() {
|
|
1449
|
+
return {
|
|
1450
|
+
kind: "record",
|
|
1451
|
+
value: `${this.modulePath}:${this.qualifiedName}`,
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
getField(key) {
|
|
1455
|
+
return this.fieldMapping[key];
|
|
1456
|
+
}
|
|
1457
|
+
newMutable(initializer) {
|
|
1458
|
+
return this.newMutableFn(initializer);
|
|
1459
|
+
}
|
|
1460
|
+
registerFieldsOrVariants(fields) {
|
|
1461
|
+
for (const field of fields) {
|
|
1462
|
+
const { name, number, property } = field;
|
|
1463
|
+
this.fields.push(field);
|
|
1464
|
+
this.slots[number] = field;
|
|
1465
|
+
this.fieldMapping[name] = field;
|
|
1466
|
+
this.fieldMapping[property] = field;
|
|
1467
|
+
this.fieldMapping[number] = field;
|
|
1468
|
+
this.initializerTemplate[property] = this.defaultValue[field.property];
|
|
1469
|
+
}
|
|
1470
|
+
// Removed numbers count as recognized slots.
|
|
1471
|
+
this.recognizedSlots =
|
|
1472
|
+
Math.max(this.slots.length - 1, ...this.removedNumbers) + 1;
|
|
1473
|
+
this.zeros.push(...Array(this.recognizedSlots).fill(0));
|
|
1474
|
+
this.reversedFields = [...this.fields].sort((a, b) => b.number - a.number);
|
|
1475
|
+
}
|
|
1476
|
+
makeRecordDefinition(recordId) {
|
|
1477
|
+
return {
|
|
1478
|
+
kind: "struct",
|
|
1479
|
+
id: recordId,
|
|
1480
|
+
fields: this.fields.map((f) => ({
|
|
1481
|
+
name: f.name,
|
|
1482
|
+
number: f.number,
|
|
1483
|
+
type: f.serializer.typeSignature,
|
|
1484
|
+
})),
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
dependencies() {
|
|
1488
|
+
return this.fields.map((f) => f.serializer);
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
class UnrecognizedEnum {
|
|
1492
|
+
constructor(token, json, bytes) {
|
|
1493
|
+
this.token = token;
|
|
1494
|
+
this.json = json;
|
|
1495
|
+
this.bytes = bytes;
|
|
1496
|
+
Object.freeze(this);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
class EnumWrapperVariantImpl {
|
|
1500
|
+
constructor(name, number, serializer, createFn) {
|
|
1501
|
+
this.name = name;
|
|
1502
|
+
this.number = number;
|
|
1503
|
+
this.serializer = serializer;
|
|
1504
|
+
this.createFn = createFn;
|
|
1505
|
+
}
|
|
1506
|
+
get type() {
|
|
1507
|
+
return this.serializer.typeDescriptor;
|
|
1508
|
+
}
|
|
1509
|
+
get(e) {
|
|
1510
|
+
return e.kind === this.name
|
|
1511
|
+
? e.value
|
|
1512
|
+
: undefined;
|
|
1513
|
+
}
|
|
1514
|
+
wrap(value) {
|
|
1515
|
+
return this.createFn({ kind: this.name, value: value });
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
class EnumSerializerImpl extends AbstractRecordSerializer {
|
|
1519
|
+
constructor(createFn) {
|
|
1520
|
+
super();
|
|
1521
|
+
this.createFn = createFn;
|
|
1522
|
+
this.kind = "enum";
|
|
1523
|
+
this.variants = [];
|
|
1524
|
+
this.variantMapping = {};
|
|
1525
|
+
this.defaultValue = createFn("?");
|
|
1526
|
+
}
|
|
1527
|
+
toJson(input, flavor) {
|
|
1528
|
+
const unrecognized = input["^"];
|
|
1529
|
+
if (unrecognized &&
|
|
1530
|
+
unrecognized.json &&
|
|
1531
|
+
unrecognized.token === this.token) {
|
|
1532
|
+
// Unrecognized variant.
|
|
1533
|
+
return unrecognized.json;
|
|
1534
|
+
}
|
|
1535
|
+
const kind = input.kind;
|
|
1536
|
+
if (kind === "?") {
|
|
1537
|
+
return flavor === "readable" ? "?" : 0;
|
|
1538
|
+
}
|
|
1539
|
+
const variant = this.variantMapping[kind];
|
|
1540
|
+
const { serializer } = variant;
|
|
1541
|
+
if (serializer) {
|
|
1542
|
+
const value = input.value;
|
|
1543
|
+
if (flavor === "readable") {
|
|
1544
|
+
return {
|
|
1545
|
+
kind: variant.name,
|
|
1546
|
+
value: serializer.toJson(value, flavor),
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
else {
|
|
1550
|
+
// Dense flavor.
|
|
1551
|
+
return [variant.number, serializer.toJson(value, flavor)];
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
else {
|
|
1555
|
+
// A constant variant.
|
|
1556
|
+
return flavor === "readable" ? variant.name : variant.number;
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
fromJson(json, keep) {
|
|
1560
|
+
const isNumber = typeof json === "number";
|
|
1561
|
+
if (isNumber || typeof json === "string") {
|
|
1562
|
+
const variant = this.variantMapping[isNumber ? json : String(json)];
|
|
1563
|
+
if (!variant) {
|
|
1564
|
+
// Check if the variant was removed, in which case we want to return
|
|
1565
|
+
// UNKNOWN, or is unrecognized.
|
|
1566
|
+
return !keep || (isNumber && this.removedNumbers.has(json))
|
|
1567
|
+
? this.defaultValue
|
|
1568
|
+
: this.createFn(new UnrecognizedEnum(this.token, copyJson(json)));
|
|
1569
|
+
}
|
|
1570
|
+
if (variant.serializer) {
|
|
1571
|
+
throw new Error(`refers to a wrapper variant: ${json}`);
|
|
1572
|
+
}
|
|
1573
|
+
return variant.constant;
|
|
1574
|
+
}
|
|
1575
|
+
let variantKey;
|
|
1576
|
+
let valueAsJson;
|
|
1577
|
+
if (json instanceof Array) {
|
|
1578
|
+
variantKey = json[0];
|
|
1579
|
+
valueAsJson = json[1];
|
|
1580
|
+
}
|
|
1581
|
+
else if (json instanceof Object) {
|
|
1582
|
+
variantKey = json["kind"];
|
|
1583
|
+
valueAsJson = json["value"];
|
|
1584
|
+
}
|
|
1585
|
+
else {
|
|
1586
|
+
throw TypeError();
|
|
1587
|
+
}
|
|
1588
|
+
const variant = this.variantMapping[variantKey];
|
|
1589
|
+
if (!variant) {
|
|
1590
|
+
// Check if the variant was removed, in which case we want to return
|
|
1591
|
+
// UNKNOWN, or is unrecognized.
|
|
1592
|
+
return !keep ||
|
|
1593
|
+
(typeof variantKey === "number" && this.removedNumbers.has(variantKey))
|
|
1594
|
+
? this.defaultValue
|
|
1595
|
+
: this.createFn(new UnrecognizedEnum(this.token, copyJson(json), undefined));
|
|
1596
|
+
}
|
|
1597
|
+
const { serializer } = variant;
|
|
1598
|
+
if (!serializer) {
|
|
1599
|
+
throw new Error(`refers to a constant variant: ${json}`);
|
|
1600
|
+
}
|
|
1601
|
+
return variant.wrap(serializer.fromJson(valueAsJson, keep));
|
|
1602
|
+
}
|
|
1603
|
+
encode(input, stream) {
|
|
1604
|
+
const unrecognized = //
|
|
1605
|
+
input["^"];
|
|
1606
|
+
if (unrecognized &&
|
|
1607
|
+
unrecognized.bytes &&
|
|
1608
|
+
unrecognized.token === this.token) {
|
|
1609
|
+
// Unrecognized variant.
|
|
1610
|
+
stream.putBytes(unrecognized.bytes);
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1613
|
+
const kind = input.kind;
|
|
1614
|
+
if (kind === "?") {
|
|
1615
|
+
stream.writeUint8(0);
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
const variant = this.variantMapping[kind];
|
|
1619
|
+
const { number, serializer } = variant;
|
|
1620
|
+
if (serializer) {
|
|
1621
|
+
// A wrapper variant.
|
|
1622
|
+
const value = input.value;
|
|
1623
|
+
if (number < 5) {
|
|
1624
|
+
// The number can't be 0 or else kind == "?".
|
|
1625
|
+
stream.writeUint8(250 + number);
|
|
1626
|
+
}
|
|
1627
|
+
else {
|
|
1628
|
+
stream.writeUint8(248);
|
|
1629
|
+
encodeUint32(number, stream);
|
|
1630
|
+
}
|
|
1631
|
+
serializer.encode(value, stream);
|
|
1632
|
+
}
|
|
1633
|
+
else {
|
|
1634
|
+
// A constant field.
|
|
1635
|
+
encodeUint32(number, stream);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
decode(stream) {
|
|
1639
|
+
const startOffset = stream.offset;
|
|
1640
|
+
const wire = stream.dataView.getUint8(startOffset);
|
|
1641
|
+
if (wire < 242) {
|
|
1642
|
+
// A number
|
|
1643
|
+
const number = decodeNumber(stream);
|
|
1644
|
+
const variant = this.variantMapping[number];
|
|
1645
|
+
if (!variant) {
|
|
1646
|
+
// Check if the variant was removed, in which case we want to return
|
|
1647
|
+
// UNKNOWN, or is unrecognized.
|
|
1648
|
+
if (!stream.keepUnrecognizedValues || this.removedNumbers.has(number)) {
|
|
1649
|
+
return this.defaultValue;
|
|
1650
|
+
}
|
|
1651
|
+
else {
|
|
1652
|
+
const { offset } = stream;
|
|
1653
|
+
const bytes = ByteString.sliceOf(stream.buffer, startOffset, offset);
|
|
1654
|
+
return this.createFn(new UnrecognizedEnum(this.token, undefined, bytes));
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
if (variant.serializer) {
|
|
1658
|
+
throw new Error(`refers to a wrapper variant: ${number}`);
|
|
1659
|
+
}
|
|
1660
|
+
return variant.constant;
|
|
1661
|
+
}
|
|
1662
|
+
else {
|
|
1663
|
+
++stream.offset;
|
|
1664
|
+
const number = wire === 248 ? decodeNumber(stream) : wire - 250;
|
|
1665
|
+
const variant = this.variantMapping[number];
|
|
1666
|
+
if (!variant) {
|
|
1667
|
+
decodeUnused(stream);
|
|
1668
|
+
// Check if the variant was removed, in which case we want to return
|
|
1669
|
+
// UNKNOWN, or is unrecognized.
|
|
1670
|
+
if (!stream.keepUnrecognizedValues || this.removedNumbers.has(number)) {
|
|
1671
|
+
return this.defaultValue;
|
|
1672
|
+
}
|
|
1673
|
+
else {
|
|
1674
|
+
const { offset } = stream;
|
|
1675
|
+
const bytes = ByteString.sliceOf(stream.buffer, startOffset, offset);
|
|
1676
|
+
return this.createFn(new UnrecognizedEnum(this.token, undefined, bytes));
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
const { serializer } = variant;
|
|
1680
|
+
if (!serializer) {
|
|
1681
|
+
throw new Error(`refers to a constant variant: ${number}`);
|
|
1682
|
+
}
|
|
1683
|
+
return variant.wrap(serializer.decode(stream));
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
get typeSignature() {
|
|
1687
|
+
return {
|
|
1688
|
+
kind: "record",
|
|
1689
|
+
value: `${this.modulePath}:${this.qualifiedName}`,
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
isDefault(input) {
|
|
1693
|
+
return input.kind === "?" && !input["^"];
|
|
1694
|
+
}
|
|
1695
|
+
getVariant(key) {
|
|
1696
|
+
return this.variantMapping[key];
|
|
1697
|
+
}
|
|
1698
|
+
registerFieldsOrVariants(fields) {
|
|
1699
|
+
for (const field of fields) {
|
|
1700
|
+
this.variants.push(field);
|
|
1701
|
+
this.variantMapping[field.name] = field;
|
|
1702
|
+
this.variantMapping[field.number] = field;
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
makeRecordDefinition(recordId) {
|
|
1706
|
+
return {
|
|
1707
|
+
kind: "enum",
|
|
1708
|
+
id: recordId,
|
|
1709
|
+
variants: this.variants
|
|
1710
|
+
// Skip the UNKNOWN variant.
|
|
1711
|
+
.filter((f) => f.number)
|
|
1712
|
+
.map((f) => {
|
|
1713
|
+
var _a;
|
|
1714
|
+
const result = {
|
|
1715
|
+
name: f.name,
|
|
1716
|
+
number: f.number,
|
|
1717
|
+
};
|
|
1718
|
+
const type = (_a = f === null || f === void 0 ? void 0 : f.serializer) === null || _a === void 0 ? void 0 : _a.typeSignature;
|
|
1719
|
+
return type ? Object.assign(Object.assign({}, result), { type: type }) : result;
|
|
1720
|
+
}),
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1723
|
+
dependencies() {
|
|
1724
|
+
const result = [];
|
|
1725
|
+
for (const f of this.variants) {
|
|
1726
|
+
if (f.serializer) {
|
|
1727
|
+
result.push(f.serializer);
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
return result;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
function copyJson(input) {
|
|
1734
|
+
if (input instanceof Array) {
|
|
1735
|
+
return Object.freeze(input.map(copyJson));
|
|
1736
|
+
}
|
|
1737
|
+
else if (input instanceof Object) {
|
|
1738
|
+
return Object.freeze(Object.fromEntries(Object.entries(input).map((k, v) => [k, copyJson(v)])));
|
|
1739
|
+
}
|
|
1740
|
+
// A boolean, a number, a string or null.
|
|
1741
|
+
return input;
|
|
1742
|
+
}
|
|
1743
|
+
function freezeDeeply(o) {
|
|
1744
|
+
if (!(o instanceof Object)) {
|
|
1745
|
+
return;
|
|
1746
|
+
}
|
|
1747
|
+
if (o instanceof _FrozenBase || o instanceof _EnumBase) {
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
if (o instanceof AbstractRecordSerializer && !o.initialized) {
|
|
1751
|
+
return;
|
|
1752
|
+
}
|
|
1753
|
+
if (Object.isFrozen(o)) {
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
Object.freeze(o);
|
|
1757
|
+
for (const v of Object.values(o)) {
|
|
1758
|
+
freezeDeeply(v);
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
const frozenArrayRegistry = new WeakMap();
|
|
1762
|
+
function freezeArray(array) {
|
|
1763
|
+
if (!frozenArrayRegistry.has(array)) {
|
|
1764
|
+
frozenArrayRegistry.set(Object.freeze(array), {});
|
|
1765
|
+
}
|
|
1766
|
+
return array;
|
|
1767
|
+
}
|
|
1768
|
+
exports._EMPTY_ARRAY = freezeArray([]);
|
|
1769
|
+
function _toFrozenArray(initializers, itemToFrozenFn) {
|
|
1770
|
+
if (!initializers.length) {
|
|
1771
|
+
return exports._EMPTY_ARRAY;
|
|
1772
|
+
}
|
|
1773
|
+
if (frozenArrayRegistry.has(initializers)) {
|
|
1774
|
+
// No need to make a copy: the given array is already deeply-frozen.
|
|
1775
|
+
return initializers;
|
|
1776
|
+
}
|
|
1777
|
+
const ret = Object.freeze(itemToFrozenFn
|
|
1778
|
+
? initializers.map(itemToFrozenFn)
|
|
1779
|
+
: initializers.slice());
|
|
1780
|
+
frozenArrayRegistry.set(ret, {});
|
|
1781
|
+
return ret;
|
|
1782
|
+
}
|
|
1783
|
+
exports._toFrozenArray = _toFrozenArray;
|
|
1784
|
+
const PRIVATE_KEY = Symbol();
|
|
1785
|
+
function forPrivateUseError(t) {
|
|
1786
|
+
const clazz = Object.getPrototypeOf(t).constructor;
|
|
1787
|
+
const { qualifiedName } = clazz.serializer;
|
|
1788
|
+
return Error([
|
|
1789
|
+
"Do not call the constructor directly; ",
|
|
1790
|
+
`instead, call ${qualifiedName}.create(...)`,
|
|
1791
|
+
].join(""));
|
|
1792
|
+
}
|
|
1793
|
+
class _FrozenBase {
|
|
1794
|
+
constructor(privateKey) {
|
|
1795
|
+
if (privateKey !== PRIVATE_KEY) {
|
|
1796
|
+
throw forPrivateUseError(this);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
toMutable() {
|
|
1800
|
+
return new (Object.getPrototypeOf(this).constructor.Mutable)(this);
|
|
1801
|
+
}
|
|
1802
|
+
toFrozen() {
|
|
1803
|
+
return this;
|
|
1804
|
+
}
|
|
1805
|
+
toString() {
|
|
1806
|
+
return toStringImpl(this);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
exports._FrozenBase = _FrozenBase;
|
|
1810
|
+
class _EnumBase {
|
|
1811
|
+
constructor(privateKey, kind, value, unrecognized) {
|
|
1812
|
+
this.kind = kind;
|
|
1813
|
+
this.value = value;
|
|
1814
|
+
if (privateKey !== PRIVATE_KEY) {
|
|
1815
|
+
throw forPrivateUseError(this);
|
|
1816
|
+
}
|
|
1817
|
+
if (unrecognized) {
|
|
1818
|
+
if (!(unrecognized instanceof UnrecognizedEnum)) {
|
|
1819
|
+
throw new TypeError();
|
|
1820
|
+
}
|
|
1821
|
+
this["^"] = unrecognized;
|
|
1822
|
+
}
|
|
1823
|
+
Object.freeze(this);
|
|
1824
|
+
}
|
|
1825
|
+
toString() {
|
|
1826
|
+
return toStringImpl(this);
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
exports._EnumBase = _EnumBase;
|
|
1830
|
+
// The TypeScript compiler complains if we define the property within the class.
|
|
1831
|
+
Object.defineProperty(_EnumBase.prototype, "union", {
|
|
1832
|
+
get: function () {
|
|
1833
|
+
return this;
|
|
1834
|
+
},
|
|
1835
|
+
});
|
|
1836
|
+
function toStringImpl(value) {
|
|
1837
|
+
const serializer = Object.getPrototypeOf(value).constructor
|
|
1838
|
+
.serializer;
|
|
1839
|
+
return serializer.toJsonCode(value, "readable");
|
|
1840
|
+
}
|
|
1841
|
+
/** Sends RPCs to a skir service. */
|
|
1842
|
+
class ServiceClient {
|
|
1843
|
+
constructor(serviceUrl, getRequestMetadata = () => ({})) {
|
|
1844
|
+
this.serviceUrl = serviceUrl;
|
|
1845
|
+
this.getRequestMetadata = getRequestMetadata;
|
|
1846
|
+
const url = new URL(serviceUrl);
|
|
1847
|
+
if (url.search) {
|
|
1848
|
+
throw new Error("Service URL must not contain a query string");
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
/** Invokes the given method on the remote server through an RPC. */
|
|
1852
|
+
invokeRemote(method, request, httpMethod = "POST") {
|
|
1853
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1854
|
+
this.lastRespHeaders = undefined;
|
|
1855
|
+
const requestJson = method.requestSerializer.toJsonCode(request);
|
|
1856
|
+
const requestBody = [method.name, method.number, "", requestJson].join(":");
|
|
1857
|
+
const requestInit = Object.assign({}, (yield Promise.resolve(this.getRequestMetadata(method))));
|
|
1858
|
+
const url = new URL(this.serviceUrl);
|
|
1859
|
+
requestInit.method = httpMethod;
|
|
1860
|
+
if (httpMethod === "POST") {
|
|
1861
|
+
requestInit.body = requestBody;
|
|
1862
|
+
}
|
|
1863
|
+
else {
|
|
1864
|
+
url.search = requestBody.replace(/%/g, "%25");
|
|
1865
|
+
}
|
|
1866
|
+
const httpResponse = yield fetch(url, requestInit);
|
|
1867
|
+
this.lastRespHeaders = httpResponse.headers;
|
|
1868
|
+
const responseData = yield httpResponse.blob();
|
|
1869
|
+
if (httpResponse.ok) {
|
|
1870
|
+
const jsonCode = yield responseData.text();
|
|
1871
|
+
return method.responseSerializer.fromJsonCode(jsonCode, "keep-unrecognized-values");
|
|
1872
|
+
}
|
|
1873
|
+
else {
|
|
1874
|
+
let message = "";
|
|
1875
|
+
if (/text\/plain\b/.test(responseData.type)) {
|
|
1876
|
+
message = `: ${yield responseData.text()}`;
|
|
1877
|
+
}
|
|
1878
|
+
throw new Error(`HTTP status ${httpResponse.status}${message}`);
|
|
1879
|
+
}
|
|
1880
|
+
});
|
|
1881
|
+
}
|
|
1882
|
+
get lastResponseHeaders() {
|
|
1883
|
+
return this.lastRespHeaders;
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
exports.ServiceClient = ServiceClient;
|
|
1887
|
+
/** Raw response returned by the server. */
|
|
1888
|
+
class RawResponse {
|
|
1889
|
+
constructor(data, type) {
|
|
1890
|
+
this.data = data;
|
|
1891
|
+
this.type = type;
|
|
1892
|
+
}
|
|
1893
|
+
get statusCode() {
|
|
1894
|
+
switch (this.type) {
|
|
1895
|
+
case "ok-json":
|
|
1896
|
+
case "ok-html":
|
|
1897
|
+
return 200;
|
|
1898
|
+
case "bad-request":
|
|
1899
|
+
return 400;
|
|
1900
|
+
case "server-error":
|
|
1901
|
+
return 500;
|
|
1902
|
+
default: {
|
|
1903
|
+
const _ = this.type;
|
|
1904
|
+
throw new Error(_);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
get contentType() {
|
|
1909
|
+
switch (this.type) {
|
|
1910
|
+
case "ok-json":
|
|
1911
|
+
return "application/json";
|
|
1912
|
+
case "ok-html":
|
|
1913
|
+
return "text/html; charset=utf-8";
|
|
1914
|
+
case "bad-request":
|
|
1915
|
+
case "server-error":
|
|
1916
|
+
return "text/plain; charset=utf-8";
|
|
1917
|
+
default: {
|
|
1918
|
+
const _ = this.type;
|
|
1919
|
+
throw new Error(_);
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
exports.RawResponse = RawResponse;
|
|
1925
|
+
// Copied from
|
|
1926
|
+
// https://github.com/gepheum/restudio/blob/main/index.jsdeliver.html
|
|
1927
|
+
const RESTUDIO_HTML = `<!DOCTYPE html>
|
|
1928
|
+
|
|
1929
|
+
<html>
|
|
1930
|
+
<head>
|
|
1931
|
+
<meta charset="utf-8" />
|
|
1932
|
+
<title>RESTudio</title>
|
|
1933
|
+
<script src="https://cdn.jsdelivr.net/npm/restudio/dist/restudio-standalone.js"></script>
|
|
1934
|
+
</head>
|
|
1935
|
+
<body style="margin: 0; padding: 0;">
|
|
1936
|
+
<restudio-app></restudio-app>
|
|
1937
|
+
</body>
|
|
1938
|
+
</html>
|
|
1939
|
+
`;
|
|
1940
|
+
/**
|
|
1941
|
+
* Implementation of a skir service.
|
|
1942
|
+
*
|
|
1943
|
+
* Usage: call `.addMethod()` to register methods, then install the service on
|
|
1944
|
+
* an HTTP server either by:
|
|
1945
|
+
* - calling the `installServiceOnExpressApp()` top-level function if you are
|
|
1946
|
+
* using ExpressJS
|
|
1947
|
+
* - writing your own implementation of `installServiceOn*()` which calls
|
|
1948
|
+
* `.handleRequest()` if you are using another web application framework
|
|
1949
|
+
*/
|
|
1950
|
+
class Service {
|
|
1951
|
+
constructor() {
|
|
1952
|
+
this.methodImpls = {};
|
|
1953
|
+
}
|
|
1954
|
+
addMethod(method, impl) {
|
|
1955
|
+
const { number } = method;
|
|
1956
|
+
if (this.methodImpls[number]) {
|
|
1957
|
+
throw new Error(`Method with the same number already registered (${number})`);
|
|
1958
|
+
}
|
|
1959
|
+
this.methodImpls[number] = {
|
|
1960
|
+
method: method,
|
|
1961
|
+
impl: impl,
|
|
1962
|
+
};
|
|
1963
|
+
return this;
|
|
1964
|
+
}
|
|
1965
|
+
/**
|
|
1966
|
+
* Parses the content of a user request and invokes the appropriate method.
|
|
1967
|
+
* If you are using ExpressJS as your web application framework, you don't
|
|
1968
|
+
* need to call this method, you can simply call the
|
|
1969
|
+
* `installServiceOnExpressApp()` top-level function.
|
|
1970
|
+
*
|
|
1971
|
+
* If the request is a GET request, pass in the decoded query string as the
|
|
1972
|
+
* request's body. The query string is the part of the URL after '?', and it
|
|
1973
|
+
* can be decoded with DecodeURIComponent.
|
|
1974
|
+
*
|
|
1975
|
+
* Pass in "keep-unrecognized-values" if the request cannot come from a
|
|
1976
|
+
* malicious user.
|
|
1977
|
+
*/
|
|
1978
|
+
handleRequest(reqBody, reqMeta, resMeta, keepUnrecognizedValues) {
|
|
1979
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1980
|
+
if (reqBody === "" || reqBody === "list") {
|
|
1981
|
+
const json = {
|
|
1982
|
+
methods: Object.values(this.methodImpls).map((methodImpl) => ({
|
|
1983
|
+
method: methodImpl.method.name,
|
|
1984
|
+
number: methodImpl.method.name,
|
|
1985
|
+
request: methodImpl.method.requestSerializer.typeDescriptor.asJson(),
|
|
1986
|
+
response: methodImpl.method.responseSerializer.typeDescriptor.asJson(),
|
|
1987
|
+
doc: methodImpl.method.doc,
|
|
1988
|
+
})),
|
|
1989
|
+
};
|
|
1990
|
+
const jsonCode = JSON.stringify(json, undefined, " ");
|
|
1991
|
+
return new RawResponse(jsonCode, "ok-json");
|
|
1992
|
+
}
|
|
1993
|
+
else if (reqBody === "debug" || reqBody === "restudio") {
|
|
1994
|
+
return new RawResponse(RESTUDIO_HTML, "ok-html");
|
|
1995
|
+
}
|
|
1996
|
+
// Parse request
|
|
1997
|
+
let methodName;
|
|
1998
|
+
let methodNumber;
|
|
1999
|
+
let format;
|
|
2000
|
+
let requestData;
|
|
2001
|
+
const firstChar = reqBody.charAt(0);
|
|
2002
|
+
if (/\s/.test(firstChar) || firstChar === "{") {
|
|
2003
|
+
// A JSON object
|
|
2004
|
+
let reqBodyJson;
|
|
2005
|
+
try {
|
|
2006
|
+
reqBodyJson = JSON.parse(reqBody);
|
|
2007
|
+
}
|
|
2008
|
+
catch (e) {
|
|
2009
|
+
return new RawResponse("bad request: invalid JSON", "bad-request");
|
|
2010
|
+
}
|
|
2011
|
+
const methodField = reqBodyJson["method"];
|
|
2012
|
+
if (methodField === undefined) {
|
|
2013
|
+
return new RawResponse("bad request: missing 'method' field in JSON", "bad-request");
|
|
2014
|
+
}
|
|
2015
|
+
if (typeof methodField === "string") {
|
|
2016
|
+
methodName = methodField;
|
|
2017
|
+
methodNumber = undefined;
|
|
2018
|
+
}
|
|
2019
|
+
else if (typeof methodField === "number") {
|
|
2020
|
+
methodName = "?";
|
|
2021
|
+
methodNumber = methodField;
|
|
2022
|
+
}
|
|
2023
|
+
else {
|
|
2024
|
+
return new RawResponse("bad request: 'method' field must be a string or a number", "bad-request");
|
|
2025
|
+
}
|
|
2026
|
+
format = "readable";
|
|
2027
|
+
const requestField = reqBodyJson["request"];
|
|
2028
|
+
if (requestField === undefined) {
|
|
2029
|
+
return new RawResponse("bad request: missing 'request' field in JSON", "bad-request");
|
|
2030
|
+
}
|
|
2031
|
+
requestData = ["json", requestField];
|
|
2032
|
+
}
|
|
2033
|
+
else {
|
|
2034
|
+
// A colon-separated string
|
|
2035
|
+
const match = reqBody.match(/^([^:]*):([^:]*):([^:]*):([\S\s]*)$/);
|
|
2036
|
+
if (!match) {
|
|
2037
|
+
return new RawResponse("bad request: invalid request format", "bad-request");
|
|
2038
|
+
}
|
|
2039
|
+
methodName = match[1];
|
|
2040
|
+
const methodNumberStr = match[2];
|
|
2041
|
+
format = match[3];
|
|
2042
|
+
requestData = ["json-code", match[4]];
|
|
2043
|
+
if (methodNumberStr) {
|
|
2044
|
+
if (!/^-?[0-9]+$/.test(methodNumberStr)) {
|
|
2045
|
+
return new RawResponse("bad request: can't parse method number", "bad-request");
|
|
2046
|
+
}
|
|
2047
|
+
methodNumber = parseInt(methodNumberStr);
|
|
2048
|
+
}
|
|
2049
|
+
else {
|
|
2050
|
+
methodNumber = undefined;
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
// Look up method by number or name
|
|
2054
|
+
if (methodNumber === undefined) {
|
|
2055
|
+
// Try to get the method number by name
|
|
2056
|
+
const allMethods = Object.values(this.methodImpls);
|
|
2057
|
+
const nameMatches = allMethods.filter((m) => m.method.name === methodName);
|
|
2058
|
+
if (nameMatches.length === 0) {
|
|
2059
|
+
return new RawResponse(`bad request: method not found: ${methodName}`, "bad-request");
|
|
2060
|
+
}
|
|
2061
|
+
else if (nameMatches.length > 1) {
|
|
2062
|
+
return new RawResponse(`bad request: method name '${methodName}' is ambiguous; use method number instead`, "bad-request");
|
|
2063
|
+
}
|
|
2064
|
+
methodNumber = nameMatches[0].method.number;
|
|
2065
|
+
}
|
|
2066
|
+
const methodImpl = this.methodImpls[methodNumber];
|
|
2067
|
+
if (!methodImpl) {
|
|
2068
|
+
return new RawResponse(`bad request: method not found: ${methodName}; number: ${methodNumber}`, "bad-request");
|
|
2069
|
+
}
|
|
2070
|
+
let req;
|
|
2071
|
+
try {
|
|
2072
|
+
if (requestData[0] == "json") {
|
|
2073
|
+
req = methodImpl.method.requestSerializer.fromJson(requestData[1], keepUnrecognizedValues);
|
|
2074
|
+
}
|
|
2075
|
+
else {
|
|
2076
|
+
req = methodImpl.method.requestSerializer.fromJsonCode(requestData[1], keepUnrecognizedValues);
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
catch (e) {
|
|
2080
|
+
return new RawResponse(`bad request: can't parse JSON: ${e}`, "bad-request");
|
|
2081
|
+
}
|
|
2082
|
+
let res;
|
|
2083
|
+
try {
|
|
2084
|
+
res = yield methodImpl.impl(req, reqMeta, resMeta);
|
|
2085
|
+
}
|
|
2086
|
+
catch (e) {
|
|
2087
|
+
return new RawResponse(`server error: ${e}`, "server-error");
|
|
2088
|
+
}
|
|
2089
|
+
let resJson;
|
|
2090
|
+
try {
|
|
2091
|
+
const flavor = format === "readable" ? "readable" : "dense";
|
|
2092
|
+
resJson = methodImpl.method.responseSerializer.toJsonCode(res, flavor);
|
|
2093
|
+
}
|
|
2094
|
+
catch (e) {
|
|
2095
|
+
return new RawResponse(`server error: can't serialize response to JSON: ${e}`, "server-error");
|
|
2096
|
+
}
|
|
2097
|
+
return new RawResponse(resJson, "ok-json");
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
exports.Service = Service;
|
|
2102
|
+
function installServiceOnExpressApp(app, queryPath, service, text, json, keepUnrecognizedValues) {
|
|
2103
|
+
const callback = (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
2104
|
+
let body;
|
|
2105
|
+
const indexOfQuestionMark = req.originalUrl.indexOf("?");
|
|
2106
|
+
if (indexOfQuestionMark >= 0) {
|
|
2107
|
+
const queryString = req.originalUrl.substring(indexOfQuestionMark + 1);
|
|
2108
|
+
body = decodeURIComponent(queryString);
|
|
2109
|
+
}
|
|
2110
|
+
else {
|
|
2111
|
+
body =
|
|
2112
|
+
typeof req.body === "string"
|
|
2113
|
+
? req.body
|
|
2114
|
+
: typeof req.body === "object"
|
|
2115
|
+
? JSON.stringify(req.body)
|
|
2116
|
+
: "";
|
|
2117
|
+
}
|
|
2118
|
+
const rawResponse = yield service.handleRequest(body, req, res, keepUnrecognizedValues);
|
|
2119
|
+
res
|
|
2120
|
+
.status(rawResponse.statusCode)
|
|
2121
|
+
.contentType(rawResponse.contentType)
|
|
2122
|
+
.send(rawResponse.data);
|
|
2123
|
+
});
|
|
2124
|
+
app.get(queryPath, callback);
|
|
2125
|
+
app.post(queryPath, text(), json(), callback);
|
|
2126
|
+
}
|
|
2127
|
+
exports.installServiceOnExpressApp = installServiceOnExpressApp;
|
|
2128
|
+
// The UNKNOWN variant is common to all enums.
|
|
2129
|
+
const UNKNOWN_FIELD_SPEC = {
|
|
2130
|
+
name: "?",
|
|
2131
|
+
number: 0,
|
|
2132
|
+
};
|
|
2133
|
+
function _initModuleClasses(modulePath, records) {
|
|
2134
|
+
var _a, _b, _c;
|
|
2135
|
+
const privateKey = PRIVATE_KEY;
|
|
2136
|
+
// First loop: add a serializer property to every record class.
|
|
2137
|
+
for (const record of records) {
|
|
2138
|
+
const clazz = record.ctor;
|
|
2139
|
+
switch (record.kind) {
|
|
2140
|
+
case "struct": {
|
|
2141
|
+
const { ctor, initFn } = record;
|
|
2142
|
+
// Create the DEFAULT value. It will be initialized in a second loop.
|
|
2143
|
+
// To see why we can't initialize it in the first loop, consider this
|
|
2144
|
+
// example:
|
|
2145
|
+
// struct Foo { bar: Bar; }
|
|
2146
|
+
// struct Bar { foo: Foo; }
|
|
2147
|
+
// The default value for Foo must contain a reference to the default
|
|
2148
|
+
// value for Bar, and the default value for Bar also needs to contain
|
|
2149
|
+
// a reference to the default value for Foo.
|
|
2150
|
+
clazz.DEFAULT = new ctor(privateKey);
|
|
2151
|
+
// Expose the mutable class as a static property of the frozen class.
|
|
2152
|
+
const mutableCtor = makeMutableClassForRecord(record, clazz.DEFAULT);
|
|
2153
|
+
clazz.Mutable = mutableCtor;
|
|
2154
|
+
// Define the 'create' static factory function.
|
|
2155
|
+
const createFn = (initializer) => {
|
|
2156
|
+
if (initializer instanceof ctor) {
|
|
2157
|
+
return initializer;
|
|
2158
|
+
}
|
|
2159
|
+
const ret = new ctor(privateKey);
|
|
2160
|
+
initFn(ret, initializer);
|
|
2161
|
+
if (initializer["^"]) {
|
|
2162
|
+
ret["^"] = initializer["^"];
|
|
2163
|
+
}
|
|
2164
|
+
return Object.freeze(ret);
|
|
2165
|
+
};
|
|
2166
|
+
clazz.create = createFn;
|
|
2167
|
+
// Create the serializer. It will be initialized in a second loop.
|
|
2168
|
+
clazz.serializer = new StructSerializerImpl(clazz.DEFAULT, createFn, () => new mutableCtor());
|
|
2169
|
+
break;
|
|
2170
|
+
}
|
|
2171
|
+
case "enum": {
|
|
2172
|
+
// Create the constants.
|
|
2173
|
+
// Prepend the UNKNOWN variant to the array of fields specified from the
|
|
2174
|
+
// generated code.
|
|
2175
|
+
record.fields = [UNKNOWN_FIELD_SPEC].concat(record.fields);
|
|
2176
|
+
for (const field of record.fields) {
|
|
2177
|
+
if (field.type) {
|
|
2178
|
+
continue;
|
|
2179
|
+
}
|
|
2180
|
+
const property = enumConstantNameToProperty(field.name);
|
|
2181
|
+
clazz[property] = new record.ctor(PRIVATE_KEY, field.name);
|
|
2182
|
+
}
|
|
2183
|
+
// Define the 'create' static factory function.
|
|
2184
|
+
const createFn = makeCreateEnumFunction(record);
|
|
2185
|
+
clazz.create = createFn;
|
|
2186
|
+
// Create the serializer. It will be initialized in a second loop.
|
|
2187
|
+
clazz.serializer = new EnumSerializerImpl(createFn);
|
|
2188
|
+
break;
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
// If the record is nested, expose the record class as a static property of
|
|
2192
|
+
// the parent class.
|
|
2193
|
+
if (record.parentCtor) {
|
|
2194
|
+
record.parentCtor[record.name] = record.ctor;
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
// Second loop: initialize the serializer of every record, initialize the
|
|
2198
|
+
// default value of every struct, and freeze every class so new properties
|
|
2199
|
+
// can't be added to it.
|
|
2200
|
+
for (const record of records) {
|
|
2201
|
+
const clazz = record.ctor;
|
|
2202
|
+
const parentTypeDescriptor = (_a = record.parentCtor) === null || _a === void 0 ? void 0 : _a.serializer;
|
|
2203
|
+
switch (record.kind) {
|
|
2204
|
+
case "struct": {
|
|
2205
|
+
// Initializer serializer.
|
|
2206
|
+
const fields = record.fields.map((f) => new StructFieldImpl(f.name, f.property, f.number, getSerializerForType(f.type)));
|
|
2207
|
+
const serializer = clazz.serializer;
|
|
2208
|
+
serializer.init(record.name, modulePath, parentTypeDescriptor, fields, (_b = record.removedNumbers) !== null && _b !== void 0 ? _b : []);
|
|
2209
|
+
// Initialize DEFAULT.
|
|
2210
|
+
const { DEFAULT } = clazz;
|
|
2211
|
+
record.initFn(DEFAULT, {});
|
|
2212
|
+
Object.freeze(DEFAULT);
|
|
2213
|
+
// Define the mutable getters in the Mutable class.
|
|
2214
|
+
const mutableCtor = clazz.Mutable;
|
|
2215
|
+
for (const field of record.fields) {
|
|
2216
|
+
if (field.mutableGetter) {
|
|
2217
|
+
Object.defineProperty(mutableCtor.prototype, field.mutableGetter, {
|
|
2218
|
+
get: makeMutableGetterFn(field),
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
// Define the search methods in the frozen class.
|
|
2223
|
+
for (const field of record.fields) {
|
|
2224
|
+
if (field.indexable) {
|
|
2225
|
+
record.ctor.prototype[field.indexable.searchMethod] =
|
|
2226
|
+
makeSearchMethod(field);
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
// Freeze the frozen class and the mutable class.
|
|
2230
|
+
Object.freeze(record.ctor);
|
|
2231
|
+
Object.freeze(record.ctor.prototype);
|
|
2232
|
+
Object.freeze(clazz.Mutable);
|
|
2233
|
+
Object.freeze(clazz.Mutable.prototype);
|
|
2234
|
+
break;
|
|
2235
|
+
}
|
|
2236
|
+
case "enum": {
|
|
2237
|
+
const serializer = clazz.serializer;
|
|
2238
|
+
const fields = record.fields.map((f) => f.type
|
|
2239
|
+
? new EnumWrapperVariantImpl(f.name, f.number, getSerializerForType(f.type), serializer.createFn)
|
|
2240
|
+
: {
|
|
2241
|
+
name: f.name,
|
|
2242
|
+
number: f.number,
|
|
2243
|
+
constant: clazz[enumConstantNameToProperty(f.name)],
|
|
2244
|
+
});
|
|
2245
|
+
serializer.init(record.name, modulePath, parentTypeDescriptor, fields, (_c = record.removedNumbers) !== null && _c !== void 0 ? _c : []);
|
|
2246
|
+
// Freeze the enum class.
|
|
2247
|
+
Object.freeze(record.ctor);
|
|
2248
|
+
Object.freeze(record.ctor.prototype);
|
|
2249
|
+
break;
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
exports._initModuleClasses = _initModuleClasses;
|
|
2255
|
+
function enumConstantNameToProperty(name) {
|
|
2256
|
+
return name === "?" ? "UNKNOWN" : name;
|
|
2257
|
+
}
|
|
2258
|
+
function makeCreateEnumFunction(enumSpec) {
|
|
2259
|
+
const { ctor, createValueFn } = enumSpec;
|
|
2260
|
+
const createValue = createValueFn || (() => undefined);
|
|
2261
|
+
const privateKey = PRIVATE_KEY;
|
|
2262
|
+
return (initializer) => {
|
|
2263
|
+
if (initializer instanceof ctor) {
|
|
2264
|
+
return initializer;
|
|
2265
|
+
}
|
|
2266
|
+
if (typeof initializer === "string") {
|
|
2267
|
+
const maybeResult = ctor[enumConstantNameToProperty(initializer)];
|
|
2268
|
+
if (maybeResult instanceof ctor) {
|
|
2269
|
+
return maybeResult;
|
|
2270
|
+
}
|
|
2271
|
+
throw new Error(`Constant not found: ${initializer}`);
|
|
2272
|
+
}
|
|
2273
|
+
if (initializer instanceof UnrecognizedEnum) {
|
|
2274
|
+
return new ctor(privateKey, "?", undefined, initializer);
|
|
2275
|
+
}
|
|
2276
|
+
const kind = initializer.kind;
|
|
2277
|
+
if (kind === undefined) {
|
|
2278
|
+
throw new Error("Missing entry: kind");
|
|
2279
|
+
}
|
|
2280
|
+
const value = createValue(initializer);
|
|
2281
|
+
if (value === undefined) {
|
|
2282
|
+
throw new Error(`Wrapper field not found: ${kind}`);
|
|
2283
|
+
}
|
|
2284
|
+
return new ctor(privateKey, kind, value);
|
|
2285
|
+
};
|
|
2286
|
+
}
|
|
2287
|
+
function makeMutableClassForRecord(structSpec, defaultFrozen) {
|
|
2288
|
+
const { ctor: frozenCtor, initFn } = structSpec;
|
|
2289
|
+
const frozenClass = frozenCtor;
|
|
2290
|
+
class Mutable {
|
|
2291
|
+
constructor(initializer = defaultFrozen) {
|
|
2292
|
+
initFn(this, initializer);
|
|
2293
|
+
if (initializer["^"]) {
|
|
2294
|
+
this["^"] = initializer["^"];
|
|
2295
|
+
}
|
|
2296
|
+
Object.seal(this);
|
|
2297
|
+
}
|
|
2298
|
+
toFrozen() {
|
|
2299
|
+
return frozenClass.create(this);
|
|
2300
|
+
}
|
|
2301
|
+
toString() {
|
|
2302
|
+
const serializer = frozenClass.serializer;
|
|
2303
|
+
return serializer.toJsonCode(this, "readable");
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
return Mutable;
|
|
2307
|
+
}
|
|
2308
|
+
function getSerializerForType(type) {
|
|
2309
|
+
switch (type.kind) {
|
|
2310
|
+
case "array":
|
|
2311
|
+
return arraySerializer(getSerializerForType(type.item), type.keyChain);
|
|
2312
|
+
case "optional":
|
|
2313
|
+
return optionalSerializer(getSerializerForType(type.other));
|
|
2314
|
+
case "primitive":
|
|
2315
|
+
return primitiveSerializer(type.primitive);
|
|
2316
|
+
case "record":
|
|
2317
|
+
return type.ctor
|
|
2318
|
+
.serializer;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
// The `mutableArray()` getter of the Mutable class returns `this.array` if and
|
|
2322
|
+
// only if `mutableArray()` was never called before or if `this.array` is the
|
|
2323
|
+
// last value returned by `mutableArray()`.
|
|
2324
|
+
// Otherwise, it makes a mutable copy of `this.array`, assigns it to
|
|
2325
|
+
// `this.array` and returns it.
|
|
2326
|
+
const arraysReturnedByMutableGetters = new WeakMap();
|
|
2327
|
+
function makeMutableGetterFn(field) {
|
|
2328
|
+
const { property, type } = field;
|
|
2329
|
+
switch (type.kind) {
|
|
2330
|
+
case "array": {
|
|
2331
|
+
class Class {
|
|
2332
|
+
static ret() {
|
|
2333
|
+
const value = this[property];
|
|
2334
|
+
if (arraysReturnedByMutableGetters.get(value) === this) {
|
|
2335
|
+
return value;
|
|
2336
|
+
}
|
|
2337
|
+
const copy = [...value];
|
|
2338
|
+
arraysReturnedByMutableGetters.set(copy, this);
|
|
2339
|
+
return (this[property] = copy);
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
return Class.ret;
|
|
2343
|
+
}
|
|
2344
|
+
case "record": {
|
|
2345
|
+
const mutableCtor = type.ctor.Mutable;
|
|
2346
|
+
class Class {
|
|
2347
|
+
static ret() {
|
|
2348
|
+
const value = this[property];
|
|
2349
|
+
if (value instanceof mutableCtor) {
|
|
2350
|
+
return value;
|
|
2351
|
+
}
|
|
2352
|
+
return (this[property] = new mutableCtor(value));
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
return Class.ret;
|
|
2356
|
+
}
|
|
2357
|
+
default: {
|
|
2358
|
+
throw new Error();
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
function makeSearchMethod(field) {
|
|
2363
|
+
var _a;
|
|
2364
|
+
const { property } = field;
|
|
2365
|
+
const indexable = field.indexable;
|
|
2366
|
+
const { keyFn } = indexable;
|
|
2367
|
+
const keyToHashable = (_a = indexable.keyToHashable) !== null && _a !== void 0 ? _a : ((e) => e);
|
|
2368
|
+
class Class {
|
|
2369
|
+
ret(key) {
|
|
2370
|
+
const array = this[property];
|
|
2371
|
+
const frozenArrayInfo = frozenArrayRegistry.get(array);
|
|
2372
|
+
let { keyFnToIndexing } = frozenArrayInfo;
|
|
2373
|
+
if (!keyFnToIndexing) {
|
|
2374
|
+
frozenArrayInfo.keyFnToIndexing = keyFnToIndexing = //
|
|
2375
|
+
new Map();
|
|
2376
|
+
}
|
|
2377
|
+
let hashableToValue = keyFnToIndexing.get(keyFn);
|
|
2378
|
+
if (!hashableToValue) {
|
|
2379
|
+
// The array has not been indexed yet. Index it.
|
|
2380
|
+
hashableToValue = new Map();
|
|
2381
|
+
for (const v of array) {
|
|
2382
|
+
const hashable = keyToHashable(keyFn(v));
|
|
2383
|
+
hashableToValue.set(hashable, v);
|
|
2384
|
+
}
|
|
2385
|
+
keyFnToIndexing.set(keyFn, hashableToValue);
|
|
2386
|
+
}
|
|
2387
|
+
return hashableToValue.get(keyToHashable(key));
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
return Class.prototype.ret;
|
|
2391
|
+
}
|
|
2392
|
+
//# sourceMappingURL=skir-client.js.map
|