diff --git a/.env b/.env new file mode 100644 index 0000000..c8a6d4a --- /dev/null +++ b/.env @@ -0,0 +1,4 @@ +# This is for tests only +API_KEY=123 +ADMIN_EMAIL=alemagio@github.com +EMAIL_CONFIG_JSON={ "foo": 42 } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c8d01a6 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,19 @@ +name: CI workflow +on: [push, pull_request] +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + node-version: [10.x, 12.x, 14.x] + os: [ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: Install Dependencies + run: npm install --ignore-scripts + - name: Test + run: npm run test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e8a627 --- /dev/null +++ b/.gitignore @@ -0,0 +1,61 @@ +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# mac files +.DS_Store + +# vim swap files +*.swp + +# marko generated files +*.marko.js +# yarn.lock + +# lock files +yarn.lock +package-lock.json + +# IDE/editors +.vscode +.idea + +# temporary/work/output folders +# build/ +out/ +temp/ +tmp/ diff --git a/.taprc b/.taprc new file mode 100644 index 0000000..1721b52 --- /dev/null +++ b/.taprc @@ -0,0 +1,4 @@ +esm: false +ts: false +jsx: false +coverage: false diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f5762de --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Alessandro Magionami + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f2db772 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# fastify-envalid + +[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/) ![CI workflow](__MY_PLUGIN_URL__ +/workflows/CI%20workflow/badge.svg) + +Supports Fastify versions `3.x` + +## Install +``` +npm i fastify-envalid +``` + +## Usage +Require `fastify-envalid` and register. +```js +const fastify = require('fastify')() + +fastify.register(require('fastify-envalid')) + +fastify.listen(3000) +``` + +The plugin will create 3 decorators on your `fastify` instance: + +* `cleanEnv` - A function which is exactly `envalid` [cleanEnv](https://github.com/af/envalid/blob/master/README.md#envalidcleanenvenvironment-validators-options) +* `validators` - An object that contains all the `envalid` [validators](https://github.com/af/envalid/blob/master/README.md#validator-types) +* `makeValidator` - A function which is exactly `envalid` [makeValidators](https://github.com/af/envalid/blob/master/README.md#custom-validators) + +## Acknowledgements + +The code is a port for Fastify of [`envalid`](https://github.com/af/envalid). + +## License + +Licensed under [MIT](./LICENSE).
diff --git a/examples/basic/server.js b/examples/basic/server.js new file mode 100644 index 0000000..14e2a2f --- /dev/null +++ b/examples/basic/server.js @@ -0,0 +1,18 @@ +const fastify = require('fastify') +const fastifyEnvalid = require('../..') + +const app = fastify({ logger: true }) + +app.register(fastifyEnvalid) + +app.get('/', async (req, res) => { + const env = app.cleanEnv(process.env, { + API_KEY: app.validators.str(), + ADMIN_EMAIL: app.validators.email({ default: 'admin@example.com' }), + EMAIL_CONFIG_JSON: app.validators.json({ desc: 'Additional email parameters' }) + }) + + return { API_KEY: env.API_KEY, ADMIN_EMAIL: env.ADMIN_EMAIL, EMAIL_CONFIG_JSON: env.EMAIL_CONFIG_JSON } +}) + +app.listen(3000) diff --git a/examples/typescript/server.ts b/examples/typescript/server.ts new file mode 100644 index 0000000..c21d5ea --- /dev/null +++ b/examples/typescript/server.ts @@ -0,0 +1,18 @@ +import fastify from 'fastify' +import fastifyEnvalid from '../..' + +const app = fastify({ logger: true }) + +app.register(fastifyEnvalid) + +app.get('/', async (req, res) => { + const env = app.cleanEnv(process.env, { + API_KEY: app.validators.str(), + ADMIN_EMAIL: app.validators.email({ default: 'admin@example.com' }), + EMAIL_CONFIG_JSON: app.validators.json({ desc: 'Additional email parameters' }) + }) + + return { API_KEY: env.API_KEY, ADMIN_EMAIL: env.ADMIN_EMAIL, EMAIL_CONFIG_JSON: env.EMAIL_CONFIG_JSON } +}) + +app.listen(3000) diff --git a/examples/typescript/tsconfig.json b/examples/typescript/tsconfig.json new file mode 100644 index 0000000..2be494f --- /dev/null +++ b/examples/typescript/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "module": "esnext", + "target": "es2017" + } +} diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..308355f --- /dev/null +++ b/index.d.ts @@ -0,0 +1,48 @@ +import { FastifyPlugin } from 'fastify' +import { Spec, ValidatorSpec, CleanOptions, StrictCleanOptions, CleanEnv } from 'envalid' + +declare module 'fastify' { + export interface FastifyInstance { + validators: Validators + cleanEnv: CleanEnvFunction + makeValidator: ( + parser: (input: string) => T, + type?: string + ) => (spec?: Spec) => ValidatorSpec + } +} + +export interface Validators { + + bool: (spec?: Spec) => ValidatorSpec + + num: (spec?: Spec) => ValidatorSpec + + str: (spec?: Spec) => ValidatorSpec + + json: (spec?: Spec) => ValidatorSpec + + url: (spec?: Spec) => ValidatorSpec + + email: (spec?: Spec) => ValidatorSpec + + host: (spec?: Spec) => ValidatorSpec + + port: (spec?: Spec) => ValidatorSpec + +} + +export declare type CleanEnvFunction = ( + environment: unknown, + validators?: { [K in keyof T]: ValidatorSpec }, + options?: CleanOptions +) => Readonly & CleanEnv + +export declare type StrictCleanEnvFunction = ( + environment: unknown, + validators?: { [K in keyof T]: ValidatorSpec }, + options?: StrictCleanOptions +) => Readonly & CleanEnv & { readonly [varName: string]: string | undefined } + +declare const fastifyEnvalid: FastifyPlugin +export default fastifyEnvalid diff --git a/index.js b/index.js new file mode 100644 index 0000000..0e74611 --- /dev/null +++ b/index.js @@ -0,0 +1,28 @@ +'use strict' + +const fp = require('fastify-plugin') +const { + str, + bool, + num, + email, + host, + url, + json, + cleanEnv, + makeValidator +} = require('envalid') + +module.exports = fp(async function (fastify, opts) { + fastify.decorate('validators', { + str, + bool, + num, + email, + host, + url, + json + }) + fastify.decorate('cleanEnv', cleanEnv) + fastify.decorate('makeValidator', makeValidator) +}, { fastify: '3.x' }) diff --git a/package.json b/package.json new file mode 100644 index 0000000..a2fc046 --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "name": "fastify-envalid", + "version": "1.0.0", + "description": "", + "main": "index.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "npm run lint && npm run unit && npm run test:typescript", + "lint": "standard && npm run lint:typescript", + "lint:typescript": "ts-standard", + "test:typescript": "tsd", + "unit": "tap test/**/*.test.js" + }, + "keywords": [], + "author": "", + "license": "MIT", + "types": "index.d.ts", + "dependencies": { + "envalid": "^6.0.2", + "fastify-plugin": "^3.0.0" + }, + "devDependencies": { + "@types/node": "^14.0.18", + "fastify": "^3.8.0", + "fastify-tsconfig": "^1.0.0", + "standard": "^14.3.3", + "tap": "^14.0.0", + "ts-standard": "^9.0.0", + "tsd": "^0.13.1", + "typescript": "^4.0.2" + }, + "tsd": { + "directory": "test" + }, + "files": [ + "index.d.ts" + ], + "ts-standard": { + "ignore": [ + "examples" + ] + } +} diff --git a/test/index.test-d.ts b/test/index.test-d.ts new file mode 100644 index 0000000..8be8261 --- /dev/null +++ b/test/index.test-d.ts @@ -0,0 +1,19 @@ +import fastify from 'fastify' +import fastifyEnvalid, { Validators, CleanEnvFunction, StrictCleanEnvFunction } from '..' +import { expectType } from 'tsd' +import { Spec, ValidatorSpec } from 'envalid' + +let app +try { + app = fastify() + await app.ready() + await app.register(fastifyEnvalid) + expectType(app.validators) + expectType(app.cleanEnv) + expectType<( + parser: (input: string) => T, + type?: string + ) => (spec?: Spec) => ValidatorSpec>(app.makeValidator) +} catch (err) { + console.error(err) +} diff --git a/test/index.test.js b/test/index.test.js new file mode 100644 index 0000000..026c5ab --- /dev/null +++ b/test/index.test.js @@ -0,0 +1,47 @@ +const { test } = require('tap') +const { + str, + bool, + num, + email, + host, + url, + json, + cleanEnv, + makeValidator +} = require('envalid') + +test('should register the correct decorators', async t => { + t.plan(6) + + const app = require('fastify')() + + app.register(require('..')) + + await app.ready() + + t.true(app.hasDecorator('validators')) + t.deepEqual(app.validators, { + str, + bool, + num, + email, + host, + url, + json + }) + t.true(app.hasDecorator('cleanEnv')) + t.deepEqual(app.cleanEnv, cleanEnv) + t.true(app.hasDecorator('makeValidator')) + t.deepEqual(app.makeValidator, makeValidator) +}) + +test('should produce the correct env', async t => { + t.plan(1) + const app = require('fastify')() + + app.register(require('..')) + + await app.ready() + t.deepEqual(app.cleanEnv(process.env), cleanEnv(process.env)) +}) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7ddf54e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "fastify-tsconfig", + "compilerOptions": { + "noEmit": true, + "esModuleInterop": true + }, + "include": [ + "**/*.ts" + ] +}