protons 7.6.1 → 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.
Files changed (60) hide show
  1. package/README.md +8 -6
  2. package/dist/bin/protons.js +1 -1
  3. package/dist/src/fields/array-field.d.ts +18 -0
  4. package/dist/src/fields/array-field.d.ts.map +1 -0
  5. package/dist/src/fields/array-field.js +83 -0
  6. package/dist/src/fields/array-field.js.map +1 -0
  7. package/dist/src/fields/enum-field.d.ts +9 -0
  8. package/dist/src/fields/enum-field.d.ts.map +1 -0
  9. package/dist/src/fields/enum-field.js +21 -0
  10. package/dist/src/fields/enum-field.js.map +1 -0
  11. package/dist/src/fields/field.d.ts +45 -0
  12. package/dist/src/fields/field.d.ts.map +1 -0
  13. package/dist/src/fields/field.js +147 -0
  14. package/dist/src/fields/field.js.map +1 -0
  15. package/dist/src/fields/map-field.d.ts +22 -0
  16. package/dist/src/fields/map-field.d.ts.map +1 -0
  17. package/dist/src/fields/map-field.js +83 -0
  18. package/dist/src/fields/map-field.js.map +1 -0
  19. package/dist/src/fields/message-field.d.ts +9 -0
  20. package/dist/src/fields/message-field.d.ts.map +1 -0
  21. package/dist/src/fields/message-field.js +23 -0
  22. package/dist/src/fields/message-field.js.map +1 -0
  23. package/dist/src/index.d.ts +190 -16
  24. package/dist/src/index.d.ts.map +1 -1
  25. package/dist/src/index.js +8 -895
  26. package/dist/src/index.js.map +1 -1
  27. package/dist/src/types/enum.d.ts +21 -0
  28. package/dist/src/types/enum.d.ts.map +1 -0
  29. package/dist/src/types/enum.js +87 -0
  30. package/dist/src/types/enum.js.map +1 -0
  31. package/dist/src/types/index.d.ts +20 -0
  32. package/dist/src/types/index.d.ts.map +1 -0
  33. package/dist/src/types/index.js +2 -0
  34. package/dist/src/types/index.js.map +1 -0
  35. package/dist/src/types/message.d.ts +49 -0
  36. package/dist/src/types/message.d.ts.map +1 -0
  37. package/dist/src/types/message.js +478 -0
  38. package/dist/src/types/message.js.map +1 -0
  39. package/dist/src/types/module.d.ts +30 -0
  40. package/dist/src/types/module.d.ts.map +1 -0
  41. package/dist/src/types/module.js +184 -0
  42. package/dist/src/types/module.js.map +1 -0
  43. package/dist/src/types/primitive.d.ts +13 -0
  44. package/dist/src/types/primitive.d.ts.map +1 -0
  45. package/dist/src/types/primitive.js +174 -0
  46. package/dist/src/types/primitive.js.map +1 -0
  47. package/dist/typedoc-urls.json +2 -4
  48. package/package.json +104 -16
  49. package/src/fields/array-field.ts +109 -0
  50. package/src/fields/enum-field.ts +30 -0
  51. package/src/fields/field.ts +201 -0
  52. package/src/fields/map-field.ts +107 -0
  53. package/src/fields/message-field.ts +29 -0
  54. package/src/index.ts +9 -1114
  55. package/src/types/enum.ts +112 -0
  56. package/src/types/index.ts +21 -0
  57. package/src/types/message.ts +558 -0
  58. package/src/types/module.ts +234 -0
  59. package/src/types/primitive.ts +215 -0
  60. package/LICENSE +0 -4
package/dist/src/index.js CHANGED
@@ -1,4 +1,3 @@
1
- /* eslint-disable max-depth */
2
1
  /**
3
2
  * @packageDocumentation
4
3
  *
@@ -32,7 +31,7 @@
32
31
  * In your code import the generated classes and use them to transform to/from bytes:
33
32
  *
34
33
  * ```js
35
- * import { Foo } from './foo.js'
34
+ * import { Foo } from './foo.ts'
36
35
  *
37
36
  * const foo = {
38
37
  * message: 'hello world'
@@ -47,7 +46,7 @@
47
46
  *
48
47
  * ## Differences from protobuf.js
49
48
  *
50
- * This module uses the internal reader/writer from `protobuf.js` as it is highly optimised and there's no point reinventing the wheel.
49
+ * This module uses the internal reader/writer from `protobuf.js` as it is highly optimized and there's no point reinventing the wheel.
51
50
  *
52
51
  * It does have one or two differences:
53
52
  *
@@ -187,818 +186,19 @@
187
186
  *
188
187
  * ## Missing features
189
188
  *
190
- * Some features are missing `OneOf`s, etc due to them not being needed so far in ipfs/libp2p. If these features are important to you, please open PRs implementing them along with tests comparing the generated bytes to `protobuf.js` and `pbjs`.
189
+ * Some features may be missing due to them not being needed in ipfs/libp2p so far.
190
+ *
191
+ * If these features are important to you, please open PRs implementing them along with tests comparing the generated bytes to `protobuf.js` and `pbjs`.
191
192
  */
192
193
  import fs from 'fs/promises';
193
194
  import path from 'path';
194
195
  import { promisify } from 'util';
195
196
  import { main as pbjs } from 'protobufjs-cli/pbjs.js';
196
- import { NoMessagesFoundError, ParseError } from 'protons-runtime';
197
- export var CODEC_TYPES;
198
- (function (CODEC_TYPES) {
199
- CODEC_TYPES[CODEC_TYPES["VARINT"] = 0] = "VARINT";
200
- CODEC_TYPES[CODEC_TYPES["BIT64"] = 1] = "BIT64";
201
- CODEC_TYPES[CODEC_TYPES["LENGTH_DELIMITED"] = 2] = "LENGTH_DELIMITED";
202
- CODEC_TYPES[CODEC_TYPES["START_GROUP"] = 3] = "START_GROUP";
203
- CODEC_TYPES[CODEC_TYPES["END_GROUP"] = 4] = "END_GROUP";
204
- CODEC_TYPES[CODEC_TYPES["BIT32"] = 5] = "BIT32";
205
- })(CODEC_TYPES || (CODEC_TYPES = {}));
197
+ import { Module } from "./types/module.js";
206
198
  function pathWithExtension(input, extension, outputDir) {
207
199
  const output = outputDir ?? path.dirname(input);
208
200
  return path.join(output, path.basename(input).split('.').slice(0, -1).join('.') + extension);
209
201
  }
210
- /**
211
- * This will be removed in a future release
212
- *
213
- * @deprecated
214
- */
215
- export class CodeError extends Error {
216
- code;
217
- constructor(message, code, options) {
218
- super(message, options);
219
- this.code = code;
220
- }
221
- }
222
- const types = {
223
- bool: 'boolean',
224
- bytes: 'Uint8Array',
225
- double: 'number',
226
- fixed32: 'number',
227
- fixed64: 'bigint',
228
- float: 'number',
229
- int32: 'number',
230
- int64: 'bigint',
231
- sfixed32: 'number',
232
- sfixed64: 'bigint',
233
- sint32: 'number',
234
- sint64: 'bigint',
235
- string: 'string',
236
- uint32: 'number',
237
- uint64: 'bigint'
238
- };
239
- const jsTypeOverrides = {
240
- JS_NUMBER: 'number',
241
- JS_STRING: 'string'
242
- };
243
- const encoderGenerators = {
244
- bool: (val) => `w.bool(${val})`,
245
- bytes: (val) => `w.bytes(${val})`,
246
- double: (val) => `w.double(${val})`,
247
- fixed32: (val) => `w.fixed32(${val})`,
248
- fixed64: (val, jsTypeOverride) => {
249
- if (jsTypeOverride === 'number') {
250
- return `w.fixed64Number(${val})`;
251
- }
252
- if (jsTypeOverride === 'string') {
253
- return `w.fixed64String(${val})`;
254
- }
255
- return `w.fixed64(${val})`;
256
- },
257
- float: (val) => `w.float(${val})`,
258
- int32: (val) => `w.int32(${val})`,
259
- int64: (val, jsTypeOverride) => {
260
- if (jsTypeOverride === 'number') {
261
- return `w.int64Number(${val})`;
262
- }
263
- if (jsTypeOverride === 'string') {
264
- return `w.int64String(${val})`;
265
- }
266
- return `w.int64(${val})`;
267
- },
268
- sfixed32: (val) => `w.sfixed32(${val})`,
269
- sfixed64: (val, jsTypeOverride) => {
270
- if (jsTypeOverride === 'number') {
271
- return `w.sfixed64Number(${val})`;
272
- }
273
- if (jsTypeOverride === 'string') {
274
- return `w.sfixed64String(${val})`;
275
- }
276
- return `w.sfixed64(${val})`;
277
- },
278
- sint32: (val) => `w.sint32(${val})`,
279
- sint64: (val, jsTypeOverride) => {
280
- if (jsTypeOverride === 'number') {
281
- return `w.sint64Number(${val})`;
282
- }
283
- if (jsTypeOverride === 'string') {
284
- return `w.sint64String(${val})`;
285
- }
286
- return `w.sint64(${val})`;
287
- },
288
- string: (val) => `w.string(${val})`,
289
- uint32: (val) => `w.uint32(${val})`,
290
- uint64: (val, jsTypeOverride) => {
291
- if (jsTypeOverride === 'number') {
292
- return `w.uint64Number(${val})`;
293
- }
294
- if (jsTypeOverride === 'string') {
295
- return `w.uint64String(${val})`;
296
- }
297
- return `w.uint64(${val})`;
298
- }
299
- };
300
- const decoderGenerators = {
301
- bool: () => 'reader.bool()',
302
- bytes: () => 'reader.bytes()',
303
- double: () => 'reader.double()',
304
- fixed32: () => 'reader.fixed32()',
305
- fixed64: (jsTypeOverride) => {
306
- if (jsTypeOverride === 'number') {
307
- return 'reader.fixed64Number()';
308
- }
309
- if (jsTypeOverride === 'string') {
310
- return 'reader.fixed64String()';
311
- }
312
- return 'reader.fixed64()';
313
- },
314
- float: () => 'reader.float()',
315
- int32: () => 'reader.int32()',
316
- int64: (jsTypeOverride) => {
317
- if (jsTypeOverride === 'number') {
318
- return 'reader.int64Number()';
319
- }
320
- if (jsTypeOverride === 'string') {
321
- return 'reader.int64String()';
322
- }
323
- return 'reader.int64()';
324
- },
325
- sfixed32: () => 'reader.sfixed32()',
326
- sfixed64: (jsTypeOverride) => {
327
- if (jsTypeOverride === 'number') {
328
- return 'reader.sfixed64Number()';
329
- }
330
- if (jsTypeOverride === 'string') {
331
- return 'reader.sfixed64String()';
332
- }
333
- return 'reader.sfixed64()';
334
- },
335
- sint32: () => 'reader.sint32()',
336
- sint64: (jsTypeOverride) => {
337
- if (jsTypeOverride === 'number') {
338
- return 'reader.sint64Number()';
339
- }
340
- if (jsTypeOverride === 'string') {
341
- return 'reader.sint64String()';
342
- }
343
- return 'reader.sint64()';
344
- },
345
- string: () => 'reader.string()',
346
- uint32: () => 'reader.uint32()',
347
- uint64: (jsTypeOverride) => {
348
- if (jsTypeOverride === 'number') {
349
- return 'reader.uint64Number()';
350
- }
351
- if (jsTypeOverride === 'string') {
352
- return 'reader.uint64String()';
353
- }
354
- return 'reader.uint64()';
355
- }
356
- };
357
- const defaultValueGenerators = {
358
- bool: () => 'false',
359
- bytes: () => 'uint8ArrayAlloc(0)',
360
- double: () => '0',
361
- fixed32: () => '0',
362
- fixed64: () => '0n',
363
- float: () => '0',
364
- int32: () => '0',
365
- int64: () => '0n',
366
- sfixed32: () => '0',
367
- sfixed64: () => '0n',
368
- sint32: () => '0',
369
- sint64: () => '0n',
370
- string: () => "''",
371
- uint32: () => '0',
372
- uint64: () => '0n'
373
- };
374
- const defaultValueGeneratorsJsTypeOverrides = {
375
- number: () => '0',
376
- string: () => "''"
377
- };
378
- const defaultValueTestGenerators = {
379
- bool: (field) => `(${field} != null && ${field} !== false)`,
380
- bytes: (field) => `(${field} != null && ${field}.byteLength > 0)`,
381
- double: (field) => `(${field} != null && ${field} !== 0)`,
382
- fixed32: (field) => `(${field} != null && ${field} !== 0)`,
383
- fixed64: (field) => `(${field} != null && ${field} !== 0n)`,
384
- float: (field) => `(${field} != null && ${field} !== 0)`,
385
- int32: (field) => `(${field} != null && ${field} !== 0)`,
386
- int64: (field) => `(${field} != null && ${field} !== 0n)`,
387
- sfixed32: (field) => `(${field} != null && ${field} !== 0)`,
388
- sfixed64: (field) => `(${field} != null && ${field} !== 0n)`,
389
- sint32: (field) => `(${field} != null && ${field} !== 0)`,
390
- sint64: (field) => `(${field} != null && ${field} !== 0n)`,
391
- string: (field) => `(${field} != null && ${field} !== '')`,
392
- uint32: (field) => `(${field} != null && ${field} !== 0)`,
393
- uint64: (field) => `(${field} != null && ${field} !== 0n)`
394
- };
395
- const defaultValueTestGeneratorsJsTypeOverrides = {
396
- number: (field) => `(${field} != null && ${field} !== 0)`,
397
- string: (field) => `(${field} != null && ${field} !== '')`
398
- };
399
- function findJsTypeOverride(defaultType, fieldDef) {
400
- if (fieldDef.options?.jstype != null && jsTypeOverrides[fieldDef.options?.jstype] != null) {
401
- if (!['int64', 'uint64', 'sint64', 'fixed64', 'sfixed64'].includes(defaultType)) {
402
- throw new Error(`jstype is only allowed on int64, uint64, sint64, fixed64 or sfixed64 fields - got "${defaultType}"`);
403
- }
404
- return jsTypeOverrides[fieldDef.options?.jstype];
405
- }
406
- }
407
- function findJsTypeName(typeName, classDef, moduleDef, fieldDef) {
408
- const override = findJsTypeOverride(typeName, fieldDef);
409
- if (override != null) {
410
- return override;
411
- }
412
- if (types[typeName] != null) {
413
- return types[typeName];
414
- }
415
- if (isEnumDef(classDef)) {
416
- throw new Error('Could not find type in enum');
417
- }
418
- if (classDef.nested?.[typeName] != null) {
419
- return `${classDef.fullName}.${typeName}`;
420
- }
421
- if (classDef.parent != null) {
422
- return findJsTypeName(typeName, classDef.parent, moduleDef, fieldDef);
423
- }
424
- if (moduleDef.globals[typeName] != null) {
425
- return typeName;
426
- }
427
- throw new Error(`Could not resolve type name "${typeName}"`);
428
- }
429
- function findDef(typeName, classDef, moduleDef) {
430
- if (isEnumDef(classDef)) {
431
- throw new Error('Could not find type in enum');
432
- }
433
- if (classDef.nested?.[typeName] != null) {
434
- return classDef.nested?.[typeName];
435
- }
436
- if (classDef.parent != null) {
437
- return findDef(typeName, classDef.parent, moduleDef);
438
- }
439
- if (moduleDef.globals[typeName] != null) {
440
- return moduleDef.globals[typeName];
441
- }
442
- throw new Error(`Could not resolve type name "${typeName}"`);
443
- }
444
- function createDefaultObject(fields, messageDef, moduleDef) {
445
- const output = Object.entries(fields)
446
- .map(([name, fieldDef]) => {
447
- if (fieldDef.map) {
448
- return `${name}: new Map<${types[fieldDef.keyType ?? 'string']}, ${types[fieldDef.valueType]}>()`;
449
- }
450
- if (fieldDef.repeated) {
451
- return `${name}: []`;
452
- }
453
- if (fieldDef.optional) {
454
- return '';
455
- }
456
- const type = fieldDef.type;
457
- let defaultValue;
458
- let defaultValueGenerator = defaultValueGenerators[type];
459
- if (defaultValueGenerator != null) {
460
- const jsTypeOverride = findJsTypeOverride(type, fieldDef);
461
- if (jsTypeOverride != null && defaultValueGeneratorsJsTypeOverrides[jsTypeOverride] != null) {
462
- defaultValueGenerator = defaultValueGeneratorsJsTypeOverrides[jsTypeOverride];
463
- }
464
- if (type === 'bytes') {
465
- moduleDef.addImport('uint8arrays/alloc', 'alloc', 'uint8ArrayAlloc');
466
- }
467
- defaultValue = defaultValueGenerator();
468
- }
469
- else {
470
- const def = findDef(fieldDef.type, messageDef, moduleDef);
471
- if (isEnumDef(def)) {
472
- // select lowest-value enum - should be 0 but it's not guaranteed
473
- const val = Object.entries(def.values)
474
- .sort((a, b) => {
475
- if (a[1] < b[1]) {
476
- return 1;
477
- }
478
- if (a[1] > b[1]) {
479
- return -1;
480
- }
481
- return 0;
482
- })
483
- .pop();
484
- if (val == null) {
485
- throw new Error(`Could not find default enum value for ${def.fullName}`);
486
- }
487
- defaultValue = `${def.name}.${val[0]}`;
488
- }
489
- else {
490
- defaultValue = 'undefined';
491
- }
492
- }
493
- return `${name}: ${defaultValue}`;
494
- })
495
- .filter(Boolean)
496
- .join(',\n ');
497
- if (output !== '') {
498
- return `
499
- ${output}
500
- `;
501
- }
502
- return '';
503
- }
504
- const encoders = {
505
- bool: 'bool',
506
- bytes: 'bytes',
507
- double: 'double',
508
- fixed32: 'fixed32',
509
- fixed64: 'fixed64',
510
- float: 'float',
511
- int32: 'int32',
512
- int64: 'int64',
513
- sfixed32: 'sfixed32',
514
- sfixed64: 'sfixed64',
515
- sint32: 'sint32',
516
- sint64: 'sint64',
517
- string: 'string',
518
- uint32: 'uint32',
519
- uint64: 'uint64'
520
- };
521
- const codecTypes = {
522
- bool: CODEC_TYPES.VARINT,
523
- bytes: CODEC_TYPES.LENGTH_DELIMITED,
524
- double: CODEC_TYPES.BIT64,
525
- enum: CODEC_TYPES.VARINT,
526
- fixed32: CODEC_TYPES.BIT32,
527
- fixed64: CODEC_TYPES.BIT64,
528
- float: CODEC_TYPES.BIT32,
529
- int32: CODEC_TYPES.VARINT,
530
- int64: CODEC_TYPES.VARINT,
531
- message: CODEC_TYPES.LENGTH_DELIMITED,
532
- sfixed32: CODEC_TYPES.BIT32,
533
- sfixed64: CODEC_TYPES.BIT64,
534
- sint32: CODEC_TYPES.VARINT,
535
- sint64: CODEC_TYPES.VARINT,
536
- string: CODEC_TYPES.LENGTH_DELIMITED,
537
- uint32: CODEC_TYPES.VARINT,
538
- uint64: CODEC_TYPES.VARINT
539
- };
540
- function isEnumDef(obj) {
541
- return obj.values != null;
542
- }
543
- function defineFields(fields, messageDef, moduleDef) {
544
- return Object.entries(fields).map(([fieldName, fieldDef]) => {
545
- if (fieldDef.map) {
546
- return `${fieldName}: Map<${findJsTypeName(fieldDef.keyType ?? 'string', messageDef, moduleDef, fieldDef)}, ${findJsTypeName(fieldDef.valueType, messageDef, moduleDef, fieldDef)}>`;
547
- }
548
- return `${fieldName}${fieldDef.optional ? '?' : ''}: ${findJsTypeName(fieldDef.type, messageDef, moduleDef, fieldDef)}${fieldDef.repeated ? '[]' : ''}`;
549
- });
550
- }
551
- function compileMessage(messageDef, moduleDef, flags) {
552
- if (isEnumDef(messageDef)) {
553
- moduleDef.addImport('protons-runtime', 'enumeration');
554
- // check that the enum def values start from 0
555
- if (Object.values(messageDef.values)[0] !== 0) {
556
- 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`;
557
- if (flags?.strict === true) {
558
- throw new ParseError(message);
559
- }
560
- else {
561
- // eslint-disable-next-line no-console
562
- console.info(`[WARN] ${message}`);
563
- }
564
- }
565
- return `
566
- export enum ${messageDef.name} {
567
- ${Object.keys(messageDef.values).map(name => {
568
- return `${name} = '${name}'`;
569
- }).join(',\n ').trim()}
570
- }
571
-
572
- enum __${messageDef.name}Values {
573
- ${Object.entries(messageDef.values).map(([name, value]) => {
574
- return `${name} = ${value}`;
575
- }).join(',\n ').trim()}
576
- }
577
-
578
- export namespace ${messageDef.name} {
579
- export const codec = (): Codec<${messageDef.name}> => {
580
- return enumeration<${messageDef.name}>(__${messageDef.name}Values)
581
- }
582
- }`.trim();
583
- }
584
- let nested = '';
585
- if (messageDef.nested != null) {
586
- nested = '\n';
587
- nested += Object.values(messageDef.nested)
588
- .map(def => compileMessage(def, moduleDef, flags).trim())
589
- .join('\n\n')
590
- .split('\n')
591
- .map(line => line.trim() === '' ? '' : ` ${line}`)
592
- .join('\n');
593
- }
594
- const fields = messageDef.fields ?? {};
595
- // import relevant modules
596
- moduleDef.addImport('protons-runtime', 'encodeMessage');
597
- moduleDef.addImport('protons-runtime', 'decodeMessage');
598
- moduleDef.addImport('protons-runtime', 'message');
599
- moduleDef.addTypeImport('protons-runtime', 'Codec');
600
- moduleDef.addTypeImport('protons-runtime', 'DecodeOptions');
601
- moduleDef.addTypeImport('uint8arraylist', 'Uint8ArrayList');
602
- const interfaceFields = defineFields(fields, messageDef, moduleDef)
603
- .join('\n ')
604
- .trim();
605
- let interfaceDef = '';
606
- let interfaceCodecDef = '';
607
- if (interfaceFields === '') {
608
- interfaceDef = `
609
- export interface ${messageDef.name} {}`;
610
- }
611
- else {
612
- interfaceDef = `
613
- export interface ${messageDef.name} {
614
- ${defineFields(fields, messageDef, moduleDef)
615
- .join('\n ')
616
- .trim()}
617
- }`;
618
- }
619
- const encodeFields = Object.entries(fields)
620
- .map(([name, fieldDef]) => {
621
- let codec = encoders[fieldDef.type];
622
- let type = fieldDef.map ? 'message' : fieldDef.type;
623
- let typeName = '';
624
- if (codec == null) {
625
- if (fieldDef.enum) {
626
- moduleDef.addImport('protons-runtime', 'enumeration');
627
- type = 'enum';
628
- }
629
- else {
630
- moduleDef.addImport('protons-runtime', 'message');
631
- type = 'message';
632
- }
633
- typeName = findJsTypeName(fieldDef.type, messageDef, moduleDef, fieldDef);
634
- codec = `${typeName}.codec()`;
635
- }
636
- let valueTest = `obj.${name} != null`;
637
- if (fieldDef.map) {
638
- valueTest = `obj.${name} != null && obj.${name}.size !== 0`;
639
- }
640
- else if (!fieldDef.optional && !fieldDef.repeated && !fieldDef.proto2Required) {
641
- let defaultValueTestGenerator = defaultValueTestGenerators[type];
642
- // proto3 singular fields should only be written out if they are not the default value
643
- if (defaultValueTestGenerator != null) {
644
- const jsTypeOverride = findJsTypeOverride(type, fieldDef);
645
- if (jsTypeOverride != null && defaultValueTestGeneratorsJsTypeOverrides[jsTypeOverride] != null) {
646
- defaultValueTestGenerator = defaultValueTestGeneratorsJsTypeOverrides[jsTypeOverride];
647
- }
648
- valueTest = `${defaultValueTestGenerator(`obj.${name}`)}`;
649
- }
650
- else if (type === 'enum') {
651
- // handle enums
652
- const def = findDef(fieldDef.type, messageDef, moduleDef);
653
- if (!isEnumDef(def)) {
654
- throw new Error(`${fieldDef.type} was not enum def`);
655
- }
656
- valueTest = `obj.${name} != null`;
657
- // singular enums default to 0, but enums can be defined without a 0
658
- // value which is against the proto3 spec but is tolerated
659
- if (Object.values(def.values)[0] === 0) {
660
- valueTest += ` && __${fieldDef.type}Values[obj.${name}] !== 0`;
661
- }
662
- }
663
- }
664
- function createWriteField(valueVar) {
665
- const id = (fieldDef.id << 3) | codecTypes[type];
666
- if (fieldDef.enum) {
667
- const def = findDef(fieldDef.type, messageDef, moduleDef);
668
- if (!isEnumDef(def)) {
669
- throw new Error(`${fieldDef.type} was not enum def`);
670
- }
671
- }
672
- let writeField = () => {
673
- const encoderGenerator = encoderGenerators[type];
674
- const jsTypeOverride = findJsTypeOverride(type, fieldDef);
675
- return `w.uint32(${id})
676
- ${encoderGenerator == null ? `${codec}.encode(${valueVar}, w)` : encoderGenerator(valueVar, jsTypeOverride)}`;
677
- };
678
- if (type === 'message') {
679
- // message fields are only written if they have values. But if a message
680
- // is part of a repeated field, and consists of only default values it
681
- // won't be written, so write a zero-length buffer if that's the case
682
- writeField = () => `w.uint32(${id})
683
- ${typeName}.codec().encode(${valueVar}, w)`;
684
- }
685
- return writeField;
686
- }
687
- let writeField = createWriteField(`obj.${name}`);
688
- if (fieldDef.repeated) {
689
- if (fieldDef.map) {
690
- writeField = () => `
691
- for (const [key, value] of obj.${name}.entries()) {
692
- ${createWriteField('{ key, value }')()
693
- .split('\n')
694
- .map(s => {
695
- const trimmed = s.trim();
696
- return trimmed === '' ? trimmed : ` ${s}`;
697
- })
698
- .join('\n')}
699
- }
700
- `.trim();
701
- }
702
- else {
703
- writeField = () => `
704
- for (const value of obj.${name}) {
705
- ${createWriteField('value')()
706
- .split('\n')
707
- .map(s => {
708
- const trimmed = s.trim();
709
- return trimmed === '' ? trimmed : ` ${s}`;
710
- })
711
- .join('\n')}
712
- }
713
- `.trim();
714
- }
715
- }
716
- return `
717
- if (${valueTest}) {
718
- ${writeField()}
719
- }`;
720
- }).join('\n');
721
- const decodeFields = Object.entries(fields)
722
- .map(([fieldName, fieldDef]) => {
723
- function createReadField(fieldName, fieldDef) {
724
- let codec = encoders[fieldDef.type];
725
- let type = fieldDef.type;
726
- if (codec == null) {
727
- if (fieldDef.enum) {
728
- moduleDef.addImport('protons-runtime', 'enumeration');
729
- type = 'enum';
730
- }
731
- else {
732
- moduleDef.addImport('protons-runtime', 'message');
733
- type = 'message';
734
- }
735
- const typeName = findJsTypeName(fieldDef.type, messageDef, moduleDef, fieldDef);
736
- codec = `${typeName}.codec()`;
737
- }
738
- // override setting type on js object
739
- const jsTypeOverride = findJsTypeOverride(fieldDef.type, fieldDef);
740
- let fieldOpts = '';
741
- if (fieldDef.message) {
742
- let suffix = '';
743
- if (fieldDef.repeated) {
744
- suffix = '$';
745
- }
746
- fieldOpts = `, {
747
- limits: opts.limits?.${fieldName}${suffix}
748
- }`;
749
- }
750
- if (fieldDef.map) {
751
- fieldOpts = `, {
752
- limits: {
753
- value: opts.limits?.${fieldName}$value
754
- }
755
- }`;
756
- // do not pass limit opts to map value types that are enums or
757
- // primitives - only support messages
758
- if (types[fieldDef.valueType] != null) {
759
- // primmitive type
760
- fieldOpts = '';
761
- }
762
- else {
763
- const valueType = findDef(fieldDef.valueType, messageDef, moduleDef);
764
- if (isEnumDef(valueType)) {
765
- // enum type
766
- fieldOpts = '';
767
- }
768
- }
769
- }
770
- const parseValue = `${decoderGenerators[type] == null
771
- ? `${codec}.decode(reader${type === 'message'
772
- ? `, reader.uint32()${fieldOpts}`
773
- : ''})`
774
- : decoderGenerators[type](jsTypeOverride)}`;
775
- if (fieldDef.map) {
776
- moduleDef.addImport('protons-runtime', 'MaxSizeError');
777
- let limit = `
778
- if (opts.limits?.${fieldName} != null && obj.${fieldName}.size === opts.limits.${fieldName}) {
779
- throw new MaxSizeError('Decode error - map field "${fieldName}" had too many elements')
780
- }
781
- `;
782
- if (fieldDef.lengthLimit != null) {
783
- limit += `
784
- if (obj.${fieldName}.size === ${fieldDef.lengthLimit}) {
785
- throw new MaxSizeError('Decode error - map field "${fieldName}" had too many elements')
786
- }
787
- `;
788
- }
789
- return `case ${fieldDef.id}: {${limit}
790
- const entry = ${parseValue}
791
- obj.${fieldName}.set(entry.key, entry.value)
792
- break
793
- }`;
794
- }
795
- else if (fieldDef.repeated) {
796
- moduleDef.addImport('protons-runtime', 'MaxLengthError');
797
- let limit = `
798
- if (opts.limits?.${fieldName} != null && obj.${fieldName}.length === opts.limits.${fieldName}) {
799
- throw new MaxLengthError('Decode error - map field "${fieldName}" had too many elements')
800
- }
801
- `;
802
- if (fieldDef.lengthLimit != null) {
803
- limit += `
804
- if (obj.${fieldName}.length === ${fieldDef.lengthLimit}) {
805
- throw new MaxLengthError('Decode error - repeated field "${fieldName}" had too many elements')
806
- }
807
- `;
808
- }
809
- return `case ${fieldDef.id}: {${limit}
810
- obj.${fieldName}.push(${parseValue})
811
- break
812
- }`;
813
- }
814
- return `case ${fieldDef.id}: {
815
- obj.${fieldName} = ${parseValue}
816
- break
817
- }`;
818
- }
819
- return createReadField(fieldName, fieldDef);
820
- })
821
- .join('\n ');
822
- interfaceCodecDef = `
823
- let _codec: Codec<${messageDef.name}>
824
-
825
- export const codec = (): Codec<${messageDef.name}> => {
826
- if (_codec == null) {
827
- _codec = message<${messageDef.name}>((obj, w, opts = {}) => {
828
- if (opts.lengthDelimited !== false) {
829
- w.fork()
830
- }
831
- ${encodeFields === '' ? '' : `${encodeFields}\n`}
832
- if (opts.lengthDelimited !== false) {
833
- w.ldelim()
834
- }
835
- }, (reader, length, opts = {}) => {
836
- const obj: any = {${createDefaultObject(fields, messageDef, moduleDef)}}
837
-
838
- const end = length == null ? reader.len : reader.pos + length
839
-
840
- while (reader.pos < end) {
841
- const tag = reader.uint32()
842
-
843
- switch (tag >>> 3) {${decodeFields === '' ? '' : `\n ${decodeFields}`}
844
- default: {
845
- reader.skipType(tag & 7)
846
- break
847
- }
848
- }
849
- }
850
-
851
- return obj
852
- })
853
- }
854
-
855
- return _codec
856
- }
857
-
858
- export const encode = (obj: Partial<${messageDef.name}>): Uint8Array => {
859
- return encodeMessage(obj, ${messageDef.name}.codec())
860
- }
861
-
862
- export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<${messageDef.name}>): ${messageDef.name} => {
863
- return decodeMessage(buf, ${messageDef.name}.codec(), opts)
864
- }`;
865
- return `
866
- ${interfaceDef}
867
-
868
- export namespace ${messageDef.name} {
869
- ${`${nested}${nested !== '' && interfaceCodecDef !== '' ? '\n' : ''}${interfaceCodecDef}`.trim()}
870
- }
871
- `.trimStart();
872
- }
873
- class ModuleDef {
874
- imports;
875
- types;
876
- compiled;
877
- globals;
878
- constructor() {
879
- this.imports = new Map();
880
- this.types = new Set();
881
- this.compiled = [];
882
- this.globals = {};
883
- }
884
- addImport(module, symbol, alias) {
885
- const defs = this._findDefs(module);
886
- for (const def of defs) {
887
- // check if we already have a definition for this symbol
888
- if (def.symbol === symbol) {
889
- if (alias !== def.alias) {
890
- throw new Error(`Type symbol ${symbol} imported from ${module} with alias ${def.alias} does not match alias ${alias}`);
891
- }
892
- // if it was a type before it's not now
893
- def.type = false;
894
- return;
895
- }
896
- }
897
- defs.push({
898
- symbol,
899
- alias,
900
- type: false
901
- });
902
- }
903
- addTypeImport(module, symbol, alias) {
904
- const defs = this._findDefs(module);
905
- for (const def of defs) {
906
- // check if we already have a definition for this symbol
907
- if (def.symbol === symbol) {
908
- if (alias !== def.alias) {
909
- throw new Error(`Type symbol ${symbol} imported from ${module} with alias ${def.alias} does not match alias ${alias}`);
910
- }
911
- return;
912
- }
913
- }
914
- defs.push({
915
- symbol,
916
- alias,
917
- type: true
918
- });
919
- }
920
- _findDefs(module) {
921
- let defs = this.imports.get(module);
922
- if (defs == null) {
923
- defs = [];
924
- this.imports.set(module, defs);
925
- }
926
- return defs;
927
- }
928
- }
929
- function defineModule(def, flags) {
930
- const moduleDef = new ModuleDef();
931
- const defs = def.nested;
932
- if (defs == null) {
933
- throw new NoMessagesFoundError('No top-level messages found in protobuf');
934
- }
935
- function defineMessage(defs, parent, flags) {
936
- for (const className of Object.keys(defs)) {
937
- const classDef = defs[className];
938
- classDef.name = className;
939
- classDef.parent = parent;
940
- classDef.fullName = parent == null ? className : `${parent.fullName}.${className}`;
941
- if (classDef.nested != null) {
942
- defineMessage(classDef.nested, classDef);
943
- }
944
- if (classDef.fields != null) {
945
- for (const name of Object.keys(classDef.fields)) {
946
- const fieldDef = classDef.fields[name];
947
- fieldDef.repeated = fieldDef.rule === 'repeated';
948
- fieldDef.optional = !fieldDef.repeated && fieldDef.options?.proto3_optional === true;
949
- fieldDef.map = fieldDef.keyType != null;
950
- fieldDef.lengthLimit = fieldDef.options?.['(protons.options).limit'];
951
- fieldDef.proto2Required = false;
952
- if (fieldDef.rule === 'required') {
953
- 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`;
954
- if (flags?.strict === true) {
955
- throw new ParseError(message);
956
- }
957
- else {
958
- fieldDef.proto2Required = true;
959
- // eslint-disable-next-line no-console
960
- console.info(`[WARN] ${message}`);
961
- }
962
- }
963
- }
964
- }
965
- if (parent == null) {
966
- moduleDef.globals[className] = classDef;
967
- }
968
- }
969
- }
970
- function updateTypes(defs, parent) {
971
- for (const className of Object.keys(defs)) {
972
- const classDef = defs[className];
973
- if (classDef.nested != null) {
974
- updateTypes(classDef.nested, classDef);
975
- }
976
- if (classDef.fields != null) {
977
- for (const name of Object.keys(classDef.fields)) {
978
- const fieldDef = classDef.fields[name];
979
- if (types[fieldDef.type] == null) {
980
- const def = findDef(fieldDef.type, classDef, moduleDef);
981
- fieldDef.enum = isEnumDef(def);
982
- fieldDef.message = !fieldDef.enum;
983
- if (fieldDef.message && !fieldDef.repeated) {
984
- // the default type for a message is unset so they are always optional
985
- // https://developers.google.com/protocol-buffers/docs/proto3#default
986
- fieldDef.optional = true;
987
- }
988
- }
989
- }
990
- }
991
- }
992
- }
993
- defineMessage(defs, undefined, flags);
994
- // set enum/message fields now all messages have been defined
995
- updateTypes(defs);
996
- for (const className of Object.keys(defs)) {
997
- const classDef = defs[className];
998
- moduleDef.compiled.push(compileMessage(classDef, moduleDef, flags));
999
- }
1000
- return moduleDef;
1001
- }
1002
202
  export async function generate(source, flags) {
1003
203
  // convert .protobuf to .json
1004
204
  const json = await promisify(pbjs)([
@@ -1010,95 +210,8 @@ export async function generate(source, flags) {
1010
210
  throw new Error(`Could not convert ${source} to intermediate JSON format`);
1011
211
  }
1012
212
  const def = JSON.parse(json);
1013
- for (const [className, classDef] of Object.entries(def.nested ?? {})) {
1014
- for (const [fieldName, fieldDef] of Object.entries(classDef.fields ?? {})) {
1015
- if (fieldDef.keyType == null) {
1016
- continue;
1017
- }
1018
- // https://developers.google.com/protocol-buffers/docs/proto3#backwards_compatibility
1019
- const mapEntryType = `${className}$${fieldName}Entry`;
1020
- classDef.nested = classDef.nested ?? {};
1021
- classDef.nested[mapEntryType] = {
1022
- fields: {
1023
- key: {
1024
- type: fieldDef.keyType,
1025
- id: 1
1026
- },
1027
- value: {
1028
- type: fieldDef.type,
1029
- id: 2
1030
- }
1031
- }
1032
- };
1033
- fieldDef.valueType = fieldDef.type;
1034
- fieldDef.type = mapEntryType;
1035
- fieldDef.rule = 'repeated';
1036
- }
1037
- }
1038
- const moduleDef = defineModule(def, flags);
1039
- const ignores = [
1040
- '/* eslint-disable import/export */',
1041
- '/* eslint-disable complexity */',
1042
- '/* eslint-disable @typescript-eslint/no-namespace */',
1043
- '/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */',
1044
- '/* eslint-disable @typescript-eslint/no-empty-interface */',
1045
- '/* eslint-disable import/consistent-type-specifier-style */',
1046
- '/* eslint-disable @typescript-eslint/no-unused-vars */'
1047
- ];
1048
- const imports = [];
1049
- const importedModules = Array.from([...moduleDef.imports.entries()])
1050
- .sort((a, b) => {
1051
- return a[0].localeCompare(b[0]);
1052
- })
1053
- .sort((a, b) => {
1054
- const aAllTypes = a[1].reduce((acc, curr) => {
1055
- return acc && curr.type;
1056
- }, true);
1057
- const bAllTypes = b[1].reduce((acc, curr) => {
1058
- return acc && curr.type;
1059
- }, true);
1060
- if (aAllTypes && !bAllTypes) {
1061
- return 1;
1062
- }
1063
- if (!aAllTypes && bAllTypes) {
1064
- return -1;
1065
- }
1066
- return 0;
1067
- });
1068
- // add imports
1069
- for (const imp of importedModules) {
1070
- const symbols = imp[1]
1071
- .filter(imp => !imp.type)
1072
- .sort((a, b) => {
1073
- return a.symbol.localeCompare(b.symbol);
1074
- }).map(imp => {
1075
- return `${imp.symbol}${imp.alias != null ? ` as ${imp.alias}` : ''}`;
1076
- }).join(', ');
1077
- if (symbols.length > 0) {
1078
- imports.push(`import { ${symbols} } from '${imp[0]}'`);
1079
- }
1080
- }
1081
- // add type imports
1082
- for (const imp of importedModules) {
1083
- const symbols = imp[1]
1084
- .filter(imp => imp.type)
1085
- .sort((a, b) => {
1086
- return a.symbol.localeCompare(b.symbol);
1087
- }).map(imp => {
1088
- return `${imp.symbol}${imp.alias != null ? ` as ${imp.alias}` : ''}`;
1089
- }).join(', ');
1090
- if (symbols.length > 0) {
1091
- imports.push(`import type { ${symbols} } from '${imp[0]}'`);
1092
- }
1093
- }
1094
- const lines = [
1095
- ...ignores,
1096
- '',
1097
- ...imports,
1098
- '',
1099
- ...moduleDef.compiled
1100
- ];
1101
- const content = lines.join('\n').trim();
213
+ const module = new Module(def, flags);
214
+ const content = module.compile();
1102
215
  const outputPath = pathWithExtension(source, '.ts', flags.output);
1103
216
  await fs.writeFile(outputPath, content + '\n');
1104
217
  }