├── .gitignore ├── .npmignore ├── .npmrc ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── README.md ├── TODO ├── package-lock.json ├── package.json ├── src ├── api-v0 │ ├── api.ts │ └── index.ts ├── create-type-info.ts ├── descriptor.ts ├── index.ts ├── operations │ ├── assert-valid.ts │ ├── check.ts │ ├── generate-value.ts │ ├── index.ts │ ├── is-valid.ts │ ├── sanitize.ts │ ├── to-json-schema.ts │ └── to-string.ts ├── type-info.ts └── utils │ ├── anonymize.ts │ ├── index.ts │ └── inspect.ts ├── tests ├── api-v0 │ ├── get-json-schema.test.ts │ ├── get-validation-errors.test.ts │ ├── is.test.ts │ ├── remove-excess-properties.test.ts │ └── to-string.test.ts ├── assert-valid.test.ts ├── check.test.ts ├── is-valid.test.ts ├── sanitize.test.ts ├── to-json-schema.test.ts ├── to-string.test.ts └── types.test.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # app data directory 61 | /appdata 62 | 63 | # fusebox cache 64 | .fusebox 65 | 66 | # dist directories 67 | dist/ 68 | 69 | # Other 70 | .cache/ 71 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | dist/test/ 3 | dist/extras/ 4 | scripts/ 5 | src/ 6 | test/ 7 | extras/ 8 | .cache/ 9 | *.ts 10 | !*.d.ts 11 | *.log 12 | *.test.js 13 | *.test.d.ts 14 | tsconfig.json 15 | README.md 16 | TODO.md 17 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "program": "${workspaceFolder}/dist/commonjs/index.js", 12 | "outFiles": [ 13 | "${workspaceFolder}/dist/commonjs/**/*.js" 14 | ] 15 | }, 16 | { 17 | "name": "Run Test Suite", 18 | "type": "node", 19 | "request": "launch", 20 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", 21 | "args": [ 22 | "-r", 23 | "ts-node/register", 24 | "-u", 25 | "bdd", 26 | "--timeout", 27 | "999999", 28 | "--colors", 29 | "${workspaceFolder}/tests/**/*.test.ts", 30 | ], 31 | "env": { 32 | "TS_NODE_PROJECT": "${workspaceFolder}/tsconfig.json" 33 | }, 34 | "internalConsoleOptions": "openOnSessionStart", 35 | "skipFiles": [ 36 | "/**", 37 | "**/node_modules/**" 38 | ] 39 | }, 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Columns at which to show vertical rulers 3 | "editor.rulers": [120], 4 | 5 | // Specifies the folder path containing the tsserver and lib*.d.ts files to use. 6 | "typescript.tsdk": "node_modules/typescript/lib", 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "build", 9 | "problemMatcher": [ 10 | "$tsc" 11 | ] 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rtti 2 | 3 | **Runtime type validation for JavaScript and TypeScript programs.** 4 | 5 | This library bring the benefits of TypeScript's type system to runtime code. By declaring types using runtime constructs, it is possible to add an extra level of runtime type safety that static checking alone cannot provide. For example: 6 | - ensure that a parsed JSON string produces a value that conforms to an expected schema 7 | - verify that a HTTP request body conforms to an expected schema 8 | - ensure that a HTTP response body does not send additional properties other that those intended for the client 9 | 10 | There is no need to declare any type twice (i.e., once for JS and once TS), since the TypeScript type can be inferred from the `example` property of any given `TypeInfo` value. 11 | 12 | ## Installation 13 | 14 | `npm install rtti` 15 | 16 | ## Usage Example 17 | 18 | ```ts 19 | import {t} from 'rtti'; 20 | 21 | const myType = t.union( 22 | t.unit('foo'), 23 | t.unit('bar') 24 | ); 25 | 26 | // prints: "foo" | "bar" 27 | console.log(myType.toString()); 28 | 29 | // prints: true 30 | console.log(myType.isValid('foo')); 31 | 32 | // prints: false 33 | console.log(myType.isValid('baz')); 34 | 35 | // prints: { 36 | // isValid: false, 37 | // errors: [ 38 | // {path: '^', message: 'The value "baz" does not conform to the union type'} 39 | // ] 40 | // } 41 | console.log(myType.check('baz')); 42 | 43 | // TypeScript only - static type inference: 44 | type MyType = typeof myType.example; // type MyType = "foo" | "bar" 45 | ``` 46 | 47 | 48 | ## API 49 | 50 | > NOTE: The [v0.4 API](https://github.com/yortus/rtti/tree/v0-api#api) is deprecated but still supported. 51 | --- 52 | ##### `t.string`, `t.object(...)`, etc 53 | Construct a `TypeInfo` instance that matches a particular set of runtime values. 54 |
55 | 56 | --- 57 | ##### `myType.assertValid(value: unknown): void` 58 | Ensures the given `value` matches the given `type`, otherwise throws an error. The error object has an `errors` 59 | property containing details about the validation error(s). 60 |
61 | 62 | --- 63 | ##### `myType.check(value: unknown): {isValid: boolean, errors: Array<{path: string, message: string}>}` 64 | Returns a list of descriptive validation errors explaining why the given `value` does not match the given `type`. 65 |
66 | 67 | --- 68 | ##### `myType.example` 69 | An example value that conforms to the given `TypeInfo` type. The TypeScript type can be inferred from this property. 70 |
71 | 72 | --- 73 | ##### `myType.isValid(value: unknown): boolean` 74 | Returns `true` if the given `value` matches the given `type`, or `false` otherwise. 75 |
76 | 77 | --- 78 | ##### `myType.sanitize(value: typeof myType.example): typeof myType.example` 79 | Returns a copy of the given `value`, but where any properties not declared in `type` have been removed. 80 |
81 | 82 | --- 83 | ##### `myType.toJsonSchema(type: TypeInfo): unknown` 84 | Returns a JSON schema representation of the given type. 85 |
86 | 87 | --- 88 | ##### `myType.toString(): string` 89 | Returns a descriptive string for the given `type`. 90 |
91 | 92 | --- 93 | ##### `TypeInfo` 94 | An object used by the RTTI library to describes a set of matching runtime values. These objects may be created using the `t.` syntax. See the following table for examples. 95 |
96 | 97 | 98 | ## Supported Types 99 | 100 | 101 | | | PRIMITIVE JAVASCRIPT TYPES | | | | 102 | | -------------- | ------------------------------------------------------------ | -------------------------------- | ----------------------------------------- | -------------------------------------------- | 103 | | **Datatype** | **Example RTTI Declaration** | **TS Type** | **Matching JS Values** | **Non-Matching JS Values** | 104 | | Boolean | `t.boolean` | `boolean` | `true`, `false` | `0`, `''`, `'yes'`, `null` | 105 | | Date | `t.date` | `Date` | `new Date()` | `'2020-01-01'` | 106 | | Null | `t.null` | `null` | `null` | `undefined`, `0` | 107 | | Number | `t.number` | `number` | `42`, `3.14` | `'three'`, `false` | 108 | | String | `t.string` | `string` | `'foo'`, `'1:1'` | `42`, `{foo: 1}` | 109 | | Undefined | `t.undefined` | `undefined` | `undefined` | `null`, `0` | 110 | | | **COMPOUND JAVASCRIPT TYPES** | | | | 111 | | **Datatype** | **Example RTTI Declaration** | **TS Type** | **Matching JS Values** | **Non-Matching JS Values** | 112 | | Array | `t.array(t.number)` | `number[]` | `[1, 2, 3]` | `123`, `[1, 'a']` | 113 | | Object | `t.object({foo: t.string, isBar: t.optional(t.boolean)})` | `{foo: string, isBar?: boolean}` | `{foo: 'foo'}`, `{foo: 'x', isBar: true}` | `{bar: 'bar'}`, `{foo: true}` | 114 | | | **ADDITIONAL TYPESCRIPT TYPES** | | | | 115 | | **Datatype** | **Example RTTI Declaration** | **TS Type** | **Matching JS Values** | **Non-Matching JS Values** | 116 | | Any | `t.any` | `any` | `42`, `'foo'`, `null`, `[1, 2]`, `{}` | - | 117 | | Branded String | `t.brandedString('usd')` | | | | 118 | | Intersection | `t.intersection(t.object({foo: t.string}), t.object({bar: t.number}))` | `{foo: string} & {bar: number}` | `{foo: 'abc', bar: 42}` | `{bar: 42}` | 119 | | Never | `t.never` | `never` | - | `42`, `'foo'`, `null`, `[1, 2]`, `{}` | 120 | | Tuple | `t.tuple(t.string, t.number)` | `[string, number]` | `['foo', 42]` | `['foo']`, `['foo', 'bar']`, `['foo', 4, 2]` | 121 | | Union | `t.union(t.object({foo: t.string}), t.object({bar: t.number}))` | `{foo: string} | {bar: number}` | `{foo: 'abc'}`, `{bar: 42}` | `{baz: 0}`, `{foo: 42}` | 122 | | Unit Type | `t.unit('foo')` | `'foo'` | `'foo'` | `'bar'`, `'abc'`, `42` | 123 | | Unknown | `t.unknown` | | `42`, `'foo'`, `null`, `[1, 2]`, `{}` | - | 124 | 125 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | TODO: 2 | [x] fix: TypeOf for optional props 3 | [x] fix: t.optional is *not* a Type, it is only valid inside objects and tuples 4 | [x] is() 5 | [x] JSON.parse 6 | [x] JSON.stringify 7 | [x] assert(): use TS3.7 'asserts' return type annotation 8 | [ ] removeExcessProperties 9 | [ ] fix: support t.optional in tuples 10 | [ ] fix: JSON.parse reviver for special values 11 | [ ] fix: JSON.stringify replacer for special values 12 | [ ] fix: JSON.stringify: use removeExcessProperties 13 | [ ] add error test cases for JSON.parse/stringify 14 | 15 | [ ] Other builtins: RegExp, Symbol, Promise, Function, BigInt 16 | [ ] Other type operators: keyof, index access, mapped, conditional, spread, rest 17 | 18 | [ ] support/detect cycles / self-referential types and values 19 | 20 | 21 | API requirments: 22 | [ ] pretty-print a type: `toString(t: Type): string` 23 | [x] basic support 24 | [ ] support/detect recursive types 25 | [ ] support bounded output length 26 | [ ] convert a value to a type `typeOf(v: unknown): Type` 27 | [ ] explicitly reject unsupported value types, eg Map, Set, etc 28 | [ ] must work with cyclic object/array references 29 | [ ] check assignability between two types: `isAssignableFrom(target: Type, source: Type): boolean` 30 | [x] list validation errors for a value that is expected to be a certain type 31 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rtti", 3 | "version": "1.1.4", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "rtti", 9 | "version": "1.1.4", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@types/chai": "^4.3.0", 13 | "@types/mocha": "^9.1.0", 14 | "@types/node": "^16.*", 15 | "chai": "^4.3.6", 16 | "mocha": "^9.2.0", 17 | "rimraf": "^3.0.2", 18 | "symlink-dir": "^5.0.1", 19 | "ts-node": "^10.5.0", 20 | "typescript": "^4.5.5" 21 | } 22 | }, 23 | "node_modules/@cspotcode/source-map-consumer": { 24 | "version": "0.8.0", 25 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", 26 | "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", 27 | "dev": true, 28 | "engines": { 29 | "node": ">= 12" 30 | } 31 | }, 32 | "node_modules/@cspotcode/source-map-support": { 33 | "version": "0.7.0", 34 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", 35 | "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", 36 | "dev": true, 37 | "dependencies": { 38 | "@cspotcode/source-map-consumer": "0.8.0" 39 | }, 40 | "engines": { 41 | "node": ">=12" 42 | } 43 | }, 44 | "node_modules/@tsconfig/node10": { 45 | "version": "1.0.8", 46 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", 47 | "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", 48 | "dev": true 49 | }, 50 | "node_modules/@tsconfig/node12": { 51 | "version": "1.0.9", 52 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", 53 | "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", 54 | "dev": true 55 | }, 56 | "node_modules/@tsconfig/node14": { 57 | "version": "1.0.1", 58 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", 59 | "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", 60 | "dev": true 61 | }, 62 | "node_modules/@tsconfig/node16": { 63 | "version": "1.0.2", 64 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", 65 | "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", 66 | "dev": true 67 | }, 68 | "node_modules/@types/chai": { 69 | "version": "4.3.0", 70 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", 71 | "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==", 72 | "dev": true 73 | }, 74 | "node_modules/@types/mocha": { 75 | "version": "9.1.0", 76 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", 77 | "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", 78 | "dev": true 79 | }, 80 | "node_modules/@types/node": { 81 | "version": "16.11.25", 82 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.25.tgz", 83 | "integrity": "sha512-NrTwfD7L1RTc2qrHQD4RTTy4p0CO2LatKBEKEds3CaVuhoM/+DJzmWZl5f+ikR8cm8F5mfJxK+9rQq07gRiSjQ==", 84 | "dev": true 85 | }, 86 | "node_modules/@ungap/promise-all-settled": { 87 | "version": "1.1.2", 88 | "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", 89 | "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", 90 | "dev": true 91 | }, 92 | "node_modules/@zkochan/rimraf": { 93 | "version": "2.1.2", 94 | "resolved": "https://registry.npmjs.org/@zkochan/rimraf/-/rimraf-2.1.2.tgz", 95 | "integrity": "sha512-Lc2oK51J6aQWcLWTloobJun5ZF41BbTDdLvE+aMcexoVWFoFqvZmnZoyXR2IZk6NJEVoZW8tjgtvQLfTsmRs2Q==", 96 | "dev": true, 97 | "dependencies": { 98 | "rimraf": "^3.0.2" 99 | }, 100 | "engines": { 101 | "node": ">=12.10" 102 | } 103 | }, 104 | "node_modules/acorn": { 105 | "version": "8.7.0", 106 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", 107 | "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", 108 | "dev": true, 109 | "bin": { 110 | "acorn": "bin/acorn" 111 | }, 112 | "engines": { 113 | "node": ">=0.4.0" 114 | } 115 | }, 116 | "node_modules/acorn-walk": { 117 | "version": "8.2.0", 118 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 119 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 120 | "dev": true, 121 | "engines": { 122 | "node": ">=0.4.0" 123 | } 124 | }, 125 | "node_modules/ansi-colors": { 126 | "version": "4.1.1", 127 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 128 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 129 | "dev": true, 130 | "engines": { 131 | "node": ">=6" 132 | } 133 | }, 134 | "node_modules/ansi-regex": { 135 | "version": "5.0.1", 136 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 137 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 138 | "dev": true, 139 | "engines": { 140 | "node": ">=8" 141 | } 142 | }, 143 | "node_modules/ansi-styles": { 144 | "version": "4.3.0", 145 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 146 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 147 | "dev": true, 148 | "dependencies": { 149 | "color-convert": "^2.0.1" 150 | }, 151 | "engines": { 152 | "node": ">=8" 153 | }, 154 | "funding": { 155 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 156 | } 157 | }, 158 | "node_modules/anymatch": { 159 | "version": "3.1.2", 160 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 161 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 162 | "dev": true, 163 | "dependencies": { 164 | "normalize-path": "^3.0.0", 165 | "picomatch": "^2.0.4" 166 | }, 167 | "engines": { 168 | "node": ">= 8" 169 | } 170 | }, 171 | "node_modules/arg": { 172 | "version": "4.1.3", 173 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 174 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 175 | "dev": true 176 | }, 177 | "node_modules/argparse": { 178 | "version": "2.0.1", 179 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 180 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 181 | "dev": true 182 | }, 183 | "node_modules/assertion-error": { 184 | "version": "1.1.0", 185 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 186 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 187 | "dev": true, 188 | "engines": { 189 | "node": "*" 190 | } 191 | }, 192 | "node_modules/balanced-match": { 193 | "version": "1.0.2", 194 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 195 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 196 | "dev": true 197 | }, 198 | "node_modules/better-path-resolve": { 199 | "version": "1.0.0", 200 | "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", 201 | "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", 202 | "dev": true, 203 | "dependencies": { 204 | "is-windows": "^1.0.0" 205 | }, 206 | "engines": { 207 | "node": ">=4" 208 | } 209 | }, 210 | "node_modules/binary-extensions": { 211 | "version": "2.2.0", 212 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 213 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 214 | "dev": true, 215 | "engines": { 216 | "node": ">=8" 217 | } 218 | }, 219 | "node_modules/brace-expansion": { 220 | "version": "1.1.11", 221 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 222 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 223 | "dev": true, 224 | "dependencies": { 225 | "balanced-match": "^1.0.0", 226 | "concat-map": "0.0.1" 227 | } 228 | }, 229 | "node_modules/braces": { 230 | "version": "3.0.2", 231 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 232 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 233 | "dev": true, 234 | "dependencies": { 235 | "fill-range": "^7.0.1" 236 | }, 237 | "engines": { 238 | "node": ">=8" 239 | } 240 | }, 241 | "node_modules/browser-stdout": { 242 | "version": "1.3.1", 243 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 244 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 245 | "dev": true 246 | }, 247 | "node_modules/camelcase": { 248 | "version": "6.3.0", 249 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 250 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 251 | "dev": true, 252 | "engines": { 253 | "node": ">=10" 254 | }, 255 | "funding": { 256 | "url": "https://github.com/sponsors/sindresorhus" 257 | } 258 | }, 259 | "node_modules/chai": { 260 | "version": "4.3.6", 261 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", 262 | "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", 263 | "dev": true, 264 | "dependencies": { 265 | "assertion-error": "^1.1.0", 266 | "check-error": "^1.0.2", 267 | "deep-eql": "^3.0.1", 268 | "get-func-name": "^2.0.0", 269 | "loupe": "^2.3.1", 270 | "pathval": "^1.1.1", 271 | "type-detect": "^4.0.5" 272 | }, 273 | "engines": { 274 | "node": ">=4" 275 | } 276 | }, 277 | "node_modules/chalk": { 278 | "version": "4.1.2", 279 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 280 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 281 | "dev": true, 282 | "dependencies": { 283 | "ansi-styles": "^4.1.0", 284 | "supports-color": "^7.1.0" 285 | }, 286 | "engines": { 287 | "node": ">=10" 288 | }, 289 | "funding": { 290 | "url": "https://github.com/chalk/chalk?sponsor=1" 291 | } 292 | }, 293 | "node_modules/chalk/node_modules/supports-color": { 294 | "version": "7.2.0", 295 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 296 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 297 | "dev": true, 298 | "dependencies": { 299 | "has-flag": "^4.0.0" 300 | }, 301 | "engines": { 302 | "node": ">=8" 303 | } 304 | }, 305 | "node_modules/check-error": { 306 | "version": "1.0.2", 307 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 308 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 309 | "dev": true, 310 | "engines": { 311 | "node": "*" 312 | } 313 | }, 314 | "node_modules/chokidar": { 315 | "version": "3.5.3", 316 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 317 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 318 | "dev": true, 319 | "funding": [ 320 | { 321 | "type": "individual", 322 | "url": "https://paulmillr.com/funding/" 323 | } 324 | ], 325 | "dependencies": { 326 | "anymatch": "~3.1.2", 327 | "braces": "~3.0.2", 328 | "glob-parent": "~5.1.2", 329 | "is-binary-path": "~2.1.0", 330 | "is-glob": "~4.0.1", 331 | "normalize-path": "~3.0.0", 332 | "readdirp": "~3.6.0" 333 | }, 334 | "engines": { 335 | "node": ">= 8.10.0" 336 | }, 337 | "optionalDependencies": { 338 | "fsevents": "~2.3.2" 339 | } 340 | }, 341 | "node_modules/cliui": { 342 | "version": "7.0.4", 343 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 344 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 345 | "dev": true, 346 | "dependencies": { 347 | "string-width": "^4.2.0", 348 | "strip-ansi": "^6.0.0", 349 | "wrap-ansi": "^7.0.0" 350 | } 351 | }, 352 | "node_modules/color-convert": { 353 | "version": "2.0.1", 354 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 355 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 356 | "dev": true, 357 | "dependencies": { 358 | "color-name": "~1.1.4" 359 | }, 360 | "engines": { 361 | "node": ">=7.0.0" 362 | } 363 | }, 364 | "node_modules/color-name": { 365 | "version": "1.1.4", 366 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 367 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 368 | "dev": true 369 | }, 370 | "node_modules/concat-map": { 371 | "version": "0.0.1", 372 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 373 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 374 | "dev": true 375 | }, 376 | "node_modules/create-require": { 377 | "version": "1.1.1", 378 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 379 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 380 | "dev": true 381 | }, 382 | "node_modules/debug": { 383 | "version": "4.3.3", 384 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", 385 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", 386 | "dev": true, 387 | "dependencies": { 388 | "ms": "2.1.2" 389 | }, 390 | "engines": { 391 | "node": ">=6.0" 392 | }, 393 | "peerDependenciesMeta": { 394 | "supports-color": { 395 | "optional": true 396 | } 397 | } 398 | }, 399 | "node_modules/debug/node_modules/ms": { 400 | "version": "2.1.2", 401 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 402 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 403 | "dev": true 404 | }, 405 | "node_modules/decamelize": { 406 | "version": "4.0.0", 407 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 408 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 409 | "dev": true, 410 | "engines": { 411 | "node": ">=10" 412 | }, 413 | "funding": { 414 | "url": "https://github.com/sponsors/sindresorhus" 415 | } 416 | }, 417 | "node_modules/deep-eql": { 418 | "version": "3.0.1", 419 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 420 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 421 | "dev": true, 422 | "dependencies": { 423 | "type-detect": "^4.0.0" 424 | }, 425 | "engines": { 426 | "node": ">=0.12" 427 | } 428 | }, 429 | "node_modules/diff": { 430 | "version": "5.0.0", 431 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 432 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 433 | "dev": true, 434 | "engines": { 435 | "node": ">=0.3.1" 436 | } 437 | }, 438 | "node_modules/emoji-regex": { 439 | "version": "8.0.0", 440 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 441 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 442 | "dev": true 443 | }, 444 | "node_modules/escalade": { 445 | "version": "3.1.1", 446 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 447 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 448 | "dev": true, 449 | "engines": { 450 | "node": ">=6" 451 | } 452 | }, 453 | "node_modules/escape-string-regexp": { 454 | "version": "4.0.0", 455 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 456 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 457 | "dev": true, 458 | "engines": { 459 | "node": ">=10" 460 | }, 461 | "funding": { 462 | "url": "https://github.com/sponsors/sindresorhus" 463 | } 464 | }, 465 | "node_modules/fill-range": { 466 | "version": "7.0.1", 467 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 468 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 469 | "dev": true, 470 | "dependencies": { 471 | "to-regex-range": "^5.0.1" 472 | }, 473 | "engines": { 474 | "node": ">=8" 475 | } 476 | }, 477 | "node_modules/find-up": { 478 | "version": "5.0.0", 479 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 480 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 481 | "dev": true, 482 | "dependencies": { 483 | "locate-path": "^6.0.0", 484 | "path-exists": "^4.0.0" 485 | }, 486 | "engines": { 487 | "node": ">=10" 488 | }, 489 | "funding": { 490 | "url": "https://github.com/sponsors/sindresorhus" 491 | } 492 | }, 493 | "node_modules/flat": { 494 | "version": "5.0.2", 495 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 496 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 497 | "dev": true, 498 | "bin": { 499 | "flat": "cli.js" 500 | } 501 | }, 502 | "node_modules/fs.realpath": { 503 | "version": "1.0.0", 504 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 505 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 506 | "dev": true 507 | }, 508 | "node_modules/fsevents": { 509 | "version": "2.3.2", 510 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 511 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 512 | "dev": true, 513 | "hasInstallScript": true, 514 | "optional": true, 515 | "os": [ 516 | "darwin" 517 | ], 518 | "engines": { 519 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 520 | } 521 | }, 522 | "node_modules/get-caller-file": { 523 | "version": "2.0.5", 524 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 525 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 526 | "dev": true, 527 | "engines": { 528 | "node": "6.* || 8.* || >= 10.*" 529 | } 530 | }, 531 | "node_modules/get-func-name": { 532 | "version": "2.0.0", 533 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 534 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 535 | "dev": true, 536 | "engines": { 537 | "node": "*" 538 | } 539 | }, 540 | "node_modules/glob": { 541 | "version": "7.2.0", 542 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 543 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 544 | "dev": true, 545 | "dependencies": { 546 | "fs.realpath": "^1.0.0", 547 | "inflight": "^1.0.4", 548 | "inherits": "2", 549 | "minimatch": "^3.0.4", 550 | "once": "^1.3.0", 551 | "path-is-absolute": "^1.0.0" 552 | }, 553 | "engines": { 554 | "node": "*" 555 | }, 556 | "funding": { 557 | "url": "https://github.com/sponsors/isaacs" 558 | } 559 | }, 560 | "node_modules/glob-parent": { 561 | "version": "5.1.2", 562 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 563 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 564 | "dev": true, 565 | "dependencies": { 566 | "is-glob": "^4.0.1" 567 | }, 568 | "engines": { 569 | "node": ">= 6" 570 | } 571 | }, 572 | "node_modules/growl": { 573 | "version": "1.10.5", 574 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 575 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 576 | "dev": true, 577 | "engines": { 578 | "node": ">=4.x" 579 | } 580 | }, 581 | "node_modules/has-flag": { 582 | "version": "4.0.0", 583 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 584 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 585 | "dev": true, 586 | "engines": { 587 | "node": ">=8" 588 | } 589 | }, 590 | "node_modules/he": { 591 | "version": "1.2.0", 592 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 593 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 594 | "dev": true, 595 | "bin": { 596 | "he": "bin/he" 597 | } 598 | }, 599 | "node_modules/inflight": { 600 | "version": "1.0.6", 601 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 602 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 603 | "dev": true, 604 | "dependencies": { 605 | "once": "^1.3.0", 606 | "wrappy": "1" 607 | } 608 | }, 609 | "node_modules/inherits": { 610 | "version": "2.0.4", 611 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 612 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 613 | "dev": true 614 | }, 615 | "node_modules/is-binary-path": { 616 | "version": "2.1.0", 617 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 618 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 619 | "dev": true, 620 | "dependencies": { 621 | "binary-extensions": "^2.0.0" 622 | }, 623 | "engines": { 624 | "node": ">=8" 625 | } 626 | }, 627 | "node_modules/is-extglob": { 628 | "version": "2.1.1", 629 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 630 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 631 | "dev": true, 632 | "engines": { 633 | "node": ">=0.10.0" 634 | } 635 | }, 636 | "node_modules/is-fullwidth-code-point": { 637 | "version": "3.0.0", 638 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 639 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 640 | "dev": true, 641 | "engines": { 642 | "node": ">=8" 643 | } 644 | }, 645 | "node_modules/is-glob": { 646 | "version": "4.0.3", 647 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 648 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 649 | "dev": true, 650 | "dependencies": { 651 | "is-extglob": "^2.1.1" 652 | }, 653 | "engines": { 654 | "node": ">=0.10.0" 655 | } 656 | }, 657 | "node_modules/is-number": { 658 | "version": "7.0.0", 659 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 660 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 661 | "dev": true, 662 | "engines": { 663 | "node": ">=0.12.0" 664 | } 665 | }, 666 | "node_modules/is-plain-obj": { 667 | "version": "2.1.0", 668 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 669 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 670 | "dev": true, 671 | "engines": { 672 | "node": ">=8" 673 | } 674 | }, 675 | "node_modules/is-unicode-supported": { 676 | "version": "0.1.0", 677 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 678 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 679 | "dev": true, 680 | "engines": { 681 | "node": ">=10" 682 | }, 683 | "funding": { 684 | "url": "https://github.com/sponsors/sindresorhus" 685 | } 686 | }, 687 | "node_modules/is-windows": { 688 | "version": "1.0.2", 689 | "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", 690 | "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", 691 | "dev": true, 692 | "engines": { 693 | "node": ">=0.10.0" 694 | } 695 | }, 696 | "node_modules/isexe": { 697 | "version": "2.0.0", 698 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 699 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 700 | "dev": true 701 | }, 702 | "node_modules/js-yaml": { 703 | "version": "4.1.0", 704 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 705 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 706 | "dev": true, 707 | "dependencies": { 708 | "argparse": "^2.0.1" 709 | }, 710 | "bin": { 711 | "js-yaml": "bin/js-yaml.js" 712 | } 713 | }, 714 | "node_modules/locate-path": { 715 | "version": "6.0.0", 716 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 717 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 718 | "dev": true, 719 | "dependencies": { 720 | "p-locate": "^5.0.0" 721 | }, 722 | "engines": { 723 | "node": ">=10" 724 | }, 725 | "funding": { 726 | "url": "https://github.com/sponsors/sindresorhus" 727 | } 728 | }, 729 | "node_modules/log-symbols": { 730 | "version": "4.1.0", 731 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 732 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 733 | "dev": true, 734 | "dependencies": { 735 | "chalk": "^4.1.0", 736 | "is-unicode-supported": "^0.1.0" 737 | }, 738 | "engines": { 739 | "node": ">=10" 740 | }, 741 | "funding": { 742 | "url": "https://github.com/sponsors/sindresorhus" 743 | } 744 | }, 745 | "node_modules/loupe": { 746 | "version": "2.3.4", 747 | "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", 748 | "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", 749 | "dev": true, 750 | "dependencies": { 751 | "get-func-name": "^2.0.0" 752 | } 753 | }, 754 | "node_modules/make-error": { 755 | "version": "1.3.6", 756 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 757 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 758 | "dev": true 759 | }, 760 | "node_modules/minimatch": { 761 | "version": "3.0.4", 762 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 763 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 764 | "dev": true, 765 | "dependencies": { 766 | "brace-expansion": "^1.1.7" 767 | }, 768 | "engines": { 769 | "node": "*" 770 | } 771 | }, 772 | "node_modules/mocha": { 773 | "version": "9.2.1", 774 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.1.tgz", 775 | "integrity": "sha512-T7uscqjJVS46Pq1XDXyo9Uvey9gd3huT/DD9cYBb4K2Xc/vbKRPUWK067bxDQRK0yIz6Jxk73IrnimvASzBNAQ==", 776 | "dev": true, 777 | "dependencies": { 778 | "@ungap/promise-all-settled": "1.1.2", 779 | "ansi-colors": "4.1.1", 780 | "browser-stdout": "1.3.1", 781 | "chokidar": "3.5.3", 782 | "debug": "4.3.3", 783 | "diff": "5.0.0", 784 | "escape-string-regexp": "4.0.0", 785 | "find-up": "5.0.0", 786 | "glob": "7.2.0", 787 | "growl": "1.10.5", 788 | "he": "1.2.0", 789 | "js-yaml": "4.1.0", 790 | "log-symbols": "4.1.0", 791 | "minimatch": "3.0.4", 792 | "ms": "2.1.3", 793 | "nanoid": "3.2.0", 794 | "serialize-javascript": "6.0.0", 795 | "strip-json-comments": "3.1.1", 796 | "supports-color": "8.1.1", 797 | "which": "2.0.2", 798 | "workerpool": "6.2.0", 799 | "yargs": "16.2.0", 800 | "yargs-parser": "20.2.4", 801 | "yargs-unparser": "2.0.0" 802 | }, 803 | "bin": { 804 | "_mocha": "bin/_mocha", 805 | "mocha": "bin/mocha" 806 | }, 807 | "engines": { 808 | "node": ">= 12.0.0" 809 | }, 810 | "funding": { 811 | "type": "opencollective", 812 | "url": "https://opencollective.com/mochajs" 813 | } 814 | }, 815 | "node_modules/ms": { 816 | "version": "2.1.3", 817 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 818 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 819 | "dev": true 820 | }, 821 | "node_modules/nanoid": { 822 | "version": "3.2.0", 823 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", 824 | "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", 825 | "dev": true, 826 | "bin": { 827 | "nanoid": "bin/nanoid.cjs" 828 | }, 829 | "engines": { 830 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 831 | } 832 | }, 833 | "node_modules/normalize-path": { 834 | "version": "3.0.0", 835 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 836 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 837 | "dev": true, 838 | "engines": { 839 | "node": ">=0.10.0" 840 | } 841 | }, 842 | "node_modules/once": { 843 | "version": "1.4.0", 844 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 845 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 846 | "dev": true, 847 | "dependencies": { 848 | "wrappy": "1" 849 | } 850 | }, 851 | "node_modules/p-limit": { 852 | "version": "3.1.0", 853 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 854 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 855 | "dev": true, 856 | "dependencies": { 857 | "yocto-queue": "^0.1.0" 858 | }, 859 | "engines": { 860 | "node": ">=10" 861 | }, 862 | "funding": { 863 | "url": "https://github.com/sponsors/sindresorhus" 864 | } 865 | }, 866 | "node_modules/p-locate": { 867 | "version": "5.0.0", 868 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 869 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 870 | "dev": true, 871 | "dependencies": { 872 | "p-limit": "^3.0.2" 873 | }, 874 | "engines": { 875 | "node": ">=10" 876 | }, 877 | "funding": { 878 | "url": "https://github.com/sponsors/sindresorhus" 879 | } 880 | }, 881 | "node_modules/path-exists": { 882 | "version": "4.0.0", 883 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 884 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 885 | "dev": true, 886 | "engines": { 887 | "node": ">=8" 888 | } 889 | }, 890 | "node_modules/path-is-absolute": { 891 | "version": "1.0.1", 892 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 893 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 894 | "dev": true, 895 | "engines": { 896 | "node": ">=0.10.0" 897 | } 898 | }, 899 | "node_modules/pathval": { 900 | "version": "1.1.1", 901 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", 902 | "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", 903 | "dev": true, 904 | "engines": { 905 | "node": "*" 906 | } 907 | }, 908 | "node_modules/picomatch": { 909 | "version": "2.3.1", 910 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 911 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 912 | "dev": true, 913 | "engines": { 914 | "node": ">=8.6" 915 | }, 916 | "funding": { 917 | "url": "https://github.com/sponsors/jonschlinkert" 918 | } 919 | }, 920 | "node_modules/randombytes": { 921 | "version": "2.1.0", 922 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 923 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 924 | "dev": true, 925 | "dependencies": { 926 | "safe-buffer": "^5.1.0" 927 | } 928 | }, 929 | "node_modules/readdirp": { 930 | "version": "3.6.0", 931 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 932 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 933 | "dev": true, 934 | "dependencies": { 935 | "picomatch": "^2.2.1" 936 | }, 937 | "engines": { 938 | "node": ">=8.10.0" 939 | } 940 | }, 941 | "node_modules/rename-overwrite": { 942 | "version": "4.0.2", 943 | "resolved": "https://registry.npmjs.org/rename-overwrite/-/rename-overwrite-4.0.2.tgz", 944 | "integrity": "sha512-L1sgBgagVgOgb1Z6QZr1yJgSMHI4SXQqAH0l/UbeyHnLKxECvKIlyVEmBo4BqsCAZGg0SBSyjCh68lis5PgC7g==", 945 | "dev": true, 946 | "dependencies": { 947 | "@zkochan/rimraf": "^2.1.2" 948 | }, 949 | "engines": { 950 | "node": ">=12.10" 951 | } 952 | }, 953 | "node_modules/require-directory": { 954 | "version": "2.1.1", 955 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 956 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 957 | "dev": true, 958 | "engines": { 959 | "node": ">=0.10.0" 960 | } 961 | }, 962 | "node_modules/rimraf": { 963 | "version": "3.0.2", 964 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 965 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 966 | "dev": true, 967 | "dependencies": { 968 | "glob": "^7.1.3" 969 | }, 970 | "bin": { 971 | "rimraf": "bin.js" 972 | }, 973 | "funding": { 974 | "url": "https://github.com/sponsors/isaacs" 975 | } 976 | }, 977 | "node_modules/safe-buffer": { 978 | "version": "5.2.1", 979 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 980 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 981 | "dev": true, 982 | "funding": [ 983 | { 984 | "type": "github", 985 | "url": "https://github.com/sponsors/feross" 986 | }, 987 | { 988 | "type": "patreon", 989 | "url": "https://www.patreon.com/feross" 990 | }, 991 | { 992 | "type": "consulting", 993 | "url": "https://feross.org/support" 994 | } 995 | ] 996 | }, 997 | "node_modules/serialize-javascript": { 998 | "version": "6.0.0", 999 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 1000 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", 1001 | "dev": true, 1002 | "dependencies": { 1003 | "randombytes": "^2.1.0" 1004 | } 1005 | }, 1006 | "node_modules/string-width": { 1007 | "version": "4.2.3", 1008 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1009 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1010 | "dev": true, 1011 | "dependencies": { 1012 | "emoji-regex": "^8.0.0", 1013 | "is-fullwidth-code-point": "^3.0.0", 1014 | "strip-ansi": "^6.0.1" 1015 | }, 1016 | "engines": { 1017 | "node": ">=8" 1018 | } 1019 | }, 1020 | "node_modules/strip-ansi": { 1021 | "version": "6.0.1", 1022 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1023 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1024 | "dev": true, 1025 | "dependencies": { 1026 | "ansi-regex": "^5.0.1" 1027 | }, 1028 | "engines": { 1029 | "node": ">=8" 1030 | } 1031 | }, 1032 | "node_modules/strip-json-comments": { 1033 | "version": "3.1.1", 1034 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1035 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1036 | "dev": true, 1037 | "engines": { 1038 | "node": ">=8" 1039 | }, 1040 | "funding": { 1041 | "url": "https://github.com/sponsors/sindresorhus" 1042 | } 1043 | }, 1044 | "node_modules/supports-color": { 1045 | "version": "8.1.1", 1046 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 1047 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 1048 | "dev": true, 1049 | "dependencies": { 1050 | "has-flag": "^4.0.0" 1051 | }, 1052 | "engines": { 1053 | "node": ">=10" 1054 | }, 1055 | "funding": { 1056 | "url": "https://github.com/chalk/supports-color?sponsor=1" 1057 | } 1058 | }, 1059 | "node_modules/symlink-dir": { 1060 | "version": "5.0.1", 1061 | "resolved": "https://registry.npmjs.org/symlink-dir/-/symlink-dir-5.0.1.tgz", 1062 | "integrity": "sha512-MeXygPBopo8AmyObuCJIpXKT+mw54d2Kp6SBuxq0uXZGDkHwHDQExpSg5+TK8BA5kCGyktawu5DJG0QWYO6acw==", 1063 | "dev": true, 1064 | "dependencies": { 1065 | "better-path-resolve": "^1.0.0", 1066 | "rename-overwrite": "^4.0.0" 1067 | }, 1068 | "bin": { 1069 | "symlink-dir": "dist/cli.js" 1070 | }, 1071 | "engines": { 1072 | "node": ">=12.10" 1073 | } 1074 | }, 1075 | "node_modules/to-regex-range": { 1076 | "version": "5.0.1", 1077 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1078 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1079 | "dev": true, 1080 | "dependencies": { 1081 | "is-number": "^7.0.0" 1082 | }, 1083 | "engines": { 1084 | "node": ">=8.0" 1085 | } 1086 | }, 1087 | "node_modules/ts-node": { 1088 | "version": "10.5.0", 1089 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.5.0.tgz", 1090 | "integrity": "sha512-6kEJKwVxAJ35W4akuiysfKwKmjkbYxwQMTBaAxo9KKAx/Yd26mPUyhGz3ji+EsJoAgrLqVsYHNuuYwQe22lbtw==", 1091 | "dev": true, 1092 | "dependencies": { 1093 | "@cspotcode/source-map-support": "0.7.0", 1094 | "@tsconfig/node10": "^1.0.7", 1095 | "@tsconfig/node12": "^1.0.7", 1096 | "@tsconfig/node14": "^1.0.0", 1097 | "@tsconfig/node16": "^1.0.2", 1098 | "acorn": "^8.4.1", 1099 | "acorn-walk": "^8.1.1", 1100 | "arg": "^4.1.0", 1101 | "create-require": "^1.1.0", 1102 | "diff": "^4.0.1", 1103 | "make-error": "^1.1.1", 1104 | "v8-compile-cache-lib": "^3.0.0", 1105 | "yn": "3.1.1" 1106 | }, 1107 | "bin": { 1108 | "ts-node": "dist/bin.js", 1109 | "ts-node-cwd": "dist/bin-cwd.js", 1110 | "ts-node-script": "dist/bin-script.js", 1111 | "ts-node-transpile-only": "dist/bin-transpile.js", 1112 | "ts-script": "dist/bin-script-deprecated.js" 1113 | }, 1114 | "peerDependencies": { 1115 | "@swc/core": ">=1.2.50", 1116 | "@swc/wasm": ">=1.2.50", 1117 | "@types/node": "*", 1118 | "typescript": ">=2.7" 1119 | }, 1120 | "peerDependenciesMeta": { 1121 | "@swc/core": { 1122 | "optional": true 1123 | }, 1124 | "@swc/wasm": { 1125 | "optional": true 1126 | } 1127 | } 1128 | }, 1129 | "node_modules/ts-node/node_modules/diff": { 1130 | "version": "4.0.2", 1131 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 1132 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 1133 | "dev": true, 1134 | "engines": { 1135 | "node": ">=0.3.1" 1136 | } 1137 | }, 1138 | "node_modules/type-detect": { 1139 | "version": "4.0.8", 1140 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 1141 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 1142 | "dev": true, 1143 | "engines": { 1144 | "node": ">=4" 1145 | } 1146 | }, 1147 | "node_modules/typescript": { 1148 | "version": "4.5.5", 1149 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", 1150 | "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", 1151 | "dev": true, 1152 | "bin": { 1153 | "tsc": "bin/tsc", 1154 | "tsserver": "bin/tsserver" 1155 | }, 1156 | "engines": { 1157 | "node": ">=4.2.0" 1158 | } 1159 | }, 1160 | "node_modules/v8-compile-cache-lib": { 1161 | "version": "3.0.0", 1162 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", 1163 | "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", 1164 | "dev": true 1165 | }, 1166 | "node_modules/which": { 1167 | "version": "2.0.2", 1168 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1169 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1170 | "dev": true, 1171 | "dependencies": { 1172 | "isexe": "^2.0.0" 1173 | }, 1174 | "bin": { 1175 | "node-which": "bin/node-which" 1176 | }, 1177 | "engines": { 1178 | "node": ">= 8" 1179 | } 1180 | }, 1181 | "node_modules/workerpool": { 1182 | "version": "6.2.0", 1183 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", 1184 | "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", 1185 | "dev": true 1186 | }, 1187 | "node_modules/wrap-ansi": { 1188 | "version": "7.0.0", 1189 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1190 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1191 | "dev": true, 1192 | "dependencies": { 1193 | "ansi-styles": "^4.0.0", 1194 | "string-width": "^4.1.0", 1195 | "strip-ansi": "^6.0.0" 1196 | }, 1197 | "engines": { 1198 | "node": ">=10" 1199 | }, 1200 | "funding": { 1201 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1202 | } 1203 | }, 1204 | "node_modules/wrappy": { 1205 | "version": "1.0.2", 1206 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1207 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1208 | "dev": true 1209 | }, 1210 | "node_modules/y18n": { 1211 | "version": "5.0.8", 1212 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1213 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1214 | "dev": true, 1215 | "engines": { 1216 | "node": ">=10" 1217 | } 1218 | }, 1219 | "node_modules/yargs": { 1220 | "version": "16.2.0", 1221 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 1222 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 1223 | "dev": true, 1224 | "dependencies": { 1225 | "cliui": "^7.0.2", 1226 | "escalade": "^3.1.1", 1227 | "get-caller-file": "^2.0.5", 1228 | "require-directory": "^2.1.1", 1229 | "string-width": "^4.2.0", 1230 | "y18n": "^5.0.5", 1231 | "yargs-parser": "^20.2.2" 1232 | }, 1233 | "engines": { 1234 | "node": ">=10" 1235 | } 1236 | }, 1237 | "node_modules/yargs-parser": { 1238 | "version": "20.2.4", 1239 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", 1240 | "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", 1241 | "dev": true, 1242 | "engines": { 1243 | "node": ">=10" 1244 | } 1245 | }, 1246 | "node_modules/yargs-unparser": { 1247 | "version": "2.0.0", 1248 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 1249 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 1250 | "dev": true, 1251 | "dependencies": { 1252 | "camelcase": "^6.0.0", 1253 | "decamelize": "^4.0.0", 1254 | "flat": "^5.0.2", 1255 | "is-plain-obj": "^2.1.0" 1256 | }, 1257 | "engines": { 1258 | "node": ">=10" 1259 | } 1260 | }, 1261 | "node_modules/yn": { 1262 | "version": "3.1.1", 1263 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 1264 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 1265 | "dev": true, 1266 | "engines": { 1267 | "node": ">=6" 1268 | } 1269 | }, 1270 | "node_modules/yocto-queue": { 1271 | "version": "0.1.0", 1272 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1273 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1274 | "dev": true, 1275 | "engines": { 1276 | "node": ">=10" 1277 | }, 1278 | "funding": { 1279 | "url": "https://github.com/sponsors/sindresorhus" 1280 | } 1281 | } 1282 | }, 1283 | "dependencies": { 1284 | "@cspotcode/source-map-consumer": { 1285 | "version": "0.8.0", 1286 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", 1287 | "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", 1288 | "dev": true 1289 | }, 1290 | "@cspotcode/source-map-support": { 1291 | "version": "0.7.0", 1292 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", 1293 | "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", 1294 | "dev": true, 1295 | "requires": { 1296 | "@cspotcode/source-map-consumer": "0.8.0" 1297 | } 1298 | }, 1299 | "@tsconfig/node10": { 1300 | "version": "1.0.8", 1301 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", 1302 | "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", 1303 | "dev": true 1304 | }, 1305 | "@tsconfig/node12": { 1306 | "version": "1.0.9", 1307 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", 1308 | "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", 1309 | "dev": true 1310 | }, 1311 | "@tsconfig/node14": { 1312 | "version": "1.0.1", 1313 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", 1314 | "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", 1315 | "dev": true 1316 | }, 1317 | "@tsconfig/node16": { 1318 | "version": "1.0.2", 1319 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", 1320 | "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", 1321 | "dev": true 1322 | }, 1323 | "@types/chai": { 1324 | "version": "4.3.0", 1325 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", 1326 | "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==", 1327 | "dev": true 1328 | }, 1329 | "@types/mocha": { 1330 | "version": "9.1.0", 1331 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", 1332 | "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", 1333 | "dev": true 1334 | }, 1335 | "@types/node": { 1336 | "version": "16.11.25", 1337 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.25.tgz", 1338 | "integrity": "sha512-NrTwfD7L1RTc2qrHQD4RTTy4p0CO2LatKBEKEds3CaVuhoM/+DJzmWZl5f+ikR8cm8F5mfJxK+9rQq07gRiSjQ==", 1339 | "dev": true 1340 | }, 1341 | "@ungap/promise-all-settled": { 1342 | "version": "1.1.2", 1343 | "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", 1344 | "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", 1345 | "dev": true 1346 | }, 1347 | "@zkochan/rimraf": { 1348 | "version": "2.1.2", 1349 | "resolved": "https://registry.npmjs.org/@zkochan/rimraf/-/rimraf-2.1.2.tgz", 1350 | "integrity": "sha512-Lc2oK51J6aQWcLWTloobJun5ZF41BbTDdLvE+aMcexoVWFoFqvZmnZoyXR2IZk6NJEVoZW8tjgtvQLfTsmRs2Q==", 1351 | "dev": true, 1352 | "requires": { 1353 | "rimraf": "^3.0.2" 1354 | } 1355 | }, 1356 | "acorn": { 1357 | "version": "8.7.0", 1358 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", 1359 | "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", 1360 | "dev": true 1361 | }, 1362 | "acorn-walk": { 1363 | "version": "8.2.0", 1364 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 1365 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 1366 | "dev": true 1367 | }, 1368 | "ansi-colors": { 1369 | "version": "4.1.1", 1370 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 1371 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 1372 | "dev": true 1373 | }, 1374 | "ansi-regex": { 1375 | "version": "5.0.1", 1376 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1377 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1378 | "dev": true 1379 | }, 1380 | "ansi-styles": { 1381 | "version": "4.3.0", 1382 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1383 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1384 | "dev": true, 1385 | "requires": { 1386 | "color-convert": "^2.0.1" 1387 | } 1388 | }, 1389 | "anymatch": { 1390 | "version": "3.1.2", 1391 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 1392 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 1393 | "dev": true, 1394 | "requires": { 1395 | "normalize-path": "^3.0.0", 1396 | "picomatch": "^2.0.4" 1397 | } 1398 | }, 1399 | "arg": { 1400 | "version": "4.1.3", 1401 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 1402 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 1403 | "dev": true 1404 | }, 1405 | "argparse": { 1406 | "version": "2.0.1", 1407 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 1408 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 1409 | "dev": true 1410 | }, 1411 | "assertion-error": { 1412 | "version": "1.1.0", 1413 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 1414 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 1415 | "dev": true 1416 | }, 1417 | "balanced-match": { 1418 | "version": "1.0.2", 1419 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1420 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1421 | "dev": true 1422 | }, 1423 | "better-path-resolve": { 1424 | "version": "1.0.0", 1425 | "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", 1426 | "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", 1427 | "dev": true, 1428 | "requires": { 1429 | "is-windows": "^1.0.0" 1430 | } 1431 | }, 1432 | "binary-extensions": { 1433 | "version": "2.2.0", 1434 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 1435 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 1436 | "dev": true 1437 | }, 1438 | "brace-expansion": { 1439 | "version": "1.1.11", 1440 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1441 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1442 | "dev": true, 1443 | "requires": { 1444 | "balanced-match": "^1.0.0", 1445 | "concat-map": "0.0.1" 1446 | } 1447 | }, 1448 | "braces": { 1449 | "version": "3.0.2", 1450 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 1451 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 1452 | "dev": true, 1453 | "requires": { 1454 | "fill-range": "^7.0.1" 1455 | } 1456 | }, 1457 | "browser-stdout": { 1458 | "version": "1.3.1", 1459 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 1460 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 1461 | "dev": true 1462 | }, 1463 | "camelcase": { 1464 | "version": "6.3.0", 1465 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 1466 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 1467 | "dev": true 1468 | }, 1469 | "chai": { 1470 | "version": "4.3.6", 1471 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", 1472 | "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", 1473 | "dev": true, 1474 | "requires": { 1475 | "assertion-error": "^1.1.0", 1476 | "check-error": "^1.0.2", 1477 | "deep-eql": "^3.0.1", 1478 | "get-func-name": "^2.0.0", 1479 | "loupe": "^2.3.1", 1480 | "pathval": "^1.1.1", 1481 | "type-detect": "^4.0.5" 1482 | } 1483 | }, 1484 | "chalk": { 1485 | "version": "4.1.2", 1486 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 1487 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 1488 | "dev": true, 1489 | "requires": { 1490 | "ansi-styles": "^4.1.0", 1491 | "supports-color": "^7.1.0" 1492 | }, 1493 | "dependencies": { 1494 | "supports-color": { 1495 | "version": "7.2.0", 1496 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1497 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1498 | "dev": true, 1499 | "requires": { 1500 | "has-flag": "^4.0.0" 1501 | } 1502 | } 1503 | } 1504 | }, 1505 | "check-error": { 1506 | "version": "1.0.2", 1507 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 1508 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 1509 | "dev": true 1510 | }, 1511 | "chokidar": { 1512 | "version": "3.5.3", 1513 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 1514 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 1515 | "dev": true, 1516 | "requires": { 1517 | "anymatch": "~3.1.2", 1518 | "braces": "~3.0.2", 1519 | "fsevents": "~2.3.2", 1520 | "glob-parent": "~5.1.2", 1521 | "is-binary-path": "~2.1.0", 1522 | "is-glob": "~4.0.1", 1523 | "normalize-path": "~3.0.0", 1524 | "readdirp": "~3.6.0" 1525 | } 1526 | }, 1527 | "cliui": { 1528 | "version": "7.0.4", 1529 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 1530 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 1531 | "dev": true, 1532 | "requires": { 1533 | "string-width": "^4.2.0", 1534 | "strip-ansi": "^6.0.0", 1535 | "wrap-ansi": "^7.0.0" 1536 | } 1537 | }, 1538 | "color-convert": { 1539 | "version": "2.0.1", 1540 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1541 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1542 | "dev": true, 1543 | "requires": { 1544 | "color-name": "~1.1.4" 1545 | } 1546 | }, 1547 | "color-name": { 1548 | "version": "1.1.4", 1549 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1550 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1551 | "dev": true 1552 | }, 1553 | "concat-map": { 1554 | "version": "0.0.1", 1555 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1556 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 1557 | "dev": true 1558 | }, 1559 | "create-require": { 1560 | "version": "1.1.1", 1561 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 1562 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 1563 | "dev": true 1564 | }, 1565 | "debug": { 1566 | "version": "4.3.3", 1567 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", 1568 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", 1569 | "dev": true, 1570 | "requires": { 1571 | "ms": "2.1.2" 1572 | }, 1573 | "dependencies": { 1574 | "ms": { 1575 | "version": "2.1.2", 1576 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1577 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1578 | "dev": true 1579 | } 1580 | } 1581 | }, 1582 | "decamelize": { 1583 | "version": "4.0.0", 1584 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 1585 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 1586 | "dev": true 1587 | }, 1588 | "deep-eql": { 1589 | "version": "3.0.1", 1590 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 1591 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 1592 | "dev": true, 1593 | "requires": { 1594 | "type-detect": "^4.0.0" 1595 | } 1596 | }, 1597 | "diff": { 1598 | "version": "5.0.0", 1599 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 1600 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 1601 | "dev": true 1602 | }, 1603 | "emoji-regex": { 1604 | "version": "8.0.0", 1605 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1606 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1607 | "dev": true 1608 | }, 1609 | "escalade": { 1610 | "version": "3.1.1", 1611 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 1612 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 1613 | "dev": true 1614 | }, 1615 | "escape-string-regexp": { 1616 | "version": "4.0.0", 1617 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1618 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1619 | "dev": true 1620 | }, 1621 | "fill-range": { 1622 | "version": "7.0.1", 1623 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 1624 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 1625 | "dev": true, 1626 | "requires": { 1627 | "to-regex-range": "^5.0.1" 1628 | } 1629 | }, 1630 | "find-up": { 1631 | "version": "5.0.0", 1632 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 1633 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 1634 | "dev": true, 1635 | "requires": { 1636 | "locate-path": "^6.0.0", 1637 | "path-exists": "^4.0.0" 1638 | } 1639 | }, 1640 | "flat": { 1641 | "version": "5.0.2", 1642 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 1643 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 1644 | "dev": true 1645 | }, 1646 | "fs.realpath": { 1647 | "version": "1.0.0", 1648 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1649 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 1650 | "dev": true 1651 | }, 1652 | "fsevents": { 1653 | "version": "2.3.2", 1654 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1655 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1656 | "dev": true, 1657 | "optional": true 1658 | }, 1659 | "get-caller-file": { 1660 | "version": "2.0.5", 1661 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 1662 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 1663 | "dev": true 1664 | }, 1665 | "get-func-name": { 1666 | "version": "2.0.0", 1667 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 1668 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 1669 | "dev": true 1670 | }, 1671 | "glob": { 1672 | "version": "7.2.0", 1673 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 1674 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 1675 | "dev": true, 1676 | "requires": { 1677 | "fs.realpath": "^1.0.0", 1678 | "inflight": "^1.0.4", 1679 | "inherits": "2", 1680 | "minimatch": "^3.0.4", 1681 | "once": "^1.3.0", 1682 | "path-is-absolute": "^1.0.0" 1683 | } 1684 | }, 1685 | "glob-parent": { 1686 | "version": "5.1.2", 1687 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1688 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1689 | "dev": true, 1690 | "requires": { 1691 | "is-glob": "^4.0.1" 1692 | } 1693 | }, 1694 | "growl": { 1695 | "version": "1.10.5", 1696 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 1697 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 1698 | "dev": true 1699 | }, 1700 | "has-flag": { 1701 | "version": "4.0.0", 1702 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1703 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1704 | "dev": true 1705 | }, 1706 | "he": { 1707 | "version": "1.2.0", 1708 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 1709 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 1710 | "dev": true 1711 | }, 1712 | "inflight": { 1713 | "version": "1.0.6", 1714 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1715 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1716 | "dev": true, 1717 | "requires": { 1718 | "once": "^1.3.0", 1719 | "wrappy": "1" 1720 | } 1721 | }, 1722 | "inherits": { 1723 | "version": "2.0.4", 1724 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1725 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1726 | "dev": true 1727 | }, 1728 | "is-binary-path": { 1729 | "version": "2.1.0", 1730 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1731 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1732 | "dev": true, 1733 | "requires": { 1734 | "binary-extensions": "^2.0.0" 1735 | } 1736 | }, 1737 | "is-extglob": { 1738 | "version": "2.1.1", 1739 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1740 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 1741 | "dev": true 1742 | }, 1743 | "is-fullwidth-code-point": { 1744 | "version": "3.0.0", 1745 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1746 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1747 | "dev": true 1748 | }, 1749 | "is-glob": { 1750 | "version": "4.0.3", 1751 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1752 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1753 | "dev": true, 1754 | "requires": { 1755 | "is-extglob": "^2.1.1" 1756 | } 1757 | }, 1758 | "is-number": { 1759 | "version": "7.0.0", 1760 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1761 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1762 | "dev": true 1763 | }, 1764 | "is-plain-obj": { 1765 | "version": "2.1.0", 1766 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 1767 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 1768 | "dev": true 1769 | }, 1770 | "is-unicode-supported": { 1771 | "version": "0.1.0", 1772 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 1773 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 1774 | "dev": true 1775 | }, 1776 | "is-windows": { 1777 | "version": "1.0.2", 1778 | "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", 1779 | "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", 1780 | "dev": true 1781 | }, 1782 | "isexe": { 1783 | "version": "2.0.0", 1784 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1785 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 1786 | "dev": true 1787 | }, 1788 | "js-yaml": { 1789 | "version": "4.1.0", 1790 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1791 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1792 | "dev": true, 1793 | "requires": { 1794 | "argparse": "^2.0.1" 1795 | } 1796 | }, 1797 | "locate-path": { 1798 | "version": "6.0.0", 1799 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1800 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1801 | "dev": true, 1802 | "requires": { 1803 | "p-locate": "^5.0.0" 1804 | } 1805 | }, 1806 | "log-symbols": { 1807 | "version": "4.1.0", 1808 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 1809 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 1810 | "dev": true, 1811 | "requires": { 1812 | "chalk": "^4.1.0", 1813 | "is-unicode-supported": "^0.1.0" 1814 | } 1815 | }, 1816 | "loupe": { 1817 | "version": "2.3.4", 1818 | "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", 1819 | "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", 1820 | "dev": true, 1821 | "requires": { 1822 | "get-func-name": "^2.0.0" 1823 | } 1824 | }, 1825 | "make-error": { 1826 | "version": "1.3.6", 1827 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 1828 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 1829 | "dev": true 1830 | }, 1831 | "minimatch": { 1832 | "version": "3.0.4", 1833 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1834 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1835 | "dev": true, 1836 | "requires": { 1837 | "brace-expansion": "^1.1.7" 1838 | } 1839 | }, 1840 | "mocha": { 1841 | "version": "9.2.1", 1842 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.1.tgz", 1843 | "integrity": "sha512-T7uscqjJVS46Pq1XDXyo9Uvey9gd3huT/DD9cYBb4K2Xc/vbKRPUWK067bxDQRK0yIz6Jxk73IrnimvASzBNAQ==", 1844 | "dev": true, 1845 | "requires": { 1846 | "@ungap/promise-all-settled": "1.1.2", 1847 | "ansi-colors": "4.1.1", 1848 | "browser-stdout": "1.3.1", 1849 | "chokidar": "3.5.3", 1850 | "debug": "4.3.3", 1851 | "diff": "5.0.0", 1852 | "escape-string-regexp": "4.0.0", 1853 | "find-up": "5.0.0", 1854 | "glob": "7.2.0", 1855 | "growl": "1.10.5", 1856 | "he": "1.2.0", 1857 | "js-yaml": "4.1.0", 1858 | "log-symbols": "4.1.0", 1859 | "minimatch": "3.0.4", 1860 | "ms": "2.1.3", 1861 | "nanoid": "3.2.0", 1862 | "serialize-javascript": "6.0.0", 1863 | "strip-json-comments": "3.1.1", 1864 | "supports-color": "8.1.1", 1865 | "which": "2.0.2", 1866 | "workerpool": "6.2.0", 1867 | "yargs": "16.2.0", 1868 | "yargs-parser": "20.2.4", 1869 | "yargs-unparser": "2.0.0" 1870 | } 1871 | }, 1872 | "ms": { 1873 | "version": "2.1.3", 1874 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1875 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1876 | "dev": true 1877 | }, 1878 | "nanoid": { 1879 | "version": "3.2.0", 1880 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", 1881 | "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", 1882 | "dev": true 1883 | }, 1884 | "normalize-path": { 1885 | "version": "3.0.0", 1886 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1887 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1888 | "dev": true 1889 | }, 1890 | "once": { 1891 | "version": "1.4.0", 1892 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1893 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1894 | "dev": true, 1895 | "requires": { 1896 | "wrappy": "1" 1897 | } 1898 | }, 1899 | "p-limit": { 1900 | "version": "3.1.0", 1901 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1902 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1903 | "dev": true, 1904 | "requires": { 1905 | "yocto-queue": "^0.1.0" 1906 | } 1907 | }, 1908 | "p-locate": { 1909 | "version": "5.0.0", 1910 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1911 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1912 | "dev": true, 1913 | "requires": { 1914 | "p-limit": "^3.0.2" 1915 | } 1916 | }, 1917 | "path-exists": { 1918 | "version": "4.0.0", 1919 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1920 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1921 | "dev": true 1922 | }, 1923 | "path-is-absolute": { 1924 | "version": "1.0.1", 1925 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1926 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1927 | "dev": true 1928 | }, 1929 | "pathval": { 1930 | "version": "1.1.1", 1931 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", 1932 | "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", 1933 | "dev": true 1934 | }, 1935 | "picomatch": { 1936 | "version": "2.3.1", 1937 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1938 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1939 | "dev": true 1940 | }, 1941 | "randombytes": { 1942 | "version": "2.1.0", 1943 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 1944 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 1945 | "dev": true, 1946 | "requires": { 1947 | "safe-buffer": "^5.1.0" 1948 | } 1949 | }, 1950 | "readdirp": { 1951 | "version": "3.6.0", 1952 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1953 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1954 | "dev": true, 1955 | "requires": { 1956 | "picomatch": "^2.2.1" 1957 | } 1958 | }, 1959 | "rename-overwrite": { 1960 | "version": "4.0.2", 1961 | "resolved": "https://registry.npmjs.org/rename-overwrite/-/rename-overwrite-4.0.2.tgz", 1962 | "integrity": "sha512-L1sgBgagVgOgb1Z6QZr1yJgSMHI4SXQqAH0l/UbeyHnLKxECvKIlyVEmBo4BqsCAZGg0SBSyjCh68lis5PgC7g==", 1963 | "dev": true, 1964 | "requires": { 1965 | "@zkochan/rimraf": "^2.1.2" 1966 | } 1967 | }, 1968 | "require-directory": { 1969 | "version": "2.1.1", 1970 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1971 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 1972 | "dev": true 1973 | }, 1974 | "rimraf": { 1975 | "version": "3.0.2", 1976 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1977 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1978 | "dev": true, 1979 | "requires": { 1980 | "glob": "^7.1.3" 1981 | } 1982 | }, 1983 | "safe-buffer": { 1984 | "version": "5.2.1", 1985 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1986 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1987 | "dev": true 1988 | }, 1989 | "serialize-javascript": { 1990 | "version": "6.0.0", 1991 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 1992 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", 1993 | "dev": true, 1994 | "requires": { 1995 | "randombytes": "^2.1.0" 1996 | } 1997 | }, 1998 | "string-width": { 1999 | "version": "4.2.3", 2000 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 2001 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 2002 | "dev": true, 2003 | "requires": { 2004 | "emoji-regex": "^8.0.0", 2005 | "is-fullwidth-code-point": "^3.0.0", 2006 | "strip-ansi": "^6.0.1" 2007 | } 2008 | }, 2009 | "strip-ansi": { 2010 | "version": "6.0.1", 2011 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 2012 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 2013 | "dev": true, 2014 | "requires": { 2015 | "ansi-regex": "^5.0.1" 2016 | } 2017 | }, 2018 | "strip-json-comments": { 2019 | "version": "3.1.1", 2020 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 2021 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 2022 | "dev": true 2023 | }, 2024 | "supports-color": { 2025 | "version": "8.1.1", 2026 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 2027 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 2028 | "dev": true, 2029 | "requires": { 2030 | "has-flag": "^4.0.0" 2031 | } 2032 | }, 2033 | "symlink-dir": { 2034 | "version": "5.0.1", 2035 | "resolved": "https://registry.npmjs.org/symlink-dir/-/symlink-dir-5.0.1.tgz", 2036 | "integrity": "sha512-MeXygPBopo8AmyObuCJIpXKT+mw54d2Kp6SBuxq0uXZGDkHwHDQExpSg5+TK8BA5kCGyktawu5DJG0QWYO6acw==", 2037 | "dev": true, 2038 | "requires": { 2039 | "better-path-resolve": "^1.0.0", 2040 | "rename-overwrite": "^4.0.0" 2041 | } 2042 | }, 2043 | "to-regex-range": { 2044 | "version": "5.0.1", 2045 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 2046 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 2047 | "dev": true, 2048 | "requires": { 2049 | "is-number": "^7.0.0" 2050 | } 2051 | }, 2052 | "ts-node": { 2053 | "version": "10.5.0", 2054 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.5.0.tgz", 2055 | "integrity": "sha512-6kEJKwVxAJ35W4akuiysfKwKmjkbYxwQMTBaAxo9KKAx/Yd26mPUyhGz3ji+EsJoAgrLqVsYHNuuYwQe22lbtw==", 2056 | "dev": true, 2057 | "requires": { 2058 | "@cspotcode/source-map-support": "0.7.0", 2059 | "@tsconfig/node10": "^1.0.7", 2060 | "@tsconfig/node12": "^1.0.7", 2061 | "@tsconfig/node14": "^1.0.0", 2062 | "@tsconfig/node16": "^1.0.2", 2063 | "acorn": "^8.4.1", 2064 | "acorn-walk": "^8.1.1", 2065 | "arg": "^4.1.0", 2066 | "create-require": "^1.1.0", 2067 | "diff": "^4.0.1", 2068 | "make-error": "^1.1.1", 2069 | "v8-compile-cache-lib": "^3.0.0", 2070 | "yn": "3.1.1" 2071 | }, 2072 | "dependencies": { 2073 | "diff": { 2074 | "version": "4.0.2", 2075 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 2076 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 2077 | "dev": true 2078 | } 2079 | } 2080 | }, 2081 | "type-detect": { 2082 | "version": "4.0.8", 2083 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 2084 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 2085 | "dev": true 2086 | }, 2087 | "typescript": { 2088 | "version": "4.5.5", 2089 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", 2090 | "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", 2091 | "dev": true 2092 | }, 2093 | "v8-compile-cache-lib": { 2094 | "version": "3.0.0", 2095 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", 2096 | "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", 2097 | "dev": true 2098 | }, 2099 | "which": { 2100 | "version": "2.0.2", 2101 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 2102 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 2103 | "dev": true, 2104 | "requires": { 2105 | "isexe": "^2.0.0" 2106 | } 2107 | }, 2108 | "workerpool": { 2109 | "version": "6.2.0", 2110 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", 2111 | "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", 2112 | "dev": true 2113 | }, 2114 | "wrap-ansi": { 2115 | "version": "7.0.0", 2116 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 2117 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 2118 | "dev": true, 2119 | "requires": { 2120 | "ansi-styles": "^4.0.0", 2121 | "string-width": "^4.1.0", 2122 | "strip-ansi": "^6.0.0" 2123 | } 2124 | }, 2125 | "wrappy": { 2126 | "version": "1.0.2", 2127 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2128 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 2129 | "dev": true 2130 | }, 2131 | "y18n": { 2132 | "version": "5.0.8", 2133 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 2134 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 2135 | "dev": true 2136 | }, 2137 | "yargs": { 2138 | "version": "16.2.0", 2139 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 2140 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 2141 | "dev": true, 2142 | "requires": { 2143 | "cliui": "^7.0.2", 2144 | "escalade": "^3.1.1", 2145 | "get-caller-file": "^2.0.5", 2146 | "require-directory": "^2.1.1", 2147 | "string-width": "^4.2.0", 2148 | "y18n": "^5.0.5", 2149 | "yargs-parser": "^20.2.2" 2150 | } 2151 | }, 2152 | "yargs-parser": { 2153 | "version": "20.2.4", 2154 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", 2155 | "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", 2156 | "dev": true 2157 | }, 2158 | "yargs-unparser": { 2159 | "version": "2.0.0", 2160 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 2161 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 2162 | "dev": true, 2163 | "requires": { 2164 | "camelcase": "^6.0.0", 2165 | "decamelize": "^4.0.0", 2166 | "flat": "^5.0.2", 2167 | "is-plain-obj": "^2.1.0" 2168 | } 2169 | }, 2170 | "yn": { 2171 | "version": "3.1.1", 2172 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 2173 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 2174 | "dev": true 2175 | }, 2176 | "yocto-queue": { 2177 | "version": "0.1.0", 2178 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 2179 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 2180 | "dev": true 2181 | } 2182 | } 2183 | } 2184 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rtti", 3 | "version": "1.1.4", 4 | "description": "Runtime type validation for JavaScript and TypeScript programs", 5 | "main": "dist/commonjs/index.js", 6 | "typings": "dist/commonjs/index.d.ts", 7 | "scripts": { 8 | "clean": "rimraf dist/", 9 | "build": "tsc", 10 | "test": "mocha -r ts-node/register -u bdd --timeout 999999 --colors tests/**/*.test.ts", 11 | "prepare": "symlink-dir . node_modules/rtti" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/yortus/rtti.git" 16 | }, 17 | "keywords": [ 18 | "validation", 19 | "validate", 20 | "type", 21 | "types", 22 | "type info" 23 | ], 24 | "author": { 25 | "name": "Troy Gerwien", 26 | "email": "yortus@gmail.com", 27 | "url": "http://github.com/yortus/" 28 | }, 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/yortus/rtti/issues" 32 | }, 33 | "devDependencies": { 34 | "@types/chai": "^4.3.0", 35 | "@types/mocha": "^9.1.0", 36 | "@types/node": "^16.*", 37 | "chai": "^4.3.6", 38 | "mocha": "^9.2.0", 39 | "rimraf": "^3.0.2", 40 | "symlink-dir": "^5.0.1", 41 | "ts-node": "^10.5.0", 42 | "typescript": "^4.5.5" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/api-v0/api.ts: -------------------------------------------------------------------------------- 1 | import {CheckOptions} from '../operations'; 2 | import {TypeInfo} from '../type-info'; 3 | 4 | /** 5 | * Throws an error if the value `v` does not conform to the type, otherwise returns. Errors thrown by `assert` 6 | * have an `errors` property with detailed information about the errors encountered during type checking. 7 | * @deprecated since 1.0; use t.assertValid(v, opts) instead 8 | */ 9 | export function assert(t: TypeInfo, v: unknown): asserts v is T { 10 | return t.assertValid(v, checkOptionsForV0); 11 | } 12 | 13 | /** 14 | * Returns a JSON Schema representation of the type. 15 | * @deprecated since 1.0; use t.toJsonSchema() instead 16 | */ 17 | export function getJsonSchema(t: TypeInfo): unknown { 18 | return t.toJsonSchema(); 19 | } 20 | 21 | /** 22 | * Checks whether the value `v` conforms to the type, returning details about any type-checking errors encountered. 23 | * @deprecated since 1.0; use t.check(v, opts) instead 24 | */ 25 | export function getValidationErrors(t: T, v: unknown): ValidationErrors { 26 | const checkResult = t.check(v, {allowExcessProperties: false}); 27 | const errors = checkResult.errors.filter(({message}) => !isWarningInV0(message)); 28 | const warnings = checkResult.errors.filter(({message}) => isWarningInV0(message)); 29 | return {errors, warnings}; 30 | } 31 | 32 | /** 33 | * Returns true if the value `v` conforms to the type, or false otherwise. 34 | * @deprecated since 1.0; use t.isValid(v, opts) instead 35 | */ 36 | export function is(t: TypeInfo, v: unknown): v is T { 37 | return t.isValid(v, checkOptionsForV0); 38 | } 39 | 40 | /** 41 | * Returns a deep clone of the value `v` containing only properties that are explicitly present in the type. 42 | * That is, all excess properties are removed in the returned value. 43 | * @deprecated since 1.0; use t.sanitize(v) instead 44 | */ 45 | export function removeExcessProperties(t: TypeInfo, v: T): T { 46 | return t.sanitize(v); 47 | } 48 | 49 | /** 50 | * Returns a human-readable representation of the type. 51 | * @deprecated since 1.0; use t.toString() instead 52 | */ 53 | export function toString(t: TypeInfo): string { 54 | return t.toString(); 55 | } 56 | 57 | export type TypeFromTypeInfo = T['example']; 58 | 59 | export interface ValidationErrors { 60 | errors: Array<{path: string, message: string}>; 61 | warnings: Array<{path: string, message: string}>; 62 | } 63 | 64 | const checkOptionsForV0: CheckOptions = {allowExcessProperties: true}; 65 | 66 | const isWarningInV0 = (message: string) => message.includes('The object has excess properties'); 67 | -------------------------------------------------------------------------------- /src/api-v0/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api'; 2 | -------------------------------------------------------------------------------- /src/create-type-info.ts: -------------------------------------------------------------------------------- 1 | import type {Descriptor} from './descriptor'; 2 | import * as op from './operations'; 3 | import type {TypeInfo} from './type-info'; // NB: type-only import (no cyclic imports at runtime) 4 | import type {Anonymize} from './utils'; 5 | 6 | /** 7 | * Internal factory function for creating a TypeInfo object from a Descriptor. An important implementation detail (ie 8 | * a detail that is not exposed outside the library but is relied on within the library) is that every TypeInfo object 9 | * is-a Descriptor object at runtime. However the descriptor properties are omitted from the TypeInfo type since the 10 | * public API does not require this detail. 11 | */ 12 | export function createTypeInfo, T = TypeFromDescriptor>(descriptor: D): TypeInfo { 13 | 14 | // Since we know that a TypeInfo is-a Descriptor at runtime, it is safe to type-cast Descriptor's `Nested` 15 | // type param from `TypeInfo` to `Descriptor` here. That makes the type usable for the operation functions. 16 | let d = descriptor as Descriptor; 17 | d = simplify(d); 18 | const result: TypeInfo = { 19 | assertValid: (v, opts) => op.assertValid(d, v, opts), 20 | check: (v, opts) => op.check(d, v, opts), 21 | get example() { return op.generateValue(d) as T}, 22 | isValid: (v, opts): v is T => op.isValid(d, v, opts), 23 | sanitize: v => op.sanitize(d, v) as T, 24 | toJsonSchema: () => op.toJsonSchema(d), 25 | toString: () => op.toString(d), 26 | }; 27 | 28 | // Make the TypeInfo value a descriptor at runtime, even though the type doesn't reveal the Descriptor props. 29 | return Object.assign(result, d); 30 | } 31 | 32 | // Helper function to flatten directly-nested intersections and unions. 33 | // TODO: add other simplifications... 34 | function simplify(d: D): D { 35 | if (d.kind !== 'intersection' && d.kind !== 'union') return d; 36 | const members = d.members.reduce( 37 | (flattened, member) => flattened.concat(member.kind === d.kind ? member.members : member), 38 | [] as Descriptor[], 39 | ); 40 | return {...d, members}; 41 | } 42 | 43 | // Helper type to map from a Descriptor type to the TypeScript type most closely resembling it. 44 | type TypeFromDescriptor> = { 45 | any: any, 46 | array: D extends {element: TypeInfo} ? Elem[] : never, 47 | boolean: boolean, 48 | brandedString: D extends {brand: infer Brand} 49 | ? Brand extends `${string}?` 50 | ? string & {__brand?: {[K in string & Brand]: never}} 51 | : string & {__brand: {[K in string & Brand]: never}} 52 | : never, 53 | date: Date, 54 | intersection: D extends {members: infer Members} ? TypeOfIntersection : never, 55 | never: never, 56 | null: null, 57 | number: number, 58 | object: D extends {properties: infer Props} ? TypeOfObject : never, 59 | string: string, 60 | tuple: D extends {elements: infer Elements} 61 | ? ({[K in keyof Elements]: Elements[K] extends TypeInfo ? T : 0}) 62 | : never, 63 | undefined: undefined, 64 | union: D extends {members: infer Members} ? TypeOfUnion : never, 65 | unit: D extends {value: infer Value} ? Value : never, 66 | unknown: unknown, 67 | }[D['kind']]; 68 | 69 | type TypeOfIntersection = Anonymize< 70 | Members[any] extends infer E ? (E extends TypeInfo ? (x: T) => 0 : 0) extends ((x: infer I) => 0) ? I : 0 : 0 71 | >; 72 | 73 | type TypeOfUnion = Members[any] extends infer E ? (E extends TypeInfo ? T : 0) : 0; 74 | 75 | type TypeOfObject = Anonymize< 76 | & {[K in RequiredPropNames]: Props[K] extends TypeInfo ? T : 0} 77 | & {[K in OptionalPropNames]?: Props[K] extends {kind: 'optional', type: TypeInfo} ? T : 0} 78 | >; 79 | type RequiredPropNames = {[K in keyof Props]: Props[K] extends {kind: 'optional'} ? never : K}[keyof Props]; 80 | type OptionalPropNames = {[K in keyof Props]: Props[K] extends {kind: 'optional'} ? K : never}[keyof Props]; 81 | -------------------------------------------------------------------------------- /src/descriptor.ts: -------------------------------------------------------------------------------- 1 | /** Runtime information describing a value type. This is an internal type used within the library */ 2 | export type Descriptor> = 3 | | Any 4 | | Array 5 | | Boolean 6 | | BrandedString 7 | | Date 8 | | Intersection 9 | | Never 10 | | Null 11 | | Number 12 | | Object>> 13 | | String 14 | | Tuple 15 | | Undefined 16 | | Union 17 | | Unit 18 | | Unknown 19 | ; 20 | 21 | /** `Optional` is an 'operator' only valid inside object and tuple descriptors. It is *not* itself a descriptor. */ 22 | export type Optional = {kind: 'optional', type: T}; 23 | 24 | type Any = {kind: 'any'}; 25 | type Array = {kind: 'array', element: Element}; 26 | type Boolean = {kind: 'boolean'}; 27 | type BrandedString = {kind: 'brandedString', brand: Brand}; 28 | type Date = {kind: 'date'}; 29 | type Intersection = {kind: 'intersection', members: Members}; 30 | type Never = {kind: 'never'}; 31 | type Null = {kind: 'null'}; 32 | type Number = {kind: 'number'}; 33 | type Object> = {kind: 'object', properties: Properties}; 34 | type String = {kind: 'string'}; 35 | type Tuple = {kind: 'tuple', elements: Elements}; 36 | type Undefined = {kind: 'undefined'}; 37 | type Union = {kind: 'union', members: Members}; 38 | type Unit = {kind: 'unit', value: T}; 39 | type Unknown = {kind: 'unknown'}; 40 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export {t, TypeInfo} from './type-info'; 2 | export * from './api-v0'; 3 | -------------------------------------------------------------------------------- /src/operations/assert-valid.ts: -------------------------------------------------------------------------------- 1 | import {Descriptor} from '../descriptor'; 2 | import {inspect} from '../utils'; 3 | import {check, CheckOptions} from './check'; 4 | import {toString} from './to-string'; 5 | 6 | export function assertValid(d: Descriptor, v: unknown, options?: CheckOptions): void { 7 | const {isValid, errors} = check(d, v, options); 8 | if (isValid) return; 9 | 10 | let desc = inspect(v); 11 | throw Object.assign(new Error(`${desc} does not conform to type ${toString(d)}`), {errors}); 12 | } 13 | -------------------------------------------------------------------------------- /src/operations/check.ts: -------------------------------------------------------------------------------- 1 | import {isValid} from './is-valid'; 2 | import {Descriptor} from '../descriptor'; 3 | import {inspect} from '../utils'; 4 | 5 | /** Options for checking whether a value confirms to a type. */ 6 | export interface CheckOptions { 7 | /** 8 | * If true, excess properties (ie properties not declared as part of the type) that are present in the value being 9 | * checked are ignored during type checking. If false, excess properties are flagged as errors. Default value: true. 10 | */ 11 | allowExcessProperties?: boolean; 12 | } 13 | 14 | export type CheckResult = 15 | | {isValid: true, errors: []} 16 | | {isValid: false, errors: Array<{path: string, message: string}>} 17 | ; 18 | 19 | export function check(d: Descriptor, v: unknown, options?: CheckOptions): CheckResult { 20 | const allowExcessProperties = options?.allowExcessProperties ?? true; 21 | let errors = [] as Array<{path: string, message: string}>; 22 | recurse(d, v, '^'); 23 | return errors.length === 0 ? {isValid: true, errors: []} : {isValid: false, errors}; 24 | 25 | function recurse(d: Descriptor, v: unknown, path: string): void { 26 | let desc = inspect(v); 27 | switch (d.kind) { 28 | case 'any': 29 | return; 30 | case 'array': 31 | if (!Array.isArray(v)) { 32 | errors.push({path, message: `Expected an array but got ${desc}`}); 33 | } 34 | else { 35 | v.forEach((el, i) => recurse(d.element, el, `${path}[${i}]`)); 36 | } 37 | return; 38 | case 'boolean': 39 | if (typeof v !== 'boolean') errors.push({path, message: `Expected a boolean but got ${desc}`}); 40 | return; 41 | case 'date': 42 | if (!(v instanceof Date)) errors.push({path, message: `Expected a Date object but got ${desc}`}); 43 | return; 44 | case 'intersection': 45 | let isEvery = d.members.every(t => isValid(t, v, options)); 46 | // TODO: improve this message with specifics 47 | if (!isEvery) errors.push({path, message: `The value ${desc} does not conform to the intersection type`}); 48 | return; 49 | case 'never': 50 | errors.push({path, message: `Expected no value but got ${desc}`}); 51 | return; 52 | case 'null': 53 | if (v !== null) errors.push({path, message: `Expected the value 'null' but got ${desc}`}); 54 | return; 55 | case 'number': 56 | if (typeof v !== 'number') errors.push({path, message: `Expected a number but got ${desc}`}); 57 | return; 58 | case 'object': { 59 | if (typeof v !== 'object' || v === null) { 60 | errors.push({path, message: `Expected an object but got ${desc}`}); 61 | return; 62 | } 63 | let requiredPropNames = Object.keys(d.properties).filter(n => d.properties[n].kind !== 'optional'); 64 | let optionalPropNames = Object.keys(d.properties).filter(n => d.properties[n].kind === 'optional'); 65 | let actualPropNames = Object.keys(v); 66 | let missingPropNames = requiredPropNames.filter(n => !actualPropNames.includes(n)); 67 | let excessPropNames = actualPropNames.filter(n => !requiredPropNames.includes(n) && !optionalPropNames.includes(n)); 68 | if (missingPropNames.length > 0) errors.push({path, message: `The following properties are missing: ${missingPropNames.join(', ')}`}); 69 | if (excessPropNames.length > 0 && !allowExcessProperties) errors.push({path, message: `The object has excess properties: ${excessPropNames.join(', ')}`}); 70 | for (let propName of Object.keys(d.properties)) { 71 | let propType = d.properties[propName]; 72 | let isOptional = propType.kind === 'optional'; 73 | propType = propType.kind === 'optional' ? propType.type as Descriptor : propType; 74 | let propValue = (v as any)[propName]; 75 | if (propValue === undefined && isOptional) continue; 76 | recurse(propType, propValue, `${path}.${propName}`); 77 | } 78 | return; 79 | } 80 | case 'string': 81 | case 'brandedString': 82 | if (typeof v !== 'string') errors.push({path, message: `Expected a string but got ${desc}`}); 83 | return; 84 | case 'tuple': { 85 | let len = d.elements.length; 86 | if (!Array.isArray(v)) { 87 | errors.push({path, message: `Expected an array but got ${desc}`}); 88 | } 89 | else if (v.length !== len) { 90 | errors.push({path, message: `Expected ${len} element(s) but got ${v.length} element(s)`}); 91 | return; 92 | } 93 | else { 94 | v.forEach((el, i) => recurse(d.elements[i], el, `${path}[${i}]`)); 95 | } 96 | return; 97 | } 98 | case 'undefined': 99 | if (v !== undefined) errors.push({path, message: `Expected the value 'undefined' but got ${desc}`}); 100 | return; 101 | case 'union': 102 | let isSome = d.members.some(t => isValid(t, v, options)); 103 | // TODO: improve this message with specifics 104 | if (!isSome) errors.push({path, message: `The value ${desc} does not conform to the union type`}); 105 | return; 106 | case 'unit': 107 | if (v !== d.value) errors.push({path, message: `Expected the value ${JSON.stringify(d.value)} but got ${desc}`}); 108 | return; 109 | case 'unknown': 110 | return; 111 | default: 112 | ((desc: never) => { throw new Error(`Unhandled case '${desc}'`) })(d); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/operations/generate-value.ts: -------------------------------------------------------------------------------- 1 | import {Descriptor} from '../descriptor'; 2 | 3 | // TODO: better fuzzing/randomization. Don't always return same values eg for any/string/number/etc 4 | // TODO: add tests 5 | 6 | export function generateValue(d: Descriptor): unknown { 7 | switch (d.kind) { 8 | case 'any': return ['any', 'value']; 9 | case 'array': return range(nat(5)).map(_ => generateValue(d.element)); 10 | case 'boolean': return nat(2) === 0; 11 | case 'brandedString': return 'a branded string'; 12 | case 'date': return new Date(); 13 | case 'intersection': { 14 | if (d.members.length === 0) return ['any', 'value']; 15 | return Object.assign({}, ...d.members.map(generateValue)); 16 | } 17 | case 'never': throw new Error(`Cannot generate a value for the 'never' type`); 18 | case 'null': return null; 19 | case 'number': return nat(1000); 20 | case 'object': { 21 | let propNames = Object.keys(d.properties); 22 | const obj: any = {}; 23 | for (let propName of propNames) { 24 | let propDesc = d.properties[propName]; 25 | let isOptional = propDesc.kind === 'optional'; 26 | propDesc = propDesc.kind === 'optional' ? propDesc.type as Descriptor : propDesc; 27 | if (isOptional && nat(2) === 0) continue; 28 | obj[propName] = generateValue(propDesc); 29 | } 30 | return obj; 31 | } 32 | case 'string': return 'any string' 33 | case 'tuple': return d.elements.map(generateValue); 34 | case 'undefined': return undefined; 35 | case 'union': { 36 | if (d.members.length === 0) throw new Error(`Cannot generate a value for an empty union`); 37 | return generateValue(d.members[nat(d.members.length)]); 38 | } 39 | case 'unit': return d.value; 40 | case 'unknown': return ['any', 'value']; 41 | default: ((desc: never) => { throw new Error(`Unhandled case '${desc}'`) })(d); 42 | } 43 | } 44 | 45 | // returns a random integer from 0..upperBound (exclusive of upperBound) 46 | function nat(upperBound: number): number { 47 | return Math.floor(Math.random() * upperBound); 48 | } 49 | 50 | // returns an array of all the natural numbers 0..upperBound (exclusive of upperBound). 51 | // eg range(6) returns [0, 1, 2, 3, 4, 5] 52 | function range(upperBound: number): number[] { 53 | const result: number[] = []; 54 | for (let i = 0; i < upperBound; ++i) result.push(i); 55 | return result; 56 | } 57 | -------------------------------------------------------------------------------- /src/operations/index.ts: -------------------------------------------------------------------------------- 1 | export {assertValid} from './assert-valid'; 2 | export {check, CheckOptions, CheckResult} from './check'; 3 | export {generateValue} from './generate-value'; 4 | export {isValid} from './is-valid'; 5 | export {sanitize} from './sanitize'; 6 | export {toJsonSchema} from './to-json-schema'; 7 | export {toString} from './to-string'; 8 | -------------------------------------------------------------------------------- /src/operations/is-valid.ts: -------------------------------------------------------------------------------- 1 | import {Descriptor} from '../descriptor'; 2 | import {CheckOptions} from './check'; 3 | 4 | export function isValid(d: Descriptor, v: unknown, options?: CheckOptions): boolean { 5 | switch (d.kind) { 6 | case 'any': return true; 7 | case 'array': return Array.isArray(v) && v.every(el => isValid(d.element, el, options)); 8 | case 'boolean': return typeof v === 'boolean'; 9 | case 'brandedString': return typeof v === 'string'; 10 | case 'date': return v instanceof Date; 11 | case 'intersection': return d.members.every(md => isValid(md, v, options)); 12 | case 'never': return false; 13 | case 'null': return v === null; 14 | case 'number': return typeof v === 'number'; 15 | case 'object': { 16 | if (typeof v !== 'object' || v === null) return false; 17 | let propNames = Object.keys(d.properties); 18 | for (let propName of propNames) { 19 | let propDesc = d.properties[propName]; 20 | let isOptional = propDesc.kind === 'optional'; 21 | propDesc = propDesc.kind === 'optional' ? propDesc.type as Descriptor : propDesc; 22 | let propValue = (v as any)[propName]; 23 | if (propValue === undefined && isOptional) continue; 24 | if (!isValid(propDesc, propValue, options)) return false; 25 | } 26 | const allowExcessProperties = options?.allowExcessProperties ?? true; 27 | if (!allowExcessProperties) { 28 | if (Object.keys(v).some(name => !propNames.includes(name))) return false; 29 | } 30 | return true; 31 | } 32 | case 'string': return typeof v === 'string'; 33 | case 'tuple': { 34 | return Array.isArray(v) 35 | && v.length === d.elements.length 36 | && v.every((el, i) => isValid(d.elements[i], el, options)); 37 | } 38 | case 'undefined': return v === undefined; 39 | case 'union': return d.members.some(md => isValid(md, v, options)); 40 | case 'unit': return v === d.value; 41 | case 'unknown': return true; 42 | default: ((desc: never) => { throw new Error(`Unhandled case '${desc}'`) })(d); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/operations/sanitize.ts: -------------------------------------------------------------------------------- 1 | import {Descriptor} from '../descriptor'; 2 | import {isValid} from './is-valid'; 3 | 4 | // Precondition: `v` must already be known to conform to the type descriptor `d`. 5 | export function sanitize(d: Descriptor, v: unknown): unknown { 6 | switch (d.kind) { 7 | 8 | // Arrays/tuples: recursively sanitize only recognised elements into a new array instance. 9 | case 'array': 10 | case 'tuple': { 11 | let ar = v as unknown[]; 12 | return ar.map((el, i) => sanitize(d.kind === 'tuple' ? d.elements[i] : d.element, el)); 13 | } 14 | 15 | // Objects: recursively sanitize only recognised properties into a new object instance. 16 | case 'object': { 17 | let obj = v as any; 18 | let clonedObj = {} as any; 19 | for (let propName of Object.keys(d.properties)) { 20 | let propType = d.properties[propName]; 21 | let isOptional = propType.kind === 'optional'; 22 | propType = propType.kind === 'optional' ? propType.type as Descriptor : propType; 23 | let propValue = obj[propName]; 24 | if (propValue === undefined && isOptional) continue; 25 | clonedObj[propName] = sanitize(propType, obj[propName]); 26 | } 27 | return clonedObj; 28 | } 29 | 30 | // Intersections: perform excess property removal against an Object descriptor with the 31 | // union of all the properties of intersection members which are Object descriptors. 32 | case 'intersection': { 33 | return sanitize({ 34 | kind: 'object', 35 | properties: d.members.reduce( 36 | (props, member) => member.kind === 'object' ? Object.assign(props, member.properties) : props, 37 | {} as Record 38 | ), 39 | }, v); 40 | } 41 | 42 | // Unions: perform excess property removal against the first union member type that 43 | // matches the value `v` (there must be at least one according to preconditions). 44 | case 'union': { 45 | let matchingType = d.members.find(m => isValid(m, v))!; 46 | return sanitize(matchingType, v); 47 | } 48 | 49 | // Primitive values: copy the value. 50 | case 'boolean': 51 | case 'brandedString': 52 | case 'null': 53 | case 'number': 54 | case 'string': 55 | case 'undefined': 56 | case 'unit': 57 | return v; 58 | 59 | // Supported reference types: Shallow-copy the reference. 60 | case 'date': 61 | return v; 62 | 63 | // Any/unknown: Shallow-copy the reference since the type does not retrict the internal structure. 64 | case 'any': 65 | case 'unknown': 66 | return v; 67 | 68 | // Never: This type has no values, so it is an error to reach this code. 69 | case 'never': 70 | throw new Error(`Precondition violation: sanitize() called on a value that does not conform to the type.`); 71 | 72 | default: 73 | ((type: never) => { throw new Error(`Unhandled type '${type}'`) })(d); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/operations/to-json-schema.ts: -------------------------------------------------------------------------------- 1 | import {Descriptor} from '../descriptor'; 2 | 3 | export function toJsonSchema(d: Descriptor): unknown { 4 | switch (d.kind) { 5 | case 'any': return {$comment: 'any value permitted'}; 6 | case 'array': return {type: 'array', items: toJsonSchema(d.element)}; 7 | case 'boolean': return {type: 'boolean'}; 8 | case 'brandedString': return {type: 'string', $comment: `branded with '${d.brand}'`} 9 | case 'date': return {type: 'string', format: 'date-time'}; 10 | case 'intersection': return {allOf: d.members.map(toJsonSchema)}; 11 | case 'never': throw new Error(`Cannot produce JSON schema for 'never' type`); 12 | case 'null': return {type: 'null'}; 13 | case 'number': return {type: 'number'}; 14 | case 'object': { 15 | const json = {type: 'object', properties: {}} as any; 16 | const required: string[] = []; 17 | for (const propName of Object.keys(d.properties)) { 18 | let propType = d.properties[propName]; 19 | const isOptional = propType.kind === 'optional'; 20 | propType = propType.kind === 'optional' ? propType.type as Descriptor : propType; 21 | json.properties[propName] = toJsonSchema(propType); 22 | if (!isOptional) required.push(propName); 23 | } 24 | if (required.length > 0) json.required = required; 25 | return json; 26 | } 27 | case 'string': return {type: 'string'}; 28 | case 'tuple': return {type: 'array', items: d.elements.map(toJsonSchema)}; 29 | case 'undefined': throw new Error(`Cannot produce JSON schema for 'undefined' type`); 30 | case 'union': return {anyOf: d.members.map(toJsonSchema)}; 31 | case 'unit': return {const: d.value}; 32 | case 'unknown': return {$comment: 'any value permitted'}; 33 | default: ((type: never) => { throw new Error(`Unhandled type '${type}'`) })(d); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/operations/to-string.ts: -------------------------------------------------------------------------------- 1 | import {Descriptor} from '../descriptor'; 2 | 3 | // TODO: return compact/abbreviated string for complex types 4 | 5 | export function toString(d: Descriptor): string { 6 | switch (d.kind) { 7 | case 'any': return 'any'; 8 | case 'array': return `Array<${toString(d.element)}>`; 9 | case 'boolean': return 'boolean'; 10 | case 'brandedString': return `BrandedString<${JSON.stringify(d.brand)}>`; 11 | case 'date': return 'Date'; 12 | case 'intersection': 13 | if (d.members.length === 0) return `unknown`; 14 | return d.members.map(md => { 15 | let result = toString(md); 16 | if (md.kind === 'intersection' || md.kind === 'union') result = `(${result})`; 17 | return result; 18 | }).join(' & '); 19 | case 'never': return 'never'; 20 | case 'null': return 'null'; 21 | case 'number': return 'number'; 22 | case 'object': 23 | let propNames = Object.keys(d.properties); 24 | let kvps = propNames.map(n => { 25 | let propDesc = d.properties[n]; 26 | let isOpt = propDesc.kind === 'optional'; 27 | return `${n}${isOpt ? '?' : ''}: ${toString(propDesc.kind === 'optional' ? propDesc.type : propDesc)}`; 28 | }); 29 | return `{${kvps.join(', ')}}`; 30 | case 'string': return 'string'; 31 | case 'tuple': return `[${d.elements.map(toString).join(', ')}]`; 32 | case 'undefined': return 'undefined'; 33 | case 'union': 34 | if (d.members.length === 0) return `never`; 35 | return d.members.map(md => { 36 | let result = toString(md); 37 | if (md.kind === 'intersection' || md.kind === 'union') result = `(${result})`; 38 | return result; 39 | }).join(' | '); 40 | case 'unit': return JSON.stringify(d.value); 41 | case 'unknown': return 'unknown'; 42 | default: ((desc: never) => { throw new Error(`Unhandled case '${desc}'`) })(d); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/type-info.ts: -------------------------------------------------------------------------------- 1 | import {createTypeInfo} from './create-type-info'; 2 | import type {Optional} from './descriptor'; 3 | import {CheckOptions} from './operations'; 4 | 5 | /** Type builder for creating runtime representations of types. */ 6 | export const t = { 7 | /** 8 | * A type containing all possible values, but whose values can also be assigned to all other types. 9 | * This is an unsound pseudo-type that corresponds to the TypeScript `any` type. 10 | */ 11 | any: createTypeInfo({kind: 'any'}), 12 | 13 | /** A type containing all arrays whose elements all conform to the element type. */ 14 | array: (element: E) => createTypeInfo({kind: 'array', element}), 15 | 16 | /** A type containing only the values `true` and `false`. */ 17 | boolean: createTypeInfo({kind: 'boolean'}), 18 | 19 | /** 20 | * A plain string at runtime, but with nominal type-checking behaviours. In TypeScript terms, it is a subtype of 21 | * `string`. Branded strings can only be assigned to/from other branded strings if they have the same brand. All 22 | * branded strings can be assigned to strings. Plain strings can only be assigned to branded strings if the brand 23 | * ends in a question mark (eg 'usd?'), otherwise a type-cast must be used. 24 | */ 25 | brandedString: (brand: Brand) => createTypeInfo({kind: 'brandedString', brand}), 26 | 27 | /** A type representing a JavaScript Date object. */ 28 | date: createTypeInfo({kind: 'date'}), 29 | 30 | /** A type that is the conjunction of all its member types. */ 31 | intersection: (...members: M) => createTypeInfo({kind: 'intersection', members}), 32 | 33 | /** A type that contains no values. Corresponds to TypeScripts bottom type `never`. */ 34 | never: createTypeInfo({kind: 'never'}), 35 | 36 | /** A type that only contains the value `null`. */ 37 | null: createTypeInfo({kind: 'null'}), 38 | 39 | /** A type that contains all JavaScript numbers (but not BigInts). */ 40 | number: createTypeInfo({kind: 'number'}), 41 | 42 | /** A type containing all objects with the given property names and types. */ 43 | object:

>(properties: P) => createTypeInfo({kind: 'object', properties}), 44 | 45 | /** An operator for declaring that an object property is optional. */ 46 | optional: (type: T) => ({kind: 'optional' as const, type}), 47 | 48 | /** A type containing all strings. */ 49 | string: createTypeInfo({kind: 'string'}), 50 | 51 | /** A type containing all arrays where each element conforms to the element type at the corresponding position. */ 52 | tuple: (...elements: E) => createTypeInfo({kind: 'tuple', elements}), 53 | 54 | /** A type that only contains the value `undefined`. */ 55 | undefined: createTypeInfo({kind: 'undefined'}), 56 | 57 | /** A type that is the disjunction of all its member types. */ 58 | union: (...members: M) => createTypeInfo({kind: 'union', members}), 59 | 60 | /** A type representing a single literal string/number/boolean value. */ 61 | unit: (value: V) => createTypeInfo({kind: 'unit', value}), 62 | 63 | /** A type containing all possible values. */ 64 | unknown: createTypeInfo({kind: 'unknown'}), 65 | }; 66 | 67 | /** A runtime representation of a type. */ 68 | export type TypeInfo = { 69 | /** 70 | * Throws an error if the value `v` does not conform to the type, otherwise returns. Errors thrown by `assertValid` 71 | * have an `errors` property with detailed information about the errors encountered during type checking. 72 | */ 73 | assertValid(v: unknown, options?: CheckOptions): asserts v is T; 74 | 75 | /** 76 | * Checks whether the value `v` conforms to the type, returning detailed information about any type-checking errors 77 | * encountered. If the value conforms to the type, the return value has `{isValid: true}`. 78 | */ 79 | check(v: unknown, options?: CheckOptions): {isValid: boolean, errors: Array<{path: string, message: string}>}; 80 | 81 | /** 82 | * A getter that generates a sample conforming value. The value may change on each access. NOTE: This getter throws 83 | * an error on access for types that have no valid values (such as `t.never`). 84 | */ 85 | example: T; 86 | 87 | /** Returns true if the value `v` conforms to the type, or false otherwise. */ 88 | isValid(v: unknown, options?: CheckOptions): v is T; 89 | 90 | /** 91 | * Given a value `v` that is already known to conform to the type, this method returns a copy of the value 92 | * containing only components that are explicitly present in the type. That is, properties and elements in the input 93 | * value that are not explicitly present in the type are not copied to the output value. 94 | * 95 | * NOTE: the output value is not guaranteed to be a deep clone. For example, values typed as `date`, `any` or 96 | * `unknown` are shallow-copied to the output value. 97 | */ 98 | sanitize(v: T): T; 99 | 100 | /** 101 | * Returns a JSON Schema representation of the type. 102 | */ 103 | toJsonSchema(): unknown; 104 | 105 | /** 106 | * Returns a human-readable representation of the type. 107 | */ 108 | toString(): string; 109 | } 110 | 111 | export {CheckOptions}; 112 | -------------------------------------------------------------------------------- /src/utils/anonymize.ts: -------------------------------------------------------------------------------- 1 | export type Anonymize = Anonymize2<{[K in keyof Obj]: Obj[K]}>; 2 | type Anonymize2 = T; 3 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export {Anonymize} from './anonymize'; 2 | export {inspect} from './inspect'; 3 | -------------------------------------------------------------------------------- /src/utils/inspect.ts: -------------------------------------------------------------------------------- 1 | export function inspect(v: unknown): string { 2 | return inspectInternal(v, new Set()); 3 | } 4 | 5 | function inspectInternal(v: unknown, seen: Set): string { 6 | if (v === null || v === undefined || typeof v === 'number' || typeof v === 'boolean') { 7 | return String(v); 8 | } 9 | 10 | if (typeof v === 'string') { 11 | return JSON.stringify(v); 12 | } 13 | 14 | if (v instanceof Date) { 15 | return v.toISOString(); 16 | } 17 | 18 | if (seen.has(v)) { 19 | return '[cyclic]'; 20 | } 21 | 22 | if (typeof v === 'object' && !Array.isArray(v)) { 23 | seen.add(v); 24 | const obj = v as Record; 25 | const props = Object.keys(obj).map(propName => { 26 | const value = inspectInternal(obj[propName], seen); 27 | if (!/^[a-z_$][a-z0-9_$]*$/ig.test(propName)) propName = JSON.stringify(propName); 28 | return `${propName}: ${value}`; 29 | }); 30 | return `{${props.join(', ')}}`; 31 | } 32 | 33 | if (typeof v === 'object' && Array.isArray(v)) { 34 | seen.add(v); 35 | return `[${v.map(elem => inspectInternal(elem, seen)).join(', ')}]`; 36 | } 37 | 38 | return `[${(v as any).constructor.name}]`; 39 | } 40 | -------------------------------------------------------------------------------- /tests/api-v0/get-json-schema.test.ts: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai'; 2 | import {t, TypeInfo, getJsonSchema, toString} from 'rtti'; 3 | 4 | describe('The getJsonSchema() function', () => { 5 | const testCases: Array<{type: TypeInfo, jsonSchema: unknown}> = [ 6 | { 7 | type: t.string, 8 | jsonSchema: {type: 'string'}, 9 | }, 10 | { 11 | type: t.number, 12 | jsonSchema: {type: 'number'}, 13 | }, 14 | { 15 | type: t.boolean, 16 | jsonSchema: {type: 'boolean'}, 17 | }, 18 | { 19 | type: t.null, 20 | jsonSchema: {type: 'null'}, 21 | }, 22 | { 23 | type: t.any, 24 | jsonSchema: {$comment: 'any value permitted'}, 25 | }, 26 | { 27 | type: t.unknown, 28 | jsonSchema: {$comment: 'any value permitted'}, 29 | }, 30 | { 31 | type: t.undefined, 32 | jsonSchema: Error, 33 | }, 34 | { 35 | type: t.never, 36 | jsonSchema: Error, 37 | }, 38 | { 39 | type: t.date, 40 | jsonSchema: {type: 'string', format: 'date-time'}, 41 | }, 42 | { 43 | type: t.brandedString('userId'), 44 | jsonSchema: {type: 'string', $comment: `branded with 'userId'`}, 45 | }, 46 | { 47 | type: t.unit('meatball'), 48 | jsonSchema: {const: 'meatball'}, 49 | }, 50 | { 51 | type: t.unit(42), 52 | jsonSchema: {const: 42}, 53 | }, 54 | { 55 | type: t.union(t.unit('foo'), t.unit('bar')), 56 | jsonSchema: {anyOf: [{const: 'foo'}, {const: 'bar'}]}, 57 | }, 58 | { 59 | type: t.union(t.unit('foo'), t.null, t.unit(42)), 60 | jsonSchema: {anyOf: [{const: 'foo'}, {type: 'null'}, {const: 42}]}, 61 | }, 62 | { 63 | type: t.object({foo: t.string}), 64 | jsonSchema: {type: 'object', properties: {foo: {type: 'string'}}, required: ['foo']}, 65 | }, 66 | { 67 | type: t.object({bar: t.optional(t.number)}), 68 | jsonSchema: {type: 'object', properties: {bar: {type: 'number'}}}, 69 | }, 70 | { 71 | type: t.object({foo: t.string, bar: t.optional(t.number)}), 72 | jsonSchema: {type: 'object', properties: {foo: {type: 'string'}, bar: {type: 'number'}}, required: ['foo']}, 73 | }, 74 | { 75 | type: t.array(t.union(t.string, t.boolean)), 76 | jsonSchema: {type: 'array', items: {anyOf: [{type: 'string'}, {type: 'boolean'}]}}, 77 | }, 78 | { 79 | type: t.array(t.unknown), 80 | jsonSchema: {type: 'array', items: {$comment: 'any value permitted'}}, 81 | }, 82 | { 83 | type: t.tuple(t.string, t.string, t.number), 84 | jsonSchema: {type: 'array', items: [{type: 'string'}, {type: 'string'}, {type: 'number'}]}, 85 | }, 86 | { 87 | type: t.intersection( 88 | t.object({base1: t.string, base2: t.optional(t.tuple(t.string)), base3: t.union(t.unit(42), t.null)}), 89 | t.object({extra1: t.optional(t.object({ 90 | ex11: t.boolean, 91 | ex12: t.unknown, 92 | }))}), 93 | t.object({extra2: t.array(t.union(t.number, t.string))}), 94 | ), 95 | jsonSchema: { 96 | allOf: [ 97 | { 98 | type: 'object', 99 | properties: { 100 | base1: {type: 'string'}, 101 | base2: {type: 'array', items: [{type: 'string'}]}, 102 | base3: {anyOf: [{const: 42}, {type: 'null'}]}, 103 | }, 104 | required: ['base1', 'base3'], 105 | }, 106 | { 107 | type: 'object', 108 | properties: { 109 | extra1: { 110 | type: 'object', 111 | properties: {ex11: {type: 'boolean'}, ex12: {$comment: 'any value permitted'}}, 112 | required: ['ex11', 'ex12'], 113 | }, 114 | }, 115 | }, 116 | { 117 | type: 'object', 118 | properties: { 119 | extra2: {type: 'array', items: {anyOf: [{type: 'number'}, {type: 'string'}]}}, 120 | }, 121 | required: ['extra2'], 122 | }, 123 | ], 124 | }, 125 | }, 126 | ]; 127 | 128 | 129 | for (let {type, jsonSchema: expected} of testCases) { 130 | it(`JSON schema for ${toString(type)}`, () => { 131 | let actual: unknown; 132 | try { 133 | actual = getJsonSchema(type); 134 | } 135 | catch { 136 | actual = Error; 137 | } 138 | expect(actual).to.deep.equal(expected); 139 | }); 140 | } 141 | }); 142 | -------------------------------------------------------------------------------- /tests/api-v0/get-validation-errors.test.ts: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai'; 2 | import {inspect} from 'util'; 3 | import {t, getValidationErrors, toString} from 'rtti'; 4 | 5 | describe('The getValidationErrors() function', () => { 6 | 7 | const tests = [ 8 | { 9 | type: t.string, 10 | value: 'foo', 11 | expectedErrors: [], 12 | expectedWarnings: [], 13 | }, 14 | { 15 | type: t.string, 16 | value: 42, 17 | expectedErrors: [{path: '^', message: `Expected a string but got 42`}], 18 | expectedWarnings: [], 19 | }, 20 | { 21 | type: t.union(t.unit('foo'), t.unit('bar')), 22 | value: 'foo', 23 | expectedErrors: [], 24 | expectedWarnings: [], 25 | }, 26 | { 27 | type: t.union(t.unit('foo'), t.unit('bar')), 28 | value: 'baz', 29 | expectedErrors: [{path: '^', message: `The value "baz" does not conform to the union type`}], 30 | expectedWarnings: [], 31 | }, 32 | { 33 | type: t.object({foo: t.string}), 34 | value: {foo: 42, bar: [1, 2, 3]}, 35 | expectedErrors: [{path: '^.foo', message: `Expected a string but got 42`}], 36 | expectedWarnings: [{path: '^', message: `The object has excess properties: bar`}], 37 | }, 38 | { 39 | type: t.object({foo: t.object({str: t.string, num: t.number})}), 40 | value: {foo: {str: true, num: {pi: 3.14}}}, 41 | expectedErrors: [ 42 | {path: '^.foo.str', message: `Expected a string but got true`}, 43 | {path: '^.foo.num', message: `Expected a number but got {pi: 3.14}`} 44 | ], 45 | expectedWarnings: [], 46 | }, 47 | { 48 | type: t.object({nums: t.array(t.number)}), 49 | value: {nums: [3.14, -1, NaN, 'one', 42, '3', {zero: true}]}, 50 | expectedErrors: [ 51 | {path: '^.nums[3]', message: `Expected a number but got "one"`}, 52 | {path: '^.nums[5]', message: `Expected a number but got "3"`}, 53 | {path: '^.nums[6]', message: `Expected a number but got {zero: true}`}, 54 | ], 55 | expectedWarnings: [], 56 | }, 57 | { 58 | type: t.object({foo: t.optional(t.string)}), 59 | value: {foo: undefined}, 60 | expectedErrors: [], 61 | expectedWarnings: [], 62 | }, 63 | { 64 | type: t.object({foo: t.optional(t.object({bar: t.number}))}), 65 | value: {foo: undefined}, 66 | expectedErrors: [], 67 | expectedWarnings: [], 68 | }, 69 | ]; 70 | 71 | for (let {type, value, expectedErrors, expectedWarnings} of tests) { 72 | let d = inspect(value, {depth: 0, compact: true, breakLength: Infinity}); 73 | it(`${d} as ${toString(type)}`, () => { 74 | let actual = getValidationErrors(type, value); 75 | expect(actual.errors).to.deep.equal(expectedErrors); 76 | expect(actual.warnings).to.deep.equal(expectedWarnings); 77 | }); 78 | } 79 | }); 80 | -------------------------------------------------------------------------------- /tests/api-v0/is.test.ts: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai'; 2 | import {t, TypeInfo, is} from 'rtti'; 3 | 4 | describe('The is() function', () => { 5 | 6 | const values = { 7 | string: 'kasdjfkjasdfgasjkdhgfkasjdhgf', 8 | stringFooOrBar: 'foo', 9 | number: 253153124123, 10 | number42: 42, 11 | // object: {a: 1, bbb: 'blah', xyz123: [true, false]}, 12 | objectWithFoo: {foo: 'bar'}, 13 | array: [1, 'foo', false], 14 | arrayOfNum: [1, 2, 3, 4], 15 | objectWithOptionalBarMissing: {baz: 10}, 16 | objectWithOptionalBarUndefined: {bar: undefined, baz: 10}, 17 | }; 18 | 19 | const types: Record = { 20 | string: t.string, 21 | stringFooOrBar: t.union(t.unit('foo'), t.unit('bar')), 22 | number: t.number, 23 | number42: t.unit(42), 24 | objectWithFoo: t.object({foo: t.string}), 25 | array: t.array(t.unknown), 26 | arrayOfNum: t.array(t.number), 27 | objectWithOptionalBar: t.object({bar: t.optional(t.string), baz: t.number}), 28 | }; 29 | 30 | for (let [valueName, value] of Object.entries(values)) { 31 | for (let [typeName, type] of Object.entries(types)) { 32 | let expected = valueName.startsWith(typeName); 33 | it(`Value ${JSON.stringify(value)} is ${expected ? '' : 'not '}of type '${typeName}'`, () => { 34 | expect(is(type, value)).to.equal(expected); 35 | }); 36 | } 37 | } 38 | }); 39 | -------------------------------------------------------------------------------- /tests/api-v0/remove-excess-properties.test.ts: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai'; 2 | import {inspect} from 'util'; 3 | import {t, removeExcessProperties, toString} from 'rtti'; 4 | 5 | describe('The removeExcessProperties() function', () => { 6 | 7 | const tests = [ 8 | { 9 | type: t.string, 10 | value: 'foo', 11 | expected: 'foo', 12 | }, 13 | { 14 | type: t.string, 15 | value: 42, 16 | expected: 42, 17 | }, 18 | { 19 | type: t.union(t.unit('foo'), t.unit('bar')), 20 | value: 'bar', 21 | expected: 'bar', 22 | }, 23 | { 24 | type: t.union(t.unit('foo'), t.object({foo: t.number})), 25 | value: {foo: 42, bar: 'hi'}, 26 | expected: {foo: 42}, 27 | }, 28 | { 29 | type: t.object({foo: t.string}), 30 | value: {foo: 'hi', bar: [1, 2, 3]}, 31 | expected: {foo: 'hi'}, 32 | }, 33 | { 34 | type: t.object({foo: t.optional(t.string)}), 35 | value: {foo: 'hi', bar: [1, 2, 3]}, 36 | expected: {foo: 'hi'}, 37 | }, 38 | { 39 | type: t.object({foo: t.optional(t.string)}), 40 | value: {quux: 'hi', bar: [1, 2, 3]}, 41 | expected: {}, 42 | }, 43 | { 44 | type: t.object({foo: t.optional(t.string)}), 45 | value: {foo: undefined, quux: 'hi'}, 46 | expected: {}, 47 | }, 48 | { 49 | type: t.object({foo: t.optional(t.object({bar: t.number}))}), 50 | value: {foo: undefined, quux: 'hi'}, 51 | expected: {}, 52 | }, 53 | { 54 | type: t.object({foo: t.object({str: t.string, num: t.number})}), 55 | value: {foo: {str: 'hi', num: 3.14}}, 56 | expected: {foo: {str: 'hi', num: 3.14}}, 57 | }, 58 | { 59 | type: t.object({foo: t.object({str: t.string, num: t.number})}), 60 | value: {foo: {x: true, str: 'hi', num: 3.14}, bar: 43}, 61 | expected: {foo: {str: 'hi', num: 3.14}}, 62 | }, 63 | { 64 | type: t.object({nums: t.array(t.number)}), 65 | value: {nums: [3.14, -1, 42], chars: 'abc'}, 66 | expected: {nums: [3.14, -1, 42]}, 67 | }, 68 | { 69 | type: t.array(t.object({a: t.optional(t.number), b: t.optional(t.number)})), 70 | value: [{}, {a: 1}, {b: 2}, {c: 3}, {a: 1, c: 3}, {b: 2, c: 3}, {a: 1, b: 2, c: 3, d: 4}], 71 | expected: [{}, {a: 1}, {b: 2}, {}, {a: 1}, {b: 2}, {a: 1, b: 2}], 72 | }, 73 | { 74 | type: t.intersection(t.object({num: t.number}), t.object({str: t.string})), 75 | value: {num: 42, str: 'hi', bool: true}, 76 | expected: {num: 42, str: 'hi'}, 77 | }, 78 | { 79 | type: t.intersection(t.array(t.any), t.object({num: t.number}), t.object({str: t.string})), 80 | value: Object.assign([1, 2, 4], {num: 42, str: 'hi', bool: true}), 81 | expected: {num: 42, str: 'hi'}, 82 | }, 83 | { 84 | type: t.union(t.object({num: t.number}), t.object({str: t.string})), 85 | value: {num: 42, str: 'hi', bool: true}, 86 | expected: {num: 42}, 87 | }, 88 | { 89 | type: t.union(t.array(t.any), t.object({num: t.number}), t.object({str: t.string})), 90 | value: Object.assign([1, 2, 4], {num: 42, str: 'hi', bool: true}), 91 | expected: [1, 2, 4], 92 | }, 93 | { 94 | type: t.unknown, 95 | value: {foo: {x: true, str: 'hi', num: 3.14}, bar: 43}, 96 | expected: {foo: {x: true, str: 'hi', num: 3.14}, bar: 43}, 97 | }, 98 | { 99 | type: t.any, 100 | value: {foo: {x: true, str: 'hi', num: 3.14}, bar: 43}, 101 | expected: {foo: {x: true, str: 'hi', num: 3.14}, bar: 43}, 102 | }, 103 | 104 | // Regression test for https://github.com/yortus/rtti/issues/3 105 | { 106 | type: t.intersection(t.intersection(t.object({a: t.any}), t.object({b: t.any})), t.object({c: t.any})), 107 | value: Object.assign([1, 2, 4], {a: 42, b: 'hi', c: true, d: [1, 2, 3]}), 108 | expected: {a: 42, b: 'hi', c: true}, 109 | }, 110 | ]; 111 | 112 | for (let {type, value, expected} of tests) { 113 | let d = inspect(value, {depth: 0, compact: true, breakLength: Infinity}); 114 | it(`${d} as ${toString(type)}`, () => { 115 | let actual = removeExcessProperties(type, value as any); 116 | expect(actual).to.deep.equal(expected); 117 | }); 118 | } 119 | }); 120 | -------------------------------------------------------------------------------- /tests/api-v0/to-string.test.ts: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai'; 2 | import {t, toString} from 'rtti'; 3 | 4 | describe('The toString() function', () => { 5 | 6 | const tests = [ 7 | { 8 | type: t.string, 9 | text: 'string', 10 | }, 11 | { 12 | type: t.union(t.unit('foo'), t.unit('bar')), 13 | text: '"foo" | "bar"', 14 | }, 15 | { 16 | type: t.number, 17 | text: 'number', 18 | }, 19 | { 20 | type: t.unit(42), 21 | text: '42', 22 | }, 23 | { 24 | type: t.object({foo: t.string}), 25 | text: '{foo: string}', 26 | }, 27 | { 28 | type: t.object({foo: t.optional(t.string)}), 29 | text: '{foo?: string}', 30 | }, 31 | { 32 | type: t.array(t.unknown), 33 | text: 'Array', 34 | }, 35 | { 36 | type: t.array(t.number), 37 | text: 'Array', 38 | }, 39 | { 40 | type: t.intersection( 41 | t.object({foo: t.string}), 42 | t.object({bar: t.number}), 43 | ), 44 | text: '{foo: string} & {bar: number}', 45 | }, 46 | ]; 47 | 48 | for (let {type, text} of tests) { 49 | it(text, () => { 50 | let expected = text; 51 | let actual = toString(type); 52 | expect(actual).to.equal(expected); 53 | }); 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /tests/assert-valid.test.ts: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai'; 2 | import {t} from 'rtti'; 3 | 4 | describe('The assertValid() method', () => { 5 | 6 | const values = { 7 | string: 'kasdjfkjasdfgasjkdhgfkasjdhgf', 8 | stringFooOrBar: 'foo', 9 | number: 253153124123, 10 | number42: 42, 11 | // object: {a: 1, bbb: 'blah', xyz123: [true, false]}, 12 | objectWithFoo: {foo: 'bar'}, 13 | array: [1, 'foo', false], 14 | arrayOfNum: [1, 2, 3, 4], 15 | }; 16 | 17 | const types = { 18 | string: t.string, 19 | stringFooOrBar: t.union(t.unit('foo'), t.unit('bar')), 20 | number: t.number, 21 | number42: t.unit(42), 22 | objectWithFoo: t.object({foo: t.string}), 23 | array: t.array(t.unknown), 24 | arrayOfNum: t.array(t.number), 25 | }; 26 | 27 | for (let [valueName, value] of Object.entries(values)) { 28 | for (let [typeName, type] of Object.entries(types)) { 29 | let expectToThrow = !valueName.startsWith(typeName); 30 | it(`Value ${JSON.stringify(value)} is ${expectToThrow ? 'not ' : ''}of type '${typeName}'`, () => { 31 | if (expectToThrow) { 32 | expect(() => type.assertValid(value)).to.throw(); 33 | } 34 | else { 35 | expect(() => type.assertValid(value)).to.not.throw(); 36 | } 37 | }); 38 | } 39 | } 40 | }); 41 | -------------------------------------------------------------------------------- /tests/check.test.ts: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai'; 2 | import {inspect} from 'util'; 3 | import {t} from 'rtti'; 4 | 5 | describe('The check() method', () => { 6 | 7 | const tests = [ 8 | { 9 | type: t.string, 10 | value: 'foo', 11 | expectedErrors: [], 12 | expectedStrictErrors: [], 13 | }, 14 | { 15 | type: t.string, 16 | value: 42, 17 | expectedErrors: [{path: '^', message: `Expected a string but got 42`}], 18 | expectedStrictErrors: [], 19 | }, 20 | { 21 | type: t.union(t.unit('foo'), t.unit('bar')), 22 | value: 'foo', 23 | expectedErrors: [], 24 | expectedStrictErrors: [], 25 | }, 26 | { 27 | type: t.union(t.unit('foo'), t.unit('bar')), 28 | value: 'baz', 29 | expectedErrors: [{path: '^', message: `The value "baz" does not conform to the union type`}], 30 | expectedStrictErrors: [], 31 | }, 32 | { 33 | type: t.object({foo: t.string}), 34 | value: {foo: 42, bar: [1, 2, 3]}, 35 | expectedErrors: [{path: '^.foo', message: `Expected a string but got 42`}], 36 | expectedStrictErrors: [{path: '^', message: `The object has excess properties: bar`}], 37 | }, 38 | { 39 | type: t.object({foo: t.object({str: t.string, num: t.number})}), 40 | value: {foo: {str: true, num: {pi: 3.14}}}, 41 | expectedErrors: [ 42 | {path: '^.foo.str', message: `Expected a string but got true`}, 43 | {path: '^.foo.num', message: `Expected a number but got {pi: 3.14}`} 44 | ], 45 | expectedStrictErrors: [], 46 | }, 47 | { 48 | type: t.object({nums: t.array(t.number)}), 49 | value: {nums: [3.14, -1, NaN, 'one', 42, '3', {zero: true}]}, 50 | expectedErrors: [ 51 | {path: '^.nums[3]', message: `Expected a number but got "one"`}, 52 | {path: '^.nums[5]', message: `Expected a number but got "3"`}, 53 | {path: '^.nums[6]', message: `Expected a number but got {zero: true}`}, 54 | ], 55 | expectedStrictErrors: [], 56 | }, 57 | { 58 | type: t.object({foo: t.optional(t.string)}), 59 | value: {foo: undefined}, 60 | expectedErrors: [], 61 | expectedStrictErrors: [], 62 | }, 63 | { 64 | type: t.object({foo: t.optional(t.object({bar: t.number}))}), 65 | value: {foo: undefined}, 66 | expectedErrors: [], 67 | expectedStrictErrors: [], 68 | }, 69 | ]; 70 | 71 | for (let {type, value, expectedErrors, expectedStrictErrors} of tests) { 72 | let d = inspect(value, {depth: 0, compact: true, breakLength: Infinity}); 73 | it(`${d} as ${type}`, () => { 74 | let actualLoose = type.check(value, {allowExcessProperties: true}); 75 | expect(actualLoose.errors).to.have.deep.members(expectedErrors); 76 | 77 | let actualStrict = type.check(value, {allowExcessProperties: false}); 78 | expect(actualStrict.errors).to.have.deep.members([...expectedErrors, ...expectedStrictErrors]); 79 | }); 80 | } 81 | }); 82 | -------------------------------------------------------------------------------- /tests/is-valid.test.ts: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai'; 2 | import {t} from 'rtti'; 3 | 4 | // TODO: test with allowExcessProperties: false 5 | 6 | describe('The isValid() method', () => { 7 | 8 | const values = { 9 | string: 'kasdjfkjasdfgasjkdhgfkasjdhgf', 10 | stringFooOrBar: 'foo', 11 | number: 253153124123, 12 | number42: 42, 13 | // object: {a: 1, bbb: 'blah', xyz123: [true, false]}, 14 | objectWithFoo: {foo: 'bar'}, 15 | array: [1, 'foo', false], 16 | arrayOfNum: [1, 2, 3, 4], 17 | objectWithOptionalBarMissing: {baz: 10}, 18 | objectWithOptionalBarUndefined: {bar: undefined, baz: 10}, 19 | }; 20 | 21 | const types = { 22 | string: t.string, 23 | stringFooOrBar: t.union(t.unit('foo'), t.unit('bar')), 24 | number: t.number, 25 | number42: t.unit(42), 26 | objectWithFoo: t.object({foo: t.string}), 27 | array: t.array(t.unknown), 28 | arrayOfNum: t.array(t.number), 29 | objectWithOptionalBar: t.object({bar: t.optional(t.string), baz: t.number}), 30 | }; 31 | 32 | for (let [valueName, value] of Object.entries(values)) { 33 | for (let [typeName, type] of Object.entries(types)) { 34 | let expected = valueName.startsWith(typeName); 35 | it(`Value ${JSON.stringify(value)} is ${expected ? '' : 'not '}of type '${typeName}'`, () => { 36 | expect(type.isValid(value)).to.equal(expected); 37 | }); 38 | } 39 | } 40 | }); 41 | -------------------------------------------------------------------------------- /tests/sanitize.test.ts: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai'; 2 | import {inspect} from 'util'; 3 | import {t} from 'rtti'; 4 | 5 | describe('The sanitize() method', () => { 6 | 7 | const tests = [ 8 | { 9 | type: t.string, 10 | value: 'foo', 11 | expected: 'foo', 12 | }, 13 | { 14 | type: t.string, 15 | value: 42, 16 | expected: 42, 17 | }, 18 | { 19 | type: t.union(t.unit('foo'), t.unit('bar')), 20 | value: 'bar', 21 | expected: 'bar', 22 | }, 23 | { 24 | type: t.union(t.unit('foo'), t.object({foo: t.number})), 25 | value: {foo: 42, bar: 'hi'}, 26 | expected: {foo: 42}, 27 | }, 28 | { 29 | type: t.object({foo: t.string}), 30 | value: {foo: 'hi', bar: [1, 2, 3]}, 31 | expected: {foo: 'hi'}, 32 | }, 33 | { 34 | type: t.object({foo: t.optional(t.string)}), 35 | value: {foo: 'hi', bar: [1, 2, 3]}, 36 | expected: {foo: 'hi'}, 37 | }, 38 | { 39 | type: t.object({foo: t.optional(t.string)}), 40 | value: {quux: 'hi', bar: [1, 2, 3]}, 41 | expected: {}, 42 | }, 43 | { 44 | type: t.object({foo: t.optional(t.string)}), 45 | value: {foo: undefined, quux: 'hi'}, 46 | expected: {}, 47 | }, 48 | { 49 | type: t.object({foo: t.optional(t.object({bar: t.number}))}), 50 | value: {foo: undefined, quux: 'hi'}, 51 | expected: {}, 52 | }, 53 | { 54 | type: t.object({foo: t.object({str: t.string, num: t.number})}), 55 | value: {foo: {str: 'hi', num: 3.14}}, 56 | expected: {foo: {str: 'hi', num: 3.14}}, 57 | }, 58 | { 59 | type: t.object({foo: t.object({str: t.string, num: t.number})}), 60 | value: {foo: {x: true, str: 'hi', num: 3.14}, bar: 43}, 61 | expected: {foo: {str: 'hi', num: 3.14}}, 62 | }, 63 | { 64 | type: t.object({nums: t.array(t.number)}), 65 | value: {nums: [3.14, -1, 42], chars: 'abc'}, 66 | expected: {nums: [3.14, -1, 42]}, 67 | }, 68 | { 69 | type: t.array(t.object({a: t.optional(t.number), b: t.optional(t.number)})), 70 | value: [{}, {a: 1}, {b: 2}, {c: 3}, {a: 1, c: 3}, {b: 2, c: 3}, {a: 1, b: 2, c: 3, d: 4}], 71 | expected: [{}, {a: 1}, {b: 2}, {}, {a: 1}, {b: 2}, {a: 1, b: 2}], 72 | }, 73 | { 74 | type: t.intersection(t.object({num: t.number}), t.object({str: t.string})), 75 | value: {num: 42, str: 'hi', bool: true}, 76 | expected: {num: 42, str: 'hi'}, 77 | }, 78 | { 79 | type: t.intersection(t.array(t.any), t.object({num: t.number}), t.object({str: t.string})), 80 | value: Object.assign([1, 2, 4], {num: 42, str: 'hi', bool: true}), 81 | expected: {num: 42, str: 'hi'}, 82 | }, 83 | { 84 | type: t.union(t.object({num: t.number}), t.object({str: t.string})), 85 | value: {num: 42, str: 'hi', bool: true}, 86 | expected: {num: 42}, 87 | }, 88 | { 89 | type: t.union(t.array(t.any), t.object({num: t.number}), t.object({str: t.string})), 90 | value: Object.assign([1, 2, 4], {num: 42, str: 'hi', bool: true}), 91 | expected: [1, 2, 4], 92 | }, 93 | { 94 | type: t.unknown, 95 | value: {foo: {x: true, str: 'hi', num: 3.14}, bar: 43}, 96 | expected: {foo: {x: true, str: 'hi', num: 3.14}, bar: 43}, 97 | }, 98 | { 99 | type: t.any, 100 | value: {foo: {x: true, str: 'hi', num: 3.14}, bar: 43}, 101 | expected: {foo: {x: true, str: 'hi', num: 3.14}, bar: 43}, 102 | }, 103 | 104 | // Regression test for https://github.com/yortus/rtti/issues/3 105 | { 106 | type: t.intersection(t.intersection(t.object({a: t.any}), t.object({b: t.any})), t.object({c: t.any})), 107 | value: Object.assign([1, 2, 4], {a: 42, b: 'hi', c: true, d: [1, 2, 3]}), 108 | expected: {a: 42, b: 'hi', c: true}, 109 | }, 110 | ]; 111 | 112 | for (let {type, value, expected} of tests) { 113 | let d = inspect(value, {depth: 0, compact: true, breakLength: Infinity}); 114 | it(`${d} as ${type}`, () => { 115 | let actual = type.sanitize(value as any); 116 | expect(actual).to.deep.equal(expected); 117 | }); 118 | } 119 | }); 120 | -------------------------------------------------------------------------------- /tests/to-json-schema.test.ts: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai'; 2 | import {t, TypeInfo} from 'rtti'; 3 | 4 | describe('The toJsonSchema() method', () => { 5 | const testCases: Array<{type: TypeInfo, jsonSchema: unknown}> = [ 6 | { 7 | type: t.string, 8 | jsonSchema: {type: 'string'}, 9 | }, 10 | { 11 | type: t.number, 12 | jsonSchema: {type: 'number'}, 13 | }, 14 | { 15 | type: t.boolean, 16 | jsonSchema: {type: 'boolean'}, 17 | }, 18 | { 19 | type: t.null, 20 | jsonSchema: {type: 'null'}, 21 | }, 22 | { 23 | type: t.any, 24 | jsonSchema: {$comment: 'any value permitted'}, 25 | }, 26 | { 27 | type: t.unknown, 28 | jsonSchema: {$comment: 'any value permitted'}, 29 | }, 30 | { 31 | type: t.undefined, 32 | jsonSchema: Error, 33 | }, 34 | { 35 | type: t.never, 36 | jsonSchema: Error, 37 | }, 38 | { 39 | type: t.date, 40 | jsonSchema: {type: 'string', format: 'date-time'}, 41 | }, 42 | { 43 | type: t.brandedString('userId'), 44 | jsonSchema: {type: 'string', $comment: `branded with 'userId'`}, 45 | }, 46 | { 47 | type: t.unit('meatball'), 48 | jsonSchema: {const: 'meatball'}, 49 | }, 50 | { 51 | type: t.unit(42), 52 | jsonSchema: {const: 42}, 53 | }, 54 | { 55 | type: t.union(t.unit('foo'), t.unit('bar')), 56 | jsonSchema: {anyOf: [{const: 'foo'}, {const: 'bar'}]}, 57 | }, 58 | { 59 | type: t.union(t.unit('foo'), t.null, t.unit(42)), 60 | jsonSchema: {anyOf: [{const: 'foo'}, {type: 'null'}, {const: 42}]}, 61 | }, 62 | { 63 | type: t.object({foo: t.string}), 64 | jsonSchema: {type: 'object', properties: {foo: {type: 'string'}}, required: ['foo']}, 65 | }, 66 | { 67 | type: t.object({bar: t.optional(t.number)}), 68 | jsonSchema: {type: 'object', properties: {bar: {type: 'number'}}}, 69 | }, 70 | { 71 | type: t.object({foo: t.string, bar: t.optional(t.number)}), 72 | jsonSchema: {type: 'object', properties: {foo: {type: 'string'}, bar: {type: 'number'}}, required: ['foo']}, 73 | }, 74 | { 75 | type: t.array(t.union(t.string, t.boolean)), 76 | jsonSchema: {type: 'array', items: {anyOf: [{type: 'string'}, {type: 'boolean'}]}}, 77 | }, 78 | { 79 | type: t.array(t.unknown), 80 | jsonSchema: {type: 'array', items: {$comment: 'any value permitted'}}, 81 | }, 82 | { 83 | type: t.tuple(t.string, t.string, t.number), 84 | jsonSchema: {type: 'array', items: [{type: 'string'}, {type: 'string'}, {type: 'number'}]}, 85 | }, 86 | { 87 | type: t.intersection( 88 | t.object({base1: t.string, base2: t.optional(t.tuple(t.string)), base3: t.union(t.unit(42), t.null)}), 89 | t.object({extra1: t.optional(t.object({ 90 | ex11: t.boolean, 91 | ex12: t.unknown, 92 | }))}), 93 | t.object({extra2: t.array(t.union(t.number, t.string))}), 94 | ), 95 | jsonSchema: { 96 | allOf: [ 97 | { 98 | type: 'object', 99 | properties: { 100 | base1: {type: 'string'}, 101 | base2: {type: 'array', items: [{type: 'string'}]}, 102 | base3: {anyOf: [{const: 42}, {type: 'null'}]}, 103 | }, 104 | required: ['base1', 'base3'], 105 | }, 106 | { 107 | type: 'object', 108 | properties: { 109 | extra1: { 110 | type: 'object', 111 | properties: {ex11: {type: 'boolean'}, ex12: {$comment: 'any value permitted'}}, 112 | required: ['ex11', 'ex12'], 113 | }, 114 | }, 115 | }, 116 | { 117 | type: 'object', 118 | properties: { 119 | extra2: {type: 'array', items: {anyOf: [{type: 'number'}, {type: 'string'}]}}, 120 | }, 121 | required: ['extra2'], 122 | }, 123 | ], 124 | }, 125 | }, 126 | ]; 127 | 128 | 129 | for (let {type, jsonSchema: expected} of testCases) { 130 | it(`JSON schema for ${type}`, () => { 131 | let actual: unknown; 132 | try { 133 | actual = type.toJsonSchema(); 134 | } 135 | catch { 136 | actual = Error; 137 | } 138 | expect(actual).to.deep.equal(expected); 139 | }); 140 | } 141 | }); 142 | -------------------------------------------------------------------------------- /tests/to-string.test.ts: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai'; 2 | import {t} from 'rtti'; 3 | 4 | describe('The toString() method', () => { 5 | 6 | const tests = [ 7 | { 8 | type: t.string, 9 | text: 'string', 10 | }, 11 | { 12 | type: t.union(t.unit('foo'), t.unit('bar')), 13 | text: '"foo" | "bar"', 14 | }, 15 | { 16 | type: t.number, 17 | text: 'number', 18 | }, 19 | { 20 | type: t.unit(42), 21 | text: '42', 22 | }, 23 | { 24 | type: t.object({foo: t.string}), 25 | text: '{foo: string}', 26 | }, 27 | { 28 | type: t.object({foo: t.optional(t.string)}), 29 | text: '{foo?: string}', 30 | }, 31 | { 32 | type: t.array(t.unknown), 33 | text: 'Array', 34 | }, 35 | { 36 | type: t.array(t.number), 37 | text: 'Array', 38 | }, 39 | { 40 | type: t.intersection( 41 | t.object({foo: t.string}), 42 | t.object({bar: t.number}), 43 | ), 44 | text: '{foo: string} & {bar: number}', 45 | }, 46 | ]; 47 | 48 | for (let {type, text} of tests) { 49 | it(text, () => { 50 | let expected = text; 51 | let actual = type.toString(); 52 | expect(actual).to.equal(expected); 53 | }); 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /tests/types.test.ts: -------------------------------------------------------------------------------- 1 | // Type tests: the code here is not executed, it just needs to pass type checks to be considered successful. 2 | 3 | // -------------------------------------------------- 4 | // Regression test for bug caused by https://github.com/microsoft/TypeScript/issues/46655. 5 | // Note that `t` is imported from the built declaration files, since that's where the regression occurred. 6 | import {t} from '../dist/commonjs'; 7 | export {test1, test2}; 8 | 9 | // Intersection 10 | const T1 = t.intersection( 11 | t.object({foo: t.string, bar: t.number}), 12 | t.object({bar: t.unknown, baz: t.number}), 13 | t.object({quux: t.array(t.string)}), 14 | ); 15 | function test1(t1: typeof T1.example) { 16 | t1.foo.padStart; 17 | t1.bar.toFixed; 18 | t1.baz.toFixed; 19 | t1.quux.map(el => el.padStart); 20 | } 21 | 22 | // Union 23 | const T2 = t.union( 24 | t.object({kind: t.unit('A'), foo: t.number}), 25 | t.object({kind: t.unit('B'), bar: t.boolean}), 26 | t.object({kind: t.unit('C'), baz: t.string}), 27 | ); 28 | function test2(t2: typeof T2.example) { 29 | switch (t2.kind) { 30 | case 'A': t2.foo.toFixed; break; 31 | case 'B': t2.bar; break; 32 | case 'C': t2.baz.padStart; break; 33 | default: ((x: never) => x)(t2); 34 | } 35 | } 36 | 37 | // -------------------------------------------------- 38 | // Regression test for https://github.com/yortus/http-schemas/issues/30 39 | const Result = t.object({ 40 | date: t.string, 41 | foobar: t.optional(t.number), 42 | }); 43 | type Result = typeof Result.example; 44 | let result: Result = {date: '1234'}; 45 | if (result.foobar) result.foobar.toExponential; 46 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "types": ["mocha", "node"], 7 | "rootDir": "src", 8 | "outDir": "dist/commonjs", 9 | "sourceMap": true, 10 | "declaration": true, 11 | "esModuleInterop": true, 12 | 13 | // Static checks 14 | "strict": true, 15 | "allowUnreachableCode": false, 16 | "allowUnusedLabels": false, 17 | "noFallthroughCasesInSwitch": true, 18 | "noImplicitAny": true, 19 | "noImplicitReturns": true, 20 | "noImplicitThis": true, 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": true, 23 | "strictNullChecks": true 24 | }, 25 | "include": ["src/**/*.ts"] 26 | } 27 | --------------------------------------------------------------------------------