├── .github └── workflows │ └── publish.yml ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── cmdy.ts ├── index.ts └── test │ ├── cmdy.test.ts │ ├── example.ts │ ├── json.ts │ └── parser.ts └── tsconfig.json /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: technote-space/can-npm-publish-action@v1 14 | - name: Use Node.js 16 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: 16 18 | cache: 'npm' 19 | cache-dependency-path: 'package-lock.json' 20 | - run: npm i -g npm@latest 21 | - run: npm ci 22 | - run: npm run build 23 | - run: npm test 24 | - name: npmjs.org npm publish 25 | uses: JS-DevTools/npm-publish@v1 26 | with: 27 | token: ${{ secrets.NPM_TOKEN }} 28 | registry: https://registry.npmjs.org 29 | access: public 30 | - run: npm pkg set "name=@${{ GITHUB.REPOSITORY }}" 31 | - name: github.com npm publish 32 | uses: JS-DevTools/npm-publish@v1 33 | with: 34 | token: ${{ GITHUB.TOKEN }} 35 | registry: https://npm.pkg.github.com 36 | access: public 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # app data 2 | .store 3 | 4 | # mac 5 | .DS_Store 6 | 7 | # windows 8 | Thumbs.db 9 | 10 | # vscode 11 | **/.vscode 12 | 13 | # codec 14 | core 15 | 16 | # npm 17 | **/node_modules 18 | **/.npmrc 19 | **/.npm 20 | 21 | # Log files 22 | **/logs 23 | *.log 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | lerna-debug.log* 28 | 29 | # apps 30 | *.app 31 | *.exe 32 | *.war 33 | 34 | # media files 35 | *.mp4 36 | *.tiff 37 | *.avi 38 | *.flv 39 | *.mov 40 | *.wmv 41 | 42 | # typescript 43 | dist 44 | src 45 | !src/**/*.ts 46 | 47 | # git 48 | !.keep 49 | !.gitkeep 50 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # global ignore 2 | * 3 | 4 | # exe scripts 5 | !bin 6 | 7 | # npm configs 8 | !package-lock.json 9 | !package.json 10 | 11 | # project infos 12 | !README.md 13 | !LICENCE 14 | 15 | # dist 16 | !dist/**/* 17 | dist/**/*.d.ts.map 18 | dist/**/*.js.map 19 | 20 | # tests 21 | dist/test/**/* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 majo418 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # typenvy (deprecated) 2 | 3 | ![CI/CD](https://github.com/majo418/typenvy/workflows/Publish/badge.svg) 4 | ![MIT](https://img.shields.io/badge/license-MIT-blue.svg) 5 | ![typescript](https://img.shields.io/badge/dynamic/json?style=plastic&color=blue&label=Typescript&prefix=v&query=devDependencies.typescript&url=https%3A%2F%2Fraw.githubusercontent.com%2Fmajo418%2Ftypenvy%2Fmain%2Fpackage.json) 6 | ![npm](https://img.shields.io/npm/v/typenvy.svg?style=plastic&logo=npm&color=red) 7 | ![github](https://img.shields.io/badge/dynamic/json?style=plastic&color=darkviolet&label=GitHub&prefix=v&query=version&url=https%3A%2F%2Fraw.githubusercontent.com%2Fmajo418%2Ftypenvy%2Fmain%2Fpackage.json) 8 | 9 | ![](https://img.shields.io/badge/dynamic/json?color=green&label=watchers&query=watchers&suffix=x&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fmajo418%2Ftypenvy) 10 | ![](https://img.shields.io/badge/dynamic/json?color=yellow&label=stars&query=stargazers_count&suffix=x&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fmajo418%2Ftypenvy) 11 | ![](https://img.shields.io/badge/dynamic/json?color=orange&label=subscribers&query=subscribers_count&suffix=x&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fmajo418%2Ftypenvy) 12 | ![](https://img.shields.io/badge/dynamic/json?color=navy&label=forks&query=forks&suffix=x&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fmajo418%2Ftypenvy) 13 | ![](https://img.shields.io/badge/dynamic/json?color=darkred&label=open%20issues&query=open_issues&suffix=x&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fmajo418%2Ftypenvy) 14 | 15 | # table of contents 16 | - [typenvy](#typenvy) 17 | - [table of contents](#table-of-contents) 18 | - [About](#about) 19 | - [Getting started](#getting-started) 20 | - [other functions](#other-functions) 21 | - [npm scripts](#npm-scripts) 22 | - [use](#use) 23 | - [base scripts](#base-scripts) 24 | - [watch mode](#watch-mode) 25 | - [contribution](#contribution) 26 | 27 | # About 28 | `typenvy` is a environment managment library. 29 | 30 | ## Getting started 31 | ### 1. install typenvy 32 | ```sh 33 | npm i typenvy 34 | ``` 35 | 36 | ### 2. env file 37 | Create a example environment file at `./src/env/env.ts`: 38 | ```ts 39 | import * as typenvy from "typenvy" 40 | export const envDefaults = { 41 | PRODUCTION: (process.env.NODE_ENV === "production") as boolean, 42 | VERBOSE: false as boolean, 43 | 44 | PORT: 8080 as number, 45 | API_KEY: undefined as string, 46 | API_URL: undefined as string, 47 | } 48 | export const envTypes: typenvy.VariablesTypes = { 49 | PRODUCTION: [typenvy.TC_BOOLEAN], 50 | VERBOSE: [typenvy.TC_BOOLEAN], 51 | 52 | PORT: [typenvy.TC_NUMBER], 53 | API_KEY: [typenvy.TC_STRING], 54 | API_URL: [typenvy.TC_STRING], 55 | } 56 | ``` 57 | 58 | ### 3. env parser 59 | Create a example environment parser file at `./src/env/envParser.ts`: 60 | ```ts 61 | import { parseEnv } from "typenvy" 62 | import { envDefaults, envTypes } from "./env" 63 | 64 | export const env = parseEnv( 65 | envDefaults, 66 | envTypes 67 | ) 68 | .setProcessEnv() 69 | .errExit() 70 | .env 71 | export default env 72 | ``` 73 | 74 | ### 4. load and print env in "./src/index.ts" 75 | ```ts 76 | import env from "./env/envParser" 77 | 78 | console.log("parser env: ", env) 79 | console.log("process env: ", process.env) 80 | ``` 81 | 82 | ### 5. start and error 83 | If you run the `index.js` after compile the app throws an error. 84 | This is because in the `env.ts` there is no default value provided for `API_KEY` and `API_URL`. 85 | 86 | There are 3 options to remove this error: 87 | 88 | #### 1. Set environment variables 89 | Define the variables in your shell: 90 | ```sh 91 | export API_KEY="qwertzui" 92 | export API_URL="https://api.github.io/v2/repo/majo418/testrepo" 93 | ``` 94 | #### 2. Allow undefined as value 95 | Allow undefined as environment variable value in `env.ts`: 96 | ```ts 97 | export const variablesTypes: typenvy.VariablesTypes = { 98 | PRODUCTION: [typenvy.TC_BOOLEAN], 99 | VERBOSE: [typenvy.TC_BOOLEAN], 100 | 101 | PORT: [typenvy.TC_NUMBER], 102 | API_KEY: [typenvy.TC_STRING, typenvy.TC_UNDEFINED], // <--- 103 | API_URL: [typenvy.TC_STRING, typenvy.TC_UNDEFINED], // <--- 104 | } 105 | ``` 106 | #### 3. Set a default value 107 | Allow default environment values in `envParser.ts`: 108 | ```ts 109 | export const defaultEnv = { 110 | PRODUCTION: (process.env.NODE_ENV === "production") as boolean, 111 | VERBOSE: false as boolean, 112 | 113 | PORT: 8080 as number, 114 | API_KEY: "myDEfaultAPIkey" as string, 115 | API_URL: "https://api.cloudflare.com/v1/dns" as string, 116 | } 117 | ``` 118 | 119 | # other functions 120 | By using the `parseEnv()` function tou get a `EnvResult`. 121 | Here are all function of the Environment Result: 122 | ```ts 123 | export interface EnvResult { 124 | // Overwrite default values 125 | overwriteEnv( 126 | env: { [key: string]: any } 127 | ): EnvResult 128 | // Set value if its missing in default values 129 | setMissingEnv( 130 | env: { [key: string]: any } 131 | ): EnvResult 132 | // Put all env value as strings into process.env 133 | setProcessEnv(): EnvResult 134 | // Clear all env values from process.env 135 | clearProcessEnv( 136 | justEqualValues: boolean = true 137 | ): EnvResult 138 | // Print env errors to console 139 | errPrint(): EnvResult 140 | // Throw env errors 141 | errThrow(): EnvResult 142 | // Exit on error 143 | errExit( 144 | exitCode: number = 1 145 | ): EnvResult | never 146 | } 147 | ``` 148 | 149 | # npm scripts 150 | The npm scripts are made for linux but can also work on mac and windows. 151 | ## use 152 | You can run npm scripts in the project folder like this: 153 | ```sh 154 | npm run 155 | ``` 156 | Here is an example: 157 | ```sh 158 | npm run test 159 | ``` 160 | 161 | ## base scripts 162 | You can find all npm scripts in the `package.json` file. 163 | This is a list of the most important npm scripts: 164 | - test // test the app 165 | - build // build the app 166 | - exec // run the app 167 | - start // build and run the app 168 | 169 | ## watch mode 170 | Like this example you can run all npm scripts in watch mode: 171 | ```sh 172 | npm run start:watch 173 | ``` 174 | 175 | # contribution 176 | - 1. fork the project 177 | - 2. implement your idea 178 | - 3. create a pull/merge request 179 | ```ts 180 | // please create seperated forks for different kind of featues/ideas/structure changes/implementations 181 | ``` 182 | 183 | --- 184 | **cya ;3** 185 | *by majo418* 186 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typenvy", 3 | "version": "1.5.15", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "typenvy", 9 | "version": "1.5.15", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@types/chai": "4", 13 | "@types/mocha": "9", 14 | "@types/node": "16", 15 | "chai": "4", 16 | "cmdy": "1", 17 | "mocha": "9", 18 | "nodemon": "2", 19 | "ts-node": "10", 20 | "typescript": "4" 21 | } 22 | }, 23 | "node_modules/@cspotcode/source-map-support": { 24 | "version": "0.8.1", 25 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 26 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 27 | "dev": true, 28 | "dependencies": { 29 | "@jridgewell/trace-mapping": "0.3.9" 30 | }, 31 | "engines": { 32 | "node": ">=12" 33 | } 34 | }, 35 | "node_modules/@jridgewell/resolve-uri": { 36 | "version": "3.1.0", 37 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 38 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 39 | "dev": true, 40 | "engines": { 41 | "node": ">=6.0.0" 42 | } 43 | }, 44 | "node_modules/@jridgewell/sourcemap-codec": { 45 | "version": "1.4.14", 46 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 47 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 48 | "dev": true 49 | }, 50 | "node_modules/@jridgewell/trace-mapping": { 51 | "version": "0.3.9", 52 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 53 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 54 | "dev": true, 55 | "dependencies": { 56 | "@jridgewell/resolve-uri": "^3.0.3", 57 | "@jridgewell/sourcemap-codec": "^1.4.10" 58 | } 59 | }, 60 | "node_modules/@tsconfig/node10": { 61 | "version": "1.0.9", 62 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", 63 | "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", 64 | "dev": true 65 | }, 66 | "node_modules/@tsconfig/node12": { 67 | "version": "1.0.11", 68 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", 69 | "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", 70 | "dev": true 71 | }, 72 | "node_modules/@tsconfig/node14": { 73 | "version": "1.0.3", 74 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", 75 | "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", 76 | "dev": true 77 | }, 78 | "node_modules/@tsconfig/node16": { 79 | "version": "1.0.3", 80 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", 81 | "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", 82 | "dev": true 83 | }, 84 | "node_modules/@types/chai": { 85 | "version": "4.3.4", 86 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", 87 | "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", 88 | "dev": true 89 | }, 90 | "node_modules/@types/mocha": { 91 | "version": "9.1.1", 92 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", 93 | "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", 94 | "dev": true 95 | }, 96 | "node_modules/@types/node": { 97 | "version": "16.18.8", 98 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.8.tgz", 99 | "integrity": "sha512-TrpoNiaPvBH5h8rQQenMtVsJXtGsVBRJrcp2Ik6oEt99jHfGvDLh20VTTq3ixTbjYujukYz1IlY4N8a8yfY0jA==", 100 | "dev": true 101 | }, 102 | "node_modules/@ungap/promise-all-settled": { 103 | "version": "1.1.2", 104 | "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", 105 | "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", 106 | "dev": true 107 | }, 108 | "node_modules/abbrev": { 109 | "version": "1.1.1", 110 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 111 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", 112 | "dev": true 113 | }, 114 | "node_modules/acorn": { 115 | "version": "8.8.1", 116 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", 117 | "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", 118 | "dev": true, 119 | "bin": { 120 | "acorn": "bin/acorn" 121 | }, 122 | "engines": { 123 | "node": ">=0.4.0" 124 | } 125 | }, 126 | "node_modules/acorn-walk": { 127 | "version": "8.2.0", 128 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 129 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 130 | "dev": true, 131 | "engines": { 132 | "node": ">=0.4.0" 133 | } 134 | }, 135 | "node_modules/ansi-colors": { 136 | "version": "4.1.1", 137 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 138 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 139 | "dev": true, 140 | "engines": { 141 | "node": ">=6" 142 | } 143 | }, 144 | "node_modules/ansi-regex": { 145 | "version": "5.0.1", 146 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 147 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 148 | "dev": true, 149 | "engines": { 150 | "node": ">=8" 151 | } 152 | }, 153 | "node_modules/ansi-styles": { 154 | "version": "4.3.0", 155 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 156 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 157 | "dev": true, 158 | "dependencies": { 159 | "color-convert": "^2.0.1" 160 | }, 161 | "engines": { 162 | "node": ">=8" 163 | }, 164 | "funding": { 165 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 166 | } 167 | }, 168 | "node_modules/anymatch": { 169 | "version": "3.1.3", 170 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 171 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 172 | "dev": true, 173 | "dependencies": { 174 | "normalize-path": "^3.0.0", 175 | "picomatch": "^2.0.4" 176 | }, 177 | "engines": { 178 | "node": ">= 8" 179 | } 180 | }, 181 | "node_modules/arg": { 182 | "version": "4.1.3", 183 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 184 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 185 | "dev": true 186 | }, 187 | "node_modules/argparse": { 188 | "version": "2.0.1", 189 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 190 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 191 | "dev": true 192 | }, 193 | "node_modules/assertion-error": { 194 | "version": "1.1.0", 195 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 196 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 197 | "dev": true, 198 | "engines": { 199 | "node": "*" 200 | } 201 | }, 202 | "node_modules/balanced-match": { 203 | "version": "1.0.2", 204 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 205 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 206 | "dev": true 207 | }, 208 | "node_modules/binary-extensions": { 209 | "version": "2.2.0", 210 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 211 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 212 | "dev": true, 213 | "engines": { 214 | "node": ">=8" 215 | } 216 | }, 217 | "node_modules/brace-expansion": { 218 | "version": "1.1.11", 219 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 220 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 221 | "dev": true, 222 | "dependencies": { 223 | "balanced-match": "^1.0.0", 224 | "concat-map": "0.0.1" 225 | } 226 | }, 227 | "node_modules/braces": { 228 | "version": "3.0.2", 229 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 230 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 231 | "dev": true, 232 | "dependencies": { 233 | "fill-range": "^7.0.1" 234 | }, 235 | "engines": { 236 | "node": ">=8" 237 | } 238 | }, 239 | "node_modules/browser-stdout": { 240 | "version": "1.3.1", 241 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 242 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 243 | "dev": true 244 | }, 245 | "node_modules/camelcase": { 246 | "version": "6.3.0", 247 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 248 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 249 | "dev": true, 250 | "engines": { 251 | "node": ">=10" 252 | }, 253 | "funding": { 254 | "url": "https://github.com/sponsors/sindresorhus" 255 | } 256 | }, 257 | "node_modules/chai": { 258 | "version": "4.3.7", 259 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", 260 | "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", 261 | "dev": true, 262 | "dependencies": { 263 | "assertion-error": "^1.1.0", 264 | "check-error": "^1.0.2", 265 | "deep-eql": "^4.1.2", 266 | "get-func-name": "^2.0.0", 267 | "loupe": "^2.3.1", 268 | "pathval": "^1.1.1", 269 | "type-detect": "^4.0.5" 270 | }, 271 | "engines": { 272 | "node": ">=4" 273 | } 274 | }, 275 | "node_modules/chalk": { 276 | "version": "4.1.2", 277 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 278 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 279 | "dev": true, 280 | "dependencies": { 281 | "ansi-styles": "^4.1.0", 282 | "supports-color": "^7.1.0" 283 | }, 284 | "engines": { 285 | "node": ">=10" 286 | }, 287 | "funding": { 288 | "url": "https://github.com/chalk/chalk?sponsor=1" 289 | } 290 | }, 291 | "node_modules/chalk/node_modules/supports-color": { 292 | "version": "7.2.0", 293 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 294 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 295 | "dev": true, 296 | "dependencies": { 297 | "has-flag": "^4.0.0" 298 | }, 299 | "engines": { 300 | "node": ">=8" 301 | } 302 | }, 303 | "node_modules/check-error": { 304 | "version": "1.0.2", 305 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 306 | "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", 307 | "dev": true, 308 | "engines": { 309 | "node": "*" 310 | } 311 | }, 312 | "node_modules/chokidar": { 313 | "version": "3.5.3", 314 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 315 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 316 | "dev": true, 317 | "funding": [ 318 | { 319 | "type": "individual", 320 | "url": "https://paulmillr.com/funding/" 321 | } 322 | ], 323 | "dependencies": { 324 | "anymatch": "~3.1.2", 325 | "braces": "~3.0.2", 326 | "glob-parent": "~5.1.2", 327 | "is-binary-path": "~2.1.0", 328 | "is-glob": "~4.0.1", 329 | "normalize-path": "~3.0.0", 330 | "readdirp": "~3.6.0" 331 | }, 332 | "engines": { 333 | "node": ">= 8.10.0" 334 | }, 335 | "optionalDependencies": { 336 | "fsevents": "~2.3.2" 337 | } 338 | }, 339 | "node_modules/cliui": { 340 | "version": "7.0.4", 341 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 342 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 343 | "dev": true, 344 | "dependencies": { 345 | "string-width": "^4.2.0", 346 | "strip-ansi": "^6.0.0", 347 | "wrap-ansi": "^7.0.0" 348 | } 349 | }, 350 | "node_modules/cmdy": { 351 | "version": "1.4.5", 352 | "resolved": "https://registry.npmjs.org/cmdy/-/cmdy-1.4.5.tgz", 353 | "integrity": "sha512-3fAAETs3C013TZndqdJrzWKLjB3MFh4KSKThSR8eJBpgxsrRsbJ30lRDgkzu4fX99Mnt2RN9m1fDMiCm8daItw==", 354 | "dev": true 355 | }, 356 | "node_modules/color-convert": { 357 | "version": "2.0.1", 358 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 359 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 360 | "dev": true, 361 | "dependencies": { 362 | "color-name": "~1.1.4" 363 | }, 364 | "engines": { 365 | "node": ">=7.0.0" 366 | } 367 | }, 368 | "node_modules/color-name": { 369 | "version": "1.1.4", 370 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 371 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 372 | "dev": true 373 | }, 374 | "node_modules/concat-map": { 375 | "version": "0.0.1", 376 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 377 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 378 | "dev": true 379 | }, 380 | "node_modules/create-require": { 381 | "version": "1.1.1", 382 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 383 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 384 | "dev": true 385 | }, 386 | "node_modules/debug": { 387 | "version": "4.3.3", 388 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", 389 | "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", 390 | "dev": true, 391 | "dependencies": { 392 | "ms": "2.1.2" 393 | }, 394 | "engines": { 395 | "node": ">=6.0" 396 | }, 397 | "peerDependenciesMeta": { 398 | "supports-color": { 399 | "optional": true 400 | } 401 | } 402 | }, 403 | "node_modules/debug/node_modules/ms": { 404 | "version": "2.1.2", 405 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 406 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 407 | "dev": true 408 | }, 409 | "node_modules/decamelize": { 410 | "version": "4.0.0", 411 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 412 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 413 | "dev": true, 414 | "engines": { 415 | "node": ">=10" 416 | }, 417 | "funding": { 418 | "url": "https://github.com/sponsors/sindresorhus" 419 | } 420 | }, 421 | "node_modules/deep-eql": { 422 | "version": "4.1.3", 423 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", 424 | "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", 425 | "dev": true, 426 | "dependencies": { 427 | "type-detect": "^4.0.0" 428 | }, 429 | "engines": { 430 | "node": ">=6" 431 | } 432 | }, 433 | "node_modules/diff": { 434 | "version": "5.0.0", 435 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 436 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 437 | "dev": true, 438 | "engines": { 439 | "node": ">=0.3.1" 440 | } 441 | }, 442 | "node_modules/emoji-regex": { 443 | "version": "8.0.0", 444 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 445 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 446 | "dev": true 447 | }, 448 | "node_modules/escalade": { 449 | "version": "3.1.1", 450 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 451 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 452 | "dev": true, 453 | "engines": { 454 | "node": ">=6" 455 | } 456 | }, 457 | "node_modules/escape-string-regexp": { 458 | "version": "4.0.0", 459 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 460 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 461 | "dev": true, 462 | "engines": { 463 | "node": ">=10" 464 | }, 465 | "funding": { 466 | "url": "https://github.com/sponsors/sindresorhus" 467 | } 468 | }, 469 | "node_modules/fill-range": { 470 | "version": "7.0.1", 471 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 472 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 473 | "dev": true, 474 | "dependencies": { 475 | "to-regex-range": "^5.0.1" 476 | }, 477 | "engines": { 478 | "node": ">=8" 479 | } 480 | }, 481 | "node_modules/find-up": { 482 | "version": "5.0.0", 483 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 484 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 485 | "dev": true, 486 | "dependencies": { 487 | "locate-path": "^6.0.0", 488 | "path-exists": "^4.0.0" 489 | }, 490 | "engines": { 491 | "node": ">=10" 492 | }, 493 | "funding": { 494 | "url": "https://github.com/sponsors/sindresorhus" 495 | } 496 | }, 497 | "node_modules/flat": { 498 | "version": "5.0.2", 499 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 500 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 501 | "dev": true, 502 | "bin": { 503 | "flat": "cli.js" 504 | } 505 | }, 506 | "node_modules/fs.realpath": { 507 | "version": "1.0.0", 508 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 509 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 510 | "dev": true 511 | }, 512 | "node_modules/fsevents": { 513 | "version": "2.3.2", 514 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 515 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 516 | "dev": true, 517 | "hasInstallScript": true, 518 | "optional": true, 519 | "os": [ 520 | "darwin" 521 | ], 522 | "engines": { 523 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 524 | } 525 | }, 526 | "node_modules/get-caller-file": { 527 | "version": "2.0.5", 528 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 529 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 530 | "dev": true, 531 | "engines": { 532 | "node": "6.* || 8.* || >= 10.*" 533 | } 534 | }, 535 | "node_modules/get-func-name": { 536 | "version": "2.0.0", 537 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 538 | "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", 539 | "dev": true, 540 | "engines": { 541 | "node": "*" 542 | } 543 | }, 544 | "node_modules/glob": { 545 | "version": "7.2.0", 546 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 547 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 548 | "dev": true, 549 | "dependencies": { 550 | "fs.realpath": "^1.0.0", 551 | "inflight": "^1.0.4", 552 | "inherits": "2", 553 | "minimatch": "^3.0.4", 554 | "once": "^1.3.0", 555 | "path-is-absolute": "^1.0.0" 556 | }, 557 | "engines": { 558 | "node": "*" 559 | }, 560 | "funding": { 561 | "url": "https://github.com/sponsors/isaacs" 562 | } 563 | }, 564 | "node_modules/glob-parent": { 565 | "version": "5.1.2", 566 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 567 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 568 | "dev": true, 569 | "dependencies": { 570 | "is-glob": "^4.0.1" 571 | }, 572 | "engines": { 573 | "node": ">= 6" 574 | } 575 | }, 576 | "node_modules/glob/node_modules/minimatch": { 577 | "version": "3.1.2", 578 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 579 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 580 | "dev": true, 581 | "dependencies": { 582 | "brace-expansion": "^1.1.7" 583 | }, 584 | "engines": { 585 | "node": "*" 586 | } 587 | }, 588 | "node_modules/growl": { 589 | "version": "1.10.5", 590 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 591 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 592 | "dev": true, 593 | "engines": { 594 | "node": ">=4.x" 595 | } 596 | }, 597 | "node_modules/has-flag": { 598 | "version": "4.0.0", 599 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 600 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 601 | "dev": true, 602 | "engines": { 603 | "node": ">=8" 604 | } 605 | }, 606 | "node_modules/he": { 607 | "version": "1.2.0", 608 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 609 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 610 | "dev": true, 611 | "bin": { 612 | "he": "bin/he" 613 | } 614 | }, 615 | "node_modules/ignore-by-default": { 616 | "version": "1.0.1", 617 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", 618 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", 619 | "dev": true 620 | }, 621 | "node_modules/inflight": { 622 | "version": "1.0.6", 623 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 624 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 625 | "dev": true, 626 | "dependencies": { 627 | "once": "^1.3.0", 628 | "wrappy": "1" 629 | } 630 | }, 631 | "node_modules/inherits": { 632 | "version": "2.0.4", 633 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 634 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 635 | "dev": true 636 | }, 637 | "node_modules/is-binary-path": { 638 | "version": "2.1.0", 639 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 640 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 641 | "dev": true, 642 | "dependencies": { 643 | "binary-extensions": "^2.0.0" 644 | }, 645 | "engines": { 646 | "node": ">=8" 647 | } 648 | }, 649 | "node_modules/is-extglob": { 650 | "version": "2.1.1", 651 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 652 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 653 | "dev": true, 654 | "engines": { 655 | "node": ">=0.10.0" 656 | } 657 | }, 658 | "node_modules/is-fullwidth-code-point": { 659 | "version": "3.0.0", 660 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 661 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 662 | "dev": true, 663 | "engines": { 664 | "node": ">=8" 665 | } 666 | }, 667 | "node_modules/is-glob": { 668 | "version": "4.0.3", 669 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 670 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 671 | "dev": true, 672 | "dependencies": { 673 | "is-extglob": "^2.1.1" 674 | }, 675 | "engines": { 676 | "node": ">=0.10.0" 677 | } 678 | }, 679 | "node_modules/is-number": { 680 | "version": "7.0.0", 681 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 682 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 683 | "dev": true, 684 | "engines": { 685 | "node": ">=0.12.0" 686 | } 687 | }, 688 | "node_modules/is-plain-obj": { 689 | "version": "2.1.0", 690 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 691 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 692 | "dev": true, 693 | "engines": { 694 | "node": ">=8" 695 | } 696 | }, 697 | "node_modules/is-unicode-supported": { 698 | "version": "0.1.0", 699 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 700 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 701 | "dev": true, 702 | "engines": { 703 | "node": ">=10" 704 | }, 705 | "funding": { 706 | "url": "https://github.com/sponsors/sindresorhus" 707 | } 708 | }, 709 | "node_modules/isexe": { 710 | "version": "2.0.0", 711 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 712 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 713 | "dev": true 714 | }, 715 | "node_modules/js-yaml": { 716 | "version": "4.1.0", 717 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 718 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 719 | "dev": true, 720 | "dependencies": { 721 | "argparse": "^2.0.1" 722 | }, 723 | "bin": { 724 | "js-yaml": "bin/js-yaml.js" 725 | } 726 | }, 727 | "node_modules/locate-path": { 728 | "version": "6.0.0", 729 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 730 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 731 | "dev": true, 732 | "dependencies": { 733 | "p-locate": "^5.0.0" 734 | }, 735 | "engines": { 736 | "node": ">=10" 737 | }, 738 | "funding": { 739 | "url": "https://github.com/sponsors/sindresorhus" 740 | } 741 | }, 742 | "node_modules/log-symbols": { 743 | "version": "4.1.0", 744 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 745 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 746 | "dev": true, 747 | "dependencies": { 748 | "chalk": "^4.1.0", 749 | "is-unicode-supported": "^0.1.0" 750 | }, 751 | "engines": { 752 | "node": ">=10" 753 | }, 754 | "funding": { 755 | "url": "https://github.com/sponsors/sindresorhus" 756 | } 757 | }, 758 | "node_modules/loupe": { 759 | "version": "2.3.6", 760 | "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", 761 | "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", 762 | "dev": true, 763 | "dependencies": { 764 | "get-func-name": "^2.0.0" 765 | } 766 | }, 767 | "node_modules/make-error": { 768 | "version": "1.3.6", 769 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 770 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 771 | "dev": true 772 | }, 773 | "node_modules/minimatch": { 774 | "version": "4.2.1", 775 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", 776 | "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", 777 | "dev": true, 778 | "dependencies": { 779 | "brace-expansion": "^1.1.7" 780 | }, 781 | "engines": { 782 | "node": ">=10" 783 | } 784 | }, 785 | "node_modules/mocha": { 786 | "version": "9.2.2", 787 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", 788 | "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", 789 | "dev": true, 790 | "dependencies": { 791 | "@ungap/promise-all-settled": "1.1.2", 792 | "ansi-colors": "4.1.1", 793 | "browser-stdout": "1.3.1", 794 | "chokidar": "3.5.3", 795 | "debug": "4.3.3", 796 | "diff": "5.0.0", 797 | "escape-string-regexp": "4.0.0", 798 | "find-up": "5.0.0", 799 | "glob": "7.2.0", 800 | "growl": "1.10.5", 801 | "he": "1.2.0", 802 | "js-yaml": "4.1.0", 803 | "log-symbols": "4.1.0", 804 | "minimatch": "4.2.1", 805 | "ms": "2.1.3", 806 | "nanoid": "3.3.1", 807 | "serialize-javascript": "6.0.0", 808 | "strip-json-comments": "3.1.1", 809 | "supports-color": "8.1.1", 810 | "which": "2.0.2", 811 | "workerpool": "6.2.0", 812 | "yargs": "16.2.0", 813 | "yargs-parser": "20.2.4", 814 | "yargs-unparser": "2.0.0" 815 | }, 816 | "bin": { 817 | "_mocha": "bin/_mocha", 818 | "mocha": "bin/mocha" 819 | }, 820 | "engines": { 821 | "node": ">= 12.0.0" 822 | }, 823 | "funding": { 824 | "type": "opencollective", 825 | "url": "https://opencollective.com/mochajs" 826 | } 827 | }, 828 | "node_modules/ms": { 829 | "version": "2.1.3", 830 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 831 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 832 | "dev": true 833 | }, 834 | "node_modules/nanoid": { 835 | "version": "3.3.1", 836 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", 837 | "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", 838 | "dev": true, 839 | "bin": { 840 | "nanoid": "bin/nanoid.cjs" 841 | }, 842 | "engines": { 843 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 844 | } 845 | }, 846 | "node_modules/nodemon": { 847 | "version": "2.0.20", 848 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", 849 | "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==", 850 | "dev": true, 851 | "dependencies": { 852 | "chokidar": "^3.5.2", 853 | "debug": "^3.2.7", 854 | "ignore-by-default": "^1.0.1", 855 | "minimatch": "^3.1.2", 856 | "pstree.remy": "^1.1.8", 857 | "semver": "^5.7.1", 858 | "simple-update-notifier": "^1.0.7", 859 | "supports-color": "^5.5.0", 860 | "touch": "^3.1.0", 861 | "undefsafe": "^2.0.5" 862 | }, 863 | "bin": { 864 | "nodemon": "bin/nodemon.js" 865 | }, 866 | "engines": { 867 | "node": ">=8.10.0" 868 | }, 869 | "funding": { 870 | "type": "opencollective", 871 | "url": "https://opencollective.com/nodemon" 872 | } 873 | }, 874 | "node_modules/nodemon/node_modules/debug": { 875 | "version": "3.2.7", 876 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 877 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 878 | "dev": true, 879 | "dependencies": { 880 | "ms": "^2.1.1" 881 | } 882 | }, 883 | "node_modules/nodemon/node_modules/has-flag": { 884 | "version": "3.0.0", 885 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 886 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", 887 | "dev": true, 888 | "engines": { 889 | "node": ">=4" 890 | } 891 | }, 892 | "node_modules/nodemon/node_modules/minimatch": { 893 | "version": "3.1.2", 894 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 895 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 896 | "dev": true, 897 | "dependencies": { 898 | "brace-expansion": "^1.1.7" 899 | }, 900 | "engines": { 901 | "node": "*" 902 | } 903 | }, 904 | "node_modules/nodemon/node_modules/supports-color": { 905 | "version": "5.5.0", 906 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 907 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 908 | "dev": true, 909 | "dependencies": { 910 | "has-flag": "^3.0.0" 911 | }, 912 | "engines": { 913 | "node": ">=4" 914 | } 915 | }, 916 | "node_modules/nopt": { 917 | "version": "1.0.10", 918 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", 919 | "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", 920 | "dev": true, 921 | "dependencies": { 922 | "abbrev": "1" 923 | }, 924 | "bin": { 925 | "nopt": "bin/nopt.js" 926 | }, 927 | "engines": { 928 | "node": "*" 929 | } 930 | }, 931 | "node_modules/normalize-path": { 932 | "version": "3.0.0", 933 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 934 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 935 | "dev": true, 936 | "engines": { 937 | "node": ">=0.10.0" 938 | } 939 | }, 940 | "node_modules/once": { 941 | "version": "1.4.0", 942 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 943 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 944 | "dev": true, 945 | "dependencies": { 946 | "wrappy": "1" 947 | } 948 | }, 949 | "node_modules/p-limit": { 950 | "version": "3.1.0", 951 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 952 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 953 | "dev": true, 954 | "dependencies": { 955 | "yocto-queue": "^0.1.0" 956 | }, 957 | "engines": { 958 | "node": ">=10" 959 | }, 960 | "funding": { 961 | "url": "https://github.com/sponsors/sindresorhus" 962 | } 963 | }, 964 | "node_modules/p-locate": { 965 | "version": "5.0.0", 966 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 967 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 968 | "dev": true, 969 | "dependencies": { 970 | "p-limit": "^3.0.2" 971 | }, 972 | "engines": { 973 | "node": ">=10" 974 | }, 975 | "funding": { 976 | "url": "https://github.com/sponsors/sindresorhus" 977 | } 978 | }, 979 | "node_modules/path-exists": { 980 | "version": "4.0.0", 981 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 982 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 983 | "dev": true, 984 | "engines": { 985 | "node": ">=8" 986 | } 987 | }, 988 | "node_modules/path-is-absolute": { 989 | "version": "1.0.1", 990 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 991 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 992 | "dev": true, 993 | "engines": { 994 | "node": ">=0.10.0" 995 | } 996 | }, 997 | "node_modules/pathval": { 998 | "version": "1.1.1", 999 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", 1000 | "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", 1001 | "dev": true, 1002 | "engines": { 1003 | "node": "*" 1004 | } 1005 | }, 1006 | "node_modules/picomatch": { 1007 | "version": "2.3.1", 1008 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1009 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1010 | "dev": true, 1011 | "engines": { 1012 | "node": ">=8.6" 1013 | }, 1014 | "funding": { 1015 | "url": "https://github.com/sponsors/jonschlinkert" 1016 | } 1017 | }, 1018 | "node_modules/pstree.remy": { 1019 | "version": "1.1.8", 1020 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", 1021 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", 1022 | "dev": true 1023 | }, 1024 | "node_modules/randombytes": { 1025 | "version": "2.1.0", 1026 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 1027 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 1028 | "dev": true, 1029 | "dependencies": { 1030 | "safe-buffer": "^5.1.0" 1031 | } 1032 | }, 1033 | "node_modules/readdirp": { 1034 | "version": "3.6.0", 1035 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1036 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1037 | "dev": true, 1038 | "dependencies": { 1039 | "picomatch": "^2.2.1" 1040 | }, 1041 | "engines": { 1042 | "node": ">=8.10.0" 1043 | } 1044 | }, 1045 | "node_modules/require-directory": { 1046 | "version": "2.1.1", 1047 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1048 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 1049 | "dev": true, 1050 | "engines": { 1051 | "node": ">=0.10.0" 1052 | } 1053 | }, 1054 | "node_modules/safe-buffer": { 1055 | "version": "5.2.1", 1056 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1057 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1058 | "dev": true, 1059 | "funding": [ 1060 | { 1061 | "type": "github", 1062 | "url": "https://github.com/sponsors/feross" 1063 | }, 1064 | { 1065 | "type": "patreon", 1066 | "url": "https://www.patreon.com/feross" 1067 | }, 1068 | { 1069 | "type": "consulting", 1070 | "url": "https://feross.org/support" 1071 | } 1072 | ] 1073 | }, 1074 | "node_modules/semver": { 1075 | "version": "5.7.1", 1076 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 1077 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 1078 | "dev": true, 1079 | "bin": { 1080 | "semver": "bin/semver" 1081 | } 1082 | }, 1083 | "node_modules/serialize-javascript": { 1084 | "version": "6.0.0", 1085 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 1086 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", 1087 | "dev": true, 1088 | "dependencies": { 1089 | "randombytes": "^2.1.0" 1090 | } 1091 | }, 1092 | "node_modules/simple-update-notifier": { 1093 | "version": "1.1.0", 1094 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", 1095 | "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", 1096 | "dev": true, 1097 | "dependencies": { 1098 | "semver": "~7.0.0" 1099 | }, 1100 | "engines": { 1101 | "node": ">=8.10.0" 1102 | } 1103 | }, 1104 | "node_modules/simple-update-notifier/node_modules/semver": { 1105 | "version": "7.0.0", 1106 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", 1107 | "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", 1108 | "dev": true, 1109 | "bin": { 1110 | "semver": "bin/semver.js" 1111 | } 1112 | }, 1113 | "node_modules/string-width": { 1114 | "version": "4.2.3", 1115 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1116 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1117 | "dev": true, 1118 | "dependencies": { 1119 | "emoji-regex": "^8.0.0", 1120 | "is-fullwidth-code-point": "^3.0.0", 1121 | "strip-ansi": "^6.0.1" 1122 | }, 1123 | "engines": { 1124 | "node": ">=8" 1125 | } 1126 | }, 1127 | "node_modules/strip-ansi": { 1128 | "version": "6.0.1", 1129 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1130 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1131 | "dev": true, 1132 | "dependencies": { 1133 | "ansi-regex": "^5.0.1" 1134 | }, 1135 | "engines": { 1136 | "node": ">=8" 1137 | } 1138 | }, 1139 | "node_modules/strip-json-comments": { 1140 | "version": "3.1.1", 1141 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1142 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1143 | "dev": true, 1144 | "engines": { 1145 | "node": ">=8" 1146 | }, 1147 | "funding": { 1148 | "url": "https://github.com/sponsors/sindresorhus" 1149 | } 1150 | }, 1151 | "node_modules/supports-color": { 1152 | "version": "8.1.1", 1153 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 1154 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 1155 | "dev": true, 1156 | "dependencies": { 1157 | "has-flag": "^4.0.0" 1158 | }, 1159 | "engines": { 1160 | "node": ">=10" 1161 | }, 1162 | "funding": { 1163 | "url": "https://github.com/chalk/supports-color?sponsor=1" 1164 | } 1165 | }, 1166 | "node_modules/to-regex-range": { 1167 | "version": "5.0.1", 1168 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1169 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1170 | "dev": true, 1171 | "dependencies": { 1172 | "is-number": "^7.0.0" 1173 | }, 1174 | "engines": { 1175 | "node": ">=8.0" 1176 | } 1177 | }, 1178 | "node_modules/touch": { 1179 | "version": "3.1.0", 1180 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", 1181 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", 1182 | "dev": true, 1183 | "dependencies": { 1184 | "nopt": "~1.0.10" 1185 | }, 1186 | "bin": { 1187 | "nodetouch": "bin/nodetouch.js" 1188 | } 1189 | }, 1190 | "node_modules/ts-node": { 1191 | "version": "10.9.1", 1192 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", 1193 | "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", 1194 | "dev": true, 1195 | "dependencies": { 1196 | "@cspotcode/source-map-support": "^0.8.0", 1197 | "@tsconfig/node10": "^1.0.7", 1198 | "@tsconfig/node12": "^1.0.7", 1199 | "@tsconfig/node14": "^1.0.0", 1200 | "@tsconfig/node16": "^1.0.2", 1201 | "acorn": "^8.4.1", 1202 | "acorn-walk": "^8.1.1", 1203 | "arg": "^4.1.0", 1204 | "create-require": "^1.1.0", 1205 | "diff": "^4.0.1", 1206 | "make-error": "^1.1.1", 1207 | "v8-compile-cache-lib": "^3.0.1", 1208 | "yn": "3.1.1" 1209 | }, 1210 | "bin": { 1211 | "ts-node": "dist/bin.js", 1212 | "ts-node-cwd": "dist/bin-cwd.js", 1213 | "ts-node-esm": "dist/bin-esm.js", 1214 | "ts-node-script": "dist/bin-script.js", 1215 | "ts-node-transpile-only": "dist/bin-transpile.js", 1216 | "ts-script": "dist/bin-script-deprecated.js" 1217 | }, 1218 | "peerDependencies": { 1219 | "@swc/core": ">=1.2.50", 1220 | "@swc/wasm": ">=1.2.50", 1221 | "@types/node": "*", 1222 | "typescript": ">=2.7" 1223 | }, 1224 | "peerDependenciesMeta": { 1225 | "@swc/core": { 1226 | "optional": true 1227 | }, 1228 | "@swc/wasm": { 1229 | "optional": true 1230 | } 1231 | } 1232 | }, 1233 | "node_modules/ts-node/node_modules/diff": { 1234 | "version": "4.0.2", 1235 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 1236 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 1237 | "dev": true, 1238 | "engines": { 1239 | "node": ">=0.3.1" 1240 | } 1241 | }, 1242 | "node_modules/type-detect": { 1243 | "version": "4.0.8", 1244 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 1245 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 1246 | "dev": true, 1247 | "engines": { 1248 | "node": ">=4" 1249 | } 1250 | }, 1251 | "node_modules/typescript": { 1252 | "version": "4.9.4", 1253 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", 1254 | "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", 1255 | "dev": true, 1256 | "bin": { 1257 | "tsc": "bin/tsc", 1258 | "tsserver": "bin/tsserver" 1259 | }, 1260 | "engines": { 1261 | "node": ">=4.2.0" 1262 | } 1263 | }, 1264 | "node_modules/undefsafe": { 1265 | "version": "2.0.5", 1266 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", 1267 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", 1268 | "dev": true 1269 | }, 1270 | "node_modules/v8-compile-cache-lib": { 1271 | "version": "3.0.1", 1272 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 1273 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", 1274 | "dev": true 1275 | }, 1276 | "node_modules/which": { 1277 | "version": "2.0.2", 1278 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1279 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1280 | "dev": true, 1281 | "dependencies": { 1282 | "isexe": "^2.0.0" 1283 | }, 1284 | "bin": { 1285 | "node-which": "bin/node-which" 1286 | }, 1287 | "engines": { 1288 | "node": ">= 8" 1289 | } 1290 | }, 1291 | "node_modules/workerpool": { 1292 | "version": "6.2.0", 1293 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", 1294 | "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", 1295 | "dev": true 1296 | }, 1297 | "node_modules/wrap-ansi": { 1298 | "version": "7.0.0", 1299 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1300 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1301 | "dev": true, 1302 | "dependencies": { 1303 | "ansi-styles": "^4.0.0", 1304 | "string-width": "^4.1.0", 1305 | "strip-ansi": "^6.0.0" 1306 | }, 1307 | "engines": { 1308 | "node": ">=10" 1309 | }, 1310 | "funding": { 1311 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1312 | } 1313 | }, 1314 | "node_modules/wrappy": { 1315 | "version": "1.0.2", 1316 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1317 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1318 | "dev": true 1319 | }, 1320 | "node_modules/y18n": { 1321 | "version": "5.0.8", 1322 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1323 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1324 | "dev": true, 1325 | "engines": { 1326 | "node": ">=10" 1327 | } 1328 | }, 1329 | "node_modules/yargs": { 1330 | "version": "16.2.0", 1331 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 1332 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 1333 | "dev": true, 1334 | "dependencies": { 1335 | "cliui": "^7.0.2", 1336 | "escalade": "^3.1.1", 1337 | "get-caller-file": "^2.0.5", 1338 | "require-directory": "^2.1.1", 1339 | "string-width": "^4.2.0", 1340 | "y18n": "^5.0.5", 1341 | "yargs-parser": "^20.2.2" 1342 | }, 1343 | "engines": { 1344 | "node": ">=10" 1345 | } 1346 | }, 1347 | "node_modules/yargs-parser": { 1348 | "version": "20.2.4", 1349 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", 1350 | "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", 1351 | "dev": true, 1352 | "engines": { 1353 | "node": ">=10" 1354 | } 1355 | }, 1356 | "node_modules/yargs-unparser": { 1357 | "version": "2.0.0", 1358 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 1359 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 1360 | "dev": true, 1361 | "dependencies": { 1362 | "camelcase": "^6.0.0", 1363 | "decamelize": "^4.0.0", 1364 | "flat": "^5.0.2", 1365 | "is-plain-obj": "^2.1.0" 1366 | }, 1367 | "engines": { 1368 | "node": ">=10" 1369 | } 1370 | }, 1371 | "node_modules/yn": { 1372 | "version": "3.1.1", 1373 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 1374 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 1375 | "dev": true, 1376 | "engines": { 1377 | "node": ">=6" 1378 | } 1379 | }, 1380 | "node_modules/yocto-queue": { 1381 | "version": "0.1.0", 1382 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1383 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1384 | "dev": true, 1385 | "engines": { 1386 | "node": ">=10" 1387 | }, 1388 | "funding": { 1389 | "url": "https://github.com/sponsors/sindresorhus" 1390 | } 1391 | } 1392 | } 1393 | } 1394 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typenvy", 3 | "description": "A simple cmd parser framework for node js/ts cli apps.", 4 | "version": "1.5.15", 5 | "main": "dist/index.js", 6 | "author": "majo418", 7 | "license": "MIT", 8 | "private": false, 9 | "keywords": [ 10 | "typescript", 11 | "node", 12 | "lib", 13 | "arg", 14 | "command" 15 | ], 16 | "repository": { 17 | "url": "git@github.com:majo418/typenvy.git", 18 | "type": "git" 19 | }, 20 | "scripts": { 21 | "tsc": "tsc -p tsconfig.json", 22 | "start": "ts-node src/index.ts", 23 | "exec": "node dist/index.js", 24 | "test": "mocha --require ts-node/register src/test/**/*.test.ts", 25 | "build": "npm run tsc", 26 | "start:watch": "nodemon -w ./src -x \"npm run start\" --ext *.ts", 27 | "build:watch": "nodemon -w ./src -x \"npm run build\" --ext *.ts", 28 | "test:watch": "nodemon -w ./src -x \"npm run test\" --ext *.ts", 29 | "exec:watch": "nodemon -w ./dist -x \"npm run exec\"", 30 | "dev": "nodemon -w ./src -x \"npm run build; npm run test; npm run start\" --ext *.ts" 31 | }, 32 | "devDependencies": { 33 | "@types/chai": "4", 34 | "@types/mocha": "9", 35 | "@types/node": "16", 36 | "chai": "4", 37 | "cmdy": "1", 38 | "mocha": "9", 39 | "nodemon": "2", 40 | "ts-node": "10", 41 | "typescript": "4" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/cmdy.ts: -------------------------------------------------------------------------------- 1 | import { EnvData, parseValue } from "./index"; 2 | 3 | export type Awaitable = Promise | PromiseLike | T 4 | 5 | export interface InputValidator { 6 | name: string; 7 | validate: (value: any) => Awaitable; 8 | } 9 | export declare type FlagValueTypes = "string" | "number" | "boolean" | InputValidator; 10 | 11 | export function isTypeOf(value: any, types: string[]) { 12 | for (const type of types) { 13 | if (typeof value == type) { 14 | return true 15 | } 16 | } 17 | return false 18 | } 19 | 20 | export interface Flag { 21 | name: string; 22 | description: string; 23 | displayName?: string; 24 | required?: boolean; 25 | default?: string | number | boolean; 26 | types?: FlagValueTypes[]; 27 | shorthand?: string; 28 | alias?: string[]; 29 | control?: (value: string) => Awaitable; 30 | exe?: (cmd: any, value: string) => Awaitable; 31 | exePriority?: number; 32 | multiValues?: boolean; 33 | } 34 | export interface BoolFlag extends Flag { 35 | types?: undefined 36 | control?: undefined 37 | default?: undefined 38 | required?: undefined 39 | multiValues?: undefined 40 | } 41 | export interface ValueFlag extends Flag { 42 | types: FlagValueTypes[] 43 | } 44 | 45 | export function cmdyFlag( 46 | flag: F, 47 | envKey: string, 48 | envData: EnvData, 49 | ignoreErrors: boolean = false, 50 | ): F { 51 | let des: string = flag.description 52 | if (envData.defaultEnv[envKey] != undefined) { 53 | des += " (default: '" + envData.defaultEnv[envKey] + "', " 54 | } else { 55 | des += " (" 56 | } 57 | des += "ENV: '" + envKey + "')" 58 | 59 | return { 60 | ...flag, 61 | description: des, 62 | async exe(cmd, value: any) { 63 | value = parseValue( 64 | value, 65 | envData.types[envKey] 66 | ) 67 | if (value == undefined) { 68 | if (Array.isArray(flag.types)) { 69 | value = envData.defaultEnv[envKey] 70 | value = parseValue( 71 | value, 72 | envData.types[envKey] 73 | ) 74 | if (!isTypeOf(value, flag.types as string[])) { 75 | value = flag.default 76 | value = parseValue( 77 | value, 78 | envData.types[envKey] 79 | ) 80 | if (!isTypeOf(value, flag.types as string[])) { 81 | value = undefined 82 | } 83 | } 84 | } else { 85 | value = envData.defaultEnv[envKey] 86 | value = parseValue( 87 | value, 88 | envData.types[envKey] 89 | ) 90 | if (typeof value != "boolean") { 91 | value = flag.default 92 | value = parseValue( 93 | value, 94 | envData.types[envKey] 95 | ) 96 | if (typeof value != "boolean") { 97 | value = undefined 98 | } else { 99 | value = !value 100 | } 101 | } else { 102 | value = !value 103 | } 104 | } 105 | } 106 | if (value == undefined) { 107 | if (!ignoreErrors) { 108 | throw new Error( 109 | "The flag '" + 110 | flag.name + 111 | "' is not type of '" + 112 | envData.types[envKey].map( 113 | (c) => c.type 114 | ).join("', '") + 115 | "'\nValue:\n" + JSON.stringify(value, null, 2) 116 | ) 117 | } 118 | } 119 | if (Array.isArray(envData.env[envKey])) { 120 | if (Array.isArray(value)) { 121 | envData.env[envKey] = [ 122 | ...envData.env[envKey], 123 | ...value, 124 | ] 125 | } else { 126 | envData.env[envKey].push(value) 127 | } 128 | } else if (flag.multiValues) { 129 | throw new Error( 130 | "The flag '" + 131 | flag.name + 132 | "' is a multiValues but env value is not an array!" 133 | ) 134 | } else { 135 | envData.env[envKey] = value 136 | } 137 | if (flag.exe) { 138 | await flag.exe(cmd, value) 139 | } 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { URL } from "url" 2 | import * as path from "path" 3 | import * as fs from "fs" 4 | import * as os from "os" 5 | import { cmdyFlag as cmdyFlag2 } from './cmdy'; 6 | 7 | export const cmdyFlag = cmdyFlag2 8 | 9 | export type BaseType = "boolean" | "number" | "string" | 10 | "symbol" | "object" | "bigint" | "function" | 11 | "unknown" | "array" | "null" | "undefined" 12 | 13 | export function fullTypeOf(value: any): BaseType { 14 | let type: BaseType = typeof value 15 | if (type == "object") { 16 | if (type == null) { 17 | type = "null" 18 | } else if (Array.isArray(value)) { 19 | type = "array" 20 | } 21 | } 22 | return type 23 | } 24 | 25 | export interface TypeChecker { 26 | check: (value: any) => T | undefined, 27 | type: string, 28 | } 29 | 30 | export type JsonTypeValue = string | number | boolean | null 31 | export type JsonType = JsonHolder | JsonTypeValue 32 | export type JsonHolder = JsonObject | JsonArray 33 | export type JsonArray = JsonType[] 34 | export interface JsonObject { 35 | [key: string]: JsonType 36 | } 37 | 38 | export interface VariablesTypes { 39 | [key: string]: [TypeChecker, ...TypeChecker[]] 40 | } 41 | 42 | export interface EnvType { 43 | [key: string]: any 44 | } 45 | 46 | export const emailRegex: RegExp = /^(([^<>()\[\]\\.,:\s@"]+(\.[^<>()\[\]\\.,:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 47 | 48 | export function anyToString(value: any): string { 49 | return value && typeof value.toString == "function" ? 50 | value.toString() : 51 | "" + value 52 | } 53 | 54 | export interface EnvData { 55 | defaultEnv: T, 56 | types: VariablesTypes, 57 | env: T, 58 | } 59 | 60 | export class EnvError extends Error { } 61 | 62 | export class EnvResult implements EnvData { 63 | public readonly envError: EnvError | undefined 64 | 65 | constructor( 66 | public readonly defaultEnv: T, 67 | public readonly types: VariablesTypes, 68 | public readonly env: T, 69 | public readonly errors: [string, Error][], 70 | ) { 71 | if (errors.length > 0) { 72 | this.envError = errors[0][1] 73 | } 74 | } 75 | 76 | overwriteEnv( 77 | env: { [key: string]: any } 78 | ): EnvResult { 79 | Object.keys(env).forEach((key) => { 80 | (this.env as any)[key] = env[key] 81 | }) 82 | return this 83 | } 84 | 85 | setMissingEnv( 86 | env: { [key: string]: any } 87 | ): EnvResult { 88 | Object.keys(env).forEach((key) => { 89 | if (!this.env[key]) { 90 | (this.env as any)[key] = env[key] 91 | } 92 | }) 93 | return this 94 | } 95 | 96 | setProcessEnv(): EnvResult { 97 | Object.keys(this.env).forEach((key) => { 98 | process.env[key] = anyToString(this.env[key]) 99 | }) 100 | return this 101 | } 102 | 103 | clearProcessEnv( 104 | justEqualValues: boolean = true 105 | ): EnvResult { 106 | Object.keys(this.env).forEach((key) => { 107 | if ( 108 | justEqualValues && 109 | this.env[key] != process.env[key] 110 | ) { 111 | return 112 | } 113 | delete process.env[key] 114 | }) 115 | return this 116 | } 117 | 118 | errPrint(): EnvResult { 119 | if (this.errors.length > 0) { 120 | console.error("Environment Errors:") 121 | for (let index = 0; index < this.errors.length; index++) { 122 | const error = this.errors[index]; 123 | console.error("########## [ " + error[0] + " ]:") 124 | console.error( 125 | ( 126 | error[1].stack ?? 127 | error[1].message ?? 128 | anyToString(error[1]) 129 | ) 130 | .split("\n") 131 | .filter((v) => v.length != 0 && v != " ") 132 | .join("\n") 133 | .split("\n\n") 134 | .join("\n") 135 | ) 136 | } 137 | } 138 | return this 139 | } 140 | 141 | errThrow(): EnvResult { 142 | if (this.envError) { 143 | throw this.envError 144 | } 145 | return this 146 | } 147 | 148 | errExit(exitCode: number = 1): EnvResult | never { 149 | if (this.errors.length > 0) { 150 | this.errPrint() 151 | process.exit(exitCode) 152 | } 153 | return this 154 | } 155 | 156 | getData(): EnvData { 157 | return { 158 | defaultEnv: { ...this.defaultEnv }, 159 | types: { ...this.types }, 160 | env: { ...this.env }, 161 | } 162 | } 163 | } 164 | 165 | export function parseValue( 166 | value: T, 167 | checker: [ 168 | TypeChecker, 169 | ...TypeChecker[] 170 | ] 171 | ): T | undefined { 172 | for (let index = 0; index < checker.length; index++) { 173 | const newValue = checker[index].check(value) 174 | if (newValue != undefined) { 175 | return newValue 176 | } 177 | } 178 | return undefined 179 | } 180 | 181 | export function parseEnv( 182 | defaultEnv: T, 183 | types: VariablesTypes, 184 | ): EnvResult { 185 | const env: T = { ...defaultEnv } 186 | const errors: [string, Error][] = [] 187 | const varNames = Object.keys(types) 188 | for (let index = 0; index < varNames.length; index++) { 189 | const varName = varNames[index] 190 | let value = process.env[varName] 191 | if ( 192 | value == undefined && 193 | env[varName] != undefined 194 | ) { 195 | value = env[varName] 196 | } 197 | value = parseValue( 198 | process.env[varName] ?? env[varName] ?? undefined, 199 | types[varName] 200 | ) 201 | if (value == undefined) { 202 | errors.push([ 203 | varName, 204 | new Error( 205 | "The environment variable '" + 206 | varName + 207 | "' is not type of '" + 208 | types[varName].map((c) => c.type).join("', '") + 209 | "'" 210 | ) 211 | ]) 212 | continue 213 | } 214 | (env as any)[varName] = value 215 | } 216 | 217 | return new EnvResult( 218 | defaultEnv, 219 | types, 220 | env, 221 | errors 222 | ) 223 | } 224 | 225 | export const TC_JSON_VALUE: TypeChecker = { 226 | check: (value) => { 227 | if (typeof value == "string") { 228 | try { 229 | value = JSON.parse(value) 230 | } catch (err) { 231 | } 232 | } 233 | if ( 234 | value == null || 235 | typeof value == "boolean" || 236 | typeof value == "number" || 237 | typeof value == "string" 238 | ) { 239 | return value as null | boolean | number | string 240 | } 241 | return undefined 242 | }, 243 | type: "JSON VALUE" 244 | } 245 | 246 | export const TC_JSON_OBJECT: TypeChecker<{ [key: string]: any }> = { 247 | check: (value) => { 248 | if ( 249 | typeof value == "string" && 250 | value.length > 0 251 | ) { 252 | try { 253 | value = JSON.parse(value) 254 | } catch (err) { 255 | } 256 | } 257 | if ( 258 | typeof value == "object" && 259 | !Array.isArray(value) && 260 | value != null 261 | ) { 262 | return value as object 263 | } 264 | return undefined 265 | }, 266 | type: "JSON OBJECT" 267 | } 268 | 269 | export const TC_JSON_ARRAY: TypeChecker = { 270 | check: (value) => { 271 | if ( 272 | typeof value == "string" && 273 | value.length > 0 274 | ) { 275 | try { 276 | value = JSON.parse(value) 277 | } catch (err) { 278 | } 279 | } 280 | if (Array.isArray(value)) { 281 | return value 282 | } 283 | return undefined 284 | }, 285 | type: "JSON ARRAY" 286 | } 287 | 288 | export const TC_CSV_ARRAY: TypeChecker = { 289 | check: (value) => { 290 | if ( 291 | typeof value == "string" && 292 | value.length > 0 293 | ) { 294 | value = value.split(",").map((v) => { 295 | while (v.startsWith(" ")) { 296 | v = v.substring(1) 297 | } 298 | while (v.endsWith(" ")) { 299 | v = v.slice(0, -1) 300 | } 301 | return v 302 | }) 303 | } 304 | if (Array.isArray(value)) { 305 | return value 306 | } 307 | return undefined 308 | }, 309 | type: "CSV ARRAY" 310 | } 311 | 312 | export const TC_NULL: TypeChecker = { 313 | check: (value) => { 314 | if ( 315 | value == null || 316 | ( 317 | typeof value == "string" && 318 | value.toLowerCase() == "null" 319 | ) 320 | ) { 321 | return null 322 | } 323 | }, 324 | type: "NULL" 325 | } 326 | 327 | export const TC_UNDEFINED_AS_NULL: TypeChecker = { 328 | check: (value) => { 329 | if ( 330 | value == undefined || 331 | TC_NULL.check(value) == null 332 | ) { 333 | return null 334 | } 335 | }, 336 | type: "NULL" 337 | } 338 | 339 | export const TC_BOOLEAN: TypeChecker = { 340 | check: (value) => { 341 | if (typeof value == "boolean") { 342 | return value 343 | } else if (typeof value == "string") { 344 | if (value.toLowerCase() == "true") { 345 | return true 346 | } else if (value.toLowerCase() == "false") { 347 | return true 348 | } 349 | } 350 | return undefined 351 | }, 352 | type: "BOOLEAN" 353 | } 354 | 355 | export const TC_EMAIL: TypeChecker = { 356 | check: (value) => { 357 | if ( 358 | typeof value == "string" && 359 | value.match(emailRegex) 360 | ) { 361 | return value 362 | } 363 | return undefined 364 | }, 365 | type: "EMAIL" 366 | } 367 | 368 | export const TC_URL_HTTP: TypeChecker = { 369 | check: (value) => { 370 | try { 371 | const url = new URL(value) 372 | if ( 373 | url.protocol != "http" && 374 | url.protocol != "https" 375 | ) { 376 | return undefined 377 | } 378 | const value2 = url.toString() 379 | if ( 380 | typeof value2 == "string" && 381 | value2.length > 0 382 | ) { 383 | return value2 384 | } 385 | } catch (err) { 386 | } 387 | return undefined 388 | }, 389 | type: "HTTP URL(STRING)" 390 | } 391 | 392 | export const TC_URL: TypeChecker = { 393 | check: (value) => { 394 | try { 395 | const url = new URL(value) 396 | const value2 = url.toString() 397 | if ( 398 | typeof value2 == "string" && 399 | value2.length > 0 400 | ) { 401 | return value2 402 | } 403 | } catch (err) { 404 | } 405 | return undefined 406 | }, 407 | type: "URL" 408 | } 409 | 410 | export const TC_PATH: TypeChecker = { 411 | check: (value) => { 412 | if (typeof value == "string") { 413 | value = value.split("\\").join("/") 414 | .split("//").join("/") 415 | if (os.platform() == "win32") { 416 | if (value[1] != ":" && value[2] != "/") { 417 | if (!value.startWith("/")) { 418 | value = "/" + value 419 | } 420 | value = process.cwd() + value 421 | } 422 | } else if (!value.startsWith("/")) { 423 | value = process.cwd() + "/" + value 424 | } 425 | value = path.normalize(value) 426 | return value 427 | } 428 | return undefined 429 | }, 430 | type: "PATH" 431 | } 432 | 433 | export const TC_PATH_DIR: TypeChecker = { 434 | check: (value) => { 435 | if (typeof value == "string") { 436 | value = value.split("\\").join("/") 437 | .split("//").join("/") 438 | if (os.platform() == "win32") { 439 | if (value[1] != ":" && value[2] != "/") { 440 | if (!value.startWith("/")) { 441 | value = "/" + value 442 | } 443 | value = process.cwd() + value 444 | } 445 | } else if (!value.startsWith("/")) { 446 | value = process.cwd() + "/" + value 447 | } 448 | value = path.normalize(value) 449 | try { 450 | const stat = fs.statSync(value) 451 | if (stat && stat.isDirectory()) { 452 | return value 453 | } 454 | } catch (err: Error | any) { 455 | if ( 456 | typeof err.message == "string" && 457 | err.message.includes("no such file or directory") 458 | ) { 459 | return undefined 460 | } 461 | throw err 462 | } 463 | } 464 | return undefined 465 | }, 466 | type: "EXISTING DIR PATH" 467 | } 468 | 469 | export const TC_PATH_FILE: TypeChecker = { 470 | check: (value) => { 471 | if (typeof value == "string") { 472 | value = value.split("\\").join("/") 473 | .split("//").join("/") 474 | if (os.platform() == "win32") { 475 | if (value[1] != ":" && value[2] != "/") { 476 | if (!value.startWith("/")) { 477 | value = "/" + value 478 | } 479 | value = process.cwd() + value 480 | } 481 | } else if (!value.startsWith("/")) { 482 | value = process.cwd() + "/" + value 483 | } 484 | value = path.normalize(value) 485 | try { 486 | const stat = fs.statSync(value) 487 | if (stat && stat.isFile()) { 488 | return value 489 | } 490 | } catch (err: Error | any) { 491 | if ( 492 | typeof err.message == "string" && 493 | err.message.includes("no such file or directory") 494 | ) { 495 | return undefined 496 | } 497 | throw err 498 | } 499 | } 500 | return undefined 501 | }, 502 | type: "EXISTING FILE PATH" 503 | } 504 | 505 | export const TC_PATH_EXIST: TypeChecker = { 506 | check: (value) => { 507 | if (typeof value == "string") { 508 | value = value.split("\\").join("/") 509 | .split("//").join("/") 510 | if (os.platform() == "win32") { 511 | if (value[1] != ":" && value[2] != "/") { 512 | if (!value.startWith("/")) { 513 | value = "/" + value 514 | } 515 | value = process.cwd() + value 516 | } 517 | } else if (!value.startsWith("/")) { 518 | value = process.cwd() + "/" + value 519 | } 520 | value = path.normalize(value) 521 | try { 522 | const stat = fs.statSync(value) 523 | if ( 524 | stat && 525 | ( 526 | stat.isFile() || 527 | stat.isDirectory() 528 | ) 529 | ) { 530 | return value 531 | } 532 | } catch (err: Error | any) { 533 | if ( 534 | typeof err.message == "string" && 535 | err.message.includes("no such file or directory") 536 | ) { 537 | return undefined 538 | } 539 | throw err 540 | } 541 | } 542 | return undefined 543 | }, 544 | type: "EXISTING PATH" 545 | } 546 | 547 | export const TC_PATH_NOT_EXIST: TypeChecker = { 548 | check: (value) => { 549 | if (typeof value == "string") { 550 | value = value.split("\\").join("/") 551 | .split("//").join("/") 552 | if (os.platform() == "win32") { 553 | if (value[1] != ":" && value[2] != "/") { 554 | if (!value.startWith("/")) { 555 | value = "/" + value 556 | } 557 | value = process.cwd() + value 558 | } 559 | } else if (!value.startsWith("/")) { 560 | value = process.cwd() + "/" + value 561 | } 562 | value = path.normalize(value) 563 | try { 564 | const stat = fs.statSync(value) 565 | if ( 566 | !stat || 567 | ( 568 | !stat.isFile() && 569 | !stat.isDirectory() 570 | ) 571 | ) { 572 | return value 573 | } 574 | } catch (err: Error | any) { 575 | if ( 576 | typeof err.message == "string" && 577 | err.message.includes("no such file or directory") 578 | ) { 579 | return value 580 | } 581 | throw err 582 | } 583 | } 584 | return undefined 585 | }, 586 | type: "NOT EXISTING PATH" 587 | } 588 | 589 | export const TC_STRING: TypeChecker = { 590 | check: (value) => { 591 | if ( 592 | typeof value == "string" && 593 | value.length > 0 594 | ) { 595 | return value 596 | } 597 | return undefined 598 | }, 599 | type: "STRING" 600 | } 601 | 602 | export const TC_EMPTY_STRING: TypeChecker = { 603 | check: (value) => { 604 | if ( 605 | typeof value == "string" 606 | ) { 607 | return value 608 | } 609 | return undefined 610 | }, 611 | type: "EMPTY STRING" 612 | } 613 | 614 | export const TC_NUMBER: TypeChecker = { 615 | check: (value) => { 616 | if (typeof value == "number") { 617 | return value as number 618 | } 619 | value = Number(value) 620 | if (!isNaN(value)) { 621 | return value as number 622 | } 623 | return undefined 624 | }, 625 | type: "NUMBER" 626 | } 627 | 628 | 629 | export const TC_PORT: TypeChecker = { 630 | check: (value) => { 631 | if ( 632 | typeof value == "number" && 633 | value >= 0 && value <= 65535 634 | ) { 635 | 636 | return value as number 637 | } 638 | value = Number(value) 639 | if ( 640 | !isNaN(value) && 641 | value >= 0 && value <= 65535 642 | ) { 643 | return value as number 644 | } 645 | return undefined 646 | }, 647 | type: "PORT" 648 | } 649 | 650 | const allowedCalculationChars = "/*+-1234567890()" 651 | export const TC_CALCULATION: TypeChecker = { 652 | check: (value) => { 653 | if (typeof value == "number") { 654 | return value as number 655 | } 656 | let newValue = Number(value) 657 | if (!isNaN(newValue)) { 658 | return newValue as number 659 | } 660 | if (typeof value == "string") { 661 | for (const char of value) { 662 | if (!allowedCalculationChars.includes(char)) { 663 | return undefined 664 | } 665 | } 666 | const newValue = Number(eval(value)) 667 | if (!isNaN(newValue)) { 668 | return newValue as number 669 | } 670 | } 671 | return undefined 672 | }, 673 | type: "MATH CALCULATION" 674 | } -------------------------------------------------------------------------------- /src/test/cmdy.test.ts: -------------------------------------------------------------------------------- 1 | import "mocha" 2 | import { expect } from "chai" 3 | import { cmdyFlag } from "../cmdy" 4 | import { fullTypeOf } from "../index" 5 | import * as typenvy from "../index" 6 | import { VariablesTypes } from '../index'; 7 | import { exampleChecker, exampleSourceEnv } from "./example"; 8 | import { uniqueStringify } from './json'; 9 | import { BoolFlag } from '../cmdy'; 10 | import { ValueFlag } from '../cmdy'; 11 | 12 | describe('Check cmdyFlag() method', () => { 13 | it("single boolean cmdy flag", async function () { 14 | let sourceEnv: typeof exampleSourceEnv = { ...exampleSourceEnv } 15 | let checker: VariablesTypes = { ...exampleChecker } 16 | 17 | const data = typenvy 18 | .parseEnv(sourceEnv, checker) 19 | .errThrow() 20 | .clearProcessEnv() 21 | 22 | const flag: BoolFlag = cmdyFlag( 23 | { 24 | name: "verbose", 25 | description: "Set the verbose data", 26 | shorthand: "v", 27 | }, 28 | "VERBOSE", 29 | data 30 | ) 31 | 32 | await flag.exe({}, undefined) 33 | expect(flag.name).is.equals("verbose") 34 | expect(flag.description).is.equals("Set the verbose data (default: 'true', ENV: 'VERBOSE')") 35 | expect(flag.shorthand).is.equals("v") 36 | expect(data.env["VERBOSE"]).is.false 37 | }) 38 | 39 | it("multiple boolean cmdy flag", async function () { 40 | let sourceEnv: typeof exampleSourceEnv = { ...exampleSourceEnv } 41 | let checker: VariablesTypes = { ...exampleChecker } 42 | 43 | const data = typenvy 44 | .parseEnv(sourceEnv, checker) 45 | .errThrow() 46 | .clearProcessEnv() 47 | 48 | const flag1: BoolFlag = cmdyFlag( 49 | { 50 | name: "verbose", 51 | description: "Set the verbose data", 52 | shorthand: "v", 53 | }, 54 | "VERBOSE", 55 | data 56 | ) 57 | 58 | const flag2: BoolFlag = cmdyFlag( 59 | { 60 | name: "ignore", 61 | description: "ignore2", 62 | shorthand: "i", 63 | }, 64 | "IGNORE_EMPTY_CERT", 65 | data 66 | ) 67 | 68 | 69 | const flag3: BoolFlag = cmdyFlag( 70 | { 71 | name: "prod", 72 | description: "prod2", 73 | shorthand: "p", 74 | }, 75 | "PRODUCTION", 76 | data 77 | ) 78 | 79 | await flag1.exe({}, undefined) 80 | await flag3.exe({}, undefined) 81 | expect(flag1.name).is.equals("verbose") 82 | expect(flag1.description).is.equals("Set the verbose data (default: 'true', ENV: 'VERBOSE')") 83 | expect(flag1.shorthand).is.equals("v") 84 | expect(data.env["VERBOSE"]).is.false 85 | 86 | expect(flag2.name).is.equals("ignore") 87 | expect(flag2.description).is.equals("ignore2 (default: 'true', ENV: 'IGNORE_EMPTY_CERT')") 88 | expect(flag2.shorthand).is.equals("i") 89 | expect(data.env["IGNORE_EMPTY_CERT"]).is.true 90 | 91 | expect(flag3.name).is.equals("prod") 92 | expect(flag3.description).is.equals("prod2 (default: 'false', ENV: 'PRODUCTION')") 93 | expect(flag3.shorthand).is.equals("p") 94 | expect(data.env["PRODUCTION"]).is.true 95 | }) 96 | 97 | it("string flag", () => { 98 | let sourceEnv: typeof exampleSourceEnv = { ...exampleSourceEnv } 99 | let checker: VariablesTypes = { ...exampleChecker } 100 | 101 | const envData = typenvy 102 | .parseEnv(sourceEnv, checker) 103 | .errThrow() 104 | .clearProcessEnv() 105 | 106 | let flag: ValueFlag = cmdyFlag( 107 | { 108 | name: "example-string-value", 109 | description: "an example", 110 | types: ["string"] 111 | }, 112 | "STATIC_PATH", 113 | envData, 114 | ) 115 | 116 | expect(flag).is.not.undefined 117 | 118 | flag.exe = typeof flag.exe as any 119 | 120 | expect(uniqueStringify( 121 | flag 122 | )).is.equals(uniqueStringify({ 123 | name: 'example-string-value', 124 | description: "an example (default: './public', ENV: 'STATIC_PATH')", 125 | types: ['string'], 126 | exe: "function" 127 | })) 128 | }) 129 | 130 | it("boolean flag", () => { 131 | let sourceEnv: typeof exampleSourceEnv = { ...exampleSourceEnv } 132 | let checker: VariablesTypes = { ...exampleChecker } 133 | 134 | const envData = typenvy 135 | .parseEnv(sourceEnv, checker) 136 | .errThrow() 137 | .clearProcessEnv() 138 | 139 | let flag: BoolFlag = cmdyFlag( 140 | { 141 | name: "example-boolean-value", 142 | description: "an example 2", 143 | }, 144 | "STATIC_DIR", 145 | envData, 146 | ) 147 | 148 | expect(flag).is.not.undefined 149 | 150 | flag.exe = typeof flag.exe as any 151 | 152 | expect(uniqueStringify( 153 | flag 154 | )).is.equals(uniqueStringify({ 155 | name: 'example-boolean-value', 156 | description: "an example 2 (ENV: 'STATIC_DIR')", 157 | exe: "function" 158 | })) 159 | }) 160 | }) 161 | -------------------------------------------------------------------------------- /src/test/example.ts: -------------------------------------------------------------------------------- 1 | import * as typenvy from "../index" 2 | import { VariablesTypes } from '../index'; 3 | 4 | export const exampleSourceEnv = { 5 | PRODUCTION: false as boolean, 6 | VERBOSE: true as boolean, 7 | 8 | DNS_SERVER_ADDRESSES: [ 9 | "127.0.0.11", 10 | "1.0.0.1", 11 | "8.8.4.4", 12 | "1.1.1.1", 13 | "8.8.8.8" 14 | ] as string[], 15 | HTTP_PORT: 80 as number, 16 | HTTPS_PORT: 443 as number, 17 | BIND_ADDRESS: "0.0.0.0" as string, 18 | 19 | CERT_PATH: "./certs/cert.pem" as string, 20 | KEY_PATH: "./certs/privkey.pem" as string, 21 | CA_PATH: "./certs/chain.pem" as string, 22 | 23 | IGNORE_EMPTY_CERT: true as boolean, 24 | 25 | STATIC_PATH: "./public" as string, 26 | } 27 | 28 | export const exampleChecker: VariablesTypes = { 29 | PRODUCTION: [typenvy.TC_BOOLEAN], 30 | VERBOSE: [typenvy.TC_BOOLEAN], 31 | DNS_SERVER_ADDRESSES: [typenvy.TC_JSON_ARRAY, typenvy.TC_CSV_ARRAY], 32 | HTTP_PORT: [typenvy.TC_PORT], 33 | HTTPS_PORT: [typenvy.TC_PORT], 34 | BIND_ADDRESS: [typenvy.TC_STRING], 35 | CERT_PATH: [typenvy.TC_PATH], 36 | KEY_PATH: [typenvy.TC_PATH], 37 | CA_PATH: [typenvy.TC_PATH], 38 | IGNORE_EMPTY_CERT: [typenvy.TC_BOOLEAN], 39 | STATIC_PATH: [typenvy.TC_PATH], 40 | } -------------------------------------------------------------------------------- /src/test/json.ts: -------------------------------------------------------------------------------- 1 | export type JsonBase = null | boolean | number | string // Json primitive types 2 | export type JsonHolder = JsonArray | JsonObject // A json object or array 3 | export type JsonArray = Array // A array with just json type values 4 | export type JsonObject = ObjectType // A object with just json type values 5 | export type JsonTypes = JsonBase | JsonHolder // Can be every json type 6 | 7 | export interface ObjectType { // Object that holds the generec type as values 8 | [key: string]: V 9 | } 10 | 11 | /** 12 | * 13 | * @param holder A json object or array. 14 | * @param recursive A boolean that enable recursive polishing inside of the holder value/sub values. 15 | * @description Convert each numeric string to a number value and true/false to a boolean. 16 | * @returns The polished holder vobject or array 17 | */ 18 | export function polishValues( 19 | holder: T, 20 | recursive: boolean = true 21 | ): T { 22 | if (Array.isArray(holder)) { 23 | const jsonArray: JsonArray = holder as JsonArray 24 | for (let index = 0; index < jsonArray.length; index++) { 25 | const value = jsonArray[index] 26 | if (typeof value == "string" && value.length > 0) { 27 | if (!isNaN(value as any)) { 28 | jsonArray[index] = Number(value) 29 | } else if (value.toLowerCase() == "true") { 30 | jsonArray[index] = true 31 | } else if (value.toLowerCase() == "false") { 32 | jsonArray[index] = false 33 | } 34 | } else if (recursive && typeof value == "object" && value != null) { 35 | jsonArray[index] = polishValues(value) 36 | } 37 | } 38 | } else { 39 | const jsonObject: JsonObject = holder as JsonObject 40 | const keys = Object.keys(jsonObject) 41 | for (let index = 0; index < keys.length; index++) { 42 | const key = keys[index]; 43 | const value = jsonObject[key] 44 | if (typeof value == "string" && value.length > 0) { 45 | if (!isNaN(value as any)) { 46 | jsonObject[key] = Number(value) 47 | } else if (value.toLowerCase() == "true") { 48 | jsonObject[key] = true 49 | } else if (value.toLowerCase() == "false") { 50 | jsonObject[key] = false 51 | } 52 | } else if (recursive && typeof value == "object" && value != null) { 53 | jsonObject[key] = polishValues(value) 54 | } 55 | } 56 | } 57 | return holder 58 | } 59 | 60 | export function uniqueStringify(value: any): string { 61 | return JSON.stringify(value, null, 4) 62 | } -------------------------------------------------------------------------------- /src/test/parser.ts: -------------------------------------------------------------------------------- 1 | import "mocha" 2 | import { expect } from "chai" 3 | 4 | import { fullTypeOf } from "../index" 5 | import * as typenvy from "../index" 6 | import { VariablesTypes } from '../index'; 7 | import { exampleChecker, exampleSourceEnv } from "./example"; 8 | import { uniqueStringify } from './json'; 9 | 10 | describe('Test typenvy EnvironmentParser class', () => { 11 | it("envParser should check types", () => { 12 | let sourceEnv: typeof exampleSourceEnv = { ...exampleSourceEnv } 13 | let checker: VariablesTypes = { ...exampleChecker } 14 | 15 | const newEnv = typenvy 16 | .parseEnv(sourceEnv, checker) 17 | .errThrow() 18 | .clearProcessEnv() 19 | .env 20 | 21 | sourceEnv["STATIC_PATH"] = process.cwd() + "/public" 22 | sourceEnv["CERT_PATH"] = process.cwd() + "/certs/cert.pem" 23 | sourceEnv["KEY_PATH"] = process.cwd() + "/certs/privkey.pem" 24 | sourceEnv["CA_PATH"] = process.cwd() + "/certs/chain.pem" 25 | 26 | expect(uniqueStringify( 27 | newEnv 28 | )).is.equals(uniqueStringify( 29 | sourceEnv 30 | )) 31 | }) 32 | 33 | it("envParser should use process.env vars", () => { 34 | let sourceEnv: typeof exampleSourceEnv = { ...exampleSourceEnv } 35 | let checker: VariablesTypes = { ...exampleChecker } 36 | 37 | process.env["VERBOSE"] = "true" 38 | process.env["HTTPS_PORT"] = "54321" 39 | process.env["BIND_ADDRESS"] = "127.0.0.1" 40 | process.env["STATIC_PATH"] = "public" 41 | 42 | const newEnv = typenvy 43 | .parseEnv(sourceEnv, checker) 44 | .errThrow() 45 | .clearProcessEnv() 46 | .env 47 | 48 | sourceEnv["VERBOSE"] = true 49 | sourceEnv["HTTPS_PORT"] = 54321 50 | sourceEnv["BIND_ADDRESS"] = "127.0.0.1" 51 | sourceEnv["STATIC_PATH"] = process.cwd() + "/public" 52 | sourceEnv["CERT_PATH"] = process.cwd() + "/certs/cert.pem" 53 | sourceEnv["KEY_PATH"] = process.cwd() + "/certs/privkey.pem" 54 | sourceEnv["CA_PATH"] = process.cwd() + "/certs/chain.pem" 55 | expect(uniqueStringify( 56 | newEnv 57 | )).is.equals(uniqueStringify( 58 | sourceEnv 59 | )) 60 | }) 61 | 62 | it("check with string value as boolean", () => { 63 | let sourceEnv: typeof exampleSourceEnv = { ...exampleSourceEnv } 64 | let checker: VariablesTypes = { ...exampleChecker } 65 | 66 | sourceEnv.PRODUCTION = "hallo world" as any 67 | 68 | const res = typenvy 69 | .parseEnv(sourceEnv, checker) 70 | .clearProcessEnv() 71 | 72 | expect(fullTypeOf(res.errors)).is.equals("array") 73 | expect(res.errors.length).is.equals(1) 74 | 75 | expect(fullTypeOf(res.errors[0])).is.equals("array") 76 | expect(res.errors[0].length).is.equals(2) 77 | expect(res.errors[0][1].message).is.equals( 78 | "The environment variable 'PRODUCTION' is not type of 'BOOLEAN'" 79 | ) 80 | expect(res.errors[1]).is.equals(undefined) 81 | }) 82 | 83 | it("check with 'true' value as port", () => { 84 | let sourceEnv: typeof exampleSourceEnv = { ...exampleSourceEnv } 85 | let checker: VariablesTypes = { ...exampleChecker } 86 | 87 | process.env["HTTPS_PORT"] = "true" 88 | 89 | const res = typenvy 90 | .parseEnv(sourceEnv, checker) 91 | 92 | expect(fullTypeOf(res.errors)).is.equals("array") 93 | expect(res.errors.length).is.equals(1) 94 | 95 | expect(fullTypeOf(res.errors[0])).is.equals("array") 96 | expect(res.errors[0].length).is.equals(2) 97 | expect(res.errors[0][1].message).is.equals( 98 | "The environment variable 'HTTPS_PORT' is not type of 'PORT'" 99 | ) 100 | expect(res.errors[1]).is.equals(undefined) 101 | delete process.env["HTTPS_PORT"] 102 | }) 103 | 104 | it("check with 65536 value as port", () => { 105 | let sourceEnv: typeof exampleSourceEnv = { ...exampleSourceEnv } 106 | let checker: VariablesTypes = { ...exampleChecker } 107 | 108 | process.env["HTTPS_PORT"] = "65536" 109 | 110 | const res = typenvy 111 | .parseEnv(sourceEnv, checker) 112 | 113 | expect(fullTypeOf(res.errors)).is.equals("array") 114 | expect(res.errors.length).is.equals(1) 115 | 116 | expect(fullTypeOf(res.errors[0])).is.equals("array") 117 | expect(res.errors[0].length).is.equals(2) 118 | expect(res.errors[0][1].message).is.equals( 119 | "The environment variable 'HTTPS_PORT' is not type of 'PORT'" 120 | ) 121 | expect(res.errors[1]).is.equals(undefined) 122 | delete process.env["HTTPS_PORT"] 123 | }) 124 | 125 | it("check with wrong type object on number", () => { 126 | let sourceEnv: typeof exampleSourceEnv = { ...exampleSourceEnv } 127 | let checker: VariablesTypes = { ...exampleChecker } 128 | 129 | checker.HTTP_PORT = [typenvy.TC_JSON_OBJECT] 130 | 131 | const res = typenvy 132 | .parseEnv(sourceEnv, checker) 133 | 134 | expect( 135 | fullTypeOf(res.errors), 136 | "Check error array is an array" 137 | ).is.equals("array") 138 | expect( 139 | res.errors.length, 140 | "Check error count" 141 | ).is.equals(1) 142 | expect( 143 | fullTypeOf(res.errors[0]), 144 | "Check if error is a tuple array" 145 | ).is.equals("array") 146 | expect( 147 | res.errors[0].length, 148 | "Check error tuple size" 149 | ).is.equals(2) 150 | expect(res.errors[0][1].message, 151 | "Check error message" 152 | ).is.equals( 153 | "The environment variable 'HTTP_PORT' is not type of 'JSON OBJECT'" 154 | ) 155 | expect(res.errors[1], "Check if 2. error is undefined").is.equals(undefined) 156 | }) 157 | 158 | it("envParser should errors if is wrong process.env types", () => { 159 | let sourceEnv: typeof exampleSourceEnv = { ...exampleSourceEnv } 160 | let checker: VariablesTypes = { ...exampleChecker } 161 | 162 | sourceEnv.PRODUCTION = "test" as any 163 | 164 | checker.STATIC_PATH = [typenvy.TC_EMAIL] 165 | 166 | process.env["VERBOSE"] = "123" 167 | process.env["HTTPS_PORT"] = "true" 168 | process.env["BIND_ADDRESS"] = "" 169 | process.env["STATIC_PATH"] = "./public" 170 | 171 | const res = typenvy 172 | .parseEnv(sourceEnv, checker) 173 | .clearProcessEnv() 174 | expect( 175 | res.errors.length, 176 | "Errors:\n - " + 177 | res.errors.map( 178 | (v) => v[1].stack 179 | ).join("\n - ") 180 | ).is.equals(5) 181 | }) 182 | }) 183 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | /* Basic Options */ 5 | "target": "ES5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 6 | "module": "CommonJS", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 7 | "lib": [ 8 | "ESNext", 9 | "ES6", 10 | "ES5" 11 | ], /* Specify library files to be included in the compilation. */ 12 | "allowJs": false, /* Allow javascript files to be compiled. */ 13 | // "skipLibCheck": true, 14 | // "checkJs": true, /* Report errors in .js files. */ 15 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 16 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 17 | "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 18 | "sourceMap": true, /* Generates corresponding '.map' file. */ 19 | // "outFile": "./", /* Concatenate and emit output to single file. */ 20 | "outDir": "dist", /* Redirect output structure to the directory. */ 21 | "rootDir": "src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 22 | // "composite": true, /* Enable project compilation */ 23 | // "incremental": true, /* Enable incremental compilation */ 24 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 25 | "removeComments": true, /* Do not emit comments to output. */ 26 | // "noEmit": true, /* Do not emit outputs. */ 27 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 28 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 29 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 30 | /* Strict Type-Checking Options */ 31 | // "strict": true, /* Enable all strict type-checking options. */ 32 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 33 | // "strictNullChecks": true, /* Enable strict null checks. */ 34 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 35 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 36 | //"strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 37 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 38 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 39 | /* Additional Checks */ 40 | //"noUnusedLocals": true, /* Report errors on unused locals. */ 41 | //"noUnusedParameters": false, /* Report errors on unused parameters. */ 42 | //"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 43 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 44 | /* Module Resolution Options */ 45 | "resolveJsonModule": true, 46 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 47 | "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 48 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 49 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 50 | "typeRoots": [ 51 | "./node_modules/@types", 52 | ], /* List of folders to include type definitions from. */ 53 | "types": [ 54 | "node" 55 | ], /* Type declaration files to be included in compilation. */ 56 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 57 | // "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 58 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 59 | /* Source Map Options */ 60 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 61 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 62 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 63 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 64 | /* Experimental Options */ 65 | "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 66 | "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 67 | }, 68 | "include": [ 69 | "src/**/*.ts", 70 | ], 71 | "exclude": [ 72 | "dist", 73 | "node_modules" 74 | ] 75 | } --------------------------------------------------------------------------------