diff --git a/package.json b/package.json index f391b77..101cd9b 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "build": "lerna run build", "lint": "lerna run lint", "dep-check": "lerna run dep-check", - "release": "lerna run release --concurrency 1" + "release": "lerna run release --concurrency 1 -- --" }, "dependencies": { "lerna": "^4.0.0", diff --git a/packages/protons-benchmark/.aegir.js b/packages/protons-benchmark/.aegir.js new file mode 100644 index 0000000..135a6a2 --- /dev/null +++ b/packages/protons-benchmark/.aegir.js @@ -0,0 +1,8 @@ + +export default { + build: { + config: { + platform: 'node' + } + } +} diff --git a/packages/protons-benchmark/package.json b/packages/protons-benchmark/package.json index 2194dcd..3bca479 100644 --- a/packages/protons-benchmark/package.json +++ b/packages/protons-benchmark/package.json @@ -59,9 +59,10 @@ ] }, "scripts": { + "clean": "aegir clean", "lint": "aegir lint", - "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js", - "build": "tsc && cp src/protobufjs/bench.js dist/src/protobufjs", + "dep-check": "aegir dep-check", + "build": "aegir build && cp -R src/protobufjs dist/src/protobufjs", "prestart": "npm run build", "start": "node dist/src/index.js" }, @@ -73,7 +74,7 @@ "protons-runtime": "^0.0.0" }, "devDependencies": { - "aegir": "^36.1.3" + "aegir": "^37.0.5" }, "private": true } diff --git a/packages/protons-benchmark/src/index.ts b/packages/protons-benchmark/src/index.ts index 22fa008..88fe0c3 100644 --- a/packages/protons-benchmark/src/index.ts +++ b/packages/protons-benchmark/src/index.ts @@ -1,6 +1,6 @@ import benny from 'benny' -import { expect } from 'aegir/utils/chai.js' +import { expect } from 'aegir/chai' import { Test as ProtonsTest } from './protons/bench.js' import { encodeTest as pbjsEncodeTest, decodeTest as pbjsDecodeTest } from './pbjs/bench.js' import { Test as ProtobufjsTest } from './protobufjs/bench.js' diff --git a/packages/protons-benchmark/src/protobufjs/bench.js b/packages/protons-benchmark/src/protobufjs/bench.js index 6a33065..d60716d 100644 --- a/packages/protons-benchmark/src/protobufjs/bench.js +++ b/packages/protons-benchmark/src/protobufjs/bench.js @@ -1,5 +1,6 @@ /*eslint-disable*/ // @ts-nocheck + import $protobuf from "protobufjs/minimal.js"; // Common aliases diff --git a/packages/protons-benchmark/src/protons/bench.ts b/packages/protons-benchmark/src/protons/bench.ts index 5ec9d7d..3489ca2 100644 --- a/packages/protons-benchmark/src/protons/bench.ts +++ b/packages/protons-benchmark/src/protons/bench.ts @@ -8,16 +8,18 @@ export interface Foo { } export namespace Foo { - export const codec = message({ - 1: { name: 'baz', codec: uint32 } - }) + export const codec = () => { + return message({ + 1: { name: 'baz', codec: uint32 } + }) + } export const encode = (obj: Foo): Uint8Array => { - return encodeMessage(obj, Foo.codec) + return encodeMessage(obj, Foo.codec()) } export const decode = (buf: Uint8Array): Foo => { - return decodeMessage(buf, Foo.codec) + return decodeMessage(buf, Foo.codec()) } } @@ -26,16 +28,18 @@ export interface Bar { } export namespace Bar { - export const codec = message({ - 1: { name: 'tmp', codec: Foo.codec } - }) + export const codec = () => { + return message({ + 1: { name: 'tmp', codec: Foo.codec() } + }) + } export const encode = (obj: Bar): Uint8Array => { - return encodeMessage(obj, Bar.codec) + return encodeMessage(obj, Bar.codec()) } export const decode = (buf: Uint8Array): Bar => { - return decodeMessage(buf, Bar.codec) + return decodeMessage(buf, Bar.codec()) } } @@ -45,7 +49,9 @@ export enum FOO { } export namespace FOO { - export const codec = enumeration(FOO) + export const codec = () => { + return enumeration(FOO) + } } export interface Yo { @@ -53,16 +59,18 @@ export interface Yo { } export namespace Yo { - export const codec = message({ - 1: { name: 'lol', codec: FOO.codec, repeats: true } - }) + export const codec = () => { + return message({ + 1: { name: 'lol', codec: FOO.codec(), repeats: true } + }) + } export const encode = (obj: Yo): Uint8Array => { - return encodeMessage(obj, Yo.codec) + return encodeMessage(obj, Yo.codec()) } export const decode = (buf: Uint8Array): Yo => { - return decodeMessage(buf, Yo.codec) + return decodeMessage(buf, Yo.codec()) } } @@ -72,17 +80,19 @@ export interface Lol { } export namespace Lol { - export const codec = message({ - 1: { name: 'lol', codec: string }, - 2: { name: 'b', codec: Bar.codec } - }) + export const codec = () => { + return message({ + 1: { name: 'lol', codec: string }, + 2: { name: 'b', codec: Bar.codec() } + }) + } export const encode = (obj: Lol): Uint8Array => { - return encodeMessage(obj, Lol.codec) + return encodeMessage(obj, Lol.codec()) } export const decode = (buf: Uint8Array): Lol => { - return decodeMessage(buf, Lol.codec) + return decodeMessage(buf, Lol.codec()) } } @@ -94,18 +104,20 @@ export interface Test { } export namespace Test { - export const codec = message({ - 6: { name: 'meh', codec: Lol.codec }, - 3: { name: 'hello', codec: uint32 }, - 1: { name: 'foo', codec: string }, - 7: { name: 'payload', codec: bytes } - }) + export const codec = () => { + return message({ + 6: { name: 'meh', codec: Lol.codec() }, + 3: { name: 'hello', codec: uint32 }, + 1: { name: 'foo', codec: string }, + 7: { name: 'payload', codec: bytes } + }) + } export const encode = (obj: Test): Uint8Array => { - return encodeMessage(obj, Test.codec) + return encodeMessage(obj, Test.codec()) } export const decode = (buf: Uint8Array): Test => { - return decodeMessage(buf, Test.codec) + return decodeMessage(buf, Test.codec()) } } diff --git a/packages/protons-benchmark/tsconfig.json b/packages/protons-benchmark/tsconfig.json index ec40462..7c78261 100644 --- a/packages/protons-benchmark/tsconfig.json +++ b/packages/protons-benchmark/tsconfig.json @@ -1,9 +1,7 @@ { "extends": "aegir/src/config/tsconfig.aegir.json", "compilerOptions": { - "outDir": "dist", - "emitDeclarationOnly": false, - "module": "ES2020" + "outDir": "dist" }, "include": [ "bin", diff --git a/packages/protons-runtime/package.json b/packages/protons-runtime/package.json index d47e412..353f465 100644 --- a/packages/protons-runtime/package.json +++ b/packages/protons-runtime/package.json @@ -138,15 +138,15 @@ }, "scripts": { "lint": "aegir lint", - "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js", - "build": "tsc", - "release": "semantic-release -e semantic-release-monorepo" + "dep-check": "aegir dep-check", + "build": "aegir build", + "release": "aegir release" }, "dependencies": { "uint8arraylist": "^1.4.0", "uint8arrays": "^3.0.0" }, "devDependencies": { - "aegir": "^36.1.3" + "aegir": "^37.0.5" } } diff --git a/packages/protons-runtime/src/codecs/message.ts b/packages/protons-runtime/src/codecs/message.ts index 64cf995..009e14d 100644 --- a/packages/protons-runtime/src/codecs/message.ts +++ b/packages/protons-runtime/src/codecs/message.ts @@ -64,12 +64,10 @@ export function message (fieldDefs: FieldDefs): Codec { const decode: DecodeFunction = function messageDecode (buffer, offset) { const length = unsigned.decode(buffer, offset) offset += unsigned.encodingLength(length) - + const end = offset + length const fields: any = {} - while (offset < buffer.length) { - // console.info('start offset', offset) - + while (offset < end) { const key = unsigned.decode(buffer, offset) offset += unsigned.encodingLength(key) @@ -78,10 +76,7 @@ export function message (fieldDefs: FieldDefs): Codec { const fieldDef = fieldDefs[fieldNumber] let fieldLength = 0 - // console.info('fieldNumber', fieldNumber, 'wireType', wireType, 'offset', offset) - if (wireType === CODEC_TYPES.VARINT) { - // console.info('decode varint') if (fieldDef != null) { // use the codec if it is available as this could be a bigint const value = fieldDef.codec.decode(buffer, offset) @@ -91,14 +86,11 @@ export function message (fieldDefs: FieldDefs): Codec { fieldLength = unsigned.encodingLength(value) } } else if (wireType === CODEC_TYPES.BIT64) { - // console.info('decode 64bit') fieldLength = 8 } else if (wireType === CODEC_TYPES.LENGTH_DELIMITED) { - // console.info('decode length delimited') const valueLength = unsigned.decode(buffer, offset) fieldLength = valueLength + unsigned.encodingLength(valueLength) } else if (wireType === CODEC_TYPES.BIT32) { - // console.info('decode 32 bit') fieldLength = 4 } else if (wireType === CODEC_TYPES.START_GROUP) { throw new Error('Unsupported wire type START_GROUP') @@ -106,10 +98,7 @@ export function message (fieldDefs: FieldDefs): Codec { throw new Error('Unsupported wire type END_GROUP') } - // console.info('fieldLength', fieldLength) - if (fieldDef != null) { - // console.info('decode', fieldDef.codec.name, fieldDef.name, 'at offset', offset) const value = fieldDef.codec.decode(buffer, offset) if (fieldDef.repeats === true) { @@ -121,8 +110,6 @@ export function message (fieldDefs: FieldDefs): Codec { } else { fields[fieldDef.name] = value } - - // console.info('decoded', value) } offset += fieldLength diff --git a/packages/protons/.aegir.js b/packages/protons/.aegir.js new file mode 100644 index 0000000..135a6a2 --- /dev/null +++ b/packages/protons/.aegir.js @@ -0,0 +1,8 @@ + +export default { + build: { + config: { + platform: 'node' + } + } +} diff --git a/packages/protons/package.json b/packages/protons/package.json index 73c0095..a7a6fe6 100644 --- a/packages/protons/package.json +++ b/packages/protons/package.json @@ -141,19 +141,18 @@ }, "scripts": { "lint": "aegir lint", - "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js", - "build": "tsc", - "pretest": "npm run build", - "test": "aegir test -t node -f ./dist/test/*.js -f ./dist/test/**/*.js", - "test:node": "npm run test -- -t node --cov", - "release": "semantic-release -e semantic-release-monorepo" + "dep-check": "aegir dep-check", + "build": "aegir build", + "test": "aegir test -t node", + "test:node": "aegir test -t node --cov", + "release": "aegir release" }, "dependencies": { "meow": "^10.1.2", "protobufjs": "^6.11.2" }, "devDependencies": { - "aegir": "^36.1.3", + "aegir": "^37.0.5", "pbjs": "^0.0.14", "protons-runtime": "^1.0.0" } diff --git a/packages/protons/src/index.ts b/packages/protons/src/index.ts index f6977b6..2f1accc 100644 --- a/packages/protons/src/index.ts +++ b/packages/protons/src/index.ts @@ -131,14 +131,16 @@ function compileMessage (messageDef: MessageDef, moduleDef: ModuleDef): string { return ` export enum ${messageDef.name} { ${ - Object.entries(messageDef.values).map(([enumValueName, enumValue]) => { + Object.keys(messageDef.values).map(enumValueName => { return `${enumValueName} = '${enumValueName}'` }).join(',\n ').trim() } } export namespace ${messageDef.name} { - export const codec = enumeration(${messageDef.name}) + export const codec = () => { + return enumeration(${messageDef.name}) + } } ` } @@ -173,8 +175,9 @@ export interface ${messageDef.name} { } export namespace ${messageDef.name} {${nested} - export const codec = message<${messageDef.name}>({ - ${Object.entries(fields) + export const codec = () => { + return message<${messageDef.name}>({ + ${Object.entries(fields) .map(([name, fieldDef]) => { let codec = encoders[fieldDef.type] @@ -188,21 +191,22 @@ export namespace ${messageDef.name} {${nested} } const typeName = findTypeName(fieldDef.type, messageDef, moduleDef) - codec = `${typeName}.codec` + codec = `${typeName}.codec()` } else { moduleDef.imports.add(codec) } return `${fieldDef.id}: { name: '${name}', codec: ${codec}${fieldDef.options?.proto3_optional === true ? ', optional: true' : ''}${fieldDef.rule === 'repeated' ? ', repeats: true' : ''} }` - }).join(',\n ')} - }) + }).join(',\n ')} + }) + } export const encode = (obj: ${messageDef.name}): Uint8Array => { - return encodeMessage(obj, ${messageDef.name}.codec) + return encodeMessage(obj, ${messageDef.name}.codec()) } export const decode = (buf: Uint8Array): ${messageDef.name} => { - return decodeMessage(buf, ${messageDef.name}.codec) + return decodeMessage(buf, ${messageDef.name}.codec()) } } ` diff --git a/packages/protons/test/fixtures/daemon.proto b/packages/protons/test/fixtures/daemon.proto new file mode 100644 index 0000000..5a79381 --- /dev/null +++ b/packages/protons/test/fixtures/daemon.proto @@ -0,0 +1,175 @@ +syntax = "proto3"; + +message Request { + enum Type { + IDENTIFY = 0; + CONNECT = 1; + STREAM_OPEN = 2; + STREAM_HANDLER = 3; + DHT = 4; + LIST_PEERS = 5; + CONNMANAGER = 6; + DISCONNECT = 7; + PUBSUB = 8; + PEERSTORE = 9; + } + + required Type type = 1; + + optional ConnectRequest connect = 2; + optional StreamOpenRequest streamOpen = 3; + optional StreamHandlerRequest streamHandler = 4; + optional DHTRequest dht = 5; + optional ConnManagerRequest connManager = 6; + optional DisconnectRequest disconnect = 7; + optional PSRequest pubsub = 8; + optional PeerstoreRequest peerStore = 9; +} + +message Response { + enum Type { + OK = 0; + ERROR = 1; + } + + required Type type = 1; + optional ErrorResponse error = 2; + optional StreamInfo streamInfo = 3; + optional IdentifyResponse identify = 4; + optional DHTResponse dht = 5; + repeated PeerInfo peers = 6; + optional PSResponse pubsub = 7; + optional PeerstoreResponse peerStore = 8; +} + +message IdentifyResponse { + required bytes id = 1; + repeated bytes addrs = 2; +} + +message ConnectRequest { + required bytes peer = 1; + repeated bytes addrs = 2; + optional int64 timeout = 3; +} + +message StreamOpenRequest { + required bytes peer = 1; + repeated string proto = 2; + optional int64 timeout = 3; +} + +message StreamHandlerRequest { + required bytes addr = 1; + repeated string proto = 2; +} + +message ErrorResponse { + required string msg = 1; +} + +message StreamInfo { + required bytes peer = 1; + required bytes addr = 2; + required string proto = 3; +} + +message DHTRequest { + enum Type { + FIND_PEER = 0; + FIND_PEERS_CONNECTED_TO_PEER = 1; + FIND_PROVIDERS = 2; + GET_CLOSEST_PEERS = 3; + GET_PUBLIC_KEY = 4; + GET_VALUE = 5; + SEARCH_VALUE = 6; + PUT_VALUE = 7; + PROVIDE = 8; + } + + required Type type = 1; + optional bytes peer = 2; + optional bytes cid = 3; + optional bytes key = 4; + optional bytes value = 5; + optional int32 count = 6; + optional int64 timeout = 7; +} + +message DHTResponse { + enum Type { + BEGIN = 0; + VALUE = 1; + END = 2; + } + + required Type type = 1; + optional PeerInfo peer = 2; + optional bytes value = 3; +} + +message PeerInfo { + required bytes id = 1; + repeated bytes addrs = 2; +} + +message ConnManagerRequest { + enum Type { + TAG_PEER = 0; + UNTAG_PEER = 1; + TRIM = 2; + } + + required Type type = 1; + + optional bytes peer = 2; + optional string tag = 3; + optional int64 weight = 4; +} + +message DisconnectRequest { + required bytes peer = 1; +} + +message PSRequest { + enum Type { + GET_TOPICS = 0; + LIST_PEERS = 1; + PUBLISH = 2; + SUBSCRIBE = 3; + } + + required Type type = 1; + optional string topic = 2; + optional bytes data = 3; +} + +message PSMessage { + optional bytes from = 1; + optional bytes data = 2; + optional bytes seqno = 3; + repeated string topicIDs = 4; + optional bytes signature = 5; + optional bytes key = 6; +} + +message PSResponse { + repeated string topics = 1; + repeated bytes peerIDs = 2; +} + +message PeerstoreRequest { + enum Type { + GET_PROTOCOLS = 1; + GET_PEER_INFO = 2; + } + + required Type type = 1; + optional bytes id = 2; + repeated string protos = 3; +} + +message PeerstoreResponse { + optional PeerInfo peer = 1; + repeated string protos = 2; +} diff --git a/packages/protons/test/fixtures/daemon.ts b/packages/protons/test/fixtures/daemon.ts new file mode 100644 index 0000000..9872fe4 --- /dev/null +++ b/packages/protons/test/fixtures/daemon.ts @@ -0,0 +1,552 @@ +/* eslint-disable import/export */ +/* eslint-disable @typescript-eslint/no-namespace */ + +import { enumeration, encodeMessage, decodeMessage, message, bytes, int64, string, int32 } from 'protons-runtime' + +export interface Request { + type: Request.Type + connect?: ConnectRequest + streamOpen?: StreamOpenRequest + streamHandler?: StreamHandlerRequest + dht?: DHTRequest + connManager?: ConnManagerRequest + disconnect?: DisconnectRequest + pubsub?: PSRequest + peerStore?: PeerstoreRequest +} + +export namespace Request { + export enum Type { + IDENTIFY = 'IDENTIFY', + CONNECT = 'CONNECT', + STREAM_OPEN = 'STREAM_OPEN', + STREAM_HANDLER = 'STREAM_HANDLER', + DHT = 'DHT', + LIST_PEERS = 'LIST_PEERS', + CONNMANAGER = 'CONNMANAGER', + DISCONNECT = 'DISCONNECT', + PUBSUB = 'PUBSUB', + PEERSTORE = 'PEERSTORE' + } + + export namespace Type { + export const codec = () => { + return enumeration(Type) + } + } + + export const codec = () => { + return message({ + 1: { name: 'type', codec: Request.Type.codec() }, + 2: { name: 'connect', codec: ConnectRequest.codec(), optional: true }, + 3: { name: 'streamOpen', codec: StreamOpenRequest.codec(), optional: true }, + 4: { name: 'streamHandler', codec: StreamHandlerRequest.codec(), optional: true }, + 5: { name: 'dht', codec: DHTRequest.codec(), optional: true }, + 6: { name: 'connManager', codec: ConnManagerRequest.codec(), optional: true }, + 7: { name: 'disconnect', codec: DisconnectRequest.codec(), optional: true }, + 8: { name: 'pubsub', codec: PSRequest.codec(), optional: true }, + 9: { name: 'peerStore', codec: PeerstoreRequest.codec(), optional: true } + }) + } + + export const encode = (obj: Request): Uint8Array => { + return encodeMessage(obj, Request.codec()) + } + + export const decode = (buf: Uint8Array): Request => { + return decodeMessage(buf, Request.codec()) + } +} + +export interface Response { + type: Response.Type + error?: ErrorResponse + streamInfo?: StreamInfo + identify?: IdentifyResponse + dht?: DHTResponse + peers: PeerInfo[] + pubsub?: PSResponse + peerStore?: PeerstoreResponse +} + +export namespace Response { + export enum Type { + OK = 'OK', + ERROR = 'ERROR' + } + + export namespace Type { + export const codec = () => { + return enumeration(Type) + } + } + + export const codec = () => { + return message({ + 1: { name: 'type', codec: Response.Type.codec() }, + 2: { name: 'error', codec: ErrorResponse.codec(), optional: true }, + 3: { name: 'streamInfo', codec: StreamInfo.codec(), optional: true }, + 4: { name: 'identify', codec: IdentifyResponse.codec(), optional: true }, + 5: { name: 'dht', codec: DHTResponse.codec(), optional: true }, + 6: { name: 'peers', codec: PeerInfo.codec(), repeats: true }, + 7: { name: 'pubsub', codec: PSResponse.codec(), optional: true }, + 8: { name: 'peerStore', codec: PeerstoreResponse.codec(), optional: true } + }) + } + + export const encode = (obj: Response): Uint8Array => { + return encodeMessage(obj, Response.codec()) + } + + export const decode = (buf: Uint8Array): Response => { + return decodeMessage(buf, Response.codec()) + } +} + +export interface IdentifyResponse { + id: Uint8Array + addrs: Uint8Array[] +} + +export namespace IdentifyResponse { + export const codec = () => { + return message({ + 1: { name: 'id', codec: bytes }, + 2: { name: 'addrs', codec: bytes, repeats: true } + }) + } + + export const encode = (obj: IdentifyResponse): Uint8Array => { + return encodeMessage(obj, IdentifyResponse.codec()) + } + + export const decode = (buf: Uint8Array): IdentifyResponse => { + return decodeMessage(buf, IdentifyResponse.codec()) + } +} + +export interface ConnectRequest { + peer: Uint8Array + addrs: Uint8Array[] + timeout?: bigint +} + +export namespace ConnectRequest { + export const codec = () => { + return message({ + 1: { name: 'peer', codec: bytes }, + 2: { name: 'addrs', codec: bytes, repeats: true }, + 3: { name: 'timeout', codec: int64, optional: true } + }) + } + + export const encode = (obj: ConnectRequest): Uint8Array => { + return encodeMessage(obj, ConnectRequest.codec()) + } + + export const decode = (buf: Uint8Array): ConnectRequest => { + return decodeMessage(buf, ConnectRequest.codec()) + } +} + +export interface StreamOpenRequest { + peer: Uint8Array + proto: string[] + timeout?: bigint +} + +export namespace StreamOpenRequest { + export const codec = () => { + return message({ + 1: { name: 'peer', codec: bytes }, + 2: { name: 'proto', codec: string, repeats: true }, + 3: { name: 'timeout', codec: int64, optional: true } + }) + } + + export const encode = (obj: StreamOpenRequest): Uint8Array => { + return encodeMessage(obj, StreamOpenRequest.codec()) + } + + export const decode = (buf: Uint8Array): StreamOpenRequest => { + return decodeMessage(buf, StreamOpenRequest.codec()) + } +} + +export interface StreamHandlerRequest { + addr: Uint8Array + proto: string[] +} + +export namespace StreamHandlerRequest { + export const codec = () => { + return message({ + 1: { name: 'addr', codec: bytes }, + 2: { name: 'proto', codec: string, repeats: true } + }) + } + + export const encode = (obj: StreamHandlerRequest): Uint8Array => { + return encodeMessage(obj, StreamHandlerRequest.codec()) + } + + export const decode = (buf: Uint8Array): StreamHandlerRequest => { + return decodeMessage(buf, StreamHandlerRequest.codec()) + } +} + +export interface ErrorResponse { + msg: string +} + +export namespace ErrorResponse { + export const codec = () => { + return message({ + 1: { name: 'msg', codec: string } + }) + } + + export const encode = (obj: ErrorResponse): Uint8Array => { + return encodeMessage(obj, ErrorResponse.codec()) + } + + export const decode = (buf: Uint8Array): ErrorResponse => { + return decodeMessage(buf, ErrorResponse.codec()) + } +} + +export interface StreamInfo { + peer: Uint8Array + addr: Uint8Array + proto: string +} + +export namespace StreamInfo { + export const codec = () => { + return message({ + 1: { name: 'peer', codec: bytes }, + 2: { name: 'addr', codec: bytes }, + 3: { name: 'proto', codec: string } + }) + } + + export const encode = (obj: StreamInfo): Uint8Array => { + return encodeMessage(obj, StreamInfo.codec()) + } + + export const decode = (buf: Uint8Array): StreamInfo => { + return decodeMessage(buf, StreamInfo.codec()) + } +} + +export interface DHTRequest { + type: DHTRequest.Type + peer?: Uint8Array + cid?: Uint8Array + key?: Uint8Array + value?: Uint8Array + count?: number + timeout?: bigint +} + +export namespace DHTRequest { + export enum Type { + FIND_PEER = 'FIND_PEER', + FIND_PEERS_CONNECTED_TO_PEER = 'FIND_PEERS_CONNECTED_TO_PEER', + FIND_PROVIDERS = 'FIND_PROVIDERS', + GET_CLOSEST_PEERS = 'GET_CLOSEST_PEERS', + GET_PUBLIC_KEY = 'GET_PUBLIC_KEY', + GET_VALUE = 'GET_VALUE', + SEARCH_VALUE = 'SEARCH_VALUE', + PUT_VALUE = 'PUT_VALUE', + PROVIDE = 'PROVIDE' + } + + export namespace Type { + export const codec = () => { + return enumeration(Type) + } + } + + export const codec = () => { + return message({ + 1: { name: 'type', codec: DHTRequest.Type.codec() }, + 2: { name: 'peer', codec: bytes, optional: true }, + 3: { name: 'cid', codec: bytes, optional: true }, + 4: { name: 'key', codec: bytes, optional: true }, + 5: { name: 'value', codec: bytes, optional: true }, + 6: { name: 'count', codec: int32, optional: true }, + 7: { name: 'timeout', codec: int64, optional: true } + }) + } + + export const encode = (obj: DHTRequest): Uint8Array => { + return encodeMessage(obj, DHTRequest.codec()) + } + + export const decode = (buf: Uint8Array): DHTRequest => { + return decodeMessage(buf, DHTRequest.codec()) + } +} + +export interface DHTResponse { + type: DHTResponse.Type + peer?: PeerInfo + value?: Uint8Array +} + +export namespace DHTResponse { + export enum Type { + BEGIN = 'BEGIN', + VALUE = 'VALUE', + END = 'END' + } + + export namespace Type { + export const codec = () => { + return enumeration(Type) + } + } + + export const codec = () => { + return message({ + 1: { name: 'type', codec: DHTResponse.Type.codec() }, + 2: { name: 'peer', codec: PeerInfo.codec(), optional: true }, + 3: { name: 'value', codec: bytes, optional: true } + }) + } + + export const encode = (obj: DHTResponse): Uint8Array => { + return encodeMessage(obj, DHTResponse.codec()) + } + + export const decode = (buf: Uint8Array): DHTResponse => { + return decodeMessage(buf, DHTResponse.codec()) + } +} + +export interface PeerInfo { + id: Uint8Array + addrs: Uint8Array[] +} + +export namespace PeerInfo { + export const codec = () => { + return message({ + 1: { name: 'id', codec: bytes }, + 2: { name: 'addrs', codec: bytes, repeats: true } + }) + } + + export const encode = (obj: PeerInfo): Uint8Array => { + return encodeMessage(obj, PeerInfo.codec()) + } + + export const decode = (buf: Uint8Array): PeerInfo => { + return decodeMessage(buf, PeerInfo.codec()) + } +} + +export interface ConnManagerRequest { + type: ConnManagerRequest.Type + peer?: Uint8Array + tag?: string + weight?: bigint +} + +export namespace ConnManagerRequest { + export enum Type { + TAG_PEER = 'TAG_PEER', + UNTAG_PEER = 'UNTAG_PEER', + TRIM = 'TRIM' + } + + export namespace Type { + export const codec = () => { + return enumeration(Type) + } + } + + export const codec = () => { + return message({ + 1: { name: 'type', codec: ConnManagerRequest.Type.codec() }, + 2: { name: 'peer', codec: bytes, optional: true }, + 3: { name: 'tag', codec: string, optional: true }, + 4: { name: 'weight', codec: int64, optional: true } + }) + } + + export const encode = (obj: ConnManagerRequest): Uint8Array => { + return encodeMessage(obj, ConnManagerRequest.codec()) + } + + export const decode = (buf: Uint8Array): ConnManagerRequest => { + return decodeMessage(buf, ConnManagerRequest.codec()) + } +} + +export interface DisconnectRequest { + peer: Uint8Array +} + +export namespace DisconnectRequest { + export const codec = () => { + return message({ + 1: { name: 'peer', codec: bytes } + }) + } + + export const encode = (obj: DisconnectRequest): Uint8Array => { + return encodeMessage(obj, DisconnectRequest.codec()) + } + + export const decode = (buf: Uint8Array): DisconnectRequest => { + return decodeMessage(buf, DisconnectRequest.codec()) + } +} + +export interface PSRequest { + type: PSRequest.Type + topic?: string + data?: Uint8Array +} + +export namespace PSRequest { + export enum Type { + GET_TOPICS = 'GET_TOPICS', + LIST_PEERS = 'LIST_PEERS', + PUBLISH = 'PUBLISH', + SUBSCRIBE = 'SUBSCRIBE' + } + + export namespace Type { + export const codec = () => { + return enumeration(Type) + } + } + + export const codec = () => { + return message({ + 1: { name: 'type', codec: PSRequest.Type.codec() }, + 2: { name: 'topic', codec: string, optional: true }, + 3: { name: 'data', codec: bytes, optional: true } + }) + } + + export const encode = (obj: PSRequest): Uint8Array => { + return encodeMessage(obj, PSRequest.codec()) + } + + export const decode = (buf: Uint8Array): PSRequest => { + return decodeMessage(buf, PSRequest.codec()) + } +} + +export interface PSMessage { + from?: Uint8Array + data?: Uint8Array + seqno?: Uint8Array + topicIDs: string[] + signature?: Uint8Array + key?: Uint8Array +} + +export namespace PSMessage { + export const codec = () => { + return message({ + 1: { name: 'from', codec: bytes, optional: true }, + 2: { name: 'data', codec: bytes, optional: true }, + 3: { name: 'seqno', codec: bytes, optional: true }, + 4: { name: 'topicIDs', codec: string, repeats: true }, + 5: { name: 'signature', codec: bytes, optional: true }, + 6: { name: 'key', codec: bytes, optional: true } + }) + } + + export const encode = (obj: PSMessage): Uint8Array => { + return encodeMessage(obj, PSMessage.codec()) + } + + export const decode = (buf: Uint8Array): PSMessage => { + return decodeMessage(buf, PSMessage.codec()) + } +} + +export interface PSResponse { + topics: string[] + peerIDs: Uint8Array[] +} + +export namespace PSResponse { + export const codec = () => { + return message({ + 1: { name: 'topics', codec: string, repeats: true }, + 2: { name: 'peerIDs', codec: bytes, repeats: true } + }) + } + + export const encode = (obj: PSResponse): Uint8Array => { + return encodeMessage(obj, PSResponse.codec()) + } + + export const decode = (buf: Uint8Array): PSResponse => { + return decodeMessage(buf, PSResponse.codec()) + } +} + +export interface PeerstoreRequest { + type: PeerstoreRequest.Type + id?: Uint8Array + protos: string[] +} + +export namespace PeerstoreRequest { + export enum Type { + GET_PROTOCOLS = 'GET_PROTOCOLS', + GET_PEER_INFO = 'GET_PEER_INFO' + } + + export namespace Type { + export const codec = () => { + return enumeration(Type) + } + } + + export const codec = () => { + return message({ + 1: { name: 'type', codec: PeerstoreRequest.Type.codec() }, + 2: { name: 'id', codec: bytes, optional: true }, + 3: { name: 'protos', codec: string, repeats: true } + }) + } + + export const encode = (obj: PeerstoreRequest): Uint8Array => { + return encodeMessage(obj, PeerstoreRequest.codec()) + } + + export const decode = (buf: Uint8Array): PeerstoreRequest => { + return decodeMessage(buf, PeerstoreRequest.codec()) + } +} + +export interface PeerstoreResponse { + peer?: PeerInfo + protos: string[] +} + +export namespace PeerstoreResponse { + export const codec = () => { + return message({ + 1: { name: 'peer', codec: PeerInfo.codec(), optional: true }, + 2: { name: 'protos', codec: string, repeats: true } + }) + } + + export const encode = (obj: PeerstoreResponse): Uint8Array => { + return encodeMessage(obj, PeerstoreResponse.codec()) + } + + export const decode = (buf: Uint8Array): PeerstoreResponse => { + return decodeMessage(buf, PeerstoreResponse.codec()) + } +} diff --git a/packages/protons/test/fixtures/peer.proto b/packages/protons/test/fixtures/peer.proto new file mode 100644 index 0000000..1c9cc16 --- /dev/null +++ b/packages/protons/test/fixtures/peer.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +message Peer { + // Multiaddrs we know about + repeated Address addresses = 1; + + // The protocols the peer supports + repeated string protocols = 2; + + // Any peer metadata + repeated Metadata metadata = 3; + + // The public key of the peer + optional bytes pub_key = 4; + + // The most recently received signed PeerRecord + optional bytes peer_record_envelope = 5; +} + +// Address represents a single multiaddr +message Address { + bytes multiaddr = 1; + + // Flag to indicate if the address comes from a certified source + optional bool isCertified = 2; +} + +message Metadata { + string key = 1; + bytes value = 2; +} diff --git a/packages/protons/test/fixtures/peer.ts b/packages/protons/test/fixtures/peer.ts new file mode 100644 index 0000000..676ded3 --- /dev/null +++ b/packages/protons/test/fixtures/peer.ts @@ -0,0 +1,76 @@ +/* eslint-disable import/export */ +/* eslint-disable @typescript-eslint/no-namespace */ + +import { encodeMessage, decodeMessage, message, string, bytes, bool } from 'protons-runtime' + +export interface Peer { + addresses: Address[] + protocols: string[] + metadata: Metadata[] + pubKey?: Uint8Array + peerRecordEnvelope?: Uint8Array +} + +export namespace Peer { + export const codec = () => { + return message({ + 1: { name: 'addresses', codec: Address.codec(), repeats: true }, + 2: { name: 'protocols', codec: string, repeats: true }, + 3: { name: 'metadata', codec: Metadata.codec(), repeats: true }, + 4: { name: 'pubKey', codec: bytes, optional: true }, + 5: { name: 'peerRecordEnvelope', codec: bytes, optional: true } + }) + } + + export const encode = (obj: Peer): Uint8Array => { + return encodeMessage(obj, Peer.codec()) + } + + export const decode = (buf: Uint8Array): Peer => { + return decodeMessage(buf, Peer.codec()) + } +} + +export interface Address { + multiaddr: Uint8Array + isCertified?: boolean +} + +export namespace Address { + export const codec = () => { + return message
({ + 1: { name: 'multiaddr', codec: bytes }, + 2: { name: 'isCertified', codec: bool, optional: true } + }) + } + + export const encode = (obj: Address): Uint8Array => { + return encodeMessage(obj, Address.codec()) + } + + export const decode = (buf: Uint8Array): Address => { + return decodeMessage(buf, Address.codec()) + } +} + +export interface Metadata { + key: string + value: Uint8Array +} + +export namespace Metadata { + export const codec = () => { + return message({ + 1: { name: 'key', codec: string }, + 2: { name: 'value', codec: bytes } + }) + } + + export const encode = (obj: Metadata): Uint8Array => { + return encodeMessage(obj, Metadata.codec()) + } + + export const decode = (buf: Uint8Array): Metadata => { + return decodeMessage(buf, Metadata.codec()) + } +} diff --git a/packages/protons/test/index.spec.ts b/packages/protons/test/index.spec.ts index d35a24d..715e733 100644 --- a/packages/protons/test/index.spec.ts +++ b/packages/protons/test/index.spec.ts @@ -1,12 +1,13 @@ /* eslint-env mocha */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ -import { expect } from 'aegir/utils/chai.js' +import { expect } from 'aegir/chai' import pbjs from 'pbjs' import { Basic } from './fixtures/basic.js' import { AllTheTypes, AnEnum } from './fixtures/test.js' import fs from 'fs' import protobufjs from 'protobufjs' +import { Peer } from './fixtures/peer.js' const Long = protobufjs.util.Long @@ -136,4 +137,27 @@ describe('encode', () => { expect(AllTheTypes.decode(encoded)).to.deep.equal(allTheTypes) expect(AllTheTypes.decode(pbjsBuf)).to.deep.equal(allTheTypes) }) + + it('decodes multiple sub messages', () => { + const peer: Peer = { + protocols: ['protocol1', 'protocol2'], + metadata: [], + addresses: [{ + multiaddr: Uint8Array.from([4, 127, 0, 0, 1, 6, 31, 64]), + isCertified: false + }, { + multiaddr: Uint8Array.from([4, 20, 0, 0, 1, 6, 31, 65]), + isCertified: false + }] + } + + const schema = pbjs.parseSchema(fs.readFileSync('./test/fixtures/peer.proto', 'utf-8')).compile() + const pbjsBuf = schema.encodePeer(peer) + + const encoded = Peer.encode(peer) + expect(encoded).to.equalBytes(pbjsBuf) + + expect(Peer.decode(encoded)).to.deep.equal(peer) + expect(Peer.decode(pbjsBuf)).to.deep.equal(peer) + }) }) diff --git a/packages/protons/tsconfig.json b/packages/protons/tsconfig.json index 7f21b4d..cac17d8 100644 --- a/packages/protons/tsconfig.json +++ b/packages/protons/tsconfig.json @@ -1,9 +1,7 @@ { "extends": "aegir/src/config/tsconfig.aegir.json", "compilerOptions": { - "outDir": "dist", - "emitDeclarationOnly": false, - "module": "ES2020" + "outDir": "dist" }, "include": [ "bin",