├── .eslintignore
├── .eslintrc.cjs
├── .github
└── workflows
│ └── nodejs.yml
├── .gitignore
├── LICENSE
├── README.md
├── benchmarks
├── e2e
│ ├── package.json
│ ├── public
│ │ ├── avsc.js
│ │ └── index.html
│ └── src
│ │ ├── data
│ │ ├── todos.json
│ │ └── todos.proto
│ │ └── index.ts
└── isolated
│ ├── data
│ ├── todos.json
│ └── todos.proto
│ ├── des.benchmark.ts
│ ├── package.json
│ ├── ser.benchmark.ts
│ └── serdes.benchmark.ts
├── examples
├── complex_object.ts
├── iterable.ts
├── object.ts
├── simple.ts
└── todos.ts
├── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── src
└── index.ts
├── tests
└── index.test.ts
├── tsconfig.cjs.json
├── tsconfig.esm.json
└── tsconfig.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | .eslintrc.cjs
2 | *.css
3 | *.csv
4 | *.dic
5 | *.dpack
6 | *.html
7 | *.md
8 | *.png
9 | *.sh
10 | *.snap
11 | *.svg
12 | *.vue
13 | *.yaml
14 | *.yml
15 | *.gif
16 | **/datasets/*.json
17 | **/snapshots/*.json
18 | stemmer
19 | **/dist/**
20 | **/dist
21 | Dockerfile
22 | **/get-imdb-dataset.mjs
23 | node_modules
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | node: true,
4 | browser: true,
5 | },
6 | parserOptions: {
7 | sourceType: "module",
8 | },
9 | globals: {
10 | Deno: "readonly",
11 | },
12 | root: true,
13 | parser: "@typescript-eslint/parser",
14 | plugins: ["@typescript-eslint", "import"],
15 | extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
16 | rules: {
17 | "import/extensions": [2, "always", { ignorePackages: true }], // This is required for proper ESM use
18 | "@typescript-eslint/no-non-null-assertion": 0,
19 | "no-async-promise-executor": 0,
20 | "@typescript-eslint/no-explicit-any": 0,
21 | },
22 | };
23 |
--------------------------------------------------------------------------------
/.github/workflows/nodejs.yml:
--------------------------------------------------------------------------------
1 | name: Nodejs
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | name: Build and Test
9 | timeout-minutes: 15
10 |
11 | strategy:
12 | matrix:
13 | node-version: [20.x]
14 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
15 |
16 | steps:
17 | - uses: actions/checkout@v3
18 | - uses: pnpm/action-setup@v2
19 | with:
20 | version: 8
21 | - name: Use Node.js ${{ matrix.node-version }}
22 | uses: actions/setup-node@v3
23 | with:
24 | node-version: ${{ matrix.node-version }}
25 | cache: 'pnpm'
26 | - name: Install dependencies
27 | run: pnpm install
28 | - name: Run lint
29 | run: pnpm run lint
30 | - name: Run test
31 | run: pnpm run test
32 |
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.toptal.com/developers/gitignore/api/node,osx
2 | # Edit at https://www.toptal.com/developers/gitignore?templates=node,osx
3 |
4 | .wrangler
5 | .env.production
6 | .env.development
7 |
8 | ### Node ###
9 | # Logs
10 | logs
11 | *.log
12 | npm-debug.log*
13 | yarn-debug.log*
14 | yarn-error.log*
15 | lerna-debug.log*
16 | .pnpm-debug.log*
17 |
18 | # Diagnostic reports (https://nodejs.org/api/report.html)
19 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
20 |
21 | # Runtime data
22 | pids
23 | *.pid
24 | *.seed
25 | *.pid.lock
26 |
27 | # Directory for instrumented libs generated by jscoverage/JSCover
28 | lib-cov
29 |
30 | # Coverage directory used by tools like istanbul
31 | coverage
32 | *.lcov
33 |
34 | # nyc test coverage
35 | .nyc_output
36 |
37 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
38 | .grunt
39 |
40 | # Bower dependency directory (https://bower.io/)
41 | bower_components
42 |
43 | # node-waf configuration
44 | .lock-wscript
45 |
46 | # Compiled binary addons (https://nodejs.org/api/addons.html)
47 | build/Release
48 |
49 | # Dependency directories
50 | node_modules/
51 | jspm_packages/
52 |
53 | # Snowpack dependency directory (https://snowpack.dev/)
54 | web_modules/
55 |
56 | # TypeScript cache
57 | *.tsbuildinfo
58 |
59 | # Optional npm cache directory
60 | .npm
61 |
62 | # Optional eslint cache
63 | .eslintcache
64 |
65 | # Optional stylelint cache
66 | .stylelintcache
67 |
68 | # Microbundle cache
69 | .rpt2_cache/
70 | .rts2_cache_cjs/
71 | .rts2_cache_es/
72 | .rts2_cache_umd/
73 |
74 | # Optional REPL history
75 | .node_repl_history
76 |
77 | # Output of 'npm pack'
78 | *.tgz
79 |
80 | # Yarn Integrity file
81 | .yarn-integrity
82 |
83 | # dotenv environment variable files
84 | .env
85 | .env.development.local
86 | .env.test.local
87 | .env.production.local
88 | .env.local
89 |
90 | # parcel-bundler cache (https://parceljs.org/)
91 | .cache
92 | .parcel-cache
93 |
94 | # Next.js build output
95 | .next
96 | out
97 |
98 | # Nuxt.js build / generate output
99 | .nuxt
100 | dist
101 |
102 | # Gatsby files
103 | .cache/
104 | # Comment in the public line in if your project uses Gatsby and not Next.js
105 | # https://nextjs.org/blog/next-9-1#public-directory-support
106 | # public
107 |
108 | # vuepress build output
109 | .vuepress/dist
110 |
111 | # vuepress v2.x temp and cache directory
112 | .temp
113 |
114 | # Docusaurus cache and generated files
115 | .docusaurus
116 |
117 | # Serverless directories
118 | .serverless/
119 |
120 | # FuseBox cache
121 | .fusebox/
122 |
123 | # DynamoDB Local files
124 | .dynamodb/
125 |
126 | # TernJS port file
127 | .tern-port
128 |
129 | # Stores VSCode versions used for testing VSCode extensions
130 | .vscode-test
131 |
132 | # yarn v2
133 | .yarn/cache
134 | .yarn/unplugged
135 | .yarn/build-state.yml
136 | .yarn/install-state.gz
137 | .pnp.*
138 |
139 | ### Node Patch ###
140 | # Serverless Webpack directories
141 | .webpack/
142 |
143 | # Optional stylelint cache
144 |
145 | # SvelteKit build / generate output
146 | .svelte-kit
147 |
148 | ### OSX ###
149 | # General
150 | .DS_Store
151 | .AppleDouble
152 | .LSOverride
153 |
154 | # Icon must end with two \r
155 | Icon
156 |
157 |
158 | # Thumbnails
159 | ._*
160 |
161 | # Files that might appear in the root of a volume
162 | .DocumentRevisions-V100
163 | .fseventsd
164 | .Spotlight-V100
165 | .TemporaryItems
166 | .Trashes
167 | .VolumeIcon.icns
168 | .com.apple.timemachine.donotpresent
169 |
170 | # Directories potentially created on remote AFP share
171 | .AppleDB
172 | .AppleDesktop
173 | Network Trash Folder
174 | Temporary Items
175 | .apdisk
176 |
177 | # End of https://www.toptal.com/developers/gitignore/api/node,osx
178 |
179 | dist
180 | node_modules
181 | !templates/v1do/node_modules
182 | tmp
183 | .env.local
184 | .env.staging
185 |
186 | *.pem
187 | !tests/data/*.pem
188 |
189 | .idea
190 |
191 | bun.lockb
192 | benchmark
193 |
194 | *.0x/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SeqProto
2 |
3 | [](https://github.com/oramasearch/seqproto/actions/workflows/nodejs.yml)
4 | 
5 |
6 | This library provides a simple way to serialize and deserialize objects in binary format.
7 |
8 | ## Why another serialization library?
9 |
10 | While I have been writing this library, I have in mind the following main goals:
11 | - **Runtime independent** - I want to have a library that runs in every javascript runtime, from Node.JS, through browsers, to CloudFlare.
12 | - **Performance** - I want to have a library that is fast and easy to use.
13 | - **Small size** - I want to have a library that is small and easy to use.
14 | - **Customizable** - Due to the JavaScript nature, the data structures are limited.
15 | - **TypeScript support** - I want to have a library that is easy to use in TypeScript.
16 |
17 | ## Installation
18 |
19 | Seqproto works in any JavaScript environment. You can install it via npm:
20 |
21 | ```sh
22 | npm install seqproto
23 | ```
24 |
25 | Or via CDN:
26 |
27 | ```js
28 | import { createSer, createDes } from 'https://unpkg.com/seqproto@latest/dist/esm/index.js'
29 | ```
30 |
31 | ## Usage
32 |
33 | ### Examples
34 |
35 | For more examples, see the [examples](https://github.com/oramasearch/seqproto/tree/main/examples) directory.
36 |
37 | ```typescript
38 | import { createSer, createDes } from 'seqproto'
39 |
40 | // Create a serializer
41 | const ser = createSer()
42 |
43 | // Serialize some data
44 | ser.serializeBoolean(true)
45 | ser.serializeUInt32(42)
46 | ser.serializeFloat32(-0.5)
47 | ser.serializeString('hello world')
48 | ser.serializeArray([1, 2, 3], (ser, n) => ser.serializeUInt32(n))
49 |
50 | // Get ArrayBuffer with serialized data
51 | const buffer = ser.getBuffer()
52 |
53 | // Create a deserializer
54 | const des = createDes(buffer)
55 |
56 | // Deserialize data
57 | const b = des.deserializeBoolean()
58 | const i = des.deserializeUInt32()
59 | const f = des.deserializeFloat32()
60 | const s = des.deserializeString()
61 | const a = des.deserializeArray((des) => des.deserializeUInt32())
62 |
63 | console.log({ b, i, f, s, a })
64 | ```
65 |
66 | ### Object
67 |
68 | ```typescript
69 | import type { Ser, Des } from 'seqproto'
70 | import { createSer, createDes } from 'seqproto'
71 |
72 | interface Todo {
73 | id: number
74 | userId: number
75 | title: string
76 | completed: boolean
77 | }
78 |
79 | function serializeTodo (ser: Ser, todo: Todo) {
80 | ser.serializeUInt32(todo.id)
81 | ser.serializeUInt32(todo.userId)
82 | ser.serializeString(todo.title)
83 | ser.serializeBoolean(todo.completed)
84 | }
85 |
86 | function deserializeTodo (des: Des): Todo {
87 | const id = des.deserializeUInt32()
88 | const userId = des.deserializeUInt32()
89 | const title = des.deserializeString()
90 | const completed = des.deserializeBoolean()
91 | return { id, userId, title, completed }
92 | }
93 |
94 | const ser: Ser = createSer()
95 |
96 | serializeTodo(ser, {
97 | id: 1,
98 | userId: 1,
99 | title: 'hello',
100 | completed: false,
101 | })
102 |
103 | const buffer = ser.getBuffer()
104 |
105 | const des: Des = createDes(buffer)
106 | const todo = deserializeTodo(des)
107 |
108 | console.log(JSON.stringify(todo, null, 2))
109 | ```
110 |
111 | ### Array of object
112 |
113 | ```typescript
114 | import type { Ser, Des } from 'seqproto'
115 | import { createSer, createDes } from 'seqproto'
116 |
117 | let buffer
118 |
119 | // Serialize
120 | const todos = [
121 | { userId: 1, id: 1, completed: false, title: "delectus aut autem" },
122 | { userId: 1, id: 2, completed: true, title: "quis ut nam facilis et officia qui" }
123 | ]
124 |
125 | const ser: Ser = createSer()
126 |
127 | ser.serializeArray(todos, (ser, todo) => {
128 | ser.serializeUInt32(todo.id)
129 | ser.serializeUInt32(todo.userId)
130 | ser.serializeString(todo.title)
131 | ser.serializeBoolean(todo.completed)
132 | })
133 |
134 | buffer = ser.getBuffer()
135 |
136 | // Deserialize
137 | const des: Des = createDes(buffer)
138 |
139 | const deserializedTodos = des.deserializeArray((des) => {
140 | const id = des.deserializeUInt32()
141 | const userId = des.deserializeUInt32()
142 | const title = des.deserializeString()
143 | const completed = des.deserializeBoolean()
144 | return { id, userId, title, completed }
145 | })
146 |
147 | console.log(deserializedTodos)
148 | ```
149 |
150 | ## API
151 |
152 | This library exports the following functions:
153 |
154 | - `createSer()`/`createSer({ bufferSize: number })`: creates a new serializer.
155 | - `createDes(buffer)`: creates a new deserializer.
156 | - `ser.reset()`: reset the serializer.
157 | - `ser.serializeBoolean(b)`: serializes a boolean value.
158 | - `des.deserializeBoolean()`: deserializes a boolean value.
159 | - `ser.serializeUInt32(uint32)`: serializes a 32-bit unsigned integer.
160 | - `des.deserializeUInt32(uint32)`: deserializes a 32-bit unsigned integer.
161 | - `ser.serializeNumber(n)`: serializes a 32-bit unsigned integer or signed integer or float.
162 | - `des.deserializeNumber(n)`: deserializes a 32-bit unsigned integer or signed integer or float.
163 | - `ser.serializeString(string)`: serializes a string.
164 | - `des.deserializeString()`: deserializes a string.
165 | - `ser.serializeArray(array, (ser, item) => { ... })`: serializes an array.
166 | - `des.deserializeArray((des) => { ... })`: deserializes an array.
167 | - `ser.serializeIterable(iterable, (ser, item) => { ... })`: serializes an iterable.
168 | - `des.deserializeIterable((des) => { ... })`: deserializes an iterable.
169 | - `ser.serializeFloat32(float32)`: serializes float 32bit.
170 | - `des.deserializeFloat32()`: deserializes float 32bit.
171 | - `ser.getBuffer()`: returns the serialized buffer.
172 |
173 | ## Benchmarks
174 |
175 | We created 3 different benchmarks to compare the performance of SeqProto with JSON and Avro.
176 |
177 | ### Isolated benchmark
178 |
179 | You can run the benchmarks with the following command:
180 | ```sh
181 | npm run benchmark:serdes
182 | ```
183 |
184 | Serialization / Deserialization:
185 | | name | ops | margin | percentSlower |
186 | | -------- | ------- | -------- | ------- |
187 | | seqproto | 29764 | 0.72 | 0 |
188 | | protobuf | 13698 | 0.19 | 53.98 |
189 | | avro | 24204 | 0.14 | 18.68 |
190 | | cbor | 803 | 0.22 | 97.3 |
191 | | cborx | 9707 | 0.32 | 67.39 |
192 | | msgpack | 6857 | 0.06 | 76.96 |
193 | | msgpackr | 10449 | 0.27 | 64.89 |
194 | | JSON | 14434 | 0.07 | 51.51 |
195 |
196 | ### Http benchmark
197 |
198 | You can run the benchmarks using 2 shells.
199 |
200 | 1.
201 | ```sh
202 | cd bechmarks/e2e
203 | pnpm install
204 | pnpm start
205 | ```
206 | 2.
207 | ```sh
208 | cd bechmarks/e2e
209 | pnpm run autocannon:json
210 | pnpm run autocannon:seqproto
211 | pnpm run autocannon:avro
212 | ```
213 |
214 | | type | req (in 10s) | Avg req/sec | Avg Bytes/Sec | Avg Latency (ms) |
215 | | -------- | ---- | --------- | ---- | ---- |
216 | | JSON | 164k | 14892 | 275 | 0.11 |
217 | | SeqProto | 269k | 26865.6 | 321 | 0.01 |
218 | | Avro | 197k | 17926.55 | 169 | 0.04 |
219 |
220 | ### e2e benchmark
221 |
222 | You can run the benchmarks with the following command:
223 | ```sh
224 | cd bechmarks/e2e
225 | pnpm install
226 | pnpm start
227 | ```
228 | And go to http://localhost:3000/public/index.html.
229 |
230 | | iteration | parallelism | type | ms |
231 | | -------- | ------- | -------- | ------- |
232 | | 10 | 1 | JSON | 30.69999998807907 |
233 | | 10 | 1 | SeqProto | 25.600000023841858 |
234 | | 10 | 1 | Avro | 30.399999976158142 |
235 | | 100 | 1 | JSON | 108.80000001192093 |
236 | | 100 | 1 | SeqProto | 96.80000001192093 |
237 | | 100 | 1 | Avro | 96 |
238 | | 100 | 3 | JSON | 162.10000002384186 |
239 | | 100 | 3 | SeqProto | 152.4000000357628 |
240 | | 100 | 3 | Avro | 167.5 |
241 | | 100 | 6 | JSON | 277.19999998807907 |
242 | | 100 | 6 | SeqProto | 263.30000001192093 |
243 | | 100 | 6 | Avro | 308.19999998807907 |
244 |
245 | ## Contributing
246 |
247 | Contributions are welcome! Please open an issue if you have any ideas for improvement or found a bug.
248 |
249 | ## License
250 |
251 | Apache-2.0
252 |
--------------------------------------------------------------------------------
/benchmarks/e2e/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "seqproto-benchmarks-e2e",
3 | "version": "0.0.1",
4 | "type": "module",
5 | "description": "",
6 | "scripts": {
7 | "start": "node --import tsx src/index.ts",
8 | "autocannon:json": "npx autocannon 'http://localhost:3000/todos'",
9 | "autocannon:seqproto": "npx autocannon -H 'accept=application/seqproto' 'http://localhost:3000/todos'",
10 | "autocannon:avro": "autocannon -H 'accept=application/avro' 'http://localhost:3000/todos'"
11 | },
12 | "author": {
13 | "name": "Tommaso Allevi",
14 | "email": "tomallevi@gmail.com",
15 | "url": "https://github.com/allevo",
16 | "author": true
17 | },
18 | "engines": {
19 | "node": ">= 20.0.0"
20 | },
21 | "devDependencies": {
22 | "@msgpack/msgpack": "3.0.0-beta2",
23 | "@types/node": "=20.8.0",
24 | "autocannon": "^7.12.0",
25 | "avro-js": "^1.11.3",
26 | "avsc": "^5.7.7",
27 | "benny": "^3.7.1",
28 | "browserify": "^17.0.0",
29 | "cbor": "^9.0.1",
30 | "cbor-x": "^1.5.4",
31 | "fast-json-stringify": "^5.9.1",
32 | "lodash": "^4.17.21",
33 | "msgpackr": "^1.9.9",
34 | "protobufjs": "^7.2.5",
35 | "ts-standard": "^12.0.2",
36 | "tsx": "^3.14.0",
37 | "typescript": "^5.2.2",
38 | "undici-types": "^5.27.2"
39 | },
40 | "dependencies": {
41 | "@fastify/static": "^6.12.0",
42 | "fastify": "^4.24.3",
43 | "seqproto": "workspace:^"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/benchmarks/e2e/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
14 |
15 |
16 | SeqProto e2e benchmarks
17 |
18 |
32 |
33 |
34 | Results
35 |
36 |
37 |
38 | iteration
39 | parallelism
40 | type
41 | ms
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
150 |
151 |
--------------------------------------------------------------------------------
/benchmarks/e2e/src/data/todos.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "userId": 1,
4 | "id": 1,
5 | "title": "delectus aut autem",
6 | "completed": false
7 | },
8 | {
9 | "userId": 1,
10 | "id": 2,
11 | "title": "quis ut nam facilis et officia qui",
12 | "completed": false
13 | },
14 | {
15 | "userId": 1,
16 | "id": 3,
17 | "title": "fugiat veniam minus",
18 | "completed": false
19 | },
20 | {
21 | "userId": 1,
22 | "id": 4,
23 | "title": "et porro tempora",
24 | "completed": true
25 | },
26 | {
27 | "userId": 1,
28 | "id": 5,
29 | "title": "laboriosam mollitia et enim quasi adipisci quia provident illum",
30 | "completed": false
31 | },
32 | {
33 | "userId": 1,
34 | "id": 6,
35 | "title": "qui ullam ratione quibusdam voluptatem quia omnis",
36 | "completed": false
37 | },
38 | {
39 | "userId": 1,
40 | "id": 7,
41 | "title": "illo expedita consequatur quia in",
42 | "completed": false
43 | },
44 | {
45 | "userId": 1,
46 | "id": 8,
47 | "title": "quo adipisci enim quam ut ab",
48 | "completed": true
49 | },
50 | {
51 | "userId": 1,
52 | "id": 9,
53 | "title": "molestiae perspiciatis ipsa",
54 | "completed": false
55 | },
56 | {
57 | "userId": 1,
58 | "id": 10,
59 | "title": "illo est ratione doloremque quia maiores aut",
60 | "completed": true
61 | },
62 | {
63 | "userId": 1,
64 | "id": 11,
65 | "title": "vero rerum temporibus dolor",
66 | "completed": true
67 | },
68 | {
69 | "userId": 1,
70 | "id": 12,
71 | "title": "ipsa repellendus fugit nisi",
72 | "completed": true
73 | },
74 | {
75 | "userId": 1,
76 | "id": 13,
77 | "title": "et doloremque nulla",
78 | "completed": false
79 | },
80 | {
81 | "userId": 1,
82 | "id": 14,
83 | "title": "repellendus sunt dolores architecto voluptatum",
84 | "completed": true
85 | },
86 | {
87 | "userId": 1,
88 | "id": 15,
89 | "title": "ab voluptatum amet voluptas",
90 | "completed": true
91 | },
92 | {
93 | "userId": 1,
94 | "id": 16,
95 | "title": "accusamus eos facilis sint et aut voluptatem",
96 | "completed": true
97 | },
98 | {
99 | "userId": 1,
100 | "id": 17,
101 | "title": "quo laboriosam deleniti aut qui",
102 | "completed": true
103 | },
104 | {
105 | "userId": 1,
106 | "id": 18,
107 | "title": "dolorum est consequatur ea mollitia in culpa",
108 | "completed": false
109 | },
110 | {
111 | "userId": 1,
112 | "id": 19,
113 | "title": "molestiae ipsa aut voluptatibus pariatur dolor nihil",
114 | "completed": true
115 | },
116 | {
117 | "userId": 1,
118 | "id": 20,
119 | "title": "ullam nobis libero sapiente ad optio sint",
120 | "completed": true
121 | },
122 | {
123 | "userId": 2,
124 | "id": 21,
125 | "title": "suscipit repellat esse quibusdam voluptatem incidunt",
126 | "completed": false
127 | },
128 | {
129 | "userId": 2,
130 | "id": 22,
131 | "title": "distinctio vitae autem nihil ut molestias quo",
132 | "completed": true
133 | },
134 | {
135 | "userId": 2,
136 | "id": 23,
137 | "title": "et itaque necessitatibus maxime molestiae qui quas velit",
138 | "completed": false
139 | },
140 | {
141 | "userId": 2,
142 | "id": 24,
143 | "title": "adipisci non ad dicta qui amet quaerat doloribus ea",
144 | "completed": false
145 | },
146 | {
147 | "userId": 2,
148 | "id": 25,
149 | "title": "voluptas quo tenetur perspiciatis explicabo natus",
150 | "completed": true
151 | },
152 | {
153 | "userId": 2,
154 | "id": 26,
155 | "title": "aliquam aut quasi",
156 | "completed": true
157 | },
158 | {
159 | "userId": 2,
160 | "id": 27,
161 | "title": "veritatis pariatur delectus",
162 | "completed": true
163 | },
164 | {
165 | "userId": 2,
166 | "id": 28,
167 | "title": "nesciunt totam sit blanditiis sit",
168 | "completed": false
169 | },
170 | {
171 | "userId": 2,
172 | "id": 29,
173 | "title": "laborum aut in quam",
174 | "completed": false
175 | },
176 | {
177 | "userId": 2,
178 | "id": 30,
179 | "title": "nemo perspiciatis repellat ut dolor libero commodi blanditiis omnis",
180 | "completed": true
181 | },
182 | {
183 | "userId": 2,
184 | "id": 31,
185 | "title": "repudiandae totam in est sint facere fuga",
186 | "completed": false
187 | },
188 | {
189 | "userId": 2,
190 | "id": 32,
191 | "title": "earum doloribus ea doloremque quis",
192 | "completed": false
193 | },
194 | {
195 | "userId": 2,
196 | "id": 33,
197 | "title": "sint sit aut vero",
198 | "completed": false
199 | },
200 | {
201 | "userId": 2,
202 | "id": 34,
203 | "title": "porro aut necessitatibus eaque distinctio",
204 | "completed": false
205 | },
206 | {
207 | "userId": 2,
208 | "id": 35,
209 | "title": "repellendus veritatis molestias dicta incidunt",
210 | "completed": true
211 | },
212 | {
213 | "userId": 2,
214 | "id": 36,
215 | "title": "excepturi deleniti adipisci voluptatem et neque optio illum ad",
216 | "completed": true
217 | },
218 | {
219 | "userId": 2,
220 | "id": 37,
221 | "title": "sunt cum tempora",
222 | "completed": false
223 | },
224 | {
225 | "userId": 2,
226 | "id": 38,
227 | "title": "totam quia non",
228 | "completed": false
229 | },
230 | {
231 | "userId": 2,
232 | "id": 39,
233 | "title": "doloremque quibusdam asperiores libero corrupti illum qui omnis",
234 | "completed": false
235 | },
236 | {
237 | "userId": 2,
238 | "id": 40,
239 | "title": "totam atque quo nesciunt",
240 | "completed": true
241 | },
242 | {
243 | "userId": 3,
244 | "id": 41,
245 | "title": "aliquid amet impedit consequatur aspernatur placeat eaque fugiat suscipit",
246 | "completed": false
247 | },
248 | {
249 | "userId": 3,
250 | "id": 42,
251 | "title": "rerum perferendis error quia ut eveniet",
252 | "completed": false
253 | },
254 | {
255 | "userId": 3,
256 | "id": 43,
257 | "title": "tempore ut sint quis recusandae",
258 | "completed": true
259 | },
260 | {
261 | "userId": 3,
262 | "id": 44,
263 | "title": "cum debitis quis accusamus doloremque ipsa natus sapiente omnis",
264 | "completed": true
265 | },
266 | {
267 | "userId": 3,
268 | "id": 45,
269 | "title": "velit soluta adipisci molestias reiciendis harum",
270 | "completed": false
271 | },
272 | {
273 | "userId": 3,
274 | "id": 46,
275 | "title": "vel voluptatem repellat nihil placeat corporis",
276 | "completed": false
277 | },
278 | {
279 | "userId": 3,
280 | "id": 47,
281 | "title": "nam qui rerum fugiat accusamus",
282 | "completed": false
283 | },
284 | {
285 | "userId": 3,
286 | "id": 48,
287 | "title": "sit reprehenderit omnis quia",
288 | "completed": false
289 | },
290 | {
291 | "userId": 3,
292 | "id": 49,
293 | "title": "ut necessitatibus aut maiores debitis officia blanditiis velit et",
294 | "completed": false
295 | },
296 | {
297 | "userId": 3,
298 | "id": 50,
299 | "title": "cupiditate necessitatibus ullam aut quis dolor voluptate",
300 | "completed": true
301 | },
302 | {
303 | "userId": 3,
304 | "id": 51,
305 | "title": "distinctio exercitationem ab doloribus",
306 | "completed": false
307 | },
308 | {
309 | "userId": 3,
310 | "id": 52,
311 | "title": "nesciunt dolorum quis recusandae ad pariatur ratione",
312 | "completed": false
313 | },
314 | {
315 | "userId": 3,
316 | "id": 53,
317 | "title": "qui labore est occaecati recusandae aliquid quam",
318 | "completed": false
319 | },
320 | {
321 | "userId": 3,
322 | "id": 54,
323 | "title": "quis et est ut voluptate quam dolor",
324 | "completed": true
325 | },
326 | {
327 | "userId": 3,
328 | "id": 55,
329 | "title": "voluptatum omnis minima qui occaecati provident nulla voluptatem ratione",
330 | "completed": true
331 | },
332 | {
333 | "userId": 3,
334 | "id": 56,
335 | "title": "deleniti ea temporibus enim",
336 | "completed": true
337 | },
338 | {
339 | "userId": 3,
340 | "id": 57,
341 | "title": "pariatur et magnam ea doloribus similique voluptatem rerum quia",
342 | "completed": false
343 | },
344 | {
345 | "userId": 3,
346 | "id": 58,
347 | "title": "est dicta totam qui explicabo doloribus qui dignissimos",
348 | "completed": false
349 | },
350 | {
351 | "userId": 3,
352 | "id": 59,
353 | "title": "perspiciatis velit id laborum placeat iusto et aliquam odio",
354 | "completed": false
355 | },
356 | {
357 | "userId": 3,
358 | "id": 60,
359 | "title": "et sequi qui architecto ut adipisci",
360 | "completed": true
361 | },
362 | {
363 | "userId": 4,
364 | "id": 61,
365 | "title": "odit optio omnis qui sunt",
366 | "completed": true
367 | },
368 | {
369 | "userId": 4,
370 | "id": 62,
371 | "title": "et placeat et tempore aspernatur sint numquam",
372 | "completed": false
373 | },
374 | {
375 | "userId": 4,
376 | "id": 63,
377 | "title": "doloremque aut dolores quidem fuga qui nulla",
378 | "completed": true
379 | },
380 | {
381 | "userId": 4,
382 | "id": 64,
383 | "title": "voluptas consequatur qui ut quia magnam nemo esse",
384 | "completed": false
385 | },
386 | {
387 | "userId": 4,
388 | "id": 65,
389 | "title": "fugiat pariatur ratione ut asperiores necessitatibus magni",
390 | "completed": false
391 | },
392 | {
393 | "userId": 4,
394 | "id": 66,
395 | "title": "rerum eum molestias autem voluptatum sit optio",
396 | "completed": false
397 | },
398 | {
399 | "userId": 4,
400 | "id": 67,
401 | "title": "quia voluptatibus voluptatem quos similique maiores repellat",
402 | "completed": false
403 | },
404 | {
405 | "userId": 4,
406 | "id": 68,
407 | "title": "aut id perspiciatis voluptatem iusto",
408 | "completed": false
409 | },
410 | {
411 | "userId": 4,
412 | "id": 69,
413 | "title": "doloribus sint dolorum ab adipisci itaque dignissimos aliquam suscipit",
414 | "completed": false
415 | },
416 | {
417 | "userId": 4,
418 | "id": 70,
419 | "title": "ut sequi accusantium et mollitia delectus sunt",
420 | "completed": false
421 | },
422 | {
423 | "userId": 4,
424 | "id": 71,
425 | "title": "aut velit saepe ullam",
426 | "completed": false
427 | },
428 | {
429 | "userId": 4,
430 | "id": 72,
431 | "title": "praesentium facilis facere quis harum voluptatibus voluptatem eum",
432 | "completed": false
433 | },
434 | {
435 | "userId": 4,
436 | "id": 73,
437 | "title": "sint amet quia totam corporis qui exercitationem commodi",
438 | "completed": true
439 | },
440 | {
441 | "userId": 4,
442 | "id": 74,
443 | "title": "expedita tempore nobis eveniet laborum maiores",
444 | "completed": false
445 | },
446 | {
447 | "userId": 4,
448 | "id": 75,
449 | "title": "occaecati adipisci est possimus totam",
450 | "completed": false
451 | },
452 | {
453 | "userId": 4,
454 | "id": 76,
455 | "title": "sequi dolorem sed",
456 | "completed": true
457 | },
458 | {
459 | "userId": 4,
460 | "id": 77,
461 | "title": "maiores aut nesciunt delectus exercitationem vel assumenda eligendi at",
462 | "completed": false
463 | },
464 | {
465 | "userId": 4,
466 | "id": 78,
467 | "title": "reiciendis est magnam amet nemo iste recusandae impedit quaerat",
468 | "completed": false
469 | },
470 | {
471 | "userId": 4,
472 | "id": 79,
473 | "title": "eum ipsa maxime ut",
474 | "completed": true
475 | },
476 | {
477 | "userId": 4,
478 | "id": 80,
479 | "title": "tempore molestias dolores rerum sequi voluptates ipsum consequatur",
480 | "completed": true
481 | },
482 | {
483 | "userId": 5,
484 | "id": 81,
485 | "title": "suscipit qui totam",
486 | "completed": true
487 | },
488 | {
489 | "userId": 5,
490 | "id": 82,
491 | "title": "voluptates eum voluptas et dicta",
492 | "completed": false
493 | },
494 | {
495 | "userId": 5,
496 | "id": 83,
497 | "title": "quidem at rerum quis ex aut sit quam",
498 | "completed": true
499 | },
500 | {
501 | "userId": 5,
502 | "id": 84,
503 | "title": "sunt veritatis ut voluptate",
504 | "completed": false
505 | },
506 | {
507 | "userId": 5,
508 | "id": 85,
509 | "title": "et quia ad iste a",
510 | "completed": true
511 | },
512 | {
513 | "userId": 5,
514 | "id": 86,
515 | "title": "incidunt ut saepe autem",
516 | "completed": true
517 | },
518 | {
519 | "userId": 5,
520 | "id": 87,
521 | "title": "laudantium quae eligendi consequatur quia et vero autem",
522 | "completed": true
523 | },
524 | {
525 | "userId": 5,
526 | "id": 88,
527 | "title": "vitae aut excepturi laboriosam sint aliquam et et accusantium",
528 | "completed": false
529 | },
530 | {
531 | "userId": 5,
532 | "id": 89,
533 | "title": "sequi ut omnis et",
534 | "completed": true
535 | },
536 | {
537 | "userId": 5,
538 | "id": 90,
539 | "title": "molestiae nisi accusantium tenetur dolorem et",
540 | "completed": true
541 | },
542 | {
543 | "userId": 5,
544 | "id": 91,
545 | "title": "nulla quis consequatur saepe qui id expedita",
546 | "completed": true
547 | },
548 | {
549 | "userId": 5,
550 | "id": 92,
551 | "title": "in omnis laboriosam",
552 | "completed": true
553 | },
554 | {
555 | "userId": 5,
556 | "id": 93,
557 | "title": "odio iure consequatur molestiae quibusdam necessitatibus quia sint",
558 | "completed": true
559 | },
560 | {
561 | "userId": 5,
562 | "id": 94,
563 | "title": "facilis modi saepe mollitia",
564 | "completed": false
565 | },
566 | {
567 | "userId": 5,
568 | "id": 95,
569 | "title": "vel nihil et molestiae iusto assumenda nemo quo ut",
570 | "completed": true
571 | },
572 | {
573 | "userId": 5,
574 | "id": 96,
575 | "title": "nobis suscipit ducimus enim asperiores voluptas",
576 | "completed": false
577 | },
578 | {
579 | "userId": 5,
580 | "id": 97,
581 | "title": "dolorum laboriosam eos qui iure aliquam",
582 | "completed": false
583 | },
584 | {
585 | "userId": 5,
586 | "id": 98,
587 | "title": "debitis accusantium ut quo facilis nihil quis sapiente necessitatibus",
588 | "completed": true
589 | },
590 | {
591 | "userId": 5,
592 | "id": 99,
593 | "title": "neque voluptates ratione",
594 | "completed": false
595 | },
596 | {
597 | "userId": 5,
598 | "id": 100,
599 | "title": "excepturi a et neque qui expedita vel voluptate",
600 | "completed": false
601 | },
602 | {
603 | "userId": 6,
604 | "id": 101,
605 | "title": "explicabo enim cumque porro aperiam occaecati minima",
606 | "completed": false
607 | },
608 | {
609 | "userId": 6,
610 | "id": 102,
611 | "title": "sed ab consequatur",
612 | "completed": false
613 | },
614 | {
615 | "userId": 6,
616 | "id": 103,
617 | "title": "non sunt delectus illo nulla tenetur enim omnis",
618 | "completed": false
619 | },
620 | {
621 | "userId": 6,
622 | "id": 104,
623 | "title": "excepturi non laudantium quo",
624 | "completed": false
625 | },
626 | {
627 | "userId": 6,
628 | "id": 105,
629 | "title": "totam quia dolorem et illum repellat voluptas optio",
630 | "completed": true
631 | },
632 | {
633 | "userId": 6,
634 | "id": 106,
635 | "title": "ad illo quis voluptatem temporibus",
636 | "completed": true
637 | },
638 | {
639 | "userId": 6,
640 | "id": 107,
641 | "title": "praesentium facilis omnis laudantium fugit ad iusto nihil nesciunt",
642 | "completed": false
643 | },
644 | {
645 | "userId": 6,
646 | "id": 108,
647 | "title": "a eos eaque nihil et exercitationem incidunt delectus",
648 | "completed": true
649 | },
650 | {
651 | "userId": 6,
652 | "id": 109,
653 | "title": "autem temporibus harum quisquam in culpa",
654 | "completed": true
655 | },
656 | {
657 | "userId": 6,
658 | "id": 110,
659 | "title": "aut aut ea corporis",
660 | "completed": true
661 | },
662 | {
663 | "userId": 6,
664 | "id": 111,
665 | "title": "magni accusantium labore et id quis provident",
666 | "completed": false
667 | },
668 | {
669 | "userId": 6,
670 | "id": 112,
671 | "title": "consectetur impedit quisquam qui deserunt non rerum consequuntur eius",
672 | "completed": false
673 | },
674 | {
675 | "userId": 6,
676 | "id": 113,
677 | "title": "quia atque aliquam sunt impedit voluptatum rerum assumenda nisi",
678 | "completed": false
679 | },
680 | {
681 | "userId": 6,
682 | "id": 114,
683 | "title": "cupiditate quos possimus corporis quisquam exercitationem beatae",
684 | "completed": false
685 | },
686 | {
687 | "userId": 6,
688 | "id": 115,
689 | "title": "sed et ea eum",
690 | "completed": false
691 | },
692 | {
693 | "userId": 6,
694 | "id": 116,
695 | "title": "ipsa dolores vel facilis ut",
696 | "completed": true
697 | },
698 | {
699 | "userId": 6,
700 | "id": 117,
701 | "title": "sequi quae est et qui qui eveniet asperiores",
702 | "completed": false
703 | },
704 | {
705 | "userId": 6,
706 | "id": 118,
707 | "title": "quia modi consequatur vero fugiat",
708 | "completed": false
709 | },
710 | {
711 | "userId": 6,
712 | "id": 119,
713 | "title": "corporis ducimus ea perspiciatis iste",
714 | "completed": false
715 | },
716 | {
717 | "userId": 6,
718 | "id": 120,
719 | "title": "dolorem laboriosam vel voluptas et aliquam quasi",
720 | "completed": false
721 | },
722 | {
723 | "userId": 7,
724 | "id": 121,
725 | "title": "inventore aut nihil minima laudantium hic qui omnis",
726 | "completed": true
727 | },
728 | {
729 | "userId": 7,
730 | "id": 122,
731 | "title": "provident aut nobis culpa",
732 | "completed": true
733 | },
734 | {
735 | "userId": 7,
736 | "id": 123,
737 | "title": "esse et quis iste est earum aut impedit",
738 | "completed": false
739 | },
740 | {
741 | "userId": 7,
742 | "id": 124,
743 | "title": "qui consectetur id",
744 | "completed": false
745 | },
746 | {
747 | "userId": 7,
748 | "id": 125,
749 | "title": "aut quasi autem iste tempore illum possimus",
750 | "completed": false
751 | },
752 | {
753 | "userId": 7,
754 | "id": 126,
755 | "title": "ut asperiores perspiciatis veniam ipsum rerum saepe",
756 | "completed": true
757 | },
758 | {
759 | "userId": 7,
760 | "id": 127,
761 | "title": "voluptatem libero consectetur rerum ut",
762 | "completed": true
763 | },
764 | {
765 | "userId": 7,
766 | "id": 128,
767 | "title": "eius omnis est qui voluptatem autem",
768 | "completed": false
769 | },
770 | {
771 | "userId": 7,
772 | "id": 129,
773 | "title": "rerum culpa quis harum",
774 | "completed": false
775 | },
776 | {
777 | "userId": 7,
778 | "id": 130,
779 | "title": "nulla aliquid eveniet harum laborum libero alias ut unde",
780 | "completed": true
781 | },
782 | {
783 | "userId": 7,
784 | "id": 131,
785 | "title": "qui ea incidunt quis",
786 | "completed": false
787 | },
788 | {
789 | "userId": 7,
790 | "id": 132,
791 | "title": "qui molestiae voluptatibus velit iure harum quisquam",
792 | "completed": true
793 | },
794 | {
795 | "userId": 7,
796 | "id": 133,
797 | "title": "et labore eos enim rerum consequatur sunt",
798 | "completed": true
799 | },
800 | {
801 | "userId": 7,
802 | "id": 134,
803 | "title": "molestiae doloribus et laborum quod ea",
804 | "completed": false
805 | },
806 | {
807 | "userId": 7,
808 | "id": 135,
809 | "title": "facere ipsa nam eum voluptates reiciendis vero qui",
810 | "completed": false
811 | },
812 | {
813 | "userId": 7,
814 | "id": 136,
815 | "title": "asperiores illo tempora fuga sed ut quasi adipisci",
816 | "completed": false
817 | },
818 | {
819 | "userId": 7,
820 | "id": 137,
821 | "title": "qui sit non",
822 | "completed": false
823 | },
824 | {
825 | "userId": 7,
826 | "id": 138,
827 | "title": "placeat minima consequatur rem qui ut",
828 | "completed": true
829 | },
830 | {
831 | "userId": 7,
832 | "id": 139,
833 | "title": "consequatur doloribus id possimus voluptas a voluptatem",
834 | "completed": false
835 | },
836 | {
837 | "userId": 7,
838 | "id": 140,
839 | "title": "aut consectetur in blanditiis deserunt quia sed laboriosam",
840 | "completed": true
841 | },
842 | {
843 | "userId": 8,
844 | "id": 141,
845 | "title": "explicabo consectetur debitis voluptates quas quae culpa rerum non",
846 | "completed": true
847 | },
848 | {
849 | "userId": 8,
850 | "id": 142,
851 | "title": "maiores accusantium architecto necessitatibus reiciendis ea aut",
852 | "completed": true
853 | },
854 | {
855 | "userId": 8,
856 | "id": 143,
857 | "title": "eum non recusandae cupiditate animi",
858 | "completed": false
859 | },
860 | {
861 | "userId": 8,
862 | "id": 144,
863 | "title": "ut eum exercitationem sint",
864 | "completed": false
865 | },
866 | {
867 | "userId": 8,
868 | "id": 145,
869 | "title": "beatae qui ullam incidunt voluptatem non nisi aliquam",
870 | "completed": false
871 | },
872 | {
873 | "userId": 8,
874 | "id": 146,
875 | "title": "molestiae suscipit ratione nihil odio libero impedit vero totam",
876 | "completed": true
877 | },
878 | {
879 | "userId": 8,
880 | "id": 147,
881 | "title": "eum itaque quod reprehenderit et facilis dolor autem ut",
882 | "completed": true
883 | },
884 | {
885 | "userId": 8,
886 | "id": 148,
887 | "title": "esse quas et quo quasi exercitationem",
888 | "completed": false
889 | },
890 | {
891 | "userId": 8,
892 | "id": 149,
893 | "title": "animi voluptas quod perferendis est",
894 | "completed": false
895 | },
896 | {
897 | "userId": 8,
898 | "id": 150,
899 | "title": "eos amet tempore laudantium fugit a",
900 | "completed": false
901 | },
902 | {
903 | "userId": 8,
904 | "id": 151,
905 | "title": "accusamus adipisci dicta qui quo ea explicabo sed vero",
906 | "completed": true
907 | },
908 | {
909 | "userId": 8,
910 | "id": 152,
911 | "title": "odit eligendi recusandae doloremque cumque non",
912 | "completed": false
913 | },
914 | {
915 | "userId": 8,
916 | "id": 153,
917 | "title": "ea aperiam consequatur qui repellat eos",
918 | "completed": false
919 | },
920 | {
921 | "userId": 8,
922 | "id": 154,
923 | "title": "rerum non ex sapiente",
924 | "completed": true
925 | },
926 | {
927 | "userId": 8,
928 | "id": 155,
929 | "title": "voluptatem nobis consequatur et assumenda magnam",
930 | "completed": true
931 | },
932 | {
933 | "userId": 8,
934 | "id": 156,
935 | "title": "nam quia quia nulla repellat assumenda quibusdam sit nobis",
936 | "completed": true
937 | },
938 | {
939 | "userId": 8,
940 | "id": 157,
941 | "title": "dolorem veniam quisquam deserunt repellendus",
942 | "completed": true
943 | },
944 | {
945 | "userId": 8,
946 | "id": 158,
947 | "title": "debitis vitae delectus et harum accusamus aut deleniti a",
948 | "completed": true
949 | },
950 | {
951 | "userId": 8,
952 | "id": 159,
953 | "title": "debitis adipisci quibusdam aliquam sed dolore ea praesentium nobis",
954 | "completed": true
955 | },
956 | {
957 | "userId": 8,
958 | "id": 160,
959 | "title": "et praesentium aliquam est",
960 | "completed": false
961 | },
962 | {
963 | "userId": 9,
964 | "id": 161,
965 | "title": "ex hic consequuntur earum omnis alias ut occaecati culpa",
966 | "completed": true
967 | },
968 | {
969 | "userId": 9,
970 | "id": 162,
971 | "title": "omnis laboriosam molestias animi sunt dolore",
972 | "completed": true
973 | },
974 | {
975 | "userId": 9,
976 | "id": 163,
977 | "title": "natus corrupti maxime laudantium et voluptatem laboriosam odit",
978 | "completed": false
979 | },
980 | {
981 | "userId": 9,
982 | "id": 164,
983 | "title": "reprehenderit quos aut aut consequatur est sed",
984 | "completed": false
985 | },
986 | {
987 | "userId": 9,
988 | "id": 165,
989 | "title": "fugiat perferendis sed aut quidem",
990 | "completed": false
991 | },
992 | {
993 | "userId": 9,
994 | "id": 166,
995 | "title": "quos quo possimus suscipit minima ut",
996 | "completed": false
997 | },
998 | {
999 | "userId": 9,
1000 | "id": 167,
1001 | "title": "et quis minus quo a asperiores molestiae",
1002 | "completed": false
1003 | },
1004 | {
1005 | "userId": 9,
1006 | "id": 168,
1007 | "title": "recusandae quia qui sunt libero",
1008 | "completed": false
1009 | },
1010 | {
1011 | "userId": 9,
1012 | "id": 169,
1013 | "title": "ea odio perferendis officiis",
1014 | "completed": true
1015 | },
1016 | {
1017 | "userId": 9,
1018 | "id": 170,
1019 | "title": "quisquam aliquam quia doloribus aut",
1020 | "completed": false
1021 | },
1022 | {
1023 | "userId": 9,
1024 | "id": 171,
1025 | "title": "fugiat aut voluptatibus corrupti deleniti velit iste odio",
1026 | "completed": true
1027 | },
1028 | {
1029 | "userId": 9,
1030 | "id": 172,
1031 | "title": "et provident amet rerum consectetur et voluptatum",
1032 | "completed": false
1033 | },
1034 | {
1035 | "userId": 9,
1036 | "id": 173,
1037 | "title": "harum ad aperiam quis",
1038 | "completed": false
1039 | },
1040 | {
1041 | "userId": 9,
1042 | "id": 174,
1043 | "title": "similique aut quo",
1044 | "completed": false
1045 | },
1046 | {
1047 | "userId": 9,
1048 | "id": 175,
1049 | "title": "laudantium eius officia perferendis provident perspiciatis asperiores",
1050 | "completed": true
1051 | },
1052 | {
1053 | "userId": 9,
1054 | "id": 176,
1055 | "title": "magni soluta corrupti ut maiores rem quidem",
1056 | "completed": false
1057 | },
1058 | {
1059 | "userId": 9,
1060 | "id": 177,
1061 | "title": "et placeat temporibus voluptas est tempora quos quibusdam",
1062 | "completed": false
1063 | },
1064 | {
1065 | "userId": 9,
1066 | "id": 178,
1067 | "title": "nesciunt itaque commodi tempore",
1068 | "completed": true
1069 | },
1070 | {
1071 | "userId": 9,
1072 | "id": 179,
1073 | "title": "omnis consequuntur cupiditate impedit itaque ipsam quo",
1074 | "completed": true
1075 | },
1076 | {
1077 | "userId": 9,
1078 | "id": 180,
1079 | "title": "debitis nisi et dolorem repellat et",
1080 | "completed": true
1081 | },
1082 | {
1083 | "userId": 10,
1084 | "id": 181,
1085 | "title": "ut cupiditate sequi aliquam fuga maiores",
1086 | "completed": false
1087 | },
1088 | {
1089 | "userId": 10,
1090 | "id": 182,
1091 | "title": "inventore saepe cumque et aut illum enim",
1092 | "completed": true
1093 | },
1094 | {
1095 | "userId": 10,
1096 | "id": 183,
1097 | "title": "omnis nulla eum aliquam distinctio",
1098 | "completed": true
1099 | },
1100 | {
1101 | "userId": 10,
1102 | "id": 184,
1103 | "title": "molestias modi perferendis perspiciatis",
1104 | "completed": false
1105 | },
1106 | {
1107 | "userId": 10,
1108 | "id": 185,
1109 | "title": "voluptates dignissimos sed doloribus animi quaerat aut",
1110 | "completed": false
1111 | },
1112 | {
1113 | "userId": 10,
1114 | "id": 186,
1115 | "title": "explicabo odio est et",
1116 | "completed": false
1117 | },
1118 | {
1119 | "userId": 10,
1120 | "id": 187,
1121 | "title": "consequuntur animi possimus",
1122 | "completed": false
1123 | },
1124 | {
1125 | "userId": 10,
1126 | "id": 188,
1127 | "title": "vel non beatae est",
1128 | "completed": true
1129 | },
1130 | {
1131 | "userId": 10,
1132 | "id": 189,
1133 | "title": "culpa eius et voluptatem et",
1134 | "completed": true
1135 | },
1136 | {
1137 | "userId": 10,
1138 | "id": 190,
1139 | "title": "accusamus sint iusto et voluptatem exercitationem",
1140 | "completed": true
1141 | },
1142 | {
1143 | "userId": 10,
1144 | "id": 191,
1145 | "title": "temporibus atque distinctio omnis eius impedit tempore molestias pariatur",
1146 | "completed": true
1147 | },
1148 | {
1149 | "userId": 10,
1150 | "id": 192,
1151 | "title": "ut quas possimus exercitationem sint voluptates",
1152 | "completed": false
1153 | },
1154 | {
1155 | "userId": 10,
1156 | "id": 193,
1157 | "title": "rerum debitis voluptatem qui eveniet tempora distinctio a",
1158 | "completed": true
1159 | },
1160 | {
1161 | "userId": 10,
1162 | "id": 194,
1163 | "title": "sed ut vero sit molestiae",
1164 | "completed": false
1165 | },
1166 | {
1167 | "userId": 10,
1168 | "id": 195,
1169 | "title": "rerum ex veniam mollitia voluptatibus pariatur",
1170 | "completed": true
1171 | },
1172 | {
1173 | "userId": 10,
1174 | "id": 196,
1175 | "title": "consequuntur aut ut fugit similique",
1176 | "completed": true
1177 | },
1178 | {
1179 | "userId": 10,
1180 | "id": 197,
1181 | "title": "dignissimos quo nobis earum saepe",
1182 | "completed": true
1183 | },
1184 | {
1185 | "userId": 10,
1186 | "id": 198,
1187 | "title": "quis eius est sint explicabo",
1188 | "completed": true
1189 | },
1190 | {
1191 | "userId": 10,
1192 | "id": 199,
1193 | "title": "numquam repellendus a magnam",
1194 | "completed": true
1195 | },
1196 | {
1197 | "userId": 10,
1198 | "id": 200,
1199 | "title": "ipsam aperiam voluptates qui",
1200 | "completed": false
1201 | }
1202 | ]
--------------------------------------------------------------------------------
/benchmarks/e2e/src/data/todos.proto:
--------------------------------------------------------------------------------
1 | // todos.proto
2 | package todos;
3 | syntax = "proto3";
4 |
5 | message TodoMessage {
6 | int32 id = 3;
7 | int32 userId = 2;
8 | string title = 1;
9 | bool completed = 4;
10 | }
11 |
12 | message TodosMessage {
13 | repeated TodoMessage objects = 1;
14 | }
15 |
--------------------------------------------------------------------------------
/benchmarks/e2e/src/index.ts:
--------------------------------------------------------------------------------
1 | import fastify from 'fastify'
2 | import { readFile } from 'fs/promises'
3 | import { createSer } from 'seqproto'
4 | import avro from 'avsc'
5 | import fStatic from '@fastify/static'
6 | import path from 'node:path'
7 |
8 | // Load data
9 |
10 | interface Todo {
11 | userId: number
12 | id: number
13 | title: string
14 | completed: boolean
15 | }
16 | const todos: Todo[] = JSON.parse(await readFile('./src/data/todos.json', 'utf8'))
17 |
18 | // Avro schema
19 | const type = avro.Type.forSchema({
20 | name: 'Todos',
21 | type: 'array',
22 | items: {
23 | name: 'Todo',
24 | type: 'record',
25 | fields: [
26 | { name: 'userId', type: 'int' },
27 | { name: 'id', type: 'int' },
28 | { name: 'title', type: 'string' },
29 | { name: 'completed', type: 'boolean' }
30 | ]
31 | }
32 | })
33 |
34 | // seqproto serializer
35 | const ser = createSer()
36 |
37 | const server = fastify()
38 |
39 | server.register(fStatic, {
40 | root: path.resolve('public'),
41 | prefix: '/public/', // optional: default '/'
42 | cacheControl: true, // optional: default true
43 | })
44 |
45 | server.get('/seqproto.js', async (request, reply) => {
46 | reply.type('application/javascript')
47 | return readFile('./node_modules/seqproto/dist/esm/index.js', 'utf8')
48 | })
49 |
50 | server.get('/todos', {
51 | schema: {
52 | response: {
53 | 200: {
54 | type: 'array',
55 | items: {
56 | type: 'object',
57 | properties: {
58 | userId: { type: 'number' },
59 | id: { type: 'number' },
60 | title: { type: 'string' },
61 | completed: { type: 'boolean' }
62 | }
63 | }
64 | }
65 | }
66 | }
67 | }, async (request) => {
68 | if (request.headers.accept === 'application/seqproto') {
69 | ser.reset()
70 | ser.serializeArray(todos, (ser, todo) => {
71 | ser.serializeUInt32(todo.id)
72 | ser.serializeUInt32(todo.userId)
73 | ser.serializeString(todo.title)
74 | ser.serializeBoolean(todo.completed)
75 | })
76 | const arrBuffer = ser.getBuffer()
77 | return new Uint8Array(arrBuffer)
78 | }
79 | if (request.headers.accept === 'application/avro') {
80 | return type.toBuffer(todos)
81 | }
82 |
83 | return todos
84 | })
85 |
86 | const d = await server.listen({ port: 3000 })
87 | console.log(d)
88 |
89 |
--------------------------------------------------------------------------------
/benchmarks/isolated/data/todos.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "userId": 1,
4 | "id": 1,
5 | "title": "delectus aut autem",
6 | "completed": false
7 | },
8 | {
9 | "userId": 1,
10 | "id": 2,
11 | "title": "quis ut nam facilis et officia qui",
12 | "completed": false
13 | },
14 | {
15 | "userId": 1,
16 | "id": 3,
17 | "title": "fugiat veniam minus",
18 | "completed": false
19 | },
20 | {
21 | "userId": 1,
22 | "id": 4,
23 | "title": "et porro tempora",
24 | "completed": true
25 | },
26 | {
27 | "userId": 1,
28 | "id": 5,
29 | "title": "laboriosam mollitia et enim quasi adipisci quia provident illum",
30 | "completed": false
31 | },
32 | {
33 | "userId": 1,
34 | "id": 6,
35 | "title": "qui ullam ratione quibusdam voluptatem quia omnis",
36 | "completed": false
37 | },
38 | {
39 | "userId": 1,
40 | "id": 7,
41 | "title": "illo expedita consequatur quia in",
42 | "completed": false
43 | },
44 | {
45 | "userId": 1,
46 | "id": 8,
47 | "title": "quo adipisci enim quam ut ab",
48 | "completed": true
49 | },
50 | {
51 | "userId": 1,
52 | "id": 9,
53 | "title": "molestiae perspiciatis ipsa",
54 | "completed": false
55 | },
56 | {
57 | "userId": 1,
58 | "id": 10,
59 | "title": "illo est ratione doloremque quia maiores aut",
60 | "completed": true
61 | },
62 | {
63 | "userId": 1,
64 | "id": 11,
65 | "title": "vero rerum temporibus dolor",
66 | "completed": true
67 | },
68 | {
69 | "userId": 1,
70 | "id": 12,
71 | "title": "ipsa repellendus fugit nisi",
72 | "completed": true
73 | },
74 | {
75 | "userId": 1,
76 | "id": 13,
77 | "title": "et doloremque nulla",
78 | "completed": false
79 | },
80 | {
81 | "userId": 1,
82 | "id": 14,
83 | "title": "repellendus sunt dolores architecto voluptatum",
84 | "completed": true
85 | },
86 | {
87 | "userId": 1,
88 | "id": 15,
89 | "title": "ab voluptatum amet voluptas",
90 | "completed": true
91 | },
92 | {
93 | "userId": 1,
94 | "id": 16,
95 | "title": "accusamus eos facilis sint et aut voluptatem",
96 | "completed": true
97 | },
98 | {
99 | "userId": 1,
100 | "id": 17,
101 | "title": "quo laboriosam deleniti aut qui",
102 | "completed": true
103 | },
104 | {
105 | "userId": 1,
106 | "id": 18,
107 | "title": "dolorum est consequatur ea mollitia in culpa",
108 | "completed": false
109 | },
110 | {
111 | "userId": 1,
112 | "id": 19,
113 | "title": "molestiae ipsa aut voluptatibus pariatur dolor nihil",
114 | "completed": true
115 | },
116 | {
117 | "userId": 1,
118 | "id": 20,
119 | "title": "ullam nobis libero sapiente ad optio sint",
120 | "completed": true
121 | },
122 | {
123 | "userId": 2,
124 | "id": 21,
125 | "title": "suscipit repellat esse quibusdam voluptatem incidunt",
126 | "completed": false
127 | },
128 | {
129 | "userId": 2,
130 | "id": 22,
131 | "title": "distinctio vitae autem nihil ut molestias quo",
132 | "completed": true
133 | },
134 | {
135 | "userId": 2,
136 | "id": 23,
137 | "title": "et itaque necessitatibus maxime molestiae qui quas velit",
138 | "completed": false
139 | },
140 | {
141 | "userId": 2,
142 | "id": 24,
143 | "title": "adipisci non ad dicta qui amet quaerat doloribus ea",
144 | "completed": false
145 | },
146 | {
147 | "userId": 2,
148 | "id": 25,
149 | "title": "voluptas quo tenetur perspiciatis explicabo natus",
150 | "completed": true
151 | },
152 | {
153 | "userId": 2,
154 | "id": 26,
155 | "title": "aliquam aut quasi",
156 | "completed": true
157 | },
158 | {
159 | "userId": 2,
160 | "id": 27,
161 | "title": "veritatis pariatur delectus",
162 | "completed": true
163 | },
164 | {
165 | "userId": 2,
166 | "id": 28,
167 | "title": "nesciunt totam sit blanditiis sit",
168 | "completed": false
169 | },
170 | {
171 | "userId": 2,
172 | "id": 29,
173 | "title": "laborum aut in quam",
174 | "completed": false
175 | },
176 | {
177 | "userId": 2,
178 | "id": 30,
179 | "title": "nemo perspiciatis repellat ut dolor libero commodi blanditiis omnis",
180 | "completed": true
181 | },
182 | {
183 | "userId": 2,
184 | "id": 31,
185 | "title": "repudiandae totam in est sint facere fuga",
186 | "completed": false
187 | },
188 | {
189 | "userId": 2,
190 | "id": 32,
191 | "title": "earum doloribus ea doloremque quis",
192 | "completed": false
193 | },
194 | {
195 | "userId": 2,
196 | "id": 33,
197 | "title": "sint sit aut vero",
198 | "completed": false
199 | },
200 | {
201 | "userId": 2,
202 | "id": 34,
203 | "title": "porro aut necessitatibus eaque distinctio",
204 | "completed": false
205 | },
206 | {
207 | "userId": 2,
208 | "id": 35,
209 | "title": "repellendus veritatis molestias dicta incidunt",
210 | "completed": true
211 | },
212 | {
213 | "userId": 2,
214 | "id": 36,
215 | "title": "excepturi deleniti adipisci voluptatem et neque optio illum ad",
216 | "completed": true
217 | },
218 | {
219 | "userId": 2,
220 | "id": 37,
221 | "title": "sunt cum tempora",
222 | "completed": false
223 | },
224 | {
225 | "userId": 2,
226 | "id": 38,
227 | "title": "totam quia non",
228 | "completed": false
229 | },
230 | {
231 | "userId": 2,
232 | "id": 39,
233 | "title": "doloremque quibusdam asperiores libero corrupti illum qui omnis",
234 | "completed": false
235 | },
236 | {
237 | "userId": 2,
238 | "id": 40,
239 | "title": "totam atque quo nesciunt",
240 | "completed": true
241 | },
242 | {
243 | "userId": 3,
244 | "id": 41,
245 | "title": "aliquid amet impedit consequatur aspernatur placeat eaque fugiat suscipit",
246 | "completed": false
247 | },
248 | {
249 | "userId": 3,
250 | "id": 42,
251 | "title": "rerum perferendis error quia ut eveniet",
252 | "completed": false
253 | },
254 | {
255 | "userId": 3,
256 | "id": 43,
257 | "title": "tempore ut sint quis recusandae",
258 | "completed": true
259 | },
260 | {
261 | "userId": 3,
262 | "id": 44,
263 | "title": "cum debitis quis accusamus doloremque ipsa natus sapiente omnis",
264 | "completed": true
265 | },
266 | {
267 | "userId": 3,
268 | "id": 45,
269 | "title": "velit soluta adipisci molestias reiciendis harum",
270 | "completed": false
271 | },
272 | {
273 | "userId": 3,
274 | "id": 46,
275 | "title": "vel voluptatem repellat nihil placeat corporis",
276 | "completed": false
277 | },
278 | {
279 | "userId": 3,
280 | "id": 47,
281 | "title": "nam qui rerum fugiat accusamus",
282 | "completed": false
283 | },
284 | {
285 | "userId": 3,
286 | "id": 48,
287 | "title": "sit reprehenderit omnis quia",
288 | "completed": false
289 | },
290 | {
291 | "userId": 3,
292 | "id": 49,
293 | "title": "ut necessitatibus aut maiores debitis officia blanditiis velit et",
294 | "completed": false
295 | },
296 | {
297 | "userId": 3,
298 | "id": 50,
299 | "title": "cupiditate necessitatibus ullam aut quis dolor voluptate",
300 | "completed": true
301 | },
302 | {
303 | "userId": 3,
304 | "id": 51,
305 | "title": "distinctio exercitationem ab doloribus",
306 | "completed": false
307 | },
308 | {
309 | "userId": 3,
310 | "id": 52,
311 | "title": "nesciunt dolorum quis recusandae ad pariatur ratione",
312 | "completed": false
313 | },
314 | {
315 | "userId": 3,
316 | "id": 53,
317 | "title": "qui labore est occaecati recusandae aliquid quam",
318 | "completed": false
319 | },
320 | {
321 | "userId": 3,
322 | "id": 54,
323 | "title": "quis et est ut voluptate quam dolor",
324 | "completed": true
325 | },
326 | {
327 | "userId": 3,
328 | "id": 55,
329 | "title": "voluptatum omnis minima qui occaecati provident nulla voluptatem ratione",
330 | "completed": true
331 | },
332 | {
333 | "userId": 3,
334 | "id": 56,
335 | "title": "deleniti ea temporibus enim",
336 | "completed": true
337 | },
338 | {
339 | "userId": 3,
340 | "id": 57,
341 | "title": "pariatur et magnam ea doloribus similique voluptatem rerum quia",
342 | "completed": false
343 | },
344 | {
345 | "userId": 3,
346 | "id": 58,
347 | "title": "est dicta totam qui explicabo doloribus qui dignissimos",
348 | "completed": false
349 | },
350 | {
351 | "userId": 3,
352 | "id": 59,
353 | "title": "perspiciatis velit id laborum placeat iusto et aliquam odio",
354 | "completed": false
355 | },
356 | {
357 | "userId": 3,
358 | "id": 60,
359 | "title": "et sequi qui architecto ut adipisci",
360 | "completed": true
361 | },
362 | {
363 | "userId": 4,
364 | "id": 61,
365 | "title": "odit optio omnis qui sunt",
366 | "completed": true
367 | },
368 | {
369 | "userId": 4,
370 | "id": 62,
371 | "title": "et placeat et tempore aspernatur sint numquam",
372 | "completed": false
373 | },
374 | {
375 | "userId": 4,
376 | "id": 63,
377 | "title": "doloremque aut dolores quidem fuga qui nulla",
378 | "completed": true
379 | },
380 | {
381 | "userId": 4,
382 | "id": 64,
383 | "title": "voluptas consequatur qui ut quia magnam nemo esse",
384 | "completed": false
385 | },
386 | {
387 | "userId": 4,
388 | "id": 65,
389 | "title": "fugiat pariatur ratione ut asperiores necessitatibus magni",
390 | "completed": false
391 | },
392 | {
393 | "userId": 4,
394 | "id": 66,
395 | "title": "rerum eum molestias autem voluptatum sit optio",
396 | "completed": false
397 | },
398 | {
399 | "userId": 4,
400 | "id": 67,
401 | "title": "quia voluptatibus voluptatem quos similique maiores repellat",
402 | "completed": false
403 | },
404 | {
405 | "userId": 4,
406 | "id": 68,
407 | "title": "aut id perspiciatis voluptatem iusto",
408 | "completed": false
409 | },
410 | {
411 | "userId": 4,
412 | "id": 69,
413 | "title": "doloribus sint dolorum ab adipisci itaque dignissimos aliquam suscipit",
414 | "completed": false
415 | },
416 | {
417 | "userId": 4,
418 | "id": 70,
419 | "title": "ut sequi accusantium et mollitia delectus sunt",
420 | "completed": false
421 | },
422 | {
423 | "userId": 4,
424 | "id": 71,
425 | "title": "aut velit saepe ullam",
426 | "completed": false
427 | },
428 | {
429 | "userId": 4,
430 | "id": 72,
431 | "title": "praesentium facilis facere quis harum voluptatibus voluptatem eum",
432 | "completed": false
433 | },
434 | {
435 | "userId": 4,
436 | "id": 73,
437 | "title": "sint amet quia totam corporis qui exercitationem commodi",
438 | "completed": true
439 | },
440 | {
441 | "userId": 4,
442 | "id": 74,
443 | "title": "expedita tempore nobis eveniet laborum maiores",
444 | "completed": false
445 | },
446 | {
447 | "userId": 4,
448 | "id": 75,
449 | "title": "occaecati adipisci est possimus totam",
450 | "completed": false
451 | },
452 | {
453 | "userId": 4,
454 | "id": 76,
455 | "title": "sequi dolorem sed",
456 | "completed": true
457 | },
458 | {
459 | "userId": 4,
460 | "id": 77,
461 | "title": "maiores aut nesciunt delectus exercitationem vel assumenda eligendi at",
462 | "completed": false
463 | },
464 | {
465 | "userId": 4,
466 | "id": 78,
467 | "title": "reiciendis est magnam amet nemo iste recusandae impedit quaerat",
468 | "completed": false
469 | },
470 | {
471 | "userId": 4,
472 | "id": 79,
473 | "title": "eum ipsa maxime ut",
474 | "completed": true
475 | },
476 | {
477 | "userId": 4,
478 | "id": 80,
479 | "title": "tempore molestias dolores rerum sequi voluptates ipsum consequatur",
480 | "completed": true
481 | },
482 | {
483 | "userId": 5,
484 | "id": 81,
485 | "title": "suscipit qui totam",
486 | "completed": true
487 | },
488 | {
489 | "userId": 5,
490 | "id": 82,
491 | "title": "voluptates eum voluptas et dicta",
492 | "completed": false
493 | },
494 | {
495 | "userId": 5,
496 | "id": 83,
497 | "title": "quidem at rerum quis ex aut sit quam",
498 | "completed": true
499 | },
500 | {
501 | "userId": 5,
502 | "id": 84,
503 | "title": "sunt veritatis ut voluptate",
504 | "completed": false
505 | },
506 | {
507 | "userId": 5,
508 | "id": 85,
509 | "title": "et quia ad iste a",
510 | "completed": true
511 | },
512 | {
513 | "userId": 5,
514 | "id": 86,
515 | "title": "incidunt ut saepe autem",
516 | "completed": true
517 | },
518 | {
519 | "userId": 5,
520 | "id": 87,
521 | "title": "laudantium quae eligendi consequatur quia et vero autem",
522 | "completed": true
523 | },
524 | {
525 | "userId": 5,
526 | "id": 88,
527 | "title": "vitae aut excepturi laboriosam sint aliquam et et accusantium",
528 | "completed": false
529 | },
530 | {
531 | "userId": 5,
532 | "id": 89,
533 | "title": "sequi ut omnis et",
534 | "completed": true
535 | },
536 | {
537 | "userId": 5,
538 | "id": 90,
539 | "title": "molestiae nisi accusantium tenetur dolorem et",
540 | "completed": true
541 | },
542 | {
543 | "userId": 5,
544 | "id": 91,
545 | "title": "nulla quis consequatur saepe qui id expedita",
546 | "completed": true
547 | },
548 | {
549 | "userId": 5,
550 | "id": 92,
551 | "title": "in omnis laboriosam",
552 | "completed": true
553 | },
554 | {
555 | "userId": 5,
556 | "id": 93,
557 | "title": "odio iure consequatur molestiae quibusdam necessitatibus quia sint",
558 | "completed": true
559 | },
560 | {
561 | "userId": 5,
562 | "id": 94,
563 | "title": "facilis modi saepe mollitia",
564 | "completed": false
565 | },
566 | {
567 | "userId": 5,
568 | "id": 95,
569 | "title": "vel nihil et molestiae iusto assumenda nemo quo ut",
570 | "completed": true
571 | },
572 | {
573 | "userId": 5,
574 | "id": 96,
575 | "title": "nobis suscipit ducimus enim asperiores voluptas",
576 | "completed": false
577 | },
578 | {
579 | "userId": 5,
580 | "id": 97,
581 | "title": "dolorum laboriosam eos qui iure aliquam",
582 | "completed": false
583 | },
584 | {
585 | "userId": 5,
586 | "id": 98,
587 | "title": "debitis accusantium ut quo facilis nihil quis sapiente necessitatibus",
588 | "completed": true
589 | },
590 | {
591 | "userId": 5,
592 | "id": 99,
593 | "title": "neque voluptates ratione",
594 | "completed": false
595 | },
596 | {
597 | "userId": 5,
598 | "id": 100,
599 | "title": "excepturi a et neque qui expedita vel voluptate",
600 | "completed": false
601 | },
602 | {
603 | "userId": 6,
604 | "id": 101,
605 | "title": "explicabo enim cumque porro aperiam occaecati minima",
606 | "completed": false
607 | },
608 | {
609 | "userId": 6,
610 | "id": 102,
611 | "title": "sed ab consequatur",
612 | "completed": false
613 | },
614 | {
615 | "userId": 6,
616 | "id": 103,
617 | "title": "non sunt delectus illo nulla tenetur enim omnis",
618 | "completed": false
619 | },
620 | {
621 | "userId": 6,
622 | "id": 104,
623 | "title": "excepturi non laudantium quo",
624 | "completed": false
625 | },
626 | {
627 | "userId": 6,
628 | "id": 105,
629 | "title": "totam quia dolorem et illum repellat voluptas optio",
630 | "completed": true
631 | },
632 | {
633 | "userId": 6,
634 | "id": 106,
635 | "title": "ad illo quis voluptatem temporibus",
636 | "completed": true
637 | },
638 | {
639 | "userId": 6,
640 | "id": 107,
641 | "title": "praesentium facilis omnis laudantium fugit ad iusto nihil nesciunt",
642 | "completed": false
643 | },
644 | {
645 | "userId": 6,
646 | "id": 108,
647 | "title": "a eos eaque nihil et exercitationem incidunt delectus",
648 | "completed": true
649 | },
650 | {
651 | "userId": 6,
652 | "id": 109,
653 | "title": "autem temporibus harum quisquam in culpa",
654 | "completed": true
655 | },
656 | {
657 | "userId": 6,
658 | "id": 110,
659 | "title": "aut aut ea corporis",
660 | "completed": true
661 | },
662 | {
663 | "userId": 6,
664 | "id": 111,
665 | "title": "magni accusantium labore et id quis provident",
666 | "completed": false
667 | },
668 | {
669 | "userId": 6,
670 | "id": 112,
671 | "title": "consectetur impedit quisquam qui deserunt non rerum consequuntur eius",
672 | "completed": false
673 | },
674 | {
675 | "userId": 6,
676 | "id": 113,
677 | "title": "quia atque aliquam sunt impedit voluptatum rerum assumenda nisi",
678 | "completed": false
679 | },
680 | {
681 | "userId": 6,
682 | "id": 114,
683 | "title": "cupiditate quos possimus corporis quisquam exercitationem beatae",
684 | "completed": false
685 | },
686 | {
687 | "userId": 6,
688 | "id": 115,
689 | "title": "sed et ea eum",
690 | "completed": false
691 | },
692 | {
693 | "userId": 6,
694 | "id": 116,
695 | "title": "ipsa dolores vel facilis ut",
696 | "completed": true
697 | },
698 | {
699 | "userId": 6,
700 | "id": 117,
701 | "title": "sequi quae est et qui qui eveniet asperiores",
702 | "completed": false
703 | },
704 | {
705 | "userId": 6,
706 | "id": 118,
707 | "title": "quia modi consequatur vero fugiat",
708 | "completed": false
709 | },
710 | {
711 | "userId": 6,
712 | "id": 119,
713 | "title": "corporis ducimus ea perspiciatis iste",
714 | "completed": false
715 | },
716 | {
717 | "userId": 6,
718 | "id": 120,
719 | "title": "dolorem laboriosam vel voluptas et aliquam quasi",
720 | "completed": false
721 | },
722 | {
723 | "userId": 7,
724 | "id": 121,
725 | "title": "inventore aut nihil minima laudantium hic qui omnis",
726 | "completed": true
727 | },
728 | {
729 | "userId": 7,
730 | "id": 122,
731 | "title": "provident aut nobis culpa",
732 | "completed": true
733 | },
734 | {
735 | "userId": 7,
736 | "id": 123,
737 | "title": "esse et quis iste est earum aut impedit",
738 | "completed": false
739 | },
740 | {
741 | "userId": 7,
742 | "id": 124,
743 | "title": "qui consectetur id",
744 | "completed": false
745 | },
746 | {
747 | "userId": 7,
748 | "id": 125,
749 | "title": "aut quasi autem iste tempore illum possimus",
750 | "completed": false
751 | },
752 | {
753 | "userId": 7,
754 | "id": 126,
755 | "title": "ut asperiores perspiciatis veniam ipsum rerum saepe",
756 | "completed": true
757 | },
758 | {
759 | "userId": 7,
760 | "id": 127,
761 | "title": "voluptatem libero consectetur rerum ut",
762 | "completed": true
763 | },
764 | {
765 | "userId": 7,
766 | "id": 128,
767 | "title": "eius omnis est qui voluptatem autem",
768 | "completed": false
769 | },
770 | {
771 | "userId": 7,
772 | "id": 129,
773 | "title": "rerum culpa quis harum",
774 | "completed": false
775 | },
776 | {
777 | "userId": 7,
778 | "id": 130,
779 | "title": "nulla aliquid eveniet harum laborum libero alias ut unde",
780 | "completed": true
781 | },
782 | {
783 | "userId": 7,
784 | "id": 131,
785 | "title": "qui ea incidunt quis",
786 | "completed": false
787 | },
788 | {
789 | "userId": 7,
790 | "id": 132,
791 | "title": "qui molestiae voluptatibus velit iure harum quisquam",
792 | "completed": true
793 | },
794 | {
795 | "userId": 7,
796 | "id": 133,
797 | "title": "et labore eos enim rerum consequatur sunt",
798 | "completed": true
799 | },
800 | {
801 | "userId": 7,
802 | "id": 134,
803 | "title": "molestiae doloribus et laborum quod ea",
804 | "completed": false
805 | },
806 | {
807 | "userId": 7,
808 | "id": 135,
809 | "title": "facere ipsa nam eum voluptates reiciendis vero qui",
810 | "completed": false
811 | },
812 | {
813 | "userId": 7,
814 | "id": 136,
815 | "title": "asperiores illo tempora fuga sed ut quasi adipisci",
816 | "completed": false
817 | },
818 | {
819 | "userId": 7,
820 | "id": 137,
821 | "title": "qui sit non",
822 | "completed": false
823 | },
824 | {
825 | "userId": 7,
826 | "id": 138,
827 | "title": "placeat minima consequatur rem qui ut",
828 | "completed": true
829 | },
830 | {
831 | "userId": 7,
832 | "id": 139,
833 | "title": "consequatur doloribus id possimus voluptas a voluptatem",
834 | "completed": false
835 | },
836 | {
837 | "userId": 7,
838 | "id": 140,
839 | "title": "aut consectetur in blanditiis deserunt quia sed laboriosam",
840 | "completed": true
841 | },
842 | {
843 | "userId": 8,
844 | "id": 141,
845 | "title": "explicabo consectetur debitis voluptates quas quae culpa rerum non",
846 | "completed": true
847 | },
848 | {
849 | "userId": 8,
850 | "id": 142,
851 | "title": "maiores accusantium architecto necessitatibus reiciendis ea aut",
852 | "completed": true
853 | },
854 | {
855 | "userId": 8,
856 | "id": 143,
857 | "title": "eum non recusandae cupiditate animi",
858 | "completed": false
859 | },
860 | {
861 | "userId": 8,
862 | "id": 144,
863 | "title": "ut eum exercitationem sint",
864 | "completed": false
865 | },
866 | {
867 | "userId": 8,
868 | "id": 145,
869 | "title": "beatae qui ullam incidunt voluptatem non nisi aliquam",
870 | "completed": false
871 | },
872 | {
873 | "userId": 8,
874 | "id": 146,
875 | "title": "molestiae suscipit ratione nihil odio libero impedit vero totam",
876 | "completed": true
877 | },
878 | {
879 | "userId": 8,
880 | "id": 147,
881 | "title": "eum itaque quod reprehenderit et facilis dolor autem ut",
882 | "completed": true
883 | },
884 | {
885 | "userId": 8,
886 | "id": 148,
887 | "title": "esse quas et quo quasi exercitationem",
888 | "completed": false
889 | },
890 | {
891 | "userId": 8,
892 | "id": 149,
893 | "title": "animi voluptas quod perferendis est",
894 | "completed": false
895 | },
896 | {
897 | "userId": 8,
898 | "id": 150,
899 | "title": "eos amet tempore laudantium fugit a",
900 | "completed": false
901 | },
902 | {
903 | "userId": 8,
904 | "id": 151,
905 | "title": "accusamus adipisci dicta qui quo ea explicabo sed vero",
906 | "completed": true
907 | },
908 | {
909 | "userId": 8,
910 | "id": 152,
911 | "title": "odit eligendi recusandae doloremque cumque non",
912 | "completed": false
913 | },
914 | {
915 | "userId": 8,
916 | "id": 153,
917 | "title": "ea aperiam consequatur qui repellat eos",
918 | "completed": false
919 | },
920 | {
921 | "userId": 8,
922 | "id": 154,
923 | "title": "rerum non ex sapiente",
924 | "completed": true
925 | },
926 | {
927 | "userId": 8,
928 | "id": 155,
929 | "title": "voluptatem nobis consequatur et assumenda magnam",
930 | "completed": true
931 | },
932 | {
933 | "userId": 8,
934 | "id": 156,
935 | "title": "nam quia quia nulla repellat assumenda quibusdam sit nobis",
936 | "completed": true
937 | },
938 | {
939 | "userId": 8,
940 | "id": 157,
941 | "title": "dolorem veniam quisquam deserunt repellendus",
942 | "completed": true
943 | },
944 | {
945 | "userId": 8,
946 | "id": 158,
947 | "title": "debitis vitae delectus et harum accusamus aut deleniti a",
948 | "completed": true
949 | },
950 | {
951 | "userId": 8,
952 | "id": 159,
953 | "title": "debitis adipisci quibusdam aliquam sed dolore ea praesentium nobis",
954 | "completed": true
955 | },
956 | {
957 | "userId": 8,
958 | "id": 160,
959 | "title": "et praesentium aliquam est",
960 | "completed": false
961 | },
962 | {
963 | "userId": 9,
964 | "id": 161,
965 | "title": "ex hic consequuntur earum omnis alias ut occaecati culpa",
966 | "completed": true
967 | },
968 | {
969 | "userId": 9,
970 | "id": 162,
971 | "title": "omnis laboriosam molestias animi sunt dolore",
972 | "completed": true
973 | },
974 | {
975 | "userId": 9,
976 | "id": 163,
977 | "title": "natus corrupti maxime laudantium et voluptatem laboriosam odit",
978 | "completed": false
979 | },
980 | {
981 | "userId": 9,
982 | "id": 164,
983 | "title": "reprehenderit quos aut aut consequatur est sed",
984 | "completed": false
985 | },
986 | {
987 | "userId": 9,
988 | "id": 165,
989 | "title": "fugiat perferendis sed aut quidem",
990 | "completed": false
991 | },
992 | {
993 | "userId": 9,
994 | "id": 166,
995 | "title": "quos quo possimus suscipit minima ut",
996 | "completed": false
997 | },
998 | {
999 | "userId": 9,
1000 | "id": 167,
1001 | "title": "et quis minus quo a asperiores molestiae",
1002 | "completed": false
1003 | },
1004 | {
1005 | "userId": 9,
1006 | "id": 168,
1007 | "title": "recusandae quia qui sunt libero",
1008 | "completed": false
1009 | },
1010 | {
1011 | "userId": 9,
1012 | "id": 169,
1013 | "title": "ea odio perferendis officiis",
1014 | "completed": true
1015 | },
1016 | {
1017 | "userId": 9,
1018 | "id": 170,
1019 | "title": "quisquam aliquam quia doloribus aut",
1020 | "completed": false
1021 | },
1022 | {
1023 | "userId": 9,
1024 | "id": 171,
1025 | "title": "fugiat aut voluptatibus corrupti deleniti velit iste odio",
1026 | "completed": true
1027 | },
1028 | {
1029 | "userId": 9,
1030 | "id": 172,
1031 | "title": "et provident amet rerum consectetur et voluptatum",
1032 | "completed": false
1033 | },
1034 | {
1035 | "userId": 9,
1036 | "id": 173,
1037 | "title": "harum ad aperiam quis",
1038 | "completed": false
1039 | },
1040 | {
1041 | "userId": 9,
1042 | "id": 174,
1043 | "title": "similique aut quo",
1044 | "completed": false
1045 | },
1046 | {
1047 | "userId": 9,
1048 | "id": 175,
1049 | "title": "laudantium eius officia perferendis provident perspiciatis asperiores",
1050 | "completed": true
1051 | },
1052 | {
1053 | "userId": 9,
1054 | "id": 176,
1055 | "title": "magni soluta corrupti ut maiores rem quidem",
1056 | "completed": false
1057 | },
1058 | {
1059 | "userId": 9,
1060 | "id": 177,
1061 | "title": "et placeat temporibus voluptas est tempora quos quibusdam",
1062 | "completed": false
1063 | },
1064 | {
1065 | "userId": 9,
1066 | "id": 178,
1067 | "title": "nesciunt itaque commodi tempore",
1068 | "completed": true
1069 | },
1070 | {
1071 | "userId": 9,
1072 | "id": 179,
1073 | "title": "omnis consequuntur cupiditate impedit itaque ipsam quo",
1074 | "completed": true
1075 | },
1076 | {
1077 | "userId": 9,
1078 | "id": 180,
1079 | "title": "debitis nisi et dolorem repellat et",
1080 | "completed": true
1081 | },
1082 | {
1083 | "userId": 10,
1084 | "id": 181,
1085 | "title": "ut cupiditate sequi aliquam fuga maiores",
1086 | "completed": false
1087 | },
1088 | {
1089 | "userId": 10,
1090 | "id": 182,
1091 | "title": "inventore saepe cumque et aut illum enim",
1092 | "completed": true
1093 | },
1094 | {
1095 | "userId": 10,
1096 | "id": 183,
1097 | "title": "omnis nulla eum aliquam distinctio",
1098 | "completed": true
1099 | },
1100 | {
1101 | "userId": 10,
1102 | "id": 184,
1103 | "title": "molestias modi perferendis perspiciatis",
1104 | "completed": false
1105 | },
1106 | {
1107 | "userId": 10,
1108 | "id": 185,
1109 | "title": "voluptates dignissimos sed doloribus animi quaerat aut",
1110 | "completed": false
1111 | },
1112 | {
1113 | "userId": 10,
1114 | "id": 186,
1115 | "title": "explicabo odio est et",
1116 | "completed": false
1117 | },
1118 | {
1119 | "userId": 10,
1120 | "id": 187,
1121 | "title": "consequuntur animi possimus",
1122 | "completed": false
1123 | },
1124 | {
1125 | "userId": 10,
1126 | "id": 188,
1127 | "title": "vel non beatae est",
1128 | "completed": true
1129 | },
1130 | {
1131 | "userId": 10,
1132 | "id": 189,
1133 | "title": "culpa eius et voluptatem et",
1134 | "completed": true
1135 | },
1136 | {
1137 | "userId": 10,
1138 | "id": 190,
1139 | "title": "accusamus sint iusto et voluptatem exercitationem",
1140 | "completed": true
1141 | },
1142 | {
1143 | "userId": 10,
1144 | "id": 191,
1145 | "title": "temporibus atque distinctio omnis eius impedit tempore molestias pariatur",
1146 | "completed": true
1147 | },
1148 | {
1149 | "userId": 10,
1150 | "id": 192,
1151 | "title": "ut quas possimus exercitationem sint voluptates",
1152 | "completed": false
1153 | },
1154 | {
1155 | "userId": 10,
1156 | "id": 193,
1157 | "title": "rerum debitis voluptatem qui eveniet tempora distinctio a",
1158 | "completed": true
1159 | },
1160 | {
1161 | "userId": 10,
1162 | "id": 194,
1163 | "title": "sed ut vero sit molestiae",
1164 | "completed": false
1165 | },
1166 | {
1167 | "userId": 10,
1168 | "id": 195,
1169 | "title": "rerum ex veniam mollitia voluptatibus pariatur",
1170 | "completed": true
1171 | },
1172 | {
1173 | "userId": 10,
1174 | "id": 196,
1175 | "title": "consequuntur aut ut fugit similique",
1176 | "completed": true
1177 | },
1178 | {
1179 | "userId": 10,
1180 | "id": 197,
1181 | "title": "dignissimos quo nobis earum saepe",
1182 | "completed": true
1183 | },
1184 | {
1185 | "userId": 10,
1186 | "id": 198,
1187 | "title": "quis eius est sint explicabo",
1188 | "completed": true
1189 | },
1190 | {
1191 | "userId": 10,
1192 | "id": 199,
1193 | "title": "numquam repellendus a magnam",
1194 | "completed": true
1195 | },
1196 | {
1197 | "userId": 10,
1198 | "id": 200,
1199 | "title": "ipsam aperiam voluptates qui",
1200 | "completed": false
1201 | }
1202 | ]
--------------------------------------------------------------------------------
/benchmarks/isolated/data/todos.proto:
--------------------------------------------------------------------------------
1 | // todos.proto
2 | package todos;
3 | syntax = "proto3";
4 |
5 | message TodoMessage {
6 | int32 id = 3;
7 | int32 userId = 2;
8 | string title = 1;
9 | bool completed = 4;
10 | }
11 |
12 | message TodosMessage {
13 | repeated TodoMessage objects = 1;
14 | }
15 |
--------------------------------------------------------------------------------
/benchmarks/isolated/des.benchmark.ts:
--------------------------------------------------------------------------------
1 | import b from 'benny'
2 | import {
3 | createDes,
4 | createSer
5 | } from '../../src/index.js'
6 | import fs from 'node:fs'
7 |
8 | import { encode as msgpackEncode, decode as msgpackDecode } from '@msgpack/msgpack'
9 | import cbor from 'cbor'
10 | import * as cborx from 'cbor-x'
11 | import * as msgpackr from 'msgpackr'
12 | import protobuf from 'protobufjs'
13 | import avro from 'avsc'
14 |
15 | interface Todo {
16 | userId: number
17 | id: number
18 | title: string
19 | completed: boolean
20 | }
21 |
22 | const todos: Todo[] = JSON.parse(fs.readFileSync('./benchmarks/isolated/data/todos.json', 'utf8'))
23 |
24 | const root = await protobuf.load('./benchmarks/isolated/data/todos.proto')
25 | const prtobufType = root.lookupType('todos.TodosMessage')
26 |
27 | const type = avro.Type.forSchema({
28 | name: 'Todos',
29 | type: 'array',
30 | items: {
31 | name: 'Todo',
32 | type: 'record',
33 | fields: [
34 | { name: 'userId', type: 'int' },
35 | { name: 'id', type: 'int' },
36 | { name: 'title', type: 'string' },
37 | { name: 'completed', type: 'boolean' }
38 | ]
39 | }
40 | })
41 | const ser = createSer()
42 | ser.reset()
43 | ser.serializeArray(todos, (ser, todo) => {
44 | ser.serializeUInt32(todo.id)
45 | ser.serializeUInt32(todo.userId)
46 | ser.serializeString(todo.title)
47 | ser.serializeBoolean(todo.completed)
48 | })
49 | const bbb = ser.getBuffer()
50 |
51 | const des = createDes(new ArrayBuffer(1))
52 |
53 | const protobufBuff = prtobufType.encode({ objects: todos }).finish()
54 | const avroBuff = type.toBuffer(todos)
55 | const cborBuff = cbor.encode(todos)
56 | const cborxBuff = cborx.encode(todos)
57 | const msgpackBuff = msgpackEncode(todos)
58 | const msgpackrBuff = msgpackr.pack(todos)
59 | const jsonStr = JSON.stringify(todos)
60 |
61 | await b.suite(
62 | 'Deserialize',
63 |
64 | b.add('seqproto', () => {
65 | des.setBuffer(bbb)
66 | des.deserializeArray(des => {
67 | const id = des.deserializeUInt32()
68 | const userId = des.deserializeUInt32()
69 | const title = des.deserializeString()
70 | const completed = des.deserializeBoolean()
71 | return { id, userId, title, completed }
72 | })
73 | }),
74 |
75 | b.add('avro', () => {
76 | type.fromBuffer(avroBuff)
77 | }),
78 |
79 | b.add('protobuf', () => {
80 | prtobufType.decode(protobufBuff)
81 | }),
82 |
83 | b.add('cbor', () => {
84 | cbor.decode(cborBuff)
85 | }),
86 |
87 | b.add('cborx', () => {
88 | cborx.decode(cborxBuff)
89 | }),
90 |
91 | b.add('msgpack', () => {
92 | msgpackDecode(msgpackBuff)
93 | }),
94 |
95 | b.add('msgpackr', () => {
96 | msgpackr.unpack(msgpackrBuff)
97 | }),
98 |
99 | b.add('JSON', () => {
100 | JSON.parse(jsonStr)
101 | }),
102 |
103 | b.cycle(),
104 | b.complete(),
105 | b.save({ file: 'deserialize', version: '1.0.0' }),
106 | b.save({ file: 'deserialize', format: 'table.html' })
107 | )
108 |
--------------------------------------------------------------------------------
/benchmarks/isolated/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "seqproto-benchmarks-isolated",
3 | "version": "0.0.1",
4 | "type": "module",
5 | "description": "",
6 | "scripts": {},
7 | "author": {
8 | "name": "Matteo Pietro Dazzi",
9 | "url": "https://github.com/ilteoood",
10 | "author": true
11 | },
12 | "engines": {
13 | "node": ">= 20.0.0"
14 | },
15 | "devDependencies": {
16 | "@msgpack/msgpack": "3.0.0-beta2",
17 | "@types/node": "=20.8.0",
18 | "autocannon": "^7.12.0",
19 | "avro-js": "^1.11.3",
20 | "avsc": "^5.7.7",
21 | "benny": "^3.7.1",
22 | "browserify": "^17.0.0",
23 | "cbor": "^9.0.1",
24 | "cbor-x": "^1.5.4",
25 | "fast-json-stringify": "^5.9.1",
26 | "lodash": "^4.17.21",
27 | "msgpackr": "^1.9.9",
28 | "protobufjs": "^7.2.5",
29 | "ts-standard": "^12.0.2",
30 | "tsx": "^3.14.0",
31 | "typescript": "^5.2.2",
32 | "undici-types": "^5.27.2"
33 | },
34 | "dependencies": {
35 | "@fastify/static": "^6.12.0",
36 | "fastify": "^4.24.3",
37 | "seqproto": "workspace:^"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/benchmarks/isolated/ser.benchmark.ts:
--------------------------------------------------------------------------------
1 | import b from 'benny'
2 | import fs from 'node:fs'
3 | import {
4 | createSer
5 | } from '../../src/index.js'
6 |
7 | import { encode as msgpackEncode } from '@msgpack/msgpack'
8 | import cbor from 'cbor'
9 | import * as cborx from 'cbor-x'
10 | import * as msgpackr from 'msgpackr'
11 | import protobuf from 'protobufjs'
12 | import avro from 'avsc'
13 | import fastJson from 'fast-json-stringify'
14 |
15 | const stringify = fastJson({
16 | title: 'Example Schema',
17 | type: 'array',
18 | items: {
19 | type: 'object',
20 | properties: {
21 | userId: { type: 'integer' },
22 | id: { type: 'integer' },
23 | title: { type: 'string' },
24 | completed: { type: 'boolean' }
25 | }
26 | }
27 | })
28 |
29 | interface Todo {
30 | userId: number
31 | id: number
32 | title: string
33 | completed: boolean
34 | }
35 |
36 | const todos: Todo[] = JSON.parse(fs.readFileSync('./benchmarks/isolated/data/todos.json', 'utf8'))
37 |
38 | const root = await protobuf.load('./benchmarks/isolated/data/todos.proto')
39 | const prtobufType = root.lookupType('todos.TodosMessage')
40 |
41 | const type = avro.Type.forSchema({
42 | name: 'Todos',
43 | type: 'array',
44 | items: {
45 | name: 'Todo',
46 | type: 'record',
47 | fields: [
48 | { name: 'userId', type: 'int' },
49 | { name: 'id', type: 'int' },
50 | { name: 'title', type: 'string' },
51 | { name: 'completed', type: 'boolean' }
52 | ]
53 | }
54 | })
55 | const ser = createSer()
56 |
57 | await b.suite(
58 | 'Serialize',
59 |
60 | b.add('seqproto', () => {
61 | ser.reset()
62 | ser.serializeArray(todos, (ser, todo) => {
63 | ser.serializeUInt32(todo.id)
64 | ser.serializeUInt32(todo.userId)
65 | ser.serializeString(todo.title)
66 | ser.serializeBoolean(todo.completed)
67 | })
68 | ser.getBuffer()
69 | }),
70 |
71 | b.add('protobuf', () => {
72 | prtobufType.encode({ objects: todos }).finish()
73 | }),
74 |
75 | b.add('avro', () => {
76 | type.toBuffer(todos)
77 | }),
78 |
79 | b.add('cbor', () => {
80 | cbor.encode(todos)
81 | }),
82 |
83 | b.add('cborx', () => {
84 | cborx.encode(todos)
85 | }),
86 |
87 | b.add('msgpack', () => {
88 | msgpackEncode(todos)
89 | }),
90 |
91 | b.add('msgpackr', () => {
92 | msgpackr.pack(todos)
93 | }),
94 |
95 | b.add('JSON', () => {
96 | JSON.stringify(todos)
97 | }),
98 |
99 | b.add('fast-json-stringify', () => {
100 | stringify(todos)
101 | }),
102 |
103 | b.cycle(),
104 | b.complete(),
105 | b.save({ file: 'serialize', version: '1.0.0' }),
106 | b.save({ file: 'serialize', format: 'table.html' })
107 | )
108 |
--------------------------------------------------------------------------------
/benchmarks/isolated/serdes.benchmark.ts:
--------------------------------------------------------------------------------
1 | import b from 'benny'
2 | import fs from 'node:fs'
3 | import {
4 | createDes,
5 | createSer
6 | } from '../../src/index.js'
7 |
8 | import { encode as msgpackEncode, decode as msgpackDecode } from '@msgpack/msgpack'
9 | import cbor from 'cbor'
10 | import * as cborx from 'cbor-x'
11 | import * as msgpackr from 'msgpackr'
12 | import protobuf from 'protobufjs'
13 | import avro from 'avsc'
14 |
15 | interface Todo {
16 | userId: number
17 | id: number
18 | title: string
19 | completed: boolean
20 | }
21 |
22 | const todos: Todo[] = JSON.parse(fs.readFileSync('./benchmarks/isolated/data/todos.json', 'utf8'))
23 |
24 | const root = await protobuf.load('./benchmarks/isolated/data/todos.proto')
25 | const protobufType = root.lookupType('todos.TodosMessage')
26 |
27 | const type = avro.Type.forSchema({
28 | name: 'Todos',
29 | type: 'array',
30 | items: {
31 | name: 'Todo',
32 | type: 'record',
33 | fields: [
34 | { name: 'userId', type: 'int' },
35 | { name: 'id', type: 'int' },
36 | { name: 'title', type: 'string' },
37 | { name: 'completed', type: 'boolean' }
38 | ]
39 | }
40 | })
41 | const ser = createSer()
42 |
43 | await b.suite(
44 | 'Serialize / Deserialize',
45 |
46 | b.add('seqproto', () => {
47 | ser.reset()
48 | ser.serializeArray(todos, (ser, todo) => {
49 | ser.serializeUInt32(todo.id)
50 | ser.serializeUInt32(todo.userId)
51 | ser.serializeString(todo.title)
52 | ser.serializeBoolean(todo.completed)
53 | })
54 | const bbb = ser.getBuffer()
55 | const des = createDes(bbb)
56 | des.deserializeArray(des => {
57 | const id = des.deserializeUInt32()
58 | const userId = des.deserializeUInt32()
59 | const title = des.deserializeString()
60 | const completed = des.deserializeBoolean()
61 | return { id, userId, title, completed }
62 | })
63 | }),
64 |
65 | b.add('protobuf', () => {
66 | protobufType.decode(protobufType.encode({ objects: todos }).finish())
67 | }),
68 |
69 | b.add('avro', () => {
70 | type.fromBuffer(type.toBuffer(todos))
71 | }),
72 |
73 | b.add('cbor', () => {
74 | cbor.decode(cbor.encode(todos))
75 | }),
76 |
77 | b.add('cborx', () => {
78 | cborx.decode(cborx.encode(todos))
79 | }),
80 |
81 | b.add('msgpack', () => {
82 | msgpackDecode(msgpackEncode(todos))
83 | }),
84 |
85 | b.add('msgpackr', () => {
86 | msgpackr.unpack(msgpackr.pack(todos))
87 | }),
88 |
89 | b.add('JSON', () => {
90 | JSON.parse(JSON.stringify(todos))
91 | }),
92 |
93 | b.cycle(),
94 | b.complete(),
95 | b.save({ file: 'des-ser-ialize', version: '1.0.0' }),
96 | b.save({ file: 'des-ser-serialize', format: 'table.html' })
97 | )
98 |
--------------------------------------------------------------------------------
/examples/complex_object.ts:
--------------------------------------------------------------------------------
1 | import { createSer, createDes } from '../src/index.js'
2 | import type { Ser, Des } from '../src/index.js'
3 |
4 | interface User {
5 | id: number
6 | username: string
7 | }
8 |
9 | interface Comment {
10 | id: number
11 | body: string
12 | user: User
13 | }
14 |
15 | interface Post {
16 | id: number
17 | title: string
18 | creator: User
19 | comments: Comment[]
20 | }
21 |
22 | function serializeUser (ser: Ser, user: User): void {
23 | ser.serializeUInt32(user.id)
24 | ser.serializeString(user.username)
25 | }
26 | function deserializeUser (des: Des): User {
27 | const id = des.deserializeUInt32()
28 | const username = des.deserializeString()
29 | return { id, username }
30 | }
31 | function serializeComment (ser: Ser, comment: Comment): void {
32 | ser.serializeUInt32(comment.id)
33 | ser.serializeString(comment.body)
34 | serializeUser(ser, comment.user)
35 | }
36 | function deserializeComment (des: Des): Comment {
37 | const id = des.deserializeUInt32()
38 | const body = des.deserializeString()
39 | const user = deserializeUser(des)
40 | return { id, body, user }
41 | }
42 | function serializePost (ser: Ser, post: Post): void {
43 | ser.serializeUInt32(post.id)
44 | ser.serializeString(post.title)
45 | serializeUser(ser, post.creator)
46 | ser.serializeArray(post.comments, serializeComment)
47 | }
48 | function deserializePost (des: Des): Post {
49 | const id = des.deserializeUInt32()
50 | const title = des.deserializeString()
51 | const creator = deserializeUser(des)
52 | const comments = des.deserializeArray(deserializeComment)
53 | return { id, title, creator, comments }
54 | }
55 |
56 | const ser: Ser = createSer()
57 | serializePost(ser, {
58 | id: 1,
59 | title: 'hello',
60 | creator: { id: 1, username: 'bob' },
61 | comments: [
62 | { id: 1, body: 'hello', user: { id: 1, username: 'bob' } },
63 | { id: 2, body: 'world', user: { id: 2, username: 'alice' } }
64 | ]
65 | })
66 | const buffer = ser.getBuffer()
67 |
68 | const des: Des = createDes(buffer)
69 | const post = deserializePost(des)
70 |
71 | console.log(JSON.stringify(post, null, 2))
72 |
--------------------------------------------------------------------------------
/examples/iterable.ts:
--------------------------------------------------------------------------------
1 | import { createSer, createDes } from '../src/index.js'
2 | import type { Ser, Des } from '../src/index.js'
3 |
4 | let buffer
5 |
6 | { // Serialize
7 | const map = new Map([
8 | [1, 'one'],
9 | [2, 'two'],
10 | [3, 'three']
11 | ])
12 | const ser: Ser = createSer()
13 | ser.serializeIterable(map, (ser, [k, v]) => {
14 | ser.serializeUInt32(k)
15 | ser.serializeString(v)
16 | })
17 | buffer = ser.getBuffer()
18 | }
19 |
20 | { // Deserialize
21 | const des: Des = createDes(buffer)
22 | const map = new Map(des.deserializeIterable((des) => {
23 | return [des.deserializeUInt32(), des.deserializeString()]
24 | }))
25 | console.log(map)
26 | }
27 |
--------------------------------------------------------------------------------
/examples/object.ts:
--------------------------------------------------------------------------------
1 | import { createSer, createDes } from '../src/index.js'
2 | import type { Ser, Des } from '../src/index.js'
3 |
4 | interface Todo {
5 | id: number
6 | userId: number
7 | title: string
8 | completed: boolean
9 | }
10 |
11 | function serializeTodo (ser: Ser, todo: Todo): void {
12 | ser.serializeUInt32(todo.id)
13 | ser.serializeUInt32(todo.userId)
14 | ser.serializeString(todo.title)
15 | ser.serializeBoolean(todo.completed)
16 | }
17 | function deserializeTodo (des: Des): Todo {
18 | const id = des.deserializeUInt32()
19 | const userId = des.deserializeUInt32()
20 | const title = des.deserializeString()
21 | const completed = des.deserializeBoolean()
22 | return { id, userId, title, completed }
23 | }
24 |
25 | const ser: Ser = createSer()
26 | serializeTodo(ser, {
27 | id: 1,
28 | userId: 1,
29 | title: 'hello',
30 | completed: false
31 | })
32 | const buffer = ser.getBuffer()
33 |
34 | const des: Des = createDes(buffer)
35 | const todo = deserializeTodo(des)
36 |
37 | console.log(JSON.stringify(todo, null, 2))
38 |
--------------------------------------------------------------------------------
/examples/simple.ts:
--------------------------------------------------------------------------------
1 | import { createSer, createDes } from '../src/index.js'
2 | import type { Ser, Des } from '../src/index.js'
3 |
4 | const ser: Ser = createSer()
5 |
6 | ser.serializeBoolean(true)
7 | ser.serializeUInt32(42)
8 | ser.serializeFloat32(-0.5)
9 | ser.serializeString('hello world')
10 | ser.serializeArray([1, 2, 3], (ser, n) => ser.serializeUInt32(n))
11 |
12 | const buffer = ser.getBuffer()
13 |
14 | const des: Des = createDes(buffer)
15 |
16 | const b = des.deserializeBoolean()
17 | const i = des.deserializeUInt32()
18 | const f = des.deserializeFloat32()
19 | const s = des.deserializeString()
20 | const a = des.deserializeArray((des) => des.deserializeUInt32())
21 |
22 | console.log({ b, i, f, s, a })
23 |
--------------------------------------------------------------------------------
/examples/todos.ts:
--------------------------------------------------------------------------------
1 | import { createSer, createDes } from '../src/index.js'
2 | import type { Ser, Des } from '../src/index.js'
3 |
4 | let buffer
5 |
6 | { // Serialize
7 | const todos = [
8 | { userId: 1, id: 1, completed: false, title: 'delectus aut autem' },
9 | { userId: 1, id: 2, completed: true, title: 'quis ut nam facilis et officia qui' }
10 | ]
11 | const ser: Ser = createSer()
12 | ser.serializeArray(todos, (ser, todo) => {
13 | ser.serializeUInt32(todo.id)
14 | ser.serializeUInt32(todo.userId)
15 | ser.serializeString(todo.title)
16 | ser.serializeBoolean(todo.completed)
17 | })
18 | buffer = ser.getBuffer()
19 | }
20 |
21 | { // Deserialize
22 | const des: Des = createDes(buffer)
23 | const todos = des.deserializeArray((des) => {
24 | const id = des.deserializeUInt32()
25 | const userId = des.deserializeUInt32()
26 | const title = des.deserializeString()
27 | const completed = des.deserializeBoolean()
28 | return { id, userId, title, completed }
29 | })
30 | console.log(todos)
31 | }
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "seqproto",
3 | "version": "0.2.3",
4 | "type": "module",
5 | "description": "More than fast serialization / deserialization utility for your structured data",
6 | "main": "./dist/cjs/index.js",
7 | "types": "./dist/cjs/index.d.ts",
8 | "license": "Apache-2.0",
9 | "exports": {
10 | "import": "./dist/esm/index.js",
11 | "require": "./dist/cjs/index.cjs"
12 | },
13 | "files": [
14 | "dist"
15 | ],
16 | "scripts": {
17 | "build": "npm run build:cjs && npm run build:esm && npm run rename:cjs",
18 | "build:cjs": "tsc -p tsconfig.cjs.json",
19 | "build:esm": "tsc -p tsconfig.esm.json",
20 | "rename:cjs": "mv ./dist/cjs/index.js ./dist/cjs/index.cjs",
21 | "lint": "ts-standard",
22 | "benchmark:ser": "node --import tsx benchmarks/isolated/ser.benchmark.ts",
23 | "benchmark:des": "node --import tsx benchmarks/isolated/des.benchmark.ts",
24 | "benchmark:serdes": "node --import tsx benchmarks/isolated/serdes.benchmark.ts",
25 | "test": "node --import tsx tests/*.test.ts"
26 | },
27 | "keywords": [
28 | "serialization",
29 | "deserialization",
30 | "performance",
31 | "structured",
32 | "protocol",
33 | "json"
34 | ],
35 | "author": {
36 | "name": "Tommaso Allevi",
37 | "email": "tomallevi@gmail.com",
38 | "url": "https://github.com/allevo",
39 | "author": true
40 | },
41 | "engines": {
42 | "node": ">= 20.0.0"
43 | },
44 | "devDependencies": {
45 | "ts-standard": "^12.0.2",
46 | "tsx": "^3.14.0",
47 | "typescript": "^5.2.2"
48 | },
49 | "ts-standard": {
50 | "ignore": [
51 | "benchmarks/e2e"
52 | ]
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - 'benchmarks/*'
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 |
2 | export type StrictArrayBuffer = ArrayBuffer & { buffer?: undefined }
3 |
4 | const TYPE_FLOAT = 0
5 | const TYPE_UINT32 = 1
6 | const TYPE_INT32 = 2
7 | const POW_2_32 = 2 ** 32
8 |
9 | export interface Ser {
10 | index: number
11 | buffer: ArrayBuffer
12 | uint32Array: Uint32Array
13 | float32Array: Float32Array
14 | reset: () => void
15 | getBuffer: () => StrictArrayBuffer
16 | serializeBoolean: (b: boolean) => void
17 | serializeUInt32: (n: number) => void
18 | serializeFloat32: (n: number) => void
19 | serializeNumber: (n: number) => void
20 | serializeString: (str: string) => void
21 | serializeArray: (arr: T[], serialize: (ser: Ser, t: T) => void) => void
22 | serializeIterable: (iterable: Iterable, serialize: (ser: Ser, t: T) => void) => void
23 | serializeIndexableArray: (arr: T[], serialize: (ser: Ser, t: T) => void) => void
24 | unsafeSerializeUint32Array: (buffer: Uint32Array) => void
25 | }
26 | export interface Des {
27 | index: number
28 | buffer: StrictArrayBuffer
29 | uint32Array: Uint32Array
30 | float32Array: Float32Array
31 | setBuffer: (buffer: StrictArrayBuffer, byteOffset?: number, byteLength?: number) => void
32 | deserializeBoolean: () => boolean
33 | deserializeUInt32: () => number
34 | deserializeFloat32: () => number
35 | deserializeNumber: () => number
36 | deserializeString: () => string
37 | deserializeArray: (deserialize: (des: Des) => T) => T[]
38 | deserializeIterable: (deserialize: (des: Des) => T) => Iterable
39 | unsafeDeserializeUint32Array: () => Uint32Array
40 | getArrayElements: (indexes: number[], deserialize: (des: Des, start: number, end: number) => T) => T[]
41 | }
42 |
43 | interface CreateSerOption {
44 | bufferSize?: number
45 | }
46 | export function createSer ({ bufferSize }: CreateSerOption = {}): Ser {
47 | const size = bufferSize ?? 2 ** 24
48 | if (size >= POW_2_32) {
49 | throw new Error('bufferSize option must be strictly less than 2 ** 32')
50 | }
51 |
52 | const buffer = new ArrayBuffer(size)
53 | return {
54 | index: 0,
55 | buffer,
56 | uint32Array: new Uint32Array(buffer),
57 | float32Array: new Float32Array(buffer),
58 | reset: function () { this.index = 0 },
59 | serializeBoolean,
60 | serializeUInt32,
61 | serializeFloat32,
62 | serializeNumber,
63 | serializeString,
64 | serializeArray,
65 | serializeIterable,
66 | serializeIndexableArray,
67 | unsafeSerializeUint32Array,
68 | getBuffer: function () { return this.buffer.slice(0, this.index * 4) }
69 | }
70 | }
71 |
72 | export function createDes (buffer: StrictArrayBuffer): Des {
73 | const n32 = Math.floor(buffer.byteLength / 4)
74 |
75 | return {
76 | index: 0,
77 | buffer,
78 | uint32Array: new Uint32Array(buffer, 0, n32),
79 | float32Array: new Float32Array(buffer, 0, n32),
80 | setBuffer: function (buffer: StrictArrayBuffer, byteOffset?: number, byteLength?: number) {
81 | if (typeof byteOffset === 'number' && typeof byteLength === 'number') {
82 | this.index = Math.floor(byteOffset / 4)
83 | const n32 = this.index + Math.ceil(byteLength / 4)
84 |
85 | this.buffer = buffer
86 | this.uint32Array = new Uint32Array(buffer, 0, n32)
87 | this.float32Array = new Float32Array(buffer, 0, n32)
88 |
89 | return
90 | }
91 |
92 | const n32 = Math.floor(buffer.byteLength / 4)
93 | this.buffer = buffer
94 | this.index = 0
95 | this.uint32Array = new Uint32Array(buffer, 0, n32)
96 | this.float32Array = new Float32Array(buffer, 0, n32)
97 | },
98 | deserializeBoolean,
99 | deserializeUInt32,
100 | deserializeFloat32,
101 | deserializeNumber,
102 | deserializeString,
103 | deserializeArray,
104 | deserializeIterable,
105 | getArrayElements,
106 | unsafeDeserializeUint32Array
107 | }
108 | }
109 |
110 | function serializeBoolean (this: Ser, b: boolean): void {
111 | this.uint32Array[this.index++] = b ? 1 : 0
112 | }
113 | function deserializeBoolean (this: Ser): boolean {
114 | return this.uint32Array[this.index++] === 1
115 | }
116 |
117 | function serializeUInt32 (this: Ser, n: number): void {
118 | this.uint32Array[this.index++] = n
119 | }
120 | function deserializeUInt32 (this: Des): number {
121 | return this.uint32Array[this.index++]
122 | }
123 | function serializeFloat32 (this: Ser, n: number): void {
124 | this.float32Array[this.index++] = n
125 | }
126 | function deserializeFloat32 (this: Des): number {
127 | return this.float32Array[this.index++]
128 | }
129 | function serializeNumber (this: Ser, n: number): void {
130 | // If it's not an integer
131 | if (n % 1 !== 0) {
132 | this.uint32Array[this.index++] = TYPE_FLOAT
133 | this.serializeFloat32(n)
134 | } else if (n >= 0) {
135 | this.uint32Array[this.index++] = TYPE_UINT32
136 | this.serializeUInt32(n)
137 | } else {
138 | this.uint32Array[this.index++] = TYPE_INT32
139 | this.uint32Array[this.index++] = POW_2_32 + n
140 | }
141 | }
142 | function deserializeNumber (this: Des): number {
143 | const type = this.uint32Array[this.index++]
144 | if (type === TYPE_FLOAT) {
145 | return this.deserializeFloat32()
146 | } else if (type === TYPE_UINT32) {
147 | return this.deserializeUInt32()
148 | } else if (type === TYPE_INT32) {
149 | return this.uint32Array[this.index++] - POW_2_32
150 | } else {
151 | throw new Error('Unknown type')
152 | }
153 | }
154 |
155 | const textEncoder = new TextEncoder()
156 | function serializeString (this: Ser, str: string): void {
157 | const r = textEncoder.encodeInto(str, new Uint8Array(this.buffer, (this.index + 1) * 4))
158 | this.uint32Array[this.index] = r.written
159 | this.index += Math.ceil(r.written / 4) + 1
160 | }
161 |
162 | const textDecoder = new TextDecoder()
163 | function deserializeString (this: Des): string {
164 | const len = this.uint32Array[this.index++]
165 | const decoded = textDecoder.decode(new Uint8Array(this.buffer, this.index * 4, len))
166 | this.index += Math.ceil(len / 4)
167 | return decoded
168 | }
169 |
170 | function serializeArray (this: Ser, arr: T[], serialize: (ser: Ser, t: T) => void): void {
171 | const len = arr.length
172 | this.serializeUInt32(len)
173 | for (let i = 0; i < len; i++) {
174 | serialize(this, arr[i])
175 | }
176 | }
177 | function deserializeArray (this: Des, deserialize: (ser: Des) => T): T[] {
178 | const len = this.deserializeUInt32()
179 | const arr = new Array(len)
180 | for (let i = 0; i < len; i++) {
181 | arr[i] = deserialize(this)
182 | }
183 | return arr
184 | }
185 |
186 | function serializeIterable (this: Ser, iterable: Iterable, serialize: (ser: Ser, t: T) => void): void {
187 | // Keep space for the length
188 | const currentIndex = this.index++
189 | let n = 0
190 | for (const t of iterable) {
191 | n++
192 | serialize(this, t)
193 | }
194 | this.uint32Array[currentIndex] = n
195 | }
196 | function deserializeIterable (this: Des, deserialize: (des: Des) => T): Iterable {
197 | const len = this.deserializeUInt32()
198 | const aGeneratorObject = (function * (des) {
199 | for (let i = 0; i < len; i++) {
200 | yield deserialize(des)
201 | }
202 | })(this)
203 |
204 | return {
205 | [Symbol.iterator] () {
206 | return aGeneratorObject
207 | }
208 | }
209 | }
210 |
211 | function unsafeSerializeUint32Array (this: Ser, arr: Uint32Array): void {
212 | const length = Math.ceil(arr.byteLength / 4)
213 | this.uint32Array[this.index++] = length
214 | this.uint32Array.set(arr, this.index)
215 | this.index += length
216 | }
217 | function unsafeDeserializeUint32Array (this: Des): Uint32Array {
218 | const byteLength = this.uint32Array[this.index++]
219 | const d = new Uint32Array(this.buffer, this.index * 4, byteLength)
220 | this.index += byteLength
221 | return d
222 | }
223 |
224 | function serializeIndexableArray (this: Ser, arr: T[], serialize: (ser: Ser, t: T) => void): void {
225 | const l = arr.length
226 | this.uint32Array[this.index++] = l
227 | let indexOffsets = this.index
228 | // Skip the length of the array twice
229 | // to store the offset + length of the array element
230 | this.index += l * 2
231 | for (let i = 0; i < l; i++) {
232 | const offsetStart = this.index
233 | serialize(this, arr[i])
234 | const offsetEnd = this.index
235 | this.uint32Array[indexOffsets++] = offsetStart
236 | this.uint32Array[indexOffsets++] = offsetEnd - offsetStart
237 | }
238 | }
239 | function getArrayElements (this: Des, indexes: number[], deserialize: (des: Des, start: number, end: number) => T): T[] {
240 | const currentIndex = this.index + 1
241 | const l = indexes.length
242 | const arr = new Array(l)
243 | for (let i = 0; i < l; i++) {
244 | const indexOffset = currentIndex + indexes[i] * 2
245 | const start = this.uint32Array[indexOffset]
246 | const end = this.uint32Array[indexOffset + 1]
247 | arr[i] = deserialize(this, start * 4, end)
248 | }
249 | return arr
250 | }
251 |
--------------------------------------------------------------------------------
/tests/index.test.ts:
--------------------------------------------------------------------------------
1 | import t from 'node:test'
2 | import assert from 'node:assert'
3 | import { createSer, createDes, Ser, Des } from '../src/index.js'
4 |
5 | await t.test('boolean', async t => {
6 | const bools = [
7 | true, false
8 | ]
9 |
10 | for (const expected of bools) {
11 | await t.test(`serialize ${JSON.stringify(expected)}`, () => {
12 | const ser = createSer()
13 | ser.serializeBoolean(expected)
14 |
15 | const des = createDes(ser.getBuffer())
16 |
17 | const actual = des.deserializeBoolean()
18 | assert.equal(actual, expected)
19 | })
20 | }
21 |
22 | await t.test('serialize multiple boolean', () => {
23 | const ser = createSer()
24 | ser.serializeBoolean(true)
25 | ser.serializeBoolean(true)
26 | ser.serializeBoolean(false)
27 | ser.serializeBoolean(true)
28 |
29 | const des = createDes(ser.getBuffer())
30 |
31 | const n1 = des.deserializeBoolean()
32 | const n2 = des.deserializeBoolean()
33 | const n3 = des.deserializeBoolean()
34 | const n4 = des.deserializeBoolean()
35 |
36 | assert.equal(n1, true)
37 | assert.equal(n2, true)
38 | assert.equal(n3, false)
39 | assert.equal(n4, true)
40 | })
41 | })
42 |
43 | await t.test('uint32', async t => {
44 | const numbers = [
45 | 0, 1, 5, 123, 42, 10000
46 | ]
47 |
48 | for (const expected of numbers) {
49 | await t.test(`serialize ${expected}`, () => {
50 | const ser = createSer()
51 | ser.serializeUInt32(expected)
52 |
53 | const des = createDes(ser.getBuffer())
54 |
55 | const actual = des.deserializeUInt32()
56 | assert.equal(actual, expected)
57 | })
58 | }
59 |
60 | await t.test('serialize multiple numbers', () => {
61 | const ser = createSer()
62 | for (const n of numbers) {
63 | ser.serializeUInt32(n)
64 | }
65 |
66 | const des = createDes(ser.getBuffer())
67 |
68 | for (let i = 0; i < numbers.length; i++) {
69 | assert.equal(des.deserializeUInt32(), numbers[i])
70 | }
71 | })
72 | })
73 |
74 | await t.test('number', async t => {
75 | const numbers = [
76 | 0, 0.0, -0, -0.0, 42, -42, 42.42, -42.42
77 | ]
78 |
79 | for (const expected of numbers) {
80 | await t.test(`serialize ${expected}`, () => {
81 | const ser = createSer()
82 | ser.serializeNumber(expected)
83 |
84 | const des = createDes(ser.getBuffer())
85 |
86 | const actual = des.deserializeNumber()
87 |
88 | if (isFloat(expected)) {
89 | assertFloat32Equals(actual, expected)
90 | } else {
91 | assert.equal(actual, expected)
92 | }
93 | })
94 | }
95 |
96 | await t.test('serialize multiple numbers', () => {
97 | const ser = createSer()
98 | for (const n of numbers) {
99 | ser.serializeNumber(n)
100 | }
101 |
102 | const des = createDes(ser.getBuffer())
103 |
104 | for (let i = 0; i < numbers.length; i++) {
105 | if (isFloat(numbers[i])) {
106 | assertFloat32Equals(des.deserializeNumber(), numbers[i])
107 | } else {
108 | assert.equal(des.deserializeNumber(), numbers[i])
109 | }
110 | }
111 | })
112 | })
113 |
114 | await t.test('float32', async t => {
115 | const numbers = [
116 | 0.01, -0.1, 99.8
117 | ]
118 |
119 | for (const expected of numbers) {
120 | await t.test(`serialize ${expected}`, () => {
121 | const ser = createSer()
122 | ser.serializeFloat32(expected)
123 |
124 | const des = createDes(ser.getBuffer())
125 |
126 | const actual = des.deserializeFloat32()
127 | assertFloat32Equals(actual, expected)
128 | })
129 | }
130 |
131 | await t.test('serialize multiple numbers', () => {
132 | const ser = createSer()
133 | ser.serializeFloat32(-3)
134 | ser.serializeFloat32(55.5)
135 | ser.serializeFloat32(42.42)
136 | ser.serializeFloat32(33.3)
137 |
138 | const des = createDes(ser.getBuffer())
139 |
140 | const n1 = des.deserializeFloat32()
141 | const n2 = des.deserializeFloat32()
142 | const n3 = des.deserializeFloat32()
143 | const n4 = des.deserializeFloat32()
144 |
145 | assertFloat32Equals(n1, -3)
146 | assertFloat32Equals(n2, 55.5)
147 | assertFloat32Equals(n3, 42.42)
148 | assertFloat32Equals(n4, 33.3)
149 | })
150 | })
151 |
152 | await t.test('string', async t => {
153 | const strings = [
154 | 'f',
155 | 'fo',
156 | 'foo',
157 | 'fooo',
158 | 'fooof',
159 | 'my name is Tommaso!',
160 | 'my name is\nTommaso!',
161 | 'my name is Tommaso!'.repeat(100),
162 | '',
163 | '👍',
164 | '👍👍',
165 | '👍👍👍',
166 | '👍👍👍👍',
167 | '👍👍👍👍👍',
168 | '☃',
169 | '☔',
170 | '🌈',
171 | '🏳️🌈',
172 | '⭕',
173 | 'السلام عليكم',
174 | '我的名字是Tommaso',
175 | 'विकिपीडिया',
176 | 'Привет'
177 | ]
178 |
179 | for (const expected of strings) {
180 | await t.test(`serialize ${expected}`, () => {
181 | const ser = createSer()
182 | ser.serializeString(expected)
183 |
184 | const des = createDes(ser.getBuffer())
185 |
186 | const actual = des.deserializeString()
187 | assert.equal(actual, expected)
188 | })
189 | }
190 |
191 | await t.test('serialize multiple numbers', () => {
192 | const ser = createSer()
193 | ser.serializeString('a')
194 | ser.serializeString('b')
195 | ser.serializeString('c')
196 |
197 | const des = createDes(ser.getBuffer())
198 |
199 | const s1 = des.deserializeString()
200 | const s2 = des.deserializeString()
201 | const s3 = des.deserializeString()
202 |
203 | assert.equal(s1, 'a')
204 | assert.equal(s2, 'b')
205 | assert.equal(s3, 'c')
206 | })
207 | })
208 |
209 | await t.test('array', async t => {
210 | await t.test('array of uint32', async t => {
211 | const numbers = [
212 | [0, 1, 10000],
213 | []
214 | ]
215 |
216 | for (const expected of numbers) {
217 | await t.test(`serialize ${JSON.stringify(expected)}`, () => {
218 | const ser = createSer()
219 | ser.serializeArray(expected, (ser, n) => ser.serializeUInt32(n))
220 |
221 | const des = createDes(ser.getBuffer())
222 |
223 | const actual = des.deserializeArray(() => des.deserializeUInt32())
224 | assert.deepStrictEqual(actual, expected)
225 | })
226 | }
227 | })
228 |
229 | await t.test('array of string', async t => {
230 | const numbers = [
231 | ['f', 'foo', 'foof'],
232 | ['f', 'foo', '👍', '🏳️🌈'],
233 | []
234 | ]
235 |
236 | for (const expected of numbers) {
237 | await t.test(`serialize ${JSON.stringify(expected)}`, () => {
238 | const ser = createSer()
239 | ser.serializeArray(expected, (ser, n) => ser.serializeString(n))
240 |
241 | const des = createDes(ser.getBuffer())
242 |
243 | const actual = des.deserializeArray(() => des.deserializeString())
244 | assert.deepStrictEqual(actual, expected)
245 | })
246 | }
247 | })
248 | })
249 |
250 | await t.test('iterable', async t => {
251 | await t.test('map', async () => {
252 | const expected: Map = new Map([[0, 1], [2, 3], [4, 5]])
253 |
254 | const ser = createSer()
255 | ser.serializeIterable(expected.entries(), (ser, n) => {
256 | ser.serializeUInt32(n[0])
257 | ser.serializeUInt32(n[1])
258 | })
259 |
260 | const des = createDes(ser.getBuffer())
261 | const m = new Map(des.deserializeIterable(() => [des.deserializeUInt32(), des.deserializeUInt32()]))
262 | assert.deepStrictEqual(m, expected)
263 | })
264 |
265 | await t.test('map', async () => {
266 | const expected: Map = new Map([
267 | ['foo', 1],
268 | ['bar', 3],
269 | ['baz', 5]
270 | ])
271 |
272 | const ser = createSer()
273 | ser.serializeIterable(expected.entries(), (ser, n) => {
274 | ser.serializeString(n[0])
275 | ser.serializeUInt32(n[1])
276 | })
277 |
278 | const des = createDes(ser.getBuffer())
279 | const m = new Map(des.deserializeIterable(() => [des.deserializeString(), des.deserializeUInt32()]))
280 | assert.deepStrictEqual(m, expected)
281 | })
282 |
283 | await t.test('set', async () => {
284 | const expected: Set = new Set([1, 3, 5])
285 |
286 | const ser = createSer()
287 | ser.serializeIterable(expected.values(), (ser, n) => {
288 | ser.serializeUInt32(n)
289 | })
290 |
291 | const des = createDes(ser.getBuffer())
292 | const m = new Set(des.deserializeIterable(() => des.deserializeUInt32()))
293 | assert.deepStrictEqual(m, expected)
294 | })
295 | })
296 |
297 | await t.test('setBuffer with options', async t => {
298 | await t.test('uint32', async () => {
299 | const ser = createSer()
300 | ser.serializeUInt32(1)
301 | ser.serializeUInt32(2)
302 | ser.serializeUInt32(3)
303 | ser.serializeUInt32(4)
304 | const buff = ser.getBuffer()
305 |
306 | const des = createDes(new ArrayBuffer(0))
307 | des.setBuffer(buff, 0, 4)
308 | assert.equal(des.deserializeUInt32(), 1)
309 |
310 | des.setBuffer(buff, 4, 4)
311 | assert.equal(des.deserializeUInt32(), 2)
312 |
313 | des.setBuffer(buff, 8, 4)
314 | assert.equal(des.deserializeUInt32(), 3)
315 |
316 | des.setBuffer(buff, 12, 4)
317 | assert.equal(des.deserializeUInt32(), 4)
318 |
319 | des.setBuffer(buff, 4, 12)
320 | assert.equal(des.deserializeUInt32(), 2)
321 | assert.equal(des.deserializeUInt32(), 3)
322 | assert.equal(des.deserializeUInt32(), 4)
323 | })
324 |
325 | await t.test('uint32 & string', async () => {
326 | const ser = createSer()
327 | ser.serializeUInt32(1)
328 | ser.serializeString('v1')
329 | const buff = ser.getBuffer()
330 |
331 | const des = createDes(new ArrayBuffer(0))
332 | des.setBuffer(buff, 0, 12)
333 | assert.equal(des.deserializeUInt32(), 1)
334 | assert.equal(des.deserializeString(), 'v1')
335 |
336 | des.setBuffer(buff, 4, 8)
337 | assert.equal(des.deserializeString(), 'v1')
338 | })
339 | })
340 |
341 | await t.test('reset', async () => {
342 | const ser = createSer()
343 | ser.serializeUInt32(42)
344 |
345 | const des = createDes(ser.getBuffer())
346 | assert.deepStrictEqual(des.deserializeUInt32(), 42)
347 |
348 | ser.serializeUInt32(33)
349 | des.setBuffer(ser.getBuffer())
350 | assert.deepStrictEqual(des.deserializeUInt32(), 42)
351 | assert.deepStrictEqual(des.deserializeUInt32(), 33)
352 |
353 | ser.reset()
354 | ser.serializeUInt32(11)
355 | des.setBuffer(ser.getBuffer())
356 | assert.deepStrictEqual(des.deserializeUInt32(), 11)
357 | })
358 |
359 | function serializeItem (ser: Ser, t: { foo: string, bar: number }): void {
360 | ser.serializeString(t.foo)
361 | ser.serializeUInt32(t.bar)
362 | }
363 |
364 | function deserializeItem (des: Des): { foo: string, bar: number } {
365 | const foo = des.deserializeString()
366 | const bar = des.deserializeUInt32()
367 | return {
368 | foo,
369 | bar
370 | }
371 | }
372 |
373 | await t.test('serialize + getArrayelements + serialize unsafe + deserialize with deserialize unsafe', async () => {
374 | const arr = [
375 | { foo: 'v1', bar: 42 },
376 | { foo: 'v2', bar: 2 },
377 | { foo: 'v3', bar: 99 }
378 | ]
379 | const elementIndexes = [0, 2]
380 |
381 | let docsStorageBuffer: ArrayBuffer
382 | {
383 | const ser = createSer()
384 | ser.serializeIndexableArray(arr, serializeItem)
385 | docsStorageBuffer = ser.getBuffer()
386 | }
387 |
388 | let foo: ArrayBuffer
389 | {
390 | const ser = createSer()
391 |
392 | const des = createDes(docsStorageBuffer)
393 | const elements = des.getArrayElements(elementIndexes, function (_des, offset, length) {
394 | return new Uint32Array(docsStorageBuffer, offset, length)
395 | })
396 |
397 | ser.serializeArray(elements, (ser, uint32Array) => {
398 | ser.unsafeSerializeUint32Array(uint32Array)
399 | })
400 | foo = ser.getBuffer()
401 | }
402 |
403 | let found: Array<{ foo: string, bar: number }>
404 | {
405 | const des = createDes(foo)
406 |
407 | const itemDes = createDes(new ArrayBuffer(0))
408 | found = des.deserializeArray((des) => {
409 | const buff = des.unsafeDeserializeUint32Array()
410 | itemDes.setBuffer(buff.buffer, buff.byteOffset, buff.byteLength)
411 | return deserializeItem(itemDes)
412 | })
413 | }
414 |
415 | assert.deepStrictEqual(found, elementIndexes.map(i => arr[i]))
416 | })
417 |
418 | await t.test('with option', async t => {
419 | await t.test('bufferSize', async () => {
420 | {
421 | const ser = createSer({ bufferSize: 4 })
422 | assert.equal(ser.buffer.byteLength, 4)
423 | }
424 | {
425 | const ser = createSer({ bufferSize: 8 })
426 | assert.equal(ser.buffer.byteLength, 8)
427 | }
428 | {
429 | const ser = createSer({ bufferSize: 2 ** 32 - 4 })
430 | assert.equal(ser.buffer.byteLength, 2 ** 32 - 4)
431 | }
432 | assert.throws(() => createSer({ bufferSize: 2 ** 32 }), err => {
433 | return (err as Error).message.includes('bufferSize option must be strictly less than 2 ** 32')
434 | })
435 | })
436 | })
437 |
438 | await t.test('random items', async t => {
439 | const elements: any[] = [
440 | { item: 'foo', serializer: 'serializeString', deserializer: 'deserializeString' },
441 | { item: 'bar', serializer: 'serializeString', deserializer: 'deserializeString' },
442 | { item: 3, serializer: 'serializeUInt32', deserializer: 'deserializeUInt32' },
443 | { item: 42, serializer: 'serializeUInt32', deserializer: 'deserializeUInt32' },
444 | { item: 0.6, serializer: 'serializeFloat32', deserializer: 'deserializeFloat32' },
445 | { item: -99.42, serializer: 'serializeFloat32', deserializer: 'deserializeFloat32' }
446 | ]
447 |
448 | const permutations: any[][] = permutator(elements)
449 |
450 | for (const permutation of permutations) {
451 | await t.test(`serialize ${JSON.stringify(permutation)}`, () => {
452 | const ser = createSer() as any
453 | for (const { item, serializer } of permutation) {
454 | ser[serializer](item)
455 | }
456 |
457 | const des = createDes(ser.getBuffer()) as any
458 |
459 | const actual = Object.entries(permutation.map(({ deserializer }) => des[deserializer]()))
460 |
461 | const notFloat = actual.filter(([, n]) => !isFloat(n))
462 | assert.deepStrictEqual(notFloat, Object.entries(permutation.map(({ item }) => item))
463 | .filter(([, n]) => !isFloat(n)))
464 |
465 | const expectedFloats = Object.entries(permutation.map(({ item }) => item))
466 | .filter(([, n]) => isFloat(n))
467 | .map(([, n]) => n)
468 | const floats = actual.filter(([, n]) => isFloat(n))
469 | .map(([, n]) => n)
470 | for (let i = 0; i < expectedFloats.length; i++) {
471 | assert.ok(Math.abs(expectedFloats[i] - floats[i]) < 0.0001)
472 | }
473 | })
474 | }
475 | })
476 |
477 | function permutator (inputArr: any): any[][] {
478 | const results: any[] = []
479 |
480 | function permute (arr: any, memo: any): any[] {
481 | let cur
482 | const c = memo
483 |
484 | for (let i = 0; i < arr.length; i++) {
485 | cur = arr.splice(i, 1)
486 | if (arr.length === 0) {
487 | results.push(c.concat(cur))
488 | }
489 | permute(arr.slice(), c.concat(cur))
490 | arr.splice(i, 0, cur[0])
491 | }
492 |
493 | return results
494 | }
495 |
496 | return permute(inputArr, [])
497 | }
498 |
499 | function isFloat (n: number): boolean {
500 | return Number(n) === n && n % 1 !== 0
501 | }
502 |
503 | function assertFloat32Equals (a: number, b: number): void {
504 | assert.ok(Math.abs(a - b) < 0.0001)
505 | }
506 |
--------------------------------------------------------------------------------
/tsconfig.cjs.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2017",
4 | "module": "CommonJS",
5 | "declaration": true,
6 | "outDir": "./dist/cjs",
7 | "rootDir": "./src",
8 | "strict": true,
9 | "esModuleInterop": true,
10 | "forceConsistentCasingInFileNames": true
11 | },
12 | "include": ["src/**/*"],
13 | "exclude": ["node_modules", "**/*.spec.ts"]
14 | }
--------------------------------------------------------------------------------
/tsconfig.esm.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2017",
4 | "module": "ESNext",
5 | "declaration": true,
6 | "outDir": "./dist/esm",
7 | "rootDir": "./src",
8 | "strict": true,
9 | "esModuleInterop": true,
10 | "forceConsistentCasingInFileNames": true
11 | },
12 | "include": ["src/**/*"],
13 | "exclude": ["node_modules", "**/*.spec.ts"]
14 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2017",
4 | "module": "NodeNext",
5 | "declaration": true,
6 | "outDir": "./dist",
7 | "moduleResolution": "nodenext",
8 | "rootDir": "./",
9 | "strict": true,
10 | "esModuleInterop": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "lib": ["ES2023", "dom"]
13 | },
14 | "include": [
15 | "src/**/*",
16 | "examples/**/*",
17 | "tests/**/*",
18 | "benchmarks/isolated/*"
19 | ],
20 | "exclude": [
21 | "node_modules",
22 | "benchmarks/e2e"
23 | ]
24 | }
--------------------------------------------------------------------------------