protons 7.7.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/bin/protons.js +1 -1
- package/dist/src/fields/array-field.d.ts +18 -0
- package/dist/src/fields/array-field.d.ts.map +1 -0
- package/dist/src/fields/array-field.js +83 -0
- package/dist/src/fields/array-field.js.map +1 -0
- package/dist/src/fields/enum-field.d.ts +9 -0
- package/dist/src/fields/enum-field.d.ts.map +1 -0
- package/dist/src/fields/enum-field.js +21 -0
- package/dist/src/fields/enum-field.js.map +1 -0
- package/dist/src/fields/field.d.ts +45 -0
- package/dist/src/fields/field.d.ts.map +1 -0
- package/dist/src/fields/field.js +147 -0
- package/dist/src/fields/field.js.map +1 -0
- package/dist/src/fields/map-field.d.ts +22 -0
- package/dist/src/fields/map-field.d.ts.map +1 -0
- package/dist/src/fields/map-field.js +83 -0
- package/dist/src/fields/map-field.js.map +1 -0
- package/dist/src/fields/message-field.d.ts +9 -0
- package/dist/src/fields/message-field.d.ts.map +1 -0
- package/dist/src/fields/message-field.js +23 -0
- package/dist/src/fields/message-field.js.map +1 -0
- package/dist/src/index.d.ts +190 -16
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -963
- package/dist/src/index.js.map +1 -1
- package/dist/src/types/enum.d.ts +21 -0
- package/dist/src/types/enum.d.ts.map +1 -0
- package/dist/src/types/enum.js +87 -0
- package/dist/src/types/enum.js.map +1 -0
- package/dist/src/types/index.d.ts +20 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/index.js +2 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/types/message.d.ts +49 -0
- package/dist/src/types/message.d.ts.map +1 -0
- package/dist/src/types/message.js +478 -0
- package/dist/src/types/message.js.map +1 -0
- package/dist/src/types/module.d.ts +30 -0
- package/dist/src/types/module.d.ts.map +1 -0
- package/dist/src/types/module.js +184 -0
- package/dist/src/types/module.js.map +1 -0
- package/dist/src/types/primitive.d.ts +13 -0
- package/dist/src/types/primitive.d.ts.map +1 -0
- package/dist/src/types/primitive.js +174 -0
- package/dist/src/types/primitive.js.map +1 -0
- package/dist/typedoc-urls.json +2 -4
- package/package.json +102 -15
- package/src/fields/array-field.ts +109 -0
- package/src/fields/enum-field.ts +30 -0
- package/src/fields/field.ts +201 -0
- package/src/fields/map-field.ts +107 -0
- package/src/fields/message-field.ts +29 -0
- package/src/index.ts +6 -1202
- package/src/types/enum.ts +112 -0
- package/src/types/index.ts +21 -0
- package/src/types/message.ts +558 -0
- package/src/types/module.ts +234 -0
- package/src/types/primitive.ts +215 -0
- package/LICENSE +0 -4
package/src/index.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable max-depth */
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* @packageDocumentation
|
|
5
3
|
*
|
|
@@ -33,7 +31,7 @@
|
|
|
33
31
|
* In your code import the generated classes and use them to transform to/from bytes:
|
|
34
32
|
*
|
|
35
33
|
* ```js
|
|
36
|
-
* import { Foo } from './foo.
|
|
34
|
+
* import { Foo } from './foo.ts'
|
|
37
35
|
*
|
|
38
36
|
* const foo = {
|
|
39
37
|
* message: 'hello world'
|
|
@@ -48,7 +46,7 @@
|
|
|
48
46
|
*
|
|
49
47
|
* ## Differences from protobuf.js
|
|
50
48
|
*
|
|
51
|
-
* This module uses the internal reader/writer from `protobuf.js` as it is highly
|
|
49
|
+
* This module uses the internal reader/writer from `protobuf.js` as it is highly optimized and there's no point reinventing the wheel.
|
|
52
50
|
*
|
|
53
51
|
* It does have one or two differences:
|
|
54
52
|
*
|
|
@@ -197,1020 +195,14 @@ import fs from 'fs/promises'
|
|
|
197
195
|
import path from 'path'
|
|
198
196
|
import { promisify } from 'util'
|
|
199
197
|
import { main as pbjs } from 'protobufjs-cli/pbjs.js'
|
|
200
|
-
import {
|
|
201
|
-
|
|
202
|
-
export enum CODEC_TYPES {
|
|
203
|
-
VARINT = 0,
|
|
204
|
-
BIT64,
|
|
205
|
-
LENGTH_DELIMITED,
|
|
206
|
-
START_GROUP,
|
|
207
|
-
END_GROUP,
|
|
208
|
-
BIT32
|
|
209
|
-
}
|
|
198
|
+
import { Module } from './types/module.ts'
|
|
210
199
|
|
|
211
200
|
function pathWithExtension (input: string, extension: string, outputDir?: string): string {
|
|
212
201
|
const output = outputDir ?? path.dirname(input)
|
|
213
202
|
return path.join(output, path.basename(input).split('.').slice(0, -1).join('.') + extension)
|
|
214
203
|
}
|
|
215
204
|
|
|
216
|
-
|
|
217
|
-
* This will be removed in a future release
|
|
218
|
-
*
|
|
219
|
-
* @deprecated
|
|
220
|
-
*/
|
|
221
|
-
export class CodeError extends Error {
|
|
222
|
-
public code: string
|
|
223
|
-
|
|
224
|
-
constructor (message: string, code: string, options?: ErrorOptions) {
|
|
225
|
-
super(message, options)
|
|
226
|
-
|
|
227
|
-
this.code = code
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const types: Record<string, string> = {
|
|
232
|
-
bool: 'boolean',
|
|
233
|
-
bytes: 'Uint8Array',
|
|
234
|
-
double: 'number',
|
|
235
|
-
fixed32: 'number',
|
|
236
|
-
fixed64: 'bigint',
|
|
237
|
-
float: 'number',
|
|
238
|
-
int32: 'number',
|
|
239
|
-
int64: 'bigint',
|
|
240
|
-
sfixed32: 'number',
|
|
241
|
-
sfixed64: 'bigint',
|
|
242
|
-
sint32: 'number',
|
|
243
|
-
sint64: 'bigint',
|
|
244
|
-
string: 'string',
|
|
245
|
-
uint32: 'number',
|
|
246
|
-
uint64: 'bigint'
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const jsTypeOverrides: Record<string, 'number' | 'string'> = {
|
|
250
|
-
JS_NUMBER: 'number',
|
|
251
|
-
JS_STRING: 'string'
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const encoderGenerators: Record<string, (val: string, jsTypeOverride?: 'number' | 'string') => string> = {
|
|
255
|
-
bool: (val) => `w.bool(${val})`,
|
|
256
|
-
bytes: (val) => `w.bytes(${val})`,
|
|
257
|
-
double: (val) => `w.double(${val})`,
|
|
258
|
-
fixed32: (val) => `w.fixed32(${val})`,
|
|
259
|
-
fixed64: (val, jsTypeOverride) => {
|
|
260
|
-
if (jsTypeOverride === 'number') {
|
|
261
|
-
return `w.fixed64Number(${val})`
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (jsTypeOverride === 'string') {
|
|
265
|
-
return `w.fixed64String(${val})`
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
return `w.fixed64(${val})`
|
|
269
|
-
},
|
|
270
|
-
float: (val) => `w.float(${val})`,
|
|
271
|
-
int32: (val) => `w.int32(${val})`,
|
|
272
|
-
int64: (val, jsTypeOverride) => {
|
|
273
|
-
if (jsTypeOverride === 'number') {
|
|
274
|
-
return `w.int64Number(${val})`
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (jsTypeOverride === 'string') {
|
|
278
|
-
return `w.int64String(${val})`
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return `w.int64(${val})`
|
|
282
|
-
},
|
|
283
|
-
sfixed32: (val) => `w.sfixed32(${val})`,
|
|
284
|
-
sfixed64: (val, jsTypeOverride) => {
|
|
285
|
-
if (jsTypeOverride === 'number') {
|
|
286
|
-
return `w.sfixed64Number(${val})`
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (jsTypeOverride === 'string') {
|
|
290
|
-
return `w.sfixed64String(${val})`
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return `w.sfixed64(${val})`
|
|
294
|
-
},
|
|
295
|
-
sint32: (val) => `w.sint32(${val})`,
|
|
296
|
-
sint64: (val, jsTypeOverride) => {
|
|
297
|
-
if (jsTypeOverride === 'number') {
|
|
298
|
-
return `w.sint64Number(${val})`
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (jsTypeOverride === 'string') {
|
|
302
|
-
return `w.sint64String(${val})`
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
return `w.sint64(${val})`
|
|
306
|
-
},
|
|
307
|
-
string: (val) => `w.string(${val})`,
|
|
308
|
-
uint32: (val) => `w.uint32(${val})`,
|
|
309
|
-
uint64: (val, jsTypeOverride) => {
|
|
310
|
-
if (jsTypeOverride === 'number') {
|
|
311
|
-
return `w.uint64Number(${val})`
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
if (jsTypeOverride === 'string') {
|
|
315
|
-
return `w.uint64String(${val})`
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
return `w.uint64(${val})`
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
const decoderGenerators: Record<string, (jsTypeOverride?: 'number' | 'string') => string> = {
|
|
323
|
-
bool: () => 'reader.bool()',
|
|
324
|
-
bytes: () => 'reader.bytes()',
|
|
325
|
-
double: () => 'reader.double()',
|
|
326
|
-
fixed32: () => 'reader.fixed32()',
|
|
327
|
-
fixed64: (jsTypeOverride) => {
|
|
328
|
-
if (jsTypeOverride === 'number') {
|
|
329
|
-
return 'reader.fixed64Number()'
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
if (jsTypeOverride === 'string') {
|
|
333
|
-
return 'reader.fixed64String()'
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return 'reader.fixed64()'
|
|
337
|
-
},
|
|
338
|
-
float: () => 'reader.float()',
|
|
339
|
-
int32: () => 'reader.int32()',
|
|
340
|
-
int64: (jsTypeOverride) => {
|
|
341
|
-
if (jsTypeOverride === 'number') {
|
|
342
|
-
return 'reader.int64Number()'
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (jsTypeOverride === 'string') {
|
|
346
|
-
return 'reader.int64String()'
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return 'reader.int64()'
|
|
350
|
-
},
|
|
351
|
-
sfixed32: () => 'reader.sfixed32()',
|
|
352
|
-
sfixed64: (jsTypeOverride) => {
|
|
353
|
-
if (jsTypeOverride === 'number') {
|
|
354
|
-
return 'reader.sfixed64Number()'
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (jsTypeOverride === 'string') {
|
|
358
|
-
return 'reader.sfixed64String()'
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
return 'reader.sfixed64()'
|
|
362
|
-
},
|
|
363
|
-
sint32: () => 'reader.sint32()',
|
|
364
|
-
sint64: (jsTypeOverride) => {
|
|
365
|
-
if (jsTypeOverride === 'number') {
|
|
366
|
-
return 'reader.sint64Number()'
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
if (jsTypeOverride === 'string') {
|
|
370
|
-
return 'reader.sint64String()'
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
return 'reader.sint64()'
|
|
374
|
-
},
|
|
375
|
-
string: () => 'reader.string()',
|
|
376
|
-
uint32: () => 'reader.uint32()',
|
|
377
|
-
uint64: (jsTypeOverride) => {
|
|
378
|
-
if (jsTypeOverride === 'number') {
|
|
379
|
-
return 'reader.uint64Number()'
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
if (jsTypeOverride === 'string') {
|
|
383
|
-
return 'reader.uint64String()'
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
return 'reader.uint64()'
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
const defaultValueGenerators: Record<string, () => string> = {
|
|
391
|
-
bool: () => 'false',
|
|
392
|
-
bytes: () => 'uint8ArrayAlloc(0)',
|
|
393
|
-
double: () => '0',
|
|
394
|
-
fixed32: () => '0',
|
|
395
|
-
fixed64: () => '0n',
|
|
396
|
-
float: () => '0',
|
|
397
|
-
int32: () => '0',
|
|
398
|
-
int64: () => '0n',
|
|
399
|
-
sfixed32: () => '0',
|
|
400
|
-
sfixed64: () => '0n',
|
|
401
|
-
sint32: () => '0',
|
|
402
|
-
sint64: () => '0n',
|
|
403
|
-
string: () => "''",
|
|
404
|
-
uint32: () => '0',
|
|
405
|
-
uint64: () => '0n'
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
const defaultValueGeneratorsJsTypeOverrides: Record<string, () => string> = {
|
|
409
|
-
number: () => '0',
|
|
410
|
-
string: () => "''"
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
const defaultValueTestGenerators: Record<string, (field: string) => string> = {
|
|
414
|
-
bool: (field) => `(${field} != null && ${field} !== false)`,
|
|
415
|
-
bytes: (field) => `(${field} != null && ${field}.byteLength > 0)`,
|
|
416
|
-
double: (field) => `(${field} != null && ${field} !== 0)`,
|
|
417
|
-
fixed32: (field) => `(${field} != null && ${field} !== 0)`,
|
|
418
|
-
fixed64: (field) => `(${field} != null && ${field} !== 0n)`,
|
|
419
|
-
float: (field) => `(${field} != null && ${field} !== 0)`,
|
|
420
|
-
int32: (field) => `(${field} != null && ${field} !== 0)`,
|
|
421
|
-
int64: (field) => `(${field} != null && ${field} !== 0n)`,
|
|
422
|
-
sfixed32: (field) => `(${field} != null && ${field} !== 0)`,
|
|
423
|
-
sfixed64: (field) => `(${field} != null && ${field} !== 0n)`,
|
|
424
|
-
sint32: (field) => `(${field} != null && ${field} !== 0)`,
|
|
425
|
-
sint64: (field) => `(${field} != null && ${field} !== 0n)`,
|
|
426
|
-
string: (field) => `(${field} != null && ${field} !== '')`,
|
|
427
|
-
uint32: (field) => `(${field} != null && ${field} !== 0)`,
|
|
428
|
-
uint64: (field) => `(${field} != null && ${field} !== 0n)`
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
const defaultValueTestGeneratorsJsTypeOverrides: Record<string, (field: string) => string> = {
|
|
432
|
-
number: (field) => `(${field} != null && ${field} !== 0)`,
|
|
433
|
-
string: (field) => `(${field} != null && ${field} !== '')`
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
function findJsTypeOverride (defaultType: string, fieldDef: FieldDef): 'number' | 'string' | undefined {
|
|
437
|
-
if (fieldDef.options?.jstype != null && jsTypeOverrides[fieldDef.options?.jstype] != null) {
|
|
438
|
-
if (!['int64', 'uint64', 'sint64', 'fixed64', 'sfixed64'].includes(defaultType)) {
|
|
439
|
-
throw new Error(`jstype is only allowed on int64, uint64, sint64, fixed64 or sfixed64 fields - got "${defaultType}"`)
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
return jsTypeOverrides[fieldDef.options?.jstype]
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
function findJsTypeName (typeName: string, classDef: MessageDef, moduleDef: ModuleDef, fieldDef: FieldDef): string {
|
|
447
|
-
const override = findJsTypeOverride(typeName, fieldDef)
|
|
448
|
-
|
|
449
|
-
if (override != null) {
|
|
450
|
-
return override
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
if (types[typeName] != null) {
|
|
454
|
-
return types[typeName]
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
if (isEnumDef(classDef)) {
|
|
458
|
-
throw new Error('Could not find type in enum')
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
if (classDef.nested?.[typeName] != null) {
|
|
462
|
-
return `${classDef.fullName}.${typeName}`
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
if (classDef.parent != null) {
|
|
466
|
-
return findJsTypeName(typeName, classDef.parent, moduleDef, fieldDef)
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
if (moduleDef.globals[typeName] != null) {
|
|
470
|
-
return typeName
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
throw new Error(`Could not resolve type name "${typeName}"`)
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
function findDef (typeName: string, classDef: MessageDef, moduleDef: ModuleDef): MessageDef {
|
|
477
|
-
if (isEnumDef(classDef)) {
|
|
478
|
-
throw new Error('Could not find type in enum')
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
if (classDef.nested?.[typeName] != null) {
|
|
482
|
-
return classDef.nested?.[typeName]
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
if (classDef.parent != null) {
|
|
486
|
-
return findDef(typeName, classDef.parent, moduleDef)
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
if (moduleDef.globals[typeName] != null) {
|
|
490
|
-
return moduleDef.globals[typeName]
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
throw new Error(`Could not resolve type name "${typeName}"`)
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
function createDefaultObject (fields: Record<string, FieldDef>, messageDef: MessageDef, moduleDef: ModuleDef): string {
|
|
497
|
-
const output = Object.entries(fields)
|
|
498
|
-
.map(([name, fieldDef]) => {
|
|
499
|
-
if (fieldDef.map) {
|
|
500
|
-
return `${name}: new Map<${types[fieldDef.keyType ?? 'string']}, ${types[fieldDef.valueType]}>()`
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
if (fieldDef.repeated) {
|
|
504
|
-
return `${name}: []`
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
if (fieldDef.optional) {
|
|
508
|
-
return ''
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
const type: string = fieldDef.type
|
|
512
|
-
let defaultValue
|
|
513
|
-
let defaultValueGenerator = defaultValueGenerators[type]
|
|
514
|
-
|
|
515
|
-
if (defaultValueGenerator != null) {
|
|
516
|
-
const jsTypeOverride = findJsTypeOverride(type, fieldDef)
|
|
517
|
-
|
|
518
|
-
if (jsTypeOverride != null && defaultValueGeneratorsJsTypeOverrides[jsTypeOverride] != null) {
|
|
519
|
-
defaultValueGenerator = defaultValueGeneratorsJsTypeOverrides[jsTypeOverride]
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
if (type === 'bytes') {
|
|
523
|
-
moduleDef.addImport('uint8arrays/alloc', 'alloc', 'uint8ArrayAlloc')
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
defaultValue = defaultValueGenerator()
|
|
527
|
-
} else {
|
|
528
|
-
const def = findDef(fieldDef.type, messageDef, moduleDef)
|
|
529
|
-
|
|
530
|
-
if (isEnumDef(def)) {
|
|
531
|
-
// select lowest-value enum - should be 0 but it's not guaranteed
|
|
532
|
-
const val = Object.entries(def.values)
|
|
533
|
-
.sort((a, b) => {
|
|
534
|
-
if (a[1] < b[1]) {
|
|
535
|
-
return 1
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
if (a[1] > b[1]) {
|
|
539
|
-
return -1
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
return 0
|
|
543
|
-
})
|
|
544
|
-
.pop()
|
|
545
|
-
|
|
546
|
-
if (val == null) {
|
|
547
|
-
throw new Error(`Could not find default enum value for ${def.fullName}`)
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
defaultValue = `${def.name}.${val[0]}`
|
|
551
|
-
} else {
|
|
552
|
-
defaultValue = 'undefined'
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
return `${name}: ${defaultValue}`
|
|
557
|
-
})
|
|
558
|
-
.filter(Boolean)
|
|
559
|
-
.join(',\n ')
|
|
560
|
-
|
|
561
|
-
if (output !== '') {
|
|
562
|
-
return `
|
|
563
|
-
${output}
|
|
564
|
-
`
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
return ''
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
const encoders: Record<string, string> = {
|
|
571
|
-
bool: 'bool',
|
|
572
|
-
bytes: 'bytes',
|
|
573
|
-
double: 'double',
|
|
574
|
-
fixed32: 'fixed32',
|
|
575
|
-
fixed64: 'fixed64',
|
|
576
|
-
float: 'float',
|
|
577
|
-
int32: 'int32',
|
|
578
|
-
int64: 'int64',
|
|
579
|
-
sfixed32: 'sfixed32',
|
|
580
|
-
sfixed64: 'sfixed64',
|
|
581
|
-
sint32: 'sint32',
|
|
582
|
-
sint64: 'sint64',
|
|
583
|
-
string: 'string',
|
|
584
|
-
uint32: 'uint32',
|
|
585
|
-
uint64: 'uint64'
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
const codecTypes: Record<string, CODEC_TYPES> = {
|
|
589
|
-
bool: CODEC_TYPES.VARINT,
|
|
590
|
-
bytes: CODEC_TYPES.LENGTH_DELIMITED,
|
|
591
|
-
double: CODEC_TYPES.BIT64,
|
|
592
|
-
enum: CODEC_TYPES.VARINT,
|
|
593
|
-
fixed32: CODEC_TYPES.BIT32,
|
|
594
|
-
fixed64: CODEC_TYPES.BIT64,
|
|
595
|
-
float: CODEC_TYPES.BIT32,
|
|
596
|
-
int32: CODEC_TYPES.VARINT,
|
|
597
|
-
int64: CODEC_TYPES.VARINT,
|
|
598
|
-
message: CODEC_TYPES.LENGTH_DELIMITED,
|
|
599
|
-
sfixed32: CODEC_TYPES.BIT32,
|
|
600
|
-
sfixed64: CODEC_TYPES.BIT64,
|
|
601
|
-
sint32: CODEC_TYPES.VARINT,
|
|
602
|
-
sint64: CODEC_TYPES.VARINT,
|
|
603
|
-
string: CODEC_TYPES.LENGTH_DELIMITED,
|
|
604
|
-
uint32: CODEC_TYPES.VARINT,
|
|
605
|
-
uint64: CODEC_TYPES.VARINT
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
interface ClassDef {
|
|
609
|
-
name: string
|
|
610
|
-
fullName: string
|
|
611
|
-
parent?: ClassDef
|
|
612
|
-
fields?: Record<string, FieldDef>
|
|
613
|
-
nested?: Record<string, ClassDef>
|
|
614
|
-
oneofs?: Array<string[]>
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
interface EnumDef {
|
|
618
|
-
name: string
|
|
619
|
-
fullName: string
|
|
620
|
-
parent?: ClassDef
|
|
621
|
-
values: Record<string, number>
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
type MessageDef = ClassDef | EnumDef
|
|
625
|
-
|
|
626
|
-
function isEnumDef (obj: any): obj is EnumDef {
|
|
627
|
-
return obj.values != null
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
interface FieldDef {
|
|
631
|
-
type: string
|
|
632
|
-
id: number
|
|
633
|
-
options?: Record<string, any>
|
|
634
|
-
rule: string
|
|
635
|
-
optional: boolean
|
|
636
|
-
repeated: boolean
|
|
637
|
-
lengthLimit?: number
|
|
638
|
-
message: boolean
|
|
639
|
-
enum: boolean
|
|
640
|
-
map: boolean
|
|
641
|
-
valueType: string
|
|
642
|
-
keyType: string
|
|
643
|
-
|
|
644
|
-
/**
|
|
645
|
-
* Support proto2 required field. This field means a value should always be
|
|
646
|
-
* in the serialized buffer, any message without it should be considered
|
|
647
|
-
* invalid. It was removed for proto3.
|
|
648
|
-
*/
|
|
649
|
-
proto2Required: boolean
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
function defineFields (fields: Record<string, FieldDef>, messageDef: MessageDef, moduleDef: ModuleDef): string[] {
|
|
653
|
-
return Object.entries(fields).map(([fieldName, fieldDef]) => {
|
|
654
|
-
if (fieldDef.map) {
|
|
655
|
-
return `${fieldName}: Map<${findJsTypeName(fieldDef.keyType ?? 'string', messageDef, moduleDef, fieldDef)}, ${findJsTypeName(fieldDef.valueType, messageDef, moduleDef, fieldDef)}>`
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
return `${fieldName}${fieldDef.optional ? '?' : ''}: ${findJsTypeName(fieldDef.type, messageDef, moduleDef, fieldDef)}${fieldDef.repeated ? '[]' : ''}`
|
|
659
|
-
})
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
function compileMessage (messageDef: MessageDef, moduleDef: ModuleDef, flags?: Flags): string {
|
|
663
|
-
if (isEnumDef(messageDef)) {
|
|
664
|
-
moduleDef.addImport('protons-runtime', 'enumeration')
|
|
665
|
-
|
|
666
|
-
// check that the enum def values start from 0
|
|
667
|
-
if (Object.values(messageDef.values)[0] !== 0) {
|
|
668
|
-
const message = `enum ${messageDef.name} does not contain a value that maps to zero as it's first element, this is required in proto3 - see https://protobuf.dev/programming-guides/proto3/#enum`
|
|
669
|
-
|
|
670
|
-
if (flags?.strict === true) {
|
|
671
|
-
throw new ParseError(message)
|
|
672
|
-
} else {
|
|
673
|
-
// eslint-disable-next-line no-console
|
|
674
|
-
console.info(`[WARN] ${message}`)
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
return `
|
|
679
|
-
export enum ${messageDef.name} {
|
|
680
|
-
${
|
|
681
|
-
Object.keys(messageDef.values).map(name => {
|
|
682
|
-
return `${name} = '${name}'`
|
|
683
|
-
}).join(',\n ').trim()
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
enum __${messageDef.name}Values {
|
|
688
|
-
${
|
|
689
|
-
Object.entries(messageDef.values).map(([name, value]) => {
|
|
690
|
-
return `${name} = ${value}`
|
|
691
|
-
}).join(',\n ').trim()
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
export namespace ${messageDef.name} {
|
|
696
|
-
export const codec = (): Codec<${messageDef.name}> => {
|
|
697
|
-
return enumeration<${messageDef.name}>(__${messageDef.name}Values)
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
`.trimStart()
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
let nested = ''
|
|
704
|
-
|
|
705
|
-
if (messageDef.nested != null) {
|
|
706
|
-
nested = '\n'
|
|
707
|
-
nested += Object.values(messageDef.nested)
|
|
708
|
-
.map(def => compileMessage(def, moduleDef, flags).trim())
|
|
709
|
-
.join('\n\n')
|
|
710
|
-
.split('\n')
|
|
711
|
-
.map(line => line.trim() === '' ? '' : ` ${line}`)
|
|
712
|
-
.join('\n')
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
const fields = messageDef.fields ?? {}
|
|
716
|
-
|
|
717
|
-
// import relevant modules
|
|
718
|
-
moduleDef.addImport('protons-runtime', 'encodeMessage')
|
|
719
|
-
moduleDef.addImport('protons-runtime', 'decodeMessage')
|
|
720
|
-
moduleDef.addImport('protons-runtime', 'message')
|
|
721
|
-
moduleDef.addTypeImport('protons-runtime', 'Codec')
|
|
722
|
-
moduleDef.addTypeImport('protons-runtime', 'DecodeOptions')
|
|
723
|
-
moduleDef.addTypeImport('uint8arraylist', 'Uint8ArrayList')
|
|
724
|
-
|
|
725
|
-
const interfaceFields = defineFields(fields, messageDef, moduleDef)
|
|
726
|
-
.join('\n ')
|
|
727
|
-
.trim()
|
|
728
|
-
|
|
729
|
-
let interfaceDef = ''
|
|
730
|
-
let interfaceCodecDef = ''
|
|
731
|
-
|
|
732
|
-
if (interfaceFields === '') {
|
|
733
|
-
interfaceDef = `
|
|
734
|
-
export interface ${messageDef.name} {}`
|
|
735
|
-
} else {
|
|
736
|
-
interfaceDef = `
|
|
737
|
-
export interface ${messageDef.name} {
|
|
738
|
-
${
|
|
739
|
-
defineFields(fields, messageDef, moduleDef)
|
|
740
|
-
.join('\n ')
|
|
741
|
-
.trim()
|
|
742
|
-
}
|
|
743
|
-
}`
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
const encodeFields = Object.entries(fields)
|
|
747
|
-
.map(([name, fieldDef]) => {
|
|
748
|
-
let codec: string = encoders[fieldDef.type]
|
|
749
|
-
let type: string = fieldDef.map ? 'message' : fieldDef.type
|
|
750
|
-
let typeName: string = ''
|
|
751
|
-
|
|
752
|
-
if (codec == null) {
|
|
753
|
-
if (fieldDef.enum) {
|
|
754
|
-
moduleDef.addImport('protons-runtime', 'enumeration')
|
|
755
|
-
type = 'enum'
|
|
756
|
-
} else {
|
|
757
|
-
moduleDef.addImport('protons-runtime', 'message')
|
|
758
|
-
type = 'message'
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
typeName = findJsTypeName(fieldDef.type, messageDef, moduleDef, fieldDef)
|
|
762
|
-
codec = `${typeName}.codec()`
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
let valueTest = `obj.${name} != null`
|
|
766
|
-
|
|
767
|
-
if (fieldDef.map) {
|
|
768
|
-
valueTest = `obj.${name} != null && obj.${name}.size !== 0`
|
|
769
|
-
} else if (!fieldDef.optional && !fieldDef.repeated && !fieldDef.proto2Required) {
|
|
770
|
-
let defaultValueTestGenerator = defaultValueTestGenerators[type]
|
|
771
|
-
|
|
772
|
-
// proto3 singular fields should only be written out if they are not the default value
|
|
773
|
-
if (defaultValueTestGenerator != null) {
|
|
774
|
-
const jsTypeOverride = findJsTypeOverride(type, fieldDef)
|
|
775
|
-
|
|
776
|
-
if (jsTypeOverride != null && defaultValueTestGeneratorsJsTypeOverrides[jsTypeOverride] != null) {
|
|
777
|
-
defaultValueTestGenerator = defaultValueTestGeneratorsJsTypeOverrides[jsTypeOverride]
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
valueTest = `${defaultValueTestGenerator(`obj.${name}`)}`
|
|
781
|
-
} else if (type === 'enum') {
|
|
782
|
-
// handle enums
|
|
783
|
-
const def = findDef(fieldDef.type, messageDef, moduleDef)
|
|
784
|
-
|
|
785
|
-
if (!isEnumDef(def)) {
|
|
786
|
-
throw new Error(`${fieldDef.type} was not enum def`)
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
valueTest = `obj.${name} != null`
|
|
790
|
-
|
|
791
|
-
// singular enums default to 0, but enums can be defined without a 0
|
|
792
|
-
// value which is against the proto3 spec but is tolerated
|
|
793
|
-
if (Object.values(def.values)[0] === 0) {
|
|
794
|
-
valueTest += ` && __${fieldDef.type}Values[obj.${name}] !== 0`
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
function createWriteField (valueVar: string): () => string {
|
|
800
|
-
const id = (fieldDef.id << 3) | codecTypes[type]
|
|
801
|
-
|
|
802
|
-
if (fieldDef.enum) {
|
|
803
|
-
const def = findDef(fieldDef.type, messageDef, moduleDef)
|
|
804
|
-
|
|
805
|
-
if (!isEnumDef(def)) {
|
|
806
|
-
throw new Error(`${fieldDef.type} was not enum def`)
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
let writeField = (): string => {
|
|
811
|
-
const encoderGenerator = encoderGenerators[type]
|
|
812
|
-
const jsTypeOverride = findJsTypeOverride(type, fieldDef)
|
|
813
|
-
|
|
814
|
-
return `w.uint32(${id})
|
|
815
|
-
${encoderGenerator == null ? `${codec}.encode(${valueVar}, w)` : encoderGenerator(valueVar, jsTypeOverride)}`
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
if (type === 'message') {
|
|
819
|
-
// message fields are only written if they have values. But if a message
|
|
820
|
-
// is part of a repeated field, and consists of only default values it
|
|
821
|
-
// won't be written, so write a zero-length buffer if that's the case
|
|
822
|
-
writeField = (): string => `w.uint32(${id})
|
|
823
|
-
${typeName}.codec().encode(${valueVar}, w)`
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
return writeField
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
let writeField = createWriteField(`obj.${name}`)
|
|
830
|
-
|
|
831
|
-
if (fieldDef.repeated) {
|
|
832
|
-
if (fieldDef.map) {
|
|
833
|
-
writeField = () => `
|
|
834
|
-
for (const [key, value] of obj.${name}.entries()) {
|
|
835
|
-
${
|
|
836
|
-
createWriteField('{ key, value }')()
|
|
837
|
-
.split('\n')
|
|
838
|
-
.map(s => {
|
|
839
|
-
const trimmed = s.trim()
|
|
840
|
-
|
|
841
|
-
return trimmed === '' ? trimmed : ` ${s}`
|
|
842
|
-
})
|
|
843
|
-
.join('\n')
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
`.trim()
|
|
847
|
-
} else {
|
|
848
|
-
writeField = () => `
|
|
849
|
-
for (const value of obj.${name}) {
|
|
850
|
-
${
|
|
851
|
-
createWriteField('value')()
|
|
852
|
-
.split('\n')
|
|
853
|
-
.map(s => {
|
|
854
|
-
const trimmed = s.trim()
|
|
855
|
-
|
|
856
|
-
return trimmed === '' ? trimmed : ` ${s}`
|
|
857
|
-
})
|
|
858
|
-
.join('\n')
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
`.trim()
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
return `
|
|
866
|
-
if (${valueTest}) {
|
|
867
|
-
${writeField()}
|
|
868
|
-
}`
|
|
869
|
-
}).join('\n')
|
|
870
|
-
|
|
871
|
-
const enforceOneOfEncoding = createOneOfEncoding(messageDef)
|
|
872
|
-
const enforceOneOfDecoding = createOneOfDecoding(messageDef)
|
|
873
|
-
|
|
874
|
-
const decodeFields = Object.entries(fields)
|
|
875
|
-
.map(([fieldName, fieldDef]) => {
|
|
876
|
-
function createReadField (fieldName: string, fieldDef: FieldDef): string {
|
|
877
|
-
let codec: string = encoders[fieldDef.type]
|
|
878
|
-
let type: string = fieldDef.type
|
|
879
|
-
|
|
880
|
-
if (codec == null) {
|
|
881
|
-
if (fieldDef.enum) {
|
|
882
|
-
moduleDef.addImport('protons-runtime', 'enumeration')
|
|
883
|
-
type = 'enum'
|
|
884
|
-
} else {
|
|
885
|
-
moduleDef.addImport('protons-runtime', 'message')
|
|
886
|
-
type = 'message'
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
const typeName = findJsTypeName(fieldDef.type, messageDef, moduleDef, fieldDef)
|
|
890
|
-
codec = `${typeName}.codec()`
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
// override setting type on js object
|
|
894
|
-
const jsTypeOverride = findJsTypeOverride(fieldDef.type, fieldDef)
|
|
895
|
-
|
|
896
|
-
let fieldOpts = ''
|
|
897
|
-
|
|
898
|
-
if (fieldDef.message) {
|
|
899
|
-
let suffix = ''
|
|
900
|
-
|
|
901
|
-
if (fieldDef.repeated) {
|
|
902
|
-
suffix = '$'
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
fieldOpts = `, {
|
|
906
|
-
limits: opts.limits?.${fieldName}${suffix}
|
|
907
|
-
}`
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
if (fieldDef.map) {
|
|
911
|
-
fieldOpts = `, {
|
|
912
|
-
limits: {
|
|
913
|
-
value: opts.limits?.${fieldName}$value
|
|
914
|
-
}
|
|
915
|
-
}`
|
|
916
|
-
|
|
917
|
-
// do not pass limit opts to map value types that are enums or
|
|
918
|
-
// primitives - only support messages
|
|
919
|
-
if (types[fieldDef.valueType] != null) {
|
|
920
|
-
// primmitive type
|
|
921
|
-
fieldOpts = ''
|
|
922
|
-
} else {
|
|
923
|
-
const valueType = findDef(fieldDef.valueType, messageDef, moduleDef)
|
|
924
|
-
|
|
925
|
-
if (isEnumDef(valueType)) {
|
|
926
|
-
// enum type
|
|
927
|
-
fieldOpts = ''
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
const parseValue = `${decoderGenerators[type] == null
|
|
933
|
-
? `${codec}.decode(reader${type === 'message'
|
|
934
|
-
? `, reader.uint32()${fieldOpts}`
|
|
935
|
-
: ''})`
|
|
936
|
-
: decoderGenerators[type](jsTypeOverride)}`
|
|
937
|
-
|
|
938
|
-
if (fieldDef.map) {
|
|
939
|
-
moduleDef.addImport('protons-runtime', 'MaxSizeError')
|
|
940
|
-
|
|
941
|
-
let limit = `
|
|
942
|
-
if (opts.limits?.${fieldName} != null && obj.${fieldName}.size === opts.limits.${fieldName}) {
|
|
943
|
-
throw new MaxSizeError('Decode error - map field "${fieldName}" had too many elements')
|
|
944
|
-
}
|
|
945
|
-
`
|
|
946
|
-
|
|
947
|
-
if (fieldDef.lengthLimit != null) {
|
|
948
|
-
limit += `
|
|
949
|
-
if (obj.${fieldName}.size === ${fieldDef.lengthLimit}) {
|
|
950
|
-
throw new MaxSizeError('Decode error - map field "${fieldName}" had too many elements')
|
|
951
|
-
}
|
|
952
|
-
`
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
return `case ${fieldDef.id}: {${limit}
|
|
956
|
-
const entry = ${parseValue}
|
|
957
|
-
obj.${fieldName}.set(entry.key, entry.value)
|
|
958
|
-
break
|
|
959
|
-
}`
|
|
960
|
-
} else if (fieldDef.repeated) {
|
|
961
|
-
moduleDef.addImport('protons-runtime', 'MaxLengthError')
|
|
962
|
-
|
|
963
|
-
let limit = `
|
|
964
|
-
if (opts.limits?.${fieldName} != null && obj.${fieldName}.length === opts.limits.${fieldName}) {
|
|
965
|
-
throw new MaxLengthError('Decode error - map field "${fieldName}" had too many elements')
|
|
966
|
-
}
|
|
967
|
-
`
|
|
968
|
-
|
|
969
|
-
if (fieldDef.lengthLimit != null) {
|
|
970
|
-
limit += `
|
|
971
|
-
if (obj.${fieldName}.length === ${fieldDef.lengthLimit}) {
|
|
972
|
-
throw new MaxLengthError('Decode error - repeated field "${fieldName}" had too many elements')
|
|
973
|
-
}
|
|
974
|
-
`
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
return `case ${fieldDef.id}: {${limit}
|
|
978
|
-
obj.${fieldName}.push(${parseValue})
|
|
979
|
-
break
|
|
980
|
-
}`
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
return `case ${fieldDef.id}: {
|
|
984
|
-
obj.${fieldName} = ${parseValue}
|
|
985
|
-
break
|
|
986
|
-
}`
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
return createReadField(fieldName, fieldDef)
|
|
990
|
-
})
|
|
991
|
-
.join('\n ')
|
|
992
|
-
|
|
993
|
-
interfaceCodecDef = `
|
|
994
|
-
let _codec: Codec<${messageDef.name}>
|
|
995
|
-
|
|
996
|
-
export const codec = (): Codec<${messageDef.name}> => {
|
|
997
|
-
if (_codec == null) {
|
|
998
|
-
_codec = message<${messageDef.name}>((obj, w, opts = {}) => {
|
|
999
|
-
if (opts.lengthDelimited !== false) {
|
|
1000
|
-
w.fork()
|
|
1001
|
-
}
|
|
1002
|
-
${enforceOneOfEncoding}${encodeFields === '' ? '' : `${encodeFields}\n`}
|
|
1003
|
-
if (opts.lengthDelimited !== false) {
|
|
1004
|
-
w.ldelim()
|
|
1005
|
-
}
|
|
1006
|
-
}, (reader, length, opts = {}) => {
|
|
1007
|
-
const obj: any = {${createDefaultObject(fields, messageDef, moduleDef)}}
|
|
1008
|
-
|
|
1009
|
-
const end = length == null ? reader.len : reader.pos + length
|
|
1010
|
-
|
|
1011
|
-
while (reader.pos < end) {
|
|
1012
|
-
const tag = reader.uint32()
|
|
1013
|
-
|
|
1014
|
-
switch (tag >>> 3) {${decodeFields === '' ? '' : `\n ${decodeFields}`}
|
|
1015
|
-
default: {
|
|
1016
|
-
reader.skipType(tag & 7)
|
|
1017
|
-
break
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
${enforceOneOfDecoding === '' ? '' : `${enforceOneOfDecoding}\n`}
|
|
1022
|
-
return obj
|
|
1023
|
-
})
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
return _codec
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
export const encode = (obj: Partial<${messageDef.name}>): Uint8Array => {
|
|
1030
|
-
return encodeMessage(obj, ${messageDef.name}.codec())
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<${messageDef.name}>): ${messageDef.name} => {
|
|
1034
|
-
return decodeMessage(buf, ${messageDef.name}.codec(), opts)
|
|
1035
|
-
}`
|
|
1036
|
-
|
|
1037
|
-
return `
|
|
1038
|
-
${interfaceDef}
|
|
1039
|
-
|
|
1040
|
-
export namespace ${messageDef.name} {
|
|
1041
|
-
${`${nested}${nested !== '' && interfaceCodecDef !== '' ? '\n' : ''}${interfaceCodecDef}`.trim()}
|
|
1042
|
-
}
|
|
1043
|
-
`.trimStart()
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
interface Import {
|
|
1047
|
-
symbol: string
|
|
1048
|
-
alias?: string
|
|
1049
|
-
type: boolean
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
class ModuleDef {
|
|
1053
|
-
imports: Map<string, Import[]>
|
|
1054
|
-
types: Set<string>
|
|
1055
|
-
compiled: string[]
|
|
1056
|
-
globals: Record<string, ClassDef>
|
|
1057
|
-
|
|
1058
|
-
constructor () {
|
|
1059
|
-
this.imports = new Map()
|
|
1060
|
-
this.types = new Set()
|
|
1061
|
-
this.compiled = []
|
|
1062
|
-
this.globals = {}
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
addImport (module: string, symbol: string, alias?: string): void {
|
|
1066
|
-
const defs = this._findDefs(module)
|
|
1067
|
-
|
|
1068
|
-
for (const def of defs) {
|
|
1069
|
-
// check if we already have a definition for this symbol
|
|
1070
|
-
if (def.symbol === symbol) {
|
|
1071
|
-
if (alias !== def.alias) {
|
|
1072
|
-
throw new Error(`Type symbol ${symbol} imported from ${module} with alias ${def.alias} does not match alias ${alias}`)
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
// if it was a type before it's not now
|
|
1076
|
-
def.type = false
|
|
1077
|
-
return
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
defs.push({
|
|
1082
|
-
symbol,
|
|
1083
|
-
alias,
|
|
1084
|
-
type: false
|
|
1085
|
-
})
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
addTypeImport (module: string, symbol: string, alias?: string): void {
|
|
1089
|
-
const defs = this._findDefs(module)
|
|
1090
|
-
|
|
1091
|
-
for (const def of defs) {
|
|
1092
|
-
// check if we already have a definition for this symbol
|
|
1093
|
-
if (def.symbol === symbol) {
|
|
1094
|
-
if (alias !== def.alias) {
|
|
1095
|
-
throw new Error(`Type symbol ${symbol} imported from ${module} with alias ${def.alias} does not match alias ${alias}`)
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
return
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
defs.push({
|
|
1103
|
-
symbol,
|
|
1104
|
-
alias,
|
|
1105
|
-
type: true
|
|
1106
|
-
})
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
_findDefs (module: string): Import[] {
|
|
1110
|
-
let defs = this.imports.get(module)
|
|
1111
|
-
|
|
1112
|
-
if (defs == null) {
|
|
1113
|
-
defs = []
|
|
1114
|
-
this.imports.set(module, defs)
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
return defs
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
function defineModule (def: ClassDef, flags: Flags): ModuleDef {
|
|
1122
|
-
const moduleDef = new ModuleDef()
|
|
1123
|
-
const defs = def.nested
|
|
1124
|
-
|
|
1125
|
-
if (defs == null) {
|
|
1126
|
-
throw new NoMessagesFoundError('No top-level messages found in protobuf')
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
function defineMessage (defs: Record<string, ClassDef>, parent?: ClassDef, flags?: Flags): void {
|
|
1130
|
-
for (const className of Object.keys(defs)) {
|
|
1131
|
-
const classDef = defs[className]
|
|
1132
|
-
|
|
1133
|
-
classDef.name = className
|
|
1134
|
-
classDef.parent = parent
|
|
1135
|
-
classDef.fullName = parent == null ? className : `${parent.fullName}.${className}`
|
|
1136
|
-
|
|
1137
|
-
if (classDef.nested != null) {
|
|
1138
|
-
defineMessage(classDef.nested, classDef)
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
if (classDef.fields != null) {
|
|
1142
|
-
for (const name of Object.keys(classDef.fields)) {
|
|
1143
|
-
const fieldDef = classDef.fields[name]
|
|
1144
|
-
fieldDef.repeated = fieldDef.rule === 'repeated'
|
|
1145
|
-
fieldDef.optional = !fieldDef.repeated && fieldDef.options?.proto3_optional === true
|
|
1146
|
-
fieldDef.map = fieldDef.keyType != null
|
|
1147
|
-
fieldDef.lengthLimit = fieldDef.options?.['(protons.options).limit']
|
|
1148
|
-
fieldDef.proto2Required = false
|
|
1149
|
-
|
|
1150
|
-
if (fieldDef.rule === 'required') {
|
|
1151
|
-
const message = `field "${name}" is required, this is not allowed in proto3. Please convert your proto2 definitions to proto3 - see https://github.com/ipfs/protons/wiki/Required-fields-and-protobuf-3`
|
|
1152
|
-
|
|
1153
|
-
if (flags?.strict === true) {
|
|
1154
|
-
throw new ParseError(message)
|
|
1155
|
-
} else {
|
|
1156
|
-
fieldDef.proto2Required = true
|
|
1157
|
-
|
|
1158
|
-
// eslint-disable-next-line no-console
|
|
1159
|
-
console.info(`[WARN] ${message}`)
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
if (parent == null) {
|
|
1166
|
-
moduleDef.globals[className] = classDef
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
function updateTypes (defs: Record<string, ClassDef>, parent?: ClassDef): void {
|
|
1172
|
-
for (const className of Object.keys(defs)) {
|
|
1173
|
-
const classDef = defs[className]
|
|
1174
|
-
|
|
1175
|
-
if (classDef.nested != null) {
|
|
1176
|
-
updateTypes(classDef.nested, classDef)
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
if (classDef.fields != null) {
|
|
1180
|
-
for (const name of Object.keys(classDef.fields)) {
|
|
1181
|
-
const fieldDef = classDef.fields[name]
|
|
1182
|
-
if (types[fieldDef.type] == null) {
|
|
1183
|
-
const def = findDef(fieldDef.type, classDef, moduleDef)
|
|
1184
|
-
|
|
1185
|
-
fieldDef.enum = isEnumDef(def)
|
|
1186
|
-
fieldDef.message = !fieldDef.enum
|
|
1187
|
-
|
|
1188
|
-
if (fieldDef.message && !fieldDef.repeated) {
|
|
1189
|
-
// the default type for a message is unset so they are always optional
|
|
1190
|
-
// https://developers.google.com/protocol-buffers/docs/proto3#default
|
|
1191
|
-
fieldDef.optional = true
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
defineMessage(defs, undefined, flags)
|
|
1200
|
-
|
|
1201
|
-
// set enum/message fields now all messages have been defined
|
|
1202
|
-
updateTypes(defs)
|
|
1203
|
-
|
|
1204
|
-
for (const className of Object.keys(defs)) {
|
|
1205
|
-
const classDef = defs[className]
|
|
1206
|
-
|
|
1207
|
-
moduleDef.compiled.push(compileMessage(classDef, moduleDef, flags))
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
return moduleDef
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
interface Flags {
|
|
205
|
+
export interface Flags {
|
|
1214
206
|
/**
|
|
1215
207
|
* Specifies an output directory
|
|
1216
208
|
*/
|
|
@@ -1240,197 +232,9 @@ export async function generate (source: string, flags: Flags): Promise<void> {
|
|
|
1240
232
|
}
|
|
1241
233
|
|
|
1242
234
|
const def = JSON.parse(json)
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
for (const [fieldName, fieldDef] of Object.entries<any>(classDef.fields ?? {})) {
|
|
1246
|
-
if (fieldDef.keyType == null) {
|
|
1247
|
-
continue
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
// https://developers.google.com/protocol-buffers/docs/proto3#backwards_compatibility
|
|
1251
|
-
const mapEntryType = `${className}$${fieldName}Entry`
|
|
1252
|
-
|
|
1253
|
-
classDef.nested = classDef.nested ?? {}
|
|
1254
|
-
classDef.nested[mapEntryType] = {
|
|
1255
|
-
fields: {
|
|
1256
|
-
key: {
|
|
1257
|
-
type: fieldDef.keyType,
|
|
1258
|
-
id: 1
|
|
1259
|
-
},
|
|
1260
|
-
value: {
|
|
1261
|
-
type: fieldDef.type,
|
|
1262
|
-
id: 2
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
fieldDef.valueType = fieldDef.type
|
|
1268
|
-
fieldDef.type = mapEntryType
|
|
1269
|
-
fieldDef.rule = 'repeated'
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
configureOnOfs(classDef)
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
const moduleDef = defineModule(def, flags)
|
|
1276
|
-
|
|
1277
|
-
const ignores = [
|
|
1278
|
-
'/* eslint-disable import/export */',
|
|
1279
|
-
'/* eslint-disable complexity */',
|
|
1280
|
-
'/* eslint-disable @typescript-eslint/no-namespace */',
|
|
1281
|
-
'/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */',
|
|
1282
|
-
'/* eslint-disable @typescript-eslint/no-empty-interface */',
|
|
1283
|
-
'/* eslint-disable import/consistent-type-specifier-style */',
|
|
1284
|
-
'/* eslint-disable @typescript-eslint/no-unused-vars */'
|
|
1285
|
-
]
|
|
1286
|
-
|
|
1287
|
-
const imports = []
|
|
1288
|
-
const importedModules = Array.from([...moduleDef.imports.entries()])
|
|
1289
|
-
.sort((a, b) => {
|
|
1290
|
-
return a[0].localeCompare(b[0])
|
|
1291
|
-
})
|
|
1292
|
-
.sort((a, b) => {
|
|
1293
|
-
const aAllTypes = a[1].reduce((acc, curr) => {
|
|
1294
|
-
return acc && curr.type
|
|
1295
|
-
}, true)
|
|
1296
|
-
|
|
1297
|
-
const bAllTypes = b[1].reduce((acc, curr) => {
|
|
1298
|
-
return acc && curr.type
|
|
1299
|
-
}, true)
|
|
1300
|
-
|
|
1301
|
-
if (aAllTypes && !bAllTypes) {
|
|
1302
|
-
return 1
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
if (!aAllTypes && bAllTypes) {
|
|
1306
|
-
return -1
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
return 0
|
|
1310
|
-
})
|
|
1311
|
-
|
|
1312
|
-
// add imports
|
|
1313
|
-
for (const imp of importedModules) {
|
|
1314
|
-
const symbols = imp[1]
|
|
1315
|
-
.filter(imp => !imp.type)
|
|
1316
|
-
.sort((a, b) => {
|
|
1317
|
-
return a.symbol.localeCompare(b.symbol)
|
|
1318
|
-
}).map(imp => {
|
|
1319
|
-
return `${imp.symbol}${imp.alias != null ? ` as ${imp.alias}` : ''}`
|
|
1320
|
-
}).join(', ')
|
|
1321
|
-
|
|
1322
|
-
if (symbols.length > 0) {
|
|
1323
|
-
imports.push(`import { ${symbols} } from '${imp[0]}'`)
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
// add type imports
|
|
1328
|
-
for (const imp of importedModules) {
|
|
1329
|
-
const symbols = imp[1]
|
|
1330
|
-
.filter(imp => imp.type)
|
|
1331
|
-
.sort((a, b) => {
|
|
1332
|
-
return a.symbol.localeCompare(b.symbol)
|
|
1333
|
-
}).map(imp => {
|
|
1334
|
-
return `${imp.symbol}${imp.alias != null ? ` as ${imp.alias}` : ''}`
|
|
1335
|
-
}).join(', ')
|
|
1336
|
-
|
|
1337
|
-
if (symbols.length > 0) {
|
|
1338
|
-
imports.push(`import type { ${symbols} } from '${imp[0]}'`)
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
const lines = [
|
|
1343
|
-
...ignores,
|
|
1344
|
-
'',
|
|
1345
|
-
...imports,
|
|
1346
|
-
'',
|
|
1347
|
-
...moduleDef.compiled
|
|
1348
|
-
]
|
|
1349
|
-
|
|
1350
|
-
const content = lines.join('\n').trim()
|
|
235
|
+
const module = new Module(def, flags)
|
|
236
|
+
const content = module.compile()
|
|
1351
237
|
const outputPath = pathWithExtension(source, '.ts', flags.output)
|
|
1352
238
|
|
|
1353
239
|
await fs.writeFile(outputPath, content + '\n')
|
|
1354
240
|
}
|
|
1355
|
-
|
|
1356
|
-
function configureOnOfs (classDef: any): void {
|
|
1357
|
-
const oneOfs: Record<string, { oneof: string[] }> = classDef.oneofs ?? {}
|
|
1358
|
-
|
|
1359
|
-
for (const [, { oneof: fields }] of Object.entries(oneOfs)) {
|
|
1360
|
-
fields.forEach(field => {
|
|
1361
|
-
classDef.fields[field].options ??= {}
|
|
1362
|
-
classDef.fields[field].options.proto3_optional = true
|
|
1363
|
-
classDef.fields[field].oneof = fields
|
|
1364
|
-
})
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
const oneofs: Record<string, { oneof: string[] }> = classDef.oneofs ?? {}
|
|
1368
|
-
|
|
1369
|
-
// reverse order of oneofs as spec says last field overwrites all others
|
|
1370
|
-
classDef.oneofs = Object.values(oneofs).map(({ oneof }) => oneof.reverse())
|
|
1371
|
-
|
|
1372
|
-
for (const nestedDef of Object.values(classDef.nested ?? {})) {
|
|
1373
|
-
configureOnOfs(nestedDef)
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
function createOneOfEncoding (messageDef: ClassDef): string {
|
|
1378
|
-
if (messageDef.oneofs == null) {
|
|
1379
|
-
return ''
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
let oneofs = messageDef.oneofs.map(fields => {
|
|
1383
|
-
if (fields.length < 2) {
|
|
1384
|
-
return ''
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
let oneof = ''
|
|
1388
|
-
|
|
1389
|
-
for (const name of fields) {
|
|
1390
|
-
oneof += `
|
|
1391
|
-
if (obj.${name} != null) {
|
|
1392
|
-
${fields
|
|
1393
|
-
.filter(field => field !== name)
|
|
1394
|
-
.map(name => ` obj.${name} = undefined`).join('\n')}
|
|
1395
|
-
}
|
|
1396
|
-
`
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
return oneof.trimEnd()
|
|
1400
|
-
}).join('\n').trimEnd()
|
|
1401
|
-
|
|
1402
|
-
if (oneofs !== '') {
|
|
1403
|
-
oneofs = `
|
|
1404
|
-
obj = { ...obj }
|
|
1405
|
-
${oneofs}
|
|
1406
|
-
`
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
return oneofs
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
function createOneOfDecoding (messageDef: ClassDef): string {
|
|
1413
|
-
if (messageDef.oneofs == null) {
|
|
1414
|
-
return ''
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
|
-
return messageDef.oneofs.map(fields => {
|
|
1418
|
-
if (fields.length < 2) {
|
|
1419
|
-
return ''
|
|
1420
|
-
}
|
|
1421
|
-
|
|
1422
|
-
let oneof = ''
|
|
1423
|
-
|
|
1424
|
-
for (const name of fields) {
|
|
1425
|
-
oneof += `
|
|
1426
|
-
if (obj.${name} != null) {
|
|
1427
|
-
${fields
|
|
1428
|
-
.filter(field => field !== name)
|
|
1429
|
-
.map(name => ` delete obj.${name}`).join('\n')}
|
|
1430
|
-
}
|
|
1431
|
-
`
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
return oneof.trimEnd()
|
|
1435
|
-
}).join('\n').trimEnd()
|
|
1436
|
-
}
|