├── .eslintrc.json ├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── decs.d.ts ├── favicon.ico ├── index.html ├── index.js ├── package-lock.json ├── package.json ├── t.js └── test.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2020": true, 5 | "node": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "parserOptions": { 9 | "ecmaVersion": 11, 10 | "sourceType": "module" 11 | }, 12 | "rules": { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: cris691 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | typetests 2 | .*.swp 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (https://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # TypeScript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | # next.js build output 64 | .next 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Cris Stringfellow & The Dosyago Corporation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :icecream: [vanillatype](https://github.com/crislin2046/vanillatype) ![npm downloads](https://img.shields.io/npm/dt/jtype-system) ![version](https://img.shields.io/npm/v/jtype-system?label=%22%22) 2 | 3 | 4 | Lightweight run time types for vanilla JavaScript and Node.JS. 5 | 6 | 7 | ## A Users example from production 8 | 9 | *users.js:* 10 | ```javascript 11 | import {T} from 'vanillatype'; 12 | 13 | T.def('User', { 14 | _id: T`ID`, 15 | _owner: T`ID`, 16 | username: T`Username`, 17 | email: T`Email`, 18 | newEmail: T.maybe(T`Email`), 19 | salt: T`Integer`, 20 | passwordHash: T`Hash`, 21 | groups: T`GroupArray`, 22 | stripeCustomerID: T`String`, 23 | verified: T.maybe(T`Boolean`) 24 | }); 25 | 26 | export default function validate(user) { 27 | const errors = T.errors(T`User`, user); 28 | 29 | validateUsernameUniqueness(user, errors); 30 | 31 | return errors; 32 | } 33 | 34 | //... 35 | ``` 36 | 37 | *types.js:* 38 | ```javascript 39 | import {T} from 'vanillatype'; 40 | 41 | // regexes 42 | 43 | const UsernameRegExp = /^[a-zA-Z][a-zA-Z0-9]{4,16}$/ 44 | const EmailRegExp = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/; 45 | const HexRegExp = /^[a-f0-9]{8,100}$/i; 46 | 47 | // common types 48 | 49 | T.defOr('MaybeBoolean', T`Boolean`, T`None`); 50 | 51 | T.defOr('ID', T`String`, T`Number`); 52 | 53 | T.def('URL', null, { 54 | verify: i => { 55 | try { 56 | return new URL(i); 57 | } catch(e) { 58 | return false; 59 | } 60 | }, 61 | help: "A valid URL" 62 | }); 63 | 64 | // user field types 65 | 66 | T.defCollection('GroupArray', { 67 | container: T`Array`, 68 | member: T`String` 69 | }); 70 | 71 | T.def('Username', null, { 72 | verify: i => UsernameRegExp.test(i) && i.length < 200, 73 | help:"Alphanumeric between 5 and 16 characters" 74 | }); 75 | 76 | T.def('Email', null, { 77 | verify: i => EmailRegExp.test(i) && i.length < 200, 78 | help: "A valid email address" 79 | }); 80 | 81 | T.def('Hash', null, { 82 | verify: i => HexRegExp.test(i) && i.length < 200, 83 | help: "A hexadecimal hash value, between 8 and 100 characters" 84 | }); 85 | ``` 86 | 87 | Taken from [servedata/_schemas/users.js](https://github.com/cris691/servedata/blob/master/_schemas/users.js) and [servedata/types.js](https://github.com/cris691/servedata/blob/master/types.js) 88 | 89 | ## Features 90 | 91 | - built-in support for built-in types! 92 | - common patterns like **collection, or, maybe** and **enum** types 93 | - fully optional 94 | - simple literate syntax 95 | - custom help fields to add context to type errors 96 | - optional partial (subset) matching 97 | 98 | ## Function list 99 | - T.check 100 | - T.sub 101 | - T.verify 102 | - T.validate 103 | - T.partialMatch 104 | - T.defEnum 105 | - T.defSub 106 | - T.defTuple 107 | - T.defCollection 108 | - T.defOr 109 | - T.option 110 | - T.defOption 111 | - T.maybe 112 | - T.guard 113 | - T.errors 114 | - annotate a function to take and return types ([coming](https://github.com/cris691/vanillatype/issues/13)!) 115 | - built in specials: 116 | ```js 117 | function defineSpecials() { 118 | T.def(`Any`, null, {verify: () => true}); 119 | T.def(`Some`, null, {verify: i => !isUnset(i)}); 120 | T.def(`None`, null, {verify: i => isUnset(i)}); 121 | T.def(`Function`, null, {verify: i => i instanceof Function}); 122 | T.def(`Integer`, null, {verify: i => Number.isInteger(i)}); 123 | T.def(`Array`, null, {verify: i => Array.isArray(i)}); 124 | T.def(`Iterable`, null, {verify: i => i[Symbol.iterator] instanceof Function}); 125 | } 126 | ``` 127 | 128 | ## Another reason I like this 129 | 130 | Apart from writing it myself to suit my own work style, which is a great reason to like something, I like this because, if I want to change the syntax, or add some new feature that I want, it's really easy to change the library, which is only a couple hundred lines of code. If I wanted the same results from a third-party library, I'd have to wait. 131 | 132 | ## More examples 133 | 134 | For more comprehensive examples see [vanillatype's test file](https://github.com/cris691/vanillatype/blob/master/test.js). 135 | 136 | ## getting and incorporating 137 | 138 | You can use the template repo or import using the old name on npm (vanillatype wasn't available to me, it is now). 139 | 140 | We use ES modules. 141 | 142 | You can use in your client side code like: 143 | 144 | ```JavaScript 145 | import {T} from 'https://unpkg.com/jtype-system/t.js'; 146 | ``` 147 | 148 | While the tests are currently only written for client side, you can use in Node.js like so: 149 | 150 | ```shell 151 | $ npm i --save vanillatype 152 | ``` 153 | 154 | or using the old name 155 | 156 | ```shell 157 | $ npm i --save jtype-system 158 | ``` 159 | 160 | Then: 161 | 162 | ```JavaScript 163 | import {T} from 'jtype-system'; 164 | ``` 165 | 166 | But you'll need to be using [ESM](https://www.npmjs.com/package/esm) or ES Modules. 167 | 168 | 169 | ------------- 170 | 171 | ***VanillaType!*** 172 | -------------------------------------------------------------------------------- /decs.d.ts: -------------------------------------------------------------------------------- 1 | declare var T; 2 | declare var typeCache; 3 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/o0101/vanillatype/58c5e559531574cdf2bf6d83d4b499bd99473786/favicon.ico -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require = require("esm")(module) 2 | module.exports = require("./t.js") 3 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vanillatype", 3 | "version": "1.4.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.10.3", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.3.tgz", 10 | "integrity": "sha512-fDx9eNW0qz0WkUeqL6tXEXzVlPh6Y5aCDEZesl0xBGA8ndRukX91Uk44ZqnkECp01NAZUdCAl+aiQNGi0k88Eg==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.3" 14 | } 15 | }, 16 | "@babel/helper-validator-identifier": { 17 | "version": "7.10.3", 18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz", 19 | "integrity": "sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw==", 20 | "dev": true 21 | }, 22 | "@babel/highlight": { 23 | "version": "7.10.3", 24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.3.tgz", 25 | "integrity": "sha512-Ih9B/u7AtgEnySE2L2F0Xm0GaM729XqqLfHkalTsbjXGyqmf/6M0Cu0WpvqueUlW+xk88BHw9Nkpj49naU+vWw==", 26 | "dev": true, 27 | "requires": { 28 | "@babel/helper-validator-identifier": "^7.10.3", 29 | "chalk": "^2.0.0", 30 | "js-tokens": "^4.0.0" 31 | }, 32 | "dependencies": { 33 | "chalk": { 34 | "version": "2.4.2", 35 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 36 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 37 | "dev": true, 38 | "requires": { 39 | "ansi-styles": "^3.2.1", 40 | "escape-string-regexp": "^1.0.5", 41 | "supports-color": "^5.3.0" 42 | } 43 | } 44 | } 45 | }, 46 | "@types/color-name": { 47 | "version": "1.1.1", 48 | "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", 49 | "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", 50 | "dev": true 51 | }, 52 | "@types/node": { 53 | "version": "14.0.13", 54 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.13.tgz", 55 | "integrity": "sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA==", 56 | "dev": true 57 | }, 58 | "acorn": { 59 | "version": "7.3.1", 60 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", 61 | "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", 62 | "dev": true 63 | }, 64 | "acorn-jsx": { 65 | "version": "5.2.0", 66 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", 67 | "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", 68 | "dev": true 69 | }, 70 | "ajv": { 71 | "version": "6.12.2", 72 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", 73 | "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", 74 | "dev": true, 75 | "requires": { 76 | "fast-deep-equal": "^3.1.1", 77 | "fast-json-stable-stringify": "^2.0.0", 78 | "json-schema-traverse": "^0.4.1", 79 | "uri-js": "^4.2.2" 80 | } 81 | }, 82 | "ansi-colors": { 83 | "version": "3.2.4", 84 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", 85 | "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", 86 | "dev": true 87 | }, 88 | "ansi-regex": { 89 | "version": "5.0.0", 90 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 91 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 92 | "dev": true 93 | }, 94 | "ansi-styles": { 95 | "version": "3.2.1", 96 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 97 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 98 | "dev": true, 99 | "requires": { 100 | "color-convert": "^1.9.0" 101 | } 102 | }, 103 | "argparse": { 104 | "version": "1.0.10", 105 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 106 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 107 | "dev": true, 108 | "requires": { 109 | "sprintf-js": "~1.0.2" 110 | } 111 | }, 112 | "astral-regex": { 113 | "version": "1.0.0", 114 | "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", 115 | "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", 116 | "dev": true 117 | }, 118 | "balanced-match": { 119 | "version": "1.0.0", 120 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 121 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 122 | "dev": true 123 | }, 124 | "brace-expansion": { 125 | "version": "1.1.11", 126 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 127 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 128 | "dev": true, 129 | "requires": { 130 | "balanced-match": "^1.0.0", 131 | "concat-map": "0.0.1" 132 | } 133 | }, 134 | "callsites": { 135 | "version": "3.1.0", 136 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 137 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 138 | "dev": true 139 | }, 140 | "chalk": { 141 | "version": "4.1.0", 142 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", 143 | "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", 144 | "dev": true, 145 | "requires": { 146 | "ansi-styles": "^4.1.0", 147 | "supports-color": "^7.1.0" 148 | }, 149 | "dependencies": { 150 | "ansi-styles": { 151 | "version": "4.2.1", 152 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", 153 | "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", 154 | "dev": true, 155 | "requires": { 156 | "@types/color-name": "^1.1.1", 157 | "color-convert": "^2.0.1" 158 | } 159 | }, 160 | "color-convert": { 161 | "version": "2.0.1", 162 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 163 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 164 | "dev": true, 165 | "requires": { 166 | "color-name": "~1.1.4" 167 | } 168 | }, 169 | "color-name": { 170 | "version": "1.1.4", 171 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 172 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 173 | "dev": true 174 | }, 175 | "has-flag": { 176 | "version": "4.0.0", 177 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 178 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 179 | "dev": true 180 | }, 181 | "supports-color": { 182 | "version": "7.1.0", 183 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", 184 | "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", 185 | "dev": true, 186 | "requires": { 187 | "has-flag": "^4.0.0" 188 | } 189 | } 190 | } 191 | }, 192 | "color-convert": { 193 | "version": "1.9.3", 194 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 195 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 196 | "dev": true, 197 | "requires": { 198 | "color-name": "1.1.3" 199 | } 200 | }, 201 | "color-name": { 202 | "version": "1.1.3", 203 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 204 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 205 | "dev": true 206 | }, 207 | "concat-map": { 208 | "version": "0.0.1", 209 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 210 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 211 | "dev": true 212 | }, 213 | "cross-spawn": { 214 | "version": "7.0.3", 215 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 216 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 217 | "dev": true, 218 | "requires": { 219 | "path-key": "^3.1.0", 220 | "shebang-command": "^2.0.0", 221 | "which": "^2.0.1" 222 | } 223 | }, 224 | "debug": { 225 | "version": "4.1.1", 226 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 227 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 228 | "dev": true, 229 | "requires": { 230 | "ms": "^2.1.1" 231 | } 232 | }, 233 | "deep-is": { 234 | "version": "0.1.3", 235 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 236 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 237 | "dev": true 238 | }, 239 | "doctrine": { 240 | "version": "3.0.0", 241 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 242 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 243 | "dev": true, 244 | "requires": { 245 | "esutils": "^2.0.2" 246 | } 247 | }, 248 | "emoji-regex": { 249 | "version": "7.0.3", 250 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 251 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 252 | "dev": true 253 | }, 254 | "enquirer": { 255 | "version": "2.3.5", 256 | "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.5.tgz", 257 | "integrity": "sha512-BNT1C08P9XD0vNg3J475yIUG+mVdp9T6towYFHUv897X0KoHBjB1shyrNmhmtHWKP17iSWgo7Gqh7BBuzLZMSA==", 258 | "dev": true, 259 | "requires": { 260 | "ansi-colors": "^3.2.1" 261 | } 262 | }, 263 | "escape-string-regexp": { 264 | "version": "1.0.5", 265 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 266 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 267 | "dev": true 268 | }, 269 | "eslint": { 270 | "version": "7.3.0", 271 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.3.0.tgz", 272 | "integrity": "sha512-dJMVXwfU5PT1cj2Nv2VPPrKahKTGdX+5Dh0Q3YuKt+Y2UhdL2YbzsVaBMyG9HC0tBismlv/r1+eZqs6SMIV38Q==", 273 | "dev": true, 274 | "requires": { 275 | "@babel/code-frame": "^7.0.0", 276 | "ajv": "^6.10.0", 277 | "chalk": "^4.0.0", 278 | "cross-spawn": "^7.0.2", 279 | "debug": "^4.0.1", 280 | "doctrine": "^3.0.0", 281 | "enquirer": "^2.3.5", 282 | "eslint-scope": "^5.1.0", 283 | "eslint-utils": "^2.0.0", 284 | "eslint-visitor-keys": "^1.2.0", 285 | "espree": "^7.1.0", 286 | "esquery": "^1.2.0", 287 | "esutils": "^2.0.2", 288 | "file-entry-cache": "^5.0.1", 289 | "functional-red-black-tree": "^1.0.1", 290 | "glob-parent": "^5.0.0", 291 | "globals": "^12.1.0", 292 | "ignore": "^4.0.6", 293 | "import-fresh": "^3.0.0", 294 | "imurmurhash": "^0.1.4", 295 | "is-glob": "^4.0.0", 296 | "js-yaml": "^3.13.1", 297 | "json-stable-stringify-without-jsonify": "^1.0.1", 298 | "levn": "^0.4.1", 299 | "lodash": "^4.17.14", 300 | "minimatch": "^3.0.4", 301 | "natural-compare": "^1.4.0", 302 | "optionator": "^0.9.1", 303 | "progress": "^2.0.0", 304 | "regexpp": "^3.1.0", 305 | "semver": "^7.2.1", 306 | "strip-ansi": "^6.0.0", 307 | "strip-json-comments": "^3.1.0", 308 | "table": "^5.2.3", 309 | "text-table": "^0.2.0", 310 | "v8-compile-cache": "^2.0.3" 311 | } 312 | }, 313 | "eslint-scope": { 314 | "version": "5.1.0", 315 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", 316 | "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", 317 | "dev": true, 318 | "requires": { 319 | "esrecurse": "^4.1.0", 320 | "estraverse": "^4.1.1" 321 | } 322 | }, 323 | "eslint-utils": { 324 | "version": "2.1.0", 325 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", 326 | "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", 327 | "dev": true, 328 | "requires": { 329 | "eslint-visitor-keys": "^1.1.0" 330 | } 331 | }, 332 | "eslint-visitor-keys": { 333 | "version": "1.3.0", 334 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 335 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", 336 | "dev": true 337 | }, 338 | "esm": { 339 | "version": "3.1.0", 340 | "resolved": "https://registry.npmjs.org/esm/-/esm-3.1.0.tgz", 341 | "integrity": "sha512-r4Go7Wh7Wh0WPinRXeeM9PIajRsUdt8SAyki5R1obVc0+BwtqvtjbngVSSdXg0jCe2xZkY8hyBMx6q/uymUkPw==" 342 | }, 343 | "espree": { 344 | "version": "7.1.0", 345 | "resolved": "https://registry.npmjs.org/espree/-/espree-7.1.0.tgz", 346 | "integrity": "sha512-dcorZSyfmm4WTuTnE5Y7MEN1DyoPYy1ZR783QW1FJoenn7RailyWFsq/UL6ZAAA7uXurN9FIpYyUs3OfiIW+Qw==", 347 | "dev": true, 348 | "requires": { 349 | "acorn": "^7.2.0", 350 | "acorn-jsx": "^5.2.0", 351 | "eslint-visitor-keys": "^1.2.0" 352 | } 353 | }, 354 | "esprima": { 355 | "version": "4.0.1", 356 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 357 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 358 | "dev": true 359 | }, 360 | "esquery": { 361 | "version": "1.3.1", 362 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", 363 | "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", 364 | "dev": true, 365 | "requires": { 366 | "estraverse": "^5.1.0" 367 | }, 368 | "dependencies": { 369 | "estraverse": { 370 | "version": "5.1.0", 371 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", 372 | "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", 373 | "dev": true 374 | } 375 | } 376 | }, 377 | "esrecurse": { 378 | "version": "4.2.1", 379 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", 380 | "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", 381 | "dev": true, 382 | "requires": { 383 | "estraverse": "^4.1.0" 384 | } 385 | }, 386 | "estraverse": { 387 | "version": "4.3.0", 388 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 389 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 390 | "dev": true 391 | }, 392 | "esutils": { 393 | "version": "2.0.3", 394 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 395 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 396 | "dev": true 397 | }, 398 | "fast-deep-equal": { 399 | "version": "3.1.3", 400 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 401 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 402 | "dev": true 403 | }, 404 | "fast-json-stable-stringify": { 405 | "version": "2.1.0", 406 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 407 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 408 | "dev": true 409 | }, 410 | "fast-levenshtein": { 411 | "version": "2.0.6", 412 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 413 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 414 | "dev": true 415 | }, 416 | "file-entry-cache": { 417 | "version": "5.0.1", 418 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", 419 | "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", 420 | "dev": true, 421 | "requires": { 422 | "flat-cache": "^2.0.1" 423 | } 424 | }, 425 | "flat-cache": { 426 | "version": "2.0.1", 427 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", 428 | "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", 429 | "dev": true, 430 | "requires": { 431 | "flatted": "^2.0.0", 432 | "rimraf": "2.6.3", 433 | "write": "1.0.3" 434 | } 435 | }, 436 | "flatted": { 437 | "version": "2.0.2", 438 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", 439 | "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", 440 | "dev": true 441 | }, 442 | "fs.realpath": { 443 | "version": "1.0.0", 444 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 445 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 446 | "dev": true 447 | }, 448 | "functional-red-black-tree": { 449 | "version": "1.0.1", 450 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 451 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 452 | "dev": true 453 | }, 454 | "glob": { 455 | "version": "7.1.6", 456 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 457 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 458 | "dev": true, 459 | "requires": { 460 | "fs.realpath": "^1.0.0", 461 | "inflight": "^1.0.4", 462 | "inherits": "2", 463 | "minimatch": "^3.0.4", 464 | "once": "^1.3.0", 465 | "path-is-absolute": "^1.0.0" 466 | } 467 | }, 468 | "glob-parent": { 469 | "version": "5.1.1", 470 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", 471 | "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", 472 | "dev": true, 473 | "requires": { 474 | "is-glob": "^4.0.1" 475 | } 476 | }, 477 | "globals": { 478 | "version": "12.4.0", 479 | "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", 480 | "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", 481 | "dev": true, 482 | "requires": { 483 | "type-fest": "^0.8.1" 484 | } 485 | }, 486 | "has-flag": { 487 | "version": "3.0.0", 488 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 489 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 490 | "dev": true 491 | }, 492 | "ignore": { 493 | "version": "4.0.6", 494 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 495 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 496 | "dev": true 497 | }, 498 | "import-fresh": { 499 | "version": "3.2.1", 500 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", 501 | "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", 502 | "dev": true, 503 | "requires": { 504 | "parent-module": "^1.0.0", 505 | "resolve-from": "^4.0.0" 506 | } 507 | }, 508 | "imurmurhash": { 509 | "version": "0.1.4", 510 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 511 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 512 | "dev": true 513 | }, 514 | "inflight": { 515 | "version": "1.0.6", 516 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 517 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 518 | "dev": true, 519 | "requires": { 520 | "once": "^1.3.0", 521 | "wrappy": "1" 522 | } 523 | }, 524 | "inherits": { 525 | "version": "2.0.4", 526 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 527 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 528 | "dev": true 529 | }, 530 | "is-extglob": { 531 | "version": "2.1.1", 532 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 533 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 534 | "dev": true 535 | }, 536 | "is-fullwidth-code-point": { 537 | "version": "2.0.0", 538 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 539 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 540 | "dev": true 541 | }, 542 | "is-glob": { 543 | "version": "4.0.1", 544 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 545 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 546 | "dev": true, 547 | "requires": { 548 | "is-extglob": "^2.1.1" 549 | } 550 | }, 551 | "isexe": { 552 | "version": "2.0.0", 553 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 554 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 555 | "dev": true 556 | }, 557 | "js-tokens": { 558 | "version": "4.0.0", 559 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 560 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 561 | "dev": true 562 | }, 563 | "js-yaml": { 564 | "version": "3.14.0", 565 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", 566 | "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", 567 | "dev": true, 568 | "requires": { 569 | "argparse": "^1.0.7", 570 | "esprima": "^4.0.0" 571 | } 572 | }, 573 | "json-schema-traverse": { 574 | "version": "0.4.1", 575 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 576 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 577 | "dev": true 578 | }, 579 | "json-stable-stringify-without-jsonify": { 580 | "version": "1.0.1", 581 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 582 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 583 | "dev": true 584 | }, 585 | "levn": { 586 | "version": "0.4.1", 587 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 588 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 589 | "dev": true, 590 | "requires": { 591 | "prelude-ls": "^1.2.1", 592 | "type-check": "~0.4.0" 593 | } 594 | }, 595 | "lodash": { 596 | "version": "4.17.19", 597 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", 598 | "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", 599 | "dev": true 600 | }, 601 | "minimatch": { 602 | "version": "3.0.4", 603 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 604 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 605 | "dev": true, 606 | "requires": { 607 | "brace-expansion": "^1.1.7" 608 | } 609 | }, 610 | "minimist": { 611 | "version": "1.2.5", 612 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 613 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 614 | "dev": true 615 | }, 616 | "mkdirp": { 617 | "version": "0.5.5", 618 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 619 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 620 | "dev": true, 621 | "requires": { 622 | "minimist": "^1.2.5" 623 | } 624 | }, 625 | "ms": { 626 | "version": "2.1.2", 627 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 628 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 629 | "dev": true 630 | }, 631 | "natural-compare": { 632 | "version": "1.4.0", 633 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 634 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 635 | "dev": true 636 | }, 637 | "once": { 638 | "version": "1.4.0", 639 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 640 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 641 | "dev": true, 642 | "requires": { 643 | "wrappy": "1" 644 | } 645 | }, 646 | "optionator": { 647 | "version": "0.9.1", 648 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", 649 | "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", 650 | "dev": true, 651 | "requires": { 652 | "deep-is": "^0.1.3", 653 | "fast-levenshtein": "^2.0.6", 654 | "levn": "^0.4.1", 655 | "prelude-ls": "^1.2.1", 656 | "type-check": "^0.4.0", 657 | "word-wrap": "^1.2.3" 658 | } 659 | }, 660 | "parent-module": { 661 | "version": "1.0.1", 662 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 663 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 664 | "dev": true, 665 | "requires": { 666 | "callsites": "^3.0.0" 667 | } 668 | }, 669 | "path-is-absolute": { 670 | "version": "1.0.1", 671 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 672 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 673 | "dev": true 674 | }, 675 | "path-key": { 676 | "version": "3.1.1", 677 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 678 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 679 | "dev": true 680 | }, 681 | "prelude-ls": { 682 | "version": "1.2.1", 683 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 684 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 685 | "dev": true 686 | }, 687 | "progress": { 688 | "version": "2.0.3", 689 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 690 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", 691 | "dev": true 692 | }, 693 | "punycode": { 694 | "version": "2.1.1", 695 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 696 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 697 | "dev": true 698 | }, 699 | "regexpp": { 700 | "version": "3.1.0", 701 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", 702 | "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", 703 | "dev": true 704 | }, 705 | "resolve-from": { 706 | "version": "4.0.0", 707 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 708 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 709 | "dev": true 710 | }, 711 | "rimraf": { 712 | "version": "2.6.3", 713 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", 714 | "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", 715 | "dev": true, 716 | "requires": { 717 | "glob": "^7.1.3" 718 | } 719 | }, 720 | "semver": { 721 | "version": "7.3.2", 722 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", 723 | "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", 724 | "dev": true 725 | }, 726 | "shebang-command": { 727 | "version": "2.0.0", 728 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 729 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 730 | "dev": true, 731 | "requires": { 732 | "shebang-regex": "^3.0.0" 733 | } 734 | }, 735 | "shebang-regex": { 736 | "version": "3.0.0", 737 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 738 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 739 | "dev": true 740 | }, 741 | "slice-ansi": { 742 | "version": "2.1.0", 743 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", 744 | "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", 745 | "dev": true, 746 | "requires": { 747 | "ansi-styles": "^3.2.0", 748 | "astral-regex": "^1.0.0", 749 | "is-fullwidth-code-point": "^2.0.0" 750 | } 751 | }, 752 | "sprintf-js": { 753 | "version": "1.0.3", 754 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 755 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 756 | "dev": true 757 | }, 758 | "string-width": { 759 | "version": "3.1.0", 760 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 761 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 762 | "dev": true, 763 | "requires": { 764 | "emoji-regex": "^7.0.1", 765 | "is-fullwidth-code-point": "^2.0.0", 766 | "strip-ansi": "^5.1.0" 767 | }, 768 | "dependencies": { 769 | "ansi-regex": { 770 | "version": "4.1.0", 771 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 772 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 773 | "dev": true 774 | }, 775 | "strip-ansi": { 776 | "version": "5.2.0", 777 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 778 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 779 | "dev": true, 780 | "requires": { 781 | "ansi-regex": "^4.1.0" 782 | } 783 | } 784 | } 785 | }, 786 | "strip-ansi": { 787 | "version": "6.0.0", 788 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 789 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 790 | "dev": true, 791 | "requires": { 792 | "ansi-regex": "^5.0.0" 793 | } 794 | }, 795 | "strip-json-comments": { 796 | "version": "3.1.0", 797 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", 798 | "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", 799 | "dev": true 800 | }, 801 | "supports-color": { 802 | "version": "5.5.0", 803 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 804 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 805 | "dev": true, 806 | "requires": { 807 | "has-flag": "^3.0.0" 808 | } 809 | }, 810 | "table": { 811 | "version": "5.4.6", 812 | "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", 813 | "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", 814 | "dev": true, 815 | "requires": { 816 | "ajv": "^6.10.2", 817 | "lodash": "^4.17.14", 818 | "slice-ansi": "^2.1.0", 819 | "string-width": "^3.0.0" 820 | } 821 | }, 822 | "text-table": { 823 | "version": "0.2.0", 824 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 825 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 826 | "dev": true 827 | }, 828 | "type-check": { 829 | "version": "0.4.0", 830 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 831 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 832 | "dev": true, 833 | "requires": { 834 | "prelude-ls": "^1.2.1" 835 | } 836 | }, 837 | "type-fest": { 838 | "version": "0.8.1", 839 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", 840 | "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", 841 | "dev": true 842 | }, 843 | "typescript": { 844 | "version": "3.9.5", 845 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.5.tgz", 846 | "integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==", 847 | "dev": true 848 | }, 849 | "uri-js": { 850 | "version": "4.2.2", 851 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 852 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 853 | "dev": true, 854 | "requires": { 855 | "punycode": "^2.1.0" 856 | } 857 | }, 858 | "v8-compile-cache": { 859 | "version": "2.1.1", 860 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", 861 | "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", 862 | "dev": true 863 | }, 864 | "which": { 865 | "version": "2.0.2", 866 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 867 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 868 | "dev": true, 869 | "requires": { 870 | "isexe": "^2.0.0" 871 | } 872 | }, 873 | "word-wrap": { 874 | "version": "1.2.3", 875 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 876 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", 877 | "dev": true 878 | }, 879 | "wrappy": { 880 | "version": "1.0.2", 881 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 882 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 883 | "dev": true 884 | }, 885 | "write": { 886 | "version": "1.0.3", 887 | "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", 888 | "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", 889 | "dev": true, 890 | "requires": { 891 | "mkdirp": "^0.5.1" 892 | } 893 | } 894 | } 895 | } 896 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": { 3 | "name": "@dosy" 4 | }, 5 | "bundleDependencies": [], 6 | "dependencies": { 7 | "esm": "^3.0.74" 8 | }, 9 | "deprecated": false, 10 | "description": "Lightweight runtime types for vanilla JS and Node", 11 | "keywords": [ 12 | "javascript", 13 | "type-system", 14 | "vanillatype", 15 | "jtype", 16 | "dosy" 17 | ], 18 | "license": "MIT", 19 | "main": "index.js", 20 | "name": "vanillatype", 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com//crislin2046/jtype" 24 | }, 25 | "scripts": { 26 | "test": "serve -p 8080", 27 | "tscheck-client": "tsc --types node --target ES2020 --checkJs --allowJs --outFile typetests/client.js --module System t.js test.js decs.d.ts", 28 | "tscheck": "npm run tscheck-client", 29 | "lint": "npx eslint t.js test.js", 30 | "check": "npm run lint && npm run tscheck" 31 | }, 32 | "version": "1.4.1", 33 | "devDependencies": { 34 | "@types/node": "^14.0.13", 35 | "eslint": "^7.3.0", 36 | "typescript": "^3.9.5" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /t.js: -------------------------------------------------------------------------------- 1 | 2 | export const BROWSER_SIDE = (() => {try{ return self.DOMParser && true; } catch(e) { return false; }})(); 3 | 4 | const BuiltIns = [ 5 | Symbol, Boolean, Number, String, Object, Set, Map, WeakMap, WeakSet, 6 | Uint8Array, Uint16Array, Uint32Array, Float32Array, Float64Array, 7 | Int8Array, Int16Array, Int32Array, 8 | Uint8ClampedArray, 9 | ...(BROWSER_SIDE ? [ 10 | Node,NodeList,Element,HTMLElement, Blob, ArrayBuffer, 11 | FileList, Text, HTMLDocument, Document, DocumentFragment, 12 | Error, File, Event, EventTarget, URL 13 | /* eslint-disable no-undef */ 14 | ] : [ Buffer ]) 15 | /* eslint-enable no-undef */ 16 | ] 17 | 18 | const DEBUG = false; 19 | const SEALED_DEFAULT = true; 20 | const isNone = instance => instance == null || instance == undefined; 21 | 22 | const typeCache = new Map(); 23 | 24 | T.def = def; 25 | T.check = check; 26 | T.sub = sub; 27 | T.verify = verify; 28 | T.validate = validate; 29 | T.partialMatch = partialMatch; 30 | T.defEnum = defEnum; 31 | T.defSub = defSub; 32 | T.defTuple = defTuple; 33 | T.defCollection = defCollection; 34 | T.defOr = defOr; 35 | T.option = option; 36 | T.defOption = defOption; 37 | T.maybe = maybe; 38 | T.guard = guard; 39 | T.errors = errors; 40 | 41 | // debug 42 | if ( DEBUG ) { 43 | self.T = T; 44 | self.typeCache = typeCache; 45 | } 46 | 47 | T[Symbol.for('jtype-system.typeCache')] = typeCache; 48 | 49 | defineSpecials(); 50 | mapBuiltins(); 51 | 52 | export function T(parts, ...vals) { 53 | const cooked = vals.reduce((prev,cur,i) => prev+cur+parts[i+1], parts[0]); 54 | const typeName = cooked; 55 | if ( !typeCache.has(typeName) ) throw new TypeError(`Cannot use type ${typeName} before it is defined.`); 56 | return typeCache.get(typeName).type; 57 | } 58 | 59 | function partialMatch(type, instance) { 60 | return validate(type, instance, {partial:true}); 61 | } 62 | 63 | function validate(type, instance, {partial: partial = false} = {}) { 64 | guardType(type); 65 | guardExists(type); 66 | const typeName = type.name; 67 | 68 | const {spec,kind,help,verify,verifiers,sealed} = typeCache.get(typeName); 69 | 70 | const specKeyPaths = spec ? allKeyPaths(spec).sort() : []; 71 | const specKeyPathSet = new Set(specKeyPaths); 72 | 73 | const bigErrors = []; 74 | 75 | switch(kind) { 76 | case "def": { 77 | let allValid = true; 78 | if ( spec ) { 79 | const keyPaths = partial ? allKeyPaths(instance, specKeyPathSet) : specKeyPaths; 80 | allValid = !isNone(instance) && keyPaths.every(kp => { 81 | // Allow lookup errors if the type match for the key path can include None 82 | 83 | const {resolved, errors:lookupErrors} = lookup(instance,kp,() => checkTypeMatch(lookup(spec,kp).resolved, T`None`)); 84 | bigErrors.push(...lookupErrors); 85 | 86 | if ( lookupErrors.length ) return false; 87 | 88 | const keyType = lookup(spec,kp).resolved; 89 | if ( !keyType || !(keyType instanceof Type) ) { 90 | bigErrors.push({ 91 | error: `Key path '${kp}' is not present in the spec for type '${typeName}'` 92 | }); 93 | return false; 94 | } 95 | 96 | const {valid, errors: validationErrors} = validate(keyType, resolved); 97 | bigErrors.push(...validationErrors); 98 | 99 | return valid; 100 | }); 101 | } 102 | let verified = true; 103 | if ( partial && ! spec && !!verify ) { 104 | throw new TypeError(`Type checking with option 'partial' is not a valid option for types that` + 105 | ` only use a verify function but have no spec`); 106 | } else if ( verify ) { 107 | try { 108 | verified = verify(instance); 109 | if ( ! verified ) { 110 | if ( verifiers ) { 111 | throw { 112 | error:`Type ${typeName} value '${JSON.stringify(instance)}' violated at least 1 verify function in:\n${ 113 | verifiers.map(f => '\t'+(f.help||'') + ' ('+f.verify.toString()+')').join('\n') 114 | }` 115 | }; 116 | } else if ( type.isSumType ) { 117 | throw { 118 | error: `Value '${JSON.stringify(instance)}' did not match any of: ${[...type.types.keys()].map(t => t.name)}`, 119 | verify, verifiers 120 | } 121 | } else { 122 | let helpMsg = ''; 123 | if ( help ) { 124 | helpMsg = `Help: ${help}. `; 125 | } 126 | throw {error:`${helpMsg}Type ${typeName} Value '${JSON.stringify(instance)}' violated verify function in: ${verify.toString()}`}; 127 | } 128 | } 129 | } catch(e) { 130 | bigErrors.push(e); 131 | verified = false; 132 | } 133 | } 134 | let sealValid = true; 135 | if ( !!sealed && !! spec ) { 136 | const type_key_paths = specKeyPaths; 137 | const all_key_paths = allKeyPaths(instance, specKeyPathSet).sort(); 138 | sealValid = all_key_paths.join(',') == type_key_paths.join(','); 139 | if ( ! sealValid ) { 140 | if ( all_key_paths.length < type_key_paths.length ) { 141 | sealValid = true; 142 | } else { 143 | const errorKeys = []; 144 | const tkp = new Set(type_key_paths); 145 | for( const k of all_key_paths ) { 146 | if ( ! tkp.has(k) ) { 147 | errorKeys.push({ 148 | error: `Key path '${k}' is not in the spec for type ${typeName}` 149 | }); 150 | } 151 | } 152 | if ( errorKeys.length ) { 153 | bigErrors.push(...errorKeys); 154 | } 155 | } 156 | } 157 | } 158 | return {valid: allValid && verified && sealValid, errors: bigErrors, partial} 159 | } case "defCollection": { 160 | const {valid:containerValid, errors:containerErrors} = validate(spec.container, instance); 161 | let membersValid = true; 162 | let verified = true; 163 | 164 | bigErrors.push(...containerErrors); 165 | if ( partial ) { 166 | throw new TypeError(`Type checking with option 'partial' is not a valid option for Collection types`); 167 | } else { 168 | if ( containerValid ) { 169 | membersValid= [...instance].every(member => { 170 | const {valid, errors} = validate(spec.member, member); 171 | bigErrors.push(...errors); 172 | return valid; 173 | }); 174 | } 175 | if ( verify ) { 176 | try { 177 | verified = verify(instance); 178 | } catch(e) { 179 | bigErrors.push(e); 180 | verified = false; 181 | } 182 | } 183 | } 184 | 185 | return {valid:containerValid && membersValid && verified, errors:bigErrors}; 186 | } default: { 187 | throw new TypeError(`Checking for type kind ${kind} is not yet implemented.`); 188 | } 189 | } 190 | } 191 | 192 | function check(...args) { 193 | return validate(...args).valid; 194 | } 195 | 196 | function lookup(obj, keyPath, canBeNone) { 197 | if ( isNone(obj) ) throw new TypeError(`Lookup requires a non-unset object.`); 198 | 199 | if ( !keyPath ) throw new TypeError(`keyPath must not be empty`); 200 | 201 | 202 | const keys = keyPath.split(/\./g); 203 | const pathComplete = []; 204 | const errors = []; 205 | 206 | let resolved = obj; 207 | 208 | while(keys.length) { 209 | const nextKey = keys.shift(); 210 | resolved = resolved[nextKey]; 211 | pathComplete.push(nextKey); 212 | if ( (resolved === null || resolved === undefined) ) { 213 | if ( keys.length ) { 214 | errors.push({ 215 | error: 216 | `Lookup on key path '${keyPath}' failed at '` + 217 | pathComplete.join('.') + 218 | `' when ${resolved} was found at '${nextKey}'.` 219 | }); 220 | } else if ( !!canBeNone && canBeNone() ) { 221 | resolved = undefined; 222 | } else { 223 | errors.push({ 224 | error: 225 | `Resolution on key path '${keyPath}' failed` + 226 | `when ${resolved} was found at '${nextKey}' and the Type of this` + 227 | `key's value cannot be None.` 228 | }); 229 | } 230 | break; 231 | } 232 | } 233 | return {resolved,errors}; 234 | } 235 | 236 | function checkTypeMatch(typeA, typeB) { 237 | guardType(typeA); 238 | guardExists(typeA); 239 | guardType(typeB); 240 | guardExists(typeB); 241 | 242 | if ( typeA === typeB ) { 243 | return true; 244 | } else if ( typeA.isSumType && typeA.types.has(typeB) ) { 245 | return true; 246 | } else if ( typeB.isSumType && typeB.types.has(typeA) ) { 247 | return true; 248 | } else if ( typeA.name.startsWith('?') && typeB == T`None` ) { 249 | return true; 250 | } else if ( typeB.name.startsWith('?') && typeA == T`None` ) { 251 | return true; 252 | } 253 | 254 | if ( typeA.name.startsWith('>') || typeB.name.startsWith('>') ) { 255 | console.error(new Error(`Check type match has not been implemented for derived//sub types yet.`)); 256 | } 257 | 258 | return false; 259 | } 260 | 261 | function option(type) { 262 | return T`?${type.name}`; 263 | } 264 | 265 | function sub(type) { 266 | return T`>${type.name}`; 267 | } 268 | 269 | function defSub(type, spec, {verify: verify = undefined, help:help = ''} = {}, name = '') { 270 | guardType(type); 271 | guardExists(type); 272 | 273 | let verifiers; 274 | 275 | if ( ! verify ) { 276 | verify = () => true; 277 | } 278 | 279 | if ( type.native ) { 280 | verifiers = [ {help,verify} ]; 281 | verify = i => i instanceof type.native; 282 | const helpMsg = `Needs to be of type ${type.native.name}. ${help||''}`; 283 | verifiers.push({help:helpMsg,verify}); 284 | } 285 | 286 | const newType = def(`${name}>${type.name}`, spec, {verify,help, verifiers}); 287 | return newType; 288 | } 289 | 290 | function defEnum(name, ...values) { 291 | if ( !name ) throw new TypeError(`Type must be named.`); 292 | guardRedefinition(name); 293 | 294 | const valueSet = new Set(values); 295 | const verify = i => valueSet.has(i); 296 | const help = `Value of Enum type ${name} must be one of ${values.join(',')}`; 297 | 298 | return def(name, null, {verify,help}); 299 | } 300 | 301 | function exists(name) { 302 | return typeCache.has(name); 303 | } 304 | 305 | function guardRedefinition(name) { 306 | if ( exists(name) ) throw new TypeError(`Type ${name} cannot be redefined.`); 307 | } 308 | 309 | function allKeyPaths(o, specKeyPaths) { 310 | const isTypeSpec = ! specKeyPaths; 311 | const keyPaths = new Set(); 312 | return recurseObject(o, keyPaths, ''); 313 | 314 | function recurseObject(o, keyPathSet, lastLevel = '') { 315 | const levelKeys = Object.getOwnPropertyNames(o); 316 | const keyPaths = levelKeys 317 | .map(k => lastLevel + (lastLevel.length ? '.' : '') + k) 318 | levelKeys.forEach((k,i) => { 319 | const v = o[k]; 320 | if ( isTypeSpec ) { 321 | if ( v instanceof Type ) { 322 | keyPathSet.add(keyPaths[i]); 323 | } else if ( typeof v == "object" ) { 324 | if ( ! Array.isArray(v) ) { 325 | recurseObject(v, keyPathSet, keyPaths[i]); 326 | } else { 327 | DEBUG && console.warn({o,v,keyPathSet, lastLevel}); 328 | throw new TypeError(`We don't support Types that use Arrays as structure, just yet.`); 329 | } 330 | } else { 331 | throw new TypeError(`Spec cannot contain leaf values that are not valid Types`); 332 | } 333 | } else { 334 | if ( specKeyPaths.has(keyPaths[i]) ) { 335 | keyPathSet.add(keyPaths[i]); 336 | } else if ( typeof v == "object" ) { 337 | if ( ! Array.isArray(v) ) { 338 | recurseObject(v, keyPathSet, keyPaths[i]); 339 | } else { 340 | v.forEach((item,index) => recurseObject(item, keyPathSet, keyPaths[i] + '.' + index)); 341 | //throw new TypeError(`We don't support Instances that use Arrays as structure, just yet.`); 342 | } 343 | } else { 344 | //console.warn("Spec has no such key", keyPaths[i]); 345 | keyPathSet.add(keyPaths[i]); 346 | } 347 | } 348 | }); 349 | return [...keyPathSet]; 350 | } 351 | } 352 | 353 | function defOption(type) { 354 | guardType(type); 355 | const typeName = type.name; 356 | return T.def(`?${typeName}`, null, {verify: i => isUnset(i) || T.check(type,i)}); 357 | } 358 | 359 | function maybe(type) { 360 | try { 361 | return defOption(type); 362 | } catch(e) { 363 | // console.log(`Option Type ${type.name} already declared.`, e); 364 | } 365 | return T`?${type.name}`; 366 | } 367 | 368 | function verify(...args) { return check(...args); } 369 | 370 | function defCollection(name, {container, member}, {sealed: sealed = SEALED_DEFAULT, verify: verify = undefined} = {}) { 371 | if ( !name ) throw new TypeError(`Type must be named.`); 372 | if ( !container || !member ) throw new TypeError(`Type must be specified.`); 373 | guardRedefinition(name); 374 | 375 | const kind = 'defCollection'; 376 | const t = new Type(name); 377 | const spec = {kind, spec: { container, member}, verify, sealed, type: t}; 378 | typeCache.set(name, spec); 379 | return t; 380 | } 381 | 382 | function defTuple(name, {pattern}) { 383 | if ( !name ) throw new TypeError(`Type must be named.`); 384 | if ( !pattern ) throw new TypeError(`Type must be specified.`); 385 | const kind = 'def'; 386 | const specObj = {}; 387 | pattern.forEach((type,key) => specObj[key] = type); 388 | const t = new Type(name); 389 | const spec = {kind, spec: specObj, type:t}; 390 | typeCache.set(name, spec); 391 | return t; 392 | } 393 | 394 | function Type(name, mods = {}) { 395 | if ( ! new.target ) throw new TypeError(`Type with new only.`); 396 | Object.defineProperty(this,'name', {get: () => name}); 397 | this.typeName = name; 398 | 399 | if ( mods.types ) { 400 | const {types} = mods; 401 | const typeSet = new Set(types); 402 | Object.defineProperty(this,'isSumType', {get: () => true}); 403 | Object.defineProperty(this,'types', {get: () => typeSet}); 404 | } 405 | 406 | if ( mods.native ) { 407 | const {native} = mods; 408 | Object.defineProperty(this,'native', {get: () => native}); 409 | } 410 | } 411 | 412 | Type.prototype.toString = function () { 413 | return `${this.typeName} Type`; 414 | }; 415 | 416 | function def(name, spec, {help:help = '', verify:verify = undefined, sealed:sealed = undefined, types:types = undefined, verifiers:verifiers = undefined, native:native = undefined} = {}) { 417 | if ( !name ) throw new TypeError(`Type must be named.`); 418 | guardRedefinition(name); 419 | 420 | if ( name.startsWith('?') ) { 421 | if ( spec ) { 422 | throw new TypeError(`Option type can not have a spec.`); 423 | } 424 | 425 | if ( ! verify(null) ) { 426 | throw new TypeError(`Option type must be OK to be unset.`); 427 | } 428 | } 429 | 430 | const kind = 'def'; 431 | if ( sealed === undefined ) { 432 | sealed = true; 433 | } 434 | const t = new Type(name, {types, native}); 435 | const cache = {spec,kind,help,verify,verifiers,sealed,types,native,type:t}; 436 | typeCache.set(name, cache); 437 | return t; 438 | } 439 | 440 | function defOr(name, ...types) { 441 | return T.def(name, null, {types, verify: i => types.some(t => check(t,i))}); 442 | } 443 | 444 | function guard(type, instance) { 445 | guardType(type); 446 | guardExists(type); 447 | const {valid, errors} = validate(type, instance); 448 | if ( ! valid ) throw new TypeError(`Type ${type} requested, but item is not of that type: ${errors.join(', ')}`); 449 | } 450 | 451 | function guardType(t) { 452 | //console.log(t); 453 | if ( !(t instanceof Type) ) throw new TypeError(`Type must be a valid Type object.`); 454 | } 455 | 456 | function guardExists(t) { 457 | const name = originalName(t); 458 | if ( ! exists(name) ) throw new TypeError(`Type must exist. Type ${name} has not been defined.`); 459 | } 460 | 461 | function errors(...args) { 462 | return validate(...args).errors; 463 | } 464 | 465 | function mapBuiltins() { 466 | BuiltIns.forEach(t => def(originalName(t), null, {native: t, verify: i => originalName(i.constructor) === originalName(t)})); 467 | BuiltIns.forEach(t => defSub(T`${originalName(t)}`)); 468 | } 469 | 470 | function defineSpecials() { 471 | T.def(`Any`, null, {verify: () => true}); 472 | T.def(`Some`, null, {verify: i => !isUnset(i)}); 473 | T.def(`None`, null, {verify: i => isUnset(i)}); 474 | T.def(`Function`, null, {verify: i => i instanceof Function}); 475 | T.def(`Integer`, null, {verify: i => Number.isInteger(i)}); 476 | T.def(`Array`, null, {verify: i => Array.isArray(i)}); 477 | T.def(`Iterable`, null, {verify: i => i[Symbol.iterator] instanceof Function}); 478 | } 479 | 480 | function isUnset(i) { 481 | return i === null || i === undefined; 482 | } 483 | 484 | function originalName(t) { 485 | if (!!t && t.name) { 486 | return t.name; 487 | } 488 | const oName = Object.prototype.toString.call(t).replace(/\[object |\]/g, ''); 489 | if ( oName.endsWith('Constructor') ) { 490 | return oName.replace(/Constructor$/,''); 491 | } 492 | return oName; 493 | } 494 | 495 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 2 | import {T} from './t.js'; 3 | Object.assign(self, {T}); 4 | 5 | const result0 = T.validate(T`Number`, 0); 6 | console.log({result0}); 7 | console.assert(result0.valid); 8 | 9 | T.defOr('KeyVal', T`String`, T`Number`); 10 | const result01 = T.validate(T`KeyVal`, 0); 11 | console.log({result01}); 12 | console.assert(result01.valid); 13 | 14 | T.def('Cris', { 15 | a: { b: { c: T`String` }} 16 | }); 17 | 18 | const result1 = T.validate(T`Cris`, {a:1}); 19 | console.log({result1}); 20 | console.assert(!result1.valid); 21 | 22 | const result2 = T.validate(T`Cris`, {a:{b:{c:'asdsad'}}}); 23 | console.log({result2}); 24 | console.assert(result2.valid); 25 | 26 | T.defCollection(`DOMList`, { 27 | container: T`>NodeList`, 28 | member: T`>Node` 29 | }); 30 | 31 | const result3 = T.validate(T`DOMList`, document.querySelectorAll('*')); 32 | const result4 = T.validate(T`DOMList`, document.body.childNodes); 33 | 34 | console.log({result3}); 35 | console.assert(result3.valid); 36 | console.log({result4}); 37 | console.assert(result4.valid); 38 | 39 | T.defTuple(`TypeMapping`, { 40 | pattern: [T`String`, T`>Object`] 41 | }); 42 | 43 | T.defCollection(`TypeMap`, { 44 | container: T`Map`, 45 | member: T`TypeMapping` 46 | }); 47 | 48 | const result5 = T.validate(T`TypeMap`, T[Symbol.for('jtype-system.typeCache')]); 49 | 50 | console.log({result5}); 51 | console.assert(result5.valid); 52 | 53 | const result6 = [ 54 | T.check(T`Iterable`, []), 55 | T.check(T`Iterable`, "ASDSAD"), 56 | T.check(T`Iterable`, new Set()), 57 | T.check(T`Iterable`, document.querySelectorAll('*')), 58 | T.check(T`Iterable`, {}), 59 | T.check(T`Iterable`, 12312), 60 | ]; 61 | 62 | console.log({result6}); 63 | console.assert(result6.join(',') == [true, true, true, true, false, false].join(',')); 64 | 65 | T.defOr(`Key`, T`String`, T`Integer`); 66 | T.defOption(T`Key`); 67 | 68 | const keys = [ 69 | "ASDSA", 70 | 1312312, 71 | "asd", 72 | 123122232 73 | ]; 74 | const not_keys = [ 75 | "ASDSA", 76 | 1312312, 77 | "asd", 78 | 1231.22232 79 | ]; 80 | const gappy_keys = [ 81 | "ASDSA", 82 | 1312312, 83 | "asd", 84 | null, 85 | 908098, 86 | null, 87 | "1232", 88 | 'safsda' 89 | ]; 90 | 91 | T.defCollection(`KeyList`, {container: T`Iterable`, member: T`Key`}); 92 | T.defCollection(`OptionalKeyList`, {container: T`Iterable`, member: T`?Key`}); 93 | 94 | const result7 = T.validate(T`KeyList`, keys); 95 | console.log({result7}); 96 | console.assert(result7.valid); 97 | const result8 = T.validate(T`KeyList`, not_keys); 98 | console.log({result8}); 99 | console.assert(!result8.valid); 100 | const result9 = T.validate(T`OptionalKeyList`, gappy_keys); 101 | console.log({result9}); 102 | console.assert(result9.valid); 103 | 104 | 105 | T.def(`StrictContact`, { 106 | name: T`String`, 107 | mobile: T`Number`, 108 | email: T`String` 109 | }, {sealed:true}); 110 | 111 | const result10 = T.validate(T`StrictContact`, {name:'Cris', mobile:999, email:'777@gmail.com'}); 112 | const result11 = T.validate(T`StrictContact`, {name:'Cris', mobile:999, email:'777@gmail.com', new:true}); 113 | 114 | console.log({result10}); 115 | console.assert(result10.valid); 116 | console.log({result11}); 117 | console.assert(!result11.valid); 118 | 119 | const result12 = T.partialMatch(T`StrictContact`, {name:'Mobile'}); 120 | console.log({result12}); 121 | console.assert(result12.valid); 122 | 123 | const result13 = T.partialMatch(T`StrictContact`, {name:'Mobile', blockhead:133133}); 124 | console.log({result13}); 125 | console.assert(!result13.valid); 126 | 127 | T.def('Err', { 128 | error: T`String`, 129 | status: T.defOr('MaybeInteger', T`Integer`, T`None`), 130 | }); 131 | 132 | const result14 = T.validate(T`Err`, {error:'No such page'}); 133 | console.log({result14}); 134 | console.assert(result14.valid); 135 | 136 | T.def('Session', { 137 | id: T`String` 138 | }); 139 | 140 | T.def('WrappedSession', { 141 | session: T`Session` 142 | }); 143 | 144 | const result15 = T.validate(T`WrappedSession`, { session: {id : 'OK' }}); 145 | console.log({result15}); 146 | console.assert(result15.valid); 147 | 148 | T.defEnum('Fruit', 'Apple', 'Pear', 'Pineapple', 'Melon'); 149 | const result16 = T.validate(T`Fruit`, 'Melon'); 150 | const result17 = T.validate(T`Fruit`, 'Rock'); 151 | console.log({result16,result17}); 152 | console.assert(result16.valid); 153 | console.assert(!result17.valid); 154 | 155 | --------------------------------------------------------------------------------