[
  {
    "path": ".gitattributes",
    "content": "# Set default behavior to automatically convert line endings\n* text=auto eol=lf\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n    open-pull-requests-limit: 10\n\n  - package-ecosystem: \"npm\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n    open-pull-requests-limit: 10\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches:\n     - main\n     - next\n     - 'v*'\n    paths-ignore:\n      - 'docs/**'\n      - '*.md'\n  pull_request:\n    paths-ignore:\n      - 'docs/**'\n      - '*.md'\n\n# This allows a subsequently queued workflow run to interrupt previous runs\nconcurrency:\n    group: \"${{ github.workflow }}-${{ github.event.pull_request.head.label || github.head_ref || github.ref }}\"\n    cancel-in-progress: true\n\npermissions:\n  contents: read\n\njobs:\n  test:\n    permissions:\n      contents: write\n      pull-requests: write\n    uses: fastify/workflows/.github/workflows/plugins-ci.yml@v6\n    with:\n      license-check: true\n      lint: true\n"
  },
  {
    "path": ".github/workflows/lock-threads.yml",
    "content": "name: Lock Threads\n\non:\n  schedule:\n    - cron: '0 0 1 * *'\n  workflow_dispatch:\n\nconcurrency:\n  group: lock\n\npermissions:\n  contents: read\n\njobs:\n  lock-threads:\n    permissions:\n      issues: write\n      pull-requests: write\n    uses: fastify/workflows/.github/workflows/lock-threads.yml@v6\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.pnpm-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Snowpack dependency directory (https://snowpack.dev/)\nweb_modules/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional stylelint cache\n.stylelintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variable files\n.env\n.env.development.local\n.env.test.local\n.env.production.local\n.env.local\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# Next.js build output\n.next\nout\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and not Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# vuepress v2.x temp and cache directory\n.temp\n.cache\n\n# Docusaurus cache and generated files\n.docusaurus\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n.vscode-test\n\n# yarn v2\n.yarn/cache\n.yarn/unplugged\n.yarn/build-state.yml\n.yarn/install-state.gz\n.pnp.*\n\n# Vim swap files\n*.swp\n\n# macOS files\n.DS_Store\n\n# Clinic\n.clinic\n\n# lock files\nbun.lockb\npackage-lock.json\npnpm-lock.yaml\nyarn.lock\n\n# editor files\n.vscode\n.idea\n\n#tap files\n.tap/\n"
  },
  {
    "path": ".npmrc",
    "content": "ignore-scripts=true\npackage-lock=false\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019-present The Fastify team <https://github.com/fastify/fastify#team>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# env-schema\n\n[![CI](https://github.com/fastify/env-schema/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fastify/env-schema/actions/workflows/ci.yml)\n[![NPM version](https://img.shields.io/npm/v/env-schema.svg?style=flat)](https://www.npmjs.com/package/env-schema)\n[![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard)\n\nUtility to check environment variables using [JSON schema](https://json-schema.org/), [Ajv](http://npm.im/ajv), with `.env` file support using\nNode.js built-in `parseEnv` from `node:util`.\n\nSee [supporting resources](#supporting-resources) section for helpful guides on getting started.\n\n## Install\n\n```\nnpm i env-schema\n```\n\n## Usage\n\n```js\nconst envSchema = require('env-schema')\n\nconst schema = {\n  type: 'object',\n  required: [ 'PORT' ],\n  properties: {\n    PORT: {\n      type: 'number',\n      default: 3000\n    }\n  }\n}\n\nconst config = envSchema({\n  schema: schema,\n  data: data, // optional, default: process.env\n  dotenv: true // load .env if it is there, default: false\n  // or you can pass DotenvConfigOptions\n  // dotenv: {\n  //   path: '/custom/path/to/.env'\n  // }\n})\n\nconsole.log(config)\n// output: { PORT: 3000 }\n```\n\nSupported `.env` file options:\n\n- `path` (string): Path to the .env file (default: '.env')\n- `encoding` (string): File encoding (default: 'utf8')\n\n### Custom ajv instance\n\nOptionally, the user can supply their own ajv instance:\n\n```js\nconst envSchema = require('env-schema')\nconst Ajv = require('ajv')\n\nconst schema = {\n  type: 'object',\n  required: [ 'PORT' ],\n  properties: {\n    PORT: {\n      type: 'number',\n      default: 3000\n    }\n  }\n}\n\nconst config = envSchema({\n  schema: schema,\n  data: data,\n  dotenv: true,\n  ajv: new Ajv({\n    allErrors: true,\n    removeAdditional: true,\n    useDefaults: true,\n    coerceTypes: true,\n    allowUnionTypes: true\n  })\n})\n\nconsole.log(config)\n// output: { PORT: 3000 }\n```\n\nIt is possible to enhance the default ajv instance providing the `customOptions` as a function or object parameter.\n\nWhen `customOptions` is an object, the provided ajv options override the default ones:\n\n```js\nconst config = envSchema({\n  schema: schema,\n  data: data,\n  dotenv: true,\n  ajv: {\n    customOptions: {\n      coerceTypes: true\n    }\n  }\n})\n```\n\nWhen `customOptions` is a function, it must return the updated ajv instance.\nThis example shows how to use the `format` keyword in your schemas.\n\n```js\nconst config = envSchema({\n  schema: schema,\n  data: data,\n  dotenv: true,\n  ajv: {\n    customOptions (ajvInstance) {\n      require('ajv-formats')(ajvInstance)\n      return ajvInstance\n    }\n  }\n})\n```\n\n### Order of configuration loading\n\nThe order of precedence for configuration data is as follows, from least\nsignificant to most:\n\n1. Data sourced from `.env` file (when `dotenv` configuration option is set) - parsed using Node.js built-in `parseEnv`\n2. Data sourced from environment variables in `process.env`\n3. Data provided via the `data` configuration option\n\n### Fluent-Schema API\n\nIt is also possible to use [fluent-json-schema](http://npm.im/fluent-json-schema):\n\n```js\nconst envSchema = require('env-schema')\nconst S = require('fluent-json-schema')\n\nconst config = envSchema({\n  schema: S.object().prop('PORT', S.number().default(3000).required()),\n  data: data, // optional, default: process.env\n  dotenv: true, // load .env if it is there, default: false\n  expandEnv: true, // expand environment variables like $VAR or ${VAR}, default: false\n})\n\nconsole.log(config)\n// output: { PORT: 3000 }\n```\n\n**NB** Support for additional properties in the schema is disabled for this plugin, with the `additionalProperties` flag set to `false` internally.\n\n### Custom keywords\n\nThis library supports the following Ajv custom keywords:\n\n#### `separator`\n\nType: `string`\n\nApplies to type: `string`\n\nWhen present, the provided schema value will be split on this value.\n\nExample:\n\n```js\nconst envSchema = require('env-schema')\n\nconst schema = {\n  type: 'object',\n  required: [ 'ALLOWED_HOSTS' ],\n  properties: {\n    ALLOWED_HOSTS: {\n      type: 'string',\n      separator: ','\n    }\n  }\n}\n\nconst data = {\n  ALLOWED_HOSTS: '127.0.0.1,0.0.0.0'\n}\n\nconst config = envSchema({\n  schema: schema,\n  data: data, // optional, default: process.env\n  dotenv: true // load .env if it is there, default: false\n})\n\n// config.ALLOWED_HOSTS => ['127.0.0.1', '0.0.0.0']\n```\n\nThe ajv keyword definition objects can be accessed through the property `keywords` on the `envSchema` function:\n\n```js\nconst envSchema = require('env-schema')\nconst Ajv = require('ajv')\n\nconst schema = {\n  type: 'object',\n  properties: {\n    names: {\n      type: 'string',\n      separator: ','\n    }\n  }\n}\n\nconst config = envSchema({\n  schema: schema,\n  data: data,\n  dotenv: true,\n  ajv: new Ajv({\n    allErrors: true,\n    removeAdditional: true,\n    useDefaults: true,\n    coerceTypes: true,\n    allowUnionTypes: true,\n    keywords: [envSchema.keywords.separator]\n  })\n})\n\nconsole.log(config)\n// output: { names: ['foo', 'bar'] }\n```\n\n### TypeScript\n\nYou can specify the type of your `config`:\n\n```ts\nimport { envSchema, JSONSchemaType } from 'env-schema'\n\ninterface Env {\n  PORT: number;\n}\n\nconst schema: JSONSchemaType<Env> = {\n  type: 'object',\n  required: [ 'PORT' ],\n  properties: {\n    PORT: {\n      type: 'number',\n      default: 3000\n    }\n  }\n}\n\nconst config = envSchema({\n  schema\n})\n```\n\nYou can also use a `JSON Schema` library like `typebox`:\n\n```ts\nimport { envSchema } from 'env-schema'\nimport { Static, Type } from 'typebox'\n\nconst schema = Type.Object({\n  PORT: Type.Number({ default: 3000 })\n})\n\ntype Schema = Static<typeof schema>\n\nconst config = envSchema<Schema>({\n  schema\n})\n```\n\nIf no type is specified the `config` will have the `EnvSchemaData` type.\n\n```ts\nexport type EnvSchemaData = {\n  [key: string]: unknown;\n}\n```\n\n## Supporting resources\n\nThe following section lists helpful reference applications, articles, guides, and other\nresources that demonstrate the use of env-schema in different use cases and scenarios:\n\n- A reference application using [Fastify with env-schema and dotenv](https://github.com/lirantal/fastify-dotenv-envschema-example)\n\n## Acknowledgments\n\nKindly sponsored by [Mia Platform](https://www.mia-platform.eu/) and\n[NearForm](https://nearform.com).\n\n## License\n\nLicensed under [MIT](./LICENSE).\n"
  },
  {
    "path": "eslint.config.js",
    "content": "'use strict'\n\nmodule.exports = require('neostandard')({\n  ignores: require('neostandard').resolveIgnoresFromGitignore(),\n  ts: true\n})\n"
  },
  {
    "path": "index.js",
    "content": "'use strict'\n\nconst Ajv = require('ajv')\nconst { parseEnv } = require('node:util')\nconst { readFileSync } = require('node:fs')\nconst { resolve } = require('node:path')\n\nconst separator = {\n  keyword: 'separator',\n  type: 'string',\n  metaSchema: {\n    type: 'string',\n    description: 'value separator'\n  },\n  modifying: true,\n  valid: true,\n  errors: false,\n  compile: (schema) => (data, { parentData: pData, parentDataProperty: pDataProperty }) => {\n    pData[pDataProperty] = data === '' ? [] : data.split(schema)\n  }\n}\n\nfunction expandVariables (obj) {\n  // Expand environment variables in the format $VAR or ${VAR}\n  for (const key in obj) {\n    const value = obj[key]\n    if (typeof value === 'string') {\n      obj[key] = value.replace(/\\$\\{?([A-Z_][A-Z0-9_]*)\\}?/gi, (match, varName) => {\n        return obj[varName] !== undefined ? obj[varName] : match\n      })\n    }\n  }\n}\n\nconst optsSchema = {\n  type: 'object',\n  required: ['schema'],\n  properties: {\n    schema: { type: 'object', additionalProperties: true },\n    data: {\n      oneOf: [\n        { type: 'array', items: { type: 'object' }, minItems: 1 },\n        { type: 'object' }\n      ],\n      default: {}\n    },\n    env: { type: 'boolean', default: true },\n    dotenv: { type: ['boolean', 'object'], default: false },\n    expandEnv: { type: ['boolean'], default: false },\n    ajv: { type: 'object', additionalProperties: true }\n  }\n}\n\nconst sharedAjvInstance = getDefaultInstance()\n\nconst optsSchemaValidator = sharedAjvInstance.compile(optsSchema)\n\nfunction envSchema (_opts) {\n  const opts = Object.assign({}, _opts)\n\n  if (opts.schema?.[Symbol.for('fluent-schema-object')]) {\n    opts.schema = opts.schema.valueOf()\n  }\n\n  const isOptionValid = optsSchemaValidator(opts)\n  if (!isOptionValid) {\n    const error = new Error(sharedAjvInstance.errorsText(optsSchemaValidator.errors, { dataVar: 'opts' }))\n    error.errors = optsSchemaValidator.errors\n    throw error\n  }\n\n  const { schema } = opts\n  schema.additionalProperties = false\n\n  let { data, dotenv, env, expandEnv } = opts\n  if (!Array.isArray(data)) {\n    data = [data]\n  }\n\n  let parsedEnv\n  if (dotenv) {\n    const dotenvOpts = typeof dotenv === 'object' ? dotenv : {}\n    const path = dotenvOpts.path || '.env'\n    const encoding = dotenvOpts.encoding || 'utf8'\n\n    try {\n      const envFileContent = readFileSync(resolve(path), encoding)\n      parsedEnv = parseEnv(envFileContent)\n    } catch (err) {\n      // Silently ignore if file doesn't exist\n      if (err.code !== 'ENOENT') {\n        throw err\n      }\n      parsedEnv = {}\n    }\n  }\n\n  /* istanbul ignore else */\n  if (env) {\n    data.unshift(process.env)\n  }\n\n  if (parsedEnv) {\n    data.unshift(parsedEnv)\n  }\n\n  const merge = {}\n  data.forEach(d => Object.assign(merge, d))\n\n  if (expandEnv) {\n    expandVariables(merge)\n  }\n\n  const ajv = chooseAjvInstance(sharedAjvInstance, opts.ajv)\n\n  const valid = ajv.validate(schema, merge)\n  if (!valid) {\n    const error = new Error(ajv.errorsText(ajv.errors, { dataVar: 'env' }))\n    error.errors = ajv.errors\n    throw error\n  }\n\n  return merge\n}\n\nfunction chooseAjvInstance (defaultInstance, ajvOpts) {\n  if (ajvOpts instanceof Ajv) {\n    return ajvOpts\n  }\n  let ajv = defaultInstance\n  if (typeof ajvOpts === 'object' && typeof ajvOpts.customOptions === 'function') {\n    ajv = ajvOpts.customOptions(getDefaultInstance())\n    if (!(ajv instanceof Ajv)) {\n      throw new TypeError('customOptions function must return an instance of Ajv')\n    }\n  } else if (typeof ajvOpts === 'object' && typeof ajvOpts.customOptions === 'object') {\n    ajv = getDefaultInstance(ajvOpts.customOptions)\n  }\n  return ajv\n}\n\nfunction getDefaultInstance (overrideOpts = {}) {\n  return new Ajv({\n    allErrors: true,\n    removeAdditional: true,\n    useDefaults: true,\n    coerceTypes: true,\n    allowUnionTypes: true,\n    addUsedSchema: false,\n    keywords: [separator],\n    ...overrideOpts\n  })\n}\n\nenvSchema.keywords = { separator }\n\nmodule.exports = envSchema\nmodule.exports.default = envSchema\nmodule.exports.envSchema = envSchema\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"env-schema\",\n  \"version\": \"7.0.0\",\n  \"description\": \"Validate your env variables using Ajv with .env file support using Node.js built-in parseEnv\",\n  \"main\": \"index.js\",\n  \"type\": \"commonjs\",\n  \"types\": \"types/index.d.ts\",\n  \"scripts\": {\n    \"lint\": \"eslint\",\n    \"lint:fix\": \"eslint --fix\",\n    \"test\": \"npm run test:unit && npm run test:typescript\",\n    \"test:unit\": \"c8 --100 node --test\",\n    \"test:typescript\": \"tsd\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/fastify/env-schema.git\"\n  },\n  \"keywords\": [\n    \"ajv\",\n    \"env\",\n    \"schema\",\n    \"json\",\n    \"dotenv\",\n    \"validate\",\n    \"extract\",\n    \"parseEnv\"\n  ],\n  \"author\": \"Matteo Collina <hello@matteocollina.com>\",\n  \"contributors\": [\n    {\n      \"name\": \"Manuel Spigolon\",\n      \"email\": \"behemoth89@gmail.com\"\n    },\n    {\n      \"name\": \"Maksim Sinik\",\n      \"url\": \"https://maksim.dev\"\n    },\n    {\n      \"name\": \"Aras Abbasi\",\n      \"email\": \"aras.abbasi@gmail.com\"\n    },\n    {\n      \"name\": \"Frazer Smith\",\n      \"email\": \"frazer.dev@icloud.com\",\n      \"url\": \"https://github.com/fdawgs\"\n    }\n  ],\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/fastify/env-schema/issues\"\n  },\n  \"homepage\": \"https://github.com/fastify/env-schema#readme\",\n  \"funding\": [\n    {\n      \"type\": \"github\",\n      \"url\": \"https://github.com/sponsors/fastify\"\n    },\n    {\n      \"type\": \"opencollective\",\n      \"url\": \"https://opencollective.com/fastify\"\n    }\n  ],\n  \"dependencies\": {\n    \"ajv\": \"^8.12.0\"\n  },\n  \"devDependencies\": {\n    \"typebox\": \"^1.0.81\",\n    \"ajv-formats\": \"^3.0.1\",\n    \"c8\": \"^11.0.0\",\n    \"eslint\": \"^9.17.0\",\n    \"fluent-json-schema\": \"^6.0.0\",\n    \"neostandard\": \"^0.13.0\",\n    \"tsd\": \"^0.33.0\"\n  }\n}\n"
  },
  {
    "path": "test/basic.test.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst makeTest = require('./make-test')\nconst { join } = require('node:path')\n\nprocess.env.VALUE_FROM_ENV = 'pippo'\n\nconst tests = [\n  {\n    name: 'empty ok',\n    schema: { type: 'object' },\n    data: { },\n    isOk: true,\n    confExpected: {}\n  },\n  {\n    name: 'simple object - ok',\n    schema: {\n      type: 'object',\n      properties: {\n        PORT: {\n          type: 'string'\n        }\n      }\n    },\n    data: {\n      PORT: '44'\n    },\n    isOk: true,\n    confExpected: {\n      PORT: '44'\n    }\n  },\n  {\n    name: 'simple object - ok - coerce value',\n    schema: {\n      type: 'object',\n      properties: {\n        PORT: {\n          type: 'integer'\n        }\n      }\n    },\n    data: {\n      PORT: '44'\n    },\n    isOk: true,\n    confExpected: {\n      PORT: 44\n    }\n  },\n  {\n    name: 'simple object - ok - remove additional properties',\n    schema: {\n      type: 'object',\n      properties: {\n        PORT: {\n          type: 'integer'\n        }\n      }\n    },\n    data: {\n      PORT: '44',\n      ANOTHER_PORT: '55'\n    },\n    isOk: true,\n    confExpected: {\n      PORT: 44\n    }\n  },\n  {\n    name: 'simple object - ok - use default',\n    schema: {\n      type: 'object',\n      properties: {\n        PORT: {\n          type: 'integer',\n          default: 5555\n        }\n      }\n    },\n    data: { },\n    isOk: true,\n    confExpected: {\n      PORT: 5555\n    }\n  },\n  {\n    name: 'simple object - ok - required + default',\n    schema: {\n      type: 'object',\n      required: ['PORT'],\n      properties: {\n        PORT: {\n          type: 'integer',\n          default: 6666\n        }\n      }\n    },\n    data: { },\n    isOk: true,\n    confExpected: {\n      PORT: 6666\n    }\n  },\n  {\n    name: 'simple object - ok - allow array',\n    schema: {\n      type: 'object',\n      required: ['PORT'],\n      properties: {\n        PORT: {\n          type: 'integer',\n          default: 6666\n        }\n      }\n    },\n    data: [{ }],\n    isOk: true,\n    confExpected: {\n      PORT: 6666\n    }\n  },\n  {\n    name: 'simple object - ok - merge multiple object + env',\n    schema: {\n      type: 'object',\n      required: ['PORT', 'MONGODB_URL'],\n      properties: {\n        PORT: {\n          type: 'integer',\n          default: 6666\n        },\n        MONGODB_URL: {\n          type: 'string'\n        },\n        VALUE_FROM_ENV: {\n          type: 'string'\n        }\n      }\n    },\n    data: [{ PORT: 3333 }, { MONGODB_URL: 'mongodb://localhost/pippo' }],\n    isOk: true,\n    confExpected: {\n      PORT: 3333,\n      MONGODB_URL: 'mongodb://localhost/pippo',\n      VALUE_FROM_ENV: 'pippo'\n    }\n  },\n  {\n    name: 'simple object - ok - load only from env',\n    schema: {\n      type: 'object',\n      required: ['VALUE_FROM_ENV'],\n      properties: {\n        VALUE_FROM_ENV: {\n          type: 'string'\n        }\n      }\n    },\n    data: undefined,\n    isOk: true,\n    confExpected: {\n      VALUE_FROM_ENV: 'pippo'\n    }\n  },\n  {\n    name: 'simple object - ok - opts override environment',\n    schema: {\n      type: 'object',\n      required: ['VALUE_FROM_ENV'],\n      properties: {\n        VALUE_FROM_ENV: {\n          type: 'string'\n        }\n      }\n    },\n    data: { VALUE_FROM_ENV: 'pluto' },\n    isOk: true,\n    confExpected: {\n      VALUE_FROM_ENV: 'pluto'\n    }\n  },\n  {\n    name: 'simple object - ok - load only from .env',\n    schema: {\n      type: 'object',\n      required: ['VALUE_FROM_DOTENV'],\n      properties: {\n        VALUE_FROM_DOTENV: {\n          type: 'string'\n        }\n      }\n    },\n    data: undefined,\n    isOk: true,\n    dotenv: { path: join(__dirname, '.env') },\n    confExpected: {\n      VALUE_FROM_DOTENV: 'look ma'\n    }\n  },\n  {\n    name: 'simple object - KO',\n    schema: {\n      type: 'object',\n      required: ['PORT'],\n      properties: {\n        PORT: {\n          type: 'integer'\n        }\n      }\n    },\n    data: { },\n    isOk: false,\n    errorMessage: 'env must have required property \\'PORT\\''\n  },\n  {\n    name: 'simple object - invalid data',\n    schema: {\n      type: 'object',\n      required: ['PORT'],\n      properties: {\n        PORT: {\n          type: 'integer'\n        }\n      }\n    },\n    data: [],\n    isOk: false,\n    errorMessage: 'opts/data must NOT have fewer than 1 items, opts/data must be object, opts/data must match exactly one schema in oneOf'\n  },\n  {\n    name: 'simple object - ok - with separator',\n    schema: {\n      type: 'object',\n      required: ['ALLOWED_HOSTS'],\n      properties: {\n        ALLOWED_HOSTS: {\n          type: 'string',\n          separator: ','\n        }\n      }\n    },\n    data: {\n      ALLOWED_HOSTS: '127.0.0.1,0.0.0.0'\n    },\n    isOk: true,\n    confExpected: {\n      ALLOWED_HOSTS: ['127.0.0.1', '0.0.0.0']\n    }\n  },\n  {\n    name: 'simple object - ok - with separator - only one value',\n    schema: {\n      type: 'object',\n      required: ['ALLOWED_HOSTS'],\n      properties: {\n        ALLOWED_HOSTS: {\n          type: 'string',\n          separator: ','\n        }\n      }\n    },\n    data: {\n      ALLOWED_HOSTS: '127.0.0.1'\n    },\n    isOk: true,\n    confExpected: {\n      ALLOWED_HOSTS: ['127.0.0.1']\n    }\n  },\n  {\n    name: 'simple object - ok - with separator - no values',\n    schema: {\n      type: 'object',\n      required: ['ALLOWED_HOSTS'],\n      properties: {\n        ALLOWED_HOSTS: {\n          type: 'string',\n          separator: ','\n        }\n      }\n    },\n    data: {\n      ALLOWED_HOSTS: ''\n    },\n    isOk: true,\n    confExpected: {\n      ALLOWED_HOSTS: []\n    }\n  },\n  {\n    name: 'simple object - KO - with separator',\n    schema: {\n      type: 'object',\n      required: ['ALLOWED_HOSTS'],\n      properties: {\n        ALLOWED_HOSTS: {\n          type: 'string',\n          separator: ','\n        }\n      }\n    },\n    data: {},\n    isOk: false,\n    errorMessage: 'env must have required property \\'ALLOWED_HOSTS\\''\n  },\n  {\n    name: 'simple object - KO - multiple required properties',\n    schema: {\n      type: 'object',\n      required: ['A', 'B', 'C'],\n      properties: {\n        A: { type: 'string' },\n        B: { type: 'string' },\n        C: { type: 'string' },\n        D: { type: 'string' }\n      }\n    },\n    data: {},\n    isOk: false,\n    errorMessage: 'env must have required property \\'A\\', env must have required property \\'B\\', env must have required property \\'C\\''\n  },\n  {\n    name: 'simple object - ok - dotenv with non-existent file',\n    schema: {\n      type: 'object',\n      properties: {\n        PORT: {\n          type: 'integer',\n          default: 3000\n        }\n      }\n    },\n    data: { PORT: 8080 },\n    dotenv: { path: join(__dirname, '.env.nonexistent') },\n    isOk: true,\n    confExpected: {\n      PORT: 8080\n    }\n  },\n  {\n    name: 'simple object - ok - dotenv true with no file',\n    schema: {\n      type: 'object',\n      properties: {\n        PORT: {\n          type: 'integer',\n          default: 3000\n        }\n      }\n    },\n    data: { PORT: 9090 },\n    dotenv: true,\n    isOk: true,\n    confExpected: {\n      PORT: 9090\n    }\n  },\n  {\n    name: 'simple object - KO - dotenv with directory instead of file',\n    schema: {\n      type: 'object',\n      properties: {\n        PORT: {\n          type: 'integer',\n          default: 3000\n        }\n      }\n    },\n    data: { PORT: 8080 },\n    dotenv: { path: __dirname },\n    isOk: false,\n    errorMessage: 'EISDIR: illegal operation on a directory, read'\n  }\n]\n\ntests.forEach(function (testConf) {\n  test(testConf.name, t => {\n    const options = {\n      schema: testConf.schema,\n      data: testConf.data,\n      dotenv: testConf.dotenv,\n      dotenvConfig: testConf.dotenvConfig\n    }\n\n    makeTest(t, options, testConf.isOk, testConf.confExpected, testConf.errorMessage)\n  })\n})\n"
  },
  {
    "path": "test/custom-ajv.test.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst Ajv = require('ajv')\nconst makeTest = require('./make-test')\nconst { join } = require('node:path')\n\nprocess.env.VALUE_FROM_ENV = 'pippo'\n\nconst tests = [\n  {\n    name: 'empty ok',\n    schema: { type: 'object' },\n    data: { },\n    isOk: true,\n    confExpected: {}\n  },\n  {\n    name: 'simple object - ok',\n    schema: {\n      type: 'object',\n      properties: {\n        PORT: {\n          type: 'string'\n        }\n      }\n    },\n    data: {\n      PORT: '44'\n    },\n    isOk: true,\n    confExpected: {\n      PORT: '44'\n    }\n  },\n  {\n    name: 'simple object - ok - coerce value',\n    schema: {\n      type: 'object',\n      properties: {\n        PORT: {\n          type: 'integer'\n        }\n      }\n    },\n    data: {\n      PORT: '44'\n    },\n    isOk: true,\n    confExpected: {\n      PORT: 44\n    }\n  },\n  {\n    name: 'simple object - ok - remove additional properties',\n    schema: {\n      type: 'object',\n      properties: {\n        PORT: {\n          type: 'integer'\n        }\n      }\n    },\n    data: {\n      PORT: '44',\n      ANOTHER_PORT: '55'\n    },\n    isOk: true,\n    confExpected: {\n      PORT: 44\n    }\n  },\n  {\n    name: 'simple object - ok - use default',\n    schema: {\n      type: 'object',\n      properties: {\n        PORT: {\n          type: 'integer',\n          default: 5555\n        }\n      }\n    },\n    data: { },\n    isOk: true,\n    confExpected: {\n      PORT: 5555\n    }\n  },\n  {\n    name: 'simple object - ok - required + default',\n    schema: {\n      type: 'object',\n      required: ['PORT'],\n      properties: {\n        PORT: {\n          type: 'integer',\n          default: 6666\n        }\n      }\n    },\n    data: { },\n    isOk: true,\n    confExpected: {\n      PORT: 6666\n    }\n  },\n  {\n    name: 'simple object - ok - allow array',\n    schema: {\n      type: 'object',\n      required: ['PORT'],\n      properties: {\n        PORT: {\n          type: 'integer',\n          default: 6666\n        }\n      }\n    },\n    data: [{ }],\n    isOk: true,\n    confExpected: {\n      PORT: 6666\n    }\n  },\n  {\n    name: 'simple object - ok - merge multiple object + env',\n    schema: {\n      type: 'object',\n      required: ['PORT', 'MONGODB_URL'],\n      properties: {\n        PORT: {\n          type: 'integer',\n          default: 6666\n        },\n        MONGODB_URL: {\n          type: 'string'\n        },\n        VALUE_FROM_ENV: {\n          type: 'string'\n        }\n      }\n    },\n    data: [{ PORT: 3333 }, { MONGODB_URL: 'mongodb://localhost/pippo' }],\n    isOk: true,\n    confExpected: {\n      PORT: 3333,\n      MONGODB_URL: 'mongodb://localhost/pippo',\n      VALUE_FROM_ENV: 'pippo'\n    }\n  },\n  {\n    name: 'simple object - ok - load only from env',\n    schema: {\n      type: 'object',\n      required: ['VALUE_FROM_ENV'],\n      properties: {\n        VALUE_FROM_ENV: {\n          type: 'string'\n        }\n      }\n    },\n    data: undefined,\n    isOk: true,\n    confExpected: {\n      VALUE_FROM_ENV: 'pippo'\n    }\n  },\n  {\n    name: 'simple object - ok - opts override environment',\n    schema: {\n      type: 'object',\n      required: ['VALUE_FROM_ENV'],\n      properties: {\n        VALUE_FROM_ENV: {\n          type: 'string'\n        }\n      }\n    },\n    data: { VALUE_FROM_ENV: 'pluto' },\n    isOk: true,\n    confExpected: {\n      VALUE_FROM_ENV: 'pluto'\n    }\n  },\n  {\n    name: 'simple object - ok - load only from .env',\n    schema: {\n      type: 'object',\n      required: ['VALUE_FROM_DOTENV'],\n      properties: {\n        VALUE_FROM_DOTENV: {\n          type: 'string'\n        }\n      }\n    },\n    data: undefined,\n    isOk: true,\n    dotenv: { path: join(__dirname, '.env') },\n    confExpected: {\n      VALUE_FROM_DOTENV: 'look ma'\n    }\n  },\n  {\n    name: 'simple object - KO',\n    schema: {\n      type: 'object',\n      required: ['PORT'],\n      properties: {\n        PORT: {\n          type: 'integer'\n        }\n      }\n    },\n    data: { },\n    isOk: false,\n    errorMessage: 'env must have required property \\'PORT\\''\n  },\n  {\n    name: 'simple object - invalid data',\n    schema: {\n      type: 'object',\n      required: ['PORT'],\n      properties: {\n        PORT: {\n          type: 'integer'\n        }\n      }\n    },\n    data: [],\n    isOk: false,\n    errorMessage: 'opts/data must NOT have fewer than 1 items, opts/data must be object, opts/data must match exactly one schema in oneOf'\n  }\n]\n\nconst ajv = new Ajv({\n  allErrors: true,\n  removeAdditional: true,\n  useDefaults: true,\n  coerceTypes: true,\n  allowUnionTypes: true\n})\n\ntests.forEach(function (testConf) {\n  test(testConf.name, t => {\n    const options = {\n      schema: testConf.schema,\n      data: testConf.data,\n      dotenv: testConf.dotenv,\n      dotenvConfig: testConf.dotenvConfig,\n      ajv\n    }\n\n    makeTest(t, options, testConf.isOk, testConf.confExpected, testConf.errorMessage)\n  })\n})\n\nconst noCoercionTest = {\n  name: 'simple object - not ok - should NOT coerce value',\n  schema: {\n    type: 'object',\n    properties: {\n      PORT: {\n        type: 'integer'\n      }\n    }\n  },\n  data: {\n    PORT: '44'\n  },\n  isOk: false,\n  errorMessage: 'env/PORT must be integer',\n  confExpected: {\n    PORT: 44\n  }\n}\n\nconst strictValidator = new Ajv({\n  allErrors: true,\n  removeAdditional: true,\n  useDefaults: true,\n  coerceTypes: false,\n  allowUnionTypes: true\n});\n\n[noCoercionTest].forEach(function (testConf) {\n  test(testConf.name, t => {\n    const options = {\n      schema: testConf.schema,\n      data: testConf.data,\n      dotenv: testConf.dotenv,\n      dotenvConfig: testConf.dotenvConfig,\n      ajv: strictValidator\n    }\n\n    makeTest(t, options, testConf.isOk, testConf.confExpected, testConf.errorMessage)\n  })\n})\n\ntest('ajv enhancement', async t => {\n  t.plan(3)\n  const testCaseFn = {\n    schema: {\n      type: 'object',\n      required: ['MONGODB_URL'],\n      properties: {\n        MONGODB_URL: {\n          type: 'string',\n          format: 'uri'\n        }\n      }\n    },\n    data: [{ PORT: 3333 }, { MONGODB_URL: 'mongodb://localhost/pippo' }],\n    isOk: true,\n    confExpected: {\n      MONGODB_URL: 'mongodb://localhost/pippo'\n    }\n  }\n  const testCaseObj = {\n    schema: {\n      type: 'object',\n      required: ['PORT'],\n      properties: {\n        PORT: {\n          type: 'string',\n        }\n      }\n    },\n    data: [{ PORT: 3333 }],\n    isOk: true,\n    confExpected: {\n      PORT: '3333'\n    }\n  }\n\n  await t.test('customOptions fn return', async t => {\n    const options = {\n      schema: testCaseFn.schema,\n      data: testCaseFn.data,\n      ajv: {\n        customOptions (ajvInstance) {\n          require('ajv-formats')(ajvInstance)\n          return ajvInstance\n        }\n      }\n    }\n    makeTest(t, options, testCaseFn.isOk, testCaseFn.confExpected)\n  })\n\n  await t.test('customOptions fn no return', async t => {\n    const options = {\n      schema: testCaseFn.schema,\n      data: testCaseFn.data,\n      ajv: {\n        customOptions (_ajvInstance) {\n          // do nothing\n        }\n      }\n    }\n    makeTest(t, options, false, undefined, 'customOptions function must return an instance of Ajv')\n  })\n\n  await t.test('customOptions object override', async t => {\n    const options = {\n      schema: testCaseObj.schema,\n      data: testCaseObj.data,\n      ajv: {\n        customOptions: {\n          coerceTypes: true,\n        }\n      }\n    }\n    makeTest(t, options, testCaseObj.isOk, testCaseObj.confExpected)\n  })\n})\n"
  },
  {
    "path": "test/expand.test.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst makeTest = require('./make-test')\nconst { join } = require('node:path')\n\nprocess.env.K8S_NAMESPACE = 'pippo'\nprocess.env.K8S_CLUSTERID = 'pluto'\nprocess.env.URL = 'https://prefix.$K8S_NAMESPACE.$K8S_CLUSTERID.my.domain.com'\nprocess.env.PASSWORD = 'password'\n\nconst tests = [\n  {\n    name: 'simple object - ok - expandEnv',\n    schema: {\n      type: 'object',\n      properties: {\n        URL: {\n          type: 'string'\n        },\n        K8S_NAMESPACE: {\n          type: 'string'\n        }\n      }\n    },\n    expandEnv: true,\n    isOk: true,\n    confExpected: {\n      URL: 'https://prefix.pippo.pluto.my.domain.com',\n      K8S_NAMESPACE: 'pippo'\n    }\n  },\n  {\n    name: 'simple object - ok - expandEnv use dotenv',\n    schema: {\n      type: 'object',\n      properties: {\n        EXPANDED_VALUE_FROM_DOTENV: {\n          type: 'string'\n        }\n      }\n    },\n    expandEnv: true,\n    isOk: true,\n    dotenv: { path: join(__dirname, '.env') },\n    confExpected: {\n      EXPANDED_VALUE_FROM_DOTENV: 'the password is password!'\n    }\n  },\n  {\n    name: 'simple object - ok - expandEnv works when passed an arbitrary new object based on process.env as data',\n    schema: {\n      type: 'object',\n      properties: {\n        URL: {\n          type: 'string'\n        },\n        K8S_NAMESPACE: {\n          type: 'string'\n        }\n      }\n    },\n    expandEnv: true,\n    isOk: true,\n    data: {\n      ...process.env,\n      K8S_NAMESPACE: 'hello'\n    },\n    confExpected: {\n      URL: 'https://prefix.hello.pluto.my.domain.com',\n      K8S_NAMESPACE: 'hello'\n    }\n  },\n  {\n    name: 'simple object - ok - expandEnv with undefined variable keeps placeholder',\n    schema: {\n      type: 'object',\n      properties: {\n        MESSAGE: {\n          type: 'string'\n        }\n      }\n    },\n    expandEnv: true,\n    isOk: true,\n    data: {\n      MESSAGE: 'Hello $UNDEFINED_VAR world'\n    },\n    confExpected: {\n      MESSAGE: 'Hello $UNDEFINED_VAR world'\n    }\n  }\n]\n\ntests.forEach(function (testConf) {\n  test(testConf.name, t => {\n    const options = {\n      schema: testConf.schema,\n      data: testConf.data,\n      dotenv: testConf.dotenv,\n      dotenvConfig: testConf.dotenvConfig,\n      expandEnv: testConf.expandEnv\n    }\n\n    makeTest(t, options, testConf.isOk, testConf.confExpected, testConf.errorMessage)\n  })\n})\n"
  },
  {
    "path": "test/fluent-schema.test.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\n\nif (parseInt(process.versions.node.split('.', 1)[0]) <= 8) {\n  test.skip('not supported')\n} else {\n  run()\n}\n\nfunction run () {\n  const S = require('fluent-json-schema')\n  const makeTest = require('./make-test')\n\n  test('simple object - fluent-json-schema', t => {\n    const options = {\n      schema: S.object().prop('PORT', S.string()),\n      data: {\n        PORT: '44'\n      }\n    }\n\n    makeTest(t, options, true, {\n      PORT: '44'\n    })\n  })\n}\n"
  },
  {
    "path": "test/make-test.js",
    "content": "'use strict'\n\nconst envSchema = require('../index')\n\nfunction makeTest (t, options, isOk, confExpected, errorMessage) {\n  t.plan(1)\n  options = Object.assign({ confKey: 'config' }, options)\n\n  try {\n    const conf = envSchema(options)\n    t.assert.deepStrictEqual(conf, confExpected)\n  } catch (err) {\n    if (isOk) {\n      t.assert.fail(err)\n      return\n    }\n    t.assert.strictEqual(err.message, errorMessage)\n  }\n}\n\nmodule.exports = makeTest\n"
  },
  {
    "path": "test/no-global.test.js",
    "content": "'use strict'\n\nconst { test } = require('node:test')\nconst envSchema = require('../index')\n\ntest('no globals', t => {\n  t.plan(2)\n\n  const options = {\n    confKey: 'secrets',\n    data: {\n      MONGO_URL: 'good'\n    },\n    schema: {\n      $id: 'schema:dotenv',\n      type: 'object',\n      required: ['MONGO_URL'],\n      properties: {\n        PORT: {\n          type: 'integer',\n          default: 3000\n        },\n        MONGO_URL: {\n          type: 'string'\n        }\n      }\n    }\n  }\n\n  {\n    const conf = envSchema(JSON.parse(JSON.stringify(options)))\n    t.assert.deepStrictEqual(conf, { MONGO_URL: 'good', PORT: 3000 })\n  }\n  {\n    const conf = envSchema(JSON.parse(JSON.stringify(options)))\n    t.assert.deepStrictEqual(conf, { MONGO_URL: 'good', PORT: 3000 })\n  }\n})\n"
  },
  {
    "path": "types/index.d.ts",
    "content": "import Ajv, { KeywordDefinition, JSONSchemaType } from 'ajv'\nimport { AnySchema } from 'ajv/dist/core'\n\n/**\n * Options for loading .env files\n */\ninterface DotenvOptions {\n  /**\n   * Path to .env file (default: '.env')\n   */\n  path?: string;\n  /**\n   * Encoding of .env file (default: 'utf8')\n   */\n  encoding?: 'ascii' | 'utf8' | 'utf-8' | 'utf16le' | 'ucs2' | 'ucs-2' | 'base64' | 'latin1' | 'binary' | 'hex';\n}\n\ntype EnvSchema = typeof envSchema\n\ndeclare namespace envSchema {\n  export type { JSONSchemaType }\n\n  export type EnvSchemaData = {\n    [key: string]: unknown;\n  }\n\n  export type EnvSchemaOpt<T = EnvSchemaData> = {\n    schema?: JSONSchemaType<T> | AnySchema;\n    data?: [EnvSchemaData, ...EnvSchemaData[]] | EnvSchemaData;\n    env?: boolean;\n    dotenv?: boolean | DotenvOptions;\n    expandEnv?: boolean;\n    ajv?:\n    | Ajv\n    | {\n      customOptions(ajvInstance: Ajv): Ajv;\n    };\n  }\n\n  export const keywords: {\n    separator: KeywordDefinition\n  }\n\n  export const envSchema: EnvSchema\n  export { envSchema as default }\n}\n\ndeclare function envSchema<T = envSchema.EnvSchemaData> (_opts?: envSchema.EnvSchemaOpt<T>): T\nexport = envSchema\n"
  },
  {
    "path": "types/index.test-d.ts",
    "content": "import { expectError, expectType } from 'tsd'\nimport envSchema, {\n  EnvSchemaData,\n  EnvSchemaOpt,\n  keywords,\n  envSchema as envSchemaNamed,\n  default as envSchemaDefault,\n} from '..'\nimport Ajv, { KeywordDefinition, JSONSchemaType } from 'ajv'\nimport { Static, Type } from 'typebox'\n\ninterface EnvData {\n  PORT: number;\n}\n\nconst schemaWithType: JSONSchemaType<EnvData> = {\n  type: 'object',\n  required: ['PORT'],\n  properties: {\n    PORT: {\n      type: 'number',\n      default: 3000,\n    },\n  },\n}\n\nconst schemaTypebox = Type.Object({\n  PORT: Type.Number({ default: 3000 }),\n})\n\ntype SchemaTypebox = Static<typeof schemaTypebox>\n\nconst data = {\n  foo: 'bar',\n}\n\nexpectType<EnvSchemaData>(envSchema())\nexpectType<EnvSchemaData>(envSchemaNamed())\nexpectType<EnvSchemaData>(envSchemaDefault())\n\nconst emptyOpt: EnvSchemaOpt = {}\nexpectType<EnvSchemaOpt>(emptyOpt)\n\nconst optWithSchemaTypebox: EnvSchemaOpt = {\n  schema: schemaTypebox,\n}\nexpectType<EnvSchemaOpt>(optWithSchemaTypebox)\n\nconst optWithSchemaWithType: EnvSchemaOpt<EnvData> = {\n  schema: schemaWithType,\n}\nexpectType<EnvSchemaOpt<EnvData>>(optWithSchemaWithType)\n\nconst optWithData: EnvSchemaOpt = {\n  data,\n}\nexpectType<EnvSchemaOpt>(optWithData)\n\nexpectError<EnvSchemaOpt>({\n  data: [], // min 1 item\n})\n\nconst optWithArrayData: EnvSchemaOpt = {\n  data: [{}],\n}\nexpectType<EnvSchemaOpt>(optWithArrayData)\n\nconst optWithMultipleItemArrayData: EnvSchemaOpt = {\n  data: [{}, {}],\n}\nexpectType<EnvSchemaOpt>(optWithMultipleItemArrayData)\n\nconst optWithDotEnvBoolean: EnvSchemaOpt = {\n  dotenv: true,\n}\nexpectType<EnvSchemaOpt>(optWithDotEnvBoolean)\n\nconst optWithDotEnvOpt: EnvSchemaOpt = {\n  dotenv: {},\n}\nexpectType<EnvSchemaOpt>(optWithDotEnvOpt)\n\nconst optWithEnvExpand: EnvSchemaOpt = {\n  expandEnv: true,\n}\nexpectType<EnvSchemaOpt>(optWithEnvExpand)\n\nconst optWithAjvInstance: EnvSchemaOpt = {\n  ajv: new Ajv(),\n}\nexpectType<EnvSchemaOpt>(optWithAjvInstance)\nexpectType<KeywordDefinition>(envSchema.keywords.separator)\n\nconst optWithAjvCustomOptions: EnvSchemaOpt = {\n  ajv: {\n    customOptions (_ajvInstance: Ajv): Ajv {\n      return new Ajv()\n    },\n  },\n}\nexpectType<EnvSchemaOpt>(optWithAjvCustomOptions)\nexpectError<EnvSchemaOpt>({\n  ajv: {\n    customOptions (_ajvInstance: Ajv) {},\n  },\n})\n\nconst envSchemaWithType = envSchema({ schema: schemaWithType })\nexpectType<EnvData>(envSchemaWithType)\n\nconst envSchemaTypebox = envSchema<SchemaTypebox>({ schema: schemaTypebox })\nexpectType<SchemaTypebox>(envSchemaTypebox)\n\nexpectType<KeywordDefinition>(keywords.separator)\nexpectType<KeywordDefinition>(envSchema.keywords.separator)\n"
  }
]