├── .github ├── dependabot.yml └── workflows │ ├── generated-pr.yml │ ├── js-test-and-release.yml │ └── stale.yml ├── .gitignore ├── CHANGELOG.md ├── FUNDING.json ├── LICENSE ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── package.json ├── packages ├── protons-benchmark │ ├── .aegir.js │ ├── LICENSE │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── package.json │ ├── src │ │ ├── implementations │ │ │ ├── bench.proto │ │ │ ├── index.ts │ │ │ ├── pbjs │ │ │ │ └── bench.ts │ │ │ ├── protobuf-es │ │ │ │ └── bench_pb.ts │ │ │ ├── protobuf-ts │ │ │ │ └── bench.ts │ │ │ ├── protobufjs │ │ │ │ ├── bench.d.ts │ │ │ │ ├── bench.js │ │ │ │ ├── rpc.d.ts │ │ │ │ └── rpc.js │ │ │ └── protons │ │ │ │ ├── bench.ts │ │ │ │ └── rpc.ts │ │ └── numbers │ │ │ ├── index.ts │ │ │ ├── read.ts │ │ │ └── write.ts │ ├── tsconfig.json │ └── typedoc.json ├── protons-runtime │ ├── CHANGELOG.md │ ├── LICENSE │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── package.json │ ├── protons.proto │ ├── src │ │ ├── codec.ts │ │ ├── codecs │ │ │ ├── enum.ts │ │ │ └── message.ts │ │ ├── decode.ts │ │ ├── encode.ts │ │ ├── index.ts │ │ └── utils │ │ │ ├── float.ts │ │ │ ├── longbits.ts │ │ │ ├── pool.ts │ │ │ ├── reader.ts │ │ │ ├── utf8.ts │ │ │ └── writer.ts │ ├── tsconfig.json │ └── typedoc.json └── protons │ ├── .aegir.js │ ├── CHANGELOG.md │ ├── LICENSE │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── bin │ └── protons.ts │ ├── package.json │ ├── src │ └── index.ts │ ├── test │ ├── bad-fixtures │ │ ├── empty.proto │ │ ├── enum.proto │ │ └── proto2.proto │ ├── compatibility.spec.ts │ ├── custom-options.spec.ts │ ├── fixtures │ │ ├── basic.proto │ │ ├── basic.ts │ │ ├── bitswap.proto │ │ ├── bitswap.ts │ │ ├── circuit.proto │ │ ├── circuit.ts │ │ ├── custom-option-jstype.proto │ │ ├── custom-option-jstype.ts │ │ ├── daemon.proto │ │ ├── daemon.ts │ │ ├── dht.proto │ │ ├── dht.ts │ │ ├── maps.proto │ │ ├── maps.ts │ │ ├── noise.proto │ │ ├── noise.ts │ │ ├── optional.proto │ │ ├── optional.ts │ │ ├── peer.proto │ │ ├── peer.ts │ │ ├── proto2.proto │ │ ├── proto2.ts │ │ ├── protons-options.proto │ │ ├── protons-options.ts │ │ ├── repeated.proto │ │ ├── repeated.ts │ │ ├── singular.proto │ │ ├── singular.ts │ │ ├── test.proto │ │ └── test.ts │ ├── maps.spec.ts │ ├── proto2.spec.ts │ ├── protons-options.spec.ts │ ├── repeated.spec.ts │ └── unsupported.spec.ts │ ├── tsconfig.json │ └── typedoc.json └── typedoc.json /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "10:00" 8 | open-pull-requests-limit: 10 9 | commit-message: 10 | prefix: "deps" 11 | prefix-development: "deps(dev)" 12 | -------------------------------------------------------------------------------- /.github/workflows/generated-pr.yml: -------------------------------------------------------------------------------- 1 | name: Close Generated PRs 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | 12 | jobs: 13 | stale: 14 | uses: ipdxco/unified-github-workflows/.github/workflows/reusable-generated-pr.yml@v1 15 | -------------------------------------------------------------------------------- /.github/workflows/js-test-and-release.yml: -------------------------------------------------------------------------------- 1 | name: test & maybe release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | permissions: 11 | contents: write 12 | id-token: write 13 | packages: write 14 | pull-requests: write 15 | 16 | concurrency: 17 | group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }} 18 | cancel-in-progress: true 19 | 20 | jobs: 21 | js-test-and-release: 22 | uses: ipdxco/unified-github-workflows/.github/workflows/js-test-and-release.yml@v1.0 23 | secrets: 24 | DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} 25 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} 26 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 27 | UCI_GITHUB_TOKEN: ${{ secrets.UCI_GITHUB_TOKEN }} 28 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 29 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close Stale Issues 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | 12 | jobs: 13 | stale: 14 | uses: ipdxco/unified-github-workflows/.github/workflows/reusable-stale-issue.yml@v1 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | dist 4 | .docs 5 | .coverage 6 | node_modules 7 | package-lock.json 8 | yarn.lock 9 | .vscode 10 | -------------------------------------------------------------------------------- /FUNDING.json: -------------------------------------------------------------------------------- 1 | { 2 | "opRetro": { 3 | "projectId": "0x7f330267969cf845a983a9d4e7b7dbcca5c700a5191269af377836d109e0bb69" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This project is dual licensed under MIT and Apache-2.0. 2 | 3 | MIT: https://www.opensource.org/licenses/mit 4 | Apache-2.0: https://www.apache.org/licenses/license-2.0 5 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 2 | 3 | http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 6 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # protons 2 | 3 | [![ipfs.tech](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.tech) 4 | [![Discuss](https://img.shields.io/discourse/https/discuss.ipfs.tech/posts.svg?style=flat-square)](https://discuss.ipfs.tech) 5 | [![codecov](https://img.shields.io/codecov/c/github/ipfs/protons.svg?style=flat-square)](https://codecov.io/gh/ipfs/protons) 6 | [![CI](https://img.shields.io/github/actions/workflow/status/ipfs/protons/js-test-and-release.yml?branch=main\&style=flat-square)](https://github.com/ipfs/protons/actions/workflows/js-test-and-release.yml?query=branch%3Amain) 7 | 8 | > Protobuf to ts transpiler 9 | 10 | `protons` is a high performance implementation of [Protocol Buffers v3](https://protobuf.dev/programming-guides/proto3/). 11 | 12 | # Packages 13 | 14 | - [`/packages/protons`](./packages/protons) The transpiler 15 | - [`/packages/protons-benchmark`](./packages/protons-benchmark) A benchmark suite 16 | - [`/packages/protons-runtime`](./packages/protons-runtime) Shared components that turn values to bytes and back again 17 | 18 | # API Docs 19 | 20 | - 21 | 22 | # License 23 | 24 | Licensed under either of 25 | 26 | - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) 27 | - MIT ([LICENSE-MIT](LICENSE-MIT) / ) 28 | 29 | # Contribute 30 | 31 | Contributions welcome! Please check out [the issues](https://github.com/ipfs/protons/issues). 32 | 33 | Also see our [contributing document](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md) for more information on how we work, and about contributing in general. 34 | 35 | Please be aware that all interactions related to this repo are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). 36 | 37 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 38 | 39 | [![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "protons", 3 | "version": "3.1.1", 4 | "description": "Protobuf to ts transpiler", 5 | "license": "Apache-2.0 OR MIT", 6 | "homepage": "https://github.com/ipfs/protons#readme", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/ipfs/protons.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/ipfs/protons/issues" 13 | }, 14 | "keywords": [ 15 | "protobuf", 16 | "protons" 17 | ], 18 | "private": true, 19 | "scripts": { 20 | "reset": "aegir run clean && aegir clean **/node_modules **/package-lock.json", 21 | "test": "aegir run test", 22 | "test:node": "aegir run test:node", 23 | "test:chrome": "aegir run test:chrome", 24 | "test:chrome-webworker": "aegir run test:chrome-webworker", 25 | "test:firefox": "aegir run test:firefox", 26 | "test:firefox-webworker": "aegir run test:firefox-webworker", 27 | "test:electron-main": "aegir run test:electron-main", 28 | "test:electron-renderer": "aegir run test:electron-renderer", 29 | "clean": "aegir run clean", 30 | "generate": "aegir run generate", 31 | "build": "aegir run build", 32 | "lint": "aegir run lint", 33 | "dep-check": "aegir run dep-check", 34 | "doc-check": "aegir run doc-check", 35 | "release": "run-s build docs:no-publish npm:release docs", 36 | "npm:release": "aegir run release", 37 | "docs": "aegir docs", 38 | "docs:no-publish": "aegir docs --publish false" 39 | }, 40 | "dependencies": { 41 | "aegir": "^47.0.5", 42 | "npm-run-all": "^4.1.5" 43 | }, 44 | "workspaces": [ 45 | "packages/*" 46 | ], 47 | "release": { 48 | "branches": [ 49 | "main" 50 | ], 51 | "plugins": [ 52 | [ 53 | "@semantic-release/commit-analyzer", 54 | { 55 | "preset": "conventionalcommits", 56 | "releaseRules": [ 57 | { 58 | "breaking": true, 59 | "release": "major" 60 | }, 61 | { 62 | "revert": true, 63 | "release": "patch" 64 | }, 65 | { 66 | "type": "feat", 67 | "release": "minor" 68 | }, 69 | { 70 | "type": "fix", 71 | "release": "patch" 72 | }, 73 | { 74 | "type": "docs", 75 | "release": "patch" 76 | }, 77 | { 78 | "type": "test", 79 | "release": "patch" 80 | }, 81 | { 82 | "type": "deps", 83 | "release": "patch" 84 | }, 85 | { 86 | "scope": "no-release", 87 | "release": false 88 | } 89 | ] 90 | } 91 | ], 92 | [ 93 | "@semantic-release/release-notes-generator", 94 | { 95 | "preset": "conventionalcommits", 96 | "presetConfig": { 97 | "types": [ 98 | { 99 | "type": "feat", 100 | "section": "Features" 101 | }, 102 | { 103 | "type": "fix", 104 | "section": "Bug Fixes" 105 | }, 106 | { 107 | "type": "chore", 108 | "section": "Trivial Changes" 109 | }, 110 | { 111 | "type": "docs", 112 | "section": "Documentation" 113 | }, 114 | { 115 | "type": "deps", 116 | "section": "Dependencies" 117 | }, 118 | { 119 | "type": "test", 120 | "section": "Tests" 121 | } 122 | ] 123 | } 124 | } 125 | ], 126 | "@semantic-release/changelog", 127 | "@semantic-release/npm", 128 | "@semantic-release/github", 129 | "@semantic-release/git" 130 | ] 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /packages/protons-benchmark/.aegir.js: -------------------------------------------------------------------------------- 1 | 2 | export default { 3 | build: { 4 | config: { 5 | platform: 'node' 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/protons-benchmark/LICENSE: -------------------------------------------------------------------------------- 1 | This project is dual licensed under MIT and Apache-2.0. 2 | 3 | MIT: https://www.opensource.org/licenses/mit 4 | Apache-2.0: https://www.apache.org/licenses/license-2.0 5 | -------------------------------------------------------------------------------- /packages/protons-benchmark/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 2 | 3 | http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 6 | -------------------------------------------------------------------------------- /packages/protons-benchmark/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/protons-benchmark/README.md: -------------------------------------------------------------------------------- 1 | # protons-benchmark 2 | 3 | [![ipfs.tech](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.tech) 4 | [![Discuss](https://img.shields.io/discourse/https/discuss.ipfs.tech/posts.svg?style=flat-square)](https://discuss.ipfs.tech) 5 | [![codecov](https://img.shields.io/codecov/c/github/ipfs/protons.svg?style=flat-square)](https://codecov.io/gh/ipfs/protons) 6 | [![CI](https://img.shields.io/github/actions/workflow/status/ipfs/protons/js-test-and-release.yml?branch=main\&style=flat-square)](https://github.com/ipfs/protons/actions/workflows/js-test-and-release.yml?query=branch%3Amain) 7 | 8 | > Protobuf to ts transpiler 9 | 10 | ## Table of contents 11 | 12 | - [Install](#install) 13 | - [Usage](#usage) 14 | - [API Docs](#api-docs) 15 | - [License](#license) 16 | - [Contribute](#contribute) 17 | 18 | ## Install 19 | 20 | ```console 21 | $ git clone git@github.com:ipfs/protons.git 22 | $ cd protons 23 | $ npm i 24 | $ npm run build 25 | $ cd packages/protons-benchmark 26 | ``` 27 | 28 | ## Usage 29 | 30 | Run the benchmark suite in node: 31 | 32 | ```console 33 | $ npm start 34 | 35 | > protons-benchmark@0.0.0 prestart 36 | > npm run build 37 | 38 | 39 | > protons-benchmark@0.0.0 build 40 | > aegir build --no-bundle && cp -R src/protobufjs dist/src/protobufjs 41 | 42 | [15:02:28] tsc [started] 43 | [15:02:32] tsc [completed] 44 | 45 | > protons-benchmark@0.0.0 start 46 | > node dist/src/index.js 47 | 48 | pbjs x 19,188 ops/sec ±0.38% (98 runs sampled) 49 | protons x 19,001 ops/sec ±0.33% (95 runs sampled) 50 | protobuf.js x 19,558 ops/sec ±0.30% (91 runs sampled) 51 | @protobuf-ts x 17,216 ops/sec ±0.32% (95 runs sampled) 52 | protobuf-es x 15,673 ops/sec ±0.48% (93 runs sampled) 53 | Fastest is protobuf.js 54 | ``` 55 | 56 | Or in a browser: 57 | 58 | ```console 59 | $ npm run start:browser 60 | 61 | > protons-benchmark@0.0.0 start:browser 62 | > npx playwright-test dist/src/index.js --runner benchmark 63 | 64 | ✔ chromium set up 65 | pbjs x 19,763 ops/sec ±0.35% (67 runs sampled) 66 | protons x 19,617 ops/sec ±0.37% (68 runs sampled) 67 | protobuf.js x 19,772 ops/sec ±0.34% (67 runs sampled) 68 | @protobuf-ts x 17,204 ops/sec ±0.33% (69 runs sampled) 69 | protobuf-es x 16,032 ops/sec ±0.38% (68 runs sampled) 70 | Fastest is protobuf.js,pbjs 71 | ``` 72 | 73 | ## API Docs 74 | 75 | - 76 | 77 | ## License 78 | 79 | Licensed under either of 80 | 81 | - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) 82 | - MIT ([LICENSE-MIT](LICENSE-MIT) / ) 83 | 84 | ## Contribute 85 | 86 | Contributions welcome! Please check out [the issues](https://github.com/ipfs/protons/issues). 87 | 88 | Also see our [contributing document](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md) for more information on how we work, and about contributing in general. 89 | 90 | Please be aware that all interactions related to this repo are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). 91 | 92 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 93 | 94 | [![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) 95 | -------------------------------------------------------------------------------- /packages/protons-benchmark/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "protons-benchmark", 3 | "version": "0.0.0", 4 | "description": "Protobuf to ts transpiler", 5 | "license": "Apache-2.0 OR MIT", 6 | "homepage": "https://github.com/ipfs/protons/tree/master/packages/protons-benchmark#readme", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/ipfs/protons.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/ipfs/protons/issues" 13 | }, 14 | "type": "module", 15 | "types": "./dist/src/index.d.ts", 16 | "files": [ 17 | "src", 18 | "dist", 19 | "!dist/test", 20 | "!**/*.tsbuildinfo" 21 | ], 22 | "exports": { 23 | ".": { 24 | "types": "./dist/src/index.d.ts", 25 | "import": "./dist/src/index.js" 26 | } 27 | }, 28 | "eslintConfig": { 29 | "extends": "ipfs", 30 | "parserOptions": { 31 | "project": true, 32 | "sourceType": "module" 33 | }, 34 | "ignorePatterns": [ 35 | "src/implementations/pbjs/*", 36 | "src/implementations/protobuf-es/*", 37 | "src/implementations/protobuf-ts/*", 38 | "src/implementations/protobufjs/*" 39 | ] 40 | }, 41 | "scripts": { 42 | "clean": "aegir clean", 43 | "lint": "aegir lint", 44 | "dep-check": "aegir dep-check --ignore @protobuf-ts/plugin pbjs protons @bufbuild/protoc-gen-es", 45 | "build": "aegir build --no-bundle && cp -R src/implementations/protobufjs dist/src/implementations/protobufjs", 46 | "prestart": "npm run build", 47 | "start": "node dist/src/implementations/index.js", 48 | "start:browser": "npx playwright-test dist/src/implementations/index.js --runner benchmark" 49 | }, 50 | "dependencies": { 51 | "@bufbuild/protobuf": "^2.0.0", 52 | "@bufbuild/protoc-gen-es": "^2.0.0", 53 | "@protobuf-ts/plugin": "^2.8.1", 54 | "@protobuf-ts/runtime": "^2.8.1", 55 | "@types/benchmark": "^2.1.1", 56 | "aegir": "^47.0.5", 57 | "benchmark": "^2.1.4", 58 | "pbjs": "^0.0.14", 59 | "protobufjs": "^7.0.0", 60 | "protons": "^7.0.0", 61 | "protons-runtime": "^5.0.0", 62 | "uint8arraylist": "^2.4.3" 63 | }, 64 | "private": true 65 | } 66 | -------------------------------------------------------------------------------- /packages/protons-benchmark/src/implementations/bench.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message Foo { 4 | optional uint32 baz = 1; 5 | } 6 | 7 | message Bar { 8 | optional Foo tmp = 1; 9 | } 10 | 11 | enum FOO { 12 | NONE=0; 13 | LOL=1; 14 | ABE=3; 15 | } 16 | 17 | message Yo { 18 | repeated FOO lol = 1; 19 | } 20 | 21 | message Lol { 22 | optional string lol = 1; 23 | Bar b = 2; 24 | } 25 | 26 | message Test { 27 | optional Lol meh = 6; 28 | optional uint32 hello = 3; 29 | optional string foo = 1; 30 | optional bytes payload = 7; 31 | } 32 | -------------------------------------------------------------------------------- /packages/protons-benchmark/src/implementations/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | /* 4 | $ node dist/src/index.js 5 | $ npx playwright-test dist/src/index.js --runner benchmark 6 | */ 7 | 8 | import { create, toBinary, fromBinary } from '@bufbuild/protobuf' 9 | import { expect } from 'aegir/chai' 10 | import Benchmark from 'benchmark' 11 | import { encodeTest as pbjsEncodeTest, decodeTest as pbjsDecodeTest } from './pbjs/bench.js' 12 | import { TestSchema as ProtobufEsTest } from './protobuf-es/bench_pb.js' 13 | import { Test as ProtobufTsTest } from './protobuf-ts/bench.js' 14 | import { Test as ProtobufjsTest } from './protobufjs/bench.js' 15 | import { Test as ProtonsTest } from './protons/bench.js' 16 | 17 | const message = { 18 | meh: { 19 | lol: 'sdkljfoee', 20 | b: { 21 | tmp: { 22 | baz: 2309292 23 | } 24 | } 25 | }, 26 | hello: 3493822, 27 | foo: 'derp derp derp', 28 | payload: Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) 29 | } 30 | 31 | function expectDecodedCorrectly (result: any): void { 32 | expect(result).to.have.nested.property('meh.lol', message.meh.lol) 33 | expect(result).to.have.nested.property('meh.b.tmp.baz', message.meh.b.tmp.baz) 34 | expect(result).to.have.property('hello', message.hello) 35 | expect(result).to.have.property('foo', message.foo) 36 | expect(result).to.have.property('payload').that.equalBytes(message.payload) 37 | } 38 | 39 | new Benchmark.Suite() 40 | .add('pbjs', () => { 41 | const buf = pbjsEncodeTest(message) 42 | const result = pbjsDecodeTest(buf) 43 | 44 | expectDecodedCorrectly(result) 45 | }) 46 | .add('protons', () => { 47 | const buf = ProtonsTest.encode(message) 48 | const result = ProtonsTest.decode(buf) 49 | 50 | expectDecodedCorrectly(result) 51 | }) 52 | .add('protobuf.js', () => { 53 | const buf = ProtobufjsTest.encode(message).finish() 54 | const result = ProtobufjsTest.decode(buf) 55 | 56 | expectDecodedCorrectly(result) 57 | }) 58 | .add('@protobuf-ts', () => { 59 | const buf = ProtobufTsTest.toBinary(message) 60 | const result = ProtobufTsTest.fromBinary(buf) 61 | 62 | expectDecodedCorrectly(result) 63 | }) 64 | .add('protobuf-es', () => { 65 | const buf = toBinary(ProtobufEsTest, create(ProtobufEsTest, message)) 66 | const result = fromBinary(ProtobufEsTest, buf) 67 | 68 | expectDecodedCorrectly(result) 69 | }) 70 | .on('error', (err: Error) => { 71 | console.error(err) 72 | }) 73 | .on('cycle', (event: any) => { 74 | console.info(String(event.target)) 75 | }) 76 | .on('complete', function () { 77 | // @ts-expect-error types are wrong 78 | console.info(`Fastest is ${this.filter('fastest').map('name')}`) 79 | }) 80 | // run async 81 | .run({ async: true }) 82 | -------------------------------------------------------------------------------- /packages/protons-benchmark/src/implementations/protobuf-es/bench_pb.ts: -------------------------------------------------------------------------------- 1 | // @generated by protoc-gen-es v2.0.0 with parameter "target=ts" 2 | // @generated from file packages/protons-benchmark/src/implementations/bench.proto (syntax proto3) 3 | /* eslint-disable */ 4 | 5 | import type { GenEnum, GenFile, GenMessage } from "@bufbuild/protobuf/codegenv1"; 6 | import { enumDesc, fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv1"; 7 | import type { Message } from "@bufbuild/protobuf"; 8 | 9 | /** 10 | * Describes the file packages/protons-benchmark/src/implementations/bench.proto. 11 | */ 12 | export const file_packages_protons_benchmark_src_implementations_bench: GenFile = /*@__PURE__*/ 13 | fileDesc("CjpwYWNrYWdlcy9wcm90b25zLWJlbmNobWFyay9zcmMvaW1wbGVtZW50YXRpb25zL2JlbmNoLnByb3RvIh8KA0ZvbxIQCgNiYXoYASABKA1IAIgBAUIGCgRfYmF6IiUKA0JhchIWCgN0bXAYASABKAsyBC5Gb29IAIgBAUIGCgRfdG1wIhcKAllvEhEKA2xvbBgBIAMoDjIELkZPTyIwCgNMb2wSEAoDbG9sGAEgASgJSACIAQESDwoBYhgCIAEoCzIELkJhckIGCgRfbG9sIoABCgRUZXN0EhYKA21laBgGIAEoCzIELkxvbEgAiAEBEhIKBWhlbGxvGAMgASgNSAGIAQESEAoDZm9vGAEgASgJSAKIAQESFAoHcGF5bG9hZBgHIAEoDEgDiAEBQgYKBF9tZWhCCAoGX2hlbGxvQgYKBF9mb29CCgoIX3BheWxvYWQqIQoDRk9PEggKBE5PTkUQABIHCgNMT0wQARIHCgNBQkUQA2IGcHJvdG8z"); 14 | 15 | /** 16 | * @generated from message Foo 17 | */ 18 | export type Foo = Message<"Foo"> & { 19 | /** 20 | * @generated from field: optional uint32 baz = 1; 21 | */ 22 | baz?: number; 23 | }; 24 | 25 | /** 26 | * Describes the message Foo. 27 | * Use `create(FooSchema)` to create a new message. 28 | */ 29 | export const FooSchema: GenMessage = /*@__PURE__*/ 30 | messageDesc(file_packages_protons_benchmark_src_implementations_bench, 0); 31 | 32 | /** 33 | * @generated from message Bar 34 | */ 35 | export type Bar = Message<"Bar"> & { 36 | /** 37 | * @generated from field: optional Foo tmp = 1; 38 | */ 39 | tmp?: Foo; 40 | }; 41 | 42 | /** 43 | * Describes the message Bar. 44 | * Use `create(BarSchema)` to create a new message. 45 | */ 46 | export const BarSchema: GenMessage = /*@__PURE__*/ 47 | messageDesc(file_packages_protons_benchmark_src_implementations_bench, 1); 48 | 49 | /** 50 | * @generated from message Yo 51 | */ 52 | export type Yo = Message<"Yo"> & { 53 | /** 54 | * @generated from field: repeated FOO lol = 1; 55 | */ 56 | lol: FOO[]; 57 | }; 58 | 59 | /** 60 | * Describes the message Yo. 61 | * Use `create(YoSchema)` to create a new message. 62 | */ 63 | export const YoSchema: GenMessage = /*@__PURE__*/ 64 | messageDesc(file_packages_protons_benchmark_src_implementations_bench, 2); 65 | 66 | /** 67 | * @generated from message Lol 68 | */ 69 | export type Lol = Message<"Lol"> & { 70 | /** 71 | * @generated from field: optional string lol = 1; 72 | */ 73 | lol?: string; 74 | 75 | /** 76 | * @generated from field: Bar b = 2; 77 | */ 78 | b?: Bar; 79 | }; 80 | 81 | /** 82 | * Describes the message Lol. 83 | * Use `create(LolSchema)` to create a new message. 84 | */ 85 | export const LolSchema: GenMessage = /*@__PURE__*/ 86 | messageDesc(file_packages_protons_benchmark_src_implementations_bench, 3); 87 | 88 | /** 89 | * @generated from message Test 90 | */ 91 | export type Test = Message<"Test"> & { 92 | /** 93 | * @generated from field: optional Lol meh = 6; 94 | */ 95 | meh?: Lol; 96 | 97 | /** 98 | * @generated from field: optional uint32 hello = 3; 99 | */ 100 | hello?: number; 101 | 102 | /** 103 | * @generated from field: optional string foo = 1; 104 | */ 105 | foo?: string; 106 | 107 | /** 108 | * @generated from field: optional bytes payload = 7; 109 | */ 110 | payload?: Uint8Array; 111 | }; 112 | 113 | /** 114 | * Describes the message Test. 115 | * Use `create(TestSchema)` to create a new message. 116 | */ 117 | export const TestSchema: GenMessage = /*@__PURE__*/ 118 | messageDesc(file_packages_protons_benchmark_src_implementations_bench, 4); 119 | 120 | /** 121 | * @generated from enum FOO 122 | */ 123 | export enum FOO { 124 | /** 125 | * @generated from enum value: NONE = 0; 126 | */ 127 | NONE = 0, 128 | 129 | /** 130 | * @generated from enum value: LOL = 1; 131 | */ 132 | LOL = 1, 133 | 134 | /** 135 | * @generated from enum value: ABE = 3; 136 | */ 137 | ABE = 3, 138 | } 139 | 140 | /** 141 | * Describes the enum FOO. 142 | */ 143 | export const FOOSchema: GenEnum = /*@__PURE__*/ 144 | enumDesc(file_packages_protons_benchmark_src_implementations_bench, 0); 145 | 146 | -------------------------------------------------------------------------------- /packages/protons-benchmark/src/implementations/protobufjs/bench.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import * as $protobuf from "protobufjs"; 3 | /** Properties of a Foo. */ 4 | export interface IFoo { 5 | 6 | /** Foo baz */ 7 | baz?: (number|null); 8 | } 9 | 10 | /** Represents a Foo. */ 11 | export class Foo implements IFoo { 12 | 13 | /** 14 | * Constructs a new Foo. 15 | * @param [p] Properties to set 16 | */ 17 | constructor(p?: IFoo); 18 | 19 | /** Foo baz. */ 20 | public baz: number; 21 | 22 | /** 23 | * Encodes the specified Foo message. Does not implicitly {@link Foo.verify|verify} messages. 24 | * @param m Foo message or plain object to encode 25 | * @param [w] Writer to encode to 26 | * @returns Writer 27 | */ 28 | public static encode(m: IFoo, w?: $protobuf.Writer): $protobuf.Writer; 29 | 30 | /** 31 | * Decodes a Foo message from the specified reader or buffer. 32 | * @param r Reader or buffer to decode from 33 | * @param [l] Message length if known beforehand 34 | * @returns Foo 35 | * @throws {Error} If the payload is not a reader or valid buffer 36 | * @throws {$protobuf.util.ProtocolError} If required fields are missing 37 | */ 38 | public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): Foo; 39 | 40 | /** 41 | * Creates a Foo message from a plain object. Also converts values to their respective internal types. 42 | * @param d Plain object 43 | * @returns Foo 44 | */ 45 | public static fromObject(d: { [k: string]: any }): Foo; 46 | 47 | /** 48 | * Creates a plain object from a Foo message. Also converts values to other types if specified. 49 | * @param m Foo 50 | * @param [o] Conversion options 51 | * @returns Plain object 52 | */ 53 | public static toObject(m: Foo, o?: $protobuf.IConversionOptions): { [k: string]: any }; 54 | 55 | /** 56 | * Converts this Foo to JSON. 57 | * @returns JSON object 58 | */ 59 | public toJSON(): { [k: string]: any }; 60 | } 61 | 62 | /** Properties of a Bar. */ 63 | export interface IBar { 64 | 65 | /** Bar tmp */ 66 | tmp?: (IFoo|null); 67 | } 68 | 69 | /** Represents a Bar. */ 70 | export class Bar implements IBar { 71 | 72 | /** 73 | * Constructs a new Bar. 74 | * @param [p] Properties to set 75 | */ 76 | constructor(p?: IBar); 77 | 78 | /** Bar tmp. */ 79 | public tmp?: (IFoo|null); 80 | 81 | /** 82 | * Encodes the specified Bar message. Does not implicitly {@link Bar.verify|verify} messages. 83 | * @param m Bar message or plain object to encode 84 | * @param [w] Writer to encode to 85 | * @returns Writer 86 | */ 87 | public static encode(m: IBar, w?: $protobuf.Writer): $protobuf.Writer; 88 | 89 | /** 90 | * Decodes a Bar message from the specified reader or buffer. 91 | * @param r Reader or buffer to decode from 92 | * @param [l] Message length if known beforehand 93 | * @returns Bar 94 | * @throws {Error} If the payload is not a reader or valid buffer 95 | * @throws {$protobuf.util.ProtocolError} If required fields are missing 96 | */ 97 | public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): Bar; 98 | 99 | /** 100 | * Creates a Bar message from a plain object. Also converts values to their respective internal types. 101 | * @param d Plain object 102 | * @returns Bar 103 | */ 104 | public static fromObject(d: { [k: string]: any }): Bar; 105 | 106 | /** 107 | * Creates a plain object from a Bar message. Also converts values to other types if specified. 108 | * @param m Bar 109 | * @param [o] Conversion options 110 | * @returns Plain object 111 | */ 112 | public static toObject(m: Bar, o?: $protobuf.IConversionOptions): { [k: string]: any }; 113 | 114 | /** 115 | * Converts this Bar to JSON. 116 | * @returns JSON object 117 | */ 118 | public toJSON(): { [k: string]: any }; 119 | } 120 | 121 | /** FOO enum. */ 122 | export enum FOO { 123 | LOL = 1, 124 | ABE = 3 125 | } 126 | 127 | /** Represents a Yo. */ 128 | export class Yo implements IYo { 129 | 130 | /** 131 | * Constructs a new Yo. 132 | * @param [p] Properties to set 133 | */ 134 | constructor(p?: IYo); 135 | 136 | /** Yo lol. */ 137 | public lol: FOO[]; 138 | 139 | /** 140 | * Encodes the specified Yo message. Does not implicitly {@link Yo.verify|verify} messages. 141 | * @param m Yo message or plain object to encode 142 | * @param [w] Writer to encode to 143 | * @returns Writer 144 | */ 145 | public static encode(m: IYo, w?: $protobuf.Writer): $protobuf.Writer; 146 | 147 | /** 148 | * Decodes a Yo message from the specified reader or buffer. 149 | * @param r Reader or buffer to decode from 150 | * @param [l] Message length if known beforehand 151 | * @returns Yo 152 | * @throws {Error} If the payload is not a reader or valid buffer 153 | * @throws {$protobuf.util.ProtocolError} If required fields are missing 154 | */ 155 | public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): Yo; 156 | 157 | /** 158 | * Creates a Yo message from a plain object. Also converts values to their respective internal types. 159 | * @param d Plain object 160 | * @returns Yo 161 | */ 162 | public static fromObject(d: { [k: string]: any }): Yo; 163 | 164 | /** 165 | * Creates a plain object from a Yo message. Also converts values to other types if specified. 166 | * @param m Yo 167 | * @param [o] Conversion options 168 | * @returns Plain object 169 | */ 170 | public static toObject(m: Yo, o?: $protobuf.IConversionOptions): { [k: string]: any }; 171 | 172 | /** 173 | * Converts this Yo to JSON. 174 | * @returns JSON object 175 | */ 176 | public toJSON(): { [k: string]: any }; 177 | } 178 | 179 | /** Represents a Lol. */ 180 | export class Lol implements ILol { 181 | 182 | /** 183 | * Constructs a new Lol. 184 | * @param [p] Properties to set 185 | */ 186 | constructor(p?: ILol); 187 | 188 | /** Lol lol. */ 189 | public lol: string; 190 | 191 | /** Lol b. */ 192 | public b: IBar; 193 | 194 | /** 195 | * Encodes the specified Lol message. Does not implicitly {@link Lol.verify|verify} messages. 196 | * @param m Lol message or plain object to encode 197 | * @param [w] Writer to encode to 198 | * @returns Writer 199 | */ 200 | public static encode(m: ILol, w?: $protobuf.Writer): $protobuf.Writer; 201 | 202 | /** 203 | * Decodes a Lol message from the specified reader or buffer. 204 | * @param r Reader or buffer to decode from 205 | * @param [l] Message length if known beforehand 206 | * @returns Lol 207 | * @throws {Error} If the payload is not a reader or valid buffer 208 | * @throws {$protobuf.util.ProtocolError} If required fields are missing 209 | */ 210 | public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): Lol; 211 | 212 | /** 213 | * Creates a Lol message from a plain object. Also converts values to their respective internal types. 214 | * @param d Plain object 215 | * @returns Lol 216 | */ 217 | public static fromObject(d: { [k: string]: any }): Lol; 218 | 219 | /** 220 | * Creates a plain object from a Lol message. Also converts values to other types if specified. 221 | * @param m Lol 222 | * @param [o] Conversion options 223 | * @returns Plain object 224 | */ 225 | public static toObject(m: Lol, o?: $protobuf.IConversionOptions): { [k: string]: any }; 226 | 227 | /** 228 | * Converts this Lol to JSON. 229 | * @returns JSON object 230 | */ 231 | public toJSON(): { [k: string]: any }; 232 | } 233 | 234 | /** Represents a Test. */ 235 | export class Test implements ITest { 236 | 237 | /** 238 | * Constructs a new Test. 239 | * @param [p] Properties to set 240 | */ 241 | constructor(p?: ITest); 242 | 243 | /** Test meh. */ 244 | public meh?: (ILol|null); 245 | 246 | /** Test hello. */ 247 | public hello: number; 248 | 249 | /** Test foo. */ 250 | public foo: string; 251 | 252 | /** Test payload. */ 253 | public payload: Uint8Array; 254 | 255 | /** 256 | * Encodes the specified Test message. Does not implicitly {@link Test.verify|verify} messages. 257 | * @param m Test message or plain object to encode 258 | * @param [w] Writer to encode to 259 | * @returns Writer 260 | */ 261 | public static encode(m: ITest, w?: $protobuf.Writer): $protobuf.Writer; 262 | 263 | /** 264 | * Decodes a Test message from the specified reader or buffer. 265 | * @param r Reader or buffer to decode from 266 | * @param [l] Message length if known beforehand 267 | * @returns Test 268 | * @throws {Error} If the payload is not a reader or valid buffer 269 | * @throws {$protobuf.util.ProtocolError} If required fields are missing 270 | */ 271 | public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): Test; 272 | 273 | /** 274 | * Creates a Test message from a plain object. Also converts values to their respective internal types. 275 | * @param d Plain object 276 | * @returns Test 277 | */ 278 | public static fromObject(d: { [k: string]: any }): Test; 279 | 280 | /** 281 | * Creates a plain object from a Test message. Also converts values to other types if specified. 282 | * @param m Test 283 | * @param [o] Conversion options 284 | * @returns Plain object 285 | */ 286 | public static toObject(m: Test, o?: $protobuf.IConversionOptions): { [k: string]: any }; 287 | 288 | /** 289 | * Converts this Test to JSON. 290 | * @returns JSON object 291 | */ 292 | public toJSON(): { [k: string]: any }; 293 | } 294 | -------------------------------------------------------------------------------- /packages/protons-benchmark/src/implementations/protons/bench.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/export */ 2 | /* eslint-disable complexity */ 3 | /* eslint-disable @typescript-eslint/no-namespace */ 4 | /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */ 5 | 6 | import { encodeMessage, decodeMessage, message, enumeration } from 'protons-runtime' 7 | import type { Codec } from 'protons-runtime' 8 | import type { Uint8ArrayList } from 'uint8arraylist' 9 | 10 | export interface Foo { 11 | baz?: number 12 | } 13 | 14 | export namespace Foo { 15 | let _codec: Codec 16 | 17 | export const codec = (): Codec => { 18 | if (_codec == null) { 19 | _codec = message((obj, w, opts = {}) => { 20 | if (opts.lengthDelimited !== false) { 21 | w.fork() 22 | } 23 | 24 | if (obj.baz != null) { 25 | w.uint32(8) 26 | w.uint32(obj.baz) 27 | } 28 | 29 | if (opts.lengthDelimited !== false) { 30 | w.ldelim() 31 | } 32 | }, (reader, length) => { 33 | const obj: any = {} 34 | 35 | const end = length == null ? reader.len : reader.pos + length 36 | 37 | while (reader.pos < end) { 38 | const tag = reader.uint32() 39 | 40 | switch (tag >>> 3) { 41 | case 1: 42 | obj.baz = reader.uint32() 43 | break 44 | default: 45 | reader.skipType(tag & 7) 46 | break 47 | } 48 | } 49 | 50 | return obj 51 | }) 52 | } 53 | 54 | return _codec 55 | } 56 | 57 | export const encode = (obj: Foo): Uint8Array => { 58 | return encodeMessage(obj, Foo.codec()) 59 | } 60 | 61 | export const decode = (buf: Uint8Array | Uint8ArrayList): Foo => { 62 | return decodeMessage(buf, Foo.codec()) 63 | } 64 | } 65 | 66 | export interface Bar { 67 | tmp?: Foo 68 | } 69 | 70 | export namespace Bar { 71 | let _codec: Codec 72 | 73 | export const codec = (): Codec => { 74 | if (_codec == null) { 75 | _codec = message((obj, w, opts = {}) => { 76 | if (opts.lengthDelimited !== false) { 77 | w.fork() 78 | } 79 | 80 | if (obj.tmp != null) { 81 | w.uint32(10) 82 | Foo.codec().encode(obj.tmp, w, { 83 | writeDefaults: false 84 | }) 85 | } 86 | 87 | if (opts.lengthDelimited !== false) { 88 | w.ldelim() 89 | } 90 | }, (reader, length) => { 91 | const obj: any = {} 92 | 93 | const end = length == null ? reader.len : reader.pos + length 94 | 95 | while (reader.pos < end) { 96 | const tag = reader.uint32() 97 | 98 | switch (tag >>> 3) { 99 | case 1: 100 | obj.tmp = Foo.codec().decode(reader, reader.uint32()) 101 | break 102 | default: 103 | reader.skipType(tag & 7) 104 | break 105 | } 106 | } 107 | 108 | return obj 109 | }) 110 | } 111 | 112 | return _codec 113 | } 114 | 115 | export const encode = (obj: Bar): Uint8Array => { 116 | return encodeMessage(obj, Bar.codec()) 117 | } 118 | 119 | export const decode = (buf: Uint8Array | Uint8ArrayList): Bar => { 120 | return decodeMessage(buf, Bar.codec()) 121 | } 122 | } 123 | 124 | export enum FOO { 125 | NONE = 'NONE', 126 | LOL = 'LOL', 127 | ABE = 'ABE' 128 | } 129 | 130 | enum __FOOValues { 131 | NONE = 0, 132 | LOL = 1, 133 | ABE = 3 134 | } 135 | 136 | export namespace FOO { 137 | export const codec = (): Codec => { 138 | return enumeration(__FOOValues) 139 | } 140 | } 141 | export interface Yo { 142 | lol: FOO[] 143 | } 144 | 145 | export namespace Yo { 146 | let _codec: Codec 147 | 148 | export const codec = (): Codec => { 149 | if (_codec == null) { 150 | _codec = message((obj, w, opts = {}) => { 151 | if (opts.lengthDelimited !== false) { 152 | w.fork() 153 | } 154 | 155 | if (obj.lol != null) { 156 | for (const value of obj.lol) { 157 | w.uint32(8) 158 | FOO.codec().encode(value, w) 159 | } 160 | } 161 | 162 | if (opts.lengthDelimited !== false) { 163 | w.ldelim() 164 | } 165 | }, (reader, length) => { 166 | const obj: any = { 167 | lol: [] 168 | } 169 | 170 | const end = length == null ? reader.len : reader.pos + length 171 | 172 | while (reader.pos < end) { 173 | const tag = reader.uint32() 174 | 175 | switch (tag >>> 3) { 176 | case 1: 177 | obj.lol.push(FOO.codec().decode(reader)) 178 | break 179 | default: 180 | reader.skipType(tag & 7) 181 | break 182 | } 183 | } 184 | 185 | return obj 186 | }) 187 | } 188 | 189 | return _codec 190 | } 191 | 192 | export const encode = (obj: Yo): Uint8Array => { 193 | return encodeMessage(obj, Yo.codec()) 194 | } 195 | 196 | export const decode = (buf: Uint8Array | Uint8ArrayList): Yo => { 197 | return decodeMessage(buf, Yo.codec()) 198 | } 199 | } 200 | 201 | export interface Lol { 202 | lol?: string 203 | b?: Bar 204 | } 205 | 206 | export namespace Lol { 207 | let _codec: Codec 208 | 209 | export const codec = (): Codec => { 210 | if (_codec == null) { 211 | _codec = message((obj, w, opts = {}) => { 212 | if (opts.lengthDelimited !== false) { 213 | w.fork() 214 | } 215 | 216 | if (obj.lol != null) { 217 | w.uint32(10) 218 | w.string(obj.lol) 219 | } 220 | 221 | if (obj.b != null) { 222 | w.uint32(18) 223 | Bar.codec().encode(obj.b, w, { 224 | writeDefaults: false 225 | }) 226 | } 227 | 228 | if (opts.lengthDelimited !== false) { 229 | w.ldelim() 230 | } 231 | }, (reader, length) => { 232 | const obj: any = {} 233 | 234 | const end = length == null ? reader.len : reader.pos + length 235 | 236 | while (reader.pos < end) { 237 | const tag = reader.uint32() 238 | 239 | switch (tag >>> 3) { 240 | case 1: 241 | obj.lol = reader.string() 242 | break 243 | case 2: 244 | obj.b = Bar.codec().decode(reader, reader.uint32()) 245 | break 246 | default: 247 | reader.skipType(tag & 7) 248 | break 249 | } 250 | } 251 | 252 | return obj 253 | }) 254 | } 255 | 256 | return _codec 257 | } 258 | 259 | export const encode = (obj: Lol): Uint8Array => { 260 | return encodeMessage(obj, Lol.codec()) 261 | } 262 | 263 | export const decode = (buf: Uint8Array | Uint8ArrayList): Lol => { 264 | return decodeMessage(buf, Lol.codec()) 265 | } 266 | } 267 | 268 | export interface Test { 269 | meh?: Lol 270 | hello?: number 271 | foo?: string 272 | payload?: Uint8Array 273 | } 274 | 275 | export namespace Test { 276 | let _codec: Codec 277 | 278 | export const codec = (): Codec => { 279 | if (_codec == null) { 280 | _codec = message((obj, w, opts = {}) => { 281 | if (opts.lengthDelimited !== false) { 282 | w.fork() 283 | } 284 | 285 | if (obj.meh != null) { 286 | w.uint32(50) 287 | Lol.codec().encode(obj.meh, w, { 288 | writeDefaults: false 289 | }) 290 | } 291 | 292 | if (obj.hello != null) { 293 | w.uint32(24) 294 | w.uint32(obj.hello) 295 | } 296 | 297 | if (obj.foo != null) { 298 | w.uint32(10) 299 | w.string(obj.foo) 300 | } 301 | 302 | if (obj.payload != null) { 303 | w.uint32(58) 304 | w.bytes(obj.payload) 305 | } 306 | 307 | if (opts.lengthDelimited !== false) { 308 | w.ldelim() 309 | } 310 | }, (reader, length) => { 311 | const obj: any = {} 312 | 313 | const end = length == null ? reader.len : reader.pos + length 314 | 315 | while (reader.pos < end) { 316 | const tag = reader.uint32() 317 | 318 | switch (tag >>> 3) { 319 | case 6: 320 | obj.meh = Lol.codec().decode(reader, reader.uint32()) 321 | break 322 | case 3: 323 | obj.hello = reader.uint32() 324 | break 325 | case 1: 326 | obj.foo = reader.string() 327 | break 328 | case 7: 329 | obj.payload = reader.bytes() 330 | break 331 | default: 332 | reader.skipType(tag & 7) 333 | break 334 | } 335 | } 336 | 337 | return obj 338 | }) 339 | } 340 | 341 | return _codec 342 | } 343 | 344 | export const encode = (obj: Test): Uint8Array => { 345 | return encodeMessage(obj, Test.codec()) 346 | } 347 | 348 | export const decode = (buf: Uint8Array | Uint8ArrayList): Test => { 349 | return decodeMessage(buf, Test.codec()) 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /packages/protons-benchmark/src/numbers/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | /* 4 | $ node dist/src/numbers/index.js 5 | $ npx playwright-test dist/src/numbers/index.js --runner benchmark 6 | */ 7 | 8 | import { readBenchmark } from './read.js' 9 | import { writeBenchmark } from './write.js' 10 | 11 | console.info('-- read --') 12 | await readBenchmark() 13 | 14 | console.info('-- write --') 15 | await writeBenchmark() 16 | -------------------------------------------------------------------------------- /packages/protons-benchmark/src/numbers/read.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import Benchmark from 'benchmark' 4 | import { writer, reader } from 'protons-runtime' 5 | 6 | const bigint = 100n 7 | 8 | const w = writer() 9 | w.uint64(bigint) 10 | 11 | const buf = w.finish() 12 | 13 | export async function readBenchmark (): Promise { 14 | return new Promise((resolve) => { 15 | new Benchmark.Suite() 16 | .add('uint64 (BigInt)', () => { 17 | const r = reader(buf) 18 | r.uint64() 19 | }) 20 | .add('uint64number', () => { 21 | const r = reader(buf) 22 | r.uint64Number() 23 | }) 24 | .add('uint64string', () => { 25 | const r = reader(buf) 26 | r.uint64String() 27 | }) 28 | .on('error', (err: Error) => { 29 | console.error(err) 30 | }) 31 | .on('cycle', (event: any) => { 32 | console.info(String(event.target)) 33 | }) 34 | .on('complete', function () { 35 | // @ts-expect-error types are wrong 36 | console.info(`Fastest is ${this.filter('fastest').map('name')}`) 37 | resolve() 38 | }) 39 | // run async 40 | .run({ async: true }) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /packages/protons-benchmark/src/numbers/write.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import Benchmark from 'benchmark' 4 | import { writer } from 'protons-runtime' 5 | 6 | const number = 100 7 | const bigint = 100n 8 | const string = '100' 9 | 10 | export async function writeBenchmark (): Promise { 11 | return new Promise((resolve) => { 12 | new Benchmark.Suite() 13 | .add('uint64 (BigInt)', () => { 14 | const w = writer() 15 | w.uint64(bigint) 16 | }) 17 | .add('uint64number', () => { 18 | const w = writer() 19 | w.uint64Number(number) 20 | }) 21 | .add('uint64string', () => { 22 | const w = writer() 23 | w.uint64String(string) 24 | }) 25 | .on('error', (err: Error) => { 26 | console.error(err) 27 | }) 28 | .on('cycle', (event: any) => { 29 | console.info(String(event.target)) 30 | }) 31 | .on('complete', function () { 32 | // @ts-expect-error types are wrong 33 | console.info(`Fastest is ${this.filter('fastest').map('name')}`) 34 | resolve() 35 | }) 36 | // run async 37 | .run({ async: true }) 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /packages/protons-benchmark/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "aegir/src/config/tsconfig.aegir.json", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "include": [ 7 | "bin", 8 | "src", 9 | "test" 10 | ], 11 | "exclude": [ 12 | "src/implementations/protobufjs/bench.js", 13 | "src/implementations/protobufjs/rpc.js" 14 | ], 15 | "references": [ 16 | { 17 | "path": "../protons" 18 | }, 19 | { 20 | "path": "../protons-runtime" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /packages/protons-benchmark/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": [ 3 | "./src/index.ts" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/protons-runtime/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## protons-runtime [5.4.0](https://github.com/ipfs/protons/compare/protons-runtime-5.3.0...protons-runtime-5.4.0) (2024-02-02) 2 | 3 | 4 | ### Features 5 | 6 | * allow limiting nested repeating fields ([#129](https://github.com/ipfs/protons/issues/129)) ([a81f997](https://github.com/ipfs/protons/commit/a81f997a490cdcc7c59d00fe5e8b666e54230745)) 7 | 8 | ## protons-runtime [5.3.0](https://github.com/ipfs/protons/compare/protons-runtime-v5.2.2...protons-runtime-5.3.0) (2024-01-31) 9 | 10 | 11 | ### Features 12 | 13 | * runtime size limits for arrays and maps ([#128](https://github.com/ipfs/protons/issues/128)) ([a737d05](https://github.com/ipfs/protons/commit/a737d05d8cd8b22568ff489d07c9e4c824cb4f40)) 14 | 15 | 16 | ### Bug Fixes 17 | 18 | * improve uint64 perf ([#122](https://github.com/ipfs/protons/issues/122)) ([3234bb6](https://github.com/ipfs/protons/commit/3234bb61eac82ebbc40925df084793efbb0f0186)) 19 | 20 | 21 | ### Trivial Changes 22 | 23 | * update publish config ([942e050](https://github.com/ipfs/protons/commit/942e050bc0c14d3c8177da218f178d8dab1c0333)) 24 | 25 | ## [protons-runtime-v5.2.2](https://github.com/ipfs/protons/compare/protons-runtime-v5.2.1...protons-runtime-v5.2.2) (2024-01-08) 26 | 27 | 28 | ### Dependencies 29 | 30 | * bump aegir from 41.3.5 to 42.0.1 ([#127](https://github.com/ipfs/protons/issues/127)) ([02eafe9](https://github.com/ipfs/protons/commit/02eafe9abc565d7719ed54b64c6ae17a55ebf235)) 31 | 32 | ## [protons-runtime-v5.2.1](https://github.com/ipfs/protons/compare/protons-runtime-v5.2.0...protons-runtime-v5.2.1) (2023-12-30) 33 | 34 | 35 | ### Dependencies 36 | 37 | * bump uint8arrays from 4.0.10 to 5.0.1 ([#126](https://github.com/ipfs/protons/issues/126)) ([1e5a0b0](https://github.com/ipfs/protons/commit/1e5a0b0608b4395283c7571353918bacc3a3bd63)) 38 | 39 | ## [protons-runtime-v5.2.0](https://github.com/ipfs/protons/compare/protons-runtime-v5.1.0...protons-runtime-v5.2.0) (2023-11-01) 40 | 41 | 42 | ### Features 43 | 44 | * add custom protons options for limiting list/map sizes ([#120](https://github.com/ipfs/protons/issues/120)) ([a5ba36b](https://github.com/ipfs/protons/commit/a5ba36bbfbfb1d2bded026e0da1251e02defacdd)), closes [#113](https://github.com/ipfs/protons/issues/113) 45 | 46 | ## [protons-runtime-v5.1.0](https://github.com/ipfs/protons/compare/protons-runtime-v5.0.5...protons-runtime-v5.1.0) (2023-10-23) 47 | 48 | 49 | ### Features 50 | 51 | * support jstype custom options ([#117](https://github.com/ipfs/protons/issues/117)) ([ba35475](https://github.com/ipfs/protons/commit/ba354756bbec60055bbeb4a6ee5bc3ab73267312)), closes [#112](https://github.com/ipfs/protons/issues/112) 52 | 53 | ## [protons-runtime-v5.0.5](https://github.com/ipfs/protons/compare/protons-runtime-v5.0.4...protons-runtime-v5.0.5) (2023-10-13) 54 | 55 | 56 | ### Bug Fixes 57 | 58 | * write string into output buffer as uint8array ([#118](https://github.com/ipfs/protons/issues/118)) ([03ab706](https://github.com/ipfs/protons/commit/03ab706e03cdc16ff897e4ab54d87f343a8d61db)) 59 | 60 | 61 | ### Trivial Changes 62 | 63 | * update project config ([c54b7ac](https://github.com/ipfs/protons/commit/c54b7acbc9ac3839d1ef2d2653f1d19b5a0fdbf4)) 64 | 65 | ## [protons-runtime-v5.0.4](https://github.com/ipfs/protons/compare/protons-runtime-v5.0.3...protons-runtime-v5.0.4) (2023-10-13) 66 | 67 | 68 | ### Bug Fixes 69 | 70 | * port protobuf reader/writer to ts ([#60](https://github.com/ipfs/protons/issues/60)) ([d101804](https://github.com/ipfs/protons/commit/d101804674e6ba42c28505fc8fdf605020ac319e)) 71 | 72 | ## [protons-runtime-v5.0.3](https://github.com/ipfs/protons/compare/protons-runtime-v5.0.2...protons-runtime-v5.0.3) (2023-10-12) 73 | 74 | 75 | ### Dependencies 76 | 77 | * bump aegir from 40.0.13 to 41.0.4 ([#116](https://github.com/ipfs/protons/issues/116)) ([b95e988](https://github.com/ipfs/protons/commit/b95e9881a5c842b3c70a40d6d93b4aa5219b8aee)) 78 | 79 | ## [protons-runtime-v5.0.2](https://github.com/ipfs/protons/compare/protons-runtime-v5.0.1...protons-runtime-v5.0.2) (2023-08-05) 80 | 81 | 82 | ### Dependencies 83 | 84 | * bump aegir from 39.0.13 to 40.0.8 ([#108](https://github.com/ipfs/protons/issues/108)) ([8b54c80](https://github.com/ipfs/protons/commit/8b54c8097683b055736a8e431728422cedf82697)) 85 | 86 | ## [protons-runtime-v5.0.1](https://github.com/ipfs/protons/compare/protons-runtime-v5.0.0...protons-runtime-v5.0.1) (2023-06-30) 87 | 88 | 89 | ### Dependencies 90 | 91 | * bump aegir from 38.1.8 to 39.0.13 ([#104](https://github.com/ipfs/protons/issues/104)) ([912e0e6](https://github.com/ipfs/protons/commit/912e0e627fbe8047b56cdcd5d26cb81bf5700bf8)) 92 | 93 | ## [protons-runtime-v5.0.0](https://github.com/ipfs/protons/compare/protons-runtime-v4.0.2...protons-runtime-v5.0.0) (2023-02-02) 94 | 95 | 96 | ### ⚠ BREAKING CHANGES 97 | 98 | * singular fields should be optional to write (#83) 99 | 100 | ### Bug Fixes 101 | 102 | * singular fields should be optional to write ([#83](https://github.com/ipfs/protons/issues/83)) ([229afbc](https://github.com/ipfs/protons/commit/229afbcb38ba0edc0622d4c2e97847462b439dc8)), closes [#42](https://github.com/ipfs/protons/issues/42) 103 | * sort imports ([#84](https://github.com/ipfs/protons/issues/84)) ([6f796f1](https://github.com/ipfs/protons/commit/6f796f1e7dfd631314f9a1df534eabd96dd1528c)) 104 | 105 | ## [protons-runtime-v4.0.2](https://github.com/ipfs/protons/compare/protons-runtime-v4.0.1...protons-runtime-v4.0.2) (2023-01-12) 106 | 107 | 108 | ### Dependencies 109 | 110 | * remove lerna, update aegir ([#76](https://github.com/ipfs/protons/issues/76)) ([83a24f2](https://github.com/ipfs/protons/commit/83a24f2a924704bd4a356b6f8a3195245d8b1062)) 111 | 112 | ## [protons-runtime-v4.0.1](https://github.com/ipfs/protons/compare/protons-runtime-v4.0.0...protons-runtime-v4.0.1) (2022-10-12) 113 | 114 | 115 | ### Bug Fixes 116 | 117 | * only import reader/writer to decrease bundle size ([#69](https://github.com/ipfs/protons/issues/69)) ([8eea129](https://github.com/ipfs/protons/commit/8eea129d1b4fe5914830b29b79c4af6348eddf73)) 118 | 119 | ## [protons-runtime-v4.0.0](https://github.com/ipfs/protons/compare/protons-runtime-v3.1.0...protons-runtime-v4.0.0) (2022-10-12) 120 | 121 | 122 | ### ⚠ BREAKING CHANGES 123 | 124 | * ts definitions will need to be generated from `.proto` files - singular message fields have become optional as message fields are always optional in proto3 125 | 126 | ### Bug Fixes 127 | 128 | * adhere more closely to the language guide for proto3 default values ([#66](https://github.com/ipfs/protons/issues/66)) ([406d775](https://github.com/ipfs/protons/commit/406d7757d490eb0dbac93343d6622dd689ff0707)), closes [#43](https://github.com/ipfs/protons/issues/43) 129 | 130 | ## [protons-runtime-v3.1.0](https://github.com/ipfs/protons/compare/protons-runtime-v3.0.1...protons-runtime-v3.1.0) (2022-08-11) 131 | 132 | 133 | ### Features 134 | 135 | * define default types during decode ([#62](https://github.com/ipfs/protons/issues/62)) ([6453809](https://github.com/ipfs/protons/commit/64538091f7339f285ab6efbb0e18054970e00f33)), closes [#43](https://github.com/ipfs/protons/issues/43) 136 | 137 | ## [protons-runtime-v3.0.1](https://github.com/ipfs/protons/compare/protons-runtime-v3.0.0...protons-runtime-v3.0.1) (2022-08-10) 138 | 139 | 140 | ### Bug Fixes 141 | 142 | * add uint8arraylist peer dep ([#61](https://github.com/ipfs/protons/issues/61)) ([eb16e86](https://github.com/ipfs/protons/commit/eb16e8690f28435c198d5f0facf5514f2d6574a3)), closes [#59](https://github.com/ipfs/protons/issues/59) 143 | 144 | ## [protons-runtime-v3.0.0](https://github.com/ipfs/protons/compare/protons-runtime-v2.0.2...protons-runtime-v3.0.0) (2022-08-10) 145 | 146 | 147 | ### ⚠ BREAKING CHANGES 148 | 149 | * the exported types of `protons-runtime` have changed and protobuf encoders/decoders will need to be regenerated 150 | 151 | ### Bug Fixes 152 | 153 | * increase encoding/decoding performance ([#58](https://github.com/ipfs/protons/issues/58)) ([9987b97](https://github.com/ipfs/protons/commit/9987b97cc6910dd67152c3a9c0941ae0ab0a8b9a)) 154 | 155 | ## [protons-runtime-v2.0.2](https://github.com/ipfs/protons/compare/protons-runtime-v2.0.1...protons-runtime-v2.0.2) (2022-07-30) 156 | 157 | 158 | ### Bug Fixes 159 | 160 | * use uint8-varint, byte-accesor and longbits modules ([#56](https://github.com/ipfs/protons/issues/56)) ([66d72f5](https://github.com/ipfs/protons/commit/66d72f50ca3733b97efa5155d3cdcb33ec531d4a)) 161 | 162 | ## [protons-runtime-v2.0.1](https://github.com/ipfs/protons/compare/protons-runtime-v2.0.0...protons-runtime-v2.0.1) (2022-07-28) 163 | 164 | 165 | ### Bug Fixes 166 | 167 | * update project config ([3199131](https://github.com/ipfs/protons/commit/3199131f1f199bcb57fcf1e7aba4ca0b6d9207db)) 168 | 169 | ## [protons-runtime-v2.0.0](https://github.com/ipfs/protons/compare/protons-runtime-v1.0.4...protons-runtime-v2.0.0) (2022-07-28) 170 | 171 | 172 | ### ⚠ BREAKING CHANGES 173 | 174 | * Uses Uint8ArrayList v2 175 | 176 | ### Features 177 | 178 | * support no-copy serialization ([#54](https://github.com/ipfs/protons/issues/54)) ([caa0d71](https://github.com/ipfs/protons/commit/caa0d71b60367f2f3551688ad09fd695840e0852)) 179 | 180 | ## [protons-runtime-v1.0.4](https://github.com/ipfs/protons/compare/protons-runtime-v1.0.3...protons-runtime-v1.0.4) (2022-05-10) 181 | 182 | 183 | ### Bug Fixes 184 | 185 | * encode enum values ([#30](https://github.com/ipfs/protons/issues/30)) ([676c01d](https://github.com/ipfs/protons/commit/676c01dae7ff5b4d3985113573079ba605d83ef6)) 186 | 187 | ## [protons-runtime-v1.0.3](https://github.com/ipfs/protons/compare/protons-runtime-v1.0.2...protons-runtime-v1.0.3) (2022-04-10) 188 | 189 | 190 | ### Bug Fixes 191 | 192 | * remove redundant defs and declare codec return type ([#28](https://github.com/ipfs/protons/issues/28)) ([c3ea5ec](https://github.com/ipfs/protons/commit/c3ea5ec9101e37d8ac0437eb22ac0b4eeeb14eb7)) 193 | 194 | ## [protons-runtime-v1.0.2](https://github.com/ipfs/protons/compare/protons-runtime-v1.0.1...protons-runtime-v1.0.2) (2022-04-08) 195 | 196 | 197 | ### Trivial Changes 198 | 199 | * update readme ([#27](https://github.com/ipfs/protons/issues/27)) ([0ccb1a3](https://github.com/ipfs/protons/commit/0ccb1a36766e620eed2fb65973ff2d6c7854caf9)) 200 | 201 | ## [protons-runtime-v1.0.1](https://github.com/ipfs/protons/compare/protons-runtime-v1.0.0...protons-runtime-v1.0.1) (2022-04-08) 202 | 203 | 204 | ### Bug Fixes 205 | 206 | * update aegir, make codec creation dynamic ([#26](https://github.com/ipfs/protons/issues/26)) ([ecc46cc](https://github.com/ipfs/protons/commit/ecc46ccad90696c4d5cda6b2cb1db723770577d0)) 207 | 208 | ## protons-runtime-v1.0.0 (2022-03-31) 209 | 210 | 211 | ### ⚠ BREAKING CHANGES 212 | 213 | * This module is now ESM only 214 | 215 | ### Features 216 | 217 | * transpile to ts ([#17](https://github.com/ipfs/protons/issues/17)) ([74d3b7a](https://github.com/ipfs/protons/commit/74d3b7abf1e857f7320c100734e797855ea728c1)) 218 | -------------------------------------------------------------------------------- /packages/protons-runtime/LICENSE: -------------------------------------------------------------------------------- 1 | This project is dual licensed under MIT and Apache-2.0. 2 | 3 | MIT: https://www.opensource.org/licenses/mit 4 | Apache-2.0: https://www.apache.org/licenses/license-2.0 5 | -------------------------------------------------------------------------------- /packages/protons-runtime/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 2 | 3 | http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 6 | -------------------------------------------------------------------------------- /packages/protons-runtime/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/protons-runtime/README.md: -------------------------------------------------------------------------------- 1 | # protons-runtime 2 | 3 | [![ipfs.tech](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.tech) 4 | [![Discuss](https://img.shields.io/discourse/https/discuss.ipfs.tech/posts.svg?style=flat-square)](https://discuss.ipfs.tech) 5 | [![codecov](https://img.shields.io/codecov/c/github/ipfs/protons.svg?style=flat-square)](https://codecov.io/gh/ipfs/protons) 6 | [![CI](https://img.shields.io/github/actions/workflow/status/ipfs/protons/js-test-and-release.yml?branch=main\&style=flat-square)](https://github.com/ipfs/protons/actions/workflows/js-test-and-release.yml?query=branch%3Amain) 7 | 8 | > Shared code to make your bundle smaller when running protons in your app 9 | 10 | # About 11 | 12 | 26 | 27 | This module contains serialization/deserialization code used when encoding/decoding protobufs. 28 | 29 | It should be declared as a dependency of your project: 30 | 31 | ```console 32 | npm i protons-runtime 33 | ``` 34 | 35 | # Install 36 | 37 | ```console 38 | $ npm i protons-runtime 39 | ``` 40 | 41 | Contains shared code to reduce code duplication between modules transpiled by protons. 42 | 43 | # API Docs 44 | 45 | - 46 | 47 | # License 48 | 49 | Licensed under either of 50 | 51 | - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) 52 | - MIT ([LICENSE-MIT](LICENSE-MIT) / ) 53 | 54 | # Contribute 55 | 56 | Contributions welcome! Please check out [the issues](https://github.com/ipfs/protons/issues). 57 | 58 | Also see our [contributing document](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md) for more information on how we work, and about contributing in general. 59 | 60 | Please be aware that all interactions related to this repo are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). 61 | 62 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 63 | 64 | [![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) 65 | -------------------------------------------------------------------------------- /packages/protons-runtime/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "protons-runtime", 3 | "version": "5.4.0", 4 | "description": "Shared code to make your bundle smaller when running protons in your app", 5 | "license": "Apache-2.0 OR MIT", 6 | "homepage": "https://github.com/ipfs/protons/tree/master/packages/protons-runtime#readme", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/ipfs/protons.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/ipfs/protons/issues" 13 | }, 14 | "publishConfig": { 15 | "access": "public", 16 | "provenance": true 17 | }, 18 | "type": "module", 19 | "types": "./dist/src/index.d.ts", 20 | "files": [ 21 | "protons.proto", 22 | "src", 23 | "dist", 24 | "!dist/test", 25 | "!**/*.tsbuildinfo" 26 | ], 27 | "exports": { 28 | ".": { 29 | "types": "./dist/src/index.d.ts", 30 | "import": "./dist/src/index.js" 31 | } 32 | }, 33 | "eslintConfig": { 34 | "extends": "ipfs", 35 | "parserOptions": { 36 | "project": true, 37 | "sourceType": "module" 38 | } 39 | }, 40 | "scripts": { 41 | "clean": "aegir clean", 42 | "lint": "aegir lint", 43 | "dep-check": "aegir dep-check", 44 | "doc-check": "aegir doc-check", 45 | "build": "aegir build", 46 | "release": "aegir release" 47 | }, 48 | "dependencies": { 49 | "uint8-varint": "^2.0.2", 50 | "uint8arraylist": "^2.4.3", 51 | "uint8arrays": "^5.0.1" 52 | }, 53 | "devDependencies": { 54 | "aegir": "^47.0.5" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/protons-runtime/protons.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "google/protobuf/descriptor.proto"; 4 | 5 | package protons; 6 | 7 | message ProtonsOptions { 8 | // limit the number of repeated fields or map entries that will be decoded 9 | optional int32 limit = 1; 10 | } 11 | 12 | // custom options available for use by protons 13 | extend google.protobuf.FieldOptions { 14 | optional ProtonsOptions options = 1186; 15 | } 16 | -------------------------------------------------------------------------------- /packages/protons-runtime/src/codec.ts: -------------------------------------------------------------------------------- 1 | import type { Writer, Reader } from './index.js' 2 | 3 | // https://developers.google.com/protocol-buffers/docs/encoding#structure 4 | export enum CODEC_TYPES { 5 | VARINT = 0, 6 | BIT64, 7 | LENGTH_DELIMITED, 8 | START_GROUP, 9 | END_GROUP, 10 | BIT32 11 | } 12 | 13 | export interface EncodeOptions { 14 | lengthDelimited?: boolean 15 | writeDefaults?: boolean 16 | } 17 | 18 | export interface EncodeFunction { 19 | (value: Partial, writer: Writer, opts?: EncodeOptions): void 20 | } 21 | 22 | // protobuf types that contain multiple values 23 | type CollectionTypes = any[] | Map 24 | 25 | // protobuf types that are not collections or messages 26 | type PrimitiveTypes = boolean | number | string | bigint | Uint8Array 27 | 28 | // recursive array/map field length limits 29 | type CollectionLimits = { 30 | [K in keyof T]: T[K] extends CollectionTypes ? number : 31 | T[K] extends PrimitiveTypes ? never : Limits 32 | } 33 | 34 | // recursive array member array/map field length limits 35 | type ArrayElementLimits = { 36 | [K in keyof T as `${string & K}$`]: T[K] extends Array ? 37 | (ElementType extends PrimitiveTypes ? never : Limits) : 38 | (T[K] extends PrimitiveTypes ? never : Limits) 39 | } 40 | 41 | // recursive map value array/map field length limits 42 | type MapValueLimits = { 43 | [K in keyof T as `${string & K}$value`]: T[K] extends Map ? 44 | (MapValueType extends PrimitiveTypes ? never : Limits) : 45 | (T[K] extends PrimitiveTypes ? never : Limits) 46 | } 47 | 48 | // union of collection and array elements 49 | type Limits = Partial & ArrayElementLimits & MapValueLimits> 50 | 51 | export interface DecodeOptions { 52 | /** 53 | * Runtime-specified limits for lengths of repeated/map fields 54 | */ 55 | limits?: Limits 56 | } 57 | 58 | export interface DecodeFunction { 59 | (reader: Reader, length?: number, opts?: DecodeOptions): T 60 | } 61 | 62 | export interface Codec { 63 | name: string 64 | type: CODEC_TYPES 65 | encode: EncodeFunction 66 | decode: DecodeFunction 67 | } 68 | 69 | export function createCodec (name: string, type: CODEC_TYPES, encode: EncodeFunction, decode: DecodeFunction): Codec { 70 | return { 71 | name, 72 | type, 73 | encode, 74 | decode 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /packages/protons-runtime/src/codecs/enum.ts: -------------------------------------------------------------------------------- 1 | import { createCodec, CODEC_TYPES } from '../codec.js' 2 | import type { DecodeFunction, EncodeFunction, Codec } from '../codec.js' 3 | 4 | export function enumeration (v: any): Codec { 5 | function findValue (val: string | number): number { 6 | // Use the reverse mapping to look up the enum key for the stored value 7 | // https://www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings 8 | if (v[val.toString()] == null) { 9 | throw new Error('Invalid enum value') 10 | } 11 | 12 | return v[val] 13 | } 14 | 15 | const encode: EncodeFunction = function enumEncode (val, writer) { 16 | const enumValue = findValue(val) 17 | 18 | writer.int32(enumValue) 19 | } 20 | 21 | const decode: DecodeFunction = function enumDecode (reader) { 22 | const val = reader.int32() 23 | 24 | return findValue(val) 25 | } 26 | 27 | // @ts-expect-error yeah yeah 28 | return createCodec('enum', CODEC_TYPES.VARINT, encode, decode) 29 | } 30 | -------------------------------------------------------------------------------- /packages/protons-runtime/src/codecs/message.ts: -------------------------------------------------------------------------------- 1 | import { createCodec, CODEC_TYPES } from '../codec.js' 2 | import type { EncodeFunction, DecodeFunction, Codec } from '../codec.js' 3 | 4 | export interface Factory { 5 | new (obj: A): T 6 | } 7 | 8 | export function message (encode: EncodeFunction, decode: DecodeFunction): Codec { 9 | return createCodec('message', CODEC_TYPES.LENGTH_DELIMITED, encode, decode) 10 | } 11 | -------------------------------------------------------------------------------- /packages/protons-runtime/src/decode.ts: -------------------------------------------------------------------------------- 1 | import { createReader } from './utils/reader.js' 2 | import type { Codec, DecodeOptions } from './codec.js' 3 | import type { Uint8ArrayList } from 'uint8arraylist' 4 | 5 | export function decodeMessage (buf: Uint8Array | Uint8ArrayList, codec: Pick, 'decode'>, opts?: DecodeOptions): T { 6 | const reader = createReader(buf) 7 | 8 | return codec.decode(reader, undefined, opts) 9 | } 10 | -------------------------------------------------------------------------------- /packages/protons-runtime/src/encode.ts: -------------------------------------------------------------------------------- 1 | import { createWriter } from './utils/writer.js' 2 | import type { Codec } from './codec.js' 3 | 4 | export function encodeMessage (message: Partial, codec: Pick, 'encode'>): Uint8Array { 5 | const w = createWriter() 6 | 7 | codec.encode(message, w, { 8 | lengthDelimited: false 9 | }) 10 | 11 | return w.finish() 12 | } 13 | -------------------------------------------------------------------------------- /packages/protons-runtime/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @packageDocumentation 3 | * 4 | * This module contains serialization/deserialization code used when encoding/decoding protobufs. 5 | * 6 | * It should be declared as a dependency of your project: 7 | * 8 | * ```console 9 | * npm i protons-runtime 10 | * ``` 11 | */ 12 | 13 | import type { Codec } from './codec.js' 14 | 15 | export interface FieldDef { 16 | name: string 17 | codec: Codec 18 | optional?: true 19 | repeats?: true 20 | packed?: true 21 | } 22 | 23 | export { 24 | decodeMessage 25 | } from './decode.js' 26 | 27 | export { 28 | encodeMessage 29 | } from './encode.js' 30 | 31 | export { enumeration } from './codecs/enum.js' 32 | export { message } from './codecs/message.js' 33 | export { createReader as reader } from './utils/reader.js' 34 | export { createWriter as writer } from './utils/writer.js' 35 | export type { Codec, EncodeOptions, DecodeOptions } from './codec.js' 36 | 37 | export interface Writer { 38 | /** 39 | * Current length 40 | */ 41 | len: number 42 | 43 | /** 44 | * Writes an unsigned 32 bit value as a varint 45 | */ 46 | uint32(value: number): this 47 | 48 | /** 49 | * Writes a signed 32 bit value as a varint` 50 | */ 51 | int32(value: number): this 52 | 53 | /** 54 | * Writes a 32 bit value as a varint, zig-zag encoded 55 | */ 56 | sint32(value: number): this 57 | 58 | /** 59 | * Writes an unsigned 64 bit value as a varint 60 | */ 61 | uint64(value: bigint): this 62 | 63 | /** 64 | * Writes an unsigned 64 bit value as a varint 65 | */ 66 | uint64Number(value: number): this 67 | 68 | /** 69 | * Writes an unsigned 64 bit value as a varint 70 | */ 71 | uint64String(value: string): this 72 | 73 | /** 74 | * Writes a signed 64 bit value as a varint 75 | */ 76 | int64(value: bigint): this 77 | 78 | /** 79 | * Writes a signed 64 bit value as a varint 80 | */ 81 | int64Number(value: number): this 82 | 83 | /** 84 | * Writes a signed 64 bit value as a varint 85 | */ 86 | int64String(value: string): this 87 | 88 | /** 89 | * Writes a signed 64 bit value as a varint, zig-zag encoded 90 | */ 91 | sint64(value: bigint): this 92 | 93 | /** 94 | * Writes a signed 64 bit value as a varint, zig-zag encoded 95 | */ 96 | sint64Number(value: number): this 97 | 98 | /** 99 | * Writes a signed 64 bit value as a varint, zig-zag encoded 100 | */ 101 | sint64String(value: string): this 102 | 103 | /** 104 | * Writes a boolish value as a varint 105 | */ 106 | bool(value: boolean): this 107 | 108 | /** 109 | * Writes an unsigned 32 bit value as fixed 32 bits 110 | */ 111 | fixed32(value: number): this 112 | 113 | /** 114 | * Writes a signed 32 bit value as fixed 32 bits 115 | */ 116 | sfixed32(value: number): this 117 | 118 | /** 119 | * Writes an unsigned 64 bit value as fixed 64 bits 120 | */ 121 | fixed64(value: bigint): this 122 | 123 | /** 124 | * Writes an unsigned 64 bit value as fixed 64 bits 125 | */ 126 | fixed64Number(value: number): this 127 | 128 | /** 129 | * Writes an unsigned 64 bit value as fixed 64 bits 130 | */ 131 | fixed64String(value: string): this 132 | 133 | /** 134 | * Writes a signed 64 bit value as fixed 64 bits 135 | */ 136 | sfixed64(value: bigint): this 137 | 138 | /** 139 | * Writes a signed 64 bit value as fixed 64 bits 140 | */ 141 | sfixed64Number(value: number): this 142 | 143 | /** 144 | * Writes a signed 64 bit value as fixed 64 bits 145 | */ 146 | sfixed64String(value: string): this 147 | 148 | /** 149 | * Writes a float (32 bit) 150 | */ 151 | float(value: number): this 152 | 153 | /** 154 | * Writes a double (64 bit float) 155 | */ 156 | double(value: number): this 157 | 158 | /** 159 | * Writes a sequence of bytes 160 | */ 161 | bytes(value: Uint8Array): this 162 | 163 | /** 164 | * Writes a string 165 | */ 166 | string(value: string): this 167 | 168 | /** 169 | * Forks this writer's state by pushing it to a stack. 170 | * Calling {@link Writer#reset|reset} or {@link Writer#ldelim|ldelim} resets the writer to the previous state. 171 | */ 172 | fork(): this 173 | 174 | /** 175 | * Resets this instance to the last state. 176 | */ 177 | reset(): this 178 | 179 | /** 180 | * Resets to the last state and appends the fork state's current write length as a varint followed by its operations. 181 | */ 182 | ldelim(): this 183 | 184 | /** 185 | * Finishes the write operation 186 | */ 187 | finish(): Uint8Array 188 | } 189 | 190 | export interface Reader { 191 | /** 192 | * Read buffer 193 | */ 194 | buf: Uint8Array 195 | 196 | /** 197 | * Read buffer position 198 | */ 199 | pos: number 200 | 201 | /** 202 | * Read buffer length 203 | */ 204 | len: number 205 | 206 | /** 207 | * Reads a varint as an unsigned 32 bit value 208 | */ 209 | uint32(): number 210 | 211 | /** 212 | * Reads a varint as a signed 32 bit value 213 | */ 214 | int32(): number 215 | 216 | /** 217 | * Reads a zig-zag encoded varint as a signed 32 bit value 218 | */ 219 | sint32(): number 220 | 221 | /** 222 | * Reads a varint as a boolean 223 | */ 224 | bool(): boolean 225 | 226 | /** 227 | * Reads fixed 32 bits as an unsigned 32 bit integer 228 | */ 229 | fixed32(): number 230 | 231 | /** 232 | * Reads fixed 32 bits as a signed 32 bit integer 233 | */ 234 | sfixed32(): number 235 | 236 | /** 237 | * Reads a float (32 bit) as a number 238 | */ 239 | float(): number 240 | 241 | /** 242 | * Reads a double (64 bit float) as a number 243 | */ 244 | double(): number 245 | 246 | /** 247 | * Reads a sequence of bytes preceded by its length as a varint 248 | */ 249 | bytes(): Uint8Array 250 | 251 | /** 252 | * Reads a string preceded by its byte length as a varint 253 | */ 254 | string(): string 255 | 256 | /** 257 | * Skips the specified number of bytes if specified, otherwise skips a varints` 258 | */ 259 | skip(length?: number): void 260 | 261 | /** 262 | * Skips the next element of the specified wire type 263 | */ 264 | skipType(wireType: number): void 265 | 266 | /** 267 | * Reads a varint as a signed 64 bit value 268 | */ 269 | int64(): bigint 270 | 271 | /** 272 | * Reads a varint as a signed 64 bit value 273 | */ 274 | int64Number(): number 275 | 276 | /** 277 | * Reads a varint as a signed 64 bit value 278 | */ 279 | int64String(): string 280 | 281 | /** 282 | * Reads a varint as an unsigned 64 bit value 283 | */ 284 | uint64(): bigint 285 | 286 | /** 287 | * Reads a varint as an unsigned 64 bit value 288 | */ 289 | uint64Number(): number 290 | 291 | /** 292 | * Reads a varint as an unsigned 64 bit value 293 | */ 294 | uint64String(): string 295 | 296 | /** 297 | * Reads a zig-zag encoded varint as a signed 64 bit value 298 | */ 299 | sint64(): bigint 300 | 301 | /** 302 | * Reads a zig-zag encoded varint as a signed 64 bit value 303 | */ 304 | sint64Number(): number 305 | 306 | /** 307 | * Reads a zig-zag encoded varint as a signed 64 bit value 308 | */ 309 | sint64String(): string 310 | 311 | /** 312 | * Reads fixed 64 bits 313 | */ 314 | fixed64(): bigint 315 | 316 | /** 317 | * Reads fixed 64 bits 318 | */ 319 | fixed64Number(): number 320 | 321 | /** 322 | * Reads fixed 64 bits 323 | */ 324 | fixed64String(): string 325 | 326 | /** 327 | * Reads zig-zag encoded fixed 64 bits 328 | */ 329 | sfixed64(): bigint 330 | 331 | /** 332 | * Reads zig-zag encoded fixed 64 bits 333 | */ 334 | sfixed64Number(): number 335 | 336 | /** 337 | * Reads zig-zag encoded fixed 64 bits 338 | */ 339 | sfixed64String(): string 340 | } 341 | 342 | /** 343 | * This will be removed in a future release 344 | * 345 | * @deprecated 346 | */ 347 | export class CodeError extends Error { 348 | public code: string 349 | 350 | constructor (message: string, code: string) { 351 | super(message) 352 | 353 | this.code = code 354 | } 355 | } 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 | } 402 | -------------------------------------------------------------------------------- /packages/protons-runtime/src/utils/float.ts: -------------------------------------------------------------------------------- 1 | const f32 = new Float32Array([-0]) 2 | const f8b = new Uint8Array(f32.buffer) 3 | 4 | /** 5 | * Writes a 32 bit float to a buffer using little endian byte order 6 | */ 7 | export function writeFloatLE (val: number, buf: Uint8Array, pos: number): void { 8 | f32[0] = val 9 | buf[pos] = f8b[0] 10 | buf[pos + 1] = f8b[1] 11 | buf[pos + 2] = f8b[2] 12 | buf[pos + 3] = f8b[3] 13 | } 14 | 15 | /** 16 | * Writes a 32 bit float to a buffer using big endian byte order 17 | */ 18 | export function writeFloatBE (val: number, buf: Uint8Array, pos: number): void { 19 | f32[0] = val 20 | buf[pos] = f8b[3] 21 | buf[pos + 1] = f8b[2] 22 | buf[pos + 2] = f8b[1] 23 | buf[pos + 3] = f8b[0] 24 | } 25 | 26 | /** 27 | * Reads a 32 bit float from a buffer using little endian byte order 28 | */ 29 | export function readFloatLE (buf: Uint8Array, pos: number): number { 30 | f8b[0] = buf[pos] 31 | f8b[1] = buf[pos + 1] 32 | f8b[2] = buf[pos + 2] 33 | f8b[3] = buf[pos + 3] 34 | return f32[0] 35 | } 36 | 37 | /** 38 | * Reads a 32 bit float from a buffer using big endian byte order 39 | */ 40 | export function readFloatBE (buf: Uint8Array, pos: number): number { 41 | f8b[3] = buf[pos] 42 | f8b[2] = buf[pos + 1] 43 | f8b[1] = buf[pos + 2] 44 | f8b[0] = buf[pos + 3] 45 | return f32[0] 46 | } 47 | 48 | const f64 = new Float64Array([-0]) 49 | const d8b = new Uint8Array(f64.buffer) 50 | 51 | /** 52 | * Writes a 64 bit double to a buffer using little endian byte order 53 | */ 54 | export function writeDoubleLE (val: number, buf: Uint8Array, pos: number): void { 55 | f64[0] = val 56 | buf[pos] = d8b[0] 57 | buf[pos + 1] = d8b[1] 58 | buf[pos + 2] = d8b[2] 59 | buf[pos + 3] = d8b[3] 60 | buf[pos + 4] = d8b[4] 61 | buf[pos + 5] = d8b[5] 62 | buf[pos + 6] = d8b[6] 63 | buf[pos + 7] = d8b[7] 64 | } 65 | 66 | /** 67 | * Writes a 64 bit double to a buffer using big endian byte order 68 | */ 69 | export function writeDoubleBE (val: number, buf: Uint8Array, pos: number): void { 70 | f64[0] = val 71 | buf[pos] = d8b[7] 72 | buf[pos + 1] = d8b[6] 73 | buf[pos + 2] = d8b[5] 74 | buf[pos + 3] = d8b[4] 75 | buf[pos + 4] = d8b[3] 76 | buf[pos + 5] = d8b[2] 77 | buf[pos + 6] = d8b[1] 78 | buf[pos + 7] = d8b[0] 79 | } 80 | 81 | /** 82 | * Reads a 64 bit double from a buffer using little endian byte order 83 | */ 84 | export function readDoubleLE (buf: Uint8Array, pos: number): number { 85 | d8b[0] = buf[pos] 86 | d8b[1] = buf[pos + 1] 87 | d8b[2] = buf[pos + 2] 88 | d8b[3] = buf[pos + 3] 89 | d8b[4] = buf[pos + 4] 90 | d8b[5] = buf[pos + 5] 91 | d8b[6] = buf[pos + 6] 92 | d8b[7] = buf[pos + 7] 93 | return f64[0] 94 | } 95 | 96 | /** 97 | * Reads a 64 bit double from a buffer using big endian byte order 98 | */ 99 | export function readDoubleBE (buf: Uint8Array, pos: number): number { 100 | d8b[7] = buf[pos] 101 | d8b[6] = buf[pos + 1] 102 | d8b[5] = buf[pos + 2] 103 | d8b[4] = buf[pos + 3] 104 | d8b[3] = buf[pos + 4] 105 | d8b[2] = buf[pos + 5] 106 | d8b[1] = buf[pos + 6] 107 | d8b[0] = buf[pos + 7] 108 | return f64[0] 109 | } 110 | -------------------------------------------------------------------------------- /packages/protons-runtime/src/utils/longbits.ts: -------------------------------------------------------------------------------- 1 | // the largest BigInt we can safely downcast to a Number 2 | const MAX_SAFE_NUMBER_INTEGER = BigInt(Number.MAX_SAFE_INTEGER) 3 | const MIN_SAFE_NUMBER_INTEGER = BigInt(Number.MIN_SAFE_INTEGER) 4 | 5 | /** 6 | * Constructs new long bits. 7 | * 8 | * @classdesc Helper class for working with the low and high bits of a 64 bit value. 9 | * @memberof util 10 | * @function Object() { [native code] } 11 | * @param {number} lo - Low 32 bits, unsigned 12 | * @param {number} hi - High 32 bits, unsigned 13 | */ 14 | export class LongBits { 15 | public lo: number 16 | public hi: number 17 | 18 | constructor (lo: number, hi: number) { 19 | // note that the casts below are theoretically unnecessary as of today, but older statically 20 | // generated converter code might still call the ctor with signed 32bits. kept for compat. 21 | 22 | /** 23 | * Low bits 24 | */ 25 | this.lo = lo | 0 26 | 27 | /** 28 | * High bits 29 | */ 30 | this.hi = hi | 0 31 | } 32 | 33 | /** 34 | * Converts this long bits to a possibly unsafe JavaScript number 35 | */ 36 | toNumber (unsigned: boolean = false): number { 37 | if (!unsigned && (this.hi >>> 31) > 0) { 38 | const lo = ~this.lo + 1 >>> 0 39 | let hi = ~this.hi >>> 0 40 | if (lo === 0) { 41 | hi = hi + 1 >>> 0 42 | } 43 | return -(lo + hi * 4294967296) 44 | } 45 | return this.lo + this.hi * 4294967296 46 | } 47 | 48 | /** 49 | * Converts this long bits to a bigint 50 | */ 51 | toBigInt (unsigned: boolean = false): bigint { 52 | if (unsigned) { 53 | return BigInt(this.lo >>> 0) + (BigInt(this.hi >>> 0) << 32n) 54 | } 55 | 56 | if ((this.hi >>> 31) !== 0) { 57 | const lo = ~this.lo + 1 >>> 0 58 | let hi = ~this.hi >>> 0 59 | if (lo === 0) { 60 | hi = hi + 1 >>> 0 61 | } 62 | return -(BigInt(lo) + (BigInt(hi) << 32n)) 63 | } 64 | 65 | return BigInt(this.lo >>> 0) + (BigInt(this.hi >>> 0) << 32n) 66 | } 67 | 68 | /** 69 | * Converts this long bits to a string 70 | */ 71 | toString (unsigned: boolean = false): string { 72 | return this.toBigInt(unsigned).toString() 73 | } 74 | 75 | /** 76 | * Zig-zag encodes this long bits 77 | */ 78 | zzEncode (): this { 79 | const mask = this.hi >> 31 80 | this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0 81 | this.lo = (this.lo << 1 ^ mask) >>> 0 82 | return this 83 | } 84 | 85 | /** 86 | * Zig-zag decodes this long bits 87 | */ 88 | zzDecode (): this { 89 | const mask = -(this.lo & 1) 90 | this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0 91 | this.hi = (this.hi >>> 1 ^ mask) >>> 0 92 | return this 93 | } 94 | 95 | /** 96 | * Calculates the length of this longbits when encoded as a varint. 97 | */ 98 | length (): number { 99 | const part0 = this.lo 100 | const part1 = (this.lo >>> 28 | this.hi << 4) >>> 0 101 | const part2 = this.hi >>> 24 102 | return part2 === 0 103 | ? part1 === 0 104 | ? part0 < 16384 105 | ? part0 < 128 ? 1 : 2 106 | : part0 < 2097152 ? 3 : 4 107 | : part1 < 16384 108 | ? part1 < 128 ? 5 : 6 109 | : part1 < 2097152 ? 7 : 8 110 | : part2 < 128 ? 9 : 10 111 | } 112 | 113 | /** 114 | * Constructs new long bits from the specified number 115 | */ 116 | static fromBigInt (value: bigint): LongBits { 117 | if (value === 0n) { 118 | return zero 119 | } 120 | 121 | if (value < MAX_SAFE_NUMBER_INTEGER && value > MIN_SAFE_NUMBER_INTEGER) { 122 | return this.fromNumber(Number(value)) 123 | } 124 | 125 | const negative = value < 0n 126 | 127 | if (negative) { 128 | value = -value 129 | } 130 | 131 | let hi = value >> 32n 132 | let lo = value - (hi << 32n) 133 | 134 | if (negative) { 135 | hi = ~hi | 0n 136 | lo = ~lo | 0n 137 | 138 | if (++lo > TWO_32) { 139 | lo = 0n 140 | if (++hi > TWO_32) { hi = 0n } 141 | } 142 | } 143 | 144 | return new LongBits(Number(lo), Number(hi)) 145 | } 146 | 147 | /** 148 | * Constructs new long bits from the specified number 149 | */ 150 | static fromNumber (value: number): LongBits { 151 | if (value === 0) { return zero } 152 | const sign = value < 0 153 | if (sign) { value = -value } 154 | let lo = value >>> 0 155 | let hi = (value - lo) / 4294967296 >>> 0 156 | if (sign) { 157 | hi = ~hi >>> 0 158 | lo = ~lo >>> 0 159 | if (++lo > 4294967295) { 160 | lo = 0 161 | if (++hi > 4294967295) { hi = 0 } 162 | } 163 | } 164 | return new LongBits(lo, hi) 165 | } 166 | 167 | /** 168 | * Constructs new long bits from a number, long or string 169 | */ 170 | static from (value: bigint | number | string | { low: number, high: number }): LongBits { 171 | if (typeof value === 'number') { 172 | return LongBits.fromNumber(value) 173 | } 174 | if (typeof value === 'bigint') { 175 | return LongBits.fromBigInt(value) 176 | } 177 | if (typeof value === 'string') { 178 | return LongBits.fromBigInt(BigInt(value)) 179 | } 180 | return value.low != null || value.high != null ? new LongBits(value.low >>> 0, value.high >>> 0) : zero 181 | } 182 | } 183 | 184 | const zero = new LongBits(0, 0) 185 | zero.toBigInt = function () { return 0n } 186 | zero.zzEncode = zero.zzDecode = function () { return this } 187 | zero.length = function () { return 1 } 188 | 189 | const TWO_32 = 4294967296n 190 | -------------------------------------------------------------------------------- /packages/protons-runtime/src/utils/pool.ts: -------------------------------------------------------------------------------- 1 | import { allocUnsafe } from 'uint8arrays/alloc' 2 | 3 | /** 4 | * A general purpose buffer pool 5 | */ 6 | export default function pool (size?: number): (size: number) => Uint8Array { 7 | const SIZE = size ?? 8192 8 | const MAX = SIZE >>> 1 9 | let slab: Uint8Array 10 | let offset = SIZE 11 | return function poolAlloc (size: number) { 12 | if (size < 1 || size > MAX) { 13 | return allocUnsafe(size) 14 | } 15 | 16 | if (offset + size > SIZE) { 17 | slab = allocUnsafe(SIZE) 18 | offset = 0 19 | } 20 | 21 | const buf = slab.subarray(offset, offset += size) 22 | 23 | if ((offset & 7) !== 0) { 24 | // align to 32 bit 25 | offset = (offset | 7) + 1 26 | } 27 | 28 | return buf 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/protons-runtime/src/utils/utf8.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Calculates the UTF8 byte length of a string 3 | */ 4 | export function length (string: string): number { 5 | let len = 0 6 | let c = 0 7 | for (let i = 0; i < string.length; ++i) { 8 | c = string.charCodeAt(i) 9 | 10 | if (c < 128) { 11 | len += 1 12 | } else if (c < 2048) { 13 | len += 2 14 | } else if ((c & 0xFC00) === 0xD800 && (string.charCodeAt(i + 1) & 0xFC00) === 0xDC00) { 15 | ++i 16 | len += 4 17 | } else { 18 | len += 3 19 | } 20 | } 21 | 22 | return len 23 | } 24 | 25 | /** 26 | * Reads UTF8 bytes as a string 27 | */ 28 | export function read (buffer: Uint8Array, start: number, end: number): string { 29 | const len = end - start 30 | 31 | if (len < 1) { 32 | return '' 33 | } 34 | 35 | let parts: string[] | undefined 36 | const chunk: number[] = [] 37 | let i = 0 // char offset 38 | let t: number // temporary 39 | 40 | while (start < end) { 41 | t = buffer[start++] 42 | 43 | if (t < 128) { 44 | chunk[i++] = t 45 | } else if (t > 191 && t < 224) { 46 | chunk[i++] = (t & 31) << 6 | buffer[start++] & 63 47 | } else if (t > 239 && t < 365) { 48 | t = ((t & 7) << 18 | (buffer[start++] & 63) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63) - 0x10000 49 | chunk[i++] = 0xD800 + (t >> 10) 50 | chunk[i++] = 0xDC00 + (t & 1023) 51 | } else { 52 | chunk[i++] = (t & 15) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63 53 | } 54 | 55 | if (i > 8191) { 56 | (parts ?? (parts = [])).push(String.fromCharCode.apply(String, chunk)) 57 | i = 0 58 | } 59 | } 60 | 61 | if (parts != null) { 62 | if (i > 0) { 63 | parts.push(String.fromCharCode.apply(String, chunk.slice(0, i))) 64 | } 65 | 66 | return parts.join('') 67 | } 68 | 69 | return String.fromCharCode.apply(String, chunk.slice(0, i)) 70 | } 71 | 72 | /** 73 | * Writes a string as UTF8 bytes 74 | */ 75 | export function write (string: string, buffer: Uint8Array, offset: number): number { 76 | const start = offset 77 | let c1 // character 1 78 | let c2 // character 2 79 | 80 | for (let i = 0; i < string.length; ++i) { 81 | c1 = string.charCodeAt(i) 82 | 83 | if (c1 < 128) { 84 | buffer[offset++] = c1 85 | } else if (c1 < 2048) { 86 | buffer[offset++] = c1 >> 6 | 192 87 | buffer[offset++] = c1 & 63 | 128 88 | } else if ((c1 & 0xFC00) === 0xD800 && ((c2 = string.charCodeAt(i + 1)) & 0xFC00) === 0xDC00) { 89 | c1 = 0x10000 + ((c1 & 0x03FF) << 10) + (c2 & 0x03FF) 90 | ++i 91 | buffer[offset++] = c1 >> 18 | 240 92 | buffer[offset++] = c1 >> 12 & 63 | 128 93 | buffer[offset++] = c1 >> 6 & 63 | 128 94 | buffer[offset++] = c1 & 63 | 128 95 | } else { 96 | buffer[offset++] = c1 >> 12 | 224 97 | buffer[offset++] = c1 >> 6 & 63 | 128 98 | buffer[offset++] = c1 & 63 | 128 99 | } 100 | } 101 | 102 | return offset - start 103 | } 104 | -------------------------------------------------------------------------------- /packages/protons-runtime/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "aegir/src/config/tsconfig.aegir.json", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "include": [ 7 | "src", 8 | "test" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/protons-runtime/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": [ 3 | "./src/index.ts" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/protons/.aegir.js: -------------------------------------------------------------------------------- 1 | 2 | export default { 3 | build: { 4 | config: { 5 | platform: 'node' 6 | }, 7 | bundle: false 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/protons/LICENSE: -------------------------------------------------------------------------------- 1 | This project is dual licensed under MIT and Apache-2.0. 2 | 3 | MIT: https://www.opensource.org/licenses/mit 4 | Apache-2.0: https://www.apache.org/licenses/license-2.0 5 | -------------------------------------------------------------------------------- /packages/protons/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 2 | 3 | http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 6 | -------------------------------------------------------------------------------- /packages/protons/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/protons/README.md: -------------------------------------------------------------------------------- 1 | # protons 2 | 3 | [![ipfs.tech](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](https://ipfs.tech) 4 | [![Discuss](https://img.shields.io/discourse/https/discuss.ipfs.tech/posts.svg?style=flat-square)](https://discuss.ipfs.tech) 5 | [![codecov](https://img.shields.io/codecov/c/github/ipfs/protons.svg?style=flat-square)](https://codecov.io/gh/ipfs/protons) 6 | [![CI](https://img.shields.io/github/actions/workflow/status/ipfs/protons/js-test-and-release.yml?branch=main\&style=flat-square)](https://github.com/ipfs/protons/actions/workflows/js-test-and-release.yml?query=branch%3Amain) 7 | 8 | > Protobuf to ts transpiler 9 | 10 | # About 11 | 12 | 26 | 27 | `protons` is a high performance implementation of [Protocol Buffers v3](https://protobuf.dev/programming-guides/proto3/). 28 | 29 | It transpiles code to TypeScript and supports BigInts for 64 bit types. 30 | 31 | The `protons` module contains the code to compile `.proto` files to `.ts` files and `protons-runtime` contains the code to do serialization/deserialization to `Uint8Array`s during application execution. 32 | 33 | Please ensure you declare them as the correct type of dependencies: 34 | 35 | ```console 36 | $ npm install --save-dev protons 37 | $ npm install --save protons-runtime 38 | ``` 39 | 40 | ## Usage 41 | 42 | First generate your `.ts` files: 43 | 44 | ```console 45 | $ protons ./path/to/foo.proto ./path/to/output.ts 46 | ``` 47 | 48 | Then run tsc over them as normal: 49 | 50 | ```console 51 | $ tsc 52 | ``` 53 | 54 | In your code import the generated classes and use them to transform to/from bytes: 55 | 56 | ```js 57 | import { Foo } from './foo.js' 58 | 59 | const foo = { 60 | message: 'hello world' 61 | } 62 | 63 | const encoded = Foo.encode(foo) 64 | const decoded = Foo.decode(encoded) 65 | 66 | console.info(decoded.message) 67 | // 'hello world' 68 | ``` 69 | 70 | ## Differences from protobuf.js 71 | 72 | This module uses the internal reader/writer from `protobuf.js` as it is highly optimised and there's no point reinventing the wheel. 73 | 74 | It does have one or two differences: 75 | 76 | 1. Supports `proto3` semantics only 77 | 2. All 64 bit values are represented as `BigInt`s and not `Long`s (e.g. `int64`, `uint64`, `sint64` etc) 78 | 3. Unset `optional` fields are set on the deserialized object forms as `undefined` instead of the default values 79 | 4. `singular` fields set to default values are not serialized and are set to default values when deserialized if not set - protobuf.js [diverges from the language guide](https://github.com/protobufjs/protobuf.js/issues/1468#issuecomment-745177012) around this feature 80 | 5. `map` fields can have keys of any type - protobufs.js [only supports strings](https://github.com/protobufjs/protobuf.js/issues/1203#issuecomment-488637338) 81 | 6. `map` fields are deserialized as ES6 `Map`s - protobuf.js uses `Object`s 82 | 83 | ## Extra features 84 | 85 | ### Limiting the size of repeated/map elements 86 | 87 | To protect decoders from malicious payloads, it's possible to limit the maximum size of repeated/map elements. 88 | 89 | You can either do this at compile time by using the [protons.options](https://github.com/protocolbuffers/protobuf/blob/6f1d88107f268b8ebdad6690d116e74c403e366e/docs/options.md?plain=1#L490-L493) extension: 90 | 91 | ``` 92 | message MyMessage { 93 | // repeatedField cannot have more than 10 entries 94 | repeated uint32 repeatedField = 1 [(protons.options).limit = 10]; 95 | 96 | // stringMap cannot have more than 10 keys 97 | map stringMap = 2 [(protons.options).limit = 10]; 98 | } 99 | ``` 100 | 101 | Or at runtime by passing objects to the `.decode` function of your message: 102 | 103 | ```TypeScript 104 | const message = MyMessage.decode(buf, { 105 | limits: { 106 | repeatedField: 10, 107 | stringMap: 10 108 | } 109 | }) 110 | ``` 111 | 112 | #### Limiting repeating fields of nested messages at runtime 113 | 114 | Sub messages with repeating elements can be limited in a similar way: 115 | 116 | ``` 117 | message SubMessage { 118 | repeated uint32 repeatedField = 1; 119 | } 120 | 121 | message MyMessage { 122 | SubMessage message = 1; 123 | } 124 | ``` 125 | 126 | ```TypeScript 127 | const message = MyMessage.decode(buf, { 128 | limits: { 129 | messages: { 130 | repeatedField: 5 // the SubMessage can not have more than 5 repeatedField entries 131 | } 132 | } 133 | }) 134 | ``` 135 | 136 | #### Limiting repeating fields of repeating messages at runtime 137 | 138 | Sub messages defined in repeating elements can be limited by appending `$` to the field name in the runtime limit options: 139 | 140 | ``` 141 | message SubMessage { 142 | repeated uint32 repeatedField = 1; 143 | } 144 | 145 | message MyMessage { 146 | repeated SubMessage messages = 1; 147 | } 148 | ``` 149 | 150 | ```TypeScript 151 | const message = MyMessage.decode(buf, { 152 | limits: { 153 | messages: 5 // max 5x SubMessages 154 | messages$: { 155 | repeatedField: 5 // no SubMessage can have more than 5 repeatedField entries 156 | } 157 | } 158 | }) 159 | ``` 160 | 161 | #### Limiting repeating fields of map entries at runtime 162 | 163 | Repeating fields in map entries can be limited by appending `$value` to the field name in the runtime limit options: 164 | 165 | ``` 166 | message SubMessage { 167 | repeated uint32 repeatedField = 1; 168 | } 169 | 170 | message MyMessage { 171 | map messages = 1; 172 | } 173 | ``` 174 | 175 | ```TypeScript 176 | const message = MyMessage.decode(buf, { 177 | limits: { 178 | messages: 5 // max 5x SubMessages in the map 179 | messages$value: { 180 | repeatedField: 5 // no SubMessage in the map can have more than 5 repeatedField entries 181 | } 182 | } 183 | }) 184 | ``` 185 | 186 | ### Overriding 64 bit types 187 | 188 | By default 64 bit types are implemented as [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)s. 189 | 190 | Sometimes this is undesirable due to [performance issues](https://betterprogramming.pub/the-downsides-of-bigints-in-javascript-6350fd807d) or code legibility. 191 | 192 | It's possible to override the JavaScript type 64 bit fields will deserialize to: 193 | 194 | ``` 195 | message MyMessage { 196 | repeated int64 bigintField = 1; 197 | repeated int64 numberField = 2 [jstype = JS_NUMBER]; 198 | repeated int64 stringField = 3 [jstype = JS_STRING]; 199 | } 200 | ``` 201 | 202 | ```TypeScript 203 | const message = MyMessage.decode(buf) 204 | 205 | console.info(typeof message.bigintField) // bigint 206 | console.info(typeof message.numberField) // number 207 | console.info(typeof message.stringField) // string 208 | ``` 209 | 210 | ## Missing features 211 | 212 | 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`. 213 | 214 | # Install 215 | 216 | ```console 217 | $ npm i protons 218 | ``` 219 | 220 | # API Docs 221 | 222 | - 223 | 224 | # License 225 | 226 | Licensed under either of 227 | 228 | - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) 229 | - MIT ([LICENSE-MIT](LICENSE-MIT) / ) 230 | 231 | # Contribute 232 | 233 | Contributions welcome! Please check out [the issues](https://github.com/ipfs/protons/issues). 234 | 235 | Also see our [contributing document](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md) for more information on how we work, and about contributing in general. 236 | 237 | Please be aware that all interactions related to this repo are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). 238 | 239 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 240 | 241 | [![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) 242 | -------------------------------------------------------------------------------- /packages/protons/bin/protons.ts: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 3 | import meow from 'meow' 4 | import { generate } from '../src/index.js' 5 | 6 | async function main (): Promise { 7 | const cli = meow(` 8 | Usage 9 | $ protons source... 10 | 11 | Options 12 | --output, -o Path to a directory to write transpiled typescript files into 13 | --strict, -s Causes parsing warnings to become errors 14 | --path, -p Adds a directory to the include path 15 | 16 | Examples 17 | $ protons ./path/to/file.proto ./path/to/other/file.proto 18 | `, { 19 | importMeta: import.meta, 20 | flags: { 21 | output: { 22 | type: 'string', 23 | shortFlag: 'o' 24 | }, 25 | strict: { 26 | type: 'boolean', 27 | shortFlag: 's' 28 | }, 29 | path: { 30 | type: 'string', 31 | shortFlag: 'p', 32 | isMultiple: true 33 | } 34 | } 35 | }) 36 | 37 | if (cli.input.length === 0) { 38 | throw new Error('source must be specified') 39 | } 40 | 41 | for (const source of cli.input) { 42 | await generate(source, cli.flags) 43 | } 44 | } 45 | 46 | main().catch(err => { 47 | console.error(err) // eslint-disable-line no-console 48 | process.exit(1) 49 | }) 50 | -------------------------------------------------------------------------------- /packages/protons/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "protons", 3 | "version": "7.5.0", 4 | "description": "Protobuf to ts transpiler", 5 | "license": "Apache-2.0 OR MIT", 6 | "homepage": "https://github.com/ipfs/protons/tree/master/packages/protons#readme", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/ipfs/protons.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/ipfs/protons/issues" 13 | }, 14 | "publishConfig": { 15 | "access": "public", 16 | "provenance": true 17 | }, 18 | "bin": { 19 | "protons": "./dist/bin/protons.js" 20 | }, 21 | "type": "module", 22 | "types": "./dist/src/index.d.ts", 23 | "files": [ 24 | "src", 25 | "dist", 26 | "!dist/test", 27 | "!**/*.tsbuildinfo" 28 | ], 29 | "exports": { 30 | ".": { 31 | "types": "./dist/src/index.d.ts", 32 | "import": "./dist/src/index.js" 33 | } 34 | }, 35 | "eslintConfig": { 36 | "extends": "ipfs", 37 | "parserOptions": { 38 | "project": true, 39 | "sourceType": "module" 40 | } 41 | }, 42 | "scripts": { 43 | "clean": "aegir clean", 44 | "lint": "aegir lint", 45 | "dep-check": "aegir dep-check", 46 | "build": "aegir build", 47 | "test": "aegir test -t node", 48 | "test:node": "aegir test -t node --cov", 49 | "release": "aegir release" 50 | }, 51 | "dependencies": { 52 | "meow": "^13.1.0", 53 | "protobufjs-cli": "^1.0.0", 54 | "protons-runtime": "^5.0.0" 55 | }, 56 | "devDependencies": { 57 | "aegir": "^47.0.5", 58 | "long": "^5.2.0", 59 | "pbjs": "^0.0.14", 60 | "protobufjs": "^7.0.0", 61 | "uint8arraylist": "^2.4.3", 62 | "uint8arrays": "^5.0.1" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /packages/protons/test/bad-fixtures/empty.proto: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipfs/protons/211cad9baeaef91dda7b00386ffa075782a0e156/packages/protons/test/bad-fixtures/empty.proto -------------------------------------------------------------------------------- /packages/protons/test/bad-fixtures/enum.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | enum AnEnum { 4 | // enum values should start from 0 5 | value1 = 1; 6 | } 7 | -------------------------------------------------------------------------------- /packages/protons/test/bad-fixtures/proto2.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | message Message { 4 | required string requiredField = 1; 5 | } 6 | -------------------------------------------------------------------------------- /packages/protons/test/custom-options.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import { expect } from 'aegir/chai' 4 | import { CustomOptionNumber, CustomOptionString } from './fixtures/custom-option-jstype.js' 5 | 6 | describe('custom options', () => { 7 | it('should allow overriding 64 bit numbers with numbers', () => { 8 | const obj: CustomOptionNumber = { 9 | num: 5, 10 | i64: 5, 11 | ui64: 5, 12 | si64: 5, 13 | f64: 5, 14 | sf64: 5 15 | } 16 | 17 | expect(CustomOptionNumber.decode(CustomOptionNumber.encode(obj))) 18 | .to.deep.equal(obj) 19 | }) 20 | 21 | it('should allow overriding 64 bit numbers with strings', () => { 22 | const obj: CustomOptionString = { 23 | num: 5, 24 | i64: '5', 25 | ui64: '5', 26 | si64: '5', 27 | f64: '5', 28 | sf64: '5' 29 | } 30 | 31 | expect(CustomOptionString.decode(CustomOptionString.encode(obj))) 32 | .to.deep.equal(obj) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/basic.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message Basic { 4 | optional string foo = 1; 5 | int32 num = 2; 6 | } 7 | 8 | message Empty { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/basic.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/export */ 2 | /* eslint-disable complexity */ 3 | /* eslint-disable @typescript-eslint/no-namespace */ 4 | /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */ 5 | /* eslint-disable @typescript-eslint/no-empty-interface */ 6 | /* eslint-disable import/consistent-type-specifier-style */ 7 | /* eslint-disable @typescript-eslint/no-unused-vars */ 8 | 9 | import { decodeMessage, encodeMessage, message } from 'protons-runtime' 10 | import type { Codec, DecodeOptions } from 'protons-runtime' 11 | import type { Uint8ArrayList } from 'uint8arraylist' 12 | 13 | export interface Basic { 14 | foo?: string 15 | num: number 16 | } 17 | 18 | export namespace Basic { 19 | let _codec: Codec 20 | 21 | export const codec = (): Codec => { 22 | if (_codec == null) { 23 | _codec = message((obj, w, opts = {}) => { 24 | if (opts.lengthDelimited !== false) { 25 | w.fork() 26 | } 27 | 28 | if (obj.foo != null) { 29 | w.uint32(10) 30 | w.string(obj.foo) 31 | } 32 | 33 | if ((obj.num != null && obj.num !== 0)) { 34 | w.uint32(16) 35 | w.int32(obj.num) 36 | } 37 | 38 | if (opts.lengthDelimited !== false) { 39 | w.ldelim() 40 | } 41 | }, (reader, length, opts = {}) => { 42 | const obj: any = { 43 | num: 0 44 | } 45 | 46 | const end = length == null ? reader.len : reader.pos + length 47 | 48 | while (reader.pos < end) { 49 | const tag = reader.uint32() 50 | 51 | switch (tag >>> 3) { 52 | case 1: { 53 | obj.foo = reader.string() 54 | break 55 | } 56 | case 2: { 57 | obj.num = reader.int32() 58 | break 59 | } 60 | default: { 61 | reader.skipType(tag & 7) 62 | break 63 | } 64 | } 65 | } 66 | 67 | return obj 68 | }) 69 | } 70 | 71 | return _codec 72 | } 73 | 74 | export const encode = (obj: Partial): Uint8Array => { 75 | return encodeMessage(obj, Basic.codec()) 76 | } 77 | 78 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): Basic => { 79 | return decodeMessage(buf, Basic.codec(), opts) 80 | } 81 | } 82 | 83 | export interface Empty {} 84 | 85 | export namespace Empty { 86 | let _codec: Codec 87 | 88 | export const codec = (): Codec => { 89 | if (_codec == null) { 90 | _codec = message((obj, w, opts = {}) => { 91 | if (opts.lengthDelimited !== false) { 92 | w.fork() 93 | } 94 | 95 | if (opts.lengthDelimited !== false) { 96 | w.ldelim() 97 | } 98 | }, (reader, length, opts = {}) => { 99 | const obj: any = {} 100 | 101 | const end = length == null ? reader.len : reader.pos + length 102 | 103 | while (reader.pos < end) { 104 | const tag = reader.uint32() 105 | 106 | switch (tag >>> 3) { 107 | default: { 108 | reader.skipType(tag & 7) 109 | break 110 | } 111 | } 112 | } 113 | 114 | return obj 115 | }) 116 | } 117 | 118 | return _codec 119 | } 120 | 121 | export const encode = (obj: Partial): Uint8Array => { 122 | return encodeMessage(obj, Empty.codec()) 123 | } 124 | 125 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): Empty => { 126 | return decodeMessage(buf, Empty.codec(), opts) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/bitswap.proto: -------------------------------------------------------------------------------- 1 | // from https://github.com/ipfs/go-bitswap/blob/master/message/pb/message.proto 2 | syntax = "proto3"; 3 | 4 | message Message { 5 | 6 | message Wantlist { 7 | enum WantType { 8 | Block = 0; 9 | Have = 1; 10 | } 11 | 12 | message Entry { 13 | bytes block = 1; // the block cid (cidV0 in bitswap 1.0.0, cidV1 in bitswap 1.1.0) 14 | int32 priority = 2; // the priority (normalized). default to 1 15 | optional bool cancel = 3; // whether this revokes an entry 16 | WantType wantType = 4; // Note: defaults to enum 0, ie Block 17 | bool sendDontHave = 5; // Note: defaults to false 18 | } 19 | 20 | repeated Entry entries = 1; // a list of wantlist entries 21 | bool full = 2; // whether this is the full wantlist. default to false 22 | } 23 | 24 | message Block { 25 | bytes prefix = 1; // CID prefix (cid version, multicodec and multihash prefix (type + length) 26 | bytes data = 2; 27 | } 28 | 29 | enum BlockPresenceType { 30 | Have = 0; 31 | DontHave = 1; 32 | } 33 | message BlockPresence { 34 | bytes cid = 1; 35 | BlockPresenceType type = 2; 36 | } 37 | 38 | Wantlist wantlist = 1; 39 | repeated bytes blocks = 2; // used to send Blocks in bitswap 1.0.0 40 | repeated Block payload = 3; // used to send Blocks in bitswap 1.1.0 41 | repeated BlockPresence blockPresences = 4; 42 | int32 pendingBytes = 5; 43 | } 44 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/circuit.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message CircuitRelay { 4 | 5 | enum Status { 6 | SUCCESS = 100; 7 | HOP_SRC_ADDR_TOO_LONG = 220; 8 | HOP_DST_ADDR_TOO_LONG = 221; 9 | HOP_SRC_MULTIADDR_INVALID = 250; 10 | HOP_DST_MULTIADDR_INVALID = 251; 11 | HOP_NO_CONN_TO_DST = 260; 12 | HOP_CANT_DIAL_DST = 261; 13 | HOP_CANT_OPEN_DST_STREAM = 262; 14 | HOP_CANT_SPEAK_RELAY = 270; 15 | HOP_CANT_RELAY_TO_SELF = 280; 16 | STOP_SRC_ADDR_TOO_LONG = 320; 17 | STOP_DST_ADDR_TOO_LONG = 321; 18 | STOP_SRC_MULTIADDR_INVALID = 350; 19 | STOP_DST_MULTIADDR_INVALID = 351; 20 | STOP_RELAY_REFUSED = 390; 21 | MALFORMED_MESSAGE = 400; 22 | } 23 | 24 | enum Type { // RPC identifier, either HOP, STOP or STATUS 25 | HOP = 1; 26 | STOP = 2; 27 | STATUS = 3; 28 | CAN_HOP = 4; 29 | } 30 | 31 | message Peer { 32 | bytes id = 1; // peer id 33 | repeated bytes addrs = 2; // peer's known addresses 34 | } 35 | 36 | optional Type type = 1; // Type of the message 37 | 38 | optional Peer srcPeer = 2; // srcPeer and dstPeer are used when Type is HOP or STATUS 39 | optional Peer dstPeer = 3; 40 | 41 | optional Status code = 4; // Status code, used when Type is STATUS 42 | } 43 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/circuit.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/export */ 2 | /* eslint-disable complexity */ 3 | /* eslint-disable @typescript-eslint/no-namespace */ 4 | /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */ 5 | /* eslint-disable @typescript-eslint/no-empty-interface */ 6 | /* eslint-disable import/consistent-type-specifier-style */ 7 | /* eslint-disable @typescript-eslint/no-unused-vars */ 8 | 9 | import { decodeMessage, encodeMessage, enumeration, MaxLengthError, message } from 'protons-runtime' 10 | import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc' 11 | import type { Codec, DecodeOptions } from 'protons-runtime' 12 | import type { Uint8ArrayList } from 'uint8arraylist' 13 | 14 | export interface CircuitRelay { 15 | type?: CircuitRelay.Type 16 | srcPeer?: CircuitRelay.Peer 17 | dstPeer?: CircuitRelay.Peer 18 | code?: CircuitRelay.Status 19 | } 20 | 21 | export namespace CircuitRelay { 22 | export enum Status { 23 | SUCCESS = 'SUCCESS', 24 | HOP_SRC_ADDR_TOO_LONG = 'HOP_SRC_ADDR_TOO_LONG', 25 | HOP_DST_ADDR_TOO_LONG = 'HOP_DST_ADDR_TOO_LONG', 26 | HOP_SRC_MULTIADDR_INVALID = 'HOP_SRC_MULTIADDR_INVALID', 27 | HOP_DST_MULTIADDR_INVALID = 'HOP_DST_MULTIADDR_INVALID', 28 | HOP_NO_CONN_TO_DST = 'HOP_NO_CONN_TO_DST', 29 | HOP_CANT_DIAL_DST = 'HOP_CANT_DIAL_DST', 30 | HOP_CANT_OPEN_DST_STREAM = 'HOP_CANT_OPEN_DST_STREAM', 31 | HOP_CANT_SPEAK_RELAY = 'HOP_CANT_SPEAK_RELAY', 32 | HOP_CANT_RELAY_TO_SELF = 'HOP_CANT_RELAY_TO_SELF', 33 | STOP_SRC_ADDR_TOO_LONG = 'STOP_SRC_ADDR_TOO_LONG', 34 | STOP_DST_ADDR_TOO_LONG = 'STOP_DST_ADDR_TOO_LONG', 35 | STOP_SRC_MULTIADDR_INVALID = 'STOP_SRC_MULTIADDR_INVALID', 36 | STOP_DST_MULTIADDR_INVALID = 'STOP_DST_MULTIADDR_INVALID', 37 | STOP_RELAY_REFUSED = 'STOP_RELAY_REFUSED', 38 | MALFORMED_MESSAGE = 'MALFORMED_MESSAGE' 39 | } 40 | 41 | enum __StatusValues { 42 | SUCCESS = 100, 43 | HOP_SRC_ADDR_TOO_LONG = 220, 44 | HOP_DST_ADDR_TOO_LONG = 221, 45 | HOP_SRC_MULTIADDR_INVALID = 250, 46 | HOP_DST_MULTIADDR_INVALID = 251, 47 | HOP_NO_CONN_TO_DST = 260, 48 | HOP_CANT_DIAL_DST = 261, 49 | HOP_CANT_OPEN_DST_STREAM = 262, 50 | HOP_CANT_SPEAK_RELAY = 270, 51 | HOP_CANT_RELAY_TO_SELF = 280, 52 | STOP_SRC_ADDR_TOO_LONG = 320, 53 | STOP_DST_ADDR_TOO_LONG = 321, 54 | STOP_SRC_MULTIADDR_INVALID = 350, 55 | STOP_DST_MULTIADDR_INVALID = 351, 56 | STOP_RELAY_REFUSED = 390, 57 | MALFORMED_MESSAGE = 400 58 | } 59 | 60 | export namespace Status { 61 | export const codec = (): Codec => { 62 | return enumeration(__StatusValues) 63 | } 64 | } 65 | 66 | export enum Type { 67 | HOP = 'HOP', 68 | STOP = 'STOP', 69 | STATUS = 'STATUS', 70 | CAN_HOP = 'CAN_HOP' 71 | } 72 | 73 | enum __TypeValues { 74 | HOP = 1, 75 | STOP = 2, 76 | STATUS = 3, 77 | CAN_HOP = 4 78 | } 79 | 80 | export namespace Type { 81 | export const codec = (): Codec => { 82 | return enumeration(__TypeValues) 83 | } 84 | } 85 | 86 | export interface Peer { 87 | id: Uint8Array 88 | addrs: Uint8Array[] 89 | } 90 | 91 | export namespace Peer { 92 | let _codec: Codec 93 | 94 | export const codec = (): Codec => { 95 | if (_codec == null) { 96 | _codec = message((obj, w, opts = {}) => { 97 | if (opts.lengthDelimited !== false) { 98 | w.fork() 99 | } 100 | 101 | if ((obj.id != null && obj.id.byteLength > 0)) { 102 | w.uint32(10) 103 | w.bytes(obj.id) 104 | } 105 | 106 | if (obj.addrs != null) { 107 | for (const value of obj.addrs) { 108 | w.uint32(18) 109 | w.bytes(value) 110 | } 111 | } 112 | 113 | if (opts.lengthDelimited !== false) { 114 | w.ldelim() 115 | } 116 | }, (reader, length, opts = {}) => { 117 | const obj: any = { 118 | id: uint8ArrayAlloc(0), 119 | addrs: [] 120 | } 121 | 122 | const end = length == null ? reader.len : reader.pos + length 123 | 124 | while (reader.pos < end) { 125 | const tag = reader.uint32() 126 | 127 | switch (tag >>> 3) { 128 | case 1: { 129 | obj.id = reader.bytes() 130 | break 131 | } 132 | case 2: { 133 | if (opts.limits?.addrs != null && obj.addrs.length === opts.limits.addrs) { 134 | throw new MaxLengthError('Decode error - map field "addrs" had too many elements') 135 | } 136 | 137 | obj.addrs.push(reader.bytes()) 138 | break 139 | } 140 | default: { 141 | reader.skipType(tag & 7) 142 | break 143 | } 144 | } 145 | } 146 | 147 | return obj 148 | }) 149 | } 150 | 151 | return _codec 152 | } 153 | 154 | export const encode = (obj: Partial): Uint8Array => { 155 | return encodeMessage(obj, Peer.codec()) 156 | } 157 | 158 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): Peer => { 159 | return decodeMessage(buf, Peer.codec(), opts) 160 | } 161 | } 162 | 163 | let _codec: Codec 164 | 165 | export const codec = (): Codec => { 166 | if (_codec == null) { 167 | _codec = message((obj, w, opts = {}) => { 168 | if (opts.lengthDelimited !== false) { 169 | w.fork() 170 | } 171 | 172 | if (obj.type != null) { 173 | w.uint32(8) 174 | CircuitRelay.Type.codec().encode(obj.type, w) 175 | } 176 | 177 | if (obj.srcPeer != null) { 178 | w.uint32(18) 179 | CircuitRelay.Peer.codec().encode(obj.srcPeer, w) 180 | } 181 | 182 | if (obj.dstPeer != null) { 183 | w.uint32(26) 184 | CircuitRelay.Peer.codec().encode(obj.dstPeer, w) 185 | } 186 | 187 | if (obj.code != null) { 188 | w.uint32(32) 189 | CircuitRelay.Status.codec().encode(obj.code, w) 190 | } 191 | 192 | if (opts.lengthDelimited !== false) { 193 | w.ldelim() 194 | } 195 | }, (reader, length, opts = {}) => { 196 | const obj: any = {} 197 | 198 | const end = length == null ? reader.len : reader.pos + length 199 | 200 | while (reader.pos < end) { 201 | const tag = reader.uint32() 202 | 203 | switch (tag >>> 3) { 204 | case 1: { 205 | obj.type = CircuitRelay.Type.codec().decode(reader) 206 | break 207 | } 208 | case 2: { 209 | obj.srcPeer = CircuitRelay.Peer.codec().decode(reader, reader.uint32(), { 210 | limits: opts.limits?.srcPeer 211 | }) 212 | break 213 | } 214 | case 3: { 215 | obj.dstPeer = CircuitRelay.Peer.codec().decode(reader, reader.uint32(), { 216 | limits: opts.limits?.dstPeer 217 | }) 218 | break 219 | } 220 | case 4: { 221 | obj.code = CircuitRelay.Status.codec().decode(reader) 222 | break 223 | } 224 | default: { 225 | reader.skipType(tag & 7) 226 | break 227 | } 228 | } 229 | } 230 | 231 | return obj 232 | }) 233 | } 234 | 235 | return _codec 236 | } 237 | 238 | export const encode = (obj: Partial): Uint8Array => { 239 | return encodeMessage(obj, CircuitRelay.codec()) 240 | } 241 | 242 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): CircuitRelay => { 243 | return decodeMessage(buf, CircuitRelay.codec(), opts) 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/custom-option-jstype.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message CustomOptionNumber { 4 | int32 num = 1; 5 | int64 i64 = 2 [jstype = JS_NUMBER]; 6 | uint64 ui64 = 3 [jstype = JS_NUMBER]; 7 | sint64 si64 = 4 [jstype = JS_NUMBER]; 8 | fixed64 f64 = 5 [jstype = JS_NUMBER]; 9 | sfixed64 sf64 = 6 [jstype = JS_NUMBER]; 10 | } 11 | 12 | message CustomOptionString { 13 | int32 num = 1; 14 | int64 i64 = 2 [jstype = JS_STRING]; 15 | uint64 ui64 = 3 [jstype = JS_STRING]; 16 | sint64 si64 = 4 [jstype = JS_STRING]; 17 | fixed64 f64 = 5 [jstype = JS_STRING]; 18 | sfixed64 sf64 = 6 [jstype = JS_STRING]; 19 | } 20 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/custom-option-jstype.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/export */ 2 | /* eslint-disable complexity */ 3 | /* eslint-disable @typescript-eslint/no-namespace */ 4 | /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */ 5 | /* eslint-disable @typescript-eslint/no-empty-interface */ 6 | /* eslint-disable import/consistent-type-specifier-style */ 7 | /* eslint-disable @typescript-eslint/no-unused-vars */ 8 | 9 | import { decodeMessage, encodeMessage, message } from 'protons-runtime' 10 | import type { Codec, DecodeOptions } from 'protons-runtime' 11 | import type { Uint8ArrayList } from 'uint8arraylist' 12 | 13 | export interface CustomOptionNumber { 14 | num: number 15 | i64: number 16 | ui64: number 17 | si64: number 18 | f64: number 19 | sf64: number 20 | } 21 | 22 | export namespace CustomOptionNumber { 23 | let _codec: Codec 24 | 25 | export const codec = (): Codec => { 26 | if (_codec == null) { 27 | _codec = message((obj, w, opts = {}) => { 28 | if (opts.lengthDelimited !== false) { 29 | w.fork() 30 | } 31 | 32 | if ((obj.num != null && obj.num !== 0)) { 33 | w.uint32(8) 34 | w.int32(obj.num) 35 | } 36 | 37 | if ((obj.i64 != null && obj.i64 !== 0)) { 38 | w.uint32(16) 39 | w.int64Number(obj.i64) 40 | } 41 | 42 | if ((obj.ui64 != null && obj.ui64 !== 0)) { 43 | w.uint32(24) 44 | w.uint64Number(obj.ui64) 45 | } 46 | 47 | if ((obj.si64 != null && obj.si64 !== 0)) { 48 | w.uint32(32) 49 | w.sint64Number(obj.si64) 50 | } 51 | 52 | if ((obj.f64 != null && obj.f64 !== 0)) { 53 | w.uint32(41) 54 | w.fixed64Number(obj.f64) 55 | } 56 | 57 | if ((obj.sf64 != null && obj.sf64 !== 0)) { 58 | w.uint32(49) 59 | w.sfixed64Number(obj.sf64) 60 | } 61 | 62 | if (opts.lengthDelimited !== false) { 63 | w.ldelim() 64 | } 65 | }, (reader, length, opts = {}) => { 66 | const obj: any = { 67 | num: 0, 68 | i64: 0, 69 | ui64: 0, 70 | si64: 0, 71 | f64: 0, 72 | sf64: 0 73 | } 74 | 75 | const end = length == null ? reader.len : reader.pos + length 76 | 77 | while (reader.pos < end) { 78 | const tag = reader.uint32() 79 | 80 | switch (tag >>> 3) { 81 | case 1: { 82 | obj.num = reader.int32() 83 | break 84 | } 85 | case 2: { 86 | obj.i64 = reader.int64Number() 87 | break 88 | } 89 | case 3: { 90 | obj.ui64 = reader.uint64Number() 91 | break 92 | } 93 | case 4: { 94 | obj.si64 = reader.sint64Number() 95 | break 96 | } 97 | case 5: { 98 | obj.f64 = reader.fixed64Number() 99 | break 100 | } 101 | case 6: { 102 | obj.sf64 = reader.sfixed64Number() 103 | break 104 | } 105 | default: { 106 | reader.skipType(tag & 7) 107 | break 108 | } 109 | } 110 | } 111 | 112 | return obj 113 | }) 114 | } 115 | 116 | return _codec 117 | } 118 | 119 | export const encode = (obj: Partial): Uint8Array => { 120 | return encodeMessage(obj, CustomOptionNumber.codec()) 121 | } 122 | 123 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): CustomOptionNumber => { 124 | return decodeMessage(buf, CustomOptionNumber.codec(), opts) 125 | } 126 | } 127 | 128 | export interface CustomOptionString { 129 | num: number 130 | i64: string 131 | ui64: string 132 | si64: string 133 | f64: string 134 | sf64: string 135 | } 136 | 137 | export namespace CustomOptionString { 138 | let _codec: Codec 139 | 140 | export const codec = (): Codec => { 141 | if (_codec == null) { 142 | _codec = message((obj, w, opts = {}) => { 143 | if (opts.lengthDelimited !== false) { 144 | w.fork() 145 | } 146 | 147 | if ((obj.num != null && obj.num !== 0)) { 148 | w.uint32(8) 149 | w.int32(obj.num) 150 | } 151 | 152 | if ((obj.i64 != null && obj.i64 !== '')) { 153 | w.uint32(16) 154 | w.int64String(obj.i64) 155 | } 156 | 157 | if ((obj.ui64 != null && obj.ui64 !== '')) { 158 | w.uint32(24) 159 | w.uint64String(obj.ui64) 160 | } 161 | 162 | if ((obj.si64 != null && obj.si64 !== '')) { 163 | w.uint32(32) 164 | w.sint64String(obj.si64) 165 | } 166 | 167 | if ((obj.f64 != null && obj.f64 !== '')) { 168 | w.uint32(41) 169 | w.fixed64String(obj.f64) 170 | } 171 | 172 | if ((obj.sf64 != null && obj.sf64 !== '')) { 173 | w.uint32(49) 174 | w.sfixed64String(obj.sf64) 175 | } 176 | 177 | if (opts.lengthDelimited !== false) { 178 | w.ldelim() 179 | } 180 | }, (reader, length, opts = {}) => { 181 | const obj: any = { 182 | num: 0, 183 | i64: '', 184 | ui64: '', 185 | si64: '', 186 | f64: '', 187 | sf64: '' 188 | } 189 | 190 | const end = length == null ? reader.len : reader.pos + length 191 | 192 | while (reader.pos < end) { 193 | const tag = reader.uint32() 194 | 195 | switch (tag >>> 3) { 196 | case 1: { 197 | obj.num = reader.int32() 198 | break 199 | } 200 | case 2: { 201 | obj.i64 = reader.int64String() 202 | break 203 | } 204 | case 3: { 205 | obj.ui64 = reader.uint64String() 206 | break 207 | } 208 | case 4: { 209 | obj.si64 = reader.sint64String() 210 | break 211 | } 212 | case 5: { 213 | obj.f64 = reader.fixed64String() 214 | break 215 | } 216 | case 6: { 217 | obj.sf64 = reader.sfixed64String() 218 | break 219 | } 220 | default: { 221 | reader.skipType(tag & 7) 222 | break 223 | } 224 | } 225 | } 226 | 227 | return obj 228 | }) 229 | } 230 | 231 | return _codec 232 | } 233 | 234 | export const encode = (obj: Partial): Uint8Array => { 235 | return encodeMessage(obj, CustomOptionString.codec()) 236 | } 237 | 238 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): CustomOptionString => { 239 | return decodeMessage(buf, CustomOptionString.codec(), opts) 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/daemon.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message Request { 4 | enum Type { 5 | IDENTIFY = 0; 6 | CONNECT = 1; 7 | STREAM_OPEN = 2; 8 | STREAM_HANDLER = 3; 9 | DHT = 4; 10 | LIST_PEERS = 5; 11 | CONNMANAGER = 6; 12 | DISCONNECT = 7; 13 | PUBSUB = 8; 14 | PEERSTORE = 9; 15 | } 16 | 17 | Type type = 1; 18 | 19 | optional ConnectRequest connect = 2; 20 | optional StreamOpenRequest streamOpen = 3; 21 | optional StreamHandlerRequest streamHandler = 4; 22 | optional DHTRequest dht = 5; 23 | optional ConnManagerRequest connManager = 6; 24 | optional DisconnectRequest disconnect = 7; 25 | optional PSRequest pubsub = 8; 26 | optional PeerstoreRequest peerStore = 9; 27 | } 28 | 29 | message Response { 30 | enum Type { 31 | OK = 0; 32 | ERROR = 1; 33 | } 34 | 35 | Type type = 1; 36 | optional ErrorResponse error = 2; 37 | optional StreamInfo streamInfo = 3; 38 | optional IdentifyResponse identify = 4; 39 | optional DHTResponse dht = 5; 40 | repeated PeerInfo peers = 6; 41 | optional PSResponse pubsub = 7; 42 | optional PeerstoreResponse peerStore = 8; 43 | } 44 | 45 | message IdentifyResponse { 46 | bytes id = 1; 47 | repeated bytes addrs = 2; 48 | } 49 | 50 | message ConnectRequest { 51 | bytes peer = 1; 52 | repeated bytes addrs = 2; 53 | optional int64 timeout = 3; 54 | } 55 | 56 | message StreamOpenRequest { 57 | bytes peer = 1; 58 | repeated string proto = 2; 59 | optional int64 timeout = 3; 60 | } 61 | 62 | message StreamHandlerRequest { 63 | bytes addr = 1; 64 | repeated string proto = 2; 65 | } 66 | 67 | message ErrorResponse { 68 | string msg = 1; 69 | } 70 | 71 | message StreamInfo { 72 | bytes peer = 1; 73 | bytes addr = 2; 74 | string proto = 3; 75 | } 76 | 77 | message DHTRequest { 78 | enum Type { 79 | FIND_PEER = 0; 80 | FIND_PEERS_CONNECTED_TO_PEER = 1; 81 | FIND_PROVIDERS = 2; 82 | GET_CLOSEST_PEERS = 3; 83 | GET_PUBLIC_KEY = 4; 84 | GET_VALUE = 5; 85 | SEARCH_VALUE = 6; 86 | PUT_VALUE = 7; 87 | PROVIDE = 8; 88 | } 89 | 90 | Type type = 1; 91 | optional bytes peer = 2; 92 | optional bytes cid = 3; 93 | optional bytes key = 4; 94 | optional bytes value = 5; 95 | optional int32 count = 6; 96 | optional int64 timeout = 7; 97 | } 98 | 99 | message DHTResponse { 100 | enum Type { 101 | BEGIN = 0; 102 | VALUE = 1; 103 | END = 2; 104 | } 105 | 106 | Type type = 1; 107 | optional PeerInfo peer = 2; 108 | optional bytes value = 3; 109 | } 110 | 111 | message PeerInfo { 112 | bytes id = 1; 113 | repeated bytes addrs = 2; 114 | } 115 | 116 | message ConnManagerRequest { 117 | enum Type { 118 | TAG_PEER = 0; 119 | UNTAG_PEER = 1; 120 | TRIM = 2; 121 | } 122 | 123 | Type type = 1; 124 | 125 | optional bytes peer = 2; 126 | optional string tag = 3; 127 | optional int64 weight = 4; 128 | } 129 | 130 | message DisconnectRequest { 131 | bytes peer = 1; 132 | } 133 | 134 | message PSRequest { 135 | enum Type { 136 | GET_TOPICS = 0; 137 | LIST_PEERS = 1; 138 | PUBLISH = 2; 139 | SUBSCRIBE = 3; 140 | } 141 | 142 | Type type = 1; 143 | optional string topic = 2; 144 | optional bytes data = 3; 145 | } 146 | 147 | message PSMessage { 148 | optional bytes from = 1; 149 | optional bytes data = 2; 150 | optional bytes seqno = 3; 151 | repeated string topicIDs = 4; 152 | optional bytes signature = 5; 153 | optional bytes key = 6; 154 | } 155 | 156 | message PSResponse { 157 | repeated string topics = 1; 158 | repeated bytes peerIDs = 2; 159 | } 160 | 161 | message PeerstoreRequest { 162 | enum Type { 163 | INVALID = 0; 164 | GET_PROTOCOLS = 1; 165 | GET_PEER_INFO = 2; 166 | } 167 | 168 | Type type = 1; 169 | optional bytes id = 2; 170 | repeated string protos = 3; 171 | } 172 | 173 | message PeerstoreResponse { 174 | optional PeerInfo peer = 1; 175 | repeated string protos = 2; 176 | } 177 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/dht.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | // can't use, because protocol-buffers doesn't support imports 3 | // so we have to duplicate for now :( 4 | // import "record.proto"; 5 | 6 | message Record { 7 | // adjusted for javascript 8 | optional bytes key = 1; 9 | optional bytes value = 2; 10 | optional bytes author = 3; 11 | optional bytes signature = 4; 12 | optional string timeReceived = 5; 13 | } 14 | 15 | message Message { 16 | enum MessageType { 17 | PUT_VALUE = 0; 18 | GET_VALUE = 1; 19 | ADD_PROVIDER = 2; 20 | GET_PROVIDERS = 3; 21 | FIND_NODE = 4; 22 | PING = 5; 23 | } 24 | 25 | enum ConnectionType { 26 | // sender does not have a connection to peer, and no extra information (default) 27 | NOT_CONNECTED = 0; 28 | 29 | // sender has a live connection to peer 30 | CONNECTED = 1; 31 | 32 | // sender recently connected to peer 33 | CAN_CONNECT = 2; 34 | 35 | // sender recently tried to connect to peer repeatedly but failed to connect 36 | // ("try" here is loose, but this should signal "made strong effort, failed") 37 | CANNOT_CONNECT = 3; 38 | } 39 | 40 | message Peer { 41 | // ID of a given peer. 42 | optional bytes id = 1; 43 | 44 | // multiaddrs for a given peer 45 | repeated bytes addrs = 2; 46 | 47 | // used to signal the sender's connection capabilities to the peer 48 | optional ConnectionType connection = 3; 49 | } 50 | 51 | // defines what type of message it is. 52 | optional MessageType type = 1; 53 | 54 | // defines what coral cluster level this query/response belongs to. 55 | // in case we want to implement coral's cluster rings in the future. 56 | optional int32 clusterLevelRaw = 10; 57 | 58 | // Used to specify the key associated with this message. 59 | // PUT_VALUE, GET_VALUE, ADD_PROVIDER, GET_PROVIDERS 60 | // adjusted for javascript 61 | optional bytes key = 2; 62 | 63 | // Used to return a value 64 | // PUT_VALUE, GET_VALUE 65 | // adjusted Record to bytes for js 66 | optional bytes record = 3; 67 | 68 | // Used to return peers closer to a key in a query 69 | // GET_VALUE, GET_PROVIDERS, FIND_NODE 70 | repeated Peer closerPeers = 8; 71 | 72 | // Used to return Providers 73 | // GET_VALUE, ADD_PROVIDER, GET_PROVIDERS 74 | repeated Peer providerPeers = 9; 75 | } 76 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/maps.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | enum EnumValue { 4 | NO_VALUE = 0; 5 | VALUE_1 = 1; 6 | VALUE_2 = 2; 7 | } 8 | 9 | message SubMessage { 10 | string foo = 1; 11 | repeated uint32 bar = 2; 12 | } 13 | 14 | message MapTypes { 15 | map stringMap = 1; 16 | map intMap = 2; 17 | map boolMap = 3; 18 | map messageMap = 4; 19 | map enumMap = 5; 20 | } 21 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/noise.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package pb; 3 | 4 | message NoiseHandshakePayload { 5 | bytes identity_key = 1; 6 | bytes identity_sig = 2; 7 | bytes data = 3; 8 | } 9 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/noise.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/export */ 2 | /* eslint-disable complexity */ 3 | /* eslint-disable @typescript-eslint/no-namespace */ 4 | /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */ 5 | /* eslint-disable @typescript-eslint/no-empty-interface */ 6 | /* eslint-disable import/consistent-type-specifier-style */ 7 | /* eslint-disable @typescript-eslint/no-unused-vars */ 8 | 9 | import { decodeMessage, encodeMessage, message } from 'protons-runtime' 10 | import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc' 11 | import type { Codec, DecodeOptions } from 'protons-runtime' 12 | import type { Uint8ArrayList } from 'uint8arraylist' 13 | 14 | export interface pb {} 15 | 16 | export namespace pb { 17 | export interface NoiseHandshakePayload { 18 | identityKey: Uint8Array 19 | identitySig: Uint8Array 20 | data: Uint8Array 21 | } 22 | 23 | export namespace NoiseHandshakePayload { 24 | let _codec: Codec 25 | 26 | export const codec = (): Codec => { 27 | if (_codec == null) { 28 | _codec = message((obj, w, opts = {}) => { 29 | if (opts.lengthDelimited !== false) { 30 | w.fork() 31 | } 32 | 33 | if ((obj.identityKey != null && obj.identityKey.byteLength > 0)) { 34 | w.uint32(10) 35 | w.bytes(obj.identityKey) 36 | } 37 | 38 | if ((obj.identitySig != null && obj.identitySig.byteLength > 0)) { 39 | w.uint32(18) 40 | w.bytes(obj.identitySig) 41 | } 42 | 43 | if ((obj.data != null && obj.data.byteLength > 0)) { 44 | w.uint32(26) 45 | w.bytes(obj.data) 46 | } 47 | 48 | if (opts.lengthDelimited !== false) { 49 | w.ldelim() 50 | } 51 | }, (reader, length, opts = {}) => { 52 | const obj: any = { 53 | identityKey: uint8ArrayAlloc(0), 54 | identitySig: uint8ArrayAlloc(0), 55 | data: uint8ArrayAlloc(0) 56 | } 57 | 58 | const end = length == null ? reader.len : reader.pos + length 59 | 60 | while (reader.pos < end) { 61 | const tag = reader.uint32() 62 | 63 | switch (tag >>> 3) { 64 | case 1: { 65 | obj.identityKey = reader.bytes() 66 | break 67 | } 68 | case 2: { 69 | obj.identitySig = reader.bytes() 70 | break 71 | } 72 | case 3: { 73 | obj.data = reader.bytes() 74 | break 75 | } 76 | default: { 77 | reader.skipType(tag & 7) 78 | break 79 | } 80 | } 81 | } 82 | 83 | return obj 84 | }) 85 | } 86 | 87 | return _codec 88 | } 89 | 90 | export const encode = (obj: Partial): Uint8Array => { 91 | return encodeMessage(obj, NoiseHandshakePayload.codec()) 92 | } 93 | 94 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): NoiseHandshakePayload => { 95 | return decodeMessage(buf, NoiseHandshakePayload.codec(), opts) 96 | } 97 | } 98 | 99 | let _codec: Codec 100 | 101 | export const codec = (): Codec => { 102 | if (_codec == null) { 103 | _codec = message((obj, w, opts = {}) => { 104 | if (opts.lengthDelimited !== false) { 105 | w.fork() 106 | } 107 | 108 | if (opts.lengthDelimited !== false) { 109 | w.ldelim() 110 | } 111 | }, (reader, length, opts = {}) => { 112 | const obj: any = {} 113 | 114 | const end = length == null ? reader.len : reader.pos + length 115 | 116 | while (reader.pos < end) { 117 | const tag = reader.uint32() 118 | 119 | switch (tag >>> 3) { 120 | default: { 121 | reader.skipType(tag & 7) 122 | break 123 | } 124 | } 125 | } 126 | 127 | return obj 128 | }) 129 | } 130 | 131 | return _codec 132 | } 133 | 134 | export const encode = (obj: Partial): Uint8Array => { 135 | return encodeMessage(obj, pb.codec()) 136 | } 137 | 138 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): pb => { 139 | return decodeMessage(buf, pb.codec(), opts) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/optional.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | enum OptionalEnum { 4 | NO_VALUE = 0; 5 | VALUE_1 = 1; 6 | VALUE_2 = 2; 7 | } 8 | 9 | message OptionalSubMessage { 10 | optional string foo = 1; 11 | optional int32 bar = 2; 12 | } 13 | 14 | message Optional { 15 | optional double double = 1; 16 | optional float float = 2; 17 | optional int32 int32 = 3; 18 | optional int64 int64 = 4; 19 | optional uint32 uint32 = 5; 20 | optional uint64 uint64 = 6; 21 | optional sint32 sint32 = 7; 22 | optional sint64 sint64 = 8; 23 | optional fixed32 fixed32 = 9; 24 | optional fixed64 fixed64 = 10; 25 | optional sfixed32 sfixed32 = 11; 26 | optional sfixed64 sfixed64 = 12; 27 | optional bool bool = 13; 28 | optional string string = 14; 29 | optional bytes bytes = 15; 30 | optional OptionalEnum enum = 16; 31 | optional OptionalSubMessage subMessage = 17; 32 | } 33 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/optional.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/export */ 2 | /* eslint-disable complexity */ 3 | /* eslint-disable @typescript-eslint/no-namespace */ 4 | /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */ 5 | /* eslint-disable @typescript-eslint/no-empty-interface */ 6 | /* eslint-disable import/consistent-type-specifier-style */ 7 | /* eslint-disable @typescript-eslint/no-unused-vars */ 8 | 9 | import { decodeMessage, encodeMessage, enumeration, message } from 'protons-runtime' 10 | import type { Codec, DecodeOptions } from 'protons-runtime' 11 | import type { Uint8ArrayList } from 'uint8arraylist' 12 | 13 | export enum OptionalEnum { 14 | NO_VALUE = 'NO_VALUE', 15 | VALUE_1 = 'VALUE_1', 16 | VALUE_2 = 'VALUE_2' 17 | } 18 | 19 | enum __OptionalEnumValues { 20 | NO_VALUE = 0, 21 | VALUE_1 = 1, 22 | VALUE_2 = 2 23 | } 24 | 25 | export namespace OptionalEnum { 26 | export const codec = (): Codec => { 27 | return enumeration(__OptionalEnumValues) 28 | } 29 | } 30 | export interface OptionalSubMessage { 31 | foo?: string 32 | bar?: number 33 | } 34 | 35 | export namespace OptionalSubMessage { 36 | let _codec: Codec 37 | 38 | export const codec = (): Codec => { 39 | if (_codec == null) { 40 | _codec = message((obj, w, opts = {}) => { 41 | if (opts.lengthDelimited !== false) { 42 | w.fork() 43 | } 44 | 45 | if (obj.foo != null) { 46 | w.uint32(10) 47 | w.string(obj.foo) 48 | } 49 | 50 | if (obj.bar != null) { 51 | w.uint32(16) 52 | w.int32(obj.bar) 53 | } 54 | 55 | if (opts.lengthDelimited !== false) { 56 | w.ldelim() 57 | } 58 | }, (reader, length, opts = {}) => { 59 | const obj: any = {} 60 | 61 | const end = length == null ? reader.len : reader.pos + length 62 | 63 | while (reader.pos < end) { 64 | const tag = reader.uint32() 65 | 66 | switch (tag >>> 3) { 67 | case 1: { 68 | obj.foo = reader.string() 69 | break 70 | } 71 | case 2: { 72 | obj.bar = reader.int32() 73 | break 74 | } 75 | default: { 76 | reader.skipType(tag & 7) 77 | break 78 | } 79 | } 80 | } 81 | 82 | return obj 83 | }) 84 | } 85 | 86 | return _codec 87 | } 88 | 89 | export const encode = (obj: Partial): Uint8Array => { 90 | return encodeMessage(obj, OptionalSubMessage.codec()) 91 | } 92 | 93 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): OptionalSubMessage => { 94 | return decodeMessage(buf, OptionalSubMessage.codec(), opts) 95 | } 96 | } 97 | 98 | export interface Optional { 99 | double?: number 100 | float?: number 101 | int32?: number 102 | int64?: bigint 103 | uint32?: number 104 | uint64?: bigint 105 | sint32?: number 106 | sint64?: bigint 107 | fixed32?: number 108 | fixed64?: bigint 109 | sfixed32?: number 110 | sfixed64?: bigint 111 | bool?: boolean 112 | string?: string 113 | bytes?: Uint8Array 114 | enum?: OptionalEnum 115 | subMessage?: OptionalSubMessage 116 | } 117 | 118 | export namespace Optional { 119 | let _codec: Codec 120 | 121 | export const codec = (): Codec => { 122 | if (_codec == null) { 123 | _codec = message((obj, w, opts = {}) => { 124 | if (opts.lengthDelimited !== false) { 125 | w.fork() 126 | } 127 | 128 | if (obj.double != null) { 129 | w.uint32(9) 130 | w.double(obj.double) 131 | } 132 | 133 | if (obj.float != null) { 134 | w.uint32(21) 135 | w.float(obj.float) 136 | } 137 | 138 | if (obj.int32 != null) { 139 | w.uint32(24) 140 | w.int32(obj.int32) 141 | } 142 | 143 | if (obj.int64 != null) { 144 | w.uint32(32) 145 | w.int64(obj.int64) 146 | } 147 | 148 | if (obj.uint32 != null) { 149 | w.uint32(40) 150 | w.uint32(obj.uint32) 151 | } 152 | 153 | if (obj.uint64 != null) { 154 | w.uint32(48) 155 | w.uint64(obj.uint64) 156 | } 157 | 158 | if (obj.sint32 != null) { 159 | w.uint32(56) 160 | w.sint32(obj.sint32) 161 | } 162 | 163 | if (obj.sint64 != null) { 164 | w.uint32(64) 165 | w.sint64(obj.sint64) 166 | } 167 | 168 | if (obj.fixed32 != null) { 169 | w.uint32(77) 170 | w.fixed32(obj.fixed32) 171 | } 172 | 173 | if (obj.fixed64 != null) { 174 | w.uint32(81) 175 | w.fixed64(obj.fixed64) 176 | } 177 | 178 | if (obj.sfixed32 != null) { 179 | w.uint32(93) 180 | w.sfixed32(obj.sfixed32) 181 | } 182 | 183 | if (obj.sfixed64 != null) { 184 | w.uint32(97) 185 | w.sfixed64(obj.sfixed64) 186 | } 187 | 188 | if (obj.bool != null) { 189 | w.uint32(104) 190 | w.bool(obj.bool) 191 | } 192 | 193 | if (obj.string != null) { 194 | w.uint32(114) 195 | w.string(obj.string) 196 | } 197 | 198 | if (obj.bytes != null) { 199 | w.uint32(122) 200 | w.bytes(obj.bytes) 201 | } 202 | 203 | if (obj.enum != null) { 204 | w.uint32(128) 205 | OptionalEnum.codec().encode(obj.enum, w) 206 | } 207 | 208 | if (obj.subMessage != null) { 209 | w.uint32(138) 210 | OptionalSubMessage.codec().encode(obj.subMessage, w) 211 | } 212 | 213 | if (opts.lengthDelimited !== false) { 214 | w.ldelim() 215 | } 216 | }, (reader, length, opts = {}) => { 217 | const obj: any = {} 218 | 219 | const end = length == null ? reader.len : reader.pos + length 220 | 221 | while (reader.pos < end) { 222 | const tag = reader.uint32() 223 | 224 | switch (tag >>> 3) { 225 | case 1: { 226 | obj.double = reader.double() 227 | break 228 | } 229 | case 2: { 230 | obj.float = reader.float() 231 | break 232 | } 233 | case 3: { 234 | obj.int32 = reader.int32() 235 | break 236 | } 237 | case 4: { 238 | obj.int64 = reader.int64() 239 | break 240 | } 241 | case 5: { 242 | obj.uint32 = reader.uint32() 243 | break 244 | } 245 | case 6: { 246 | obj.uint64 = reader.uint64() 247 | break 248 | } 249 | case 7: { 250 | obj.sint32 = reader.sint32() 251 | break 252 | } 253 | case 8: { 254 | obj.sint64 = reader.sint64() 255 | break 256 | } 257 | case 9: { 258 | obj.fixed32 = reader.fixed32() 259 | break 260 | } 261 | case 10: { 262 | obj.fixed64 = reader.fixed64() 263 | break 264 | } 265 | case 11: { 266 | obj.sfixed32 = reader.sfixed32() 267 | break 268 | } 269 | case 12: { 270 | obj.sfixed64 = reader.sfixed64() 271 | break 272 | } 273 | case 13: { 274 | obj.bool = reader.bool() 275 | break 276 | } 277 | case 14: { 278 | obj.string = reader.string() 279 | break 280 | } 281 | case 15: { 282 | obj.bytes = reader.bytes() 283 | break 284 | } 285 | case 16: { 286 | obj.enum = OptionalEnum.codec().decode(reader) 287 | break 288 | } 289 | case 17: { 290 | obj.subMessage = OptionalSubMessage.codec().decode(reader, reader.uint32(), { 291 | limits: opts.limits?.subMessage 292 | }) 293 | break 294 | } 295 | default: { 296 | reader.skipType(tag & 7) 297 | break 298 | } 299 | } 300 | } 301 | 302 | return obj 303 | }) 304 | } 305 | 306 | return _codec 307 | } 308 | 309 | export const encode = (obj: Partial): Uint8Array => { 310 | return encodeMessage(obj, Optional.codec()) 311 | } 312 | 313 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): Optional => { 314 | return decodeMessage(buf, Optional.codec(), opts) 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/peer.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message Peer { 4 | // Multiaddrs we know about 5 | repeated Address addresses = 1; 6 | 7 | // The protocols the peer supports 8 | repeated string protocols = 2; 9 | 10 | // Any peer metadata 11 | repeated Metadata metadata = 3; 12 | 13 | // The public key of the peer 14 | optional bytes pub_key = 4; 15 | 16 | // The most recently received signed PeerRecord 17 | optional bytes peer_record_envelope = 5; 18 | } 19 | 20 | // Address represents a single multiaddr 21 | message Address { 22 | bytes multiaddr = 1; 23 | 24 | // Flag to indicate if the address comes from a certified source 25 | optional bool isCertified = 2; 26 | } 27 | 28 | message Metadata { 29 | string key = 1; 30 | bytes value = 2; 31 | } 32 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/peer.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/export */ 2 | /* eslint-disable complexity */ 3 | /* eslint-disable @typescript-eslint/no-namespace */ 4 | /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */ 5 | /* eslint-disable @typescript-eslint/no-empty-interface */ 6 | /* eslint-disable import/consistent-type-specifier-style */ 7 | /* eslint-disable @typescript-eslint/no-unused-vars */ 8 | 9 | import { decodeMessage, encodeMessage, MaxLengthError, message } from 'protons-runtime' 10 | import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc' 11 | import type { Codec, DecodeOptions } from 'protons-runtime' 12 | import type { Uint8ArrayList } from 'uint8arraylist' 13 | 14 | export interface Peer { 15 | addresses: Address[] 16 | protocols: string[] 17 | metadata: Metadata[] 18 | pubKey?: Uint8Array 19 | peerRecordEnvelope?: Uint8Array 20 | } 21 | 22 | export namespace Peer { 23 | let _codec: Codec 24 | 25 | export const codec = (): Codec => { 26 | if (_codec == null) { 27 | _codec = message((obj, w, opts = {}) => { 28 | if (opts.lengthDelimited !== false) { 29 | w.fork() 30 | } 31 | 32 | if (obj.addresses != null) { 33 | for (const value of obj.addresses) { 34 | w.uint32(10) 35 | Address.codec().encode(value, w) 36 | } 37 | } 38 | 39 | if (obj.protocols != null) { 40 | for (const value of obj.protocols) { 41 | w.uint32(18) 42 | w.string(value) 43 | } 44 | } 45 | 46 | if (obj.metadata != null) { 47 | for (const value of obj.metadata) { 48 | w.uint32(26) 49 | Metadata.codec().encode(value, w) 50 | } 51 | } 52 | 53 | if (obj.pubKey != null) { 54 | w.uint32(34) 55 | w.bytes(obj.pubKey) 56 | } 57 | 58 | if (obj.peerRecordEnvelope != null) { 59 | w.uint32(42) 60 | w.bytes(obj.peerRecordEnvelope) 61 | } 62 | 63 | if (opts.lengthDelimited !== false) { 64 | w.ldelim() 65 | } 66 | }, (reader, length, opts = {}) => { 67 | const obj: any = { 68 | addresses: [], 69 | protocols: [], 70 | metadata: [] 71 | } 72 | 73 | const end = length == null ? reader.len : reader.pos + length 74 | 75 | while (reader.pos < end) { 76 | const tag = reader.uint32() 77 | 78 | switch (tag >>> 3) { 79 | case 1: { 80 | if (opts.limits?.addresses != null && obj.addresses.length === opts.limits.addresses) { 81 | throw new MaxLengthError('Decode error - map field "addresses" had too many elements') 82 | } 83 | 84 | obj.addresses.push(Address.codec().decode(reader, reader.uint32(), { 85 | limits: opts.limits?.addresses$ 86 | })) 87 | break 88 | } 89 | case 2: { 90 | if (opts.limits?.protocols != null && obj.protocols.length === opts.limits.protocols) { 91 | throw new MaxLengthError('Decode error - map field "protocols" had too many elements') 92 | } 93 | 94 | obj.protocols.push(reader.string()) 95 | break 96 | } 97 | case 3: { 98 | if (opts.limits?.metadata != null && obj.metadata.length === opts.limits.metadata) { 99 | throw new MaxLengthError('Decode error - map field "metadata" had too many elements') 100 | } 101 | 102 | obj.metadata.push(Metadata.codec().decode(reader, reader.uint32(), { 103 | limits: opts.limits?.metadata$ 104 | })) 105 | break 106 | } 107 | case 4: { 108 | obj.pubKey = reader.bytes() 109 | break 110 | } 111 | case 5: { 112 | obj.peerRecordEnvelope = reader.bytes() 113 | break 114 | } 115 | default: { 116 | reader.skipType(tag & 7) 117 | break 118 | } 119 | } 120 | } 121 | 122 | return obj 123 | }) 124 | } 125 | 126 | return _codec 127 | } 128 | 129 | export const encode = (obj: Partial): Uint8Array => { 130 | return encodeMessage(obj, Peer.codec()) 131 | } 132 | 133 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): Peer => { 134 | return decodeMessage(buf, Peer.codec(), opts) 135 | } 136 | } 137 | 138 | export interface Address { 139 | multiaddr: Uint8Array 140 | isCertified?: boolean 141 | } 142 | 143 | export namespace Address { 144 | let _codec: Codec
145 | 146 | export const codec = (): Codec
=> { 147 | if (_codec == null) { 148 | _codec = message
((obj, w, opts = {}) => { 149 | if (opts.lengthDelimited !== false) { 150 | w.fork() 151 | } 152 | 153 | if ((obj.multiaddr != null && obj.multiaddr.byteLength > 0)) { 154 | w.uint32(10) 155 | w.bytes(obj.multiaddr) 156 | } 157 | 158 | if (obj.isCertified != null) { 159 | w.uint32(16) 160 | w.bool(obj.isCertified) 161 | } 162 | 163 | if (opts.lengthDelimited !== false) { 164 | w.ldelim() 165 | } 166 | }, (reader, length, opts = {}) => { 167 | const obj: any = { 168 | multiaddr: uint8ArrayAlloc(0) 169 | } 170 | 171 | const end = length == null ? reader.len : reader.pos + length 172 | 173 | while (reader.pos < end) { 174 | const tag = reader.uint32() 175 | 176 | switch (tag >>> 3) { 177 | case 1: { 178 | obj.multiaddr = reader.bytes() 179 | break 180 | } 181 | case 2: { 182 | obj.isCertified = reader.bool() 183 | break 184 | } 185 | default: { 186 | reader.skipType(tag & 7) 187 | break 188 | } 189 | } 190 | } 191 | 192 | return obj 193 | }) 194 | } 195 | 196 | return _codec 197 | } 198 | 199 | export const encode = (obj: Partial
): Uint8Array => { 200 | return encodeMessage(obj, Address.codec()) 201 | } 202 | 203 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions
): Address => { 204 | return decodeMessage(buf, Address.codec(), opts) 205 | } 206 | } 207 | 208 | export interface Metadata { 209 | key: string 210 | value: Uint8Array 211 | } 212 | 213 | export namespace Metadata { 214 | let _codec: Codec 215 | 216 | export const codec = (): Codec => { 217 | if (_codec == null) { 218 | _codec = message((obj, w, opts = {}) => { 219 | if (opts.lengthDelimited !== false) { 220 | w.fork() 221 | } 222 | 223 | if ((obj.key != null && obj.key !== '')) { 224 | w.uint32(10) 225 | w.string(obj.key) 226 | } 227 | 228 | if ((obj.value != null && obj.value.byteLength > 0)) { 229 | w.uint32(18) 230 | w.bytes(obj.value) 231 | } 232 | 233 | if (opts.lengthDelimited !== false) { 234 | w.ldelim() 235 | } 236 | }, (reader, length, opts = {}) => { 237 | const obj: any = { 238 | key: '', 239 | value: uint8ArrayAlloc(0) 240 | } 241 | 242 | const end = length == null ? reader.len : reader.pos + length 243 | 244 | while (reader.pos < end) { 245 | const tag = reader.uint32() 246 | 247 | switch (tag >>> 3) { 248 | case 1: { 249 | obj.key = reader.string() 250 | break 251 | } 252 | case 2: { 253 | obj.value = reader.bytes() 254 | break 255 | } 256 | default: { 257 | reader.skipType(tag & 7) 258 | break 259 | } 260 | } 261 | } 262 | 263 | return obj 264 | }) 265 | } 266 | 267 | return _codec 268 | } 269 | 270 | export const encode = (obj: Partial): Uint8Array => { 271 | return encodeMessage(obj, Metadata.codec()) 272 | } 273 | 274 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): Metadata => { 275 | return decodeMessage(buf, Metadata.codec(), opts) 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/proto2.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | message MessageWithRequired { 4 | required int32 scalarField = 1; 5 | } 6 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/proto2.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/export */ 2 | /* eslint-disable complexity */ 3 | /* eslint-disable @typescript-eslint/no-namespace */ 4 | /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */ 5 | /* eslint-disable @typescript-eslint/no-empty-interface */ 6 | /* eslint-disable import/consistent-type-specifier-style */ 7 | /* eslint-disable @typescript-eslint/no-unused-vars */ 8 | 9 | import { decodeMessage, encodeMessage, message } from 'protons-runtime' 10 | import type { Codec, DecodeOptions } from 'protons-runtime' 11 | import type { Uint8ArrayList } from 'uint8arraylist' 12 | 13 | export interface MessageWithRequired { 14 | scalarField: number 15 | } 16 | 17 | export namespace MessageWithRequired { 18 | let _codec: Codec 19 | 20 | export const codec = (): Codec => { 21 | if (_codec == null) { 22 | _codec = message((obj, w, opts = {}) => { 23 | if (opts.lengthDelimited !== false) { 24 | w.fork() 25 | } 26 | 27 | if (obj.scalarField != null) { 28 | w.uint32(8) 29 | w.int32(obj.scalarField) 30 | } 31 | 32 | if (opts.lengthDelimited !== false) { 33 | w.ldelim() 34 | } 35 | }, (reader, length, opts = {}) => { 36 | const obj: any = { 37 | scalarField: 0 38 | } 39 | 40 | const end = length == null ? reader.len : reader.pos + length 41 | 42 | while (reader.pos < end) { 43 | const tag = reader.uint32() 44 | 45 | switch (tag >>> 3) { 46 | case 1: { 47 | obj.scalarField = reader.int32() 48 | break 49 | } 50 | default: { 51 | reader.skipType(tag & 7) 52 | break 53 | } 54 | } 55 | } 56 | 57 | return obj 58 | }) 59 | } 60 | 61 | return _codec 62 | } 63 | 64 | export const encode = (obj: Partial): Uint8Array => { 65 | return encodeMessage(obj, MessageWithRequired.codec()) 66 | } 67 | 68 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): MessageWithRequired => { 69 | return decodeMessage(buf, MessageWithRequired.codec(), opts) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/protons-options.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message MessageWithSizeLimitedRepeatedField { 4 | repeated string repeatedField = 1 [(protons.options).limit = 1]; 5 | } 6 | 7 | message MessageWithSizeLimitedMap { 8 | map mapField = 1 [(protons.options).limit = 1]; 9 | } 10 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/protons-options.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/export */ 2 | /* eslint-disable complexity */ 3 | /* eslint-disable @typescript-eslint/no-namespace */ 4 | /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */ 5 | /* eslint-disable @typescript-eslint/no-empty-interface */ 6 | /* eslint-disable import/consistent-type-specifier-style */ 7 | /* eslint-disable @typescript-eslint/no-unused-vars */ 8 | 9 | import { decodeMessage, encodeMessage, MaxLengthError, MaxSizeError, message } from 'protons-runtime' 10 | import type { Codec, DecodeOptions } from 'protons-runtime' 11 | import type { Uint8ArrayList } from 'uint8arraylist' 12 | 13 | export interface MessageWithSizeLimitedRepeatedField { 14 | repeatedField: string[] 15 | } 16 | 17 | export namespace MessageWithSizeLimitedRepeatedField { 18 | let _codec: Codec 19 | 20 | export const codec = (): Codec => { 21 | if (_codec == null) { 22 | _codec = message((obj, w, opts = {}) => { 23 | if (opts.lengthDelimited !== false) { 24 | w.fork() 25 | } 26 | 27 | if (obj.repeatedField != null) { 28 | for (const value of obj.repeatedField) { 29 | w.uint32(10) 30 | w.string(value) 31 | } 32 | } 33 | 34 | if (opts.lengthDelimited !== false) { 35 | w.ldelim() 36 | } 37 | }, (reader, length, opts = {}) => { 38 | const obj: any = { 39 | repeatedField: [] 40 | } 41 | 42 | const end = length == null ? reader.len : reader.pos + length 43 | 44 | while (reader.pos < end) { 45 | const tag = reader.uint32() 46 | 47 | switch (tag >>> 3) { 48 | case 1: { 49 | if (opts.limits?.repeatedField != null && obj.repeatedField.length === opts.limits.repeatedField) { 50 | throw new MaxLengthError('Decode error - map field "repeatedField" had too many elements') 51 | } 52 | 53 | if (obj.repeatedField.length === 1) { 54 | throw new MaxLengthError('Decode error - repeated field "repeatedField" had too many elements') 55 | } 56 | 57 | obj.repeatedField.push(reader.string()) 58 | break 59 | } 60 | default: { 61 | reader.skipType(tag & 7) 62 | break 63 | } 64 | } 65 | } 66 | 67 | return obj 68 | }) 69 | } 70 | 71 | return _codec 72 | } 73 | 74 | export const encode = (obj: Partial): Uint8Array => { 75 | return encodeMessage(obj, MessageWithSizeLimitedRepeatedField.codec()) 76 | } 77 | 78 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): MessageWithSizeLimitedRepeatedField => { 79 | return decodeMessage(buf, MessageWithSizeLimitedRepeatedField.codec(), opts) 80 | } 81 | } 82 | 83 | export interface MessageWithSizeLimitedMap { 84 | mapField: Map 85 | } 86 | 87 | export namespace MessageWithSizeLimitedMap { 88 | export interface MessageWithSizeLimitedMap$mapFieldEntry { 89 | key: string 90 | value: string 91 | } 92 | 93 | export namespace MessageWithSizeLimitedMap$mapFieldEntry { 94 | let _codec: Codec 95 | 96 | export const codec = (): Codec => { 97 | if (_codec == null) { 98 | _codec = message((obj, w, opts = {}) => { 99 | if (opts.lengthDelimited !== false) { 100 | w.fork() 101 | } 102 | 103 | if ((obj.key != null && obj.key !== '')) { 104 | w.uint32(10) 105 | w.string(obj.key) 106 | } 107 | 108 | if ((obj.value != null && obj.value !== '')) { 109 | w.uint32(18) 110 | w.string(obj.value) 111 | } 112 | 113 | if (opts.lengthDelimited !== false) { 114 | w.ldelim() 115 | } 116 | }, (reader, length, opts = {}) => { 117 | const obj: any = { 118 | key: '', 119 | value: '' 120 | } 121 | 122 | const end = length == null ? reader.len : reader.pos + length 123 | 124 | while (reader.pos < end) { 125 | const tag = reader.uint32() 126 | 127 | switch (tag >>> 3) { 128 | case 1: { 129 | obj.key = reader.string() 130 | break 131 | } 132 | case 2: { 133 | obj.value = reader.string() 134 | break 135 | } 136 | default: { 137 | reader.skipType(tag & 7) 138 | break 139 | } 140 | } 141 | } 142 | 143 | return obj 144 | }) 145 | } 146 | 147 | return _codec 148 | } 149 | 150 | export const encode = (obj: Partial): Uint8Array => { 151 | return encodeMessage(obj, MessageWithSizeLimitedMap$mapFieldEntry.codec()) 152 | } 153 | 154 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): MessageWithSizeLimitedMap$mapFieldEntry => { 155 | return decodeMessage(buf, MessageWithSizeLimitedMap$mapFieldEntry.codec(), opts) 156 | } 157 | } 158 | 159 | let _codec: Codec 160 | 161 | export const codec = (): Codec => { 162 | if (_codec == null) { 163 | _codec = message((obj, w, opts = {}) => { 164 | if (opts.lengthDelimited !== false) { 165 | w.fork() 166 | } 167 | 168 | if (obj.mapField != null && obj.mapField.size !== 0) { 169 | for (const [key, value] of obj.mapField.entries()) { 170 | w.uint32(10) 171 | MessageWithSizeLimitedMap.MessageWithSizeLimitedMap$mapFieldEntry.codec().encode({ key, value }, w) 172 | } 173 | } 174 | 175 | if (opts.lengthDelimited !== false) { 176 | w.ldelim() 177 | } 178 | }, (reader, length, opts = {}) => { 179 | const obj: any = { 180 | mapField: new Map() 181 | } 182 | 183 | const end = length == null ? reader.len : reader.pos + length 184 | 185 | while (reader.pos < end) { 186 | const tag = reader.uint32() 187 | 188 | switch (tag >>> 3) { 189 | case 1: { 190 | if (opts.limits?.mapField != null && obj.mapField.size === opts.limits.mapField) { 191 | throw new MaxSizeError('Decode error - map field "mapField" had too many elements') 192 | } 193 | 194 | if (obj.mapField.size === 1) { 195 | throw new MaxSizeError('Decode error - map field "mapField" had too many elements') 196 | } 197 | 198 | const entry = MessageWithSizeLimitedMap.MessageWithSizeLimitedMap$mapFieldEntry.codec().decode(reader, reader.uint32()) 199 | obj.mapField.set(entry.key, entry.value) 200 | break 201 | } 202 | default: { 203 | reader.skipType(tag & 7) 204 | break 205 | } 206 | } 207 | } 208 | 209 | return obj 210 | }) 211 | } 212 | 213 | return _codec 214 | } 215 | 216 | export const encode = (obj: Partial): Uint8Array => { 217 | return encodeMessage(obj, MessageWithSizeLimitedMap.codec()) 218 | } 219 | 220 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): MessageWithSizeLimitedMap => { 221 | return decodeMessage(buf, MessageWithSizeLimitedMap.codec(), opts) 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/repeated.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message SubSubMessage { 4 | repeated string foo = 1; 5 | optional uint32 nonRepeating = 2; 6 | } 7 | 8 | message SubMessage { 9 | repeated string foo = 1; 10 | optional uint32 nonRepeating = 2; 11 | optional SubSubMessage message = 3; 12 | repeated SubSubMessage messages = 4; 13 | } 14 | 15 | message RepeatedTypes { 16 | repeated uint32 number = 1; 17 | repeated uint32 limitedNumber = 2 [(protons.options).limit = 1]; 18 | repeated SubMessage messages = 3; 19 | optional SubMessage message = 4; 20 | optional uint32 nonRepeating = 5; 21 | } 22 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/repeated.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/export */ 2 | /* eslint-disable complexity */ 3 | /* eslint-disable @typescript-eslint/no-namespace */ 4 | /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */ 5 | /* eslint-disable @typescript-eslint/no-empty-interface */ 6 | /* eslint-disable import/consistent-type-specifier-style */ 7 | /* eslint-disable @typescript-eslint/no-unused-vars */ 8 | 9 | import { decodeMessage, encodeMessage, MaxLengthError, message } from 'protons-runtime' 10 | import type { Codec, DecodeOptions } from 'protons-runtime' 11 | import type { Uint8ArrayList } from 'uint8arraylist' 12 | 13 | export interface SubSubMessage { 14 | foo: string[] 15 | nonRepeating?: number 16 | } 17 | 18 | export namespace SubSubMessage { 19 | let _codec: Codec 20 | 21 | export const codec = (): Codec => { 22 | if (_codec == null) { 23 | _codec = message((obj, w, opts = {}) => { 24 | if (opts.lengthDelimited !== false) { 25 | w.fork() 26 | } 27 | 28 | if (obj.foo != null) { 29 | for (const value of obj.foo) { 30 | w.uint32(10) 31 | w.string(value) 32 | } 33 | } 34 | 35 | if (obj.nonRepeating != null) { 36 | w.uint32(16) 37 | w.uint32(obj.nonRepeating) 38 | } 39 | 40 | if (opts.lengthDelimited !== false) { 41 | w.ldelim() 42 | } 43 | }, (reader, length, opts = {}) => { 44 | const obj: any = { 45 | foo: [] 46 | } 47 | 48 | const end = length == null ? reader.len : reader.pos + length 49 | 50 | while (reader.pos < end) { 51 | const tag = reader.uint32() 52 | 53 | switch (tag >>> 3) { 54 | case 1: { 55 | if (opts.limits?.foo != null && obj.foo.length === opts.limits.foo) { 56 | throw new MaxLengthError('Decode error - map field "foo" had too many elements') 57 | } 58 | 59 | obj.foo.push(reader.string()) 60 | break 61 | } 62 | case 2: { 63 | obj.nonRepeating = reader.uint32() 64 | break 65 | } 66 | default: { 67 | reader.skipType(tag & 7) 68 | break 69 | } 70 | } 71 | } 72 | 73 | return obj 74 | }) 75 | } 76 | 77 | return _codec 78 | } 79 | 80 | export const encode = (obj: Partial): Uint8Array => { 81 | return encodeMessage(obj, SubSubMessage.codec()) 82 | } 83 | 84 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): SubSubMessage => { 85 | return decodeMessage(buf, SubSubMessage.codec(), opts) 86 | } 87 | } 88 | 89 | export interface SubMessage { 90 | foo: string[] 91 | nonRepeating?: number 92 | message?: SubSubMessage 93 | messages: SubSubMessage[] 94 | } 95 | 96 | export namespace SubMessage { 97 | let _codec: Codec 98 | 99 | export const codec = (): Codec => { 100 | if (_codec == null) { 101 | _codec = message((obj, w, opts = {}) => { 102 | if (opts.lengthDelimited !== false) { 103 | w.fork() 104 | } 105 | 106 | if (obj.foo != null) { 107 | for (const value of obj.foo) { 108 | w.uint32(10) 109 | w.string(value) 110 | } 111 | } 112 | 113 | if (obj.nonRepeating != null) { 114 | w.uint32(16) 115 | w.uint32(obj.nonRepeating) 116 | } 117 | 118 | if (obj.message != null) { 119 | w.uint32(26) 120 | SubSubMessage.codec().encode(obj.message, w) 121 | } 122 | 123 | if (obj.messages != null) { 124 | for (const value of obj.messages) { 125 | w.uint32(34) 126 | SubSubMessage.codec().encode(value, w) 127 | } 128 | } 129 | 130 | if (opts.lengthDelimited !== false) { 131 | w.ldelim() 132 | } 133 | }, (reader, length, opts = {}) => { 134 | const obj: any = { 135 | foo: [], 136 | messages: [] 137 | } 138 | 139 | const end = length == null ? reader.len : reader.pos + length 140 | 141 | while (reader.pos < end) { 142 | const tag = reader.uint32() 143 | 144 | switch (tag >>> 3) { 145 | case 1: { 146 | if (opts.limits?.foo != null && obj.foo.length === opts.limits.foo) { 147 | throw new MaxLengthError('Decode error - map field "foo" had too many elements') 148 | } 149 | 150 | obj.foo.push(reader.string()) 151 | break 152 | } 153 | case 2: { 154 | obj.nonRepeating = reader.uint32() 155 | break 156 | } 157 | case 3: { 158 | obj.message = SubSubMessage.codec().decode(reader, reader.uint32(), { 159 | limits: opts.limits?.message 160 | }) 161 | break 162 | } 163 | case 4: { 164 | if (opts.limits?.messages != null && obj.messages.length === opts.limits.messages) { 165 | throw new MaxLengthError('Decode error - map field "messages" had too many elements') 166 | } 167 | 168 | obj.messages.push(SubSubMessage.codec().decode(reader, reader.uint32(), { 169 | limits: opts.limits?.messages$ 170 | })) 171 | break 172 | } 173 | default: { 174 | reader.skipType(tag & 7) 175 | break 176 | } 177 | } 178 | } 179 | 180 | return obj 181 | }) 182 | } 183 | 184 | return _codec 185 | } 186 | 187 | export const encode = (obj: Partial): Uint8Array => { 188 | return encodeMessage(obj, SubMessage.codec()) 189 | } 190 | 191 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): SubMessage => { 192 | return decodeMessage(buf, SubMessage.codec(), opts) 193 | } 194 | } 195 | 196 | export interface RepeatedTypes { 197 | number: number[] 198 | limitedNumber: number[] 199 | messages: SubMessage[] 200 | message?: SubMessage 201 | nonRepeating?: number 202 | } 203 | 204 | export namespace RepeatedTypes { 205 | let _codec: Codec 206 | 207 | export const codec = (): Codec => { 208 | if (_codec == null) { 209 | _codec = message((obj, w, opts = {}) => { 210 | if (opts.lengthDelimited !== false) { 211 | w.fork() 212 | } 213 | 214 | if (obj.number != null) { 215 | for (const value of obj.number) { 216 | w.uint32(8) 217 | w.uint32(value) 218 | } 219 | } 220 | 221 | if (obj.limitedNumber != null) { 222 | for (const value of obj.limitedNumber) { 223 | w.uint32(16) 224 | w.uint32(value) 225 | } 226 | } 227 | 228 | if (obj.messages != null) { 229 | for (const value of obj.messages) { 230 | w.uint32(26) 231 | SubMessage.codec().encode(value, w) 232 | } 233 | } 234 | 235 | if (obj.message != null) { 236 | w.uint32(34) 237 | SubMessage.codec().encode(obj.message, w) 238 | } 239 | 240 | if (obj.nonRepeating != null) { 241 | w.uint32(40) 242 | w.uint32(obj.nonRepeating) 243 | } 244 | 245 | if (opts.lengthDelimited !== false) { 246 | w.ldelim() 247 | } 248 | }, (reader, length, opts = {}) => { 249 | const obj: any = { 250 | number: [], 251 | limitedNumber: [], 252 | messages: [] 253 | } 254 | 255 | const end = length == null ? reader.len : reader.pos + length 256 | 257 | while (reader.pos < end) { 258 | const tag = reader.uint32() 259 | 260 | switch (tag >>> 3) { 261 | case 1: { 262 | if (opts.limits?.number != null && obj.number.length === opts.limits.number) { 263 | throw new MaxLengthError('Decode error - map field "number" had too many elements') 264 | } 265 | 266 | obj.number.push(reader.uint32()) 267 | break 268 | } 269 | case 2: { 270 | if (opts.limits?.limitedNumber != null && obj.limitedNumber.length === opts.limits.limitedNumber) { 271 | throw new MaxLengthError('Decode error - map field "limitedNumber" had too many elements') 272 | } 273 | 274 | if (obj.limitedNumber.length === 1) { 275 | throw new MaxLengthError('Decode error - repeated field "limitedNumber" had too many elements') 276 | } 277 | 278 | obj.limitedNumber.push(reader.uint32()) 279 | break 280 | } 281 | case 3: { 282 | if (opts.limits?.messages != null && obj.messages.length === opts.limits.messages) { 283 | throw new MaxLengthError('Decode error - map field "messages" had too many elements') 284 | } 285 | 286 | obj.messages.push(SubMessage.codec().decode(reader, reader.uint32(), { 287 | limits: opts.limits?.messages$ 288 | })) 289 | break 290 | } 291 | case 4: { 292 | obj.message = SubMessage.codec().decode(reader, reader.uint32(), { 293 | limits: opts.limits?.message 294 | }) 295 | break 296 | } 297 | case 5: { 298 | obj.nonRepeating = reader.uint32() 299 | break 300 | } 301 | default: { 302 | reader.skipType(tag & 7) 303 | break 304 | } 305 | } 306 | } 307 | 308 | return obj 309 | }) 310 | } 311 | 312 | return _codec 313 | } 314 | 315 | export const encode = (obj: Partial): Uint8Array => { 316 | return encodeMessage(obj, RepeatedTypes.codec()) 317 | } 318 | 319 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): RepeatedTypes => { 320 | return decodeMessage(buf, RepeatedTypes.codec(), opts) 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/singular.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | enum SingularEnum { 4 | NO_VALUE = 0; 5 | VALUE_1 = 1; 6 | VALUE_2 = 2; 7 | } 8 | 9 | message SingularSubMessage { 10 | string foo = 1; 11 | int32 bar = 2; 12 | } 13 | 14 | message Singular { 15 | double double = 1; 16 | float float = 2; 17 | int32 int32 = 3; 18 | int64 int64 = 4; 19 | uint32 uint32 = 5; 20 | uint64 uint64 = 6; 21 | sint32 sint32 = 7; 22 | sint64 sint64 = 8; 23 | fixed32 fixed32 = 9; 24 | fixed64 fixed64 = 10; 25 | sfixed32 sfixed32 = 11; 26 | sfixed64 sfixed64 = 12; 27 | bool bool = 13; 28 | string string = 14; 29 | bytes bytes = 15; 30 | SingularEnum enum = 16; 31 | SingularSubMessage subMessage = 17; 32 | } 33 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/singular.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/export */ 2 | /* eslint-disable complexity */ 3 | /* eslint-disable @typescript-eslint/no-namespace */ 4 | /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */ 5 | /* eslint-disable @typescript-eslint/no-empty-interface */ 6 | /* eslint-disable import/consistent-type-specifier-style */ 7 | /* eslint-disable @typescript-eslint/no-unused-vars */ 8 | 9 | import { decodeMessage, encodeMessage, enumeration, message } from 'protons-runtime' 10 | import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc' 11 | import type { Codec, DecodeOptions } from 'protons-runtime' 12 | import type { Uint8ArrayList } from 'uint8arraylist' 13 | 14 | export enum SingularEnum { 15 | NO_VALUE = 'NO_VALUE', 16 | VALUE_1 = 'VALUE_1', 17 | VALUE_2 = 'VALUE_2' 18 | } 19 | 20 | enum __SingularEnumValues { 21 | NO_VALUE = 0, 22 | VALUE_1 = 1, 23 | VALUE_2 = 2 24 | } 25 | 26 | export namespace SingularEnum { 27 | export const codec = (): Codec => { 28 | return enumeration(__SingularEnumValues) 29 | } 30 | } 31 | export interface SingularSubMessage { 32 | foo: string 33 | bar: number 34 | } 35 | 36 | export namespace SingularSubMessage { 37 | let _codec: Codec 38 | 39 | export const codec = (): Codec => { 40 | if (_codec == null) { 41 | _codec = message((obj, w, opts = {}) => { 42 | if (opts.lengthDelimited !== false) { 43 | w.fork() 44 | } 45 | 46 | if ((obj.foo != null && obj.foo !== '')) { 47 | w.uint32(10) 48 | w.string(obj.foo) 49 | } 50 | 51 | if ((obj.bar != null && obj.bar !== 0)) { 52 | w.uint32(16) 53 | w.int32(obj.bar) 54 | } 55 | 56 | if (opts.lengthDelimited !== false) { 57 | w.ldelim() 58 | } 59 | }, (reader, length, opts = {}) => { 60 | const obj: any = { 61 | foo: '', 62 | bar: 0 63 | } 64 | 65 | const end = length == null ? reader.len : reader.pos + length 66 | 67 | while (reader.pos < end) { 68 | const tag = reader.uint32() 69 | 70 | switch (tag >>> 3) { 71 | case 1: { 72 | obj.foo = reader.string() 73 | break 74 | } 75 | case 2: { 76 | obj.bar = reader.int32() 77 | break 78 | } 79 | default: { 80 | reader.skipType(tag & 7) 81 | break 82 | } 83 | } 84 | } 85 | 86 | return obj 87 | }) 88 | } 89 | 90 | return _codec 91 | } 92 | 93 | export const encode = (obj: Partial): Uint8Array => { 94 | return encodeMessage(obj, SingularSubMessage.codec()) 95 | } 96 | 97 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): SingularSubMessage => { 98 | return decodeMessage(buf, SingularSubMessage.codec(), opts) 99 | } 100 | } 101 | 102 | export interface Singular { 103 | double: number 104 | float: number 105 | int32: number 106 | int64: bigint 107 | uint32: number 108 | uint64: bigint 109 | sint32: number 110 | sint64: bigint 111 | fixed32: number 112 | fixed64: bigint 113 | sfixed32: number 114 | sfixed64: bigint 115 | bool: boolean 116 | string: string 117 | bytes: Uint8Array 118 | enum: SingularEnum 119 | subMessage?: SingularSubMessage 120 | } 121 | 122 | export namespace Singular { 123 | let _codec: Codec 124 | 125 | export const codec = (): Codec => { 126 | if (_codec == null) { 127 | _codec = message((obj, w, opts = {}) => { 128 | if (opts.lengthDelimited !== false) { 129 | w.fork() 130 | } 131 | 132 | if ((obj.double != null && obj.double !== 0)) { 133 | w.uint32(9) 134 | w.double(obj.double) 135 | } 136 | 137 | if ((obj.float != null && obj.float !== 0)) { 138 | w.uint32(21) 139 | w.float(obj.float) 140 | } 141 | 142 | if ((obj.int32 != null && obj.int32 !== 0)) { 143 | w.uint32(24) 144 | w.int32(obj.int32) 145 | } 146 | 147 | if ((obj.int64 != null && obj.int64 !== 0n)) { 148 | w.uint32(32) 149 | w.int64(obj.int64) 150 | } 151 | 152 | if ((obj.uint32 != null && obj.uint32 !== 0)) { 153 | w.uint32(40) 154 | w.uint32(obj.uint32) 155 | } 156 | 157 | if ((obj.uint64 != null && obj.uint64 !== 0n)) { 158 | w.uint32(48) 159 | w.uint64(obj.uint64) 160 | } 161 | 162 | if ((obj.sint32 != null && obj.sint32 !== 0)) { 163 | w.uint32(56) 164 | w.sint32(obj.sint32) 165 | } 166 | 167 | if ((obj.sint64 != null && obj.sint64 !== 0n)) { 168 | w.uint32(64) 169 | w.sint64(obj.sint64) 170 | } 171 | 172 | if ((obj.fixed32 != null && obj.fixed32 !== 0)) { 173 | w.uint32(77) 174 | w.fixed32(obj.fixed32) 175 | } 176 | 177 | if ((obj.fixed64 != null && obj.fixed64 !== 0n)) { 178 | w.uint32(81) 179 | w.fixed64(obj.fixed64) 180 | } 181 | 182 | if ((obj.sfixed32 != null && obj.sfixed32 !== 0)) { 183 | w.uint32(93) 184 | w.sfixed32(obj.sfixed32) 185 | } 186 | 187 | if ((obj.sfixed64 != null && obj.sfixed64 !== 0n)) { 188 | w.uint32(97) 189 | w.sfixed64(obj.sfixed64) 190 | } 191 | 192 | if ((obj.bool != null && obj.bool !== false)) { 193 | w.uint32(104) 194 | w.bool(obj.bool) 195 | } 196 | 197 | if ((obj.string != null && obj.string !== '')) { 198 | w.uint32(114) 199 | w.string(obj.string) 200 | } 201 | 202 | if ((obj.bytes != null && obj.bytes.byteLength > 0)) { 203 | w.uint32(122) 204 | w.bytes(obj.bytes) 205 | } 206 | 207 | if (obj.enum != null && __SingularEnumValues[obj.enum] !== 0) { 208 | w.uint32(128) 209 | SingularEnum.codec().encode(obj.enum, w) 210 | } 211 | 212 | if (obj.subMessage != null) { 213 | w.uint32(138) 214 | SingularSubMessage.codec().encode(obj.subMessage, w) 215 | } 216 | 217 | if (opts.lengthDelimited !== false) { 218 | w.ldelim() 219 | } 220 | }, (reader, length, opts = {}) => { 221 | const obj: any = { 222 | double: 0, 223 | float: 0, 224 | int32: 0, 225 | int64: 0n, 226 | uint32: 0, 227 | uint64: 0n, 228 | sint32: 0, 229 | sint64: 0n, 230 | fixed32: 0, 231 | fixed64: 0n, 232 | sfixed32: 0, 233 | sfixed64: 0n, 234 | bool: false, 235 | string: '', 236 | bytes: uint8ArrayAlloc(0), 237 | enum: SingularEnum.NO_VALUE 238 | } 239 | 240 | const end = length == null ? reader.len : reader.pos + length 241 | 242 | while (reader.pos < end) { 243 | const tag = reader.uint32() 244 | 245 | switch (tag >>> 3) { 246 | case 1: { 247 | obj.double = reader.double() 248 | break 249 | } 250 | case 2: { 251 | obj.float = reader.float() 252 | break 253 | } 254 | case 3: { 255 | obj.int32 = reader.int32() 256 | break 257 | } 258 | case 4: { 259 | obj.int64 = reader.int64() 260 | break 261 | } 262 | case 5: { 263 | obj.uint32 = reader.uint32() 264 | break 265 | } 266 | case 6: { 267 | obj.uint64 = reader.uint64() 268 | break 269 | } 270 | case 7: { 271 | obj.sint32 = reader.sint32() 272 | break 273 | } 274 | case 8: { 275 | obj.sint64 = reader.sint64() 276 | break 277 | } 278 | case 9: { 279 | obj.fixed32 = reader.fixed32() 280 | break 281 | } 282 | case 10: { 283 | obj.fixed64 = reader.fixed64() 284 | break 285 | } 286 | case 11: { 287 | obj.sfixed32 = reader.sfixed32() 288 | break 289 | } 290 | case 12: { 291 | obj.sfixed64 = reader.sfixed64() 292 | break 293 | } 294 | case 13: { 295 | obj.bool = reader.bool() 296 | break 297 | } 298 | case 14: { 299 | obj.string = reader.string() 300 | break 301 | } 302 | case 15: { 303 | obj.bytes = reader.bytes() 304 | break 305 | } 306 | case 16: { 307 | obj.enum = SingularEnum.codec().decode(reader) 308 | break 309 | } 310 | case 17: { 311 | obj.subMessage = SingularSubMessage.codec().decode(reader, reader.uint32(), { 312 | limits: opts.limits?.subMessage 313 | }) 314 | break 315 | } 316 | default: { 317 | reader.skipType(tag & 7) 318 | break 319 | } 320 | } 321 | } 322 | 323 | return obj 324 | }) 325 | } 326 | 327 | return _codec 328 | } 329 | 330 | export const encode = (obj: Partial): Uint8Array => { 331 | return encodeMessage(obj, Singular.codec()) 332 | } 333 | 334 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): Singular => { 335 | return decodeMessage(buf, Singular.codec(), opts) 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/test.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | enum AnEnum { 4 | HERP = 0; 5 | DERP = 1; 6 | } 7 | 8 | message SubMessage { 9 | string foo = 1; 10 | } 11 | 12 | message AllTheTypes { 13 | optional bool field1 = 1; 14 | optional int32 field2 = 2; 15 | optional int64 field3 = 3; 16 | optional uint32 field4 = 4; 17 | optional uint64 field5 = 5; 18 | optional sint32 field6 = 6; 19 | optional sint64 field7 = 7; 20 | optional double field8 = 8; 21 | optional float field9 = 9; 22 | optional string field10 = 10; 23 | optional bytes field11 = 11; 24 | optional AnEnum field12 = 12; 25 | optional SubMessage field13 = 13; 26 | repeated string field14 = 14; 27 | optional fixed32 field15 = 15; 28 | optional fixed64 field16 = 16; 29 | optional sfixed32 field17 = 17; 30 | optional sfixed64 field18 = 18; 31 | } 32 | -------------------------------------------------------------------------------- /packages/protons/test/fixtures/test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/export */ 2 | /* eslint-disable complexity */ 3 | /* eslint-disable @typescript-eslint/no-namespace */ 4 | /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */ 5 | /* eslint-disable @typescript-eslint/no-empty-interface */ 6 | /* eslint-disable import/consistent-type-specifier-style */ 7 | /* eslint-disable @typescript-eslint/no-unused-vars */ 8 | 9 | import { decodeMessage, encodeMessage, enumeration, MaxLengthError, message } from 'protons-runtime' 10 | import type { Codec, DecodeOptions } from 'protons-runtime' 11 | import type { Uint8ArrayList } from 'uint8arraylist' 12 | 13 | export enum AnEnum { 14 | HERP = 'HERP', 15 | DERP = 'DERP' 16 | } 17 | 18 | enum __AnEnumValues { 19 | HERP = 0, 20 | DERP = 1 21 | } 22 | 23 | export namespace AnEnum { 24 | export const codec = (): Codec => { 25 | return enumeration(__AnEnumValues) 26 | } 27 | } 28 | export interface SubMessage { 29 | foo: string 30 | } 31 | 32 | export namespace SubMessage { 33 | let _codec: Codec 34 | 35 | export const codec = (): Codec => { 36 | if (_codec == null) { 37 | _codec = message((obj, w, opts = {}) => { 38 | if (opts.lengthDelimited !== false) { 39 | w.fork() 40 | } 41 | 42 | if ((obj.foo != null && obj.foo !== '')) { 43 | w.uint32(10) 44 | w.string(obj.foo) 45 | } 46 | 47 | if (opts.lengthDelimited !== false) { 48 | w.ldelim() 49 | } 50 | }, (reader, length, opts = {}) => { 51 | const obj: any = { 52 | foo: '' 53 | } 54 | 55 | const end = length == null ? reader.len : reader.pos + length 56 | 57 | while (reader.pos < end) { 58 | const tag = reader.uint32() 59 | 60 | switch (tag >>> 3) { 61 | case 1: { 62 | obj.foo = reader.string() 63 | break 64 | } 65 | default: { 66 | reader.skipType(tag & 7) 67 | break 68 | } 69 | } 70 | } 71 | 72 | return obj 73 | }) 74 | } 75 | 76 | return _codec 77 | } 78 | 79 | export const encode = (obj: Partial): Uint8Array => { 80 | return encodeMessage(obj, SubMessage.codec()) 81 | } 82 | 83 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): SubMessage => { 84 | return decodeMessage(buf, SubMessage.codec(), opts) 85 | } 86 | } 87 | 88 | export interface AllTheTypes { 89 | field1?: boolean 90 | field2?: number 91 | field3?: bigint 92 | field4?: number 93 | field5?: bigint 94 | field6?: number 95 | field7?: bigint 96 | field8?: number 97 | field9?: number 98 | field10?: string 99 | field11?: Uint8Array 100 | field12?: AnEnum 101 | field13?: SubMessage 102 | field14: string[] 103 | field15?: number 104 | field16?: bigint 105 | field17?: number 106 | field18?: bigint 107 | } 108 | 109 | export namespace AllTheTypes { 110 | let _codec: Codec 111 | 112 | export const codec = (): Codec => { 113 | if (_codec == null) { 114 | _codec = message((obj, w, opts = {}) => { 115 | if (opts.lengthDelimited !== false) { 116 | w.fork() 117 | } 118 | 119 | if (obj.field1 != null) { 120 | w.uint32(8) 121 | w.bool(obj.field1) 122 | } 123 | 124 | if (obj.field2 != null) { 125 | w.uint32(16) 126 | w.int32(obj.field2) 127 | } 128 | 129 | if (obj.field3 != null) { 130 | w.uint32(24) 131 | w.int64(obj.field3) 132 | } 133 | 134 | if (obj.field4 != null) { 135 | w.uint32(32) 136 | w.uint32(obj.field4) 137 | } 138 | 139 | if (obj.field5 != null) { 140 | w.uint32(40) 141 | w.uint64(obj.field5) 142 | } 143 | 144 | if (obj.field6 != null) { 145 | w.uint32(48) 146 | w.sint32(obj.field6) 147 | } 148 | 149 | if (obj.field7 != null) { 150 | w.uint32(56) 151 | w.sint64(obj.field7) 152 | } 153 | 154 | if (obj.field8 != null) { 155 | w.uint32(65) 156 | w.double(obj.field8) 157 | } 158 | 159 | if (obj.field9 != null) { 160 | w.uint32(77) 161 | w.float(obj.field9) 162 | } 163 | 164 | if (obj.field10 != null) { 165 | w.uint32(82) 166 | w.string(obj.field10) 167 | } 168 | 169 | if (obj.field11 != null) { 170 | w.uint32(90) 171 | w.bytes(obj.field11) 172 | } 173 | 174 | if (obj.field12 != null) { 175 | w.uint32(96) 176 | AnEnum.codec().encode(obj.field12, w) 177 | } 178 | 179 | if (obj.field13 != null) { 180 | w.uint32(106) 181 | SubMessage.codec().encode(obj.field13, w) 182 | } 183 | 184 | if (obj.field14 != null) { 185 | for (const value of obj.field14) { 186 | w.uint32(114) 187 | w.string(value) 188 | } 189 | } 190 | 191 | if (obj.field15 != null) { 192 | w.uint32(125) 193 | w.fixed32(obj.field15) 194 | } 195 | 196 | if (obj.field16 != null) { 197 | w.uint32(129) 198 | w.fixed64(obj.field16) 199 | } 200 | 201 | if (obj.field17 != null) { 202 | w.uint32(141) 203 | w.sfixed32(obj.field17) 204 | } 205 | 206 | if (obj.field18 != null) { 207 | w.uint32(145) 208 | w.sfixed64(obj.field18) 209 | } 210 | 211 | if (opts.lengthDelimited !== false) { 212 | w.ldelim() 213 | } 214 | }, (reader, length, opts = {}) => { 215 | const obj: any = { 216 | field14: [] 217 | } 218 | 219 | const end = length == null ? reader.len : reader.pos + length 220 | 221 | while (reader.pos < end) { 222 | const tag = reader.uint32() 223 | 224 | switch (tag >>> 3) { 225 | case 1: { 226 | obj.field1 = reader.bool() 227 | break 228 | } 229 | case 2: { 230 | obj.field2 = reader.int32() 231 | break 232 | } 233 | case 3: { 234 | obj.field3 = reader.int64() 235 | break 236 | } 237 | case 4: { 238 | obj.field4 = reader.uint32() 239 | break 240 | } 241 | case 5: { 242 | obj.field5 = reader.uint64() 243 | break 244 | } 245 | case 6: { 246 | obj.field6 = reader.sint32() 247 | break 248 | } 249 | case 7: { 250 | obj.field7 = reader.sint64() 251 | break 252 | } 253 | case 8: { 254 | obj.field8 = reader.double() 255 | break 256 | } 257 | case 9: { 258 | obj.field9 = reader.float() 259 | break 260 | } 261 | case 10: { 262 | obj.field10 = reader.string() 263 | break 264 | } 265 | case 11: { 266 | obj.field11 = reader.bytes() 267 | break 268 | } 269 | case 12: { 270 | obj.field12 = AnEnum.codec().decode(reader) 271 | break 272 | } 273 | case 13: { 274 | obj.field13 = SubMessage.codec().decode(reader, reader.uint32(), { 275 | limits: opts.limits?.field13 276 | }) 277 | break 278 | } 279 | case 14: { 280 | if (opts.limits?.field14 != null && obj.field14.length === opts.limits.field14) { 281 | throw new MaxLengthError('Decode error - map field "field14" had too many elements') 282 | } 283 | 284 | obj.field14.push(reader.string()) 285 | break 286 | } 287 | case 15: { 288 | obj.field15 = reader.fixed32() 289 | break 290 | } 291 | case 16: { 292 | obj.field16 = reader.fixed64() 293 | break 294 | } 295 | case 17: { 296 | obj.field17 = reader.sfixed32() 297 | break 298 | } 299 | case 18: { 300 | obj.field18 = reader.sfixed64() 301 | break 302 | } 303 | default: { 304 | reader.skipType(tag & 7) 305 | break 306 | } 307 | } 308 | } 309 | 310 | return obj 311 | }) 312 | } 313 | 314 | return _codec 315 | } 316 | 317 | export const encode = (obj: Partial): Uint8Array => { 318 | return encodeMessage(obj, AllTheTypes.codec()) 319 | } 320 | 321 | export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions): AllTheTypes => { 322 | return decodeMessage(buf, AllTheTypes.codec(), opts) 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /packages/protons/test/maps.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import { expect } from 'aegir/chai' 4 | import Long from 'long' 5 | import protobufjs from 'protobufjs' 6 | import { MapTypes } from './fixtures/maps.js' 7 | import type { SubMessage, EnumValue } from './fixtures/maps.js' 8 | 9 | function longifyBigInts (obj: any): any { 10 | const output = { 11 | ...obj 12 | } 13 | 14 | for (const key of Object.keys(output)) { 15 | if (typeof output[key] === 'bigint') { 16 | output[key] = Long.fromString(`${output[key].toString()}`) 17 | } 18 | } 19 | 20 | return output 21 | } 22 | 23 | function bigintifyLongs (obj: any): any { 24 | const output = { 25 | ...obj 26 | } 27 | 28 | for (const key of Object.keys(output)) { 29 | if (output[key]?.low != null && output[key]?.high != null) { 30 | output[key] = BigInt(new Long(output[key].low, output[key].high, output[key].unsigned).toString()) 31 | } 32 | } 33 | 34 | return output 35 | } 36 | 37 | function uint8ArrayifyBytes (obj: any): any { 38 | const output = { 39 | ...obj 40 | } 41 | 42 | for (const key of Object.keys(output)) { 43 | if (output[key] instanceof Uint8Array) { 44 | output[key] = Uint8Array.from(output[key]) 45 | } 46 | } 47 | 48 | return output 49 | } 50 | 51 | function objectifyMaps (obj: any): any { 52 | const output = { 53 | ...obj 54 | } 55 | 56 | for (const key of Object.keys(output)) { 57 | if (output[key] instanceof Map) { 58 | const obj: Record = {} 59 | const entries: MapIterator<[any, any]> = output[key].entries() 60 | 61 | for (const [key, value] of entries) { 62 | obj[key] = value 63 | } 64 | 65 | output[key] = obj 66 | } 67 | } 68 | 69 | return output 70 | } 71 | 72 | /** 73 | * Paper over differences between protons and protobuf.js output 74 | */ 75 | function normalizeProtonbufjs (obj: any, target: any): any { 76 | let output = bigintifyLongs(obj) 77 | output = uint8ArrayifyBytes(output) 78 | 79 | for (const key of Object.keys(output)) { 80 | // protobujs sets unset message fields to `null`, protons does not set the field at all 81 | if (output[key] === null && target[key] == null) { 82 | delete output[key] // eslint-disable-line @typescript-eslint/no-dynamic-delete 83 | } 84 | } 85 | 86 | for (const key of Object.keys(target)) { 87 | // protobujs uses plain objects instead of maps 88 | if (target[key] instanceof Map) { 89 | output[key] = new Map(Object.entries(output[key] ?? {})) 90 | } 91 | } 92 | 93 | return output 94 | } 95 | 96 | interface TestEncodingOptions { 97 | compareBytes?: boolean 98 | comparePbjs?: boolean 99 | } 100 | 101 | /** 102 | * Ensure: 103 | * 104 | * 1. the generated bytes between protons, pbjs and protobuf.js are the same 105 | * 2. protons and protobuf.js agree on deserialization 106 | */ 107 | function testEncodings (obj: any, protons: any, proto: string, typeName: string, opts: TestEncodingOptions = {}): void { 108 | const protobufJsSchema = protobufjs.loadSync(proto).lookupType(typeName) 109 | const protobufJsBuf = protobufJsSchema.encode(protobufJsSchema.fromObject(objectifyMaps(longifyBigInts(obj)))).finish() 110 | 111 | const encoded = protons.encode(obj) 112 | 113 | if (opts.compareBytes !== false) { 114 | expect(encoded).to.equalBytes(protobufJsBuf) 115 | } 116 | 117 | expect(protons.decode(encoded)).to.deep.equal(obj) 118 | expect(protons.decode(protobufJsBuf)).to.deep.equal(obj) 119 | 120 | expect(normalizeProtonbufjs(protobufJsSchema.toObject(protobufJsSchema.decode(encoded), { 121 | enums: String, 122 | defaults: true 123 | }), obj)).to.deep.equal(obj) 124 | } 125 | 126 | describe('maps', () => { 127 | it('should encode all the types', () => { 128 | const obj: MapTypes = { 129 | stringMap: new Map(), 130 | intMap: new Map(), 131 | boolMap: new Map(), 132 | messageMap: new Map(), 133 | enumMap: new Map() 134 | } 135 | 136 | testEncodings(obj, MapTypes, './test/fixtures/maps.proto', 'MapTypes') 137 | }) 138 | 139 | it('should encode all the types with values', () => { 140 | const obj: MapTypes = { 141 | stringMap: new Map([['key', 'value']]), 142 | intMap: new Map(), // protobuf.js only supports strings as keys 143 | boolMap: new Map(), // protobuf.js only supports strings as keys 144 | messageMap: new Map([['key', { foo: 'bar', bar: [] }]]), 145 | enumMap: new Map() 146 | } 147 | 148 | testEncodings(obj, MapTypes, './test/fixtures/maps.proto', 'MapTypes') 149 | }) 150 | 151 | it('should limit map sizes using runtime options', () => { 152 | const obj: MapTypes = { 153 | stringMap: new Map([['key', 'value'], ['foo', 'bar']]), 154 | intMap: new Map(), 155 | boolMap: new Map(), 156 | messageMap: new Map(), 157 | enumMap: new Map() 158 | } 159 | 160 | const buf = MapTypes.encode(obj) 161 | expect(() => MapTypes.decode(buf, { 162 | limits: { 163 | stringMap: 1 164 | } 165 | })).to.throw(/too many elements/) 166 | }) 167 | 168 | it('should limit nested message collection sizes using runtime options', () => { 169 | const obj: MapTypes = { 170 | stringMap: new Map(), 171 | intMap: new Map(), 172 | boolMap: new Map(), 173 | messageMap: new Map([['foo', { foo: 'hello', bar: [1, 2, 3, 4, 5] }]]), 174 | enumMap: new Map() 175 | } 176 | 177 | const buf = MapTypes.encode(obj) 178 | expect(() => MapTypes.decode(buf, { 179 | limits: { 180 | messageMap$value: { 181 | bar: 1 182 | } 183 | } 184 | })).to.throw(/too many elements/) 185 | }) 186 | }) 187 | -------------------------------------------------------------------------------- /packages/protons/test/proto2.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import { expect } from 'aegir/chai' 4 | import { MessageWithRequired } from './fixtures/proto2.js' 5 | 6 | describe('proto2 support', () => { 7 | it('should write a required field with a default value', () => { 8 | const obj: MessageWithRequired = { 9 | scalarField: 0 10 | } 11 | 12 | const buf = MessageWithRequired.encode(obj) 13 | 14 | expect(buf).to.equalBytes([8, 0]) 15 | }) 16 | 17 | it('should write a required field with a non-default value', () => { 18 | const obj: MessageWithRequired = { 19 | scalarField: 5 20 | } 21 | 22 | const buf = MessageWithRequired.encode(obj) 23 | 24 | expect(buf).to.equalBytes([8, 5]) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /packages/protons/test/protons-options.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import { expect } from 'aegir/chai' 4 | import { MessageWithSizeLimitedMap, MessageWithSizeLimitedRepeatedField } from './fixtures/protons-options.js' 5 | 6 | describe('protons options', () => { 7 | it('should not decode message with map that is too big', () => { 8 | const obj: MessageWithSizeLimitedMap = { 9 | mapField: new Map([['one', 'two'], ['three', 'four']]) 10 | } 11 | 12 | const buf = MessageWithSizeLimitedMap.encode(obj) 13 | 14 | expect(() => MessageWithSizeLimitedMap.decode(buf)) 15 | .to.throw().with.property('code', 'ERR_MAX_SIZE') 16 | }) 17 | 18 | it('should not decode message with list that is too big', () => { 19 | const obj: MessageWithSizeLimitedRepeatedField = { 20 | repeatedField: ['0', '1'] 21 | } 22 | 23 | const buf = MessageWithSizeLimitedRepeatedField.encode(obj) 24 | 25 | expect(() => MessageWithSizeLimitedRepeatedField.decode(buf)) 26 | .to.throw().with.property('code', 'ERR_MAX_LENGTH') 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /packages/protons/test/repeated.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import { expect } from 'aegir/chai' 4 | import { RepeatedTypes } from './fixtures/repeated.js' 5 | 6 | describe('repeated', () => { 7 | it('should encode repeated fields', () => { 8 | const obj: RepeatedTypes = { 9 | number: [], 10 | limitedNumber: [], 11 | messages: [] 12 | } 13 | 14 | const buf = RepeatedTypes.encode(obj) 15 | expect(RepeatedTypes.decode(buf)).to.deep.equal(obj) 16 | }) 17 | 18 | it('should limit repeated fields', () => { 19 | const obj: RepeatedTypes = { 20 | number: [], 21 | limitedNumber: [1, 2], 22 | messages: [] 23 | } 24 | 25 | const buf = RepeatedTypes.encode(obj) 26 | expect(() => RepeatedTypes.decode(buf)).to.throw(/too many elements/) 27 | }) 28 | 29 | it('should limit repeated fields using runtime options', () => { 30 | const obj: RepeatedTypes = { 31 | number: [1, 2], 32 | limitedNumber: [], 33 | messages: [] 34 | } 35 | 36 | const buf = RepeatedTypes.encode(obj) 37 | expect(() => RepeatedTypes.decode(buf, { 38 | limits: { 39 | number: 1 40 | } 41 | })).to.throw(/too many elements/) 42 | }) 43 | 44 | it('should limit repeating repeating fields using runtime options', () => { 45 | const obj: RepeatedTypes = { 46 | number: [], 47 | limitedNumber: [], 48 | messages: [{ 49 | foo: ['one', 'two'], 50 | nonRepeating: 0, 51 | messages: [] 52 | }], 53 | nonRepeating: 5 54 | } 55 | 56 | const buf = RepeatedTypes.encode(obj) 57 | expect(() => RepeatedTypes.decode(buf, { 58 | limits: { 59 | messages$: { 60 | foo: 1 61 | } 62 | } 63 | })).to.throw(/too many elements/) 64 | }) 65 | 66 | it('should limit repeating nested repeating fields using runtime options', () => { 67 | const obj: RepeatedTypes = { 68 | number: [], 69 | limitedNumber: [], 70 | messages: [{ 71 | foo: [], 72 | nonRepeating: 0, 73 | messages: [{ 74 | foo: ['one', 'two'], 75 | nonRepeating: 0 76 | }] 77 | }], 78 | nonRepeating: 5 79 | } 80 | 81 | const buf = RepeatedTypes.encode(obj) 82 | expect(() => RepeatedTypes.decode(buf, { 83 | limits: { 84 | messages$: { 85 | messages$: { 86 | foo: 1 87 | } 88 | } 89 | } 90 | })).to.throw(/too many elements/) 91 | }) 92 | 93 | it('should limit nested repeating fields using runtime options', () => { 94 | const obj: RepeatedTypes = { 95 | number: [], 96 | limitedNumber: [], 97 | messages: [], 98 | nonRepeating: 5, 99 | message: { 100 | foo: ['one', 'two'], 101 | messages: [] 102 | } 103 | } 104 | 105 | const buf = RepeatedTypes.encode(obj) 106 | expect(() => RepeatedTypes.decode(buf, { 107 | limits: { 108 | message: { 109 | foo: 1 110 | } 111 | } 112 | })).to.throw(/too many elements/) 113 | }) 114 | 115 | it('should limit nested repeating nested repeating fields using runtime options', () => { 116 | const obj: RepeatedTypes = { 117 | number: [], 118 | limitedNumber: [], 119 | messages: [], 120 | nonRepeating: 5, 121 | message: { 122 | foo: [], 123 | messages: [{ 124 | foo: ['one', 'two'] 125 | }] 126 | } 127 | } 128 | 129 | const buf = RepeatedTypes.encode(obj) 130 | expect(() => RepeatedTypes.decode(buf, { 131 | limits: { 132 | message: { 133 | messages$: { 134 | foo: 1 135 | } 136 | } 137 | } 138 | })).to.throw(/too many elements/) 139 | }) 140 | }) 141 | -------------------------------------------------------------------------------- /packages/protons/test/unsupported.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import { expect } from 'aegir/chai' 4 | import { generate } from '../src/index.js' 5 | 6 | describe('unsupported', () => { 7 | it('should refuse to generate source from proto2 definition', async () => { 8 | await expect(generate('test/bad-fixtures/proto2.proto', { 9 | strict: true 10 | })).to.eventually.be.rejected 11 | .with.property('code', 'ERR_PARSE_ERROR') 12 | }) 13 | 14 | it('should refuse to generate source from enum definition that does not start from 0', async () => { 15 | await expect(generate('test/bad-fixtures/enum.proto', { 16 | strict: true 17 | })).to.eventually.be.rejected 18 | .with.property('code', 'ERR_PARSE_ERROR') 19 | }) 20 | 21 | it('should refuse to generate source from empty definition', async () => { 22 | await expect(generate('test/bad-fixtures/empty.proto', {})).to.eventually.be.rejected 23 | .with.property('code', 'ERR_NO_MESSAGES_FOUND') 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /packages/protons/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "aegir/src/config/tsconfig.aegir.json", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "include": [ 7 | "bin", 8 | "src", 9 | "test" 10 | ], 11 | "references": [ 12 | { 13 | "path": "../protons-runtime" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/protons/typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": [ 3 | "./src/index.ts" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://typedoc.org/schema.json", 3 | "name": "Protons", 4 | "exclude": [ 5 | "packages/protons-benchmark" 6 | ] 7 | } 8 | --------------------------------------------------------------------------------