diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..d9b3fd0 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,34 @@ +module.exports = { + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "prettier" + ], + "overrides": [ + { + "env": { + "node": true + }, + "files": [ + ".eslintrc.{js,cjs}" + ], + "parserOptions": { + "sourceType": "script" + } + } + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b71b438 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI + +on: [push, workflow_dispatch] + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + lint-and-test: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [16.x, 18.x, 20.x] + steps: + - uses: actions/checkout@v4 + name: Checkout + + - uses: pnpm/action-setup@v2 + with: + version: 8 + + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: pnpm + cache-dependency-path: ./pnpm-lock.yaml + + - run: pnpm i && pnpm t + name: Lint and Test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..167535c --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +dist +.env +*.log +coverage +node_modules + +# Using pnpm now +package-lock.json diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..d0a4977 --- /dev/null +++ b/.npmignore @@ -0,0 +1,10 @@ +.git* +src +tests +tsconfig.json +pnpm-lock.yaml +.idea +.vscode +.editorconfig +.nvmrc +.DS_Store \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..e667af2 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +publish-branch=current diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..d94d3c4 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +*.md +docs diff --git a/README.md b/README.md new file mode 100644 index 0000000..3a03bfd --- /dev/null +++ b/README.md @@ -0,0 +1,224 @@ +Config Simple +============================================================================================ + +This library presents a simple configuration system that works for both front-end and back-end services. + +**Goals:** + +1. We want our config definitions to be simple, readable and well-typed. +2. We want to consume config via a frozen `config` constant, like `config.my.value`, and we want the types to be + readable and accurate; +3. We do not need/want to update or alter config at runtime (config updates should require an env var update and a + reboot of the container); +4. We want to coerce certain config strings into other types in the app (e.g., "false" → false); +5. We want to allow certain configs to be undefined but require values for others; +6. We want to support certain secret stores (e.g., kubernetes) that provide secrets via files mounted into the + container. +7. We want our system to work in both node and browser. + +> +> **NOTE: See also [Config Node](https://github.com/wymp/config-node) ([pkg](https://www.npmjs.com/package/@wymp/config-node)) +> for an alternative config system for node.** +> + + +## Usage + +```ts +// src/config.ts +import { ConfigError, configValue, REQUIRED, validate, Validators } from "@wymp/config-simple"; + +// The `configValue` function returns any of the following: +// +// * the type indicated ("num" => number, "str" => string, "bool" => boolean, etc.) +// * undefined (if allowed) +// * An error string starting with ::ERROR:: +// +// We use `configValue` to define/coerce our values + +enum ENVS = { + DEVELOPMENT = 'development', + STAGING = 'staging', + PRODUCTION = 'production', +} +const env = configValue('APP_ENV', REQUIRED, Validators.oneOf(Object.values(ENVS))) as ENVS | ConfigError; + +const configDef = { + env, + + // required number defaulting to 1234 coming from var/file PORT + port: configValue("PORT", "num", 1234), + + db: { + // non-required string (default undefined) coming from DATABASE_URL + url: configValue(["DATABASE_URL", "OTHER_POSSIBLE_DB_URL"], "str"), + // hard-coded; cannot change + type: "postgres" as const, + port: 5432, + }, + + services: { + // non-required string + myAuthServiceUrl: configValue("AUTH_SERVICE_URL", "str"), + // non-required boolean defaulting to false + stubAuthService: configValue("STUB_AUTH_SERVICE", "bool", false), + // required number with no default + somethingNonOptional: configValue("SOME_THING", "num", REQUIRED), + }, + + someOtherVal: configValue( + "SOME_OTHER_THING", + [ + Validators.requiredForEnv(env, [ENVS.STAGING, ENVS.PRODUCTION]), + Validators.httpHost + ] + ), +} + +// The type of configDef is now +// { +// env: ENVS | ConfigError; +// port: number | ConfigError; +// db: { +// url: string | undefined; +// type: "postgres"; +// port: number; +// }; +// services: { +// myAuthServiceUrl: string | undefined; +// stubAuthService: boolean | ConfigError; +// somethingNonOptional: number | ConfigError; +// }, +// someOtherVal: string | undefined | ConfigError +// } +// +// Now we run the config def through the validator to get the final config +// +// This throws an error if there are any `::ERROR::` values; otherwise, returns a frozen object with all types excluding +// the `::ERROR::${string}` template type. + +export const config = validate(configDef); + +// Alternatively, if we don't want to throw, we can do this: +const result = validate(configDef, "dont-throw"); +if (result.t === "error") { + console.error(`CONFIG ERRORS:\n\n * ${result.errors.join("\n * ")}`); +} + +// NOTE: This config is typed as "clean" (without errors), but it may still have error strings. This can result in +// runtime issues (for example, trying to do math on a value that's supposed to be a number but really contains an error +// string). Use at your own risk. +export dirtyConfig = result.value; +``` + + +### Usage With Weenie + +This library is typically used to create an in-place, bespoke config object. Therefore, it's already sort of +Weenie-compatible out of the box. + +Assuming the example above is in a file like `src/config.ts`, you would include it in your DI container like so: + +```ts +// src/main.ts +import { config } from "./config"; +import * as Weenie from "@wymp/weenie-framework"; + +const deps = Weenie.Weenie({ config }) + .and(Weenie.logger) + .and(Weenie.mysql) + .done(d => d); +``` + + +### Usage With Environment-Specific Dot-Env Files + +This library works well with a particular pattern of using dot-env files to modify config on a per-environment basis. To +facilitate this, you might add the following to the top of the `src/config.ts` file we were playing with in our example +above: + +```ts +// src/config.ts +import { ConfigError, configValue, REQUIRED, validate, Validators } from "@wymp/config-simple"; +import * as dotenv from "dotenv"; + +enum ENVS = { + DEVELOPMENT = 'development', + STAGING = 'staging', + PRODUCTION = 'production', +} +const env = configValue('APP_ENV', ENVS.DEVELOPMENT, Validators.oneOf(Object.values(ENVS))) as ENVS | ConfigError; +[".env/local", `.env/${env}`].forEach(f => { + if (existsSync(f)) { + dotenv.config({ path: f }); + } +}); + +const configDef = { + env, + // ... +} +// ... +``` + +With this in place, you would then create the `.env` directory and populate it with files for each environment. For +example, you might have the following: + +```sh +# .env/development +PORT=80 +DATABASE_URL=postgres://postgres:5432/service-db +AUTH_SERVICE_URL=http://auth-service +STUB_AUTH_SERVICE=false +SOME_THING=2 +SOME_OTHER_THING=http://other-thing +``` + +```sh +# .env/production +PORT=80 +STUB_AUTH_SERVICE=false +``` + +etc... + +This way, you can easily manage non-sensitive environment config as part of the codebase while still maintaining the +ability to easily override it with env vars should the need arise. Devs can also easily create a `.env/local` file to +override anything they may want to override locally. + + +### Usage In Front-End Codebases + +Obviously front-end codebases don't have access to `process.env`. However, almost all modern systems provide _some_ way +of accessing `process.env` at build-time and using certain values to populate a simulated `process.env` object provided +at runtime. This often works via some sort of allowlist or prefix mechanism that signals to the build system which env +vars are ok to include in the app. + +Because of this, you can still use this system on the front-end, provided your build system provides this sort of +`process.env` polyfill. Experiment to see what you have access to, and be ready to provide your own polyfill in the +event that your particular build system does not provide one. + + +### Usage As Deployment Prevalidation + +It's often very useful to make sure your application has a valid config prior to deployment. You can fairly easily do +this with a script both front- and back-end that you run automatically pre-build (front-end) or pre-deploy (back-end). +For example, Heroku has a `release` stage that you can use for this, and Kubernetes has `init` containers that serve +a similar purpose. + +In the simplest case, the script will simply execute the `src/config.ts` file, which should throw an error if you set it +up to throw. If you passed `dont-throw` to the `validate` function, then you should probe the result for validity and +throw from your prevalidation script if the config is invalid. + + +### Usage Tips and Tricks + +Probably the most important piece of advice with this system is this: + +**Only provide default values if the production value can safely be used as a default. If a given config key requires a +value and there is no production-safe default, use the `REQUIRED` symbol to mark the value required and then provide +env-specific overrides in the files under `.env/`** + +This way, you will never deploy your production app with unsafe values, and if you implement deployment prevalidation as +indicated above, you will be unable to deploy your app with missing values. Thus, you can be fairly certain that if your +app has successfully deployed, it has a correct and sensible config. diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..e2ac661 --- /dev/null +++ b/docs/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/assets/highlight.css b/docs/assets/highlight.css new file mode 100644 index 0000000..57aaddf --- /dev/null +++ b/docs/assets/highlight.css @@ -0,0 +1,106 @@ +:root { + --light-hl-0: #AF00DB; + --dark-hl-0: #C586C0; + --light-hl-1: #000000; + --dark-hl-1: #D4D4D4; + --light-hl-2: #001080; + --dark-hl-2: #9CDCFE; + --light-hl-3: #A31515; + --dark-hl-3: #CE9178; + --light-hl-4: #008000; + --dark-hl-4: #6A9955; + --light-hl-5: #0000FF; + --dark-hl-5: #569CD6; + --light-hl-6: #0070C1; + --dark-hl-6: #4FC1FF; + --light-hl-7: #795E26; + --dark-hl-7: #DCDCAA; + --light-hl-8: #098658; + --dark-hl-8: #B5CEA8; + --light-hl-9: #EE0000; + --dark-hl-9: #D7BA7D; + --light-hl-10: #000000FF; + --dark-hl-10: #D4D4D4; + --light-hl-11: #267F99; + --dark-hl-11: #4EC9B0; + --light-code-background: #FFFFFF; + --dark-code-background: #1E1E1E; +} + +@media (prefers-color-scheme: light) { :root { + --hl-0: var(--light-hl-0); + --hl-1: var(--light-hl-1); + --hl-2: var(--light-hl-2); + --hl-3: var(--light-hl-3); + --hl-4: var(--light-hl-4); + --hl-5: var(--light-hl-5); + --hl-6: var(--light-hl-6); + --hl-7: var(--light-hl-7); + --hl-8: var(--light-hl-8); + --hl-9: var(--light-hl-9); + --hl-10: var(--light-hl-10); + --hl-11: var(--light-hl-11); + --code-background: var(--light-code-background); +} } + +@media (prefers-color-scheme: dark) { :root { + --hl-0: var(--dark-hl-0); + --hl-1: var(--dark-hl-1); + --hl-2: var(--dark-hl-2); + --hl-3: var(--dark-hl-3); + --hl-4: var(--dark-hl-4); + --hl-5: var(--dark-hl-5); + --hl-6: var(--dark-hl-6); + --hl-7: var(--dark-hl-7); + --hl-8: var(--dark-hl-8); + --hl-9: var(--dark-hl-9); + --hl-10: var(--dark-hl-10); + --hl-11: var(--dark-hl-11); + --code-background: var(--dark-code-background); +} } + +:root[data-theme='light'] { + --hl-0: var(--light-hl-0); + --hl-1: var(--light-hl-1); + --hl-2: var(--light-hl-2); + --hl-3: var(--light-hl-3); + --hl-4: var(--light-hl-4); + --hl-5: var(--light-hl-5); + --hl-6: var(--light-hl-6); + --hl-7: var(--light-hl-7); + --hl-8: var(--light-hl-8); + --hl-9: var(--light-hl-9); + --hl-10: var(--light-hl-10); + --hl-11: var(--light-hl-11); + --code-background: var(--light-code-background); +} + +:root[data-theme='dark'] { + --hl-0: var(--dark-hl-0); + --hl-1: var(--dark-hl-1); + --hl-2: var(--dark-hl-2); + --hl-3: var(--dark-hl-3); + --hl-4: var(--dark-hl-4); + --hl-5: var(--dark-hl-5); + --hl-6: var(--dark-hl-6); + --hl-7: var(--dark-hl-7); + --hl-8: var(--dark-hl-8); + --hl-9: var(--dark-hl-9); + --hl-10: var(--dark-hl-10); + --hl-11: var(--dark-hl-11); + --code-background: var(--dark-code-background); +} + +.hl-0 { color: var(--hl-0); } +.hl-1 { color: var(--hl-1); } +.hl-2 { color: var(--hl-2); } +.hl-3 { color: var(--hl-3); } +.hl-4 { color: var(--hl-4); } +.hl-5 { color: var(--hl-5); } +.hl-6 { color: var(--hl-6); } +.hl-7 { color: var(--hl-7); } +.hl-8 { color: var(--hl-8); } +.hl-9 { color: var(--hl-9); } +.hl-10 { color: var(--hl-10); } +.hl-11 { color: var(--hl-11); } +pre, code { background: var(--code-background); } diff --git a/docs/assets/main.js b/docs/assets/main.js new file mode 100644 index 0000000..d0aa8d5 --- /dev/null +++ b/docs/assets/main.js @@ -0,0 +1,59 @@ +"use strict"; +"use strict";(()=>{var Pe=Object.create;var ne=Object.defineProperty;var Ie=Object.getOwnPropertyDescriptor;var Oe=Object.getOwnPropertyNames;var _e=Object.getPrototypeOf,Re=Object.prototype.hasOwnProperty;var Me=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var Fe=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Oe(e))!Re.call(t,i)&&i!==n&&ne(t,i,{get:()=>e[i],enumerable:!(r=Ie(e,i))||r.enumerable});return t};var De=(t,e,n)=>(n=t!=null?Pe(_e(t)):{},Fe(e||!t||!t.__esModule?ne(n,"default",{value:t,enumerable:!0}):n,t));var ae=Me((se,oe)=>{(function(){var t=function(e){var n=new t.Builder;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),n.searchPipeline.add(t.stemmer),e.call(n,n),n.build()};t.version="2.3.9";t.utils={},t.utils.warn=function(e){return function(n){e.console&&console.warn&&console.warn(n)}}(this),t.utils.asString=function(e){return e==null?"":e.toString()},t.utils.clone=function(e){if(e==null)return e;for(var n=Object.create(null),r=Object.keys(e),i=0;i0){var d=t.utils.clone(n)||{};d.position=[a,u],d.index=s.length,s.push(new t.Token(r.slice(a,o),d))}a=o+1}}return s},t.tokenizer.separator=/[\s\-]+/;t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions=Object.create(null),t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. +`,e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(r){var i=t.Pipeline.registeredFunctions[r];if(i)n.add(i);else throw new Error("Cannot load unregistered function: "+r)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(n){t.Pipeline.warnIfFunctionNotRegistered(n),this._stack.push(n)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");r=r+1,this._stack.splice(r,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");this._stack.splice(r,0,n)},t.Pipeline.prototype.remove=function(e){var n=this._stack.indexOf(e);n!=-1&&this._stack.splice(n,1)},t.Pipeline.prototype.run=function(e){for(var n=this._stack.length,r=0;r1&&(oe&&(r=s),o!=e);)i=r-n,s=n+Math.floor(i/2),o=this.elements[s*2];if(o==e||o>e)return s*2;if(ol?d+=2:a==l&&(n+=r[u+1]*i[d+1],u+=2,d+=2);return n},t.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},t.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),n=1,r=0;n0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new t.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new t.TokenSet;s.node.edges["*"]=l}if(s.str.length==0&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new t.TokenSet;s.node.edges["*"]=u}s.str.length==1&&(u.final=!0),i.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var d=s.str.charAt(0),v=s.str.charAt(1),f;v in s.node.edges?f=s.node.edges[v]:(f=new t.TokenSet,s.node.edges[v]=f),s.str.length==1&&(f.final=!0),i.push({node:f,editsRemaining:s.editsRemaining-1,str:d+s.str.slice(2)})}}}return r},t.TokenSet.fromString=function(e){for(var n=new t.TokenSet,r=n,i=0,s=e.length;i=e;n--){var r=this.uncheckedNodes[n],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r.char]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}};t.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},t.Index.prototype.search=function(e){return this.query(function(n){var r=new t.QueryParser(e,n);r.parse()})},t.Index.prototype.query=function(e){for(var n=new t.Query(this.fields),r=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),l=0;l1?this._b=1:this._b=e},t.Builder.prototype.k1=function(e){this._k1=e},t.Builder.prototype.add=function(e,n){var r=e[this._ref],i=Object.keys(this._fields);this._documents[r]=n||{},this.documentCount+=1;for(var s=0;s=this.length)return t.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},t.QueryLexer.prototype.width=function(){return this.pos-this.start},t.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},t.QueryLexer.prototype.backup=function(){this.pos-=1},t.QueryLexer.prototype.acceptDigitRun=function(){var e,n;do e=this.next(),n=e.charCodeAt(0);while(n>47&&n<58);e!=t.QueryLexer.EOS&&this.backup()},t.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(t.QueryLexer.TERM)),e.ignore(),e.more())return t.QueryLexer.lexText},t.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.EDIT_DISTANCE),t.QueryLexer.lexText},t.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.BOOST),t.QueryLexer.lexText},t.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(t.QueryLexer.TERM)},t.QueryLexer.termSeparator=t.tokenizer.separator,t.QueryLexer.lexText=function(e){for(;;){var n=e.next();if(n==t.QueryLexer.EOS)return t.QueryLexer.lexEOS;if(n.charCodeAt(0)==92){e.escapeCharacter();continue}if(n==":")return t.QueryLexer.lexField;if(n=="~")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexEditDistance;if(n=="^")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexBoost;if(n=="+"&&e.width()===1||n=="-"&&e.width()===1)return e.emit(t.QueryLexer.PRESENCE),t.QueryLexer.lexText;if(n.match(t.QueryLexer.termSeparator))return t.QueryLexer.lexTerm}},t.QueryParser=function(e,n){this.lexer=new t.QueryLexer(e),this.query=n,this.currentClause={},this.lexemeIdx=0},t.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=t.QueryParser.parseClause;e;)e=e(this);return this.query},t.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},t.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},t.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},t.QueryParser.parseClause=function(e){var n=e.peekLexeme();if(n!=null)switch(n.type){case t.QueryLexer.PRESENCE:return t.QueryParser.parsePresence;case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expected either a field or a term, found "+n.type;throw n.str.length>=1&&(r+=" with value '"+n.str+"'"),new t.QueryParseError(r,n.start,n.end)}},t.QueryParser.parsePresence=function(e){var n=e.consumeLexeme();if(n!=null){switch(n.str){case"-":e.currentClause.presence=t.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=t.Query.presence.REQUIRED;break;default:var r="unrecognised presence operator'"+n.str+"'";throw new t.QueryParseError(r,n.start,n.end)}var i=e.peekLexeme();if(i==null){var r="expecting term or field, found nothing";throw new t.QueryParseError(r,n.start,n.end)}switch(i.type){case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expecting term or field, found '"+i.type+"'";throw new t.QueryParseError(r,i.start,i.end)}}},t.QueryParser.parseField=function(e){var n=e.consumeLexeme();if(n!=null){if(e.query.allFields.indexOf(n.str)==-1){var r=e.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+n.str+"', possible fields: "+r;throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.fields=[n.str];var s=e.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new t.QueryParseError(i,n.start,n.end)}switch(s.type){case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var i="expecting term, found '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseTerm=function(e){var n=e.consumeLexeme();if(n!=null){e.currentClause.term=n.str.toLowerCase(),n.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var r=e.peekLexeme();if(r==null){e.nextClause();return}switch(r.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+r.type+"'";throw new t.QueryParseError(i,r.start,r.end)}}},t.QueryParser.parseEditDistance=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="edit distance must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.editDistance=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseBoost=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="boost must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.boost=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},function(e,n){typeof define=="function"&&define.amd?define(n):typeof se=="object"?oe.exports=n():e.lunr=n()}(this,function(){return t})})()});var re=[];function G(t,e){re.push({selector:e,constructor:t})}var U=class{constructor(){this.alwaysVisibleMember=null;this.createComponents(document.body),this.ensureActivePageVisible(),this.ensureFocusedElementVisible(),this.listenForCodeCopies(),window.addEventListener("hashchange",()=>this.ensureFocusedElementVisible())}createComponents(e){re.forEach(n=>{e.querySelectorAll(n.selector).forEach(r=>{r.dataset.hasInstance||(new n.constructor({el:r,app:this}),r.dataset.hasInstance=String(!0))})})}filterChanged(){this.ensureFocusedElementVisible()}ensureActivePageVisible(){let e=document.querySelector(".tsd-navigation .current"),n=e?.parentElement;for(;n&&!n.classList.contains(".tsd-navigation");)n instanceof HTMLDetailsElement&&(n.open=!0),n=n.parentElement;if(e){let r=e.getBoundingClientRect().top-document.documentElement.clientHeight/4;document.querySelector(".site-menu").scrollTop=r}}ensureFocusedElementVisible(){if(this.alwaysVisibleMember&&(this.alwaysVisibleMember.classList.remove("always-visible"),this.alwaysVisibleMember.firstElementChild.remove(),this.alwaysVisibleMember=null),!location.hash)return;let e=document.getElementById(location.hash.substring(1));if(!e)return;let n=e.parentElement;for(;n&&n.tagName!=="SECTION";)n=n.parentElement;if(n&&n.offsetParent==null){this.alwaysVisibleMember=n,n.classList.add("always-visible");let r=document.createElement("p");r.classList.add("warning"),r.textContent="This member is normally hidden due to your filter settings.",n.prepend(r)}}listenForCodeCopies(){document.querySelectorAll("pre > button").forEach(e=>{let n;e.addEventListener("click",()=>{e.previousElementSibling instanceof HTMLElement&&navigator.clipboard.writeText(e.previousElementSibling.innerText.trim()),e.textContent="Copied!",e.classList.add("visible"),clearTimeout(n),n=setTimeout(()=>{e.classList.remove("visible"),n=setTimeout(()=>{e.textContent="Copy"},100)},1e3)})})}};var ie=(t,e=100)=>{let n;return()=>{clearTimeout(n),n=setTimeout(()=>t(),e)}};var de=De(ae());async function le(t,e){if(!window.searchData)return;let n=await fetch(window.searchData),r=new Blob([await n.arrayBuffer()]).stream().pipeThrough(new DecompressionStream("gzip")),i=await new Response(r).json();t.data=i,t.index=de.Index.load(i.index),e.classList.remove("loading"),e.classList.add("ready")}function he(){let t=document.getElementById("tsd-search");if(!t)return;let e={base:t.dataset.base+"/"},n=document.getElementById("tsd-search-script");t.classList.add("loading"),n&&(n.addEventListener("error",()=>{t.classList.remove("loading"),t.classList.add("failure")}),n.addEventListener("load",()=>{le(e,t)}),le(e,t));let r=document.querySelector("#tsd-search input"),i=document.querySelector("#tsd-search .results");if(!r||!i)throw new Error("The input field or the result list wrapper was not found");let s=!1;i.addEventListener("mousedown",()=>s=!0),i.addEventListener("mouseup",()=>{s=!1,t.classList.remove("has-focus")}),r.addEventListener("focus",()=>t.classList.add("has-focus")),r.addEventListener("blur",()=>{s||(s=!1,t.classList.remove("has-focus"))}),Ae(t,i,r,e)}function Ae(t,e,n,r){n.addEventListener("input",ie(()=>{Ne(t,e,n,r)},200));let i=!1;n.addEventListener("keydown",s=>{i=!0,s.key=="Enter"?Ve(e,n):s.key=="Escape"?n.blur():s.key=="ArrowUp"?ue(e,-1):s.key==="ArrowDown"?ue(e,1):i=!1}),n.addEventListener("keypress",s=>{i&&s.preventDefault()}),document.body.addEventListener("keydown",s=>{s.altKey||s.ctrlKey||s.metaKey||!n.matches(":focus")&&s.key==="/"&&(n.focus(),s.preventDefault())})}function Ne(t,e,n,r){if(!r.index||!r.data)return;e.textContent="";let i=n.value.trim(),s;if(i){let o=i.split(" ").map(a=>a.length?`*${a}*`:"").join(" ");s=r.index.search(o)}else s=[];for(let o=0;oa.score-o.score);for(let o=0,a=Math.min(10,s.length);o`,d=ce(l.name,i);globalThis.DEBUG_SEARCH_WEIGHTS&&(d+=` (score: ${s[o].score.toFixed(2)})`),l.parent&&(d=` + ${ce(l.parent,i)}.${d}`);let v=document.createElement("li");v.classList.value=l.classes??"";let f=document.createElement("a");f.href=r.base+l.url,f.innerHTML=u+d,v.append(f),e.appendChild(v)}}function ue(t,e){let n=t.querySelector(".current");if(!n)n=t.querySelector(e==1?"li:first-child":"li:last-child"),n&&n.classList.add("current");else{let r=n;if(e===1)do r=r.nextElementSibling??void 0;while(r instanceof HTMLElement&&r.offsetParent==null);else do r=r.previousElementSibling??void 0;while(r instanceof HTMLElement&&r.offsetParent==null);r&&(n.classList.remove("current"),r.classList.add("current"))}}function Ve(t,e){let n=t.querySelector(".current");if(n||(n=t.querySelector("li:first-child")),n){let r=n.querySelector("a");r&&(window.location.href=r.href),e.blur()}}function ce(t,e){if(e==="")return t;let n=t.toLocaleLowerCase(),r=e.toLocaleLowerCase(),i=[],s=0,o=n.indexOf(r);for(;o!=-1;)i.push(K(t.substring(s,o)),`${K(t.substring(o,o+r.length))}`),s=o+r.length,o=n.indexOf(r,s);return i.push(K(t.substring(s))),i.join("")}var Be={"&":"&","<":"<",">":">","'":"'",'"':"""};function K(t){return t.replace(/[&<>"'"]/g,e=>Be[e])}var C=class{constructor(e){this.el=e.el,this.app=e.app}};var F="mousedown",pe="mousemove",B="mouseup",J={x:0,y:0},fe=!1,ee=!1,He=!1,D=!1,me=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);document.documentElement.classList.add(me?"is-mobile":"not-mobile");me&&"ontouchstart"in document.documentElement&&(He=!0,F="touchstart",pe="touchmove",B="touchend");document.addEventListener(F,t=>{ee=!0,D=!1;let e=F=="touchstart"?t.targetTouches[0]:t;J.y=e.pageY||0,J.x=e.pageX||0});document.addEventListener(pe,t=>{if(ee&&!D){let e=F=="touchstart"?t.targetTouches[0]:t,n=J.x-(e.pageX||0),r=J.y-(e.pageY||0);D=Math.sqrt(n*n+r*r)>10}});document.addEventListener(B,()=>{ee=!1});document.addEventListener("click",t=>{fe&&(t.preventDefault(),t.stopImmediatePropagation(),fe=!1)});var X=class extends C{constructor(n){super(n);this.className=this.el.dataset.toggle||"",this.el.addEventListener(B,r=>this.onPointerUp(r)),this.el.addEventListener("click",r=>r.preventDefault()),document.addEventListener(F,r=>this.onDocumentPointerDown(r)),document.addEventListener(B,r=>this.onDocumentPointerUp(r))}setActive(n){if(this.active==n)return;this.active=n,document.documentElement.classList.toggle("has-"+this.className,n),this.el.classList.toggle("active",n);let r=(this.active?"to-has-":"from-has-")+this.className;document.documentElement.classList.add(r),setTimeout(()=>document.documentElement.classList.remove(r),500)}onPointerUp(n){D||(this.setActive(!0),n.preventDefault())}onDocumentPointerDown(n){if(this.active){if(n.target.closest(".col-sidebar, .tsd-filter-group"))return;this.setActive(!1)}}onDocumentPointerUp(n){if(!D&&this.active&&n.target.closest(".col-sidebar")){let r=n.target.closest("a");if(r){let i=window.location.href;i.indexOf("#")!=-1&&(i=i.substring(0,i.indexOf("#"))),r.href.substring(0,i.length)==i&&setTimeout(()=>this.setActive(!1),250)}}}};var te;try{te=localStorage}catch{te={getItem(){return null},setItem(){}}}var Q=te;var ve=document.head.appendChild(document.createElement("style"));ve.dataset.for="filters";var Y=class extends C{constructor(n){super(n);this.key=`filter-${this.el.name}`,this.value=this.el.checked,this.el.addEventListener("change",()=>{this.setLocalStorage(this.el.checked)}),this.setLocalStorage(this.fromLocalStorage()),ve.innerHTML+=`html:not(.${this.key}) .tsd-is-${this.el.name} { display: none; } +`}fromLocalStorage(){let n=Q.getItem(this.key);return n?n==="true":this.el.checked}setLocalStorage(n){Q.setItem(this.key,n.toString()),this.value=n,this.handleValueChange()}handleValueChange(){this.el.checked=this.value,document.documentElement.classList.toggle(this.key,this.value),this.app.filterChanged(),document.querySelectorAll(".tsd-index-section").forEach(n=>{n.style.display="block";let r=Array.from(n.querySelectorAll(".tsd-index-link")).every(i=>i.offsetParent==null);n.style.display=r?"none":"block"})}};var Z=class extends C{constructor(n){super(n);this.summary=this.el.querySelector(".tsd-accordion-summary"),this.icon=this.summary.querySelector("svg"),this.key=`tsd-accordion-${this.summary.dataset.key??this.summary.textContent.trim().replace(/\s+/g,"-").toLowerCase()}`;let r=Q.getItem(this.key);this.el.open=r?r==="true":this.el.open,this.el.addEventListener("toggle",()=>this.update());let i=this.summary.querySelector("a");i&&i.addEventListener("click",()=>{location.assign(i.href)}),this.update()}update(){this.icon.style.transform=`rotate(${this.el.open?0:-90}deg)`,Q.setItem(this.key,this.el.open.toString())}};function ge(t){let e=Q.getItem("tsd-theme")||"os";t.value=e,ye(e),t.addEventListener("change",()=>{Q.setItem("tsd-theme",t.value),ye(t.value)})}function ye(t){document.documentElement.dataset.theme=t}var Le;function be(){let t=document.getElementById("tsd-nav-script");t&&(t.addEventListener("load",xe),xe())}async function xe(){let t=document.getElementById("tsd-nav-container");if(!t||!window.navigationData)return;let n=await(await fetch(window.navigationData)).arrayBuffer(),r=new Blob([n]).stream().pipeThrough(new DecompressionStream("gzip")),i=await new Response(r).json();Le=t.dataset.base+"/",t.innerHTML="";for(let s of i)we(s,t,[]);window.app.createComponents(t),window.app.ensureActivePageVisible()}function we(t,e,n){let r=e.appendChild(document.createElement("li"));if(t.children){let i=[...n,t.text],s=r.appendChild(document.createElement("details"));s.className=t.class?`${t.class} tsd-index-accordion`:"tsd-index-accordion",s.dataset.key=i.join("$");let o=s.appendChild(document.createElement("summary"));o.className="tsd-accordion-summary",o.innerHTML='',Ee(t,o);let a=s.appendChild(document.createElement("div"));a.className="tsd-accordion-details";let l=a.appendChild(document.createElement("ul"));l.className="tsd-nested-navigation";for(let u of t.children)we(u,l,i)}else Ee(t,r,t.class)}function Ee(t,e,n){if(t.path){let r=e.appendChild(document.createElement("a"));r.href=Le+t.path,n&&(r.className=n),location.href===r.href&&r.classList.add("current"),t.kind&&(r.innerHTML=``),r.appendChild(document.createElement("span")).textContent=t.text}else e.appendChild(document.createElement("span")).textContent=t.text}G(X,"a[data-toggle]");G(Z,".tsd-index-accordion");G(Y,".tsd-filter-item input[type=checkbox]");var Se=document.getElementById("tsd-theme");Se&&ge(Se);var je=new U;Object.defineProperty(window,"app",{value:je});he();be();})(); +/*! Bundled license information: + +lunr/lunr.js: + (** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 + * Copyright (C) 2020 Oliver Nightingale + * @license MIT + *) + (*! + * lunr.utils + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Set + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.tokenizer + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Pipeline + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Vector + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.stemmer + * Copyright (C) 2020 Oliver Nightingale + * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt + *) + (*! + * lunr.stopWordFilter + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.trimmer + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.TokenSet + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Index + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Builder + * Copyright (C) 2020 Oliver Nightingale + *) +*/ diff --git a/docs/assets/navigation.js b/docs/assets/navigation.js new file mode 100644 index 0000000..6965932 --- /dev/null +++ b/docs/assets/navigation.js @@ -0,0 +1 @@ +window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAA4WRPQ+CMBRF/8ubifgFRlbFxFGiLMahQiuN0JK2EI3xvzsYaIEG5nPf6b3p9QMKvxQEsOOM0EcoBBfgQIlUBgGod4mla6BZpoocHHhSlkKwnG83C2/5dXqWGOUVPlQs0SbKFBYEJa2uzfSUnm/oovB0OUbhXntqJCi651i6Dever8w2Z4GYJFwUeLDJQFObjGh308BkmTNmk7ZZJh+ZFqOcpkhxa6UOnCrUhq11NB0pk+gf1Q5SsURRzqRr4K7EXxuS+v+S1dCwwfntBx0VjivBAgAA" \ No newline at end of file diff --git a/docs/assets/search.js b/docs/assets/search.js new file mode 100644 index 0000000..171e24a --- /dev/null +++ b/docs/assets/search.js @@ -0,0 +1 @@ +window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAAA8VZ227jNhD9F/aVdTxDyxe9Fd0sGqBA0cU2L4IRaGV6I1SWXEl2vTD874WoCzkyaUubRfokODxnZnjOkJSYM8uzfwvmB2f2d5xumI/enLM03Enms1+zdBt/fQ6Tg/x4SCPG2SFPmM/itJT5Noxk8dCDTF7LXcI4i5KwKGTBfMYuvA09n3WRI03rom4PaVTGWVo8GKM3AwrsAn7Ow7TYZvlO5kUX8RjmcfglkcWDOXy7Rs8TWoCXl/LbXg6J91MHNcJytg9zmZb9+nQ6mKIW5UuWJSNyTRr8/YSTpjhH3qg4fs5+yfPw25jshPXmGtLDbkzyGv7mrEWZj8law78nq9Gpnx7//Ovp0+MHS+J26GaH4nS1AA97i/QxzzM9lyp7uzbVyKiIxlTIsq+j9ka/fzW5w41YTKrAQVNx5/2BUxhbvqsxyxYyLNXExN9Net2efdmewyTehGVm85+MvVW662D3xKOlWQ+XYw2xnSzt0NBjpctmO1T04I84UnrRBuvgPE7kKYzK32U6ONfEYNxLet1Db5pml7qJ+zMMr0GX7RDitSz3v2VFObwagzFeCJJ7F5bR6/DELfy95Vd5W+3FCO3rgp2TP43qvw7//tM/Gb3njZr/6Ubn7eJ0nAAt/t0FUIlbARZjBKhLdgiQpfKP7fAyWvh7T1/lbWe/GjH7umDH5HP5zyHO5eZjlj+mxwEnSFvONfG9BelV0O3KY7bl/izuqPQ0ok8I5//S5qnrGBizYxq1X9acxelGnph/ZkeZF3GWMp/hREyqJtzGMtlU3+Z1pZxF2W5XhV03Y88yUi8AflBDHqaMB1MuYLJYwnrNg5ahBtQfFAwYD8AGAwJDxgO0wZDABOOB4DCdeIslgQkCmzEezGzRZgTmMR54NphHYHPGg7kNNiewBePBwgZbENiS8WBpgy0JbMV4sLLBVlTeSm2w+gA9I8ClHVAroJIc7J5RN8BpB1A/oNIdrP4CtQQq6UFYkdQVmDuTU1+g0h+s7QDUGqgsAGtHAHUHVs7k1B9U/li7B6k/6PQHe0tF+WPtNKT+YOUCWJsNqUE4cyan/qDyx9qYSP1Bpz9I/cHKBLS2MFJ/cOkMSe3BygS0tjBSf8TUuaNQe0RlAlpbWFB/BDpD9jazygO09rqg9ginPaK2R23uR5mXcvNUb/JB0B02Z/bS7PyiPYjOTDD/fOEMoHm2v+fNc1U/sRnHWfNsxnFZP8W0eWLzVLiLPj+qX1XB9QWiLmWmS3FR6qtZWd82aeZKM1c3mcf6ylczQTPhPnOr7iM0e6rZUxe7OJZZWN9TaqKniZ6DqD4rk+p13Kh2rnnoylh9Pb6qr0eDuDCI6CA2X38Ga2mwhJN16ldp2IGu6e3itEdDQ01cOGjq5lVzDD3mDkaWymxL8hieo6td2tckk2iosbxD22a5VK/ORlo0Vpqr11p+TCs2F6nLB3U7rDmG4S4tjVs7wzyjTHAtw9K81zS45mpydZnB7S8nMDoAXO1dkn+1GCIZrjqo+mbOyGnsOuBS6ti+Rl9VbDgDrlbv2KReMPYAsPbTmrN9vJdJnErmB+vL5T9/Sm5/LhsAAA=="; \ No newline at end of file diff --git a/docs/assets/style.css b/docs/assets/style.css new file mode 100644 index 0000000..07a385b --- /dev/null +++ b/docs/assets/style.css @@ -0,0 +1,1394 @@ +:root { + /* Light */ + --light-color-background: #f2f4f8; + --light-color-background-secondary: #eff0f1; + --light-color-warning-text: #222; + --light-color-background-warning: #e6e600; + --light-color-icon-background: var(--light-color-background); + --light-color-accent: #c5c7c9; + --light-color-active-menu-item: var(--light-color-accent); + --light-color-text: #222; + --light-color-text-aside: #6e6e6e; + --light-color-link: #1f70c2; + + --light-color-ts-keyword: #056bd6; + --light-color-ts-project: #b111c9; + --light-color-ts-module: var(--light-color-ts-project); + --light-color-ts-namespace: var(--light-color-ts-project); + --light-color-ts-enum: #7e6f15; + --light-color-ts-enum-member: var(--light-color-ts-enum); + --light-color-ts-variable: #4760ec; + --light-color-ts-function: #572be7; + --light-color-ts-class: #1f70c2; + --light-color-ts-interface: #108024; + --light-color-ts-constructor: var(--light-color-ts-class); + --light-color-ts-property: var(--light-color-ts-variable); + --light-color-ts-method: var(--light-color-ts-function); + --light-color-ts-call-signature: var(--light-color-ts-method); + --light-color-ts-index-signature: var(--light-color-ts-property); + --light-color-ts-constructor-signature: var(--light-color-ts-constructor); + --light-color-ts-parameter: var(--light-color-ts-variable); + /* type literal not included as links will never be generated to it */ + --light-color-ts-type-parameter: var(--light-color-ts-type-alias); + --light-color-ts-accessor: var(--light-color-ts-property); + --light-color-ts-get-signature: var(--light-color-ts-accessor); + --light-color-ts-set-signature: var(--light-color-ts-accessor); + --light-color-ts-type-alias: #d51270; + /* reference not included as links will be colored with the kind that it points to */ + + --light-external-icon: url("data:image/svg+xml;utf8,"); + --light-color-scheme: light; + + /* Dark */ + --dark-color-background: #2b2e33; + --dark-color-background-secondary: #1e2024; + --dark-color-background-warning: #bebe00; + --dark-color-warning-text: #222; + --dark-color-icon-background: var(--dark-color-background-secondary); + --dark-color-accent: #9096a2; + --dark-color-active-menu-item: #5d5d6a; + --dark-color-text: #f5f5f5; + --dark-color-text-aside: #dddddd; + --dark-color-link: #00aff4; + + --dark-color-ts-keyword: #3399ff; + --dark-color-ts-project: #e358ff; + --dark-color-ts-module: var(--dark-color-ts-project); + --dark-color-ts-namespace: var(--dark-color-ts-project); + --dark-color-ts-enum: #f4d93e; + --dark-color-ts-enum-member: var(--dark-color-ts-enum); + --dark-color-ts-variable: #798dff; + --dark-color-ts-function: #a280ff; + --dark-color-ts-class: #8ac4ff; + --dark-color-ts-interface: #6cff87; + --dark-color-ts-constructor: var(--dark-color-ts-class); + --dark-color-ts-property: var(--dark-color-ts-variable); + --dark-color-ts-method: var(--dark-color-ts-function); + --dark-color-ts-call-signature: var(--dark-color-ts-method); + --dark-color-ts-index-signature: var(--dark-color-ts-property); + --dark-color-ts-constructor-signature: var(--dark-color-ts-constructor); + --dark-color-ts-parameter: var(--dark-color-ts-variable); + /* type literal not included as links will never be generated to it */ + --dark-color-ts-type-parameter: var(--dark-color-ts-type-alias); + --dark-color-ts-accessor: var(--dark-color-ts-property); + --dark-color-ts-get-signature: var(--dark-color-ts-accessor); + --dark-color-ts-set-signature: var(--dark-color-ts-accessor); + --dark-color-ts-type-alias: #ff6492; + /* reference not included as links will be colored with the kind that it points to */ + + --dark-external-icon: url("data:image/svg+xml;utf8,"); + --dark-color-scheme: dark; +} + +@media (prefers-color-scheme: light) { + :root { + --color-background: var(--light-color-background); + --color-background-secondary: var(--light-color-background-secondary); + --color-background-warning: var(--light-color-background-warning); + --color-warning-text: var(--light-color-warning-text); + --color-icon-background: var(--light-color-icon-background); + --color-accent: var(--light-color-accent); + --color-active-menu-item: var(--light-color-active-menu-item); + --color-text: var(--light-color-text); + --color-text-aside: var(--light-color-text-aside); + --color-link: var(--light-color-link); + + --color-ts-keyword: var(--light-color-ts-keyword); + --color-ts-module: var(--light-color-ts-module); + --color-ts-namespace: var(--light-color-ts-namespace); + --color-ts-enum: var(--light-color-ts-enum); + --color-ts-enum-member: var(--light-color-ts-enum-member); + --color-ts-variable: var(--light-color-ts-variable); + --color-ts-function: var(--light-color-ts-function); + --color-ts-class: var(--light-color-ts-class); + --color-ts-interface: var(--light-color-ts-interface); + --color-ts-constructor: var(--light-color-ts-constructor); + --color-ts-property: var(--light-color-ts-property); + --color-ts-method: var(--light-color-ts-method); + --color-ts-call-signature: var(--light-color-ts-call-signature); + --color-ts-index-signature: var(--light-color-ts-index-signature); + --color-ts-constructor-signature: var( + --light-color-ts-constructor-signature + ); + --color-ts-parameter: var(--light-color-ts-parameter); + --color-ts-type-parameter: var(--light-color-ts-type-parameter); + --color-ts-accessor: var(--light-color-ts-accessor); + --color-ts-get-signature: var(--light-color-ts-get-signature); + --color-ts-set-signature: var(--light-color-ts-set-signature); + --color-ts-type-alias: var(--light-color-ts-type-alias); + + --external-icon: var(--light-external-icon); + --color-scheme: var(--light-color-scheme); + } +} + +@media (prefers-color-scheme: dark) { + :root { + --color-background: var(--dark-color-background); + --color-background-secondary: var(--dark-color-background-secondary); + --color-background-warning: var(--dark-color-background-warning); + --color-warning-text: var(--dark-color-warning-text); + --color-icon-background: var(--dark-color-icon-background); + --color-accent: var(--dark-color-accent); + --color-active-menu-item: var(--dark-color-active-menu-item); + --color-text: var(--dark-color-text); + --color-text-aside: var(--dark-color-text-aside); + --color-link: var(--dark-color-link); + + --color-ts-keyword: var(--dark-color-ts-keyword); + --color-ts-module: var(--dark-color-ts-module); + --color-ts-namespace: var(--dark-color-ts-namespace); + --color-ts-enum: var(--dark-color-ts-enum); + --color-ts-enum-member: var(--dark-color-ts-enum-member); + --color-ts-variable: var(--dark-color-ts-variable); + --color-ts-function: var(--dark-color-ts-function); + --color-ts-class: var(--dark-color-ts-class); + --color-ts-interface: var(--dark-color-ts-interface); + --color-ts-constructor: var(--dark-color-ts-constructor); + --color-ts-property: var(--dark-color-ts-property); + --color-ts-method: var(--dark-color-ts-method); + --color-ts-call-signature: var(--dark-color-ts-call-signature); + --color-ts-index-signature: var(--dark-color-ts-index-signature); + --color-ts-constructor-signature: var( + --dark-color-ts-constructor-signature + ); + --color-ts-parameter: var(--dark-color-ts-parameter); + --color-ts-type-parameter: var(--dark-color-ts-type-parameter); + --color-ts-accessor: var(--dark-color-ts-accessor); + --color-ts-get-signature: var(--dark-color-ts-get-signature); + --color-ts-set-signature: var(--dark-color-ts-set-signature); + --color-ts-type-alias: var(--dark-color-ts-type-alias); + + --external-icon: var(--dark-external-icon); + --color-scheme: var(--dark-color-scheme); + } +} + +html { + color-scheme: var(--color-scheme); +} + +body { + margin: 0; +} + +:root[data-theme="light"] { + --color-background: var(--light-color-background); + --color-background-secondary: var(--light-color-background-secondary); + --color-background-warning: var(--light-color-background-warning); + --color-warning-text: var(--light-color-warning-text); + --color-icon-background: var(--light-color-icon-background); + --color-accent: var(--light-color-accent); + --color-active-menu-item: var(--light-color-active-menu-item); + --color-text: var(--light-color-text); + --color-text-aside: var(--light-color-text-aside); + --color-link: var(--light-color-link); + + --color-ts-keyword: var(--light-color-ts-keyword); + --color-ts-module: var(--light-color-ts-module); + --color-ts-namespace: var(--light-color-ts-namespace); + --color-ts-enum: var(--light-color-ts-enum); + --color-ts-enum-member: var(--light-color-ts-enum-member); + --color-ts-variable: var(--light-color-ts-variable); + --color-ts-function: var(--light-color-ts-function); + --color-ts-class: var(--light-color-ts-class); + --color-ts-interface: var(--light-color-ts-interface); + --color-ts-constructor: var(--light-color-ts-constructor); + --color-ts-property: var(--light-color-ts-property); + --color-ts-method: var(--light-color-ts-method); + --color-ts-call-signature: var(--light-color-ts-call-signature); + --color-ts-index-signature: var(--light-color-ts-index-signature); + --color-ts-constructor-signature: var( + --light-color-ts-constructor-signature + ); + --color-ts-parameter: var(--light-color-ts-parameter); + --color-ts-type-parameter: var(--light-color-ts-type-parameter); + --color-ts-accessor: var(--light-color-ts-accessor); + --color-ts-get-signature: var(--light-color-ts-get-signature); + --color-ts-set-signature: var(--light-color-ts-set-signature); + --color-ts-type-alias: var(--light-color-ts-type-alias); + + --external-icon: var(--light-external-icon); + --color-scheme: var(--light-color-scheme); +} + +:root[data-theme="dark"] { + --color-background: var(--dark-color-background); + --color-background-secondary: var(--dark-color-background-secondary); + --color-background-warning: var(--dark-color-background-warning); + --color-warning-text: var(--dark-color-warning-text); + --color-icon-background: var(--dark-color-icon-background); + --color-accent: var(--dark-color-accent); + --color-active-menu-item: var(--dark-color-active-menu-item); + --color-text: var(--dark-color-text); + --color-text-aside: var(--dark-color-text-aside); + --color-link: var(--dark-color-link); + + --color-ts-keyword: var(--dark-color-ts-keyword); + --color-ts-module: var(--dark-color-ts-module); + --color-ts-namespace: var(--dark-color-ts-namespace); + --color-ts-enum: var(--dark-color-ts-enum); + --color-ts-enum-member: var(--dark-color-ts-enum-member); + --color-ts-variable: var(--dark-color-ts-variable); + --color-ts-function: var(--dark-color-ts-function); + --color-ts-class: var(--dark-color-ts-class); + --color-ts-interface: var(--dark-color-ts-interface); + --color-ts-constructor: var(--dark-color-ts-constructor); + --color-ts-property: var(--dark-color-ts-property); + --color-ts-method: var(--dark-color-ts-method); + --color-ts-call-signature: var(--dark-color-ts-call-signature); + --color-ts-index-signature: var(--dark-color-ts-index-signature); + --color-ts-constructor-signature: var( + --dark-color-ts-constructor-signature + ); + --color-ts-parameter: var(--dark-color-ts-parameter); + --color-ts-type-parameter: var(--dark-color-ts-type-parameter); + --color-ts-accessor: var(--dark-color-ts-accessor); + --color-ts-get-signature: var(--dark-color-ts-get-signature); + --color-ts-set-signature: var(--dark-color-ts-set-signature); + --color-ts-type-alias: var(--dark-color-ts-type-alias); + + --external-icon: var(--dark-external-icon); + --color-scheme: var(--dark-color-scheme); +} + +.always-visible, +.always-visible .tsd-signatures { + display: inherit !important; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + line-height: 1.2; +} + +h1 > a, +h2 > a, +h3 > a, +h4 > a, +h5 > a, +h6 > a { + text-decoration: none; + color: var(--color-text); +} + +h1 { + font-size: 1.875rem; + margin: 0.67rem 0; +} + +h2 { + font-size: 1.5rem; + margin: 0.83rem 0; +} + +h3 { + font-size: 1.25rem; + margin: 1rem 0; +} + +h4 { + font-size: 1.05rem; + margin: 1.33rem 0; +} + +h5 { + font-size: 1rem; + margin: 1.5rem 0; +} + +h6 { + font-size: 0.875rem; + margin: 2.33rem 0; +} + +.uppercase { + text-transform: uppercase; +} + +dl, +menu, +ol, +ul { + margin: 1em 0; +} + +dd { + margin: 0 0 0 40px; +} + +.container { + max-width: 1700px; + padding: 0 2rem; +} + +/* Footer */ +.tsd-generator { + border-top: 1px solid var(--color-accent); + padding-top: 1rem; + padding-bottom: 1rem; + max-height: 3.5rem; +} + +.tsd-generator > p { + margin-top: 0; + margin-bottom: 0; + padding: 0 1rem; +} + +.container-main { + margin: 0 auto; + /* toolbar, footer, margin */ + min-height: calc(100vh - 41px - 56px - 4rem); +} + +@keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +@keyframes fade-out { + from { + opacity: 1; + visibility: visible; + } + to { + opacity: 0; + } +} +@keyframes fade-in-delayed { + 0% { + opacity: 0; + } + 33% { + opacity: 0; + } + 100% { + opacity: 1; + } +} +@keyframes fade-out-delayed { + 0% { + opacity: 1; + visibility: visible; + } + 66% { + opacity: 0; + } + 100% { + opacity: 0; + } +} +@keyframes pop-in-from-right { + from { + transform: translate(100%, 0); + } + to { + transform: translate(0, 0); + } +} +@keyframes pop-out-to-right { + from { + transform: translate(0, 0); + visibility: visible; + } + to { + transform: translate(100%, 0); + } +} +body { + background: var(--color-background); + font-family: "Segoe UI", sans-serif; + font-size: 16px; + color: var(--color-text); +} + +a { + color: var(--color-link); + text-decoration: none; +} +a:hover { + text-decoration: underline; +} +a.external[target="_blank"] { + background-image: var(--external-icon); + background-position: top 3px right; + background-repeat: no-repeat; + padding-right: 13px; +} + +code, +pre { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + padding: 0.2em; + margin: 0; + font-size: 0.875rem; + border-radius: 0.8em; +} + +pre { + position: relative; + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; + padding: 10px; + border: 1px solid var(--color-accent); +} +pre code { + padding: 0; + font-size: 100%; +} +pre > button { + position: absolute; + top: 10px; + right: 10px; + opacity: 0; + transition: opacity 0.1s; + box-sizing: border-box; +} +pre:hover > button, +pre > button.visible { + opacity: 1; +} + +blockquote { + margin: 1em 0; + padding-left: 1em; + border-left: 4px solid gray; +} + +.tsd-typography { + line-height: 1.333em; +} +.tsd-typography ul { + list-style: square; + padding: 0 0 0 20px; + margin: 0; +} +.tsd-typography .tsd-index-panel h3, +.tsd-index-panel .tsd-typography h3, +.tsd-typography h4, +.tsd-typography h5, +.tsd-typography h6 { + font-size: 1em; +} +.tsd-typography h5, +.tsd-typography h6 { + font-weight: normal; +} +.tsd-typography p, +.tsd-typography ul, +.tsd-typography ol { + margin: 1em 0; +} +.tsd-typography table { + border-collapse: collapse; + border: none; +} +.tsd-typography td, +.tsd-typography th { + padding: 6px 13px; + border: 1px solid var(--color-accent); +} +.tsd-typography thead, +.tsd-typography tr:nth-child(even) { + background-color: var(--color-background-secondary); +} + +.tsd-breadcrumb { + margin: 0; + padding: 0; + color: var(--color-text-aside); +} +.tsd-breadcrumb a { + color: var(--color-text-aside); + text-decoration: none; +} +.tsd-breadcrumb a:hover { + text-decoration: underline; +} +.tsd-breadcrumb li { + display: inline; +} +.tsd-breadcrumb li:after { + content: " / "; +} + +.tsd-comment-tags { + display: flex; + flex-direction: column; +} +dl.tsd-comment-tag-group { + display: flex; + align-items: center; + overflow: hidden; + margin: 0.5em 0; +} +dl.tsd-comment-tag-group dt { + display: flex; + margin-right: 0.5em; + font-size: 0.875em; + font-weight: normal; +} +dl.tsd-comment-tag-group dd { + margin: 0; +} +code.tsd-tag { + padding: 0.25em 0.4em; + border: 0.1em solid var(--color-accent); + margin-right: 0.25em; + font-size: 70%; +} +h1 code.tsd-tag:first-of-type { + margin-left: 0.25em; +} + +dl.tsd-comment-tag-group dd:before, +dl.tsd-comment-tag-group dd:after { + content: " "; +} +dl.tsd-comment-tag-group dd pre, +dl.tsd-comment-tag-group dd:after { + clear: both; +} +dl.tsd-comment-tag-group p { + margin: 0; +} + +.tsd-panel.tsd-comment .lead { + font-size: 1.1em; + line-height: 1.333em; + margin-bottom: 2em; +} +.tsd-panel.tsd-comment .lead:last-child { + margin-bottom: 0; +} + +.tsd-filter-visibility h4 { + font-size: 1rem; + padding-top: 0.75rem; + padding-bottom: 0.5rem; + margin: 0; +} +.tsd-filter-item:not(:last-child) { + margin-bottom: 0.5rem; +} +.tsd-filter-input { + display: flex; + width: fit-content; + width: -moz-fit-content; + align-items: center; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + cursor: pointer; +} +.tsd-filter-input input[type="checkbox"] { + cursor: pointer; + position: absolute; + width: 1.5em; + height: 1.5em; + opacity: 0; +} +.tsd-filter-input input[type="checkbox"]:disabled { + pointer-events: none; +} +.tsd-filter-input svg { + cursor: pointer; + width: 1.5em; + height: 1.5em; + margin-right: 0.5em; + border-radius: 0.33em; + /* Leaving this at full opacity breaks event listeners on Firefox. + Don't remove unless you know what you're doing. */ + opacity: 0.99; +} +.tsd-filter-input input[type="checkbox"]:focus + svg { + transform: scale(0.95); +} +.tsd-filter-input input[type="checkbox"]:focus:not(:focus-visible) + svg { + transform: scale(1); +} +.tsd-checkbox-background { + fill: var(--color-accent); +} +input[type="checkbox"]:checked ~ svg .tsd-checkbox-checkmark { + stroke: var(--color-text); +} +.tsd-filter-input input:disabled ~ svg > .tsd-checkbox-background { + fill: var(--color-background); + stroke: var(--color-accent); + stroke-width: 0.25rem; +} +.tsd-filter-input input:disabled ~ svg > .tsd-checkbox-checkmark { + stroke: var(--color-accent); +} + +.tsd-theme-toggle { + padding-top: 0.75rem; +} +.tsd-theme-toggle > h4 { + display: inline; + vertical-align: middle; + margin-right: 0.75rem; +} + +.tsd-hierarchy { + list-style: square; + margin: 0; +} +.tsd-hierarchy .target { + font-weight: bold; +} + +.tsd-panel-group.tsd-index-group { + margin-bottom: 0; +} +.tsd-index-panel .tsd-index-list { + list-style: none; + line-height: 1.333em; + margin: 0; + padding: 0.25rem 0 0 0; + overflow: hidden; + display: grid; + grid-template-columns: repeat(3, 1fr); + column-gap: 1rem; + grid-template-rows: auto; +} +@media (max-width: 1024px) { + .tsd-index-panel .tsd-index-list { + grid-template-columns: repeat(2, 1fr); + } +} +@media (max-width: 768px) { + .tsd-index-panel .tsd-index-list { + grid-template-columns: repeat(1, 1fr); + } +} +.tsd-index-panel .tsd-index-list li { + -webkit-page-break-inside: avoid; + -moz-page-break-inside: avoid; + -ms-page-break-inside: avoid; + -o-page-break-inside: avoid; + page-break-inside: avoid; +} + +.tsd-flag { + display: inline-block; + padding: 0.25em 0.4em; + border-radius: 4px; + color: var(--color-comment-tag-text); + background-color: var(--color-comment-tag); + text-indent: 0; + font-size: 75%; + line-height: 1; + font-weight: normal; +} + +.tsd-anchor { + position: relative; + top: -100px; +} + +.tsd-member { + position: relative; +} +.tsd-member .tsd-anchor + h3 { + display: flex; + align-items: center; + margin-top: 0; + margin-bottom: 0; + border-bottom: none; +} + +.tsd-navigation.settings { + margin: 1rem 0; +} +.tsd-navigation > a, +.tsd-navigation .tsd-accordion-summary { + width: calc(100% - 0.5rem); +} +.tsd-navigation a, +.tsd-navigation summary > span, +.tsd-page-navigation a { + display: inline-flex; + align-items: center; + padding: 0.25rem; + color: var(--color-text); + text-decoration: none; + box-sizing: border-box; +} +.tsd-navigation a.current, +.tsd-page-navigation a.current { + background: var(--color-active-menu-item); +} +.tsd-navigation a:hover, +.tsd-page-navigation a:hover { + text-decoration: underline; +} +.tsd-navigation ul, +.tsd-page-navigation ul { + margin-top: 0; + margin-bottom: 0; + padding: 0; + list-style: none; +} +.tsd-navigation li, +.tsd-page-navigation li { + padding: 0; + max-width: 100%; +} +.tsd-nested-navigation { + margin-left: 3rem; +} +.tsd-nested-navigation > li > details { + margin-left: -1.5rem; +} +.tsd-small-nested-navigation { + margin-left: 1.5rem; +} +.tsd-small-nested-navigation > li > details { + margin-left: -1.5rem; +} + +.tsd-nested-navigation > li > a, +.tsd-nested-navigation > li > span { + width: calc(100% - 1.75rem - 0.5rem); +} + +.tsd-page-navigation ul { + padding-left: 1.75rem; +} + +#tsd-sidebar-links a { + margin-top: 0; + margin-bottom: 0.5rem; + line-height: 1.25rem; +} +#tsd-sidebar-links a:last-of-type { + margin-bottom: 0; +} + +a.tsd-index-link { + padding: 0.25rem 0 !important; + font-size: 1rem; + line-height: 1.25rem; + display: inline-flex; + align-items: center; + color: var(--color-text); +} +.tsd-accordion-summary { + list-style-type: none; /* hide marker on non-safari */ + outline: none; /* broken on safari, so just hide it */ +} +.tsd-accordion-summary::-webkit-details-marker { + display: none; /* hide marker on safari */ +} +.tsd-accordion-summary, +.tsd-accordion-summary a { + user-select: none; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + + cursor: pointer; +} +.tsd-accordion-summary a { + width: calc(100% - 1.5rem); +} +.tsd-accordion-summary > * { + margin-top: 0; + margin-bottom: 0; + padding-top: 0; + padding-bottom: 0; +} +.tsd-index-accordion .tsd-accordion-summary > svg { + margin-left: 0.25rem; +} +.tsd-index-content > :not(:first-child) { + margin-top: 0.75rem; +} +.tsd-index-heading { + margin-top: 1.5rem; + margin-bottom: 0.75rem; +} + +.tsd-kind-icon { + margin-right: 0.5rem; + width: 1.25rem; + height: 1.25rem; + min-width: 1.25rem; + min-height: 1.25rem; +} +.tsd-kind-icon path { + transform-origin: center; + transform: scale(1.1); +} +.tsd-signature > .tsd-kind-icon { + margin-right: 0.8rem; +} + +.tsd-panel { + margin-bottom: 2.5rem; +} +.tsd-panel.tsd-member { + margin-bottom: 4rem; +} +.tsd-panel:empty { + display: none; +} +.tsd-panel > h1, +.tsd-panel > h2, +.tsd-panel > h3 { + margin: 1.5rem -1.5rem 0.75rem -1.5rem; + padding: 0 1.5rem 0.75rem 1.5rem; +} +.tsd-panel > h1.tsd-before-signature, +.tsd-panel > h2.tsd-before-signature, +.tsd-panel > h3.tsd-before-signature { + margin-bottom: 0; + border-bottom: none; +} + +.tsd-panel-group { + margin: 4rem 0; +} +.tsd-panel-group.tsd-index-group { + margin: 2rem 0; +} +.tsd-panel-group.tsd-index-group details { + margin: 2rem 0; +} + +#tsd-search { + transition: background-color 0.2s; +} +#tsd-search .title { + position: relative; + z-index: 2; +} +#tsd-search .field { + position: absolute; + left: 0; + top: 0; + right: 2.5rem; + height: 100%; +} +#tsd-search .field input { + box-sizing: border-box; + position: relative; + top: -50px; + z-index: 1; + width: 100%; + padding: 0 10px; + opacity: 0; + outline: 0; + border: 0; + background: transparent; + color: var(--color-text); +} +#tsd-search .field label { + position: absolute; + overflow: hidden; + right: -40px; +} +#tsd-search .field input, +#tsd-search .title, +#tsd-toolbar-links a { + transition: opacity 0.2s; +} +#tsd-search .results { + position: absolute; + visibility: hidden; + top: 40px; + width: 100%; + margin: 0; + padding: 0; + list-style: none; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); +} +#tsd-search .results li { + background-color: var(--color-background); + line-height: initial; + padding: 4px; +} +#tsd-search .results li:nth-child(even) { + background-color: var(--color-background-secondary); +} +#tsd-search .results li.state { + display: none; +} +#tsd-search .results li.current:not(.no-results), +#tsd-search .results li:hover:not(.no-results) { + background-color: var(--color-accent); +} +#tsd-search .results a { + display: flex; + align-items: center; + padding: 0.25rem; + box-sizing: border-box; +} +#tsd-search .results a:before { + top: 10px; +} +#tsd-search .results span.parent { + color: var(--color-text-aside); + font-weight: normal; +} +#tsd-search.has-focus { + background-color: var(--color-accent); +} +#tsd-search.has-focus .field input { + top: 0; + opacity: 1; +} +#tsd-search.has-focus .title, +#tsd-search.has-focus #tsd-toolbar-links a { + z-index: 0; + opacity: 0; +} +#tsd-search.has-focus .results { + visibility: visible; +} +#tsd-search.loading .results li.state.loading { + display: block; +} +#tsd-search.failure .results li.state.failure { + display: block; +} + +#tsd-toolbar-links { + position: absolute; + top: 0; + right: 2rem; + height: 100%; + display: flex; + align-items: center; + justify-content: flex-end; +} +#tsd-toolbar-links a { + margin-left: 1.5rem; +} +#tsd-toolbar-links a:hover { + text-decoration: underline; +} + +.tsd-signature { + margin: 0 0 1rem 0; + padding: 1rem 0.5rem; + border: 1px solid var(--color-accent); + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + font-size: 14px; + overflow-x: auto; +} + +.tsd-signature-keyword { + color: var(--color-ts-keyword); + font-weight: normal; +} + +.tsd-signature-symbol { + color: var(--color-text-aside); + font-weight: normal; +} + +.tsd-signature-type { + font-style: italic; + font-weight: normal; +} + +.tsd-signatures { + padding: 0; + margin: 0 0 1em 0; + list-style-type: none; +} +.tsd-signatures .tsd-signature { + margin: 0; + border-color: var(--color-accent); + border-width: 1px 0; + transition: background-color 0.1s; +} +.tsd-description .tsd-signatures .tsd-signature { + border-width: 1px; +} + +ul.tsd-parameter-list, +ul.tsd-type-parameter-list { + list-style: square; + margin: 0; + padding-left: 20px; +} +ul.tsd-parameter-list > li.tsd-parameter-signature, +ul.tsd-type-parameter-list > li.tsd-parameter-signature { + list-style: none; + margin-left: -20px; +} +ul.tsd-parameter-list h5, +ul.tsd-type-parameter-list h5 { + font-size: 16px; + margin: 1em 0 0.5em 0; +} +.tsd-sources { + margin-top: 1rem; + font-size: 0.875em; +} +.tsd-sources a { + color: var(--color-text-aside); + text-decoration: underline; +} +.tsd-sources ul { + list-style: none; + padding: 0; +} + +.tsd-page-toolbar { + position: sticky; + z-index: 1; + top: 0; + left: 0; + width: 100%; + color: var(--color-text); + background: var(--color-background-secondary); + border-bottom: 1px var(--color-accent) solid; + transition: transform 0.3s ease-in-out; +} +.tsd-page-toolbar a { + color: var(--color-text); + text-decoration: none; +} +.tsd-page-toolbar a.title { + font-weight: bold; +} +.tsd-page-toolbar a.title:hover { + text-decoration: underline; +} +.tsd-page-toolbar .tsd-toolbar-contents { + display: flex; + justify-content: space-between; + height: 2.5rem; + margin: 0 auto; +} +.tsd-page-toolbar .table-cell { + position: relative; + white-space: nowrap; + line-height: 40px; +} +.tsd-page-toolbar .table-cell:first-child { + width: 100%; +} +.tsd-page-toolbar .tsd-toolbar-icon { + box-sizing: border-box; + line-height: 0; + padding: 12px 0; +} + +.tsd-widget { + display: inline-block; + overflow: hidden; + opacity: 0.8; + height: 40px; + transition: + opacity 0.1s, + background-color 0.2s; + vertical-align: bottom; + cursor: pointer; +} +.tsd-widget:hover { + opacity: 0.9; +} +.tsd-widget.active { + opacity: 1; + background-color: var(--color-accent); +} +.tsd-widget.no-caption { + width: 40px; +} +.tsd-widget.no-caption:before { + margin: 0; +} + +.tsd-widget.options, +.tsd-widget.menu { + display: none; +} +input[type="checkbox"] + .tsd-widget:before { + background-position: -120px 0; +} +input[type="checkbox"]:checked + .tsd-widget:before { + background-position: -160px 0; +} + +img { + max-width: 100%; +} + +.tsd-anchor-icon { + display: inline-flex; + align-items: center; + margin-left: 0.5rem; + vertical-align: middle; + color: var(--color-text); +} + +.tsd-anchor-icon svg { + width: 1em; + height: 1em; + visibility: hidden; +} + +.tsd-anchor-link:hover > .tsd-anchor-icon svg { + visibility: visible; +} + +.deprecated { + text-decoration: line-through !important; +} + +.warning { + padding: 1rem; + color: var(--color-warning-text); + background: var(--color-background-warning); +} + +.tsd-kind-project { + color: var(--color-ts-project); +} +.tsd-kind-module { + color: var(--color-ts-module); +} +.tsd-kind-namespace { + color: var(--color-ts-namespace); +} +.tsd-kind-enum { + color: var(--color-ts-enum); +} +.tsd-kind-enum-member { + color: var(--color-ts-enum-member); +} +.tsd-kind-variable { + color: var(--color-ts-variable); +} +.tsd-kind-function { + color: var(--color-ts-function); +} +.tsd-kind-class { + color: var(--color-ts-class); +} +.tsd-kind-interface { + color: var(--color-ts-interface); +} +.tsd-kind-constructor { + color: var(--color-ts-constructor); +} +.tsd-kind-property { + color: var(--color-ts-property); +} +.tsd-kind-method { + color: var(--color-ts-method); +} +.tsd-kind-call-signature { + color: var(--color-ts-call-signature); +} +.tsd-kind-index-signature { + color: var(--color-ts-index-signature); +} +.tsd-kind-constructor-signature { + color: var(--color-ts-constructor-signature); +} +.tsd-kind-parameter { + color: var(--color-ts-parameter); +} +.tsd-kind-type-literal { + color: var(--color-ts-type-literal); +} +.tsd-kind-type-parameter { + color: var(--color-ts-type-parameter); +} +.tsd-kind-accessor { + color: var(--color-ts-accessor); +} +.tsd-kind-get-signature { + color: var(--color-ts-get-signature); +} +.tsd-kind-set-signature { + color: var(--color-ts-set-signature); +} +.tsd-kind-type-alias { + color: var(--color-ts-type-alias); +} + +/* if we have a kind icon, don't color the text by kind */ +.tsd-kind-icon ~ span { + color: var(--color-text); +} + +* { + scrollbar-width: thin; + scrollbar-color: var(--color-accent) var(--color-icon-background); +} + +*::-webkit-scrollbar { + width: 0.75rem; +} + +*::-webkit-scrollbar-track { + background: var(--color-icon-background); +} + +*::-webkit-scrollbar-thumb { + background-color: var(--color-accent); + border-radius: 999rem; + border: 0.25rem solid var(--color-icon-background); +} + +/* mobile */ +@media (max-width: 769px) { + .tsd-widget.options, + .tsd-widget.menu { + display: inline-block; + } + + .container-main { + display: flex; + } + html .col-content { + float: none; + max-width: 100%; + width: 100%; + } + html .col-sidebar { + position: fixed !important; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + z-index: 1024; + top: 0 !important; + bottom: 0 !important; + left: auto !important; + right: 0 !important; + padding: 1.5rem 1.5rem 0 0; + width: 75vw; + visibility: hidden; + background-color: var(--color-background); + transform: translate(100%, 0); + } + html .col-sidebar > *:last-child { + padding-bottom: 20px; + } + html .overlay { + content: ""; + display: block; + position: fixed; + z-index: 1023; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.75); + visibility: hidden; + } + + .to-has-menu .overlay { + animation: fade-in 0.4s; + } + + .to-has-menu .col-sidebar { + animation: pop-in-from-right 0.4s; + } + + .from-has-menu .overlay { + animation: fade-out 0.4s; + } + + .from-has-menu .col-sidebar { + animation: pop-out-to-right 0.4s; + } + + .has-menu body { + overflow: hidden; + } + .has-menu .overlay { + visibility: visible; + } + .has-menu .col-sidebar { + visibility: visible; + transform: translate(0, 0); + display: flex; + flex-direction: column; + gap: 1.5rem; + max-height: 100vh; + padding: 1rem 2rem; + } + .has-menu .tsd-navigation { + max-height: 100%; + } +} + +/* one sidebar */ +@media (min-width: 770px) { + .container-main { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); + grid-template-areas: "sidebar content"; + margin: 2rem auto; + } + + .col-sidebar { + grid-area: sidebar; + } + .col-content { + grid-area: content; + padding: 0 1rem; + } +} +@media (min-width: 770px) and (max-width: 1399px) { + .col-sidebar { + max-height: calc(100vh - 2rem - 42px); + overflow: auto; + position: sticky; + top: 42px; + padding-top: 1rem; + } + .site-menu { + margin-top: 1rem; + } +} + +/* two sidebars */ +@media (min-width: 1200px) { + .container-main { + grid-template-columns: minmax(0, 1fr) minmax(0, 2.5fr) minmax(0, 20rem); + grid-template-areas: "sidebar content toc"; + } + + .col-sidebar { + display: contents; + } + + .page-menu { + grid-area: toc; + padding-left: 1rem; + } + .site-menu { + grid-area: sidebar; + } + + .site-menu { + margin-top: 1rem 0; + } + + .page-menu, + .site-menu { + max-height: calc(100vh - 2rem - 42px); + overflow: auto; + position: sticky; + top: 42px; + } +} diff --git a/docs/functions/configValue.html b/docs/functions/configValue.html new file mode 100644 index 0000000..2007871 --- /dev/null +++ b/docs/functions/configValue.html @@ -0,0 +1,326 @@ +configValue | @wymp/config-simple

Function configValue

  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]

    Returns undefined | string

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • defaultOrRequired: DefOrReq<string>

    Returns string

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • validator: ValidatorArg<string>

    Returns undefined | string

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • required: typeof REQUIRED
    • validator: ValidatorArg<string>

    Returns string

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • defaultValue: string
    • validator: ValidatorArg<string>

    Returns string

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • t: "str"

    Returns undefined | string

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • t: "str"
    • defaultOrRequired: DefOrReq<string>

    Returns string

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • t: "str"
    • validator: ValidatorArg<string>

    Returns undefined | string

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • t: "str"
    • required: typeof REQUIRED
    • validator: ValidatorArg<string>

    Returns string

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • t: "str"
    • defaultValue: string
    • validator: ValidatorArg<string>

    Returns string

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • t: "num"

    Returns undefined | number | `::ERROR::${string}`

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • t: "num"
    • defaultOrRequired: DefOrReq<number>

    Returns number | `::ERROR::${string}`

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • t: "num"
    • validator: ValidatorArg<number>

    Returns number | `::ERROR::${string}`

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • t: "num"
    • required: typeof REQUIRED
    • validator: ValidatorArg<number>

    Returns number | `::ERROR::${string}`

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • t: "num"
    • defaultValue: number
    • validator: ValidatorArg<number>

    Returns number | `::ERROR::${string}`

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • t: "bool"

    Returns undefined | boolean | `::ERROR::${string}`

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • t: "bool"
    • defaultOrRequired: DefOrReq<boolean>

    Returns boolean | `::ERROR::${string}`

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • t: "bool"
    • validator: ValidatorArg<boolean>

    Returns undefined | boolean | `::ERROR::${string}`

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • t: "bool"
    • required: typeof REQUIRED
    • validator: ValidatorArg<boolean>

    Returns boolean | `::ERROR::${string}`

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Parameters

    • envVarName: string | string[]
    • t: "bool"
    • defaultValue: boolean
    • validator: ValidatorArg<boolean>

    Returns boolean | `::ERROR::${string}`

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Type Parameters

    • T

    Parameters

    Returns undefined | `::ERROR::${string}` | T

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Type Parameters

    • T

    Parameters

    • envVarName: string | string[]
    • t: Transformer<T>
    • defaultOrRequired: DefOrReq<T>

    Returns `::ERROR::${string}` | T

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Type Parameters

    • T

    Parameters

    • envVarName: string | string[]
    • t: Transformer<T>
    • validator: ValidatorArg<T>

    Returns undefined | `::ERROR::${string}` | T

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Type Parameters

    • T

    Parameters

    • envVarName: string | string[]
    • t: Transformer<T>
    • required: typeof REQUIRED
    • validator: ValidatorArg<T>

    Returns `::ERROR::${string}` | T

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +
  • Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can +also pass a custom transformer function (see TransformerFunc) as well a validator function or array of +validator functions. (Import the Validators library from +this library to explore the bundled validators or write your own by implementing ValidatorFunc.)

    +

    The most common usage of this will be the following (see more examples below for advanced usage in context):

    +
    // Get a possibly-undefined string from the SOME_VAR environment variable
    configValue('SOME_VAR');

    // Get a string with a default value of 'some default' from the SOME_VAR environment variable
    configValue('SOME_VAR', 'some default');

    // Get a possibly-undefined number from the SOME_VAR environment variable
    configValue('SOME_VAR', 'num');

    // Get a number with a default value of 5 from SOME_VAR
    configValue('SOME_VAR', 'num', 5);

    // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this
    // library)
    configValue('SOME_VAR', REQUIRED);

    // Get a required number from SOME_VAR with no default
    configValue('SOME_VAR', 'num', REQUIRED); +
    +

    Type Parameters

    • T

    Parameters

    • envVarName: string | string[]
    • t: Transformer<T>
    • defaultValue: T
    • validator: ValidatorArg<T>

    Returns `::ERROR::${string}` | T

    Example

    Following is a more real-world example of how this might all be used in context

    +
    const configDef = {
    port: configValue('PORT', 'num', 3000),
    hiPort: configValue('PORT', 'num', 30_000, hiport),
    host: configValue('HOST', 'str', 'http://localhost', hostValidator),
    db: {
    url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'),
    migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false),
    },
    signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]),
    secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required),
    }

    export const config = validate(configDef); +
    +

    NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require +either one argument configuration or the other, which gets messy in a function like this. Since this function is +itself properly overloaded, it's safe to use any-casts to solve this here.

    +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/functions/validate.html b/docs/functions/validate.html new file mode 100644 index 0000000..c35fd7a --- /dev/null +++ b/docs/functions/validate.html @@ -0,0 +1,10 @@ +validate | @wymp/config-simple
  • Validate a configuration object and return a frozen copy.

    +

    Type Parameters

    • T

    Parameters

    • obj: T

    Returns CleanFrozenConfig<T>

    Example

    export const config = validate(configDef);
    export const dirtyConfig = validate(configDef, 'dont-throw'); +
    +

    In the first form, this function will throw an error if the configuration is invalid. In the second form, it will +return a ConfigResult object that can be inspected for errors.

    +

    NOTE: THE 'ERROR' SIDE OF THE RESPONSE FROM THE SECOND FORM IS DANGEROUS. We want the option to not throw on config +errors, but we also don't want to have to use type-guards to handle bad config throughout our entire codebase. This +type is a dangerous compromise that allows us to blindly use values as if they were valid, while knowing that they +may not be.

    +
  • Type Parameters

    • T

    Parameters

    • obj: T
    • behavior: "dont-throw"

    Returns ConfigResult<T>

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..543e8fb --- /dev/null +++ b/docs/index.html @@ -0,0 +1,26 @@ +@wymp/config-simple

@wymp/config-simple

Config Simple

This library presents a simple configuration system that works for both front-end and back-end services.

+

Goals:

+
    +
  1. We want our config definitions to be simple, readable and well-typed.
  2. +
  3. We want to consume config via a frozen config constant, like config.my.value, and we want the types to be +readable and accurate;
  4. +
  5. We do not need/want to update or alter config at runtime (config updates should require an env var update and a +reboot of the container);
  6. +
  7. We want to coerce certain config strings into other types in the app (e.g., "false" → false);
  8. +
  9. We want to allow certain configs to be undefined but require values for others;
  10. +
  11. We want to support certain secret stores (e.g., kubernetes) that provide secrets via files mounted into the +container.
  12. +
  13. We want our system to work in both node and browser.
  14. +
+
+

NOTE: See also Config Node (pkg) +for an alternative config system for node.

+
+

Usage

import { configValue, REQUIRED, validate } from "@wymp/config-simple";

// The `configValue` function returns any of the following:
//
// * the type indicated ("num" => number, "str" => string, "bool" => boolean, etc.)
// * undefined (if allowed)
// * An error string starting with ::ERROR::
//
// We use `configValue` to define/coerce our values

const configDef = {
// required number defaulting to 1234 coming from var/file PORT
port: configValue("PORT", "num", 1234),
db: {
// non-required string (default undefined) coming from DATABASE_URL
url: configValue("DATABASE_URL", "str"),
// hard-coded; cannot change
type: "postgres" as const,
port: 5432,
},
services: {
// non-required string
myAuthServiceUrl: configValue("AUTH_SERVICE_URL", "str"),
// non-required boolean defaulting to false
stubAuthService: configValue("STUB_AUTH_SERVICE", "bool", false),
// required number with no default
somethingNonOptional: configValue("SOME_THING", "num", REQUIRED),
}
}

// The type of configDef is now
// {
// port: number | `::ERROR::${string}`;
// db: {
// url: string | undefined;
// type: "postgres";
// port: number;
// };
// services: {
// myAuthServiceUrl: string | undefined;
// stubAuthService: boolean | `::ERROR::${string}`;
// somethingNonOptional: number | `::ERROR::${string}`;
// }
// }
//
// Now we run the config def through the validator to get the final config
//
// This throws an error if there are any `::ERROR::` values; otherwise, returns a frozen object with all types excluding
// the `::ERROR::${string}` template type.

export const config = validate(configDef);

// Alternatively, if we don't want to throw, we can do this:
const result = validate(configDef, "dont-throw");
if (result.t === "error") {
console.error(`CONFIG ERRORS:\n\n * ${result.errors.join("\n * ")}`);
}

// NOTE: This config is typed as "clean" (without errors), but it may still have error strings. This can result in
// runtime issues (for example, trying to do math on a value that's supposed to be a number but really contains an error
// string). Use at your own risk.
export dirtyConfig = result.value; +
+

Usage With Weenie

This library is typically used to create an in-place, bespoke config object. Therefore, it's already sort of +Weenie-compatible out of the box.

+

Assuming the example above is in a file like src/config.ts, you would include it in your DI container like so:

+
// src/main.ts
import { config } from "./config";
import * as Weenie from "@wymp/weenie-framework";

const deps = Weenie.Weenie({ config })
.and(Weenie.logger)
.and(Weenie.mysql)
.done(d => d); +
+

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/interfaces/ConfigValueFunc.html b/docs/interfaces/ConfigValueFunc.html new file mode 100644 index 0000000..9fc64f2 --- /dev/null +++ b/docs/interfaces/ConfigValueFunc.html @@ -0,0 +1,2 @@ +ConfigValueFunc | @wymp/config-simple

Interface ConfigValueFunc

interface ConfigValueFunc {
    (envVarName): undefined | string;
    (envVarName, defaultOrRequired): string;
    (envVarName, validator): undefined | string;
    (envVarName, required, validator): string;
    (envVarName, defaultValue, validator): string;
    (envVarName, t): undefined | string;
    (envVarName, t, defaultOrRequired): string;
    (envVarName, t, validator): undefined | string;
    (envVarName, t, required, validator): string;
    (envVarName, t, defaultValue, validator): string;
    (envVarName, t): undefined | number | `::ERROR::${string}`;
    (envVarName, t, defaultOrRequired): number | `::ERROR::${string}`;
    (envVarName, t, validator): number | `::ERROR::${string}`;
    (envVarName, t, required, validator): number | `::ERROR::${string}`;
    (envVarName, t, defaultValue, validator): number | `::ERROR::${string}`;
    (envVarName, t): undefined | boolean | `::ERROR::${string}`;
    (envVarName, t, defaultOrRequired): boolean | `::ERROR::${string}`;
    (envVarName, t, validator): undefined | boolean | `::ERROR::${string}`;
    (envVarName, t, required, validator): boolean | `::ERROR::${string}`;
    (envVarName, t, defaultValue, validator): boolean | `::ERROR::${string}`;
    <T>(envVarName, t): undefined | `::ERROR::${string}` | T;
    <T>(envVarName, t, defaultOrRequired): `::ERROR::${string}` | T;
    <T>(envVarName, t, validator): undefined | `::ERROR::${string}` | T;
    <T>(envVarName, t, required, validator): `::ERROR::${string}` | T;
    <T>(envVarName, t, defaultValue, validator): `::ERROR::${string}` | T;
}
  • Parameters

    • envVarName: string | string[]

    Returns undefined | string

  • Parameters

    • envVarName: string | string[]
    • defaultOrRequired: DefOrReq<string>

    Returns string

  • Parameters

    • envVarName: string | string[]
    • validator: ValidatorArg<string>

    Returns undefined | string

  • Parameters

    • envVarName: string | string[]
    • required: typeof REQUIRED
    • validator: ValidatorArg<string>

    Returns string

  • Parameters

    • envVarName: string | string[]
    • defaultValue: string
    • validator: ValidatorArg<string>

    Returns string

  • Parameters

    • envVarName: string | string[]
    • t: "str"

    Returns undefined | string

  • Parameters

    • envVarName: string | string[]
    • t: "str"
    • defaultOrRequired: DefOrReq<string>

    Returns string

  • Parameters

    • envVarName: string | string[]
    • t: "str"
    • validator: ValidatorArg<string>

    Returns undefined | string

  • Parameters

    • envVarName: string | string[]
    • t: "str"
    • required: typeof REQUIRED
    • validator: ValidatorArg<string>

    Returns string

  • Parameters

    • envVarName: string | string[]
    • t: "str"
    • defaultValue: string
    • validator: ValidatorArg<string>

    Returns string

  • Parameters

    • envVarName: string | string[]
    • t: "num"

    Returns undefined | number | `::ERROR::${string}`

  • Parameters

    • envVarName: string | string[]
    • t: "num"
    • defaultOrRequired: DefOrReq<number>

    Returns number | `::ERROR::${string}`

  • Parameters

    • envVarName: string | string[]
    • t: "num"
    • validator: ValidatorArg<number>

    Returns number | `::ERROR::${string}`

  • Parameters

    • envVarName: string | string[]
    • t: "num"
    • required: typeof REQUIRED
    • validator: ValidatorArg<number>

    Returns number | `::ERROR::${string}`

  • Parameters

    • envVarName: string | string[]
    • t: "num"
    • defaultValue: number
    • validator: ValidatorArg<number>

    Returns number | `::ERROR::${string}`

  • Parameters

    • envVarName: string | string[]
    • t: "bool"

    Returns undefined | boolean | `::ERROR::${string}`

  • Parameters

    • envVarName: string | string[]
    • t: "bool"
    • defaultOrRequired: DefOrReq<boolean>

    Returns boolean | `::ERROR::${string}`

  • Parameters

    • envVarName: string | string[]
    • t: "bool"
    • validator: ValidatorArg<boolean>

    Returns undefined | boolean | `::ERROR::${string}`

  • Parameters

    • envVarName: string | string[]
    • t: "bool"
    • required: typeof REQUIRED
    • validator: ValidatorArg<boolean>

    Returns boolean | `::ERROR::${string}`

  • Parameters

    • envVarName: string | string[]
    • t: "bool"
    • defaultValue: boolean
    • validator: ValidatorArg<boolean>

    Returns boolean | `::ERROR::${string}`

  • Type Parameters

    • T

    Parameters

    Returns undefined | `::ERROR::${string}` | T

  • Type Parameters

    • T

    Parameters

    • envVarName: string | string[]
    • t: Transformer<T>
    • defaultOrRequired: DefOrReq<T>

    Returns `::ERROR::${string}` | T

  • Type Parameters

    • T

    Parameters

    • envVarName: string | string[]
    • t: Transformer<T>
    • validator: ValidatorArg<T>

    Returns undefined | `::ERROR::${string}` | T

  • Type Parameters

    • T

    Parameters

    • envVarName: string | string[]
    • t: Transformer<T>
    • required: typeof REQUIRED
    • validator: ValidatorArg<T>

    Returns `::ERROR::${string}` | T

  • Type Parameters

    • T

    Parameters

    • envVarName: string | string[]
    • t: Transformer<T>
    • defaultValue: T
    • validator: ValidatorArg<T>

    Returns `::ERROR::${string}` | T

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/modules.html b/docs/modules.html new file mode 100644 index 0000000..9aaf92c --- /dev/null +++ b/docs/modules.html @@ -0,0 +1,11 @@ +@wymp/config-simple

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/ConfigError.html b/docs/types/ConfigError.html new file mode 100644 index 0000000..b79ef2d --- /dev/null +++ b/docs/types/ConfigError.html @@ -0,0 +1,2 @@ +ConfigError | @wymp/config-simple

Type alias ConfigError

ConfigError: `::ERROR::${string}`

A type indicating an error. You can return this from a custom transformer function as well

+

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/Transformer.html b/docs/types/Transformer.html new file mode 100644 index 0000000..32cb8fe --- /dev/null +++ b/docs/types/Transformer.html @@ -0,0 +1,2 @@ +Transformer | @wymp/config-simple

Type alias Transformer<T>

Transformer<T>: {
    transform: TransformerFunc<T>;
}

A transformer. See TransformerFunc for more details.

+

Type Parameters

  • T

Type declaration

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/TransformerFunc.html b/docs/types/TransformerFunc.html new file mode 100644 index 0000000..a556f09 --- /dev/null +++ b/docs/types/TransformerFunc.html @@ -0,0 +1,5 @@ +TransformerFunc | @wymp/config-simple

Type alias TransformerFunc<T>

TransformerFunc<T>: ((val, varname) => T | undefined | ConfigError)

Type Parameters

  • T

Type declaration

    • (val, varname): T | undefined | ConfigError
    • Any function conforming to this interface may be passed to the configValue function to transform the value of a given +config key.

      +

      Parameters

      • val: string | undefined
      • varname: string

      Returns T | undefined | ConfigError

      Example

      const myType = ((val: string | undefined, varname: string) => {
      if (!val) {
      if (def === REQUIRED) {
      return `::ERROR::${varname}::This value is required`;
      }
      return def;
      }
      try {
      // Normally you would validate but for testing we're skipping that and just returning whatever
      return JSON.parse(val) as MyType;
      } catch (e: any) {
      return `::ERROR::${varname}::Invalid JSON string passed: ${e.message}`;
      }
      }) as TransformerFunction<MyType>;

      // ...

      const configDef = {
      myType: configValue('MY_TYPE', myType, REQUIRED),
      // ...
      } +
      +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/types/ValidatorFunc.html b/docs/types/ValidatorFunc.html new file mode 100644 index 0000000..94d5694 --- /dev/null +++ b/docs/types/ValidatorFunc.html @@ -0,0 +1,3 @@ +ValidatorFunc | @wymp/config-simple

Type alias ValidatorFunc<T>

ValidatorFunc<T>: ((val) => void | string | string[])

Type Parameters

  • T

Type declaration

    • (val): void | string | string[]
    • To implement a custom validator, just implement a function that takes the value to be validated and returns either +undefined or an error of error strings.

      +

      Parameters

      • val: T | undefined

      Returns void | string | string[]

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/variables/REQUIRED.html b/docs/variables/REQUIRED.html new file mode 100644 index 0000000..cada3db --- /dev/null +++ b/docs/variables/REQUIRED.html @@ -0,0 +1,2 @@ +REQUIRED | @wymp/config-simple

Variable REQUIREDConst

REQUIRED: typeof REQUIRED = ...

A special symbol indicating that the given value is required

+

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/variables/Transformers.html b/docs/variables/Transformers.html new file mode 100644 index 0000000..8dc6bb3 --- /dev/null +++ b/docs/variables/Transformers.html @@ -0,0 +1,4 @@ +Transformers | @wymp/config-simple

Variable TransformersConst

Transformers: {
    bool: Transformer<boolean>;
    csvToArray: Transformer<string[]>;
    num: Transformer<number>;
    str: Transformer<string>;
} = ...

A library of transformer functions that are bundled with this library. Note: It is generally easier to use the string +aliases to access these functions (e.g., configValue('MY_VAR', 'num') instead of importing and using them +directly.)

+

Type declaration

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/variables/Validators.html b/docs/variables/Validators.html new file mode 100644 index 0000000..fb34895 --- /dev/null +++ b/docs/variables/Validators.html @@ -0,0 +1,6 @@ +Validators | @wymp/config-simple

Variable ValidatorsConst

Validators: {
    exactLen: ((len) => ValidatorFunc<string>);
    httpHost: ValidatorFunc<string>;
    match: ((pattern) => ValidatorFunc<string>);
    maxLen: ((max) => ValidatorFunc<string>);
    minLen: ((min) => ValidatorFunc<string>);
    oneOf: ((values) => ValidatorFunc<string>);
    requiredForEnvs: (<T>(env, requiredEnvs) => ValidatorFunc<T>);
    requiredIf: (<T>(condition) => ValidatorFunc<T>);
} = ...

A library of bundled validators that you may use in your config definitions. You can also write your own validators +by implementing the ValidatorFunc interface.

+

Type declaration

Generated using TypeDoc

\ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..f6271ee --- /dev/null +++ b/package.json @@ -0,0 +1,60 @@ +{ + "name": "@wymp/config-simple", + "version": "1.0.0", + "description": "A simple, dotenv-based configuration system that works in node and browser", + "main": "dist/index.js", + "typings": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "clean": "rm -Rf ./dist || true; rm -Rf ./docs || true", + "docs:gen": "typedoc src/index.ts --sort visibility --sort static-first --sort alphabetical", + "docs:serve": "pnpx http-server ./docs", + "format": "pnpm prettier:fix && pnpm lint:fix", + "lint": "eslint --cache --cache-location ./node_modules/.cache/eslintcache src tests", + "lint:fix": "pnpm lint --fix", + "prettier": "prettier src tests --check", + "prettier:fix": "pnpm prettier --write", + "test": "pnpm typecheck && pnpm prettier && pnpm lint && pnpm test:jest", + "test:jest": "jest --verbose", + "typecheck": "tsc --noEmit", + "prepublishOnly": "pnpm build && pnpm docs:gen" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wymp/config-simple.git" + }, + "keywords": [ + "config" + ], + "author": "Kael Shipman", + "license": "ISC", + "bugs": { + "url": "https://github.com/wymp/config-simple/issues" + }, + "homepage": "https://github.com/wymp/config-simple#readme", + "devDependencies": { + "@types/jest": "^29.5.7", + "@types/node": "^20.8.10", + "@typescript-eslint/eslint-plugin": "^6.9.1", + "@typescript-eslint/parser": "^6.9.1", + "eslint": "^8.52.0", + "eslint-config-prettier": "^9.0.0", + "jest": "^29.7.0", + "prettier": "^3.1.1", + "ts-jest": "^29.1.1", + "typedoc": "^0.25.4", + "typescript": "^5.2.2" + }, + "prettier": { + "printWidth": 120, + "trailingComma": "es5" + }, + "jest": { + "roots": [ + "/tests" + ], + "transform": { + "^.+\\.tsx?$": "ts-jest" + } + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..7b273dc --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,3063 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +devDependencies: + '@types/jest': + specifier: ^29.5.7 + version: 29.5.11 + '@types/node': + specifier: ^20.8.10 + version: 20.10.5 + '@typescript-eslint/eslint-plugin': + specifier: ^6.9.1 + version: 6.16.0(@typescript-eslint/parser@6.16.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': + specifier: ^6.9.1 + version: 6.16.0(eslint@8.56.0)(typescript@5.3.3) + eslint: + specifier: ^8.52.0 + version: 8.56.0 + eslint-config-prettier: + specifier: ^9.0.0 + version: 9.1.0(eslint@8.56.0) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@20.10.5) + prettier: + specifier: ^3.1.1 + version: 3.1.1 + ts-jest: + specifier: ^29.1.1 + version: 29.1.1(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.3.3) + typedoc: + specifier: ^0.25.4 + version: 0.25.4(typescript@5.3.3) + typescript: + specifier: ^5.2.2 + version: 5.3.3 + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@babel/code-frame@7.22.13: + resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.22.20 + chalk: 2.4.2 + dev: true + + /@babel/compat-data@7.23.2: + resolution: {integrity: sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core@7.23.2: + resolution: {integrity: sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.23.0 + '@babel/helper-compilation-targets': 7.22.15 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) + '@babel/helpers': 7.23.2 + '@babel/parser': 7.23.0 + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.2 + '@babel/types': 7.23.0 + convert-source-map: 2.0.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator@7.23.0: + resolution: {integrity: sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.20 + jsesc: 2.5.2 + dev: true + + /@babel/helper-compilation-targets@7.22.15: + resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.23.2 + '@babel/helper-validator-option': 7.22.15 + browserslist: 4.22.1 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: true + + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-module-transforms@7.23.0(@babel/core@7.23.2): + resolution: {integrity: sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: true + + /@babel/helper-plugin-utils@7.22.5: + resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/helper-string-parser@7.22.5: + resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-option@7.22.15: + resolution: {integrity: sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers@7.23.2: + resolution: {integrity: sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.22.15 + '@babel/traverse': 7.23.2 + '@babel/types': 7.23.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight@7.22.20: + resolution: {integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/parser@7.23.0: + resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.2): + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.2): + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.2): + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.2): + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.2): + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.23.2): + resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.2): + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.2): + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.2): + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.2): + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.2): + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.2): + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.2): + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.23.2): + resolution: {integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.2 + '@babel/helper-plugin-utils': 7.22.5 + dev: true + + /@babel/template@7.22.15: + resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.13 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + dev: true + + /@babel/traverse@7.23.2: + resolution: {integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.23.0 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types@7.23.0: + resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: true + + /@bcoe/v8-coverage@0.2.3: + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.56.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.56.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.4: + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.0 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.56.0: + resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@humanwhocodes/config-array@0.11.13: + resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 2.0.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@2.0.1: + resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} + dev: true + + /@istanbuljs/load-nyc-config@1.1.0: + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + dev: true + + /@istanbuljs/schema@0.1.3: + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + dev: true + + /@jest/console@29.7.0: + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.10.5 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + dev: true + + /@jest/core@29.7.0: + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.10.5 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@20.10.5) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /@jest/environment@29.7.0: + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.10.5 + jest-mock: 29.7.0 + dev: true + + /@jest/expect-utils@29.7.0: + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.6.3 + dev: true + + /@jest/expect@29.7.0: + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/fake-timers@29.7.0: + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 20.10.5 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + dev: true + + /@jest/globals@29.7.0: + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/reporters@29.7.0: + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.20 + '@types/node': 20.10.5 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.0 + istanbul-lib-instrument: 6.0.1 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.6 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.1.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + + /@jest/source-map@29.6.3: + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jridgewell/trace-mapping': 0.3.20 + callsites: 3.1.0 + graceful-fs: 4.2.11 + dev: true + + /@jest/test-result@29.7.0: + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.5 + collect-v8-coverage: 1.0.2 + dev: true + + /@jest/test-sequencer@29.7.0: + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + dev: true + + /@jest/transform@29.7.0: + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.23.2 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.20 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.5 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/types@29.6.3: + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.5 + '@types/istanbul-reports': 3.0.3 + '@types/node': 20.10.5 + '@types/yargs': 17.0.29 + chalk: 4.1.2 + dev: true + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.20: + resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.16.0 + dev: true + + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + + /@sinonjs/commons@3.0.0: + resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==} + dependencies: + type-detect: 4.0.8 + dev: true + + /@sinonjs/fake-timers@10.3.0: + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + dependencies: + '@sinonjs/commons': 3.0.0 + dev: true + + /@types/babel__core@7.20.3: + resolution: {integrity: sha512-54fjTSeSHwfan8AyHWrKbfBWiEUrNTZsUwPTDSNaaP1QDQIZbeNUg3a59E9D+375MzUw/x1vx2/0F5LBz+AeYA==} + dependencies: + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + '@types/babel__generator': 7.6.6 + '@types/babel__template': 7.4.3 + '@types/babel__traverse': 7.20.3 + dev: true + + /@types/babel__generator@7.6.6: + resolution: {integrity: sha512-66BXMKb/sUWbMdBNdMvajU7i/44RkrA3z/Yt1c7R5xejt8qh84iU54yUWCtm0QwGJlDcf/gg4zd/x4mpLAlb/w==} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@types/babel__template@7.4.3: + resolution: {integrity: sha512-ciwyCLeuRfxboZ4isgdNZi/tkt06m8Tw6uGbBSBgWrnnZGNXiEyM27xc/PjXGQLqlZ6ylbgHMnm7ccF9tCkOeQ==} + dependencies: + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + dev: true + + /@types/babel__traverse@7.20.3: + resolution: {integrity: sha512-Lsh766rGEFbaxMIDH7Qa+Yha8cMVI3qAK6CHt3OR0YfxOIn5Z54iHiyDRycHrBqeIiqGa20Kpsv1cavfBKkRSw==} + dependencies: + '@babel/types': 7.23.0 + dev: true + + /@types/graceful-fs@4.1.8: + resolution: {integrity: sha512-NhRH7YzWq8WiNKVavKPBmtLYZHxNY19Hh+az28O/phfp68CF45pMFud+ZzJ8ewnxnC5smIdF3dqFeiSUQ5I+pw==} + dependencies: + '@types/node': 20.10.5 + dev: true + + /@types/istanbul-lib-coverage@2.0.5: + resolution: {integrity: sha512-zONci81DZYCZjiLe0r6equvZut0b+dBRPBN5kBDjsONnutYNtJMoWQ9uR2RkL1gLG9NMTzvf+29e5RFfPbeKhQ==} + dev: true + + /@types/istanbul-lib-report@3.0.2: + resolution: {integrity: sha512-8toY6FgdltSdONav1XtUHl4LN1yTmLza+EuDazb/fEmRNCwjyqNVIQWs2IfC74IqjHkREs/nQ2FWq5kZU9IC0w==} + dependencies: + '@types/istanbul-lib-coverage': 2.0.5 + dev: true + + /@types/istanbul-reports@3.0.3: + resolution: {integrity: sha512-1nESsePMBlf0RPRffLZi5ujYh7IH1BWL4y9pr+Bn3cJBdxz+RTP8bUFljLz9HvzhhOSWKdyBZ4DIivdL6rvgZg==} + dependencies: + '@types/istanbul-lib-report': 3.0.2 + dev: true + + /@types/jest@29.5.11: + resolution: {integrity: sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==} + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + dev: true + + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + dev: true + + /@types/node@20.10.5: + resolution: {integrity: sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/semver@7.5.6: + resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} + dev: true + + /@types/stack-utils@2.0.2: + resolution: {integrity: sha512-g7CK9nHdwjK2n0ymT2CW698FuWJRIx+RP6embAzZ2Qi8/ilIrA1Imt2LVSeHUzKvpoi7BhmmQcXz95eS0f2JXw==} + dev: true + + /@types/yargs-parser@21.0.2: + resolution: {integrity: sha512-5qcvofLPbfjmBfKaLfj/+f+Sbd6pN4zl7w7VSVI5uz7m9QZTuB2aZAa2uo1wHFBNN2x6g/SoTkXmd8mQnQF2Cw==} + dev: true + + /@types/yargs@17.0.29: + resolution: {integrity: sha512-nacjqA3ee9zRF/++a3FUY1suHTFKZeHba2n8WeDw9cCVdmzmHpIxyzOJBcpHvvEmS8E9KqWlSnWHUkOrkhWcvA==} + dependencies: + '@types/yargs-parser': 21.0.2 + dev: true + + /@typescript-eslint/eslint-plugin@6.16.0(@typescript-eslint/parser@6.16.0)(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-O5f7Kv5o4dLWQtPX4ywPPa+v9G+1q1x8mz0Kr0pXUtKsevo+gIJHLkGc8RxaZWtP8RrhwhSNIWThnW42K9/0rQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 6.16.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.16.0 + '@typescript-eslint/type-utils': 6.16.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.16.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.16.0 + debug: 4.3.4 + eslint: 8.56.0 + graphemer: 1.4.0 + ignore: 5.3.0 + natural-compare: 1.4.0 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@6.16.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 6.16.0 + '@typescript-eslint/types': 6.16.0 + '@typescript-eslint/typescript-estree': 6.16.0(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.16.0 + debug: 4.3.4 + eslint: 8.56.0 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@6.16.0: + resolution: {integrity: sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.16.0 + '@typescript-eslint/visitor-keys': 6.16.0 + dev: true + + /@typescript-eslint/type-utils@6.16.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-ThmrEOcARmOnoyQfYkHw/DX2SEYBalVECmoldVuH6qagKROp/jMnfXpAU/pAIWub9c4YTxga+XwgAkoA0pxfmg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 6.16.0(typescript@5.3.3) + '@typescript-eslint/utils': 6.16.0(eslint@8.56.0)(typescript@5.3.3) + debug: 4.3.4 + eslint: 8.56.0 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@6.16.0: + resolution: {integrity: sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/typescript-estree@6.16.0(typescript@5.3.3): + resolution: {integrity: sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.16.0 + '@typescript-eslint/visitor-keys': 6.16.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@6.16.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-T83QPKrBm6n//q9mv7oiSvy/Xq/7Hyw9SzSEhMHJwznEmQayfBM87+oAlkNAMEO7/MjIwKyOHgBJbxB0s7gx2A==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.6 + '@typescript-eslint/scope-manager': 6.16.0 + '@typescript-eslint/types': 6.16.0 + '@typescript-eslint/typescript-estree': 6.16.0(typescript@5.3.3) + eslint: 8.56.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@6.16.0: + resolution: {integrity: sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.16.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: true + + /acorn-jsx@5.3.2(acorn@8.11.3): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.11.3 + dev: true + + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-sequence-parser@1.1.1: + resolution: {integrity: sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==} + dev: true + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /babel-jest@29.7.0(@babel/core@7.23.2): + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + dependencies: + '@babel/core': 7.23.2 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.3 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.23.2) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + dependencies: + '@babel/helper-plugin-utils': 7.22.5 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/template': 7.22.15 + '@babel/types': 7.23.0 + '@types/babel__core': 7.20.3 + '@types/babel__traverse': 7.20.3 + dev: true + + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.2): + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.2 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.2) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.2) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.2) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.2) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.2) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.2) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.2) + dev: true + + /babel-preset-jest@29.6.3(@babel/core@7.23.2): + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.2 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.2) + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browserslist@4.22.1: + resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001559 + electron-to-chromium: 1.4.572 + node-releases: 2.0.13 + update-browserslist-db: 1.0.13(browserslist@4.22.1) + dev: true + + /bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + dependencies: + fast-json-stable-stringify: 2.1.0 + dev: true + + /bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + dependencies: + node-int64: 0.4.0 + dev: true + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + dev: true + + /camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: true + + /caniuse-lite@1.0.30001559: + resolution: {integrity: sha512-cPiMKZgqgkg5LY3/ntGeLFUpi6tzddBNS58A4tnTgQw1zON7u2sZMU7SzOeVH4tj20++9ggL+V6FDOFMTaFFYA==} + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + dev: true + + /ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + dev: true + + /cjs-module-lexer@1.2.3: + resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + dev: true + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + + /co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + dev: true + + /collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + + /create-jest@29.7.0(@types/node@20.10.5): + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@20.10.5) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /dedent@1.5.1: + resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + dev: true + + /detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + dev: true + + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /electron-to-chromium@1.4.572: + resolution: {integrity: sha512-RlFobl4D3ieetbnR+2EpxdzFl9h0RAJkPK3pfiwMug2nhBin2ZCsGIAJWdpNniLz43sgXam/CgipOmvTA+rUiA==} + dev: true + + /emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + dev: true + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-config-prettier@9.1.0(eslint@8.56.0): + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.56.0 + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.56.0: + resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.56.0 + '@humanwhocodes/config-array': 0.11.13 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) + eslint-visitor-keys: 3.4.3 + dev: true + + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + dev: true + + /expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.16.0: + resolution: {integrity: sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==} + dependencies: + reusify: 1.0.4 + dev: true + + /fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + dependencies: + bser: 2.1.1 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.2.0 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.9 + keyv: 4.5.4 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + requiresBuild: true + dev: true + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true + + /get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + dev: true + + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.0 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /hasown@2.0.0: + resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + engines: {node: '>= 0.4'} + requiresBuild: true + dependencies: + function-bind: 1.1.2 + dev: true + + /html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + dev: true + + /human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: true + + /ignore@5.3.0: + resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /import-local@3.1.0: + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} + hasBin: true + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + requiresBuild: true + dependencies: + hasown: 2.0.0 + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /istanbul-lib-coverage@3.2.0: + resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} + engines: {node: '>=8'} + dev: true + + /istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + dependencies: + '@babel/core': 7.23.2 + '@babel/parser': 7.23.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-instrument@6.0.1: + resolution: {integrity: sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==} + engines: {node: '>=10'} + dependencies: + '@babel/core': 7.23.2 + '@babel/parser': 7.23.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + dependencies: + istanbul-lib-coverage: 3.2.0 + make-dir: 4.0.0 + supports-color: 7.2.0 + dev: true + + /istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + dependencies: + debug: 4.3.4 + istanbul-lib-coverage: 3.2.0 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-reports@3.1.6: + resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} + engines: {node: '>=8'} + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + dev: true + + /jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + dev: true + + /jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.10.5 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.5.1 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.0.4 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-cli@29.7.0(@types/node@20.10.5): + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@20.10.5) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@20.10.5) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /jest-config@29.7.0(@types/node@20.10.5): + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.23.2 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.10.5 + babel-jest: 29.7.0(@babel/core@7.23.2) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + detect-newline: 3.1.0 + dev: true + + /jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + dev: true + + /jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.10.5 + jest-mock: 29.7.0 + jest-util: 29.7.0 + dev: true + + /jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.8 + '@types/node': 20.10.5 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + dev: true + + /jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/code-frame': 7.22.13 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.2 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + dev: true + + /jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.10.5 + jest-util: 29.7.0 + dev: true + + /jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + dependencies: + jest-resolve: 29.7.0 + dev: true + + /jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.8 + resolve.exports: 2.0.2 + slash: 3.0.0 + dev: true + + /jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.10.5 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.10.5 + chalk: 4.1.2 + cjs-module-lexer: 1.2.3 + collect-v8-coverage: 1.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.23.2 + '@babel/generator': 7.23.0 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2) + '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.23.2) + '@babel/types': 7.23.0 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.2) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.10.5 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + dev: true + + /jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + dev: true + + /jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.10.5 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + dev: true + + /jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@types/node': 20.10.5 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + + /jest@29.7.0(@types/node@20.10.5): + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0 + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@20.10.5) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + dev: true + + /leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /lunr@2.3.9: + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + dev: true + + /make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + dependencies: + semver: 7.5.4 + dev: true + + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + + /makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + dependencies: + tmpl: 1.0.5 + dev: true + + /marked@4.3.0: + resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} + engines: {node: '>= 12'} + hasBin: true + dev: true + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + dev: true + + /node-releases@2.0.13: + resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.22.13 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + requiresBuild: true + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + dev: true + + /pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier@3.1.1: + resolution: {integrity: sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: true + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: true + + /pure-rand@6.0.4: + resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: true + + /resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + dependencies: + resolve-from: 5.0.0 + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + dev: true + + /resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + dev: true + + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /shiki@0.14.7: + resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==} + dependencies: + ansi-sequence-parser: 1.1.1 + jsonc-parser: 3.2.0 + vscode-oniguruma: 1.7.0 + vscode-textmate: 8.0.0 + dev: true + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true + + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: true + + /stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + dependencies: + escape-string-regexp: 2.0.0 + dev: true + + /string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + dev: true + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + dev: true + + /strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + requiresBuild: true + dev: true + + /test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + dev: true + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /ts-api-utils@1.0.3(typescript@5.3.3): + resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} + engines: {node: '>=16.13.0'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.3.3 + dev: true + + /ts-jest@29.1.1(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.3.3): + resolution: {integrity: sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.23.2 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@20.10.5) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.5.4 + typescript: 5.3.3 + yargs-parser: 21.1.1 + dev: true + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + + /typedoc@0.25.4(typescript@5.3.3): + resolution: {integrity: sha512-Du9ImmpBCw54bX275yJrxPVnjdIyJO/84co0/L9mwe0R3G4FSR6rQ09AlXVRvZEGMUg09+z/usc8mgygQ1aidA==} + engines: {node: '>= 16'} + hasBin: true + peerDependencies: + typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x + dependencies: + lunr: 2.3.9 + marked: 4.3.0 + minimatch: 9.0.3 + shiki: 0.14.7 + typescript: 5.3.3 + dev: true + + /typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + + /update-browserslist-db@1.0.13(browserslist@4.22.1): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.22.1 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + + /v8-to-istanbul@9.1.3: + resolution: {integrity: sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==} + engines: {node: '>=10.12.0'} + dependencies: + '@jridgewell/trace-mapping': 0.3.20 + '@types/istanbul-lib-coverage': 2.0.5 + convert-source-map: 2.0.0 + dev: true + + /vscode-oniguruma@1.7.0: + resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} + dev: true + + /vscode-textmate@8.0.0: + resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} + dev: true + + /walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + dependencies: + makeerror: 1.0.12 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + dev: true + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: true + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/src/configValue.ts b/src/configValue.ts new file mode 100644 index 0000000..cc4b3a0 --- /dev/null +++ b/src/configValue.ts @@ -0,0 +1,235 @@ +import { Transformers } from "./transformers/transformers"; +import { ConfigError, REQUIRED, Transformer, ValidatorFunc } from "./types"; +import { isConfigError } from "./utils"; +import { required as requiredValidator } from "./validators/required"; + +type ValidatorArg = ValidatorFunc | ValidatorFunc[]; +type DefOrReq = T | typeof REQUIRED; + +/** @see {@link configValue} */ +export interface ConfigValueFunc { + (envVarName: string | string[]): string | undefined; + (envVarName: string | string[], defaultOrRequired: DefOrReq): string | ConfigError; + (envVarName: string | string[], validator: ValidatorArg): string | undefined | ConfigError; + (envVarName: string | string[], required: typeof REQUIRED, validator: ValidatorArg): string | ConfigError; + (envVarName: string | string[], defaultValue: string, validator: ValidatorArg): string; + (envVarName: string | string[], t: "str"): string | undefined; + (envVarName: string | string[], t: "str", defaultOrRequired: DefOrReq): string | ConfigError; + (envVarName: string | string[], t: "str", validator: ValidatorArg): string | undefined | ConfigError; + ( + envVarName: string | string[], + t: "str", + required: typeof REQUIRED, + validator: ValidatorArg + ): string | ConfigError; + ( + envVarName: string | string[], + t: "str", + defaultValue: string, + validator: ValidatorArg + ): string | ConfigError; + (envVarName: string | string[], t: "num"): number | undefined | ConfigError; + (envVarName: string | string[], t: "num", defaultOrRequired: DefOrReq): number | ConfigError; + (envVarName: string | string[], t: "num", validator: ValidatorArg): number | ConfigError; + ( + envVarName: string | string[], + t: "num", + required: typeof REQUIRED, + validator: ValidatorArg + ): number | ConfigError; + ( + envVarName: string | string[], + t: "num", + defaultValue: number, + validator: ValidatorArg + ): number | ConfigError; + (envVarName: string | string[], t: "bool"): boolean | undefined | ConfigError; + (envVarName: string | string[], t: "bool", defaultOrRequired: DefOrReq): boolean | ConfigError; + (envVarName: string | string[], t: "bool", validator: ValidatorArg): boolean | undefined | ConfigError; + ( + envVarName: string | string[], + t: "bool", + required: typeof REQUIRED, + validator: ValidatorArg + ): boolean | ConfigError; + ( + envVarName: string | string[], + t: "bool", + defaultValue: boolean, + validator: ValidatorArg + ): boolean | ConfigError; + (envVarName: string | string[], t: Transformer): T | undefined | ConfigError; + (envVarName: string | string[], t: Transformer, defaultOrRequired: DefOrReq): T | ConfigError; + (envVarName: string | string[], t: Transformer, validator: ValidatorArg): T | undefined | ConfigError; + ( + envVarName: string | string[], + t: Transformer, + required: typeof REQUIRED, + validator: ValidatorArg + ): T | ConfigError; + (envVarName: string | string[], t: Transformer, defaultValue: T, validator: ValidatorArg): T | ConfigError; +} + +/** + * Get a value with the given specification. The standard transformers available are 'str', 'num' and 'bool'. You can + * also pass a custom transformer function (see {@link TransformerFunc}) as well a validator function or array of + * validator functions. (Import the {@link Validators | `Validators`} library from + * this library to explore the bundled validators or write your own by implementing {@link ValidatorFunc}.) + * + * The most common usage of this will be the following (see more examples below for advanced usage in context): + * + * ```ts + * // Get a possibly-undefined string from the SOME_VAR environment variable + * configValue('SOME_VAR'); + * + * // Get a string with a default value of 'some default' from the SOME_VAR environment variable + * configValue('SOME_VAR', 'some default'); + * + * // Get a possibly-undefined number from the SOME_VAR environment variable + * configValue('SOME_VAR', 'num'); + * + * // Get a number with a default value of 5 from SOME_VAR + * configValue('SOME_VAR', 'num', 5); + * + * // Get a required string from SOME_VAR with no default (note: `REQUIRED` is a special imported symbol from this + * // library) + * configValue('SOME_VAR', REQUIRED); + * + * // Get a required number from SOME_VAR with no default + * configValue('SOME_VAR', 'num', REQUIRED); + * ``` + * + * @example + * Following is a more real-world example of how this might all be used in context + * + * ```ts + * const configDef = { + * port: configValue('PORT', 'num', 3000), + * hiPort: configValue('PORT', 'num', 30_000, hiport), + * host: configValue('HOST', 'str', 'http://localhost', hostValidator), + * db: { + * url: configValue('DB_URL', 'str', 'postgres://postgres@localhost:5432'), + * migrateOnStart: configValue('DB_MIGRATE_ON_START', 'bool', false), + * }, + * signatureSecret: configValue('SIGNATURE_SECRET', 'str', [required, exactLength(32), hex]), + * secondsToWaitForThing: configValue('SECONDS_TO_WAIT_FOR_THING', 'num', required), + * } + * + * export const config = validate(configDef); + * ``` + * + * NOTE: This function uses any-casts. This is because the underlying transformer functions are overloaded and require + * _either_ one argument configuration _or_ the other, which gets messy in a function like this. Since this function is + * itself properly overloaded, it's safe to use any-casts to solve this here. + */ +export const configValue: ConfigValueFunc = ( + envVarNameArr: string | string[], + defOrReqOrTransOrVal?: string | typeof REQUIRED | "str" | "num" | "bool" | Transformer | ValidatorArg, + defOrReqOrValidator?: ValidatorArg | T | typeof REQUIRED, + validatorArg?: ValidatorArg +) => { + const { defaultValue, envVarName, required, transformer, validators } = processArgs( + envVarNameArr, + defOrReqOrTransOrVal, + defOrReqOrValidator, + validatorArg + ); + + // If required, add our special `required` validator to the validators stack + if (required) { + validators.unshift(requiredValidator); + } + + // Get and transform the value + let val = transformer(process.env[envVarName], envVarName) as T | undefined | ConfigError; + + // If we don't have a value but do have a default, set our value to that + if (val === undefined && defaultValue !== undefined) { + val = defaultValue as T; + } + + // If the transformation worked, pass the value through our validators + if (!isConfigError(val) && validators?.length) { + const errors: string[] = []; + for (const validator of validators) { + const newErrors = validator(val); + if (newErrors) { + errors.push(...(Array.isArray(newErrors) ? newErrors : [newErrors])); + } + } + + if (errors.length > 0) { + val = `::ERROR::${envVarName}::${errors.join("|")}`; + } + } + + // Return whatever we've got + return val; +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const isCustomTransformer = (obj: any): obj is Transformer => + obj && typeof obj === "object" && typeof obj.transform === "function"; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const isValidatorArg = (obj: any): obj is ValidatorArg => typeof obj === "function" || Array.isArray(obj); + +/** + * Do some super-intense argument analysis to figure out what's what here and return a normalized set of arguments. + * + * God help ye who wanders here............ + */ +const processArgs = ( + envVarNameArr: string | string[], + defOrReqOrTransOrVal?: string | typeof REQUIRED | "str" | "num" | "bool" | Transformer | ValidatorArg, + defOrReqOrValidator?: ValidatorArg | T | typeof REQUIRED, + validatorArg?: ValidatorArg +) => { + // Get the best env var to use + const envVarName = Array.isArray(envVarNameArr) + ? envVarNameArr.find((v) => process.env[v] !== undefined) || envVarNameArr[0]! + : envVarNameArr; + + const required = defOrReqOrTransOrVal === REQUIRED || defOrReqOrValidator === REQUIRED; + + // Get the transformer function to use + const transformer = isCustomTransformer(defOrReqOrTransOrVal) + ? defOrReqOrTransOrVal.transform + : typeof defOrReqOrTransOrVal === "string" && defOrReqOrTransOrVal in Transformers + ? Transformers[defOrReqOrTransOrVal as keyof typeof Transformers].transform + : Transformers.str.transform; + + // Get and normalize validators, if there are any + const validators = validatorArg + ? Array.isArray(validatorArg) + ? validatorArg + : [validatorArg] + : isValidatorArg(defOrReqOrValidator) + ? typeof defOrReqOrValidator === "function" + ? [defOrReqOrValidator] + : defOrReqOrValidator + : isValidatorArg(defOrReqOrTransOrVal) + ? typeof defOrReqOrTransOrVal === "function" + ? [defOrReqOrTransOrVal] + : defOrReqOrTransOrVal + : []; + + // Get the default value, if specified + const defaultValue = + defOrReqOrTransOrVal !== REQUIRED && + !isValidatorArg(defOrReqOrTransOrVal) && + !isCustomTransformer(defOrReqOrTransOrVal) && + !((defOrReqOrTransOrVal as string) in Transformers) + ? (defOrReqOrTransOrVal as T | undefined) + : defOrReqOrValidator !== undefined && !isValidatorArg(defOrReqOrValidator) && defOrReqOrValidator !== REQUIRED + ? (defOrReqOrValidator as T) + : undefined; + + // Return everything all bundled up + return { + defaultValue, + envVarName, + required, + transformer, + validators, + }; +}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..0142ad4 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,5 @@ +export * from "./configValue"; +export * from "./transformers/transformers"; +export * from "./types"; +export * from "./validate"; +export * from "./validators/validators"; diff --git a/src/transformers/bool.ts b/src/transformers/bool.ts new file mode 100644 index 0000000..b7cd712 --- /dev/null +++ b/src/transformers/bool.ts @@ -0,0 +1,18 @@ +import { Transformer } from "../types"; + +/** A standard boolean transformer */ +export const bool: Transformer = { + transform: (val: string | undefined, varname: string) => { + if (!val) { + return undefined; + } + val = val.toLowerCase(); + if (val === "true" || val === "1") { + return true; + } + if (val === "false" || val === "0") { + return false; + } + return `::ERROR::${varname}::This value must look like a boolean ('true', 'false', '0' or '1'). You provided '${val}'`; + }, +}; diff --git a/src/transformers/csvToArray.ts b/src/transformers/csvToArray.ts new file mode 100644 index 0000000..5f7c2a0 --- /dev/null +++ b/src/transformers/csvToArray.ts @@ -0,0 +1,10 @@ +import { Transformer } from "../types"; + +export const csvToArray: Transformer = { + transform: (val: string | undefined) => { + if (val) { + return val.split(/ *, */); + } + return undefined; + }, +}; diff --git a/src/transformers/num.ts b/src/transformers/num.ts new file mode 100644 index 0000000..e4df427 --- /dev/null +++ b/src/transformers/num.ts @@ -0,0 +1,15 @@ +import { Transformer } from "../types"; + +/** A standard number transformer */ +export const num: Transformer = { + transform: (val: string | undefined, varname: string) => { + if (!val) { + return undefined; + } + const n = Number(val); + if (isNaN(n)) { + return `::ERROR::${varname}::This value must be a number. You passed ${val}`; + } + return n; + }, +}; diff --git a/src/transformers/str.ts b/src/transformers/str.ts new file mode 100644 index 0000000..78b8fde --- /dev/null +++ b/src/transformers/str.ts @@ -0,0 +1,6 @@ +import { Transformer } from "../types"; + +/** A standard string transformer */ +export const str: Transformer = { + transform: (val: string | undefined) => val || undefined, +}; diff --git a/src/transformers/transformers.ts b/src/transformers/transformers.ts new file mode 100644 index 0000000..1c7a4fe --- /dev/null +++ b/src/transformers/transformers.ts @@ -0,0 +1,11 @@ +import { bool } from "./bool"; +import { csvToArray } from "./csvToArray"; +import { num } from "./num"; +import { str } from "./str"; + +/** + * A library of transformer functions that are bundled with this library. Note: It is generally easier to use the string + * aliases to access these functions (e.g., `configValue('MY_VAR', 'num')` instead of importing and using them + * directly.) + */ +export const Transformers = { bool, csvToArray, num, str }; diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..8df14aa --- /dev/null +++ b/src/types.ts @@ -0,0 +1,47 @@ +/** A special symbol indicating that the given value is required */ +export const REQUIRED = Symbol.for("required"); + +/** A type indicating an error. You can return this from a custom transformer function as well */ +export type ConfigError = `::ERROR::${string}`; + +/** + * Any function conforming to this interface may be passed to the `configValue` function to transform the value of a given + * config key. + * + * @example + * ```ts + * const myType = ((val: string | undefined, varname: string) => { + * if (!val) { + * if (def === REQUIRED) { + * return `::ERROR::${varname}::This value is required`; + * } + * return def; + * } + * try { + * // Normally you would validate but for testing we're skipping that and just returning whatever + * return JSON.parse(val) as MyType; + * } catch (e: any) { + * return `::ERROR::${varname}::Invalid JSON string passed: ${e.message}`; + * } + * }) as TransformerFunction; + * + * // ... + * + * const configDef = { + * myType: configValue('MY_TYPE', myType, REQUIRED), + * // ... + * } + * ``` + */ +export type TransformerFunc = (val: string | undefined, varname: string) => T | undefined | ConfigError; + +/** + * A transformer. See {@link TransformerFunc} for more details. + */ +export type Transformer = { transform: TransformerFunc }; + +/** + * To implement a custom validator, just implement a function that takes the value to be validated and returns either + * undefined or an error of error strings. + */ +export type ValidatorFunc = (val: T | undefined) => void | string | string[]; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..f249aa2 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,5 @@ +import { ConfigError } from "./types"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const isConfigError = (val: any): val is ConfigError => + Boolean(val && typeof val === "string" && val.startsWith("::ERROR::")); diff --git a/src/validate.ts b/src/validate.ts new file mode 100644 index 0000000..3904cd7 --- /dev/null +++ b/src/validate.ts @@ -0,0 +1,80 @@ +import { ConfigError } from "./types"; + +type CleanFrozenConfig = T extends object + ? { readonly [K in keyof T]: CleanFrozenConfig } + : Exclude; + +type ConfigResult = + | { t: "success"; value: CleanFrozenConfig } + | { + t: "error"; + errors: string[]; + value: CleanFrozenConfig; + }; + +/** + * Validate a configuration object and return a frozen copy. + * + * @example + * ```ts + * export const config = validate(configDef); + * export const dirtyConfig = validate(configDef, 'dont-throw'); + * ``` + * + * In the first form, this function will throw an error if the configuration is invalid. In the second form, it will + * return a `ConfigResult` object that can be inspected for errors. + * + * NOTE: THE 'ERROR' SIDE OF THE RESPONSE FROM THE SECOND FORM IS DANGEROUS. We want the option to not throw on config + * errors, but we also don't want to have to use type-guards to handle bad config throughout our entire codebase. This + * type is a dangerous compromise that allows us to blindly use values as if they were valid, while knowing that they + * may not be. + */ +export function validate(obj: T): CleanFrozenConfig; +export function validate(obj: T, behavior: "dont-throw"): ConfigResult; +export function validate(obj: T, behavior?: "dont-throw"): CleanFrozenConfig | ConfigResult { + const result = reportConfigResult(obj); + if (behavior === "dont-throw") { + return result; + } + if (result.t === "error") { + throw new Error(`Invalid configuration: \n\n * ${result.errors.join("\n * ")}`); + } + return result.value; +} + +const reportConfigResult = (obj: T, path: string[] = []): ConfigResult => { + const errors: string[] = []; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const response: any = Array.isArray(obj) ? [...obj] : { ...obj }; + for (const k in response) { + if (Object.prototype.hasOwnProperty.call(response, k)) { + const val = response[k]; + if (typeof val === "string" && val.startsWith("::ERROR::")) { + const match = val.match(/^::ERROR::(?:([^:]+)::)?(.*)$/); + const key = [...path, k].join("."); + const varname = match?.[1] ? ` (${match[1]})` : ""; + const messages = match?.[2] ? match[2].split("|") : ["Invalid value (unknown error)"]; + if (messages.length === 1) { + errors.push(`${key}${varname}: ${messages[0]}`); + } else { + errors.push(`${key}${varname}:\n * ${messages.join("\n * ")}`); + } + } else { + if (response[k] && typeof response[k] === "object") { + const result = reportConfigResult(response[k], [...path, k]); + if (result.t === "error") { + errors.push(...result.errors); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + response[k] = result.value as any; + } + } + } + } + + if (errors.length > 0) { + return { t: "error", errors, value: Object.freeze(response) as CleanFrozenConfig }; + } else { + return { t: "success", value: Object.freeze(response) as CleanFrozenConfig }; + } +}; diff --git a/src/validators/required.ts b/src/validators/required.ts new file mode 100644 index 0000000..6899f30 --- /dev/null +++ b/src/validators/required.ts @@ -0,0 +1,26 @@ +import { ConfigError, ValidatorFunc } from "../types"; + +/** NOTE: This is intended to be an INTERNAL validator function */ +export const required = (val: T | undefined | ConfigError): string | void => { + if (val === undefined) { + return `This value is required`; + } +}; + +/** Produces an error if the given value is not set for the given environments */ +export const requiredForEnvs = + (env: string, requiredEnvs: string[]): ValidatorFunc => + (val: T | undefined | ConfigError) => { + if (requiredEnvs.includes(env)) { + return required(val); + } + }; + +/** Produces an error if the given value is not set and the given condition evaluates to true */ +export const requiredIf = + (condition: boolean): ValidatorFunc => + (val: T | undefined | ConfigError) => { + if (condition) { + return required(val); + } + }; diff --git a/src/validators/string.ts b/src/validators/string.ts new file mode 100644 index 0000000..be3545d --- /dev/null +++ b/src/validators/string.ts @@ -0,0 +1,50 @@ +import { ValidatorFunc } from "../types"; + +export const exactLen = + (len: number): ValidatorFunc => + (val: string | undefined): string | void => { + if (val && val.length !== len) { + return `Value must be exactly ${len} characters long`; + } + }; + +export const httpHost: ValidatorFunc = (val: string | undefined): string | void => { + if (val && !val.match(/^https?:\/\//)) { + return "Value must start with http:// or https://"; + } +}; + +export const match = + (pattern: RegExp | string): ValidatorFunc => + (val: string | undefined): string | void => { + if (val) { + if (!val.match(pattern)) { + return `Value must match pattern '${pattern}'`; + } + } + }; + +export const maxLen = + (max: number): ValidatorFunc => + (val: string | undefined): string | void => { + if (val && val.length > max) { + return `Value must be at most ${max} characters long`; + } + }; + +export const minLen = + (min: number): ValidatorFunc => + (val: string | undefined): string | void => { + if (val && val.length < min) { + return `Value must be at least ${min} characters long`; + } + }; + +/** Validate that the given value is one of the given options */ +export const oneOf = + (values: string[]): ValidatorFunc => + (val: string | undefined): string | void => { + if (val && !values.includes(val)) { + return `Value must be one of '${values.join(`', '`)}'`; + } + }; diff --git a/src/validators/validators.ts b/src/validators/validators.ts new file mode 100644 index 0000000..3f8a2c5 --- /dev/null +++ b/src/validators/validators.ts @@ -0,0 +1,17 @@ +import { requiredForEnvs, requiredIf } from "./required"; +import { exactLen, httpHost, match, maxLen, minLen, oneOf } from "./string"; + +/** + * A library of bundled validators that you may use in your config definitions. You can also write your own validators + * by implementing the {@link ValidatorFunc} interface. + */ +export const Validators = { + exactLen, + httpHost, + match, + maxLen, + minLen, + oneOf, + requiredForEnvs, + requiredIf, +}; diff --git a/tests/__snapshots__/validate.test.ts.snap b/tests/__snapshots__/validate.test.ts.snap new file mode 100644 index 0000000..3cd91db --- /dev/null +++ b/tests/__snapshots__/validate.test.ts.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`validate handles multiple errors per key 1`] = ` +"Invalid configuration: + + * a (MY_ENV_VAR): + * bad + * worse + * worst" +`; + +exports[`validate should return a ConfigResult if there are errors and behavior is "dont-throw" 1`] = ` +{ + "errors": [ + "c.d: bad", + ], + "t": "error", + "value": { + "a": 1, + "b": "2", + "c": { + "d": "::ERROR::bad", + }, + }, +} +`; + +exports[`validate should throw an error if there are errors 1`] = ` +"Invalid configuration: + + * c.d: bad + * e: more bad" +`; diff --git a/tests/configValue.test.ts b/tests/configValue.test.ts new file mode 100644 index 0000000..06d9f92 --- /dev/null +++ b/tests/configValue.test.ts @@ -0,0 +1,310 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { configValue } from "../src/configValue"; +import { REQUIRED, TransformerFunc, ValidatorFunc } from "../src/types"; + +// A test type for a custom transformer +type MyType = { one: number; two: string }; + +const testValidator = + (pass: boolean): ValidatorFunc => + (): string | void => { + if (!pass) { + return "This is an error"; + } + }; + +describe("configValue function", () => { + beforeEach(() => { + process.env.TEST_STRING = "test"; + process.env.TEST_NUM = "123"; + process.env.TEST_TRUE = "true"; + process.env.TEST_FALSE = "false"; + process.env.TEST_1 = "1"; + process.env.TEST_0 = "0"; + process.env.EMPTY_STRING = ""; + }); + + test("string", () => { + // (envVar: string | string[]): string | undefined; + expect(configValue("TEST_STRING")).toBe("test"); + expect(configValue("NON_EXISTENT")).toBe(undefined); + + // (envVar: string | string[], defaultOrRequired: DefOrReq): string; + expect(configValue("TEST_STRING", "my-default")).toBe("test"); + expect(configValue("NON_EXISTENT", "my-default")).toBe("my-default"); + expect(configValue("TEST_STRING", REQUIRED)).toBe("test"); + expect(configValue("NON_EXISTENT", REQUIRED)).toMatch(/^::ERROR::/); + + // (envVar: string | string[], validator: ValidatorArg): string | undefined | ConfigError; + expect(configValue("TEST_STRING", testValidator(true))).toBe("test"); + expect(configValue("NON_EXISTENT", testValidator(true))).toBe(undefined); + expect(configValue("TEST_STRING", testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", [testValidator(true)])).toBe("test"); + expect(configValue("NON_EXISTENT", [testValidator(true)])).toBe(undefined); + expect(configValue("TEST_STRING", [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", [testValidator(false)])).toMatch(/^::ERROR::/); + + // (envVar: string | string[], required: typeof REQUIRED, validator: ValidatorArg): string | ConfigError; + expect(configValue("TEST_STRING", REQUIRED, testValidator(true))).toBe("test"); + expect(configValue("NON_EXISTENT", REQUIRED, testValidator(true))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", REQUIRED, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", REQUIRED, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", REQUIRED, [testValidator(true)])).toBe("test"); + expect(configValue("NON_EXISTENT", REQUIRED, [testValidator(true)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", REQUIRED, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", REQUIRED, [testValidator(false)])).toMatch(/^::ERROR::/); + + // (envVar: string | string[], defaultValue: string, validator: ValidatorArg): string; + expect(configValue("TEST_STRING", "my-default", testValidator(true))).toBe("test"); + expect(configValue("NON_EXISTENT", "my-default", testValidator(true))).toBe("my-default"); + expect(configValue("TEST_STRING", "my-default", testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "my-default", testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "my-default", [testValidator(true)])).toBe("test"); + expect(configValue("NON_EXISTENT", "my-default", [testValidator(true)])).toBe("my-default"); + expect(configValue("TEST_STRING", "my-default", [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "my-default", [testValidator(false)])).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: 'str'): string | undefined; + expect(configValue("TEST_STRING", "str")).toBe("test"); + expect(configValue("NON_EXISTENT", "str")).toBe(undefined); + + // (envVar: string | string[], t: 'str', defaultOrRequired: DefOrReq): string; + expect(configValue("TEST_STRING", "str", "my-default")).toBe("test"); + expect(configValue("TEST_STRING", "str", REQUIRED)).toBe("test"); + expect(configValue("NON_EXISTENT", "str", "my-default")).toBe("my-default"); + expect(configValue("NON_EXISTENT", "str", REQUIRED)).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: 'str', validator: ValidatorArg): string | undefined | ConfigError; + expect(configValue("TEST_STRING", "str", testValidator(true))).toBe("test"); + expect(configValue("NON_EXISTENT", "str", testValidator(true))).toBe(undefined); + expect(configValue("TEST_STRING", "str", testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "str", testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "str", [testValidator(true)])).toBe("test"); + expect(configValue("NON_EXISTENT", "str", [testValidator(true)])).toBe(undefined); + expect(configValue("TEST_STRING", "str", [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "str", [testValidator(false)])).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: 'str', required: typeof REQUIRED, validator: ValidatorArg): string | ConfigError; + expect(configValue("TEST_STRING", "str", REQUIRED, testValidator(true))).toBe("test"); + expect(configValue("NON_EXISTENT", "str", REQUIRED, testValidator(true))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "str", REQUIRED, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "str", REQUIRED, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "str", REQUIRED, [testValidator(true)])).toBe("test"); + expect(configValue("NON_EXISTENT", "str", REQUIRED, [testValidator(true)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "str", REQUIRED, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "str", REQUIRED, [testValidator(false)])).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: 'str', defaultValue: string, validator: ValidatorArg): string | ConfigError; + expect(configValue("TEST_STRING", "str", "my-default", testValidator(true))).toBe("test"); + expect(configValue("NON_EXISTENT", "str", "my-default", testValidator(true))).toMatch("my-default"); + expect(configValue("TEST_STRING", "str", "my-default", testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "str", "my-default", testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "str", "my-default", [testValidator(true)])).toBe("test"); + expect(configValue("NON_EXISTENT", "str", "my-default", [testValidator(true)])).toMatch("my-default"); + expect(configValue("TEST_STRING", "str", "my-default", [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "str", "my-default", [testValidator(false)])).toMatch(/^::ERROR::/); + }); + + test("number", () => { + // (envVar: string | string[], t: 'num'): number | undefined | ConfigError; + expect(configValue("TEST_NUM", "num")).toBe(123); + expect(configValue("NON_EXISTENT", "num")).toBe(undefined); + expect(configValue("TEST_STRING", "num")).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: 'num', defaultOrRequired: DefOrReq): number | ConfigError; + expect(configValue("TEST_NUM", "num", 456)).toBe(123); + expect(configValue("NON_EXISTENT", "num", 456)).toBe(456); + expect(configValue("TEST_STRING", "num", 456)).toMatch(/^::ERROR::/); + expect(configValue("TEST_NUM", "num", REQUIRED)).toBe(123); + expect(configValue("NON_EXISTENT", "num", REQUIRED)).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "num", REQUIRED)).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: 'num', validator: ValidatorArg): number | ConfigError; + expect(configValue("TEST_NUM", "num", testValidator(true))).toBe(123); + expect(configValue("NON_EXISTENT", "num", testValidator(true))).toBe(undefined); + expect(configValue("TEST_STRING", "num", testValidator(true))).toMatch(/^::ERROR::/); + expect(configValue("TEST_NUM", "num", testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "num", testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "num", testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_NUM", "num", [testValidator(true)])).toBe(123); + expect(configValue("NON_EXISTENT", "num", [testValidator(true)])).toBe(undefined); + expect(configValue("TEST_STRING", "num", [testValidator(true)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_NUM", "num", [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "num", [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "num", [testValidator(false)])).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: 'num', required: typeof REQUIRED, validator: ValidatorArg): number | ConfigError; + expect(configValue("TEST_NUM", "num", REQUIRED, testValidator(true))).toBe(123); + expect(configValue("NON_EXISTENT", "num", REQUIRED, testValidator(true))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "num", REQUIRED, testValidator(true))).toMatch(/^::ERROR::/); + expect(configValue("TEST_NUM", "num", REQUIRED, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "num", REQUIRED, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "num", REQUIRED, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_NUM", "num", REQUIRED, [testValidator(true)])).toBe(123); + expect(configValue("NON_EXISTENT", "num", REQUIRED, [testValidator(true)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "num", REQUIRED, [testValidator(true)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_NUM", "num", REQUIRED, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "num", REQUIRED, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "num", REQUIRED, [testValidator(false)])).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: 'num', defaultValue: number, validator: ValidatorArg): number | ConfigError; + expect(configValue("TEST_NUM", "num", 456, testValidator(true))).toBe(123); + expect(configValue("NON_EXISTENT", "num", 456, testValidator(true))).toBe(456); + expect(configValue("TEST_STRING", "num", 456, testValidator(true))).toMatch(/^::ERROR::/); + expect(configValue("TEST_NUM", "num", 456, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "num", 456, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "num", 456, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_NUM", "num", 456, [testValidator(true)])).toBe(123); + expect(configValue("NON_EXISTENT", "num", 456, [testValidator(true)])).toBe(456); + expect(configValue("TEST_STRING", "num", 456, [testValidator(true)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_NUM", "num", 456, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "num", 456, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "num", 456, [testValidator(false)])).toMatch(/^::ERROR::/); + }); + + test("boolean", () => { + // (envVar: string | string[], t: 'bool'): boolean | undefined | ConfigError; + expect(configValue("TEST_TRUE", "bool")).toBe(true); + expect(configValue("TEST_FALSE", "bool")).toBe(false); + expect(configValue("TEST_1", "bool")).toBe(true); + expect(configValue("TEST_0", "bool")).toBe(false); + expect(configValue("NON_EXISTENT", "bool")).toBe(undefined); + expect(configValue("TEST_STRING", "bool")).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: 'bool', defaultOrRequired: DefOrReq): boolean | ConfigError; + expect(configValue("TEST_TRUE", "bool", REQUIRED)).toBe(true); + expect(configValue("NON_EXISTENT", "bool", REQUIRED)).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "bool", REQUIRED)).toMatch(/^::ERROR::/); + expect(configValue("TEST_TRUE", "bool", false)).toBe(true); + expect(configValue("NON_EXISTENT", "bool", false)).toBe(false); + expect(configValue("TEST_STRING", "bool", false)).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: 'bool', validator: ValidatorArg): boolean | undefined | ConfigError; + expect(configValue("TEST_TRUE", "bool", testValidator(true))).toBe(true); + expect(configValue("NON_EXISTENT", "bool", testValidator(true))).toBe(undefined); + expect(configValue("TEST_STRING", "bool", testValidator(true))).toMatch(/^::ERROR::/); + expect(configValue("TEST_TRUE", "bool", testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "bool", testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "bool", testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_TRUE", "bool", [testValidator(true)])).toBe(true); + expect(configValue("NON_EXISTENT", "bool", [testValidator(true)])).toBe(undefined); + expect(configValue("TEST_STRING", "bool", [testValidator(true)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_TRUE", "bool", [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "bool", [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "bool", [testValidator(false)])).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: 'bool', required: typeof REQUIRED, validator: ValidatorArg): boolean | ConfigError; + expect(configValue("TEST_TRUE", "bool", REQUIRED, testValidator(true))).toBe(true); + expect(configValue("NON_EXISTENT", "bool", REQUIRED, testValidator(true))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "bool", REQUIRED, testValidator(true))).toMatch(/^::ERROR::/); + expect(configValue("TEST_TRUE", "bool", REQUIRED, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "bool", REQUIRED, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "bool", REQUIRED, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_TRUE", "bool", REQUIRED, [testValidator(true)])).toBe(true); + expect(configValue("NON_EXISTENT", "bool", REQUIRED, [testValidator(true)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "bool", REQUIRED, [testValidator(true)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_TRUE", "bool", REQUIRED, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "bool", REQUIRED, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "bool", REQUIRED, [testValidator(false)])).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: 'bool', defaultValue: boolean, validator: ValidatorArg): boolean | ConfigError; + expect(configValue("TEST_TRUE", "bool", false, testValidator(true))).toBe(true); + expect(configValue("NON_EXISTENT", "bool", false, testValidator(true))).toBe(false); + expect(configValue("TEST_STRING", "bool", false, testValidator(true))).toMatch(/^::ERROR::/); + expect(configValue("TEST_TRUE", "bool", false, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "bool", false, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "bool", false, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_TRUE", "bool", false, [testValidator(true)])).toBe(true); + expect(configValue("NON_EXISTENT", "bool", false, [testValidator(true)])).toBe(false); + expect(configValue("TEST_STRING", "bool", false, [testValidator(true)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_TRUE", "bool", false, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", "bool", false, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", "bool", false, [testValidator(false)])).toMatch(/^::ERROR::/); + }); + + test("custom", () => { + const myType: TransformerFunc = (val: string | undefined, varname: string) => { + if (!val) { + return undefined; + } + try { + // Normally you would validate but for testing we're skipping that and just returning whatever + return JSON.parse(val) as MyType; + } catch (e) { + return `::ERROR::${varname}::Invalid JSON string passed: ${e.message}`; + } + }; + + const obj: MyType = { one: 1, two: "two" }; + const defMyType: MyType = { one: 3, two: "four" }; + + // eslint-disable-next-line no-process-env + process.env.MY_TYPE = JSON.stringify(obj); + + // (envVar: string | string[], t: { transform: TransformerFunc }): T | undefined | ConfigError; + expect(configValue("MY_TYPE", { transform: myType })).toStrictEqual(obj); + expect(configValue("NON_EXISTENT", { transform: myType })).toBe(undefined); + expect(configValue("TEST_STRING", { transform: myType })).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: { transform: TransformerFunc }, defaultOrRequired: DefOrReq): T | ConfigError; + expect(configValue("MY_TYPE", { transform: myType }, defMyType)).toStrictEqual(obj); + expect(configValue("NON_EXISTENT", { transform: myType }, defMyType)).toStrictEqual(defMyType); + expect(configValue("TEST_STRING", { transform: myType }, defMyType)).toMatch(/^::ERROR::/); + expect(configValue("MY_TYPE", { transform: myType }, REQUIRED)).toStrictEqual(obj); + expect(configValue("NON_EXISTENT", { transform: myType }, REQUIRED)).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", { transform: myType }, REQUIRED)).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: { transform: TransformerFunc }, validator: ValidatorArg): T | undefined | ConfigError; + expect(configValue("MY_TYPE", { transform: myType }, testValidator(true))).toStrictEqual(obj); + expect(configValue("NON_EXISTENT", { transform: myType }, testValidator(true))).toBe(undefined); + expect(configValue("TEST_STRING", { transform: myType }, testValidator(true))).toMatch(/^::ERROR::/); + expect(configValue("MY_TYPE", { transform: myType }, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", { transform: myType }, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", { transform: myType }, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("MY_TYPE", { transform: myType }, [testValidator(true)])).toStrictEqual(obj); + expect(configValue("NON_EXISTENT", { transform: myType }, [testValidator(true)])).toBe(undefined); + expect(configValue("TEST_STRING", { transform: myType }, [testValidator(true)])).toMatch(/^::ERROR::/); + expect(configValue("MY_TYPE", { transform: myType }, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", { transform: myType }, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", { transform: myType }, [testValidator(false)])).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: { transform: TransformerFunc }, required: typeof REQUIRED, validator: ValidatorArg): T | ConfigError; + expect(configValue("MY_TYPE", { transform: myType }, REQUIRED, testValidator(true))).toStrictEqual(obj); + expect(configValue("NON_EXISTENT", { transform: myType }, REQUIRED, testValidator(true))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", { transform: myType }, REQUIRED, testValidator(true))).toMatch(/^::ERROR::/); + expect(configValue("MY_TYPE", { transform: myType }, REQUIRED, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", { transform: myType }, REQUIRED, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", { transform: myType }, REQUIRED, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("MY_TYPE", { transform: myType }, REQUIRED, [testValidator(true)])).toStrictEqual(obj); + expect(configValue("NON_EXISTENT", { transform: myType }, REQUIRED, [testValidator(true)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", { transform: myType }, REQUIRED, [testValidator(true)])).toMatch(/^::ERROR::/); + expect(configValue("MY_TYPE", { transform: myType }, REQUIRED, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", { transform: myType }, REQUIRED, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", { transform: myType }, REQUIRED, [testValidator(false)])).toMatch(/^::ERROR::/); + + // (envVar: string | string[], t: { transform: TransformerFunc }, defaultValue: T, validator: ValidatorArg): T | ConfigError; + expect(configValue("MY_TYPE", { transform: myType }, defMyType, testValidator(true))).toStrictEqual(obj); + expect(configValue("NON_EXISTENT", { transform: myType }, defMyType, testValidator(true))).toStrictEqual(defMyType); + expect(configValue("TEST_STRING", { transform: myType }, defMyType, testValidator(true))).toMatch(/^::ERROR::/); + expect(configValue("MY_TYPE", { transform: myType }, defMyType, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", { transform: myType }, defMyType, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", { transform: myType }, defMyType, testValidator(false))).toMatch(/^::ERROR::/); + expect(configValue("MY_TYPE", { transform: myType }, defMyType, [testValidator(true)])).toStrictEqual(obj); + expect(configValue("NON_EXISTENT", { transform: myType }, defMyType, [testValidator(true)])).toStrictEqual( + defMyType + ); + expect(configValue("TEST_STRING", { transform: myType }, defMyType, [testValidator(true)])).toMatch(/^::ERROR::/); + expect(configValue("MY_TYPE", { transform: myType }, defMyType, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("NON_EXISTENT", { transform: myType }, defMyType, [testValidator(false)])).toMatch(/^::ERROR::/); + expect(configValue("TEST_STRING", { transform: myType }, defMyType, [testValidator(false)])).toMatch(/^::ERROR::/); + }); + + test("handles arrays of env vars", () => { + process.env.THREE = "3"; + process.env.FOUR = "4"; + expect(configValue(["ONE", "TWO", "THREE"], "num", 5)).toBe(3); + expect(configValue(["FOUR", "THREE", "TWO", "ONE"], "num", 5)).toBe(4); + expect(configValue(["TWO", "ONE"], "num", 5)).toBe(5); + }); +}); diff --git a/tests/transformers/csvToArray.test.ts b/tests/transformers/csvToArray.test.ts new file mode 100644 index 0000000..98e06c8 --- /dev/null +++ b/tests/transformers/csvToArray.test.ts @@ -0,0 +1,23 @@ +import { csvToArray } from "../../src/transformers/csvToArray"; + +describe(`csvToArray Transformer`, () => { + test(`should return undefined if the value is undefined`, () => { + expect(csvToArray.transform(undefined, "MY_VAR")).toBeUndefined(); + }); + + test(`should return undefined if the value is an empty string`, () => { + expect(csvToArray.transform("", "MY_VAR")).toBeUndefined(); + }); + + test(`should return an array of strings if the value is a comma-separated string`, () => { + expect(csvToArray.transform("a,b,c", "MY_VAR")).toEqual(["a", "b", "c"]); + }); + + test(`should return an array of trimmed strings if the value is a comma-separated string with spaces`, () => { + expect(csvToArray.transform("a, b, c", "MY_VAR")).toEqual(["a", "b", "c"]); + }); + + test(`should return a single-element array if string has no commas`, () => { + expect(csvToArray.transform("aaaaa bbbbb", "MY_VAR")).toEqual(["aaaaa bbbbb"]); + }); +}); diff --git a/tests/validate.test.ts b/tests/validate.test.ts new file mode 100644 index 0000000..499ca57 --- /dev/null +++ b/tests/validate.test.ts @@ -0,0 +1,56 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { ConfigError } from "../src/types"; +import { validate } from "../src/validate"; + +describe("validate", () => { + test("should return the same object if there are no errors", () => { + const obj = { a: 1, b: "2", c: { d: 3 } }; + expect(validate(obj)).toStrictEqual(obj); + }); + + test("should throw an error if there are errors", () => { + const obj = { a: 1, b: "2", c: { d: "::ERROR::bad" }, e: "::ERROR::more bad" }; + expect(() => validate(obj)).toThrowErrorMatchingSnapshot(); + }); + + test('should return a ConfigResult if there are errors and behavior is "dont-throw"', () => { + const obj = { + a: 1 as number | ConfigError, + b: "2" as string | undefined | ConfigError, + c: { + d: "::ERROR::bad" as boolean | undefined | ConfigError, + }, + }; + expect(validate(obj, "dont-throw")).toMatchSnapshot(); + }); + + test("should return a deeply frozen object", () => { + const obj = { a: 1, b: "2", c: { d: 3 } }; + const result = validate(obj); + expect(Object.isFrozen(result)).toBe(true); + expect(Object.isFrozen(result.c)).toBe(true); + }); + + test("should return both object path and env var name in error", () => { + const obj = { + a: 1 as number | ConfigError, + b: "2" as string | undefined | ConfigError, + c: { + d: "::ERROR::MY_ENV_VAR::bad" as boolean | undefined | ConfigError, + }, + }; + const result = validate(obj, "dont-throw"); + expect((result as any).errors?.[0]).toMatch(/\bc.d\b/); + expect((result as any).errors?.[0]).toMatch(/\bMY_ENV_VAR\b/); + }); + + test("handles multiple errors per key", () => { + const obj = { a: "::ERROR::MY_ENV_VAR::bad|worse|worst" }; + expect(() => validate(obj)).toThrowErrorMatchingSnapshot(); + }); + + test("should return arrays as arrays", () => { + const obj = { a: [1, 2, 3] }; + expect(Array.isArray(validate(obj).a)).toBe(true); + }); +}); diff --git a/tests/validators/required.test.ts b/tests/validators/required.test.ts new file mode 100644 index 0000000..d15a5c2 --- /dev/null +++ b/tests/validators/required.test.ts @@ -0,0 +1,75 @@ +import { required, requiredForEnvs, requiredIf } from "../../src/validators/required"; + +describe('"required" validators', () => { + describe("required", () => { + test("returns an error if the value is undefined", () => { + expect(required(undefined)).not.toBeUndefined(); + }); + test("does not return an error if the value is not undefined", () => { + expect(required("")).toBeUndefined(); + expect(required("str")).toBeUndefined(); + expect(required(true)).toBeUndefined(); + expect(required(false)).toBeUndefined(); + expect(required(null)).toBeUndefined(); + expect(required(0)).toBeUndefined(); + expect(required(1)).toBeUndefined(); + expect(required({ one: 1 })).toBeUndefined(); + }); + }); + + describe("requiredForEnvs", () => { + test("returns an error if the value is undefined and the env is in the list", () => { + expect(requiredForEnvs("test", ["test", "prod", "stage"])(undefined)).not.toBeUndefined(); + }); + test("does not return an error if the value is undefined and the env is not in the list", () => { + expect(requiredForEnvs("test", ["prod", "stage"])(undefined)).toBeUndefined(); + }); + test("does not return an error if the value is not undefined", () => { + expect(requiredForEnvs("test", ["test", "prod", "stage"])("")).toBeUndefined(); + expect(requiredForEnvs("test", ["test", "prod", "stage"])("str")).toBeUndefined(); + expect(requiredForEnvs("test", ["test", "prod", "stage"])(true)).toBeUndefined(); + expect(requiredForEnvs("test", ["test", "prod", "stage"])(false)).toBeUndefined(); + expect(requiredForEnvs("test", ["test", "prod", "stage"])(null)).toBeUndefined(); + expect(requiredForEnvs("test", ["test", "prod", "stage"])(0)).toBeUndefined(); + expect(requiredForEnvs("test", ["test", "prod", "stage"])(1)).toBeUndefined(); + expect(requiredForEnvs("test", ["test", "prod", "stage"])({ one: 1 })).toBeUndefined(); + + expect(requiredForEnvs("test", ["prod", "stage"])("")).toBeUndefined(); + expect(requiredForEnvs("test", ["prod", "stage"])("str")).toBeUndefined(); + expect(requiredForEnvs("test", ["prod", "stage"])(true)).toBeUndefined(); + expect(requiredForEnvs("test", ["prod", "stage"])(false)).toBeUndefined(); + expect(requiredForEnvs("test", ["prod", "stage"])(null)).toBeUndefined(); + expect(requiredForEnvs("test", ["prod", "stage"])(0)).toBeUndefined(); + expect(requiredForEnvs("test", ["prod", "stage"])(1)).toBeUndefined(); + expect(requiredForEnvs("test", ["prod", "stage"])({ one: 1 })).toBeUndefined(); + }); + }); + + describe("requiredIf", () => { + test("returns an error if the value is undefined and the condition is true", () => { + expect(requiredIf(true)(undefined)).not.toBeUndefined(); + }); + test("does not return an error if the value is undefined and the condition is not true", () => { + expect(requiredIf(false)(undefined)).toBeUndefined(); + }); + test("does not return an error if the value is not undefined", () => { + expect(requiredIf(true)("")).toBeUndefined(); + expect(requiredIf(true)("str")).toBeUndefined(); + expect(requiredIf(true)(true)).toBeUndefined(); + expect(requiredIf(true)(false)).toBeUndefined(); + expect(requiredIf(true)(null)).toBeUndefined(); + expect(requiredIf(true)(0)).toBeUndefined(); + expect(requiredIf(true)(1)).toBeUndefined(); + expect(requiredIf(true)({ one: 1 })).toBeUndefined(); + + expect(requiredIf(false)("")).toBeUndefined(); + expect(requiredIf(false)("str")).toBeUndefined(); + expect(requiredIf(false)(true)).toBeUndefined(); + expect(requiredIf(false)(false)).toBeUndefined(); + expect(requiredIf(false)(null)).toBeUndefined(); + expect(requiredIf(false)(0)).toBeUndefined(); + expect(requiredIf(false)(1)).toBeUndefined(); + expect(requiredIf(false)({ one: 1 })).toBeUndefined(); + }); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..15bccd8 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "esModuleInterop": false, + "target": "es6", + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noUnusedLocals": true, + "useUnknownInCatchVariables": false, + "moduleResolution": "node", + "sourceMap": true, + "strictNullChecks": true, + "outDir": "dist", + "baseUrl": ".", + "types": ["node", "jest"] + }, + "include": ["src/**/*"] +}