From 65fdf8dca3c366f728b871c3b42c801a6e013f1b Mon Sep 17 00:00:00 2001 From: saltyaom Date: Sat, 15 Feb 2025 22:37:27 +0700 Subject: [PATCH 01/14] :tada: feat: sanitize --- CHANGELOG.md | 7 ++ README.md | 67 +++++++++++++- benchmarks/large-manual.ts | 175 +++++++++++++++++++++++++++++++++++ benchmarks/medium-manual.ts | 79 ++++++++++++++++ benchmarks/medium.ts | 6 +- benchmarks/quote.ts | 15 +++ benchmarks/small-manual.ts | 32 +++++++ benchmarks/small.ts | 2 +- benchmarks/utils.ts | 16 ++-- bun.lock | 56 +++++------ example/index.ts | 6 +- package.json | 4 +- src/index.ts | 88 ++++++++++++++++-- test/sanitize-auto.test.ts | 62 +++++++++++++ test/sanitize-manual.test.ts | 62 +++++++++++++ test/sanitize-throw.test.ts | 92 ++++++++++++++++++ 16 files changed, 712 insertions(+), 57 deletions(-) create mode 100644 benchmarks/large-manual.ts create mode 100644 benchmarks/medium-manual.ts create mode 100644 benchmarks/quote.ts create mode 100644 benchmarks/small-manual.ts create mode 100644 test/sanitize-auto.test.ts create mode 100644 test/sanitize-manual.test.ts create mode 100644 test/sanitize-throw.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f2f1da..dafbb58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 0.1.0 - 5 Feb 2025 +Feature: +- replace `arrayItems.join('",\"')` in favour of inline `joinStringArray` to improve performance +- add `sanitize` option for handling unsafe character + - new behavior is `sanitize`, previously is equivalent to `manual` +- support inline a literal value + # 0.0.2 - 4 Feb 2025 Feature: - support integer, bigint, date, datetime diff --git a/README.md b/README.md index e2f074a..6daede5 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Accelerate JSON stringification by providing OpenAPI/TypeBox model. By providing model ahead of time, the library will generate a function that will serialize the object into a JSON string. ``` -$ npx tsx benchmarks/medium.ts +$ npx tsx benchmarks/medium-manual.ts clk: ~3.02 GHz cpu: Apple M1 Max @@ -13,8 +13,8 @@ runtime: node 22.6.0 (arm64-darwin) summary JSON Accelerator - 1.8x faster than JSON Stingify - 2.15x faster than Fast Json Stringify + 2.12x faster than JSON Stingify + 2.66x faster than Fast Json Stringify ``` ## Installation @@ -52,7 +52,7 @@ console.log(encode(value)) // {"id":0,"name":"saltyaom"} ## Caveat -This library **WILL NOT** check for the validity of the schema, it is expected that the schema is **always** correct. +This library **WILL NOT** check for the type validity of the schema, it is expected that the schema is **always** correct. This can be achieved by checking the input validity with TypeBox before passing it to the accelerator. @@ -78,3 +78,62 @@ if (guard.Check(value)) encode(value) ``` If the shape is incorrect, the output will try to corece the value into an expected model but if failed the error will be thrown. + +## Options + +This section is used to configure the behavior of the encoder. + +`Options` can be passed as a second argument to `createAccelerator`. + +```ts +createAccelerator(shape, { + unsafe: 'throw' +}) +``` + +## Unsafe + +If unsafe character is found, how should the encoder handle it? + +This value only applied to string field. + +- `'auto'`: Sanitize the string and continue encoding +- `'manual'`: Ignore the unsafe character, this implied that end user should specify fields that should be sanitized manually +- `'throw'`: Throw an error + +The default behavior is `auto`. + +### format sanitize + +Since this library is designed for a controlled environment (eg. Restful API), most fields are controlled by end user which doesn't include unsafe characters for JSON encoding. + +We can improve performance by specifying a `sanitize: 'manual'` and provide a field that should be sanitized manually. + +We can add `sanitize: true` to a schema which is an uncontrolled field submit by user that might contains unsafe characters. + +When a field is marked as `sanitize`, the encoder will sanitize the string and continue encoding regardless of the `unsafe` configuration. + +```ts +import { Type as t } from '@sinclair/typebox' +import { createAccelerator } from 'json-accelerator' + +const shape = t.Object({ + name: t.String(), + id: t.Number(), + unknown: t.String({ sanitize: true }) +}) + +const value = { + id: 0, + name: 'saltyaom', + unknown: `hello\nworld` +} satisfies typeof shape.static + +const encode = createAccelerator(shape, { + sanitize: 'manual' +}) + +console.log(encode(value)) // {"id":0,"name":"saltyaom","unknown":"hello\\nworld"} +``` + +This allows us to speed up a hardcode diff --git a/benchmarks/large-manual.ts b/benchmarks/large-manual.ts new file mode 100644 index 0000000..186f04c --- /dev/null +++ b/benchmarks/large-manual.ts @@ -0,0 +1,175 @@ +/** + * *-manual.ts is where end user specifiy which fields should be sanitized manually + **/ + +import { t } from 'elysia' +import { benchmark } from './utils' + +benchmark( + t.Array( + t.Object({ + id: t.Number(), + name: t.String(), + bio: t.String({ + sanitize: true + }), + user: t.Object({ + name: t.String(), + password: t.String(), + email: t.Optional(t.String({ format: 'email' })), + age: t.Optional(t.Number()), + avatar: t.Optional(t.String({ format: 'uri' })), + cover: t.Optional(t.String({ format: 'uri' })) + }), + playing: t.Optional(t.String()), + wishlist: t.Optional(t.Array(t.Number())), + games: t.Array( + t.Object({ + id: t.Number(), + name: t.String(), + hoursPlay: t.Optional(t.Number({ default: 0 })), + tags: t.Array( + t.Object({ + name: t.String(), + count: t.Number() + }) + ) + }) + ), + metadata: t.Intersect([ + t.Object({ + alias: t.String() + }), + t.Object({ + country: t.Nullable(t.String()), + region: t.Optional(t.String()) + }) + ]), + social: t.Optional( + t.Object({ + facebook: t.Optional(t.String()), + twitter: t.Optional(t.String()), + youtube: t.Optional(t.String()) + }) + ) + }) + ), + [ + { + id: 1, + name: 'SaltyAom', + bio: 'I like train\n', + user: { + name: 'SaltyAom', + password: '123456', + avatar: 'https://avatars.githubusercontent.com/u/35027979?v=4', + cover: 'https://saltyaom.com/cosplay/pekomama.webp' + }, + playing: 'Strinova', + wishlist: [4_154_456, 2_345_345], + games: [ + { + id: 4_154_456, + name: 'MiSide', + hoursPlay: 17, + tags: [ + { name: 'Psychological Horror', count: 236_432 }, + { name: 'Cute', count: 495_439 }, + { name: 'Dating Sim', count: 395_532 } + ] + }, + { + id: 4_356_345, + name: 'Strinova', + hoursPlay: 365, + tags: [ + { name: 'Free to Play', count: 205_593 }, + { name: 'Anime', count: 504_304 }, + { name: 'Third-Person Shooter', count: 395_532 } + ] + }, + { + id: 2_345_345, + name: "Tom Clancy's Rainbow Six Siege", + hoursPlay: 287, + tags: [ + { name: 'FPS', count: 855_324 }, + { name: 'Multiplayer', count: 456_567 }, + { name: 'Tactical', count: 544_467 } + ] + } + ], + metadata: { + alias: 'SaltyAom', + country: 'Thailand', + region: 'Asia' + }, + social: { + twitter: 'SaltyAom' + } + }, + { + id: 2, + name: 'VLost', + bio: 'ไม่พี่คืองี้\n', + user: { + name: 'nattapon_kub', + password: '123456' + }, + games: [ + { + id: 4_154_456, + name: 'MiSide', + hoursPlay: 17, + tags: [ + { name: 'Psychological Horror', count: 236_432 }, + { name: 'Cute', count: 495_439 }, + { name: 'Dating Sim', count: 395_532 } + ] + }, + { + id: 4_356_345, + name: 'Strinova', + hoursPlay: 365, + tags: [ + { name: 'Free to Play', count: 205_593 }, + { name: 'Anime', count: 504_304 }, + { name: 'Third-Person Shooter', count: 395_532 } + ] + } + ], + metadata: { + alias: 'vlost', + country: 'Thailand' + } + }, + { + id: 2, + name: 'eika', + bio: 'こんにちわ!', + user: { + name: 'ei_ka', + password: '123456' + }, + games: [ + { + id: 4_356_345, + name: 'Strinova', + hoursPlay: 365, + tags: [ + { name: 'Free to Play', count: 205_593 }, + { name: 'Anime', count: 504_304 }, + { name: 'Third-Person Shooter', count: 395_532 } + ] + } + ], + metadata: { + alias: 'eika', + country: 'Japan' + } + } + ], + { + sanitize: 'manual' + } +) diff --git a/benchmarks/medium-manual.ts b/benchmarks/medium-manual.ts new file mode 100644 index 0000000..8c7fa96 --- /dev/null +++ b/benchmarks/medium-manual.ts @@ -0,0 +1,79 @@ +/** + * *-manual.ts is where end user specifiy which fields should be sanitized manually + **/ + +import { t } from 'elysia' +import { benchmark } from './utils' + +benchmark( + t.Object({ + id: t.Number(), + name: t.Literal('SaltyAom'), + bio: t.String({ + sanitize: true + }), + user: t.Object({ + name: t.String(), + password: t.String() + }), + playing: t.Optional(t.String()), + games: t.Array( + t.Object({ + name: t.String(), + hoursPlay: t.Number({ default: 0 }), + tags: t.Array(t.String()) + }) + ), + metadata: t.Intersect([ + t.Object({ + alias: t.String() + }), + t.Object({ + country: t.Nullable(t.String()) + }) + ]), + social: t.Optional( + t.Object({ + facebook: t.Optional(t.String()), + twitter: t.Optional(t.String()), + youtube: t.Optional(t.String()) + }) + ) + }), + { + id: 1, + name: 'SaltyAom', + bio: 'I like train\n', + user: { + name: 'SaltyAom', + password: '123456' + }, + games: [ + { + name: 'MiSide', + hoursPlay: 17, + tags: ['Psychological Horror', 'Cute', 'Dating Sim'] + }, + { + name: 'Strinova', + hoursPlay: 365, + tags: ['Free to Play', 'Anime', 'Third-Person Shooter'] + }, + { + name: "Tom Clancy's Rainbow Six Siege", + hoursPlay: 287, + tags: ['FPS', 'Multiplayer', 'Tactical'] + } + ], + metadata: { + alias: 'SaltyAom', + country: 'Thailand' + }, + social: { + twitter: 'SaltyAom' + } + }, + { + sanitize: 'manual' + } +) diff --git a/benchmarks/medium.ts b/benchmarks/medium.ts index 60c5a3e..5a88112 100644 --- a/benchmarks/medium.ts +++ b/benchmarks/medium.ts @@ -5,7 +5,9 @@ benchmark( t.Object({ id: t.Number(), name: t.Literal('SaltyAom'), - bio: t.String(), + bio: t.String({ + sanitize: true + }), user: t.Object({ name: t.String(), password: t.String() @@ -37,7 +39,7 @@ benchmark( { id: 1, name: 'SaltyAom', - bio: 'I like train', + bio: 'I like train\nhere', user: { name: 'SaltyAom', password: '123456' diff --git a/benchmarks/quote.ts b/benchmarks/quote.ts new file mode 100644 index 0000000..11cb2a9 --- /dev/null +++ b/benchmarks/quote.ts @@ -0,0 +1,15 @@ +import { t } from 'elysia' +import { benchmark } from './utils' + +benchmark( + t.Object({ + id: t.String({ + format: 'input' + }), + name: t.String() + }), + { + id: '\n', + name: 'SaltyAom' + } +) diff --git a/benchmarks/small-manual.ts b/benchmarks/small-manual.ts new file mode 100644 index 0000000..0a7f6b9 --- /dev/null +++ b/benchmarks/small-manual.ts @@ -0,0 +1,32 @@ +/** + * *-manual.ts is where end user specifiy which fields should be sanitized manually + **/ + +import { t } from 'elysia' +import { benchmark } from './utils' + +benchmark( + t.Object({ + id: t.Number(), + name: t.String(), + bio: t.String({ + sanitize: true + }), + metadata: t.Object({ + alias: t.String(), + country: t.String() + }) + }), + { + id: 1, + name: 'SaltyAom', + bio: 'I like train\n', + metadata: { + alias: 'SaltyAom', + country: 'Thailand' + } + }, + { + sanitize: 'manual' + } +) diff --git a/benchmarks/small.ts b/benchmarks/small.ts index 368ea7a..95d60c9 100644 --- a/benchmarks/small.ts +++ b/benchmarks/small.ts @@ -4,7 +4,7 @@ import { benchmark } from './utils' benchmark( t.Object({ id: t.Number(), - name: t.Literal('SaltyAom'), + name: t.String(), bio: t.String(), metadata: t.Object({ alias: t.String(), diff --git a/benchmarks/utils.ts b/benchmarks/utils.ts index c89e292..7f553cd 100644 --- a/benchmarks/utils.ts +++ b/benchmarks/utils.ts @@ -6,19 +6,21 @@ import type { TAnySchema } from '@sinclair/typebox' export const benchmark = ( model: T, - value: T['static'] + value: T['static'], + options?: Parameters[1] ) => { const fastJsonStringify = fastJson(model) - const encode = createAccelerator(model) - - if (encode(value) !== JSON.stringify(value)) { - console.log(encode(value)) - throw new Error('Invalid result') - } + const encode = createAccelerator(model, options) if (process.env.DEBUG) { console.log(encode.toString()) + } + + if (encode(value) !== JSON.stringify(value)) { console.log(encode(value)) + console.log('---') + console.log(encode.toString()) + throw new Error('Invalid result') } compact(() => { diff --git a/bun.lock b/bun.lock index fb40190..fd7a9b5 100644 --- a/bun.lock +++ b/bun.lock @@ -2,7 +2,7 @@ "lockfileVersion": 1, "workspaces": { "": { - "name": "@elysiajs/bearer", + "name": "json-accelerator", "devDependencies": { "@types/bun": "1.2.2", "elysia": "^1.2.11", @@ -14,7 +14,7 @@ "typescript": "^5.5.3", }, "peerDependencies": { - "@sinclair/typebox": ">= 0.34.0", + "@sinclair/typebox": "^0.34.15", }, "optionalPeers": [ "@sinclair/typebox", @@ -110,51 +110,51 @@ "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.34.0", "", { "os": "android", "cpu": "arm" }, "sha512-Eeao7ewDq79jVEsrtWIj5RNqB8p2knlm9fhR6uJ2gqP7UfbLrTrxevudVrEPDM7Wkpn/HpRC2QfazH7MXLz3vQ=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.34.7", "", { "os": "android", "cpu": "arm" }, "sha512-l6CtzHYo8D2TQ3J7qJNpp3Q1Iye56ssIAtqbM2H8axxCEEwvN7o8Ze9PuIapbxFL3OHrJU2JBX6FIIVnP/rYyw=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.34.0", "", { "os": "android", "cpu": "arm64" }, "sha512-yVh0Kf1f0Fq4tWNf6mWcbQBCLDpDrDEl88lzPgKhrgTcDrTtlmun92ywEF9dCjmYO3EFiSuJeeo9cYRxl2FswA=="], + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.34.7", "", { "os": "android", "cpu": "arm64" }, "sha512-KvyJpFUueUnSp53zhAa293QBYqwm94TgYTIfXyOTtidhm5V0LbLCJQRGkQClYiX3FXDQGSvPxOTD/6rPStMMDg=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.34.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gCs0ErAZ9s0Osejpc3qahTsqIPUDjSKIyxK/0BGKvL+Tn0n3Kwvj8BrCv7Y5sR1Ypz1K2qz9Ny0VvkVyoXBVUQ=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.34.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-jq87CjmgL9YIKvs8ybtIC98s/M3HdbqXhllcy9EdLV0yMg1DpxES2gr65nNy7ObNo/vZ/MrOTxt0bE5LinL6mA=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.34.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-aIB5Anc8hngk15t3GUkiO4pv42ykXHfmpXGS+CzM9CTyiWyT8HIS5ygRAy7KcFb/wiw4Br+vh1byqcHRTfq2tQ=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.34.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rSI/m8OxBjsdnMMg0WEetu/w+LhLAcCDEiL66lmMX4R3oaml3eXz3Dxfvrxs1FbzPbJMaItQiksyMfv1hoIxnA=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.34.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-kpdsUdMlVJMRMaOf/tIvxk8TQdzHhY47imwmASOuMajg/GXpw8GKNd8LNwIHE5Yd1onehNpcUB9jHY6wgw9nHQ=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.34.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-oIoJRy3ZrdsXpFuWDtzsOOa/E/RbRWXVokpVrNnkS7npz8GEG++E1gYbzhYxhxHbO2om1T26BZjVmdIoyN2WtA=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.34.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-D0RDyHygOBCQiqookcPevrvgEarN0CttBecG4chOeIYCNtlKHmf5oi5kAVpXV7qs0Xh/WO2RnxeicZPtT50V0g=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.34.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-X++QSLm4NZfZ3VXGVwyHdRf58IBbCu9ammgJxuWZYLX0du6kZvdNqPwrjvDfwmi6wFdvfZ/s6K7ia0E5kI7m8Q=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.34.0", "", { "os": "linux", "cpu": "arm" }, "sha512-mCIw8j5LPDXmCOW8mfMZwT6F/Kza03EnSr4wGYEswrEfjTfVsFOxvgYfuRMxTuUF/XmRb9WSMD5GhCWDe2iNrg=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.34.7", "", { "os": "linux", "cpu": "arm" }, "sha512-Z0TzhrsNqukTz3ISzrvyshQpFnFRfLunYiXxlCRvcrb3nvC5rVKI+ZXPFG/Aa4jhQa1gHgH3A0exHaRRN4VmdQ=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.34.0", "", { "os": "linux", "cpu": "arm" }, "sha512-AwwldAu4aCJPob7zmjuDUMvvuatgs8B/QiVB0KwkUarAcPB3W+ToOT+18TQwY4z09Al7G0BvCcmLRop5zBLTag=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.34.7", "", { "os": "linux", "cpu": "arm" }, "sha512-nkznpyXekFAbvFBKBy4nNppSgneB1wwG1yx/hujN3wRnhnkrYVugMTCBXED4+Ni6thoWfQuHNYbFjgGH0MBXtw=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.34.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-e7kDUGVP+xw05pV65ZKb0zulRploU3gTu6qH1qL58PrULDGxULIS0OSDQJLH7WiFnpd3ZKUU4VM3u/Z7Zw+e7Q=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.34.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-KCjlUkcKs6PjOcxolqrXglBDcfCuUCTVlX5BgzgoJHw+1rWH1MCkETLkLe5iLLS9dP5gKC7mp3y6x8c1oGBUtA=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.34.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-SXYJw3zpwHgaBqTXeAZ31qfW/v50wq4HhNVvKFhRr5MnptRX2Af4KebLWR1wpxGJtLgfS2hEPuALRIY3LPAAcA=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.34.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-uFLJFz6+utmpbR313TTx+NpPuAXbPz4BhTQzgaP0tozlLnGnQ6rCo6tLwaSa6b7l6gRErjLicXQ1iPiXzYotjw=="], - "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.34.0", "", { "os": "linux", "cpu": "none" }, "sha512-e5XiCinINCI4RdyU3sFyBH4zzz7LiQRvHqDtRe9Dt8o/8hTBaYpdPimayF00eY2qy5j4PaaWK0azRgUench6WQ=="], + "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.34.7", "", { "os": "linux", "cpu": "none" }, "sha512-ws8pc68UcJJqCpneDFepnwlsMUFoWvPbWXT/XUrJ7rWUL9vLoIN3GAasgG+nCvq8xrE3pIrd+qLX/jotcLy0Qw=="], - "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.34.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3SWN3e0bAsm9ToprLFBSro8nJe6YN+5xmB11N4FfNf92wvLye/+Rh5JGQtKOpwLKt6e61R1RBc9g+luLJsc23A=="], + "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.34.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-vrDk9JDa/BFkxcS2PbWpr0C/LiiSLxFbNOBgfbW6P8TBe9PPHx9Wqbvx2xgNi1TOAyQHQJ7RZFqBiEohm79r0w=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.34.0", "", { "os": "linux", "cpu": "none" }, "sha512-B1Oqt3GLh7qmhvfnc2WQla4NuHlcxAD5LyueUi5WtMc76ZWY+6qDtQYqnxARx9r+7mDGfamD+8kTJO0pKUJeJA=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.34.7", "", { "os": "linux", "cpu": "none" }, "sha512-rB+ejFyjtmSo+g/a4eovDD1lHWHVqizN8P0Hm0RElkINpS0XOdpaXloqM4FBkF9ZWEzg6bezymbpLmeMldfLTw=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.34.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-UfUCo0h/uj48Jq2lnhX0AOhZPSTAq3Eostas+XZ+GGk22pI+Op1Y6cxQ1JkUuKYu2iU+mXj1QjPrZm9nNWV9rg=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.34.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-nNXNjo4As6dNqRn7OrsnHzwTgtypfRA3u3AKr0B3sOOo+HkedIbn8ZtFnB+4XyKJojIfqDKmbIzO1QydQ8c+Pw=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.34.0", "", { "os": "linux", "cpu": "x64" }, "sha512-chZLTUIPbgcpm+Z7ALmomXW8Zh+wE2icrG+K6nt/HenPLmtwCajhQC5flNSk1Xy5EDMt/QAOz2MhzfOfJOLSiA=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.34.7", "", { "os": "linux", "cpu": "x64" }, "sha512-9kPVf9ahnpOMSGlCxXGv980wXD0zRR3wyk8+33/MXQIpQEOpaNe7dEHm5LMfyRZRNt9lMEQuH0jUKj15MkM7QA=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.34.0", "", { "os": "linux", "cpu": "x64" }, "sha512-jo0UolK70O28BifvEsFD/8r25shFezl0aUk2t0VJzREWHkq19e+pcLu4kX5HiVXNz5qqkD+aAq04Ct8rkxgbyQ=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.34.7", "", { "os": "linux", "cpu": "x64" }, "sha512-7wJPXRWTTPtTFDFezA8sle/1sdgxDjuMoRXEKtx97ViRxGGkVQYovem+Q8Pr/2HxiHp74SSRG+o6R0Yq0shPwQ=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.34.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Vmg0NhAap2S54JojJchiu5An54qa6t/oKT7LmDaWggpIcaiL8WcWHEN6OQrfTdL6mQ2GFyH7j2T5/3YPEDOOGA=="], + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.34.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-MN7aaBC7mAjsiMEZcsJvwNsQVNZShgES/9SzWp1HC9Yjqb5OpexYnRjF7RmE4itbeesHMYYQiAtUAQaSKs2Rfw=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.34.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-CV2aqhDDOsABKHKhNcs1SZFryffQf8vK2XrxP6lxC99ELZAdvsDgPklIBfd65R8R+qvOm1SmLaZ/Fdq961+m7A=="], + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.34.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-aeawEKYswsFu1LhDM9RIgToobquzdtSc4jSVqHV8uApz4FVvhFl/mKh92wc8WpFc6aYCothV/03UjY6y7yLgbg=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.34.0", "", { "os": "win32", "cpu": "x64" }, "sha512-g2ASy1QwHP88y5KWvblUolJz9rN+i4ZOsYzkEwcNfaNooxNUXG+ON6F5xFo0NIItpHqxcdAyls05VXpBnludGw=="], + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.34.7", "", { "os": "win32", "cpu": "x64" }, "sha512-4ZedScpxxIrVO7otcZ8kCX1mZArtH2Wfj3uFCxRJ9NO80gg1XV0U/b2f/MKaGwj2X3QopHfoWiDQ917FRpwY3w=="], - "@sinclair/typebox": ["@sinclair/typebox@0.34.15", "", {}, "sha512-xeIzl3h1Znn9w/LTITqpiwag0gXjA+ldi2ZkXIBxGEppGCW211Tza+eL6D4pKqs10bj5z2umBWk5WL6spQ2OCQ=="], + "@sinclair/typebox": ["@sinclair/typebox@0.34.22", "", {}, "sha512-0avTcz3XUm6mMcq5tQRoEnxyvmr3uanplFepD+a/TiDzOBZ0Us5bsShW41xOO2kST7AYv4xiCsmE5Ag02yOPfQ=="], "@types/bun": ["@types/bun@1.2.2", "", { "dependencies": { "bun-types": "1.2.2" } }, "sha512-tr74gdku+AEDN5ergNiBnplr7hpDp3V1h7fqI2GcR/rsUaM39jpSeKH0TFibRvU0KwniRx5POgaYnaXbk0hU+w=="], "@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="], - "@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="], + "@types/node": ["@types/node@22.13.4", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg=="], "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], @@ -212,7 +212,7 @@ "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], - "elysia": ["elysia@1.2.11", "", { "dependencies": { "@sinclair/typebox": "^0.34.15", "cookie": "^1.0.2", "memoirist": "^0.3.0", "openapi-types": "^12.1.3" }, "peerDependencies": { "typescript": ">= 5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-9bt2tsru9LcFAVrWDfcREJFFSIz0pauzo/XO+5kYPjtHNbjsVkRLQTNDYydy3mQQmz4Acxavoi2MCBbay3DETw=="], + "elysia": ["elysia@1.2.12", "", { "dependencies": { "@sinclair/typebox": "^0.34.15", "cookie": "^1.0.2", "memoirist": "^0.3.0", "openapi-types": "^12.1.3" }, "peerDependencies": { "typescript": ">= 5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-X1bZo09qe8/Poa/5tz08Y+sE/77B/wLwnA5xDDENU3FCrsUtYJuBVcy6BPXGRCgnJ1fPQpc0Ov2ZU5MYJXluTg=="], "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], @@ -326,7 +326,7 @@ "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], - "mitata": ["mitata@1.0.33", "", {}, "sha512-ZRbHD4ZGAbC1B9SYCZXjLox2scPCauhTPkXGk2o7CGj/wNeBNjagwFutphDCgJNbEF80fyMBcPdkfr+WFC9cHw=="], + "mitata": ["mitata@1.0.34", "", {}, "sha512-Mc3zrtNBKIMeHSCQ0XqRLo1vbdIx1wvFV9c8NJAiyho6AjNfMY8bVhbS12bwciUdd1t4rj8099CH3N3NFahaUA=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -368,7 +368,7 @@ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], - "readdirp": ["readdirp@4.1.1", "", {}, "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw=="], + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], @@ -380,7 +380,7 @@ "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], - "rollup": ["rollup@4.34.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.34.0", "@rollup/rollup-android-arm64": "4.34.0", "@rollup/rollup-darwin-arm64": "4.34.0", "@rollup/rollup-darwin-x64": "4.34.0", "@rollup/rollup-freebsd-arm64": "4.34.0", "@rollup/rollup-freebsd-x64": "4.34.0", "@rollup/rollup-linux-arm-gnueabihf": "4.34.0", "@rollup/rollup-linux-arm-musleabihf": "4.34.0", "@rollup/rollup-linux-arm64-gnu": "4.34.0", "@rollup/rollup-linux-arm64-musl": "4.34.0", "@rollup/rollup-linux-loongarch64-gnu": "4.34.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.34.0", "@rollup/rollup-linux-riscv64-gnu": "4.34.0", "@rollup/rollup-linux-s390x-gnu": "4.34.0", "@rollup/rollup-linux-x64-gnu": "4.34.0", "@rollup/rollup-linux-x64-musl": "4.34.0", "@rollup/rollup-win32-arm64-msvc": "4.34.0", "@rollup/rollup-win32-ia32-msvc": "4.34.0", "@rollup/rollup-win32-x64-msvc": "4.34.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-+4C/cgJ9w6sudisA0nZz0+O7lTP9a3CzNLsoDwaRumM8QHwghUsu6tqHXiTmNUp/rqNiM14++7dkzHDyCRs0Jg=="], + "rollup": ["rollup@4.34.7", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.34.7", "@rollup/rollup-android-arm64": "4.34.7", "@rollup/rollup-darwin-arm64": "4.34.7", "@rollup/rollup-darwin-x64": "4.34.7", "@rollup/rollup-freebsd-arm64": "4.34.7", "@rollup/rollup-freebsd-x64": "4.34.7", "@rollup/rollup-linux-arm-gnueabihf": "4.34.7", "@rollup/rollup-linux-arm-musleabihf": "4.34.7", "@rollup/rollup-linux-arm64-gnu": "4.34.7", "@rollup/rollup-linux-arm64-musl": "4.34.7", "@rollup/rollup-linux-loongarch64-gnu": "4.34.7", "@rollup/rollup-linux-powerpc64le-gnu": "4.34.7", "@rollup/rollup-linux-riscv64-gnu": "4.34.7", "@rollup/rollup-linux-s390x-gnu": "4.34.7", "@rollup/rollup-linux-x64-gnu": "4.34.7", "@rollup/rollup-linux-x64-musl": "4.34.7", "@rollup/rollup-win32-arm64-msvc": "4.34.7", "@rollup/rollup-win32-ia32-msvc": "4.34.7", "@rollup/rollup-win32-x64-msvc": "4.34.7", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-8qhyN0oZ4x0H6wmBgfKxJtxM7qS98YJ0k0kNh5ECVtuchIJ7z9IVVvzpmtQyT10PXKMtBxYr1wQ5Apg8RS8kXQ=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], @@ -430,7 +430,7 @@ "typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="], - "undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], diff --git a/example/index.ts b/example/index.ts index b7ae3b8..0ac9727 100644 --- a/example/index.ts +++ b/example/index.ts @@ -3,11 +3,9 @@ import { createAccelerator } from '../src/index' const shape = t.Object({ name: t.String(), - playing: t.Nullable(t.Integer({ default: 2 })) + playing: t.Nullable(t.String()) }) -console.log(t.Integer({ default: 2 })) - const value = { name: 'saltyaom', playing: null @@ -16,4 +14,4 @@ const value = { const stringify = createAccelerator(shape) console.log(stringify(value)) -console.log(stringify.toString()) +// console.log(stringify.toString()) diff --git a/package.json b/package.json index 90b1168..581a027 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "json-accelerator", - "version": "0.0.2", + "version": "0.1.0", "description": "Speed up JSON stringification by providing OpenAPI/TypeBox model", "license": "MIT", "scripts": { @@ -11,7 +11,7 @@ "release": "npm run build && npm run test && npm publish --access public" }, "peerDependencies": { - "@sinclair/typebox": ">= 0.34.0" + "@sinclair/typebox": "^0.34.15" }, "peerDependenciesMeta": { "@sinclair/typebox": { diff --git a/src/index.ts b/src/index.ts index 719e136..f176fb0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -151,9 +151,43 @@ const isDateType = (schema: TAnySchema): boolean => { interface Instruction { array: number optional: number + hasString: boolean properties: string[] + /** + * If unsafe character is found, how should the encoder handle it? + * + * This value only applied to string field. + * + * - 'throw': Throw an error + * - 'ignore': Ignore the unsafe character, this implied that end user should handle it + * - 'sanitize': Sanitize the string and continue encoding + * + * @default 'sanitize' + **/ + sanitize: 'auto' | 'manual' | 'throw' } +const SANITIZE = { + auto: (property: string) => + `re.test(${property})?JSON.stringify(${property}):\`"$\{${property}}"\``, + manual: (property: string) => `${property}`, + throw: (property: string) => + `re.test(${property})?(()=>{throw new Error("Property '${property}' contains invalid characters")})():${property}` +} satisfies Record string> + +const joinStringArray = (p: string) => + `"$\{` + + `(()=>{` + + `if(${p}.length===1)return ${p}\n` + + `let ars=''\n` + + `for(let i=0;i<${p}.length;i++){` + + `if(i===0)ars+=${p}[i]\n` + + `else ars+=\`","\${${p}[i]}\`` + + `}` + + `return ars` + + '})()' + + '}"' + const accelerate = ( schema: TAnySchema, property: string, @@ -178,11 +212,37 @@ const accelerate = ( ? `${property}===undefined` : '' + let sanitize = SANITIZE[instruction.sanitize] + switch (schema.type) { case 'string': - if (nullableCondition) - v = `\${${nullableCondition}?${schema.default !== undefined ? `'"${schema.default}"'` : `'null'`}:\`\\"\${${property}}\\"\`}` - else v = `\"\${${property}}\"` + instruction.hasString = true + + // string operation would be repeated multiple time + // it's fine to optimize it to the most optimized way + if ( + instruction.sanitize === 'auto' || + // Elysia specific format, this implied that format might contain unescaped JSON string + schema.sanitize + ) { + sanitize = SANITIZE['auto'] + + // Sanitize use JSON.stringify which wrap double quotes + // this handle the case where the string contains double quotes + // As slice(1,-1) is use several compute and would be called multiple times + // it's not ideal to slice(1, -1) of JSON.stringify + if (nullableCondition) + v = `\${${nullableCondition}?${schema.const !== undefined ? `'${JSON.stringify(schema.const)}'` : schema.default !== undefined ? `'${JSON.stringify(schema.default)}'` : `'null'`}:${sanitize(property)}}` + else + v = `${schema.const !== undefined ? `${JSON.stringify(schema.const)}` : `\${${sanitize(property)}}`}` + } else { + // In this case quote is handle outside to improve performance + if (nullableCondition) + v = `\${${nullableCondition}?${schema.const !== undefined ? `'${JSON.stringify(schema.const)}'` : schema.default !== undefined ? `'${JSON.stringify(schema.default)}'` : `'null'`}:\`\\"\${${sanitize(property)}}\\"\`}` + else + v = `${schema.const !== undefined ? `${JSON.stringify(schema.const)}` : `"\${${sanitize(property)}}"`}` + } + break case 'number': @@ -272,9 +332,9 @@ const accelerate = ( if (schema.items.type === 'string') { if (nullableCondition) - v += `\${${nullableCondition}?"null":${property}.length?\`["$\{${property}.join('",\"')}"]\`:"[]"}` + v += `\${${nullableCondition}?"null":${property}.length?\`[${joinStringArray(property)}]\`:"[]"}` else - v += `\${${property}.length?\`["$\{${property}.join('",\"')}"]\`:"[]"}` + v += `\${${property}.length?\`[${joinStringArray(property)}]\`:"[]"}` break } @@ -286,9 +346,9 @@ const accelerate = ( isInteger(schema.items) ) { if (nullableCondition) - v += `\${${nullableCondition}?'"null"':${property}.length?\`[$\{${property}.join(',')}]\`:"[]"` + v += `\${${nullableCondition}?'"null"':${property}.length?\`[$\{${property}.toString()}]\`:"[]"` else - v += `\${${property}.length?\`[$\{${property}.join(',')}]\`:"[]"}` + v += `\${${property}.length?\`[$\{${property}.toString()}]\`:"[]"}` break } @@ -344,6 +404,9 @@ const accelerate = ( let setup = '' + if (instruction.hasString) + setup += `const re=/[\\b\\f\\n\\r\\t\\\\\\\\/"]/\n` + if (instruction.optional) { setup += 'let ' @@ -372,12 +435,19 @@ const accelerate = ( } export const createAccelerator = ( - schema: T + schema: T, + { + sanitize = 'auto' + }: { + sanitize?: Instruction['sanitize'] + } = {} ): ((v: T['static']) => string) => { const f = accelerate(schema, 'v', { array: 0, optional: 0, - properties: [] + properties: [], + hasString: false, + sanitize }) return Function('v', f) as any diff --git a/test/sanitize-auto.test.ts b/test/sanitize-auto.test.ts new file mode 100644 index 0000000..9976c2a --- /dev/null +++ b/test/sanitize-auto.test.ts @@ -0,0 +1,62 @@ +import { t } from 'elysia' +import { type TAnySchema } from '@sinclair/typebox' +import { createAccelerator } from '../src' + +import { describe, expect, it } from 'bun:test' + +const isEqual = (shape: TAnySchema, value: unknown) => + expect(JSON.parse(createAccelerator(shape)(value))).toEqual(value) + +describe('sanitize auto', () => { + it('sanitize invalid value', () => { + const shape = t.Object({ + a: t.String(), + b: t.String() + }) + + const value = { + a: 'hello', + b: 'hello\nworld' + } satisfies typeof shape.static + + expect(() => + createAccelerator(shape, { + sanitize: 'auto' + })(value) + ).not.toThrow() + + expect(() => + JSON.parse( + createAccelerator(shape, { + sanitize: 'auto' + })(value) + ) + ).not.toThrow() + + expect( + createAccelerator(shape, { + sanitize: 'auto' + })(value) + ).toEqual(`{"a":"hello","b":"hello\\nworld"}`) + }) + + it('create literal value', () => { + const shape = t.Object({ + a: t.String(), + b: t.Literal('SaltyAom') + }) + + const value = { + a: 'hello', + b: 'SaltyAom' + } satisfies typeof shape.static + + expect(() => + createAccelerator(shape, { + sanitize: 'auto' + })(value) + ).not.toThrow() + + isEqual(shape, value) + }) +}) diff --git a/test/sanitize-manual.test.ts b/test/sanitize-manual.test.ts new file mode 100644 index 0000000..2811169 --- /dev/null +++ b/test/sanitize-manual.test.ts @@ -0,0 +1,62 @@ +import { t } from 'elysia' +import { type TAnySchema } from '@sinclair/typebox' +import { createAccelerator } from '../src' + +import { describe, expect, it } from 'bun:test' + +const isEqual = (shape: TAnySchema, value: unknown) => + expect(JSON.parse(createAccelerator(shape)(value))).toEqual(value) + +describe('sanitize manual', () => { + it('ignore invalid value', () => { + const shape = t.Object({ + a: t.String(), + b: t.String() + }) + + const value = { + a: 'hello', + b: 'hello\nworld' + } satisfies typeof shape.static + + expect(() => + createAccelerator(shape, { + sanitize: 'manual' + })(value) + ).not.toThrow() + + expect(() => + JSON.parse( + createAccelerator(shape, { + sanitize: 'manual' + })(value) + ) + ).toThrow() + + expect( + createAccelerator(shape, { + sanitize: 'manual' + })(value) + ).toEqual(`{"a":"hello","b":"hello\nworld"}`) + }) + + it('create a literal value', () => { + const shape = t.Object({ + a: t.String(), + b: t.Literal('SaltyAom') + }) + + const value = { + a: 'hello', + b: 'SaltyAom' + } satisfies typeof shape.static + + expect(() => + createAccelerator(shape, { + sanitize: 'manual' + })(value) + ).not.toThrow() + + isEqual(shape, value) + }) +}) diff --git a/test/sanitize-throw.test.ts b/test/sanitize-throw.test.ts new file mode 100644 index 0000000..356d830 --- /dev/null +++ b/test/sanitize-throw.test.ts @@ -0,0 +1,92 @@ +import { t } from 'elysia' +import { type TAnySchema } from '@sinclair/typebox' +import { createAccelerator } from '../src' + +import { describe, expect, it } from 'bun:test' + +const isEqual = (shape: TAnySchema, value: unknown) => + expect(JSON.parse(createAccelerator(shape)(value))).toEqual(value) + +describe('sanitize throw', () => { + it('throw on invalid value', () => { + const shape = t.Object({ + a: t.String(), + b: t.String() + }) + + const value = { + a: 'hello', + b: 'hello\nworld' + } satisfies typeof shape.static + + expect(() => + createAccelerator(shape, { + sanitize: 'throw' + })(value) + ).toThrow() + }) + + it("don't throw on valid value", () => { + const shape = t.Object({ + a: t.String(), + b: t.String() + }) + + const value = { + a: 'hello', + b: 'hello world' + } satisfies typeof shape.static + + expect(() => + createAccelerator(shape, { + sanitize: 'throw' + })(value) + ).not.toThrow() + + isEqual(shape, value) + }) + + it("don't throw on valid value", () => { + const shape = t.Object({ + a: t.String(), + b: t.String({ + sanitize: true + }) + }) + + const value = { + a: 'hello', + b: 'hello world' + } satisfies typeof shape.static + + expect(() => + createAccelerator(shape, { + sanitize: 'throw' + })(value) + ).not.toThrow() + + isEqual(shape, value) + }) + + it('handle sanitize value', () => { + const shape = t.Object({ + a: t.String(), + b: t.String({ + sanitize: true + }) + }) + + const value = { + a: 'hello', + b: 'hello\nworld' + } satisfies typeof shape.static + + expect(() => + createAccelerator(shape, { + sanitize: 'throw' + })(value) + ).not.toThrow() + + isEqual(shape, value) + }) +}) From 47d479502fa0d7636ee9e9435d01a6e04615f49d Mon Sep 17 00:00:00 2001 From: saltyaom Date: Tue, 4 Mar 2025 18:51:56 +0700 Subject: [PATCH 02/14] :tada: feat: record --- CHANGELOG.md | 7 ++++ benchmarks/small2.ts | 29 ++++++++++++++++ benchmarks/utils.ts | 10 ++++++ example/index.ts | 15 ++++---- package.json | 2 +- src/index.ts | 49 ++++++++++++++++++++++++-- test/record.test.ts | 83 ++++++++++++++++++++++++++++++++++++++++++++ test/tuple.test.ts | 46 ++++++++++++++++++++++++ 8 files changed, 232 insertions(+), 9 deletions(-) create mode 100644 benchmarks/small2.ts create mode 100644 test/record.test.ts create mode 100644 test/tuple.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index dafbb58..4f471aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 0.1.1 - 4 Mar 2025 +Feature: +- support Record + +Improvement: +- add Tuple test case + # 0.1.0 - 5 Feb 2025 Feature: - replace `arrayItems.join('",\"')` in favour of inline `joinStringArray` to improve performance diff --git a/benchmarks/small2.ts b/benchmarks/small2.ts new file mode 100644 index 0000000..c7ff66b --- /dev/null +++ b/benchmarks/small2.ts @@ -0,0 +1,29 @@ +import { t } from 'elysia' +import { benchmark } from './utils' + +benchmark( + t.Array( + t.Object({ + name: t.String(), + pwd: t.String(), + id: t.Array(t.Number()) + }) + ), + [ + { + name: 'SaltyAom', + pwd: 'password', + id: [1, 2, 3] + }, + { + name: 'JohnDoe', + pwd: 'password', + id: [4, 5, 6] + }, + { + name: 'JaneDoe', + pwd: 'password', + id: [7, 8, 9] + } + ] +) diff --git a/benchmarks/utils.ts b/benchmarks/utils.ts index 7f553cd..37c0809 100644 --- a/benchmarks/utils.ts +++ b/benchmarks/utils.ts @@ -1,7 +1,9 @@ import { bench, run, barplot, summary, compact } from 'mitata' import { createAccelerator } from '../src' +import { TypeCompiler } from '@sinclair/typebox/compiler' import fastJson from 'fast-json-stringify' + import type { TAnySchema } from '@sinclair/typebox' export const benchmark = ( @@ -37,6 +39,14 @@ export const benchmark = ( bench('JSON Accelerator', () => { return encode(value) }) + + const validator = TypeCompiler.Compile(model) + + bench('JSON Accelerator w/ validation', () => { + validator.Check(value) + + return encode(value) + }) }) }) }) diff --git a/example/index.ts b/example/index.ts index 0ac9727..92497a6 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,14 +1,17 @@ import { t } from 'elysia' import { createAccelerator } from '../src/index' -const shape = t.Object({ - name: t.String(), - playing: t.Nullable(t.String()) -}) +const shape = t.Record( + t.String(), + t.Object({ + a: t.String(), + b: t.String() + }) +) const value = { - name: 'saltyaom', - playing: null + a: { a: 'a', b: 'a' }, + c: { a: 'a', b: 'b' } } satisfies typeof shape.static const stringify = createAccelerator(shape) diff --git a/package.json b/package.json index 581a027..5b74877 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "json-accelerator", - "version": "0.1.0", + "version": "0.1.1", "description": "Speed up JSON stringification by providing OpenAPI/TypeBox model", "license": "MIT", "scripts": { diff --git a/src/index.ts b/src/index.ts index f176fb0..be7ce04 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,13 @@ -import type { TAnySchema } from '@sinclair/typebox' +import type { TAnySchema, TRecord } from '@sinclair/typebox' const Kind = Symbol.for('TypeBox.Kind') const OptionalKind = Symbol.for('TypeBox.Optional') const isSpecialProperty = (name: string) => /(\ |-|\t|\n)/.test(name) -const joinProperty = (v1: string, v2: string) => { +const joinProperty = (v1: string, v2: string | number) => { + if (typeof v2 === 'number') return `${v1}[${v2}]` + if (isSpecialProperty(v2)) return `${v1}["${v2}"]` return `${v1}.${v2}` @@ -188,6 +190,34 @@ const joinStringArray = (p: string) => '})()' + '}"' +const handleRecord = ( + schema: TRecord, + property: string, + instruction: Instruction +) => { + const child = + schema.patternProperties['^(.*)$'] ?? + schema.patternProperties[Object.keys(schema.patternProperties)[0]] + + if (!child) return property + + const i = instruction.array + instruction.array++ + + return ( + `\${(()=>{` + + `const ar${i}s=Object.keys(${property});` + + `let ar${i}v='{';` + + `for(let i=0;i + expect(JSON.parse(createAccelerator(shape)(value))).toEqual(expected) + +describe('Record', () => { + it('handle record', () => { + const shape = t.Record(t.String(), t.String()) + + const value = { + name: 'saltyaom', + alias: 'saltyaom' + } satisfies typeof shape.static + + isEqual(shape, value) + }) + + it('handle record object', () => { + const shape = t.Record( + t.String(), + t.Object({ + name: t.String(), + age: t.Number() + }) + ) + + const value = { + saltyaom: { + name: 'saltyaom', + age: 23 + }, + chiffon: { + name: 'chiffon', + age: 24 + } + } satisfies typeof shape.static + + isEqual(shape, value) + }) + + it('handle nested record', () => { + const shape = t.Record(t.String(), t.Record(t.String(), t.Number())) + + const value = { + saltyaom: { + id: 1, + age: 23 + }, + chiffon: { + id: 2, + age: 24 + } + } satisfies typeof shape.static + + isEqual(shape, value) + }) + + it('handle unknown record', () => { + const shape = t.Object( + {}, + { + patternProperties: { + '^[a-z]+$': t.String() + } + } + ) + + const value = { + name: 'saltyaom', + alias: 'saltyaom', + unknown: { + a: 1, + b: ['a', { hello: 'world' }] + } + } satisfies typeof shape.static + + isEqual(shape, value) + }) +}) diff --git a/test/tuple.test.ts b/test/tuple.test.ts new file mode 100644 index 0000000..4110449 --- /dev/null +++ b/test/tuple.test.ts @@ -0,0 +1,46 @@ +import { t } from 'elysia' +import { type TAnySchema } from '@sinclair/typebox' +import { createAccelerator } from '../src' + +import { describe, expect, it } from 'bun:test' + +const isEqual = (shape: TAnySchema, value: unknown, expected = value) => + expect(JSON.parse(createAccelerator(shape)(value))).toEqual(expected) + +describe('Tuple', () => { + it('handle tuple', () => { + const shape = t.Tuple([t.String(), t.Number()]) + + const value = ['saltyaom', 123] satisfies typeof shape.static + + isEqual(shape, value) + }) + + it('handle tuple object', () => { + const shape = t.Tuple([ + t.String(), + t.Object({ + name: t.String(), + age: t.Number() + }) + ]) + + const value = [ + 'a', + { + name: 'saltyaom', + age: 123 + } + ] satisfies typeof shape.static + + isEqual(shape, value) + }) + + it('handle nested tuple', () => { + const shape = t.Tuple([t.String(), t.Tuple([t.String(), t.Number()])]) + + const value = ['a', ['b', 123]] satisfies typeof shape.static + + isEqual(shape, value) + }) +}) From 0d1bd0d1c3bbfd3b0a7e765969ce5f820ec84d3b Mon Sep 17 00:00:00 2001 From: saltyaom Date: Tue, 4 Mar 2025 19:22:30 +0700 Subject: [PATCH 03/14] :wrench: fix: handle root array --- CHANGELOG.md | 4 ++ example/index.ts | 13 +----- package.json | 2 +- src/index.ts | 8 ++++ test/array.test.ts | 112 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 12 deletions(-) create mode 100644 test/array.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f471aa..38429f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.1.2 - 4 Mar 2025 +Bug fix: +- handle primitive type when array is root + # 0.1.1 - 4 Mar 2025 Feature: - support Record diff --git a/example/index.ts b/example/index.ts index 92497a6..264400c 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,18 +1,9 @@ import { t } from 'elysia' import { createAccelerator } from '../src/index' -const shape = t.Record( - t.String(), - t.Object({ - a: t.String(), - b: t.String() - }) -) +const shape = t.Array(t.Number()) -const value = { - a: { a: 'a', b: 'a' }, - c: { a: 'a', b: 'b' } -} satisfies typeof shape.static +const value = [1,2] satisfies typeof shape.static const stringify = createAccelerator(shape) diff --git a/package.json b/package.json index 5b74877..01a6172 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "json-accelerator", - "version": "0.1.1", + "version": "0.1.2", "description": "Speed up JSON stringification by providing OpenAPI/TypeBox model", "license": "MIT", "scripts": { diff --git a/src/index.ts b/src/index.ts index be7ce04..8f76cdd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -376,11 +376,15 @@ const accelerate = ( instruction.array++ if (schema.items.type === 'string') { + if(isRoot) v += 'return `' + if (nullableCondition) v += `\${${nullableCondition}?"null":${property}.length?\`[${joinStringArray(property)}]\`:"[]"}` else v += `\${${property}.length?\`[${joinStringArray(property)}]\`:"[]"}` + if(isRoot) v += '`' + break } @@ -390,11 +394,15 @@ const accelerate = ( schema.items.type === 'bigint' || isInteger(schema.items) ) { + if(isRoot) v += 'return `' + if (nullableCondition) v += `\${${nullableCondition}?'"null"':${property}.length?\`[$\{${property}.toString()}]\`:"[]"` else v += `\${${property}.length?\`[$\{${property}.toString()}]\`:"[]"}` + if(isRoot)v += '`' + break } diff --git a/test/array.test.ts b/test/array.test.ts new file mode 100644 index 0000000..c5e6348 --- /dev/null +++ b/test/array.test.ts @@ -0,0 +1,112 @@ +import { t } from 'elysia' +import { type TAnySchema } from '@sinclair/typebox' +import { createAccelerator } from '../src' + +import { describe, expect, it } from 'bun:test' + +const isEqual = (shape: TAnySchema, value: unknown, expected = value) => + expect(JSON.parse(createAccelerator(shape)(value))).toEqual(expected) + +describe('Array', () => { + it('handle string array at root', () => { + const shape = t.Array(t.String()) + + isEqual(shape, ['a', 'b']) + }) + + it('handle number array at root', () => { + const shape = t.Array(t.Number()) + + isEqual(shape, [1, 2]) + }) + + it('handle boolean array at root', () => { + const shape = t.Array(t.Number()) + + isEqual(shape, [true, false]) + }) + + it('handle big int array at root', () => { + const shape = t.Array(t.Number()) + + isEqual(shape, [1n, 2n], [1, 2]) + }) + + it('handle array union at root', () => { + const shape = t.Array(t.Union([t.String(), t.Number()])) + + isEqual(shape, ['a', 'b', 1, 2, 'c']) + }) + + it('handle array object', () => { + const shape = t.Array( + t.Object({ + a: t.String(), + b: t.String() + }) + ) + + isEqual( + shape, + [ + { + a: 'a', + b: 'b' + }, + { + a: 'a', + b: 'b', + c: 'c' + } + ], + [ + { + a: 'a', + b: 'b' + }, + { + a: 'a', + b: 'b' + } + ] + ) + }) + + it('handle array object with optional', () => { + const shape = t.Array( + t.Object({ + a: t.String(), + b: t.Optional(t.String()) + }) + ) + + isEqual( + shape, + [ + { + a: 'a' + }, + { + a: 'a', + b: 'b' + }, + { + a: 'a', + b: 'b', + c: 'c' + } + ], + [ + { a: 'a' }, + { + a: 'a', + b: 'b' + }, + { + a: 'a', + b: 'b' + } + ] + ) + }) +}) From 15f6a439800a79c1553cd95d9a3e5bcebfa35b67 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Fri, 14 Mar 2025 17:12:10 +0700 Subject: [PATCH 04/14] :tada: feat: ref --- CHANGELOG.md | 4 ++ example/index.ts | 22 +++++-- package.json | 2 +- src/index.ts | 36 ++++++++--- test/array.test.ts | 10 +--- test/default.test.ts | 10 +--- test/index.test.ts | 10 ++-- test/merge-intersection.test.ts | 5 +- test/record.test.ts | 10 +--- test/ref.test.ts | 102 ++++++++++++++++++++++++++++++++ test/sample.test.ts | 8 +-- test/sanitize-auto.test.ts | 9 +-- test/sanitize-manual.test.ts | 10 ++-- test/sanitize-throw.test.ts | 10 ++-- test/tuple.test.ts | 10 +--- test/utils.ts | 7 +++ 16 files changed, 190 insertions(+), 75 deletions(-) create mode 100644 test/ref.test.ts create mode 100644 test/utils.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 38429f0..0e30453 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.1.3 - 14 Mar 2025 +Bug fix: +- support `t.Module`, `t.Ref` + # 0.1.2 - 4 Mar 2025 Bug fix: - handle primitive type when array is root diff --git a/example/index.ts b/example/index.ts index 264400c..41b4e72 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,11 +1,23 @@ import { t } from 'elysia' import { createAccelerator } from '../src/index' -const shape = t.Array(t.Number()) +const v = t.Module({ + a: t.Object({ + name: t.String(), + job: t.Optional(t.Ref('job')), + trait: t.Optional(t.String()) + }), + job: t.Number() +}) -const value = [1,2] satisfies typeof shape.static +const shape = v.Import('a') -const stringify = createAccelerator(shape) +const value = { + name: 'Jane Doe', + job: 'Software Engineer', + trait: 'Friendly' +} satisfies typeof shape.static -console.log(stringify(value)) -// console.log(stringify.toString()) +const mirror = createAccelerator(shape) + +console.log(mirror(value)) diff --git a/package.json b/package.json index 01a6172..7176a95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "json-accelerator", - "version": "0.1.2", + "version": "0.1.3", "description": "Speed up JSON stringification by providing OpenAPI/TypeBox model", "license": "MIT", "scripts": { diff --git a/src/index.ts b/src/index.ts index 8f76cdd..cfac437 100644 --- a/src/index.ts +++ b/src/index.ts @@ -167,6 +167,7 @@ interface Instruction { * @default 'sanitize' **/ sanitize: 'auto' | 'manual' | 'throw' + definitions: Record } const SANITIZE = { @@ -225,6 +226,16 @@ const accelerate = ( ): string => { if (!schema) return '' + if ( + Kind in schema && + schema[Kind] === 'Import' && + schema.$ref in schema.$defs + ) + return accelerate(schema.$defs[schema.$ref], property, { + ...instruction, + definitions: Object.assign(instruction.definitions, schema.$defs) + }) + let v = '' const isRoot = property === 'v' @@ -376,14 +387,14 @@ const accelerate = ( instruction.array++ if (schema.items.type === 'string') { - if(isRoot) v += 'return `' + if (isRoot) v += 'return `' if (nullableCondition) v += `\${${nullableCondition}?"null":${property}.length?\`[${joinStringArray(property)}]\`:"[]"}` else v += `\${${property}.length?\`[${joinStringArray(property)}]\`:"[]"}` - if(isRoot) v += '`' + if (isRoot) v += '`' break } @@ -394,14 +405,14 @@ const accelerate = ( schema.items.type === 'bigint' || isInteger(schema.items) ) { - if(isRoot) v += 'return `' + if (isRoot) v += 'return `' if (nullableCondition) v += `\${${nullableCondition}?'"null"':${property}.length?\`[$\{${property}.toString()}]\`:"[]"` else v += `\${${property}.length?\`[$\{${property}.toString()}]\`:"[]"}` - if(isRoot)v += '`' + if (isRoot) v += '`' break } @@ -427,6 +438,13 @@ const accelerate = ( break default: + if (schema.$ref && schema.$ref in instruction.definitions) + return accelerate( + instruction.definitions[schema.$ref], + property, + instruction + ) + if (isDateType(schema)) { if (isNullable || isUndefinable) v = `\${${nullableCondition}?${schema.default !== undefined ? `'"${schema.default}"'` : "'null'"}:typeof ${property}==="object"?\`\"\${${property}.toISOString()}\"\`:${property}}` @@ -490,17 +508,17 @@ const accelerate = ( export const createAccelerator = ( schema: T, { - sanitize = 'auto' - }: { - sanitize?: Instruction['sanitize'] - } = {} + sanitize = 'auto', + definitions = {} + }: Partial> = {} ): ((v: T['static']) => string) => { const f = accelerate(schema, 'v', { array: 0, optional: 0, properties: [], hasString: false, - sanitize + sanitize, + definitions }) return Function('v', f) as any diff --git a/test/array.test.ts b/test/array.test.ts index c5e6348..28d1be9 100644 --- a/test/array.test.ts +++ b/test/array.test.ts @@ -1,11 +1,7 @@ -import { t } from 'elysia' -import { type TAnySchema } from '@sinclair/typebox' -import { createAccelerator } from '../src' - -import { describe, expect, it } from 'bun:test' +import { describe, it } from 'bun:test' +import { isEqual } from './utils' -const isEqual = (shape: TAnySchema, value: unknown, expected = value) => - expect(JSON.parse(createAccelerator(shape)(value))).toEqual(expected) +import { t } from 'elysia' describe('Array', () => { it('handle string array at root', () => { diff --git a/test/default.test.ts b/test/default.test.ts index e1720a4..413153b 100644 --- a/test/default.test.ts +++ b/test/default.test.ts @@ -1,11 +1,7 @@ -import { t } from 'elysia' -import { type TAnySchema } from '@sinclair/typebox' -import { createAccelerator } from '../src' - -import { describe, expect, it } from 'bun:test' +import { describe, it } from 'bun:test' +import { isEqual } from './utils' -const isEqual = (shape: TAnySchema, value: unknown, expected = value) => - expect(JSON.parse(createAccelerator(shape)(value))).toEqual(expected) +import { t } from 'elysia' describe('Default', () => { it('handle default value for optional string', () => { diff --git a/test/index.test.ts b/test/index.test.ts index d1344bd..a8ee5a3 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -1,11 +1,9 @@ -import { t } from 'elysia' -import { type TAnySchema } from '@sinclair/typebox' -import { createAccelerator } from '../src' - import { describe, expect, it } from 'bun:test' +import { isEqual } from './utils' -const isEqual = (shape: TAnySchema, value: unknown, expected = value) => - expect(JSON.parse(createAccelerator(shape)(value))).toEqual(expected) +import { t } from 'elysia' + +import { createAccelerator } from '../src' describe('Core', () => { it('handle string', () => { diff --git a/test/merge-intersection.test.ts b/test/merge-intersection.test.ts index a434181..e966ce3 100644 --- a/test/merge-intersection.test.ts +++ b/test/merge-intersection.test.ts @@ -1,7 +1,8 @@ +import { describe, expect, it } from 'bun:test' + import { Type as t } from '@sinclair/typebox' -import { mergeObjectIntersection } from '../src' -import { describe, expect, it } from 'bun:test' +import { mergeObjectIntersection } from '../src' describe('Merge Object Intersection', () => { it('work', () => { diff --git a/test/record.test.ts b/test/record.test.ts index 9106c83..90430b3 100644 --- a/test/record.test.ts +++ b/test/record.test.ts @@ -1,11 +1,7 @@ -import { t } from 'elysia' -import { type TAnySchema } from '@sinclair/typebox' -import { createAccelerator } from '../src' - -import { describe, expect, it } from 'bun:test' +import { describe, it } from 'bun:test' +import { isEqual } from './utils' -const isEqual = (shape: TAnySchema, value: unknown, expected = value) => - expect(JSON.parse(createAccelerator(shape)(value))).toEqual(expected) +import { t } from 'elysia' describe('Record', () => { it('handle record', () => { diff --git a/test/ref.test.ts b/test/ref.test.ts new file mode 100644 index 0000000..c980ece --- /dev/null +++ b/test/ref.test.ts @@ -0,0 +1,102 @@ +import { describe, it, expect } from 'bun:test' +import { isEqual } from './utils' + +import { t } from 'elysia' + +import { createAccelerator } from '../src' + +describe('Ref', () => { + it('handle module', () => { + const modules = t.Module({ + object: t.Object({ + name: t.String(), + optional: t.Optional(t.String()) + }) + }) + + const shape = modules.Import('object') + + const value = { + name: 'salt' + } satisfies typeof shape.static + + isEqual(shape, value) + }) + + it('handle nested ref', () => { + const modules = t.Module({ + object: t.Object({ + name: t.String(), + info: t.Ref('info') + }), + info: t.Object({ + id: t.Number(), + name: t.String() + }) + }) + + const shape = modules.Import('object') + + const value = { + name: 'salt', + info: { + id: 123, + name: 'salt' + } + } satisfies typeof shape.static + + isEqual(shape, value) + }) + + it('handle optional ref', () => { + const modules = t.Module({ + object: t.Object({ + name: t.String(), + info: t.Optional(t.Ref('info')) + }), + info: t.Object({ + id: t.Number(), + name: t.String() + }) + }) + + const shape = modules.Import('object') + + const value = { + name: 'salt' + } satisfies typeof shape.static + + isEqual(shape, { + name: 'salt' + }) + + isEqual(shape, { + name: 'salt', + info: { + id: 123, + name: 'salt' + } + }) + }) + + it('handle custom modules', () => { + const definitions = { + object: t.Object({ + name: t.String(), + optional: t.Optional(t.String()) + }) + } + + const shape = definitions.object + + const value = { + name: 'salt' + } satisfies typeof shape.static + + expect( + createAccelerator(shape, { + definitions + })(value) + ).toEqual(JSON.stringify(value)) + }) +}) diff --git a/test/sample.test.ts b/test/sample.test.ts index 25b3c8f..ee9f055 100644 --- a/test/sample.test.ts +++ b/test/sample.test.ts @@ -1,11 +1,7 @@ -import { t } from 'elysia' -import { type TAnySchema } from '@sinclair/typebox' -import { createAccelerator } from '../src' - import { describe, expect, it } from 'bun:test' +import { isEqual } from './utils' -const isEqual = (shape: TAnySchema, value: unknown) => - expect(JSON.parse(createAccelerator(shape)(value))).toEqual(value) +import { t } from 'elysia' describe('Sample', () => { it('Medium', () => { diff --git a/test/sanitize-auto.test.ts b/test/sanitize-auto.test.ts index 9976c2a..bee927d 100644 --- a/test/sanitize-auto.test.ts +++ b/test/sanitize-auto.test.ts @@ -1,11 +1,8 @@ -import { t } from 'elysia' -import { type TAnySchema } from '@sinclair/typebox' -import { createAccelerator } from '../src' - import { describe, expect, it } from 'bun:test' +import { isEqual } from './utils' -const isEqual = (shape: TAnySchema, value: unknown) => - expect(JSON.parse(createAccelerator(shape)(value))).toEqual(value) +import { t } from 'elysia' +import { createAccelerator } from '../src' describe('sanitize auto', () => { it('sanitize invalid value', () => { diff --git a/test/sanitize-manual.test.ts b/test/sanitize-manual.test.ts index 2811169..31e8dbc 100644 --- a/test/sanitize-manual.test.ts +++ b/test/sanitize-manual.test.ts @@ -1,11 +1,9 @@ -import { t } from 'elysia' -import { type TAnySchema } from '@sinclair/typebox' -import { createAccelerator } from '../src' - import { describe, expect, it } from 'bun:test' +import { isEqual } from './utils' -const isEqual = (shape: TAnySchema, value: unknown) => - expect(JSON.parse(createAccelerator(shape)(value))).toEqual(value) +import { t } from 'elysia' + +import { createAccelerator } from '../src' describe('sanitize manual', () => { it('ignore invalid value', () => { diff --git a/test/sanitize-throw.test.ts b/test/sanitize-throw.test.ts index 356d830..849ed27 100644 --- a/test/sanitize-throw.test.ts +++ b/test/sanitize-throw.test.ts @@ -1,11 +1,9 @@ -import { t } from 'elysia' -import { type TAnySchema } from '@sinclair/typebox' -import { createAccelerator } from '../src' - import { describe, expect, it } from 'bun:test' +import { isEqual } from './utils' -const isEqual = (shape: TAnySchema, value: unknown) => - expect(JSON.parse(createAccelerator(shape)(value))).toEqual(value) +import { t } from 'elysia' + +import { createAccelerator } from '../src' describe('sanitize throw', () => { it('throw on invalid value', () => { diff --git a/test/tuple.test.ts b/test/tuple.test.ts index 4110449..a61d615 100644 --- a/test/tuple.test.ts +++ b/test/tuple.test.ts @@ -1,11 +1,7 @@ -import { t } from 'elysia' -import { type TAnySchema } from '@sinclair/typebox' -import { createAccelerator } from '../src' - -import { describe, expect, it } from 'bun:test' +import { describe, it } from 'bun:test' +import { isEqual } from './utils' -const isEqual = (shape: TAnySchema, value: unknown, expected = value) => - expect(JSON.parse(createAccelerator(shape)(value))).toEqual(expected) +import { t } from 'elysia' describe('Tuple', () => { it('handle tuple', () => { diff --git a/test/utils.ts b/test/utils.ts new file mode 100644 index 0000000..b93e45e --- /dev/null +++ b/test/utils.ts @@ -0,0 +1,7 @@ +import { type TAnySchema } from '@sinclair/typebox' +import { createAccelerator } from '../src' + +import { expect } from 'bun:test' + +export const isEqual = (shape: TAnySchema, value: unknown, expected = value) => + expect(JSON.parse(createAccelerator(shape)(value))).toEqual(expected) From ca2ca401100fb3ff3c0d3e95f3b04d6952d9c80f Mon Sep 17 00:00:00 2001 From: saltyaom Date: Thu, 27 Mar 2025 16:57:17 +0700 Subject: [PATCH 05/14] :tada: feat: avoid closure reference --- CHANGELOG.md | 4 + benchmarks/array.ts | 33 ++++++++ bun.lock | 178 ++++++++++++++++---------------------------- package.json | 4 +- src/index.ts | 26 +++---- 5 files changed, 117 insertions(+), 128 deletions(-) create mode 100644 benchmarks/array.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e30453..ed5600a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.1.4 - 27 Mar 2025 +Improvement: +- Improve array performance by avoiding unnecessary closure reference + # 0.1.3 - 14 Mar 2025 Bug fix: - support `t.Module`, `t.Ref` diff --git a/benchmarks/array.ts b/benchmarks/array.ts new file mode 100644 index 0000000..b4b92d4 --- /dev/null +++ b/benchmarks/array.ts @@ -0,0 +1,33 @@ +import { t } from 'elysia' +import { benchmark } from './utils' + +benchmark( + t.Object({ + ids: t.Array(t.Number()), + names: t.Array(t.String()), + games: t.Array( + t.Object({ + name: t.String(), + tags: t.Array(t.String()) + }) + ) + }), + { + ids: [1, 2, 3], + names: ['SaltyAom', 'SaltyAom', 'SaltyAom'], + games: [ + { + name: 'MiSide', + tags: ['Psychological Horror', 'Cute', 'Dating Sim'] + }, + { + name: 'Strinova', + tags: ['Free to Play', 'Anime', 'Third-Person Shooter'] + }, + { + name: "Tom Clancy's Rainbow Six Siege", + tags: ['FPS', 'Multiplayer', 'Tactical'] + } + ] + } +) diff --git a/bun.lock b/bun.lock index fd7a9b5..7216f9e 100644 --- a/bun.lock +++ b/bun.lock @@ -14,7 +14,7 @@ "typescript": "^5.5.3", }, "peerDependencies": { - "@sinclair/typebox": "^0.34.15", + "@sinclair/typebox": "^0.34.31", }, "optionalPeers": [ "@sinclair/typebox", @@ -22,63 +22,63 @@ }, }, "packages": { - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.24.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.24.2", "", { "os": "android", "cpu": "arm" }, "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.1", "", { "os": "android", "cpu": "arm" }, "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.24.2", "", { "os": "android", "cpu": "arm64" }, "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.1", "", { "os": "android", "cpu": "arm64" }, "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.24.2", "", { "os": "android", "cpu": "x64" }, "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.1", "", { "os": "android", "cpu": "x64" }, "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.24.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.24.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.24.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.24.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.24.2", "", { "os": "linux", "cpu": "arm" }, "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.1", "", { "os": "linux", "cpu": "arm" }, "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.24.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.24.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.24.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.24.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.24.2", "", { "os": "linux", "cpu": "x64" }, "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.24.2", "", { "os": "none", "cpu": "arm64" }, "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.1", "", { "os": "none", "cpu": "arm64" }, "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.24.2", "", { "os": "none", "cpu": "x64" }, "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.1", "", { "os": "none", "cpu": "x64" }, "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.24.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.24.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.24.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.24.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.24.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.24.2", "", { "os": "win32", "cpu": "x64" }, "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg=="], - "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.4.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA=="], + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.5.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w=="], "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], "@eslint/config-array": ["@eslint/config-array@0.17.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.4", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA=="], - "@eslint/eslintrc": ["@eslint/eslintrc@3.2.0", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w=="], + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], "@eslint/js": ["@eslint/js@9.6.0", "", {}, "sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A=="], @@ -110,55 +110,57 @@ "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.34.7", "", { "os": "android", "cpu": "arm" }, "sha512-l6CtzHYo8D2TQ3J7qJNpp3Q1Iye56ssIAtqbM2H8axxCEEwvN7o8Ze9PuIapbxFL3OHrJU2JBX6FIIVnP/rYyw=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.37.0", "", { "os": "android", "cpu": "arm" }, "sha512-l7StVw6WAa8l3vA1ov80jyetOAEo1FtHvZDbzXDO/02Sq/QVvqlHkYoFwDJPIMj0GKiistsBudfx5tGFnwYWDQ=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.34.7", "", { "os": "android", "cpu": "arm64" }, "sha512-KvyJpFUueUnSp53zhAa293QBYqwm94TgYTIfXyOTtidhm5V0LbLCJQRGkQClYiX3FXDQGSvPxOTD/6rPStMMDg=="], + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.37.0", "", { "os": "android", "cpu": "arm64" }, "sha512-6U3SlVyMxezt8Y+/iEBcbp945uZjJwjZimu76xoG7tO1av9VO691z8PkhzQ85ith2I8R2RddEPeSfcbyPfD4hA=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.34.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-jq87CjmgL9YIKvs8ybtIC98s/M3HdbqXhllcy9EdLV0yMg1DpxES2gr65nNy7ObNo/vZ/MrOTxt0bE5LinL6mA=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.37.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+iTQ5YHuGmPt10NTzEyMPbayiNTcOZDWsbxZYR1ZnmLnZxG17ivrPSWFO9j6GalY0+gV3Jtwrrs12DBscxnlYA=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.34.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rSI/m8OxBjsdnMMg0WEetu/w+LhLAcCDEiL66lmMX4R3oaml3eXz3Dxfvrxs1FbzPbJMaItQiksyMfv1hoIxnA=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.37.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-m8W2UbxLDcmRKVjgl5J/k4B8d7qX2EcJve3Sut7YGrQoPtCIQGPH5AMzuFvYRWZi0FVS0zEY4c8uttPfX6bwYQ=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.34.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-oIoJRy3ZrdsXpFuWDtzsOOa/E/RbRWXVokpVrNnkS7npz8GEG++E1gYbzhYxhxHbO2om1T26BZjVmdIoyN2WtA=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.37.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-FOMXGmH15OmtQWEt174v9P1JqqhlgYge/bUjIbiVD1nI1NeJ30HYT9SJlZMqdo1uQFyt9cz748F1BHghWaDnVA=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.34.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-X++QSLm4NZfZ3VXGVwyHdRf58IBbCu9ammgJxuWZYLX0du6kZvdNqPwrjvDfwmi6wFdvfZ/s6K7ia0E5kI7m8Q=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.37.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SZMxNttjPKvV14Hjck5t70xS3l63sbVwl98g3FlVVx2YIDmfUIy29jQrsw06ewEYQ8lQSuY9mpAPlmgRD2iSsA=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.34.7", "", { "os": "linux", "cpu": "arm" }, "sha512-Z0TzhrsNqukTz3ISzrvyshQpFnFRfLunYiXxlCRvcrb3nvC5rVKI+ZXPFG/Aa4jhQa1gHgH3A0exHaRRN4VmdQ=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.37.0", "", { "os": "linux", "cpu": "arm" }, "sha512-hhAALKJPidCwZcj+g+iN+38SIOkhK2a9bqtJR+EtyxrKKSt1ynCBeqrQy31z0oWU6thRZzdx53hVgEbRkuI19w=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.34.7", "", { "os": "linux", "cpu": "arm" }, "sha512-nkznpyXekFAbvFBKBy4nNppSgneB1wwG1yx/hujN3wRnhnkrYVugMTCBXED4+Ni6thoWfQuHNYbFjgGH0MBXtw=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.37.0", "", { "os": "linux", "cpu": "arm" }, "sha512-jUb/kmn/Gd8epbHKEqkRAxq5c2EwRt0DqhSGWjPFxLeFvldFdHQs/n8lQ9x85oAeVb6bHcS8irhTJX2FCOd8Ag=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.34.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-KCjlUkcKs6PjOcxolqrXglBDcfCuUCTVlX5BgzgoJHw+1rWH1MCkETLkLe5iLLS9dP5gKC7mp3y6x8c1oGBUtA=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.37.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-oNrJxcQT9IcbcmKlkF+Yz2tmOxZgG9D9GRq+1OE6XCQwCVwxixYAa38Z8qqPzQvzt1FCfmrHX03E0pWoXm1DqA=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.34.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-uFLJFz6+utmpbR313TTx+NpPuAXbPz4BhTQzgaP0tozlLnGnQ6rCo6tLwaSa6b7l6gRErjLicXQ1iPiXzYotjw=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.37.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-pfxLBMls+28Ey2enpX3JvjEjaJMBX5XlPCZNGxj4kdJyHduPBXtxYeb8alo0a7bqOoWZW2uKynhHxF/MWoHaGQ=="], - "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.34.7", "", { "os": "linux", "cpu": "none" }, "sha512-ws8pc68UcJJqCpneDFepnwlsMUFoWvPbWXT/XUrJ7rWUL9vLoIN3GAasgG+nCvq8xrE3pIrd+qLX/jotcLy0Qw=="], + "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.37.0", "", { "os": "linux", "cpu": "none" }, "sha512-yCE0NnutTC/7IGUq/PUHmoeZbIwq3KRh02e9SfFh7Vmc1Z7atuJRYWhRME5fKgT8aS20mwi1RyChA23qSyRGpA=="], - "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.34.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-vrDk9JDa/BFkxcS2PbWpr0C/LiiSLxFbNOBgfbW6P8TBe9PPHx9Wqbvx2xgNi1TOAyQHQJ7RZFqBiEohm79r0w=="], + "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.37.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NxcICptHk06E2Lh3a4Pu+2PEdZ6ahNHuK7o6Np9zcWkrBMuv21j10SQDJW3C9Yf/A/P7cutWoC/DptNLVsZ0VQ=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.34.7", "", { "os": "linux", "cpu": "none" }, "sha512-rB+ejFyjtmSo+g/a4eovDD1lHWHVqizN8P0Hm0RElkINpS0XOdpaXloqM4FBkF9ZWEzg6bezymbpLmeMldfLTw=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.37.0", "", { "os": "linux", "cpu": "none" }, "sha512-PpWwHMPCVpFZLTfLq7EWJWvrmEuLdGn1GMYcm5MV7PaRgwCEYJAwiN94uBuZev0/J/hFIIJCsYw4nLmXA9J7Pw=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.34.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-nNXNjo4As6dNqRn7OrsnHzwTgtypfRA3u3AKr0B3sOOo+HkedIbn8ZtFnB+4XyKJojIfqDKmbIzO1QydQ8c+Pw=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.37.0", "", { "os": "linux", "cpu": "none" }, "sha512-DTNwl6a3CfhGTAOYZ4KtYbdS8b+275LSLqJVJIrPa5/JuIufWWZ/QFvkxp52gpmguN95eujrM68ZG+zVxa8zHA=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.34.7", "", { "os": "linux", "cpu": "x64" }, "sha512-9kPVf9ahnpOMSGlCxXGv980wXD0zRR3wyk8+33/MXQIpQEOpaNe7dEHm5LMfyRZRNt9lMEQuH0jUKj15MkM7QA=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.37.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-hZDDU5fgWvDdHFuExN1gBOhCuzo/8TMpidfOR+1cPZJflcEzXdCy1LjnklQdW8/Et9sryOPJAKAQRw8Jq7Tg+A=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.34.7", "", { "os": "linux", "cpu": "x64" }, "sha512-7wJPXRWTTPtTFDFezA8sle/1sdgxDjuMoRXEKtx97ViRxGGkVQYovem+Q8Pr/2HxiHp74SSRG+o6R0Yq0shPwQ=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.37.0", "", { "os": "linux", "cpu": "x64" }, "sha512-pKivGpgJM5g8dwj0ywBwe/HeVAUSuVVJhUTa/URXjxvoyTT/AxsLTAbkHkDHG7qQxLoW2s3apEIl26uUe08LVQ=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.34.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-MN7aaBC7mAjsiMEZcsJvwNsQVNZShgES/9SzWp1HC9Yjqb5OpexYnRjF7RmE4itbeesHMYYQiAtUAQaSKs2Rfw=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.37.0", "", { "os": "linux", "cpu": "x64" }, "sha512-E2lPrLKE8sQbY/2bEkVTGDEk4/49UYRVWgj90MY8yPjpnGBQ+Xi1Qnr7b7UIWw1NOggdFQFOLZ8+5CzCiz143w=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.34.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-aeawEKYswsFu1LhDM9RIgToobquzdtSc4jSVqHV8uApz4FVvhFl/mKh92wc8WpFc6aYCothV/03UjY6y7yLgbg=="], + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.37.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Jm7biMazjNzTU4PrQtr7VS8ibeys9Pn29/1bm4ph7CP2kf21950LgN+BaE2mJ1QujnvOc6p54eWWiVvn05SOBg=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.34.7", "", { "os": "win32", "cpu": "x64" }, "sha512-4ZedScpxxIrVO7otcZ8kCX1mZArtH2Wfj3uFCxRJ9NO80gg1XV0U/b2f/MKaGwj2X3QopHfoWiDQ917FRpwY3w=="], + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.37.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-e3/1SFm1OjefWICB2Ucstg2dxYDkDTZGDYgwufcbsxTHyqQps1UQf33dFEChBNmeSsTOyrjw2JJq0zbG5GF6RA=="], - "@sinclair/typebox": ["@sinclair/typebox@0.34.22", "", {}, "sha512-0avTcz3XUm6mMcq5tQRoEnxyvmr3uanplFepD+a/TiDzOBZ0Us5bsShW41xOO2kST7AYv4xiCsmE5Ag02yOPfQ=="], + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.37.0", "", { "os": "win32", "cpu": "x64" }, "sha512-LWbXUBwn/bcLx2sSsqy7pK5o+Nr+VCoRoAohfJ5C/aBio9nfJmGQqHAhU6pwxV/RmyTk5AqdySma7uwWGlmeuA=="], + + "@sinclair/typebox": ["@sinclair/typebox@0.34.31", "", {}, "sha512-qQ71T9DsITbX3dVCrcBERbs11YuSMg3wZPnT472JhqhWGPdiLgyvihJXU8m+ADJtJvRdjATIiACJD22dEknBrQ=="], "@types/bun": ["@types/bun@1.2.2", "", { "dependencies": { "bun-types": "1.2.2" } }, "sha512-tr74gdku+AEDN5ergNiBnplr7hpDp3V1h7fqI2GcR/rsUaM39jpSeKH0TFibRvU0KwniRx5POgaYnaXbk0hU+w=="], "@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="], - "@types/node": ["@types/node@22.13.4", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg=="], + "@types/node": ["@types/node@22.13.14", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w=="], "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], - "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], + "acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], @@ -198,7 +200,7 @@ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - "consola": ["consola@3.4.0", "", {}, "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA=="], + "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], "cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="], @@ -212,17 +214,17 @@ "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], - "elysia": ["elysia@1.2.12", "", { "dependencies": { "@sinclair/typebox": "^0.34.15", "cookie": "^1.0.2", "memoirist": "^0.3.0", "openapi-types": "^12.1.3" }, "peerDependencies": { "typescript": ">= 5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-X1bZo09qe8/Poa/5tz08Y+sE/77B/wLwnA5xDDENU3FCrsUtYJuBVcy6BPXGRCgnJ1fPQpc0Ov2ZU5MYJXluTg=="], + "elysia": ["elysia@1.2.25", "", { "dependencies": { "@sinclair/typebox": "^0.34.27", "cookie": "^1.0.2", "memoirist": "^0.3.0", "openapi-types": "^12.1.3" }, "peerDependencies": { "typescript": ">= 5.0.0" }, "optionalPeers": ["typescript"] }, "sha512-WsdQpORJvb4uszzeqYT0lg97knw1iBW1NTzJ1Jm57tiHg+DfAotlWXYbjmvQ039ssV0fYELDHinLLoUazZkEHg=="], "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], - "esbuild": ["esbuild@0.24.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.24.2", "@esbuild/android-arm": "0.24.2", "@esbuild/android-arm64": "0.24.2", "@esbuild/android-x64": "0.24.2", "@esbuild/darwin-arm64": "0.24.2", "@esbuild/darwin-x64": "0.24.2", "@esbuild/freebsd-arm64": "0.24.2", "@esbuild/freebsd-x64": "0.24.2", "@esbuild/linux-arm": "0.24.2", "@esbuild/linux-arm64": "0.24.2", "@esbuild/linux-ia32": "0.24.2", "@esbuild/linux-loong64": "0.24.2", "@esbuild/linux-mips64el": "0.24.2", "@esbuild/linux-ppc64": "0.24.2", "@esbuild/linux-riscv64": "0.24.2", "@esbuild/linux-s390x": "0.24.2", "@esbuild/linux-x64": "0.24.2", "@esbuild/netbsd-arm64": "0.24.2", "@esbuild/netbsd-x64": "0.24.2", "@esbuild/openbsd-arm64": "0.24.2", "@esbuild/openbsd-x64": "0.24.2", "@esbuild/sunos-x64": "0.24.2", "@esbuild/win32-arm64": "0.24.2", "@esbuild/win32-ia32": "0.24.2", "@esbuild/win32-x64": "0.24.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA=="], + "esbuild": ["esbuild@0.25.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.1", "@esbuild/android-arm": "0.25.1", "@esbuild/android-arm64": "0.25.1", "@esbuild/android-x64": "0.25.1", "@esbuild/darwin-arm64": "0.25.1", "@esbuild/darwin-x64": "0.25.1", "@esbuild/freebsd-arm64": "0.25.1", "@esbuild/freebsd-x64": "0.25.1", "@esbuild/linux-arm": "0.25.1", "@esbuild/linux-arm64": "0.25.1", "@esbuild/linux-ia32": "0.25.1", "@esbuild/linux-loong64": "0.25.1", "@esbuild/linux-mips64el": "0.25.1", "@esbuild/linux-ppc64": "0.25.1", "@esbuild/linux-riscv64": "0.25.1", "@esbuild/linux-s390x": "0.25.1", "@esbuild/linux-x64": "0.25.1", "@esbuild/netbsd-arm64": "0.25.1", "@esbuild/netbsd-x64": "0.25.1", "@esbuild/openbsd-arm64": "0.25.1", "@esbuild/openbsd-x64": "0.25.1", "@esbuild/sunos-x64": "0.25.1", "@esbuild/win32-arm64": "0.25.1", "@esbuild/win32-ia32": "0.25.1", "@esbuild/win32-x64": "0.25.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ=="], "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], "eslint": ["eslint@9.6.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/config-array": "^0.17.0", "@eslint/eslintrc": "^3.1.0", "@eslint/js": "9.6.0", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.0.1", "eslint-visitor-keys": "^4.0.0", "espree": "^10.1.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-ElQkdLMEEqQNM9Njff+2Y4q2afHk7JpkPvrd7Xh7xefwgQynqPxwf55J7di9+MEibWUGdNjFF9ITG9Pck5M84w=="], - "eslint-scope": ["eslint-scope@8.2.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A=="], + "eslint-scope": ["eslint-scope@8.3.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ=="], "eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="], @@ -246,7 +248,7 @@ "fast-uri": ["fast-uri@3.0.6", "", {}, "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw=="], - "fastq": ["fastq@1.19.0", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA=="], + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], "fdir": ["fdir@6.4.3", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw=="], @@ -256,9 +258,9 @@ "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], - "flatted": ["flatted@3.3.2", "", {}, "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA=="], + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], - "foreground-child": ["foreground-child@3.3.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" } }, "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg=="], + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -376,11 +378,11 @@ "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], - "reusify": ["reusify@1.0.4", "", {}, "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="], + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], - "rollup": ["rollup@4.34.7", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.34.7", "@rollup/rollup-android-arm64": "4.34.7", "@rollup/rollup-darwin-arm64": "4.34.7", "@rollup/rollup-darwin-x64": "4.34.7", "@rollup/rollup-freebsd-arm64": "4.34.7", "@rollup/rollup-freebsd-x64": "4.34.7", "@rollup/rollup-linux-arm-gnueabihf": "4.34.7", "@rollup/rollup-linux-arm-musleabihf": "4.34.7", "@rollup/rollup-linux-arm64-gnu": "4.34.7", "@rollup/rollup-linux-arm64-musl": "4.34.7", "@rollup/rollup-linux-loongarch64-gnu": "4.34.7", "@rollup/rollup-linux-powerpc64le-gnu": "4.34.7", "@rollup/rollup-linux-riscv64-gnu": "4.34.7", "@rollup/rollup-linux-s390x-gnu": "4.34.7", "@rollup/rollup-linux-x64-gnu": "4.34.7", "@rollup/rollup-linux-x64-musl": "4.34.7", "@rollup/rollup-win32-arm64-msvc": "4.34.7", "@rollup/rollup-win32-ia32-msvc": "4.34.7", "@rollup/rollup-win32-x64-msvc": "4.34.7", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-8qhyN0oZ4x0H6wmBgfKxJtxM7qS98YJ0k0kNh5ECVtuchIJ7z9IVVvzpmtQyT10PXKMtBxYr1wQ5Apg8RS8kXQ=="], + "rollup": ["rollup@4.37.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.37.0", "@rollup/rollup-android-arm64": "4.37.0", "@rollup/rollup-darwin-arm64": "4.37.0", "@rollup/rollup-darwin-x64": "4.37.0", "@rollup/rollup-freebsd-arm64": "4.37.0", "@rollup/rollup-freebsd-x64": "4.37.0", "@rollup/rollup-linux-arm-gnueabihf": "4.37.0", "@rollup/rollup-linux-arm-musleabihf": "4.37.0", "@rollup/rollup-linux-arm64-gnu": "4.37.0", "@rollup/rollup-linux-arm64-musl": "4.37.0", "@rollup/rollup-linux-loongarch64-gnu": "4.37.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.37.0", "@rollup/rollup-linux-riscv64-gnu": "4.37.0", "@rollup/rollup-linux-riscv64-musl": "4.37.0", "@rollup/rollup-linux-s390x-gnu": "4.37.0", "@rollup/rollup-linux-x64-gnu": "4.37.0", "@rollup/rollup-linux-x64-musl": "4.37.0", "@rollup/rollup-win32-arm64-msvc": "4.37.0", "@rollup/rollup-win32-ia32-msvc": "4.37.0", "@rollup/rollup-win32-x64-msvc": "4.37.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-iAtQy/L4QFU+rTJ1YUjXqJOJzuwEghqWzCEYD2FEghT7Gsy1VdABntrO4CLopA5IkflTyqNiLNwPcOJ3S7UKLg=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], @@ -414,7 +416,7 @@ "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], - "tinyglobby": ["tinyglobby@0.2.10", "", { "dependencies": { "fdir": "^6.4.2", "picomatch": "^4.0.2" } }, "sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew=="], + "tinyglobby": ["tinyglobby@0.2.12", "", { "dependencies": { "fdir": "^6.4.3", "picomatch": "^4.0.2" } }, "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww=="], "tr46": ["tr46@1.0.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA=="], @@ -422,13 +424,13 @@ "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], - "tsup": ["tsup@8.3.6", "", { "dependencies": { "bundle-require": "^5.0.0", "cac": "^6.7.14", "chokidar": "^4.0.1", "consola": "^3.2.3", "debug": "^4.3.7", "esbuild": "^0.24.0", "joycon": "^3.1.1", "picocolors": "^1.1.1", "postcss-load-config": "^6.0.1", "resolve-from": "^5.0.0", "rollup": "^4.24.0", "source-map": "0.8.0-beta.0", "sucrase": "^3.35.0", "tinyexec": "^0.3.1", "tinyglobby": "^0.2.9", "tree-kill": "^1.2.2" }, "peerDependencies": { "@microsoft/api-extractor": "^7.36.0", "@swc/core": "^1", "postcss": "^8.4.12", "typescript": ">=4.5.0" }, "optionalPeers": ["@microsoft/api-extractor", "@swc/core", "postcss", "typescript"], "bin": { "tsup": "dist/cli-default.js", "tsup-node": "dist/cli-node.js" } }, "sha512-XkVtlDV/58S9Ye0JxUUTcrQk4S+EqlOHKzg6Roa62rdjL1nGWNUstG0xgI4vanHdfIpjP448J8vlN0oK6XOJ5g=="], + "tsup": ["tsup@8.4.0", "", { "dependencies": { "bundle-require": "^5.1.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "consola": "^3.4.0", "debug": "^4.4.0", "esbuild": "^0.25.0", "joycon": "^3.1.1", "picocolors": "^1.1.1", "postcss-load-config": "^6.0.1", "resolve-from": "^5.0.0", "rollup": "^4.34.8", "source-map": "0.8.0-beta.0", "sucrase": "^3.35.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.11", "tree-kill": "^1.2.2" }, "peerDependencies": { "@microsoft/api-extractor": "^7.36.0", "@swc/core": "^1", "postcss": "^8.4.12", "typescript": ">=4.5.0" }, "optionalPeers": ["@microsoft/api-extractor", "@swc/core", "postcss", "typescript"], "bin": { "tsup": "dist/cli-default.js", "tsup-node": "dist/cli-node.js" } }, "sha512-b+eZbPCjz10fRryaAA7C8xlIHnf8VnsaRqydheLIqwG/Mcpfk8Z5zp3HayX7GaTygkigHl5cBUs+IhcySiIexQ=="], - "tsx": ["tsx@4.19.2", "", { "dependencies": { "esbuild": "~0.23.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g=="], + "tsx": ["tsx@4.19.3", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ=="], "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], - "typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="], + "typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="], "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], @@ -464,8 +466,6 @@ "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - "tsx/esbuild": ["esbuild@0.23.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.23.1", "@esbuild/android-arm": "0.23.1", "@esbuild/android-arm64": "0.23.1", "@esbuild/android-x64": "0.23.1", "@esbuild/darwin-arm64": "0.23.1", "@esbuild/darwin-x64": "0.23.1", "@esbuild/freebsd-arm64": "0.23.1", "@esbuild/freebsd-x64": "0.23.1", "@esbuild/linux-arm": "0.23.1", "@esbuild/linux-arm64": "0.23.1", "@esbuild/linux-ia32": "0.23.1", "@esbuild/linux-loong64": "0.23.1", "@esbuild/linux-mips64el": "0.23.1", "@esbuild/linux-ppc64": "0.23.1", "@esbuild/linux-riscv64": "0.23.1", "@esbuild/linux-s390x": "0.23.1", "@esbuild/linux-x64": "0.23.1", "@esbuild/netbsd-x64": "0.23.1", "@esbuild/openbsd-arm64": "0.23.1", "@esbuild/openbsd-x64": "0.23.1", "@esbuild/sunos-x64": "0.23.1", "@esbuild/win32-arm64": "0.23.1", "@esbuild/win32-ia32": "0.23.1", "@esbuild/win32-x64": "0.23.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg=="], - "wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], "wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], @@ -482,54 +482,6 @@ "string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], - "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.23.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ=="], - - "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.23.1", "", { "os": "android", "cpu": "arm" }, "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ=="], - - "tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.23.1", "", { "os": "android", "cpu": "arm64" }, "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw=="], - - "tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.23.1", "", { "os": "android", "cpu": "x64" }, "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg=="], - - "tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.23.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q=="], - - "tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.23.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw=="], - - "tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.23.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA=="], - - "tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.23.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g=="], - - "tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.23.1", "", { "os": "linux", "cpu": "arm" }, "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ=="], - - "tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.23.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g=="], - - "tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.23.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ=="], - - "tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.23.1", "", { "os": "linux", "cpu": "none" }, "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw=="], - - "tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.23.1", "", { "os": "linux", "cpu": "none" }, "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q=="], - - "tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.23.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw=="], - - "tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.23.1", "", { "os": "linux", "cpu": "none" }, "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA=="], - - "tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.23.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw=="], - - "tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.23.1", "", { "os": "linux", "cpu": "x64" }, "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ=="], - - "tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.23.1", "", { "os": "none", "cpu": "x64" }, "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA=="], - - "tsx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.23.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q=="], - - "tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.23.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA=="], - - "tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.23.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA=="], - - "tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.23.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A=="], - - "tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.23.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ=="], - - "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.23.1", "", { "os": "win32", "cpu": "x64" }, "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg=="], - "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], diff --git a/package.json b/package.json index 7176a95..071cb71 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "json-accelerator", - "version": "0.1.3", + "version": "0.1.4", "description": "Speed up JSON stringification by providing OpenAPI/TypeBox model", "license": "MIT", "scripts": { @@ -11,7 +11,7 @@ "release": "npm run build && npm run test && npm publish --access public" }, "peerDependencies": { - "@sinclair/typebox": "^0.34.15" + "@sinclair/typebox": "^0.34.31" }, "peerDependenciesMeta": { "@sinclair/typebox": { diff --git a/src/index.ts b/src/index.ts index cfac437..28afbce 100644 --- a/src/index.ts +++ b/src/index.ts @@ -180,15 +180,15 @@ const SANITIZE = { const joinStringArray = (p: string) => `"$\{` + - `(()=>{` + - `if(${p}.length===1)return ${p}\n` + + `((p)=>{` + + `if(p.length===1)return p\n` + `let ars=''\n` + - `for(let i=0;i<${p}.length;i++){` + - `if(i===0)ars+=${p}[i]\n` + - `else ars+=\`","\${${p}[i]}\`` + + `for(let i=0;i{` + - `const ar${i}s=Object.keys(${property});` + + `\${((ar${i}n)=>{` + + `const ar${i}s=Object.keys(ar${i}n);` + `let ar${i}v='{';` + `for(let i=0;i{` + if (isRoot) v += `const ar${i}s=${property};` + else v += `\${((ar${i}s)=>{` v += - `const ar${i}s=${property};` + `let ar${i}v='[';` + `for(let i=0;i Date: Thu, 27 Mar 2025 19:06:26 +0700 Subject: [PATCH 06/14] :tada: feat: avoid closure reference --- benchmarks/large.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmarks/large.ts b/benchmarks/large.ts index 38a192e..b10bbdf 100644 --- a/benchmarks/large.ts +++ b/benchmarks/large.ts @@ -10,10 +10,10 @@ benchmark( user: t.Object({ name: t.String(), password: t.String(), - email: t.Optional(t.String({ format: 'email' })), + email: t.Optional(t.String()), age: t.Optional(t.Number()), - avatar: t.Optional(t.String({ format: 'uri' })), - cover: t.Optional(t.String({ format: 'uri' })) + avatar: t.Optional(t.String()), + cover: t.Optional(t.String()) }), playing: t.Optional(t.String()), wishlist: t.Optional(t.Array(t.Number())), From 193eb7fbd84f344a2477997ef3cd82f795456750 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Thu, 27 Mar 2025 20:16:09 +0700 Subject: [PATCH 07/14] :wrench: fix: typebox integer type --- example/index.ts | 17 ++++++----------- src/index.ts | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/example/index.ts b/example/index.ts index 41b4e72..b389168 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,21 +1,16 @@ import { t } from 'elysia' import { createAccelerator } from '../src/index' -const v = t.Module({ - a: t.Object({ - name: t.String(), - job: t.Optional(t.Ref('job')), - trait: t.Optional(t.String()) - }), - job: t.Number() +const shape = t.Object({ + name: t.String(), + playing: t.Nullable(t.Integer({ default: 1 })) }) -const shape = v.Import('a') +console.log(t.Optional(t.String())) const value = { - name: 'Jane Doe', - job: 'Software Engineer', - trait: 'Friendly' + name: 'saltyaom', + playing: null } satisfies typeof shape.static const mirror = createAccelerator(shape) diff --git a/src/index.ts b/src/index.ts index 28afbce..e133cd0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,7 +36,7 @@ const isInteger = (schema: TAnySchema) => { continue } - if (!hasNumberType && type.type === 'number') { + if (!hasNumberType && (type.type === 'number' || type.type === 'integer')) { hasNumberType = true continue } From 4f06583cd81906a0adb228de0ef289ef7258c2f2 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Tue, 22 Apr 2025 18:41:45 +0700 Subject: [PATCH 08/14] :tada: feat: add schema.trusted for string --- CHANGELOG.md | 7 ++++++- benchmarks/message.ts | 11 +++++++++++ example/index.ts | 10 ++++------ package.json | 4 ++-- src/index.ts | 22 +++++++++++++++++----- 5 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 benchmarks/message.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ed5600a..e869b84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ +# 0.1.5 - 22 Apr 2025 +Improvement: +- add `schema.trusted` for string +- improve string placement when using `t.String({ trusted: true })` and `sanitize: manual` + # 0.1.4 - 27 Mar 2025 Improvement: -- Improve array performance by avoiding unnecessary closure reference +- improve array performance by avoiding unnecessary closure reference # 0.1.3 - 14 Mar 2025 Bug fix: diff --git a/benchmarks/message.ts b/benchmarks/message.ts new file mode 100644 index 0000000..222c9b6 --- /dev/null +++ b/benchmarks/message.ts @@ -0,0 +1,11 @@ +import { t } from 'elysia' +import { benchmark } from './utils' + +benchmark( + t.Object({ + message: t.String({ + trusted: true + }) + }), + { message: 'Hello, World!' as const } +) diff --git a/example/index.ts b/example/index.ts index b389168..c21541f 100644 --- a/example/index.ts +++ b/example/index.ts @@ -2,15 +2,13 @@ import { t } from 'elysia' import { createAccelerator } from '../src/index' const shape = t.Object({ - name: t.String(), - playing: t.Nullable(t.Integer({ default: 1 })) + message: t.String({ + trusted: true + }) }) -console.log(t.Optional(t.String())) - const value = { - name: 'saltyaom', - playing: null + message: 'a' } satisfies typeof shape.static const mirror = createAccelerator(shape) diff --git a/package.json b/package.json index 071cb71..618805f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "json-accelerator", - "version": "0.1.4", + "version": "0.1.5", "description": "Speed up JSON stringification by providing OpenAPI/TypeBox model", "license": "MIT", "scripts": { @@ -11,7 +11,7 @@ "release": "npm run build && npm run test && npm publish --access public" }, "peerDependencies": { - "@sinclair/typebox": "^0.34.31" + "@sinclair/typebox": ">= 0.34.0" }, "peerDependenciesMeta": { "@sinclair/typebox": { diff --git a/src/index.ts b/src/index.ts index e133cd0..94cebc9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,7 +36,10 @@ const isInteger = (schema: TAnySchema) => { continue } - if (!hasNumberType && (type.type === 'number' || type.type === 'integer')) { + if ( + !hasNumberType && + (type.type === 'number' || type.type === 'integer') + ) { hasNumberType = true continue } @@ -257,7 +260,7 @@ const accelerate = ( switch (schema.type) { case 'string': - instruction.hasString = true + if (!schema.const && !schema.trusted) instruction.hasString = true // string operation would be repeated multiple time // it's fine to optimize it to the most optimized way @@ -272,10 +275,19 @@ const accelerate = ( // this handle the case where the string contains double quotes // As slice(1,-1) is use several compute and would be called multiple times // it's not ideal to slice(1, -1) of JSON.stringify - if (nullableCondition) + if (nullableCondition) { + if (schema.trusted) + sanitize = (v: string) => + `\`"$\{${SANITIZE['manual'](v)}}"\`` + v = `\${${nullableCondition}?${schema.const !== undefined ? `'${JSON.stringify(schema.const)}'` : schema.default !== undefined ? `'${JSON.stringify(schema.default)}'` : `'null'`}:${sanitize(property)}}` - else - v = `${schema.const !== undefined ? `${JSON.stringify(schema.const)}` : `\${${sanitize(property)}}`}` + } else { + if (schema.const !== undefined) + v = JSON.stringify(schema.const) + else if (schema.trusted) + v = `"\${${SANITIZE['manual'](property)}}"` + else v = `\${${sanitize(property)}}` + } } else { // In this case quote is handle outside to improve performance if (nullableCondition) From 16aadb5973462a18d735e2883402a08ea5d3c848 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Thu, 24 Apr 2025 23:39:29 +0700 Subject: [PATCH 09/14] :tada: feat: improve string operation --- CHANGELOG.md | 5 +++++ benchmarks/utils.ts | 14 +++++++------- bun.lock | 2 +- example/index.ts | 10 ++++++---- src/index.ts | 21 +++++++++------------ 5 files changed, 28 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e869b84..11fd38e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 0.1.6 - 24 Apr 2025 +Improvement: +- reduce instruction for string placement +- inline regex test to each string + # 0.1.5 - 22 Apr 2025 Improvement: - add `schema.trusted` for string diff --git a/benchmarks/utils.ts b/benchmarks/utils.ts index 37c0809..98ec118 100644 --- a/benchmarks/utils.ts +++ b/benchmarks/utils.ts @@ -1,4 +1,4 @@ -import { bench, run, barplot, summary, compact } from 'mitata' +import { bench, run, barplot, summary, compact, do_not_optimize } from 'mitata' import { createAccelerator } from '../src' import { TypeCompiler } from '@sinclair/typebox/compiler' @@ -28,7 +28,7 @@ export const benchmark = ( compact(() => { barplot(() => { summary(() => { - bench('JSON Stingify', () => { + bench('JSON Stringify', () => { return JSON.stringify(value) }) @@ -40,13 +40,13 @@ export const benchmark = ( return encode(value) }) - const validator = TypeCompiler.Compile(model) + // const validator = TypeCompiler.Compile(model) - bench('JSON Accelerator w/ validation', () => { - validator.Check(value) + // bench('JSON Accelerator w/ validation', () => { + // validator.Check(value) - return encode(value) - }) + // return encode(value) + // }) }) }) }) diff --git a/bun.lock b/bun.lock index 7216f9e..9a6a936 100644 --- a/bun.lock +++ b/bun.lock @@ -14,7 +14,7 @@ "typescript": "^5.5.3", }, "peerDependencies": { - "@sinclair/typebox": "^0.34.31", + "@sinclair/typebox": ">= 0.34.0", }, "optionalPeers": [ "@sinclair/typebox", diff --git a/example/index.ts b/example/index.ts index c21541f..fee8d56 100644 --- a/example/index.ts +++ b/example/index.ts @@ -2,15 +2,17 @@ import { t } from 'elysia' import { createAccelerator } from '../src/index' const shape = t.Object({ - message: t.String({ - trusted: true + name: t.String({ + // trusted: true }) }) +const string = `hi awd` + const value = { - message: 'a' + name: string } satisfies typeof shape.static const mirror = createAccelerator(shape) -console.log(mirror(value)) +console.log(JSON.parse(mirror(value))) diff --git a/src/index.ts b/src/index.ts index 94cebc9..d638f9c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -156,7 +156,6 @@ const isDateType = (schema: TAnySchema): boolean => { interface Instruction { array: number optional: number - hasString: boolean properties: string[] /** * If unsafe character is found, how should the encoder handle it? @@ -173,12 +172,15 @@ interface Instruction { definitions: Record } +// equivalent to /["\n\r\t\b\f\v]/ +const findEscapeSequence = /["\b\t\n\v\f\r\/]/ + const SANITIZE = { auto: (property: string) => - `re.test(${property})?JSON.stringify(${property}):\`"$\{${property}}"\``, + `${findEscapeSequence}.test(${property})?JSON.stringify(${property}).slice(1,-1):${property}`, manual: (property: string) => `${property}`, throw: (property: string) => - `re.test(${property})?(()=>{throw new Error("Property '${property}' contains invalid characters")})():${property}` + `${findEscapeSequence}.test(${property})?(()=>{throw new Error("Property '${property}' contains invalid characters")})():${property}` } satisfies Record string> const joinStringArray = (p: string) => @@ -260,8 +262,6 @@ const accelerate = ( switch (schema.type) { case 'string': - if (!schema.const && !schema.trusted) instruction.hasString = true - // string operation would be repeated multiple time // it's fine to optimize it to the most optimized way if ( @@ -280,13 +280,13 @@ const accelerate = ( sanitize = (v: string) => `\`"$\{${SANITIZE['manual'](v)}}"\`` - v = `\${${nullableCondition}?${schema.const !== undefined ? `'${JSON.stringify(schema.const)}'` : schema.default !== undefined ? `'${JSON.stringify(schema.default)}'` : `'null'`}:${sanitize(property)}}` + v = `\${${nullableCondition}?${schema.const !== undefined ? `'${JSON.stringify(schema.const)}'` : schema.default !== undefined ? `'${JSON.stringify(schema.default)}'` : `'null'`}:\`"\${${sanitize(property)}}"\`}` } else { if (schema.const !== undefined) v = JSON.stringify(schema.const) else if (schema.trusted) v = `"\${${SANITIZE['manual'](property)}}"` - else v = `\${${sanitize(property)}}` + else v = `"\${${sanitize(property)}}"` } } else { // In this case quote is handle outside to improve performance @@ -356,7 +356,8 @@ const accelerate = ( const name = joinProperty(property, key) const hasShortName = schema.properties[key].type === 'object' && - !name.startsWith('ar') + !name.startsWith('ar') && + Object.keys(schema.properties).length > 5 const i = instruction.properties.length if (hasShortName) instruction.properties.push(name) @@ -487,9 +488,6 @@ const accelerate = ( let setup = '' - if (instruction.hasString) - setup += `const re=/[\\b\\f\\n\\r\\t\\\\\\\\/"]/\n` - if (instruction.optional) { setup += 'let ' @@ -528,7 +526,6 @@ export const createAccelerator = ( array: 0, optional: 0, properties: [], - hasString: false, sanitize, definitions }) From 33d5a64179b3a8c73fd49f9abdda922465ad21d5 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Thu, 24 Apr 2025 23:44:28 +0700 Subject: [PATCH 10/14] :tada: feat: improve string operation --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 618805f..43bef97 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "json-accelerator", - "version": "0.1.5", + "version": "0.1.6", "description": "Speed up JSON stringification by providing OpenAPI/TypeBox model", "license": "MIT", "scripts": { From a7c3d428d4a6552738fd8cef5a55518f66c432de Mon Sep 17 00:00:00 2001 From: saltyaom Date: Fri, 25 Apr 2025 01:03:43 +0700 Subject: [PATCH 11/14] :broom: chore: update dependency --- benchmarks/utils.ts | 10 +++++----- bun.lock | 16 +++++++--------- package.json | 10 +++++----- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/benchmarks/utils.ts b/benchmarks/utils.ts index 98ec118..83eae1f 100644 --- a/benchmarks/utils.ts +++ b/benchmarks/utils.ts @@ -28,16 +28,16 @@ export const benchmark = ( compact(() => { barplot(() => { summary(() => { - bench('JSON Stringify', () => { - return JSON.stringify(value) + bench('JSON Accelerator', () => { + return encode(value) }) - bench('Fast Json Stringify', () => { + bench('Fast JSON Stringify', () => { return fastJsonStringify(value) }) - bench('JSON Accelerator', () => { - return encode(value) + bench('JSON Stringify', () => { + return JSON.stringify(value) }) // const validator = TypeCompiler.Compile(model) diff --git a/bun.lock b/bun.lock index 9a6a936..3d28ba1 100644 --- a/bun.lock +++ b/bun.lock @@ -4,14 +4,14 @@ "": { "name": "json-accelerator", "devDependencies": { - "@types/bun": "1.2.2", - "elysia": "^1.2.11", + "@types/bun": "1.2.10", + "elysia": "^1.2.25", "eslint": "9.6.0", "fast-json-stringify": "^6.0.1", - "mitata": "^1.0.33", - "tsup": "^8.1.0", + "mitata": "^1.0.34", + "tsup": "^8.4.0", "tsx": "^4.19.2", - "typescript": "^5.5.3", + "typescript": "^5.8.2", }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0", @@ -152,14 +152,12 @@ "@sinclair/typebox": ["@sinclair/typebox@0.34.31", "", {}, "sha512-qQ71T9DsITbX3dVCrcBERbs11YuSMg3wZPnT472JhqhWGPdiLgyvihJXU8m+ADJtJvRdjATIiACJD22dEknBrQ=="], - "@types/bun": ["@types/bun@1.2.2", "", { "dependencies": { "bun-types": "1.2.2" } }, "sha512-tr74gdku+AEDN5ergNiBnplr7hpDp3V1h7fqI2GcR/rsUaM39jpSeKH0TFibRvU0KwniRx5POgaYnaXbk0hU+w=="], + "@types/bun": ["@types/bun@1.2.10", "", { "dependencies": { "bun-types": "1.2.10" } }, "sha512-eilv6WFM3M0c9ztJt7/g80BDusK98z/FrFwseZgT4bXCq2vPhXD4z8R3oddmAn+R/Nmz9vBn4kweJKmGTZj+lg=="], "@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="], "@types/node": ["@types/node@22.13.14", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w=="], - "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], - "acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], @@ -180,7 +178,7 @@ "brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], - "bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="], + "bun-types": ["bun-types@1.2.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-b5ITZMnVdf3m1gMvJHG+gIfeJHiQPJak0f7925Hxu6ZN5VKA8AGy4GZ4lM+Xkn6jtWxg5S3ldWvfmXdvnkp3GQ=="], "bundle-require": ["bundle-require@5.1.0", "", { "dependencies": { "load-tsconfig": "^0.2.3" }, "peerDependencies": { "esbuild": ">=0.18" } }, "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA=="], diff --git a/package.json b/package.json index 43bef97..3d92b78 100644 --- a/package.json +++ b/package.json @@ -19,14 +19,14 @@ } }, "devDependencies": { - "@types/bun": "1.2.2", - "elysia": "^1.2.11", + "@types/bun": "1.2.10", + "elysia": "^1.2.25", "eslint": "9.6.0", "fast-json-stringify": "^6.0.1", - "mitata": "^1.0.33", - "tsup": "^8.1.0", + "mitata": "^1.0.34", + "tsup": "^8.4.0", "tsx": "^4.19.2", - "typescript": "^5.5.3" + "typescript": "^5.8.2" }, "main": "./dist/cjs/index.js", "module": "./dist/index.mjs", From 2ee87b3ba5ccf0bb71536fba9d0c909c153299aa Mon Sep 17 00:00:00 2001 From: saltyaom Date: Fri, 25 Apr 2025 01:53:44 +0700 Subject: [PATCH 12/14] :tada: feat: improve array operation --- CHANGELOG.md | 4 ++++ src/index.ts | 13 ++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11fd38e..5dd9c07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.1.7 +Improvement: +- improve array operation + # 0.1.6 - 24 Apr 2025 Improvement: - reduce instruction for string placement diff --git a/src/index.ts b/src/index.ts index d638f9c..49564cf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -187,10 +187,9 @@ const joinStringArray = (p: string) => `"$\{` + `((p)=>{` + `if(p.length===1)return p\n` + - `let ars=''\n` + - `for(let i=0;i Date: Fri, 25 Apr 2025 06:35:53 +0700 Subject: [PATCH 13/14] :tada: feat: improve array operation --- CHANGELOG.md | 2 +- example/index.ts | 23 +++++++++++++---------- package.json | 2 +- src/index.ts | 5 +++-- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dd9c07..6c0385a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# 0.1.7 +# 0.1.7 - 25 Apr 2025 Improvement: - improve array operation diff --git a/example/index.ts b/example/index.ts index fee8d56..baf8e21 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,18 +1,21 @@ import { t } from 'elysia' import { createAccelerator } from '../src/index' -const shape = t.Object({ - name: t.String({ - // trusted: true +const shape = t.Array( + t.Object({ + name: t.String() }) -}) +) -const string = `hi awd` - -const value = { - name: string -} satisfies typeof shape.static +const value = [ + { + name: 'a' + }, + { + name: 'b' + } +] satisfies typeof shape.static const mirror = createAccelerator(shape) -console.log(JSON.parse(mirror(value))) +console.log((mirror(value))) diff --git a/package.json b/package.json index 3d92b78..03f482b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "json-accelerator", - "version": "0.1.6", + "version": "0.1.7", "description": "Speed up JSON stringification by providing OpenAPI/TypeBox model", "license": "MIT", "scripts": { diff --git a/src/index.ts b/src/index.ts index 49564cf..40eca96 100644 --- a/src/index.ts +++ b/src/index.ts @@ -355,8 +355,7 @@ const accelerate = ( const name = joinProperty(property, key) const hasShortName = schema.properties[key].type === 'object' && - !name.startsWith('ar') && - Object.keys(schema.properties).length > 5 + !name.startsWith('ar') const i = instruction.properties.length if (hasShortName) instruction.properties.push(name) @@ -529,6 +528,8 @@ export const createAccelerator = ( definitions }) + console.log(f) + return Function('v', f) as any } From 7fcbf62c8a627cb87c89cf660438808a8128f837 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Fri, 25 Apr 2025 06:36:01 +0700 Subject: [PATCH 14/14] :tada: feat: improve array operation --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 40eca96..7d874bc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -528,7 +528,7 @@ export const createAccelerator = ( definitions }) - console.log(f) + // console.log(f) return Function('v', f) as any }