Skip to content

Commit

Permalink
feat: add error names (#140)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
achingbrain authored Aug 7, 2024
1 parent 3a86981 commit 2a3b047
Show file tree
Hide file tree
Showing 12 changed files with 119 additions and 62 deletions.
55 changes: 53 additions & 2 deletions packages/protons-runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,12 +339,63 @@ export interface Reader {
sfixed64String(): string
}

/**
* This will be removed in a future release
*
* @deprecated
*/
export class CodeError extends Error {
public code: string

constructor (message: string, code: string, options?: ErrorOptions) {
super(message, options)
constructor (message: string, code: string) {
super(message)

this.code = code
}
}

/**
* Thrown when a repeated field has too many elements
*/
export class MaxLengthError extends Error {
/**
* This will be removed in a future release
*
* @deprecated use the `.name` property instead
*/
public code = 'ERR_MAX_LENGTH'
public name = 'MaxLengthError'
}

/**
* Thrown when a map has too many elements
*/
export class MaxSizeError extends Error {
/**
* This will be removed in a future release
*
* @deprecated use the `.name` property instead
*/
public code = 'ERR_MAX_SIZE'
public name = 'MaxSizeError'
}

export class ParseError extends Error {
/**
* This will be removed in a future release
*
* @deprecated use the `.name` property instead
*/
public code = 'ERR_PARSE_ERROR'
public name = 'ParseError'
}

export class NoMessagesFoundError extends Error {
/**
* This will be removed in a future release
*
* @deprecated use the `.name` property instead
*/
public code = 'ERR_NO_MESSAGES_FOUND'
public name = 'NoMessagesFoundError'
}
4 changes: 2 additions & 2 deletions packages/protons/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@
},
"dependencies": {
"meow": "^13.1.0",
"protobufjs-cli": "^1.0.0"
"protobufjs-cli": "^1.0.0",
"protons-runtime": "^5.0.0"
},
"devDependencies": {
"aegir": "^44.1.0",
"long": "^5.2.0",
"pbjs": "^0.0.14",
"protobufjs": "^7.0.0",
"protons-runtime": "^5.0.0",
"uint8arraylist": "^2.4.3",
"uint8arrays": "^5.0.1"
}
Expand Down
24 changes: 15 additions & 9 deletions packages/protons/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ import fs from 'fs/promises'
import path from 'path'
import { promisify } from 'util'
import { main as pbjs } from 'protobufjs-cli/pbjs.js'
import { NoMessagesFoundError, ParseError } from 'protons-runtime'

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

/**
* This will be removed in a future release
*
* @deprecated
*/
export class CodeError extends Error {
public code: string

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

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

if (fieldDef.map) {
moduleDef.addImport('protons-runtime', 'CodeError')
moduleDef.addImport('protons-runtime', 'MaxSizeError')

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

if (fieldDef.lengthLimit != null) {
limit += `
if (obj.${fieldName}.size === ${fieldDef.lengthLimit}) {
throw new CodeError('decode error - map field "${fieldName}" had too many elements', 'ERR_MAX_SIZE')
throw new MaxSizeError('Decode error - map field "${fieldName}" had too many elements')
}
`
}
Expand All @@ -945,18 +951,18 @@ export interface ${messageDef.name} {
break
}`
} else if (fieldDef.repeated) {
moduleDef.addImport('protons-runtime', 'CodeError')
moduleDef.addImport('protons-runtime', 'MaxLengthError')

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

if (fieldDef.lengthLimit != null) {
limit += `
if (obj.${fieldName}.length === ${fieldDef.lengthLimit}) {
throw new CodeError('decode error - repeated field "${fieldName}" had too many elements', 'ERR_MAX_LENGTH')
throw new MaxLengthError('Decode error - repeated field "${fieldName}" had too many elements')
}
`
}
Expand Down Expand Up @@ -1110,7 +1116,7 @@ function defineModule (def: ClassDef, flags: Flags): ModuleDef {
const defs = def.nested

if (defs == null) {
throw new CodeError('No top-level messages found in protobuf', 'ERR_NO_MESSAGES_FOUND')
throw new NoMessagesFoundError('No top-level messages found in protobuf')
}

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

if (flags?.strict === true) {
throw new CodeError(message, 'ERR_PARSE_ERROR')
throw new ParseError(message)
} else {
fieldDef.proto2Required = true

Expand Down
10 changes: 5 additions & 5 deletions packages/protons/test/fixtures/bitswap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
/* eslint-disable @typescript-eslint/no-empty-interface */

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

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

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

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

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

obj.blockPresences.push(Message.BlockPresence.codec().decode(reader, reader.uint32(), {
Expand Down
4 changes: 2 additions & 2 deletions packages/protons/test/fixtures/circuit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
/* eslint-disable @typescript-eslint/no-empty-interface */

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

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

obj.addrs.push(reader.bytes())
Expand Down
24 changes: 12 additions & 12 deletions packages/protons/test/fixtures/daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
/* eslint-disable @typescript-eslint/no-empty-interface */

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

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

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

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

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

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

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

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

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

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

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

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

obj.protos.push(reader.string())
Expand Down
8 changes: 4 additions & 4 deletions packages/protons/test/fixtures/dht.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
/* eslint-disable @typescript-eslint/no-empty-interface */

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

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

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

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

obj.providerPeers.push(Message.Peer.codec().decode(reader, reader.uint32(), {
Expand Down
Loading

0 comments on commit 2a3b047

Please sign in to comment.