├── .github └── workflows │ ├── publish.yml │ └── pull-request.yml ├── .gitignore ├── .npmignore ├── .prettierignore ├── LICENSE ├── LOG ├── README.md ├── bench ├── basic │ ├── banditypes.ts │ ├── raw.ts │ ├── sample.ts │ ├── superstruct.ts │ └── zod.ts ├── full │ ├── banditypes.ts │ ├── raw.ts │ ├── sample.ts │ ├── superstruct.ts │ └── zod.ts ├── minimal │ ├── banditypes.ts │ ├── raw.ts │ ├── superstruct.ts │ └── zod.ts └── run.js ├── package-lock.json ├── package.json ├── src └── index.ts ├── tests ├── index.test.ts ├── tsconfig.dts.json ├── tsconfig.json ├── typings │ ├── Banditype.ts │ ├── any.ts │ ├── array.ts │ ├── assign.ts │ ├── boolean.ts │ ├── coerce.ts │ ├── date.ts │ ├── defaulted.ts │ ├── empty.ts │ ├── enums.ts │ ├── func.ts │ ├── infer.ts │ ├── instance.ts │ ├── integer.ts │ ├── lazy.ts │ ├── literal.ts │ ├── map.ts │ ├── max.ts │ ├── min.ts │ ├── never.ts │ ├── nullable.ts │ ├── number.ts │ ├── object.ts │ ├── objectLoose.ts │ ├── optional.ts │ ├── pattern.ts │ ├── record.ts │ ├── refine.ts │ ├── regexp.ts │ ├── set.ts │ ├── string.ts │ ├── struct.ts │ ├── trimmed.ts │ ├── tuple.ts │ ├── union.ts │ └── unknown.ts └── validation │ ├── array │ ├── invalid-element-property.ts │ ├── invalid-element.ts │ ├── invalid-opaque.ts │ ├── invalid.ts │ ├── valid-opaque.ts │ └── valid.ts │ ├── boolean │ ├── invalid.ts │ └── valid.ts │ ├── coerce │ ├── changed.ts │ ├── condition-not-met.ts │ └── unchanged.ts │ ├── defaulted │ ├── function.ts │ ├── nested.ts │ └── value.ts │ ├── enums │ ├── invalid-numbers.ts │ ├── invalid-strings.ts │ └── valid.ts │ ├── function │ ├── invalid.ts │ └── valid.ts │ ├── instance │ ├── invalid.ts │ └── valid.ts │ ├── lazy │ ├── invalid.ts │ ├── valid.ts │ └── with-refiners.ts │ ├── literal │ ├── invalid.ts │ └── valid.ts │ ├── map │ ├── invalid-opaque.ts │ ├── invalid-property.ts │ ├── invalid.ts │ ├── valid-opaque.ts │ └── valid.ts │ ├── never │ └── invalid.ts │ ├── nullable │ ├── invalid.ts │ ├── valid-defined-nested.ts │ ├── valid-defined.ts │ ├── valid-null-nested.ts │ └── valid-null.ts │ ├── number │ ├── invalid.ts │ └── valid.ts │ ├── object │ ├── invalid-element-nested.ts │ ├── invalid-opaque.ts │ ├── invalid-property-nested.ts │ ├── invalid-property.ts │ ├── invalid.ts │ ├── valid-nested.ts │ ├── valid-property-extra.ts │ └── valid.ts │ ├── objectLoose │ ├── invalid-property-nested.ts │ ├── invalid-property.ts │ ├── invalid.ts │ ├── valid-opaque.ts │ └── valid.ts │ ├── optional │ ├── invalid.ts │ ├── valid-defined-nested.ts │ ├── valid-defined.ts │ ├── valid-undefined-nested.ts │ └── valid-undefined.ts │ ├── record │ ├── invalid-property.ts │ ├── invalid.ts │ └── valid.ts │ ├── refine │ ├── invalid-multiple-refinements.ts │ ├── invalid-shorthand.ts │ ├── invalid.ts │ └── valid.ts │ ├── set │ ├── invalid-element.ts │ ├── invalid-opaque.ts │ ├── invalid.ts │ ├── valid-opaque.ts │ └── valid.ts │ ├── string │ ├── invalid.ts │ └── valid.ts │ ├── trimmed │ ├── invalid.ts │ └── valid.ts │ ├── tuple │ ├── invalid-element-missing.ts │ ├── invalid-element.ts │ ├── invalid.ts │ ├── valid-trim.ts │ └── valid.ts │ ├── union │ ├── coercion-object.ts │ ├── coercion-type.ts │ ├── coercion.ts │ ├── invalid.ts │ └── valid.ts │ └── unknown │ ├── valid-number.ts │ ├── valid-string.ts │ └── valid-undefined.ts ├── tsconfig.cjs.json └── tsconfig.json /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | newversion: 6 | description: "Version bump" 7 | type: choice 8 | options: 9 | - major 10 | - minor 11 | - patch 12 | required: true 13 | jobs: 14 | release: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: actions/setup-node@v4 19 | with: 20 | token: ${{ secrets.NPM_TOKEN }} 21 | registry-url: "https://registry.npmjs.org" 22 | node-version: 16 23 | cache: npm 24 | - run: npm ci 25 | - run: npm run build 26 | - run: git config --global user.email "v.klepov@gmail.com" 27 | - run: git config --global user.name "thoughtspile" 28 | - run: npm version ${{ inputs.newversion }} 29 | - run: git push --follow-tags 30 | - run: npm publish 31 | env: 32 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 33 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Test PR 2 | on: pull_request 3 | jobs: 4 | full: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v4 8 | - uses: actions/setup-node@v4 9 | with: 10 | node-version: 16 11 | cache: npm 12 | - run: npm ci 13 | - run: npm run lint 14 | - run: npm run test:coverage 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/tests 3 | tsconfig.json 4 | coverage 5 | tests 6 | bench 7 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Vladimir Klepov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LOG: -------------------------------------------------------------------------------- 1 | Base: 1282 / 485 2 | 3 | ES6: 855 / 462, lots of function() repetition 4 | 5 | Implement literalUnion via includes: 837 / 447 6 | 7 | Remove literal in favor of literalUnion([item]) : 809 / 431 8 | 9 | Remove null / undefined in favor of literalUnion([null]): 783 / 428 10 | 11 | !Remove string / number / boolean in favor of primitive(tag): 751 / 440 12 | !inlining effect 13 | 14 | remove anyObject typeof: 744 / 435 15 | 16 | loose object with runtime error on access: 713 / 422 17 | 18 | Array.isArray -> instance(Array): 688 / 408 19 | 20 | !check mode: 707 / 421 21 | !banditype -> unknown().and 22 | 23 | remove never — use plain fail(): 685 / 407 24 | 25 | function -> const for primitive: 658 / 397 26 | 27 | !spread banditype: 642 / 394 28 | !not cloning function in banditype 29 | 30 | instance-based primitives: 612 / 374 31 | 32 | throw new TypeError(message || 'BandiType error') -> null[message || 'BandiType error']: 595 / 364 33 | 34 | const -> var: 583 / 361 35 | 36 | !tuple via array 37 | !instanceof via sample 38 | - problematic sample 39 | - long "constructor" word 40 | !=== -> == 41 | !throw -> return null 42 | - inconvenient 43 | - uses optional chaining 44 | - -26 from baseline / -18 from obscure error 45 | - nullable items, fixing restores size 46 | !spread literal arg 47 | 48 | Important: 49 | - chain depth increases DCE passes 50 | - signature compatibility increases gzip ratio 51 | 52 | 53 | --- 54 | chain pipe / or: 108/216/399 55 | functional pipe / or: 51 /203/382 56 | curried pipe / or: 51 /201/381 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Banditypes — the mighty 400-byte validator 2 | 3 | Check if data conforms to a TS type at runtime — much like [zod,](https://zod.dev/) [yup](https://github.com/jquense/yup) or [superstruct](https://docs.superstructjs.org/), but in a tiny 400-byte package. Despite the small size, it's not a toy: 4 | 5 | - Passes the relevant superstruct test suite. 6 | - Rich built-in types: [maps, sets, tuples, literals,](#types) and generic [union types.](#or) 7 | - Mostly API-compatible with the established libraries. 8 | - Supports _both_ deriving TS types from schema _and_ [declaring a schema for an existing TS type.](#ts-first-schemas) 9 | - User-defined [types,](#cast-functions) [refinements, and conversions.](#map) 10 | - Decent performance — among the top libraries not using code generation. 11 | 12 | Banditypes is a 400-byte lib, tradeoffs have been made: 13 | 14 | - _No_ detailed errors with messages and paths, just a throw in a predictable location. 15 | - _No_ built-in refinements (empty, integer, etc.). 16 | - Compiled to ES2017: uses ...spreads and arrows. Can be transpiled further down. 17 | - Validation and conversion are mangled, so you have to use the returned object. "Pure validation" is impossible. 18 | - Some syntax might be a bit odd. 19 | 20 | > Small size is the primary focus of banditypes. It's the smallest validation library, AFAIK, and I'll do my best to keep the core under 400 bytes (unless some critical bugs need fixing, in which case it might go slightly above that). 21 | 22 | This is not a library for everybody, but it gets the job done, and it's small. Here's a usage example: 23 | 24 | ```ts 25 | import { 26 | assert, 27 | object, 28 | number, 29 | string, 30 | array, 31 | optional, 32 | fail, 33 | Infer, 34 | } from "banditypes"; 35 | 36 | const parseGunslinger = object({ 37 | name: string(), 38 | kills: number(), 39 | guns: array(string()), 40 | born: object({ 41 | state: string().or(optional()), 42 | year: number().map((n) => (Number.isInteger(n) ? n : fail())), 43 | }), 44 | }); 45 | 46 | // Explicit inference 47 | type Gunslinger = Infer; 48 | 49 | const raw = JSON.parse(`{ 50 | "name": "Dirty Bobby", 51 | "kills": 17, 52 | "guns": ["Colt 45"], 53 | "born": { 54 | "state": "Idaho", 55 | "year": 1872 56 | } 57 | }`); 58 | try { 59 | const data = parseGunslinger(raw); 60 | // fully type-safe access 61 | console.log(`${data.name} from ${data.born.state} is out to kill ya`); 62 | } catch (err) { 63 | console.log("invalid JSON"); 64 | } 65 | ``` 66 | 67 | 400 bytes is an _approximate_ gzip bundle increase from using _all_ built-in validations. It may vary based on the minifier and the amount of validations used. A typical usage (primitives + object + array) is closer to 200 bytes, the core is around 100. Find out more about the [measurement technique.](#size-measurement) 68 | 69 | If you like banditypes, check out [banditstash](https://github.com/thoughtspile/banditstash) — a tiny localStorage wrapper with runtime validation, fully configurable using plugins. 70 | 71 | ## Table of contents 72 | 73 | - [Install](#install) 74 | - [Types](#types) 75 | - [Operators](#operators) 76 | - [or](#or) 77 | - [map](#map) 78 | - [Cast functions](#cast-functions) 79 | - [TS-first schemas](#ts-first-schemas) 80 | - [Size measurement](#size-measurement) 81 | - [Acknowledgements](#acknowledgements) 82 | - [License (it's MIT)](#license) 83 | 84 | ## Install 85 | 86 | ```sh 87 | npm install --save banditypes 88 | ``` 89 | 90 | ## Types 91 | 92 | banditypes includes all the types you'd expect in a validation library: 93 | 94 | ```ts 95 | // primitives 96 | string(); 97 | number(); 98 | boolean(); 99 | 100 | // always fails 101 | never(); 102 | // always passes 103 | unknown(); 104 | 105 | // instanceof check 106 | instance(MyClass); 107 | 108 | // checks if value is a function 109 | // static input / output validation is not possible in JS 110 | func(); 111 | 112 | // { key: string; nullable: string | null; maybe?: string } 113 | object({ 114 | key: string(), 115 | // nullable field 116 | nullable: string().or(nullable()), 117 | // optional field 118 | maybe: string().or(optional()), 119 | }); 120 | // { key: string }, but don't remove other properties 121 | objectLoose({ 122 | key: string(), 123 | }); 124 | // number[] 125 | array(number()); 126 | // Record 127 | record(boolean()); 128 | 129 | // Set 130 | set(number()); 131 | // Map 132 | map(number(), boolean()); 133 | // [number, string] 134 | // NOTE: "as const" must be used 135 | tuple([number(), string()] as const); 136 | 137 | // value comes from a set 138 | enums([1, 2]); // infers 1 | 2 139 | // mixed-type enums are OK: 140 | enums([true, 0, ""]); 141 | // literal type is a single-value enum: 142 | enums([42]); 143 | ``` 144 | 145 | Every validator is just a function that returns the argument if it passes validation _or_ throws: 146 | 147 | ```js 148 | const yes = string()("ok"); 149 | const no = string()(0); 150 | ``` 151 | 152 | - Non-primitive validators always clone the data passed. 153 | - `object` strips the keys not defined in the schema — to pass-through undeclared keys, use `objectLoose`. 154 | - `tuple` trims the undeclared tail of the array. 155 | - Object keys where validation returns `undefined` are stripped. 156 | - Strict object and tuple validations (that throw on undeclared keys) are not built-in. 157 | 158 | ## Operators 159 | 160 | As a luxury treat, every banditype has two methods: `map` for conversion and refinement, and `or` for making union types. I could strip around 17 bytes by turning these into functions, but I think it would make the library much less pleasant to use. 161 | 162 | ### or 163 | 164 | `type1.or(type2)` passes input through `type2` _if_ `type1` fails. Useful for union types... 165 | 166 | ```ts 167 | const schema = string().or(number()); 168 | schema(0); // ok 169 | schema("hello"); // ok 170 | schema(null); // throws 171 | type S = Infer; // string | number 172 | ``` 173 | 174 | ...nullable or optional types... 175 | 176 | ```ts 177 | // string | undefined 178 | const optionalString = string().or(optional()); 179 | // string | null 180 | const optionalString = string().or(nullable()); 181 | ``` 182 | 183 | ...and default values — note that it is called on every validation error, not just missing values: 184 | 185 | ```ts 186 | const defaulted = string().or(() => "Manos arriba"); 187 | defaulted("hello"); // 'hello' 188 | defaulted(null); // 'Manos arriba' 189 | defaulted({ hello: true }); // 'Manos arriba' 190 | ``` 191 | 192 | ### map 193 | 194 | `banditype.map` can be used for type refinement: run the check and return the value if it passes, or `fail()`: 195 | 196 | ```ts 197 | const nonemptyString = string().map((s) => (s.length ? s : fail())); 198 | const date = instance(Date).map((date) => 199 | Number.isNaN(+date) ? fail() : date, 200 | ); 201 | ``` 202 | 203 | Or to convert between types: 204 | 205 | ```ts 206 | const sum = array(number()).map((arr) => arr.reduce((acc, x) => acc + x, 0)); 207 | sum([1, 2, 3]); // -> 6 208 | sum(["1", "2", "3"]); // throws 209 | const strFromNum = number().map(String); 210 | strFromNum(9); // -> '9' 211 | strFromNum("9"); // throws 212 | ``` 213 | 214 | Or _maybe_ as an intersection type, but the inferred type is always the type of the final cast, _not_ the intersection: 215 | 216 | ```ts 217 | const ab = objectLoose({ a: string() }).map(objectLoose({ b: string() })); 218 | type AB = Infer; // { b: string } 219 | ``` 220 | 221 | ## Cast functions 222 | 223 | Cast functions are the central concept of banditypes: they accept `unknown` argument and return a value of type `T` or throw. These all are string-cast functions: 224 | 225 | ```ts 226 | const isString = (raw: unknown) => (typeof raw === "string" ? raw : fail()); 227 | const isNonemptyString = (raw: unknown) => 228 | typeof raw === "string" && raw.length > 0 ? raw : fail(); 229 | ``` 230 | 231 | But so are these, doing type conversion: 232 | 233 | ```ts 234 | const toString = (raw: unknown) => String(raw); 235 | const toJson = (raw: unknown) => JSON.stringify(raw); 236 | ``` 237 | 238 | Bare cast functions are allowed as arguments in collection types: 239 | 240 | ```ts 241 | const tag = Symbol(); 242 | object({ 243 | // unique symbol check 244 | tag: (x) => (x === tag ? x : fail()), 245 | }); 246 | // array of falsy values 247 | array((raw) => (!raw ? raw : fail())); 248 | ``` 249 | 250 | Wrapping a cast in `banditype()` appends `.map` and `.or` methods, giving you a custom chainable type (note that the function you pass is mutated): 251 | 252 | ```ts 253 | const mySheriff = banditype((raw) => 254 | MySheriff.isSheriff(raw) ? raw : fail(), 255 | ); 256 | const angrySheriff = mySheriff.map((s) => (s.isAngry ? s : fail())); 257 | ``` 258 | 259 | ## TS-first schemas 260 | 261 | Unlike _some_ validation libraries, banditypes support pre-defined TS schemas: 262 | 263 | ```ts 264 | interface Bank { 265 | name: string; 266 | money: number; 267 | } 268 | const bankSchema = object({ 269 | name: string(), 270 | money: number(), 271 | }); 272 | ``` 273 | 274 | Very handy if your types are code-generated from GraphQL. 275 | 276 | ## Size measurement 277 | 278 | The 400-byte size reported assumes 5-pass terser and gzip. Brotli is slightly smaller, `esbuild` minification is slightly larger, but overall, banditypes is a very very small library. I don't think you can go much smaller. If you have any ideas on how to decrease the size further (_without_ throwing away the chainable API) — let me know! 279 | 280 | I use an unconventional (but sensible) approach to size measurement. Instead of measuring the gzip size of the library bundle, I build two versions of a "sample app" — one without validation, one using banditypes. This avoids measuring stuff that won't actually affect the bundle size: 281 | 282 | - `export` keywords and names — lib module is usually inlined, and export names are mangled. 283 | - 22-byte gzip End of Central Directory Record that's present in every gzipped file, so your app already has it. 284 | - repetitions of common JS syntax like `=>` or `const` 285 | 286 | However, it also measures the code for integrating the library into user app — schema definition and actual validation. I can't do party tricks, removing functionality from library core, and making the user implement it manually. Otherwise, you could say "I made a 0-byte library, but you have to check all the types yourself". We optimize the overall bundle size when using the lib, not the lib size itself. 287 | 288 | This technique can measure bundle size for different subsets of functionality (all validations; only primitives and objects; only core), and with different minifiers. This makes optimizing for tree-shaking and dead code elimination simple. 289 | 290 | This is a great approach, especially for smaller libraries. Check out the samples and code in [`/bench`](./bench/) 291 | 292 | ## Acknowledgements 293 | 294 | [Superstruct](https://github.com/ianstormtaylor/superstruct) was a major influence on banditypes with its modular design; shout out to [Ian Storm Taylor](https://twitter.com/ianstormtaylor) and all the contributors. I also borrowed superstruct's test suite. 295 | 296 | ## License 297 | 298 | [MIT License](./LICENSE) 299 | -------------------------------------------------------------------------------- /bench/basic/banditypes.ts: -------------------------------------------------------------------------------- 1 | import { 2 | string, 3 | number, 4 | boolean, 5 | array, 6 | object, 7 | nullable, 8 | optional, 9 | } from "../../src"; 10 | import { sample, type Data } from "./sample"; 11 | 12 | const schema = object({ 13 | array: array(string()), 14 | boolean: boolean().or(optional()), 15 | count: number().or(nullable()), 16 | }); 17 | 18 | try { 19 | console.log(schema(sample as any)); 20 | } catch (err) { 21 | console.log("fail"); 22 | } 23 | -------------------------------------------------------------------------------- /bench/basic/raw.ts: -------------------------------------------------------------------------------- 1 | import { sample } from "./sample"; 2 | 3 | try { 4 | console.log(sample); 5 | } catch (err) { 6 | console.log("fail"); 7 | } 8 | -------------------------------------------------------------------------------- /bench/basic/sample.ts: -------------------------------------------------------------------------------- 1 | export interface Data { 2 | array: string[]; 3 | count: number | null; 4 | boolean?: boolean; 5 | } 6 | 7 | export const sample: Data = { 8 | boolean: true, 9 | array: ["tag", "date"], 10 | count: 10, 11 | }; 12 | -------------------------------------------------------------------------------- /bench/basic/superstruct.ts: -------------------------------------------------------------------------------- 1 | import { 2 | string, 3 | number, 4 | boolean, 5 | array, 6 | object, 7 | nullable, 8 | optional, 9 | } from "superstruct"; 10 | import { sample, type Data } from "./sample"; 11 | 12 | const schema = object({ 13 | array: array(string()), 14 | boolean: optional(boolean()), 15 | count: nullable(number()), 16 | }); 17 | 18 | try { 19 | console.log(schema.create(sample as any)); 20 | } catch (err) { 21 | console.log("fail"); 22 | } 23 | -------------------------------------------------------------------------------- /bench/basic/zod.ts: -------------------------------------------------------------------------------- 1 | import { 2 | string, 3 | number, 4 | boolean, 5 | array, 6 | object, 7 | nullable, 8 | optional, 9 | } from "zod"; 10 | import { sample, type Data } from "./sample"; 11 | 12 | const schema = object({ 13 | array: array(string()), 14 | boolean: optional(boolean()), 15 | count: nullable(number()), 16 | }); 17 | 18 | try { 19 | console.log(schema.parse(sample as any)); 20 | } catch (err) { 21 | console.log("fail"); 22 | } 23 | -------------------------------------------------------------------------------- /bench/full/banditypes.ts: -------------------------------------------------------------------------------- 1 | import { 2 | string, 3 | number, 4 | boolean, 5 | func, 6 | instance, 7 | array, 8 | object, 9 | objectLoose, 10 | tuple, 11 | record, 12 | set, 13 | map, 14 | nullable, 15 | optional, 16 | enums, 17 | unknown, 18 | } from "../../src"; 19 | import { sample, type Data } from "./sample"; 20 | 21 | const schema = object({ 22 | array: array(string()), 23 | boolean: boolean(), 24 | func: func(), 25 | date: instance(Date), 26 | tuple: tuple([number(), number()] as const), 27 | nullableEnums: enums(["EU", "US"]).or(nullable()), 28 | optionalLiteral: enums(["HELLO"]).or(optional()), 29 | set: set(string()), 30 | map: map(string(), boolean()), 31 | extras: objectLoose({ 32 | form: record(unknown()), 33 | }), 34 | }); 35 | 36 | try { 37 | console.log(schema(sample as any)); 38 | } catch (err) { 39 | console.log("fail"); 40 | } 41 | -------------------------------------------------------------------------------- /bench/full/raw.ts: -------------------------------------------------------------------------------- 1 | import { sample } from "./sample"; 2 | 3 | try { 4 | console.log(sample); 5 | } catch (err) { 6 | console.log("fail"); 7 | } 8 | -------------------------------------------------------------------------------- /bench/full/sample.ts: -------------------------------------------------------------------------------- 1 | export interface Data { 2 | array: string[]; 3 | boolean: boolean; 4 | func: Function; 5 | date: Date; 6 | tuple: [number, number]; 7 | nullableEnums: "EU" | "US" | null; 8 | optionalLiteral?: "HELLO"; 9 | set: Set; 10 | map: Map; 11 | extras: { 12 | form: Record; 13 | }; 14 | } 15 | 16 | export const sample: Data = { 17 | array: ["tag", "date"], 18 | boolean: true, 19 | func: () => {}, 20 | date: new Date(2022, 1, 12), 21 | tuple: [73, 15], 22 | nullableEnums: "EU", 23 | set: new Set(["tag", "gato"]), 24 | map: new Map([["key", true]]), 25 | extras: { 26 | form: { 27 | field: { gadbage: false }, 28 | }, 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /bench/full/superstruct.ts: -------------------------------------------------------------------------------- 1 | import { 2 | string, 3 | number, 4 | boolean, 5 | func, 6 | instance, 7 | array, 8 | object, 9 | type, 10 | tuple, 11 | record, 12 | set, 13 | map, 14 | nullable, 15 | optional, 16 | enums, 17 | unknown, 18 | } from "superstruct"; 19 | import { sample } from "./sample"; 20 | 21 | const schema = object({ 22 | array: array(string()), 23 | boolean: boolean(), 24 | func: func(), 25 | date: instance(Date), 26 | tuple: tuple([number(), number()]), 27 | nullableEnums: nullable(enums(["EU", "US"])), 28 | optionalLiteral: optional(enums(["HELLO"])), 29 | set: set(string()), 30 | map: map(string(), boolean()), 31 | extras: type({ 32 | form: record(string(), unknown()), 33 | }), 34 | }); 35 | 36 | try { 37 | console.log(schema.create(sample as any)); 38 | } catch (err) { 39 | console.log("fail"); 40 | } 41 | -------------------------------------------------------------------------------- /bench/full/zod.ts: -------------------------------------------------------------------------------- 1 | import { 2 | string, 3 | number, 4 | boolean, 5 | instanceof as instance, 6 | array, 7 | object, 8 | tuple, 9 | record, 10 | set, 11 | map, 12 | nullable, 13 | optional, 14 | enum as enums, 15 | unknown, 16 | literal, 17 | } from "zod"; 18 | import { sample } from "./sample"; 19 | 20 | const schema = object({ 21 | array: array(string()), 22 | boolean: boolean(), 23 | func: unknown(), 24 | date: instance(Date), 25 | tuple: tuple([number(), number()]), 26 | nullableEnums: nullable(enums(["EU", "US"])), 27 | optionalLiteral: optional(literal("HELLO")), 28 | set: set(string()), 29 | map: map(string(), boolean()), 30 | extras: object({ 31 | form: record(unknown()), 32 | }).passthrough(), 33 | }); 34 | 35 | try { 36 | console.log(schema.parse(sample as any)); 37 | } catch (err) { 38 | console.log("fail"); 39 | } 40 | -------------------------------------------------------------------------------- /bench/minimal/banditypes.ts: -------------------------------------------------------------------------------- 1 | import { banditype, fail } from "../../src"; 2 | 3 | console.log(banditype((raw) => (raw ? fail() : raw))(window[""])); 4 | -------------------------------------------------------------------------------- /bench/minimal/raw.ts: -------------------------------------------------------------------------------- 1 | console.log(window[""]); 2 | -------------------------------------------------------------------------------- /bench/minimal/superstruct.ts: -------------------------------------------------------------------------------- 1 | import { create, define } from "superstruct"; 2 | 3 | console.log(define("", (value) => !value || "").create(window[""])); 4 | -------------------------------------------------------------------------------- /bench/minimal/zod.ts: -------------------------------------------------------------------------------- 1 | import { custom } from "zod"; 2 | 3 | console.log(custom((input) => input).parse(window[""])); 4 | -------------------------------------------------------------------------------- /bench/run.js: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { writeFileSync } from "fs"; 4 | import { mkdir } from "fs/promises"; 5 | import * as esbuild from "esbuild"; 6 | import * as terser from "terser"; 7 | import { gzipSync, brotliCompressSync } from "zlib"; 8 | 9 | const __dirname = dirname(fileURLToPath(import.meta.url)); 10 | 11 | const buildSuite = async (suite) => { 12 | const outfile = `${__dirname}/dist/${suite}.js`; 13 | const res = esbuild.buildSync({ 14 | entryPoints: [`${__dirname}/${suite}.ts`], 15 | outfile, 16 | bundle: true, 17 | minify: false, 18 | write: false, 19 | }); 20 | if (res.errors.length) { 21 | console.error(res.errors); 22 | process.exit(1); 23 | } 24 | 25 | const rawText = res.outputFiles[0].text; 26 | writeFileSync(outfile, rawText); 27 | const text = (await terser.minify(rawText, {})).code; 28 | writeFileSync(outfile.replace(".js", ".min.js"), text); 29 | 30 | return { 31 | rawSize: text.length, 32 | gzipSize: gzipSync(text).length, 33 | brotliSize: brotliCompressSync(text).length, 34 | }; 35 | }; 36 | 37 | function diff(baseline, checked) { 38 | return { 39 | rawSize: checked.rawSize - baseline.rawSize, 40 | gzipSize: checked.gzipSize - baseline.gzipSize, 41 | brotliSize: checked.brotliSize - baseline.brotliSize, 42 | }; 43 | } 44 | 45 | async function runSuite(dir) { 46 | await mkdir(`${__dirname}/dist/${dir}`).catch(() => {}); 47 | const baseline = await buildSuite(`${dir}/raw`); 48 | console.log(`\n${dir}\n---`); 49 | console.log("\nbaseline", baseline); 50 | 51 | for (const suite of ["zod", "superstruct", "banditypes"]) { 52 | const stats = await buildSuite(`${dir}/${suite}`); 53 | console.log(`${suite}\n `, stats); 54 | console.log(`${suite}:diff\n `, diff(baseline, stats)); 55 | } 56 | } 57 | 58 | await mkdir(`${__dirname}/dist`).catch(() => {}); 59 | runSuite("full"); 60 | runSuite("basic"); 61 | runSuite("minimal"); 62 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "banditypes", 3 | "version": "0.2.5", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "banditypes", 9 | "version": "0.2.5", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@types/node": "^18.14.1", 13 | "c8": "^10.1.3", 14 | "esbuild": "^0.24.2", 15 | "expect-type": "^1.1.0", 16 | "nano-staged": "^0.8.0", 17 | "prettier": "^3.4.2", 18 | "simple-git-hooks": "^2.11.1", 19 | "superstruct": "^2.0.2", 20 | "terser": "^5.37.0", 21 | "typescript": "^5.7.2", 22 | "uvu": "^0.5.6", 23 | "zod": "^3.24.1" 24 | }, 25 | "engines": { 26 | "node": "^14 || ^16 || >=18" 27 | } 28 | }, 29 | "node_modules/@bcoe/v8-coverage": { 30 | "version": "1.0.1", 31 | "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.1.tgz", 32 | "integrity": "sha512-W+a0/JpU28AqH4IKtwUPcEUnUyXMDLALcn5/JLczGGT9fHE2sIby/xP/oQnx3nxkForzgzPy201RAKcB4xPAFQ==", 33 | "dev": true, 34 | "engines": { 35 | "node": ">=18" 36 | } 37 | }, 38 | "node_modules/@esbuild/aix-ppc64": { 39 | "version": "0.24.2", 40 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", 41 | "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", 42 | "cpu": [ 43 | "ppc64" 44 | ], 45 | "dev": true, 46 | "optional": true, 47 | "os": [ 48 | "aix" 49 | ], 50 | "engines": { 51 | "node": ">=18" 52 | } 53 | }, 54 | "node_modules/@esbuild/android-arm": { 55 | "version": "0.24.2", 56 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", 57 | "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", 58 | "cpu": [ 59 | "arm" 60 | ], 61 | "dev": true, 62 | "optional": true, 63 | "os": [ 64 | "android" 65 | ], 66 | "engines": { 67 | "node": ">=18" 68 | } 69 | }, 70 | "node_modules/@esbuild/android-arm64": { 71 | "version": "0.24.2", 72 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", 73 | "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", 74 | "cpu": [ 75 | "arm64" 76 | ], 77 | "dev": true, 78 | "optional": true, 79 | "os": [ 80 | "android" 81 | ], 82 | "engines": { 83 | "node": ">=18" 84 | } 85 | }, 86 | "node_modules/@esbuild/android-x64": { 87 | "version": "0.24.2", 88 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", 89 | "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", 90 | "cpu": [ 91 | "x64" 92 | ], 93 | "dev": true, 94 | "optional": true, 95 | "os": [ 96 | "android" 97 | ], 98 | "engines": { 99 | "node": ">=18" 100 | } 101 | }, 102 | "node_modules/@esbuild/darwin-arm64": { 103 | "version": "0.24.2", 104 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", 105 | "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", 106 | "cpu": [ 107 | "arm64" 108 | ], 109 | "dev": true, 110 | "optional": true, 111 | "os": [ 112 | "darwin" 113 | ], 114 | "engines": { 115 | "node": ">=18" 116 | } 117 | }, 118 | "node_modules/@esbuild/darwin-x64": { 119 | "version": "0.24.2", 120 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", 121 | "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", 122 | "cpu": [ 123 | "x64" 124 | ], 125 | "dev": true, 126 | "optional": true, 127 | "os": [ 128 | "darwin" 129 | ], 130 | "engines": { 131 | "node": ">=18" 132 | } 133 | }, 134 | "node_modules/@esbuild/freebsd-arm64": { 135 | "version": "0.24.2", 136 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", 137 | "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", 138 | "cpu": [ 139 | "arm64" 140 | ], 141 | "dev": true, 142 | "optional": true, 143 | "os": [ 144 | "freebsd" 145 | ], 146 | "engines": { 147 | "node": ">=18" 148 | } 149 | }, 150 | "node_modules/@esbuild/freebsd-x64": { 151 | "version": "0.24.2", 152 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", 153 | "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", 154 | "cpu": [ 155 | "x64" 156 | ], 157 | "dev": true, 158 | "optional": true, 159 | "os": [ 160 | "freebsd" 161 | ], 162 | "engines": { 163 | "node": ">=18" 164 | } 165 | }, 166 | "node_modules/@esbuild/linux-arm": { 167 | "version": "0.24.2", 168 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", 169 | "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", 170 | "cpu": [ 171 | "arm" 172 | ], 173 | "dev": true, 174 | "optional": true, 175 | "os": [ 176 | "linux" 177 | ], 178 | "engines": { 179 | "node": ">=18" 180 | } 181 | }, 182 | "node_modules/@esbuild/linux-arm64": { 183 | "version": "0.24.2", 184 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", 185 | "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", 186 | "cpu": [ 187 | "arm64" 188 | ], 189 | "dev": true, 190 | "optional": true, 191 | "os": [ 192 | "linux" 193 | ], 194 | "engines": { 195 | "node": ">=18" 196 | } 197 | }, 198 | "node_modules/@esbuild/linux-ia32": { 199 | "version": "0.24.2", 200 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", 201 | "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", 202 | "cpu": [ 203 | "ia32" 204 | ], 205 | "dev": true, 206 | "optional": true, 207 | "os": [ 208 | "linux" 209 | ], 210 | "engines": { 211 | "node": ">=18" 212 | } 213 | }, 214 | "node_modules/@esbuild/linux-loong64": { 215 | "version": "0.24.2", 216 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", 217 | "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", 218 | "cpu": [ 219 | "loong64" 220 | ], 221 | "dev": true, 222 | "optional": true, 223 | "os": [ 224 | "linux" 225 | ], 226 | "engines": { 227 | "node": ">=18" 228 | } 229 | }, 230 | "node_modules/@esbuild/linux-mips64el": { 231 | "version": "0.24.2", 232 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", 233 | "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", 234 | "cpu": [ 235 | "mips64el" 236 | ], 237 | "dev": true, 238 | "optional": true, 239 | "os": [ 240 | "linux" 241 | ], 242 | "engines": { 243 | "node": ">=18" 244 | } 245 | }, 246 | "node_modules/@esbuild/linux-ppc64": { 247 | "version": "0.24.2", 248 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", 249 | "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", 250 | "cpu": [ 251 | "ppc64" 252 | ], 253 | "dev": true, 254 | "optional": true, 255 | "os": [ 256 | "linux" 257 | ], 258 | "engines": { 259 | "node": ">=18" 260 | } 261 | }, 262 | "node_modules/@esbuild/linux-riscv64": { 263 | "version": "0.24.2", 264 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", 265 | "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", 266 | "cpu": [ 267 | "riscv64" 268 | ], 269 | "dev": true, 270 | "optional": true, 271 | "os": [ 272 | "linux" 273 | ], 274 | "engines": { 275 | "node": ">=18" 276 | } 277 | }, 278 | "node_modules/@esbuild/linux-s390x": { 279 | "version": "0.24.2", 280 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", 281 | "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", 282 | "cpu": [ 283 | "s390x" 284 | ], 285 | "dev": true, 286 | "optional": true, 287 | "os": [ 288 | "linux" 289 | ], 290 | "engines": { 291 | "node": ">=18" 292 | } 293 | }, 294 | "node_modules/@esbuild/linux-x64": { 295 | "version": "0.24.2", 296 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", 297 | "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", 298 | "cpu": [ 299 | "x64" 300 | ], 301 | "dev": true, 302 | "optional": true, 303 | "os": [ 304 | "linux" 305 | ], 306 | "engines": { 307 | "node": ">=18" 308 | } 309 | }, 310 | "node_modules/@esbuild/netbsd-arm64": { 311 | "version": "0.24.2", 312 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", 313 | "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", 314 | "cpu": [ 315 | "arm64" 316 | ], 317 | "dev": true, 318 | "optional": true, 319 | "os": [ 320 | "netbsd" 321 | ], 322 | "engines": { 323 | "node": ">=18" 324 | } 325 | }, 326 | "node_modules/@esbuild/netbsd-x64": { 327 | "version": "0.24.2", 328 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", 329 | "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", 330 | "cpu": [ 331 | "x64" 332 | ], 333 | "dev": true, 334 | "optional": true, 335 | "os": [ 336 | "netbsd" 337 | ], 338 | "engines": { 339 | "node": ">=18" 340 | } 341 | }, 342 | "node_modules/@esbuild/openbsd-arm64": { 343 | "version": "0.24.2", 344 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", 345 | "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", 346 | "cpu": [ 347 | "arm64" 348 | ], 349 | "dev": true, 350 | "optional": true, 351 | "os": [ 352 | "openbsd" 353 | ], 354 | "engines": { 355 | "node": ">=18" 356 | } 357 | }, 358 | "node_modules/@esbuild/openbsd-x64": { 359 | "version": "0.24.2", 360 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", 361 | "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", 362 | "cpu": [ 363 | "x64" 364 | ], 365 | "dev": true, 366 | "optional": true, 367 | "os": [ 368 | "openbsd" 369 | ], 370 | "engines": { 371 | "node": ">=18" 372 | } 373 | }, 374 | "node_modules/@esbuild/sunos-x64": { 375 | "version": "0.24.2", 376 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", 377 | "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", 378 | "cpu": [ 379 | "x64" 380 | ], 381 | "dev": true, 382 | "optional": true, 383 | "os": [ 384 | "sunos" 385 | ], 386 | "engines": { 387 | "node": ">=18" 388 | } 389 | }, 390 | "node_modules/@esbuild/win32-arm64": { 391 | "version": "0.24.2", 392 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", 393 | "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", 394 | "cpu": [ 395 | "arm64" 396 | ], 397 | "dev": true, 398 | "optional": true, 399 | "os": [ 400 | "win32" 401 | ], 402 | "engines": { 403 | "node": ">=18" 404 | } 405 | }, 406 | "node_modules/@esbuild/win32-ia32": { 407 | "version": "0.24.2", 408 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", 409 | "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", 410 | "cpu": [ 411 | "ia32" 412 | ], 413 | "dev": true, 414 | "optional": true, 415 | "os": [ 416 | "win32" 417 | ], 418 | "engines": { 419 | "node": ">=18" 420 | } 421 | }, 422 | "node_modules/@esbuild/win32-x64": { 423 | "version": "0.24.2", 424 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", 425 | "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", 426 | "cpu": [ 427 | "x64" 428 | ], 429 | "dev": true, 430 | "optional": true, 431 | "os": [ 432 | "win32" 433 | ], 434 | "engines": { 435 | "node": ">=18" 436 | } 437 | }, 438 | "node_modules/@isaacs/cliui": { 439 | "version": "8.0.2", 440 | "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", 441 | "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", 442 | "dev": true, 443 | "dependencies": { 444 | "string-width": "^5.1.2", 445 | "string-width-cjs": "npm:string-width@^4.2.0", 446 | "strip-ansi": "^7.0.1", 447 | "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", 448 | "wrap-ansi": "^8.1.0", 449 | "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" 450 | }, 451 | "engines": { 452 | "node": ">=12" 453 | } 454 | }, 455 | "node_modules/@istanbuljs/schema": { 456 | "version": "0.1.3", 457 | "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", 458 | "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", 459 | "dev": true, 460 | "engines": { 461 | "node": ">=8" 462 | } 463 | }, 464 | "node_modules/@jridgewell/gen-mapping": { 465 | "version": "0.3.8", 466 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", 467 | "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", 468 | "dev": true, 469 | "dependencies": { 470 | "@jridgewell/set-array": "^1.2.1", 471 | "@jridgewell/sourcemap-codec": "^1.4.10", 472 | "@jridgewell/trace-mapping": "^0.3.24" 473 | }, 474 | "engines": { 475 | "node": ">=6.0.0" 476 | } 477 | }, 478 | "node_modules/@jridgewell/resolve-uri": { 479 | "version": "3.1.2", 480 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 481 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 482 | "dev": true, 483 | "engines": { 484 | "node": ">=6.0.0" 485 | } 486 | }, 487 | "node_modules/@jridgewell/set-array": { 488 | "version": "1.2.1", 489 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", 490 | "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", 491 | "dev": true, 492 | "engines": { 493 | "node": ">=6.0.0" 494 | } 495 | }, 496 | "node_modules/@jridgewell/source-map": { 497 | "version": "0.3.6", 498 | "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", 499 | "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", 500 | "dev": true, 501 | "dependencies": { 502 | "@jridgewell/gen-mapping": "^0.3.5", 503 | "@jridgewell/trace-mapping": "^0.3.25" 504 | } 505 | }, 506 | "node_modules/@jridgewell/sourcemap-codec": { 507 | "version": "1.5.0", 508 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", 509 | "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", 510 | "dev": true 511 | }, 512 | "node_modules/@jridgewell/trace-mapping": { 513 | "version": "0.3.25", 514 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", 515 | "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", 516 | "dev": true, 517 | "dependencies": { 518 | "@jridgewell/resolve-uri": "^3.1.0", 519 | "@jridgewell/sourcemap-codec": "^1.4.14" 520 | } 521 | }, 522 | "node_modules/@pkgjs/parseargs": { 523 | "version": "0.11.0", 524 | "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", 525 | "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", 526 | "dev": true, 527 | "optional": true, 528 | "engines": { 529 | "node": ">=14" 530 | } 531 | }, 532 | "node_modules/@types/istanbul-lib-coverage": { 533 | "version": "2.0.6", 534 | "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", 535 | "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", 536 | "dev": true 537 | }, 538 | "node_modules/@types/node": { 539 | "version": "18.19.69", 540 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.69.tgz", 541 | "integrity": "sha512-ECPdY1nlaiO/Y6GUnwgtAAhLNaQ53AyIVz+eILxpEo5OvuqE6yWkqWBIb5dU0DqhKQtMeny+FBD3PK6lm7L5xQ==", 542 | "dev": true, 543 | "dependencies": { 544 | "undici-types": "~5.26.4" 545 | } 546 | }, 547 | "node_modules/acorn": { 548 | "version": "8.14.0", 549 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", 550 | "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", 551 | "dev": true, 552 | "bin": { 553 | "acorn": "bin/acorn" 554 | }, 555 | "engines": { 556 | "node": ">=0.4.0" 557 | } 558 | }, 559 | "node_modules/ansi-regex": { 560 | "version": "6.1.0", 561 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", 562 | "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", 563 | "dev": true, 564 | "engines": { 565 | "node": ">=12" 566 | }, 567 | "funding": { 568 | "url": "https://github.com/chalk/ansi-regex?sponsor=1" 569 | } 570 | }, 571 | "node_modules/ansi-styles": { 572 | "version": "6.2.1", 573 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", 574 | "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", 575 | "dev": true, 576 | "engines": { 577 | "node": ">=12" 578 | }, 579 | "funding": { 580 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 581 | } 582 | }, 583 | "node_modules/balanced-match": { 584 | "version": "1.0.2", 585 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 586 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 587 | "dev": true 588 | }, 589 | "node_modules/brace-expansion": { 590 | "version": "2.0.1", 591 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 592 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 593 | "dev": true, 594 | "dependencies": { 595 | "balanced-match": "^1.0.0" 596 | } 597 | }, 598 | "node_modules/buffer-from": { 599 | "version": "1.1.2", 600 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 601 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 602 | "dev": true 603 | }, 604 | "node_modules/c8": { 605 | "version": "10.1.3", 606 | "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", 607 | "integrity": "sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==", 608 | "dev": true, 609 | "dependencies": { 610 | "@bcoe/v8-coverage": "^1.0.1", 611 | "@istanbuljs/schema": "^0.1.3", 612 | "find-up": "^5.0.0", 613 | "foreground-child": "^3.1.1", 614 | "istanbul-lib-coverage": "^3.2.0", 615 | "istanbul-lib-report": "^3.0.1", 616 | "istanbul-reports": "^3.1.6", 617 | "test-exclude": "^7.0.1", 618 | "v8-to-istanbul": "^9.0.0", 619 | "yargs": "^17.7.2", 620 | "yargs-parser": "^21.1.1" 621 | }, 622 | "bin": { 623 | "c8": "bin/c8.js" 624 | }, 625 | "engines": { 626 | "node": ">=18" 627 | }, 628 | "peerDependencies": { 629 | "monocart-coverage-reports": "^2" 630 | }, 631 | "peerDependenciesMeta": { 632 | "monocart-coverage-reports": { 633 | "optional": true 634 | } 635 | } 636 | }, 637 | "node_modules/cliui": { 638 | "version": "8.0.1", 639 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", 640 | "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", 641 | "dev": true, 642 | "dependencies": { 643 | "string-width": "^4.2.0", 644 | "strip-ansi": "^6.0.1", 645 | "wrap-ansi": "^7.0.0" 646 | }, 647 | "engines": { 648 | "node": ">=12" 649 | } 650 | }, 651 | "node_modules/cliui/node_modules/ansi-regex": { 652 | "version": "5.0.1", 653 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 654 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 655 | "dev": true, 656 | "engines": { 657 | "node": ">=8" 658 | } 659 | }, 660 | "node_modules/cliui/node_modules/ansi-styles": { 661 | "version": "4.3.0", 662 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 663 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 664 | "dev": true, 665 | "dependencies": { 666 | "color-convert": "^2.0.1" 667 | }, 668 | "engines": { 669 | "node": ">=8" 670 | }, 671 | "funding": { 672 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 673 | } 674 | }, 675 | "node_modules/cliui/node_modules/emoji-regex": { 676 | "version": "8.0.0", 677 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 678 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 679 | "dev": true 680 | }, 681 | "node_modules/cliui/node_modules/string-width": { 682 | "version": "4.2.3", 683 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 684 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 685 | "dev": true, 686 | "dependencies": { 687 | "emoji-regex": "^8.0.0", 688 | "is-fullwidth-code-point": "^3.0.0", 689 | "strip-ansi": "^6.0.1" 690 | }, 691 | "engines": { 692 | "node": ">=8" 693 | } 694 | }, 695 | "node_modules/cliui/node_modules/strip-ansi": { 696 | "version": "6.0.1", 697 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 698 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 699 | "dev": true, 700 | "dependencies": { 701 | "ansi-regex": "^5.0.1" 702 | }, 703 | "engines": { 704 | "node": ">=8" 705 | } 706 | }, 707 | "node_modules/cliui/node_modules/wrap-ansi": { 708 | "version": "7.0.0", 709 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 710 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 711 | "dev": true, 712 | "dependencies": { 713 | "ansi-styles": "^4.0.0", 714 | "string-width": "^4.1.0", 715 | "strip-ansi": "^6.0.0" 716 | }, 717 | "engines": { 718 | "node": ">=10" 719 | }, 720 | "funding": { 721 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 722 | } 723 | }, 724 | "node_modules/color-convert": { 725 | "version": "2.0.1", 726 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 727 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 728 | "dev": true, 729 | "dependencies": { 730 | "color-name": "~1.1.4" 731 | }, 732 | "engines": { 733 | "node": ">=7.0.0" 734 | } 735 | }, 736 | "node_modules/color-name": { 737 | "version": "1.1.4", 738 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 739 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 740 | "dev": true 741 | }, 742 | "node_modules/commander": { 743 | "version": "2.20.3", 744 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 745 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 746 | "dev": true 747 | }, 748 | "node_modules/convert-source-map": { 749 | "version": "2.0.0", 750 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", 751 | "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", 752 | "dev": true 753 | }, 754 | "node_modules/cross-spawn": { 755 | "version": "7.0.6", 756 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", 757 | "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 758 | "dev": true, 759 | "dependencies": { 760 | "path-key": "^3.1.0", 761 | "shebang-command": "^2.0.0", 762 | "which": "^2.0.1" 763 | }, 764 | "engines": { 765 | "node": ">= 8" 766 | } 767 | }, 768 | "node_modules/dequal": { 769 | "version": "2.0.3", 770 | "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", 771 | "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", 772 | "dev": true, 773 | "engines": { 774 | "node": ">=6" 775 | } 776 | }, 777 | "node_modules/diff": { 778 | "version": "5.2.0", 779 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", 780 | "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", 781 | "dev": true, 782 | "engines": { 783 | "node": ">=0.3.1" 784 | } 785 | }, 786 | "node_modules/eastasianwidth": { 787 | "version": "0.2.0", 788 | "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", 789 | "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", 790 | "dev": true 791 | }, 792 | "node_modules/emoji-regex": { 793 | "version": "9.2.2", 794 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", 795 | "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", 796 | "dev": true 797 | }, 798 | "node_modules/esbuild": { 799 | "version": "0.24.2", 800 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", 801 | "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", 802 | "dev": true, 803 | "hasInstallScript": true, 804 | "bin": { 805 | "esbuild": "bin/esbuild" 806 | }, 807 | "engines": { 808 | "node": ">=18" 809 | }, 810 | "optionalDependencies": { 811 | "@esbuild/aix-ppc64": "0.24.2", 812 | "@esbuild/android-arm": "0.24.2", 813 | "@esbuild/android-arm64": "0.24.2", 814 | "@esbuild/android-x64": "0.24.2", 815 | "@esbuild/darwin-arm64": "0.24.2", 816 | "@esbuild/darwin-x64": "0.24.2", 817 | "@esbuild/freebsd-arm64": "0.24.2", 818 | "@esbuild/freebsd-x64": "0.24.2", 819 | "@esbuild/linux-arm": "0.24.2", 820 | "@esbuild/linux-arm64": "0.24.2", 821 | "@esbuild/linux-ia32": "0.24.2", 822 | "@esbuild/linux-loong64": "0.24.2", 823 | "@esbuild/linux-mips64el": "0.24.2", 824 | "@esbuild/linux-ppc64": "0.24.2", 825 | "@esbuild/linux-riscv64": "0.24.2", 826 | "@esbuild/linux-s390x": "0.24.2", 827 | "@esbuild/linux-x64": "0.24.2", 828 | "@esbuild/netbsd-arm64": "0.24.2", 829 | "@esbuild/netbsd-x64": "0.24.2", 830 | "@esbuild/openbsd-arm64": "0.24.2", 831 | "@esbuild/openbsd-x64": "0.24.2", 832 | "@esbuild/sunos-x64": "0.24.2", 833 | "@esbuild/win32-arm64": "0.24.2", 834 | "@esbuild/win32-ia32": "0.24.2", 835 | "@esbuild/win32-x64": "0.24.2" 836 | } 837 | }, 838 | "node_modules/escalade": { 839 | "version": "3.2.0", 840 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", 841 | "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", 842 | "dev": true, 843 | "engines": { 844 | "node": ">=6" 845 | } 846 | }, 847 | "node_modules/expect-type": { 848 | "version": "1.1.0", 849 | "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", 850 | "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", 851 | "dev": true, 852 | "engines": { 853 | "node": ">=12.0.0" 854 | } 855 | }, 856 | "node_modules/find-up": { 857 | "version": "5.0.0", 858 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 859 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 860 | "dev": true, 861 | "dependencies": { 862 | "locate-path": "^6.0.0", 863 | "path-exists": "^4.0.0" 864 | }, 865 | "engines": { 866 | "node": ">=10" 867 | }, 868 | "funding": { 869 | "url": "https://github.com/sponsors/sindresorhus" 870 | } 871 | }, 872 | "node_modules/foreground-child": { 873 | "version": "3.3.0", 874 | "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", 875 | "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", 876 | "dev": true, 877 | "dependencies": { 878 | "cross-spawn": "^7.0.0", 879 | "signal-exit": "^4.0.1" 880 | }, 881 | "engines": { 882 | "node": ">=14" 883 | }, 884 | "funding": { 885 | "url": "https://github.com/sponsors/isaacs" 886 | } 887 | }, 888 | "node_modules/get-caller-file": { 889 | "version": "2.0.5", 890 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 891 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 892 | "dev": true, 893 | "engines": { 894 | "node": "6.* || 8.* || >= 10.*" 895 | } 896 | }, 897 | "node_modules/glob": { 898 | "version": "10.4.5", 899 | "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", 900 | "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", 901 | "dev": true, 902 | "dependencies": { 903 | "foreground-child": "^3.1.0", 904 | "jackspeak": "^3.1.2", 905 | "minimatch": "^9.0.4", 906 | "minipass": "^7.1.2", 907 | "package-json-from-dist": "^1.0.0", 908 | "path-scurry": "^1.11.1" 909 | }, 910 | "bin": { 911 | "glob": "dist/esm/bin.mjs" 912 | }, 913 | "funding": { 914 | "url": "https://github.com/sponsors/isaacs" 915 | } 916 | }, 917 | "node_modules/has-flag": { 918 | "version": "4.0.0", 919 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 920 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 921 | "dev": true, 922 | "engines": { 923 | "node": ">=8" 924 | } 925 | }, 926 | "node_modules/html-escaper": { 927 | "version": "2.0.2", 928 | "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", 929 | "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", 930 | "dev": true 931 | }, 932 | "node_modules/is-fullwidth-code-point": { 933 | "version": "3.0.0", 934 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 935 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 936 | "dev": true, 937 | "engines": { 938 | "node": ">=8" 939 | } 940 | }, 941 | "node_modules/isexe": { 942 | "version": "2.0.0", 943 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 944 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 945 | "dev": true 946 | }, 947 | "node_modules/istanbul-lib-coverage": { 948 | "version": "3.2.2", 949 | "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", 950 | "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", 951 | "dev": true, 952 | "engines": { 953 | "node": ">=8" 954 | } 955 | }, 956 | "node_modules/istanbul-lib-report": { 957 | "version": "3.0.1", 958 | "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", 959 | "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", 960 | "dev": true, 961 | "dependencies": { 962 | "istanbul-lib-coverage": "^3.0.0", 963 | "make-dir": "^4.0.0", 964 | "supports-color": "^7.1.0" 965 | }, 966 | "engines": { 967 | "node": ">=10" 968 | } 969 | }, 970 | "node_modules/istanbul-reports": { 971 | "version": "3.1.7", 972 | "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", 973 | "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", 974 | "dev": true, 975 | "dependencies": { 976 | "html-escaper": "^2.0.0", 977 | "istanbul-lib-report": "^3.0.0" 978 | }, 979 | "engines": { 980 | "node": ">=8" 981 | } 982 | }, 983 | "node_modules/jackspeak": { 984 | "version": "3.4.3", 985 | "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", 986 | "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", 987 | "dev": true, 988 | "dependencies": { 989 | "@isaacs/cliui": "^8.0.2" 990 | }, 991 | "funding": { 992 | "url": "https://github.com/sponsors/isaacs" 993 | }, 994 | "optionalDependencies": { 995 | "@pkgjs/parseargs": "^0.11.0" 996 | } 997 | }, 998 | "node_modules/kleur": { 999 | "version": "4.1.5", 1000 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", 1001 | "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", 1002 | "dev": true, 1003 | "engines": { 1004 | "node": ">=6" 1005 | } 1006 | }, 1007 | "node_modules/locate-path": { 1008 | "version": "6.0.0", 1009 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1010 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1011 | "dev": true, 1012 | "dependencies": { 1013 | "p-locate": "^5.0.0" 1014 | }, 1015 | "engines": { 1016 | "node": ">=10" 1017 | }, 1018 | "funding": { 1019 | "url": "https://github.com/sponsors/sindresorhus" 1020 | } 1021 | }, 1022 | "node_modules/lru-cache": { 1023 | "version": "10.4.3", 1024 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", 1025 | "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", 1026 | "dev": true 1027 | }, 1028 | "node_modules/make-dir": { 1029 | "version": "4.0.0", 1030 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", 1031 | "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", 1032 | "dev": true, 1033 | "dependencies": { 1034 | "semver": "^7.5.3" 1035 | }, 1036 | "engines": { 1037 | "node": ">=10" 1038 | }, 1039 | "funding": { 1040 | "url": "https://github.com/sponsors/sindresorhus" 1041 | } 1042 | }, 1043 | "node_modules/minimatch": { 1044 | "version": "9.0.5", 1045 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", 1046 | "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", 1047 | "dev": true, 1048 | "dependencies": { 1049 | "brace-expansion": "^2.0.1" 1050 | }, 1051 | "engines": { 1052 | "node": ">=16 || 14 >=14.17" 1053 | }, 1054 | "funding": { 1055 | "url": "https://github.com/sponsors/isaacs" 1056 | } 1057 | }, 1058 | "node_modules/minipass": { 1059 | "version": "7.1.2", 1060 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", 1061 | "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", 1062 | "dev": true, 1063 | "engines": { 1064 | "node": ">=16 || 14 >=14.17" 1065 | } 1066 | }, 1067 | "node_modules/mri": { 1068 | "version": "1.2.0", 1069 | "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", 1070 | "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", 1071 | "dev": true, 1072 | "engines": { 1073 | "node": ">=4" 1074 | } 1075 | }, 1076 | "node_modules/nano-staged": { 1077 | "version": "0.8.0", 1078 | "resolved": "https://registry.npmjs.org/nano-staged/-/nano-staged-0.8.0.tgz", 1079 | "integrity": "sha512-QSEqPGTCJbkHU2yLvfY6huqYPjdBrOaTMKatO1F8nCSrkQGXeKwtCiCnsdxnuMhbg3DTVywKaeWLGCE5oJpq0g==", 1080 | "dev": true, 1081 | "dependencies": { 1082 | "picocolors": "^1.0.0" 1083 | }, 1084 | "bin": { 1085 | "nano-staged": "lib/bin.js" 1086 | }, 1087 | "engines": { 1088 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 1089 | } 1090 | }, 1091 | "node_modules/p-limit": { 1092 | "version": "3.1.0", 1093 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1094 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1095 | "dev": true, 1096 | "dependencies": { 1097 | "yocto-queue": "^0.1.0" 1098 | }, 1099 | "engines": { 1100 | "node": ">=10" 1101 | }, 1102 | "funding": { 1103 | "url": "https://github.com/sponsors/sindresorhus" 1104 | } 1105 | }, 1106 | "node_modules/p-locate": { 1107 | "version": "5.0.0", 1108 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1109 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1110 | "dev": true, 1111 | "dependencies": { 1112 | "p-limit": "^3.0.2" 1113 | }, 1114 | "engines": { 1115 | "node": ">=10" 1116 | }, 1117 | "funding": { 1118 | "url": "https://github.com/sponsors/sindresorhus" 1119 | } 1120 | }, 1121 | "node_modules/package-json-from-dist": { 1122 | "version": "1.0.1", 1123 | "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", 1124 | "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", 1125 | "dev": true 1126 | }, 1127 | "node_modules/path-exists": { 1128 | "version": "4.0.0", 1129 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1130 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1131 | "dev": true, 1132 | "engines": { 1133 | "node": ">=8" 1134 | } 1135 | }, 1136 | "node_modules/path-key": { 1137 | "version": "3.1.1", 1138 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1139 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1140 | "dev": true, 1141 | "engines": { 1142 | "node": ">=8" 1143 | } 1144 | }, 1145 | "node_modules/path-scurry": { 1146 | "version": "1.11.1", 1147 | "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", 1148 | "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", 1149 | "dev": true, 1150 | "dependencies": { 1151 | "lru-cache": "^10.2.0", 1152 | "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" 1153 | }, 1154 | "engines": { 1155 | "node": ">=16 || 14 >=14.18" 1156 | }, 1157 | "funding": { 1158 | "url": "https://github.com/sponsors/isaacs" 1159 | } 1160 | }, 1161 | "node_modules/picocolors": { 1162 | "version": "1.1.1", 1163 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", 1164 | "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", 1165 | "dev": true 1166 | }, 1167 | "node_modules/prettier": { 1168 | "version": "3.4.2", 1169 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", 1170 | "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", 1171 | "dev": true, 1172 | "bin": { 1173 | "prettier": "bin/prettier.cjs" 1174 | }, 1175 | "engines": { 1176 | "node": ">=14" 1177 | }, 1178 | "funding": { 1179 | "url": "https://github.com/prettier/prettier?sponsor=1" 1180 | } 1181 | }, 1182 | "node_modules/require-directory": { 1183 | "version": "2.1.1", 1184 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1185 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 1186 | "dev": true, 1187 | "engines": { 1188 | "node": ">=0.10.0" 1189 | } 1190 | }, 1191 | "node_modules/sade": { 1192 | "version": "1.8.1", 1193 | "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", 1194 | "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", 1195 | "dev": true, 1196 | "dependencies": { 1197 | "mri": "^1.1.0" 1198 | }, 1199 | "engines": { 1200 | "node": ">=6" 1201 | } 1202 | }, 1203 | "node_modules/semver": { 1204 | "version": "7.6.3", 1205 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", 1206 | "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", 1207 | "dev": true, 1208 | "bin": { 1209 | "semver": "bin/semver.js" 1210 | }, 1211 | "engines": { 1212 | "node": ">=10" 1213 | } 1214 | }, 1215 | "node_modules/shebang-command": { 1216 | "version": "2.0.0", 1217 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1218 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1219 | "dev": true, 1220 | "dependencies": { 1221 | "shebang-regex": "^3.0.0" 1222 | }, 1223 | "engines": { 1224 | "node": ">=8" 1225 | } 1226 | }, 1227 | "node_modules/shebang-regex": { 1228 | "version": "3.0.0", 1229 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1230 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1231 | "dev": true, 1232 | "engines": { 1233 | "node": ">=8" 1234 | } 1235 | }, 1236 | "node_modules/signal-exit": { 1237 | "version": "4.1.0", 1238 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", 1239 | "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", 1240 | "dev": true, 1241 | "engines": { 1242 | "node": ">=14" 1243 | }, 1244 | "funding": { 1245 | "url": "https://github.com/sponsors/isaacs" 1246 | } 1247 | }, 1248 | "node_modules/simple-git-hooks": { 1249 | "version": "2.11.1", 1250 | "resolved": "https://registry.npmjs.org/simple-git-hooks/-/simple-git-hooks-2.11.1.tgz", 1251 | "integrity": "sha512-tgqwPUMDcNDhuf1Xf6KTUsyeqGdgKMhzaH4PAZZuzguOgTl5uuyeYe/8mWgAr6IBxB5V06uqEf6Dy37gIWDtDg==", 1252 | "dev": true, 1253 | "hasInstallScript": true, 1254 | "bin": { 1255 | "simple-git-hooks": "cli.js" 1256 | } 1257 | }, 1258 | "node_modules/source-map": { 1259 | "version": "0.6.1", 1260 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1261 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1262 | "dev": true, 1263 | "engines": { 1264 | "node": ">=0.10.0" 1265 | } 1266 | }, 1267 | "node_modules/source-map-support": { 1268 | "version": "0.5.21", 1269 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 1270 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 1271 | "dev": true, 1272 | "dependencies": { 1273 | "buffer-from": "^1.0.0", 1274 | "source-map": "^0.6.0" 1275 | } 1276 | }, 1277 | "node_modules/string-width": { 1278 | "version": "5.1.2", 1279 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", 1280 | "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", 1281 | "dev": true, 1282 | "dependencies": { 1283 | "eastasianwidth": "^0.2.0", 1284 | "emoji-regex": "^9.2.2", 1285 | "strip-ansi": "^7.0.1" 1286 | }, 1287 | "engines": { 1288 | "node": ">=12" 1289 | }, 1290 | "funding": { 1291 | "url": "https://github.com/sponsors/sindresorhus" 1292 | } 1293 | }, 1294 | "node_modules/string-width-cjs": { 1295 | "name": "string-width", 1296 | "version": "4.2.3", 1297 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1298 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1299 | "dev": true, 1300 | "dependencies": { 1301 | "emoji-regex": "^8.0.0", 1302 | "is-fullwidth-code-point": "^3.0.0", 1303 | "strip-ansi": "^6.0.1" 1304 | }, 1305 | "engines": { 1306 | "node": ">=8" 1307 | } 1308 | }, 1309 | "node_modules/string-width-cjs/node_modules/ansi-regex": { 1310 | "version": "5.0.1", 1311 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1312 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1313 | "dev": true, 1314 | "engines": { 1315 | "node": ">=8" 1316 | } 1317 | }, 1318 | "node_modules/string-width-cjs/node_modules/emoji-regex": { 1319 | "version": "8.0.0", 1320 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1321 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1322 | "dev": true 1323 | }, 1324 | "node_modules/string-width-cjs/node_modules/strip-ansi": { 1325 | "version": "6.0.1", 1326 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1327 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1328 | "dev": true, 1329 | "dependencies": { 1330 | "ansi-regex": "^5.0.1" 1331 | }, 1332 | "engines": { 1333 | "node": ">=8" 1334 | } 1335 | }, 1336 | "node_modules/strip-ansi": { 1337 | "version": "7.1.0", 1338 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", 1339 | "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", 1340 | "dev": true, 1341 | "dependencies": { 1342 | "ansi-regex": "^6.0.1" 1343 | }, 1344 | "engines": { 1345 | "node": ">=12" 1346 | }, 1347 | "funding": { 1348 | "url": "https://github.com/chalk/strip-ansi?sponsor=1" 1349 | } 1350 | }, 1351 | "node_modules/strip-ansi-cjs": { 1352 | "name": "strip-ansi", 1353 | "version": "6.0.1", 1354 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1355 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1356 | "dev": true, 1357 | "dependencies": { 1358 | "ansi-regex": "^5.0.1" 1359 | }, 1360 | "engines": { 1361 | "node": ">=8" 1362 | } 1363 | }, 1364 | "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { 1365 | "version": "5.0.1", 1366 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1367 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1368 | "dev": true, 1369 | "engines": { 1370 | "node": ">=8" 1371 | } 1372 | }, 1373 | "node_modules/superstruct": { 1374 | "version": "2.0.2", 1375 | "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz", 1376 | "integrity": "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==", 1377 | "dev": true, 1378 | "engines": { 1379 | "node": ">=14.0.0" 1380 | } 1381 | }, 1382 | "node_modules/supports-color": { 1383 | "version": "7.2.0", 1384 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1385 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1386 | "dev": true, 1387 | "dependencies": { 1388 | "has-flag": "^4.0.0" 1389 | }, 1390 | "engines": { 1391 | "node": ">=8" 1392 | } 1393 | }, 1394 | "node_modules/terser": { 1395 | "version": "5.37.0", 1396 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", 1397 | "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", 1398 | "dev": true, 1399 | "dependencies": { 1400 | "@jridgewell/source-map": "^0.3.3", 1401 | "acorn": "^8.8.2", 1402 | "commander": "^2.20.0", 1403 | "source-map-support": "~0.5.20" 1404 | }, 1405 | "bin": { 1406 | "terser": "bin/terser" 1407 | }, 1408 | "engines": { 1409 | "node": ">=10" 1410 | } 1411 | }, 1412 | "node_modules/test-exclude": { 1413 | "version": "7.0.1", 1414 | "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", 1415 | "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", 1416 | "dev": true, 1417 | "dependencies": { 1418 | "@istanbuljs/schema": "^0.1.2", 1419 | "glob": "^10.4.1", 1420 | "minimatch": "^9.0.4" 1421 | }, 1422 | "engines": { 1423 | "node": ">=18" 1424 | } 1425 | }, 1426 | "node_modules/typescript": { 1427 | "version": "5.7.2", 1428 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", 1429 | "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", 1430 | "dev": true, 1431 | "bin": { 1432 | "tsc": "bin/tsc", 1433 | "tsserver": "bin/tsserver" 1434 | }, 1435 | "engines": { 1436 | "node": ">=14.17" 1437 | } 1438 | }, 1439 | "node_modules/undici-types": { 1440 | "version": "5.26.5", 1441 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 1442 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", 1443 | "dev": true 1444 | }, 1445 | "node_modules/uvu": { 1446 | "version": "0.5.6", 1447 | "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", 1448 | "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", 1449 | "dev": true, 1450 | "dependencies": { 1451 | "dequal": "^2.0.0", 1452 | "diff": "^5.0.0", 1453 | "kleur": "^4.0.3", 1454 | "sade": "^1.7.3" 1455 | }, 1456 | "bin": { 1457 | "uvu": "bin.js" 1458 | }, 1459 | "engines": { 1460 | "node": ">=8" 1461 | } 1462 | }, 1463 | "node_modules/v8-to-istanbul": { 1464 | "version": "9.3.0", 1465 | "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", 1466 | "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", 1467 | "dev": true, 1468 | "dependencies": { 1469 | "@jridgewell/trace-mapping": "^0.3.12", 1470 | "@types/istanbul-lib-coverage": "^2.0.1", 1471 | "convert-source-map": "^2.0.0" 1472 | }, 1473 | "engines": { 1474 | "node": ">=10.12.0" 1475 | } 1476 | }, 1477 | "node_modules/which": { 1478 | "version": "2.0.2", 1479 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1480 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1481 | "dev": true, 1482 | "dependencies": { 1483 | "isexe": "^2.0.0" 1484 | }, 1485 | "bin": { 1486 | "node-which": "bin/node-which" 1487 | }, 1488 | "engines": { 1489 | "node": ">= 8" 1490 | } 1491 | }, 1492 | "node_modules/wrap-ansi": { 1493 | "version": "8.1.0", 1494 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", 1495 | "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", 1496 | "dev": true, 1497 | "dependencies": { 1498 | "ansi-styles": "^6.1.0", 1499 | "string-width": "^5.0.1", 1500 | "strip-ansi": "^7.0.1" 1501 | }, 1502 | "engines": { 1503 | "node": ">=12" 1504 | }, 1505 | "funding": { 1506 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1507 | } 1508 | }, 1509 | "node_modules/wrap-ansi-cjs": { 1510 | "name": "wrap-ansi", 1511 | "version": "7.0.0", 1512 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1513 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1514 | "dev": true, 1515 | "dependencies": { 1516 | "ansi-styles": "^4.0.0", 1517 | "string-width": "^4.1.0", 1518 | "strip-ansi": "^6.0.0" 1519 | }, 1520 | "engines": { 1521 | "node": ">=10" 1522 | }, 1523 | "funding": { 1524 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1525 | } 1526 | }, 1527 | "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { 1528 | "version": "5.0.1", 1529 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1530 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1531 | "dev": true, 1532 | "engines": { 1533 | "node": ">=8" 1534 | } 1535 | }, 1536 | "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { 1537 | "version": "4.3.0", 1538 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1539 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1540 | "dev": true, 1541 | "dependencies": { 1542 | "color-convert": "^2.0.1" 1543 | }, 1544 | "engines": { 1545 | "node": ">=8" 1546 | }, 1547 | "funding": { 1548 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 1549 | } 1550 | }, 1551 | "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { 1552 | "version": "8.0.0", 1553 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1554 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1555 | "dev": true 1556 | }, 1557 | "node_modules/wrap-ansi-cjs/node_modules/string-width": { 1558 | "version": "4.2.3", 1559 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1560 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1561 | "dev": true, 1562 | "dependencies": { 1563 | "emoji-regex": "^8.0.0", 1564 | "is-fullwidth-code-point": "^3.0.0", 1565 | "strip-ansi": "^6.0.1" 1566 | }, 1567 | "engines": { 1568 | "node": ">=8" 1569 | } 1570 | }, 1571 | "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { 1572 | "version": "6.0.1", 1573 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1574 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1575 | "dev": true, 1576 | "dependencies": { 1577 | "ansi-regex": "^5.0.1" 1578 | }, 1579 | "engines": { 1580 | "node": ">=8" 1581 | } 1582 | }, 1583 | "node_modules/y18n": { 1584 | "version": "5.0.8", 1585 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1586 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1587 | "dev": true, 1588 | "engines": { 1589 | "node": ">=10" 1590 | } 1591 | }, 1592 | "node_modules/yargs": { 1593 | "version": "17.7.2", 1594 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", 1595 | "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", 1596 | "dev": true, 1597 | "dependencies": { 1598 | "cliui": "^8.0.1", 1599 | "escalade": "^3.1.1", 1600 | "get-caller-file": "^2.0.5", 1601 | "require-directory": "^2.1.1", 1602 | "string-width": "^4.2.3", 1603 | "y18n": "^5.0.5", 1604 | "yargs-parser": "^21.1.1" 1605 | }, 1606 | "engines": { 1607 | "node": ">=12" 1608 | } 1609 | }, 1610 | "node_modules/yargs-parser": { 1611 | "version": "21.1.1", 1612 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", 1613 | "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", 1614 | "dev": true, 1615 | "engines": { 1616 | "node": ">=12" 1617 | } 1618 | }, 1619 | "node_modules/yargs/node_modules/ansi-regex": { 1620 | "version": "5.0.1", 1621 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1622 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1623 | "dev": true, 1624 | "engines": { 1625 | "node": ">=8" 1626 | } 1627 | }, 1628 | "node_modules/yargs/node_modules/emoji-regex": { 1629 | "version": "8.0.0", 1630 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1631 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1632 | "dev": true 1633 | }, 1634 | "node_modules/yargs/node_modules/string-width": { 1635 | "version": "4.2.3", 1636 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1637 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1638 | "dev": true, 1639 | "dependencies": { 1640 | "emoji-regex": "^8.0.0", 1641 | "is-fullwidth-code-point": "^3.0.0", 1642 | "strip-ansi": "^6.0.1" 1643 | }, 1644 | "engines": { 1645 | "node": ">=8" 1646 | } 1647 | }, 1648 | "node_modules/yargs/node_modules/strip-ansi": { 1649 | "version": "6.0.1", 1650 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1651 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1652 | "dev": true, 1653 | "dependencies": { 1654 | "ansi-regex": "^5.0.1" 1655 | }, 1656 | "engines": { 1657 | "node": ">=8" 1658 | } 1659 | }, 1660 | "node_modules/yocto-queue": { 1661 | "version": "0.1.0", 1662 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1663 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1664 | "dev": true, 1665 | "engines": { 1666 | "node": ">=10" 1667 | }, 1668 | "funding": { 1669 | "url": "https://github.com/sponsors/sindresorhus" 1670 | } 1671 | }, 1672 | "node_modules/zod": { 1673 | "version": "3.24.1", 1674 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", 1675 | "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", 1676 | "dev": true, 1677 | "funding": { 1678 | "url": "https://github.com/sponsors/colinhacks" 1679 | } 1680 | } 1681 | } 1682 | } 1683 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "banditypes", 3 | "version": "0.2.5", 4 | "description": "The might 400-byte schema validator", 5 | "author": "Vladimir Klepov v.klepov@gmail.com", 6 | "license": "MIT", 7 | "type": "module", 8 | "sideEffects": false, 9 | "module": "./dist/index.js", 10 | "main": "./dist/cjs/index.cjs", 11 | "exports": { 12 | ".": { 13 | "import": "./dist/index.js", 14 | "require": "./dist/cjs/index.cjs" 15 | } 16 | }, 17 | "types": "./dist/index.d.ts", 18 | "engines": { 19 | "node": "^14 || ^16 || >=18" 20 | }, 21 | "scripts": { 22 | "test:types": "tsc && tsc -p tests/tsconfig.dts.json --noEmit", 23 | "test:runtime": "rm -f dist/tests/**/*.{ts,js} && tsc -p tests/tsconfig.json && uvu dist/tests '.test.js$'", 24 | "test": "npm run test:types && npm run test:runtime", 25 | "test:coverage": "c8 --100 --exclude dist/tests npm test", 26 | "lint": "prettier --check --log-level warn .", 27 | "lint:fix": "prettier --loglevel silent --write .", 28 | "size": "node bench/run", 29 | "build:cjs": "tsc -p tsconfig.cjs.json && mv dist/cjs/index.js dist/cjs/index.cjs", 30 | "build": "rm -rf dist && tsc && npm run build:cjs" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/thoughtspile/banditypes.git" 35 | }, 36 | "keywords": [ 37 | "typescript", 38 | "superstruct", 39 | "schema", 40 | "validation", 41 | "zod" 42 | ], 43 | "bugs": { 44 | "url": "https://github.com/thoughtspile/banditypes/issues" 45 | }, 46 | "homepage": "https://github.com/thoughtspile/banditypes#readme", 47 | "devDependencies": { 48 | "@types/node": "^18.14.1", 49 | "c8": "^10.1.3", 50 | "esbuild": "^0.24.2", 51 | "expect-type": "^1.1.0", 52 | "nano-staged": "^0.8.0", 53 | "prettier": "^3.4.2", 54 | "simple-git-hooks": "^2.11.1", 55 | "superstruct": "^2.0.2", 56 | "terser": "^5.37.0", 57 | "typescript": "^5.7.2", 58 | "uvu": "^0.5.6", 59 | "zod": "^3.24.1" 60 | }, 61 | "nano-staged": { 62 | "*": "prettier --loglevel silent --write" 63 | }, 64 | "simple-git-hooks": { 65 | "pre-commit": "./node_modules/.bin/nano-staged" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | type Simplify = T extends Object ? { [K in keyof T]: T[K] } : T; 2 | type WithOptionalProps = Simplify< 3 | Partial & 4 | Pick< 5 | T, 6 | { 7 | [K in keyof T]: T[K] extends Exclude ? K : never; 8 | }[keyof T] 9 | > 10 | >; 11 | 12 | export type Cast = (data: Source) => T; 13 | export type Infer> = ReturnType; 14 | // Chainable API 15 | export interface Banditype extends Cast { 16 | map: (extra: Cast) => Banditype; 17 | or: (extra: Cast) => Banditype; 18 | } 19 | 20 | // Core 21 | export const banditype = (cast: Cast): Banditype => { 22 | (cast as Banditype).map = (extra) => banditype((raw) => extra(cast(raw))); 23 | (cast as Banditype).or = (extra) => 24 | banditype((raw) => { 25 | try { 26 | return cast(raw); 27 | } catch (err) { 28 | return extra(raw); 29 | } 30 | }); 31 | return cast as Banditype; 32 | }; 33 | 34 | // Error helper 35 | export const fail = () => ("bad banditype" as any)() as never; 36 | 37 | export const never = () => banditype(() => fail()); 38 | export const unknown = () => banditype((raw) => raw); 39 | 40 | // literals 41 | // not sure why, but this signature prevents widening [90] -> number[] 42 | type Primitive = string | number | null | undefined | boolean | symbol | object; 43 | export const enums = (items: T) => 44 | banditype((raw) => 45 | items.includes(raw as T[number]) ? (raw as T[number]) : fail(), 46 | ); 47 | 48 | // Basic types 49 | type Func = (...args: unknown[]) => unknown; 50 | export interface Like { 51 | (tag: string): Banditype; 52 | (tag: number): Banditype; 53 | (tag: boolean): Banditype; 54 | (tag: bigint): Banditype; 55 | (tag: Func): Banditype; 56 | (tag: symbol): Banditype; 57 | (): Banditype; 58 | } 59 | export const like = ((tag: unknown) => 60 | banditype((raw) => (typeof raw === typeof tag ? raw : fail()))) as Like; 61 | export const string = () => like(""); 62 | export const number = () => like(0); 63 | export const boolean = () => like(true); 64 | export const func = () => like(fail); 65 | export const optional = () => like(); 66 | export const nullable = () => banditype((raw) => (raw === null ? raw : fail())); 67 | 68 | // Classes 69 | export const instance = (proto: new (...args: unknown[]) => T) => 70 | banditype((raw) => (raw instanceof proto ? (raw as T) : fail())); 71 | 72 | // objects 73 | export const record = ( 74 | castValue: Cast, 75 | ): Banditype> => 76 | instance(Object).map((raw: any) => { 77 | const res: Record = {}; 78 | for (const key in raw) { 79 | const f = castValue(raw[key]); 80 | f !== undefined && (res[key] = f); 81 | } 82 | return res; 83 | }); 84 | 85 | export const object = >(schema: { 86 | [K in keyof T]-?: Cast; 87 | }) => 88 | instance(Object).map((raw: any) => { 89 | const res = {} as T; 90 | for (const key in schema) { 91 | const f = schema[key](raw[key]); 92 | f !== undefined && (res[key] = f); 93 | } 94 | return res as WithOptionalProps; 95 | }); 96 | export const objectLoose = < 97 | T extends Record = Record, 98 | >(schema: { 99 | [K in keyof T]-?: Cast; 100 | }) => 101 | instance(Object).map((raw: any) => { 102 | const res = { ...raw }; 103 | for (const key in schema) { 104 | const f = schema[key](raw[key]); 105 | f !== undefined && (res[key] = f); 106 | } 107 | return res as WithOptionalProps; 108 | }); 109 | 110 | // arrays 111 | export const array = (castItem: Cast) => 112 | instance(Array).map((arr) => arr.map(castItem)); 113 | export const tuple = []>(schema: T) => 114 | instance(Array).map((arr) => { 115 | return schema.map((cast, i) => cast(arr[i])) as { 116 | -readonly [K in keyof T]: Infer; 117 | }; 118 | }); 119 | 120 | export const set = (castItem: Cast) => 121 | instance(Set).map((set) => new Set([...set].map(castItem))); 122 | export const map = (castKey: Cast, castValue: Cast) => 123 | instance(Map).map((map) => { 124 | return new Map([...map].map(([k, v]) => [castKey(k), castValue(v)])); 125 | }); 126 | 127 | export const lazy = (cast: () => Cast) => banditype((raw) => cast()(raw)); 128 | -------------------------------------------------------------------------------- /tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import { suite as makeSuite } from "uvu"; 2 | import { equal, throws } from "uvu/assert"; 3 | import fs from "fs"; 4 | import { dirname, resolve } from "path"; 5 | import { fileURLToPath } from "url"; 6 | const __dirname = dirname(fileURLToPath(import.meta.url)); 7 | 8 | async function testValidation() { 9 | const kindsDir = resolve(__dirname, "validation"); 10 | const kinds = fs.readdirSync(kindsDir).filter((t) => t[0] !== "."); 11 | 12 | for (const kind of kinds) { 13 | const suite = makeSuite(kind); 14 | 15 | const testsDir = resolve(kindsDir, kind); 16 | const tests = fs 17 | .readdirSync(testsDir) 18 | .filter((t) => t[0] !== "." && !t.endsWith(".d.ts")); 19 | 20 | for (const name of tests) { 21 | const module = await import(resolve(testsDir, name)); 22 | const { Struct, data, only, skip, output } = module; 23 | const run = only ? suite.only : skip ? suite.skip : suite; 24 | run(name, () => { 25 | let actual; 26 | let err; 27 | 28 | if ("output" in module) { 29 | equal(Struct(data), output); 30 | } else if ("failures" in module) { 31 | throws(() => Struct(data)); 32 | // const props = ['type', 'path', 'refinement', 'value', 'branch'] 33 | // const actualFailures = err 34 | // .failures() 35 | // .map((failure) => pick(failure, ...props)) 36 | 37 | // assert.deepStrictEqual(actualFailures, failures) 38 | // assert.deepStrictEqual(pick(err, ...props), failures[0]) 39 | } else { 40 | throw new Error( 41 | `The "${name}" fixture did not define an \`output\` or \`failures\` export.`, 42 | ); 43 | } 44 | }); 45 | } 46 | suite.run(); 47 | } 48 | } 49 | 50 | await testValidation(); 51 | -------------------------------------------------------------------------------- /tests/tsconfig.dts.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noEmit": true, 4 | "paths": { 5 | "$lib/*": ["../dist/*"] 6 | }, 7 | "incremental": true 8 | }, 9 | "extends": "../tsconfig.json", 10 | "include": ["./typings"] 11 | } 12 | -------------------------------------------------------------------------------- /tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "esnext", 4 | "paths": { 5 | "$lib/*": ["../src/*"] 6 | }, 7 | "incremental": true, 8 | "tsBuildInfoFile": "../dist/test.tsbuildinfo" 9 | }, 10 | "extends": "../tsconfig.json", 11 | "include": ["./**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /tests/typings/Banditype.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type Banditype, 3 | object, 4 | array, 5 | string, 6 | number, 7 | record, 8 | enums, 9 | tuple, 10 | never, 11 | boolean, 12 | func, 13 | map, 14 | nullable, 15 | optional, 16 | set, 17 | unknown, 18 | } from "$lib/index.js"; 19 | import { expectTypeOf } from "expect-type"; 20 | 21 | expectTypeOf(array(string())).toEqualTypeOf>>(); 22 | 23 | expectTypeOf(boolean()).toEqualTypeOf>(); 24 | 25 | expectTypeOf(enums(["a", "b", "c"])).toEqualTypeOf< 26 | Banditype<"a" | "b" | "c"> 27 | >(); 28 | 29 | expectTypeOf(enums([1, 2, 3])).toEqualTypeOf>(); 30 | 31 | expectTypeOf(func()).toEqualTypeOf< 32 | Banditype<(...args: unknown[]) => unknown> 33 | >(); 34 | 35 | expectTypeOf(enums([false])).toEqualTypeOf>(); 36 | 37 | expectTypeOf(enums([42])).toEqualTypeOf>(); 38 | 39 | expectTypeOf(enums(["test"])).toEqualTypeOf>(); 40 | 41 | expectTypeOf(map(string(), number())).toEqualTypeOf< 42 | Banditype> 43 | >(); 44 | 45 | expectTypeOf(never()).toEqualTypeOf>(); 46 | 47 | expectTypeOf(string().or(nullable())).toEqualTypeOf>(); 48 | 49 | expectTypeOf(number()).toEqualTypeOf>(); 50 | 51 | expectTypeOf(object({ name: string() })).toEqualTypeOf< 52 | Banditype<{ name: string }> 53 | >(); 54 | 55 | expectTypeOf(object({ name: string().or(optional()) })).toEqualTypeOf< 56 | Banditype<{ name?: string }> 57 | >(); 58 | 59 | expectTypeOf(string().or(optional())).toEqualTypeOf< 60 | Banditype 61 | >(); 62 | 63 | expectTypeOf(record(number())).toEqualTypeOf< 64 | Banditype> 65 | >(); 66 | 67 | expectTypeOf(set(number())).toEqualTypeOf>>(); 68 | 69 | expectTypeOf(string()).toEqualTypeOf>(); 70 | 71 | expectTypeOf(tuple([string()] as const)).toEqualTypeOf>(); 72 | 73 | expectTypeOf(tuple([string(), number()] as const)).toEqualTypeOf< 74 | Banditype<[string, number]> 75 | >(); 76 | 77 | expectTypeOf(string().or(number())).toEqualTypeOf>(); 78 | 79 | expectTypeOf(unknown()).toEqualTypeOf>(); 80 | -------------------------------------------------------------------------------- /tests/typings/any.ts: -------------------------------------------------------------------------------- 1 | import { expectTypeOf } from "expect-type"; 2 | import { banditype } from "$lib/index.js"; 3 | 4 | expectTypeOf(banditype((raw) => raw)).returns.toEqualTypeOf(); 5 | -------------------------------------------------------------------------------- /tests/typings/array.ts: -------------------------------------------------------------------------------- 1 | import { array, number, unknown } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(array(unknown())).returns.toEqualTypeOf(); 5 | expectTypeOf(array(number())).returns.toEqualTypeOf(); 6 | -------------------------------------------------------------------------------- /tests/typings/assign.ts: -------------------------------------------------------------------------------- 1 | import { object, number, string } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | const schemaA = { a: number() }; 5 | const schemaB = { b: string() }; 6 | const merged = object({ ...schemaA, ...schemaB }); 7 | 8 | expectTypeOf(merged).returns.toEqualTypeOf<{ 9 | a: number; 10 | b: string; 11 | }>(); 12 | -------------------------------------------------------------------------------- /tests/typings/boolean.ts: -------------------------------------------------------------------------------- 1 | import { boolean } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(boolean()).returns.toEqualTypeOf(); 5 | -------------------------------------------------------------------------------- /tests/typings/coerce.ts: -------------------------------------------------------------------------------- 1 | import { string } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf( 5 | string().map((x) => parseFloat(x)), 6 | ).returns.toEqualTypeOf(); 7 | -------------------------------------------------------------------------------- /tests/typings/date.ts: -------------------------------------------------------------------------------- 1 | import { instance, fail } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf( 5 | instance(Date).map((v) => (isNaN(v.getTime()) ? fail() : v)), 6 | ).returns.toEqualTypeOf(); 7 | -------------------------------------------------------------------------------- /tests/typings/defaulted.ts: -------------------------------------------------------------------------------- 1 | import { string } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(string().or(() => "default")).returns.toEqualTypeOf(); 5 | -------------------------------------------------------------------------------- /tests/typings/empty.ts: -------------------------------------------------------------------------------- 1 | import { string, array, map, set, fail, unknown } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf( 5 | string().map((v) => (v.length ? fail() : v)), 6 | ).returns.toEqualTypeOf(); 7 | 8 | expectTypeOf( 9 | array(unknown()).map((v) => (v.length ? fail() : v)), 10 | ).returns.toEqualTypeOf>(); 11 | 12 | expectTypeOf( 13 | set(unknown()).map((v) => (v.size ? fail() : v)), 14 | ).returns.toEqualTypeOf>(); 15 | 16 | expectTypeOf( 17 | map(unknown(), unknown()).map((v) => (v.size ? fail() : v)), 18 | ).returns.toEqualTypeOf>(); 19 | -------------------------------------------------------------------------------- /tests/typings/enums.ts: -------------------------------------------------------------------------------- 1 | import { enums } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf((x) => { 5 | return enums(["a", "b", "c"])(x); 6 | }).returns.toEqualTypeOf<"a" | "b" | "c">(); 7 | 8 | expectTypeOf((x) => { 9 | return enums([1, 2, 3])(x); 10 | }).returns.toEqualTypeOf<1 | 2 | 3>(); 11 | 12 | expectTypeOf((x) => { 13 | return enums([1, 2, 3] as const)(x); 14 | }).returns.toEqualTypeOf<1 | 2 | 3>(); 15 | 16 | expectTypeOf((x) => { 17 | return enums([1, true, "1"])(x); 18 | }).returns.toEqualTypeOf<1 | true | "1">(); 19 | 20 | const unique = Symbol(); 21 | const unique2 = Symbol(); 22 | expectTypeOf((x) => { 23 | return enums([unique, unique2])(x); 24 | }).returns.toEqualTypeOf(); 25 | 26 | expectTypeOf((x) => { 27 | let values = [1, 2, 3]; 28 | return enums(values)(x); 29 | }).returns.toEqualTypeOf(); 30 | -------------------------------------------------------------------------------- /tests/typings/func.ts: -------------------------------------------------------------------------------- 1 | import { func } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(func()).returns.toEqualTypeOf<(...args: unknown[]) => unknown>(); 5 | -------------------------------------------------------------------------------- /tests/typings/infer.ts: -------------------------------------------------------------------------------- 1 | import { type Infer, object } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | const Struct = object({}); 5 | type T = Infer; 6 | 7 | expectTypeOf(Struct).returns.toEqualTypeOf(); 8 | -------------------------------------------------------------------------------- /tests/typings/instance.ts: -------------------------------------------------------------------------------- 1 | import { instance } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(instance(Date)).returns.toEqualTypeOf(); 5 | expectTypeOf(instance(RegExp)).returns.toEqualTypeOf(); 6 | -------------------------------------------------------------------------------- /tests/typings/integer.ts: -------------------------------------------------------------------------------- 1 | import { fail, number } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf( 5 | number().map((x) => (Number.isInteger(x) ? x : fail())), 6 | ).returns.toEqualTypeOf(); 7 | -------------------------------------------------------------------------------- /tests/typings/lazy.ts: -------------------------------------------------------------------------------- 1 | import { lazy, string } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(lazy(() => string())).returns.toEqualTypeOf(); 5 | -------------------------------------------------------------------------------- /tests/typings/literal.ts: -------------------------------------------------------------------------------- 1 | import { enums } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(enums([true])).returns.toEqualTypeOf(); 5 | 6 | expectTypeOf(enums(["a"])).returns.toEqualTypeOf<"a">(); 7 | 8 | expectTypeOf(enums([42])).returns.toEqualTypeOf<42>(); 9 | 10 | expectTypeOf(enums([new Date()])).returns.toEqualTypeOf(); 11 | -------------------------------------------------------------------------------- /tests/typings/map.ts: -------------------------------------------------------------------------------- 1 | import { map, string, number } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(map(string(), number())).returns.toEqualTypeOf< 5 | Map 6 | >(); 7 | -------------------------------------------------------------------------------- /tests/typings/max.ts: -------------------------------------------------------------------------------- 1 | import { number, fail } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf( 5 | number().map((num) => (num < 0 ? num : fail())), 6 | ).returns.toEqualTypeOf(); 7 | -------------------------------------------------------------------------------- /tests/typings/min.ts: -------------------------------------------------------------------------------- 1 | import { number, fail } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf( 5 | number().map((num) => (num > 0 ? num : fail())), 6 | ).returns.toEqualTypeOf(); 7 | -------------------------------------------------------------------------------- /tests/typings/never.ts: -------------------------------------------------------------------------------- 1 | import { never } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(never()).returns.toEqualTypeOf(); 5 | -------------------------------------------------------------------------------- /tests/typings/nullable.ts: -------------------------------------------------------------------------------- 1 | import { string, object, enums } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(string().or(enums([null]))).returns.toEqualTypeOf(); 5 | 6 | expectTypeOf(object({ a: string().or(enums([null])) })).returns.toEqualTypeOf<{ 7 | a: string | null; 8 | }>(); 9 | 10 | expectTypeOf(enums(["a", "b", null])).returns.toEqualTypeOf<"a" | "b" | null>(); 11 | -------------------------------------------------------------------------------- /tests/typings/number.ts: -------------------------------------------------------------------------------- 1 | import { number } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(number()).returns.toEqualTypeOf(); 5 | -------------------------------------------------------------------------------- /tests/typings/object.ts: -------------------------------------------------------------------------------- 1 | import { object, number, string, type Cast } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(object({})).returns.toEqualTypeOf>(); 5 | 6 | expectTypeOf( 7 | object({ 8 | a: number(), 9 | b: string(), 10 | }), 11 | ).returns.toEqualTypeOf<{ 12 | a: number; 13 | b: string; 14 | }>(); 15 | 16 | expectTypeOf(object<{ key?: string }>) 17 | .parameter(0) 18 | .toEqualTypeOf<{ 19 | key: Cast; 20 | }>(); 21 | -------------------------------------------------------------------------------- /tests/typings/objectLoose.ts: -------------------------------------------------------------------------------- 1 | import { objectLoose, number } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(objectLoose({ a: number() })).returns.toEqualTypeOf<{ 5 | a: number; 6 | }>(); 7 | -------------------------------------------------------------------------------- /tests/typings/optional.ts: -------------------------------------------------------------------------------- 1 | import { optional, string, number, object, enums } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(string().or(optional())).returns.toEqualTypeOf< 5 | string | undefined 6 | >(); 7 | 8 | expectTypeOf( 9 | object({ 10 | a: number().or(optional()), 11 | b: string(), 12 | }), 13 | ).returns.toEqualTypeOf<{ 14 | a?: number; 15 | b: string; 16 | }>(); 17 | -------------------------------------------------------------------------------- /tests/typings/pattern.ts: -------------------------------------------------------------------------------- 1 | import { fail, string } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf( 5 | string().map((x) => (/.*/.test(x) ? x : fail())), 6 | ).returns.toEqualTypeOf(); 7 | -------------------------------------------------------------------------------- /tests/typings/record.ts: -------------------------------------------------------------------------------- 1 | import { record, number } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(record(number())).returns.toEqualTypeOf>(); 5 | -------------------------------------------------------------------------------- /tests/typings/refine.ts: -------------------------------------------------------------------------------- 1 | import { string } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(string().map((x) => x)).returns.toEqualTypeOf(); 5 | -------------------------------------------------------------------------------- /tests/typings/regexp.ts: -------------------------------------------------------------------------------- 1 | import { instance } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(instance(RegExp)).returns.toEqualTypeOf(); 5 | -------------------------------------------------------------------------------- /tests/typings/set.ts: -------------------------------------------------------------------------------- 1 | import { set, string } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(set(string())).returns.toEqualTypeOf>(); 5 | -------------------------------------------------------------------------------- /tests/typings/string.ts: -------------------------------------------------------------------------------- 1 | import { string } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(string()).returns.toEqualTypeOf(); 5 | -------------------------------------------------------------------------------- /tests/typings/struct.ts: -------------------------------------------------------------------------------- 1 | import { banditype } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf( 5 | banditype((x) => x as string), 6 | ).returns.toEqualTypeOf(); 7 | -------------------------------------------------------------------------------- /tests/typings/trimmed.ts: -------------------------------------------------------------------------------- 1 | import { string } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(string().map((x) => x.trim())).returns.toEqualTypeOf(); 5 | -------------------------------------------------------------------------------- /tests/typings/tuple.ts: -------------------------------------------------------------------------------- 1 | import { tuple, string, number, enums } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(tuple([string(), number()] as const)).returns.toEqualTypeOf< 5 | [string, number] 6 | >(); 7 | 8 | // Maximum call stack of 41 items 9 | expectTypeOf( 10 | tuple([ 11 | enums(["1"]), 12 | enums(["2"]), 13 | enums(["3"]), 14 | enums(["4"]), 15 | enums(["5"]), 16 | enums(["6"]), 17 | enums(["7"]), 18 | enums(["8"]), 19 | enums(["9"]), 20 | enums(["10"]), 21 | enums(["11"]), 22 | enums(["12"]), 23 | enums(["13"]), 24 | enums(["14"]), 25 | enums(["15"]), 26 | enums(["16"]), 27 | enums(["17"]), 28 | enums(["18"]), 29 | enums(["19"]), 30 | enums(["20"]), 31 | enums(["21"]), 32 | enums(["22"]), 33 | enums(["23"]), 34 | enums(["24"]), 35 | enums(["25"]), 36 | enums(["26"]), 37 | enums(["27"]), 38 | enums(["28"]), 39 | enums(["29"]), 40 | enums(["30"]), 41 | enums(["31"]), 42 | enums(["32"]), 43 | enums(["33"]), 44 | enums(["34"]), 45 | enums(["35"]), 46 | enums(["36"]), 47 | enums(["37"]), 48 | enums(["38"]), 49 | enums(["39"]), 50 | enums(["40"]), 51 | enums(["41"]), 52 | ] as const), 53 | ).returns.toEqualTypeOf< 54 | [ 55 | "1", 56 | "2", 57 | "3", 58 | "4", 59 | "5", 60 | "6", 61 | "7", 62 | "8", 63 | "9", 64 | "10", 65 | "11", 66 | "12", 67 | "13", 68 | "14", 69 | "15", 70 | "16", 71 | "17", 72 | "18", 73 | "19", 74 | "20", 75 | "21", 76 | "22", 77 | "23", 78 | "24", 79 | "25", 80 | "26", 81 | "27", 82 | "28", 83 | "29", 84 | "30", 85 | "31", 86 | "32", 87 | "33", 88 | "34", 89 | "35", 90 | "36", 91 | "37", 92 | "38", 93 | "39", 94 | "40", 95 | "41", 96 | ] 97 | >; 98 | -------------------------------------------------------------------------------- /tests/typings/union.ts: -------------------------------------------------------------------------------- 1 | import { object, string, enums } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf( 5 | object({ a: string() }).or(object({ b: string() })), 6 | ).returns.toEqualTypeOf<{ a: string } | { b: string }>(); 7 | 8 | // Maximum call stack of 40 items 9 | expectTypeOf( 10 | enums(["1"]) 11 | .or(enums(["2"])) 12 | .or(enums(["3"])) 13 | .or(enums(["4"])) 14 | .or(enums(["5"])) 15 | .or(enums(["6"])) 16 | .or(enums(["7"])) 17 | .or(enums(["8"])) 18 | .or(enums(["9"])) 19 | .or(enums(["10"])) 20 | .or(enums(["11"])) 21 | .or(enums(["12"])) 22 | .or(enums(["13"])) 23 | .or(enums(["14"])) 24 | .or(enums(["15"])) 25 | .or(enums(["16"])) 26 | .or(enums(["17"])) 27 | .or(enums(["18"])) 28 | .or(enums(["19"])) 29 | .or(enums(["20"])) 30 | .or(enums(["21"])) 31 | .or(enums(["22"])) 32 | .or(enums(["23"])) 33 | .or(enums(["24"])) 34 | .or(enums(["25"])) 35 | .or(enums(["26"])) 36 | .or(enums(["27"])) 37 | .or(enums(["28"])) 38 | .or(enums(["29"])) 39 | .or(enums(["30"])) 40 | .or(enums(["31"])) 41 | .or(enums(["32"])) 42 | .or(enums(["33"])) 43 | .or(enums(["34"])) 44 | .or(enums(["35"])) 45 | .or(enums(["36"])) 46 | .or(enums(["37"])) 47 | .or(enums(["38"])) 48 | .or(enums(["39"])) 49 | .or(enums(["40"])), 50 | ).returns.toEqualTypeOf< 51 | | "1" 52 | | "2" 53 | | "3" 54 | | "4" 55 | | "5" 56 | | "6" 57 | | "7" 58 | | "8" 59 | | "9" 60 | | "10" 61 | | "11" 62 | | "12" 63 | | "13" 64 | | "14" 65 | | "15" 66 | | "16" 67 | | "17" 68 | | "18" 69 | | "19" 70 | | "20" 71 | | "21" 72 | | "22" 73 | | "23" 74 | | "24" 75 | | "25" 76 | | "26" 77 | | "27" 78 | | "28" 79 | | "29" 80 | | "30" 81 | | "31" 82 | | "32" 83 | | "33" 84 | | "34" 85 | | "35" 86 | | "36" 87 | | "37" 88 | | "38" 89 | | "39" 90 | | "40" 91 | >; 92 | -------------------------------------------------------------------------------- /tests/typings/unknown.ts: -------------------------------------------------------------------------------- 1 | import { unknown } from "$lib/index.js"; 2 | import { expectTypeOf } from "expect-type"; 3 | 4 | expectTypeOf(unknown()).returns.toEqualTypeOf(); 5 | -------------------------------------------------------------------------------- /tests/validation/array/invalid-element-property.ts: -------------------------------------------------------------------------------- 1 | import { array, object, string } from "../../../src/index.js"; 2 | 3 | export const Struct = array(object({ id: string() })); 4 | 5 | export const data = [{ id: "1" }, { id: false }, { id: "3" }]; 6 | 7 | export const failures = [ 8 | { 9 | value: false, 10 | type: "string", 11 | refinement: undefined, 12 | path: [1, "id"], 13 | branch: [data, data[1], data[1]!.id], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/array/invalid-element.ts: -------------------------------------------------------------------------------- 1 | import { array, number } from "../../../src/index.js"; 2 | 3 | export const Struct = array(number()); 4 | 5 | export const data = [1, "invalid", 3]; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "number", 11 | refinement: undefined, 12 | path: [1], 13 | branch: [data, data[1]], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/array/invalid-opaque.ts: -------------------------------------------------------------------------------- 1 | import { array, unknown } from "../../../src/index.js"; 2 | 3 | export const Struct = array(unknown()); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "array", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/array/invalid.ts: -------------------------------------------------------------------------------- 1 | import { array, number } from "../../../src/index.js"; 2 | 3 | export const Struct = array(number()); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "array", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/array/valid-opaque.ts: -------------------------------------------------------------------------------- 1 | import { array, unknown } from "../../../src/index.js"; 2 | 3 | export const Struct = array(unknown()); 4 | 5 | export const data = [1, "b", true]; 6 | 7 | export const output = [1, "b", true]; 8 | -------------------------------------------------------------------------------- /tests/validation/array/valid.ts: -------------------------------------------------------------------------------- 1 | import { array, number } from "../../../src/index.js"; 2 | 3 | export const Struct = array(number()); 4 | 5 | export const data = [1, 2, 3]; 6 | 7 | export const output = [1, 2, 3]; 8 | -------------------------------------------------------------------------------- /tests/validation/boolean/invalid.ts: -------------------------------------------------------------------------------- 1 | import { boolean } from "../../../src/index.js"; 2 | 3 | export const Struct = boolean(); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "boolean", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/boolean/valid.ts: -------------------------------------------------------------------------------- 1 | import { boolean } from "../../../src/index.js"; 2 | 3 | export const Struct = boolean(); 4 | 5 | export const data = true; 6 | 7 | export const output = true; 8 | -------------------------------------------------------------------------------- /tests/validation/coerce/changed.ts: -------------------------------------------------------------------------------- 1 | import { unknown } from "../../../src/index.js"; 2 | 3 | export const Struct = unknown().map((x) => (x == null ? "unknown" : x)); 4 | 5 | export const data = null; 6 | 7 | export const output = "unknown"; 8 | 9 | export const create = true; 10 | -------------------------------------------------------------------------------- /tests/validation/coerce/condition-not-met.ts: -------------------------------------------------------------------------------- 1 | import { number } from "../../../src/index.js"; 2 | 3 | export const Struct = number().map(() => "known"); 4 | 5 | export const data = false; 6 | 7 | export const failures = [ 8 | { 9 | value: false, 10 | type: "string", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | 17 | export const create = true; 18 | -------------------------------------------------------------------------------- /tests/validation/coerce/unchanged.ts: -------------------------------------------------------------------------------- 1 | import { string, unknown } from "../../../src/index.js"; 2 | 3 | export const Struct = unknown() 4 | .map((x) => (x == null ? "unknown" : x)) 5 | .map(string()); 6 | 7 | export const data = "known"; 8 | 9 | export const output = "known"; 10 | 11 | export const create = true; 12 | -------------------------------------------------------------------------------- /tests/validation/defaulted/function.ts: -------------------------------------------------------------------------------- 1 | import { number } from "../../../src/index.js"; 2 | 3 | export const Struct = number().or(() => 42); 4 | 5 | export const data = undefined; 6 | 7 | export const output = 42; 8 | 9 | export const create = true; 10 | -------------------------------------------------------------------------------- /tests/validation/defaulted/nested.ts: -------------------------------------------------------------------------------- 1 | import { string, object } from "../../../src/index.js"; 2 | 3 | export const Struct = object({ 4 | title: string().or(() => "Untitled"), 5 | }); 6 | 7 | export const data = {}; 8 | 9 | export const output = { 10 | title: "Untitled", 11 | }; 12 | 13 | export const create = true; 14 | -------------------------------------------------------------------------------- /tests/validation/defaulted/value.ts: -------------------------------------------------------------------------------- 1 | import { number } from "../../../src/index.js"; 2 | 3 | export const Struct = number().or(() => 42); 4 | 5 | export const data = undefined; 6 | 7 | export const output = 42; 8 | 9 | export const create = true; 10 | -------------------------------------------------------------------------------- /tests/validation/enums/invalid-numbers.ts: -------------------------------------------------------------------------------- 1 | import { enums } from "../../../src/index.js"; 2 | 3 | export const Struct = enums([1, 2]); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "enums", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/enums/invalid-strings.ts: -------------------------------------------------------------------------------- 1 | import { enums } from "../../../src/index.js"; 2 | 3 | export const Struct = enums(["one", "two"]); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "enums", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/enums/valid.ts: -------------------------------------------------------------------------------- 1 | import { enums } from "../../../src/index.js"; 2 | 3 | export const Struct = enums(["one", "two"]); 4 | 5 | export const data = "two"; 6 | 7 | export const output = "two"; 8 | -------------------------------------------------------------------------------- /tests/validation/function/invalid.ts: -------------------------------------------------------------------------------- 1 | import { func } from "../../../src/index.js"; 2 | 3 | export const Struct = func(); 4 | 5 | export const data = false; 6 | 7 | export const failures = [ 8 | { 9 | value: false, 10 | type: "func", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/function/valid.ts: -------------------------------------------------------------------------------- 1 | import { func } from "../../../src/index.js"; 2 | 3 | export const Struct = func(); 4 | 5 | export const data = function () {}; 6 | 7 | export const output = data; 8 | -------------------------------------------------------------------------------- /tests/validation/instance/invalid.ts: -------------------------------------------------------------------------------- 1 | import { instance } from "../../../src/index.js"; 2 | 3 | export const Struct = instance(Array); 4 | 5 | export const data = false; 6 | 7 | export const failures = [ 8 | { 9 | value: false, 10 | type: "instance", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/instance/valid.ts: -------------------------------------------------------------------------------- 1 | import { instance } from "../../../src/index.js"; 2 | 3 | export const Struct = instance(Array); 4 | 5 | export const data = [1]; 6 | 7 | export const output = [1]; 8 | -------------------------------------------------------------------------------- /tests/validation/lazy/invalid.ts: -------------------------------------------------------------------------------- 1 | import { lazy, string } from "../../../src/index.js"; 2 | 3 | export const Struct = lazy(() => string()); 4 | 5 | export const data = 3; 6 | 7 | export const failures = [ 8 | { 9 | value: 3, 10 | type: "string", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/lazy/valid.ts: -------------------------------------------------------------------------------- 1 | import { lazy, string } from "../../../src/index.js"; 2 | 3 | export const Struct = lazy(() => string()); 4 | 5 | export const data = "two"; 6 | 7 | export const output = "two"; 8 | -------------------------------------------------------------------------------- /tests/validation/lazy/with-refiners.ts: -------------------------------------------------------------------------------- 1 | import { lazy, fail, string } from "../../../src/index.js"; 2 | 3 | export const Struct = lazy(() => 4 | string().map((s) => (s.length > 0 ? s : fail())), 5 | ); 6 | 7 | export const data = ""; 8 | 9 | export const failures = [ 10 | { 11 | value: data, 12 | type: "string", 13 | refinement: "nonempty", 14 | path: [], 15 | branch: [data], 16 | }, 17 | ]; 18 | -------------------------------------------------------------------------------- /tests/validation/literal/invalid.ts: -------------------------------------------------------------------------------- 1 | import { enums } from "../../../src/index.js"; 2 | 3 | export const Struct = enums([42]); 4 | 5 | export const data = false; 6 | 7 | export const failures = [ 8 | { 9 | value: false, 10 | type: "literal", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/literal/valid.ts: -------------------------------------------------------------------------------- 1 | import { enums } from "../../../src/index.js"; 2 | 3 | export const Struct = enums([42]); 4 | 5 | export const data = 42; 6 | 7 | export const output = 42; 8 | -------------------------------------------------------------------------------- /tests/validation/map/invalid-opaque.ts: -------------------------------------------------------------------------------- 1 | import { map, unknown } from "../../../src/index.js"; 2 | 3 | export const Struct = map(unknown(), unknown()); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "map", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/map/invalid-property.ts: -------------------------------------------------------------------------------- 1 | import { map, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = map(string(), number()); 4 | 5 | export const data = new Map([ 6 | ["a", "a"], 7 | ["b", "b"], 8 | ]); 9 | 10 | export const failures = [ 11 | { 12 | value: "a", 13 | type: "number", 14 | refinement: undefined, 15 | path: ["a"], 16 | branch: [data, "a"], 17 | }, 18 | { 19 | value: "b", 20 | type: "number", 21 | refinement: undefined, 22 | path: ["b"], 23 | branch: [data, "b"], 24 | }, 25 | ]; 26 | -------------------------------------------------------------------------------- /tests/validation/map/invalid.ts: -------------------------------------------------------------------------------- 1 | import { map, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = map(string(), number()); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "map", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/map/valid-opaque.ts: -------------------------------------------------------------------------------- 1 | import { map, unknown } from "../../../src/index.js"; 2 | 3 | export const Struct = map(unknown(), unknown()); 4 | 5 | export const data = new Map([ 6 | ["a", 1], 7 | [2, true], 8 | ] as any); 9 | 10 | export const output = data; 11 | -------------------------------------------------------------------------------- /tests/validation/map/valid.ts: -------------------------------------------------------------------------------- 1 | import { map, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = map(string(), number()); 4 | 5 | export const data = new Map([ 6 | ["a", 1], 7 | ["b", 2], 8 | ]); 9 | 10 | export const output = new Map([ 11 | ["a", 1], 12 | ["b", 2], 13 | ]); 14 | -------------------------------------------------------------------------------- /tests/validation/never/invalid.ts: -------------------------------------------------------------------------------- 1 | import { never } from "../../../src/index.js"; 2 | 3 | export const Struct = never(); 4 | 5 | export const data = true; 6 | 7 | export const failures = [ 8 | { 9 | value: true, 10 | type: "never", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/nullable/invalid.ts: -------------------------------------------------------------------------------- 1 | import { number, nullable } from "../../../src/index.js"; 2 | 3 | export const Struct = number().or(nullable()); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "number", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/nullable/valid-defined-nested.ts: -------------------------------------------------------------------------------- 1 | import { objectLoose, string, number, nullable } from "../../../src/index.js"; 2 | 3 | export const Struct = objectLoose({ 4 | name: string().or(nullable()), 5 | age: number(), 6 | }); 7 | 8 | export const data = { 9 | name: "Jill", 10 | age: 42, 11 | }; 12 | 13 | export const output = { 14 | name: "Jill", 15 | age: 42, 16 | }; 17 | -------------------------------------------------------------------------------- /tests/validation/nullable/valid-defined.ts: -------------------------------------------------------------------------------- 1 | import { number, nullable } from "../../../src/index.js"; 2 | 3 | export const Struct = number().or(nullable()); 4 | 5 | export const data = 42; 6 | 7 | export const output = 42; 8 | -------------------------------------------------------------------------------- /tests/validation/nullable/valid-null-nested.ts: -------------------------------------------------------------------------------- 1 | import { objectLoose, string, number, nullable } from "../../../src/index.js"; 2 | 3 | export const Struct = objectLoose({ 4 | name: string().or(nullable()), 5 | age: number(), 6 | }); 7 | 8 | export const data = { 9 | name: null, 10 | age: 42, 11 | }; 12 | 13 | export const output = { 14 | name: null, 15 | age: 42, 16 | }; 17 | -------------------------------------------------------------------------------- /tests/validation/nullable/valid-null.ts: -------------------------------------------------------------------------------- 1 | import { number, nullable } from "../../../src/index.js"; 2 | 3 | export const Struct = number().or(nullable()); 4 | 5 | export const data = null; 6 | 7 | export const output = null; 8 | -------------------------------------------------------------------------------- /tests/validation/number/invalid.ts: -------------------------------------------------------------------------------- 1 | import { number } from "../../../src/index.js"; 2 | 3 | export const Struct = number(); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "number", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/number/valid.ts: -------------------------------------------------------------------------------- 1 | import { number } from "../../../src/index.js"; 2 | 3 | export const Struct = number(); 4 | 5 | export const data = 42; 6 | 7 | export const output = 42; 8 | -------------------------------------------------------------------------------- /tests/validation/object/invalid-element-nested.ts: -------------------------------------------------------------------------------- 1 | import { object, array, string } from "../../../src/index.js"; 2 | 3 | export const Struct = object({ 4 | name: string(), 5 | emails: array(string()), 6 | }); 7 | 8 | export const data = { 9 | name: "john", 10 | emails: ["name@example.com", false], 11 | }; 12 | 13 | export const failures = [ 14 | { 15 | value: false, 16 | type: "string", 17 | refinement: undefined, 18 | path: ["emails", 1], 19 | branch: [data, data.emails, data.emails[1]], 20 | }, 21 | ]; 22 | -------------------------------------------------------------------------------- /tests/validation/object/invalid-opaque.ts: -------------------------------------------------------------------------------- 1 | import { object } from "../../../src/index.js"; 2 | 3 | export const Struct = object({}); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "object", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/object/invalid-property-nested.ts: -------------------------------------------------------------------------------- 1 | import { object, string } from "../../../src/index.js"; 2 | 3 | export const Struct = object({ 4 | name: string(), 5 | address: object({ 6 | street: string(), 7 | city: string(), 8 | }), 9 | }); 10 | 11 | export const data = { 12 | name: "john", 13 | address: { 14 | street: 123, 15 | city: "Springfield", 16 | }, 17 | }; 18 | 19 | export const failures = [ 20 | { 21 | value: 123, 22 | type: "string", 23 | refinement: undefined, 24 | path: ["address", "street"], 25 | branch: [data, data.address, data.address.street], 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /tests/validation/object/invalid-property.ts: -------------------------------------------------------------------------------- 1 | import { object, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = object({ 4 | name: string(), 5 | age: number(), 6 | height: string(), 7 | }); 8 | 9 | export const data = { 10 | name: "john", 11 | age: "invalid", 12 | height: 2, 13 | }; 14 | 15 | export const failures = [ 16 | { 17 | value: "invalid", 18 | type: "number", 19 | refinement: undefined, 20 | path: ["age"], 21 | branch: [data, data.age], 22 | }, 23 | { 24 | value: 2, 25 | type: "string", 26 | refinement: undefined, 27 | path: ["height"], 28 | branch: [data, data.height], 29 | }, 30 | ]; 31 | -------------------------------------------------------------------------------- /tests/validation/object/invalid.ts: -------------------------------------------------------------------------------- 1 | import { object, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = object({ 4 | name: string(), 5 | age: number(), 6 | }); 7 | 8 | export const data = "invalid"; 9 | 10 | export const failures = [ 11 | { 12 | value: "invalid", 13 | type: "object", 14 | refinement: undefined, 15 | path: [], 16 | branch: [data], 17 | }, 18 | ]; 19 | -------------------------------------------------------------------------------- /tests/validation/object/valid-nested.ts: -------------------------------------------------------------------------------- 1 | import { object, string } from "../../../src/index.js"; 2 | 3 | export const Struct = object({ 4 | name: string(), 5 | address: object({ 6 | street: string(), 7 | city: string(), 8 | }), 9 | }); 10 | 11 | export const data = { 12 | name: "john", 13 | address: { 14 | street: "123 Fake St", 15 | city: "Springfield", 16 | }, 17 | }; 18 | 19 | export const output = { 20 | name: "john", 21 | address: { 22 | street: "123 Fake St", 23 | city: "Springfield", 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /tests/validation/object/valid-property-extra.ts: -------------------------------------------------------------------------------- 1 | import { object, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = object({ 4 | name: string(), 5 | age: number(), 6 | }); 7 | 8 | export const data = { 9 | name: "john", 10 | age: 42, 11 | unknown: true, 12 | }; 13 | 14 | export const output = { 15 | name: "john", 16 | age: 42, 17 | }; 18 | -------------------------------------------------------------------------------- /tests/validation/object/valid.ts: -------------------------------------------------------------------------------- 1 | import { object, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = object({ 4 | name: string(), 5 | age: number(), 6 | }); 7 | 8 | export const data = { 9 | name: "john", 10 | age: 42, 11 | }; 12 | 13 | export const output = { 14 | name: "john", 15 | age: 42, 16 | }; 17 | -------------------------------------------------------------------------------- /tests/validation/objectLoose/invalid-property-nested.ts: -------------------------------------------------------------------------------- 1 | import { objectLoose, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = objectLoose({ 4 | id: number(), 5 | person: objectLoose({ 6 | name: string(), 7 | age: number(), 8 | }), 9 | }); 10 | 11 | export const data = { 12 | id: 1, 13 | }; 14 | 15 | export const failures = [ 16 | { 17 | value: undefined, 18 | type: "type", 19 | refinement: undefined, 20 | path: ["person"], 21 | branch: [data, undefined], 22 | }, 23 | ]; 24 | -------------------------------------------------------------------------------- /tests/validation/objectLoose/invalid-property.ts: -------------------------------------------------------------------------------- 1 | import { objectLoose, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = objectLoose({ 4 | name: string(), 5 | age: number(), 6 | }); 7 | 8 | export const data = { 9 | name: "john", 10 | age: "invalid", 11 | }; 12 | 13 | export const failures = [ 14 | { 15 | value: "invalid", 16 | type: "number", 17 | refinement: undefined, 18 | path: ["age"], 19 | branch: [data, data.age], 20 | }, 21 | ]; 22 | -------------------------------------------------------------------------------- /tests/validation/objectLoose/invalid.ts: -------------------------------------------------------------------------------- 1 | import { objectLoose, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = objectLoose({ 4 | name: string(), 5 | age: number(), 6 | }); 7 | 8 | export const data = "invalid"; 9 | 10 | export const failures = [ 11 | { 12 | value: "invalid", 13 | type: "type", 14 | refinement: undefined, 15 | path: [], 16 | branch: [data], 17 | }, 18 | ]; 19 | -------------------------------------------------------------------------------- /tests/validation/objectLoose/valid-opaque.ts: -------------------------------------------------------------------------------- 1 | import { objectLoose } from "../../../src/index.js"; 2 | 3 | export const Struct = objectLoose({}); 4 | 5 | export const data = { 6 | a: "string", 7 | b: 42, 8 | }; 9 | 10 | export const output = { 11 | a: "string", 12 | b: 42, 13 | }; 14 | -------------------------------------------------------------------------------- /tests/validation/objectLoose/valid.ts: -------------------------------------------------------------------------------- 1 | import { objectLoose, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = objectLoose({ 4 | name: string(), 5 | age: number(), 6 | }); 7 | 8 | export const data = { 9 | name: "john", 10 | age: 42, 11 | extra: "pie", 12 | }; 13 | 14 | export const output = data; 15 | -------------------------------------------------------------------------------- /tests/validation/optional/invalid.ts: -------------------------------------------------------------------------------- 1 | import { number, optional } from "../../../src/index.js"; 2 | 3 | export const Struct = number().or(optional()); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "number", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/optional/valid-defined-nested.ts: -------------------------------------------------------------------------------- 1 | import { objectLoose, string, number, optional } from "../../../src/index.js"; 2 | 3 | export const Struct = objectLoose({ 4 | name: string().or(optional()), 5 | age: number(), 6 | }); 7 | 8 | export const data = { 9 | name: "Jill", 10 | age: 42, 11 | }; 12 | 13 | export const output = { 14 | name: "Jill", 15 | age: 42, 16 | }; 17 | -------------------------------------------------------------------------------- /tests/validation/optional/valid-defined.ts: -------------------------------------------------------------------------------- 1 | import { number, optional } from "../../../src/index.js"; 2 | 3 | export const Struct = number().or(optional()); 4 | 5 | export const data = 42; 6 | 7 | export const output = 42; 8 | -------------------------------------------------------------------------------- /tests/validation/optional/valid-undefined-nested.ts: -------------------------------------------------------------------------------- 1 | import { objectLoose, string, number, optional } from "../../../src/index.js"; 2 | 3 | export const Struct = objectLoose({ 4 | name: string().or(optional()), 5 | age: number(), 6 | }); 7 | 8 | export const data = { 9 | age: 42, 10 | }; 11 | 12 | export const output = { 13 | age: 42, 14 | }; 15 | -------------------------------------------------------------------------------- /tests/validation/optional/valid-undefined.ts: -------------------------------------------------------------------------------- 1 | import { number, optional } from "../../../src/index.js"; 2 | 3 | export const Struct = number().or(optional()); 4 | 5 | export const data = undefined; 6 | 7 | export const output = undefined; 8 | -------------------------------------------------------------------------------- /tests/validation/record/invalid-property.ts: -------------------------------------------------------------------------------- 1 | import { record, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = record(number()); 4 | 5 | export const data = { 6 | a: "a", 7 | b: "b", 8 | }; 9 | 10 | export const failures = [ 11 | { 12 | value: "a", 13 | type: "number", 14 | refinement: undefined, 15 | path: ["a"], 16 | branch: [data, data.a], 17 | }, 18 | { 19 | value: "b", 20 | type: "number", 21 | refinement: undefined, 22 | path: ["b"], 23 | branch: [data, data.b], 24 | }, 25 | ]; 26 | -------------------------------------------------------------------------------- /tests/validation/record/invalid.ts: -------------------------------------------------------------------------------- 1 | import { record, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = record(number()); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "record", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/record/valid.ts: -------------------------------------------------------------------------------- 1 | import { record, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = record(number()); 4 | 5 | export const data = { 6 | a: 1, 7 | b: 2, 8 | }; 9 | 10 | export const output = { 11 | a: 1, 12 | b: 2, 13 | }; 14 | -------------------------------------------------------------------------------- /tests/validation/refine/invalid-multiple-refinements.ts: -------------------------------------------------------------------------------- 1 | import { string, object, fail } from "../../../src/index.js"; 2 | 3 | const PasswordValidator = string().map((pw) => (pw.length >= 8 ? pw : fail())); 4 | const changePasswordStruct = object({ 5 | newPassword: PasswordValidator, 6 | confirmPassword: string(), 7 | }); 8 | 9 | export const Struct = changePasswordStruct.map((values) => { 10 | return values.newPassword === values.confirmPassword ? values : fail(); 11 | }); 12 | 13 | export const data = { 14 | newPassword: "1234567", 15 | confirmPassword: "123456789", 16 | }; 17 | 18 | export const failures = [ 19 | { 20 | value: data.newPassword, 21 | type: "string", 22 | refinement: "MinimumLength", 23 | path: ["newPassword"], 24 | branch: [data, data.newPassword], 25 | }, 26 | { 27 | value: data, 28 | type: "object", 29 | refinement: "PasswordsDoNotMatch", 30 | path: [], 31 | branch: [data], 32 | }, 33 | ]; 34 | -------------------------------------------------------------------------------- /tests/validation/refine/invalid-shorthand.ts: -------------------------------------------------------------------------------- 1 | import { fail, number } from "../../../src/index.js"; 2 | 3 | export const Struct = number().map((v) => (v > 0 ? v : fail())); 4 | 5 | export const data = -1; 6 | 7 | export const failures = [ 8 | { 9 | value: -1, 10 | type: "number", 11 | refinement: "positive", 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/refine/invalid.ts: -------------------------------------------------------------------------------- 1 | import { string, fail } from "../../../src/index.js"; 2 | 3 | export const Struct = string().map(() => fail()); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "string", 11 | refinement: "email", 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/refine/valid.ts: -------------------------------------------------------------------------------- 1 | import { string, fail } from "../../../src/index.js"; 2 | 3 | export const Struct = string().map((x) => (x === data ? x : fail())); 4 | 5 | export const data = "name@example.com"; 6 | 7 | export const output = "name@example.com"; 8 | -------------------------------------------------------------------------------- /tests/validation/set/invalid-element.ts: -------------------------------------------------------------------------------- 1 | import { set, number } from "../../../src/index.js"; 2 | 3 | export const Struct = set(number()); 4 | 5 | export const data = new Set([1, "b", 3]); 6 | 7 | export const failures = [ 8 | { 9 | value: "b", 10 | type: "number", 11 | refinement: undefined, 12 | path: ["b"], 13 | branch: [data, "b"], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/set/invalid-opaque.ts: -------------------------------------------------------------------------------- 1 | import { set, unknown } from "../../../src/index.js"; 2 | 3 | export const Struct = set(unknown()); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "set", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/set/invalid.ts: -------------------------------------------------------------------------------- 1 | import { set, number } from "../../../src/index.js"; 2 | 3 | export const Struct = set(number()); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "set", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/set/valid-opaque.ts: -------------------------------------------------------------------------------- 1 | import { set, unknown } from "../../../src/index.js"; 2 | 3 | export const Struct = set(unknown()); 4 | 5 | export const data = new Set(["a", 2, true]); 6 | 7 | export const output = new Set(["a", 2, true]); 8 | -------------------------------------------------------------------------------- /tests/validation/set/valid.ts: -------------------------------------------------------------------------------- 1 | import { set, number } from "../../../src/index.js"; 2 | 3 | export const Struct = set(number()); 4 | 5 | export const data = new Set([1, 2, 3]); 6 | 7 | export const output = new Set([1, 2, 3]); 8 | -------------------------------------------------------------------------------- /tests/validation/string/invalid.ts: -------------------------------------------------------------------------------- 1 | import { string } from "../../../src/index.js"; 2 | 3 | export const Struct = string(); 4 | 5 | export const data = false; 6 | 7 | export const failures = [ 8 | { 9 | value: false, 10 | type: "string", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/string/valid.ts: -------------------------------------------------------------------------------- 1 | import { string } from "../../../src/index.js"; 2 | 3 | export const Struct = string(); 4 | 5 | export const data = "valid"; 6 | 7 | export const output = "valid"; 8 | -------------------------------------------------------------------------------- /tests/validation/trimmed/invalid.ts: -------------------------------------------------------------------------------- 1 | import { string } from "../../../src/index.js"; 2 | 3 | export const Struct = string().map((s) => s.trim()); 4 | 5 | export const data = false; 6 | 7 | export const failures = [ 8 | { 9 | value: false, 10 | type: "string", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | 17 | export const create = true; 18 | -------------------------------------------------------------------------------- /tests/validation/trimmed/valid.ts: -------------------------------------------------------------------------------- 1 | import { string } from "../../../src/index.js"; 2 | 3 | export const Struct = string().map((s) => s.trim()); 4 | 5 | export const data = " valid "; 6 | 7 | export const output = "valid"; 8 | 9 | export const create = true; 10 | -------------------------------------------------------------------------------- /tests/validation/tuple/invalid-element-missing.ts: -------------------------------------------------------------------------------- 1 | import { tuple, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = tuple([string(), number()]); 4 | 5 | export const data = ["A"]; 6 | 7 | export const failures = [ 8 | { 9 | value: undefined, 10 | type: "number", 11 | refinement: undefined, 12 | path: [1], 13 | branch: [data, data[1]], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/tuple/invalid-element.ts: -------------------------------------------------------------------------------- 1 | import { tuple, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = tuple([string(), number()]); 4 | 5 | export const data = [false, 3]; 6 | 7 | export const failures = [ 8 | { 9 | value: false, 10 | type: "string", 11 | refinement: undefined, 12 | path: [0], 13 | branch: [data, data[0]], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/tuple/invalid.ts: -------------------------------------------------------------------------------- 1 | import { tuple, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = tuple([string(), number()]); 4 | 5 | export const data = "invalid"; 6 | 7 | export const failures = [ 8 | { 9 | value: "invalid", 10 | type: "tuple", 11 | refinement: undefined, 12 | path: [], 13 | branch: [data], 14 | }, 15 | ]; 16 | -------------------------------------------------------------------------------- /tests/validation/tuple/valid-trim.ts: -------------------------------------------------------------------------------- 1 | import { tuple, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = tuple([string(), number()]); 4 | 5 | export const data = ["A", 3, "unknown"]; 6 | 7 | export const output = ["A", 3]; 8 | -------------------------------------------------------------------------------- /tests/validation/tuple/valid.ts: -------------------------------------------------------------------------------- 1 | import { tuple, string, number } from "../../../src/index.js"; 2 | 3 | export const Struct = tuple([string(), number()]); 4 | 5 | export const data = ["A", 1]; 6 | 7 | export const output = ["A", 1]; 8 | -------------------------------------------------------------------------------- /tests/validation/union/coercion-object.ts: -------------------------------------------------------------------------------- 1 | import { string, number, object } from "../../../src/index.js"; 2 | 3 | const A = string(); 4 | const B = object({ a: number(), b: number().or(() => 5) }); 5 | 6 | export const Struct = A.or(B); 7 | 8 | export const data = { a: 5 }; 9 | 10 | export const output = { a: 5, b: 5 }; 11 | 12 | export const create = true; 13 | -------------------------------------------------------------------------------- /tests/validation/union/coercion-type.ts: -------------------------------------------------------------------------------- 1 | import { string, number, objectLoose } from "../../../src/index.js"; 2 | 3 | const A = string(); 4 | const B = objectLoose({ a: number(), b: number().or(() => 5) }); 5 | 6 | export const Struct = A.or(B); 7 | 8 | export const data = { a: 5 }; 9 | 10 | export const output = { a: 5, b: 5 }; 11 | 12 | export const create = true; 13 | -------------------------------------------------------------------------------- /tests/validation/union/coercion.ts: -------------------------------------------------------------------------------- 1 | import { string, number } from "../../../src/index.js"; 2 | 3 | const A = string().or(() => "foo"); 4 | const B = number(); 5 | 6 | export const Struct = A.or(B); 7 | 8 | export const data = undefined; 9 | 10 | export const output = "foo"; 11 | 12 | export const create = true; 13 | -------------------------------------------------------------------------------- /tests/validation/union/invalid.ts: -------------------------------------------------------------------------------- 1 | import { objectLoose, string, number } from "../../../src/index.js"; 2 | 3 | const A = objectLoose({ a: string() }); 4 | const B = objectLoose({ b: number() }); 5 | 6 | export const Struct = A.or(B); 7 | 8 | export const data = { 9 | b: "invalid", 10 | }; 11 | 12 | export const failures = [ 13 | { 14 | value: { b: "invalid" }, 15 | type: "union", 16 | refinement: undefined, 17 | path: [], 18 | branch: [data], 19 | }, 20 | { 21 | value: undefined, 22 | type: "string", 23 | refinement: undefined, 24 | path: ["a"], 25 | branch: [data, undefined], 26 | }, 27 | { 28 | value: "invalid", 29 | type: "number", 30 | refinement: undefined, 31 | path: ["b"], 32 | branch: [data, data.b], 33 | }, 34 | ]; 35 | -------------------------------------------------------------------------------- /tests/validation/union/valid.ts: -------------------------------------------------------------------------------- 1 | import { objectLoose, string, number } from "../../../src/index.js"; 2 | 3 | const A = objectLoose({ a: string() }); 4 | const B = objectLoose({ b: number() }); 5 | 6 | export const Struct = A.or(B); 7 | 8 | export const data = { 9 | a: "a", 10 | }; 11 | 12 | export const output = { 13 | a: "a", 14 | }; 15 | -------------------------------------------------------------------------------- /tests/validation/unknown/valid-number.ts: -------------------------------------------------------------------------------- 1 | import { unknown } from "../../../src/index.js"; 2 | 3 | export const Struct = unknown(); 4 | 5 | export const data = 1; 6 | 7 | export const output = 1; 8 | -------------------------------------------------------------------------------- /tests/validation/unknown/valid-string.ts: -------------------------------------------------------------------------------- 1 | import { unknown } from "../../../src/index.js"; 2 | 3 | export const Struct = unknown(); 4 | 5 | export const data = "valid"; 6 | 7 | export const output = "valid"; 8 | -------------------------------------------------------------------------------- /tests/validation/unknown/valid-undefined.ts: -------------------------------------------------------------------------------- 1 | import { unknown } from "../../../src/index.js"; 2 | 3 | export const Struct = unknown(); 4 | 5 | export const data = undefined; 6 | 7 | export const output = undefined; 8 | -------------------------------------------------------------------------------- /tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "CommonJS", 5 | "outDir": "dist/cjs" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "esModuleInterop": true, 5 | "exactOptionalPropertyTypes": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "isolatedModules": true, 8 | "lib": ["ES2017", "DOM"], 9 | "module": "es2020", 10 | "moduleResolution": "node", 11 | "noUncheckedIndexedAccess": true, 12 | "outDir": "dist", 13 | "rootDirs": ["./src"], 14 | "strictNullChecks": true, 15 | "target": "ES2018", 16 | "incremental": true 17 | }, 18 | "include": ["src"] 19 | } 20 | --------------------------------------------------------------------------------