├── .eslintrc.js ├── .github ├── FUNDING.yaml ├── ISSUE_TEMPLATE │ ├── 2-bug-report.yml │ ├── 3-feature-request.yml │ └── config.yml ├── dependabot.yml └── workflows │ ├── ci.yml │ └── publish.yml ├── .gitignore ├── .npmignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── benchmarks ├── array.ts ├── large.ts ├── medium.ts ├── multiple-queries.ts ├── small.ts ├── stuff.ts └── utils.ts ├── build.ts ├── bun.lock ├── example └── index.ts ├── package.json ├── src └── index.ts ├── test ├── array.test.ts ├── index.test.ts ├── node │ ├── .gitignore │ ├── cjs │ │ ├── index.js │ │ └── package.json │ └── esm │ │ ├── index.js │ │ └── package.json ├── optional.test.ts ├── record.test.ts ├── recursive.test.ts ├── ref.test.ts ├── sample.test.ts ├── sanitize.test.ts ├── tuple.test.ts ├── union.test.ts └── utils.ts ├── tsconfig.dts.json └── tsconfig.json /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "es2021": true, 4 | "node": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:@typescript-eslint/recommended" 9 | ], 10 | "parser": "@typescript-eslint/parser", 11 | "parserOptions": { 12 | "ecmaVersion": "latest", 13 | "sourceType": "module" 14 | }, 15 | "plugins": [ 16 | "@typescript-eslint" 17 | ], 18 | "rules": { 19 | "@typescript-eslint/ban-types": 'off', 20 | '@typescript-eslint/no-explicit-any': 'off' 21 | }, 22 | "ignorePatterns": ["example/*", "tests/**/*"] 23 | } 24 | -------------------------------------------------------------------------------- /.github/FUNDING.yaml: -------------------------------------------------------------------------------- 1 | github: SaltyAom -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2-bug-report.yml: -------------------------------------------------------------------------------- 1 | name: 🐛 Bug Report 2 | description: Report an issue that should be fixed 3 | labels: [bug] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thank you for submitting a bug report. It helps make Elysia.JS better. 9 | 10 | If you need help or support using Elysia.JS, and are not reporting a bug, please 11 | head over to Q&A discussions [Discussions](https://github.com/elysiajs/elysia/discussions/categories/q-a), where you can ask questions in the Q&A forum. 12 | 13 | Make sure you are running the version of Elysia.JS and Bun.Sh 14 | The bug you are experiencing may already have been fixed. 15 | 16 | Please try to include as much information as possible. 17 | 18 | - type: input 19 | attributes: 20 | label: What version of Elysia is running? 21 | description: Copy the output of `Elysia --revision` 22 | - type: input 23 | attributes: 24 | label: What platform is your computer? 25 | description: | 26 | For MacOS and Linux: copy the output of `uname -mprs` 27 | For Windows: copy the output of `"$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })"` in the PowerShell console 28 | - type: textarea 29 | attributes: 30 | label: What steps can reproduce the bug? 31 | description: Explain the bug and provide a code snippet that can reproduce it. 32 | validations: 33 | required: true 34 | - type: textarea 35 | attributes: 36 | label: What is the expected behavior? 37 | description: If possible, please provide text instead of a screenshot. 38 | - type: textarea 39 | attributes: 40 | label: What do you see instead? 41 | description: If possible, please provide text instead of a screenshot. 42 | - type: textarea 43 | attributes: 44 | label: Additional information 45 | description: Is there anything else you think we should know? 46 | - type: input 47 | attributes: 48 | label: Have you try removing the `node_modules` and `bun.lockb` and try again yet? 49 | description: rm -rf node_modules && bun.lockb 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3-feature-request.yml: -------------------------------------------------------------------------------- 1 | name: 🚀 Feature Request 2 | description: Suggest an idea, feature, or enhancement 3 | labels: [enhancement] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thank you for submitting an idea. It helps make Elysia.JS better. 9 | 10 | If you want to discuss Elysia.JS, or learn how others are using Elysia.JS, please 11 | head to our [Discord](https://discord.com/invite/y7kH46ZE) server, where you can chat among the community. 12 | - type: textarea 13 | attributes: 14 | label: What is the problem this feature would solve? 15 | validations: 16 | required: true 17 | - type: textarea 18 | attributes: 19 | label: What is the feature you are proposing to solve the problem? 20 | validations: 21 | required: true 22 | - type: textarea 23 | attributes: 24 | label: What alternatives have you considered? 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: 📗 Documentation Issue 4 | url: https://github.com/elysiajs/documentation/issues/new/choose 5 | about: Head over to our Documentation repository! 6 | - name: 💬 Ask a Question 7 | url: https://discord.gg/eaFJ2KDJck 8 | about: Head over to our Discord! 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'npm' 4 | directory: './' 5 | schedule: 6 | interval: 'daily' 7 | 8 | - package-ecosystem: 'github-actions' 9 | directory: './' 10 | schedule: 11 | interval: 'daily' 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | build: 9 | name: Build and test code 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | 16 | - name: Setup bun 17 | uses: oven-sh/setup-bun@v1 18 | with: 19 | bun-version: latest 20 | 21 | - name: Install packages 22 | run: bun install 23 | 24 | - name: Build code 25 | run: bun run build 26 | 27 | - name: Test 28 | run: bun run test -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | defaults: 8 | run: 9 | shell: bash 10 | 11 | permissions: 12 | id-token: write 13 | 14 | env: 15 | # Enable debug logging for actions 16 | ACTIONS_RUNNER_DEBUG: true 17 | 18 | jobs: 19 | publish-npm: 20 | name: 'Publish: npm Registry' 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: 'Checkout' 24 | uses: actions/checkout@v4 25 | 26 | - name: 'Setup Bun' 27 | uses: oven-sh/setup-bun@v1 28 | with: 29 | bun-version: latest 30 | registry-url: "https://registry.npmjs.org" 31 | 32 | - uses: actions/setup-node@v4 33 | with: 34 | node-version: '20.x' 35 | registry-url: 'https://registry.npmjs.org' 36 | 37 | - name: Install packages 38 | run: bun install 39 | 40 | - name: Build code 41 | run: bun run build 42 | 43 | - name: Test 44 | run: bun run test 45 | 46 | - name: 'Publish' 47 | env: 48 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 49 | run: | 50 | npm publish --provenance --access=public 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | node_modules 4 | .pnpm-debug.log 5 | dist 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | .gitignore 4 | .prettierrc 5 | .cjs.swcrc 6 | .es.swcrc 7 | bun.lockb 8 | bun.lock 9 | 10 | node_modules 11 | tsconfig.json 12 | pnpm-lock.yaml 13 | jest.config.js 14 | nodemon.json 15 | benchmarks 16 | 17 | example 18 | tests 19 | test 20 | CHANGELOG.md 21 | .eslintrc.js 22 | tsconfig.cjs.json 23 | tsconfig.esm.json 24 | tsconfig.dts.json 25 | 26 | build.ts 27 | src 28 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": true, 6 | "trailingComma": "none" 7 | } 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.1.2 - 8 May Apr 2025 2 | Feature: 3 | - add `removeUnknownUnionType` 4 | 5 | # 0.1.1 - 24 Apr 2025 6 | Bug fix: 7 | - handle reference of an union in an array 8 | 9 | # 0.1.0 - 24 Apr 2025 10 | Improvement: 11 | - handle recursion from t.Ref and t.Recursive 12 | 13 | # 0.0.9 - 24 Apr 2025 14 | Bug fix: 15 | - handle sanitize on top-level string 16 | 17 | # 0.0.8 - 24 Apr 2025 18 | Feature: 19 | - Add sanitize options 20 | 21 | # 0.0.7 - 22 Apr 2025 22 | Improvement: 23 | - Use ?. to access undefined property 24 | 25 | # 0.0.6 - 27 Mar 2025 26 | Improvement: 27 | - Improve array performance by avoiding unnecessary closure reference 28 | 29 | # 0.0.5 - 5 Mar 2025 30 | Feature: 31 | - support `t.Module`, `t.Ref` 32 | 33 | # 0.0.4 - 4 Mar 2025 34 | Bug fix: 35 | - handle undefined union 36 | 37 | # 0.0.3 - 4 Mar 2025 38 | Bug fix: 39 | - handle root array 40 | 41 | # 0.0.2 - 4 Mar 2025 42 | Feature: 43 | - support Record, Tuple, Union 44 | 45 | # 0.0.1 - 4 Mar 2025 46 | Bug fix: 47 | - incorrect array bracket limit 48 | - handle deep nested optional object property 49 | - using pointer instead of created value for 50 | 51 | # 0.0.0 - 4 Mar 2025 52 | Feature: 53 | - initial release 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 saltyAom 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Exact Mirror 2 | 3 | Enforce value to TypeBox/OpenAPI model 4 | 5 | By providing model ahead of time, the library will generate a function to mirror a value to an exact type 6 | 7 | ``` 8 | $ bun benchmarks/small 9 | 10 | clk: ~3.13 GHz 11 | cpu: Apple M1 Max 12 | runtime: bun 1.2.4 (arm64-darwin) 13 | 14 | summary 15 | Exact Mirror 16 | 556.23x faster than TypeBox Value.Clean 17 | ``` 18 | 19 | ## Installation 20 | 21 | ```bash 22 | # Using either one of the package manager 23 | npm install exact-mirror 24 | yarn add exact-mirror 25 | pnpm add exact-mirror 26 | bun add exact-mirror 27 | ``` 28 | 29 | ## Usage 30 | 31 | It is designed to be used with [TypeBox](https://github.com/sinclairzx81/typebox) but an OpenAPI schema should also work. 32 | 33 | ```typescript 34 | import { Type as t } from '@sinclair/typebox' 35 | import { createMirror } from 'exact-mirror' 36 | 37 | const shape = t.Object({ 38 | name: t.String(), 39 | id: t.Number() 40 | }) 41 | 42 | const value = { 43 | id: 0, 44 | name: 'saltyaom', 45 | // @ts-expect-error 46 | shoudBeRemoved: true 47 | } satisfies typeof shape.static 48 | 49 | const mirror = createMirror(shape) 50 | 51 | console.log(mirror(value)) // {"id":0,"name":"saltyaom"} 52 | ``` 53 | -------------------------------------------------------------------------------- /benchmarks/array.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | import { benchmark } from './utils' 3 | 4 | benchmark( 5 | t.Object({ 6 | ids: t.Array(t.Number()), 7 | names: t.Array(t.String()), 8 | games: t.Array( 9 | t.Object({ 10 | name: t.String(), 11 | tags: t.Array(t.String()) 12 | }) 13 | ) 14 | }), 15 | { 16 | ids: [1, 2, 3], 17 | names: ['SaltyAom', 'SaltyAom', 'SaltyAom'], 18 | games: [ 19 | { 20 | name: 'MiSide', 21 | tags: ['Psychological Horror', 'Cute', 'Dating Sim'] 22 | }, 23 | { 24 | name: 'Strinova', 25 | tags: ['Free to Play', 'Anime', 'Third-Person Shooter'] 26 | }, 27 | { 28 | name: "Tom Clancy's Rainbow Six Siege", 29 | tags: ['FPS', 'Multiplayer', 'Tactical'] 30 | } 31 | ] 32 | } 33 | ) 34 | -------------------------------------------------------------------------------- /benchmarks/large.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | import { benchmark } from './utils' 3 | 4 | benchmark( 5 | t.Array( 6 | t.Object({ 7 | id: t.Number(), 8 | name: t.String(), 9 | bio: t.String(), 10 | user: t.Object({ 11 | name: t.String(), 12 | password: t.String(), 13 | email: t.Optional(t.String({ format: 'email' })), 14 | age: t.Optional(t.Number()), 15 | avatar: t.Optional(t.String({ format: 'uri' })), 16 | cover: t.Optional(t.String({ format: 'uri' })) 17 | }), 18 | playing: t.Optional(t.String()), 19 | wishlist: t.Optional(t.Array(t.Number())), 20 | games: t.Array( 21 | t.Object({ 22 | id: t.Number(), 23 | name: t.String(), 24 | hoursPlay: t.Optional(t.Number({ default: 0 })), 25 | tags: t.Array( 26 | t.Object({ 27 | name: t.String(), 28 | count: t.Number() 29 | }) 30 | ) 31 | }) 32 | ), 33 | metadata: t.Intersect([ 34 | t.Object({ 35 | alias: t.String() 36 | }), 37 | t.Object({ 38 | country: t.Nullable(t.String()), 39 | region: t.Optional(t.String()) 40 | }) 41 | ]), 42 | social: t.Optional( 43 | t.Object({ 44 | facebook: t.Optional(t.String()), 45 | twitter: t.Optional(t.String()), 46 | youtube: t.Optional(t.String()) 47 | }) 48 | ) 49 | }) 50 | ), 51 | [ 52 | { 53 | id: 1, 54 | name: 'SaltyAom', 55 | bio: 'I like train', 56 | user: { 57 | name: 'SaltyAom', 58 | password: '123456', 59 | avatar: 'https://avatars.githubusercontent.com/u/35027979?v=4', 60 | cover: 'https://saltyaom.com/cosplay/pekomama.webp' 61 | }, 62 | playing: 'Strinova', 63 | wishlist: [4_154_456, 2_345_345], 64 | games: [ 65 | { 66 | id: 4_154_456, 67 | name: 'MiSide', 68 | hoursPlay: 17, 69 | tags: [ 70 | { name: 'Psychological Horror', count: 236_432 }, 71 | { name: 'Cute', count: 495_439 }, 72 | { name: 'Dating Sim', count: 395_532 } 73 | ] 74 | }, 75 | { 76 | id: 4_356_345, 77 | name: 'Strinova', 78 | hoursPlay: 365, 79 | tags: [ 80 | { name: 'Free to Play', count: 205_593 }, 81 | { name: 'Anime', count: 504_304 }, 82 | { name: 'Third-Person Shooter', count: 395_532 } 83 | ] 84 | }, 85 | { 86 | id: 2_345_345, 87 | name: "Tom Clancy's Rainbow Six Siege", 88 | hoursPlay: 287, 89 | tags: [ 90 | { name: 'FPS', count: 855_324 }, 91 | { name: 'Multiplayer', count: 456_567 }, 92 | { name: 'Tactical', count: 544_467 } 93 | ] 94 | } 95 | ], 96 | metadata: { 97 | alias: 'SaltyAom', 98 | country: 'Thailand', 99 | region: 'Asia' 100 | }, 101 | social: { 102 | twitter: 'SaltyAom' 103 | } 104 | }, 105 | { 106 | id: 2, 107 | name: 'VLost', 108 | bio: 'ไม่พี่คืองี้', 109 | user: { 110 | name: 'nattapon_kub', 111 | password: '123456' 112 | }, 113 | games: [ 114 | { 115 | id: 4_154_456, 116 | name: 'MiSide', 117 | hoursPlay: 17, 118 | tags: [ 119 | { name: 'Psychological Horror', count: 236_432 }, 120 | { name: 'Cute', count: 495_439 }, 121 | { name: 'Dating Sim', count: 395_532 } 122 | ] 123 | }, 124 | { 125 | id: 4_356_345, 126 | name: 'Strinova', 127 | hoursPlay: 365, 128 | tags: [ 129 | { name: 'Free to Play', count: 205_593 }, 130 | { name: 'Anime', count: 504_304 }, 131 | { name: 'Third-Person Shooter', count: 395_532 } 132 | ] 133 | } 134 | ], 135 | metadata: { 136 | alias: 'vlost', 137 | country: 'Thailand' 138 | } 139 | }, 140 | { 141 | id: 2, 142 | name: 'eika', 143 | bio: 'こんにちわ!', 144 | user: { 145 | name: 'ei_ka', 146 | password: '123456' 147 | }, 148 | games: [ 149 | { 150 | id: 4_356_345, 151 | name: 'Strinova', 152 | hoursPlay: 365, 153 | tags: [ 154 | { name: 'Free to Play', count: 205_593 }, 155 | { name: 'Anime', count: 504_304 }, 156 | { name: 'Third-Person Shooter', count: 395_532 } 157 | ] 158 | } 159 | ], 160 | metadata: { 161 | alias: 'eika', 162 | country: 'Japan' 163 | } 164 | } 165 | ] 166 | ) 167 | -------------------------------------------------------------------------------- /benchmarks/medium.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | import { benchmark } from './utils' 3 | 4 | benchmark( 5 | t.Object({ 6 | id: t.Number(), 7 | name: t.Literal('SaltyAom'), 8 | bio: t.String({ 9 | sanitize: true 10 | }), 11 | user: t.Object({ 12 | name: t.String(), 13 | password: t.String() 14 | }), 15 | playing: t.Optional(t.String()), 16 | games: t.Array( 17 | t.Object({ 18 | name: t.String(), 19 | hoursPlay: t.Number({ default: 0 }), 20 | tags: t.Array(t.String()) 21 | }) 22 | ), 23 | metadata: t.Intersect([ 24 | t.Object({ 25 | alias: t.String() 26 | }), 27 | t.Object({ 28 | country: t.Nullable(t.String()) 29 | }) 30 | ]), 31 | social: t.Optional( 32 | t.Object({ 33 | facebook: t.Optional(t.String()), 34 | twitter: t.Optional(t.String()), 35 | youtube: t.Optional(t.String()) 36 | }) 37 | ) 38 | }), 39 | { 40 | id: 1, 41 | name: 'SaltyAom', 42 | bio: 'I like train\nhere', 43 | user: { 44 | name: 'SaltyAom', 45 | password: '123456' 46 | }, 47 | games: [ 48 | { 49 | name: 'MiSide', 50 | hoursPlay: 17, 51 | tags: ['Psychological Horror', 'Cute', 'Dating Sim'] 52 | }, 53 | { 54 | name: 'Strinova', 55 | hoursPlay: 365, 56 | tags: ['Free to Play', 'Anime', 'Third-Person Shooter'] 57 | }, 58 | { 59 | name: "Tom Clancy's Rainbow Six Siege", 60 | hoursPlay: 287, 61 | tags: ['FPS', 'Multiplayer', 'Tactical'] 62 | } 63 | ], 64 | metadata: { 65 | alias: 'SaltyAom', 66 | country: 'Thailand' 67 | }, 68 | social: { 69 | twitter: 'SaltyAom' 70 | } 71 | } 72 | ) 73 | -------------------------------------------------------------------------------- /benchmarks/multiple-queries.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | import { benchmark } from './utils' 3 | 4 | benchmark( 5 | t.Array( 6 | t.Object({ 7 | id: t.Number(), 8 | randomNumber: t.Number() 9 | }) 10 | ), 11 | [ 12 | { id: 4174, randomNumber: 331 }, 13 | { id: 51, randomNumber: 6544 }, 14 | { id: 4462, randomNumber: 952 }, 15 | { id: 2221, randomNumber: 532 }, 16 | { id: 9276, randomNumber: 3097 }, 17 | { id: 3056, randomNumber: 7293 }, 18 | { id: 6964, randomNumber: 620 }, 19 | { id: 675, randomNumber: 6601 }, 20 | { id: 8414, randomNumber: 6569 }, 21 | { id: 2753, randomNumber: 4065 } 22 | ] 23 | ) 24 | -------------------------------------------------------------------------------- /benchmarks/small.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | import { benchmark } from './utils' 3 | 4 | benchmark( 5 | t.Object({ 6 | id: t.Number(), 7 | name: t.String(), 8 | bio: t.String(), 9 | metadata: t.Object({ 10 | alias: t.String(), 11 | country: t.String() 12 | }) 13 | }), 14 | { 15 | id: 1, 16 | name: 'SaltyAom', 17 | bio: 'I like train', 18 | metadata: { 19 | alias: 'SaltyAom', 20 | country: 'Thailand' 21 | } 22 | } 23 | ) 24 | -------------------------------------------------------------------------------- /benchmarks/stuff.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | import { benchmark } from './utils' 3 | 4 | benchmark( 5 | t.Object({ 6 | type: t.Literal('email'), 7 | name: t.String(), 8 | details: t.Object( 9 | { 10 | from: t.String({ 11 | default: 'mydefault', 12 | maxLength: 512 13 | }), 14 | subject: t.String({ 15 | maxLength: 512 16 | }) 17 | }, 18 | { default: {} } 19 | ) 20 | }), 21 | {} 22 | ) 23 | -------------------------------------------------------------------------------- /benchmarks/utils.ts: -------------------------------------------------------------------------------- 1 | import { bench, run, barplot, summary, compact } from 'mitata' 2 | 3 | import { createMirror } from '../src' 4 | 5 | import { Value } from '@sinclair/typebox/value' 6 | import { TypeCompiler } from '@sinclair/typebox/compiler' 7 | 8 | import type { TAnySchema } from '@sinclair/typebox' 9 | 10 | export const benchmark = ( 11 | model: T, 12 | value: T['static'], 13 | options?: Parameters[1] 14 | ) => { 15 | const mirror = createMirror(model, { 16 | TypeCompiler 17 | }) 18 | 19 | if (process.env.DEBUG) { 20 | console.log(mirror.toString()) 21 | } 22 | 23 | if ( 24 | JSON.stringify(mirror(value)) !== 25 | JSON.stringify(Value.Clean(model, value)) 26 | ) { 27 | console.log(mirror(value)) 28 | console.log('---') 29 | console.log(mirror.toString()) 30 | throw new Error('Invalid result') 31 | } 32 | 33 | compact(() => { 34 | barplot(() => { 35 | summary(() => { 36 | bench('TypeBox Value.Clean', () => { 37 | return Value.Clean(model, value) 38 | }) 39 | 40 | bench('Exact Mirror', () => { 41 | try { 42 | return mirror(value) 43 | } catch {} 44 | }) 45 | 46 | // const validator = TypeCompiler.Compile(model) 47 | 48 | // bench('Mirror w/ validation', () => { 49 | // return validator.Check(mirror(value)) 50 | // }) 51 | }) 52 | }) 53 | }) 54 | 55 | run() 56 | } 57 | -------------------------------------------------------------------------------- /build.ts: -------------------------------------------------------------------------------- 1 | import { $ } from 'bun' 2 | import { build, type Options } from 'tsup' 3 | 4 | await $`rm -rf dist` 5 | 6 | const tsupConfig: Options = { 7 | entry: ['src/**/*.ts'], 8 | splitting: false, 9 | sourcemap: false, 10 | clean: true, 11 | bundle: true 12 | } satisfies Options 13 | 14 | await Promise.all([ 15 | // ? tsup esm 16 | build({ 17 | outDir: 'dist', 18 | format: 'esm', 19 | target: 'node20', 20 | cjsInterop: false, 21 | ...tsupConfig 22 | }), 23 | // ? tsup cjs 24 | build({ 25 | outDir: 'dist/cjs', 26 | format: 'cjs', 27 | target: 'node20', 28 | // dts: true, 29 | ...tsupConfig 30 | }) 31 | ]) 32 | 33 | await $`tsc --project tsconfig.dts.json` 34 | 35 | await Promise.all([$`cp dist/*.d.ts dist/cjs`]) 36 | 37 | process.exit() 38 | -------------------------------------------------------------------------------- /bun.lock: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1, 3 | "workspaces": { 4 | "": { 5 | "name": "json-accelerator", 6 | "devDependencies": { 7 | "@types/bun": "1.2.2", 8 | "elysia": "^1.2.11", 9 | "eslint": "9.6.0", 10 | "mitata": "^1.0.33", 11 | "tsup": "^8.1.0", 12 | "tsx": "^4.19.2", 13 | "typescript": "^5.5.3", 14 | }, 15 | "peerDependencies": { 16 | "@sinclair/typebox": "^0.34.15", 17 | }, 18 | "optionalPeers": [ 19 | "@sinclair/typebox", 20 | ], 21 | }, 22 | }, 23 | "packages": { 24 | "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.24.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA=="], 25 | 26 | "@esbuild/android-arm": ["@esbuild/android-arm@0.24.2", "", { "os": "android", "cpu": "arm" }, "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q=="], 27 | 28 | "@esbuild/android-arm64": ["@esbuild/android-arm64@0.24.2", "", { "os": "android", "cpu": "arm64" }, "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg=="], 29 | 30 | "@esbuild/android-x64": ["@esbuild/android-x64@0.24.2", "", { "os": "android", "cpu": "x64" }, "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw=="], 31 | 32 | "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.24.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA=="], 33 | 34 | "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.24.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA=="], 35 | 36 | "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.24.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg=="], 37 | 38 | "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.24.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q=="], 39 | 40 | "@esbuild/linux-arm": ["@esbuild/linux-arm@0.24.2", "", { "os": "linux", "cpu": "arm" }, "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA=="], 41 | 42 | "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.24.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg=="], 43 | 44 | "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.24.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw=="], 45 | 46 | "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ=="], 47 | 48 | "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw=="], 49 | 50 | "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.24.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw=="], 51 | 52 | "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q=="], 53 | 54 | "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.24.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw=="], 55 | 56 | "@esbuild/linux-x64": ["@esbuild/linux-x64@0.24.2", "", { "os": "linux", "cpu": "x64" }, "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q=="], 57 | 58 | "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.24.2", "", { "os": "none", "cpu": "arm64" }, "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw=="], 59 | 60 | "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.24.2", "", { "os": "none", "cpu": "x64" }, "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw=="], 61 | 62 | "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.24.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A=="], 63 | 64 | "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.24.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA=="], 65 | 66 | "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.24.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig=="], 67 | 68 | "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.24.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ=="], 69 | 70 | "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.24.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA=="], 71 | 72 | "@esbuild/win32-x64": ["@esbuild/win32-x64@0.24.2", "", { "os": "win32", "cpu": "x64" }, "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg=="], 73 | 74 | "@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=="], 75 | 76 | "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], 77 | 78 | "@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=="], 79 | 80 | "@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=="], 81 | 82 | "@eslint/js": ["@eslint/js@9.6.0", "", {}, "sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A=="], 83 | 84 | "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], 85 | 86 | "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], 87 | 88 | "@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], 89 | 90 | "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], 91 | 92 | "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], 93 | 94 | "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], 95 | 96 | "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], 97 | 98 | "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], 99 | 100 | "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], 101 | 102 | "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], 103 | 104 | "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], 105 | 106 | "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], 107 | 108 | "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], 109 | 110 | "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.34.7", "", { "os": "android", "cpu": "arm" }, "sha512-l6CtzHYo8D2TQ3J7qJNpp3Q1Iye56ssIAtqbM2H8axxCEEwvN7o8Ze9PuIapbxFL3OHrJU2JBX6FIIVnP/rYyw=="], 111 | 112 | "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.34.7", "", { "os": "android", "cpu": "arm64" }, "sha512-KvyJpFUueUnSp53zhAa293QBYqwm94TgYTIfXyOTtidhm5V0LbLCJQRGkQClYiX3FXDQGSvPxOTD/6rPStMMDg=="], 113 | 114 | "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.34.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-jq87CjmgL9YIKvs8ybtIC98s/M3HdbqXhllcy9EdLV0yMg1DpxES2gr65nNy7ObNo/vZ/MrOTxt0bE5LinL6mA=="], 115 | 116 | "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.34.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rSI/m8OxBjsdnMMg0WEetu/w+LhLAcCDEiL66lmMX4R3oaml3eXz3Dxfvrxs1FbzPbJMaItQiksyMfv1hoIxnA=="], 117 | 118 | "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.34.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-oIoJRy3ZrdsXpFuWDtzsOOa/E/RbRWXVokpVrNnkS7npz8GEG++E1gYbzhYxhxHbO2om1T26BZjVmdIoyN2WtA=="], 119 | 120 | "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.34.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-X++QSLm4NZfZ3VXGVwyHdRf58IBbCu9ammgJxuWZYLX0du6kZvdNqPwrjvDfwmi6wFdvfZ/s6K7ia0E5kI7m8Q=="], 121 | 122 | "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.34.7", "", { "os": "linux", "cpu": "arm" }, "sha512-Z0TzhrsNqukTz3ISzrvyshQpFnFRfLunYiXxlCRvcrb3nvC5rVKI+ZXPFG/Aa4jhQa1gHgH3A0exHaRRN4VmdQ=="], 123 | 124 | "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.34.7", "", { "os": "linux", "cpu": "arm" }, "sha512-nkznpyXekFAbvFBKBy4nNppSgneB1wwG1yx/hujN3wRnhnkrYVugMTCBXED4+Ni6thoWfQuHNYbFjgGH0MBXtw=="], 125 | 126 | "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.34.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-KCjlUkcKs6PjOcxolqrXglBDcfCuUCTVlX5BgzgoJHw+1rWH1MCkETLkLe5iLLS9dP5gKC7mp3y6x8c1oGBUtA=="], 127 | 128 | "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.34.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-uFLJFz6+utmpbR313TTx+NpPuAXbPz4BhTQzgaP0tozlLnGnQ6rCo6tLwaSa6b7l6gRErjLicXQ1iPiXzYotjw=="], 129 | 130 | "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.34.7", "", { "os": "linux", "cpu": "none" }, "sha512-ws8pc68UcJJqCpneDFepnwlsMUFoWvPbWXT/XUrJ7rWUL9vLoIN3GAasgG+nCvq8xrE3pIrd+qLX/jotcLy0Qw=="], 131 | 132 | "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.34.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-vrDk9JDa/BFkxcS2PbWpr0C/LiiSLxFbNOBgfbW6P8TBe9PPHx9Wqbvx2xgNi1TOAyQHQJ7RZFqBiEohm79r0w=="], 133 | 134 | "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.34.7", "", { "os": "linux", "cpu": "none" }, "sha512-rB+ejFyjtmSo+g/a4eovDD1lHWHVqizN8P0Hm0RElkINpS0XOdpaXloqM4FBkF9ZWEzg6bezymbpLmeMldfLTw=="], 135 | 136 | "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.34.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-nNXNjo4As6dNqRn7OrsnHzwTgtypfRA3u3AKr0B3sOOo+HkedIbn8ZtFnB+4XyKJojIfqDKmbIzO1QydQ8c+Pw=="], 137 | 138 | "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.34.7", "", { "os": "linux", "cpu": "x64" }, "sha512-9kPVf9ahnpOMSGlCxXGv980wXD0zRR3wyk8+33/MXQIpQEOpaNe7dEHm5LMfyRZRNt9lMEQuH0jUKj15MkM7QA=="], 139 | 140 | "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.34.7", "", { "os": "linux", "cpu": "x64" }, "sha512-7wJPXRWTTPtTFDFezA8sle/1sdgxDjuMoRXEKtx97ViRxGGkVQYovem+Q8Pr/2HxiHp74SSRG+o6R0Yq0shPwQ=="], 141 | 142 | "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.34.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-MN7aaBC7mAjsiMEZcsJvwNsQVNZShgES/9SzWp1HC9Yjqb5OpexYnRjF7RmE4itbeesHMYYQiAtUAQaSKs2Rfw=="], 143 | 144 | "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.34.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-aeawEKYswsFu1LhDM9RIgToobquzdtSc4jSVqHV8uApz4FVvhFl/mKh92wc8WpFc6aYCothV/03UjY6y7yLgbg=="], 145 | 146 | "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.34.7", "", { "os": "win32", "cpu": "x64" }, "sha512-4ZedScpxxIrVO7otcZ8kCX1mZArtH2Wfj3uFCxRJ9NO80gg1XV0U/b2f/MKaGwj2X3QopHfoWiDQ917FRpwY3w=="], 147 | 148 | "@sinclair/typebox": ["@sinclair/typebox@0.34.22", "", {}, "sha512-0avTcz3XUm6mMcq5tQRoEnxyvmr3uanplFepD+a/TiDzOBZ0Us5bsShW41xOO2kST7AYv4xiCsmE5Ag02yOPfQ=="], 149 | 150 | "@types/bun": ["@types/bun@1.2.2", "", { "dependencies": { "bun-types": "1.2.2" } }, "sha512-tr74gdku+AEDN5ergNiBnplr7hpDp3V1h7fqI2GcR/rsUaM39jpSeKH0TFibRvU0KwniRx5POgaYnaXbk0hU+w=="], 151 | 152 | "@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="], 153 | 154 | "@types/node": ["@types/node@22.13.4", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg=="], 155 | 156 | "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], 157 | 158 | "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], 159 | 160 | "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], 161 | 162 | "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], 163 | 164 | "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], 165 | 166 | "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], 167 | 168 | "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], 169 | 170 | "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], 171 | 172 | "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], 173 | 174 | "brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], 175 | 176 | "bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="], 177 | 178 | "bundle-require": ["bundle-require@5.1.0", "", { "dependencies": { "load-tsconfig": "^0.2.3" }, "peerDependencies": { "esbuild": ">=0.18" } }, "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA=="], 179 | 180 | "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], 181 | 182 | "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], 183 | 184 | "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], 185 | 186 | "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], 187 | 188 | "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], 189 | 190 | "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], 191 | 192 | "commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], 193 | 194 | "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], 195 | 196 | "consola": ["consola@3.4.0", "", {}, "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA=="], 197 | 198 | "cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="], 199 | 200 | "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], 201 | 202 | "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], 203 | 204 | "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], 205 | 206 | "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], 207 | 208 | "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=="], 209 | 210 | "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], 211 | 212 | "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=="], 213 | 214 | "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], 215 | 216 | "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=="], 217 | 218 | "eslint-scope": ["eslint-scope@8.2.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A=="], 219 | 220 | "eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="], 221 | 222 | "espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="], 223 | 224 | "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], 225 | 226 | "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], 227 | 228 | "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], 229 | 230 | "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], 231 | 232 | "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], 233 | 234 | "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], 235 | 236 | "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], 237 | 238 | "fastq": ["fastq@1.19.0", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA=="], 239 | 240 | "fdir": ["fdir@6.4.3", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw=="], 241 | 242 | "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], 243 | 244 | "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], 245 | 246 | "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], 247 | 248 | "flatted": ["flatted@3.3.2", "", {}, "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA=="], 249 | 250 | "foreground-child": ["foreground-child@3.3.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" } }, "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg=="], 251 | 252 | "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 253 | 254 | "get-tsconfig": ["get-tsconfig@4.10.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A=="], 255 | 256 | "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], 257 | 258 | "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], 259 | 260 | "globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], 261 | 262 | "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], 263 | 264 | "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], 265 | 266 | "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], 267 | 268 | "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], 269 | 270 | "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], 271 | 272 | "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], 273 | 274 | "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], 275 | 276 | "is-path-inside": ["is-path-inside@3.0.3", "", {}, "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="], 277 | 278 | "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], 279 | 280 | "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], 281 | 282 | "joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="], 283 | 284 | "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], 285 | 286 | "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], 287 | 288 | "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], 289 | 290 | "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], 291 | 292 | "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], 293 | 294 | "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], 295 | 296 | "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], 297 | 298 | "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], 299 | 300 | "load-tsconfig": ["load-tsconfig@0.2.5", "", {}, "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg=="], 301 | 302 | "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], 303 | 304 | "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], 305 | 306 | "lodash.sortby": ["lodash.sortby@4.7.0", "", {}, "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="], 307 | 308 | "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], 309 | 310 | "memoirist": ["memoirist@0.3.0", "", {}, "sha512-wR+4chMgVPq+T6OOsk40u9Wlpw1Pjx66NMNiYxCQQ4EUJ7jDs3D9kTCeKdBOkvAiqXlHLVJlvYL01PvIJ1MPNg=="], 311 | 312 | "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], 313 | 314 | "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], 315 | 316 | "mitata": ["mitata@1.0.34", "", {}, "sha512-Mc3zrtNBKIMeHSCQ0XqRLo1vbdIx1wvFV9c8NJAiyho6AjNfMY8bVhbS12bwciUdd1t4rj8099CH3N3NFahaUA=="], 317 | 318 | "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 319 | 320 | "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], 321 | 322 | "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], 323 | 324 | "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], 325 | 326 | "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], 327 | 328 | "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], 329 | 330 | "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], 331 | 332 | "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], 333 | 334 | "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], 335 | 336 | "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], 337 | 338 | "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], 339 | 340 | "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], 341 | 342 | "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], 343 | 344 | "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], 345 | 346 | "picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], 347 | 348 | "pirates": ["pirates@4.0.6", "", {}, "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg=="], 349 | 350 | "postcss-load-config": ["postcss-load-config@6.0.1", "", { "dependencies": { "lilconfig": "^3.1.1" }, "peerDependencies": { "jiti": ">=1.21.0", "postcss": ">=8.0.9", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["jiti", "postcss", "tsx", "yaml"] }, "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g=="], 351 | 352 | "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], 353 | 354 | "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], 355 | 356 | "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], 357 | 358 | "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], 359 | 360 | "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], 361 | 362 | "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], 363 | 364 | "reusify": ["reusify@1.0.4", "", {}, "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="], 365 | 366 | "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=="], 367 | 368 | "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], 369 | 370 | "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], 371 | 372 | "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], 373 | 374 | "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], 375 | 376 | "source-map": ["source-map@0.8.0-beta.0", "", { "dependencies": { "whatwg-url": "^7.0.0" } }, "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA=="], 377 | 378 | "string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], 379 | 380 | "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], 381 | 382 | "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], 383 | 384 | "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], 385 | 386 | "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], 387 | 388 | "sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="], 389 | 390 | "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], 391 | 392 | "text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="], 393 | 394 | "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], 395 | 396 | "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], 397 | 398 | "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], 399 | 400 | "tinyglobby": ["tinyglobby@0.2.10", "", { "dependencies": { "fdir": "^6.4.2", "picomatch": "^4.0.2" } }, "sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew=="], 401 | 402 | "tr46": ["tr46@1.0.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA=="], 403 | 404 | "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], 405 | 406 | "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], 407 | 408 | "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=="], 409 | 410 | "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=="], 411 | 412 | "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], 413 | 414 | "typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="], 415 | 416 | "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], 417 | 418 | "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], 419 | 420 | "webidl-conversions": ["webidl-conversions@4.0.2", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="], 421 | 422 | "whatwg-url": ["whatwg-url@7.1.0", "", { "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", "webidl-conversions": "^4.0.2" } }, "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg=="], 423 | 424 | "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], 425 | 426 | "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], 427 | 428 | "wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], 429 | 430 | "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], 431 | 432 | "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], 433 | 434 | "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], 435 | 436 | "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], 437 | 438 | "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], 439 | 440 | "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], 441 | 442 | "string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], 443 | 444 | "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], 445 | 446 | "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=="], 447 | 448 | "wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], 449 | 450 | "wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], 451 | 452 | "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], 453 | 454 | "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], 455 | 456 | "glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], 457 | 458 | "string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], 459 | 460 | "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.23.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ=="], 461 | 462 | "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.23.1", "", { "os": "android", "cpu": "arm" }, "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ=="], 463 | 464 | "tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.23.1", "", { "os": "android", "cpu": "arm64" }, "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw=="], 465 | 466 | "tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.23.1", "", { "os": "android", "cpu": "x64" }, "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg=="], 467 | 468 | "tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.23.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q=="], 469 | 470 | "tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.23.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw=="], 471 | 472 | "tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.23.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA=="], 473 | 474 | "tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.23.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g=="], 475 | 476 | "tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.23.1", "", { "os": "linux", "cpu": "arm" }, "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ=="], 477 | 478 | "tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.23.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g=="], 479 | 480 | "tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.23.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ=="], 481 | 482 | "tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.23.1", "", { "os": "linux", "cpu": "none" }, "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw=="], 483 | 484 | "tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.23.1", "", { "os": "linux", "cpu": "none" }, "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q=="], 485 | 486 | "tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.23.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw=="], 487 | 488 | "tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.23.1", "", { "os": "linux", "cpu": "none" }, "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA=="], 489 | 490 | "tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.23.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw=="], 491 | 492 | "tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.23.1", "", { "os": "linux", "cpu": "x64" }, "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ=="], 493 | 494 | "tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.23.1", "", { "os": "none", "cpu": "x64" }, "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA=="], 495 | 496 | "tsx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.23.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q=="], 497 | 498 | "tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.23.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA=="], 499 | 500 | "tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.23.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA=="], 501 | 502 | "tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.23.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A=="], 503 | 504 | "tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.23.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ=="], 505 | 506 | "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.23.1", "", { "os": "win32", "cpu": "x64" }, "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg=="], 507 | 508 | "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], 509 | 510 | "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], 511 | } 512 | } 513 | -------------------------------------------------------------------------------- /example/index.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | import { createMirror } from '../src/index' 3 | import { TypeCompiler } from '@sinclair/typebox/compiler' 4 | 5 | const shape = t.Object({ 6 | foo: t.Optional( 7 | t.Nullable( 8 | t.Object({ 9 | a: t.Number({ 10 | error: 'Must be a number' 11 | }) 12 | }) 13 | ) 14 | ) 15 | }) 16 | 17 | const value = { 18 | foo: 123 19 | } satisfies typeof shape.static 20 | 21 | const mirror = createMirror(shape, { 22 | TypeCompiler 23 | }) 24 | 25 | console.dir(mirror(value), { 26 | depth: null 27 | }) 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exact-mirror", 3 | "version": "0.1.2", 4 | "description": "Mirror exact value to TypeBox/OpenAPI model", 5 | "license": "MIT", 6 | "scripts": { 7 | "dev": "bun run --watch example/index.ts", 8 | "test": "bun test && npm run test:node", 9 | "test:node": "npm install --prefix ./test/node/cjs/ && npm install --prefix ./test/node/esm/ && node ./test/node/cjs/index.js && node ./test/node/esm/index.js", 10 | "build": "bun build.ts", 11 | "release": "npm run build && npm run test && npm publish --access public" 12 | }, 13 | "peerDependencies": { 14 | "@sinclair/typebox": "^0.34.15" 15 | }, 16 | "peerDependenciesMeta": { 17 | "@sinclair/typebox": { 18 | "optional": true 19 | } 20 | }, 21 | "devDependencies": { 22 | "@types/bun": "1.2.2", 23 | "elysia": "^1.2.11", 24 | "eslint": "9.6.0", 25 | "mitata": "^1.0.33", 26 | "tsup": "^8.1.0", 27 | "tsx": "^4.19.2", 28 | "typescript": "^5.5.3" 29 | }, 30 | "main": "./dist/cjs/index.js", 31 | "module": "./dist/index.mjs", 32 | "types": "./dist/index.d.ts", 33 | "exports": { 34 | "./package.json": "./package.json", 35 | ".": { 36 | "types": "./dist/index.d.ts", 37 | "import": "./dist/index.mjs", 38 | "require": "./dist/cjs/index.js" 39 | } 40 | }, 41 | "keywords": [ 42 | "elysia", 43 | "exact", 44 | "mirror", 45 | "typebox" 46 | ], 47 | "repository": { 48 | "type": "git", 49 | "url": "https://github.com/elysiajs/exact-mirror" 50 | }, 51 | "author": { 52 | "name": "saltyAom", 53 | "url": "https://github.com/SaltyAom", 54 | "email": "saltyaom@gmail.com" 55 | }, 56 | "homepage": "https://github.com/elysiajs/exact-mirror", 57 | "bugs": "https://github.com/elysiajs/exact-mirror/issues" 58 | } 59 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { TypeCompiler, type TypeCheck } from '@sinclair/typebox/compiler' 2 | import type { TAnySchema, TModule, TRecord } from '@sinclair/typebox' 3 | import { deepMatch } from 'bun' 4 | 5 | const Kind = Symbol.for('TypeBox.Kind') 6 | const Hint = Symbol.for('TypeBox.Hint') 7 | 8 | const isSpecialProperty = (name: string) => /(\ |-|\t|\n)/.test(name) 9 | 10 | const joinProperty = (v1: string, v2: string | number, isOptional = false) => { 11 | if (typeof v2 === 'number') return `${v1}[${v2}]` 12 | 13 | if (isSpecialProperty(v2)) return `${v1}${isOptional ? '?.' : ''}["${v2}"]` 14 | 15 | return `${v1}${isOptional ? '?' : ''}.${v2}` 16 | } 17 | 18 | const encodeProperty = (v: string) => (isSpecialProperty(v) ? `"${v}"` : v) 19 | 20 | const sanitize = (key: string, sanitize = 0, schema: TAnySchema) => { 21 | if (schema.type !== 'string' || schema.const || schema.trusted) return key 22 | 23 | let hof = '' 24 | for (let i = sanitize - 1; i >= 0; i--) hof += `d.h${i}(` 25 | return hof + key + ')'.repeat(sanitize) 26 | } 27 | 28 | export const mergeObjectIntersection = (schema: TAnySchema): TAnySchema => { 29 | if ( 30 | !schema.allOf || 31 | (Kind in schema && 32 | (schema[Kind] !== 'Intersect' || schema.type !== 'object')) 33 | ) 34 | return schema 35 | 36 | const { allOf, ...newSchema } = schema 37 | newSchema.properties = {} 38 | 39 | if (Kind in newSchema) newSchema[Kind as any] = 'Object' 40 | 41 | for (const type of allOf) { 42 | if (type.type !== 'object') continue 43 | 44 | const { properties, required, type: _, [Kind]: __, ...rest } = type 45 | 46 | if (required) 47 | newSchema.required = newSchema.required 48 | ? newSchema.required.concat(required) 49 | : required 50 | 51 | Object.assign(newSchema, rest) 52 | 53 | for (const property in type.properties) 54 | newSchema.properties[property] = mergeObjectIntersection( 55 | type.properties[property] 56 | ) 57 | } 58 | 59 | return newSchema 60 | } 61 | 62 | type MaybeArray = T | T[] 63 | 64 | export interface Instruction { 65 | optionals: string[] 66 | optionalsInArray: string[][] 67 | parentIsOptional: boolean 68 | array: number 69 | unions: TypeCheck[][] 70 | unionKeys: Record 71 | sanitize: MaybeArray<(v: string) => string> | undefined 72 | /** 73 | * TypeCompiler is required when using Union 74 | * 75 | * Left as opt-in to reduce bundle size 76 | * many end-user doesn't use Union 77 | * 78 | * @default undefined 79 | */ 80 | TypeCompiler?: typeof TypeCompiler 81 | typeCompilerWanred?: boolean 82 | modules?: TModule 83 | definitions: Record 84 | recursion: number 85 | /** 86 | * @default 8 87 | */ 88 | recursionLimit: number 89 | /** 90 | * If incorrect type is passed to Union value, should it be removed? 91 | * 92 | * If you check a value later, it's recommended to set this to `false` 93 | * otherwise, set this to true 94 | * 95 | * @default false 96 | */ 97 | removeUnknownUnionType: boolean 98 | } 99 | 100 | const handleRecord = ( 101 | schema: TRecord, 102 | property: string, 103 | instruction: Instruction 104 | ) => { 105 | const child = 106 | schema.patternProperties['^(.*)$'] ?? 107 | schema.patternProperties[Object.keys(schema.patternProperties)[0]] 108 | 109 | if (!child) return property 110 | 111 | const i = instruction.array 112 | instruction.array++ 113 | 114 | return ( 115 | `(()=>{` + 116 | `const ar${i}s=Object.keys(${property}),` + 117 | `ar${i}v={};` + 118 | `for(let i=0;i { 132 | const i = instruction.array 133 | instruction.array++ 134 | 135 | const isRoot = property === 'v' && !instruction.unions.length 136 | 137 | let v = '' 138 | if (!isRoot) v = `(()=>{` 139 | 140 | v += `const ar${i}v=[` 141 | 142 | for (let i = 0; i < schema.length; i++) { 143 | if (i !== 0) v += ',' 144 | 145 | v += mirror( 146 | schema[i], 147 | joinProperty(property, i, instruction.parentIsOptional), 148 | instruction 149 | ) 150 | } 151 | 152 | v += `];` 153 | 154 | if (!isRoot) v += `return ar${i}v})()` 155 | 156 | return v 157 | } 158 | 159 | export function deepClone(source: T, weak = new WeakMap()): T { 160 | if ( 161 | source === null || 162 | typeof source !== 'object' || 163 | typeof source === 'function' 164 | ) 165 | return source 166 | 167 | // Circular‑reference guard 168 | if (weak.has(source as object)) return weak.get(source as object) 169 | 170 | if (Array.isArray(source)) { 171 | const copy: any[] = new Array(source.length) 172 | weak.set(source, copy) 173 | 174 | for (let i = 0; i < source.length; i++) 175 | copy[i] = deepClone(source[i], weak) 176 | 177 | return copy as any 178 | } 179 | 180 | if (typeof source === 'object') { 181 | const keys = Object.keys(source).concat( 182 | Object.getOwnPropertySymbols(source) as any[] 183 | ) 184 | 185 | const cloned: Partial = {} 186 | 187 | for (const key of keys) 188 | cloned[key as keyof T] = deepClone((source as any)[key], weak) 189 | 190 | return cloned as T 191 | } 192 | 193 | return source 194 | } 195 | 196 | const handleUnion = ( 197 | schemas: TAnySchema[], 198 | property: string, 199 | instruction: Instruction 200 | ) => { 201 | // TODO: optimize null 202 | // if (schemas.length === 2 && schemas.find((x) => x.type === 'null')) { 203 | // const schema = schemas.find((x) => x.type !== 'null') 204 | 205 | // if (schema) return mirror(schema, property, instruction) 206 | // } 207 | 208 | if (instruction.TypeCompiler === undefined) { 209 | if (!instruction.typeCompilerWanred) { 210 | console.warn( 211 | new Error( 212 | "[exact-mirror] TypeBox's TypeCompiler is required to use Union" 213 | ) 214 | ) 215 | instruction.typeCompilerWanred = true 216 | } 217 | 218 | return property 219 | } 220 | 221 | instruction.unionKeys[property] = 1 222 | 223 | const ui = instruction.unions.length 224 | const typeChecks = (instruction.unions[ui] = []>[]) 225 | 226 | let v = `(()=>{\n` 227 | 228 | const unwrapRef = (type: TAnySchema) => { 229 | if (!(Kind in type) || !type.$ref) return type 230 | 231 | if (type[Kind] === 'This') { 232 | return deepClone(instruction.definitions[type.$ref]) 233 | } else if (type[Kind] === 'Ref') { 234 | if (!instruction.modules) 235 | console.warn( 236 | new Error( 237 | '[exact-mirror] modules is required when using nested cyclic reference' 238 | ) 239 | ) 240 | else 241 | return instruction.modules.Import( 242 | type.$ref 243 | ) as any as TAnySchema 244 | } 245 | 246 | return type 247 | } 248 | 249 | for (let i = 0; i < schemas.length; i++) { 250 | let type = unwrapRef(schemas[i]) 251 | 252 | if (Array.isArray(type.anyOf)) 253 | for (let i = 0; i < type.anyOf.length; i++) 254 | type.anyOf[i] = unwrapRef(type.anyOf[i]) 255 | else if (type.items) { 256 | if (Array.isArray(type.items)) 257 | for (let i = 0; i < type.items.length; i++) 258 | type.items[i] = unwrapRef(type.items[i]) 259 | else type.items = unwrapRef(type.items) 260 | } 261 | 262 | typeChecks.push(TypeCompiler.Compile(type)) 263 | v += `if(d.unions[${ui}][${i}].Check(${property})){return ${mirror( 264 | type, 265 | property, 266 | { 267 | ...instruction, 268 | recursion: instruction.recursion + 1, 269 | parentIsOptional: true 270 | } 271 | )}}\n` 272 | } 273 | 274 | // unknown type, return as-is (this is a default intended behavior) 275 | // because it's expected that exact-mirror input should always be a correct value 276 | // returning an incorrect value then later checked is expected 277 | v += 278 | `return ${instruction.removeUnknownUnionType ? 'undefined' : property}` + 279 | `})()` 280 | 281 | return v 282 | } 283 | 284 | const mirror = ( 285 | schema: TAnySchema, 286 | property: string, 287 | instruction: Instruction 288 | ): string => { 289 | if (!schema) return '' 290 | 291 | const isRoot = property === 'v' && !instruction.unions.length 292 | 293 | if ( 294 | Kind in schema && 295 | schema[Kind] === 'Import' && 296 | schema.$ref in schema.$defs 297 | ) 298 | return mirror(schema.$defs[schema.$ref], property, { 299 | ...instruction, 300 | definitions: Object.assign(instruction.definitions, schema.$defs) 301 | }) 302 | 303 | if ( 304 | isRoot && 305 | schema.type !== 'object' && 306 | schema.type !== 'array' && 307 | !schema.anyOf 308 | ) 309 | return `return ${sanitize('v', instruction.sanitize?.length, schema)}` 310 | 311 | if (instruction.recursion >= instruction.recursionLimit) return property 312 | 313 | let v = '' 314 | 315 | if (schema.$id && Hint in schema) 316 | instruction.definitions[schema.$id] = schema 317 | 318 | switch (schema.type) { 319 | case 'object': 320 | if (schema[Kind as any] === 'Record') { 321 | v = handleRecord(schema as TRecord, property, instruction) 322 | 323 | break 324 | } 325 | 326 | schema = mergeObjectIntersection(schema) 327 | 328 | v += '{' 329 | 330 | if (schema.additionalProperties) v += `...${property}` 331 | 332 | const keys = Object.keys(schema.properties) 333 | for (let i = 0; i < keys.length; i++) { 334 | const key = keys[i] 335 | 336 | let isOptional = 337 | (schema.required && !schema.required.includes(key)) || 338 | Array.isArray(schema.properties[key].anyOf) 339 | 340 | const name = joinProperty( 341 | property, 342 | key, 343 | instruction.parentIsOptional 344 | ) 345 | 346 | if (isOptional) { 347 | const index = instruction.array 348 | 349 | if (property.startsWith('ar')) { 350 | const refName = name.slice(name.indexOf('.') + 1) 351 | const array = instruction.optionalsInArray 352 | 353 | if (array[index]) array[index].push(refName) 354 | else array[index] = [refName] 355 | } else { 356 | instruction.optionals.push(name) 357 | } 358 | } 359 | 360 | const child = schema.properties[key] 361 | 362 | if (schema.additionalProperties && child.type !== 'object') 363 | continue 364 | 365 | if (i !== 0) v += ',' 366 | 367 | v += `${encodeProperty(key)}:${isOptional ? `${name}===undefined?undefined:` : ''}${mirror( 368 | child, 369 | name, 370 | { 371 | ...instruction, 372 | recursion: instruction.recursion + 1, 373 | parentIsOptional: isOptional 374 | } 375 | )}` 376 | } 377 | 378 | v += '}' 379 | 380 | break 381 | 382 | case 'array': 383 | if ( 384 | schema.items.type !== 'object' && 385 | schema.items.type !== 'array' 386 | ) { 387 | if (Array.isArray(schema.items)) { 388 | v = handleTuple(schema.items, property, instruction) 389 | break 390 | } else if (isRoot) return 'return v' 391 | else if ( 392 | Kind in schema.items && 393 | schema.items.$ref && 394 | (schema.items[Kind] === 'Ref' || 395 | schema.items[Kind] === 'This') 396 | ) 397 | v = mirror( 398 | deepClone(instruction.definitions[schema.items.$ref]), 399 | property, 400 | { 401 | ...instruction, 402 | parentIsOptional: true, 403 | recursion: instruction.recursion + 1 404 | } 405 | ) 406 | else { 407 | v = property 408 | break 409 | } 410 | } 411 | 412 | const i = instruction.array 413 | instruction.array++ 414 | 415 | let reference = property 416 | 417 | if (isRoot) v = `const ar${i}v=new Array(${property}.length);` 418 | else { 419 | reference = `ar${i}s` 420 | v = 421 | `((${reference})=>{` + 422 | `const ar${i}v=new Array(${reference}.length);` 423 | } 424 | 425 | v += 426 | `for(let i=0;i<${reference}.length;i++){` + 427 | `const ar${i}p=${reference}[i];` + 428 | `ar${i}v[i]=${mirror(schema.items, `ar${i}p`, instruction)}` 429 | 430 | const optionals = instruction.optionalsInArray[i + 1] 431 | if (optionals) { 432 | // optional index 433 | for (let oi = 0; oi < optionals.length; oi++) { 434 | // since pointer is checked in object case with ternary as undefined, this is not need 435 | // const pointer = `ar${i}p.${optionals[oi]}` 436 | 437 | const target = `ar${i}v[i].${optionals[oi]}` 438 | 439 | // we can add semi-colon here because it delimit recursive mirror 440 | v += `;if(${target}===undefined)delete ${target}` 441 | } 442 | } 443 | 444 | v += `}` 445 | 446 | if (!isRoot) v += `return ar${i}v})(${property})` 447 | 448 | break 449 | 450 | default: 451 | if (schema.$ref && schema.$ref in instruction.definitions) 452 | return mirror( 453 | instruction.definitions[schema.$ref], 454 | property, 455 | instruction 456 | ) 457 | 458 | if (Array.isArray(schema.anyOf)) { 459 | v = handleUnion(schema.anyOf, property, instruction) 460 | 461 | break 462 | } 463 | 464 | v = sanitize(property, instruction.sanitize?.length, schema) 465 | 466 | break 467 | } 468 | 469 | if (!isRoot) return v 470 | 471 | if (schema.type === 'array') return `${v}return ar0v` 472 | 473 | v = `const x=${v}\n` 474 | 475 | for (let i = 0; i < instruction.optionals.length; i++) { 476 | const key = instruction.optionals[i] 477 | const prop = key.slice(1) 478 | 479 | v += `if(${key}===undefined` 480 | 481 | if (instruction.unionKeys[key]) v += `||x${prop}===undefined` 482 | 483 | v += `)delete x${prop.charCodeAt(0) !== 63 ? '?' : ''}${prop}\n` 484 | } 485 | 486 | return `${v}return x` 487 | } 488 | 489 | export const createMirror = ( 490 | schema: T, 491 | { 492 | TypeCompiler, 493 | modules, 494 | definitions, 495 | sanitize, 496 | recursionLimit = 8, 497 | removeUnknownUnionType = false 498 | }: Partial< 499 | Pick< 500 | Instruction, 501 | | 'TypeCompiler' 502 | | 'definitions' 503 | | 'sanitize' 504 | | 'modules' 505 | | 'recursionLimit' 506 | | 'removeUnknownUnionType' 507 | > 508 | > = {} 509 | ): ((v: T['static']) => T['static']) => { 510 | const unions = [] 511 | 512 | if (typeof sanitize === 'function') sanitize = [sanitize] 513 | 514 | const f = mirror(schema, 'v', { 515 | optionals: [], 516 | optionalsInArray: [], 517 | array: 0, 518 | parentIsOptional: false, 519 | unions, 520 | unionKeys: {}, 521 | TypeCompiler, 522 | modules, 523 | // @ts-ignore private property 524 | definitions: definitions ?? modules?.$defs ?? {}, 525 | sanitize, 526 | recursion: 0, 527 | recursionLimit, 528 | removeUnknownUnionType 529 | }) 530 | 531 | if (!unions.length && !sanitize?.length) return Function('v', f) as any 532 | 533 | let hof: Record | undefined 534 | if (sanitize?.length) { 535 | hof = {} 536 | for (let i = 0; i < sanitize.length; i++) hof[`h${i}`] = sanitize[i] 537 | } 538 | 539 | return Function( 540 | 'd', 541 | `return function mirror(v){${f}}` 542 | )({ 543 | unions, 544 | ...hof 545 | }) as any 546 | } 547 | 548 | export default createMirror 549 | 550 | // const shape = t.Object({ 551 | // a: t.Nullable( 552 | // t.Object({ 553 | // a: t.String() 554 | // }) 555 | // ) 556 | // }) 557 | 558 | // const shape = t.Object({ 559 | // a: t.String(), 560 | // social: t.Optional( 561 | // t.Object({ 562 | // facebook: t.Nullable(t.String()), 563 | // twitter: t.Nullable(t.String()), 564 | // youtube: t.Nullable(t.String()) 565 | // }) 566 | // ) 567 | // }) 568 | 569 | // const stringify = createaccelerate(shape) 570 | 571 | // console.log( 572 | // stringify({ 573 | // a: 'a', 574 | // social: { 575 | // a: 'a', 576 | // } 577 | // }) 578 | // ) 579 | -------------------------------------------------------------------------------- /test/array.test.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | 3 | import { describe, it } from 'bun:test' 4 | import { isEqual } from './utils' 5 | 6 | describe('Array', () => { 7 | it('handle array at root', () => { 8 | const shape = t.Array(t.String()) 9 | 10 | isEqual(shape, ['a', 'b']) 11 | }) 12 | 13 | it('handle array union at root', () => { 14 | const shape = t.Array(t.Union([t.String(), t.Number()])) 15 | 16 | isEqual(shape, ['a', 'b', 1, 2, 'c']) 17 | }) 18 | 19 | it('handle array object', () => { 20 | const shape = t.Array( 21 | t.Object({ 22 | a: t.String(), 23 | b: t.String() 24 | }) 25 | ) 26 | 27 | isEqual( 28 | shape, 29 | [ 30 | { 31 | a: 'a', 32 | b: 'b' 33 | }, 34 | { 35 | a: 'a', 36 | b: 'b', 37 | // @ts-expect-error 38 | c: 'c' 39 | } 40 | ], 41 | [ 42 | { 43 | a: 'a', 44 | b: 'b' 45 | }, 46 | { 47 | a: 'a', 48 | b: 'b' 49 | } 50 | ] 51 | ) 52 | }) 53 | 54 | it('handle array object with optional', () => { 55 | const shape = t.Array( 56 | t.Object({ 57 | a: t.String(), 58 | b: t.Optional(t.String()) 59 | }) 60 | ) 61 | 62 | isEqual( 63 | shape, 64 | [ 65 | { 66 | a: 'a' 67 | }, 68 | { 69 | a: 'a', 70 | b: 'b' 71 | }, 72 | { 73 | a: 'a', 74 | b: 'b', 75 | // @ts-expect-error 76 | c: 'c' 77 | } 78 | ], 79 | [ 80 | { a: 'a' }, 81 | { 82 | a: 'a', 83 | b: 'b' 84 | }, 85 | { 86 | a: 'a', 87 | b: 'b' 88 | } 89 | ] 90 | ) 91 | }) 92 | }) 93 | -------------------------------------------------------------------------------- /test/index.test.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | 3 | import { describe, it, mock } from 'bun:test' 4 | import { isEqual } from './utils' 5 | 6 | describe('Core', () => { 7 | it('handle string', () => { 8 | const shape = t.String() 9 | 10 | const value = 'saltyaom' satisfies typeof shape.static 11 | 12 | isEqual(shape, value) 13 | }) 14 | 15 | it('handle number', () => { 16 | const shape = t.Number() 17 | 18 | const value = 0 satisfies typeof shape.static 19 | 20 | isEqual(shape, value) 21 | }) 22 | 23 | it('handle boolean', () => { 24 | const shape = t.Boolean() 25 | 26 | const value = true satisfies typeof shape.static 27 | 28 | isEqual(shape, value) 29 | }) 30 | 31 | it('handle literal', () => { 32 | const shape = t.Literal('saltyaom') 33 | 34 | const value = 'saltyaom' satisfies typeof shape.static 35 | 36 | isEqual(shape, value) 37 | }) 38 | 39 | it('handle null', () => { 40 | const shape = t.Null() 41 | 42 | const value = null satisfies typeof shape.static 43 | 44 | isEqual(shape, value) 45 | }) 46 | 47 | it('handle object', () => { 48 | const shape = t.Object({ 49 | name: t.String() 50 | }) 51 | 52 | const value = { 53 | name: 'salt', 54 | // @ts-expect-error 55 | additional: 'b' 56 | } satisfies typeof shape.static 57 | 58 | const expected = { 59 | name: 'salt' 60 | } satisfies typeof shape.static 61 | 62 | isEqual(shape, value, expected) 63 | }) 64 | 65 | it('handle nested object', () => { 66 | const shape = t.Object({ 67 | name: t.String(), 68 | info: t.Object({ 69 | alias: t.String() 70 | }) 71 | }) 72 | 73 | const value = { 74 | name: 'salt', 75 | additional: 'b', 76 | info: { 77 | alias: 'salty', 78 | // @ts-expect-error 79 | additional: 'b' 80 | } 81 | } satisfies typeof shape.static 82 | 83 | const expected = { 84 | name: 'salt', 85 | info: { 86 | alias: 'salty' 87 | } 88 | } satisfies typeof shape.static 89 | 90 | isEqual(shape, value, expected) 91 | }) 92 | 93 | it('handle object with optional', () => { 94 | const shape = t.Object({ 95 | name: t.String(), 96 | optional1: t.Optional(t.String()), 97 | optional2: t.Optional(t.String()) 98 | }) 99 | 100 | const value = { 101 | name: 'salt', 102 | optional1: 'ok', 103 | // @ts-expect-error 104 | additional: 'b' 105 | } satisfies typeof shape.static 106 | 107 | const expected = { 108 | name: 'salt', 109 | optional1: 'ok' 110 | } satisfies typeof shape.static 111 | 112 | isEqual(shape, value, expected) 113 | }) 114 | 115 | it('handle array object', () => { 116 | const shape = t.Array( 117 | t.Object({ 118 | name: t.String() 119 | }) 120 | ) 121 | 122 | const value = [ 123 | { 124 | name: 'salt', 125 | // @ts-expect-error 126 | additional: 'b' 127 | }, 128 | { 129 | name: 'chiffon' 130 | } 131 | ] satisfies typeof shape.static 132 | 133 | const expected = [ 134 | { 135 | name: 'salt' 136 | }, 137 | { 138 | name: 'chiffon' 139 | } 140 | ] satisfies typeof shape.static 141 | 142 | isEqual(shape, value, expected) 143 | }) 144 | 145 | it('handle array nested object', () => { 146 | const shape = t.Array( 147 | t.Object({ 148 | name: t.String(), 149 | info: t.Object({ 150 | alias: t.String() 151 | }) 152 | }) 153 | ) 154 | 155 | const value = [ 156 | { 157 | name: 'salt', 158 | additional: 'b', 159 | info: { 160 | alias: 'salty', 161 | // @ts-expect-error 162 | additional: 'b' 163 | } 164 | }, 165 | { 166 | name: 'chiffon', 167 | info: { 168 | alias: 'chiffon' 169 | } 170 | } 171 | ] satisfies typeof shape.static 172 | 173 | const expected = [ 174 | { 175 | name: 'salt', 176 | info: { 177 | alias: 'salty' 178 | } 179 | }, 180 | { 181 | name: 'chiffon', 182 | info: { 183 | alias: 'chiffon' 184 | } 185 | } 186 | ] satisfies typeof shape.static 187 | 188 | isEqual(shape, value, expected) 189 | }) 190 | 191 | it('handle intersect object', () => { 192 | const shape = t.Intersect([ 193 | t.Object({ 194 | name: t.String() 195 | }), 196 | t.Object({ 197 | info: t.Object({ 198 | alias: t.String() 199 | }) 200 | }) 201 | ]) 202 | 203 | const value = { 204 | name: 'salt', 205 | additional: 'b', 206 | info: { 207 | alias: 'salty', 208 | // @ts-expect-error 209 | additional: 'b' 210 | } 211 | } satisfies typeof shape.static 212 | 213 | const expected = { 214 | name: 'salt', 215 | info: { 216 | alias: 'salty' 217 | } 218 | } satisfies typeof shape.static 219 | 220 | isEqual(shape, value, expected) 221 | }) 222 | }) 223 | -------------------------------------------------------------------------------- /test/node/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json -------------------------------------------------------------------------------- /test/node/cjs/index.js: -------------------------------------------------------------------------------- 1 | if ('Bun' in globalThis) { 2 | throw new Error('❌ Use Node.js to run this test!'); 3 | } 4 | 5 | const { createMirror } = require('exact-mirror'); 6 | 7 | if (typeof createMirror !== 'function') { 8 | throw new Error('❌ CommonJS Node.js failed'); 9 | } 10 | 11 | console.log('✅ CommonJS Node.js works!'); 12 | -------------------------------------------------------------------------------- /test/node/cjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "commonjs", 3 | "dependencies": { 4 | "exact-mirror": "../../.." 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/node/esm/index.js: -------------------------------------------------------------------------------- 1 | if ('Bun' in globalThis) { 2 | throw new Error('❌ Use Node.js to run this test!'); 3 | } 4 | 5 | import { createMirror } from 'exact-mirror'; 6 | 7 | if (typeof createMirror !== 'function') { 8 | throw new Error('❌ ESM Node.js failed'); 9 | } 10 | 11 | console.log('✅ ESM Node.js works!'); 12 | -------------------------------------------------------------------------------- /test/node/esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "dependencies": { 4 | "exact-mirror": "../../.." 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/optional.test.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | 3 | import { describe, it } from 'bun:test' 4 | import { isEqual } from './utils' 5 | 6 | describe('Optional', () => { 7 | it('handle nested object with optional', () => { 8 | const shape = t.Object({ 9 | name: t.String(), 10 | info: t.Object({ 11 | alias: t.String(), 12 | optional1: t.Optional(t.String()), 13 | optional2: t.Optional(t.String()) 14 | }), 15 | optional1: t.Optional(t.String()), 16 | optional2: t.Optional(t.String()) 17 | }) 18 | 19 | const value = { 20 | name: 'salt', 21 | additional: 'b', 22 | info: { 23 | alias: 'salty', 24 | // @ts-expect-error 25 | additional: 'b', 26 | optional2: 'ok' 27 | }, 28 | optional1: 'ok', 29 | // @ts-expect-error 30 | additional: 'b' 31 | } satisfies typeof shape.static 32 | 33 | const expected = { 34 | name: 'salt', 35 | info: { 36 | alias: 'salty', 37 | optional2: 'ok' 38 | }, 39 | optional1: 'ok' 40 | } satisfies typeof shape.static 41 | 42 | isEqual(shape, value, expected) 43 | }) 44 | 45 | it('handle array object with optional', () => { 46 | const shape = t.Array( 47 | t.Object({ 48 | name: t.String(), 49 | optional1: t.Optional(t.String()), 50 | optional2: t.Optional(t.String()) 51 | }) 52 | ) 53 | 54 | const value = [ 55 | { 56 | name: 'salt', 57 | optional1: 'ok', 58 | // @ts-expect-error 59 | additional: 'b' 60 | }, 61 | { 62 | name: 'chiffon' 63 | } 64 | ] satisfies typeof shape.static 65 | 66 | const expected = [ 67 | { 68 | name: 'salt', 69 | optional1: 'ok' 70 | }, 71 | { 72 | name: 'chiffon' 73 | } 74 | ] satisfies typeof shape.static 75 | 76 | isEqual(shape, value, expected) 77 | }) 78 | 79 | it('handle array nested object with optional', () => { 80 | const shape = t.Array( 81 | t.Object({ 82 | name: t.String(), 83 | info: t.Object({ 84 | alias: t.String(), 85 | optional1: t.Optional(t.String()), 86 | optional2: t.Optional(t.String()) 87 | }), 88 | optional1: t.Optional(t.String()), 89 | optional2: t.Optional(t.String()) 90 | }) 91 | ) 92 | 93 | const value = [ 94 | { 95 | name: 'salt', 96 | additional: 'b', 97 | info: { 98 | alias: 'salty', 99 | // @ts-expect-error 100 | additional: 'b', 101 | optional2: 'ok' 102 | }, 103 | optional1: 'ok', 104 | // @ts-expect-error 105 | additional: 'b' 106 | }, 107 | { 108 | name: 'chiffon', 109 | info: { 110 | alias: 'chiffon' 111 | } 112 | } 113 | ] satisfies typeof shape.static 114 | 115 | const expected = [ 116 | { 117 | name: 'salt', 118 | info: { 119 | alias: 'salty', 120 | optional2: 'ok' 121 | }, 122 | optional1: 'ok' 123 | }, 124 | { 125 | name: 'chiffon', 126 | info: { 127 | alias: 'chiffon' 128 | } 129 | } 130 | ] satisfies typeof shape.static 131 | 132 | isEqual(shape, value, expected) 133 | }) 134 | }) 135 | -------------------------------------------------------------------------------- /test/record.test.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | 3 | import { describe, it } from 'bun:test' 4 | import { isEqual } from './utils' 5 | 6 | describe('Record', () => { 7 | it('handle record', () => { 8 | const shape = t.Record(t.String(), t.String()) 9 | 10 | isEqual(shape, { 11 | a: 'a', 12 | b: 'b' 13 | }) 14 | isEqual(shape, { a: 'a', b: 'b' }) 15 | }) 16 | 17 | it('handle record object', () => { 18 | const shape = t.Record( 19 | t.String(), 20 | t.Object({ 21 | a: t.String(), 22 | b: t.Optional(t.String()) 23 | }) 24 | ) 25 | 26 | isEqual( 27 | shape, 28 | { 29 | a: { a: 'a' }, 30 | b: { a: 'a', b: 'b' }, 31 | // @ts-expect-error 32 | c: { a: 'a', b: 'b', c: 'c' } 33 | }, 34 | { 35 | a: { a: 'a' }, 36 | b: { a: 'a', b: 'b' }, 37 | c: { a: 'a', b: 'b' } 38 | } 39 | ) 40 | }) 41 | 42 | it('handle record array', () => { 43 | const shape = t.Record(t.String(), t.Array(t.String())) 44 | 45 | isEqual(shape, { 46 | a: ['a'], 47 | b: ['a', 'b'] 48 | }) 49 | isEqual(shape, { a: ['a'], b: ['a', 'b'] }) 50 | }) 51 | 52 | it('handle nested record', () => { 53 | const shape = t.Record(t.String(), t.Record(t.String(), t.String())) 54 | 55 | isEqual(shape, { 56 | a: { a: 'a' }, 57 | b: { a: 'a', b: 'b' } 58 | }) 59 | isEqual(shape, { a: { a: 'a' }, b: { a: 'a', b: 'b' } }) 60 | }) 61 | }) 62 | -------------------------------------------------------------------------------- /test/recursive.test.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | 3 | import { describe, it, expect } from 'bun:test' 4 | import { isEqual } from './utils' 5 | 6 | describe('Recursive', () => { 7 | it('exit from union', () => { 8 | const shape = t.Recursive( 9 | (This) => 10 | t.Object({ 11 | type: t.String(), 12 | a: t.Optional(This) 13 | }), 14 | { $id: 'Node' } 15 | ) 16 | 17 | const value = { 18 | type: 'a', 19 | a: { 20 | type: 'b', 21 | a: { 22 | type: 'c' 23 | } 24 | } 25 | } satisfies typeof shape.static 26 | }) 27 | 28 | it('exit from array', () => { 29 | const shape = t.Recursive( 30 | (This) => 31 | t.Object({ 32 | type: t.String(), 33 | a: t.Array(This) 34 | }), 35 | { $id: 'Node' } 36 | ) 37 | 38 | const value = { 39 | type: 'a', 40 | a: [ 41 | { 42 | type: 'a', 43 | a: [] 44 | } 45 | ] 46 | } 47 | 48 | isEqual(shape, value) 49 | }) 50 | 51 | it('handle reference in array', () => { 52 | const shape = t.Recursive((This) => 53 | t.Object({ 54 | type: t.String(), 55 | data: t.Array(This) 56 | }) 57 | ) 58 | 59 | const value = { 60 | type: 'yea', 61 | data: [ 62 | { 63 | type: 'ok', 64 | data: [ 65 | { 66 | type: 'cool', 67 | data: [] 68 | } 69 | ] 70 | } 71 | ] 72 | } satisfies typeof shape.static 73 | 74 | isEqual(shape, value) 75 | }) 76 | 77 | it('handle reference of an union in an array', () => { 78 | const shape = t.Recursive((This) => 79 | t.Object({ 80 | type: t.String(), 81 | data: t.Union([t.Nullable(This), t.Array(This)]) 82 | }) 83 | ) 84 | 85 | const value = { 86 | type: 'yea', 87 | data: { 88 | type: 'ok', 89 | data: [ 90 | { 91 | type: 'cool', 92 | data: null 93 | } 94 | ] 95 | } 96 | } satisfies typeof shape.static 97 | 98 | isEqual(shape, value) 99 | }) 100 | }) 101 | -------------------------------------------------------------------------------- /test/ref.test.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | 3 | import createMirror from '../src' 4 | 5 | import { TypeCompiler } from '@sinclair/typebox/compiler' 6 | 7 | import { describe, it, expect } from 'bun:test' 8 | import { isEqual } from './utils' 9 | 10 | describe('Ref', () => { 11 | it('handle module', () => { 12 | const modules = t.Module({ 13 | object: t.Object({ 14 | name: t.String(), 15 | optional: t.Optional(t.String()) 16 | }) 17 | }) 18 | 19 | const shape = modules.Import('object') 20 | 21 | const value = { 22 | name: 'salt' 23 | } satisfies typeof shape.static 24 | 25 | isEqual(shape, value) 26 | }) 27 | 28 | it('handle nested ref', () => { 29 | const modules = t.Module({ 30 | object: t.Object({ 31 | name: t.String(), 32 | info: t.Ref('info') 33 | }), 34 | info: t.Object({ 35 | id: t.Number(), 36 | name: t.String() 37 | }) 38 | }) 39 | 40 | const shape = modules.Import('object') 41 | 42 | const value = { 43 | name: 'salt', 44 | info: { 45 | id: 123, 46 | name: 'salt' 47 | } 48 | } satisfies typeof shape.static 49 | 50 | isEqual(shape, value) 51 | }) 52 | 53 | it('handle optional ref', () => { 54 | const modules = t.Module({ 55 | object: t.Object({ 56 | name: t.String(), 57 | info: t.Optional(t.Ref('info')) 58 | }), 59 | info: t.Object({ 60 | id: t.Number(), 61 | name: t.String() 62 | }) 63 | }) 64 | 65 | const shape = modules.Import('object') 66 | 67 | const value = { 68 | name: 'salt' 69 | } satisfies typeof shape.static 70 | 71 | isEqual(shape, { 72 | name: 'salt' 73 | }) 74 | 75 | isEqual(shape, { 76 | name: 'salt', 77 | info: { 78 | id: 123, 79 | name: 'salt' 80 | } 81 | }) 82 | }) 83 | 84 | it('handle custom modules', () => { 85 | const definitions = { 86 | object: t.Object({ 87 | name: t.String(), 88 | optional: t.Optional(t.String()) 89 | }) 90 | } 91 | 92 | const shape = definitions.object 93 | 94 | const value = { 95 | name: 'salt' 96 | } satisfies typeof shape.static 97 | 98 | expect( 99 | createMirror(shape, { 100 | definitions 101 | })(value) 102 | ).toEqual(value) 103 | }) 104 | 105 | it('handle recursion', () => { 106 | const shape = t.Module({ 107 | a: t.Object({ type: t.String(), a: t.Nullable(t.Ref('a')) }) 108 | }) 109 | 110 | const actual = shape.Import('a') 111 | 112 | const value = { 113 | type: 'a', 114 | a: { 115 | type: 'a', 116 | a: { 117 | type: 'a', 118 | a: null 119 | } 120 | } 121 | } satisfies typeof actual.static 122 | 123 | expect( 124 | createMirror(actual, { 125 | TypeCompiler, 126 | modules: shape 127 | })(value) 128 | ).toEqual(value) 129 | }) 130 | 131 | it('handle recusion array', () => { 132 | const shape = t.Module({ 133 | a: t.Object({ type: t.String(), a: t.Array(t.Ref('a')) }) 134 | }) 135 | 136 | const actual = shape.Import('a') 137 | 138 | const value = { 139 | type: 'a', 140 | a: [ 141 | { type: 'a', a: [{ type: 'a', a: [] }] }, 142 | { type: 'a', a: [{ type: 'a', a: [] }] } 143 | ] 144 | } satisfies typeof actual.static 145 | 146 | expect( 147 | createMirror(actual, { 148 | TypeCompiler, 149 | modules: shape 150 | })(value) 151 | ).toEqual(value) 152 | }) 153 | 154 | it('handle', () => { 155 | const shape = t.Module({ 156 | a: t.Object({ 157 | type: t.String(), 158 | data: t.Union([t.Nullable(t.Ref('a')), t.Array(t.Ref('a'))]) 159 | }) 160 | }) 161 | 162 | const actual = shape.Import('a') 163 | 164 | const value = { 165 | type: 'yea', 166 | data: { 167 | type: 'ok', 168 | data: [ 169 | { 170 | type: 'cool', 171 | data: null 172 | } 173 | ] 174 | } 175 | } satisfies typeof actual.static 176 | 177 | expect( 178 | createMirror(actual, { 179 | TypeCompiler, 180 | modules: shape 181 | })(value) 182 | ).toEqual(value) 183 | }) 184 | }) 185 | -------------------------------------------------------------------------------- /test/sample.test.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | 3 | import { describe, it } from 'bun:test' 4 | import { isEqualToTypeBox } from './utils' 5 | 6 | describe('Sample', () => { 7 | it('small', () => { 8 | isEqualToTypeBox( 9 | t.Object({ 10 | id: t.Number(), 11 | name: t.String(), 12 | bio: t.String(), 13 | metadata: t.Object({ 14 | alias: t.String(), 15 | country: t.String() 16 | }) 17 | }), 18 | { 19 | id: 1, 20 | name: 'SaltyAom', 21 | bio: 'I like train', 22 | metadata: { 23 | alias: 'SaltyAom', 24 | country: 'Thailand' 25 | } 26 | } 27 | ) 28 | }) 29 | 30 | it('medium', () => { 31 | isEqualToTypeBox( 32 | t.Object({ 33 | id: t.Number(), 34 | name: t.Literal('SaltyAom'), 35 | bio: t.String({ 36 | sanitize: true 37 | }), 38 | user: t.Object({ 39 | name: t.String(), 40 | password: t.String() 41 | }), 42 | playing: t.Optional(t.String()), 43 | games: t.Array( 44 | t.Object({ 45 | name: t.String(), 46 | hoursPlay: t.Number({ default: 0 }), 47 | tags: t.Array(t.String()) 48 | }) 49 | ), 50 | metadata: t.Intersect([ 51 | t.Object({ 52 | alias: t.String() 53 | }), 54 | t.Object({ 55 | country: t.Nullable(t.String()) 56 | }) 57 | ]), 58 | social: t.Optional( 59 | t.Object({ 60 | facebook: t.Optional(t.String()), 61 | twitter: t.Optional(t.String()), 62 | youtube: t.Optional(t.String()) 63 | }) 64 | ) 65 | }), 66 | { 67 | id: 1, 68 | name: 'SaltyAom', 69 | bio: 'I like train\nhere', 70 | user: { 71 | name: 'SaltyAom', 72 | password: '123456' 73 | }, 74 | games: [ 75 | { 76 | name: 'MiSide', 77 | hoursPlay: 17, 78 | tags: ['Psychological Horror', 'Cute', 'Dating Sim'] 79 | }, 80 | { 81 | name: 'Strinova', 82 | hoursPlay: 365, 83 | tags: ['Free to Play', 'Anime', 'Third-Person Shooter'] 84 | }, 85 | { 86 | name: "Tom Clancy's Rainbow Six Siege", 87 | hoursPlay: 287, 88 | tags: ['FPS', 'Multiplayer', 'Tactical'] 89 | } 90 | ], 91 | metadata: { 92 | alias: 'SaltyAom', 93 | country: 'Thailand' 94 | }, 95 | social: { 96 | twitter: 'SaltyAom' 97 | } 98 | } 99 | ) 100 | }) 101 | 102 | it('large', () => { 103 | isEqualToTypeBox( 104 | t.Array( 105 | t.Object({ 106 | id: t.Number(), 107 | name: t.String(), 108 | bio: t.String(), 109 | user: t.Object({ 110 | name: t.String(), 111 | password: t.String(), 112 | email: t.Optional(t.String({ format: 'email' })), 113 | age: t.Optional(t.Number()), 114 | avatar: t.Optional(t.String({ format: 'uri' })), 115 | cover: t.Optional(t.String({ format: 'uri' })) 116 | }), 117 | playing: t.Optional(t.String()), 118 | wishlist: t.Optional(t.Array(t.Number())), 119 | games: t.Array( 120 | t.Object({ 121 | id: t.Number(), 122 | name: t.String(), 123 | hoursPlay: t.Optional(t.Number({ default: 0 })), 124 | tags: t.Array( 125 | t.Object({ 126 | name: t.String(), 127 | count: t.Number() 128 | }) 129 | ) 130 | }) 131 | ), 132 | metadata: t.Intersect([ 133 | t.Object({ 134 | alias: t.String() 135 | }), 136 | t.Object({ 137 | country: t.Nullable(t.String()), 138 | region: t.Optional(t.String()) 139 | }) 140 | ]), 141 | social: t.Optional( 142 | t.Object({ 143 | facebook: t.Optional(t.String()), 144 | twitter: t.Optional(t.String()), 145 | youtube: t.Optional(t.String()) 146 | }) 147 | ) 148 | }) 149 | ), 150 | [ 151 | { 152 | id: 1, 153 | name: 'SaltyAom', 154 | bio: 'I like train', 155 | user: { 156 | name: 'SaltyAom', 157 | password: '123456', 158 | avatar: 'https://avatars.githubusercontent.com/u/35027979?v=4', 159 | cover: 'https://saltyaom.com/cosplay/pekomama.webp' 160 | }, 161 | playing: 'Strinova', 162 | wishlist: [4_154_456, 2_345_345], 163 | games: [ 164 | { 165 | id: 4_154_456, 166 | name: 'MiSide', 167 | hoursPlay: 17, 168 | tags: [ 169 | { 170 | name: 'Psychological Horror', 171 | count: 236_432 172 | }, 173 | { name: 'Cute', count: 495_439 }, 174 | { name: 'Dating Sim', count: 395_532 } 175 | ] 176 | }, 177 | { 178 | id: 4_356_345, 179 | name: 'Strinova', 180 | hoursPlay: 365, 181 | tags: [ 182 | { name: 'Free to Play', count: 205_593 }, 183 | { name: 'Anime', count: 504_304 }, 184 | { name: 'Third-Person Shooter', count: 395_532 } 185 | ] 186 | }, 187 | { 188 | id: 2_345_345, 189 | name: "Tom Clancy's Rainbow Six Siege", 190 | hoursPlay: 287, 191 | tags: [ 192 | { name: 'FPS', count: 855_324 }, 193 | { name: 'Multiplayer', count: 456_567 }, 194 | { name: 'Tactical', count: 544_467 } 195 | ] 196 | } 197 | ], 198 | metadata: { 199 | alias: 'SaltyAom', 200 | country: 'Thailand', 201 | region: 'Asia' 202 | }, 203 | social: { 204 | twitter: 'SaltyAom' 205 | } 206 | }, 207 | { 208 | id: 2, 209 | name: 'VLost', 210 | bio: 'ไม่พี่คืองี้', 211 | user: { 212 | name: 'nattapon_kub', 213 | password: '123456' 214 | }, 215 | games: [ 216 | { 217 | id: 4_154_456, 218 | name: 'MiSide', 219 | hoursPlay: 17, 220 | tags: [ 221 | { 222 | name: 'Psychological Horror', 223 | count: 236_432 224 | }, 225 | { name: 'Cute', count: 495_439 }, 226 | { name: 'Dating Sim', count: 395_532 } 227 | ] 228 | }, 229 | { 230 | id: 4_356_345, 231 | name: 'Strinova', 232 | hoursPlay: 365, 233 | tags: [ 234 | { name: 'Free to Play', count: 205_593 }, 235 | { name: 'Anime', count: 504_304 }, 236 | { name: 'Third-Person Shooter', count: 395_532 } 237 | ] 238 | } 239 | ], 240 | metadata: { 241 | alias: 'vlost', 242 | country: 'Thailand' 243 | } 244 | }, 245 | { 246 | id: 2, 247 | name: 'eika', 248 | bio: 'こんにちわ!', 249 | user: { 250 | name: 'ei_ka', 251 | password: '123456' 252 | }, 253 | games: [ 254 | { 255 | id: 4_356_345, 256 | name: 'Strinova', 257 | hoursPlay: 365, 258 | tags: [ 259 | { name: 'Free to Play', count: 205_593 }, 260 | { name: 'Anime', count: 504_304 }, 261 | { name: 'Third-Person Shooter', count: 395_532 } 262 | ] 263 | } 264 | ], 265 | metadata: { 266 | alias: 'eika', 267 | country: 'Japan' 268 | } 269 | } 270 | ] 271 | ) 272 | }) 273 | }) 274 | -------------------------------------------------------------------------------- /test/sanitize.test.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | import { TypeCompiler } from '@sinclair/typebox/compiler' 3 | 4 | import { describe, expect, it } from 'bun:test' 5 | import createMirror from '../src' 6 | 7 | describe('sanitize', () => { 8 | it('handle single sanitizer', () => { 9 | const shape = t.Object({ 10 | hello: t.String(), 11 | world: t.String() 12 | }) 13 | 14 | const value = { 15 | hello: 'Hello', 16 | world: 'World' 17 | } satisfies typeof shape.static 18 | 19 | expect( 20 | createMirror(shape, { 21 | sanitize: [(v) => (v === 'Hello' ? 'Hi' : v)] 22 | })(value) 23 | ).toEqual({ 24 | hello: 'Hi', 25 | world: 'World' 26 | }) 27 | }) 28 | 29 | it('handle multiple sanitizers', () => { 30 | const shape = t.Object({ 31 | hello: t.String(), 32 | world: t.String() 33 | }) 34 | 35 | const value = { 36 | hello: 'Hello', 37 | world: 'World' 38 | } satisfies typeof shape.static 39 | 40 | expect( 41 | createMirror(shape, { 42 | sanitize: [ 43 | (v) => (v === 'Hello' ? 'Hi' : v), 44 | (v) => (v === 'World' ? 'Salty' : v) 45 | ] 46 | })(value) 47 | ).toEqual({ 48 | hello: 'Hi', 49 | world: 'Salty' 50 | }) 51 | }) 52 | 53 | it('handle sanitize as function', () => { 54 | const shape = t.Object({ 55 | hello: t.String(), 56 | world: t.String() 57 | }) 58 | 59 | const value = { 60 | hello: 'Hello', 61 | world: 'World' 62 | } satisfies typeof shape.static 63 | 64 | expect( 65 | createMirror(shape, { 66 | sanitize: (v) => (v === 'Hello' ? 'Hi' : v) 67 | })(value) 68 | ).toEqual({ 69 | hello: 'Hi', 70 | world: 'World' 71 | }) 72 | }) 73 | 74 | it('handle top-level string', () => { 75 | const shape = t.String() 76 | 77 | const value = 'Hello' satisfies typeof shape.static 78 | 79 | expect( 80 | createMirror(shape, { 81 | sanitize: (v) => (v === 'Hello' ? 'Hi' : v) 82 | })(value) 83 | ).toBe('Hi') 84 | }) 85 | 86 | it('handle nested string', () => { 87 | const shape = t.Object({ 88 | hello: t.String(), 89 | detail: t.Object({ 90 | world: t.String() 91 | }) 92 | }) 93 | 94 | const value = { 95 | hello: 'Hello', 96 | detail: { world: 'World' } 97 | } satisfies typeof shape.static 98 | 99 | expect( 100 | createMirror(shape, { 101 | sanitize: [ 102 | (v) => (v === 'Hello' ? 'Hi' : v), 103 | (v) => (v === 'World' ? 'Salty' : v) 104 | ] 105 | })(value) 106 | ).toEqual({ 107 | hello: 'Hi', 108 | detail: { 109 | world: 'Salty' 110 | } 111 | }) 112 | }) 113 | 114 | it('handle union', async () => { 115 | const shape = t.Object({ 116 | hello: t.String(), 117 | detail: t.Union([ 118 | t.Object({ 119 | world: t.String() 120 | }), 121 | t.Object({ 122 | world2: t.String() 123 | }) 124 | ]) 125 | }) 126 | 127 | const mirror = createMirror(shape, { 128 | sanitize: [ 129 | (v) => (v === 'Hello' ? 'Hi' : v), 130 | (v) => (v === 'World' ? 'Salty' : v) 131 | ], 132 | TypeCompiler 133 | }) 134 | 135 | const value1 = { 136 | hello: 'Hello', 137 | detail: { world: 'World' } 138 | } satisfies typeof shape.static 139 | 140 | expect(mirror(value1)).toEqual({ 141 | hello: 'Hi', 142 | detail: { 143 | world: 'Salty' 144 | } 145 | }) 146 | 147 | const value2 = { 148 | hello: 'Hello', 149 | detail: { world2: 'World' } 150 | } satisfies typeof shape.static 151 | 152 | expect(mirror(value2)).toEqual({ 153 | hello: 'Hi', 154 | detail: { 155 | world2: 'Salty' 156 | } 157 | }) 158 | }) 159 | }) 160 | -------------------------------------------------------------------------------- /test/tuple.test.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | 3 | import { describe, it } from 'bun:test' 4 | import { isEqual } from './utils' 5 | 6 | describe('Tuple', () => { 7 | it('handle tuple', () => { 8 | const shape = t.Tuple([t.Literal('a'), t.Literal('b')]) 9 | 10 | isEqual(shape, ['a', 'b']) 11 | // @ts-expect-error 12 | isEqual(shape, ['a', 'b', 'c'], ['a', 'b']) 13 | }) 14 | 15 | it('handle tuple object', () => { 16 | const shape = t.Tuple([ 17 | t.Literal('a'), 18 | t.Object({ 19 | a: t.String(), 20 | b: t.Optional(t.String()) 21 | }) 22 | ]) 23 | 24 | isEqual(shape, [ 25 | 'a', 26 | { 27 | a: 'a' 28 | } 29 | ]) 30 | isEqual(shape, [ 31 | 'a', 32 | { 33 | a: 'a', 34 | b: 'b' 35 | } 36 | ]) 37 | 38 | isEqual( 39 | shape, 40 | [ 41 | 'a', 42 | { 43 | a: 'a', 44 | b: 'b', 45 | // @ts-expect-error 46 | c: 'c' 47 | } 48 | ], 49 | [ 50 | 'a', 51 | { 52 | a: 'a', 53 | b: 'b' 54 | } 55 | ] 56 | ) 57 | }) 58 | 59 | it('handle tuple array', () => { 60 | const shape = t.Tuple([t.Literal('a'), t.Array(t.String())]) 61 | 62 | isEqual(shape, ['a', ['a', 'b']]) 63 | // @ts-expect-error 64 | // isEqual(shape, ['a']) 65 | }) 66 | 67 | it('handle nested tuple', () => { 68 | const shape = t.Tuple([ 69 | t.Literal('a'), 70 | t.Tuple([t.Literal('b'), t.Literal('c')]) 71 | ]) 72 | 73 | isEqual(shape, ['a', ['b', 'c']]) 74 | }) 75 | }) 76 | -------------------------------------------------------------------------------- /test/union.test.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | 3 | import { describe, it } from 'bun:test' 4 | 5 | import { isEqual, notEqual, isUndefined } from './utils' 6 | 7 | describe('Union', () => { 8 | it('handle union at root', () => { 9 | const shape = t.Union([t.String(), t.Number()]) 10 | 11 | isEqual(shape, 'saltyaom') 12 | isEqual(shape, 1) 13 | // @ts-expect-error 14 | isEqual(shape, true) 15 | }) 16 | 17 | it('handle union object', () => { 18 | const shape = t.Union([ 19 | t.Object({ 20 | a: t.String() 21 | }), 22 | t.Object( 23 | { 24 | b: t.String() 25 | }, 26 | { 27 | additionalProperties: true 28 | } 29 | ), 30 | t.Number() 31 | ]) 32 | 33 | isEqual(shape, { 34 | a: 'a' 35 | }) 36 | notEqual(shape, { 37 | a: 'a', 38 | b: 'b' 39 | }) 40 | 41 | isEqual(shape, { 42 | b: 'a', 43 | // @ts-expect-error 44 | c: 'c' 45 | }) 46 | notEqual( 47 | shape, 48 | { 49 | b: 'a', 50 | // @ts-expect-error 51 | c: 'c' 52 | }, 53 | { 54 | b: 'a' 55 | } 56 | ) 57 | 58 | isEqual(shape, 1) 59 | 60 | // @ts-expect-error 61 | isEqual(shape, 'a') 62 | }) 63 | 64 | it('handle union object with optional', () => { 65 | const shape = t.Union([ 66 | t.Object({ 67 | a: t.String(), 68 | d: t.Optional(t.String()) 69 | }), 70 | t.Object( 71 | { 72 | b: t.String() 73 | }, 74 | { 75 | additionalProperties: true 76 | } 77 | ), 78 | t.Number() 79 | ]) 80 | 81 | isEqual(shape, { 82 | a: 'a' 83 | }) 84 | notEqual(shape, { 85 | a: 'a', 86 | b: 'b' 87 | }) 88 | 89 | isEqual(shape, { 90 | a: 'a', 91 | d: 'd' 92 | }) 93 | notEqual(shape, { 94 | a: 'a', 95 | b: 'b', 96 | d: 'd' 97 | }) 98 | 99 | isEqual(shape, { 100 | b: 'a', 101 | // @ts-expect-error 102 | c: 'c' 103 | }) 104 | notEqual( 105 | shape, 106 | { 107 | b: 'a', 108 | // @ts-expect-error 109 | c: 'c' 110 | }, 111 | { 112 | b: 'a' 113 | } 114 | ) 115 | 116 | isEqual(shape, 1) 117 | 118 | // @ts-expect-error 119 | isEqual(shape, 'a') 120 | }) 121 | 122 | it('handle nested union', () => { 123 | const shape = t.Union([ 124 | t.Object({ 125 | a: t.String() 126 | }), 127 | t.Object( 128 | { 129 | b: t.String() 130 | }, 131 | { 132 | additionalProperties: true 133 | } 134 | ), 135 | t.Number() 136 | ]) 137 | 138 | isEqual(shape, { 139 | a: 'a' 140 | }) 141 | notEqual(shape, { 142 | a: 'a', 143 | b: 'b' 144 | }) 145 | 146 | isEqual(shape, { 147 | b: 'a', 148 | // @ts-expect-error 149 | c: 'c' 150 | }) 151 | notEqual( 152 | shape, 153 | { 154 | b: 'a', 155 | // @ts-expect-error 156 | c: 'c' 157 | }, 158 | { 159 | b: 'a' 160 | } 161 | ) 162 | 163 | isEqual(shape, 1) 164 | 165 | // @ts-expect-error 166 | isEqual(shape, 'a') 167 | }) 168 | 169 | it('handle undefined union', () => { 170 | const shape = t.Union([ 171 | t.Undefined(), 172 | t.Object({ 173 | name: t.String(), 174 | job: t.String(), 175 | trait: t.Optional(t.String()) 176 | }) 177 | ]) 178 | 179 | isEqual(shape, undefined) 180 | 181 | isEqual(shape, { 182 | name: 'a', 183 | job: 'b', 184 | trait: 'c' 185 | }) 186 | 187 | isEqual(shape, { 188 | name: 'a', 189 | job: 'b' 190 | }) 191 | }) 192 | 193 | it('return shape regardless of correctness', () => { 194 | const shape = t.Object({ 195 | foo: t.Optional( 196 | t.Nullable( 197 | t.Object({ 198 | a: t.Number({ 199 | error: 'Must be a number' 200 | }) 201 | }) 202 | ) 203 | ) 204 | }) 205 | 206 | const value = { 207 | // @ts-expect-error 208 | foo: 123 209 | } satisfies typeof shape.static 210 | 211 | isEqual( 212 | shape, 213 | // @ts-expect-error 214 | value 215 | ) 216 | }) 217 | }) 218 | -------------------------------------------------------------------------------- /test/utils.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'elysia' 2 | 3 | import { Value } from '@sinclair/typebox/value' 4 | import { TypeCompiler } from '@sinclair/typebox/compiler' 5 | import { type TAnySchema } from '@sinclair/typebox' 6 | 7 | import { createMirror } from '../src' 8 | 9 | import { expect } from 'bun:test' 10 | 11 | export const isEqual = ( 12 | shape: T, 13 | value: T['static'], 14 | expected: T['static'] = value 15 | ) => 16 | expect( 17 | createMirror(shape, { 18 | TypeCompiler 19 | })(value) 20 | ).toEqual(expected) 21 | 22 | export const notEqual = ( 23 | shape: T, 24 | value: T['static'], 25 | expected: T['static'] = value 26 | ) => 27 | expect( 28 | createMirror(shape, { 29 | TypeCompiler 30 | })(value) 31 | ).not.toEqual(expected) 32 | 33 | export const isUndefined = ( 34 | shape: T, 35 | value: T['static'] 36 | ) => 37 | expect( 38 | createMirror(shape, { 39 | TypeCompiler 40 | })(value) 41 | ).toEqual(undefined) 42 | 43 | export const isEqualToTypeBox = ( 44 | shape: T, 45 | value: T['static'], 46 | expected: T['static'] = value 47 | ) => 48 | expect( 49 | createMirror(shape, { 50 | TypeCompiler 51 | })(value) 52 | ).toEqual(Value.Clean(shape, value)) 53 | -------------------------------------------------------------------------------- /tsconfig.dts.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "preserveSymlinks": true, 4 | /* Visit https://aka.ms/tsconfig to read more about this file */ 5 | 6 | /* Projects */ 7 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 8 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 9 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 10 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 11 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 12 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 13 | 14 | /* Language and Environment */ 15 | "target": "ES2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 16 | "lib": ["ESNext"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 17 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 18 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 19 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 20 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 21 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 22 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 23 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 24 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 25 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 26 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 27 | 28 | /* Modules */ 29 | "module": "ES2022", /* Specify what module code is generated. */ 30 | "rootDir": "./src", /* Specify the root folder within your source files. */ 31 | "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 32 | // "baseUrl": "./src", /* Specify the base directory to resolve non-relative module names. */ 33 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 34 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 35 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 36 | // "types": ["bun-types"], /* Specify type package names to be included without being referenced in a source file. */ 37 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 38 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 39 | "resolveJsonModule": true, /* Enable importing .json files. */ 40 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 41 | 42 | /* JavaScript Support */ 43 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 44 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 45 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 46 | 47 | /* Emit */ 48 | "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 49 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 50 | "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 51 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 52 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 53 | "outDir": "./dist", /* Specify an output folder for all emitted files. */ 54 | // "removeComments": true, /* Disable emitting comments. */ 55 | // "noEmit": true, /* Disable emitting files from a compilation. */ 56 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 57 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 58 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 59 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 60 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 61 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 62 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 63 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 64 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 65 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 66 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 67 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 68 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 69 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 70 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 71 | 72 | /* Interop Constraints */ 73 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 74 | "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 75 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 76 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 77 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 78 | 79 | /* Type Checking */ 80 | "strict": true, /* Enable all strict type-checking options. */ 81 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 82 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 83 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 84 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 85 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 86 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 87 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 88 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 89 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 90 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 91 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 92 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 93 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 94 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 95 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 96 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 97 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 98 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 99 | 100 | /* Completeness */ 101 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 102 | "skipLibCheck": true, /* Skip type checking all .d.ts files. */ 103 | }, 104 | "exclude": ["node_modules", "test", "example", "dist", "build.ts", "benchmarks"] 105 | // "include": ["src/**/*"] 106 | } 107 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "ES2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | "lib": ["ESNext", "DOM", "ScriptHost"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "ES2022", /* Specify what module code is generated. */ 29 | // "rootDir": "./src", /* Specify the root folder within your source files. */ 30 | "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./src", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": ["bun-types"], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | // "resolveJsonModule": true, /* Enable importing .json files. */ 39 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 40 | 41 | /* JavaScript Support */ 42 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 43 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 44 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 45 | 46 | /* Emit */ 47 | "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 48 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 49 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 50 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 51 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 52 | // "outDir": "./dist", /* Specify an output folder for all emitted files. */ 53 | // "removeComments": true, /* Disable emitting comments. */ 54 | "noEmit": true, /* Disable emitting files from a compilation. */ 55 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 56 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 57 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 58 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 60 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 61 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 62 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 63 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 64 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 65 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 66 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 67 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 68 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 69 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 70 | 71 | /* Interop Constraints */ 72 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 73 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 74 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 75 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 76 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 77 | 78 | /* Type Checking */ 79 | "strict": true, /* Enable all strict type-checking options. */ 80 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 81 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 82 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 83 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 84 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 85 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 86 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 87 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 88 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 89 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 90 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 91 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 92 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 93 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 94 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 95 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 96 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 97 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 98 | 99 | /* Completeness */ 100 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 101 | "skipLibCheck": true, /* Skip type checking all .d.ts files. */ 102 | }, 103 | // "include": ["src/**/*"] 104 | } 105 | --------------------------------------------------------------------------------