├── .github └── workflows │ └── main.yml ├── .gitignore ├── .npmrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── benchmark ├── .gitignore ├── LICENSE ├── README.md ├── build-src.js ├── example.js ├── package.json ├── pridepack.json ├── results │ ├── charts │ │ ├── circular-dedupe to string.chart.html │ │ ├── circular-simple to string.chart.html │ │ ├── dedupe-object to string.chart.html │ │ ├── large-circular-collection to string.chart.html │ │ ├── large-complex-collection to string.chart.html │ │ ├── large-dedupe-collection to string.chart.html │ │ ├── large-invalid-keys-collection to string.chart.html │ │ ├── large-simple-collection to string.chart.html │ │ ├── simple-object to string.chart.html │ │ └── small-collection to string.chart.html │ ├── csv │ │ ├── circular-dedupe to string.csv │ │ ├── circular-simple to string.csv │ │ ├── dedupe-object to string.csv │ │ ├── large-circular-collection to string.csv │ │ ├── large-complex-collection to string.csv │ │ ├── large-dedupe-collection to string.csv │ │ ├── large-invalid-keys-collection to string.csv │ │ ├── large-simple-collection to string.csv │ │ ├── simple-object to string.csv │ │ └── small-collection to string.csv │ └── json │ │ ├── circular-dedupe to string.json │ │ ├── circular-simple to string.json │ │ ├── dedupe-object to string.json │ │ ├── large-circular-collection to string.json │ │ ├── large-complex-collection to string.json │ │ ├── large-dedupe-collection to string.json │ │ ├── large-invalid-keys-collection to string.json │ │ ├── large-simple-collection to string.json │ │ ├── simple-object to string.json │ │ └── small-collection to string.json ├── src │ ├── fixtures │ │ ├── circular-dedupe.ts │ │ ├── circular-simple.ts │ │ ├── dedupe-object.ts │ │ ├── large-circular-collection.ts │ │ ├── large-complex-collection.ts │ │ ├── large-dedupe-collection.ts │ │ ├── large-invalid-keys-collection.ts │ │ ├── large-simple-collection.ts │ │ ├── simple-object.ts │ │ └── small-collection.ts │ ├── index.ts │ └── tools │ │ ├── devalue.ts │ │ ├── flatted.ts │ │ ├── json.ts │ │ ├── next-json.ts │ │ ├── oson.ts │ │ ├── serialize-javascript.ts │ │ ├── seroval.ts │ │ ├── superjson.ts │ │ ├── tosource.ts │ │ ├── turbo-stream.ts │ │ └── warp10.ts └── tsconfig.json ├── biome.json ├── docs ├── compatibility.md ├── isomorphic-refs.md └── serialization.md ├── lerna.json ├── package.json ├── packages ├── plugins │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── pridepack.json │ ├── tests │ │ └── web │ │ │ ├── __snapshots__ │ │ │ ├── abort-signal.test.ts.snap │ │ │ ├── blob.test.ts.snap │ │ │ ├── custom-event.test.ts.snap │ │ │ ├── dom-exception.test.ts.snap │ │ │ ├── event.test.ts.snap │ │ │ ├── file.test.ts.snap │ │ │ ├── form-data.test.ts.snap │ │ │ ├── headers.test.ts.snap │ │ │ ├── readable-stream.test.ts.snap │ │ │ ├── request.test.ts.snap │ │ │ ├── response.test.ts.snap │ │ │ ├── url-search-params.test.ts.snap │ │ │ └── url.test.ts.snap │ │ │ ├── abort-signal.test.ts │ │ │ ├── blob.test.ts │ │ │ ├── custom-event.test.ts │ │ │ ├── dom-exception.test.ts │ │ │ ├── event.test.ts │ │ │ ├── file.test.ts │ │ │ ├── form-data.test.ts │ │ │ ├── headers.test.ts │ │ │ ├── readable-stream.test.ts │ │ │ ├── request.test.ts │ │ │ ├── response.test.ts │ │ │ ├── url-search-params.test.ts │ │ │ └── url.test.ts │ ├── tsconfig.json │ └── web │ │ ├── abort-signal.ts │ │ ├── blob.ts │ │ ├── custom-event.ts │ │ ├── dom-exception.ts │ │ ├── event.ts │ │ ├── file.ts │ │ ├── form-data.ts │ │ ├── headers.ts │ │ ├── image-data.ts │ │ ├── index.ts │ │ ├── readable-stream.ts │ │ ├── request.ts │ │ ├── response.ts │ │ ├── url-search-params.ts │ │ └── url.ts └── seroval │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── assets │ └── global-header.js │ ├── cyclic.js │ ├── example.js │ ├── package.json │ ├── pridepack.json │ ├── src │ ├── core │ │ ├── Serializer.ts │ │ ├── base-primitives.ts │ │ ├── compat.ts │ │ ├── constants.ts │ │ ├── context │ │ │ ├── deserializer.ts │ │ │ ├── parser.ts │ │ │ ├── parser │ │ │ │ ├── async.ts │ │ │ │ ├── stream.ts │ │ │ │ └── sync.ts │ │ │ └── serializer.ts │ │ ├── cross │ │ │ ├── async.ts │ │ │ ├── deserializer.ts │ │ │ ├── index.ts │ │ │ ├── parser.ts │ │ │ ├── serializer.ts │ │ │ ├── stream.ts │ │ │ └── sync.ts │ │ ├── errors.ts │ │ ├── function-string.ts │ │ ├── keys.ts │ │ ├── literals.ts │ │ ├── node.ts │ │ ├── opaque-reference.ts │ │ ├── plugin.ts │ │ ├── reference.ts │ │ ├── special-reference.ts │ │ ├── stream.ts │ │ ├── string.ts │ │ ├── tree │ │ │ ├── async.ts │ │ │ ├── deserializer.ts │ │ │ ├── index.ts │ │ │ ├── serializer.ts │ │ │ └── sync.ts │ │ ├── types.ts │ │ └── utils │ │ │ ├── assert.ts │ │ │ ├── deferred.ts │ │ │ ├── error.ts │ │ │ ├── get-identifier.ts │ │ │ ├── get-object-flag.ts │ │ │ ├── is-valid-identifier.ts │ │ │ ├── iterator-to-sequence.ts │ │ │ ├── promise-to-result.ts │ │ │ └── typed-array.ts │ └── index.ts │ ├── test.js │ ├── test │ ├── __snapshots__ │ │ ├── array.test.ts.snap │ │ ├── async-iterable.test.ts.snap │ │ ├── bigint.test.ts.snap │ │ ├── boolean.test.ts.snap │ │ ├── boxed-bigint.test.ts.snap │ │ ├── boxed-boolean.test.ts.snap │ │ ├── boxed-number.test.ts.snap │ │ ├── boxed-string.test.ts.snap │ │ ├── data-view.test.ts.snap │ │ ├── date.test.ts.snap │ │ ├── error.test.ts.snap │ │ ├── frozen-object.test.ts.snap │ │ ├── iterable.test.ts.snap │ │ ├── map.test.ts.snap │ │ ├── mutual-cycle.test.ts.snap │ │ ├── null-constructor.test.ts.snap │ │ ├── number.test.ts.snap │ │ ├── object.test.ts.snap │ │ ├── opaque-reference.test.ts.snap │ │ ├── plugin.test.ts.snap │ │ ├── promise.test.ts.snap │ │ ├── reference.test.ts.snap │ │ ├── regexp.test.ts.snap │ │ ├── sealed-object.test.ts.snap │ │ ├── set.test.ts.snap │ │ ├── sparse-array.test.ts.snap │ │ ├── string.test.ts.snap │ │ ├── typed-array.test.ts.snap │ │ └── wk-symbols.test.ts.snap │ ├── array.test.ts │ ├── async-iterable.test.ts │ ├── bigint.test.ts │ ├── boolean.test.ts │ ├── boxed-bigint.test.ts │ ├── boxed-boolean.test.ts │ ├── boxed-number.test.ts │ ├── boxed-string.test.ts │ ├── data-view.test.ts │ ├── date.test.ts │ ├── error.test.ts │ ├── frozen-object.test.ts │ ├── iterable.test.ts │ ├── map.test.ts │ ├── mutual-cycle.test.ts │ ├── null-constructor.test.ts │ ├── number.test.ts │ ├── object.test.ts │ ├── opaque-reference.test.ts │ ├── plugin.test.ts │ ├── promise.test.ts │ ├── reference.test.ts │ ├── regexp.test.ts │ ├── sealed-object.test.ts │ ├── set.test.ts │ ├── sparse-array.test.ts │ ├── string.test.ts │ ├── typed-array.test.ts │ └── wk-symbols.test.ts │ ├── theory.js │ └── tsconfig.json ├── pnpm-lock.yaml └── pnpm-workspace.yaml /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - push 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: pnpm/action-setup@v4 11 | with: 12 | version: 8 13 | run_install: true 14 | 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: 22 18 | cache: "pnpm" 19 | 20 | - name: Clean 21 | run: pnpm recursive run clean 22 | env: 23 | CI: true 24 | 25 | - name: Build 26 | run: pnpm recursive run build 27 | env: 28 | CI: true 29 | 30 | - name: Begin Tests 31 | run: pnpm recursive run test 32 | env: 33 | CI: true 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # parcel-bundler cache (https://parceljs.org/) 61 | .cache 62 | 63 | # next.js build output 64 | .next 65 | 66 | # nuxt.js build output 67 | .nuxt 68 | 69 | # vuepress build output 70 | .vuepress/dist 71 | 72 | # Serverless directories 73 | .serverless 74 | 75 | # FuseBox cache 76 | .fusebox/ 77 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | prefer-workspace-packages=true 2 | link-workspace-packages=true -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "biomejs.biome", 3 | "[typescript]": { 4 | "editor.defaultFormatter": "biomejs.biome" 5 | }, 6 | "[typescriptreact]": { 7 | "editor.defaultFormatter": "biomejs.biome" 8 | }, 9 | "[javascript]": { 10 | "editor.defaultFormatter": "biomejs.biome" 11 | }, 12 | "[json]": { 13 | "editor.defaultFormatter": "biomejs.biome" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Alexis Munsayac 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # seroval 2 | 3 | > Stringify JS values 4 | 5 | [![NPM](https://img.shields.io/npm/v/seroval.svg)](https://www.npmjs.com/package/seroval) [![JavaScript Style Guide](https://badgen.net/badge/code%20style/airbnb/ff5a5f?icon=airbnb)](https://github.com/airbnb/javascript) 6 | 7 | ## Install 8 | 9 | ```bash 10 | npm install --save seroval 11 | ``` 12 | 13 | ```bash 14 | yarn add seroval 15 | ``` 16 | 17 | ```bash 18 | pnpm add seroval 19 | ``` 20 | 21 | ## Usage 22 | 23 | ```js 24 | import { serialize } from 'seroval'; 25 | 26 | const object = { 27 | number: [Math.random(), -0, NaN, Infinity, -Infinity], 28 | string: ['hello world', ''], 29 | boolean: [true, false], 30 | null: null, 31 | undefined: undefined, 32 | bigint: 9007199254740991n, 33 | array: [,,,], // holes 34 | regexp: /[a-z0-9]+/i, 35 | date: new Date(), 36 | map: new Map([['hello', 'world']]), 37 | set: new Set(['hello', 'world']), 38 | }; 39 | 40 | // self cyclic references 41 | // recursive objects 42 | object.self = object; 43 | // recursive arrays 44 | object.array.push(object.array); 45 | // recursive maps 46 | object.map.set('self', object.map); 47 | // recursive sets 48 | object.set.add(object.set); 49 | 50 | // mutual cyclic references 51 | object.array.push(object.map); 52 | object.map.set('mutual', object.set); 53 | object.set.add(object.array); 54 | 55 | const result = serialize(object); 56 | console.log(result); 57 | ``` 58 | 59 | Output (as a string): 60 | 61 | ```js 62 | ((h,j,k,m,o)=>(o={number:[0.5337763749243287,-0,0/0,1/0,-1/0],string:["hello world","\x3Cscript>Hello World\x3C/script>"],boolean:[!0,!1],null:null,undefined:void 0,bigint:9007199254740991n,array:h=[,,,,k=(j=[],new Map([["hello","world"],["mutual",m=new Set(["hello","world"])]]))],regexp:/[a-z0-9]+/i,date:new Date("2023-12-07T17:28:57.909Z"),map:k,set:m},h[3]=h,k.set("self",k),m.add(m).add(h),o.self=o,o))() 63 | 64 | // Formatted for readability 65 | ((h, j, k, m, o) => ( 66 | (o = { 67 | number: [0.5337763749243287, -0, 0 / 0, 1 / 0, -1 / 0], 68 | string: ["hello world", "\x3Cscript>Hello World\x3C/script>"], 69 | boolean: [!0, !1], 70 | null: null, 71 | undefined: void 0, 72 | bigint: 9007199254740991n, 73 | array: (h = [ 74 | , 75 | , 76 | , 77 | , 78 | (k = 79 | ((j = []), 80 | new Map([ 81 | ["hello", "world"], 82 | ["mutual", (m = new Set(["hello", "world"]))], 83 | ]))), 84 | ]), 85 | regexp: /[a-z0-9]+/i, 86 | date: new Date("2023-12-07T17:28:57.909Z"), 87 | map: k, 88 | set: m, 89 | }), 90 | (h[3] = h), 91 | k.set("self", k), 92 | m.add(m).add(h), 93 | (o.self = o), 94 | o 95 | ))(); 96 | ``` 97 | 98 | ## Docs 99 | 100 | - [Serialization](https://github.com/lxsmnsyc/seroval/blob/main/docs/serialization.md) 101 | - [Compatibility](https://github.com/lxsmnsyc/seroval/blob/main/docs/compatibility.md) 102 | - [Isomorphic References](https://github.com/lxsmnsyc/seroval/blob/main/docs/isomorphic-refs.md) 103 | 104 | ## Sponsors 105 | 106 | ![Sponsors](https://github.com/lxsmnsyc/sponsors/blob/main/sponsors.svg?raw=true) 107 | 108 | ## License 109 | 110 | MIT © [lxsmnsyc](https://github.com/lxsmnsyc) 111 | -------------------------------------------------------------------------------- /benchmark/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.production 74 | .env.development 75 | 76 | # parcel-bundler cache (https://parceljs.org/) 77 | .cache 78 | 79 | # Next.js build output 80 | .next 81 | 82 | # Nuxt.js build / generate output 83 | .nuxt 84 | dist 85 | 86 | # Gatsby files 87 | .cache/ 88 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 89 | # https://nextjs.org/blog/next-9-1#public-directory-support 90 | # public 91 | 92 | # vuepress build output 93 | .vuepress/dist 94 | 95 | # Serverless directories 96 | .serverless/ 97 | 98 | # FuseBox cache 99 | .fusebox/ 100 | 101 | # DynamoDB Local files 102 | .dynamodb/ 103 | 104 | # TernJS port file 105 | .tern-port 106 | 107 | .npmrc 108 | 109 | 110 | build -------------------------------------------------------------------------------- /benchmark/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License Copyright (c) 2025 Alexis Munsayac 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /benchmark/README.md: -------------------------------------------------------------------------------- 1 | # benchmarks 2 | 3 | ## Libraries 4 | 5 | - [`devalue` by Rich Harris](https://github.com/Rich-Harris/devalue) 6 | - [`flatted` by WebReflection (Andrea Giammarchi)](https://github.com/WebReflection/flatted) 7 | - [`next-json` by Daniele Ricci](https://github.com/iccicci/next-json) 8 | - [`oson` by KnorpelSenf](https://github.com/KnorpelSenf/oson) 9 | - [`serialize-javascript` by Yahoo](https://github.com/yahoo/serialize-javascript) 10 | - [`superjson` by BlitzJS](https://github.com/blitz-js/superjson) 11 | - [`tosource` by Marcello Bastéa-Forte](https://github.com/marcello3d/node-tosource) 12 | - [`warp10` by Patrick Steele-Idem](https://github.com/patrick-steele-idem/warp10) 13 | 14 | ## Credits 15 | 16 | - [Dylan Piercey](https://github.com/DylanPiercey) 17 | -------------------------------------------------------------------------------- /benchmark/build-src.js: -------------------------------------------------------------------------------- 1 | import * as esbuild from 'esbuild'; 2 | 3 | esbuild.buildSync({ 4 | entryPoints: ['./src/index.ts'], 5 | outfile: './build/index.cjs', 6 | bundle: true, 7 | minify: true, 8 | sourcemap: false, 9 | format: 'cjs', 10 | platform: 'node', 11 | tsconfig: './tsconfig.json', 12 | target: 'es2018', 13 | legalComments: 'eof', 14 | external: ['benny'], 15 | }); 16 | -------------------------------------------------------------------------------- /benchmark/example.js: -------------------------------------------------------------------------------- 1 | import { serializeAsync } from 'seroval'; 2 | import { BlobPlugin } from 'seroval-plugins/web'; 3 | 4 | const example = new Blob(['Hello, World!'], { type: 'text/plain ' }); 5 | console.log( 6 | await serializeAsync(example, { 7 | plugins: [BlobPlugin], 8 | }), 9 | ); 10 | -------------------------------------------------------------------------------- /benchmark/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "seroval-benchmarks", 3 | "type": "module", 4 | "private": true, 5 | "scripts": { 6 | "build": "node build-src", 7 | "start": "node build-src && node build/index.cjs" 8 | }, 9 | "devDependencies": { 10 | "@types/node": "^22.15.12", 11 | "esbuild": "^0.25.1", 12 | "tslib": "^2.6.2", 13 | "typescript": "^5.8.3" 14 | }, 15 | "dependencies": { 16 | "benny": "^3.7.1", 17 | "devalue": "^5.1.1", 18 | "flatted": "^3.3.3", 19 | "next-json": "^0.4.0", 20 | "o-son": "^1.0.4", 21 | "serialize-javascript": "^6.0.2", 22 | "seroval": "workspace:*", 23 | "seroval-plugins": "workspace:*", 24 | "superjson": "^2.2.2", 25 | "tosource": "2.0.0-alpha.3", 26 | "turbo-stream": "^3.1.0", 27 | "warp10": "^2.1.0" 28 | }, 29 | "types": "./dist/types/index.d.ts", 30 | "main": "./dist/cjs/production/index.cjs", 31 | "module": "./dist/esm/production/index.mjs", 32 | "exports": { 33 | ".": { 34 | "development": { 35 | "require": "./dist/cjs/development/index.cjs", 36 | "import": "./dist/esm/development/index.mjs" 37 | }, 38 | "require": "./dist/cjs/production/index.cjs", 39 | "import": "./dist/esm/production/index.mjs", 40 | "types": "./dist/types/index.d.ts" 41 | } 42 | }, 43 | "typesVersions": { 44 | "*": {} 45 | }, 46 | "version": "1.3.2" 47 | } 48 | -------------------------------------------------------------------------------- /benchmark/pridepack.json: -------------------------------------------------------------------------------- 1 | { 2 | "target": "es2017" 3 | } 4 | -------------------------------------------------------------------------------- /benchmark/results/charts/circular-dedupe to string.chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | circular-dedupe to string 9 | 28 | 29 | 30 |
31 | 32 |
33 | 115 | 116 | -------------------------------------------------------------------------------- /benchmark/results/charts/circular-simple to string.chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | circular-simple to string 9 | 28 | 29 | 30 |
31 | 32 |
33 | 115 | 116 | -------------------------------------------------------------------------------- /benchmark/results/charts/dedupe-object to string.chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | dedupe-object to string 9 | 28 | 29 | 30 |
31 | 32 |
33 | 115 | 116 | -------------------------------------------------------------------------------- /benchmark/results/charts/simple-object to string.chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | simple-object to string 9 | 28 | 29 | 30 |
31 | 32 |
33 | 115 | 116 | -------------------------------------------------------------------------------- /benchmark/results/csv/circular-dedupe to string.csv: -------------------------------------------------------------------------------- 1 | "name","ops","margin","percentSlower" 2 | "devalue",219823,7.46,54.37 3 | "flatted",268047,2.07,44.36 4 | "o-son",448549,2.02,6.89 5 | "seroval",315383,0.22,34.54 6 | "turbo-stream",87216,6.18,81.9 7 | "warp10",481766,2.97,0 -------------------------------------------------------------------------------- /benchmark/results/csv/circular-simple to string.csv: -------------------------------------------------------------------------------- 1 | "name","ops","margin","percentSlower" 2 | "devalue",719969,3.33,64.68 3 | "flatted",1076294,0.58,47.2 4 | "next-json",615416,0.7,69.81 5 | "o-son",2038385,5.18,0 6 | "seroval",1100284,0.17,46.02 7 | "superjson",693240,0.57,65.99 8 | "turbo-stream",143123,22.3,92.98 9 | "warp10",2029482,1.26,0.44 -------------------------------------------------------------------------------- /benchmark/results/csv/dedupe-object to string.csv: -------------------------------------------------------------------------------- 1 | "name","ops","margin","percentSlower" 2 | "devalue",378078,2.11,69.1 3 | "flatted",474644,0.46,61.21 4 | "next-json",296957,1.45,75.73 5 | "o-son",780330,1.24,36.23 6 | "seroval",524487,0.54,57.14 7 | "superjson",264776,0.42,78.36 8 | "turbo-stream",121732,25.85,90.05 9 | "warp10",1223643,0.48,0 -------------------------------------------------------------------------------- /benchmark/results/csv/large-circular-collection to string.csv: -------------------------------------------------------------------------------- 1 | "name","ops","margin","percentSlower" 2 | "devalue",2062,0.6,71.92 3 | "flatted",2380,0.56,67.59 4 | "next-json",1541,0.43,79.01 5 | "o-son",3880,0.42,47.16 6 | "seroval",2098,0.5,71.43 7 | "superjson",1017,0.71,86.15 8 | "turbo-stream",1625,23.7,77.87 9 | "warp10",7343,0.13,0 -------------------------------------------------------------------------------- /benchmark/results/csv/large-complex-collection to string.csv: -------------------------------------------------------------------------------- 1 | "name","ops","margin","percentSlower" 2 | "devalue",14797,0.1,77.26 3 | "flatted",12780,0.08,80.36 4 | "JSON",65082,0.08,0 5 | "next-json",14281,0.11,78.06 6 | "o-son",25067,0.15,61.48 7 | "serialize-javascript",15676,1.75,75.91 8 | "seroval",19212,0.11,70.48 9 | "superjson",7604,9.41,88.32 10 | "tosource",16968,4.86,73.93 11 | "turbo-stream",9843,5.31,84.88 12 | "warp10",29435,3.52,54.77 -------------------------------------------------------------------------------- /benchmark/results/csv/large-dedupe-collection to string.csv: -------------------------------------------------------------------------------- 1 | "name","ops","margin","percentSlower" 2 | "devalue",2508,1.99,59.58 3 | "flatted",2462,0.89,60.32 4 | "JSON",3811,0.72,38.58 5 | "next-json",2263,0.29,63.53 6 | "o-son",4545,0.19,26.75 7 | "serialize-javascript",1419,0.86,77.13 8 | "seroval",2828,0.95,54.42 9 | "superjson",942,0.34,84.82 10 | "tosource",1001,0.5,83.87 11 | "turbo-stream",1964,2,68.35 12 | "warp10",6205,1.12,0 -------------------------------------------------------------------------------- /benchmark/results/csv/large-invalid-keys-collection to string.csv: -------------------------------------------------------------------------------- 1 | "name","ops","margin","percentSlower" 2 | "devalue",497,6.62,86.78 3 | "flatted",810,0.76,78.46 4 | "JSON",3760,0.57,0 5 | "next-json",688,0.81,81.7 6 | "o-son",1244,1,66.91 7 | "serialize-javascript",1315,3.37,65.03 8 | "seroval",884,2.12,76.49 9 | "superjson",392,5.28,89.57 10 | "tosource",569,4.78,84.87 11 | "turbo-stream",500,2.74,86.7 12 | "warp10",2108,1.54,43.94 -------------------------------------------------------------------------------- /benchmark/results/csv/large-simple-collection to string.csv: -------------------------------------------------------------------------------- 1 | "name","ops","margin","percentSlower" 2 | "devalue",742,2.24,77.16 3 | "flatted",739,1.77,77.25 4 | "JSON",3248,1.31,0 5 | "next-json",484,2.19,85.1 6 | "o-son",1025,5.16,68.44 7 | "serialize-javascript",1266,4.64,61.02 8 | "seroval",764,4.24,76.48 9 | "superjson",415,1.88,87.22 10 | "tosource",685,4.61,78.91 11 | "turbo-stream",500,4.68,84.61 12 | "warp10",2365,0.73,27.19 -------------------------------------------------------------------------------- /benchmark/results/csv/simple-object to string.csv: -------------------------------------------------------------------------------- 1 | "name","ops","margin","percentSlower" 2 | "devalue",1942992,1.39,46.95 3 | "flatted",3064783,0.96,16.32 4 | "next-json",1930111,0.76,47.3 5 | "o-son",3662355,0.97,0 6 | "seroval",2895279,1.97,20.94 7 | "superjson",1512774,2.01,58.69 8 | "turbo-stream",169238,23.26,95.38 9 | "warp10",2371186,36.7,35.26 -------------------------------------------------------------------------------- /benchmark/results/csv/small-collection to string.csv: -------------------------------------------------------------------------------- 1 | "name","ops","margin","percentSlower" 2 | "devalue",195427,8.94,83.7 3 | "flatted",146661,4.31,87.77 4 | "JSON",1198704,2.25,0 5 | "next-json",164459,2.59,86.28 6 | "o-son",210654,1.77,82.43 7 | "serialize-javascript",565628,0.53,52.81 8 | "seroval",271654,0.98,77.34 9 | "superjson",119836,1.07,90 10 | "tosource",259888,1.01,78.32 11 | "turbo-stream",84889,28.01,92.92 12 | "warp10",801806,0.71,33.11 -------------------------------------------------------------------------------- /benchmark/results/json/circular-dedupe to string.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "circular-dedupe to string", 3 | "date": "2025-02-25T08:49:00.450Z", 4 | "version": null, 5 | "results": [ 6 | { 7 | "name": "devalue", 8 | "ops": 219823, 9 | "margin": 7.46, 10 | "percentSlower": 54.37 11 | }, 12 | { 13 | "name": "flatted", 14 | "ops": 268047, 15 | "margin": 2.07, 16 | "percentSlower": 44.36 17 | }, 18 | { 19 | "name": "o-son", 20 | "ops": 448549, 21 | "margin": 2.02, 22 | "percentSlower": 6.89 23 | }, 24 | { 25 | "name": "seroval", 26 | "ops": 315383, 27 | "margin": 0.22, 28 | "percentSlower": 34.54 29 | }, 30 | { 31 | "name": "turbo-stream", 32 | "ops": 87216, 33 | "margin": 6.18, 34 | "percentSlower": 81.9 35 | }, 36 | { 37 | "name": "warp10", 38 | "ops": 481766, 39 | "margin": 2.97, 40 | "percentSlower": 0 41 | } 42 | ], 43 | "fastest": { 44 | "name": "warp10", 45 | "index": 5 46 | }, 47 | "slowest": { 48 | "name": "turbo-stream", 49 | "index": 4 50 | } 51 | } -------------------------------------------------------------------------------- /benchmark/results/json/circular-simple to string.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "circular-simple to string", 3 | "date": "2025-02-25T08:49:44.259Z", 4 | "version": null, 5 | "results": [ 6 | { 7 | "name": "devalue", 8 | "ops": 719969, 9 | "margin": 3.33, 10 | "percentSlower": 64.68 11 | }, 12 | { 13 | "name": "flatted", 14 | "ops": 1076294, 15 | "margin": 0.58, 16 | "percentSlower": 47.2 17 | }, 18 | { 19 | "name": "next-json", 20 | "ops": 615416, 21 | "margin": 0.7, 22 | "percentSlower": 69.81 23 | }, 24 | { 25 | "name": "o-son", 26 | "ops": 2038385, 27 | "margin": 5.18, 28 | "percentSlower": 0 29 | }, 30 | { 31 | "name": "seroval", 32 | "ops": 1100284, 33 | "margin": 0.17, 34 | "percentSlower": 46.02 35 | }, 36 | { 37 | "name": "superjson", 38 | "ops": 693240, 39 | "margin": 0.57, 40 | "percentSlower": 65.99 41 | }, 42 | { 43 | "name": "turbo-stream", 44 | "ops": 143123, 45 | "margin": 22.3, 46 | "percentSlower": 92.98 47 | }, 48 | { 49 | "name": "warp10", 50 | "ops": 2029482, 51 | "margin": 1.26, 52 | "percentSlower": 0.44 53 | } 54 | ], 55 | "fastest": { 56 | "name": "o-son", 57 | "index": 3 58 | }, 59 | "slowest": { 60 | "name": "turbo-stream", 61 | "index": 6 62 | } 63 | } -------------------------------------------------------------------------------- /benchmark/results/json/dedupe-object to string.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dedupe-object to string", 3 | "date": "2025-02-25T08:50:28.807Z", 4 | "version": null, 5 | "results": [ 6 | { 7 | "name": "devalue", 8 | "ops": 378078, 9 | "margin": 2.11, 10 | "percentSlower": 69.1 11 | }, 12 | { 13 | "name": "flatted", 14 | "ops": 474644, 15 | "margin": 0.46, 16 | "percentSlower": 61.21 17 | }, 18 | { 19 | "name": "next-json", 20 | "ops": 296957, 21 | "margin": 1.45, 22 | "percentSlower": 75.73 23 | }, 24 | { 25 | "name": "o-son", 26 | "ops": 780330, 27 | "margin": 1.24, 28 | "percentSlower": 36.23 29 | }, 30 | { 31 | "name": "seroval", 32 | "ops": 524487, 33 | "margin": 0.54, 34 | "percentSlower": 57.14 35 | }, 36 | { 37 | "name": "superjson", 38 | "ops": 264776, 39 | "margin": 0.42, 40 | "percentSlower": 78.36 41 | }, 42 | { 43 | "name": "turbo-stream", 44 | "ops": 121732, 45 | "margin": 25.85, 46 | "percentSlower": 90.05 47 | }, 48 | { 49 | "name": "warp10", 50 | "ops": 1223643, 51 | "margin": 0.48, 52 | "percentSlower": 0 53 | } 54 | ], 55 | "fastest": { 56 | "name": "warp10", 57 | "index": 7 58 | }, 59 | "slowest": { 60 | "name": "turbo-stream", 61 | "index": 6 62 | } 63 | } -------------------------------------------------------------------------------- /benchmark/results/json/large-circular-collection to string.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "large-circular-collection to string", 3 | "date": "2025-02-25T08:51:11.938Z", 4 | "version": null, 5 | "results": [ 6 | { 7 | "name": "devalue", 8 | "ops": 2062, 9 | "margin": 0.6, 10 | "percentSlower": 71.92 11 | }, 12 | { 13 | "name": "flatted", 14 | "ops": 2380, 15 | "margin": 0.56, 16 | "percentSlower": 67.59 17 | }, 18 | { 19 | "name": "next-json", 20 | "ops": 1541, 21 | "margin": 0.43, 22 | "percentSlower": 79.01 23 | }, 24 | { 25 | "name": "o-son", 26 | "ops": 3880, 27 | "margin": 0.42, 28 | "percentSlower": 47.16 29 | }, 30 | { 31 | "name": "seroval", 32 | "ops": 2098, 33 | "margin": 0.5, 34 | "percentSlower": 71.43 35 | }, 36 | { 37 | "name": "superjson", 38 | "ops": 1017, 39 | "margin": 0.71, 40 | "percentSlower": 86.15 41 | }, 42 | { 43 | "name": "turbo-stream", 44 | "ops": 1625, 45 | "margin": 23.7, 46 | "percentSlower": 77.87 47 | }, 48 | { 49 | "name": "warp10", 50 | "ops": 7343, 51 | "margin": 0.13, 52 | "percentSlower": 0 53 | } 54 | ], 55 | "fastest": { 56 | "name": "warp10", 57 | "index": 7 58 | }, 59 | "slowest": { 60 | "name": "superjson", 61 | "index": 5 62 | } 63 | } -------------------------------------------------------------------------------- /benchmark/results/json/large-complex-collection to string.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "large-complex-collection to string", 3 | "date": "2025-02-25T08:52:11.531Z", 4 | "version": null, 5 | "results": [ 6 | { 7 | "name": "devalue", 8 | "ops": 14797, 9 | "margin": 0.1, 10 | "percentSlower": 77.26 11 | }, 12 | { 13 | "name": "flatted", 14 | "ops": 12780, 15 | "margin": 0.08, 16 | "percentSlower": 80.36 17 | }, 18 | { 19 | "name": "JSON", 20 | "ops": 65082, 21 | "margin": 0.08, 22 | "percentSlower": 0 23 | }, 24 | { 25 | "name": "next-json", 26 | "ops": 14281, 27 | "margin": 0.11, 28 | "percentSlower": 78.06 29 | }, 30 | { 31 | "name": "o-son", 32 | "ops": 25067, 33 | "margin": 0.15, 34 | "percentSlower": 61.48 35 | }, 36 | { 37 | "name": "serialize-javascript", 38 | "ops": 15676, 39 | "margin": 1.75, 40 | "percentSlower": 75.91 41 | }, 42 | { 43 | "name": "seroval", 44 | "ops": 19212, 45 | "margin": 0.11, 46 | "percentSlower": 70.48 47 | }, 48 | { 49 | "name": "superjson", 50 | "ops": 7604, 51 | "margin": 9.41, 52 | "percentSlower": 88.32 53 | }, 54 | { 55 | "name": "tosource", 56 | "ops": 16968, 57 | "margin": 4.86, 58 | "percentSlower": 73.93 59 | }, 60 | { 61 | "name": "turbo-stream", 62 | "ops": 9843, 63 | "margin": 5.31, 64 | "percentSlower": 84.88 65 | }, 66 | { 67 | "name": "warp10", 68 | "ops": 29435, 69 | "margin": 3.52, 70 | "percentSlower": 54.77 71 | } 72 | ], 73 | "fastest": { 74 | "name": "JSON", 75 | "index": 2 76 | }, 77 | "slowest": { 78 | "name": "superjson", 79 | "index": 7 80 | } 81 | } -------------------------------------------------------------------------------- /benchmark/results/json/large-dedupe-collection to string.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "large-dedupe-collection to string", 3 | "date": "2025-02-25T08:53:10.216Z", 4 | "version": null, 5 | "results": [ 6 | { 7 | "name": "devalue", 8 | "ops": 2508, 9 | "margin": 1.99, 10 | "percentSlower": 59.58 11 | }, 12 | { 13 | "name": "flatted", 14 | "ops": 2462, 15 | "margin": 0.89, 16 | "percentSlower": 60.32 17 | }, 18 | { 19 | "name": "JSON", 20 | "ops": 3811, 21 | "margin": 0.72, 22 | "percentSlower": 38.58 23 | }, 24 | { 25 | "name": "next-json", 26 | "ops": 2263, 27 | "margin": 0.29, 28 | "percentSlower": 63.53 29 | }, 30 | { 31 | "name": "o-son", 32 | "ops": 4545, 33 | "margin": 0.19, 34 | "percentSlower": 26.75 35 | }, 36 | { 37 | "name": "serialize-javascript", 38 | "ops": 1419, 39 | "margin": 0.86, 40 | "percentSlower": 77.13 41 | }, 42 | { 43 | "name": "seroval", 44 | "ops": 2828, 45 | "margin": 0.95, 46 | "percentSlower": 54.42 47 | }, 48 | { 49 | "name": "superjson", 50 | "ops": 942, 51 | "margin": 0.34, 52 | "percentSlower": 84.82 53 | }, 54 | { 55 | "name": "tosource", 56 | "ops": 1001, 57 | "margin": 0.5, 58 | "percentSlower": 83.87 59 | }, 60 | { 61 | "name": "turbo-stream", 62 | "ops": 1964, 63 | "margin": 2, 64 | "percentSlower": 68.35 65 | }, 66 | { 67 | "name": "warp10", 68 | "ops": 6205, 69 | "margin": 1.12, 70 | "percentSlower": 0 71 | } 72 | ], 73 | "fastest": { 74 | "name": "warp10", 75 | "index": 10 76 | }, 77 | "slowest": { 78 | "name": "superjson", 79 | "index": 7 80 | } 81 | } -------------------------------------------------------------------------------- /benchmark/results/json/large-invalid-keys-collection to string.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "large-invalid-keys-collection to string", 3 | "date": "2025-02-25T08:54:08.698Z", 4 | "version": null, 5 | "results": [ 6 | { 7 | "name": "devalue", 8 | "ops": 497, 9 | "margin": 6.62, 10 | "percentSlower": 86.78 11 | }, 12 | { 13 | "name": "flatted", 14 | "ops": 810, 15 | "margin": 0.76, 16 | "percentSlower": 78.46 17 | }, 18 | { 19 | "name": "JSON", 20 | "ops": 3760, 21 | "margin": 0.57, 22 | "percentSlower": 0 23 | }, 24 | { 25 | "name": "next-json", 26 | "ops": 688, 27 | "margin": 0.81, 28 | "percentSlower": 81.7 29 | }, 30 | { 31 | "name": "o-son", 32 | "ops": 1244, 33 | "margin": 1, 34 | "percentSlower": 66.91 35 | }, 36 | { 37 | "name": "serialize-javascript", 38 | "ops": 1315, 39 | "margin": 3.37, 40 | "percentSlower": 65.03 41 | }, 42 | { 43 | "name": "seroval", 44 | "ops": 884, 45 | "margin": 2.12, 46 | "percentSlower": 76.49 47 | }, 48 | { 49 | "name": "superjson", 50 | "ops": 392, 51 | "margin": 5.28, 52 | "percentSlower": 89.57 53 | }, 54 | { 55 | "name": "tosource", 56 | "ops": 569, 57 | "margin": 4.78, 58 | "percentSlower": 84.87 59 | }, 60 | { 61 | "name": "turbo-stream", 62 | "ops": 500, 63 | "margin": 2.74, 64 | "percentSlower": 86.7 65 | }, 66 | { 67 | "name": "warp10", 68 | "ops": 2108, 69 | "margin": 1.54, 70 | "percentSlower": 43.94 71 | } 72 | ], 73 | "fastest": { 74 | "name": "JSON", 75 | "index": 2 76 | }, 77 | "slowest": { 78 | "name": "superjson", 79 | "index": 7 80 | } 81 | } -------------------------------------------------------------------------------- /benchmark/results/json/large-simple-collection to string.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "large-simple-collection to string", 3 | "date": "2025-02-25T08:55:07.350Z", 4 | "version": null, 5 | "results": [ 6 | { 7 | "name": "devalue", 8 | "ops": 742, 9 | "margin": 2.24, 10 | "percentSlower": 77.16 11 | }, 12 | { 13 | "name": "flatted", 14 | "ops": 739, 15 | "margin": 1.77, 16 | "percentSlower": 77.25 17 | }, 18 | { 19 | "name": "JSON", 20 | "ops": 3248, 21 | "margin": 1.31, 22 | "percentSlower": 0 23 | }, 24 | { 25 | "name": "next-json", 26 | "ops": 484, 27 | "margin": 2.19, 28 | "percentSlower": 85.1 29 | }, 30 | { 31 | "name": "o-son", 32 | "ops": 1025, 33 | "margin": 5.16, 34 | "percentSlower": 68.44 35 | }, 36 | { 37 | "name": "serialize-javascript", 38 | "ops": 1266, 39 | "margin": 4.64, 40 | "percentSlower": 61.02 41 | }, 42 | { 43 | "name": "seroval", 44 | "ops": 764, 45 | "margin": 4.24, 46 | "percentSlower": 76.48 47 | }, 48 | { 49 | "name": "superjson", 50 | "ops": 415, 51 | "margin": 1.88, 52 | "percentSlower": 87.22 53 | }, 54 | { 55 | "name": "tosource", 56 | "ops": 685, 57 | "margin": 4.61, 58 | "percentSlower": 78.91 59 | }, 60 | { 61 | "name": "turbo-stream", 62 | "ops": 500, 63 | "margin": 4.68, 64 | "percentSlower": 84.61 65 | }, 66 | { 67 | "name": "warp10", 68 | "ops": 2365, 69 | "margin": 0.73, 70 | "percentSlower": 27.19 71 | } 72 | ], 73 | "fastest": { 74 | "name": "JSON", 75 | "index": 2 76 | }, 77 | "slowest": { 78 | "name": "superjson", 79 | "index": 7 80 | } 81 | } -------------------------------------------------------------------------------- /benchmark/results/json/simple-object to string.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-object to string", 3 | "date": "2025-02-25T08:55:50.971Z", 4 | "version": null, 5 | "results": [ 6 | { 7 | "name": "devalue", 8 | "ops": 1942992, 9 | "margin": 1.39, 10 | "percentSlower": 46.95 11 | }, 12 | { 13 | "name": "flatted", 14 | "ops": 3064783, 15 | "margin": 0.96, 16 | "percentSlower": 16.32 17 | }, 18 | { 19 | "name": "next-json", 20 | "ops": 1930111, 21 | "margin": 0.76, 22 | "percentSlower": 47.3 23 | }, 24 | { 25 | "name": "o-son", 26 | "ops": 3662355, 27 | "margin": 0.97, 28 | "percentSlower": 0 29 | }, 30 | { 31 | "name": "seroval", 32 | "ops": 2895279, 33 | "margin": 1.97, 34 | "percentSlower": 20.94 35 | }, 36 | { 37 | "name": "superjson", 38 | "ops": 1512774, 39 | "margin": 2.01, 40 | "percentSlower": 58.69 41 | }, 42 | { 43 | "name": "turbo-stream", 44 | "ops": 169238, 45 | "margin": 23.26, 46 | "percentSlower": 95.38 47 | }, 48 | { 49 | "name": "warp10", 50 | "ops": 2371186, 51 | "margin": 36.7, 52 | "percentSlower": 35.26 53 | } 54 | ], 55 | "fastest": { 56 | "name": "o-son", 57 | "index": 3 58 | }, 59 | "slowest": { 60 | "name": "turbo-stream", 61 | "index": 6 62 | } 63 | } -------------------------------------------------------------------------------- /benchmark/results/json/small-collection to string.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "small-collection to string", 3 | "date": "2025-02-25T08:56:50.692Z", 4 | "version": null, 5 | "results": [ 6 | { 7 | "name": "devalue", 8 | "ops": 195427, 9 | "margin": 8.94, 10 | "percentSlower": 83.7 11 | }, 12 | { 13 | "name": "flatted", 14 | "ops": 146661, 15 | "margin": 4.31, 16 | "percentSlower": 87.77 17 | }, 18 | { 19 | "name": "JSON", 20 | "ops": 1198704, 21 | "margin": 2.25, 22 | "percentSlower": 0 23 | }, 24 | { 25 | "name": "next-json", 26 | "ops": 164459, 27 | "margin": 2.59, 28 | "percentSlower": 86.28 29 | }, 30 | { 31 | "name": "o-son", 32 | "ops": 210654, 33 | "margin": 1.77, 34 | "percentSlower": 82.43 35 | }, 36 | { 37 | "name": "serialize-javascript", 38 | "ops": 565628, 39 | "margin": 0.53, 40 | "percentSlower": 52.81 41 | }, 42 | { 43 | "name": "seroval", 44 | "ops": 271654, 45 | "margin": 0.98, 46 | "percentSlower": 77.34 47 | }, 48 | { 49 | "name": "superjson", 50 | "ops": 119836, 51 | "margin": 1.07, 52 | "percentSlower": 90 53 | }, 54 | { 55 | "name": "tosource", 56 | "ops": 259888, 57 | "margin": 1.01, 58 | "percentSlower": 78.32 59 | }, 60 | { 61 | "name": "turbo-stream", 62 | "ops": 84889, 63 | "margin": 28.01, 64 | "percentSlower": 92.92 65 | }, 66 | { 67 | "name": "warp10", 68 | "ops": 801806, 69 | "margin": 0.71, 70 | "percentSlower": 33.11 71 | } 72 | ], 73 | "fastest": { 74 | "name": "JSON", 75 | "index": 2 76 | }, 77 | "slowest": { 78 | "name": "turbo-stream", 79 | "index": 9 80 | } 81 | } -------------------------------------------------------------------------------- /benchmark/src/fixtures/circular-dedupe.ts: -------------------------------------------------------------------------------- 1 | interface Parent { 2 | name: string; 3 | age: number; 4 | children: Child[]; 5 | } 6 | 7 | interface Child { 8 | name: string; 9 | age: number; 10 | father: Parent; 11 | mother: Parent; 12 | } 13 | 14 | export default () => { 15 | const mother: Parent = { 16 | name: 'Jane', 17 | age: 30, 18 | children: [], 19 | }; 20 | 21 | const father: Parent = { 22 | name: 'Frank', 23 | age: 32, 24 | children: [], 25 | }; 26 | 27 | const child1: Child = { 28 | name: 'Sue', 29 | age: 5, 30 | mother, // circular 31 | father, // circular 32 | }; 33 | 34 | const child2: Child = { 35 | name: 'Henry', 36 | age: 10, 37 | mother, // circular 38 | father, // circular 39 | }; 40 | 41 | mother.children.push(child1, child2); 42 | father.children.push(child1 /* duplicate */, child2 /* duplicate */); 43 | 44 | return { 45 | mother, 46 | father, 47 | }; 48 | }; 49 | -------------------------------------------------------------------------------- /benchmark/src/fixtures/circular-simple.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | const parent = { 3 | name: 'parent', 4 | } as { name: string; child: typeof child }; 5 | 6 | const child = { parent }; 7 | 8 | parent.child = child; 9 | 10 | return parent; 11 | }; 12 | -------------------------------------------------------------------------------- /benchmark/src/fixtures/dedupe-object.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | const child = { 3 | name: 'Henry', 4 | }; 5 | 6 | const mother = { 7 | name: 'Jane', 8 | child, 9 | }; 10 | 11 | const father = { 12 | name: 'Frank', 13 | child, 14 | }; 15 | 16 | return { 17 | mother, 18 | father, 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /benchmark/src/fixtures/large-circular-collection.ts: -------------------------------------------------------------------------------- 1 | import getData from './large-simple-collection'; 2 | 3 | export default () => { 4 | const result = getData().slice(0, 250); 5 | 6 | for (let i = 0; i < 250; i++) { 7 | const item = result[i]; 8 | (item as any).selfA = (item as any).selfB = item; 9 | } 10 | 11 | return result; 12 | }; 13 | -------------------------------------------------------------------------------- /benchmark/src/fixtures/large-dedupe-collection.ts: -------------------------------------------------------------------------------- 1 | import getData from './large-simple-collection'; 2 | 3 | export default () => { 4 | const result = getData().slice(0, 250); 5 | 6 | for (let i = 0; i < 250; i++) { 7 | const item = result[i]; 8 | result.push(item, item, item); 9 | } 10 | 11 | return result; 12 | }; 13 | -------------------------------------------------------------------------------- /benchmark/src/fixtures/simple-object.ts: -------------------------------------------------------------------------------- 1 | export default () => ({ 2 | simple: true, 3 | }); 4 | -------------------------------------------------------------------------------- /benchmark/src/fixtures/small-collection.ts: -------------------------------------------------------------------------------- 1 | export default () => [ 2 | { 3 | id: 1, 4 | first_name: 'Jimmy', 5 | last_name: 'Hansen', 6 | email: 'jhansen0@skyrock.com', 7 | gender: 'Male', 8 | ip_address: '166.6.70.130', 9 | }, 10 | { 11 | id: 1, 12 | first_name: 'Judy', 13 | last_name: 'Cook', 14 | email: 'jcook0@themeforest.net', 15 | gender: 'Female', 16 | ip_address: '171.246.40.83', 17 | }, 18 | { 19 | id: 2, 20 | first_name: 'Anne', 21 | last_name: 'Thomas', 22 | email: 'athomas1@usda.gov', 23 | gender: 'Female', 24 | ip_address: '158.159.200.150', 25 | }, 26 | ]; 27 | -------------------------------------------------------------------------------- /benchmark/src/tools/devalue.ts: -------------------------------------------------------------------------------- 1 | import { uneval } from 'devalue'; 2 | 3 | export const name = 'devalue'; 4 | 5 | export function toString(data: unknown) { 6 | return uneval(data); 7 | } 8 | 9 | export function fromString(str: string) { 10 | return (0, eval)(`(${str})`); 11 | } 12 | -------------------------------------------------------------------------------- /benchmark/src/tools/flatted.ts: -------------------------------------------------------------------------------- 1 | import { stringify, parse } from 'flatted'; 2 | 3 | export const name = 'flatted'; 4 | 5 | export function toString(data: unknown) { 6 | return stringify(data); 7 | } 8 | 9 | export function fromString(str: string) { 10 | return parse(str); 11 | } 12 | -------------------------------------------------------------------------------- /benchmark/src/tools/json.ts: -------------------------------------------------------------------------------- 1 | const ESCAPED_CHARS = /[<\u2028\u2029]/g; 2 | 3 | export const name = 'JSON'; 4 | 5 | function replacer(m: string) { 6 | switch (m) { 7 | case '<': 8 | return '\\x3C'; 9 | case '\u2028': 10 | return '\\u2028'; 11 | case '\u2029': 12 | return '\\u2029'; 13 | default: 14 | return ''; 15 | } 16 | } 17 | 18 | // This is testing using JSON.stringify output injected into a '], 29 | boolean: [true, false], 30 | null: null, 31 | undefined: undefined, 32 | bigint: 9007199254740991n, 33 | array: [,,,], // holes 34 | regexp: /[a-z0-9]+/i, 35 | date: new Date(), 36 | map: new Map([['hello', 'world']]), 37 | set: new Set(['hello', 'world']), 38 | }; 39 | 40 | // self cyclic references 41 | // recursive objects 42 | object.self = object; 43 | // recursive arrays 44 | object.array.push(object.array); 45 | // recursive maps 46 | object.map.set('self', object.map); 47 | // recursive sets 48 | object.set.add(object.set); 49 | 50 | // mutual cyclic references 51 | object.array.push(object.map); 52 | object.map.set('mutual', object.set); 53 | object.set.add(object.array); 54 | 55 | const result = serialize(object); 56 | console.log(result); 57 | ``` 58 | 59 | Output (as a string): 60 | 61 | ```js 62 | ((h,j,k,m,o)=>(o={number:[0.5337763749243287,-0,0/0,1/0,-1/0],string:["hello world","\x3Cscript>Hello World\x3C/script>"],boolean:[!0,!1],null:null,undefined:void 0,bigint:9007199254740991n,array:h=[,,,,k=(j=[],new Map([["hello","world"],["mutual",m=new Set(["hello","world"])]]))],regexp:/[a-z0-9]+/i,date:new Date("2023-12-07T17:28:57.909Z"),map:k,set:m},h[3]=h,k.set("self",k),m.add(m).add(h),o.self=o,o))() 63 | 64 | // Formatted for readability 65 | ((h, j, k, m, o) => ( 66 | (o = { 67 | number: [0.5337763749243287, -0, 0 / 0, 1 / 0, -1 / 0], 68 | string: ["hello world", "\x3Cscript>Hello World\x3C/script>"], 69 | boolean: [!0, !1], 70 | null: null, 71 | undefined: void 0, 72 | bigint: 9007199254740991n, 73 | array: (h = [ 74 | , 75 | , 76 | , 77 | , 78 | (k = 79 | ((j = []), 80 | new Map([ 81 | ["hello", "world"], 82 | ["mutual", (m = new Set(["hello", "world"]))], 83 | ]))), 84 | ]), 85 | regexp: /[a-z0-9]+/i, 86 | date: new Date("2023-12-07T17:28:57.909Z"), 87 | map: k, 88 | set: m, 89 | }), 90 | (h[3] = h), 91 | k.set("self", k), 92 | m.add(m).add(h), 93 | (o.self = o), 94 | o 95 | ))(); 96 | ``` 97 | 98 | ## Docs 99 | 100 | - [Serialization](https://github.com/lxsmnsyc/seroval/blob/main/docs/serialization.md) 101 | - [Compatibility](https://github.com/lxsmnsyc/seroval/blob/main/docs/compatibility.md) 102 | - [Isomorphic References](https://github.com/lxsmnsyc/seroval/blob/main/docs/isomorphic-refs.md) 103 | 104 | ## Sponsors 105 | 106 | ![Sponsors](https://github.com/lxsmnsyc/sponsors/blob/main/sponsors.svg?raw=true) 107 | 108 | ## License 109 | 110 | MIT © [lxsmnsyc](https://github.com/lxsmnsyc) 111 | -------------------------------------------------------------------------------- /packages/seroval/assets/global-header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is used to represent the minified header script located at ./keys.ts 3 | * If anything, this code is the unminified, readable version of it. 4 | */ 5 | 6 | // Global references array. Global because we (ideally) want '], 6 | boolean: [true, false], 7 | null: null, 8 | undefined: undefined, 9 | bigint: 9007199254740991n, 10 | array: [, , ,], // holes 11 | regexp: /[a-z0-9]+/i, 12 | date: new Date(), 13 | map: new Map([['hello', 'world']]), 14 | set: new Set(['hello', 'world']), 15 | }; 16 | 17 | // self cyclic references 18 | // recursive objects 19 | object.self = object; 20 | // recursive arrays 21 | object.array.push(object.array); 22 | // recursive maps 23 | object.map.set('self', object.map); 24 | // recursive sets 25 | object.set.add(object.set); 26 | 27 | // mutual cyclic references 28 | object.array.push(object.map); 29 | object.map.set('mutual', object.set); 30 | object.set.add(object.array); 31 | 32 | const result = serialize(object); 33 | console.log(result); 34 | -------------------------------------------------------------------------------- /packages/seroval/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "seroval", 3 | "type": "module", 4 | "version": "1.3.2", 5 | "files": [ 6 | "dist", 7 | "src" 8 | ], 9 | "engines": { 10 | "node": ">=10" 11 | }, 12 | "license": "MIT", 13 | "keywords": [ 14 | "pridepack" 15 | ], 16 | "devDependencies": { 17 | "@types/node": "^22.15.12", 18 | "@vitest/ui": "^3.1.3", 19 | "pridepack": "2.6.4", 20 | "ts-prune": "^0.10.3", 21 | "tslib": "^2.8.1", 22 | "typescript": "^5.8.3", 23 | "vitest": "^3.1.3" 24 | }, 25 | "scripts": { 26 | "prepublishOnly": "pridepack clean && pridepack build", 27 | "build": "pridepack build", 28 | "type-check": "pridepack check", 29 | "clean": "pridepack clean", 30 | "watch": "pridepack watch", 31 | "start": "pridepack start", 32 | "dev": "pridepack dev", 33 | "test": "vitest", 34 | "test:ui": "vitest --ui", 35 | "prune": "ts-prune" 36 | }, 37 | "private": false, 38 | "description": "Stringify JS values", 39 | "repository": { 40 | "url": "https://github.com/lxsmnsyc/seroval.git", 41 | "type": "git" 42 | }, 43 | "homepage": "https://github.com/lxsmnsyc/seroval/tree/main/packages/seroval", 44 | "bugs": { 45 | "url": "https://github.com/lxsmnsyc/seroval/issues" 46 | }, 47 | "publishConfig": { 48 | "access": "public" 49 | }, 50 | "author": "Alexis Munsayac", 51 | "types": "./dist/types/index.d.ts", 52 | "main": "./dist/cjs/production/index.cjs", 53 | "module": "./dist/esm/production/index.mjs", 54 | "exports": { 55 | ".": { 56 | "types": "./dist/types/index.d.ts", 57 | "development": { 58 | "require": "./dist/cjs/development/index.cjs", 59 | "import": "./dist/esm/development/index.mjs" 60 | }, 61 | "require": "./dist/cjs/production/index.cjs", 62 | "import": "./dist/esm/production/index.mjs" 63 | } 64 | }, 65 | "typesVersions": { 66 | "*": {} 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /packages/seroval/pridepack.json: -------------------------------------------------------------------------------- 1 | { 2 | "target": "es2018" 3 | } 4 | -------------------------------------------------------------------------------- /packages/seroval/src/core/Serializer.ts: -------------------------------------------------------------------------------- 1 | import { crossSerializeStream } from './cross'; 2 | import { 3 | resolvePlugins, 4 | type Plugin, 5 | type PluginAccessOptions, 6 | } from './plugin'; 7 | import { serializeString } from './string'; 8 | 9 | export interface SerializerOptions extends PluginAccessOptions { 10 | globalIdentifier: string; 11 | scopeId?: string; 12 | disabledFeatures?: number; 13 | onData: (result: string) => void; 14 | onError: (error: unknown) => void; 15 | onDone?: () => void; 16 | } 17 | 18 | export default class Serializer { 19 | private alive = true; 20 | 21 | private flushed = false; 22 | 23 | private done = false; 24 | 25 | private pending = 0; 26 | 27 | private cleanups: (() => void)[] = []; 28 | 29 | private refs = new Map(); 30 | 31 | private plugins?: Plugin[]; 32 | 33 | constructor(private options: SerializerOptions) { 34 | this.plugins = resolvePlugins(options.plugins); 35 | } 36 | 37 | keys = new Set(); 38 | 39 | write(key: string, value: unknown): void { 40 | if (this.alive && !this.flushed) { 41 | this.pending++; 42 | this.keys.add(key); 43 | this.cleanups.push( 44 | crossSerializeStream(value, { 45 | plugins: this.plugins, 46 | scopeId: this.options.scopeId, 47 | refs: this.refs, 48 | disabledFeatures: this.options.disabledFeatures, 49 | onError: this.options.onError, 50 | onSerialize: (data, initial) => { 51 | if (this.alive) { 52 | this.options.onData( 53 | initial 54 | ? this.options.globalIdentifier + 55 | '["' + 56 | serializeString(key) + 57 | '"]=' + 58 | data 59 | : data, 60 | ); 61 | } 62 | }, 63 | onDone: () => { 64 | if (this.alive) { 65 | this.pending--; 66 | if ( 67 | this.pending <= 0 && 68 | this.flushed && 69 | !this.done && 70 | this.options.onDone 71 | ) { 72 | this.options.onDone(); 73 | this.done = true; 74 | } 75 | } 76 | }, 77 | }), 78 | ); 79 | } 80 | } 81 | 82 | ids = 0; 83 | 84 | private getNextID(): string { 85 | while (this.keys.has('' + this.ids)) { 86 | this.ids++; 87 | } 88 | return '' + this.ids; 89 | } 90 | 91 | push(value: unknown): string { 92 | const newID = this.getNextID(); 93 | this.write(newID, value); 94 | return newID; 95 | } 96 | 97 | flush(): void { 98 | if (this.alive) { 99 | this.flushed = true; 100 | if (this.pending <= 0 && !this.done && this.options.onDone) { 101 | this.options.onDone(); 102 | this.done = true; 103 | } 104 | } 105 | } 106 | 107 | close(): void { 108 | if (this.alive) { 109 | for (let i = 0, len = this.cleanups.length; i < len; i++) { 110 | this.cleanups[i](); 111 | } 112 | if (!this.done && this.options.onDone) { 113 | this.options.onDone(); 114 | this.done = true; 115 | } 116 | this.alive = false; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /packages/seroval/src/core/compat.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * References 3 | * - https://compat-table.github.io/compat-table/es6/ 4 | * - MDN 5 | */ 6 | 7 | export const enum Feature { 8 | AggregateError = 0x01, 9 | ArrowFunction = 0x02, 10 | ErrorPrototypeStack = 0x04, 11 | ObjectAssign = 0x08, 12 | BigIntTypedArray = 0x10, 13 | } 14 | 15 | export const ALL_ENABLED = 16 | Feature.AggregateError | 17 | Feature.ArrowFunction | 18 | Feature.ErrorPrototypeStack | 19 | Feature.ObjectAssign | 20 | Feature.BigIntTypedArray; 21 | -------------------------------------------------------------------------------- /packages/seroval/src/core/cross/async.ts: -------------------------------------------------------------------------------- 1 | import BaseAsyncParserContext from '../context/parser/async'; 2 | import type { SerovalMode } from '../plugin'; 3 | import type { CrossParserContextOptions } from './parser'; 4 | 5 | export type CrossAsyncParserContextOptions = CrossParserContextOptions; 6 | 7 | export default class CrossAsyncParserContext extends BaseAsyncParserContext { 8 | readonly mode: SerovalMode = 'cross'; 9 | } 10 | -------------------------------------------------------------------------------- /packages/seroval/src/core/cross/deserializer.ts: -------------------------------------------------------------------------------- 1 | import type { BaseDeserializerOptions } from '../context/deserializer'; 2 | import BaseDeserializerContext from '../context/deserializer'; 3 | import type { SerovalMode } from '../plugin'; 4 | 5 | export type CrossDeserializerContextOptions = BaseDeserializerOptions; 6 | 7 | export default class CrossDeserializerContext extends BaseDeserializerContext { 8 | readonly mode: SerovalMode = 'cross'; 9 | 10 | assignIndexedValue(index: number, value: T): T { 11 | if (!this.refs.has(index)) { 12 | this.refs.set(index, value); 13 | } 14 | return value; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/seroval/src/core/cross/parser.ts: -------------------------------------------------------------------------------- 1 | import type { BaseParserContextOptions } from '../context/parser'; 2 | 3 | export type CrossParserContextOptions = BaseParserContextOptions; 4 | 5 | export interface CrossContextOptions { 6 | scopeId?: string; 7 | } 8 | -------------------------------------------------------------------------------- /packages/seroval/src/core/cross/serializer.ts: -------------------------------------------------------------------------------- 1 | import { SerovalNodeType } from '../constants'; 2 | import type { BaseSerializerContextOptions } from '../context/serializer'; 3 | import BaseSerializerContext from '../context/serializer'; 4 | import { GLOBAL_CONTEXT_REFERENCES } from '../keys'; 5 | import type { SerovalMode } from '../plugin'; 6 | import { serializeString } from '../string'; 7 | import type { SerovalNode } from '../types'; 8 | import type { CrossContextOptions } from './parser'; 9 | 10 | export interface CrossSerializerContextOptions 11 | extends BaseSerializerContextOptions, 12 | CrossContextOptions {} 13 | 14 | export default class CrossSerializerContext extends BaseSerializerContext { 15 | readonly mode: SerovalMode = 'cross'; 16 | 17 | scopeId?: string; 18 | 19 | constructor(options: CrossSerializerContextOptions) { 20 | super(options); 21 | this.scopeId = options.scopeId; 22 | } 23 | 24 | getRefParam(id: number): string { 25 | return GLOBAL_CONTEXT_REFERENCES + '[' + id + ']'; 26 | } 27 | 28 | protected assignIndexedValue(index: number, value: string): string { 29 | // In cross-reference, we have to assume that 30 | // every reference are going to be referenced 31 | // in the future, and so we need to store 32 | // all of it into the reference array. 33 | return this.getRefParam(index) + '=' + value; 34 | } 35 | 36 | serializeTop(tree: SerovalNode): string { 37 | // Get the serialized result 38 | const result = this.serialize(tree); 39 | // If the node is a non-reference, return 40 | // the result immediately 41 | const id = tree.i; 42 | if (id == null) { 43 | return result; 44 | } 45 | // Get the patches 46 | const patches = this.resolvePatches(); 47 | // Get the variable that represents the root 48 | const ref = this.getRefParam(id); 49 | // Parameters needed for scoping 50 | const params = this.scopeId == null ? '' : GLOBAL_CONTEXT_REFERENCES; 51 | // If there are patches, append it after the result 52 | const body = patches ? '(' + result + ',' + patches + ref + ')' : result; 53 | // If there are no params, there's no need to generate a function 54 | if (params === '') { 55 | if (tree.t === SerovalNodeType.Object && !patches) { 56 | return '(' + body + ')'; 57 | } 58 | return body; 59 | } 60 | // Get the arguments for the IIFE 61 | const args = 62 | this.scopeId == null 63 | ? '()' 64 | : '(' + 65 | GLOBAL_CONTEXT_REFERENCES + 66 | '["' + 67 | serializeString(this.scopeId) + 68 | '"])'; 69 | // Create the IIFE 70 | return '(' + this.createFunction([params], body) + ')' + args; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /packages/seroval/src/core/cross/stream.ts: -------------------------------------------------------------------------------- 1 | import type { BaseStreamParserContextOptions } from '../context/parser/stream'; 2 | import BaseStreamParserContext from '../context/parser/stream'; 3 | import type { SerovalMode } from '../plugin'; 4 | 5 | export type CrossStreamParserContextOptions = BaseStreamParserContextOptions; 6 | 7 | export default class CrossStreamParserContext extends BaseStreamParserContext { 8 | readonly mode: SerovalMode = 'cross'; 9 | } 10 | -------------------------------------------------------------------------------- /packages/seroval/src/core/cross/sync.ts: -------------------------------------------------------------------------------- 1 | import BaseSyncParserContext from '../context/parser/sync'; 2 | import type { SerovalMode } from '../plugin'; 3 | import type { CrossParserContextOptions } from './parser'; 4 | 5 | export type CrossSyncParserContextOptions = CrossParserContextOptions; 6 | 7 | export default class CrossSyncParserContext extends BaseSyncParserContext { 8 | readonly mode: SerovalMode = 'cross'; 9 | } 10 | -------------------------------------------------------------------------------- /packages/seroval/src/core/errors.ts: -------------------------------------------------------------------------------- 1 | import { serializeString } from './string'; 2 | import type { SerovalNode } from './types'; 3 | 4 | const { toString: objectToString } = /* @__PURE__ */ Object.prototype; 5 | 6 | function getErrorMessage(type: string, cause: any): string { 7 | if (cause instanceof Error) { 8 | return `Seroval caught an error during the ${type} process. 9 | 10 | ${cause.name} 11 | ${cause.message} 12 | 13 | - For more information, please check the "cause" property of this error. 14 | - If you believe this is an error in Seroval, please submit an issue at https://github.com/lxsmnsyc/seroval/issues/new`; 15 | } 16 | return `Seroval caught an error during the ${type} process. 17 | 18 | "${objectToString.call(cause)}" 19 | 20 | For more information, please check the "cause" property of this error.`; 21 | } 22 | 23 | export class SerovalError extends Error { 24 | constructor( 25 | type: string, 26 | public cause: any, 27 | ) { 28 | super(getErrorMessage(type, cause)); 29 | } 30 | } 31 | 32 | export class SerovalParserError extends SerovalError { 33 | constructor(cause: any) { 34 | super('parsing', cause); 35 | } 36 | } 37 | 38 | export class SerovalSerializationError extends SerovalError { 39 | constructor(cause: any) { 40 | super('serialization', cause); 41 | } 42 | } 43 | 44 | export class SerovalDeserializationError extends SerovalError { 45 | constructor(cause: any) { 46 | super('deserialization', cause); 47 | } 48 | } 49 | 50 | export class SerovalUnsupportedTypeError extends Error { 51 | constructor(public value: unknown) { 52 | super( 53 | `The value ${objectToString.call(value)} of type "${typeof value}" cannot be parsed/serialized. 54 | 55 | There are few workarounds for this problem: 56 | - Transform the value in a way that it can be serialized. 57 | - If the reference is present on multiple runtimes (isomorphic), you can use the Reference API to map the references.`, 58 | ); 59 | } 60 | } 61 | 62 | export class SerovalUnsupportedNodeError extends Error { 63 | constructor(node: SerovalNode) { 64 | super('Unsupported node type "' + node.t + '".'); 65 | } 66 | } 67 | 68 | export class SerovalMissingPluginError extends Error { 69 | constructor(tag: string) { 70 | super('Missing plugin for tag "' + tag + '".'); 71 | } 72 | } 73 | 74 | export class SerovalMissingInstanceError extends Error { 75 | constructor(tag: string) { 76 | super('Missing "' + tag + '" instance.'); 77 | } 78 | } 79 | 80 | export class SerovalMissingReferenceError extends Error { 81 | constructor(public value: unknown) { 82 | super( 83 | 'Missing reference for the value "' + 84 | objectToString.call(value) + 85 | '" of type "' + 86 | typeof value + 87 | '"', 88 | ); 89 | } 90 | } 91 | 92 | export class SerovalMissingReferenceForIdError extends Error { 93 | constructor(id: string) { 94 | super('Missing reference for id "' + serializeString(id) + '"'); 95 | } 96 | } 97 | 98 | export class SerovalUnknownTypedArrayError extends Error { 99 | constructor(name: string) { 100 | super('Unknown TypedArray "' + name + '"'); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /packages/seroval/src/core/function-string.ts: -------------------------------------------------------------------------------- 1 | import { Feature } from './compat'; 2 | 3 | export function createFunction( 4 | features: number, 5 | parameters: string[], 6 | body: string, 7 | ): string { 8 | if (features & Feature.ArrowFunction) { 9 | const joined = 10 | parameters.length === 1 11 | ? parameters[0] 12 | : '(' + parameters.join(',') + ')'; 13 | return joined + '=>' + (body.startsWith('{') ? '(' + body + ')' : body); 14 | } 15 | return 'function(' + parameters.join(',') + '){return ' + body + '}'; 16 | } 17 | 18 | export function createEffectfulFunction( 19 | features: number, 20 | parameters: string[], 21 | body: string, 22 | ): string { 23 | if (features & Feature.ArrowFunction) { 24 | const joined = 25 | parameters.length === 1 26 | ? parameters[0] 27 | : '(' + parameters.join(',') + ')'; 28 | return joined + '=>{' + body + '}'; 29 | } 30 | return 'function(' + parameters.join(',') + '){' + body + '}'; 31 | } 32 | -------------------------------------------------------------------------------- /packages/seroval/src/core/keys.ts: -------------------------------------------------------------------------------- 1 | import { serializeString } from './string'; 2 | 3 | // Used for mapping isomorphic references 4 | export const REFERENCES_KEY = '__SEROVAL_REFS__'; 5 | 6 | export const GLOBAL_CONTEXT_REFERENCES = '$R'; 7 | 8 | const GLOBAL_CONTEXT_R = `self.${GLOBAL_CONTEXT_REFERENCES}`; 9 | 10 | export function getCrossReferenceHeader(id?: string): string { 11 | if (id == null) { 12 | return `${GLOBAL_CONTEXT_R}=${GLOBAL_CONTEXT_R}||[]`; 13 | } 14 | return `(${GLOBAL_CONTEXT_R}=${GLOBAL_CONTEXT_R}||{})["${serializeString( 15 | id, 16 | )}"]=[]`; 17 | } 18 | -------------------------------------------------------------------------------- /packages/seroval/src/core/literals.ts: -------------------------------------------------------------------------------- 1 | import { NIL, SerovalConstant, SerovalNodeType } from './constants'; 2 | import { createSerovalNode } from './node'; 3 | import type { SerovalConstantNode } from './types'; 4 | 5 | function createConstantNode(value: SerovalConstant): SerovalConstantNode { 6 | return createSerovalNode( 7 | SerovalNodeType.Constant, 8 | NIL, 9 | value, 10 | NIL, 11 | NIL, 12 | NIL, 13 | NIL, 14 | NIL, 15 | NIL, 16 | NIL, 17 | NIL, 18 | NIL, 19 | ); 20 | } 21 | 22 | export const TRUE_NODE = /* @__PURE__ */ createConstantNode( 23 | SerovalConstant.True, 24 | ); 25 | export const FALSE_NODE = /* @__PURE__ */ createConstantNode( 26 | SerovalConstant.False, 27 | ); 28 | export const UNDEFINED_NODE = /* @__PURE__ */ createConstantNode( 29 | SerovalConstant.Undefined, 30 | ); 31 | export const NULL_NODE = /* @__PURE__ */ createConstantNode( 32 | SerovalConstant.Null, 33 | ); 34 | export const NEG_ZERO_NODE = /* @__PURE__ */ createConstantNode( 35 | SerovalConstant.NegZero, 36 | ); 37 | export const INFINITY_NODE = /* @__PURE__ */ createConstantNode( 38 | SerovalConstant.Inf, 39 | ); 40 | export const NEG_INFINITY_NODE = /* @__PURE__ */ createConstantNode( 41 | SerovalConstant.NegInf, 42 | ); 43 | export const NAN_NODE = /* @__PURE__ */ createConstantNode(SerovalConstant.Nan); 44 | -------------------------------------------------------------------------------- /packages/seroval/src/core/node.ts: -------------------------------------------------------------------------------- 1 | import type { SerovalNodeType } from './constants'; 2 | import type { SerovalNode } from './types'; 3 | 4 | type ExtractedNodeType = Extract< 5 | SerovalNode, 6 | { t: T } 7 | >; 8 | 9 | export function createSerovalNode< 10 | T extends SerovalNodeType, 11 | N extends ExtractedNodeType, 12 | >( 13 | t: T, 14 | i: N['i'], 15 | s: N['s'], 16 | l: N['l'], 17 | c: N['c'], 18 | m: N['m'], 19 | p: N['p'], 20 | e: N['e'], 21 | a: N['a'], 22 | f: N['f'], 23 | b: N['b'], 24 | o: N['o'], 25 | ): N { 26 | return { 27 | t, 28 | i, 29 | s, 30 | l, 31 | c, 32 | m, 33 | p, 34 | e, 35 | a, 36 | f, 37 | b, 38 | o, 39 | } as N; 40 | } 41 | -------------------------------------------------------------------------------- /packages/seroval/src/core/opaque-reference.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * An opaque reference allows hiding values from the serializer. 3 | */ 4 | export class OpaqueReference { 5 | constructor( 6 | public readonly value: V, 7 | public readonly replacement?: R, 8 | ) {} 9 | } 10 | -------------------------------------------------------------------------------- /packages/seroval/src/core/plugin.ts: -------------------------------------------------------------------------------- 1 | import type BaseDeserializerContext from './context/deserializer'; 2 | import type BaseAsyncParserContext from './context/parser/async'; 3 | import type BaseStreamParserContext from './context/parser/stream'; 4 | import type BaseSyncParserContext from './context/parser/sync'; 5 | import type BaseSerializerContext from './context/serializer'; 6 | 7 | export type SerovalMode = 'vanilla' | 'cross'; 8 | 9 | export interface PluginData { 10 | id: number; 11 | } 12 | 13 | export interface Plugin { 14 | /** 15 | * A unique string that helps idenfity the plugin 16 | */ 17 | tag: string; 18 | /** 19 | * List of dependency plugins 20 | */ 21 | extends?: Plugin[]; 22 | /** 23 | * Method to test if a value is an expected value of the plugin 24 | * @param value 25 | */ 26 | test(value: unknown): boolean; 27 | /** 28 | * Parsing modes 29 | */ 30 | parse: { 31 | sync?: (value: Value, ctx: BaseSyncParserContext, data: PluginData) => Node; 32 | async?: ( 33 | value: Value, 34 | ctx: BaseAsyncParserContext, 35 | data: PluginData, 36 | ) => Promise; 37 | stream?: ( 38 | value: Value, 39 | ctx: BaseStreamParserContext, 40 | data: PluginData, 41 | ) => Node; 42 | }; 43 | /** 44 | * Convert the parsed node into a JS string 45 | */ 46 | serialize(node: Node, ctx: BaseSerializerContext, data: PluginData): string; 47 | /** 48 | * Convert the parsed node into its runtime equivalent. 49 | */ 50 | deserialize( 51 | node: Node, 52 | ctx: BaseDeserializerContext, 53 | data: PluginData, 54 | ): Value; 55 | } 56 | 57 | export function createPlugin( 58 | plugin: Plugin, 59 | ): Plugin { 60 | return plugin; 61 | } 62 | 63 | export interface PluginAccessOptions { 64 | plugins?: Plugin[]; 65 | } 66 | 67 | function dedupePlugins( 68 | deduped: Set>, 69 | plugins: Plugin[], 70 | ): void { 71 | for (let i = 0, len = plugins.length; i < len; i++) { 72 | const current = plugins[i]; 73 | if (!deduped.has(current)) { 74 | deduped.add(current); 75 | if (current.extends) { 76 | dedupePlugins(deduped, current.extends); 77 | } 78 | } 79 | } 80 | } 81 | 82 | export function resolvePlugins( 83 | plugins?: Plugin[], 84 | ): Plugin[] | undefined { 85 | if (plugins) { 86 | const deduped = new Set>(); 87 | dedupePlugins(deduped, plugins); 88 | return [...deduped]; 89 | } 90 | return undefined; 91 | } 92 | -------------------------------------------------------------------------------- /packages/seroval/src/core/reference.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SerovalMissingReferenceError, 3 | SerovalMissingReferenceForIdError, 4 | } from '..'; 5 | import { REFERENCES_KEY } from './keys'; 6 | import assert from './utils/assert'; 7 | 8 | const REFERENCE = new Map(); 9 | const INV_REFERENCE = new Map(); 10 | 11 | export function createReference(id: string, value: T): T { 12 | REFERENCE.set(value, id); 13 | INV_REFERENCE.set(id, value); 14 | return value; 15 | } 16 | 17 | export function hasReferenceID(value: T): boolean { 18 | return REFERENCE.has(value); 19 | } 20 | 21 | export function hasReference(id: string): boolean { 22 | return INV_REFERENCE.has(id); 23 | } 24 | 25 | export function getReferenceID(value: T): string { 26 | assert(hasReferenceID(value), new SerovalMissingReferenceError(value)); 27 | return REFERENCE.get(value)!; 28 | } 29 | 30 | export function getReference(id: string): T { 31 | assert(hasReference(id), new SerovalMissingReferenceForIdError(id)); 32 | return INV_REFERENCE.get(id) as T; 33 | } 34 | 35 | if (typeof globalThis !== 'undefined') { 36 | Object.defineProperty(globalThis, REFERENCES_KEY, { 37 | value: INV_REFERENCE, 38 | configurable: true, 39 | writable: false, 40 | enumerable: false, 41 | }); 42 | } else if (typeof window !== 'undefined') { 43 | Object.defineProperty(window, REFERENCES_KEY, { 44 | value: INV_REFERENCE, 45 | configurable: true, 46 | writable: false, 47 | enumerable: false, 48 | }); 49 | } else if (typeof self !== 'undefined') { 50 | Object.defineProperty(self, REFERENCES_KEY, { 51 | value: INV_REFERENCE, 52 | configurable: true, 53 | writable: false, 54 | enumerable: false, 55 | }); 56 | } else if (typeof global !== 'undefined') { 57 | Object.defineProperty(global, REFERENCES_KEY, { 58 | value: INV_REFERENCE, 59 | configurable: true, 60 | writable: false, 61 | enumerable: false, 62 | }); 63 | } 64 | -------------------------------------------------------------------------------- /packages/seroval/src/core/special-reference.ts: -------------------------------------------------------------------------------- 1 | import { createEffectfulFunction, createFunction } from './function-string'; 2 | 3 | export const ITERATOR = {}; 4 | 5 | export const ASYNC_ITERATOR = {}; 6 | 7 | export const enum SpecialReference { 8 | MapSentinel = 0, 9 | PromiseConstructor = 1, 10 | PromiseSuccess = 2, 11 | PromiseFailure = 3, 12 | StreamConstructor = 4, 13 | } 14 | 15 | /** 16 | * Placeholder references 17 | */ 18 | export const SPECIAL_REFS: Record = { 19 | [SpecialReference.MapSentinel]: {}, 20 | [SpecialReference.PromiseConstructor]: {}, 21 | [SpecialReference.PromiseSuccess]: {}, 22 | [SpecialReference.PromiseFailure]: {}, 23 | [SpecialReference.StreamConstructor]: {}, 24 | }; 25 | 26 | function serializePromiseConstructor(features: number): string { 27 | return createFunction( 28 | features, 29 | ['r'], 30 | '(r.p=new Promise(' + 31 | createEffectfulFunction(features, ['s', 'f'], 'r.s=s,r.f=f') + 32 | '))', 33 | ); 34 | } 35 | 36 | function serializePromiseSuccess(features: number): string { 37 | return createEffectfulFunction( 38 | features, 39 | ['r', 'd'], 40 | 'r.s(d),r.p.s=1,r.p.v=d', 41 | ); 42 | } 43 | 44 | function serializePromiseFailure(features: number): string { 45 | return createEffectfulFunction( 46 | features, 47 | ['r', 'd'], 48 | 'r.f(d),r.p.s=2,r.p.v=d', 49 | ); 50 | } 51 | 52 | function serializeStreamConstructor(features: number): string { 53 | return createFunction( 54 | features, 55 | ['b', 'a', 's', 'l', 'p', 'f', 'e', 'n'], 56 | '(b=[],a=!0,s=!1,l=[],p=0,f=' + 57 | createEffectfulFunction( 58 | features, 59 | ['v', 'm', 'x'], 60 | 'for(x=0;x" and "\" to avoid invalid escapes in the output. 32 | // http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4 33 | export function serializeString(str: string): string { 34 | let result = ''; 35 | let lastPos = 0; 36 | let replacement: string | undefined; 37 | for (let i = 0, len = str.length; i < len; i++) { 38 | replacement = serializeChar(str[i]); 39 | if (replacement) { 40 | result += str.slice(lastPos, i) + replacement; 41 | lastPos = i + 1; 42 | } 43 | } 44 | if (lastPos === 0) { 45 | result = str; 46 | } else { 47 | result += str.slice(lastPos); 48 | } 49 | return result; 50 | } 51 | 52 | function deserializeReplacer(str: string): string { 53 | switch (str) { 54 | case '\\\\': 55 | return '\\'; 56 | case '\\"': 57 | return '"'; 58 | case '\\n': 59 | return '\n'; 60 | case '\\r': 61 | return '\r'; 62 | case '\\b': 63 | return '\b'; 64 | case '\\t': 65 | return '\t'; 66 | case '\\f': 67 | return '\f'; 68 | case '\\x3C': 69 | return '\x3C'; 70 | case '\\u2028': 71 | return '\u2028'; 72 | case '\\u2029': 73 | return '\u2029'; 74 | default: 75 | return str; 76 | } 77 | } 78 | 79 | export function deserializeString(str: string): string { 80 | return str.replace( 81 | /(\\\\|\\"|\\n|\\r|\\b|\\t|\\f|\\u2028|\\u2029|\\x3C)/g, 82 | deserializeReplacer, 83 | ); 84 | } 85 | -------------------------------------------------------------------------------- /packages/seroval/src/core/tree/async.ts: -------------------------------------------------------------------------------- 1 | import type { BaseParserContextOptions } from '../context/parser'; 2 | import BaseAsyncParserContext from '../context/parser/async'; 3 | import type { SerovalMode } from '../plugin'; 4 | 5 | export type AsyncParserContextOptions = Omit; 6 | 7 | export default class AsyncParserContext extends BaseAsyncParserContext { 8 | readonly mode: SerovalMode = 'vanilla'; 9 | } 10 | -------------------------------------------------------------------------------- /packages/seroval/src/core/tree/deserializer.ts: -------------------------------------------------------------------------------- 1 | import type { BaseDeserializerOptions } from '../context/deserializer'; 2 | import BaseDeserializerContext from '../context/deserializer'; 3 | import type { SerovalMode } from '../plugin'; 4 | 5 | export interface VanillaDeserializerContextOptions 6 | extends Omit { 7 | markedRefs: number[] | Set; 8 | } 9 | 10 | export default class VanillaDeserializerContext extends BaseDeserializerContext { 11 | readonly mode: SerovalMode = 'vanilla'; 12 | 13 | marked: Set; 14 | 15 | constructor(options: VanillaDeserializerContextOptions) { 16 | super(options); 17 | this.marked = new Set(options.markedRefs); 18 | } 19 | 20 | assignIndexedValue(index: number, value: T): T { 21 | if (this.marked.has(index)) { 22 | this.refs.set(index, value); 23 | } 24 | return value; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/seroval/src/core/tree/index.ts: -------------------------------------------------------------------------------- 1 | import { type PluginAccessOptions, resolvePlugins } from '../plugin'; 2 | import type { SerovalNode } from '../types'; 3 | import type { AsyncParserContextOptions } from './async'; 4 | import AsyncParserContext from './async'; 5 | import VanillaDeserializerContext from './deserializer'; 6 | import VanillaSerializerContext from './serializer'; 7 | import type { SyncParserContextOptions } from './sync'; 8 | import SyncParserContext from './sync'; 9 | 10 | export function serialize( 11 | source: T, 12 | options: SyncParserContextOptions = {}, 13 | ): string { 14 | const plugins = resolvePlugins(options.plugins); 15 | const ctx = new SyncParserContext({ 16 | plugins, 17 | disabledFeatures: options.disabledFeatures, 18 | }); 19 | const tree = ctx.parseTop(source); 20 | const serial = new VanillaSerializerContext({ 21 | plugins, 22 | features: ctx.features, 23 | markedRefs: ctx.marked, 24 | }); 25 | return serial.serializeTop(tree); 26 | } 27 | 28 | export async function serializeAsync( 29 | source: T, 30 | options: AsyncParserContextOptions = {}, 31 | ): Promise { 32 | const plugins = resolvePlugins(options.plugins); 33 | const ctx = new AsyncParserContext({ 34 | plugins, 35 | disabledFeatures: options.disabledFeatures, 36 | }); 37 | const tree = await ctx.parseTop(source); 38 | const serial = new VanillaSerializerContext({ 39 | plugins, 40 | features: ctx.features, 41 | markedRefs: ctx.marked, 42 | }); 43 | return serial.serializeTop(tree); 44 | } 45 | 46 | export function deserialize(source: string): T { 47 | return (0, eval)(source) as T; 48 | } 49 | 50 | export interface SerovalJSON { 51 | t: SerovalNode; 52 | f: number; 53 | m: number[]; 54 | } 55 | 56 | export function toJSON( 57 | source: T, 58 | options: SyncParserContextOptions = {}, 59 | ): SerovalJSON { 60 | const plugins = resolvePlugins(options.plugins); 61 | const ctx = new SyncParserContext({ 62 | plugins, 63 | disabledFeatures: options.disabledFeatures, 64 | }); 65 | return { 66 | t: ctx.parseTop(source), 67 | f: ctx.features, 68 | m: Array.from(ctx.marked), 69 | }; 70 | } 71 | 72 | export async function toJSONAsync( 73 | source: T, 74 | options: AsyncParserContextOptions = {}, 75 | ): Promise { 76 | const plugins = resolvePlugins(options.plugins); 77 | const ctx = new AsyncParserContext({ 78 | plugins, 79 | disabledFeatures: options.disabledFeatures, 80 | }); 81 | return { 82 | t: await ctx.parseTop(source), 83 | f: ctx.features, 84 | m: Array.from(ctx.marked), 85 | }; 86 | } 87 | 88 | export function compileJSON( 89 | source: SerovalJSON, 90 | options: PluginAccessOptions = {}, 91 | ): string { 92 | const plugins = resolvePlugins(options.plugins); 93 | const ctx = new VanillaSerializerContext({ 94 | plugins, 95 | features: source.f, 96 | markedRefs: source.m, 97 | }); 98 | return ctx.serializeTop(source.t); 99 | } 100 | 101 | export function fromJSON( 102 | source: SerovalJSON, 103 | options: PluginAccessOptions = {}, 104 | ): T { 105 | const plugins = resolvePlugins(options.plugins); 106 | const ctx = new VanillaDeserializerContext({ 107 | plugins, 108 | markedRefs: source.m, 109 | }); 110 | return ctx.deserializeTop(source.t) as T; 111 | } 112 | -------------------------------------------------------------------------------- /packages/seroval/src/core/tree/serializer.ts: -------------------------------------------------------------------------------- 1 | import { SerovalNodeType } from '../constants'; 2 | import type { BaseSerializerContextOptions } from '../context/serializer'; 3 | import BaseSerializerContext from '../context/serializer'; 4 | import { SerovalUnsupportedNodeError } from '../errors'; 5 | import type { SerovalMode } from '../plugin'; 6 | import type { 7 | SerovalNode, 8 | SerovalPromiseConstructorNode, 9 | SerovalPromiseRejectNode, 10 | SerovalPromiseResolveNode, 11 | } from '../types'; 12 | import getIdentifier from '../utils/get-identifier'; 13 | 14 | export type VanillaSerializerContextOptions = BaseSerializerContextOptions; 15 | 16 | export default class VanillaSerializerContext extends BaseSerializerContext { 17 | readonly mode: SerovalMode = 'vanilla'; 18 | 19 | /** 20 | * Map tree refs to actual refs 21 | * @private 22 | */ 23 | valid = new Map(); 24 | 25 | /** 26 | * Variables 27 | * @private 28 | */ 29 | vars: string[] = []; 30 | 31 | /** 32 | * Creates the reference param (identifier) from the given reference ID 33 | * Calling this function means the value has been referenced somewhere 34 | */ 35 | getRefParam(index: number): string { 36 | /** 37 | * Creates a new reference ID from a given reference ID 38 | * This new reference ID means that the reference itself 39 | * has been referenced at least once, and is used to generate 40 | * the variables 41 | */ 42 | let actualIndex = this.valid.get(index); 43 | if (actualIndex == null) { 44 | actualIndex = this.valid.size; 45 | this.valid.set(index, actualIndex); 46 | } 47 | let identifier = this.vars[actualIndex]; 48 | if (identifier == null) { 49 | identifier = getIdentifier(actualIndex); 50 | this.vars[actualIndex] = identifier; 51 | } 52 | return identifier; 53 | } 54 | 55 | protected assignIndexedValue(index: number, value: string): string { 56 | if (this.isMarked(index)) { 57 | return this.getRefParam(index) + '=' + value; 58 | } 59 | return value; 60 | } 61 | 62 | protected serializePromiseConstructor( 63 | node: SerovalPromiseConstructorNode, 64 | ): string { 65 | throw new SerovalUnsupportedNodeError(node); 66 | } 67 | 68 | protected serializePromiseResolve(node: SerovalPromiseResolveNode): string { 69 | throw new SerovalUnsupportedNodeError(node); 70 | } 71 | 72 | protected serializePromiseReject(node: SerovalPromiseRejectNode): string { 73 | throw new SerovalUnsupportedNodeError(node); 74 | } 75 | 76 | serializeTop(tree: SerovalNode): string { 77 | const result = this.serialize(tree); 78 | // Shared references detected 79 | if (tree.i != null && this.vars.length) { 80 | const patches = this.resolvePatches(); 81 | let body = result; 82 | if (patches) { 83 | // Get (or create) a ref from the source 84 | const index = this.getRefParam(tree.i); 85 | body = result + ',' + patches + index; 86 | if (!result.startsWith(index + '=')) { 87 | body = index + '=' + body; 88 | } 89 | body = '(' + body + ')'; 90 | } 91 | return '(' + this.createFunction(this.vars, body) + ')()'; 92 | } 93 | if (tree.t === SerovalNodeType.Object) { 94 | return '(' + result + ')'; 95 | } 96 | return result; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /packages/seroval/src/core/tree/sync.ts: -------------------------------------------------------------------------------- 1 | import BaseSyncParserContext from '../context/parser/sync'; 2 | import type { BaseParserContextOptions } from '../context/parser'; 3 | import type { SerovalMode } from '../plugin'; 4 | 5 | export type SyncParserContextOptions = Omit; 6 | 7 | export default class SyncParserContext extends BaseSyncParserContext { 8 | readonly mode: SerovalMode = 'vanilla'; 9 | } 10 | -------------------------------------------------------------------------------- /packages/seroval/src/core/utils/assert.ts: -------------------------------------------------------------------------------- 1 | export default function assert(cond: unknown, error: Error): asserts cond { 2 | if (!cond) { 3 | throw error; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/seroval/src/core/utils/deferred.ts: -------------------------------------------------------------------------------- 1 | export interface Deferred { 2 | promise: Promise; 3 | resolve(value: unknown): void; 4 | reject(value: unknown): void; 5 | } 6 | 7 | export function createDeferred(): Deferred { 8 | let resolve: Deferred['resolve']; 9 | let reject: Deferred['reject']; 10 | return { 11 | promise: new Promise((res, rej) => { 12 | resolve = res; 13 | reject = rej; 14 | }), 15 | resolve(value): void { 16 | resolve(value); 17 | }, 18 | reject(value): void { 19 | reject(value); 20 | }, 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /packages/seroval/src/core/utils/error.ts: -------------------------------------------------------------------------------- 1 | import { Feature } from '../compat'; 2 | import { ERROR_CONSTRUCTOR_STRING, ErrorConstructorTag } from '../constants'; 3 | 4 | type ErrorValue = 5 | | Error 6 | | AggregateError 7 | | EvalError 8 | | RangeError 9 | | ReferenceError 10 | | TypeError 11 | | SyntaxError 12 | | URIError; 13 | 14 | export function getErrorConstructor(error: ErrorValue): ErrorConstructorTag { 15 | if (error instanceof EvalError) { 16 | return ErrorConstructorTag.EvalError; 17 | } 18 | if (error instanceof RangeError) { 19 | return ErrorConstructorTag.RangeError; 20 | } 21 | if (error instanceof ReferenceError) { 22 | return ErrorConstructorTag.ReferenceError; 23 | } 24 | if (error instanceof SyntaxError) { 25 | return ErrorConstructorTag.SyntaxError; 26 | } 27 | if (error instanceof TypeError) { 28 | return ErrorConstructorTag.TypeError; 29 | } 30 | if (error instanceof URIError) { 31 | return ErrorConstructorTag.URIError; 32 | } 33 | return ErrorConstructorTag.Error; 34 | } 35 | 36 | function getInitialErrorOptions( 37 | error: Error, 38 | ): Record | undefined { 39 | const construct = ERROR_CONSTRUCTOR_STRING[getErrorConstructor(error)]; 40 | // Name has been modified 41 | if (error.name !== construct) { 42 | return { name: error.name }; 43 | } 44 | if (error.constructor.name !== construct) { 45 | // Otherwise, name is overriden because 46 | // the Error class is extended 47 | return { name: error.constructor.name }; 48 | } 49 | return {}; 50 | } 51 | 52 | export function getErrorOptions( 53 | error: Error, 54 | features: number, 55 | ): Record | undefined { 56 | let options = getInitialErrorOptions(error); 57 | const names = Object.getOwnPropertyNames(error); 58 | for (let i = 0, len = names.length, name: string; i < len; i++) { 59 | name = names[i]; 60 | if (name !== 'name' && name !== 'message') { 61 | if (name === 'stack') { 62 | if (features & Feature.ErrorPrototypeStack) { 63 | options = options || {}; 64 | options[name] = error[name as keyof Error]; 65 | } 66 | } else { 67 | options = options || {}; 68 | options[name] = error[name as keyof Error]; 69 | } 70 | } 71 | } 72 | return options; 73 | } 74 | -------------------------------------------------------------------------------- /packages/seroval/src/core/utils/get-identifier.ts: -------------------------------------------------------------------------------- 1 | // Written by https://github.com/DylanPiercey and is distributed under the MIT license. 2 | const REF_START_CHARS = /* @__PURE__ */ 'hjkmoquxzABCDEFGHIJKLNPQRTUVWXYZ$_'; // Avoids chars that could evaluate to a reserved word. 3 | const REF_START_CHARS_LEN = /* @__PURE__ */ REF_START_CHARS.length; 4 | const REF_CHARS = 5 | /* @__PURE__ */ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$_'; 6 | const REF_CHARS_LEN = /* @__PURE__ */ REF_CHARS.length; 7 | 8 | export default function getIdentifier(index: number): string { 9 | let mod = index % REF_START_CHARS_LEN; 10 | let ref = REF_START_CHARS[mod]; 11 | index = (index - mod) / REF_START_CHARS_LEN; 12 | while (index > 0) { 13 | mod = index % REF_CHARS_LEN; 14 | ref += REF_CHARS[mod]; 15 | index = (index - mod) / REF_CHARS_LEN; 16 | } 17 | return ref; 18 | } 19 | -------------------------------------------------------------------------------- /packages/seroval/src/core/utils/get-object-flag.ts: -------------------------------------------------------------------------------- 1 | import { SerovalObjectFlags } from '../constants'; 2 | 3 | export function getObjectFlag(obj: unknown): SerovalObjectFlags { 4 | if (Object.isFrozen(obj)) { 5 | return SerovalObjectFlags.Frozen; 6 | } 7 | if (Object.isSealed(obj)) { 8 | return SerovalObjectFlags.Sealed; 9 | } 10 | if (Object.isExtensible(obj)) { 11 | return SerovalObjectFlags.None; 12 | } 13 | return SerovalObjectFlags.NonExtensible; 14 | } 15 | -------------------------------------------------------------------------------- /packages/seroval/src/core/utils/is-valid-identifier.ts: -------------------------------------------------------------------------------- 1 | const IDENTIFIER_CHECK = /^[$A-Z_][0-9A-Z_$]*$/i; 2 | 3 | export function isValidIdentifier(name: string): boolean { 4 | const char = name[0]; 5 | return ( 6 | (char === '$' || 7 | char === '_' || 8 | (char >= 'A' && char <= 'Z') || 9 | (char >= 'a' && char <= 'z')) && 10 | IDENTIFIER_CHECK.test(name) 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /packages/seroval/src/core/utils/iterator-to-sequence.ts: -------------------------------------------------------------------------------- 1 | import { NIL } from "../constants"; 2 | 3 | export interface Sequence { 4 | v: unknown[]; 5 | t: number; 6 | d: number; 7 | } 8 | 9 | export function iteratorToSequence(source: Iterable): Sequence { 10 | const values: unknown[] = []; 11 | let throwsAt = -1; 12 | let doneAt = -1; 13 | 14 | const iterator = source[Symbol.iterator](); 15 | 16 | while (true) { 17 | try { 18 | const value = iterator.next(); 19 | values.push(value.value); 20 | if (value.done) { 21 | doneAt = values.length - 1; 22 | break; 23 | } 24 | } catch (error) { 25 | throwsAt = values.length; 26 | values.push(error); 27 | } 28 | } 29 | 30 | return { 31 | v: values, 32 | t: throwsAt, 33 | d: doneAt, 34 | }; 35 | } 36 | 37 | export function sequenceToIterator( 38 | sequence: Sequence, 39 | ): () => IterableIterator { 40 | return (): IterableIterator => { 41 | let index = 0; 42 | 43 | return { 44 | [Symbol.iterator](): IterableIterator { 45 | return this; 46 | }, 47 | next(): IteratorResult { 48 | if (index > sequence.d) { 49 | return { 50 | done: true, 51 | value: NIL, 52 | }; 53 | } 54 | const currentIndex = index++; 55 | const currentItem = sequence.v[currentIndex]; 56 | if (currentIndex === sequence.t) { 57 | throw currentItem; 58 | } 59 | return { 60 | done: currentIndex === sequence.d, 61 | value: currentItem as T, 62 | }; 63 | }, 64 | }; 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /packages/seroval/src/core/utils/promise-to-result.ts: -------------------------------------------------------------------------------- 1 | export default async function promiseToResult( 2 | current: Promise, 3 | ): Promise<[0 | 1, unknown]> { 4 | try { 5 | return [1, await current]; 6 | } catch (e) { 7 | return [0, e]; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/seroval/src/core/utils/typed-array.ts: -------------------------------------------------------------------------------- 1 | import { SerovalUnknownTypedArrayError } from '../errors'; 2 | 3 | type TypedArrayConstructor = 4 | | Int8ArrayConstructor 5 | | Int16ArrayConstructor 6 | | Int32ArrayConstructor 7 | | Uint8ArrayConstructor 8 | | Uint16ArrayConstructor 9 | | Uint32ArrayConstructor 10 | | Uint8ClampedArrayConstructor 11 | | Float32ArrayConstructor 12 | | Float64ArrayConstructor 13 | | BigInt64ArrayConstructor 14 | | BigUint64ArrayConstructor; 15 | 16 | export type TypedArrayValue = 17 | | Int8Array 18 | | Int16Array 19 | | Int32Array 20 | | Uint8Array 21 | | Uint16Array 22 | | Uint32Array 23 | | Uint8ClampedArray 24 | | Float32Array 25 | | Float64Array; 26 | 27 | export type BigIntTypedArrayValue = BigInt64Array | BigUint64Array; 28 | 29 | export function getTypedArrayConstructor(name: string): TypedArrayConstructor { 30 | switch (name) { 31 | case 'Int8Array': 32 | return Int8Array; 33 | case 'Int16Array': 34 | return Int16Array; 35 | case 'Int32Array': 36 | return Int32Array; 37 | case 'Uint8Array': 38 | return Uint8Array; 39 | case 'Uint16Array': 40 | return Uint16Array; 41 | case 'Uint32Array': 42 | return Uint32Array; 43 | case 'Uint8ClampedArray': 44 | return Uint8ClampedArray; 45 | case 'Float32Array': 46 | return Float32Array; 47 | case 'Float64Array': 48 | return Float64Array; 49 | case 'BigInt64Array': 50 | return BigInt64Array; 51 | case 'BigUint64Array': 52 | return BigUint64Array; 53 | default: 54 | throw new SerovalUnknownTypedArrayError(name); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/seroval/src/index.ts: -------------------------------------------------------------------------------- 1 | export { Feature } from './core/compat'; 2 | export { createReference } from './core/reference'; 3 | 4 | export * from './core/cross'; 5 | export * from './core/tree'; 6 | 7 | export { getCrossReferenceHeader } from './core/keys'; 8 | 9 | export * from './core/plugin'; 10 | export { default as Serializer } from './core/Serializer'; 11 | 12 | export { createStream } from './core/stream'; 13 | export type { Stream } from './core/stream'; 14 | 15 | export * from './core/errors'; 16 | export type { SerovalNode } from './core/types'; 17 | 18 | export { OpaqueReference } from './core/opaque-reference'; 19 | -------------------------------------------------------------------------------- /packages/seroval/test.js: -------------------------------------------------------------------------------- 1 | import { serialize } from './dist/esm/development/index.mjs'; 2 | 3 | function example() { 4 | return new Set([ 5 | { 6 | id: 1, 7 | first_name: 'Jimmy', 8 | last_name: 'Hansen', 9 | email: 'jhansen0@skyrock.com', 10 | gender: 'Male', 11 | ip_address: '166.6.70.130', 12 | }, 13 | { 14 | id: 1, 15 | first_name: 'Judy', 16 | last_name: 'Cook', 17 | email: 'jcook0@themeforest.net', 18 | gender: 'Female', 19 | ip_address: '171.246.40.83', 20 | }, 21 | { 22 | id: 2, 23 | first_name: 'Anne', 24 | last_name: 'Thomas', 25 | email: 'athomas1@usda.gov', 26 | gender: 'Female', 27 | ip_address: '158.159.200.150', 28 | }, 29 | ]); 30 | } 31 | 32 | for (let i = 0; i < 10000; i++) { 33 | serialize(example()); 34 | } 35 | -------------------------------------------------------------------------------- /packages/seroval/test/__snapshots__/bigint.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`bigint > crossSerialize > supports bigint 1`] = `"9007199254740991n"`; 4 | 5 | exports[`bigint > crossSerializeAsync > supports bigint 1`] = `"$R[0]=Promise.resolve(9007199254740991n)"`; 6 | 7 | exports[`bigint > crossSerializeStream > supports bigint 1`] = `"$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0})"`; 8 | 9 | exports[`bigint > crossSerializeStream > supports bigint 2`] = `"($R[3]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],9007199254740991n)"`; 10 | 11 | exports[`bigint > serialize > supports bigint 1`] = `"9007199254740991n"`; 12 | 13 | exports[`bigint > serializeAsync > supports bigint 1`] = `"Promise.resolve(9007199254740991n)"`; 14 | 15 | exports[`bigint > toCrossJSON > supports bigint 1`] = `"{"t":3,"s":"9007199254740991"}"`; 16 | 17 | exports[`bigint > toCrossJSONAsync > supports bigint 1`] = `"{"t":12,"i":0,"s":1,"f":{"t":3,"s":"9007199254740991"}}"`; 18 | 19 | exports[`bigint > toCrossJSONStream > supports bigint 1`] = `"{"t":22,"i":0,"s":1,"f":{"t":26,"i":2,"s":1}}"`; 20 | 21 | exports[`bigint > toCrossJSONStream > supports bigint 2`] = `"{"t":23,"i":1,"a":[{"t":26,"i":3,"s":2},{"t":3,"s":"9007199254740991"}]}"`; 22 | 23 | exports[`bigint > toJSON > supports bigint 1`] = `"{"t":{"t":3,"s":"9007199254740991"},"f":31,"m":[]}"`; 24 | 25 | exports[`bigint > toJSONAsync > supports bigint 1`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":3,"s":"9007199254740991"}},"f":31,"m":[]}"`; 26 | -------------------------------------------------------------------------------- /packages/seroval/test/__snapshots__/boolean.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`boolean > crossSerialize > supports boolean 1`] = `"!0"`; 4 | 5 | exports[`boolean > crossSerialize > supports boolean 2`] = `"!1"`; 6 | 7 | exports[`boolean > crossSerializeAsync > supports boolean 1`] = `"$R[0]=Promise.resolve(!0)"`; 8 | 9 | exports[`boolean > crossSerializeAsync > supports boolean 2`] = `"$R[0]=Promise.resolve(!1)"`; 10 | 11 | exports[`boolean > crossSerializeStream > supports false value 1`] = `"$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0})"`; 12 | 13 | exports[`boolean > crossSerializeStream > supports false value 2`] = `"($R[3]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],!1)"`; 14 | 15 | exports[`boolean > crossSerializeStream > supports true value 1`] = `"$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0})"`; 16 | 17 | exports[`boolean > crossSerializeStream > supports true value 2`] = `"($R[3]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],!0)"`; 18 | 19 | exports[`boolean > toCrossJSON > supports boolean 1`] = `"{"t":2,"s":2}"`; 20 | 21 | exports[`boolean > toCrossJSON > supports boolean 2`] = `"{"t":2,"s":3}"`; 22 | 23 | exports[`boolean > toCrossJSONAsync > supports boolean 1`] = `"{"t":12,"i":0,"s":1,"f":{"t":2,"s":2}}"`; 24 | 25 | exports[`boolean > toCrossJSONAsync > supports boolean 2`] = `"{"t":12,"i":0,"s":1,"f":{"t":2,"s":3}}"`; 26 | 27 | exports[`boolean > toCrossJSONStream > supports false value 1`] = `"{"t":22,"i":0,"s":1,"f":{"t":26,"i":2,"s":1}}"`; 28 | 29 | exports[`boolean > toCrossJSONStream > supports false value 2`] = `"{"t":23,"i":1,"a":[{"t":26,"i":3,"s":2},{"t":2,"s":3}]}"`; 30 | 31 | exports[`boolean > toCrossJSONStream > supports true value 1`] = `"{"t":22,"i":0,"s":1,"f":{"t":26,"i":2,"s":1}}"`; 32 | 33 | exports[`boolean > toCrossJSONStream > supports true value 2`] = `"{"t":23,"i":1,"a":[{"t":26,"i":3,"s":2},{"t":2,"s":2}]}"`; 34 | 35 | exports[`boolean > toJSON > supports boolean 1`] = `"{"t":{"t":2,"s":2},"f":31,"m":[]}"`; 36 | 37 | exports[`boolean > toJSON > supports boolean 2`] = `"{"t":{"t":2,"s":3},"f":31,"m":[]}"`; 38 | 39 | exports[`boolean > toJSONAsync > supports boolean 1`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":2,"s":2}},"f":31,"m":[]}"`; 40 | 41 | exports[`boolean > toJSONAsync > supports boolean 2`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":2,"s":3}},"f":31,"m":[]}"`; 42 | -------------------------------------------------------------------------------- /packages/seroval/test/__snapshots__/boxed-bigint.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`boxed bigint > crossSerialize > scoped > supports boxed bigint 1`] = `"($R=>$R[0]=Object(9007199254740991n))($R["example"])"`; 4 | 5 | exports[`boxed bigint > crossSerialize > supports boxed bigint 1`] = `"$R[0]=Object(9007199254740991n)"`; 6 | 7 | exports[`boxed bigint > crossSerializeAsync > scoped > supports boxed bigint 1`] = `"($R=>$R[0]=Promise.resolve($R[1]=Object(9007199254740991n)))($R["example"])"`; 8 | 9 | exports[`boxed bigint > crossSerializeAsync > supports boxed bigint 1`] = `"$R[0]=Promise.resolve($R[1]=Object(9007199254740991n))"`; 10 | 11 | exports[`boxed bigint > crossSerializeStream > scoped > supports boxed bigint 1`] = `"($R=>$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0}))($R["example"])"`; 12 | 13 | exports[`boxed bigint > crossSerializeStream > scoped > supports boxed bigint 2`] = `"($R=>($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=Object(9007199254740991n)))($R["example"])"`; 14 | 15 | exports[`boxed bigint > crossSerializeStream > supports boxed bigint 1`] = `"$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0})"`; 16 | 17 | exports[`boxed bigint > crossSerializeStream > supports boxed bigint 2`] = `"($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=Object(9007199254740991n))"`; 18 | 19 | exports[`boxed bigint > serialize > supports boxed bigint 1`] = `"Object(9007199254740991n)"`; 20 | 21 | exports[`boxed bigint > serializeAsync > supports boxed bigint 1`] = `"Promise.resolve(Object(9007199254740991n))"`; 22 | 23 | exports[`boxed bigint > toCrossJSON > supports boxed bigint 1`] = `"{"t":21,"i":0,"f":{"t":3,"s":"9007199254740991"}}"`; 24 | 25 | exports[`boxed bigint > toCrossJSONAsync > supports boxed bigint 1`] = `"{"t":12,"i":0,"s":1,"f":{"t":21,"i":1,"f":{"t":3,"s":"9007199254740991"}}}"`; 26 | 27 | exports[`boxed bigint > toCrossJSONStream > supports boxed bigint 1`] = `"{"t":22,"i":0,"s":1,"f":{"t":26,"i":2,"s":1}}"`; 28 | 29 | exports[`boxed bigint > toCrossJSONStream > supports boxed bigint 2`] = `"{"t":23,"i":1,"a":[{"t":26,"i":4,"s":2},{"t":21,"i":3,"f":{"t":3,"s":"9007199254740991"}}]}"`; 30 | 31 | exports[`boxed bigint > toJSON > supports boxed bigint 1`] = `"{"t":{"t":21,"i":0,"f":{"t":3,"s":"9007199254740991"}},"f":31,"m":[]}"`; 32 | 33 | exports[`boxed bigint > toJSONAsync > supports boxed bigint 1`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":21,"i":1,"f":{"t":3,"s":"9007199254740991"}}},"f":31,"m":[]}"`; 34 | -------------------------------------------------------------------------------- /packages/seroval/test/__snapshots__/boxed-boolean.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`boxed boolean > crossSerialize > scoped > supports boolean 1`] = `"($R=>$R[0]=Object(!0))($R["example"])"`; 4 | 5 | exports[`boxed boolean > crossSerialize > scoped > supports boolean 2`] = `"($R=>$R[0]=Object(!1))($R["example"])"`; 6 | 7 | exports[`boxed boolean > crossSerialize > supports boolean 1`] = `"$R[0]=Object(!0)"`; 8 | 9 | exports[`boxed boolean > crossSerialize > supports boolean 2`] = `"$R[0]=Object(!1)"`; 10 | 11 | exports[`boxed boolean > crossSerializeAsync > scoped > supports boolean 1`] = `"($R=>$R[0]=Object(!0))($R["example"])"`; 12 | 13 | exports[`boxed boolean > crossSerializeAsync > scoped > supports boolean 2`] = `"($R=>$R[0]=Object(!1))($R["example"])"`; 14 | 15 | exports[`boxed boolean > crossSerializeAsync > supports boolean 1`] = `"$R[0]=Object(!0)"`; 16 | 17 | exports[`boxed boolean > crossSerializeAsync > supports boolean 2`] = `"$R[0]=Object(!1)"`; 18 | 19 | exports[`boxed boolean > crossSerializeStream > scoped > supports boxed false 1`] = `"($R=>$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0}))($R["example"])"`; 20 | 21 | exports[`boxed boolean > crossSerializeStream > scoped > supports boxed false 2`] = `"($R=>($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=Object(!1)))($R["example"])"`; 22 | 23 | exports[`boxed boolean > crossSerializeStream > scoped > supports boxed true 1`] = `"($R=>$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0}))($R["example"])"`; 24 | 25 | exports[`boxed boolean > crossSerializeStream > scoped > supports boxed true 2`] = `"($R=>($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=Object(!0)))($R["example"])"`; 26 | 27 | exports[`boxed boolean > crossSerializeStream > supports boxed false 1`] = `"$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0})"`; 28 | 29 | exports[`boxed boolean > crossSerializeStream > supports boxed false 2`] = `"($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=Object(!1))"`; 30 | 31 | exports[`boxed boolean > crossSerializeStream > supports boxed true 1`] = `"$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0})"`; 32 | 33 | exports[`boxed boolean > crossSerializeStream > supports boxed true 2`] = `"($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=Object(!0))"`; 34 | 35 | exports[`boxed boolean > toCrossJSON > supports boolean 1`] = `"{"t":21,"i":0,"f":{"t":2,"s":2}}"`; 36 | 37 | exports[`boxed boolean > toCrossJSON > supports boolean 2`] = `"{"t":21,"i":0,"f":{"t":2,"s":3}}"`; 38 | 39 | exports[`boxed boolean > toCrossJSONAsync > supports boolean 1`] = `"{"t":12,"i":0,"s":1,"f":{"t":21,"i":1,"f":{"t":2,"s":2}}}"`; 40 | 41 | exports[`boxed boolean > toCrossJSONAsync > supports boolean 2`] = `"{"t":12,"i":0,"s":1,"f":{"t":21,"i":1,"f":{"t":2,"s":3}}}"`; 42 | 43 | exports[`boxed boolean > toCrossJSONStream > supports boxed false 1`] = `"{"t":22,"i":0,"s":1,"f":{"t":26,"i":2,"s":1}}"`; 44 | 45 | exports[`boxed boolean > toCrossJSONStream > supports boxed false 2`] = `"{"t":23,"i":1,"a":[{"t":26,"i":4,"s":2},{"t":21,"i":3,"f":{"t":2,"s":3}}]}"`; 46 | 47 | exports[`boxed boolean > toCrossJSONStream > supports boxed true 1`] = `"{"t":22,"i":0,"s":1,"f":{"t":26,"i":2,"s":1}}"`; 48 | 49 | exports[`boxed boolean > toCrossJSONStream > supports boxed true 2`] = `"{"t":23,"i":1,"a":[{"t":26,"i":4,"s":2},{"t":21,"i":3,"f":{"t":2,"s":2}}]}"`; 50 | 51 | exports[`boxed boolean > toJSON > supports boolean 1`] = `"{"t":{"t":21,"i":0,"f":{"t":2,"s":2}},"f":31,"m":[]}"`; 52 | 53 | exports[`boxed boolean > toJSON > supports boolean 2`] = `"{"t":{"t":21,"i":0,"f":{"t":2,"s":3}},"f":31,"m":[]}"`; 54 | 55 | exports[`boxed boolean > toJSONAsync > supports boolean 1`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":21,"i":1,"f":{"t":2,"s":2}}},"f":31,"m":[]}"`; 56 | 57 | exports[`boxed boolean > toJSONAsync > supports boolean 2`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":21,"i":1,"f":{"t":2,"s":3}}},"f":31,"m":[]}"`; 58 | -------------------------------------------------------------------------------- /packages/seroval/test/__snapshots__/data-view.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`DataView > crossSerialize > scoped > supports DataView 1`] = `"($R=>$R[0]=new DataView($R[1]=new Uint8Array([0,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0]).buffer,0,16))($R["example"])"`; 4 | 5 | exports[`DataView > crossSerialize > supports DataView 1`] = `"$R[0]=new DataView($R[1]=new Uint8Array([0,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0]).buffer,0,16)"`; 6 | 7 | exports[`DataView > crossSerializeAsync > scoped > supports DataView 1`] = `"($R=>$R[0]=Promise.resolve($R[1]=new DataView($R[2]=new Uint8Array([0,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0]).buffer,0,16)))($R["example"])"`; 8 | 9 | exports[`DataView > crossSerializeAsync > supports DataView 1`] = `"$R[0]=Promise.resolve($R[1]=new DataView($R[2]=new Uint8Array([0,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0]).buffer,0,16))"`; 10 | 11 | exports[`DataView > crossSerializeStream > scoped > supports DataView 1`] = `"($R=>$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0}))($R["example"])"`; 12 | 13 | exports[`DataView > crossSerializeStream > scoped > supports DataView 2`] = `"($R=>($R[5]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=new DataView($R[4]=new Uint8Array([0,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0]).buffer,0,16)))($R["example"])"`; 14 | 15 | exports[`DataView > crossSerializeStream > supports DataView 1`] = `"$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0})"`; 16 | 17 | exports[`DataView > crossSerializeStream > supports DataView 2`] = `"($R[5]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=new DataView($R[4]=new Uint8Array([0,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0]).buffer,0,16))"`; 18 | 19 | exports[`DataView > serialize > supports DataView 1`] = `"new DataView(new Uint8Array([0,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0]).buffer,0,16)"`; 20 | 21 | exports[`DataView > serializeAsync > supports DataView 1`] = `"Promise.resolve(new DataView(new Uint8Array([0,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0]).buffer,0,16))"`; 22 | 23 | exports[`DataView > toCrossJSON > supports DataView 1`] = `"{"t":20,"i":0,"l":16,"f":{"t":19,"i":1,"s":[0,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0]},"b":0}"`; 24 | 25 | exports[`DataView > toCrossJSONAsync > supports DataView 1`] = `"{"t":12,"i":0,"s":1,"f":{"t":20,"i":1,"l":16,"f":{"t":19,"i":2,"s":[0,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0]},"b":0}}"`; 26 | 27 | exports[`DataView > toCrossJSONStream > supports DataView 1`] = `"{"t":22,"i":0,"s":1,"f":{"t":26,"i":2,"s":1}}"`; 28 | 29 | exports[`DataView > toCrossJSONStream > supports DataView 2`] = `"{"t":23,"i":1,"a":[{"t":26,"i":5,"s":2},{"t":20,"i":3,"l":16,"f":{"t":19,"i":4,"s":[0,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0]},"b":0}]}"`; 30 | 31 | exports[`DataView > toJSON > supports DataView 1`] = `"{"t":{"t":20,"i":0,"l":16,"f":{"t":19,"i":1,"s":[0,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0]},"b":0},"f":31,"m":[]}"`; 32 | 33 | exports[`DataView > toJSONAsync > supports DataView 1`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":20,"i":1,"l":16,"f":{"t":19,"i":2,"s":[0,0,42,0,0,0,0,0,0,0,0,0,0,0,0,0]},"b":0}},"f":31,"m":[]}"`; 34 | -------------------------------------------------------------------------------- /packages/seroval/test/__snapshots__/date.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`Date > crossSerialize > scoped > supports Date 1`] = `"($R=>$R[0]=new Date("2023-03-14T11:16:24.879Z"))($R["example"])"`; 4 | 5 | exports[`Date > crossSerialize > supports Date 1`] = `"$R[0]=new Date("2023-03-14T11:16:24.879Z")"`; 6 | 7 | exports[`Date > crossSerializeAsync > scoped > supports Date 1`] = `"($R=>$R[0]=Promise.resolve($R[1]=new Date("2023-03-14T11:16:24.879Z")))($R["example"])"`; 8 | 9 | exports[`Date > crossSerializeAsync > supports Date 1`] = `"$R[0]=Promise.resolve($R[1]=new Date("2023-03-14T11:16:24.879Z"))"`; 10 | 11 | exports[`Date > crossSerializeStream > scoped > supports Date 1`] = `"($R=>$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0}))($R["example"])"`; 12 | 13 | exports[`Date > crossSerializeStream > scoped > supports Date 2`] = `"($R=>($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=new Date("2023-03-14T11:16:24.879Z")))($R["example"])"`; 14 | 15 | exports[`Date > crossSerializeStream > supports Date 1`] = `"$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0})"`; 16 | 17 | exports[`Date > crossSerializeStream > supports Date 2`] = `"($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=new Date("2023-03-14T11:16:24.879Z"))"`; 18 | 19 | exports[`Date > serialize > supports Date 1`] = `"new Date("2023-03-14T11:16:24.879Z")"`; 20 | 21 | exports[`Date > serializeAsync > supports Date 1`] = `"Promise.resolve(new Date("2023-03-14T11:16:24.879Z"))"`; 22 | 23 | exports[`Date > toCrossJSON > supports Date 1`] = `"{"t":5,"i":0,"s":"2023-03-14T11:16:24.879Z"}"`; 24 | 25 | exports[`Date > toCrossJSONAsync > supports Date 1`] = `"{"t":12,"i":0,"s":1,"f":{"t":5,"i":1,"s":"2023-03-14T11:16:24.879Z"}}"`; 26 | 27 | exports[`Date > toCrossJSONStream > supports Date 1`] = `"{"t":22,"i":0,"s":1,"f":{"t":26,"i":2,"s":1}}"`; 28 | 29 | exports[`Date > toCrossJSONStream > supports Date 2`] = `"{"t":23,"i":1,"a":[{"t":26,"i":4,"s":2},{"t":5,"i":3,"s":"2023-03-14T11:16:24.879Z"}]}"`; 30 | 31 | exports[`Date > toJSON > supports Date 1`] = `"{"t":{"t":5,"i":0,"s":"2023-03-14T11:16:24.879Z"},"f":31,"m":[]}"`; 32 | 33 | exports[`Date > toJSONAsync > supports Date 1`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":5,"i":1,"s":"2023-03-14T11:16:24.879Z"}},"f":31,"m":[]}"`; 34 | -------------------------------------------------------------------------------- /packages/seroval/test/__snapshots__/plugin.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`Plugin > crossSerialize > scoped > supports Plugin 1`] = `"($R=>$R[0]=Buffer.from("SGVsbG8sIFdvcmxkIQ==", "base64"))($R["example"])"`; 4 | 5 | exports[`Plugin > crossSerialize > supports Plugin 1`] = `"$R[0]=Buffer.from("SGVsbG8sIFdvcmxkIQ==", "base64")"`; 6 | 7 | exports[`Plugin > crossSerializeAsync > scoped > supports Plugin 1`] = `"($R=>$R[0]=Promise.resolve($R[1]=Buffer.from("SGVsbG8sIFdvcmxkIQ==", "base64")))($R["example"])"`; 8 | 9 | exports[`Plugin > crossSerializeAsync > supports Plugin 1`] = `"$R[0]=Promise.resolve($R[1]=Buffer.from("SGVsbG8sIFdvcmxkIQ==", "base64"))"`; 10 | 11 | exports[`Plugin > crossSerializeStream > scoped > supports Plugin 1`] = `"($R=>$R[0]=Buffer.from("SGVsbG8sIFdvcmxkIQ==", "base64"))($R["example"])"`; 12 | 13 | exports[`Plugin > crossSerializeStream > supports Plugin 1`] = `"$R[0]=Buffer.from("SGVsbG8sIFdvcmxkIQ==", "base64")"`; 14 | 15 | exports[`Plugin > serialize > supports Plugin 1`] = `"Buffer.from("SGVsbG8sIFdvcmxkIQ==", "base64")"`; 16 | 17 | exports[`Plugin > serializeAsync > supports Plugin 1`] = `"Promise.resolve(Buffer.from("SGVsbG8sIFdvcmxkIQ==", "base64"))"`; 18 | 19 | exports[`Plugin > toCrossJSON > supports Plugin 1`] = `"{"t":25,"i":0,"s":{"t":1,"s":"SGVsbG8sIFdvcmxkIQ=="},"c":"Buffer"}"`; 20 | 21 | exports[`Plugin > toCrossJSONAsync > supports Plugin 1`] = `"{"t":12,"i":0,"s":1,"f":{"t":25,"i":1,"s":{"t":1,"s":"SGVsbG8sIFdvcmxkIQ=="},"c":"Buffer"}}"`; 22 | 23 | exports[`Plugin > toCrossJSONStream > supports Plugin 1`] = `"{"t":25,"i":0,"s":{"t":1,"s":"SGVsbG8sIFdvcmxkIQ=="},"c":"Buffer"}"`; 24 | 25 | exports[`Plugin > toJSON > supports Plugin 1`] = `"{"t":{"t":25,"i":0,"s":{"t":1,"s":"SGVsbG8sIFdvcmxkIQ=="},"c":"Buffer"},"f":31,"m":[]}"`; 26 | 27 | exports[`Plugin > toJSONAsync > supports Plugin 1`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":25,"i":1,"s":{"t":1,"s":"SGVsbG8sIFdvcmxkIQ=="},"c":"Buffer"}},"f":31,"m":[]}"`; 28 | -------------------------------------------------------------------------------- /packages/seroval/test/__snapshots__/promise.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`Promise > compat > should use function expression instead of arrow functions 1`] = `"(function(h){return h={self:Promise.resolve().then(function(){return h})}})()"`; 4 | 5 | exports[`Promise > compat > should use function expression instead of arrow functions 2`] = `"(function(h){return h={self:Promise.reject().catch(function(){throw h})}})()"`; 6 | -------------------------------------------------------------------------------- /packages/seroval/test/__snapshots__/reference.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`Reference > crossSerialize > crossSerialize > supports Reference 1`] = `"($R=>$R[0]=__SEROVAL_REFS__.get("example"))($R["example"])"`; 4 | 5 | exports[`Reference > crossSerialize > supports Reference 1`] = `"$R[0]=__SEROVAL_REFS__.get("example")"`; 6 | 7 | exports[`Reference > crossSerializeAsync > scoped > supports Reference 1`] = `"($R=>$R[0]=Promise.resolve($R[1]=__SEROVAL_REFS__.get("example")))($R["example"])"`; 8 | 9 | exports[`Reference > crossSerializeAsync > supports Reference 1`] = `"$R[0]=Promise.resolve($R[1]=__SEROVAL_REFS__.get("example"))"`; 10 | 11 | exports[`Reference > crossSerializeStream > scoped > supports Reference 1`] = `"($R=>$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0}))($R["example"])"`; 12 | 13 | exports[`Reference > crossSerializeStream > scoped > supports Reference 2`] = `"($R=>($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=__SEROVAL_REFS__.get("example")))($R["example"])"`; 14 | 15 | exports[`Reference > crossSerializeStream > supports Reference 1`] = `"$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0})"`; 16 | 17 | exports[`Reference > crossSerializeStream > supports Reference 2`] = `"($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=__SEROVAL_REFS__.get("example"))"`; 18 | 19 | exports[`Reference > serialize > supports Reference 1`] = `"__SEROVAL_REFS__.get("example")"`; 20 | 21 | exports[`Reference > serializeAsync > supports Reference 1`] = `"Promise.resolve(__SEROVAL_REFS__.get("example"))"`; 22 | 23 | exports[`Reference > toCrossJSON > supports Reference 1`] = `"{"t":18,"i":0,"s":"example"}"`; 24 | 25 | exports[`Reference > toCrossJSONAsync > supports Reference 1`] = `"{"t":12,"i":0,"s":1,"f":{"t":18,"i":1,"s":"example"}}"`; 26 | 27 | exports[`Reference > toCrossJSONStream > supports Reference 1`] = `"{"t":22,"i":0,"s":1,"f":{"t":26,"i":2,"s":1}}"`; 28 | 29 | exports[`Reference > toCrossJSONStream > supports Reference 2`] = `"{"t":23,"i":1,"a":[{"t":26,"i":4,"s":2},{"t":18,"i":3,"s":"example"}]}"`; 30 | 31 | exports[`Reference > toJSON > supports Reference 1`] = `"{"t":{"t":18,"i":0,"s":"example"},"f":31,"m":[]}"`; 32 | 33 | exports[`Reference > toJSONAsync > supports Reference 1`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":18,"i":1,"s":"example"}},"f":31,"m":[]}"`; 34 | -------------------------------------------------------------------------------- /packages/seroval/test/__snapshots__/regexp.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`RegExp > crossSerialize > scoped > supports RegExp 1`] = `"($R=>$R[0]=/[a-z0-9]+/i)($R["example"])"`; 4 | 5 | exports[`RegExp > crossSerialize > supports RegExp 1`] = `"$R[0]=/[a-z0-9]+/i"`; 6 | 7 | exports[`RegExp > crossSerializeAsync > scoped > supports RegExp 1`] = `"($R=>$R[0]=Promise.resolve($R[1]=/[a-z0-9]+/i))($R["example"])"`; 8 | 9 | exports[`RegExp > crossSerializeAsync > supports RegExp 1`] = `"$R[0]=Promise.resolve($R[1]=/[a-z0-9]+/i)"`; 10 | 11 | exports[`RegExp > crossSerializeStream > scoped > supports RegExp 1`] = `"($R=>$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0}))($R["example"])"`; 12 | 13 | exports[`RegExp > crossSerializeStream > scoped > supports RegExp 2`] = `"($R=>($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=/[a-z0-9]+/i))($R["example"])"`; 14 | 15 | exports[`RegExp > crossSerializeStream > supports RegExp 1`] = `"$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0})"`; 16 | 17 | exports[`RegExp > crossSerializeStream > supports RegExp 2`] = `"($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=/[a-z0-9]+/i)"`; 18 | 19 | exports[`RegExp > serialize > supports RegExp 1`] = `"/[a-z0-9]+/i"`; 20 | 21 | exports[`RegExp > serializeAsync > supports RegExp 1`] = `"Promise.resolve(/[a-z0-9]+/i)"`; 22 | 23 | exports[`RegExp > toCrossJSON > supports RegExp 1`] = `"{"t":6,"i":0,"c":"[a-z0-9]+","m":"i"}"`; 24 | 25 | exports[`RegExp > toCrossJSONAsync > supports RegExp 1`] = `"{"t":12,"i":0,"s":1,"f":{"t":6,"i":1,"c":"[a-z0-9]+","m":"i"}}"`; 26 | 27 | exports[`RegExp > toCrossJSONStream > supports RegExp 1`] = `"{"t":22,"i":0,"s":1,"f":{"t":26,"i":2,"s":1}}"`; 28 | 29 | exports[`RegExp > toCrossJSONStream > supports RegExp 2`] = `"{"t":23,"i":1,"a":[{"t":26,"i":4,"s":2},{"t":6,"i":3,"c":"[a-z0-9]+","m":"i"}]}"`; 30 | 31 | exports[`RegExp > toJSON > supports RegExp 1`] = `"{"t":{"t":6,"i":0,"c":"[a-z0-9]+","m":"i"},"f":31,"m":[]}"`; 32 | 33 | exports[`RegExp > toJSONAsync > supports RegExp 1`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":6,"i":1,"c":"[a-z0-9]+","m":"i"}},"f":31,"m":[]}"`; 34 | -------------------------------------------------------------------------------- /packages/seroval/test/__snapshots__/set.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`Set > crossSerialize > scoped > supports Set 1`] = `"($R=>$R[0]=new Set([1,2,3]))($R["example"])"`; 4 | 5 | exports[`Set > crossSerialize > scoped > supports self-recursion 1`] = `"($R=>($R[0]=new Set,$R[0].add($R[0]),$R[0]))($R["example"])"`; 6 | 7 | exports[`Set > crossSerialize > supports Set 1`] = `"$R[0]=new Set([1,2,3])"`; 8 | 9 | exports[`Set > crossSerialize > supports self-recursion 1`] = `"($R[0]=new Set,$R[0].add($R[0]),$R[0])"`; 10 | 11 | exports[`Set > crossSerializeAsync > scoped > supports Set 1`] = `"($R=>$R[0]=Promise.resolve($R[1]=new Set([1,2,3])))($R["example"])"`; 12 | 13 | exports[`Set > crossSerializeAsync > scoped > supports self-recursion 1`] = `"($R=>$R[0]=new Set([$R[1]=Promise.resolve().then(()=>$R[0])]))($R["example"])"`; 14 | 15 | exports[`Set > crossSerializeAsync > supports Set 1`] = `"$R[0]=Promise.resolve($R[1]=new Set([1,2,3]))"`; 16 | 17 | exports[`Set > crossSerializeAsync > supports self-recursion 1`] = `"$R[0]=new Set([$R[1]=Promise.resolve().then(()=>$R[0])])"`; 18 | 19 | exports[`Set > crossSerializeStream > scoped > supports Set 1`] = `"($R=>$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0}))($R["example"])"`; 20 | 21 | exports[`Set > crossSerializeStream > scoped > supports Set 2`] = `"($R=>($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=new Set([1,2,3])))($R["example"])"`; 22 | 23 | exports[`Set > crossSerializeStream > scoped > supports self-recursion 1`] = `"($R=>$R[0]=new Set([$R[1]=($R[3]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[2]={p:0,s:0,f:0})]))($R["example"])"`; 24 | 25 | exports[`Set > crossSerializeStream > scoped > supports self-recursion 2`] = `"($R=>($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[2],$R[0]))($R["example"])"`; 26 | 27 | exports[`Set > crossSerializeStream > supports Set 1`] = `"$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0})"`; 28 | 29 | exports[`Set > crossSerializeStream > supports Set 2`] = `"($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=new Set([1,2,3]))"`; 30 | 31 | exports[`Set > crossSerializeStream > supports self-recursion 1`] = `"$R[0]=new Set([$R[1]=($R[3]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[2]={p:0,s:0,f:0})])"`; 32 | 33 | exports[`Set > crossSerializeStream > supports self-recursion 2`] = `"($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[2],$R[0])"`; 34 | 35 | exports[`Set > serialize > supports Set 1`] = `"new Set([1,2,3])"`; 36 | 37 | exports[`Set > serialize > supports self-recursion 1`] = `"(h=>(h=new Set,h.add(h),h))()"`; 38 | 39 | exports[`Set > serializeAsync > supports Set 1`] = `"Promise.resolve(new Set([1,2,3]))"`; 40 | 41 | exports[`Set > serializeAsync > supports self-recursion 1`] = `"(h=>h=new Set([Promise.resolve().then(()=>h)]))()"`; 42 | 43 | exports[`Set > toCrossJSON > supports Set 1`] = `"{"t":7,"i":0,"l":3,"a":[{"t":0,"s":1},{"t":0,"s":2},{"t":0,"s":3}]}"`; 44 | 45 | exports[`Set > toCrossJSON > supports self-recursion 1`] = `"{"t":7,"i":0,"l":1,"a":[{"t":4,"i":0}]}"`; 46 | 47 | exports[`Set > toCrossJSONAsync > supports Set 1`] = `"{"t":12,"i":0,"s":1,"f":{"t":7,"i":1,"l":3,"a":[{"t":0,"s":1},{"t":0,"s":2},{"t":0,"s":3}]}}"`; 48 | 49 | exports[`Set > toCrossJSONAsync > supports self-recursion 1`] = `"{"t":7,"i":0,"l":1,"a":[{"t":12,"i":1,"s":1,"f":{"t":4,"i":0}}]}"`; 50 | 51 | exports[`Set > toJSON > supports Set 1`] = `"{"t":{"t":7,"i":0,"l":3,"a":[{"t":0,"s":1},{"t":0,"s":2},{"t":0,"s":3}]},"f":31,"m":[]}"`; 52 | 53 | exports[`Set > toJSON > supports self-recursion 1`] = `"{"t":{"t":7,"i":0,"l":1,"a":[{"t":4,"i":0}]},"f":31,"m":[0]}"`; 54 | 55 | exports[`Set > toJSONAsync > supports Set 1`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":7,"i":1,"l":3,"a":[{"t":0,"s":1},{"t":0,"s":2},{"t":0,"s":3}]}},"f":31,"m":[]}"`; 56 | 57 | exports[`Set > toJSONAsync > supports self-recursion 1`] = `"{"t":{"t":7,"i":0,"l":1,"a":[{"t":12,"i":1,"s":1,"f":{"t":4,"i":0}}]},"f":31,"m":[0]}"`; 58 | -------------------------------------------------------------------------------- /packages/seroval/test/__snapshots__/sparse-array.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`sparse arrays > crossSerialize > scoped > supports sparse arrays 1`] = `"($R=>$R[0]=[,,,,,,,,,,])($R["example"])"`; 4 | 5 | exports[`sparse arrays > crossSerialize > supports sparse arrays 1`] = `"$R[0]=[,,,,,,,,,,]"`; 6 | 7 | exports[`sparse arrays > crossSerializeAsync > scoped > supports sparse arrays 1`] = `"($R=>$R[0]=Promise.resolve($R[1]=[,,,,,,,,,,]))($R["example"])"`; 8 | 9 | exports[`sparse arrays > crossSerializeAsync > supports sparse arrays 1`] = `"$R[0]=Promise.resolve($R[1]=[,,,,,,,,,,])"`; 10 | 11 | exports[`sparse arrays > crossSerializeStream > scoped > supports sparse arrays 1`] = `"($R=>$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0}))($R["example"])"`; 12 | 13 | exports[`sparse arrays > crossSerializeStream > scoped > supports sparse arrays 2`] = `"($R=>($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=[,,,,,,,,,,]))($R["example"])"`; 14 | 15 | exports[`sparse arrays > crossSerializeStream > supports sparse arrays 1`] = `"$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0})"`; 16 | 17 | exports[`sparse arrays > crossSerializeStream > supports sparse arrays 2`] = `"($R[4]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=[,,,,,,,,,,])"`; 18 | 19 | exports[`sparse arrays > serialize > supports sparse arrays 1`] = `"[,,,,,,,,,,]"`; 20 | 21 | exports[`sparse arrays > serializeAsync > supports sparse arrays 1`] = `"Promise.resolve([,,,,,,,,,,])"`; 22 | 23 | exports[`sparse arrays > toCrossJSON > supports sparse arrays 1`] = `"{"t":9,"i":0,"l":10,"a":[],"o":0}"`; 24 | 25 | exports[`sparse arrays > toCrossJSONAsync > supports sparse arrays 1`] = `"{"t":12,"i":0,"s":1,"f":{"t":9,"i":1,"l":10,"a":[],"o":0}}"`; 26 | 27 | exports[`sparse arrays > toCrossJSONStream > supports sparse arrays 1`] = `"{"t":22,"i":0,"s":1,"f":{"t":26,"i":2,"s":1}}"`; 28 | 29 | exports[`sparse arrays > toCrossJSONStream > supports sparse arrays 2`] = `"{"t":23,"i":1,"a":[{"t":26,"i":4,"s":2},{"t":9,"i":3,"l":10,"a":[],"o":0}]}"`; 30 | 31 | exports[`sparse arrays > toJSON > supports sparse arrays 1`] = `"{"t":{"t":9,"i":0,"l":10,"a":[],"o":0},"f":31,"m":[]}"`; 32 | 33 | exports[`sparse arrays > toJSONAsync > supports sparse arrays 1`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":9,"i":1,"l":10,"a":[],"o":0}},"f":31,"m":[]}"`; 34 | -------------------------------------------------------------------------------- /packages/seroval/test/__snapshots__/string.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`string > crossSerialize > supports strings 1`] = `""\\"hello\\"""`; 4 | 5 | exports[`string > crossSerialize > supports strings 2`] = `""\\x3Cscript>\\x3C/script>""`; 6 | 7 | exports[`string > crossSerializeAsync > supports strings 1`] = `"$R[0]=Promise.resolve("\\"hello\\"")"`; 8 | 9 | exports[`string > crossSerializeAsync > supports strings 2`] = `"$R[0]=Promise.resolve("\\x3Cscript>\\x3C/script>")"`; 10 | 11 | exports[`string > crossSerializeStream > supports sanitized strings 1`] = `"$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0})"`; 12 | 13 | exports[`string > crossSerializeStream > supports sanitized strings 2`] = `"($R[3]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],"\\x3Cscript>\\x3C/script>")"`; 14 | 15 | exports[`string > crossSerializeStream > supports strings 1`] = `"$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0})"`; 16 | 17 | exports[`string > crossSerializeStream > supports strings 2`] = `"($R[3]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],"\\"hello\\"")"`; 18 | 19 | exports[`string > serialize > supports strings 1`] = `""\\"hello\\"""`; 20 | 21 | exports[`string > serialize > supports strings 2`] = `""\\x3Cscript>\\x3C/script>""`; 22 | 23 | exports[`string > serializeAsync > supports strings 1`] = `"Promise.resolve("\\"hello\\"")"`; 24 | 25 | exports[`string > serializeAsync > supports strings 2`] = `"Promise.resolve("\\x3Cscript>\\x3C/script>")"`; 26 | 27 | exports[`string > toCrossJSON > supports strings 1`] = `"{"t":1,"s":"\\\\\\"hello\\\\\\""}"`; 28 | 29 | exports[`string > toCrossJSON > supports strings 2`] = `"{"t":1,"s":"\\\\x3Cscript>\\\\x3C/script>"}"`; 30 | 31 | exports[`string > toCrossJSONAsync > supports strings 1`] = `"{"t":12,"i":0,"s":1,"f":{"t":1,"s":"\\\\\\"hello\\\\\\""}}"`; 32 | 33 | exports[`string > toCrossJSONAsync > supports strings 2`] = `"{"t":12,"i":0,"s":1,"f":{"t":1,"s":"\\\\x3Cscript>\\\\x3C/script>"}}"`; 34 | 35 | exports[`string > toCrossJSONStream > supports sanitized strings 1`] = `"{"t":22,"i":0,"s":1,"f":{"t":26,"i":2,"s":1}}"`; 36 | 37 | exports[`string > toCrossJSONStream > supports sanitized strings 2`] = `"{"t":23,"i":1,"a":[{"t":26,"i":3,"s":2},{"t":1,"s":"\\\\x3Cscript>\\\\x3C/script>"}]}"`; 38 | 39 | exports[`string > toCrossJSONStream > supports strings 1`] = `"{"t":22,"i":0,"s":1,"f":{"t":26,"i":2,"s":1}}"`; 40 | 41 | exports[`string > toCrossJSONStream > supports strings 2`] = `"{"t":23,"i":1,"a":[{"t":26,"i":3,"s":2},{"t":1,"s":"\\\\\\"hello\\\\\\""}]}"`; 42 | 43 | exports[`string > toJSON > supports strings 1`] = `"{"t":{"t":1,"s":"\\\\\\"hello\\\\\\""},"f":31,"m":[]}"`; 44 | 45 | exports[`string > toJSON > supports strings 2`] = `"{"t":{"t":1,"s":"\\\\x3Cscript>\\\\x3C/script>"},"f":31,"m":[]}"`; 46 | 47 | exports[`string > toJSONAsync > supports strings 1`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":1,"s":"\\\\\\"hello\\\\\\""}},"f":31,"m":[]}"`; 48 | 49 | exports[`string > toJSONAsync > supports strings 2`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":1,"s":"\\\\x3Cscript>\\\\x3C/script>"}},"f":31,"m":[]}"`; 50 | -------------------------------------------------------------------------------- /packages/seroval/test/__snapshots__/typed-array.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`typed arrays > crossSerialize > scoped > supports typed arrays 1`] = `"($R=>$R[0]=new Uint32Array($R[1]=new Uint8Array([255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]).buffer,0,4))($R["example"])"`; 4 | 5 | exports[`typed arrays > crossSerialize > supports typed arrays 1`] = `"$R[0]=new Uint32Array($R[1]=new Uint8Array([255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]).buffer,0,4)"`; 6 | 7 | exports[`typed arrays > crossSerializeAsync > scoped > supports typed arrays 1`] = `"($R=>$R[0]=Promise.resolve($R[1]=new Uint32Array($R[2]=new Uint8Array([255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]).buffer,0,4)))($R["example"])"`; 8 | 9 | exports[`typed arrays > crossSerializeAsync > supports typed arrays 1`] = `"$R[0]=Promise.resolve($R[1]=new Uint32Array($R[2]=new Uint8Array([255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]).buffer,0,4))"`; 10 | 11 | exports[`typed arrays > crossSerializeStream > scoped > supports typed arrays 1`] = `"($R=>$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0}))($R["example"])"`; 12 | 13 | exports[`typed arrays > crossSerializeStream > scoped > supports typed arrays 2`] = `"($R=>($R[5]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=new Uint32Array($R[4]=new Uint8Array([255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]).buffer,0,4)))($R["example"])"`; 14 | 15 | exports[`typed arrays > crossSerializeStream > supports typed arrays 1`] = `"$R[0]=($R[2]=r=>(r.p=new Promise((s,f)=>{r.s=s,r.f=f})))($R[1]={p:0,s:0,f:0})"`; 16 | 17 | exports[`typed arrays > crossSerializeStream > supports typed arrays 2`] = `"($R[5]=(r,d)=>{r.s(d),r.p.s=1,r.p.v=d})($R[1],$R[3]=new Uint32Array($R[4]=new Uint8Array([255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]).buffer,0,4))"`; 18 | 19 | exports[`typed arrays > serialize > supports typed arrays 1`] = `"new Uint32Array(new Uint8Array([255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]).buffer,0,4)"`; 20 | 21 | exports[`typed arrays > serializeAsync > supports typed arrays 1`] = `"Promise.resolve(new Uint32Array(new Uint8Array([255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]).buffer,0,4))"`; 22 | 23 | exports[`typed arrays > toCrossJSON > supports typed arrays 1`] = `"{"t":15,"i":0,"l":4,"c":"Uint32Array","f":{"t":19,"i":1,"s":[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]},"b":0}"`; 24 | 25 | exports[`typed arrays > toCrossJSONAsync > supports typed arrays 1`] = `"{"t":12,"i":0,"s":1,"f":{"t":15,"i":1,"l":4,"c":"Uint32Array","f":{"t":19,"i":2,"s":[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]},"b":0}}"`; 26 | 27 | exports[`typed arrays > toCrossJSONStream > supports typed arrays 1`] = `"{"t":22,"i":0,"s":1,"f":{"t":26,"i":2,"s":1}}"`; 28 | 29 | exports[`typed arrays > toCrossJSONStream > supports typed arrays 2`] = `"{"t":23,"i":1,"a":[{"t":26,"i":5,"s":2},{"t":15,"i":3,"l":4,"c":"Uint32Array","f":{"t":19,"i":4,"s":[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]},"b":0}]}"`; 30 | 31 | exports[`typed arrays > toJSON > supports typed arrays 1`] = `"{"t":{"t":15,"i":0,"l":4,"c":"Uint32Array","f":{"t":19,"i":1,"s":[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]},"b":0},"f":31,"m":[]}"`; 32 | 33 | exports[`typed arrays > toJSONAsync > supports typed arrays 1`] = `"{"t":{"t":12,"i":0,"s":1,"f":{"t":15,"i":1,"l":4,"c":"Uint32Array","f":{"t":19,"i":2,"s":[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]},"b":0}},"f":31,"m":[]}"`; 34 | -------------------------------------------------------------------------------- /packages/seroval/test/bigint.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { 3 | crossSerialize, 4 | crossSerializeAsync, 5 | crossSerializeStream, 6 | deserialize, 7 | fromCrossJSON, 8 | fromJSON, 9 | serialize, 10 | serializeAsync, 11 | toCrossJSON, 12 | toCrossJSONAsync, 13 | toCrossJSONStream, 14 | toJSON, 15 | toJSONAsync, 16 | } from '../src'; 17 | 18 | const EXAMPLE = 9007199254740991n; 19 | 20 | describe('bigint', () => { 21 | describe('serialize', () => { 22 | it('supports bigint', () => { 23 | expect(serialize(EXAMPLE)).toMatchSnapshot(); 24 | expect(deserialize(serialize(EXAMPLE))).toBe(EXAMPLE); 25 | }); 26 | }); 27 | describe('serializeAsync', () => { 28 | it('supports bigint', async () => { 29 | expect(await serializeAsync(Promise.resolve(EXAMPLE))).toMatchSnapshot(); 30 | }); 31 | }); 32 | describe('toJSON', () => { 33 | it('supports bigint', () => { 34 | expect(JSON.stringify(toJSON(EXAMPLE))).toMatchSnapshot(); 35 | expect(fromJSON(toJSON(EXAMPLE))).toBe(EXAMPLE); 36 | }); 37 | }); 38 | describe('toJSONAsync', () => { 39 | it('supports bigint', async () => { 40 | expect( 41 | JSON.stringify(await toJSONAsync(Promise.resolve(EXAMPLE))), 42 | ).toMatchSnapshot(); 43 | }); 44 | }); 45 | describe('crossSerialize', () => { 46 | it('supports bigint', () => { 47 | expect(crossSerialize(EXAMPLE)).toMatchSnapshot(); 48 | }); 49 | }); 50 | describe('crossSerializeAsync', () => { 51 | it('supports bigint', async () => { 52 | expect( 53 | await crossSerializeAsync(Promise.resolve(EXAMPLE)), 54 | ).toMatchSnapshot(); 55 | }); 56 | }); 57 | describe('crossSerializeStream', () => { 58 | it('supports bigint', async () => 59 | new Promise((resolve, reject) => { 60 | crossSerializeStream(Promise.resolve(EXAMPLE), { 61 | onSerialize(data) { 62 | expect(data).toMatchSnapshot(); 63 | }, 64 | onDone() { 65 | resolve(); 66 | }, 67 | onError(error) { 68 | reject(error); 69 | }, 70 | }); 71 | })); 72 | }); 73 | describe('toCrossJSON', () => { 74 | it('supports bigint', () => { 75 | expect(JSON.stringify(toCrossJSON(EXAMPLE))).toMatchSnapshot(); 76 | expect(fromCrossJSON(toCrossJSON(EXAMPLE), { refs: new Map() })).toBe( 77 | EXAMPLE, 78 | ); 79 | }); 80 | }); 81 | describe('toCrossJSONAsync', () => { 82 | it('supports bigint', async () => { 83 | expect( 84 | JSON.stringify(await toCrossJSONAsync(Promise.resolve(EXAMPLE))), 85 | ).toMatchSnapshot(); 86 | }); 87 | }); 88 | describe('toCrossJSONStream', () => { 89 | it('supports bigint', async () => 90 | new Promise((resolve, reject) => { 91 | toCrossJSONStream(Promise.resolve(EXAMPLE), { 92 | onParse(data) { 93 | expect(JSON.stringify(data)).toMatchSnapshot(); 94 | }, 95 | onDone() { 96 | resolve(); 97 | }, 98 | onError(error) { 99 | reject(error); 100 | }, 101 | }); 102 | })); 103 | }); 104 | }); 105 | -------------------------------------------------------------------------------- /packages/seroval/test/promise.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { Feature, serializeAsync } from '../src'; 3 | 4 | describe('Promise', () => { 5 | describe('compat', () => { 6 | it('should use function expression instead of arrow functions', async () => { 7 | const example: Record> = {}; 8 | example.self = Promise.resolve(example); 9 | expect( 10 | await serializeAsync(example, { 11 | disabledFeatures: Feature.ArrowFunction, 12 | }), 13 | ).toMatchSnapshot(); 14 | }); 15 | it('should use function expression instead of arrow functions', async () => { 16 | const example: Record> = {}; 17 | example.self = Promise.reject(example); 18 | expect( 19 | await serializeAsync(example, { 20 | disabledFeatures: Feature.ArrowFunction, 21 | }), 22 | ).toMatchSnapshot(); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /packages/seroval/theory.js: -------------------------------------------------------------------------------- 1 | import { toCrossJSONStream, fromCrossJSON, crossSerializeStream } from "./dist/esm/development/index.mjs"; 2 | 3 | const delay = (delay, value) => new Promise((resolve) => { 4 | setTimeout(() => resolve(value), delay) 5 | }) 6 | 7 | const bla = { 8 | a: 'Hello', 9 | b: delay(1000, "World"), 10 | c: delay(2000, "Bla"), 11 | d: delay(3000, { 12 | e: 2500 13 | }) 14 | } 15 | 16 | const refs = new Map(); 17 | 18 | toCrossJSONStream(bla, { 19 | onParse(node, initial) { 20 | console.log(fromCrossJSON(node, { refs })) 21 | } 22 | }); 23 | 24 | // crossSerializeStream(bla, { 25 | // onSerialize(node) { 26 | // console.log(node); 27 | // } 28 | // }) -------------------------------------------------------------------------------- /packages/seroval/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "exclude": ["node_modules"], 3 | "include": ["src", "types", "test"], 4 | "compilerOptions": { 5 | "module": "ESNext", 6 | "lib": ["ESNext", "DOM"], 7 | "importHelpers": true, 8 | "declaration": true, 9 | "sourceMap": true, 10 | "rootDir": "./src", 11 | "strict": true, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "moduleResolution": "bundler", 17 | "jsx": "react", 18 | "esModuleInterop": true, 19 | "target": "ESNext", 20 | "useDefineForClassFields": false, 21 | "declarationMap": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - packages/**/* 3 | - benchmark 4 | onlyBuiltDependencies: 5 | - esbuild 6 | --------------------------------------------------------------------------------