├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── nodejs.yml │ └── pages.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── .yarn ├── releases │ └── yarn-4.0.0-rc.2.cjs └── sdks │ ├── integrations.yml │ └── typescript │ ├── bin │ ├── tsc │ └── tsserver │ ├── lib │ ├── tsc.js │ ├── tsserver.js │ ├── tsserverlibrary.js │ └── typescript.js │ └── package.json ├── .yarnrc.yml ├── README.md ├── package.json ├── rollup.config.js ├── sources ├── index.ts ├── internal │ ├── format.ts │ ├── regexps.ts │ └── tools.ts ├── predicates │ ├── cascadingPredicates.ts │ ├── helperPredicates.ts │ └── typePredicates.ts ├── tools.ts └── types.ts ├── tests ├── index.test.ts ├── type-guards.ts └── type-inferences.ts ├── tsconfig.build.json ├── tsconfig.json ├── website ├── config.js └── docs │ ├── examples.md │ ├── getting-started.md │ ├── overview.md │ ├── predicates │ ├── cascading.md │ ├── helpers.md │ └── types.md │ └── showcase.md └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,json}] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /.yarn/** linguist-vendored 2 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [14.x, 16.x, 18.x] 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | 21 | - name: Use Node.js ${{matrix.node-version}} 22 | uses: actions/setup-node@v1 23 | with: 24 | node-version: ${{matrix.node-version}} 25 | 26 | - run: yarn 27 | - run: yarn test 28 | -------------------------------------------------------------------------------- /.github/workflows/pages.yml: -------------------------------------------------------------------------------- 1 | name: Pages 2 | 3 | on: 4 | workflow_dispatch: 5 | page_build: 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | 14 | - name: Build the website 15 | run: cd website && curl https://raw.githubusercontent.com/arcanis/mael.dev-docs/main/install.sh | tee /dev/stderr | bash 16 | 17 | - name: Upload Artifact 18 | uses: actions/upload-pages-artifact@v3 19 | with: 20 | path: ./website/build 21 | 22 | deploy: 23 | runs-on: ubuntu-latest 24 | needs: build 25 | 26 | permissions: 27 | pages: write 28 | id-token: write 29 | 30 | environment: 31 | name: github-pages 32 | url: ${{steps.deployment.outputs.page_url}} 33 | 34 | steps: 35 | - name: Deploy to GitHub Pages 36 | id: deployment 37 | uses: actions/deploy-pages@v4 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /lib 2 | 3 | /.yarn/* 4 | !/.yarn/sdks 5 | !/.yarn/releases 6 | !/.yarn/plugins 7 | /.pnp.* 8 | /.node-version 9 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "arcanis.vscode-zipfs" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": ".yarn/sdks/typescript/lib", 3 | "typescript.enablePromptUseWorkspaceTsdk": true, 4 | "search.exclude": { 5 | "**/.yarn": true, 6 | "**/.pnp.*": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.yarn/sdks/integrations.yml: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by @yarnpkg/sdks. 2 | # Manual changes might be lost! 3 | 4 | integrations: 5 | - vscode 6 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/bin/tsc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire, createRequireFromPath} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require typescript/bin/tsc 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real typescript/bin/tsc your application uses 20 | module.exports = absRequire(`typescript/bin/tsc`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/bin/tsserver: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire, createRequireFromPath} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require typescript/bin/tsserver 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real typescript/bin/tsserver your application uses 20 | module.exports = absRequire(`typescript/bin/tsserver`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/lib/tsc.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire, createRequireFromPath} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require typescript/lib/tsc.js 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real typescript/lib/tsc.js your application uses 20 | module.exports = absRequire(`typescript/lib/tsc.js`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/lib/tsserver.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire, createRequireFromPath} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath); 11 | 12 | const moduleWrapper = tsserver => { 13 | if (!process.versions.pnp) { 14 | return tsserver; 15 | } 16 | 17 | const {isAbsolute} = require(`path`); 18 | const pnpApi = require(`pnpapi`); 19 | 20 | const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//); 21 | const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`); 22 | 23 | const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => { 24 | return `${locator.name}@${locator.reference}`; 25 | })); 26 | 27 | // VSCode sends the zip paths to TS using the "zip://" prefix, that TS 28 | // doesn't understand. This layer makes sure to remove the protocol 29 | // before forwarding it to TS, and to add it back on all returned paths. 30 | 31 | function toEditorPath(str) { 32 | // We add the `zip:` prefix to both `.zip/` paths and virtual paths 33 | if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) { 34 | // We also take the opportunity to turn virtual paths into physical ones; 35 | // this makes it much easier to work with workspaces that list peer 36 | // dependencies, since otherwise Ctrl+Click would bring us to the virtual 37 | // file instances instead of the real ones. 38 | // 39 | // We only do this to modules owned by the the dependency tree roots. 40 | // This avoids breaking the resolution when jumping inside a vendor 41 | // with peer dep (otherwise jumping into react-dom would show resolution 42 | // errors on react). 43 | // 44 | const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str; 45 | if (resolved) { 46 | const locator = pnpApi.findPackageLocator(resolved); 47 | if (locator && dependencyTreeRoots.has(`${locator.name}@${locator.reference}`)) { 48 | str = resolved; 49 | } 50 | } 51 | 52 | str = normalize(str); 53 | 54 | if (str.match(/\.zip\//)) { 55 | switch (hostInfo) { 56 | // Absolute VSCode `Uri.fsPath`s need to start with a slash. 57 | // VSCode only adds it automatically for supported schemes, 58 | // so we have to do it manually for the `zip` scheme. 59 | // The path needs to start with a caret otherwise VSCode doesn't handle the protocol 60 | // 61 | // Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910 62 | // 63 | // Update Oct 8 2021: VSCode changed their format in 1.61. 64 | // Before | ^zip:/c:/foo/bar.zip/package.json 65 | // After | ^/zip//c:/foo/bar.zip/package.json 66 | // 67 | case `vscode <1.61`: { 68 | str = `^zip:${str}`; 69 | } break; 70 | 71 | case `vscode`: { 72 | str = `^/zip/${str}`; 73 | } break; 74 | 75 | // To make "go to definition" work, 76 | // We have to resolve the actual file system path from virtual path 77 | // and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip) 78 | case `coc-nvim`: { 79 | str = normalize(resolved).replace(/\.zip\//, `.zip::`); 80 | str = resolve(`zipfile:${str}`); 81 | } break; 82 | 83 | // Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server) 84 | // We have to resolve the actual file system path from virtual path, 85 | // everything else is up to neovim 86 | case `neovim`: { 87 | str = normalize(resolved).replace(/\.zip\//, `.zip::`); 88 | str = `zipfile:${str}`; 89 | } break; 90 | 91 | default: { 92 | str = `zip:${str}`; 93 | } break; 94 | } 95 | } 96 | } 97 | 98 | return str; 99 | } 100 | 101 | function fromEditorPath(str) { 102 | switch (hostInfo) { 103 | case `coc-nvim`: 104 | case `neovim`: { 105 | str = str.replace(/\.zip::/, `.zip/`); 106 | // The path for coc-nvim is in format of //zipfile://.yarn/... 107 | // So in order to convert it back, we use .* to match all the thing 108 | // before `zipfile:` 109 | return process.platform === `win32` 110 | ? str.replace(/^.*zipfile:\//, ``) 111 | : str.replace(/^.*zipfile:/, ``); 112 | } break; 113 | 114 | case `vscode`: 115 | default: { 116 | return process.platform === `win32` 117 | ? str.replace(/^\^?(zip:|\/zip)\/+/, ``) 118 | : str.replace(/^\^?(zip:|\/zip)\/+/, `/`); 119 | } break; 120 | } 121 | } 122 | 123 | // Force enable 'allowLocalPluginLoads' 124 | // TypeScript tries to resolve plugins using a path relative to itself 125 | // which doesn't work when using the global cache 126 | // https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238 127 | // VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but 128 | // TypeScript already does local loads and if this code is running the user trusts the workspace 129 | // https://github.com/microsoft/vscode/issues/45856 130 | const ConfiguredProject = tsserver.server.ConfiguredProject; 131 | const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype; 132 | ConfiguredProject.prototype.enablePluginsWithOptions = function() { 133 | this.projectService.allowLocalPluginLoads = true; 134 | return originalEnablePluginsWithOptions.apply(this, arguments); 135 | }; 136 | 137 | // And here is the point where we hijack the VSCode <-> TS communications 138 | // by adding ourselves in the middle. We locate everything that looks 139 | // like an absolute path of ours and normalize it. 140 | 141 | const Session = tsserver.server.Session; 142 | const {onMessage: originalOnMessage, send: originalSend} = Session.prototype; 143 | let hostInfo = `unknown`; 144 | 145 | Object.assign(Session.prototype, { 146 | onMessage(/** @type {string} */ message) { 147 | const parsedMessage = JSON.parse(message) 148 | 149 | if ( 150 | parsedMessage != null && 151 | typeof parsedMessage === `object` && 152 | parsedMessage.arguments && 153 | typeof parsedMessage.arguments.hostInfo === `string` 154 | ) { 155 | hostInfo = parsedMessage.arguments.hostInfo; 156 | if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK && process.env.VSCODE_IPC_HOOK.match(/Code\/1\.([1-5][0-9]|60)\./)) { 157 | hostInfo += ` <1.61`; 158 | } 159 | } 160 | 161 | return originalOnMessage.call(this, JSON.stringify(parsedMessage, (key, value) => { 162 | return typeof value === `string` ? fromEditorPath(value) : value; 163 | })); 164 | }, 165 | 166 | send(/** @type {any} */ msg) { 167 | return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => { 168 | return typeof value === `string` ? toEditorPath(value) : value; 169 | }))); 170 | } 171 | }); 172 | 173 | return tsserver; 174 | }; 175 | 176 | if (existsSync(absPnpApiPath)) { 177 | if (!process.versions.pnp) { 178 | // Setup the environment to be able to require typescript/lib/tsserver.js 179 | require(absPnpApiPath).setup(); 180 | } 181 | } 182 | 183 | // Defer to the real typescript/lib/tsserver.js your application uses 184 | module.exports = moduleWrapper(absRequire(`typescript/lib/tsserver.js`)); 185 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/lib/tsserverlibrary.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire, createRequireFromPath} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath); 11 | 12 | const moduleWrapper = tsserver => { 13 | if (!process.versions.pnp) { 14 | return tsserver; 15 | } 16 | 17 | const {isAbsolute} = require(`path`); 18 | const pnpApi = require(`pnpapi`); 19 | 20 | const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//); 21 | const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`); 22 | 23 | const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => { 24 | return `${locator.name}@${locator.reference}`; 25 | })); 26 | 27 | // VSCode sends the zip paths to TS using the "zip://" prefix, that TS 28 | // doesn't understand. This layer makes sure to remove the protocol 29 | // before forwarding it to TS, and to add it back on all returned paths. 30 | 31 | function toEditorPath(str) { 32 | // We add the `zip:` prefix to both `.zip/` paths and virtual paths 33 | if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) { 34 | // We also take the opportunity to turn virtual paths into physical ones; 35 | // this makes it much easier to work with workspaces that list peer 36 | // dependencies, since otherwise Ctrl+Click would bring us to the virtual 37 | // file instances instead of the real ones. 38 | // 39 | // We only do this to modules owned by the the dependency tree roots. 40 | // This avoids breaking the resolution when jumping inside a vendor 41 | // with peer dep (otherwise jumping into react-dom would show resolution 42 | // errors on react). 43 | // 44 | const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str; 45 | if (resolved) { 46 | const locator = pnpApi.findPackageLocator(resolved); 47 | if (locator && dependencyTreeRoots.has(`${locator.name}@${locator.reference}`)) { 48 | str = resolved; 49 | } 50 | } 51 | 52 | str = normalize(str); 53 | 54 | if (str.match(/\.zip\//)) { 55 | switch (hostInfo) { 56 | // Absolute VSCode `Uri.fsPath`s need to start with a slash. 57 | // VSCode only adds it automatically for supported schemes, 58 | // so we have to do it manually for the `zip` scheme. 59 | // The path needs to start with a caret otherwise VSCode doesn't handle the protocol 60 | // 61 | // Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910 62 | // 63 | // Update Oct 8 2021: VSCode changed their format in 1.61. 64 | // Before | ^zip:/c:/foo/bar.zip/package.json 65 | // After | ^/zip//c:/foo/bar.zip/package.json 66 | // 67 | case `vscode <1.61`: { 68 | str = `^zip:${str}`; 69 | } break; 70 | 71 | case `vscode`: { 72 | str = `^/zip/${str}`; 73 | } break; 74 | 75 | // To make "go to definition" work, 76 | // We have to resolve the actual file system path from virtual path 77 | // and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip) 78 | case `coc-nvim`: { 79 | str = normalize(resolved).replace(/\.zip\//, `.zip::`); 80 | str = resolve(`zipfile:${str}`); 81 | } break; 82 | 83 | // Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server) 84 | // We have to resolve the actual file system path from virtual path, 85 | // everything else is up to neovim 86 | case `neovim`: { 87 | str = normalize(resolved).replace(/\.zip\//, `.zip::`); 88 | str = `zipfile:${str}`; 89 | } break; 90 | 91 | default: { 92 | str = `zip:${str}`; 93 | } break; 94 | } 95 | } 96 | } 97 | 98 | return str; 99 | } 100 | 101 | function fromEditorPath(str) { 102 | switch (hostInfo) { 103 | case `coc-nvim`: 104 | case `neovim`: { 105 | str = str.replace(/\.zip::/, `.zip/`); 106 | // The path for coc-nvim is in format of //zipfile://.yarn/... 107 | // So in order to convert it back, we use .* to match all the thing 108 | // before `zipfile:` 109 | return process.platform === `win32` 110 | ? str.replace(/^.*zipfile:\//, ``) 111 | : str.replace(/^.*zipfile:/, ``); 112 | } break; 113 | 114 | case `vscode`: 115 | default: { 116 | return process.platform === `win32` 117 | ? str.replace(/^\^?(zip:|\/zip)\/+/, ``) 118 | : str.replace(/^\^?(zip:|\/zip)\/+/, `/`); 119 | } break; 120 | } 121 | } 122 | 123 | // Force enable 'allowLocalPluginLoads' 124 | // TypeScript tries to resolve plugins using a path relative to itself 125 | // which doesn't work when using the global cache 126 | // https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238 127 | // VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but 128 | // TypeScript already does local loads and if this code is running the user trusts the workspace 129 | // https://github.com/microsoft/vscode/issues/45856 130 | const ConfiguredProject = tsserver.server.ConfiguredProject; 131 | const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype; 132 | ConfiguredProject.prototype.enablePluginsWithOptions = function() { 133 | this.projectService.allowLocalPluginLoads = true; 134 | return originalEnablePluginsWithOptions.apply(this, arguments); 135 | }; 136 | 137 | // And here is the point where we hijack the VSCode <-> TS communications 138 | // by adding ourselves in the middle. We locate everything that looks 139 | // like an absolute path of ours and normalize it. 140 | 141 | const Session = tsserver.server.Session; 142 | const {onMessage: originalOnMessage, send: originalSend} = Session.prototype; 143 | let hostInfo = `unknown`; 144 | 145 | Object.assign(Session.prototype, { 146 | onMessage(/** @type {string} */ message) { 147 | const parsedMessage = JSON.parse(message) 148 | 149 | if ( 150 | parsedMessage != null && 151 | typeof parsedMessage === `object` && 152 | parsedMessage.arguments && 153 | typeof parsedMessage.arguments.hostInfo === `string` 154 | ) { 155 | hostInfo = parsedMessage.arguments.hostInfo; 156 | if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK && process.env.VSCODE_IPC_HOOK.match(/Code\/1\.([1-5][0-9]|60)\./)) { 157 | hostInfo += ` <1.61`; 158 | } 159 | } 160 | 161 | return originalOnMessage.call(this, JSON.stringify(parsedMessage, (key, value) => { 162 | return typeof value === `string` ? fromEditorPath(value) : value; 163 | })); 164 | }, 165 | 166 | send(/** @type {any} */ msg) { 167 | return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => { 168 | return typeof value === `string` ? toEditorPath(value) : value; 169 | }))); 170 | } 171 | }); 172 | 173 | return tsserver; 174 | }; 175 | 176 | if (existsSync(absPnpApiPath)) { 177 | if (!process.versions.pnp) { 178 | // Setup the environment to be able to require typescript/lib/tsserverlibrary.js 179 | require(absPnpApiPath).setup(); 180 | } 181 | } 182 | 183 | // Defer to the real typescript/lib/tsserverlibrary.js your application uses 184 | module.exports = moduleWrapper(absRequire(`typescript/lib/tsserverlibrary.js`)); 185 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/lib/typescript.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire, createRequireFromPath} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require typescript/lib/typescript.js 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real typescript/lib/typescript.js your application uses 20 | module.exports = absRequire(`typescript/lib/typescript.js`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript", 3 | "version": "4.1.2-sdk", 4 | "main": "./lib/typescript.js", 5 | "type": "commonjs" 6 | } 7 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | yarnPath: .yarn/releases/yarn-4.0.0-rc.2.cjs 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Typanion 2 | 3 | > Static and runtime type assertion library with no dependencies 4 | 5 | [![](https://img.shields.io/npm/v/typanion.svg)]() [![](https://img.shields.io/npm/l/typanion.svg)]() [![](https://img.shields.io/badge/developed%20with-Yarn%202-blue)](https://github.com/yarnpkg/berry) 6 | 7 | ## Installation 8 | 9 | ``` 10 | yarn add typanion 11 | ``` 12 | 13 | ## Why 14 | 15 | - Typanion can validate nested arbitrary data structures 16 | - Typanion is type-safe; it uses [type predicates](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates) 17 | - Typanion allows you to derive types from your schemas 18 | - Typanion can report detailed error reports 19 | 20 | Compared to [yup](https://github.com/jquense/yup), Typanion has a better inference support for TypeScript + supports `isOneOf`. Its functional API makes it very easy to tree shake, which is another bonus (although the library isn't very large in itself). 21 | 22 | ## Documentation 23 | 24 | Check the website for our documentation: [mael.dev/typanion](https://mael.dev/typanion/). 25 | 26 | ## Usage 27 | 28 | First define a schema using the builtin operators: 29 | 30 | ```ts 31 | import * as t from 'typanion'; 32 | 33 | const isMovie = t.isObject({ 34 | title: t.isString(), 35 | description: t.isString(), 36 | }); 37 | ``` 38 | 39 | Then just call the schema to validate any `unknown` value: 40 | 41 | ```ts 42 | const userData = JSON.parse(input); 43 | 44 | if (isMovie(userData)) { 45 | console.log(userData.title); 46 | } 47 | ``` 48 | 49 | Passing a second parameter allows you to retrieve detailed errors: 50 | 51 | ```ts 52 | const userData = JSON.parse(input); 53 | const errors: string[] = []; 54 | 55 | if (!isMovie(userData, {errors})) { 56 | console.log(errors); 57 | } 58 | ``` 59 | 60 | You can also apply coercion over the user input: 61 | 62 | ```ts 63 | const userData = JSON.parse(input); 64 | const coercions: Coercion[] = []; 65 | 66 | if (isMovie(userData, {coercions})) { 67 | // Coercions aren't flushed by default 68 | for (const [p, op] of coercions) op(); 69 | 70 | // All relevant fields have now been coerced 71 | // ... 72 | } 73 | ``` 74 | 75 | You can derive the type from the schema and use it in other functions: 76 | 77 | ```ts 78 | import * as t from 'typanion'; 79 | 80 | const isMovie = t.isObject({ 81 | title: t.isString(), 82 | description: t.isString(), 83 | }); 84 | 85 | type Movie = t.InferType; 86 | 87 | // Then just use your alias: 88 | const printMovie = (movie: Movie) => { 89 | // ... 90 | }; 91 | ``` 92 | 93 | Schemas can be stored in multiple variables if needed: 94 | 95 | ```ts 96 | import * as t from 'typanion'; 97 | 98 | const isActor = t.isObject({ 99 | name: t.isString(); 100 | }); 101 | 102 | const isMovie = t.isObject({ 103 | title: t.isString(), 104 | description: t.isString(), 105 | actors: t.isArray(isActor), 106 | }); 107 | ``` 108 | 109 | ## License (MIT) 110 | 111 | > **Copyright © 2020 Mael Nison** 112 | > 113 | > Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 114 | > 115 | > The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 116 | > 117 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 118 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typanion", 3 | "description": "Simple runtime TypeScript validator library", 4 | "homepage": "https://mael.dev/typanion/", 5 | "version": "3.14.0", 6 | "main": "sources/index", 7 | "license": "MIT", 8 | "sideEffects": false, 9 | "repository": { 10 | "url": "https://github.com/arcanis/typanion", 11 | "type": "git" 12 | }, 13 | "workspaces": [ 14 | "website" 15 | ], 16 | "devDependencies": { 17 | "@rollup/plugin-typescript": "^8.2.1", 18 | "@types/chai": "^4.2.11", 19 | "@types/mocha": "^7.0.2", 20 | "@types/node": "^17.0.21", 21 | "chai": "^4.3.4", 22 | "mocha": "^8.0.1", 23 | "rollup": "^2.17.0", 24 | "ts-node": "^8.10.2", 25 | "tslib": "^2.0.0", 26 | "typescript": "^4.6.3" 27 | }, 28 | "scripts": { 29 | "prepack": "rm -rf lib && rollup -c", 30 | "postpack": "rm -rf lib", 31 | "test": "yarn tsc && TS_NODE_TRANSPILE_ONLY=true mocha --require ts-node/register --extension ts tests/**/*.test.ts" 32 | }, 33 | "publishConfig": { 34 | "main": "lib/index" 35 | }, 36 | "files": [ 37 | "lib" 38 | ], 39 | "packageManager": "yarn@4.0.0-rc.2" 40 | } 41 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import ts from "@rollup/plugin-typescript"; 2 | 3 | export default { 4 | input: './sources/index.ts', 5 | output: [ 6 | { 7 | dir: 'lib', 8 | entryFileNames: '[name].mjs', 9 | format: 'es' 10 | }, 11 | { 12 | dir: 'lib', 13 | entryFileNames: '[name].js', 14 | format: 'cjs' 15 | }, 16 | ], 17 | plugins: [ 18 | ts({ 19 | tsconfig: 'tsconfig.build.json' 20 | }), 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /sources/index.ts: -------------------------------------------------------------------------------- 1 | export * from './predicates/cascadingPredicates'; 2 | export * from './predicates/typePredicates'; 3 | export * from './predicates/helperPredicates'; 4 | 5 | export * from './tools'; 6 | export * from './types'; 7 | -------------------------------------------------------------------------------- /sources/internal/format.ts: -------------------------------------------------------------------------------- 1 | import {ValidationState} from '../types'; 2 | 3 | const simpleKeyRegExp = /^[a-zA-Z_][a-zA-Z0-9_]*$/; 4 | 5 | export function getPrintable(value: unknown) { 6 | if (value === null) 7 | return `null`; 8 | if (value === undefined) 9 | return `undefined`; 10 | if (value === ``) 11 | return `an empty string`; 12 | if (typeof value === 'symbol') 13 | return `<${value.toString()}>`; 14 | if (Array.isArray(value)) 15 | return `an array`; 16 | 17 | return JSON.stringify(value); 18 | } 19 | 20 | export function getPrintableArray(value: unknown[], conjunction: string) { 21 | if (value.length === 0) 22 | return `nothing`; 23 | 24 | if (value.length === 1) 25 | return getPrintable(value[0]); 26 | 27 | const rest = value.slice(0, -1); 28 | const trailing = value[value.length - 1]; 29 | 30 | const separator = value.length > 2 31 | ? `, ${conjunction} ` 32 | : ` ${conjunction} `; 33 | 34 | return `${rest.map(value => getPrintable(value)).join(`, `)}${separator}${getPrintable(trailing)}`; 35 | } 36 | 37 | export function computeKey(state: ValidationState | undefined, key: string | number) { 38 | if (typeof key === `number`) { 39 | return `${state?.p ?? `.`}[${key}]`; 40 | } else if (simpleKeyRegExp.test(key)) { 41 | return `${state?.p ?? ``}.${key}`; 42 | } else { 43 | return `${state?.p ?? `.`}[${JSON.stringify(key)}]`; 44 | } 45 | } 46 | 47 | export function plural(n: number, singular: string, plural: string) { 48 | return n === 1 ? singular : plural; 49 | } 50 | -------------------------------------------------------------------------------- /sources/internal/regexps.ts: -------------------------------------------------------------------------------- 1 | export const colorStringRegExp = /^#[0-9a-f]{6}$/i; 2 | export const colorStringAlphaRegExp = /^#[0-9a-f]{6}([0-9a-f]{2})?$/i; 3 | 4 | // https://stackoverflow.com/a/475217/880703 5 | export const base64RegExp = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; 6 | 7 | // https://stackoverflow.com/a/14166194/880703 8 | export const uuid4RegExp = /^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}$/i; 9 | 10 | // https://stackoverflow.com/a/28022901/880703 + https://www.debuggex.com/r/bl8J35wMKk48a7u_ 11 | export const iso8601RegExp = /^(?:[1-9]\d{3}(-?)(?:(?:0[1-9]|1[0-2])\1(?:0[1-9]|1\d|2[0-8])|(?:0[13-9]|1[0-2])\1(?:29|30)|(?:0[13578]|1[02])(?:\1)31|00[1-9]|0[1-9]\d|[12]\d{2}|3(?:[0-5]\d|6[0-5]))|(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)(?:(-?)02(?:\2)29|-?366))T(?:[01]\d|2[0-3])(:?)[0-5]\d(?:\3[0-5]\d)?(?:Z|[+-][01]\d(?:\3[0-5]\d)?)$/; 12 | -------------------------------------------------------------------------------- /sources/internal/tools.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BoundCoercionFn, 3 | CoercionFn, 4 | ValidationState, 5 | } from '../types'; 6 | 7 | export function pushError({errors, p}: ValidationState = {}, message: string) { 8 | errors?.push(`${p ?? `.`}: ${message}`); 9 | return false; 10 | } 11 | 12 | export function makeSetter(target: any, key: any) { 13 | return (v: any) => { 14 | target[key] = v; 15 | }; 16 | } 17 | 18 | export function makeCoercionFn(target: any, key: any): CoercionFn { 19 | return (v: any) => { 20 | const previous = target[key]; 21 | target[key] = v; 22 | return makeCoercionFn(target, key).bind(null, previous); 23 | }; 24 | } 25 | 26 | export function makeLazyCoercionFn(fn: CoercionFn, orig: any, generator: () => any): BoundCoercionFn { 27 | const commit = () => { 28 | fn(generator()); 29 | return revert; 30 | }; 31 | 32 | const revert = () => { 33 | fn(orig); 34 | return commit; 35 | }; 36 | 37 | return commit; 38 | } 39 | -------------------------------------------------------------------------------- /sources/predicates/cascadingPredicates.ts: -------------------------------------------------------------------------------- 1 | import { 2 | getPrintable, 3 | } from '../internal/format'; 4 | 5 | import { 6 | base64RegExp, 7 | colorStringAlphaRegExp, 8 | colorStringRegExp, 9 | iso8601RegExp, 10 | uuid4RegExp, 11 | } from '../internal/regexps'; 12 | 13 | import { 14 | pushError, 15 | } from '../internal/tools'; 16 | 17 | import { 18 | makeValidator, 19 | } from '../tools'; 20 | 21 | import { 22 | AnyStrictValidator, 23 | } from '../types'; 24 | 25 | import { 26 | isUnknown, 27 | } from './typePredicates'; 28 | 29 | /** 30 | * Create a validator that checks that the tested array or string has at least 31 | * the specified length. 32 | */ 33 | export function hasMinLength(length: number) { 34 | return makeValidator({ 35 | test: (value, state) => { 36 | if (!(value.length >= length)) 37 | return pushError(state, `Expected to have a length of at least ${length} elements (got ${value.length})`); 38 | 39 | return true; 40 | }, 41 | }); 42 | } 43 | 44 | /** 45 | * Create a validator that checks that the tested array or string has at most 46 | * the specified length. 47 | */ 48 | export function hasMaxLength(length: number) { 49 | return makeValidator({ 50 | test: (value, state) => { 51 | if (!(value.length <= length)) 52 | return pushError(state, `Expected to have a length of at most ${length} elements (got ${value.length})`); 53 | 54 | return true; 55 | }, 56 | }); 57 | } 58 | 59 | /** 60 | * Create a validator that checks that the tested array or string has exactly 61 | * the specified length. 62 | */ 63 | export function hasExactLength(length: number) { 64 | return makeValidator({ 65 | test: (value, state) => { 66 | if (!(value.length === length)) 67 | return pushError(state, `Expected to have a length of exactly ${length} elements (got ${value.length})`); 68 | 69 | return true; 70 | }, 71 | }); 72 | } 73 | 74 | /** 75 | * Create a validator that checks that the tested array only contains unique 76 | * elements. The optional `map` parameter lets you define a transform to 77 | * apply before making the check (the result of this transform will be 78 | * discarded afterwards). 79 | */ 80 | export function hasUniqueItems({ 81 | map, 82 | }: { 83 | map?: (value: T) => unknown, 84 | } = {}) { 85 | return makeValidator({ 86 | test: (value, state) => { 87 | const set = new Set(); 88 | const dup = new Set(); 89 | 90 | for (let t = 0, T = value.length; t < T; ++t) { 91 | const sub = value[t]; 92 | 93 | const key = typeof map !== `undefined` 94 | ? map(sub) 95 | : sub; 96 | 97 | if (set.has(key)) { 98 | if (dup.has(key)) 99 | continue; 100 | 101 | pushError(state, `Expected to contain unique elements; got a duplicate with ${getPrintable(value)}`); 102 | dup.add(key); 103 | } else { 104 | set.add(key); 105 | } 106 | } 107 | 108 | return dup.size === 0; 109 | }, 110 | }); 111 | } 112 | 113 | /** 114 | * Create a validator that checks that the tested number is strictly less than 0. 115 | */ 116 | export function isNegative() { 117 | return makeValidator({ 118 | test: (value, state) => { 119 | if (!(value <= 0)) 120 | return pushError(state, `Expected to be negative (got ${value})`); 121 | 122 | return true; 123 | }, 124 | }); 125 | } 126 | 127 | /** 128 | * Create a validator that checks that the tested number is equal or greater 129 | * than 0. 130 | */ 131 | export function isPositive() { 132 | return makeValidator({ 133 | test: (value, state) => { 134 | if (!(value >= 0)) 135 | return pushError(state, `Expected to be positive (got ${value})`); 136 | 137 | return true; 138 | }, 139 | }); 140 | } 141 | 142 | /** 143 | * Create a validator that checks that the tested number is equal or greater 144 | * than the specified reference. 145 | */ 146 | export function isAtLeast(n: number) { 147 | return makeValidator({ 148 | test: (value, state) => { 149 | if (!(value >= n)) 150 | return pushError(state, `Expected to be at least ${n} (got ${value})`); 151 | 152 | return true; 153 | }, 154 | }); 155 | } 156 | 157 | /** 158 | * Create a validator that checks that the tested number is equal or smaller 159 | * than the specified reference. 160 | */ 161 | export function isAtMost(n: number) { 162 | return makeValidator({ 163 | test: (value, state) => { 164 | if (!(value <= n)) 165 | return pushError(state, `Expected to be at most ${n} (got ${value})`); 166 | 167 | return true; 168 | }, 169 | }); 170 | } 171 | 172 | /** 173 | * Create a validator that checks that the tested number is between the 174 | * specified references (including the upper boundary). 175 | */ 176 | export function isInInclusiveRange(a: number, b: number) { 177 | return makeValidator({ 178 | test: (value, state) => { 179 | if (!(value >= a && value <= b)) 180 | return pushError(state, `Expected to be in the [${a}; ${b}] range (got ${value})`); 181 | 182 | return true; 183 | }, 184 | }); 185 | } 186 | 187 | /** 188 | * Create a validator that checks that the tested number is between the 189 | * specified references (excluding the upper boundary). 190 | */ 191 | export function isInExclusiveRange(a: number, b: number) { 192 | return makeValidator({ 193 | test: (value, state) => { 194 | if (!(value >= a && value < b)) 195 | return pushError(state, `Expected to be in the [${a}; ${b}[ range (got ${value})`); 196 | 197 | return true; 198 | }, 199 | }); 200 | } 201 | 202 | /** 203 | * Create a validator that checks that the tested number is an integer. 204 | * 205 | * By default Typanion will also check that it's a *safe* integer. For example, 206 | * 2^53 wouldn't be a safe integer because 2^53+1 would be rounded to 2^53, 207 | * which could put your applications at risk when used in loops. 208 | */ 209 | export function isInteger({ 210 | unsafe = false, 211 | }: { 212 | unsafe?: boolean, 213 | } = {}) { 214 | return makeValidator({ 215 | test: (value, state) => { 216 | if (value !== Math.round(value)) 217 | return pushError(state, `Expected to be an integer (got ${value})`); 218 | 219 | if (!unsafe && !Number.isSafeInteger(value)) 220 | return pushError(state, `Expected to be a safe integer (got ${value})`); 221 | 222 | return true; 223 | }, 224 | }); 225 | } 226 | 227 | /** 228 | * Create a validator that checks that the tested string matches the given 229 | * regular expression. 230 | */ 231 | export function matchesRegExp(regExp: RegExp) { 232 | return makeValidator({ 233 | test: (value, state) => { 234 | if (!regExp.test(value)) 235 | return pushError(state, `Expected to match the pattern ${regExp.toString()} (got ${getPrintable(value)})`); 236 | 237 | return true; 238 | }, 239 | }); 240 | } 241 | 242 | /** 243 | * Create a validator that checks that the tested string only contain lowercase 244 | * characters. 245 | */ 246 | export function isLowerCase() { 247 | return makeValidator({ 248 | test: (value, state) => { 249 | if (value !== value.toLowerCase()) 250 | return pushError(state, `Expected to be all-lowercase (got ${value})`); 251 | 252 | return true; 253 | }, 254 | }); 255 | } 256 | 257 | /** 258 | * Create a validator that checks that the tested string only contain uppercase 259 | * characters. 260 | */ 261 | export function isUpperCase() { 262 | return makeValidator({ 263 | test: (value, state) => { 264 | if (value !== value.toUpperCase()) 265 | return pushError(state, `Expected to be all-uppercase (got ${value})`); 266 | 267 | return true; 268 | }, 269 | }); 270 | } 271 | 272 | /** 273 | * Create a validator that checks that the tested string is a valid UUID v4. 274 | */ 275 | export function isUUID4() { 276 | return makeValidator({ 277 | test: (value, state) => { 278 | if (!uuid4RegExp.test(value)) 279 | return pushError(state, `Expected to be a valid UUID v4 (got ${getPrintable(value)})`); 280 | 281 | return true; 282 | }, 283 | }); 284 | } 285 | 286 | /** 287 | * Create a validator that checks that the tested string is a valid ISO8601 288 | * date. 289 | */ 290 | export function isISO8601() { 291 | return makeValidator({ 292 | test: (value, state) => { 293 | if (!iso8601RegExp.test(value)) 294 | return pushError(state, `Expected to be a valid ISO 8601 date string (got ${getPrintable(value)})`); 295 | 296 | return true; 297 | }, 298 | }); 299 | } 300 | 301 | /** 302 | * Create a validator that checks that the tested string is a valid hexadecimal 303 | * color. Setting the optional `alpha` parameter to `true` allows an additional 304 | * transparency channel to be included. 305 | */ 306 | export function isHexColor({ 307 | alpha = false, 308 | }: { 309 | alpha?: boolean, 310 | }) { 311 | return makeValidator({ 312 | test: (value, state) => { 313 | const res = alpha 314 | ? colorStringRegExp.test(value) 315 | : colorStringAlphaRegExp.test(value); 316 | 317 | if (!res) 318 | return pushError(state, `Expected to be a valid hexadecimal color string (got ${getPrintable(value)})`); 319 | 320 | return true; 321 | }, 322 | }); 323 | } 324 | 325 | /** 326 | * Create a validator that checks that the tested string is valid base64. 327 | */ 328 | export function isBase64() { 329 | return makeValidator({ 330 | test: (value, state) => { 331 | if (!base64RegExp.test(value)) 332 | return pushError(state, `Expected to be a valid base 64 string (got ${getPrintable(value)})`); 333 | 334 | return true; 335 | }, 336 | }); 337 | } 338 | 339 | /** 340 | * Create a validator that checks that the tested string is valid JSON. A 341 | * optional spec can be passed as parameter, in which case the data will be 342 | * deserialized and validated against the spec (coercion will be disabled 343 | * for this check, and even if successful the returned value will still be 344 | * the original string). 345 | */ 346 | export function isJSON(spec: AnyStrictValidator = isUnknown()) { 347 | return makeValidator({ 348 | test: (value, state) => { 349 | let data; 350 | try { 351 | data = JSON.parse(value); 352 | } catch { 353 | return pushError(state, `Expected to be a valid JSON string (got ${getPrintable(value)})`); 354 | } 355 | 356 | return spec(data, state); 357 | }, 358 | }); 359 | } 360 | -------------------------------------------------------------------------------- /sources/predicates/helperPredicates.ts: -------------------------------------------------------------------------------- 1 | import { 2 | getPrintableArray, 3 | plural, 4 | } from '../internal/format'; 5 | 6 | import { 7 | makeCoercionFn, 8 | pushError, 9 | } from '../internal/tools'; 10 | 11 | import { 12 | makeValidator, 13 | } from '../tools'; 14 | 15 | import { 16 | AnyStrictValidator, 17 | BoundCoercionFn, 18 | Coercion, 19 | InferType, 20 | LooseTest, 21 | StrictTest, 22 | StrictValidator, 23 | } from '../types'; 24 | 25 | /** 26 | * Create a validator that runs the provided spec before applying a series of 27 | * followup validation on the refined type. This is useful when you not only 28 | * wish to validate the data type itself, but also its format. 29 | * 30 | * For example, the following would validate that a value is a valid port: 31 | * t.cascade(t.isNumber(), t.isInteger(), t.isInInclusiveRange(1, 655356)) 32 | * 33 | * And the following would validate that a value is base64: 34 | * t.cascade(t.isString(), t.isBase64()) 35 | */ 36 | export function cascade(spec: T, followups: Array, InferType> | LooseTest>>): StrictValidator>; 37 | export function cascade(spec: T, ...followups: Array, InferType> | LooseTest>>): StrictValidator>; 38 | export function cascade(spec: T, ...followups: any) { 39 | const resolvedFollowups: Array, InferType> | LooseTest>> = Array.isArray(followups[0]) 40 | ? followups[0] 41 | : followups; 42 | 43 | return makeValidator>({ 44 | test: (value, state): value is InferType => { 45 | const context = {value: value as any}; 46 | 47 | const subCoercion = typeof state?.coercions !== `undefined` 48 | ? makeCoercionFn(context, `value`) : undefined; 49 | 50 | const subCoercions = typeof state?.coercions !== `undefined` 51 | ? [] as Coercion[] : undefined; 52 | 53 | if (!spec(value, {...state, coercion: subCoercion, coercions: subCoercions})) 54 | return false; 55 | 56 | const reverts: BoundCoercionFn[] = []; 57 | if (typeof subCoercions !== `undefined`) 58 | for (const [, coercion] of subCoercions) 59 | reverts.push(coercion()); 60 | 61 | try { 62 | if (typeof state?.coercions !== `undefined`) { 63 | if (context.value !== value) { 64 | if (typeof state?.coercion === `undefined`) 65 | return pushError(state, `Unbound coercion result`); 66 | 67 | state.coercions.push([state.p ?? `.`, state.coercion.bind(null, context.value)]); 68 | } 69 | 70 | state?.coercions?.push(...subCoercions!); 71 | } 72 | 73 | return resolvedFollowups.every(spec => { 74 | return spec(context.value as InferType, state); 75 | }); 76 | } finally { 77 | for (const revert of reverts) { 78 | revert(); 79 | } 80 | } 81 | }, 82 | }); 83 | } 84 | 85 | /** 86 | * @deprecated Replace `applyCascade` by `cascade` 87 | */ 88 | export function applyCascade(spec: T, followups: Array, InferType> | LooseTest>>): StrictValidator>; 89 | export function applyCascade(spec: T, ...followups: Array, InferType> | LooseTest>>): StrictValidator>; 90 | export function applyCascade(spec: T, ...followups: any) { 91 | const resolvedFollowups: Array, InferType> | LooseTest>> = Array.isArray(followups[0]) 92 | ? followups[0] 93 | : followups; 94 | 95 | return cascade(spec, resolvedFollowups); 96 | } 97 | 98 | /** 99 | * Wraps the given spec to also allow `undefined`. 100 | */ 101 | export function isOptional(spec: T) { 102 | return makeValidator | undefined>({ 103 | test: (value, state): value is InferType | undefined => { 104 | if (typeof value === `undefined`) 105 | return true; 106 | 107 | return spec(value, state); 108 | }, 109 | }); 110 | } 111 | 112 | /** 113 | * Wraps the given spec to also allow `null`. 114 | */ 115 | export function isNullable(spec: T) { 116 | return makeValidator | null>({ 117 | test: (value, state): value is InferType | null => { 118 | if (value === null) 119 | return true; 120 | 121 | return spec(value, state); 122 | }, 123 | }); 124 | } 125 | 126 | export type MissingType = 'missing' | 'undefined' | 'nil' | 'falsy'; 127 | 128 | const checks: {[index in MissingType]: (keys: Set, key: string, value: Record) => boolean } = { 129 | missing: (keys, key) => keys.has(key), 130 | undefined: (keys, key, value) => keys.has(key) && typeof value[key] !== `undefined`, 131 | nil: (keys, key, value) => keys.has(key) && value[key] != null, 132 | falsy: (keys, key, value) => keys.has(key) && !!value[key], 133 | }; 134 | 135 | /** 136 | * Create a validator that checks that the tested object contains the specified 137 | * keys. 138 | */ 139 | export function hasRequiredKeys(requiredKeys: string[], options?: { missingIf?: MissingType }) { 140 | const requiredSet = new Set(requiredKeys); 141 | const check = checks[options?.missingIf ?? 'missing']; 142 | 143 | return makeValidator>({ 144 | test: (value, state) => { 145 | const keys = new Set(Object.keys(value)); 146 | 147 | const problems: string[] = []; 148 | for (const key of requiredSet) 149 | if (!check(keys, key, value)) 150 | problems.push(key); 151 | 152 | if (problems.length > 0) 153 | return pushError(state, `Missing required ${plural(problems.length, `property`, `properties`)} ${getPrintableArray(problems, `and`)}`); 154 | 155 | return true; 156 | }, 157 | }); 158 | } 159 | 160 | /** 161 | * Create a validator that checks that the tested object contains at least one 162 | * of the specified keys. 163 | */ 164 | export function hasAtLeastOneKey(requiredKeys: string[], options?: { missingIf?: MissingType }) { 165 | const requiredSet = new Set(requiredKeys); 166 | const check = checks[options?.missingIf ?? 'missing']; 167 | 168 | return makeValidator>({ 169 | test: (value, state) => { 170 | const keys = Object.keys(value); 171 | 172 | const valid = keys.some(key => check(requiredSet, key, value)); 173 | if (!valid) 174 | return pushError(state, `Missing at least one property from ${getPrintableArray(Array.from(requiredSet), `or`)}`); 175 | 176 | return true; 177 | }, 178 | }); 179 | } 180 | 181 | /** 182 | * Create a validator that checks that the tested object contains none of the 183 | * specified keys. 184 | */ 185 | export function hasForbiddenKeys(forbiddenKeys: string[], options?: { missingIf?: MissingType }) { 186 | const forbiddenSet = new Set(forbiddenKeys); 187 | const check = checks[options?.missingIf ?? 'missing']; 188 | 189 | return makeValidator<{[key: string]: unknown}>({ 190 | test: (value, state) => { 191 | const keys = new Set(Object.keys(value)); 192 | 193 | const problems: string[] = []; 194 | for (const key of forbiddenSet) 195 | if (check(keys, key, value)) 196 | problems.push(key); 197 | 198 | if (problems.length > 0) 199 | return pushError(state, `Forbidden ${plural(problems.length, `property`, `properties`)} ${getPrintableArray(problems, `and`)}`); 200 | 201 | return true; 202 | }, 203 | }); 204 | } 205 | 206 | /** 207 | * Create a validator that checks that the tested object contains at most one 208 | * of the specified keys. 209 | */ 210 | export function hasMutuallyExclusiveKeys(exclusiveKeys: string[], options?: { missingIf?: MissingType }) { 211 | const exclusiveSet = new Set(exclusiveKeys); 212 | const check = checks[options?.missingIf ?? 'missing']; 213 | 214 | return makeValidator<{[key: string]: unknown}>({ 215 | test: (value, state) => { 216 | const keys = new Set(Object.keys(value)); 217 | 218 | const used: string[] = []; 219 | for (const key of exclusiveSet) 220 | if (check(keys, key, value)) 221 | used.push(key); 222 | 223 | if (used.length > 1) 224 | return pushError(state, `Mutually exclusive properties ${getPrintableArray(used, `and`)}`); 225 | 226 | return true; 227 | }, 228 | }); 229 | } 230 | 231 | export enum KeyRelationship { 232 | Forbids = `Forbids`, 233 | Requires = `Requires`, 234 | }; 235 | 236 | const keyRelationships = { 237 | [KeyRelationship.Forbids]: { 238 | expect: false, 239 | message: `forbids using`, 240 | }, 241 | [KeyRelationship.Requires]: { 242 | expect: true, 243 | message: `requires using`, 244 | }, 245 | }; 246 | 247 | /** 248 | * Create a validator that checks that, when the specified subject property is 249 | * set, the relationship is satisfied. 250 | */ 251 | export function hasKeyRelationship(subject: string, relationship: KeyRelationship, others: string[], options?: { ignore?: any[], missingIf?: MissingType }) { 252 | const skipped = new Set(options?.ignore ?? []); 253 | const check = checks[options?.missingIf ?? 'missing']; 254 | 255 | const otherSet = new Set(others); 256 | const spec = keyRelationships[relationship]; 257 | 258 | const conjunction = relationship === KeyRelationship.Forbids 259 | ? `or` 260 | : `and`; 261 | 262 | return makeValidator<{[key: string]: unknown}>({ 263 | test: (value, state) => { 264 | const keys = new Set(Object.keys(value)); 265 | if (!check(keys, subject, value) || skipped.has(value[subject])) 266 | return true; 267 | 268 | const problems: string[] = []; 269 | for (const key of otherSet) 270 | if ((check(keys, key, value) && !skipped.has(value[key])) !== spec.expect) 271 | problems.push(key); 272 | 273 | if (problems.length >= 1) 274 | return pushError(state, `Property "${subject}" ${spec.message} ${plural(problems.length, `property`, `properties`)} ${getPrintableArray(problems, conjunction)}`); 275 | 276 | return true; 277 | }, 278 | }) 279 | }; 280 | -------------------------------------------------------------------------------- /sources/predicates/typePredicates.ts: -------------------------------------------------------------------------------- 1 | import { 2 | computeKey, 3 | getPrintableArray, 4 | getPrintable, 5 | } from '../internal/format'; 6 | 7 | import { 8 | iso8601RegExp, 9 | } from '../internal/regexps'; 10 | 11 | import { 12 | makeCoercionFn, 13 | makeLazyCoercionFn, 14 | makeSetter, 15 | pushError, 16 | } from '../internal/tools'; 17 | 18 | import { 19 | makeValidator, 20 | softAssert, 21 | } from '../tools'; 22 | 23 | import { 24 | AnyStrictValidator, 25 | Coercion, 26 | InferType, 27 | StrictValidator, 28 | } from '../types'; 29 | 30 | import { 31 | hasExactLength, 32 | } from './cascadingPredicates'; 33 | 34 | /** 35 | * Create a validator that always returns true and never refines the type. 36 | */ 37 | export function isUnknown() { 38 | return makeValidator({ 39 | test: (value, state): value is unknown => { 40 | return true; 41 | }, 42 | }); 43 | } 44 | 45 | /** 46 | * Create a validator that only returns true when the tested value is exactly 47 | * the same as the expected one. 48 | * 49 | * Refines the type to the provided literal, as much as possible. For example, 50 | * if you provide a literal string as parameter, the resulting type will be 51 | * whatever this literal string is, not a generic `string` type. The same is 52 | * true for `null`, `true`, `false`, and literal numbers. 53 | */ 54 | export function isLiteral(expected: null): StrictValidator; 55 | export function isLiteral(expected: true): StrictValidator; 56 | export function isLiteral(expected: false): StrictValidator; 57 | export function isLiteral(expected: T): StrictValidator; 58 | export function isLiteral(expected: T): StrictValidator; 59 | export function isLiteral(expected: T): StrictValidator; 60 | export function isLiteral(expected: T) { 61 | return makeValidator({ 62 | test: (value, state): value is T => { 63 | if (value !== expected) 64 | return pushError(state, `Expected ${getPrintable(expected)} (got ${getPrintable(value)})`); 65 | 66 | return true; 67 | }, 68 | }); 69 | }; 70 | 71 | /** 72 | * Create a validator that only returns true when the tested value is a string. 73 | * Refines the type to `string`. 74 | */ 75 | export function isString() { 76 | return makeValidator({ 77 | test: (value, state): value is string => { 78 | if (typeof value !== `string`) 79 | return pushError(state, `Expected a string (got ${getPrintable(value)})`); 80 | 81 | return true; 82 | }, 83 | }); 84 | } 85 | 86 | /** 87 | * Create a validator that only returns true when the tested value is amongst 88 | * the expected set of values. Accepts both value arrays (['foo', 'bar']) and 89 | * dictionaries ({foo: 'foo', bar: 'bar'}), which makes them compatible with 90 | * regular TypeScript enumerations (as long as they're not declared as `const 91 | * enum`). 92 | * 93 | * Refines the type to the enumeration values as much as possible. For example, 94 | * if you pass a TypeScript enumeration as expected set of values, the 95 | * resulting type will be the union of all values in this enumeration. 96 | */ 97 | export function isEnum(values: ReadonlyArray): StrictValidator; 98 | export function isEnum(enumSpec: Record): StrictValidator; 99 | export function isEnum(enumSpec: any): StrictValidator { 100 | const valuesArray: T[] = Array.isArray(enumSpec) ? enumSpec : Object.values(enumSpec); 101 | const isAlphaNum = valuesArray.every(item => typeof item === 'string' || typeof item === 'number'); 102 | 103 | const values = new Set(valuesArray); 104 | if (values.size === 1) 105 | return isLiteral([...values][0]); 106 | 107 | return makeValidator({ 108 | test: (value, state): value is T => { 109 | if (!values.has(value as T)) { 110 | if (isAlphaNum) { 111 | return pushError(state, `Expected one of ${getPrintableArray(valuesArray, `or`)} (got ${getPrintable(value)})`); 112 | } else { 113 | return pushError(state, `Expected a valid enumeration value (got ${getPrintable(value)})`); 114 | } 115 | } 116 | 117 | return true; 118 | }, 119 | }); 120 | } 121 | 122 | const BOOLEAN_COERCIONS = new Map([ 123 | [`true`, true], 124 | [`True`, true], 125 | [`1`, true], 126 | [1, true], 127 | 128 | [`false`, false], 129 | [`False`, false], 130 | [`0`, false], 131 | [0, false], 132 | ]); 133 | 134 | /** 135 | * Create a validator that only returns true when the tested value is a 136 | * boolean. Refines the type to `boolean`. 137 | * 138 | * Supports coercion: 139 | * - 'true' / 'True' / '1' / 1 will turn to `true` 140 | * - 'false' / 'False' / '0' / 0 will turn to `false` 141 | */ 142 | export function isBoolean() { 143 | return makeValidator({ 144 | test: (value, state): value is boolean => { 145 | if (typeof value !== `boolean`) { 146 | if (typeof state?.coercions !== `undefined`) { 147 | if (typeof state?.coercion === `undefined`) 148 | return pushError(state, `Unbound coercion result`); 149 | 150 | const coercion = BOOLEAN_COERCIONS.get(value); 151 | if (typeof coercion !== `undefined`) { 152 | state.coercions.push([state.p ?? `.`, state.coercion.bind(null, coercion)]); 153 | return true; 154 | } 155 | } 156 | 157 | return pushError(state, `Expected a boolean (got ${getPrintable(value)})`); 158 | } 159 | 160 | return true; 161 | }, 162 | }); 163 | } 164 | 165 | /** 166 | * Create a validator that only returns true when the tested value is a 167 | * number (including floating numbers; use `cascade` and `isInteger` to 168 | * restrict the range further). Refines the type to `number`. 169 | * 170 | * Supports coercion. 171 | */ 172 | export function isNumber() { 173 | return makeValidator({ 174 | test: (value, state): value is number => { 175 | if (typeof value !== `number`) { 176 | if (typeof state?.coercions !== `undefined`) { 177 | if (typeof state?.coercion === `undefined`) 178 | return pushError(state, `Unbound coercion result`); 179 | 180 | let coercion: number | undefined; 181 | if (typeof value === `string`) { 182 | let val; 183 | try { 184 | val = JSON.parse(value); 185 | } catch {} 186 | 187 | // We check against JSON.stringify that the output is the same to ensure that the number can be safely represented in JS 188 | if (typeof val === `number`) { 189 | if (JSON.stringify(val) === value) { 190 | coercion = val; 191 | } else { 192 | return pushError(state, `Received a number that can't be safely represented by the runtime (${value})`); 193 | } 194 | } 195 | } 196 | 197 | if (typeof coercion !== `undefined`) { 198 | state.coercions.push([state.p ?? `.`, state.coercion.bind(null, coercion)]); 199 | return true; 200 | } 201 | } 202 | 203 | return pushError(state, `Expected a number (got ${getPrintable(value)})`); 204 | } 205 | 206 | return true; 207 | }, 208 | }); 209 | } 210 | 211 | /** 212 | * Important: This validator only makes sense when used in conjunction with 213 | * coercion! It will always error when used without. 214 | * 215 | * Create a validator that only returns true when the tested value is a 216 | * JSON representation of the expected type. Refines the type to the 217 | * expected type, and casts the value into its inner value. 218 | */ 219 | export function isPayload(spec: T) { 220 | return makeValidator>({ 221 | test: (value, state): value is InferType => { 222 | if (typeof state?.coercions === `undefined`) 223 | return pushError(state, `The isPayload predicate can only be used with coercion enabled`); 224 | 225 | if (typeof state.coercion === `undefined`) 226 | return pushError(state, `Unbound coercion result`); 227 | 228 | if (typeof value !== `string`) 229 | return pushError(state, `Expected a string (got ${getPrintable(value)})`); 230 | 231 | let inner: unknown; 232 | try { 233 | inner = JSON.parse(value); 234 | } catch { 235 | return pushError(state, `Expected a JSON string (got ${getPrintable(value)})`); 236 | } 237 | 238 | const wrapper = {value: inner}; 239 | if (!spec(inner, {...state, coercion: makeCoercionFn(wrapper, `value`)})) 240 | return false; 241 | 242 | state.coercions.push([state.p ?? `.`, state.coercion.bind(null, wrapper.value)]); 243 | return true; 244 | }, 245 | }); 246 | } 247 | 248 | /** 249 | * Create a validator that only returns true when the tested value is a 250 | * valid date. Refines the type to `Date`. 251 | * 252 | * Supports coercion via one of the following formats: 253 | * - ISO86001 strings 254 | * - Unix timestamps 255 | */ 256 | export function isDate() { 257 | return makeValidator({ 258 | test: (value, state): value is Date => { 259 | if (!(value instanceof Date)) { 260 | if (typeof state?.coercions !== `undefined`) { 261 | if (typeof state?.coercion === `undefined`) 262 | return pushError(state, `Unbound coercion result`); 263 | 264 | let coercion: Date | undefined; 265 | 266 | if (typeof value === `string` && iso8601RegExp.test(value)) { 267 | coercion = new Date(value); 268 | } else { 269 | let timestamp: number | undefined; 270 | if (typeof value === `string`) { 271 | let val; 272 | try { 273 | val = JSON.parse(value); 274 | } catch {} 275 | 276 | if (typeof val === `number`) { 277 | timestamp = val; 278 | } 279 | } else if (typeof value === `number`) { 280 | timestamp = value; 281 | } 282 | 283 | if (typeof timestamp !== `undefined`) { 284 | if (Number.isSafeInteger(timestamp) || !Number.isSafeInteger(timestamp * 1000)) { 285 | coercion = new Date(timestamp * 1000); 286 | } else { 287 | return pushError(state, `Received a timestamp that can't be safely represented by the runtime (${value})`); 288 | } 289 | } 290 | } 291 | 292 | if (typeof coercion !== `undefined`) { 293 | state.coercions.push([state.p ?? `.`, state.coercion.bind(null, coercion)]); 294 | return true; 295 | } 296 | } 297 | 298 | return pushError(state, `Expected a date (got ${getPrintable(value)})`); 299 | } 300 | 301 | return true; 302 | }, 303 | }); 304 | } 305 | 306 | /** 307 | * Create a validator that only returns true when the tested value is an 308 | * array whose all values match the provided subspec. Refines the type to 309 | * `Array`, with `T` being the subspec inferred type. 310 | * 311 | * Supports coercion if the `delimiter` option is set, in which case strings 312 | * will be split accordingly. 313 | */ 314 | export function isArray(spec: T, {delimiter}: {delimiter?: string | RegExp} = {}) { 315 | return makeValidator>>({ 316 | test: (value, state): value is Array> => { 317 | const originalValue = value; 318 | 319 | if (typeof value === `string` && typeof delimiter !== `undefined`) { 320 | if (typeof state?.coercions !== `undefined`) { 321 | if (typeof state?.coercion === `undefined`) 322 | return pushError(state, `Unbound coercion result`); 323 | 324 | value = value.split(delimiter); 325 | } 326 | } 327 | 328 | if (!Array.isArray(value)) 329 | return pushError(state, `Expected an array (got ${getPrintable(value)})`); 330 | 331 | let valid = true; 332 | 333 | for (let t = 0, T = value.length; t < T; ++t) { 334 | valid = spec(value[t], {...state, p: computeKey(state, t), coercion: makeCoercionFn(value, t)}) && valid; 335 | 336 | if (!valid && state?.errors == null) { 337 | break; 338 | } 339 | } 340 | 341 | if (value !== originalValue) 342 | state!.coercions!.push([state!.p ?? `.`, state!.coercion!.bind(null, value)]); 343 | 344 | return valid; 345 | }, 346 | }); 347 | } 348 | 349 | /** 350 | * Create a validator that only returns true when the tested value is an 351 | * set whose all values match the provided subspec. Refines the type to 352 | * `Set`, with `T` being the subspec inferred type. 353 | * 354 | * Supports coercion from arrays (or anything that can be coerced into an 355 | * array). 356 | */ 357 | export function isSet(spec: T, {delimiter}: {delimiter?: string | RegExp} = {}) { 358 | const isArrayValidator = isArray(spec, {delimiter}); 359 | 360 | return makeValidator>>({ 361 | test: (value, state): value is Set> => { 362 | if (Object.getPrototypeOf(value).toString() === `[object Set]`) { 363 | softAssert(value, isInstanceOf(Set)); 364 | 365 | if (typeof state?.coercions !== `undefined`) { 366 | if (typeof state?.coercion === `undefined`) 367 | return pushError(state, `Unbound coercion result`); 368 | 369 | const originalValues = [...value]; 370 | const coercedValues = [...value]; 371 | 372 | if (!isArrayValidator(coercedValues, {...state, coercion: undefined})) 373 | return false; 374 | 375 | const updateValue = () => coercedValues.some((val, t) => val !== originalValues[t]) 376 | ? new Set(coercedValues) 377 | : value; 378 | 379 | state.coercions.push([state.p ?? `.`, makeLazyCoercionFn(state.coercion, value, updateValue)]); 380 | return true; 381 | } else { 382 | let valid = true; 383 | 384 | for (const subValue of value) { 385 | valid = spec(subValue, {...state}) && valid; 386 | 387 | if (!valid && state?.errors == null) { 388 | break; 389 | } 390 | } 391 | 392 | return valid; 393 | } 394 | } 395 | 396 | if (typeof state?.coercions !== `undefined`) { 397 | if (typeof state?.coercion === `undefined`) 398 | return pushError(state, `Unbound coercion result`); 399 | 400 | const store = {value}; 401 | if (!isArrayValidator(value, {...state, coercion: makeCoercionFn(store, `value`)})) 402 | return false; 403 | 404 | state.coercions.push([state.p ?? `.`, makeLazyCoercionFn(state.coercion, value, () => new Set(store.value as any))]); 405 | return true; 406 | } 407 | 408 | return pushError(state, `Expected a set (got ${getPrintable(value)})`); 409 | } 410 | }); 411 | }; 412 | 413 | /** 414 | * Create a validator that only returns true when the tested value is an 415 | * map whose all values match the provided subspecs. Refines the type to 416 | * `Map`, with `U` being the key subspec inferred type and `V` being 417 | * the value subspec inferred type. 418 | * 419 | * Supports coercion from array of tuples (or anything that can be coerced into 420 | * an array of tuples). 421 | */ 422 | export function isMap(keySpec: TKey, valueSpec: TValue) { 423 | const isArrayValidator = isArray(isTuple([keySpec, valueSpec])); 424 | const isRecordValidator = isRecord(valueSpec, {keys: keySpec}); 425 | 426 | return makeValidator, InferType>>({ 427 | test: (value, state): value is Map, InferType> => { 428 | if (Object.getPrototypeOf(value).toString() === `[object Map]`) { 429 | softAssert(value, isInstanceOf(Map)); 430 | 431 | if (typeof state?.coercions !== `undefined`) { 432 | if (typeof state?.coercion === `undefined`) 433 | return pushError(state, `Unbound coercion result`); 434 | 435 | const originalValues = [...value]; 436 | const coercedValues = [...value]; 437 | 438 | if (!isArrayValidator(coercedValues, {...state, coercion: undefined})) 439 | return false; 440 | 441 | const updateValue = () => coercedValues.some((val, t) => val[0] !== originalValues[t][0] || val[1] !== originalValues[t][1]) 442 | ? new Map(coercedValues) 443 | : value; 444 | 445 | state.coercions.push([state.p ?? `.`, makeLazyCoercionFn(state.coercion, value, updateValue)]); 446 | return true; 447 | } else { 448 | let valid = true; 449 | 450 | for (const [key, subValue] of value) { 451 | valid = keySpec(key, {...state}) && valid; 452 | if (!valid && state?.errors == null) { 453 | break; 454 | } 455 | 456 | valid = valueSpec(subValue, {...state, p: computeKey(state, key)}) && valid; 457 | if (!valid && state?.errors == null) { 458 | break; 459 | } 460 | } 461 | 462 | return valid; 463 | } 464 | } 465 | 466 | if (typeof state?.coercions !== `undefined`) { 467 | if (typeof state?.coercion === `undefined`) 468 | return pushError(state, `Unbound coercion result`); 469 | 470 | const store = {value}; 471 | if (Array.isArray(value)) { 472 | if (!isArrayValidator(value, {...state, coercion: undefined})) 473 | return false; 474 | 475 | state.coercions.push([state.p ?? `.`, makeLazyCoercionFn(state.coercion, value, () => new Map(store.value as any))]); 476 | return true; 477 | } else { 478 | if (!isRecordValidator(value, {...state, coercion: makeCoercionFn(store, `value`)})) 479 | return false; 480 | 481 | state.coercions.push([state.p ?? `.`, makeLazyCoercionFn(state.coercion, value, () => new Map(Object.entries(store.value as any)))]); 482 | return true; 483 | } 484 | } 485 | 486 | return pushError(state, `Expected a map (got ${getPrintable(value)})`); 487 | } 488 | }); 489 | }; 490 | 491 | type AnyStrictValidatorTuple = AnyStrictValidator[] | []; 492 | 493 | type InferTypeFromTuple = {[K in keyof T]: InferType}; 494 | 495 | /** 496 | * Create a validator that only returns true when the tested value is a 497 | * tuple whose each value matches the corresponding subspec. Refines the type 498 | * into a tuple whose each item has the type inferred by the corresponding 499 | * tuple. 500 | * 501 | * Supports coercion if the `delimiter` option is set, in which case strings 502 | * will be split accordingly. 503 | */ 504 | export function isTuple(spec: T, {delimiter}: {delimiter?: string | RegExp} = {}) { 505 | const lengthValidator = hasExactLength(spec.length); 506 | 507 | return makeValidator>({ 508 | test: (value, state): value is InferTypeFromTuple => { 509 | if (typeof value === `string` && typeof delimiter !== `undefined`) { 510 | if (typeof state?.coercions !== `undefined`) { 511 | if (typeof state?.coercion === `undefined`) 512 | return pushError(state, `Unbound coercion result`); 513 | 514 | value = value.split(delimiter); 515 | state.coercions.push([state.p ?? `.`, state.coercion.bind(null, value)]); 516 | } 517 | } 518 | 519 | if (!Array.isArray(value)) 520 | return pushError(state, `Expected a tuple (got ${getPrintable(value)})`); 521 | 522 | let valid = lengthValidator(value, {...state}); 523 | 524 | for (let t = 0, T = value.length; t < T && t < spec.length; ++t) { 525 | valid = spec[t](value[t], {...state, p: computeKey(state, t), coercion: makeCoercionFn(value, t)}) && valid; 526 | 527 | if (!valid && state?.errors == null) { 528 | break; 529 | } 530 | } 531 | 532 | return valid; 533 | }, 534 | }); 535 | }; 536 | 537 | /** 538 | * Create a validator that only returns true when the tested value is an 539 | * object with any amount of properties that must all match the provided 540 | * subspec. Refines the type to `Record`, with `T` being the 541 | * subspec inferred type. 542 | * 543 | * Keys can be optionally validated as well by using the `keys` optional 544 | * subspec parameter. 545 | */ 546 | export function isRecord(spec: T, { 547 | keys: keySpec = null, 548 | }: { 549 | keys?: StrictValidator | null, 550 | } = {}) { 551 | const isArrayValidator = isArray(isTuple([keySpec ?? isString(), spec])); 552 | 553 | return makeValidator>>({ 554 | test: (value, state): value is Record> => { 555 | if (Array.isArray(value)) { 556 | if (typeof state?.coercions !== `undefined`) { 557 | if (typeof state?.coercion === `undefined`) 558 | return pushError(state, `Unbound coercion result`); 559 | 560 | if (!isArrayValidator(value, {...state, coercion: undefined})) 561 | return false; 562 | 563 | value = Object.fromEntries(value); 564 | state.coercions.push([state.p ?? `.`, state.coercion.bind(null, value)]); 565 | 566 | return true; 567 | } 568 | } 569 | 570 | if (typeof value !== `object` || value === null) 571 | return pushError(state, `Expected an object (got ${getPrintable(value)})`); 572 | 573 | const keys = Object.keys(value); 574 | 575 | let valid = true; 576 | for (let t = 0, T = keys.length; t < T && (valid || state?.errors != null); ++t) { 577 | const key = keys[t]; 578 | const sub = (value as Record)[key]; 579 | 580 | if (key === `__proto__` || key === `constructor`) { 581 | valid = pushError({...state, p: computeKey(state, key)}, `Unsafe property name`); 582 | continue; 583 | } 584 | 585 | if (keySpec !== null && !keySpec(key, state)) { 586 | valid = false; 587 | continue; 588 | } 589 | 590 | if (!spec(sub, {...state, p: computeKey(state, key), coercion: makeCoercionFn(value, key)})) { 591 | valid = false; 592 | continue; 593 | } 594 | } 595 | 596 | return valid; 597 | }, 598 | }); 599 | } 600 | 601 | /** 602 | * @deprecated Replace `isDict` by `isRecord` 603 | */ 604 | export function isDict(spec: T, opts: { 605 | keys?: StrictValidator | null, 606 | } = {}) { 607 | return isRecord(spec, opts); 608 | } 609 | 610 | // https://stackoverflow.com/a/68261113/880703 611 | type ExtractIndex = {[K in keyof T as {} extends Record ? K : never]: T[K]}; 612 | type RemoveIndex = {[K in keyof T as {} extends Record ? never : K]: T[K]}; 613 | 614 | // https://stackoverflow.com/a/56146934/880703 615 | type UndefinedProperties = {[P in keyof T]-?: undefined extends T[P] ? P : never}[keyof T] 616 | type UndefinedToOptional = Partial>> & Pick>> 617 | 618 | type ObjectType = UndefinedToOptional> & ExtractIndex; 619 | 620 | /** 621 | * Create a validator that only returns true when the tested value is an 622 | * object whose all properties match their corresponding subspec. Refines 623 | * the type into an object whose each property has the type inferred by the 624 | * corresponding subspec. 625 | * 626 | * Unlike `t.isPartial`, `t.isObject` doesn't allow extraneous properties by 627 | * default. This behaviour can be altered by using the `extra` optional 628 | * subspec parameter, which will be called to validate an object only 629 | * containing the extraneous properties. 630 | * 631 | * Calling `t.isObject(..., {extra: t.isRecord(t.isUnknown())})` is 632 | * essentially the same as calling `t.isPartial(...)`. 633 | */ 634 | export function isObject>(props: T, { 635 | extra: extraSpec = null, 636 | }: { 637 | extra?: UnknownValidator | null, 638 | } = {}) { 639 | const specKeys = Object.keys(props); 640 | 641 | // We need to store this type inside an alias, otherwise TS seems to miss the "value is ..." guard 642 | type RequestedShape = ObjectType<{[P in keyof T]: InferType<(typeof props)[P]>} & InferType>; 643 | 644 | const validator = makeValidator({ 645 | test: (value, state): value is RequestedShape => { 646 | if (typeof value !== `object` || value === null) 647 | return pushError(state, `Expected an object (got ${getPrintable(value)})`); 648 | 649 | const keys = new Set([...specKeys, ...Object.keys(value)]); 650 | const extra: {[key: string]: unknown} = {}; 651 | 652 | let valid = true; 653 | for (const key of keys) { 654 | if (key === `constructor` || key === `__proto__`) { 655 | valid = pushError({...state, p: computeKey(state, key)}, `Unsafe property name`); 656 | } else { 657 | const spec = Object.prototype.hasOwnProperty.call(props, key) 658 | ? (props as any)[key] as AnyStrictValidator | undefined 659 | : undefined; 660 | 661 | const sub = Object.prototype.hasOwnProperty.call(value, key) 662 | ? (value as any)[key] as unknown 663 | : undefined; 664 | 665 | if (typeof spec !== `undefined`) { 666 | valid = spec(sub, {...state, p: computeKey(state, key), coercion: makeCoercionFn(value, key)}) && valid; 667 | } else if (extraSpec === null) { 668 | valid = pushError({...state, p: computeKey(state, key)}, `Extraneous property (got ${getPrintable(sub)})`); 669 | } else { 670 | Object.defineProperty(extra, key, { 671 | enumerable: true, 672 | get: () => sub, 673 | set: makeSetter(value, key) 674 | }); 675 | } 676 | } 677 | 678 | if (!valid && state?.errors == null) { 679 | break; 680 | } 681 | } 682 | 683 | if (extraSpec !== null && (valid || state?.errors != null)) 684 | valid = extraSpec(extra, state) && valid; 685 | 686 | return valid; 687 | }, 688 | }); 689 | 690 | return Object.assign(validator, { 691 | properties: props, 692 | }); 693 | }; 694 | 695 | /** 696 | * Create a validator that only returns true when the tested value is an 697 | * object whose all properties match their corresponding subspec. Refines 698 | * the type into an object whose each property has the type inferred by the 699 | * corresponding subspec. 700 | * 701 | * Unlike `t.isObject`, `t.isPartial` allows extraneous properties. The 702 | * resulting type will reflect this behaviour by including an index 703 | * signature (each extraneous property being typed `unknown`). 704 | * 705 | * Calling `t.isPartial(...)` is essentially the same as calling 706 | * `t.isObject(..., {extra: t.isRecord(t.isUnknown())})`. 707 | */ 708 | export function isPartial(props: T) { 709 | return isObject(props, {extra: isRecord(isUnknown())}); 710 | }; 711 | 712 | /** 713 | * Create a validator that only returns true when the tested value is an 714 | * object whose prototype is derived from the given class. Refines the type 715 | * into a class instance. 716 | */ 717 | export const isInstanceOf = InstanceType>(constructor: T) => makeValidator>({ 718 | test: (value, state): value is InstanceType => { 719 | if (!(value instanceof constructor)) 720 | return pushError(state, `Expected an instance of ${constructor.name} (got ${getPrintable(value)})`); 721 | 722 | return true; 723 | }, 724 | }); 725 | 726 | /** 727 | * Create a validator that only returns true when the tested value is an 728 | * object matching any of the provided subspecs. If the optional `exclusive` 729 | * parameter is set to `true`, the behaviour changes so that the validator 730 | * only returns true when exactly one subspec matches. 731 | */ 732 | export const isOneOf = (specs: ReadonlyArray, { 733 | exclusive = false, 734 | }: { 735 | exclusive?: boolean, 736 | } = {}) => makeValidator>({ 737 | test: (value, state): value is InferType => { 738 | const matches: [string, (Coercion[] | undefined)][] = []; 739 | const errorBuffer = typeof state?.errors !== `undefined` 740 | ? [] : undefined; 741 | 742 | for (let t = 0, T = specs.length; t < T; ++t) { 743 | const subErrors = typeof state?.errors !== `undefined` 744 | ? [] : undefined; 745 | 746 | const subCoercions = typeof state?.coercions !== `undefined` 747 | ? [] : undefined; 748 | 749 | if (specs[t](value, {...state, errors: subErrors, coercions: subCoercions, p: `${state?.p ?? `.`}#${t + 1}`})) { 750 | matches.push([`#${t + 1}`, subCoercions]); 751 | if (!exclusive) { 752 | break; 753 | } 754 | } else { 755 | errorBuffer?.push(subErrors![0]); 756 | } 757 | } 758 | 759 | if (matches.length === 1) { 760 | const [, subCoercions] = matches[0]; 761 | if (typeof subCoercions !== `undefined`) 762 | state?.coercions?.push(...subCoercions); 763 | return true; 764 | } 765 | 766 | if (matches.length > 1) 767 | pushError(state, `Expected to match exactly a single predicate (matched ${matches.join(`, `)})`); 768 | else 769 | state?.errors?.push(...errorBuffer!); 770 | 771 | return false; 772 | }, 773 | }); 774 | -------------------------------------------------------------------------------- /sources/tools.ts: -------------------------------------------------------------------------------- 1 | import { 2 | makeCoercionFn, 3 | } from './internal/tools'; 4 | 5 | import { 6 | isTuple, 7 | } from './predicates/typePredicates'; 8 | 9 | import { 10 | AnyStrictValidator, 11 | Coercion, 12 | InferType, 13 | LooseTest, 14 | LooseValidator, 15 | StrictTest, 16 | StrictValidator, 17 | Trait, 18 | } from './types'; 19 | 20 | export function makeTrait(value: U) { 21 | return () => { 22 | return value as U & Trait; 23 | }; 24 | } 25 | 26 | export function makeValidator({test}: {test: StrictTest}): StrictValidator; 27 | export function makeValidator({test}: {test: LooseTest}): LooseValidator; 28 | export function makeValidator({test}: {test: StrictTest | LooseTest}) { 29 | return makeTrait(test)(); 30 | } 31 | 32 | export class TypeAssertionError extends Error { 33 | constructor({errors}: {errors?: string[]} = {}) { 34 | let errorMessage = `Type mismatch`; 35 | 36 | if (errors && errors.length > 0) { 37 | errorMessage += `\n`; 38 | for (const error of errors) { 39 | errorMessage += `\n- ${error}`; 40 | } 41 | } 42 | 43 | super(errorMessage); 44 | } 45 | } 46 | 47 | /** 48 | * Check that the specified value matches the given validator, and throws an 49 | * exception if it doesn't. Refine the type if it passes. 50 | */ 51 | export function assert(val: unknown, validator: T): asserts val is InferType { 52 | if (!validator(val)) { 53 | throw new TypeAssertionError(); 54 | } 55 | } 56 | 57 | /** 58 | * Check that the specified value matches the given validator, and throws an 59 | * exception if it doesn't. Refine the type if it passes. 60 | * 61 | * Thrown exceptions include details about what exactly looks invalid in the 62 | * tested value. 63 | */ 64 | export function assertWithErrors(val: unknown, validator: T): asserts val is InferType { 65 | const errors: string[] = []; 66 | if (!validator(val, {errors})) { 67 | throw new TypeAssertionError({errors}); 68 | } 69 | } 70 | 71 | /** 72 | * Compile-time only. Refine the type as if the validator was matching the 73 | * tested value, but doesn't actually run it. Similar to the classic `as` 74 | * operator in TypeScript. 75 | */ 76 | export function softAssert(val: unknown, validator: T): asserts val is InferType { 77 | // It's a soft assert; we tell TypeScript about the type, but we don't need to check it 78 | } 79 | 80 | /** 81 | * Check that the value matches the given validator. Returns a tuple where the 82 | * first element is the validated value, and the second the reported errors. 83 | * 84 | * If the `errors` field is set to `false` (the default), the error reporting 85 | * will be a single boolean. If set to `true`, it'll be an array of strings. 86 | */ 87 | export function as(value: unknown, validator: T, opts: {coerce?: boolean, errors?: boolean, throw: true}): InferType; 88 | export function as(value: unknown, validator: T, opts: {coerce?: boolean, errors: false, throw?: false}): {value: InferType, errors: undefined} | {value: unknown, errors: true}; 89 | export function as(value: unknown, validator: T, opts: {coerce?: boolean, errors: true, throw?: false}): {value: InferType, errors: undefined} | {value: unknown, errors: Array}; 90 | export function as(value: unknown, validator: T, opts?: {coerce?: boolean, errors?: boolean, throw?: false}): {value: InferType, errors: undefined} | {value: unknown, errors: Array | true}; 91 | export function as(value: unknown, validator: T, {coerce = false, errors: storeErrors, throw: throws}: {coerce?: boolean, errors?: boolean, throw?: boolean} = {}): InferType | {value: InferType, errors: undefined} | {value: unknown, errors: Array | true} { 92 | const errors = storeErrors ? [] : undefined; 93 | 94 | if (!coerce) { 95 | if (validator(value, {errors})) { 96 | return throws ? value : {value, errors: undefined}; 97 | } else if (!throws) { 98 | return {value: undefined as never, errors: errors ?? true}; 99 | } else { 100 | throw new TypeAssertionError({errors}) 101 | } 102 | } 103 | 104 | const state = {value}; 105 | 106 | const coercion = makeCoercionFn(state, `value`); 107 | const coercions: Coercion[] = []; 108 | 109 | if (!validator(value, {errors, coercion, coercions})) { 110 | if (!throws) { 111 | return {value: undefined as never, errors: errors ?? true}; 112 | } else { 113 | throw new TypeAssertionError({errors}); 114 | } 115 | } 116 | 117 | for (const [, apply] of coercions) 118 | apply(); 119 | 120 | if (throws) { 121 | return state.value as InferType; 122 | } else { 123 | return {value: state.value as InferType, errors: undefined}; 124 | } 125 | } 126 | 127 | type FnValidatedArgument = 128 | T extends [AnyStrictValidator, ...AnyStrictValidator[]] 129 | ? {[K in keyof T]: InferType} 130 | : []; 131 | 132 | interface FnValidatedFunction { 133 | (...args: FnValidatedArgument): Ret; 134 | }; 135 | 136 | /** 137 | * Create and return a new function that apply the given validators to each 138 | * corresponding argument passed to the function and throws an exception in 139 | * case of a mismatch. 140 | */ 141 | export function fn(validators: T, fn: (...args: FnValidatedArgument) => Ret): FnValidatedFunction { 142 | const isValidArgList = isTuple(validators); 143 | 144 | return ((...args: FnValidatedArgument) => { 145 | const check = isValidArgList(args); 146 | if (!check) 147 | throw new TypeAssertionError(); 148 | 149 | return fn(...args as any); 150 | }) as FnValidatedFunction; 151 | } 152 | -------------------------------------------------------------------------------- /sources/types.ts: -------------------------------------------------------------------------------- 1 | export type BoundCoercionFn = () => BoundCoercionFn; 2 | export type CoercionFn = (v: any) => BoundCoercionFn; 3 | export type Coercion = [string, BoundCoercionFn]; 4 | 5 | /** 6 | * Given a Typanion validator, return the type the validator guarantees if it 7 | * matches. 8 | */ 9 | export type InferType = U extends Trait ? V : never; 10 | export type Trait = {__trait: Type}; 11 | 12 | export type LooseTest = (value: U, test?: ValidationState) => boolean; 13 | export type StrictTest = (value: U, test?: ValidationState) => value is V; 14 | 15 | export type LooseValidator = LooseTest & Trait; 16 | export type StrictValidator = StrictTest & Trait; 17 | 18 | export type AnyStrictValidator = StrictValidator; 19 | 20 | export type ValidationState = { 21 | p?: string, 22 | errors?: string[], 23 | coercions?: Coercion[], 24 | coercion?: CoercionFn, 25 | }; 26 | -------------------------------------------------------------------------------- /tests/index.test.ts: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai'; 2 | // @ts-ignore 3 | import util from 'util'; 4 | import * as t from '../sources'; 5 | 6 | enum TestEnum{ 7 | foo = `foo`, 8 | bar = `bar`, 9 | } 10 | const testSymbol1 = Symbol() 11 | const testSymbol2 = Symbol() 12 | const otherSymbol = Symbol() 13 | 14 | const VALIDATION_TESTS: { 15 | validator: () => t.StrictValidator; 16 | tests: [unknown, boolean][]; 17 | }[] = [{ 18 | validator: () => t.isString(), 19 | tests: [ 20 | [42, false], 21 | [`foo`, true], 22 | [``, true], 23 | [null, false], 24 | ], 25 | }, { 26 | validator: () => t.isLiteral(`foo`), 27 | tests: [ 28 | [42, false], 29 | [`foo`, true], 30 | [`bar`, false], 31 | ], 32 | }, { 33 | validator: () => t.isLiteral(42), 34 | tests: [ 35 | [21, false], 36 | [42, true], 37 | [`42`, false], 38 | ], 39 | }, { 40 | validator: () => t.isNumber(), 41 | tests: [ 42 | [false, false], 43 | [21, true], 44 | [42, true], 45 | [`42`, false], 46 | [null, false], 47 | [undefined, false], 48 | ], 49 | }, { 50 | validator: () => t.isBoolean(), 51 | tests: [ 52 | [true, true], 53 | [false, true], 54 | [0, false], 55 | [1, false], 56 | [null, false], 57 | [undefined, false], 58 | ], 59 | }, { 60 | validator: () => t.isEnum([`foo`, `bar`]), 61 | tests: [ 62 | [`foo`, true], 63 | [`bar`, true], 64 | [`baz`, false], 65 | ], 66 | }, { 67 | validator: () => t.isEnum({FOO: `foo`, BAR: `bar`}), 68 | tests: [ 69 | [`foo`, true], 70 | [`bar`, true], 71 | [`baz`, false], 72 | ], 73 | }, { 74 | validator: () => t.isEnum([testSymbol1, testSymbol2]), 75 | tests: [ 76 | [testSymbol1, true], 77 | [testSymbol2, true], 78 | [otherSymbol, false], 79 | ], 80 | }, { 81 | validator: () => t.isObject({foo: t.isString()}), 82 | tests: [ 83 | [{}, false], 84 | [{foo: `hello`}, true], 85 | [{foo: 42}, false], 86 | [{foo: `hello`, bar: `test`}, false], 87 | [{bar: `test`}, false], 88 | ], 89 | }, { 90 | validator: () => t.isArray(t.isString()), 91 | tests: [ 92 | [{}, false], 93 | [[], true], 94 | [[`foo`], true], 95 | [[42], false], 96 | ], 97 | }, { 98 | validator: () => t.isSet(t.isString()), 99 | tests: [ 100 | [new Set([]), true], 101 | [new Set([`foo`]), true], 102 | [new Set([42]), false], 103 | [new Set([`foo`, 42]), false], 104 | ], 105 | }, { 106 | validator: () => t.isMap(t.isNumber(), t.isString()), 107 | tests: [ 108 | [new Map([]), true], 109 | [new Map([[42, `foo`]]), true], 110 | [new Map([[`foo`, `bar`]]), false], 111 | [new Map([[42, 42]]), false], 112 | ], 113 | }, { 114 | validator: () => t.isTuple([t.isString(), t.isNumber(), t.isBoolean()]), 115 | tests: [ 116 | [{}, false], 117 | [[], false], 118 | [[`foo`], false], 119 | [[`foo`, 42], false], 120 | [[`foo`, 42, true], true], 121 | [[`foo`, 42, true, false], false], 122 | [[`foo`, true, 42], false], 123 | ], 124 | }, { 125 | validator: () => t.isObject({}, {extra: t.isUnknown()}), 126 | tests: [ 127 | [{}, true], 128 | [{foo: 42}, true], 129 | [42, false], 130 | [undefined, false], 131 | [null, false], 132 | ], 133 | }, { 134 | validator: () => t.isOneOf([t.isObject({foo: t.isString()}, {extra: t.isUnknown()}), t.isObject({bar: t.isString()}, {extra: t.isUnknown()})]), 135 | tests: [ 136 | [{foo: `foo`}, true], 137 | [{bar: `bar`}, true], 138 | [{baz: `baz`}, false], 139 | [{foo: `foo`, bar: `bar`}, true], 140 | ], 141 | }, { 142 | validator: () => t.isOneOf([t.isObject({foo: t.isString()}, {extra: t.isUnknown()}), t.isObject({bar: t.isString()}, {extra: t.isUnknown()})], {exclusive: true}), 143 | tests: [ 144 | [{foo: `foo`}, true], 145 | [{bar: `bar`}, true], 146 | [{baz: `baz`}, false], 147 | [{foo: `foo`, bar: `bar`}, false], 148 | ], 149 | }, { 150 | validator: () => t.isRecord(t.isUnknown()), 151 | tests: [ 152 | [{}, true], 153 | [{foo: 42}, true], 154 | [42, false], 155 | [undefined, false], 156 | [null, false], 157 | ], 158 | }, { 159 | validator: () => t.isRecord(t.isNumber()), 160 | tests: [ 161 | [{}, true], 162 | [{foo: 42}, true], 163 | [{foo: `foo`}, false], 164 | [{foo: 42, bar: 42}, true], 165 | [{foo: 42, bar: `bar`}, false], 166 | [{foo: `foo`, bar: 42}, false], 167 | [42, false], 168 | ], 169 | }, { 170 | validator: () => t.isRecord(t.isNumber(), {keys: t.cascade(t.isString(), [t.isUUID4()])}), 171 | tests: [ 172 | [{}, true], 173 | [{[`806af6da-bd31-4a8a-b3dc-a0fafdc3757a`]: 42}, true], 174 | [{[`806af6da-bd31-4a8a-b3dc-a0fafdc3757a`]: `foo`}, false], 175 | [{foo: 42}, false], 176 | ], 177 | }, { 178 | validator: () => t.isOptional(t.isString()), 179 | tests: [ 180 | [`foo`, true], 181 | [undefined, true], 182 | [42, false], 183 | [null, false], 184 | ], 185 | }, { 186 | validator: () => t.isNullable(t.isString()), 187 | tests: [ 188 | [`foo`, true], 189 | [undefined, false], 190 | [42, false], 191 | [null, true], 192 | ], 193 | }, { 194 | validator: () => t.isOptional(t.isNullable(t.isString())), 195 | tests: [ 196 | [`foo`, true], 197 | [undefined, true], 198 | [42, false], 199 | [null, true], 200 | ], 201 | }, { 202 | validator: () => t.isInstanceOf(Error), 203 | tests: [ 204 | [new Error(), true], 205 | [Error, false], 206 | [`foo`, false], 207 | [new (class CustomError extends Error {})(), true], 208 | ], 209 | }]; 210 | 211 | describe(`Validation Tests`, () => { 212 | for (const {validator, tests} of VALIDATION_TESTS) { 213 | describe(`Validation for ${validator.toString()}`, () => { 214 | const schema = validator(); 215 | 216 | for (const [value, expectation] of tests) { 217 | const what = expectation 218 | ? `allow` 219 | : `disallow`; 220 | 221 | it(`it should ${what} ${JSON.stringify(value)}`, () => { 222 | expect(schema(value)).to.equal(expectation); 223 | }); 224 | } 225 | }); 226 | } 227 | }) 228 | 229 | const ERROR_TESTS: { 230 | validator: () => t.StrictValidator; 231 | tests: [unknown, string[]][]; 232 | }[] = [{ 233 | validator: () => t.isString(), 234 | tests: [ 235 | [42, [`.: Expected a string (got 42)`]], 236 | ], 237 | }, { 238 | validator: () => t.isObject({foo: t.isString()}), 239 | tests: [ 240 | [{}, [`.foo: Expected a string (got undefined)`]], 241 | [{foo: ``, bar: ``}, [`.bar: Extraneous property (got an empty string)`]], 242 | [{foo: ``, [`foo bar`]: ``}, [`.["foo bar"]: Extraneous property (got an empty string)`]], 243 | ], 244 | }, { 245 | validator: () => t.isObject({}), 246 | tests: [ 247 | [JSON.parse(`{"constructor": "foo"}`), [`.constructor: Unsafe property name`]], 248 | [JSON.parse(`{"__proto__": "foo"}`), [`.__proto__: Unsafe property name`]], 249 | ], 250 | }, { 251 | validator: () => t.isRecord(t.isString()), 252 | tests: [ 253 | [JSON.parse(`{"constructor": "foo"}`), [`.constructor: Unsafe property name`]], 254 | [JSON.parse(`{"__proto__": "foo"}`), [`.__proto__: Unsafe property name`]], 255 | ], 256 | }, { 257 | validator: () => t.isEnum([`foo`, `bar`]), 258 | tests: [ 259 | [`baz`, [`.: Expected one of "foo" or "bar" (got "baz")`]], 260 | ], 261 | }, { 262 | validator: () => t.isEnum([5,10,15]), 263 | tests: [ 264 | [42, [`.: Expected one of 5, 10, or 15 (got 42)`]], 265 | ], 266 | }, { 267 | validator: () => t.isEnum({FOO: `foo`, BAR: `bar`}), 268 | tests: [ 269 | [`baz`, [`.: Expected one of "foo" or "bar" (got "baz")`]], 270 | ], 271 | }, { 272 | validator: () => t.isEnum(TestEnum), 273 | tests: [ 274 | [`baz`, [`.: Expected one of "foo" or "bar" (got "baz")`]], 275 | ], 276 | }, { 277 | validator: () => t.isEnum([testSymbol1, testSymbol2]), 278 | tests: [ 279 | [otherSymbol, [`.: Expected a valid enumeration value (got )`]], 280 | ], 281 | }, { 282 | validator: () => t.isOneOf([t.isString(), t.isBoolean()]), 283 | tests: [ 284 | [42, [`.#1: Expected a string (got 42)`, `.#2: Expected a boolean (got 42)`]], 285 | [true, []], 286 | ], 287 | }, { 288 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasForbiddenKeys([`foo`, `bar`])]), 289 | tests: [ 290 | [{foo: 42}, [`.: Forbidden property "foo"`]], 291 | [{foo: undefined, bar: null}, [`.: Forbidden properties "foo" and "bar"`]], 292 | [{foo: false, bar: null}, [`.: Forbidden properties "foo" and "bar"`]], 293 | [{foo: false, bar: 0}, [`.: Forbidden properties "foo" and "bar"`]], 294 | [{foo: 42, bar: 42}, [`.: Forbidden properties "foo" and "bar"`]], 295 | [{baz: 42}, []], 296 | ], 297 | }, { 298 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasForbiddenKeys([`foo`, `bar`], { missingIf: 'missing' })]), 299 | tests: [ 300 | [{foo: 42}, [`.: Forbidden property "foo"`]], 301 | [{foo: undefined, bar: null}, [`.: Forbidden properties "foo" and "bar"`]], 302 | [{foo: false, bar: null}, [`.: Forbidden properties "foo" and "bar"`]], 303 | [{foo: false, bar: 0}, [`.: Forbidden properties "foo" and "bar"`]], 304 | [{foo: 42, bar: 42}, [`.: Forbidden properties "foo" and "bar"`]], 305 | [{baz: 42}, []], 306 | ], 307 | }, { 308 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasForbiddenKeys([`foo`, `bar`], { missingIf: 'undefined' })]), 309 | tests: [ 310 | [{foo: 42}, [`.: Forbidden property "foo"`]], 311 | [{foo: undefined, bar: null}, [`.: Forbidden property "bar"`]], 312 | [{foo: false, bar: null}, [`.: Forbidden properties "foo" and "bar"`]], 313 | [{foo: false, bar: 0}, [`.: Forbidden properties "foo" and "bar"`]], 314 | [{foo: 42, bar: 42}, [`.: Forbidden properties "foo" and "bar"`]], 315 | [{baz: 42}, []], 316 | ], 317 | }, { 318 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasForbiddenKeys([`foo`, `bar`], { missingIf: 'nil' })]), 319 | tests: [ 320 | [{foo: 42}, [`.: Forbidden property "foo"`]], 321 | [{foo: undefined, bar: null}, []], 322 | [{foo: false, bar: null}, [`.: Forbidden property "foo"`]], 323 | [{foo: false, bar: 0}, [`.: Forbidden properties "foo" and "bar"`]], 324 | [{foo: 42, bar: 42}, [`.: Forbidden properties "foo" and "bar"`]], 325 | [{baz: 42}, []], 326 | ], 327 | }, { 328 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasForbiddenKeys([`foo`, `bar`], { missingIf: 'falsy' })]), 329 | tests: [ 330 | [{foo: 42}, [`.: Forbidden property "foo"`]], 331 | [{foo: undefined, bar: null}, []], 332 | [{foo: false, bar: null}, []], 333 | [{foo: false, bar: 0}, []], 334 | [{foo: 42, bar: 42}, [`.: Forbidden properties "foo" and "bar"`]], 335 | [{baz: 42}, []], 336 | ], 337 | }, { 338 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasRequiredKeys([`foo`, `bar`])]), 339 | tests: [ 340 | [{foo: 42}, [`.: Missing required property "bar"`]], 341 | [{foo: 42, bar: 42}, []], 342 | [{foo: undefined, bar: null}, []], 343 | [{foo: false, bar: null}, []], 344 | [{foo: false, bar: 0}, []], 345 | [{baz: 42}, [`.: Missing required properties "foo" and "bar"`]], 346 | ], 347 | }, { 348 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasRequiredKeys([`foo`, `bar`], { missingIf: 'missing' })]), 349 | tests: [ 350 | [{foo: 42}, [`.: Missing required property "bar"`]], 351 | [{foo: 42, bar: 42}, []], 352 | [{foo: undefined, bar: null}, []], 353 | [{foo: false, bar: null}, []], 354 | [{foo: false, bar: 0}, []], 355 | [{baz: 42}, [`.: Missing required properties "foo" and "bar"`]], 356 | ], 357 | }, { 358 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasRequiredKeys([`foo`, `bar`], { missingIf: 'undefined' })]), 359 | tests: [ 360 | [{foo: 42}, [`.: Missing required property "bar"`]], 361 | [{foo: 42, bar: 42}, []], 362 | [{foo: undefined, bar: null}, [`.: Missing required property "foo"`]], 363 | [{foo: false, bar: null}, []], 364 | [{foo: false, bar: 0}, []], 365 | [{baz: 42}, [`.: Missing required properties "foo" and "bar"`]], 366 | ], 367 | }, { 368 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasRequiredKeys([`foo`, `bar`], { missingIf: 'nil' })]), 369 | tests: [ 370 | [{foo: 42}, [`.: Missing required property "bar"`]], 371 | [{foo: 42, bar: 42}, []], 372 | [{foo: undefined, bar: null}, [`.: Missing required properties "foo" and "bar"`]], 373 | [{foo: false, bar: null}, [`.: Missing required property "bar"`]], 374 | [{foo: false, bar: 0}, []], 375 | [{baz: 42}, [`.: Missing required properties "foo" and "bar"`]], 376 | ], 377 | }, { 378 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasRequiredKeys([`foo`, `bar`], { missingIf: 'falsy' })]), 379 | tests: [ 380 | [{foo: 42}, [`.: Missing required property "bar"`]], 381 | [{foo: 42, bar: 42}, []], 382 | [{foo: undefined, bar: null}, [`.: Missing required properties "foo" and "bar"`]], 383 | [{foo: false, bar: null}, [`.: Missing required properties "foo" and "bar"`]], 384 | [{foo: false, bar: 0}, [`.: Missing required properties "foo" and "bar"`]], 385 | [{baz: 42}, [`.: Missing required properties "foo" and "bar"`]], 386 | ], 387 | }, { 388 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasAtLeastOneKey([`foo`, `bar`])]), 389 | tests: [ 390 | [{foo: 42}, []], 391 | [{foo: 42, bar: 42}, []], 392 | [{foo: undefined, bar: null}, []], 393 | [{foo: false, bar: null}, []], 394 | [{foo: false, bar: 0}, []], 395 | [{baz: 42}, [`.: Missing at least one property from "foo" or "bar"`]], 396 | ], 397 | }, { 398 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasAtLeastOneKey([`foo`, `bar`], { missingIf: 'missing' })]), 399 | tests: [ 400 | [{foo: 42}, []], 401 | [{foo: 42, bar: 42}, []], 402 | [{foo: undefined, bar: null}, []], 403 | [{foo: false, bar: null}, []], 404 | [{foo: false, bar: 0}, []], 405 | [{baz: 42}, [`.: Missing at least one property from "foo" or "bar"`]], 406 | ], 407 | }, { 408 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasAtLeastOneKey([`foo`, `bar`], { missingIf: 'undefined' })]), 409 | tests: [ 410 | [{foo: 42}, []], 411 | [{foo: 42, bar: 42}, []], 412 | [{foo: undefined, bar: null}, []], 413 | [{foo: false, bar: null}, []], 414 | [{foo: false, bar: 0}, []], 415 | [{baz: 42}, [`.: Missing at least one property from "foo" or "bar"`]], 416 | ], 417 | }, { 418 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasAtLeastOneKey([`foo`, `bar`], { missingIf: 'nil' })]), 419 | tests: [ 420 | [{foo: 42}, []], 421 | [{foo: 42, bar: 42}, []], 422 | [{foo: undefined, bar: null}, [`.: Missing at least one property from "foo" or "bar"`]], 423 | [{foo: false, bar: null}, []], 424 | [{foo: false, bar: 0}, []], 425 | [{baz: 42}, [`.: Missing at least one property from "foo" or "bar"`]], 426 | ], 427 | }, { 428 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasAtLeastOneKey([`foo`, `bar`], { missingIf: 'falsy' })]), 429 | tests: [ 430 | [{foo: 42}, []], 431 | [{foo: 42, bar: 42}, []], 432 | [{foo: undefined, bar: null}, [`.: Missing at least one property from "foo" or "bar"`]], 433 | [{foo: false, bar: null}, [`.: Missing at least one property from "foo" or "bar"`]], 434 | [{foo: false, bar: 0}, [`.: Missing at least one property from "foo" or "bar"`]], 435 | [{baz: 42}, [`.: Missing at least one property from "foo" or "bar"`]], 436 | ], 437 | }, { 438 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasMutuallyExclusiveKeys([`foo`, `bar`])]), 439 | tests: [ 440 | [{foo: 42}, []], 441 | [{bar: 42}, []], 442 | [{foo: undefined, bar: null}, [`.: Mutually exclusive properties "foo" and "bar"`]], 443 | [{foo: false, bar: null}, [`.: Mutually exclusive properties "foo" and "bar"`]], 444 | [{foo: 42, bar: 42, baz: 42}, [`.: Mutually exclusive properties "foo" and "bar"`]], 445 | [{baz: 42}, []], 446 | ], 447 | }, { 448 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasMutuallyExclusiveKeys([`foo`, `bar`], { missingIf: 'missing' })]), 449 | tests: [ 450 | [{foo: 42}, []], 451 | [{bar: 42}, []], 452 | [{foo: undefined, bar: null}, [`.: Mutually exclusive properties "foo" and "bar"`]], 453 | [{foo: false, bar: null}, [`.: Mutually exclusive properties "foo" and "bar"`]], 454 | [{foo: false, bar: 0}, [`.: Mutually exclusive properties "foo" and "bar"`]], 455 | [{foo: 42, bar: 42, baz: 42}, [`.: Mutually exclusive properties "foo" and "bar"`]], 456 | [{baz: 42}, []], 457 | ], 458 | }, { 459 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasMutuallyExclusiveKeys([`foo`, `bar`], { missingIf: 'undefined' })]), 460 | tests: [ 461 | [{foo: 42}, []], 462 | [{bar: 42}, []], 463 | [{foo: undefined, bar: null}, []], 464 | [{foo: false, bar: null}, [`.: Mutually exclusive properties "foo" and "bar"`]], 465 | [{foo: false, bar: 0}, [`.: Mutually exclusive properties "foo" and "bar"`]], 466 | [{foo: 42, bar: 42, baz: 42}, [`.: Mutually exclusive properties "foo" and "bar"`]], 467 | [{baz: 42}, []], 468 | ], 469 | }, { 470 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasMutuallyExclusiveKeys([`foo`, `bar`], { missingIf: 'nil' })]), 471 | tests: [ 472 | [{foo: 42}, []], 473 | [{bar: 42}, []], 474 | [{foo: undefined, bar: null}, []], 475 | [{foo: false, bar: null}, []], 476 | [{foo: false, bar: 0}, [`.: Mutually exclusive properties "foo" and "bar"`]], 477 | [{foo: 42, bar: 42, baz: 42}, [`.: Mutually exclusive properties "foo" and "bar"`]], 478 | [{baz: 42}, []], 479 | ], 480 | }, { 481 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasMutuallyExclusiveKeys([`foo`, `bar`], { missingIf: 'falsy' })]), 482 | tests: [ 483 | [{foo: 42}, []], 484 | [{bar: 42}, []], 485 | [{foo: undefined, bar: null}, []], 486 | [{foo: false, bar: null}, []], 487 | [{foo: false, bar: 0}, []], 488 | [{foo: 42, bar: 42, baz: 42}, [`.: Mutually exclusive properties "foo" and "bar"`]], 489 | [{baz: 42}, []], 490 | ], 491 | }, { 492 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasKeyRelationship(`foo`, t.KeyRelationship.Forbids, [`bar`, `baz`])]), 493 | tests: [ 494 | [{foo: 42}, []], 495 | [{bar: 42}, []], 496 | [{foo: 42, bar: 42}, [`.: Property "foo" forbids using property "bar"`]], 497 | [{foo: 42, bar: 42, baz: 42}, [`.: Property "foo" forbids using properties "bar" or "baz"`]], 498 | [{foo: 42, qux: 42}, []], 499 | ], 500 | }, { 501 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasKeyRelationship(`foo`, t.KeyRelationship.Forbids, [`bar`, `baz`], {ignore: [false]})]), 502 | tests: [ 503 | [{foo: 42}, []], 504 | [{bar: 42}, []], 505 | [{foo: false, bar: 42}, []], 506 | [{foo: 42, bar: false}, []], 507 | [{foo: 42, bar: false, baz: false}, []], 508 | [{foo: 42, bar: false, baz: 42}, [`.: Property "foo" forbids using property "baz"`]], 509 | [{foo: 42, qux: 42}, []], 510 | ], 511 | }, { 512 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasKeyRelationship(`foo`, t.KeyRelationship.Forbids, [`bar`, `baz`], {missingIf: `falsy`})]), 513 | tests: [ 514 | [{foo: 42}, []], 515 | [{bar: 42}, []], 516 | [{foo: false, bar: 42}, []], 517 | [{foo: 42, bar: false}, []], 518 | [{foo: 42, bar: false, baz: false}, []], 519 | [{foo: 42, bar: false, baz: 42}, [`.: Property "foo" forbids using property "baz"`]], 520 | [{foo: 42, qux: 42}, []], 521 | ], 522 | }, { 523 | validator: () => t.cascade(t.isRecord(t.isUnknown()), [t.hasKeyRelationship(`foo`, t.KeyRelationship.Requires, [`bar`, `baz`])]), 524 | tests: [ 525 | [{foo: 42}, [`.: Property "foo" requires using properties "bar" and "baz"`]], 526 | [{bar: 42}, []], 527 | [{foo: 42, bar: 42}, [`.: Property "foo" requires using property "baz"`]], 528 | [{foo: 42, bar: 42, baz: 42}, []], 529 | [{foo: 42, qux: 42}, [`.: Property "foo" requires using properties "bar" and "baz"`]], 530 | ], 531 | }]; 532 | 533 | for (const {validator, tests} of ERROR_TESTS) { 534 | describe(`Errors for ${validator.toString()}`, () => { 535 | const schema = validator(); 536 | 537 | for (const [value, expectations] of tests) { 538 | const what = expectations.length !== 1 539 | ? `errors` 540 | : `error`; 541 | 542 | it(`Report the right ${what} for ${JSON.stringify(value)}`, () => { 543 | const errors: string[] = []; 544 | 545 | expect(schema(value, {errors})).to.equal(expectations.length === 0); 546 | expect(errors).to.deep.equal(expectations); 547 | }); 548 | } 549 | }); 550 | } 551 | 552 | const COERCION_TESTS: { 553 | validator: () => t.StrictValidator; 554 | tests: ([unknown, [], any] | [unknown, string[]])[], 555 | }[] = [{ 556 | validator: () => t.isObject({foo: t.isBoolean()}), 557 | tests: [ 558 | [{foo: `true`}, [], {foo: true}], 559 | [{foo: `True`}, [], {foo: true}], 560 | [{foo: `1`}, [], {foo: true}], 561 | [{foo: 1}, [], {foo: true}], 562 | 563 | [{foo: `false`}, [], {foo: false}], 564 | [{foo: `False`}, [], {foo: false}], 565 | [{foo: `0`}, [], {foo: false}], 566 | [{foo: 0}, [], {foo: false}], 567 | 568 | [{foo: `truE`}, [`.foo: Expected a boolean (got \"truE\")`]], 569 | [{foo: `fAlse`}, [`.foo: Expected a boolean (got \"fAlse\")`]], 570 | [{foo: `42`}, [`.foo: Expected a boolean (got \"42\")`]], 571 | [{foo: 42}, [`.foo: Expected a boolean (got 42)`]], 572 | ], 573 | }, { 574 | validator: () => t.isObject({foo: t.isNumber()}), 575 | tests: [ 576 | [{foo: `4242`}, [], {foo: 4242}], 577 | [{foo: `42.42`}, [], {foo: 42.42}], 578 | [{foo: `0`}, [], {foo: 0}], 579 | 580 | [{foo: `-4242`}, [], {foo: -4242}], 581 | [{foo: `-42.42`}, [], {foo: -42.42}], 582 | 583 | [{foo: `123456789`.repeat(5)}, [`.foo: Received a number that can't be safely represented by the runtime (${`123456789`.repeat(5)})`]], 584 | [{foo: `0.123456789123456789123456789`}, [`.foo: Received a number that can't be safely represented by the runtime (0.123456789123456789123456789)`]], 585 | ], 586 | }, { 587 | validator: () => t.isObject({foo: t.isDate()}), 588 | tests: [ 589 | [{foo: `0`}, [], {foo: new Date(`1970-01-01T00:00:00.000Z`)}], 590 | [{foo: 0}, [], {foo: new Date(`1970-01-01T00:00:00.000Z`)}], 591 | 592 | [{foo: `679881600`}, [], {foo: new Date(`1991-07-19T00:00:00.000Z`)}], 593 | [{foo: 679881600}, [], {foo: new Date(`1991-07-19T00:00:00.000Z`)}], 594 | 595 | [{foo: `42.42`}, [`.foo: Received a timestamp that can't be safely represented by the runtime (42.42)`]], 596 | [{foo: `hello`}, [`.foo: Expected a date (got \"hello\")`]], 597 | ], 598 | }, { 599 | validator: () => t.isArray(t.isBoolean()), 600 | tests: [ 601 | [[`true`], [], [true]], 602 | ], 603 | }, { 604 | validator: () => t.isObject({foo: t.isArray(t.isBoolean(), {delimiter: /,/})}), 605 | tests: [ 606 | [{foo: `true`}, [], {foo: [true]}], 607 | ], 608 | }, { 609 | validator: () => t.isObject({foo: t.isArray(t.isBoolean(), {delimiter: /,/})}), 610 | tests: [ 611 | [{foo: `true,false`}, [], {foo: [true, false]}], 612 | ], 613 | }, { 614 | validator: () => t.isObject({foo: t.isSet(t.isBoolean())}), 615 | tests: [ 616 | [{foo: []}, [], {foo: new Set([])}], 617 | [{foo: [`true`, `false`]}, [], {foo: new Set([true, false])}], 618 | [{foo: [true, false]}, [], {foo: new Set([true, false])}], 619 | [{foo: new Set([true, false])}, [], {foo: new Set([true, false])}], 620 | [{foo: new Set([`true`, false])}, [], {foo: new Set([true, false])}], 621 | ], 622 | }, { 623 | validator: () => t.isObject({foo: t.isSet(t.isBoolean(), {delimiter: /,/})}), 624 | tests: [ 625 | [{foo: `true,false`}, [], {foo: new Set([true, false])}], 626 | [{foo: `true,foo`}, [`.foo[1]: Expected a boolean (got "foo")`]], 627 | ], 628 | }, { 629 | validator: () => t.isObject({foo: t.isMap(t.isNumber(), t.isBoolean())}), 630 | tests: [ 631 | [{foo: []}, [], {foo: new Map([])}], 632 | [{foo: [[`42`, `false`]]}, [], {foo: new Map([[42, false]])}], 633 | [{foo: [[42, false]]}, [], {foo: new Map([[42, false]])}], 634 | [{foo: new Map([[42, false]])}, [], {foo: new Map([[42, false]])}], 635 | [{foo: new Map([[`42`, `false`]])}, [], {foo: new Map([[42, false]])}], 636 | ], 637 | }, { 638 | validator: () => t.isTuple([t.isString(), t.isNumber(), t.isBoolean()]), 639 | tests: [ 640 | [[`hello`, `42`, `true`], [], [`hello`, 42, true]], 641 | ], 642 | }, { 643 | validator: () => t.isObject({foo: t.isTuple([t.isString(), t.isNumber(), t.isBoolean()], {delimiter: /,/})}), 644 | tests: [ 645 | [{foo: `hello,42,true`}, [], {foo: [`hello`, 42, true]}], 646 | ], 647 | }, { 648 | validator: () => t.isRecord(t.isBoolean()), 649 | tests: [ 650 | [{foo: `true`}, [], {foo: true}], 651 | ], 652 | }, { 653 | validator: () => t.isRecord(t.isOneOf([t.isBoolean()])), 654 | tests: [ 655 | [{foo: `true`}, [], {foo: true}], 656 | ], 657 | }, { 658 | validator: () => t.isRecord(t.isOneOf([t.isObject({foo: t.isBoolean(), bar: t.isLiteral(`hello`)}), t.isObject({foo: t.isString(), bar: t.isLiteral(`world`)})])), 659 | tests: [ 660 | [{val: {foo: `true`, bar: `hello`}}, [], {val: {foo: true, bar: `hello`}}], 661 | [{val: {foo: `true`, bar: `world`}}, [], {val: {foo: `true`, bar: `world`}}], 662 | ], 663 | }, { 664 | validator: () => t.isRecord(t.cascade(t.isNumber(), t.isInteger())), 665 | tests: [ 666 | [{val: 42}, [], {val: 42}], 667 | [{val: `42`}, [], {val: 42}], 668 | [{val: `42.21`}, [`.val: Expected to be an integer (got 42.21)`]], 669 | ], 670 | }, { 671 | validator: () => t.isRecord(t.isNumber()), 672 | tests: [ 673 | [[[`val`, 42]], [], {val: 42}], 674 | [[[`val`, 42, 12]], [`.[0]: Expected to have a length of exactly 2 elements (got 3)`]], 675 | ] 676 | }, { 677 | validator: () => t.isPayload(t.isObject({foo: t.isBoolean()})), 678 | tests: [ 679 | [`{"foo": true}`, [], {foo: true}], 680 | [`{"foo": 1}`, [], {foo: true}], 681 | [`{"foo": 0}`, [], {foo: false}], 682 | [`{"foo": "true"}`, [], {foo: true}], 683 | [`{"foo": "false"}`, [], {foo: false}], 684 | ], 685 | }]; 686 | 687 | describe(`Coercion Tests`, () => { 688 | for (const {validator, tests} of COERCION_TESTS) { 689 | describe(`Coercions for ${validator.toString()}`, () => { 690 | const schema = validator(); 691 | 692 | for (const [value, errorExpectations, coercionExpectation] of tests) { 693 | const what = errorExpectations.length > 0 694 | ? `Doesn't coerce` 695 | : `Coerces`; 696 | 697 | it(`${what} ${util.format(value)}, as expected`, () => { 698 | const res = t.as(value, schema, {coerce: true, errors: true}); 699 | expect(res.errors ?? []).to.deep.equal(errorExpectations); 700 | 701 | if (!res.errors) { 702 | try { 703 | expect(res.value).to.deep.equal(coercionExpectation); 704 | } catch (err) { 705 | // @ts-ignore 706 | console.log({value, coercionExpectation}); 707 | throw err; 708 | } 709 | } 710 | }); 711 | } 712 | }); 713 | } 714 | 715 | it(`Doesn't apply coercion if a cascading predicates fail`, () => { 716 | const schema = t.isRecord(t.cascade(t.isNumber(), [t.isInteger()])); 717 | const val = {val: `42.21`}; 718 | 719 | const coercions: t.Coercion[] = []; 720 | expect(schema(val)).to.equal(false); 721 | 722 | expect(val).to.deep.equal({val: `42.21`}); 723 | }); 724 | }); 725 | 726 | describe(`t.fn()`, () => { 727 | it(`should reject a function call with invalid arguments`, () => { 728 | const fn = t.fn([t.isNumber()], val => { 729 | return val * 42; 730 | }); 731 | 732 | expect(() => fn(`foo` as any)).to.throw(); 733 | }); 734 | 735 | it(`should accept a function call with valid arguments`, () => { 736 | const fn = t.fn([t.isNumber()], val => { 737 | return val * 42; 738 | }); 739 | 740 | expect(fn(2)).to.equal(84); 741 | }); 742 | }); 743 | 744 | describe(`t.as()`, () => { 745 | it(`should return a value with "errors" set to "true" if the "errors" option is disabled`, () => { 746 | const res = t.as(null, t.isString()); 747 | expect(res.errors).to.deep.equal(true); 748 | }); 749 | 750 | it(`should return a value with "errors" set to an array if the "errors" option is enabled`, () => { 751 | const res = t.as(null, t.isString(), {errors: true}); 752 | expect(res.errors).to.deep.equal([`.: Expected a string (got null)`]); 753 | }); 754 | 755 | it(`should throw an error if the "throw" option is enabled ("errors" disabled)`, () => { 756 | expect(() => { 757 | t.as(null, t.isString(), {throw: true}); 758 | }).to.throw(/^Type mismatch$/); 759 | }); 760 | 761 | it(`should throw an error if the "throw" option is enabled ("errors" enabled)`, () => { 762 | expect(() => { 763 | t.as(null, t.isString(), {throw: true, errors: true}); 764 | }).to.throw(/^Type mismatch\n\n- \.: Expected a string \(got null\)$/); 765 | }); 766 | 767 | it(`should return an object with "errors" set to undefined when the value is valid ("errors" disabled)`, () => { 768 | const res = t.as(``, t.isString()); 769 | expect(res.errors).to.deep.equal(undefined); 770 | }); 771 | 772 | it(`should return an object with "errors" set to undefined when the value is valid ("errors" enabled)`, () => { 773 | const res = t.as(``, t.isString(), {errors: true}); 774 | expect(res.errors).to.deep.equal(undefined); 775 | }); 776 | 777 | it(`should return an object with "value" set to the input when the value is valid ("errors" disabled)`, () => { 778 | const res = t.as(``, t.isString()); 779 | expect(res.value).to.deep.equal(``); 780 | }); 781 | 782 | it(`should return an object with "value" set to the input when the value is valid ("errors" enabled)`, () => { 783 | const res = t.as(``, t.isString(), {errors: true}); 784 | expect(res.value).to.deep.equal(``); 785 | }); 786 | 787 | it(`should return the input when the value is valid and the "throw" option is enabled`, () => { 788 | const res = t.as(``, t.isString(), {throw: true}); 789 | expect(res).to.deep.equal(``); 790 | }); 791 | 792 | it(`should return an object with "value" set to the casted input when the value is valid after coercion`, () => { 793 | const res = t.as(`42`, t.isNumber(), {coerce: true}); 794 | expect(res.value).to.deep.equal(42); 795 | }); 796 | 797 | it(`should cast the input when the value is valid after coercion and the "throw" option is enabled`, () => { 798 | const res = t.as(`42`, t.isNumber(), {throw: true, coerce: true}); 799 | expect(res).to.deep.equal(42); 800 | }); 801 | }); 802 | -------------------------------------------------------------------------------- /tests/type-guards.ts: -------------------------------------------------------------------------------- 1 | import * as t from '../sources'; 2 | 3 | (foo: unknown) => { 4 | if (true) { 5 | // @ts-expect-error 6 | const bar: unknown[] = foo; 7 | } 8 | 9 | if (t.isLiteral(null)(foo)) { 10 | const bar: null = foo; 11 | } 12 | 13 | if (t.isLiteral(`foo`)(foo)) { 14 | const bar: `foo` = foo; 15 | } 16 | 17 | if (t.isLiteral(true)(foo)) { 18 | const bar: true = foo; 19 | } 20 | 21 | if (t.isLiteral(false)(foo)) { 22 | const bar: false = foo; 23 | } 24 | 25 | if (t.isLiteral(42)(foo)) { 26 | const bar: 42 = foo; 27 | } 28 | } 29 | 30 | (foo: unknown) => { 31 | if (true) { 32 | // @ts-expect-error 33 | const bar: unknown[] = foo; 34 | } 35 | 36 | if (t.isArray(t.isUnknown())(foo)) { 37 | const bar: unknown[] = foo; 38 | } 39 | }; 40 | 41 | 42 | (foo: unknown) => { 43 | if (true) { 44 | // @ts-expect-error 45 | const bar: 'foo' | 'bar' = foo; 46 | } 47 | 48 | if (t.isEnum(['foo', 'bar'])(foo)) { 49 | const bar: 'foo' | 'bar' = foo; 50 | } 51 | 52 | enum Enum { 53 | FOO = 'foo', 54 | BAR = 'bar', 55 | }; 56 | 57 | if (t.isEnum(Enum)(foo)) { 58 | const bar: 'foo' | 'bar' = foo; 59 | } 60 | }; 61 | 62 | (foo: unknown) => { 63 | if (true) { 64 | // @ts-expect-error 65 | const bar: [string, number, boolean] = foo; 66 | } 67 | 68 | if (t.isTuple([t.isString(), t.isNumber(), t.isBoolean()])(foo)) { 69 | const bar: [string, number, boolean] = foo; 70 | } 71 | }; 72 | 73 | { 74 | const foo = [1, 2]; 75 | 76 | if (t.hasMinLength(8)(foo)) { 77 | const bar: unknown[] = foo; 78 | } else { 79 | const bar: unknown[] = foo; 80 | } 81 | } 82 | 83 | (foo: unknown) => { 84 | if (true) { 85 | // @ts-expect-error 86 | const bar: number = foo; 87 | } 88 | 89 | if (t.isNumber()(foo)) { 90 | const bar: number = foo; 91 | } 92 | }; 93 | 94 | (foo: unknown) => { 95 | if (true) { 96 | // @ts-expect-error 97 | const bar: boolean = foo; 98 | } 99 | 100 | if (t.isBoolean()(foo)) { 101 | const bar: boolean = foo; 102 | } 103 | }; 104 | 105 | (foo: unknown) => { 106 | if (true) { 107 | // @ts-expect-error 108 | const bar: number = foo.bar; 109 | } 110 | 111 | if (t.isObject({bar: t.isString()})(foo)) { 112 | // @ts-expect-error 113 | const bar: number = foo.bar; 114 | } 115 | 116 | if (t.isObject({bar: t.isString()})(foo)) { 117 | const bar: string = foo.bar; 118 | } 119 | }; 120 | 121 | (foo: unknown) => { 122 | if (true) { 123 | // @ts-expect-error 124 | const bar: number = foo; 125 | } 126 | 127 | if (t.isObject({}, {extra: t.isDict(t.isString())})(foo)) { 128 | // @ts-expect-error 129 | const bar: number = foo['bar']; 130 | } 131 | 132 | if (t.isObject({}, {extra: t.isDict(t.isString())})(foo)) { 133 | const bar: string = foo['bar']; 134 | } 135 | }; 136 | 137 | (foo: unknown) => { 138 | if (true) { 139 | // @ts-expect-error 140 | const bar: Error = foo; 141 | } 142 | 143 | if (t.isInstanceOf(Error)(foo)) { 144 | const bar: Error = foo; 145 | } 146 | }; 147 | -------------------------------------------------------------------------------- /tests/type-inferences.ts: -------------------------------------------------------------------------------- 1 | import * as t from '../sources'; 2 | 3 | declare const unknown: unknown; 4 | 5 | type AssertEqual = [T, Expected] extends [Expected, T] ? true : false; 6 | 7 | function assertEqual() { 8 | return (val: V, expected: AssertEqual) => {}; 9 | } 10 | 11 | { 12 | const schema = t.isArray(t.isString()); 13 | type MyType = t.InferType; 14 | 15 | // @ts-expect-error 16 | const foo: MyType = 42; 17 | 18 | // @ts-expect-error 19 | const bar: MyType = [42]; 20 | 21 | const baz: MyType = [`foo`]; 22 | } 23 | 24 | { 25 | const schema = t.isObject({foo: t.isOptional(t.isNumber())}); 26 | type MyType = t.InferType; 27 | 28 | // ts-expect-error: cannot work until https://github.com/microsoft/TypeScript/issues/29063 is fixed 29 | // const foo: MyType = 42; 30 | 31 | // ts-expect-error: cannot work until https://github.com/microsoft/TypeScript/issues/29063 is fixed 32 | // const bar: MyType = [42]; 33 | 34 | const baz: MyType = {}; 35 | 36 | const qux: MyType = {foo: 42}; 37 | 38 | // @ts-expect-error 39 | const quux: MyType = {foo: `foo`}; 40 | 41 | const quuz: MyType = {foo: undefined}; 42 | } 43 | 44 | { 45 | const schemaBase = t.isObject({foo: t.isOptional(t.isNumber())}); 46 | const schema = t.isObject({...schemaBase.properties, bar: t.isOptional(t.isString())}); 47 | 48 | type MyType = t.InferType; 49 | 50 | const foo: MyType = {}; 51 | const bar: MyType = {foo: 42}; 52 | const baz: MyType = {bar: `foo`}; 53 | 54 | // @ts-expect-error 55 | const qux: MyType = {foo: `foo`}; 56 | 57 | // @ts-expect-error 58 | const quux: MyType = {bar: 42}; 59 | } 60 | 61 | { 62 | const schema = t.isEnum([`a`, `b`]); 63 | type MyType = t.InferType; 64 | 65 | // @ts-expect-error 66 | const foo: MyType = `c`; 67 | } 68 | 69 | { 70 | const schema = t.isEnum([`a`, `b`] as const); 71 | type MyType = t.InferType; 72 | 73 | // @ts-expect-error 74 | const foo: MyType = `c`; 75 | } 76 | 77 | { 78 | const schema = t.isObject({ 79 | enum: t.isEnum([`a`, `b`] as const) 80 | }); 81 | type MyType = t.InferType; 82 | 83 | // @ts-expect-error 84 | const foo: MyType = {enum: `c`}; 85 | } 86 | 87 | { 88 | const schema = t.isObject({ 89 | map: t.isMap(t.isNumber(), t.isString()) 90 | }); 91 | type MyType = t.InferType; 92 | 93 | // @ts-expect-error 94 | const foo: MyType = {map: new Map([[`42`, `foo`]])}; 95 | 96 | const bar: MyType = {map: new Map([[42, `foo`]])}; 97 | } 98 | 99 | { 100 | const schema = t.isPartial({ 101 | foo: t.isString() 102 | }); 103 | type MyType = t.InferType; 104 | 105 | const foo: MyType = {foo: `c`}; 106 | const bar: MyType = {foo: `c`, test: 42}; 107 | } 108 | 109 | // Does not currently work, see #9 110 | // { 111 | // const schema = t.isObject({ 112 | // enum: t.isEnum([`a`, `b`]) 113 | // } as const); 114 | // type MyType = t.InferType; 115 | // 116 | // // @ts-expect-error 117 | // const foo: MyType = {enum: `c`}; 118 | // } 119 | 120 | { 121 | t.isOneOf([] as const); 122 | } 123 | 124 | { 125 | t.fn([t.isNumber(), t.isString()], (val1, val2) => { 126 | assertEqual()(val1, true); 127 | assertEqual()(val2, true); 128 | }); 129 | 130 | const fn = t.fn([t.isNumber()], val => { 131 | return val.toString(); 132 | }); 133 | 134 | let check: [number] = null as any; 135 | assertEqual>()(check, true); 136 | } 137 | 138 | { 139 | const schema = t.isEnum([`a`, `b`]); 140 | type MyType = t.InferType; 141 | 142 | const res = t.as(null, schema); 143 | if (!res.errors) { 144 | const foo: MyType = res.value; 145 | } else { 146 | // @ts-expect-error 147 | const foo: MyType = res.value; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": false, 5 | "rootDir": "sources", 6 | "outDir": "lib", 7 | }, 8 | "include": [ 9 | "sources/**/*", 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "esModuleInterop": true, 5 | "lib": [ 6 | "es2017" 7 | ], 8 | "module": "commonjs", 9 | "noEmit": true, 10 | "strict": true, 11 | "target": "es2017" 12 | }, 13 | "include": [ 14 | "sources/**/*", 15 | "tests/**/*" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /website/config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | module.exports = { 4 | name: `Typanion`, 5 | repository: `typanion`, 6 | description: `Type-safe runtime type validation with no dependencies`, 7 | algolia: `47075646ffba88bf3475b4b640843da6`, 8 | 9 | icon: { 10 | letter: `T`, 11 | }, 12 | 13 | colors: { 14 | primary: `#70b1fc`, 15 | }, 16 | 17 | sidebar: { 18 | General: [`overview`, `getting-started`, `examples`], 19 | API: [`predicates/cascading`, `predicates/helpers`, `predicates/types`], 20 | }, 21 | 22 | index: { 23 | overview: `/docs`, 24 | getStarted: `/docs/getting-started`, 25 | features: [{ 26 | title: `TypeScript integration`, 27 | description: `Typanion provides strong type inference; if your validator functions pass, TypeScript will refine values accordingly.`, 28 | }, { 29 | title: `Feature complete`, 30 | description: `Despite being very small, Typanion supports error messages, coercions, and various utilities outside of pure JSON validation.`, 31 | }, { 32 | title: `Tree-shakeable`, 33 | description: `Typanion uses a functional approach that lends itself very well to being optimized away by most bundlers.`, 34 | }], 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /website/docs/examples.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: examples 3 | title: Examples 4 | --- 5 | 6 | #### Validate that an unknown value is a port protocol: 7 | 8 | ```ts 9 | const isPort = t.applyCascade(t.isNumber(), [ 10 | t.isInteger(), 11 | t.isInInclusiveRange(1, 65535), 12 | ]); 13 | 14 | isPort(42000); 15 | ``` 16 | 17 | #### Validate that an unknown value is a record with specific fields, regardless of the others: 18 | 19 | ```ts 20 | const isDiv = t.isObject({ 21 | tagName: t.isLiteral(`DIV`), 22 | }, { 23 | extra: t.isUnknown(), 24 | }); 25 | 26 | isDiv({tagName: `div`, appendChild: () => {}}); 27 | ``` 28 | 29 | #### Validate that a specific field is a specific value, and that others are all numbers: 30 | 31 | ```ts 32 | const isModel = t.isObject({ 33 | uid: t.isString(), 34 | }, { 35 | extra: t.isDict(t.isNumber()), 36 | }); 37 | 38 | isModel({uid: `foo`, valA: 12, valB: 24}); 39 | ``` 40 | -------------------------------------------------------------------------------- /website/docs/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: getting-started 3 | title: Getting Started 4 | --- 5 | 6 | ## Installation 7 | 8 | Add Typanion to your project using Yarn: 9 | 10 | ```bash 11 | yarn add typanion 12 | ``` 13 | 14 | ## Overview 15 | 16 | You can mix and match the prebuilt Typanion operators to describe the data structure you need: 17 | 18 | ```ts 19 | import * as t from 'typanion'; 20 | 21 | const isBlogPost = t.isObject({ 22 | title: t.isString(), 23 | description: t.isString(), 24 | published: t.isBoolean(), 25 | }); 26 | ``` 27 | 28 | Then use this validator to validate any `unknown` type you have to work with. If they match, Typanion will provide the refinement to the correct type: 29 | 30 | ```ts 31 | declare const userData: unknown; 32 | 33 | if (isBlogPost(userData)) { 34 | console.log(userData.title); 35 | } 36 | ``` 37 | 38 | If you need to use the derived type into other parts of your application, just use the provided helper to infer it without any risk of them becoming outdated: 39 | 40 | ```ts 41 | import * as t from 'typanion'; 42 | 43 | type BlogPost = t.InferType; 44 | ``` 45 | 46 | Typanion can also list provide detailed errors if you provide an error array: 47 | 48 | ```ts 49 | const errors: string[] = []; 50 | 51 | if (!isBlogPost(userData, {errors})) { 52 | throw new Error(`Validation errors:\n${errors.join(`\n`)}`); 53 | } 54 | ``` 55 | 56 | Various helpers can be used to remove boilerplate: 57 | 58 | ```ts 59 | import * as t from 'typanion'; 60 | 61 | // Will throw if userData isn't a blogPost 62 | t.assert(userData, isBlogPost); 63 | 64 | // Same, but will also return the value (optionally coerced, see below) 65 | const val = t.as(userData, isBlogPost, {throw: true}); 66 | ``` 67 | 68 | You can use the `as` helper along with its `coerce` option to optionally tell Typanion that mutating the value is fine: 69 | 70 | ```ts 71 | import * as t from 'typanion'; 72 | 73 | const validatedData = t.as(userData, isBlogPost, {coerce: true, throw: true})) 74 | 75 | // Will have turned "true" and "false" strings into the proper boolean values 76 | console.log(validatedData.published); 77 | ``` 78 | 79 | Note that coercion may mutate the data received in input. If you do not wish this to happen, consider using [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) to obtain a clone you can pass to the validators. 80 | -------------------------------------------------------------------------------- /website/docs/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: overview 3 | title: Overview 4 | slug: / 5 | --- 6 | 7 | Typanion is a library meant to provide simple but powerful runtime validation that fully integrate with the type system. It provides many operators that should be composed together to describe your data structures, then derives from them both validation protocols and type hints. 8 | -------------------------------------------------------------------------------- /website/docs/predicates/cascading.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: cascading 3 | title: Cascading predicates 4 | --- 5 | 6 | Cascading predicate don't contribute to refining the value type, but are handful to assert that the value itself follows a particular pattern. You would compose them using `cascade` (cf the [Examples](#Examples) section). 7 | 8 | ## `hasExactLength` 9 | 10 | ```ts twoslash 11 | import * as t from 'typanion'; 12 | declare const length: number; 13 | // ---cut--- 14 | const validate = t.hasExactLength(length); 15 | ``` 16 | 17 | Ensure that the values all have a `length` property exactly equal to the specified value. 18 | 19 | ## `hasForbiddenKeys` 20 | 21 | ```ts twoslash 22 | import * as t from 'typanion'; 23 | declare const forbiddenKey1: string; 24 | declare const forbiddenKey2: string; 25 | declare const forbiddenKeyN: Array; 26 | declare const options: {missingIf: t.MissingType}; 27 | // ---cut--- 28 | const validate = t.hasForbiddenKeys([forbiddenKey1, forbiddenKey2, ...forbiddenKeyN], options); 29 | ``` 30 | 31 | Ensure that the objects don't contain any of the specified keys. (cf [`hasMutuallyExclusiveKeys`](#hasMutuallyExclusiveKeys) for the `options` parameter) 32 | 33 | ## `hasKeyRelationship` 34 | 35 | ```ts twoslash 36 | import * as t from 'typanion'; 37 | declare const subjectKey: string; 38 | declare const relationship: t.KeyRelationship; 39 | declare const otherKeys: Array; 40 | declare const ignore: Array | undefined; 41 | // ---cut--- 42 | const validate = t.hasKeyRelationship(subjectKey, relationship, otherKeys, {ignore}); 43 | ``` 44 | 45 | Ensure that when the subject key is found, the specified relationship (one of `t.KeyRelationship.Forbids` or `t.KeyRelationship.Requires`) is true. Values listed in `ignore` will lead their properties to be considered missing for the purpose of this check. 46 | 47 | ## `hasMaxLength` 48 | 49 | ```ts twoslash 50 | import * as t from 'typanion'; 51 | declare const length: number; 52 | // ---cut--- 53 | const validate = t.hasMaxLength(length); 54 | ``` 55 | 56 | Ensure that the values all have a `length` property at most equal to the specified value. 57 | 58 | ## `hasMinLength` 59 | 60 | ```ts twoslash 61 | import * as t from 'typanion'; 62 | declare const length: number; 63 | // ---cut--- 64 | const validate = t.hasMinLength(length); 65 | ``` 66 | 67 | Ensure that the values all have a `length` property at least equal to the specified value. 68 | 69 | ## `hasMutuallyExclusiveKeys` 70 | 71 | ```ts twoslash 72 | import * as t from 'typanion'; 73 | declare const keys: Array; 74 | declare const options: {missingIf: t.MissingType}; 75 | // ---cut--- 76 | const validate = t.hasMutuallyExclusiveKeys(keys, options); 77 | ``` 78 | 79 | Ensure that the objects don't contain more than one of the specified keys. Keys will be considered missing based on `options.missingIf`. 80 | 81 | Options: 82 | - `missingIf`: 83 | - `missing` (default): the key isn't present. 84 | - `undefined`: the key is `undefined`. 85 | - `nil`: the key is either `undefined` or `null`. 86 | - `falsy`: the key has a falsy value (ex: `0`, `false`, `undefined`, `null`) 87 | 88 | ## `hasRequiredKeys` 89 | 90 | ```ts twoslash 91 | import * as t from 'typanion'; 92 | declare const keys: Array; 93 | declare const options: {missingIf: t.MissingType}; 94 | // ---cut--- 95 | const validate = t.hasRequiredKeys(keys, options); 96 | ``` 97 | 98 | Ensure that the objects contain all of the specified keys. (cf [`hasMutuallyExclusiveKeys`](#hasMutuallyExclusiveKeys) for the `options` parameter) 99 | 100 | ## `hasAtLeastOneKey` 101 | 102 | ```ts twoslash 103 | import * as t from 'typanion'; 104 | declare const keys: Array; 105 | declare const options: {missingIf: t.MissingType}; 106 | // ---cut--- 107 | const validate = t.hasAtLeastOneKey(keys, options); 108 | ``` 109 | 110 | Ensure that the objects contain at least one of the specified keys. (cf [`hasMutuallyExclusiveKeys`](#hasMutuallyExclusiveKeys) for the `options` parameter) 111 | 112 | ## `hasUniqueItems` 113 | 114 | ```ts 115 | const validate = t.hasUniqueItems({map?}); 116 | ``` 117 | 118 | Ensure that the values only have unique items (`map` will transform before comparing). 119 | 120 | ## `isAtLeast` 121 | 122 | ```ts 123 | const validate = t.isAtLeast(n); 124 | ``` 125 | 126 | Ensure that the values compare positively with the specified value. 127 | 128 | ## `isAtMost` 129 | 130 | ```ts 131 | const validate = t.isAtMost(n); 132 | ``` 133 | 134 | Ensure that the values compare positively with the specified value. 135 | 136 | ## `isBase64` 137 | 138 | ```ts 139 | const validate = t.isBase64(); 140 | ``` 141 | 142 | Ensure that the values are valid base 64 data. 143 | 144 | ## `isHexColor` 145 | 146 | ```ts 147 | const validate = t.isHexColor({alpha?}); 148 | ``` 149 | 150 | Ensure that the values are hexadecimal colors (enabling `alpha` will allow an additional channel). 151 | 152 | ## `isInExclusiveRange` 153 | 154 | ```ts 155 | const validate = t.isInExclusiveRange(a, b); 156 | ``` 157 | 158 | Ensure that the values compare positively with the specified value. 159 | 160 | ## `isInInclusiveRange` 161 | 162 | ```ts 163 | const validate = t.isInInclusiveRange(a, b); 164 | ``` 165 | 166 | Ensure that the values compare positively with the specified value. 167 | 168 | ## `isInteger` 169 | 170 | ```ts 171 | const validate = t.isInteger(n, {unsafe?}); 172 | ``` 173 | 174 | Ensure that the values are round safe integers (enabling `unsafe` will allow [unsafe ones](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger)). 175 | 176 | ## `isJSON` 177 | 178 | ```ts 179 | const validate = t.isJSON(schema?); 180 | ``` 181 | 182 | Ensure that the values are valid JSON, and optionally match them against a nested schema. Because it's a cascading predicate, it has no bearing on the type inference, and as a result doesn't support coercion. For a JSON predicate that supports coercion, check [`isPayload`](types.md#isPayload). 183 | 184 | ## `isLowerCase` 185 | 186 | ```ts 187 | const validate = t.isLowerCase(); 188 | ``` 189 | 190 | Ensure that the values only contain lowercase characters. 191 | 192 | ## `isNegative` 193 | 194 | ```ts 195 | const validate = t.isNegative(); 196 | ``` 197 | 198 | Ensure that the values are at most 0. 199 | 200 | ## `isPositive` 201 | 202 | ```ts 203 | const validate = t.isPositive(); 204 | ``` 205 | 206 | Ensure that the values are at least 0. 207 | 208 | ## `isISO8601` 209 | 210 | ```ts 211 | const validate = t.isISO8601(); 212 | ``` 213 | 214 | Ensure that the values are dates following the ISO 8601 standard. 215 | 216 | ## `isUpperCase` 217 | 218 | ```ts 219 | const validate = t.isUpperCase(); 220 | ``` 221 | 222 | Ensure that the values only contain uppercase characters. 223 | 224 | ## `isUUID4` 225 | 226 | ```ts 227 | const validate = t.isUUID4(); 228 | ``` 229 | 230 | Ensure that the values are valid UUID 4 strings. 231 | 232 | ## `matchesRegExp` 233 | 234 | ```ts 235 | const validate = t.matchesRegExp(); 236 | ``` 237 | 238 | Ensure that the values all match the given regular expression. 239 | -------------------------------------------------------------------------------- /website/docs/predicates/helpers.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: helpers 3 | title: Helper predicates 4 | --- 5 | 6 | ## `cascade` 7 | 8 | ```ts 9 | const validate = t.cascade(spec, [specA, specB, ...]); 10 | ``` 11 | 12 | Ensure that the values all match `spec` and, if they do, run the followup validations as well. Since those followups will not contribute to the inference (only the lead schema will), you'll typically want to put here anything that's a logical validation, rather than a typed one (cf the [Cascading Predicates](#Cascading-predicate) section). 13 | 14 | One note of caution when using `cascace` with coercion: since the cascading predicates will need to operate on the coerced result of the received value, `cascade` will apply the coercion operations before shelling out to the cascading predicates, and revert them after they have finished executing. This is typically an implementation detail, except if the source data contain setters, which may then be triggered repeatedly. 15 | 16 | ## `isNullable` 17 | 18 | ```ts 19 | const validate = t.isNullable(spec); 20 | ``` 21 | 22 | Add `null` as an allowed value for the given specification. 23 | 24 | ## `isOneOf` 25 | 26 | ```ts 27 | const validate = t.isOneOf([specA, specB], {exclusive?}); 28 | ``` 29 | 30 | Ensure that the values all match any of the provided schema. As a result, the inferred type is the union of all candidates. If `exclusive` is set, only one variant is allowed to match. 31 | 32 | ## `isOptional` 33 | 34 | ```ts 35 | const validate = t.isOptional(spec); 36 | ``` 37 | 38 | Add `undefined` as an allowed value for the given specification. 39 | -------------------------------------------------------------------------------- /website/docs/predicates/types.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: types 3 | title: Type predicates 4 | --- 5 | 6 | ## `isArray` 7 | 8 | ```ts 9 | const validate = t.isArray(values, {delimiter?}): 10 | ``` 11 | 12 | Ensure that the values are arrays whose values all match the specified schema. The `delimiter` option only matters when coercion is enabled, and will allow string inputs where each item is separated by the given delimiter. 13 | 14 | ## `isBoolean` 15 | 16 | ```ts 17 | const validate = t.isBoolean(); 18 | ``` 19 | 20 | Ensure that the values are all booleans. Prefer `isLiteral` if you wish to specifically check for one of `true` or `false`. This predicate supports coercion. 21 | 22 | ## `isDate` 23 | 24 | ```ts 25 | const validate = t.isDate(); 26 | ``` 27 | 28 | Ensure that the values are proper `Date` instances. This predicate supports coercion via either ISO8601, or raw numbers (in which case they're interpreted as the number of *seconds* since epoch, not milliseconds). 29 | 30 | ## `isDict` 31 | 32 | ```ts 33 | const validate = t.isDict(values, {keys?}); 34 | ``` 35 | 36 | Ensure that the values are all a standard JavaScript objects containing an arbitrary number of fields whose values all match the given schema. The `keys` option can be used to apply a schema on the keys as well (this will always have to be strings, so you'll likely want to use `cascade(isString(), [...])` to define the pattern). 37 | 38 | ## `isEnum` 39 | 40 | ```ts 41 | const validate = t.isEnum(values); 42 | ``` 43 | 44 | Ensure that the values are all amongst the allowed set of values. This predicate accepts either an array of literals as parameter, or a record. Since the classic TS enums also happen to be records, you can use them as well: 45 | 46 | ```ts 47 | enum MyEnum { /* ... */ }; 48 | 49 | const validate = t.isEnum(MyEnum); 50 | ``` 51 | 52 | ## `isLiteral` 53 | 54 | ```ts 55 | const validate = t.isLiteral(value); 56 | ``` 57 | 58 | Ensure that the values are strictly equal to the specified expected value. It's an handy tool that you can combine with `isOneOf` and `isObject` to parse structures similar to Redux actions, etc. 59 | 60 | ## `isMap` 61 | 62 | ```ts 63 | const validate = t.isMap(keySpec, valueSpec); 64 | ``` 65 | 66 | Ensure that the values are a `Map` instance. This predicate supports coercion under the form of arrays of tuples (like what standard maps spread into). 67 | 68 | ## `isNumber` 69 | 70 | ```ts 71 | const validate = t.isNumber(); 72 | ``` 73 | 74 | Ensure that the values are all numbers. This predicate supports coercion. 75 | 76 | ## `isObject` 77 | 78 | ```ts 79 | const validate = t.isObject(props, {extra?}); 80 | ``` 81 | 82 | Ensure that the values are plain old objects whose properties match the given shape. Extraneous properties will be aggregated and validated against the optional `extra` schema. If you need to validate against an object that may have any number of extraneous properties, either use `isPartial` instead or set `extra` to `isDict(isUnknown())`. 83 | 84 | ## `isPartial` 85 | 86 | ```ts 87 | const validate = t.isPartial(props); 88 | ``` 89 | 90 | Same as `isObject`, but allows any number of extraneous properties. 91 | 92 | ## `isPayload` 93 | 94 | ```ts 95 | const validate = t.isPayload(spec); 96 | ``` 97 | 98 | Ensure that the values are JSON string whose parsed representation match the given validator. Unlike [`isJSON`](cascading.md#isJSON), `isPayload` will coerce the original value to match the nested spec. The drawback, however, is that it can only be used if the coercion is enabled, as it will otherwise always fail (that's because if it were to pass, then the resulting type refinement would be incorrect). 99 | 100 | ## `isSet` 101 | 102 | ```ts 103 | const validate = t.isSet(values, {delimiter?}); 104 | ``` 105 | 106 | Ensure that the values are a `Set` instance. This predicate supports coercion as either regular arrays or, if configured, delimiter-separated strings. 107 | 108 | ## `isString` 109 | 110 | ```ts 111 | const validate = t.isString(); 112 | ``` 113 | 114 | Ensure that the values are all regular strings. 115 | 116 | ## `isTuple` 117 | 118 | ```ts 119 | const validate = t.isTuple(values, {delimiter?}); 120 | ``` 121 | 122 | Ensure that the values are tuples whose items match the specified schemas. The `delimiter` option only matters when coercion is enabled, and will allow string inputs where each item is separated by the given delimiter. 123 | 124 | ## `isUnknown` 125 | 126 | ```ts 127 | const validate = t.isUnknown(); 128 | ``` 129 | 130 | Accept whatever is the input without validating it, but without refining the type inference either. Note that by default `isUnknown` will forbid `undefined` and `null`, but this can be switched off by explicitly allowing them via `isOptional` and `isNullable`. 131 | 132 | ## `isInstanceOf` 133 | 134 | ```ts 135 | const validate = t.isInstanceOf(constructor); 136 | ``` 137 | 138 | Ensure that the values are instances of a given constructor. 139 | -------------------------------------------------------------------------------- /website/docs/showcase.md: -------------------------------------------------------------------------------- 1 | ```ts twoslash 2 | declare const value: unknown; 3 | declare const server: import('http').Server; 4 | // ---cut--- 5 | import * as t from 'typanion'; 6 | 7 | const isPort = t.cascade(t.isNumber(), [ 8 | t.isInteger(), 9 | t.isInInclusiveRange(1, 65535), 10 | ]); 11 | 12 | if (isPort(value)) { 13 | server.listen(value); 14 | } 15 | ``` 16 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # This file is generated by running "yarn install" inside your project. 2 | # Manual changes might be lost - proceed with caution! 3 | 4 | __metadata: 5 | version: 7 6 | cacheKey: 9 7 | 8 | "@rollup/plugin-typescript@npm:^8.2.1": 9 | version: 8.2.1 10 | resolution: "@rollup/plugin-typescript@npm:8.2.1" 11 | dependencies: 12 | "@rollup/pluginutils": "npm:^3.1.0" 13 | resolve: "npm:^1.17.0" 14 | peerDependencies: 15 | rollup: ^2.14.0 16 | tslib: "*" 17 | typescript: ">=3.7.0" 18 | checksum: da3901092b576057c534bb25f055bd9c96e4441b433c20178321c0b940f0e34b9186d8632ff8127f37cffb05358f42c48835b9530df36da0b56d479e293eb0ee 19 | languageName: node 20 | linkType: hard 21 | 22 | "@rollup/pluginutils@npm:^3.1.0": 23 | version: 3.1.0 24 | resolution: "@rollup/pluginutils@npm:3.1.0" 25 | dependencies: 26 | "@types/estree": "npm:0.0.39" 27 | estree-walker: "npm:^1.0.1" 28 | picomatch: "npm:^2.2.2" 29 | peerDependencies: 30 | rollup: ^1.20.0||^2.0.0 31 | checksum: cd1fa3b565bab5556479aeea8ae14d4c622e2071abbac2de4182715c5220074a55d6ec08be31a1207676d307330eef6697fc739d2d7e1cf34937e87d23770dc3 32 | languageName: node 33 | linkType: hard 34 | 35 | "@types/chai@npm:^4.2.11": 36 | version: 4.2.11 37 | resolution: "@types/chai@npm:4.2.11" 38 | checksum: bb5066bd73d09d8dd1f5afe14c40c0a8a7c3776e8aff5db8cb7099634dca2e7de4b96d72d9c62b01f14d3878683d3111078f5bd5ac0a5a0695a2b5a98147fe82 39 | languageName: node 40 | linkType: hard 41 | 42 | "@types/estree@npm:0.0.39": 43 | version: 0.0.39 44 | resolution: "@types/estree@npm:0.0.39" 45 | checksum: 17875aa97e2734f4a6c5d1e7d4978888961f4c890694c6e0d1c51b17b3eaceba6be1478cf76c50730860002766e4a05c3e8afa12cccbcd5f0b628c458b607357 46 | languageName: node 47 | linkType: hard 48 | 49 | "@types/mocha@npm:^7.0.2": 50 | version: 7.0.2 51 | resolution: "@types/mocha@npm:7.0.2" 52 | checksum: 961d92cbf02d9d898ca793beef6208cf8993e363197a4a8bc81196cbcb150e840254e13db8cfcc2d6b64fd55f8c56e553292b2bed837a4a6e34c9a56a0aa7a89 53 | languageName: node 54 | linkType: hard 55 | 56 | "@types/node@npm:^17.0.21": 57 | version: 17.0.21 58 | resolution: "@types/node@npm:17.0.21" 59 | checksum: 1a3674a912ac910691a636b863c0f67cf5669bf125a77d7d53e42bc77719b8e208b3c1be615379110dda0fd5b9938861df307a157499ca4f0e1f3f36abb548eb 60 | languageName: node 61 | linkType: hard 62 | 63 | "abbrev@npm:1": 64 | version: 1.1.1 65 | resolution: "abbrev@npm:1.1.1" 66 | checksum: 76e7fb9283b13208d5cf55df46669f9cf5e72007cb66595849be2d5e96c0a43704132d030c5705f9447266183986e1e8a4fc3e9578cb60a1f19cf0157664f957 67 | languageName: node 68 | linkType: hard 69 | 70 | "ajv@npm:^6.5.5": 71 | version: 6.12.2 72 | resolution: "ajv@npm:6.12.2" 73 | dependencies: 74 | fast-deep-equal: "npm:^3.1.1" 75 | fast-json-stable-stringify: "npm:^2.0.0" 76 | json-schema-traverse: "npm:^0.4.1" 77 | uri-js: "npm:^4.2.2" 78 | checksum: 6eed21a07200470849bb45d33ff95b9e83e494cae7e1459136bcde49532a1b6d8ced895f6c3309cf3102953d2cbda81eba79e901dfbce541f56744f1ce834c51 79 | languageName: node 80 | linkType: hard 81 | 82 | "ansi-colors@npm:4.1.1": 83 | version: 4.1.1 84 | resolution: "ansi-colors@npm:4.1.1" 85 | checksum: e0851875eb583ed0a4fa7e3f41042a0cb2514e70a58377e30cb211fb938581a522a1178b2ace421bab43739af2b49e69e3e195e867ef596b98ad89ef40703f76 86 | languageName: node 87 | linkType: hard 88 | 89 | "ansi-regex@npm:^2.0.0": 90 | version: 2.1.1 91 | resolution: "ansi-regex@npm:2.1.1" 92 | checksum: 2e99d1e01bb3bb0318b41c595acf106287c23693016753484928b33483cbdced97674ae006b0207a89bbf475c2a8ccbf1f007abfa26defdcebd166d63390c69b 93 | languageName: node 94 | linkType: hard 95 | 96 | "ansi-regex@npm:^3.0.0": 97 | version: 3.0.0 98 | resolution: "ansi-regex@npm:3.0.0" 99 | checksum: 12f9311d91d093ce5117c0026d15e533f57044db53a150fb6d9f2c857221c6b8957fb9e8035126f0af1fb59abd33810d73515f12e5ae8159f3d6d726410659fa 100 | languageName: node 101 | linkType: hard 102 | 103 | "ansi-regex@npm:^4.1.0": 104 | version: 4.1.0 105 | resolution: "ansi-regex@npm:4.1.0" 106 | checksum: 5927936cdd85b2c5afc966d7e33817b0b7695ad301cf5eac4f56447f38f1fadc1b6fe1ab3012813c6e0e25aed31ef68de522d893e0ece13cf48c2e950f733dd8 107 | languageName: node 108 | linkType: hard 109 | 110 | "ansi-styles@npm:^3.2.0, ansi-styles@npm:^3.2.1": 111 | version: 3.2.1 112 | resolution: "ansi-styles@npm:3.2.1" 113 | dependencies: 114 | color-convert: "npm:^1.9.0" 115 | checksum: 88847a8969fcf787779a2cd03e73cd85ac45cbccace293e1227445dd6452cdf11df752c5f9afdb47343439762b96ae7baad1caf848360576d60be5e92f6842ab 116 | languageName: node 117 | linkType: hard 118 | 119 | "anymatch@npm:~3.1.1": 120 | version: 3.1.1 121 | resolution: "anymatch@npm:3.1.1" 122 | dependencies: 123 | normalize-path: "npm:^3.0.0" 124 | picomatch: "npm:^2.0.4" 125 | checksum: daa36372ff3036eba792bcbd04dc8b9a594264fa9e5b2c9a8f37cde7dcf73a703004aefa8312c0128134d584017dcd5b4a9bf22c078ec8cc5abb405185855153 126 | languageName: node 127 | linkType: hard 128 | 129 | "aproba@npm:^1.0.3": 130 | version: 1.2.0 131 | resolution: "aproba@npm:1.2.0" 132 | checksum: 57bbff1b287201cb850e405ee5a494ba1e9e0d4064c21a454d7643863b3f2228d654c4e463841aef9273d7398b9b779d86e2239f11381a1bb5aa7055d03f3d3d 133 | languageName: node 134 | linkType: hard 135 | 136 | "are-we-there-yet@npm:~1.1.2": 137 | version: 1.1.5 138 | resolution: "are-we-there-yet@npm:1.1.5" 139 | dependencies: 140 | delegates: "npm:^1.0.0" 141 | readable-stream: "npm:^2.0.6" 142 | checksum: 95d8e99d6a6ce90b3a60e84de28953d35948f4e2cdc026f9a007f20cf576b595033083fec1199894228a0db3859e3be74bdedc464cf2c4b2a8a93f3885751faf 143 | languageName: node 144 | linkType: hard 145 | 146 | "arg@npm:^4.1.0": 147 | version: 4.1.3 148 | resolution: "arg@npm:4.1.3" 149 | checksum: a60e3881540ab44af1058bf3c9bdbcdd45a82cb930299ae875e609b60b44435410d152b26d55816e8ef2cf1096cfa39271f5b1bd3dd931355f3f24f043dc7ca5 150 | languageName: node 151 | linkType: hard 152 | 153 | "argparse@npm:^1.0.7": 154 | version: 1.0.10 155 | resolution: "argparse@npm:1.0.10" 156 | dependencies: 157 | sprintf-js: "npm:~1.0.2" 158 | checksum: 6112e287a501a4badb8451c3b84420daa75dc4e1ac55d7ce086a492b2cf7d55f2fc0473acb62fc6af2d8013cf255d5d24734c10b4c2c6e440731644f8845c96b 159 | languageName: node 160 | linkType: hard 161 | 162 | "array.prototype.map@npm:^1.0.1": 163 | version: 1.0.2 164 | resolution: "array.prototype.map@npm:1.0.2" 165 | dependencies: 166 | define-properties: "npm:^1.1.3" 167 | es-abstract: "npm:^1.17.0-next.1" 168 | es-array-method-boxes-properly: "npm:^1.0.0" 169 | is-string: "npm:^1.0.4" 170 | checksum: df3009fb20d1bdadd8784574c5f5307647a43bfab7710d93a97b537851d0e59e1bf7ac1ec31631587ed0888b4b147b6b179d16dc503577b74c88f6c4a48d786e 171 | languageName: node 172 | linkType: hard 173 | 174 | "asn1@npm:~0.2.3": 175 | version: 0.2.4 176 | resolution: "asn1@npm:0.2.4" 177 | dependencies: 178 | safer-buffer: "npm:~2.1.0" 179 | checksum: 7d5d50e2f00df3bd7d7b1951b33f4cc2315e50cc07525cf0d87c40b598721dfa07de6055426b8c2cdf4ec248e759e254ae00dc309c33f8ddaa852e035a08bfc8 180 | languageName: node 181 | linkType: hard 182 | 183 | "assert-plus@npm:1.0.0, assert-plus@npm:^1.0.0": 184 | version: 1.0.0 185 | resolution: "assert-plus@npm:1.0.0" 186 | checksum: 38cb6f1d545a2cc3b1c30101324720ae0a659c071615b49274b423d8ca7efaecebc85c128d6dc35a46e4d7c6077385783cb46a1901896e3b7f10f619c7111057 187 | languageName: node 188 | linkType: hard 189 | 190 | "assertion-error@npm:^1.1.0": 191 | version: 1.1.0 192 | resolution: "assertion-error@npm:1.1.0" 193 | checksum: 8e52a3ca8f1f789419cfa4d6e77a4be12ca441ca9ed64a671fd28a0efb1eac304579ee1d5cceb92a43a61d8caac10e00c3b6326ede54c515e0929572320388c8 194 | languageName: node 195 | linkType: hard 196 | 197 | "asynckit@npm:^0.4.0": 198 | version: 0.4.0 199 | resolution: "asynckit@npm:0.4.0" 200 | checksum: e4d1381289f9effe69a4dbc18e8b4e2059113dfb23634d0f4064226042870dbc53175fbf261f982d055fa2952163a8b7608781ea58314a17bb6a2cd6815af4f1 201 | languageName: node 202 | linkType: hard 203 | 204 | "aws-sign2@npm:~0.7.0": 205 | version: 0.7.0 206 | resolution: "aws-sign2@npm:0.7.0" 207 | checksum: 6af052d2392aee7cc9e63bc3737e282dcd392c1bfb4c97b8aff45b6b02a59d62eb6a084bcc16c67ccdb63924bb17e0aa14d38e12724345a1e4f4d648b768ecd5 208 | languageName: node 209 | linkType: hard 210 | 211 | "aws4@npm:^1.8.0": 212 | version: 1.10.0 213 | resolution: "aws4@npm:1.10.0" 214 | checksum: 77a3997925585a073255519cbd03a49541559bce2ac23fb428c362af1fd1bab83a5a5e83494f2260a684801ec3cb42fad838cde1ed6cde2c19f6bbe0587e7e9f 215 | languageName: node 216 | linkType: hard 217 | 218 | "balanced-match@npm:^1.0.0": 219 | version: 1.0.0 220 | resolution: "balanced-match@npm:1.0.0" 221 | checksum: ce6b90a9a21e1ecad52d6e42f299d7f70ca4ae338146cbdd6a8b99cfe7d2a5ba67b70048bf939e79e62ff915bb3445b734ea3e07648c6f523b6b5a7f1e09ae10 222 | languageName: node 223 | linkType: hard 224 | 225 | "bcrypt-pbkdf@npm:^1.0.0": 226 | version: 1.0.2 227 | resolution: "bcrypt-pbkdf@npm:1.0.2" 228 | dependencies: 229 | tweetnacl: "npm:^0.14.3" 230 | checksum: 26dacae8fcd8926b2d477eea173937e4fd1255a665435f8c827a016d3939cbe9c2382946cbedcce37e3bdb069716f26be26c663598449dbeb2fefb64eb478df4 231 | languageName: node 232 | linkType: hard 233 | 234 | "binary-extensions@npm:^2.0.0": 235 | version: 2.0.0 236 | resolution: "binary-extensions@npm:2.0.0" 237 | checksum: d9aeff7603805a72bcdfaf91750a7f19cdd6c4d017bba782ccfbb6e0b9d910fb9d16163815eb253d53503856893b04959b3e627e624b8f22de16a18e24e43471 238 | languageName: node 239 | linkType: hard 240 | 241 | "brace-expansion@npm:^1.1.7": 242 | version: 1.1.11 243 | resolution: "brace-expansion@npm:1.1.11" 244 | dependencies: 245 | balanced-match: "npm:^1.0.0" 246 | concat-map: "npm:0.0.1" 247 | checksum: 5ecc6da29cd3b4d49a832fd8e48f3a8b6ac058f82fe778eb6751ed30a206c5ec5171f6f632aa1946ffb4f8151136740803f620b15edca8437a9348cbb21a8ba8 248 | languageName: node 249 | linkType: hard 250 | 251 | "braces@npm:~3.0.2": 252 | version: 3.0.2 253 | resolution: "braces@npm:3.0.2" 254 | dependencies: 255 | fill-range: "npm:^7.0.1" 256 | checksum: 1aa7f7f39e1dff23894196303515503dd945f36adcb78073ee067b421ecc595265556911183b24d1bc4e51011d3536d63d117cb4493e5123fcc7456596a93637 257 | languageName: node 258 | linkType: hard 259 | 260 | "browser-stdout@npm:1.3.1": 261 | version: 1.3.1 262 | resolution: "browser-stdout@npm:1.3.1" 263 | checksum: 7741b1c2b005c9ada075d953866522f9d55dd97ae32719dfb4d5a0b8fc271b13152e771702453d2cf8add9dc1c3b048689fc7bb170216e8555282ccfca83a754 264 | languageName: node 265 | linkType: hard 266 | 267 | "buffer-from@npm:^1.0.0": 268 | version: 1.1.1 269 | resolution: "buffer-from@npm:1.1.1" 270 | checksum: 04881f5b499d47e8f92b90f9cc140fe7ceb8c2d82ae55bde2f47c5c1a5c9bae2e5e288c9af47d043eeb58be7e64d30bb620aeb8e6ef81e4d2a0cd72b658ad9a4 271 | languageName: node 272 | linkType: hard 273 | 274 | "camelcase@npm:^5.0.0": 275 | version: 5.3.1 276 | resolution: "camelcase@npm:5.3.1" 277 | checksum: 3875260be8f9761ab3870045b7c5c826f584070fe92f5c13a2800a84572d6edf16e6da01db01e135c6d080569fcd690bd2376bdabc3bc80a91da81d1b1c5e773 278 | languageName: node 279 | linkType: hard 280 | 281 | "caseless@npm:~0.12.0": 282 | version: 0.12.0 283 | resolution: "caseless@npm:0.12.0" 284 | checksum: 33c585c818defa51505672e3957409b0f27d760dd711536d36a782627651d5c0cd3dc02b96b45ed702cd78bb88148e7949eb2aad7b1c4e4274fe70184d789c52 285 | languageName: node 286 | linkType: hard 287 | 288 | "chai@npm:^4.3.4": 289 | version: 4.3.4 290 | resolution: "chai@npm:4.3.4" 291 | dependencies: 292 | assertion-error: "npm:^1.1.0" 293 | check-error: "npm:^1.0.2" 294 | deep-eql: "npm:^3.0.1" 295 | get-func-name: "npm:^2.0.0" 296 | pathval: "npm:^1.1.1" 297 | type-detect: "npm:^4.0.5" 298 | checksum: b03417cc45590d3382d971f1cf9df128f915374b4ed4939baae5563bd8f4a67d18a6110a36ac1d9abea9a2a0b678d9f16b12c6e4ac5cbd9963743845c490f273 299 | languageName: node 300 | linkType: hard 301 | 302 | "chalk@npm:^2.4.2": 303 | version: 2.4.2 304 | resolution: "chalk@npm:2.4.2" 305 | dependencies: 306 | ansi-styles: "npm:^3.2.1" 307 | escape-string-regexp: "npm:^1.0.5" 308 | supports-color: "npm:^5.3.0" 309 | checksum: befd2fe888067cfc8ceac2e7a6a62ee763b26112479dce4ee396981288fa21d5cdf3cc1b45692c94c7c6dc3638c4dc3ee6ec1c794efdf42b02e02f93039285ec 310 | languageName: node 311 | linkType: hard 312 | 313 | "check-error@npm:^1.0.2": 314 | version: 1.0.2 315 | resolution: "check-error@npm:1.0.2" 316 | checksum: 5ef1bce78b7105bd5b3f2e7d80a2c2d405a52c3f53d8c48da34d4b8d05f2a63cda26a66e058c4bcc4111be79246ba9ba93074bc4d8e2a65fe3566f8a3f2f7851 317 | languageName: node 318 | linkType: hard 319 | 320 | "chokidar@npm:3.3.1": 321 | version: 3.3.1 322 | resolution: "chokidar@npm:3.3.1" 323 | dependencies: 324 | anymatch: "npm:~3.1.1" 325 | braces: "npm:~3.0.2" 326 | fsevents: "npm:~2.1.2" 327 | glob-parent: "npm:~5.1.0" 328 | is-binary-path: "npm:~2.1.0" 329 | is-glob: "npm:~4.0.1" 330 | normalize-path: "npm:~3.0.0" 331 | readdirp: "npm:~3.3.0" 332 | dependenciesMeta: 333 | fsevents: 334 | optional: true 335 | checksum: c467213a06ff194546249060e2effda8d8e00f7a21284c4da4d14ddafd4a082d49a778e66dddd28c17c3a5d4f9d2eca85d7b7045f411312852695662879d67af 336 | languageName: node 337 | linkType: hard 338 | 339 | "chownr@npm:^2.0.0": 340 | version: 2.0.0 341 | resolution: "chownr@npm:2.0.0" 342 | checksum: 7b240ff920db951fd3841116c5e0e2ec4750e20c85cd044ea78f636202e1fa47ce0a20d48c3c912edc52ea0f1615aba37bdd6297d3a731b517647ed33c3dee09 343 | languageName: node 344 | linkType: hard 345 | 346 | "cliui@npm:^5.0.0": 347 | version: 5.0.0 348 | resolution: "cliui@npm:5.0.0" 349 | dependencies: 350 | string-width: "npm:^3.1.0" 351 | strip-ansi: "npm:^5.2.0" 352 | wrap-ansi: "npm:^5.1.0" 353 | checksum: 61bd58182b6cb9714c8158090abaab9905740287b6e0d0639cca592e5e7b9b79bcbcc77bd3697e243527434d24e37314c13914b2daf68b6a62609ecf286c0723 354 | languageName: node 355 | linkType: hard 356 | 357 | "code-point-at@npm:^1.0.0": 358 | version: 1.1.0 359 | resolution: "code-point-at@npm:1.1.0" 360 | checksum: aa1fa193a67389a2b0760066d23abf8efb25aa548242c61fefb037ebde908f25a9afccd304124c6ed6f812d21adb77c4d2cb05b38270f9674a524de0f4923c0d 361 | languageName: node 362 | linkType: hard 363 | 364 | "color-convert@npm:^1.9.0": 365 | version: 1.9.3 366 | resolution: "color-convert@npm:1.9.3" 367 | dependencies: 368 | color-name: "npm:1.1.3" 369 | checksum: 42f852d574dc58609bba286cd7d10a407e213e20515c0d5d1dd8059b3d4373cd76d1057c3a242f441f2dfc6667badeb790a792662082c8038889c9235f4cd9fa 370 | languageName: node 371 | linkType: hard 372 | 373 | "color-name@npm:1.1.3": 374 | version: 1.1.3 375 | resolution: "color-name@npm:1.1.3" 376 | checksum: b7313c98fd745336a5e1d64921591bcd60e4e0b3894afb56286a4793c4fd304d4a38b00b514845381215ca5ed2994be05d2e1a5a80860b996d26f5f285c77dda 377 | languageName: node 378 | linkType: hard 379 | 380 | "combined-stream@npm:^1.0.6, combined-stream@npm:~1.0.6": 381 | version: 1.0.8 382 | resolution: "combined-stream@npm:1.0.8" 383 | dependencies: 384 | delayed-stream: "npm:~1.0.0" 385 | checksum: c3224efc798a4f2066ff2f65c28d60b48ec73b38bf76331ecc61814875cc5c8a93beccc268ca08aaa98a141c262de5787d68685b6682b8b67ad2dadb8bd2ddd2 386 | languageName: node 387 | linkType: hard 388 | 389 | "concat-map@npm:0.0.1": 390 | version: 0.0.1 391 | resolution: "concat-map@npm:0.0.1" 392 | checksum: 88222f18b3a68b71fe4473a146c8ed3315ec0488703104319c53543ad4668af3e79418ab79e2fa8032ee04c3eb45cc478815b89877a048cc5ba34e201bc15c35 393 | languageName: node 394 | linkType: hard 395 | 396 | "console-control-strings@npm:^1.0.0, console-control-strings@npm:~1.1.0": 397 | version: 1.1.0 398 | resolution: "console-control-strings@npm:1.1.0" 399 | checksum: d286ffd439aac97472557325e6aa4cc3a2eefe495a70a9640b89508880db4bba1bd1b29bb011608c23033d884c84cac8da95c8f12ca0ec69ccc70d6d5f39c618 400 | languageName: node 401 | linkType: hard 402 | 403 | "core-util-is@npm:1.0.2, core-util-is@npm:~1.0.0": 404 | version: 1.0.2 405 | resolution: "core-util-is@npm:1.0.2" 406 | checksum: f6006dfc860ac490b330431be370c58e9b8601d3affe85a08309665970431e12a672ebf1c57799795e145f4fc488c208b2ee992c42fa57faae2649c6f514845e 407 | languageName: node 408 | linkType: hard 409 | 410 | "dashdash@npm:^1.12.0": 411 | version: 1.14.1 412 | resolution: "dashdash@npm:1.14.1" 413 | dependencies: 414 | assert-plus: "npm:^1.0.0" 415 | checksum: 4904e050758457a2c9730e8eb783e1d6ba9c16d115aae263762606479ff94eb5272ed4d3e0e8cadebdb666485af89fcfcc369d32fbc4d78e2cd6088c4be436f4 416 | languageName: node 417 | linkType: hard 418 | 419 | "debug@npm:3.2.6": 420 | version: 3.2.6 421 | resolution: "debug@npm:3.2.6" 422 | dependencies: 423 | ms: "npm:^2.1.1" 424 | checksum: 2ed2def05047d5f70c43a0572ad3db3a027a61f32bfa6a43d02a6e7d70d6abd1d78b7cd8fd4b89a2d026969b65fd4ab67c5702bc01cdb17d1347298aa69f7e09 425 | languageName: node 426 | linkType: hard 427 | 428 | "decamelize@npm:^1.2.0": 429 | version: 1.2.0 430 | resolution: "decamelize@npm:1.2.0" 431 | checksum: 78728512bf37e5c8d093bf375191b808d54bea424d3cf61730d4c00fe11f404bde37c02e5bd28da7d4981411a4c5369e67a72d92b038126ddf5e5fcc0d03b645 432 | languageName: node 433 | linkType: hard 434 | 435 | "deep-eql@npm:^3.0.1": 436 | version: 3.0.1 437 | resolution: "deep-eql@npm:3.0.1" 438 | dependencies: 439 | type-detect: "npm:^4.0.0" 440 | checksum: 65824d56734864b95537d70bdf3a7857d157ad217bb6d16bbe92477b7aed0ab04c0d14c3909abdd47fba35f714d012627309377adba80fb0420dc3a72c746e62 441 | languageName: node 442 | linkType: hard 443 | 444 | "define-properties@npm:^1.1.2, define-properties@npm:^1.1.3": 445 | version: 1.1.3 446 | resolution: "define-properties@npm:1.1.3" 447 | dependencies: 448 | object-keys: "npm:^1.0.12" 449 | checksum: 49eec63bfd91af1fd4e0a80c5a6df540b5e7a5c377bbb4140b19a3b3df0ec3bc0103b1370ea3a185d9fe95301f762215f6169c5765edc7d58af8821d47471cb7 450 | languageName: node 451 | linkType: hard 452 | 453 | "delayed-stream@npm:~1.0.0": 454 | version: 1.0.0 455 | resolution: "delayed-stream@npm:1.0.0" 456 | checksum: 22f11ed342773dbc427e84d5a972e5c67fc34a44bf80eead5a41d8697c9303ae32991e568921cbd82553deeb1b33f3d6ecc148bf0efe3789589c8cb7b0e1a53a 457 | languageName: node 458 | linkType: hard 459 | 460 | "delegates@npm:^1.0.0": 461 | version: 1.0.0 462 | resolution: "delegates@npm:1.0.0" 463 | checksum: 2ef8c043c6caea7f00f23236e0606b00f10d2b497657d63d230e50efdef307936b070734187b03960b9c4afe64ce9e09a77c01da60e661d42dcefec11ce41c30 464 | languageName: node 465 | linkType: hard 466 | 467 | "diff@npm:4.0.2, diff@npm:^4.0.1": 468 | version: 4.0.2 469 | resolution: "diff@npm:4.0.2" 470 | checksum: 1b445113c0727e15646a058b2794df63366bd1e32abf078990b78c2a355fe72e4e3c8de3399f2c5d67f06cd461acdebd91b5f71cb2cd02f7300bdb926a3cd6e2 471 | languageName: node 472 | linkType: hard 473 | 474 | "ecc-jsbn@npm:~0.1.1": 475 | version: 0.1.2 476 | resolution: "ecc-jsbn@npm:0.1.2" 477 | dependencies: 478 | jsbn: "npm:~0.1.0" 479 | safer-buffer: "npm:^2.1.0" 480 | checksum: cef3f6f2462c6c5d03dc1ebe1532afee95655c3bb1aa89c89462588355f0168afa6e7c63b0d2e3989493c1e4090fae33b7f4d1b57d76fdcea226f3555b15fbcd 481 | languageName: node 482 | linkType: hard 483 | 484 | "emoji-regex@npm:^7.0.1": 485 | version: 7.0.3 486 | resolution: "emoji-regex@npm:7.0.3" 487 | checksum: d08d7ce8580bb237987b89c8493a076059fb203c03965c71b90b5a7dae2f88f7be5e5887fa1370bf9db24ef90848150d18d38abbd4f3347e728a4f1ea83a09de 488 | languageName: node 489 | linkType: hard 490 | 491 | "env-paths@npm:^2.2.0": 492 | version: 2.2.0 493 | resolution: "env-paths@npm:2.2.0" 494 | checksum: 4548c9081a83edea67fb8635ca8d2296a1262e747c77262cb3b7e04699fe8239e5688552227d29fe1c6015c4e4805183840f2f5a9f7fcde62e186e903e25bb85 495 | languageName: node 496 | linkType: hard 497 | 498 | "es-abstract@npm:^1.17.0-next.1, es-abstract@npm:^1.17.4, es-abstract@npm:^1.17.5": 499 | version: 1.17.6 500 | resolution: "es-abstract@npm:1.17.6" 501 | dependencies: 502 | es-to-primitive: "npm:^1.2.1" 503 | function-bind: "npm:^1.1.1" 504 | has: "npm:^1.0.3" 505 | has-symbols: "npm:^1.0.1" 506 | is-callable: "npm:^1.2.0" 507 | is-regex: "npm:^1.1.0" 508 | object-inspect: "npm:^1.7.0" 509 | object-keys: "npm:^1.1.1" 510 | object.assign: "npm:^4.1.0" 511 | string.prototype.trimend: "npm:^1.0.1" 512 | string.prototype.trimstart: "npm:^1.0.1" 513 | checksum: 1c89828d0e7dca50a14d8739079a172fe718867ce17328334623ecdeba236fbb94bc2916cfac18fb8c5e6626a0e3e0f1209dad478556d3d09b14216ae68b867b 514 | languageName: node 515 | linkType: hard 516 | 517 | "es-array-method-boxes-properly@npm:^1.0.0": 518 | version: 1.0.0 519 | resolution: "es-array-method-boxes-properly@npm:1.0.0" 520 | checksum: 89cdd370a77eba1a5a66dcaeb8f796caaec0ea45644aeecc4a3c4d70e804d0736dcb061d9008def9a9f1780fbcd07eb47d828166e83fc1bb569eab36f596c189 521 | languageName: node 522 | linkType: hard 523 | 524 | "es-get-iterator@npm:^1.0.2": 525 | version: 1.1.0 526 | resolution: "es-get-iterator@npm:1.1.0" 527 | dependencies: 528 | es-abstract: "npm:^1.17.4" 529 | has-symbols: "npm:^1.0.1" 530 | is-arguments: "npm:^1.0.4" 531 | is-map: "npm:^2.0.1" 532 | is-set: "npm:^2.0.1" 533 | is-string: "npm:^1.0.5" 534 | isarray: "npm:^2.0.5" 535 | checksum: a65b7a93f855a9961d7e7738538d421eee75f832cfc7eaaefc913e07d3a83459bf1e940c20a8859cd2e1dd8bf1dc804bbca617144d6b490af98446cb8dedd0aa 536 | languageName: node 537 | linkType: hard 538 | 539 | "es-to-primitive@npm:^1.2.1": 540 | version: 1.2.1 541 | resolution: "es-to-primitive@npm:1.2.1" 542 | dependencies: 543 | is-callable: "npm:^1.1.4" 544 | is-date-object: "npm:^1.0.1" 545 | is-symbol: "npm:^1.0.2" 546 | checksum: b419a547ffcbd08c23272e283a20018723bd2f39cbee39f66a8fd0fb110b01728ede799bf1365d5981e57a7afb6901916ad147f374e87bb2b11613ebb1d1aaec 547 | languageName: node 548 | linkType: hard 549 | 550 | "escape-string-regexp@npm:1.0.5, escape-string-regexp@npm:^1.0.5": 551 | version: 1.0.5 552 | resolution: "escape-string-regexp@npm:1.0.5" 553 | checksum: 14d2c74a990b4a0ae55f299409693533a620402a6efa02b201d7e2ea60c71a516c36ccfcaf2aa604262eec6c4628bf8b9647e211fb179277cb479bd870c906fa 554 | languageName: node 555 | linkType: hard 556 | 557 | "esprima@npm:^4.0.0": 558 | version: 4.0.1 559 | resolution: "esprima@npm:4.0.1" 560 | bin: 561 | esparse: ./bin/esparse.js 562 | esvalidate: ./bin/esvalidate.js 563 | checksum: 08b3015538b1f7f087a4ea49b5a3d8ff9590ecf7eb43511182c9198cfe168a5cc1736c2ae33263c79cfbe9e984c1880ee971b64ad96e7c84db74488e6ee93c1b 564 | languageName: node 565 | linkType: hard 566 | 567 | "estree-walker@npm:^1.0.1": 568 | version: 1.0.1 569 | resolution: "estree-walker@npm:1.0.1" 570 | checksum: 09ecd33e911a135ff62716f88a48f8dbf1206f8f6d6869205885c1e6d1aebf5049b5e0c725fe2d907b1394a17afb2002f7a57aafdc8b7bbff255dffa8c3b98c9 571 | languageName: node 572 | linkType: hard 573 | 574 | "extend@npm:~3.0.2": 575 | version: 3.0.2 576 | resolution: "extend@npm:3.0.2" 577 | checksum: 312babdc3cfd8d5d003b109f02b8b639e8bdf2262f2f06acebfc3c991d8c004b73c2c10eaaaab00cfb2fb2a760845006806af10945b279d9390eed064505dfdb 578 | languageName: node 579 | linkType: hard 580 | 581 | "extsprintf@npm:1.3.0, extsprintf@npm:^1.2.0": 582 | version: 1.3.0 583 | resolution: "extsprintf@npm:1.3.0" 584 | checksum: afdc88aaa7ad260bd3a4aeabc087aa03de8eaf6346a59685a97943549d8ca54c312b2353e8a4fbe234e59eb202b5b45274a6d959f1309b750bf2a15852ca7485 585 | languageName: node 586 | linkType: hard 587 | 588 | "fast-deep-equal@npm:^3.1.1": 589 | version: 3.1.3 590 | resolution: "fast-deep-equal@npm:3.1.3" 591 | checksum: 5f83fabf1f0bac0df5117e881ee15756dc8a9ee48c8020ed63cb84a7935d78c338dc0982b3b7b6ad0792905f5ef0c35293db9cae2f3208a6f09071c43887a02f 592 | languageName: node 593 | linkType: hard 594 | 595 | "fast-json-stable-stringify@npm:^2.0.0": 596 | version: 2.1.0 597 | resolution: "fast-json-stable-stringify@npm:2.1.0" 598 | checksum: cc64810b004155f5ac29b208ebd5c862599a1a8aef3c4d27a34dfb694db7797e121dceda183507ec4a2a5413d9cb59521fd2540d0d00a5589ee6ea6bfac3c12e 599 | languageName: node 600 | linkType: hard 601 | 602 | "fill-range@npm:^7.0.1": 603 | version: 7.0.1 604 | resolution: "fill-range@npm:7.0.1" 605 | dependencies: 606 | to-regex-range: "npm:^5.0.1" 607 | checksum: e5ccb299de8a12ea5dcef663f658933e2fbdf40aeab3e7e5af9132e82d7f6bdd0984ac2e122dc1825707f33917c308bc40b632b852331c900c317c5d64bb7bf0 608 | languageName: node 609 | linkType: hard 610 | 611 | "find-up@npm:4.1.0": 612 | version: 4.1.0 613 | resolution: "find-up@npm:4.1.0" 614 | dependencies: 615 | locate-path: "npm:^5.0.0" 616 | path-exists: "npm:^4.0.0" 617 | checksum: ae51bbfc4040bb85937589c31dd5f1ac0e80df18feccabcfbdd78ee7a9fc06b198ae73bb87a9d398ab98314dded1cacebde9f77e1c80195a5a68446ba7ee1ae3 618 | languageName: node 619 | linkType: hard 620 | 621 | "find-up@npm:^3.0.0": 622 | version: 3.0.0 623 | resolution: "find-up@npm:3.0.0" 624 | dependencies: 625 | locate-path: "npm:^3.0.0" 626 | checksum: edbd2334fcfb1391af9f246bbf6aa2e7187bdc807150ba7e39dca2c0a7a07560ea49dd7a86e266465de0934958da6ad0f9526d46af1e952f1d2fb858d76bc598 627 | languageName: node 628 | linkType: hard 629 | 630 | "flat@npm:^4.1.0": 631 | version: 4.1.0 632 | resolution: "flat@npm:4.1.0" 633 | dependencies: 634 | is-buffer: "npm:~2.0.3" 635 | bin: 636 | flat: cli.js 637 | checksum: 79ec33adc6f37ba882682eab6c4b81b4aa320ea6d2318fd924529fda655656b1c28bb83c077153f7dcbda6d4fce6e07c7079124596658047d413eed2e2820692 638 | languageName: node 639 | linkType: hard 640 | 641 | "forever-agent@npm:~0.6.1": 642 | version: 0.6.1 643 | resolution: "forever-agent@npm:0.6.1" 644 | checksum: b426cf45f0bdea79970a4320cb550b84d0bcd0530d544e0424456f44272a19641a000ea921f8e58dba5511b71f94d95c80692e3d13ce5f0b766f18426430efd5 645 | languageName: node 646 | linkType: hard 647 | 648 | "form-data@npm:~2.3.2": 649 | version: 2.3.3 650 | resolution: "form-data@npm:2.3.3" 651 | dependencies: 652 | asynckit: "npm:^0.4.0" 653 | combined-stream: "npm:^1.0.6" 654 | mime-types: "npm:^2.1.12" 655 | checksum: 0f88d2d298ac7751fbef88eb1148e709727560bbe6ed17ca1fd10745b8b572cdab7d51d934b97ccdc411add4e39afdb414bc400580a348de2d39a49401f3f5ec 656 | languageName: node 657 | linkType: hard 658 | 659 | "fs-minipass@npm:^2.0.0": 660 | version: 2.1.0 661 | resolution: "fs-minipass@npm:2.1.0" 662 | dependencies: 663 | minipass: "npm:^3.0.0" 664 | checksum: 56d19f9a034cbef50b7fe846a71ab1a6a7ee7906205f9f18b7c9696e1f6d83c4d708a0196c65536f34e569205664840dd4f97f1286a26148a4c5bf74a67fe8db 665 | languageName: node 666 | linkType: hard 667 | 668 | "fs.realpath@npm:^1.0.0": 669 | version: 1.0.0 670 | resolution: "fs.realpath@npm:1.0.0" 671 | checksum: 477fb3547134ce67d71531a19b2597028d2efaeced56a2fcb125ba9994a4204685d256795e4a5b68e5d866d11d8d0dd9050937cb44037beb4caeb3acb75602e2 672 | languageName: node 673 | linkType: hard 674 | 675 | "fsevents@npm:~2.1.2": 676 | version: 2.1.3 677 | resolution: "fsevents@npm:2.1.3" 678 | dependencies: 679 | node-gyp: "npm:latest" 680 | checksum: d3a1babf50868af47da8c5f6ae4df9e6f57a1bbbbae7876684c4d18b852cc4c6f84e4cd70e6182a1d16708f6ea4be496b8f48802d395335945f405bdc8401e23 681 | conditions: os=darwin 682 | languageName: node 683 | linkType: hard 684 | 685 | "fsevents@patch:fsevents@npm%3A~2.1.2#optional!builtin": 686 | version: 2.1.3 687 | resolution: "fsevents@patch:fsevents@npm%3A2.1.3#optional!builtin::version=2.1.3&hash=18f3a7" 688 | dependencies: 689 | node-gyp: "npm:latest" 690 | conditions: os=darwin 691 | languageName: node 692 | linkType: hard 693 | 694 | "function-bind@npm:^1.1.1": 695 | version: 1.1.1 696 | resolution: "function-bind@npm:1.1.1" 697 | checksum: 8a644b8118679030cb3aeb783b024a9ee358b15c5780bdb49fe5d482f6df54672bda860e19bce87d756a5e165740caaa96f5e8487fa98933c327f631e23a5490 698 | languageName: node 699 | linkType: hard 700 | 701 | "gauge@npm:~2.7.3": 702 | version: 2.7.4 703 | resolution: "gauge@npm:2.7.4" 704 | dependencies: 705 | aproba: "npm:^1.0.3" 706 | console-control-strings: "npm:^1.0.0" 707 | has-unicode: "npm:^2.0.0" 708 | object-assign: "npm:^4.1.0" 709 | signal-exit: "npm:^3.0.0" 710 | string-width: "npm:^1.0.1" 711 | strip-ansi: "npm:^3.0.1" 712 | wide-align: "npm:^1.1.0" 713 | checksum: 54255b3bad3e254603770c16b551a6d5d4905e428331c4e37092edacb6cd57ea5f379c6d0bce8c3fd0629d303ed86b5193e282c3aa3cf25c94f2c97367012fd8 714 | languageName: node 715 | linkType: hard 716 | 717 | "get-caller-file@npm:^2.0.1": 718 | version: 2.0.5 719 | resolution: "get-caller-file@npm:2.0.5" 720 | checksum: 24c1eb494b27c789e9267d7220bb131e409427b793f9e2b07f772f8d84c44eb0b42b90c258d858ee758ec6a21092c16a1c78c5fac02c0df7c156bb7113307192 721 | languageName: node 722 | linkType: hard 723 | 724 | "get-func-name@npm:^2.0.0": 725 | version: 2.0.0 726 | resolution: "get-func-name@npm:2.0.0" 727 | checksum: e56bed23b2160cf3aeedb2677ca019334543dd49790c1976e44d168b5f83283747b1a41675706bc114b7a1563da978dbdf6d2b5e9282534dbdeaa2c1184cae6a 728 | languageName: node 729 | linkType: hard 730 | 731 | "getpass@npm:^0.1.1": 732 | version: 0.1.7 733 | resolution: "getpass@npm:0.1.7" 734 | dependencies: 735 | assert-plus: "npm:^1.0.0" 736 | checksum: ffcc370a58a53b0e9e6c5a92db6c7340e3705d84d6bebd448e4afcf7d8a9329cd65be2c3d47ced58c5c8098c3bda21ee65401ba908e3bd37160bec75748a8f54 737 | languageName: node 738 | linkType: hard 739 | 740 | "glob-parent@npm:~5.1.0": 741 | version: 5.1.1 742 | resolution: "glob-parent@npm:5.1.1" 743 | dependencies: 744 | is-glob: "npm:^4.0.1" 745 | checksum: f0ac80c2d9a995d9b5037f9d30be61d6db32ef1f54ed24f5c7b14f8413dfd83980cade1c9b8c1bc17830140fa2b9d6a8484cbd2e059f20abb82f8b1d09ff2e90 746 | languageName: node 747 | linkType: hard 748 | 749 | "glob@npm:7.1.6, glob@npm:^7.1.3, glob@npm:^7.1.4": 750 | version: 7.1.6 751 | resolution: "glob@npm:7.1.6" 752 | dependencies: 753 | fs.realpath: "npm:^1.0.0" 754 | inflight: "npm:^1.0.4" 755 | inherits: "npm:2" 756 | minimatch: "npm:^3.0.4" 757 | once: "npm:^1.3.0" 758 | path-is-absolute: "npm:^1.0.0" 759 | checksum: d50636c269f66c01b688468f60eea9fd8fe98f8c1dc9837fd7767229b47274eeb3c18a1b5c314ce53550d05326d33d9ec531194d8b908fb312cf658664c8cc29 760 | languageName: node 761 | linkType: hard 762 | 763 | "graceful-fs@npm:^4.2.3": 764 | version: 4.2.4 765 | resolution: "graceful-fs@npm:4.2.4" 766 | checksum: c87a58baecc3b9832d932ce6bacc6f90fa89c865db7fde29623bfad0074cee64e37fad84ec55a0d15c543ee51c39909379ee9cbb3c1f07444fd6e9d7591dac71 767 | languageName: node 768 | linkType: hard 769 | 770 | "growl@npm:1.10.5": 771 | version: 1.10.5 772 | resolution: "growl@npm:1.10.5" 773 | checksum: f404b823fbc688b76945b5bb66447ccc7da9d54421f2a02543b121b3748ed588e5dc48ea6a6798f10f41c8b6080f5b1219a67db9e9456adf2f271206adfec0a3 774 | languageName: node 775 | linkType: hard 776 | 777 | "har-schema@npm:^2.0.0": 778 | version: 2.0.0 779 | resolution: "har-schema@npm:2.0.0" 780 | checksum: 45f992760bede24d0787edd6b5b4407d50fe0db5370904002a55e7d4bf3373cc12e7c4f4414a4fc937b25096cdf8957f8ca97b56be6bae8d173a38b2f390826f 781 | languageName: node 782 | linkType: hard 783 | 784 | "har-validator@npm:~5.1.3": 785 | version: 5.1.3 786 | resolution: "har-validator@npm:5.1.3" 787 | dependencies: 788 | ajv: "npm:^6.5.5" 789 | har-schema: "npm:^2.0.0" 790 | checksum: e1f263a02c6b06cbbcf9c356e85ef24e4934f55252c7a69e66c039439ca1af58e902c6906d14898c35f4cdcc47a05f594ae8495e155f58777730f07550fe31bb 791 | languageName: node 792 | linkType: hard 793 | 794 | "has-flag@npm:^3.0.0": 795 | version: 3.0.0 796 | resolution: "has-flag@npm:3.0.0" 797 | checksum: b1cb757b71bca736b4f7a060d52a7914b1438d7bd7ba3cb783f71728c7a72d51520955d477d54fce75e19a859d93fadc9b707de019c141c45f2e560c48beb1f9 798 | languageName: node 799 | linkType: hard 800 | 801 | "has-flag@npm:^4.0.0": 802 | version: 4.0.0 803 | resolution: "has-flag@npm:4.0.0" 804 | checksum: 71f182c441adda71ea3014dec578691a9d74356dd57c238fb2fc88247a94ca10892fe307cda0eb608b91f982d7da34aa2e46f763c4449351dedac26a0493e591 805 | languageName: node 806 | linkType: hard 807 | 808 | "has-symbols@npm:^1.0.0, has-symbols@npm:^1.0.1": 809 | version: 1.0.1 810 | resolution: "has-symbols@npm:1.0.1" 811 | checksum: 049d4612a2038528522c63c24ddf1e8d3041c6c8c78e9e1e390fad9ba8ca5e5fd7e767eb497e24c21fa74fd8c713a302e6d6d4aded5961ae77c4f174a4db338c 812 | languageName: node 813 | linkType: hard 814 | 815 | "has-unicode@npm:^2.0.0": 816 | version: 2.0.1 817 | resolution: "has-unicode@npm:2.0.1" 818 | checksum: d7f38422bc8e339b52014ed5aea2fdcb6545e583ac252081bc7d0970ae8eaa6efa3d056aa3119ac5825bc51fc289b53fa7b3588a40b8bf71a0dabc346513c485 819 | languageName: node 820 | linkType: hard 821 | 822 | "has@npm:^1.0.3": 823 | version: 1.0.3 824 | resolution: "has@npm:1.0.3" 825 | dependencies: 826 | function-bind: "npm:^1.1.1" 827 | checksum: 3e8c4d87ccd9c160d61a5db829b5fb647acac79e482476c857d5d1dc580517c6a77cf84337808f28361f6263008ce1ce5aff44407bd9241af93c623ef8d8d4f1 828 | languageName: node 829 | linkType: hard 830 | 831 | "he@npm:1.2.0": 832 | version: 1.2.0 833 | resolution: "he@npm:1.2.0" 834 | bin: 835 | he: bin/he 836 | checksum: 624468c0a4a0086a722b756a53eddf35a141a16ab41ab965028d0280010753cd2e12a1181e2e638ffd4c9d5131949e198fd8e509b61645b02e8e36a7bdeadc97 837 | languageName: node 838 | linkType: hard 839 | 840 | "http-signature@npm:~1.2.0": 841 | version: 1.2.0 842 | resolution: "http-signature@npm:1.2.0" 843 | dependencies: 844 | assert-plus: "npm:^1.0.0" 845 | jsprim: "npm:^1.2.2" 846 | sshpk: "npm:^1.7.0" 847 | checksum: 4e2f77bd1fc16dbfaf9abc17420c35b09baf0463a300078064446bee1848cfc14778293d62f5b259dac3530ae2ab4823b8508ce8f6f8bbac2099904fad30a59e 848 | languageName: node 849 | linkType: hard 850 | 851 | "inflight@npm:^1.0.4": 852 | version: 1.0.6 853 | resolution: "inflight@npm:1.0.6" 854 | dependencies: 855 | once: "npm:^1.3.0" 856 | wrappy: "npm:1" 857 | checksum: 40d0e5db34e05d49b9ad9ac678334269745644f73206862a8dee6e50ada1c8b3e70774ce28d5e6e3b03b7b868c9d9ae1edaf6eff253fc50209e4c69decad1811 858 | languageName: node 859 | linkType: hard 860 | 861 | "inherits@npm:2, inherits@npm:~2.0.3": 862 | version: 2.0.4 863 | resolution: "inherits@npm:2.0.4" 864 | checksum: ca76c7e45ec715bfe6c1dd67b780b9a15068f37b37ab56cf8b773537b2654238469a42950f5f4d301212755e7512be888f627752e778e1863d95cfedefc8b8bd 865 | languageName: node 866 | linkType: hard 867 | 868 | "is-arguments@npm:^1.0.4": 869 | version: 1.0.4 870 | resolution: "is-arguments@npm:1.0.4" 871 | checksum: c970c4085c6cbd4cd92851cdb4d107613b3d815d68521579ca3c4faa89bf326076bb950e2524555fb545e39a4fa878971e634e8d69626412e0a5abec9c2a9f7f 872 | languageName: node 873 | linkType: hard 874 | 875 | "is-binary-path@npm:~2.1.0": 876 | version: 2.1.0 877 | resolution: "is-binary-path@npm:2.1.0" 878 | dependencies: 879 | binary-extensions: "npm:^2.0.0" 880 | checksum: f6ed933392b85facdc081bbe3539602ac70cf35fe5d3d7e02da0b9c4bc65fa673d815142f16bf6253de84a561332a680382be1ade1406c89c9102832a571620f 881 | languageName: node 882 | linkType: hard 883 | 884 | "is-buffer@npm:~2.0.3": 885 | version: 2.0.4 886 | resolution: "is-buffer@npm:2.0.4" 887 | checksum: c164e8f0966e54777236a7c256c39c1c6356ab31e4cfa7961322e1bc16f1260661bddebc654652be0d548ef28e953aec8a3a8fd23f55125acd6e00bbef2980d4 888 | languageName: node 889 | linkType: hard 890 | 891 | "is-callable@npm:^1.1.4, is-callable@npm:^1.2.0": 892 | version: 1.2.0 893 | resolution: "is-callable@npm:1.2.0" 894 | checksum: caa4405d9b90d5f7cdf76c6342ad4af01141b9e1d47ca4e89685c73248d980d2a4f1f99b131d7f752b59e5e27a95bc84dfef47ded7f5d85b674326bc545429ea 895 | languageName: node 896 | linkType: hard 897 | 898 | "is-core-module@npm:^2.2.0": 899 | version: 2.4.0 900 | resolution: "is-core-module@npm:2.4.0" 901 | dependencies: 902 | has: "npm:^1.0.3" 903 | checksum: 08420cb9ca2598a3f0f8ab8e81750703d94e50560726ebb765ebc4e385c53654460f078e57886b2f4acdc2d79599f31f891bc916366ce53039885b4d08937057 904 | languageName: node 905 | linkType: hard 906 | 907 | "is-date-object@npm:^1.0.1": 908 | version: 1.0.2 909 | resolution: "is-date-object@npm:1.0.2" 910 | checksum: 429216d3582fda73e229adf857f37f46233a6ad8bb286cf74189468376130fb9a4263ab4959ec3280fd677d3e331b02db569db4df3799d7f416a079c324fece8 911 | languageName: node 912 | linkType: hard 913 | 914 | "is-extglob@npm:^2.1.1": 915 | version: 2.1.1 916 | resolution: "is-extglob@npm:2.1.1" 917 | checksum: 226b9f6eee1e7da52f72c98ed4ea7fc71ee3a087b6d1c62655c9a81c601caa2fd98b9f9be42fb8163eef2720cdbf046bc7c5548a76755651e540f4b08ff3b120 918 | languageName: node 919 | linkType: hard 920 | 921 | "is-fullwidth-code-point@npm:^1.0.0": 922 | version: 1.0.0 923 | resolution: "is-fullwidth-code-point@npm:1.0.0" 924 | dependencies: 925 | number-is-nan: "npm:^1.0.0" 926 | checksum: 4ac2325a174bf9bad857527715b947a117d54a6b7b2bc1a059dd7220f30609da5aa4467b4bc523bf78f2b3375d25169bddd31234513713870ab1d8e1d8509d44 927 | languageName: node 928 | linkType: hard 929 | 930 | "is-fullwidth-code-point@npm:^2.0.0": 931 | version: 2.0.0 932 | resolution: "is-fullwidth-code-point@npm:2.0.0" 933 | checksum: 1da88fa5daab611878db8cd1031318b3a4daf9c3b4579d0371da7e28eee9bb6db4070344cfccc41f68b06b4c68971dbc118dad5782a302a09b3a8f84388d0b97 934 | languageName: node 935 | linkType: hard 936 | 937 | "is-glob@npm:^4.0.1, is-glob@npm:~4.0.1": 938 | version: 4.0.1 939 | resolution: "is-glob@npm:4.0.1" 940 | dependencies: 941 | is-extglob: "npm:^2.1.1" 942 | checksum: 6f682939c74f4b873d2017b03bbdc709020771a1a217999f2276184cbcde07a0149c0bafeca090b018ed19854b5c27c4dc7ef49346729dc42941dac9c973bf78 943 | languageName: node 944 | linkType: hard 945 | 946 | "is-map@npm:^2.0.1": 947 | version: 2.0.1 948 | resolution: "is-map@npm:2.0.1" 949 | checksum: a0f07da2c740392e3ca19a6a1962fad49b52de2fb6aa7765fb3aa725eb7891a73b1ed5d05dacf8acfea70abab18edd2c843e3fa4f6ea32c4c0fc931ce5761aa2 950 | languageName: node 951 | linkType: hard 952 | 953 | "is-number@npm:^7.0.0": 954 | version: 7.0.0 955 | resolution: "is-number@npm:7.0.0" 956 | checksum: 748df55ae14cc960b090a7611932940df9fa703b7e0fb4f73943b4eb94c4b5391f27ba3881fab8f5bf7a2f097490e812db0d58d05c92154e70fdf14f93d6fa95 957 | languageName: node 958 | linkType: hard 959 | 960 | "is-regex@npm:^1.1.0": 961 | version: 1.1.0 962 | resolution: "is-regex@npm:1.1.0" 963 | dependencies: 964 | has-symbols: "npm:^1.0.1" 965 | checksum: 28453f225d17d173a72a43f16799fd43f2165b9761068a4c75a4df30cadb7c58097e8e9ffa5c6dcb1f6e2c6cf4cc62f5736756bf1518cda58b42a63c11f0576f 966 | languageName: node 967 | linkType: hard 968 | 969 | "is-set@npm:^2.0.1": 970 | version: 2.0.1 971 | resolution: "is-set@npm:2.0.1" 972 | checksum: 225ed3ba032fb3b45b6ac3f5a36feba2c409951dfe3df2a7e3c72d92a9a02c95bbb8625d6d2927b5d5fca744e904081a8941ee9e22ff53d0793a5f37bba6c20e 973 | languageName: node 974 | linkType: hard 975 | 976 | "is-string@npm:^1.0.4, is-string@npm:^1.0.5": 977 | version: 1.0.5 978 | resolution: "is-string@npm:1.0.5" 979 | checksum: 8b5b740035b6a2a47ebdb9033a71a351c5f6fa8d391871e56bebb8a57f107dae49231133bf6aea589bf59c2522f12ea623437e5e81f6fde523006770c1d7ee51 980 | languageName: node 981 | linkType: hard 982 | 983 | "is-symbol@npm:^1.0.2": 984 | version: 1.0.3 985 | resolution: "is-symbol@npm:1.0.3" 986 | dependencies: 987 | has-symbols: "npm:^1.0.1" 988 | checksum: f57305707b4dbb948d49e3bd85b8dbf71df4c2cf10b855bb11f866287b96db05f5010c0f5d6b0b540609d5d54a9fb0a775a4e9746868153a4b8c78fcb80e11c3 989 | languageName: node 990 | linkType: hard 991 | 992 | "is-typedarray@npm:~1.0.0": 993 | version: 1.0.0 994 | resolution: "is-typedarray@npm:1.0.0" 995 | checksum: f918df0d4215dbde9d0d29375cf39e353abe59ef3964862afc87bb6ce503e7439f4131260a7b1777074f5fcc64f659c75a4ce5a93ceb603901375cd0b13eedab 996 | languageName: node 997 | linkType: hard 998 | 999 | "isarray@npm:^2.0.5": 1000 | version: 2.0.5 1001 | resolution: "isarray@npm:2.0.5" 1002 | checksum: bd46a907ad163c4c937d08ee6520fc9482cf5457dc0d168457ef755d8f26e75b5e2649962722a4c0f5ab2398a95e431c8469c86a004c42db21230ef40b8720ee 1003 | languageName: node 1004 | linkType: hard 1005 | 1006 | "isarray@npm:~1.0.0": 1007 | version: 1.0.0 1008 | resolution: "isarray@npm:1.0.0" 1009 | checksum: 7b41a2a80d6285328dddeecd3e45a5c73264e8ff8817bb7dc39f6f47323dfaa28e27c13918aac4aa88e48800a4f1eee2e5e966da433e06085ef0a7592dcf6880 1010 | languageName: node 1011 | linkType: hard 1012 | 1013 | "isexe@npm:^2.0.0": 1014 | version: 2.0.0 1015 | resolution: "isexe@npm:2.0.0" 1016 | checksum: b37fe0a7983c0c151c7b31ca716405aaea190ac9cd6ef3f79355f4afb043ed4d3182a6addd73b20df7a0b229269737ad0daf64116821a048bfbe6b8fb7eb842c 1017 | languageName: node 1018 | linkType: hard 1019 | 1020 | "isstream@npm:~0.1.2": 1021 | version: 0.1.2 1022 | resolution: "isstream@npm:0.1.2" 1023 | checksum: 0458850e4cc11c29dece587a48a73b44e423738fc8824bfa946f11cc5371ccf94e9e9fcbc4025ced0116c420e08ed3a61cfb14393d2b4c989587888acdd6b0ab 1024 | languageName: node 1025 | linkType: hard 1026 | 1027 | "iterate-iterator@npm:^1.0.1": 1028 | version: 1.0.1 1029 | resolution: "iterate-iterator@npm:1.0.1" 1030 | checksum: 94b366c61a6748e80bc56672d4758c0d9497b0cc6f80e10a66f8d28a9be1955f8cb5a5262163e79c1724b20a457eac58a90b47524c86364bb0538969af07155d 1031 | languageName: node 1032 | linkType: hard 1033 | 1034 | "iterate-value@npm:^1.0.0": 1035 | version: 1.0.2 1036 | resolution: "iterate-value@npm:1.0.2" 1037 | dependencies: 1038 | es-get-iterator: "npm:^1.0.2" 1039 | iterate-iterator: "npm:^1.0.1" 1040 | checksum: 3aa4975cfd578efa26d4cbe4e4dee3ae761ec62f2f0a9f4ee625829b8509460bfbd6dae713ae9b70d2b9775605173e90b13c65afe0d114b00ecf5f9611b7584f 1041 | languageName: node 1042 | linkType: hard 1043 | 1044 | "js-yaml@npm:3.13.1": 1045 | version: 3.13.1 1046 | resolution: "js-yaml@npm:3.13.1" 1047 | dependencies: 1048 | argparse: "npm:^1.0.7" 1049 | esprima: "npm:^4.0.0" 1050 | bin: 1051 | js-yaml: bin/js-yaml.js 1052 | checksum: 14907be8b66b3b964ca5b3a9c870070147bdaf727a335428d75bbb2a45891f2a256e9eef10bcf3c72e080a80fb4d6c6a2330e15b72a0756c97d8e50377fe5ea2 1053 | languageName: node 1054 | linkType: hard 1055 | 1056 | "jsbn@npm:~0.1.0": 1057 | version: 0.1.1 1058 | resolution: "jsbn@npm:0.1.1" 1059 | checksum: b30785edca016891c4da40f97916476858a0e14745ebb14ac59162a9110b5a1f80cdd550b80b627234ba63ea16f83e233502625572e7fdd9dcf703c99a0d753e 1060 | languageName: node 1061 | linkType: hard 1062 | 1063 | "json-schema-traverse@npm:^0.4.1": 1064 | version: 0.4.1 1065 | resolution: "json-schema-traverse@npm:0.4.1" 1066 | checksum: 4c9b10ebd277b894fa66f7130ffcf6b8c0d2c41754ce3784d82149695dbd928c15523aab230b8206c4be5b48127cafc0467760774673ba61045e1abb52e74de2 1067 | languageName: node 1068 | linkType: hard 1069 | 1070 | "json-schema@npm:0.2.3": 1071 | version: 0.2.3 1072 | resolution: "json-schema@npm:0.2.3" 1073 | checksum: bbba8f93830e3b3161f74176a87547473371ae1d61f512e3c931cf0ebac2518d899bf941760b98649b52b519e318f7ca25e4f6072870923d7944381d88d00c97 1074 | languageName: node 1075 | linkType: hard 1076 | 1077 | "json-stringify-safe@npm:~5.0.1": 1078 | version: 5.0.1 1079 | resolution: "json-stringify-safe@npm:5.0.1" 1080 | checksum: e86f7bb748bb84f73b171bb68c8209a1e68f40d41f943952f746fa4ca3802c1edf4602e86977c2de44eba1e64e4cabe2498f4499003cc471e99db83bfba95898 1081 | languageName: node 1082 | linkType: hard 1083 | 1084 | "jsprim@npm:^1.2.2": 1085 | version: 1.4.1 1086 | resolution: "jsprim@npm:1.4.1" 1087 | dependencies: 1088 | assert-plus: "npm:1.0.0" 1089 | extsprintf: "npm:1.3.0" 1090 | json-schema: "npm:0.2.3" 1091 | verror: "npm:1.10.0" 1092 | checksum: b52c973890b4c58b7e6b4b554db366a3e688299111cb3f4b74c96ffc24df872c1104a4df3c5b60685a5c73dd087febfd1cecc2356ec839ccbbc7d53b08e0e38b 1093 | languageName: node 1094 | linkType: hard 1095 | 1096 | "locate-path@npm:^3.0.0": 1097 | version: 3.0.0 1098 | resolution: "locate-path@npm:3.0.0" 1099 | dependencies: 1100 | p-locate: "npm:^3.0.0" 1101 | path-exists: "npm:^3.0.0" 1102 | checksum: ca3f5b4f7f8f9dc8f650b7a9ced56babaeeb3da4b34eea236cc75a62ac69626aa13b784685d3a9d6e8ce383c8921912823c8a2d16cd8cd68a0484d8ca8d98e09 1103 | languageName: node 1104 | linkType: hard 1105 | 1106 | "locate-path@npm:^5.0.0": 1107 | version: 5.0.0 1108 | resolution: "locate-path@npm:5.0.0" 1109 | dependencies: 1110 | p-locate: "npm:^4.1.0" 1111 | checksum: 990eddf17c761030216219e58575787fc0ba8050058eaddc04fd419473524840349c3be6dde342f93007cacc00d6d950f906c44b72a58f68c347c1da8c0dd3a1 1112 | languageName: node 1113 | linkType: hard 1114 | 1115 | "lodash@npm:^4.17.15": 1116 | version: 4.17.15 1117 | resolution: "lodash@npm:4.17.15" 1118 | checksum: 9965316f037d06697cedd257a81fead6f992644b87d4948192887fec40a44ce49503772fed3e14deee246bbadedc7cbc17df6e04dcbd987461d9424b46662fa5 1119 | languageName: node 1120 | linkType: hard 1121 | 1122 | "log-symbols@npm:3.0.0": 1123 | version: 3.0.0 1124 | resolution: "log-symbols@npm:3.0.0" 1125 | dependencies: 1126 | chalk: "npm:^2.4.2" 1127 | checksum: aaaf759780d9e8ce4ae768a3d1715f262266078e730ea46bd3b9e462af6793f4b2738a74b1631dcdfab0d373e49dbc020b249a0463f9affd793e4e5167026078 1128 | languageName: node 1129 | linkType: hard 1130 | 1131 | "make-error@npm:^1.1.1": 1132 | version: 1.3.6 1133 | resolution: "make-error@npm:1.3.6" 1134 | checksum: 4b81ce1392495d554ce5fd28c8de95066642e5e1a5efd395e3b3413bc75068a025d8a567aefb0738ba6da18e73323ffde17794780f632fe4395e009aa9ebcc8a 1135 | languageName: node 1136 | linkType: hard 1137 | 1138 | "mime-db@npm:1.44.0": 1139 | version: 1.44.0 1140 | resolution: "mime-db@npm:1.44.0" 1141 | checksum: 52a84717f9ae1ab518d0bf249f81abad2475c91b0bf416ba5cfcd0deb487f85f1b69a46f82cc82ad148dd880482cd395b5f55dcfde7e252b983eb2ab73962503 1142 | languageName: node 1143 | linkType: hard 1144 | 1145 | "mime-types@npm:^2.1.12, mime-types@npm:~2.1.19": 1146 | version: 2.1.27 1147 | resolution: "mime-types@npm:2.1.27" 1148 | dependencies: 1149 | mime-db: "npm:1.44.0" 1150 | checksum: 4d0c0ba923087bc046b3e0d3b11286792121a05fe521a60f98a873f398d3e5d3b7469cc7bc62d2296a6eb91a110700ccc05c8dc9223fecac42459e27c427d41d 1151 | languageName: node 1152 | linkType: hard 1153 | 1154 | "minimatch@npm:3.0.4, minimatch@npm:^3.0.4": 1155 | version: 3.0.4 1156 | resolution: "minimatch@npm:3.0.4" 1157 | dependencies: 1158 | brace-expansion: "npm:^1.1.7" 1159 | checksum: 2579a9237b4947989dd0ebf3fbf6975c06d6fb676e83dde945ed94f18fa09485caa415dc12ae8119132325d533a5872cbf060530a49f236d65e2bcce95a9b23f 1160 | languageName: node 1161 | linkType: hard 1162 | 1163 | "minipass@npm:^3.0.0": 1164 | version: 3.1.3 1165 | resolution: "minipass@npm:3.1.3" 1166 | dependencies: 1167 | yallist: "npm:^4.0.0" 1168 | checksum: 3065ffed7fe5cafd79517dd9540e3772cc056a4ade2268798ad57177f20261c7931e47fccc6d3a1860a736059a6f0382a2c62094fba8b0e7712d843f7ea24806 1169 | languageName: node 1170 | linkType: hard 1171 | 1172 | "minizlib@npm:^2.1.0": 1173 | version: 2.1.0 1174 | resolution: "minizlib@npm:2.1.0" 1175 | dependencies: 1176 | minipass: "npm:^3.0.0" 1177 | yallist: "npm:^4.0.0" 1178 | checksum: ffa6705360c390285c19998dd79aa50ba98d5b9fdde95c897e83601257b0dbc4b33b48c4bb8630003290629644ace3c11f124472c2f4efad280d56363e785e65 1179 | languageName: node 1180 | linkType: hard 1181 | 1182 | "mkdirp@npm:^1.0.3": 1183 | version: 1.0.4 1184 | resolution: "mkdirp@npm:1.0.4" 1185 | bin: 1186 | mkdirp: bin/cmd.js 1187 | checksum: 123361119829ab8115234f36ed8ef8f697b0f6f83ec9f9bc8f76da587487976d74bc874ffa892e7a66df607fa8f2cc758eed8db225e9cd3a84846350209e53db 1188 | languageName: node 1189 | linkType: hard 1190 | 1191 | "mocha@npm:^8.0.1": 1192 | version: 8.0.1 1193 | resolution: "mocha@npm:8.0.1" 1194 | dependencies: 1195 | ansi-colors: "npm:4.1.1" 1196 | browser-stdout: "npm:1.3.1" 1197 | chokidar: "npm:3.3.1" 1198 | debug: "npm:3.2.6" 1199 | diff: "npm:4.0.2" 1200 | escape-string-regexp: "npm:1.0.5" 1201 | find-up: "npm:4.1.0" 1202 | glob: "npm:7.1.6" 1203 | growl: "npm:1.10.5" 1204 | he: "npm:1.2.0" 1205 | js-yaml: "npm:3.13.1" 1206 | log-symbols: "npm:3.0.0" 1207 | minimatch: "npm:3.0.4" 1208 | ms: "npm:2.1.2" 1209 | object.assign: "npm:4.1.0" 1210 | promise.allsettled: "npm:1.0.2" 1211 | serialize-javascript: "npm:3.0.0" 1212 | strip-json-comments: "npm:3.0.1" 1213 | supports-color: "npm:7.1.0" 1214 | which: "npm:2.0.2" 1215 | wide-align: "npm:1.1.3" 1216 | workerpool: "npm:6.0.0" 1217 | yargs: "npm:13.3.2" 1218 | yargs-parser: "npm:13.1.2" 1219 | yargs-unparser: "npm:1.6.0" 1220 | bin: 1221 | _mocha: bin/_mocha 1222 | mocha: bin/mocha 1223 | checksum: 252f7c192f3a9e00583a6ca63f87124613fe8bd56857c09b855f08ccbfe85d14dd7dc4cb7654dec02f3fb43b9d6e90f92d1cc262aca17dcb5d65cc2cca30ae87 1224 | languageName: node 1225 | linkType: hard 1226 | 1227 | "ms@npm:2.1.2, ms@npm:^2.1.1": 1228 | version: 2.1.2 1229 | resolution: "ms@npm:2.1.2" 1230 | checksum: 3f46af60a08158f1c77746c06c2f6c7aba7feddafd41335f9baa2d7e0741d7539774aa7d5d1661a7f2b7eed55a7063771297eea016051924dbb04d4c2bf40bcb 1231 | languageName: node 1232 | linkType: hard 1233 | 1234 | "node-gyp@npm:latest": 1235 | version: 7.0.0 1236 | resolution: "node-gyp@npm:7.0.0" 1237 | dependencies: 1238 | env-paths: "npm:^2.2.0" 1239 | glob: "npm:^7.1.4" 1240 | graceful-fs: "npm:^4.2.3" 1241 | nopt: "npm:^4.0.3" 1242 | npmlog: "npm:^4.1.2" 1243 | request: "npm:^2.88.2" 1244 | rimraf: "npm:^2.6.3" 1245 | semver: "npm:^7.3.2" 1246 | tar: "npm:^6.0.1" 1247 | which: "npm:^2.0.2" 1248 | bin: 1249 | node-gyp: bin/node-gyp.js 1250 | checksum: dc94704e15975260f4d9e41f06f5d9f18389df285b0a2ef3512b69b3f44a86d61bda7bd8c84336c1bb6a91f85e552c864f861cfadc16d9e5b641774bcba596e0 1251 | languageName: node 1252 | linkType: hard 1253 | 1254 | "nopt@npm:^4.0.3": 1255 | version: 4.0.3 1256 | resolution: "nopt@npm:4.0.3" 1257 | dependencies: 1258 | abbrev: "npm:1" 1259 | osenv: "npm:^0.1.4" 1260 | bin: 1261 | nopt: bin/nopt.js 1262 | checksum: 4feed2f2c230f076bd63377fa51c9ad8a54292b731157298a4d51eeab199233366fa856658f5ed30e16f96660fd707ddf77baf94a87cd8eff60766169ebe19eb 1263 | languageName: node 1264 | linkType: hard 1265 | 1266 | "normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": 1267 | version: 3.0.0 1268 | resolution: "normalize-path@npm:3.0.0" 1269 | checksum: 66de83885051c8a7266566cb175281ec583e3d66b5054c744b46a0eebc4eaac1e1d74c640aaf72144086a9661aa60e89ac0b5c92eb76608e5b8a5056dbcf9e27 1270 | languageName: node 1271 | linkType: hard 1272 | 1273 | "npmlog@npm:^4.1.2": 1274 | version: 4.1.2 1275 | resolution: "npmlog@npm:4.1.2" 1276 | dependencies: 1277 | are-we-there-yet: "npm:~1.1.2" 1278 | console-control-strings: "npm:~1.1.0" 1279 | gauge: "npm:~2.7.3" 1280 | set-blocking: "npm:~2.0.0" 1281 | checksum: b78a3f970f174e4eeaa28faf182319bb44aecf1849441807024c01a0c5186641df504c4d67e3b2b2f4ad0da55e9a7b415c3a20adbb13df94afe7a885ec0a69f8 1282 | languageName: node 1283 | linkType: hard 1284 | 1285 | "number-is-nan@npm:^1.0.0": 1286 | version: 1.0.1 1287 | resolution: "number-is-nan@npm:1.0.1" 1288 | checksum: 5d277673bdfae98f79b02c4ea9bb13989df7435f1b90b708b8177d276e0977e9377eb6a391d28a115b577baa0edacaea99c1b755bfeb8b348a7dc9281d32fc02 1289 | languageName: node 1290 | linkType: hard 1291 | 1292 | "oauth-sign@npm:~0.9.0": 1293 | version: 0.9.0 1294 | resolution: "oauth-sign@npm:0.9.0" 1295 | checksum: 7f90bdcedf7b624a85106ef0b7ac65fafd736a1f073554714363becdae7d9f3caed00a282b876eecef39797451c188e4e1dd72b455fa1880469ee6f0710c0a3e 1296 | languageName: node 1297 | linkType: hard 1298 | 1299 | "object-assign@npm:^4.1.0": 1300 | version: 4.1.1 1301 | resolution: "object-assign@npm:4.1.1" 1302 | checksum: f5cd1f2f1e82e12207e4f2377d9d7d90fbc0d9822a6afa717a6dcab6930d8925e1ebbbb25df770c31ff11335ee423459ba65ffa2e53999926c328b806b4d73d6 1303 | languageName: node 1304 | linkType: hard 1305 | 1306 | "object-inspect@npm:^1.7.0": 1307 | version: 1.7.0 1308 | resolution: "object-inspect@npm:1.7.0" 1309 | checksum: 23cd7c8ec9a42505526fb42f43b34716d306fd1ae3da538a6dfc207e624ce8b9bf5afe108f759faf42d6d619488fa57316e1d2c67b54f127bbbc73a77e9b002f 1310 | languageName: node 1311 | linkType: hard 1312 | 1313 | "object-keys@npm:^1.0.11, object-keys@npm:^1.0.12, object-keys@npm:^1.1.1": 1314 | version: 1.1.1 1315 | resolution: "object-keys@npm:1.1.1" 1316 | checksum: 23343006d68702a85c299dafd4fc4205dbf729561a7d0acc1a75f6211636fcc1bbbdf26f0740119c43a7a98463e56b8afb74cbb4670509452007f5bc2f64cc36 1317 | languageName: node 1318 | linkType: hard 1319 | 1320 | "object.assign@npm:4.1.0, object.assign@npm:^4.1.0": 1321 | version: 4.1.0 1322 | resolution: "object.assign@npm:4.1.0" 1323 | dependencies: 1324 | define-properties: "npm:^1.1.2" 1325 | function-bind: "npm:^1.1.1" 1326 | has-symbols: "npm:^1.0.0" 1327 | object-keys: "npm:^1.0.11" 1328 | checksum: 52fc3e6123814686cc3b1a6c74cf497823e281586a436fba18fb79084619ca4de8f5ea0e1b4b2bfce3b43de989e1da5d6bbed0172078fd904079b997adaaa08e 1329 | languageName: node 1330 | linkType: hard 1331 | 1332 | "once@npm:^1.3.0": 1333 | version: 1.4.0 1334 | resolution: "once@npm:1.4.0" 1335 | dependencies: 1336 | wrappy: "npm:1" 1337 | checksum: 12d5c6ece331855387577e71c96ab5b60269390b131cf9403494206274fa520221c88f8b8d431d7227d080127730460da8907c402ab4142e592c34aacb5c9817 1338 | languageName: node 1339 | linkType: hard 1340 | 1341 | "os-homedir@npm:^1.0.0": 1342 | version: 1.0.2 1343 | resolution: "os-homedir@npm:1.0.2" 1344 | checksum: a9952fc2f0428609088f9be0b399c7029ef090c9a9d065c5cfa1c41b25a7d441df98f19368d2cc7c19c7e932759b918fe9e6cb7ee61b1343341a42f2481ab6ca 1345 | languageName: node 1346 | linkType: hard 1347 | 1348 | "os-tmpdir@npm:^1.0.0": 1349 | version: 1.0.2 1350 | resolution: "os-tmpdir@npm:1.0.2" 1351 | checksum: c69d1cc11e9da80f1e2b21a08566fec9a690e4b5bc47b3ac996cfe8d24f4e9e6857779a39a326bf322f2e8bc936ada1a92d48aa10c6dda99c13c551c23bdadfb 1352 | languageName: node 1353 | linkType: hard 1354 | 1355 | "osenv@npm:^0.1.4": 1356 | version: 0.1.5 1357 | resolution: "osenv@npm:0.1.5" 1358 | dependencies: 1359 | os-homedir: "npm:^1.0.0" 1360 | os-tmpdir: "npm:^1.0.0" 1361 | checksum: eb75705a80d3b8d37f1dea0c855420650993b8aa0a395105bdf27c3908541f693fd2d42f57d28ce249886cd42f3edd64e24636750004b96cc1b73e8e77896c05 1362 | languageName: node 1363 | linkType: hard 1364 | 1365 | "p-limit@npm:^2.0.0, p-limit@npm:^2.2.0": 1366 | version: 2.3.0 1367 | resolution: "p-limit@npm:2.3.0" 1368 | dependencies: 1369 | p-try: "npm:^2.0.0" 1370 | checksum: c317600da8c93ba548091ddee29772a00fab9eca806af5167ed0e756c086702f0e25b51c4d29e75bb09869c0c005dc25eb03fad9958066923f6eb34d90df0465 1371 | languageName: node 1372 | linkType: hard 1373 | 1374 | "p-locate@npm:^3.0.0": 1375 | version: 3.0.0 1376 | resolution: "p-locate@npm:3.0.0" 1377 | dependencies: 1378 | p-limit: "npm:^2.0.0" 1379 | checksum: b54aaaebb15cc2d854752e424d73f9626aefdc5700821836a247f41039b668ebfa9e702e672adc79643ecdb7518fce92d0d721ea59754afcef32681aab4a732d 1380 | languageName: node 1381 | linkType: hard 1382 | 1383 | "p-locate@npm:^4.1.0": 1384 | version: 4.1.0 1385 | resolution: "p-locate@npm:4.1.0" 1386 | dependencies: 1387 | p-limit: "npm:^2.2.0" 1388 | checksum: 3e073a6fdbbe9864ed7b0fd9905d39b38e3ed95d76ab64e3389d44a1baa5345a16683efbdeff3598036fb9406917f273aad4255a55dc3174a809dc618ddcc1ce 1389 | languageName: node 1390 | linkType: hard 1391 | 1392 | "p-try@npm:^2.0.0": 1393 | version: 2.2.0 1394 | resolution: "p-try@npm:2.2.0" 1395 | checksum: 1b9a6b5d6f42a46e36f053ee737a72cbe8f7990ee65e0d7bc3f8f8324e233d5b5e790f9f660bcc44d93738a2b12108dec1f7a39c9650d276fd1f9d73d54d4f55 1396 | languageName: node 1397 | linkType: hard 1398 | 1399 | "path-exists@npm:^3.0.0": 1400 | version: 3.0.0 1401 | resolution: "path-exists@npm:3.0.0" 1402 | checksum: 6479d25601e17c2dbe1a02b3f00fe62416f3c8909ab7352f4f492bdc781ed745d8d0ef03fe233c20323a44fac38b3a6c3cc6865b7d0c68635fdff9e2abf7304c 1403 | languageName: node 1404 | linkType: hard 1405 | 1406 | "path-exists@npm:^4.0.0": 1407 | version: 4.0.0 1408 | resolution: "path-exists@npm:4.0.0" 1409 | checksum: 28623865ba71cdc25d2d80021407b1500d64bb74d5072f03276221b4febedbb543132f5bcc57d7fc42b32b45f4175bbae919e1810535892faa4ba9e8f2edc6dd 1410 | languageName: node 1411 | linkType: hard 1412 | 1413 | "path-is-absolute@npm:^1.0.0": 1414 | version: 1.0.1 1415 | resolution: "path-is-absolute@npm:1.0.1" 1416 | checksum: 6bb8fef4324c3f744e5d216980aa053095e1fc533d40fa47f9c1adc16be7fa52d3c4858370c7685406c32ab143a4dca0798f2e2c0f57d7937af66d8dd79267f6 1417 | languageName: node 1418 | linkType: hard 1419 | 1420 | "path-parse@npm:^1.0.6": 1421 | version: 1.0.6 1422 | resolution: "path-parse@npm:1.0.6" 1423 | checksum: bc1690808308682ac5153ea9cb47cd1a10dfe84224978dc5016b6504b47b45f8eab7c5119fbdd24eb6262bbab111c30379ffdb376840586370f84fda975e1bf8 1424 | languageName: node 1425 | linkType: hard 1426 | 1427 | "pathval@npm:^1.1.1": 1428 | version: 1.1.1 1429 | resolution: "pathval@npm:1.1.1" 1430 | checksum: 13fed3f7d8efa938ed9a5e3e5c6d35c6081e4d05b2fd97274702966477ff28af7599e44418bfeebf032acd407379a77b4db180cc78294e5b8dcd971567a0efe8 1431 | languageName: node 1432 | linkType: hard 1433 | 1434 | "performance-now@npm:^2.1.0": 1435 | version: 2.1.0 1436 | resolution: "performance-now@npm:2.1.0" 1437 | checksum: a0fae1e610b785e04b20ae146033a7ea1e639f1aa583a1d4d01b36be787dfebe31227402a7ef3b1ffb621d04750ca73c17b03ec943f2389f7416f95236a61e31 1438 | languageName: node 1439 | linkType: hard 1440 | 1441 | "picomatch@npm:^2.0.4, picomatch@npm:^2.0.7, picomatch@npm:^2.2.2": 1442 | version: 2.2.2 1443 | resolution: "picomatch@npm:2.2.2" 1444 | checksum: 768d1f500b9492fbda311892ed947bd874582ceea521ab349737ae4ec27fec7422bb07e761a439816c96de6b4eed9eabf0ed65e6152c54f1c4fbf6878d4e4113 1445 | languageName: node 1446 | linkType: hard 1447 | 1448 | "process-nextick-args@npm:~2.0.0": 1449 | version: 2.0.1 1450 | resolution: "process-nextick-args@npm:2.0.1" 1451 | checksum: 09ec0ec8e28a923bdf8d0b926bfbba475553de2cf0be9232d76904a21a3c8c03b6dd4625738ee0bab8fa10b9b2f2fda8a3f9d18815c3407c30f13b51f84605e9 1452 | languageName: node 1453 | linkType: hard 1454 | 1455 | "promise.allsettled@npm:1.0.2": 1456 | version: 1.0.2 1457 | resolution: "promise.allsettled@npm:1.0.2" 1458 | dependencies: 1459 | array.prototype.map: "npm:^1.0.1" 1460 | define-properties: "npm:^1.1.3" 1461 | es-abstract: "npm:^1.17.0-next.1" 1462 | function-bind: "npm:^1.1.1" 1463 | iterate-value: "npm:^1.0.0" 1464 | checksum: fda7bdf39183f5d5dd468e9456dc15b57189b3404d155f6f09782f1d44e82a142e45f076cdb2542e4c150f7596f936a755d9e1b7e4f9786204d416aeb74f84fd 1465 | languageName: node 1466 | linkType: hard 1467 | 1468 | "psl@npm:^1.1.28": 1469 | version: 1.8.0 1470 | resolution: "psl@npm:1.8.0" 1471 | checksum: cebdf3dcaf1b05ea817ea422e4ea91973c0c54a9deb12112052273c87139f414eeb349dd80aa16cbc969cb5604329dc1e3aa3c8d5748de3e3bd2e1b7debd5c89 1472 | languageName: node 1473 | linkType: hard 1474 | 1475 | "punycode@npm:^2.1.0, punycode@npm:^2.1.1": 1476 | version: 2.1.1 1477 | resolution: "punycode@npm:2.1.1" 1478 | checksum: fd728ef9db90e7b4db37d5c4937d6c6302cf4f64748b2dea3abbf1efd21e6193bb670efb7814766c858b2e1ccdb65ce34e44b498d734922e1dcb2a8623a925d8 1479 | languageName: node 1480 | linkType: hard 1481 | 1482 | "qs@npm:~6.5.2": 1483 | version: 6.5.2 1484 | resolution: "qs@npm:6.5.2" 1485 | checksum: e996d1229afeab8ddf0c1dba9a08214891688160251b0b30168a2ab8845eedf6af8ea2201a81938d14af7df2bc84aaa561c1949bd2a1f62edb86af38ec731110 1486 | languageName: node 1487 | linkType: hard 1488 | 1489 | "readable-stream@npm:^2.0.6": 1490 | version: 2.3.7 1491 | resolution: "readable-stream@npm:2.3.7" 1492 | dependencies: 1493 | core-util-is: "npm:~1.0.0" 1494 | inherits: "npm:~2.0.3" 1495 | isarray: "npm:~1.0.0" 1496 | process-nextick-args: "npm:~2.0.0" 1497 | safe-buffer: "npm:~5.1.1" 1498 | string_decoder: "npm:~1.1.1" 1499 | util-deprecate: "npm:~1.0.1" 1500 | checksum: 23c757366d6e0dd9115660c7313d10fc6a57fa50f5a62d1fde329cee13d4bc0de7f3db6d2f25722b1bd98171abe3d4bea626545556b4684864e20ecc70a2a57d 1501 | languageName: node 1502 | linkType: hard 1503 | 1504 | "readdirp@npm:~3.3.0": 1505 | version: 3.3.0 1506 | resolution: "readdirp@npm:3.3.0" 1507 | dependencies: 1508 | picomatch: "npm:^2.0.7" 1509 | checksum: 6fdd3272a4ed1afce45edc24088e5638cba1858e1a4d97cb46499f67343d8a65abec9be65f526cb146d0e76a576e2b5f20cf1b45ec258800f6a1f7151c667edc 1510 | languageName: node 1511 | linkType: hard 1512 | 1513 | "request@npm:^2.88.2": 1514 | version: 2.88.2 1515 | resolution: "request@npm:2.88.2" 1516 | dependencies: 1517 | aws-sign2: "npm:~0.7.0" 1518 | aws4: "npm:^1.8.0" 1519 | caseless: "npm:~0.12.0" 1520 | combined-stream: "npm:~1.0.6" 1521 | extend: "npm:~3.0.2" 1522 | forever-agent: "npm:~0.6.1" 1523 | form-data: "npm:~2.3.2" 1524 | har-validator: "npm:~5.1.3" 1525 | http-signature: "npm:~1.2.0" 1526 | is-typedarray: "npm:~1.0.0" 1527 | isstream: "npm:~0.1.2" 1528 | json-stringify-safe: "npm:~5.0.1" 1529 | mime-types: "npm:~2.1.19" 1530 | oauth-sign: "npm:~0.9.0" 1531 | performance-now: "npm:^2.1.0" 1532 | qs: "npm:~6.5.2" 1533 | safe-buffer: "npm:^5.1.2" 1534 | tough-cookie: "npm:~2.5.0" 1535 | tunnel-agent: "npm:^0.6.0" 1536 | uuid: "npm:^3.3.2" 1537 | checksum: 0b6b8f79ddd55bd51d820b9b8f8c3c16945173e7605de652c300bcff5814b576b7f8fd6538fa9a0c886249fbbeb83d5e4424d9baf6fd4e3332e9a803e6b03319 1538 | languageName: node 1539 | linkType: hard 1540 | 1541 | "require-directory@npm:^2.1.1": 1542 | version: 2.1.1 1543 | resolution: "require-directory@npm:2.1.1" 1544 | checksum: 1b1289dc30006e3c6576dd899ed812921f680d652005118cfabcf5d0679e885ff19a6659219e6705571a6ba7f4278f24d93b17f7e7e9ba28dc4b38e256f35d61 1545 | languageName: node 1546 | linkType: hard 1547 | 1548 | "require-main-filename@npm:^2.0.0": 1549 | version: 2.0.0 1550 | resolution: "require-main-filename@npm:2.0.0" 1551 | checksum: 69a00ac8d82f99fecc4b265f2eb8bfeb1e3bf04e1f6579a1e03b3e6e351ea7f1f7da61d460cb7eaf0b2bc6d657c076e87767e51c523db8db4860744d3a4f8749 1552 | languageName: node 1553 | linkType: hard 1554 | 1555 | "resolve@npm:^1.17.0": 1556 | version: 1.20.0 1557 | resolution: "resolve@npm:1.20.0" 1558 | dependencies: 1559 | is-core-module: "npm:^2.2.0" 1560 | path-parse: "npm:^1.0.6" 1561 | checksum: da7ee98e6a1637afc8ad988af9bb052ab09bdcdaa5c786ed9af3d74a5b19d96428e418d56e2338782e65802182ad737f7540cbb7a82af27559a1bdb9653c01f8 1562 | languageName: node 1563 | linkType: hard 1564 | 1565 | "resolve@patch:resolve@npm%3A^1.17.0#optional!builtin": 1566 | version: 1.20.0 1567 | resolution: "resolve@patch:resolve@npm%3A1.20.0#optional!builtin::version=1.20.0&hash=07638b" 1568 | dependencies: 1569 | is-core-module: "npm:^2.2.0" 1570 | path-parse: "npm:^1.0.6" 1571 | checksum: 976d9e2ccefbecc754be43a1d1a6a5e689339ac8fb5c0b2ad0bcf7b78d7ec3da938645efb9d9986c66e760306394ea1a3cdae84aa5d7078714f88a020a5190f2 1572 | languageName: node 1573 | linkType: hard 1574 | 1575 | "rimraf@npm:^2.6.3": 1576 | version: 2.7.1 1577 | resolution: "rimraf@npm:2.7.1" 1578 | dependencies: 1579 | glob: "npm:^7.1.3" 1580 | bin: 1581 | rimraf: ./bin.js 1582 | checksum: 35e2f6ca89242c02380f70895af2caf2b8a31e5a9b05b380ebf0aa5f48005ec9d242eb4fb32d8578a34c42dc012d16866dfc0e0d0b8601ec8c72ff7065755f19 1583 | languageName: node 1584 | linkType: hard 1585 | 1586 | "rollup@npm:^2.17.0": 1587 | version: 2.17.0 1588 | resolution: "rollup@npm:2.17.0" 1589 | dependencies: 1590 | fsevents: "npm:~2.1.2" 1591 | dependenciesMeta: 1592 | fsevents: 1593 | optional: true 1594 | bin: 1595 | rollup: dist/bin/rollup 1596 | checksum: dedf21befa3d53b278b4c6a7f2e1e57b42adcea5d111e199cb2de3d1f273251110f85ef00db6161e191c3593ecddf5e932a1c4ed178d7dd8d456078efa2345ca 1597 | languageName: node 1598 | linkType: hard 1599 | 1600 | "safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": 1601 | version: 5.1.2 1602 | resolution: "safe-buffer@npm:5.1.2" 1603 | checksum: 86939c6de6b62c1d39b7da860a56d5e50ede9b0ab35a91b0620bff8a96f1f798084ff910059f605087c2c500dc23dfdf77ff5bc3bcc8d4d38e3d634de2e3e426 1604 | languageName: node 1605 | linkType: hard 1606 | 1607 | "safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:~2.1.0": 1608 | version: 2.1.2 1609 | resolution: "safer-buffer@npm:2.1.2" 1610 | checksum: d4199666e9e792968c0b88c2c35dd400f56d3eecb9affbcf5207922822eadf30cc06995bae3c5d0a653851bbd40fc0af578bf046bbf734199ce22433ba4da659 1611 | languageName: node 1612 | linkType: hard 1613 | 1614 | "semver@npm:^7.3.2": 1615 | version: 7.3.2 1616 | resolution: "semver@npm:7.3.2" 1617 | bin: 1618 | semver: bin/semver.js 1619 | checksum: d788eb4a24d643bac6705630eb015eef548b0f19d79912bf375e7ed685c7294c80e1019a7842c7d48eaee3578630d6ed4d2a952fecf4a159b99f89d047f121eb 1620 | languageName: node 1621 | linkType: hard 1622 | 1623 | "serialize-javascript@npm:3.0.0": 1624 | version: 3.0.0 1625 | resolution: "serialize-javascript@npm:3.0.0" 1626 | checksum: a98bb13c765e8c3101b23b4f448c380f5cd95e6b5c8848fe708fe802c5abdd0bc8e9807babfaaabff837da214c846ecaa52f93aa09592b54628234c5e62be113 1627 | languageName: node 1628 | linkType: hard 1629 | 1630 | "set-blocking@npm:^2.0.0, set-blocking@npm:~2.0.0": 1631 | version: 2.0.0 1632 | resolution: "set-blocking@npm:2.0.0" 1633 | checksum: 9e8f5aeb7cd850a60b5dbf47d42051137c14f58f375d9a70ca227b797d6ffed3dabf659587d2f183231085f1da2dc3067e2af9f5fcd66fb65c98da5fb54a22fb 1634 | languageName: node 1635 | linkType: hard 1636 | 1637 | "signal-exit@npm:^3.0.0": 1638 | version: 3.0.3 1639 | resolution: "signal-exit@npm:3.0.3" 1640 | checksum: 3a00292532e38048019c1b1f437b216f1e3ede040c5a1850f291528a462c916267e891c6483183b004aa83e855a3a58f61070caff1c5cb2c4ff1358416422c75 1641 | languageName: node 1642 | linkType: hard 1643 | 1644 | "source-map-support@npm:^0.5.17": 1645 | version: 0.5.19 1646 | resolution: "source-map-support@npm:0.5.19" 1647 | dependencies: 1648 | buffer-from: "npm:^1.0.0" 1649 | source-map: "npm:^0.6.0" 1650 | checksum: 038a8a96f34fca88a2c0f61814a319f5cf7cb9191127d51656140322cab9d703373cacdfc6a4480b4a24ff70c3d4d86703e0aeddf5c9c16ee0c3b4b80f22e832 1651 | languageName: node 1652 | linkType: hard 1653 | 1654 | "source-map@npm:^0.6.0": 1655 | version: 0.6.1 1656 | resolution: "source-map@npm:0.6.1" 1657 | checksum: cba9f44c3a4a0485f44a7760ebe427eecdd3b58011ae0459c05506b54f898835b2302073d6afa563a19b60ee9e54c82e33bc4a032e28bebacdfc635f1d0bf7e0 1658 | languageName: node 1659 | linkType: hard 1660 | 1661 | "sprintf-js@npm:~1.0.2": 1662 | version: 1.0.3 1663 | resolution: "sprintf-js@npm:1.0.3" 1664 | checksum: 3e0738f581ab5582868689318a4987ea532cdf220266c1af6fdc5a5091f5c4e758fe3fed9125ac82ed91119ec2cbe0762c0e069b59b929bf70e8bbbf879e56e5 1665 | languageName: node 1666 | linkType: hard 1667 | 1668 | "sshpk@npm:^1.7.0": 1669 | version: 1.16.1 1670 | resolution: "sshpk@npm:1.16.1" 1671 | dependencies: 1672 | asn1: "npm:~0.2.3" 1673 | assert-plus: "npm:^1.0.0" 1674 | bcrypt-pbkdf: "npm:^1.0.0" 1675 | dashdash: "npm:^1.12.0" 1676 | ecc-jsbn: "npm:~0.1.1" 1677 | getpass: "npm:^0.1.1" 1678 | jsbn: "npm:~0.1.0" 1679 | safer-buffer: "npm:^2.0.2" 1680 | tweetnacl: "npm:~0.14.0" 1681 | bin: 1682 | sshpk-conv: bin/sshpk-conv 1683 | sshpk-sign: bin/sshpk-sign 1684 | sshpk-verify: bin/sshpk-verify 1685 | checksum: bd822f8483cf27d07518eae5a82b36f1ad00acfe366354801dcd0ef41c5b97a60fb48b21edd407b93fd602b989a4b95d3fd96313d304c3468ab0088d2db82158 1686 | languageName: node 1687 | linkType: hard 1688 | 1689 | "string-width@npm:^1.0.1": 1690 | version: 1.0.2 1691 | resolution: "string-width@npm:1.0.2" 1692 | dependencies: 1693 | code-point-at: "npm:^1.0.0" 1694 | is-fullwidth-code-point: "npm:^1.0.0" 1695 | strip-ansi: "npm:^3.0.0" 1696 | checksum: 956c33f6e7da724d0b3f40c810f0a83f2384912c9e3d18baa09715d5891181af1da57ac9b2ab13a0264ec696899ca203f84fad910e0b2b43f1472d89bd20e8fb 1697 | languageName: node 1698 | linkType: hard 1699 | 1700 | "string-width@npm:^1.0.2 || 2": 1701 | version: 2.1.1 1702 | resolution: "string-width@npm:2.1.1" 1703 | dependencies: 1704 | is-fullwidth-code-point: "npm:^2.0.0" 1705 | strip-ansi: "npm:^4.0.0" 1706 | checksum: 0eae0e29fc8a95505ad3042d89cb1548afc811e787249e315f8f50ce6f935f6c03cab31ed31d325f439ff33fb54bb5b08e1a68504fb3c6857297f04ca3f9cff7 1707 | languageName: node 1708 | linkType: hard 1709 | 1710 | "string-width@npm:^3.0.0, string-width@npm:^3.1.0": 1711 | version: 3.1.0 1712 | resolution: "string-width@npm:3.1.0" 1713 | dependencies: 1714 | emoji-regex: "npm:^7.0.1" 1715 | is-fullwidth-code-point: "npm:^2.0.0" 1716 | strip-ansi: "npm:^5.1.0" 1717 | checksum: 5379ec68d3eefb1249cf1cc9116ec284a03f7ee0784b10849e5552e594c701064b4a5808dd9bb841e585746afc0ac79e46eac759332245501a2b119e493133fb 1718 | languageName: node 1719 | linkType: hard 1720 | 1721 | "string.prototype.trimend@npm:^1.0.1": 1722 | version: 1.0.1 1723 | resolution: "string.prototype.trimend@npm:1.0.1" 1724 | dependencies: 1725 | define-properties: "npm:^1.1.3" 1726 | es-abstract: "npm:^1.17.5" 1727 | checksum: 729f030d41c9123321e96f76a43ada93620ccaa519c83596573c5bfa3fa4bc2e365ea042fe20727096a985a92ebc207d78e5771458d7a274d61d5c569ca77714 1728 | languageName: node 1729 | linkType: hard 1730 | 1731 | "string.prototype.trimstart@npm:^1.0.1": 1732 | version: 1.0.1 1733 | resolution: "string.prototype.trimstart@npm:1.0.1" 1734 | dependencies: 1735 | define-properties: "npm:^1.1.3" 1736 | es-abstract: "npm:^1.17.5" 1737 | checksum: e232ee6e58f80558d28091360262db7c7ab4c63d77b6c8d068dee4a529158635ea4202f2f31ede6aaaaf612efa7b27a6c29b34efa3e440eb03233ade0e3bbb42 1738 | languageName: node 1739 | linkType: hard 1740 | 1741 | "string_decoder@npm:~1.1.1": 1742 | version: 1.1.1 1743 | resolution: "string_decoder@npm:1.1.1" 1744 | dependencies: 1745 | safe-buffer: "npm:~5.1.0" 1746 | checksum: 385c6f229dc54d087d10279049fbc75b0e648dd56ee63dbf15a526975947875fe2b41e0e26addc2e6f2c6e517753a77cfb05338e61d76ac44f49387e7238e025 1747 | languageName: node 1748 | linkType: hard 1749 | 1750 | "strip-ansi@npm:^3.0.0, strip-ansi@npm:^3.0.1": 1751 | version: 3.0.1 1752 | resolution: "strip-ansi@npm:3.0.1" 1753 | dependencies: 1754 | ansi-regex: "npm:^2.0.0" 1755 | checksum: 9ea89aab5ee05cd6b64bf8c919acf0d7b923d7bbb7a8a678b7b5cfb2b0a92cda18a35e1f16d04c5c00d1eb509c06383687ea2039dd8591ce83b8861602a67114 1756 | languageName: node 1757 | linkType: hard 1758 | 1759 | "strip-ansi@npm:^4.0.0": 1760 | version: 4.0.0 1761 | resolution: "strip-ansi@npm:4.0.0" 1762 | dependencies: 1763 | ansi-regex: "npm:^3.0.0" 1764 | checksum: 4617637523972a3b247162d3136cbac440b74166d9c3a51b62c0ddc6b8ec34b02d69e8adcaf42d090608447f9d3d57c40cd8953c6e24ddb80b4a948d5337c795 1765 | languageName: node 1766 | linkType: hard 1767 | 1768 | "strip-ansi@npm:^5.0.0, strip-ansi@npm:^5.1.0, strip-ansi@npm:^5.2.0": 1769 | version: 5.2.0 1770 | resolution: "strip-ansi@npm:5.2.0" 1771 | dependencies: 1772 | ansi-regex: "npm:^4.1.0" 1773 | checksum: c1a35871e148a1c60310d33afbce3bdec6b3f471c4c3aab25b6b2a893aac4b18791a24206bb89a399e8e1a48b09da85e0bd87060c840c6dd0fbd06361b3c8f90 1774 | languageName: node 1775 | linkType: hard 1776 | 1777 | "strip-json-comments@npm:3.0.1": 1778 | version: 3.0.1 1779 | resolution: "strip-json-comments@npm:3.0.1" 1780 | checksum: 6b0a84d23297e44144e31d86b8938981cf14b9452fdae2345f86cd9440ec69d3bb0a3ff4710968560bac2445c0712de075859235fa76066a396861d3f4127ee6 1781 | languageName: node 1782 | linkType: hard 1783 | 1784 | "supports-color@npm:7.1.0": 1785 | version: 7.1.0 1786 | resolution: "supports-color@npm:7.1.0" 1787 | dependencies: 1788 | has-flag: "npm:^4.0.0" 1789 | checksum: dbb602a5369f478aae4f2bd4e2287679275e94f5c52721419fd2bdd6accfb31421f0750a365913a59da1b79a79e22074ec856e9a03efd515852f209f8dd72b98 1790 | languageName: node 1791 | linkType: hard 1792 | 1793 | "supports-color@npm:^5.3.0": 1794 | version: 5.5.0 1795 | resolution: "supports-color@npm:5.5.0" 1796 | dependencies: 1797 | has-flag: "npm:^3.0.0" 1798 | checksum: 2eca8c4c8fccd2bd0027af240f85e99b1c9cb221186288dd478ce0fc61bdc07394e47f1bba2c91fe3ae432764772e3639e9c48bef19817267f151ae4a9b9ebef 1799 | languageName: node 1800 | linkType: hard 1801 | 1802 | "tar@npm:^6.0.1": 1803 | version: 6.0.2 1804 | resolution: "tar@npm:6.0.2" 1805 | dependencies: 1806 | chownr: "npm:^2.0.0" 1807 | fs-minipass: "npm:^2.0.0" 1808 | minipass: "npm:^3.0.0" 1809 | minizlib: "npm:^2.1.0" 1810 | mkdirp: "npm:^1.0.3" 1811 | yallist: "npm:^4.0.0" 1812 | checksum: 0bbcac881d27597151c8b65b970891682cea131a583729187f19fe8fc4eeb73a79581e8b2f54793dfbf6a05fe5dba42a4ad52353d7038b9da83dbb60836bc833 1813 | languageName: node 1814 | linkType: hard 1815 | 1816 | "to-regex-range@npm:^5.0.1": 1817 | version: 5.0.1 1818 | resolution: "to-regex-range@npm:5.0.1" 1819 | dependencies: 1820 | is-number: "npm:^7.0.0" 1821 | checksum: 16564897c76bbd25bd3c375ee8d4b1fd3ac965fc4ab550ff034a1dddb53816ec06dc27095468394ad4de5978d5e831a9d1ae4cb31080dc4ebd9ba80a47dc1a4f 1822 | languageName: node 1823 | linkType: hard 1824 | 1825 | "tough-cookie@npm:~2.5.0": 1826 | version: 2.5.0 1827 | resolution: "tough-cookie@npm:2.5.0" 1828 | dependencies: 1829 | psl: "npm:^1.1.28" 1830 | punycode: "npm:^2.1.1" 1831 | checksum: 93504e7af3f117ea2feb8ae14f16931430f0ed94a4d0242d7f8efb9ac16e970731bd660242dd7f0afa20b750eb97affd5053cfc8302f77714d123a7b6f4d60b8 1832 | languageName: node 1833 | linkType: hard 1834 | 1835 | "ts-node@npm:^8.10.2": 1836 | version: 8.10.2 1837 | resolution: "ts-node@npm:8.10.2" 1838 | dependencies: 1839 | arg: "npm:^4.1.0" 1840 | diff: "npm:^4.0.1" 1841 | make-error: "npm:^1.1.1" 1842 | source-map-support: "npm:^0.5.17" 1843 | yn: "npm:3.1.1" 1844 | peerDependencies: 1845 | typescript: ">=2.7" 1846 | bin: 1847 | ts-node: dist/bin.js 1848 | ts-node-script: dist/bin-script.js 1849 | ts-node-transpile-only: dist/bin-transpile.js 1850 | ts-script: dist/bin-script-deprecated.js 1851 | checksum: 92d6fa807921a0785c035ed5e57f759a3bbb44e8c4a3cb706f9111cebfbc61c5ffb5d00c3ad860a6f70460934ce171d619f51c232a9e3afc82217149b59f28f7 1852 | languageName: node 1853 | linkType: hard 1854 | 1855 | "tslib@npm:^2.0.0": 1856 | version: 2.0.0 1857 | resolution: "tslib@npm:2.0.0" 1858 | checksum: e3dbf86ccf6847e71a57952cb5071d27cc61220f161ddcc76f5619a9255a7c491752f6bd40b4d5ea4accb83329f1a438ee779fd4edec3678ff0c0820e18a35f3 1859 | languageName: node 1860 | linkType: hard 1861 | 1862 | "tunnel-agent@npm:^0.6.0": 1863 | version: 0.6.0 1864 | resolution: "tunnel-agent@npm:0.6.0" 1865 | dependencies: 1866 | safe-buffer: "npm:^5.0.1" 1867 | checksum: 04bb1f31a4f757d78547536d3c58bf7d24645735ecc5af75536cf9ee46e8d4d8c802518a16062d9c07f78874946dd2ea600d2df42e5c538cdd9a414994bce54d 1868 | languageName: node 1869 | linkType: hard 1870 | 1871 | "tweetnacl@npm:^0.14.3, tweetnacl@npm:~0.14.0": 1872 | version: 0.14.5 1873 | resolution: "tweetnacl@npm:0.14.5" 1874 | checksum: bd01b852653d25afd67c3145b4241f93db1fda9753b78d3d848f3eed5f32af4f1e49b6cd44571b32b0498d18a7344ff4033d6b1f76c3732c8cf4b85049f9cf49 1875 | languageName: node 1876 | linkType: hard 1877 | 1878 | "typanion@workspace:.": 1879 | version: 0.0.0-use.local 1880 | resolution: "typanion@workspace:." 1881 | dependencies: 1882 | "@rollup/plugin-typescript": "npm:^8.2.1" 1883 | "@types/chai": "npm:^4.2.11" 1884 | "@types/mocha": "npm:^7.0.2" 1885 | "@types/node": "npm:^17.0.21" 1886 | chai: "npm:^4.3.4" 1887 | mocha: "npm:^8.0.1" 1888 | rollup: "npm:^2.17.0" 1889 | ts-node: "npm:^8.10.2" 1890 | tslib: "npm:^2.0.0" 1891 | typescript: "npm:^4.6.3" 1892 | languageName: unknown 1893 | linkType: soft 1894 | 1895 | "type-detect@npm:^4.0.0, type-detect@npm:^4.0.5": 1896 | version: 4.0.8 1897 | resolution: "type-detect@npm:4.0.8" 1898 | checksum: 2d2111a44529a381e9be7090066cc89b60ac2c822194e3d213a0d5f630e81abfd07d2b91a324ef4a173973c5b0c68b0bdf29ac6896459cf819914a6f56199e0f 1899 | languageName: node 1900 | linkType: hard 1901 | 1902 | "typescript@npm:^4.6.3": 1903 | version: 4.6.3 1904 | resolution: "typescript@npm:4.6.3" 1905 | bin: 1906 | tsc: bin/tsc 1907 | tsserver: bin/tsserver 1908 | checksum: 6d79afd83b60614b18d469795c0d5725dcc89d8c943b0b09162ce15e1ec7cd73111642accdbb10c2be21bfabccb5b296554b5ea09948b2931d51ecbdece22a48 1909 | languageName: node 1910 | linkType: hard 1911 | 1912 | "typescript@patch:typescript@npm%3A^4.6.3#optional!builtin": 1913 | version: 4.6.3 1914 | resolution: "typescript@patch:typescript@npm%3A4.6.3#optional!builtin::version=4.6.3&hash=7ad353" 1915 | bin: 1916 | tsc: bin/tsc 1917 | tsserver: bin/tsserver 1918 | checksum: 694ec10096afa6f172ffeb1b3794bbf4a354f9d59af4f1ac62d4a705a4cdc708f970514deaf7d9a87c7ecdc4b1f5d297ce89ecbde720a251caac801adf5588f6 1919 | languageName: node 1920 | linkType: hard 1921 | 1922 | "uri-js@npm:^4.2.2": 1923 | version: 4.2.2 1924 | resolution: "uri-js@npm:4.2.2" 1925 | dependencies: 1926 | punycode: "npm:^2.1.0" 1927 | checksum: c9ac43cb22e335dfa639f5c35b3c73331b6af6071bdb23ed0866642d9001c19b461966288dd29e0e5a06ce96dc3604cb46770dc1fef0fd134af0cd70363750a3 1928 | languageName: node 1929 | linkType: hard 1930 | 1931 | "util-deprecate@npm:~1.0.1": 1932 | version: 1.0.2 1933 | resolution: "util-deprecate@npm:1.0.2" 1934 | checksum: 6a88ed8344d07f2324b304ee36def365d967953b5a9c15baa3213eb3909e86a7da1ee70a4c2133e80c23d6c1987590e9c3c57d874e20a124f9e41620b462fa57 1935 | languageName: node 1936 | linkType: hard 1937 | 1938 | "uuid@npm:^3.3.2": 1939 | version: 3.4.0 1940 | resolution: "uuid@npm:3.4.0" 1941 | bin: 1942 | uuid: ./bin/uuid 1943 | checksum: c84dbfcb94389fea5a09020802df2a1227d183ceabaa5256658194dfad045c83fe72366b64b165b6445a480fac8a75d0e982033f3cb393713674b3cd938063fa 1944 | languageName: node 1945 | linkType: hard 1946 | 1947 | "verror@npm:1.10.0": 1948 | version: 1.10.0 1949 | resolution: "verror@npm:1.10.0" 1950 | dependencies: 1951 | assert-plus: "npm:^1.0.0" 1952 | core-util-is: "npm:1.0.2" 1953 | extsprintf: "npm:^1.2.0" 1954 | checksum: ec26653c2110a7c2cfbaf41e3f3e87a5e08cbde81f7a568603c5ae4c9459acef5a1e81cbec551f4b9d0352e4b99121ee891e77c621b8237be9ff3862764d55f5 1955 | languageName: node 1956 | linkType: hard 1957 | 1958 | "which-module@npm:^2.0.0": 1959 | version: 2.0.0 1960 | resolution: "which-module@npm:2.0.0" 1961 | checksum: 34b64cec4bde9d8a0205374ac1355cb5fed3a83cf5fd9b7582775a81ac7bb2de7d96887ad0861a29f1e0c4758705a3fd8a4132223c27ef07f9f9d4029ac7f4b5 1962 | languageName: node 1963 | linkType: hard 1964 | 1965 | "which@npm:2.0.2, which@npm:^2.0.2": 1966 | version: 2.0.2 1967 | resolution: "which@npm:2.0.2" 1968 | dependencies: 1969 | isexe: "npm:^2.0.0" 1970 | bin: 1971 | node-which: ./bin/node-which 1972 | checksum: 3728616c789b289c36ba2572887145e0736f06fe3435b8fef17e27eb5ec0696f61a21e356dd7fa58486346e57186863afa1b6c27c7665f7e674c8124f7f61157 1973 | languageName: node 1974 | linkType: hard 1975 | 1976 | "wide-align@npm:1.1.3, wide-align@npm:^1.1.0": 1977 | version: 1.1.3 1978 | resolution: "wide-align@npm:1.1.3" 1979 | dependencies: 1980 | string-width: "npm:^1.0.2 || 2" 1981 | checksum: a1162c3ebacebb62cb1eb373934d0e9c13487a4fa46f5055d854625b19369d976ac967a03f0892b96189410d391198bbc9417093323a1111e8cf1f45b8aa3cdc 1982 | languageName: node 1983 | linkType: hard 1984 | 1985 | "workerpool@npm:6.0.0": 1986 | version: 6.0.0 1987 | resolution: "workerpool@npm:6.0.0" 1988 | checksum: eaf97dc42002b2bd78f4f5803f0c500b403b0458b4df4808ccb4a2c9693cae6a623429bf4e76f11a941bc2c9b069e790f9df4107e85608f46e715821474bcf9e 1989 | languageName: node 1990 | linkType: hard 1991 | 1992 | "wrap-ansi@npm:^5.1.0": 1993 | version: 5.1.0 1994 | resolution: "wrap-ansi@npm:5.1.0" 1995 | dependencies: 1996 | ansi-styles: "npm:^3.2.0" 1997 | string-width: "npm:^3.0.0" 1998 | strip-ansi: "npm:^5.0.0" 1999 | checksum: 5199389096e8fc8021566a14a212fa58096b2d62faa517a65d4f654a003082ff75c952961c427cc69d15798240800bcc2bd6792e474dcc9a669d78d00e5ecbf6 2000 | languageName: node 2001 | linkType: hard 2002 | 2003 | "wrappy@npm:1": 2004 | version: 1.0.2 2005 | resolution: "wrappy@npm:1.0.2" 2006 | checksum: 37d243a577dfeee20586eae1e3208dfb4e4cea1211a2a4116a19b50d91e619ff3dbc5ec934e28ca9baaa11a65df826c8d65c5fd1bb81f0ce0dadb469d47061c2 2007 | languageName: node 2008 | linkType: hard 2009 | 2010 | "y18n@npm:^4.0.0": 2011 | version: 4.0.0 2012 | resolution: "y18n@npm:4.0.0" 2013 | checksum: 185a63d5ce3ef460152817b32efb38d036a68dc3ac5d1d4e859fdc8216202fccf1cfed9e63b9a3e4ca3417802448eb92789aa412b21dcdfa3ca2790f94ad2ef4 2014 | languageName: node 2015 | linkType: hard 2016 | 2017 | "yallist@npm:^4.0.0": 2018 | version: 4.0.0 2019 | resolution: "yallist@npm:4.0.0" 2020 | checksum: cd7fe32508c6942d8b979278fbe13846fe88cd6840d78043d08c6b2c74d67ce38b58bd21618dca8a4e132dcc025fc0e66a7d87ca10cf6ed338465607ebff4378 2021 | languageName: node 2022 | linkType: hard 2023 | 2024 | "yargs-parser@npm:13.1.2, yargs-parser@npm:^13.1.2": 2025 | version: 13.1.2 2026 | resolution: "yargs-parser@npm:13.1.2" 2027 | dependencies: 2028 | camelcase: "npm:^5.0.0" 2029 | decamelize: "npm:^1.2.0" 2030 | checksum: 7f80a65155bc4096a82581e5b67e525055fd9104823883ce1c89231f9e55db4a95eac415d67db41bd07485540e81c0fa830229323363ca0ca63e03d7d4fae05e 2031 | languageName: node 2032 | linkType: hard 2033 | 2034 | "yargs-unparser@npm:1.6.0": 2035 | version: 1.6.0 2036 | resolution: "yargs-unparser@npm:1.6.0" 2037 | dependencies: 2038 | flat: "npm:^4.1.0" 2039 | lodash: "npm:^4.17.15" 2040 | yargs: "npm:^13.3.0" 2041 | checksum: eb49cdba0cb172df4f3024db01175ff33ceb8f101099c4eab800a29b2677be2d6ea8126b4090f3f7ea692b7313864794decbee0c5f497fdb9a66d8ccd0823f5b 2042 | languageName: node 2043 | linkType: hard 2044 | 2045 | "yargs@npm:13.3.2, yargs@npm:^13.3.0": 2046 | version: 13.3.2 2047 | resolution: "yargs@npm:13.3.2" 2048 | dependencies: 2049 | cliui: "npm:^5.0.0" 2050 | find-up: "npm:^3.0.0" 2051 | get-caller-file: "npm:^2.0.1" 2052 | require-directory: "npm:^2.1.1" 2053 | require-main-filename: "npm:^2.0.0" 2054 | set-blocking: "npm:^2.0.0" 2055 | string-width: "npm:^3.0.0" 2056 | which-module: "npm:^2.0.0" 2057 | y18n: "npm:^4.0.0" 2058 | yargs-parser: "npm:^13.1.2" 2059 | checksum: b78174cd2d5a704cfec92b00ccc618de483e5083f39ce80d0321480a33fa428e986799e993ba71d28a3467645fbd49cff78f7a1b4e64d6600312b51d5a185a10 2060 | languageName: node 2061 | linkType: hard 2062 | 2063 | "yn@npm:3.1.1": 2064 | version: 3.1.1 2065 | resolution: "yn@npm:3.1.1" 2066 | checksum: 890a9ce10f1f6691316f521444dcdc2d012dbfba423ec2252444dab5888def4ee48751304e51302c6d14197a1e9407256153a357c955bff1d659df592cfda456 2067 | languageName: node 2068 | linkType: hard 2069 | --------------------------------------------------------------------------------