Skip to content

Commit 2a3b047

Browse files
authored
feat: add error names (#140)
Adds a `.name` property to thrown errors. Retains the `.code` property for backwards compatibility but deprecates it so it can be removed in a future release.
1 parent 3a86981 commit 2a3b047

File tree

12 files changed

+119
-62
lines changed

12 files changed

+119
-62
lines changed

packages/protons-runtime/src/index.ts

+53-2
Original file line numberDiff line numberDiff line change
@@ -339,12 +339,63 @@ export interface Reader {
339339
sfixed64String(): string
340340
}
341341

342+
/**
343+
* This will be removed in a future release
344+
*
345+
* @deprecated
346+
*/
342347
export class CodeError extends Error {
343348
public code: string
344349

345-
constructor (message: string, code: string, options?: ErrorOptions) {
346-
super(message, options)
350+
constructor (message: string, code: string) {
351+
super(message)
347352

348353
this.code = code
349354
}
350355
}
356+
357+
/**
358+
* Thrown when a repeated field has too many elements
359+
*/
360+
export class MaxLengthError extends Error {
361+
/**
362+
* This will be removed in a future release
363+
*
364+
* @deprecated use the `.name` property instead
365+
*/
366+
public code = 'ERR_MAX_LENGTH'
367+
public name = 'MaxLengthError'
368+
}
369+
370+
/**
371+
* Thrown when a map has too many elements
372+
*/
373+
export class MaxSizeError extends Error {
374+
/**
375+
* This will be removed in a future release
376+
*
377+
* @deprecated use the `.name` property instead
378+
*/
379+
public code = 'ERR_MAX_SIZE'
380+
public name = 'MaxSizeError'
381+
}
382+
383+
export class ParseError extends Error {
384+
/**
385+
* This will be removed in a future release
386+
*
387+
* @deprecated use the `.name` property instead
388+
*/
389+
public code = 'ERR_PARSE_ERROR'
390+
public name = 'ParseError'
391+
}
392+
393+
export class NoMessagesFoundError extends Error {
394+
/**
395+
* This will be removed in a future release
396+
*
397+
* @deprecated use the `.name` property instead
398+
*/
399+
public code = 'ERR_NO_MESSAGES_FOUND'
400+
public name = 'NoMessagesFoundError'
401+
}

packages/protons/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,14 @@
4949
},
5050
"dependencies": {
5151
"meow": "^13.1.0",
52-
"protobufjs-cli": "^1.0.0"
52+
"protobufjs-cli": "^1.0.0",
53+
"protons-runtime": "^5.0.0"
5354
},
5455
"devDependencies": {
5556
"aegir": "^44.1.0",
5657
"long": "^5.2.0",
5758
"pbjs": "^0.0.14",
5859
"protobufjs": "^7.0.0",
59-
"protons-runtime": "^5.0.0",
6060
"uint8arraylist": "^2.4.3",
6161
"uint8arrays": "^5.0.1"
6262
}

packages/protons/src/index.ts

+15-9
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ import fs from 'fs/promises'
195195
import path from 'path'
196196
import { promisify } from 'util'
197197
import { main as pbjs } from 'protobufjs-cli/pbjs.js'
198+
import { NoMessagesFoundError, ParseError } from 'protons-runtime'
198199

199200
export enum CODEC_TYPES {
200201
VARINT = 0,
@@ -210,6 +211,11 @@ function pathWithExtension (input: string, extension: string, outputDir?: string
210211
return path.join(output, path.basename(input).split('.').slice(0, -1).join('.') + extension)
211212
}
212213

214+
/**
215+
* This will be removed in a future release
216+
*
217+
* @deprecated
218+
*/
213219
export class CodeError extends Error {
214220
public code: string
215221

@@ -659,7 +665,7 @@ function compileMessage (messageDef: MessageDef, moduleDef: ModuleDef, flags?: F
659665
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`
660666

661667
if (flags?.strict === true) {
662-
throw new CodeError(message, 'ERR_PARSE_ERROR')
668+
throw new ParseError(message)
663669
} else {
664670
// eslint-disable-next-line no-console
665671
console.info(`[WARN] ${message}`)
@@ -923,18 +929,18 @@ export interface ${messageDef.name} {
923929
: decoderGenerators[type](jsTypeOverride)}`
924930

925931
if (fieldDef.map) {
926-
moduleDef.addImport('protons-runtime', 'CodeError')
932+
moduleDef.addImport('protons-runtime', 'MaxSizeError')
927933

928934
let limit = `
929935
if (opts.limits?.${fieldName} != null && obj.${fieldName}.size === opts.limits.${fieldName}) {
930-
throw new CodeError('decode error - map field "${fieldName}" had too many elements', 'ERR_MAX_SIZE')
936+
throw new MaxSizeError('Decode error - map field "${fieldName}" had too many elements')
931937
}
932938
`
933939

934940
if (fieldDef.lengthLimit != null) {
935941
limit += `
936942
if (obj.${fieldName}.size === ${fieldDef.lengthLimit}) {
937-
throw new CodeError('decode error - map field "${fieldName}" had too many elements', 'ERR_MAX_SIZE')
943+
throw new MaxSizeError('Decode error - map field "${fieldName}" had too many elements')
938944
}
939945
`
940946
}
@@ -945,18 +951,18 @@ export interface ${messageDef.name} {
945951
break
946952
}`
947953
} else if (fieldDef.repeated) {
948-
moduleDef.addImport('protons-runtime', 'CodeError')
954+
moduleDef.addImport('protons-runtime', 'MaxLengthError')
949955

950956
let limit = `
951957
if (opts.limits?.${fieldName} != null && obj.${fieldName}.length === opts.limits.${fieldName}) {
952-
throw new CodeError('decode error - map field "${fieldName}" had too many elements', 'ERR_MAX_LENGTH')
958+
throw new MaxLengthError('Decode error - map field "${fieldName}" had too many elements')
953959
}
954960
`
955961

956962
if (fieldDef.lengthLimit != null) {
957963
limit += `
958964
if (obj.${fieldName}.length === ${fieldDef.lengthLimit}) {
959-
throw new CodeError('decode error - repeated field "${fieldName}" had too many elements', 'ERR_MAX_LENGTH')
965+
throw new MaxLengthError('Decode error - repeated field "${fieldName}" had too many elements')
960966
}
961967
`
962968
}
@@ -1110,7 +1116,7 @@ function defineModule (def: ClassDef, flags: Flags): ModuleDef {
11101116
const defs = def.nested
11111117

11121118
if (defs == null) {
1113-
throw new CodeError('No top-level messages found in protobuf', 'ERR_NO_MESSAGES_FOUND')
1119+
throw new NoMessagesFoundError('No top-level messages found in protobuf')
11141120
}
11151121

11161122
function defineMessage (defs: Record<string, ClassDef>, parent?: ClassDef, flags?: Flags): void {
@@ -1138,7 +1144,7 @@ function defineModule (def: ClassDef, flags: Flags): ModuleDef {
11381144
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`
11391145

11401146
if (flags?.strict === true) {
1141-
throw new CodeError(message, 'ERR_PARSE_ERROR')
1147+
throw new ParseError(message)
11421148
} else {
11431149
fieldDef.proto2Required = true
11441150

packages/protons/test/fixtures/bitswap.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
55
/* eslint-disable @typescript-eslint/no-empty-interface */
66

7-
import { type Codec, CodeError, decodeMessage, type DecodeOptions, encodeMessage, enumeration, message } from 'protons-runtime'
7+
import { type Codec, decodeMessage, type DecodeOptions, encodeMessage, enumeration, MaxLengthError, message } from 'protons-runtime'
88
import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc'
99
import type { Uint8ArrayList } from 'uint8arraylist'
1010

@@ -180,7 +180,7 @@ export namespace Message {
180180
switch (tag >>> 3) {
181181
case 1: {
182182
if (opts.limits?.entries != null && obj.entries.length === opts.limits.entries) {
183-
throw new CodeError('decode error - map field "entries" had too many elements', 'ERR_MAX_LENGTH')
183+
throw new MaxLengthError('Decode error - map field "entries" had too many elements')
184184
}
185185

186186
obj.entries.push(Message.Wantlist.Entry.codec().decode(reader, reader.uint32(), {
@@ -438,15 +438,15 @@ export namespace Message {
438438
}
439439
case 2: {
440440
if (opts.limits?.blocks != null && obj.blocks.length === opts.limits.blocks) {
441-
throw new CodeError('decode error - map field "blocks" had too many elements', 'ERR_MAX_LENGTH')
441+
throw new MaxLengthError('Decode error - map field "blocks" had too many elements')
442442
}
443443

444444
obj.blocks.push(reader.bytes())
445445
break
446446
}
447447
case 3: {
448448
if (opts.limits?.payload != null && obj.payload.length === opts.limits.payload) {
449-
throw new CodeError('decode error - map field "payload" had too many elements', 'ERR_MAX_LENGTH')
449+
throw new MaxLengthError('Decode error - map field "payload" had too many elements')
450450
}
451451

452452
obj.payload.push(Message.Block.codec().decode(reader, reader.uint32(), {
@@ -456,7 +456,7 @@ export namespace Message {
456456
}
457457
case 4: {
458458
if (opts.limits?.blockPresences != null && obj.blockPresences.length === opts.limits.blockPresences) {
459-
throw new CodeError('decode error - map field "blockPresences" had too many elements', 'ERR_MAX_LENGTH')
459+
throw new MaxLengthError('Decode error - map field "blockPresences" had too many elements')
460460
}
461461

462462
obj.blockPresences.push(Message.BlockPresence.codec().decode(reader, reader.uint32(), {

packages/protons/test/fixtures/circuit.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
55
/* eslint-disable @typescript-eslint/no-empty-interface */
66

7-
import { type Codec, CodeError, decodeMessage, type DecodeOptions, encodeMessage, enumeration, message } from 'protons-runtime'
7+
import { type Codec, decodeMessage, type DecodeOptions, encodeMessage, enumeration, MaxLengthError, message } from 'protons-runtime'
88
import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc'
99
import type { Uint8ArrayList } from 'uint8arraylist'
1010

@@ -128,7 +128,7 @@ export namespace CircuitRelay {
128128
}
129129
case 2: {
130130
if (opts.limits?.addrs != null && obj.addrs.length === opts.limits.addrs) {
131-
throw new CodeError('decode error - map field "addrs" had too many elements', 'ERR_MAX_LENGTH')
131+
throw new MaxLengthError('Decode error - map field "addrs" had too many elements')
132132
}
133133

134134
obj.addrs.push(reader.bytes())

packages/protons/test/fixtures/daemon.ts

+12-12
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
55
/* eslint-disable @typescript-eslint/no-empty-interface */
66

7-
import { type Codec, CodeError, decodeMessage, type DecodeOptions, encodeMessage, enumeration, message } from 'protons-runtime'
7+
import { type Codec, decodeMessage, type DecodeOptions, encodeMessage, enumeration, MaxLengthError, message } from 'protons-runtime'
88
import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc'
99
import type { Uint8ArrayList } from 'uint8arraylist'
1010

@@ -320,7 +320,7 @@ export namespace Response {
320320
}
321321
case 6: {
322322
if (opts.limits?.peers != null && obj.peers.length === opts.limits.peers) {
323-
throw new CodeError('decode error - map field "peers" had too many elements', 'ERR_MAX_LENGTH')
323+
throw new MaxLengthError('Decode error - map field "peers" had too many elements')
324324
}
325325

326326
obj.peers.push(PeerInfo.codec().decode(reader, reader.uint32(), {
@@ -411,7 +411,7 @@ export namespace IdentifyResponse {
411411
}
412412
case 2: {
413413
if (opts.limits?.addrs != null && obj.addrs.length === opts.limits.addrs) {
414-
throw new CodeError('decode error - map field "addrs" had too many elements', 'ERR_MAX_LENGTH')
414+
throw new MaxLengthError('Decode error - map field "addrs" had too many elements')
415415
}
416416

417417
obj.addrs.push(reader.bytes())
@@ -494,7 +494,7 @@ export namespace ConnectRequest {
494494
}
495495
case 2: {
496496
if (opts.limits?.addrs != null && obj.addrs.length === opts.limits.addrs) {
497-
throw new CodeError('decode error - map field "addrs" had too many elements', 'ERR_MAX_LENGTH')
497+
throw new MaxLengthError('Decode error - map field "addrs" had too many elements')
498498
}
499499

500500
obj.addrs.push(reader.bytes())
@@ -581,7 +581,7 @@ export namespace StreamOpenRequest {
581581
}
582582
case 2: {
583583
if (opts.limits?.proto != null && obj.proto.length === opts.limits.proto) {
584-
throw new CodeError('decode error - map field "proto" had too many elements', 'ERR_MAX_LENGTH')
584+
throw new MaxLengthError('Decode error - map field "proto" had too many elements')
585585
}
586586

587587
obj.proto.push(reader.string())
@@ -662,7 +662,7 @@ export namespace StreamHandlerRequest {
662662
}
663663
case 2: {
664664
if (opts.limits?.proto != null && obj.proto.length === opts.limits.proto) {
665-
throw new CodeError('decode error - map field "proto" had too many elements', 'ERR_MAX_LENGTH')
665+
throw new MaxLengthError('Decode error - map field "proto" had too many elements')
666666
}
667667

668668
obj.proto.push(reader.string())
@@ -1131,7 +1131,7 @@ export namespace PeerInfo {
11311131
}
11321132
case 2: {
11331133
if (opts.limits?.addrs != null && obj.addrs.length === opts.limits.addrs) {
1134-
throw new CodeError('decode error - map field "addrs" had too many elements', 'ERR_MAX_LENGTH')
1134+
throw new MaxLengthError('Decode error - map field "addrs" had too many elements')
11351135
}
11361136

11371137
obj.addrs.push(reader.bytes())
@@ -1507,7 +1507,7 @@ export namespace PSMessage {
15071507
}
15081508
case 4: {
15091509
if (opts.limits?.topicIDs != null && obj.topicIDs.length === opts.limits.topicIDs) {
1510-
throw new CodeError('decode error - map field "topicIDs" had too many elements', 'ERR_MAX_LENGTH')
1510+
throw new MaxLengthError('Decode error - map field "topicIDs" had too many elements')
15111511
}
15121512

15131513
obj.topicIDs.push(reader.string())
@@ -1590,15 +1590,15 @@ export namespace PSResponse {
15901590
switch (tag >>> 3) {
15911591
case 1: {
15921592
if (opts.limits?.topics != null && obj.topics.length === opts.limits.topics) {
1593-
throw new CodeError('decode error - map field "topics" had too many elements', 'ERR_MAX_LENGTH')
1593+
throw new MaxLengthError('Decode error - map field "topics" had too many elements')
15941594
}
15951595

15961596
obj.topics.push(reader.string())
15971597
break
15981598
}
15991599
case 2: {
16001600
if (opts.limits?.peerIDs != null && obj.peerIDs.length === opts.limits.peerIDs) {
1601-
throw new CodeError('decode error - map field "peerIDs" had too many elements', 'ERR_MAX_LENGTH')
1601+
throw new MaxLengthError('Decode error - map field "peerIDs" had too many elements')
16021602
}
16031603

16041604
obj.peerIDs.push(reader.bytes())
@@ -1703,7 +1703,7 @@ export namespace PeerstoreRequest {
17031703
}
17041704
case 3: {
17051705
if (opts.limits?.protos != null && obj.protos.length === opts.limits.protos) {
1706-
throw new CodeError('decode error - map field "protos" had too many elements', 'ERR_MAX_LENGTH')
1706+
throw new MaxLengthError('Decode error - map field "protos" had too many elements')
17071707
}
17081708

17091709
obj.protos.push(reader.string())
@@ -1781,7 +1781,7 @@ export namespace PeerstoreResponse {
17811781
}
17821782
case 2: {
17831783
if (opts.limits?.protos != null && obj.protos.length === opts.limits.protos) {
1784-
throw new CodeError('decode error - map field "protos" had too many elements', 'ERR_MAX_LENGTH')
1784+
throw new MaxLengthError('Decode error - map field "protos" had too many elements')
17851785
}
17861786

17871787
obj.protos.push(reader.string())

packages/protons/test/fixtures/dht.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
55
/* eslint-disable @typescript-eslint/no-empty-interface */
66

7-
import { type Codec, CodeError, decodeMessage, type DecodeOptions, encodeMessage, enumeration, message } from 'protons-runtime'
7+
import { type Codec, decodeMessage, type DecodeOptions, encodeMessage, enumeration, MaxLengthError, message } from 'protons-runtime'
88
import type { Uint8ArrayList } from 'uint8arraylist'
99

1010
export interface Record {
@@ -212,7 +212,7 @@ export namespace Message {
212212
}
213213
case 2: {
214214
if (opts.limits?.addrs != null && obj.addrs.length === opts.limits.addrs) {
215-
throw new CodeError('decode error - map field "addrs" had too many elements', 'ERR_MAX_LENGTH')
215+
throw new MaxLengthError('Decode error - map field "addrs" had too many elements')
216216
}
217217

218218
obj.addrs.push(reader.bytes())
@@ -321,7 +321,7 @@ export namespace Message {
321321
}
322322
case 8: {
323323
if (opts.limits?.closerPeers != null && obj.closerPeers.length === opts.limits.closerPeers) {
324-
throw new CodeError('decode error - map field "closerPeers" had too many elements', 'ERR_MAX_LENGTH')
324+
throw new MaxLengthError('Decode error - map field "closerPeers" had too many elements')
325325
}
326326

327327
obj.closerPeers.push(Message.Peer.codec().decode(reader, reader.uint32(), {
@@ -331,7 +331,7 @@ export namespace Message {
331331
}
332332
case 9: {
333333
if (opts.limits?.providerPeers != null && obj.providerPeers.length === opts.limits.providerPeers) {
334-
throw new CodeError('decode error - map field "providerPeers" had too many elements', 'ERR_MAX_LENGTH')
334+
throw new MaxLengthError('Decode error - map field "providerPeers" had too many elements')
335335
}
336336

337337
obj.providerPeers.push(Message.Peer.codec().decode(reader, reader.uint32(), {

0 commit comments

Comments
 (0)