Full Code of fastify/env-schema for AI

main 9824690a8f88 cached
19 files
39.0 KB
11.3k tokens
16 symbols
1 requests
Download .txt
Repository: fastify/env-schema
Branch: main
Commit: 9824690a8f88
Files: 19
Total size: 39.0 KB

Directory structure:
gitextract_31c2uptn/

├── .gitattributes
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── ci.yml
│       └── lock-threads.yml
├── .gitignore
├── .npmrc
├── LICENSE
├── README.md
├── eslint.config.js
├── index.js
├── package.json
├── test/
│   ├── basic.test.js
│   ├── custom-ajv.test.js
│   ├── expand.test.js
│   ├── fluent-schema.test.js
│   ├── make-test.js
│   └── no-global.test.js
└── types/
    ├── index.d.ts
    └── index.test-d.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
# Set default behavior to automatically convert line endings
* text=auto eol=lf


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "monthly"
    open-pull-requests-limit: 10

  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "monthly"
    open-pull-requests-limit: 10


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  push:
    branches:
     - main
     - next
     - 'v*'
    paths-ignore:
      - 'docs/**'
      - '*.md'
  pull_request:
    paths-ignore:
      - 'docs/**'
      - '*.md'

# This allows a subsequently queued workflow run to interrupt previous runs
concurrency:
    group: "${{ github.workflow }}-${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
    cancel-in-progress: true

permissions:
  contents: read

jobs:
  test:
    permissions:
      contents: write
      pull-requests: write
    uses: fastify/workflows/.github/workflows/plugins-ci.yml@v6
    with:
      license-check: true
      lint: true


================================================
FILE: .github/workflows/lock-threads.yml
================================================
name: Lock Threads

on:
  schedule:
    - cron: '0 0 1 * *'
  workflow_dispatch:

concurrency:
  group: lock

permissions:
  contents: read

jobs:
  lock-threads:
    permissions:
      issues: write
      pull-requests: write
    uses: fastify/workflows/.github/workflows/lock-threads.yml@v6


================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp
.cache

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

# Vim swap files
*.swp

# macOS files
.DS_Store

# Clinic
.clinic

# lock files
bun.lockb
package-lock.json
pnpm-lock.yaml
yarn.lock

# editor files
.vscode
.idea

#tap files
.tap/


================================================
FILE: .npmrc
================================================
ignore-scripts=true
package-lock=false


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019-present The Fastify team <https://github.com/fastify/fastify#team>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# env-schema

[![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)
[![NPM version](https://img.shields.io/npm/v/env-schema.svg?style=flat)](https://www.npmjs.com/package/env-schema)
[![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard)

Utility to check environment variables using [JSON schema](https://json-schema.org/), [Ajv](http://npm.im/ajv), with `.env` file support using
Node.js built-in `parseEnv` from `node:util`.

See [supporting resources](#supporting-resources) section for helpful guides on getting started.

## Install

```
npm i env-schema
```

## Usage

```js
const envSchema = require('env-schema')

const schema = {
  type: 'object',
  required: [ 'PORT' ],
  properties: {
    PORT: {
      type: 'number',
      default: 3000
    }
  }
}

const config = envSchema({
  schema: schema,
  data: data, // optional, default: process.env
  dotenv: true // load .env if it is there, default: false
  // or you can pass DotenvConfigOptions
  // dotenv: {
  //   path: '/custom/path/to/.env'
  // }
})

console.log(config)
// output: { PORT: 3000 }
```

Supported `.env` file options:

- `path` (string): Path to the .env file (default: '.env')
- `encoding` (string): File encoding (default: 'utf8')

### Custom ajv instance

Optionally, the user can supply their own ajv instance:

```js
const envSchema = require('env-schema')
const Ajv = require('ajv')

const schema = {
  type: 'object',
  required: [ 'PORT' ],
  properties: {
    PORT: {
      type: 'number',
      default: 3000
    }
  }
}

const config = envSchema({
  schema: schema,
  data: data,
  dotenv: true,
  ajv: new Ajv({
    allErrors: true,
    removeAdditional: true,
    useDefaults: true,
    coerceTypes: true,
    allowUnionTypes: true
  })
})

console.log(config)
// output: { PORT: 3000 }
```

It is possible to enhance the default ajv instance providing the `customOptions` as a function or object parameter.

When `customOptions` is an object, the provided ajv options override the default ones:

```js
const config = envSchema({
  schema: schema,
  data: data,
  dotenv: true,
  ajv: {
    customOptions: {
      coerceTypes: true
    }
  }
})
```

When `customOptions` is a function, it must return the updated ajv instance.
This example shows how to use the `format` keyword in your schemas.

```js
const config = envSchema({
  schema: schema,
  data: data,
  dotenv: true,
  ajv: {
    customOptions (ajvInstance) {
      require('ajv-formats')(ajvInstance)
      return ajvInstance
    }
  }
})
```

### Order of configuration loading

The order of precedence for configuration data is as follows, from least
significant to most:

1. Data sourced from `.env` file (when `dotenv` configuration option is set) - parsed using Node.js built-in `parseEnv`
2. Data sourced from environment variables in `process.env`
3. Data provided via the `data` configuration option

### Fluent-Schema API

It is also possible to use [fluent-json-schema](http://npm.im/fluent-json-schema):

```js
const envSchema = require('env-schema')
const S = require('fluent-json-schema')

const config = envSchema({
  schema: S.object().prop('PORT', S.number().default(3000).required()),
  data: data, // optional, default: process.env
  dotenv: true, // load .env if it is there, default: false
  expandEnv: true, // expand environment variables like $VAR or ${VAR}, default: false
})

console.log(config)
// output: { PORT: 3000 }
```

**NB** Support for additional properties in the schema is disabled for this plugin, with the `additionalProperties` flag set to `false` internally.

### Custom keywords

This library supports the following Ajv custom keywords:

#### `separator`

Type: `string`

Applies to type: `string`

When present, the provided schema value will be split on this value.

Example:

```js
const envSchema = require('env-schema')

const schema = {
  type: 'object',
  required: [ 'ALLOWED_HOSTS' ],
  properties: {
    ALLOWED_HOSTS: {
      type: 'string',
      separator: ','
    }
  }
}

const data = {
  ALLOWED_HOSTS: '127.0.0.1,0.0.0.0'
}

const config = envSchema({
  schema: schema,
  data: data, // optional, default: process.env
  dotenv: true // load .env if it is there, default: false
})

// config.ALLOWED_HOSTS => ['127.0.0.1', '0.0.0.0']
```

The ajv keyword definition objects can be accessed through the property `keywords` on the `envSchema` function:

```js
const envSchema = require('env-schema')
const Ajv = require('ajv')

const schema = {
  type: 'object',
  properties: {
    names: {
      type: 'string',
      separator: ','
    }
  }
}

const config = envSchema({
  schema: schema,
  data: data,
  dotenv: true,
  ajv: new Ajv({
    allErrors: true,
    removeAdditional: true,
    useDefaults: true,
    coerceTypes: true,
    allowUnionTypes: true,
    keywords: [envSchema.keywords.separator]
  })
})

console.log(config)
// output: { names: ['foo', 'bar'] }
```

### TypeScript

You can specify the type of your `config`:

```ts
import { envSchema, JSONSchemaType } from 'env-schema'

interface Env {
  PORT: number;
}

const schema: JSONSchemaType<Env> = {
  type: 'object',
  required: [ 'PORT' ],
  properties: {
    PORT: {
      type: 'number',
      default: 3000
    }
  }
}

const config = envSchema({
  schema
})
```

You can also use a `JSON Schema` library like `typebox`:

```ts
import { envSchema } from 'env-schema'
import { Static, Type } from 'typebox'

const schema = Type.Object({
  PORT: Type.Number({ default: 3000 })
})

type Schema = Static<typeof schema>

const config = envSchema<Schema>({
  schema
})
```

If no type is specified the `config` will have the `EnvSchemaData` type.

```ts
export type EnvSchemaData = {
  [key: string]: unknown;
}
```

## Supporting resources

The following section lists helpful reference applications, articles, guides, and other
resources that demonstrate the use of env-schema in different use cases and scenarios:

- A reference application using [Fastify with env-schema and dotenv](https://github.com/lirantal/fastify-dotenv-envschema-example)

## Acknowledgments

Kindly sponsored by [Mia Platform](https://www.mia-platform.eu/) and
[NearForm](https://nearform.com).

## License

Licensed under [MIT](./LICENSE).


================================================
FILE: eslint.config.js
================================================
'use strict'

module.exports = require('neostandard')({
  ignores: require('neostandard').resolveIgnoresFromGitignore(),
  ts: true
})


================================================
FILE: index.js
================================================
'use strict'

const Ajv = require('ajv')
const { parseEnv } = require('node:util')
const { readFileSync } = require('node:fs')
const { resolve } = require('node:path')

const separator = {
  keyword: 'separator',
  type: 'string',
  metaSchema: {
    type: 'string',
    description: 'value separator'
  },
  modifying: true,
  valid: true,
  errors: false,
  compile: (schema) => (data, { parentData: pData, parentDataProperty: pDataProperty }) => {
    pData[pDataProperty] = data === '' ? [] : data.split(schema)
  }
}

function expandVariables (obj) {
  // Expand environment variables in the format $VAR or ${VAR}
  for (const key in obj) {
    const value = obj[key]
    if (typeof value === 'string') {
      obj[key] = value.replace(/\$\{?([A-Z_][A-Z0-9_]*)\}?/gi, (match, varName) => {
        return obj[varName] !== undefined ? obj[varName] : match
      })
    }
  }
}

const optsSchema = {
  type: 'object',
  required: ['schema'],
  properties: {
    schema: { type: 'object', additionalProperties: true },
    data: {
      oneOf: [
        { type: 'array', items: { type: 'object' }, minItems: 1 },
        { type: 'object' }
      ],
      default: {}
    },
    env: { type: 'boolean', default: true },
    dotenv: { type: ['boolean', 'object'], default: false },
    expandEnv: { type: ['boolean'], default: false },
    ajv: { type: 'object', additionalProperties: true }
  }
}

const sharedAjvInstance = getDefaultInstance()

const optsSchemaValidator = sharedAjvInstance.compile(optsSchema)

function envSchema (_opts) {
  const opts = Object.assign({}, _opts)

  if (opts.schema?.[Symbol.for('fluent-schema-object')]) {
    opts.schema = opts.schema.valueOf()
  }

  const isOptionValid = optsSchemaValidator(opts)
  if (!isOptionValid) {
    const error = new Error(sharedAjvInstance.errorsText(optsSchemaValidator.errors, { dataVar: 'opts' }))
    error.errors = optsSchemaValidator.errors
    throw error
  }

  const { schema } = opts
  schema.additionalProperties = false

  let { data, dotenv, env, expandEnv } = opts
  if (!Array.isArray(data)) {
    data = [data]
  }

  let parsedEnv
  if (dotenv) {
    const dotenvOpts = typeof dotenv === 'object' ? dotenv : {}
    const path = dotenvOpts.path || '.env'
    const encoding = dotenvOpts.encoding || 'utf8'

    try {
      const envFileContent = readFileSync(resolve(path), encoding)
      parsedEnv = parseEnv(envFileContent)
    } catch (err) {
      // Silently ignore if file doesn't exist
      if (err.code !== 'ENOENT') {
        throw err
      }
      parsedEnv = {}
    }
  }

  /* istanbul ignore else */
  if (env) {
    data.unshift(process.env)
  }

  if (parsedEnv) {
    data.unshift(parsedEnv)
  }

  const merge = {}
  data.forEach(d => Object.assign(merge, d))

  if (expandEnv) {
    expandVariables(merge)
  }

  const ajv = chooseAjvInstance(sharedAjvInstance, opts.ajv)

  const valid = ajv.validate(schema, merge)
  if (!valid) {
    const error = new Error(ajv.errorsText(ajv.errors, { dataVar: 'env' }))
    error.errors = ajv.errors
    throw error
  }

  return merge
}

function chooseAjvInstance (defaultInstance, ajvOpts) {
  if (ajvOpts instanceof Ajv) {
    return ajvOpts
  }
  let ajv = defaultInstance
  if (typeof ajvOpts === 'object' && typeof ajvOpts.customOptions === 'function') {
    ajv = ajvOpts.customOptions(getDefaultInstance())
    if (!(ajv instanceof Ajv)) {
      throw new TypeError('customOptions function must return an instance of Ajv')
    }
  } else if (typeof ajvOpts === 'object' && typeof ajvOpts.customOptions === 'object') {
    ajv = getDefaultInstance(ajvOpts.customOptions)
  }
  return ajv
}

function getDefaultInstance (overrideOpts = {}) {
  return new Ajv({
    allErrors: true,
    removeAdditional: true,
    useDefaults: true,
    coerceTypes: true,
    allowUnionTypes: true,
    addUsedSchema: false,
    keywords: [separator],
    ...overrideOpts
  })
}

envSchema.keywords = { separator }

module.exports = envSchema
module.exports.default = envSchema
module.exports.envSchema = envSchema


================================================
FILE: package.json
================================================
{
  "name": "env-schema",
  "version": "7.0.0",
  "description": "Validate your env variables using Ajv with .env file support using Node.js built-in parseEnv",
  "main": "index.js",
  "type": "commonjs",
  "types": "types/index.d.ts",
  "scripts": {
    "lint": "eslint",
    "lint:fix": "eslint --fix",
    "test": "npm run test:unit && npm run test:typescript",
    "test:unit": "c8 --100 node --test",
    "test:typescript": "tsd"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/fastify/env-schema.git"
  },
  "keywords": [
    "ajv",
    "env",
    "schema",
    "json",
    "dotenv",
    "validate",
    "extract",
    "parseEnv"
  ],
  "author": "Matteo Collina <hello@matteocollina.com>",
  "contributors": [
    {
      "name": "Manuel Spigolon",
      "email": "behemoth89@gmail.com"
    },
    {
      "name": "Maksim Sinik",
      "url": "https://maksim.dev"
    },
    {
      "name": "Aras Abbasi",
      "email": "aras.abbasi@gmail.com"
    },
    {
      "name": "Frazer Smith",
      "email": "frazer.dev@icloud.com",
      "url": "https://github.com/fdawgs"
    }
  ],
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/fastify/env-schema/issues"
  },
  "homepage": "https://github.com/fastify/env-schema#readme",
  "funding": [
    {
      "type": "github",
      "url": "https://github.com/sponsors/fastify"
    },
    {
      "type": "opencollective",
      "url": "https://opencollective.com/fastify"
    }
  ],
  "dependencies": {
    "ajv": "^8.12.0"
  },
  "devDependencies": {
    "typebox": "^1.0.81",
    "ajv-formats": "^3.0.1",
    "c8": "^11.0.0",
    "eslint": "^9.17.0",
    "fluent-json-schema": "^6.0.0",
    "neostandard": "^0.13.0",
    "tsd": "^0.33.0"
  }
}


================================================
FILE: test/basic.test.js
================================================
'use strict'

const { test } = require('node:test')
const makeTest = require('./make-test')
const { join } = require('node:path')

process.env.VALUE_FROM_ENV = 'pippo'

const tests = [
  {
    name: 'empty ok',
    schema: { type: 'object' },
    data: { },
    isOk: true,
    confExpected: {}
  },
  {
    name: 'simple object - ok',
    schema: {
      type: 'object',
      properties: {
        PORT: {
          type: 'string'
        }
      }
    },
    data: {
      PORT: '44'
    },
    isOk: true,
    confExpected: {
      PORT: '44'
    }
  },
  {
    name: 'simple object - ok - coerce value',
    schema: {
      type: 'object',
      properties: {
        PORT: {
          type: 'integer'
        }
      }
    },
    data: {
      PORT: '44'
    },
    isOk: true,
    confExpected: {
      PORT: 44
    }
  },
  {
    name: 'simple object - ok - remove additional properties',
    schema: {
      type: 'object',
      properties: {
        PORT: {
          type: 'integer'
        }
      }
    },
    data: {
      PORT: '44',
      ANOTHER_PORT: '55'
    },
    isOk: true,
    confExpected: {
      PORT: 44
    }
  },
  {
    name: 'simple object - ok - use default',
    schema: {
      type: 'object',
      properties: {
        PORT: {
          type: 'integer',
          default: 5555
        }
      }
    },
    data: { },
    isOk: true,
    confExpected: {
      PORT: 5555
    }
  },
  {
    name: 'simple object - ok - required + default',
    schema: {
      type: 'object',
      required: ['PORT'],
      properties: {
        PORT: {
          type: 'integer',
          default: 6666
        }
      }
    },
    data: { },
    isOk: true,
    confExpected: {
      PORT: 6666
    }
  },
  {
    name: 'simple object - ok - allow array',
    schema: {
      type: 'object',
      required: ['PORT'],
      properties: {
        PORT: {
          type: 'integer',
          default: 6666
        }
      }
    },
    data: [{ }],
    isOk: true,
    confExpected: {
      PORT: 6666
    }
  },
  {
    name: 'simple object - ok - merge multiple object + env',
    schema: {
      type: 'object',
      required: ['PORT', 'MONGODB_URL'],
      properties: {
        PORT: {
          type: 'integer',
          default: 6666
        },
        MONGODB_URL: {
          type: 'string'
        },
        VALUE_FROM_ENV: {
          type: 'string'
        }
      }
    },
    data: [{ PORT: 3333 }, { MONGODB_URL: 'mongodb://localhost/pippo' }],
    isOk: true,
    confExpected: {
      PORT: 3333,
      MONGODB_URL: 'mongodb://localhost/pippo',
      VALUE_FROM_ENV: 'pippo'
    }
  },
  {
    name: 'simple object - ok - load only from env',
    schema: {
      type: 'object',
      required: ['VALUE_FROM_ENV'],
      properties: {
        VALUE_FROM_ENV: {
          type: 'string'
        }
      }
    },
    data: undefined,
    isOk: true,
    confExpected: {
      VALUE_FROM_ENV: 'pippo'
    }
  },
  {
    name: 'simple object - ok - opts override environment',
    schema: {
      type: 'object',
      required: ['VALUE_FROM_ENV'],
      properties: {
        VALUE_FROM_ENV: {
          type: 'string'
        }
      }
    },
    data: { VALUE_FROM_ENV: 'pluto' },
    isOk: true,
    confExpected: {
      VALUE_FROM_ENV: 'pluto'
    }
  },
  {
    name: 'simple object - ok - load only from .env',
    schema: {
      type: 'object',
      required: ['VALUE_FROM_DOTENV'],
      properties: {
        VALUE_FROM_DOTENV: {
          type: 'string'
        }
      }
    },
    data: undefined,
    isOk: true,
    dotenv: { path: join(__dirname, '.env') },
    confExpected: {
      VALUE_FROM_DOTENV: 'look ma'
    }
  },
  {
    name: 'simple object - KO',
    schema: {
      type: 'object',
      required: ['PORT'],
      properties: {
        PORT: {
          type: 'integer'
        }
      }
    },
    data: { },
    isOk: false,
    errorMessage: 'env must have required property \'PORT\''
  },
  {
    name: 'simple object - invalid data',
    schema: {
      type: 'object',
      required: ['PORT'],
      properties: {
        PORT: {
          type: 'integer'
        }
      }
    },
    data: [],
    isOk: false,
    errorMessage: 'opts/data must NOT have fewer than 1 items, opts/data must be object, opts/data must match exactly one schema in oneOf'
  },
  {
    name: 'simple object - ok - with separator',
    schema: {
      type: 'object',
      required: ['ALLOWED_HOSTS'],
      properties: {
        ALLOWED_HOSTS: {
          type: 'string',
          separator: ','
        }
      }
    },
    data: {
      ALLOWED_HOSTS: '127.0.0.1,0.0.0.0'
    },
    isOk: true,
    confExpected: {
      ALLOWED_HOSTS: ['127.0.0.1', '0.0.0.0']
    }
  },
  {
    name: 'simple object - ok - with separator - only one value',
    schema: {
      type: 'object',
      required: ['ALLOWED_HOSTS'],
      properties: {
        ALLOWED_HOSTS: {
          type: 'string',
          separator: ','
        }
      }
    },
    data: {
      ALLOWED_HOSTS: '127.0.0.1'
    },
    isOk: true,
    confExpected: {
      ALLOWED_HOSTS: ['127.0.0.1']
    }
  },
  {
    name: 'simple object - ok - with separator - no values',
    schema: {
      type: 'object',
      required: ['ALLOWED_HOSTS'],
      properties: {
        ALLOWED_HOSTS: {
          type: 'string',
          separator: ','
        }
      }
    },
    data: {
      ALLOWED_HOSTS: ''
    },
    isOk: true,
    confExpected: {
      ALLOWED_HOSTS: []
    }
  },
  {
    name: 'simple object - KO - with separator',
    schema: {
      type: 'object',
      required: ['ALLOWED_HOSTS'],
      properties: {
        ALLOWED_HOSTS: {
          type: 'string',
          separator: ','
        }
      }
    },
    data: {},
    isOk: false,
    errorMessage: 'env must have required property \'ALLOWED_HOSTS\''
  },
  {
    name: 'simple object - KO - multiple required properties',
    schema: {
      type: 'object',
      required: ['A', 'B', 'C'],
      properties: {
        A: { type: 'string' },
        B: { type: 'string' },
        C: { type: 'string' },
        D: { type: 'string' }
      }
    },
    data: {},
    isOk: false,
    errorMessage: 'env must have required property \'A\', env must have required property \'B\', env must have required property \'C\''
  },
  {
    name: 'simple object - ok - dotenv with non-existent file',
    schema: {
      type: 'object',
      properties: {
        PORT: {
          type: 'integer',
          default: 3000
        }
      }
    },
    data: { PORT: 8080 },
    dotenv: { path: join(__dirname, '.env.nonexistent') },
    isOk: true,
    confExpected: {
      PORT: 8080
    }
  },
  {
    name: 'simple object - ok - dotenv true with no file',
    schema: {
      type: 'object',
      properties: {
        PORT: {
          type: 'integer',
          default: 3000
        }
      }
    },
    data: { PORT: 9090 },
    dotenv: true,
    isOk: true,
    confExpected: {
      PORT: 9090
    }
  },
  {
    name: 'simple object - KO - dotenv with directory instead of file',
    schema: {
      type: 'object',
      properties: {
        PORT: {
          type: 'integer',
          default: 3000
        }
      }
    },
    data: { PORT: 8080 },
    dotenv: { path: __dirname },
    isOk: false,
    errorMessage: 'EISDIR: illegal operation on a directory, read'
  }
]

tests.forEach(function (testConf) {
  test(testConf.name, t => {
    const options = {
      schema: testConf.schema,
      data: testConf.data,
      dotenv: testConf.dotenv,
      dotenvConfig: testConf.dotenvConfig
    }

    makeTest(t, options, testConf.isOk, testConf.confExpected, testConf.errorMessage)
  })
})


================================================
FILE: test/custom-ajv.test.js
================================================
'use strict'

const { test } = require('node:test')
const Ajv = require('ajv')
const makeTest = require('./make-test')
const { join } = require('node:path')

process.env.VALUE_FROM_ENV = 'pippo'

const tests = [
  {
    name: 'empty ok',
    schema: { type: 'object' },
    data: { },
    isOk: true,
    confExpected: {}
  },
  {
    name: 'simple object - ok',
    schema: {
      type: 'object',
      properties: {
        PORT: {
          type: 'string'
        }
      }
    },
    data: {
      PORT: '44'
    },
    isOk: true,
    confExpected: {
      PORT: '44'
    }
  },
  {
    name: 'simple object - ok - coerce value',
    schema: {
      type: 'object',
      properties: {
        PORT: {
          type: 'integer'
        }
      }
    },
    data: {
      PORT: '44'
    },
    isOk: true,
    confExpected: {
      PORT: 44
    }
  },
  {
    name: 'simple object - ok - remove additional properties',
    schema: {
      type: 'object',
      properties: {
        PORT: {
          type: 'integer'
        }
      }
    },
    data: {
      PORT: '44',
      ANOTHER_PORT: '55'
    },
    isOk: true,
    confExpected: {
      PORT: 44
    }
  },
  {
    name: 'simple object - ok - use default',
    schema: {
      type: 'object',
      properties: {
        PORT: {
          type: 'integer',
          default: 5555
        }
      }
    },
    data: { },
    isOk: true,
    confExpected: {
      PORT: 5555
    }
  },
  {
    name: 'simple object - ok - required + default',
    schema: {
      type: 'object',
      required: ['PORT'],
      properties: {
        PORT: {
          type: 'integer',
          default: 6666
        }
      }
    },
    data: { },
    isOk: true,
    confExpected: {
      PORT: 6666
    }
  },
  {
    name: 'simple object - ok - allow array',
    schema: {
      type: 'object',
      required: ['PORT'],
      properties: {
        PORT: {
          type: 'integer',
          default: 6666
        }
      }
    },
    data: [{ }],
    isOk: true,
    confExpected: {
      PORT: 6666
    }
  },
  {
    name: 'simple object - ok - merge multiple object + env',
    schema: {
      type: 'object',
      required: ['PORT', 'MONGODB_URL'],
      properties: {
        PORT: {
          type: 'integer',
          default: 6666
        },
        MONGODB_URL: {
          type: 'string'
        },
        VALUE_FROM_ENV: {
          type: 'string'
        }
      }
    },
    data: [{ PORT: 3333 }, { MONGODB_URL: 'mongodb://localhost/pippo' }],
    isOk: true,
    confExpected: {
      PORT: 3333,
      MONGODB_URL: 'mongodb://localhost/pippo',
      VALUE_FROM_ENV: 'pippo'
    }
  },
  {
    name: 'simple object - ok - load only from env',
    schema: {
      type: 'object',
      required: ['VALUE_FROM_ENV'],
      properties: {
        VALUE_FROM_ENV: {
          type: 'string'
        }
      }
    },
    data: undefined,
    isOk: true,
    confExpected: {
      VALUE_FROM_ENV: 'pippo'
    }
  },
  {
    name: 'simple object - ok - opts override environment',
    schema: {
      type: 'object',
      required: ['VALUE_FROM_ENV'],
      properties: {
        VALUE_FROM_ENV: {
          type: 'string'
        }
      }
    },
    data: { VALUE_FROM_ENV: 'pluto' },
    isOk: true,
    confExpected: {
      VALUE_FROM_ENV: 'pluto'
    }
  },
  {
    name: 'simple object - ok - load only from .env',
    schema: {
      type: 'object',
      required: ['VALUE_FROM_DOTENV'],
      properties: {
        VALUE_FROM_DOTENV: {
          type: 'string'
        }
      }
    },
    data: undefined,
    isOk: true,
    dotenv: { path: join(__dirname, '.env') },
    confExpected: {
      VALUE_FROM_DOTENV: 'look ma'
    }
  },
  {
    name: 'simple object - KO',
    schema: {
      type: 'object',
      required: ['PORT'],
      properties: {
        PORT: {
          type: 'integer'
        }
      }
    },
    data: { },
    isOk: false,
    errorMessage: 'env must have required property \'PORT\''
  },
  {
    name: 'simple object - invalid data',
    schema: {
      type: 'object',
      required: ['PORT'],
      properties: {
        PORT: {
          type: 'integer'
        }
      }
    },
    data: [],
    isOk: false,
    errorMessage: 'opts/data must NOT have fewer than 1 items, opts/data must be object, opts/data must match exactly one schema in oneOf'
  }
]

const ajv = new Ajv({
  allErrors: true,
  removeAdditional: true,
  useDefaults: true,
  coerceTypes: true,
  allowUnionTypes: true
})

tests.forEach(function (testConf) {
  test(testConf.name, t => {
    const options = {
      schema: testConf.schema,
      data: testConf.data,
      dotenv: testConf.dotenv,
      dotenvConfig: testConf.dotenvConfig,
      ajv
    }

    makeTest(t, options, testConf.isOk, testConf.confExpected, testConf.errorMessage)
  })
})

const noCoercionTest = {
  name: 'simple object - not ok - should NOT coerce value',
  schema: {
    type: 'object',
    properties: {
      PORT: {
        type: 'integer'
      }
    }
  },
  data: {
    PORT: '44'
  },
  isOk: false,
  errorMessage: 'env/PORT must be integer',
  confExpected: {
    PORT: 44
  }
}

const strictValidator = new Ajv({
  allErrors: true,
  removeAdditional: true,
  useDefaults: true,
  coerceTypes: false,
  allowUnionTypes: true
});

[noCoercionTest].forEach(function (testConf) {
  test(testConf.name, t => {
    const options = {
      schema: testConf.schema,
      data: testConf.data,
      dotenv: testConf.dotenv,
      dotenvConfig: testConf.dotenvConfig,
      ajv: strictValidator
    }

    makeTest(t, options, testConf.isOk, testConf.confExpected, testConf.errorMessage)
  })
})

test('ajv enhancement', async t => {
  t.plan(3)
  const testCaseFn = {
    schema: {
      type: 'object',
      required: ['MONGODB_URL'],
      properties: {
        MONGODB_URL: {
          type: 'string',
          format: 'uri'
        }
      }
    },
    data: [{ PORT: 3333 }, { MONGODB_URL: 'mongodb://localhost/pippo' }],
    isOk: true,
    confExpected: {
      MONGODB_URL: 'mongodb://localhost/pippo'
    }
  }
  const testCaseObj = {
    schema: {
      type: 'object',
      required: ['PORT'],
      properties: {
        PORT: {
          type: 'string',
        }
      }
    },
    data: [{ PORT: 3333 }],
    isOk: true,
    confExpected: {
      PORT: '3333'
    }
  }

  await t.test('customOptions fn return', async t => {
    const options = {
      schema: testCaseFn.schema,
      data: testCaseFn.data,
      ajv: {
        customOptions (ajvInstance) {
          require('ajv-formats')(ajvInstance)
          return ajvInstance
        }
      }
    }
    makeTest(t, options, testCaseFn.isOk, testCaseFn.confExpected)
  })

  await t.test('customOptions fn no return', async t => {
    const options = {
      schema: testCaseFn.schema,
      data: testCaseFn.data,
      ajv: {
        customOptions (_ajvInstance) {
          // do nothing
        }
      }
    }
    makeTest(t, options, false, undefined, 'customOptions function must return an instance of Ajv')
  })

  await t.test('customOptions object override', async t => {
    const options = {
      schema: testCaseObj.schema,
      data: testCaseObj.data,
      ajv: {
        customOptions: {
          coerceTypes: true,
        }
      }
    }
    makeTest(t, options, testCaseObj.isOk, testCaseObj.confExpected)
  })
})


================================================
FILE: test/expand.test.js
================================================
'use strict'

const { test } = require('node:test')
const makeTest = require('./make-test')
const { join } = require('node:path')

process.env.K8S_NAMESPACE = 'pippo'
process.env.K8S_CLUSTERID = 'pluto'
process.env.URL = 'https://prefix.$K8S_NAMESPACE.$K8S_CLUSTERID.my.domain.com'
process.env.PASSWORD = 'password'

const tests = [
  {
    name: 'simple object - ok - expandEnv',
    schema: {
      type: 'object',
      properties: {
        URL: {
          type: 'string'
        },
        K8S_NAMESPACE: {
          type: 'string'
        }
      }
    },
    expandEnv: true,
    isOk: true,
    confExpected: {
      URL: 'https://prefix.pippo.pluto.my.domain.com',
      K8S_NAMESPACE: 'pippo'
    }
  },
  {
    name: 'simple object - ok - expandEnv use dotenv',
    schema: {
      type: 'object',
      properties: {
        EXPANDED_VALUE_FROM_DOTENV: {
          type: 'string'
        }
      }
    },
    expandEnv: true,
    isOk: true,
    dotenv: { path: join(__dirname, '.env') },
    confExpected: {
      EXPANDED_VALUE_FROM_DOTENV: 'the password is password!'
    }
  },
  {
    name: 'simple object - ok - expandEnv works when passed an arbitrary new object based on process.env as data',
    schema: {
      type: 'object',
      properties: {
        URL: {
          type: 'string'
        },
        K8S_NAMESPACE: {
          type: 'string'
        }
      }
    },
    expandEnv: true,
    isOk: true,
    data: {
      ...process.env,
      K8S_NAMESPACE: 'hello'
    },
    confExpected: {
      URL: 'https://prefix.hello.pluto.my.domain.com',
      K8S_NAMESPACE: 'hello'
    }
  },
  {
    name: 'simple object - ok - expandEnv with undefined variable keeps placeholder',
    schema: {
      type: 'object',
      properties: {
        MESSAGE: {
          type: 'string'
        }
      }
    },
    expandEnv: true,
    isOk: true,
    data: {
      MESSAGE: 'Hello $UNDEFINED_VAR world'
    },
    confExpected: {
      MESSAGE: 'Hello $UNDEFINED_VAR world'
    }
  }
]

tests.forEach(function (testConf) {
  test(testConf.name, t => {
    const options = {
      schema: testConf.schema,
      data: testConf.data,
      dotenv: testConf.dotenv,
      dotenvConfig: testConf.dotenvConfig,
      expandEnv: testConf.expandEnv
    }

    makeTest(t, options, testConf.isOk, testConf.confExpected, testConf.errorMessage)
  })
})


================================================
FILE: test/fluent-schema.test.js
================================================
'use strict'

const { test } = require('node:test')

if (parseInt(process.versions.node.split('.', 1)[0]) <= 8) {
  test.skip('not supported')
} else {
  run()
}

function run () {
  const S = require('fluent-json-schema')
  const makeTest = require('./make-test')

  test('simple object - fluent-json-schema', t => {
    const options = {
      schema: S.object().prop('PORT', S.string()),
      data: {
        PORT: '44'
      }
    }

    makeTest(t, options, true, {
      PORT: '44'
    })
  })
}


================================================
FILE: test/make-test.js
================================================
'use strict'

const envSchema = require('../index')

function makeTest (t, options, isOk, confExpected, errorMessage) {
  t.plan(1)
  options = Object.assign({ confKey: 'config' }, options)

  try {
    const conf = envSchema(options)
    t.assert.deepStrictEqual(conf, confExpected)
  } catch (err) {
    if (isOk) {
      t.assert.fail(err)
      return
    }
    t.assert.strictEqual(err.message, errorMessage)
  }
}

module.exports = makeTest


================================================
FILE: test/no-global.test.js
================================================
'use strict'

const { test } = require('node:test')
const envSchema = require('../index')

test('no globals', t => {
  t.plan(2)

  const options = {
    confKey: 'secrets',
    data: {
      MONGO_URL: 'good'
    },
    schema: {
      $id: 'schema:dotenv',
      type: 'object',
      required: ['MONGO_URL'],
      properties: {
        PORT: {
          type: 'integer',
          default: 3000
        },
        MONGO_URL: {
          type: 'string'
        }
      }
    }
  }

  {
    const conf = envSchema(JSON.parse(JSON.stringify(options)))
    t.assert.deepStrictEqual(conf, { MONGO_URL: 'good', PORT: 3000 })
  }
  {
    const conf = envSchema(JSON.parse(JSON.stringify(options)))
    t.assert.deepStrictEqual(conf, { MONGO_URL: 'good', PORT: 3000 })
  }
})


================================================
FILE: types/index.d.ts
================================================
import Ajv, { KeywordDefinition, JSONSchemaType } from 'ajv'
import { AnySchema } from 'ajv/dist/core'

/**
 * Options for loading .env files
 */
interface DotenvOptions {
  /**
   * Path to .env file (default: '.env')
   */
  path?: string;
  /**
   * Encoding of .env file (default: 'utf8')
   */
  encoding?: 'ascii' | 'utf8' | 'utf-8' | 'utf16le' | 'ucs2' | 'ucs-2' | 'base64' | 'latin1' | 'binary' | 'hex';
}

type EnvSchema = typeof envSchema

declare namespace envSchema {
  export type { JSONSchemaType }

  export type EnvSchemaData = {
    [key: string]: unknown;
  }

  export type EnvSchemaOpt<T = EnvSchemaData> = {
    schema?: JSONSchemaType<T> | AnySchema;
    data?: [EnvSchemaData, ...EnvSchemaData[]] | EnvSchemaData;
    env?: boolean;
    dotenv?: boolean | DotenvOptions;
    expandEnv?: boolean;
    ajv?:
    | Ajv
    | {
      customOptions(ajvInstance: Ajv): Ajv;
    };
  }

  export const keywords: {
    separator: KeywordDefinition
  }

  export const envSchema: EnvSchema
  export { envSchema as default }
}

declare function envSchema<T = envSchema.EnvSchemaData> (_opts?: envSchema.EnvSchemaOpt<T>): T
export = envSchema


================================================
FILE: types/index.test-d.ts
================================================
import { expectError, expectType } from 'tsd'
import envSchema, {
  EnvSchemaData,
  EnvSchemaOpt,
  keywords,
  envSchema as envSchemaNamed,
  default as envSchemaDefault,
} from '..'
import Ajv, { KeywordDefinition, JSONSchemaType } from 'ajv'
import { Static, Type } from 'typebox'

interface EnvData {
  PORT: number;
}

const schemaWithType: JSONSchemaType<EnvData> = {
  type: 'object',
  required: ['PORT'],
  properties: {
    PORT: {
      type: 'number',
      default: 3000,
    },
  },
}

const schemaTypebox = Type.Object({
  PORT: Type.Number({ default: 3000 }),
})

type SchemaTypebox = Static<typeof schemaTypebox>

const data = {
  foo: 'bar',
}

expectType<EnvSchemaData>(envSchema())
expectType<EnvSchemaData>(envSchemaNamed())
expectType<EnvSchemaData>(envSchemaDefault())

const emptyOpt: EnvSchemaOpt = {}
expectType<EnvSchemaOpt>(emptyOpt)

const optWithSchemaTypebox: EnvSchemaOpt = {
  schema: schemaTypebox,
}
expectType<EnvSchemaOpt>(optWithSchemaTypebox)

const optWithSchemaWithType: EnvSchemaOpt<EnvData> = {
  schema: schemaWithType,
}
expectType<EnvSchemaOpt<EnvData>>(optWithSchemaWithType)

const optWithData: EnvSchemaOpt = {
  data,
}
expectType<EnvSchemaOpt>(optWithData)

expectError<EnvSchemaOpt>({
  data: [], // min 1 item
})

const optWithArrayData: EnvSchemaOpt = {
  data: [{}],
}
expectType<EnvSchemaOpt>(optWithArrayData)

const optWithMultipleItemArrayData: EnvSchemaOpt = {
  data: [{}, {}],
}
expectType<EnvSchemaOpt>(optWithMultipleItemArrayData)

const optWithDotEnvBoolean: EnvSchemaOpt = {
  dotenv: true,
}
expectType<EnvSchemaOpt>(optWithDotEnvBoolean)

const optWithDotEnvOpt: EnvSchemaOpt = {
  dotenv: {},
}
expectType<EnvSchemaOpt>(optWithDotEnvOpt)

const optWithEnvExpand: EnvSchemaOpt = {
  expandEnv: true,
}
expectType<EnvSchemaOpt>(optWithEnvExpand)

const optWithAjvInstance: EnvSchemaOpt = {
  ajv: new Ajv(),
}
expectType<EnvSchemaOpt>(optWithAjvInstance)
expectType<KeywordDefinition>(envSchema.keywords.separator)

const optWithAjvCustomOptions: EnvSchemaOpt = {
  ajv: {
    customOptions (_ajvInstance: Ajv): Ajv {
      return new Ajv()
    },
  },
}
expectType<EnvSchemaOpt>(optWithAjvCustomOptions)
expectError<EnvSchemaOpt>({
  ajv: {
    customOptions (_ajvInstance: Ajv) {},
  },
})

const envSchemaWithType = envSchema({ schema: schemaWithType })
expectType<EnvData>(envSchemaWithType)

const envSchemaTypebox = envSchema<SchemaTypebox>({ schema: schemaTypebox })
expectType<SchemaTypebox>(envSchemaTypebox)

expectType<KeywordDefinition>(keywords.separator)
expectType<KeywordDefinition>(envSchema.keywords.separator)
Download .txt
gitextract_31c2uptn/

├── .gitattributes
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── ci.yml
│       └── lock-threads.yml
├── .gitignore
├── .npmrc
├── LICENSE
├── README.md
├── eslint.config.js
├── index.js
├── package.json
├── test/
│   ├── basic.test.js
│   ├── custom-ajv.test.js
│   ├── expand.test.js
│   ├── fluent-schema.test.js
│   ├── make-test.js
│   └── no-global.test.js
└── types/
    ├── index.d.ts
    └── index.test-d.ts
Download .txt
SYMBOL INDEX (16 symbols across 6 files)

FILE: index.js
  function expandVariables (line 23) | function expandVariables (obj) {
  function envSchema (line 58) | function envSchema (_opts) {
  function chooseAjvInstance (line 126) | function chooseAjvInstance (defaultInstance, ajvOpts) {
  function getDefaultInstance (line 142) | function getDefaultInstance (overrideOpts = {}) {

FILE: test/custom-ajv.test.js
  method customOptions (line 341) | customOptions (ajvInstance) {
  method customOptions (line 355) | customOptions (_ajvInstance) {

FILE: test/fluent-schema.test.js
  function run (line 11) | function run () {

FILE: test/make-test.js
  function makeTest (line 5) | function makeTest (t, options, isOk, confExpected, errorMessage) {

FILE: types/index.d.ts
  type DotenvOptions (line 7) | interface DotenvOptions {
  type EnvSchema (line 18) | type EnvSchema = typeof envSchema
  type EnvSchemaData (line 23) | type EnvSchemaData = {
  type EnvSchemaOpt (line 27) | type EnvSchemaOpt<T = EnvSchemaData> = {

FILE: types/index.test-d.ts
  type EnvData (line 12) | interface EnvData {
  type SchemaTypebox (line 31) | type SchemaTypebox = Static<typeof schemaTypebox>
  method customOptions (line 96) | customOptions (_ajvInstance: Ajv): Ajv {
  method customOptions (line 104) | customOptions (_ajvInstance: Ajv) {}
Condensed preview — 19 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (43K chars).
[
  {
    "path": ".gitattributes",
    "chars": 80,
    "preview": "# Set default behavior to automatically convert line endings\n* text=auto eol=lf\n"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 274,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n "
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 651,
    "preview": "name: CI\n\non:\n  push:\n    branches:\n     - main\n     - next\n     - 'v*'\n    paths-ignore:\n      - 'docs/**'\n      - '*.m"
  },
  {
    "path": ".github/workflows/lock-threads.yml",
    "chars": 293,
    "preview": "name: Lock Threads\n\non:\n  schedule:\n    - cron: '0 0 1 * *'\n  workflow_dispatch:\n\nconcurrency:\n  group: lock\n\npermission"
  },
  {
    "path": ".gitignore",
    "chars": 2229,
    "preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.pnpm-debug.log*\n\n# Diagnostic reports"
  },
  {
    "path": ".npmrc",
    "chars": 39,
    "preview": "ignore-scripts=true\npackage-lock=false\n"
  },
  {
    "path": "LICENSE",
    "chars": 1123,
    "preview": "MIT License\n\nCopyright (c) 2019-present The Fastify team <https://github.com/fastify/fastify#team>\n\nPermission is hereby"
  },
  {
    "path": "README.md",
    "chars": 6407,
    "preview": "# env-schema\n\n[![CI](https://github.com/fastify/env-schema/actions/workflows/ci.yml/badge.svg?branch=main)](https://gith"
  },
  {
    "path": "eslint.config.js",
    "chars": 135,
    "preview": "'use strict'\n\nmodule.exports = require('neostandard')({\n  ignores: require('neostandard').resolveIgnoresFromGitignore(),"
  },
  {
    "path": "index.js",
    "chars": 4050,
    "preview": "'use strict'\n\nconst Ajv = require('ajv')\nconst { parseEnv } = require('node:util')\nconst { readFileSync } = require('nod"
  },
  {
    "path": "package.json",
    "chars": 1738,
    "preview": "{\n  \"name\": \"env-schema\",\n  \"version\": \"7.0.0\",\n  \"description\": \"Validate your env variables using Ajv with .env file s"
  },
  {
    "path": "test/basic.test.js",
    "chars": 7694,
    "preview": "'use strict'\n\nconst { test } = require('node:test')\nconst makeTest = require('./make-test')\nconst { join } = require('no"
  },
  {
    "path": "test/custom-ajv.test.js",
    "chars": 7379,
    "preview": "'use strict'\n\nconst { test } = require('node:test')\nconst Ajv = require('ajv')\nconst makeTest = require('./make-test')\nc"
  },
  {
    "path": "test/expand.test.js",
    "chars": 2366,
    "preview": "'use strict'\n\nconst { test } = require('node:test')\nconst makeTest = require('./make-test')\nconst { join } = require('no"
  },
  {
    "path": "test/fluent-schema.test.js",
    "chars": 503,
    "preview": "'use strict'\n\nconst { test } = require('node:test')\n\nif (parseInt(process.versions.node.split('.', 1)[0]) <= 8) {\n  test"
  },
  {
    "path": "test/make-test.js",
    "chars": 447,
    "preview": "'use strict'\n\nconst envSchema = require('../index')\n\nfunction makeTest (t, options, isOk, confExpected, errorMessage) {\n"
  },
  {
    "path": "test/no-global.test.js",
    "chars": 772,
    "preview": "'use strict'\n\nconst { test } = require('node:test')\nconst envSchema = require('../index')\n\ntest('no globals', t => {\n  t"
  },
  {
    "path": "types/index.d.ts",
    "chars": 1155,
    "preview": "import Ajv, { KeywordDefinition, JSONSchemaType } from 'ajv'\nimport { AnySchema } from 'ajv/dist/core'\n\n/**\n * Options f"
  },
  {
    "path": "types/index.test-d.ts",
    "chars": 2598,
    "preview": "import { expectError, expectType } from 'tsd'\nimport envSchema, {\n  EnvSchemaData,\n  EnvSchemaOpt,\n  keywords,\n  envSche"
  }
]

About this extraction

This page contains the full source code of the fastify/env-schema GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 19 files (39.0 KB), approximately 11.3k tokens, and a symbol index with 16 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!