diff --git a/README.md b/README.md index 71d697c..620e709 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ const config = envSchema({ schema: schema, data: data, // optional, default: process.env dotenv: true // load .env if it is there, default: false -}) +}) // config.data => ['127.0.0.1', '0.0.0.0'] ``` @@ -198,13 +198,13 @@ console.log(config) You can specify the type of your `config`: ```ts -import envSchema from 'env-schema'; +import { envSchema, JSONSchemaType } from 'env-schema' interface Env { PORT: number; } -const schema = { +const schema: JSONSchemaType = { type: 'object', required: [ 'PORT' ], properties: { @@ -215,8 +215,25 @@ const schema = { } } -const config = envSchema({ - schema, +const config = envSchema({ + schema +}) +``` + +You can also use a `JSON Schema` library like `typebox`: + +```ts +import { envSchema } from 'env-schema' +import { Static, Type } from '@sinclair/typebox' + +const schema = Type.Object({ + PORT: Type.Number({ default: 3000 }) +}) + +type Schema = Static + +const config = envSchema({ + schema }) ``` diff --git a/package.json b/package.json index 6b02c7e..8557e17 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ }, "devDependencies": { "@fastify/pre-commit": "^2.0.2", + "@sinclair/typebox": "^0.24.43", "ajv-formats": "^2.1.1", "fluent-json-schema": "^3.0.0", "snazzy": "^9.0.0", diff --git a/types/index.d.ts b/types/index.d.ts index f98e005..b7406f7 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,27 +1,33 @@ -import Ajv, { KeywordDefinition } from "ajv"; -import { DotenvConfigOptions } from "dotenv"; +import Ajv, { KeywordDefinition, JSONSchemaType } from 'ajv'; +import { AnySchema } from 'ajv/dist/core'; +import { DotenvConfigOptions } from 'dotenv'; +import { ObjectSchema } from 'fluent-json-schema'; + +export type { JSONSchemaType }; export type EnvSchemaData = { [key: string]: unknown; }; -export type EnvSchemaOpt = { - schema?: object; +export type EnvSchemaOpt = { + schema?: JSONSchemaType | AnySchema | ObjectSchema; data?: [EnvSchemaData, ...EnvSchemaData[]] | EnvSchemaData; env?: boolean; dotenv?: boolean | DotenvConfigOptions; - expandEnv?: boolean - ajv?: Ajv | { - customOptions(ajvInstance: Ajv): Ajv; - }; + expandEnv?: boolean; + ajv?: + | Ajv + | { + customOptions(ajvInstance: Ajv): Ajv; + }; }; declare const loadAndValidateEnvironment: { - (_opts?: EnvSchemaOpt): T; + (_opts?: EnvSchemaOpt): T; keywords: { separator: KeywordDefinition; - } -} + }; +}; export default loadAndValidateEnvironment; export { loadAndValidateEnvironment as envSchema }; diff --git a/types/index.test-d.ts b/types/index.test-d.ts index 325c3a2..d5979bf 100644 --- a/types/index.test-d.ts +++ b/types/index.test-d.ts @@ -1,19 +1,36 @@ -import { expectError, expectType } from "tsd"; -import envSchema, { EnvSchemaData, EnvSchemaOpt, envSchema as envSchemaNamed, default as envSchemaDefault } from ".."; -import Ajv, { KeywordDefinition } from 'ajv' +import { expectError, expectType } from 'tsd'; +import envSchema, { + EnvSchemaData, + EnvSchemaOpt, + envSchema as envSchemaNamed, + default as envSchemaDefault, +} from '..'; +import Ajv, { KeywordDefinition, JSONSchemaType } from 'ajv'; +import { Static, Type } from '@sinclair/typebox'; -const schema = { - type: "object", - required: ["PORT"], +interface EnvData { + PORT: number; +} + +const schemaWithType: JSONSchemaType = { + type: 'object', + required: ['PORT'], properties: { PORT: { - type: "string", + type: 'number', default: 3000, }, }, }; + +const schemaTypebox = Type.Object({ + PORT: Type.Number({ default: 3000 }), +}); + +type SchemaTypebox = Static; + const data = { - foo: "bar", + foo: 'bar', }; expectType(envSchema()); @@ -23,10 +40,15 @@ expectType(envSchemaDefault()); const emptyOpt: EnvSchemaOpt = {}; expectType(emptyOpt); -const optWithSchema: EnvSchemaOpt = { - schema, +const optWithSchemaTypebox: EnvSchemaOpt = { + schema: schemaTypebox, +}; +expectType(optWithSchemaTypebox); + +const optWithSchemaWithType: EnvSchemaOpt = { + schema: schemaWithType, }; -expectType(optWithSchema); +expectType>(optWithSchemaWithType); const optWithData: EnvSchemaOpt = { data, @@ -58,37 +80,32 @@ const optWithDotEnvOpt: EnvSchemaOpt = { expectType(optWithDotEnvOpt); const optWithEnvExpand: EnvSchemaOpt = { - expandEnv: true -} + expandEnv: true, +}; expectType(optWithEnvExpand); const optWithAjvInstance: EnvSchemaOpt = { - ajv: new Ajv() + ajv: new Ajv(), }; -expectType(optWithAjvInstance) -expectType(envSchema.keywords.separator) - +expectType(optWithAjvInstance); +expectType(envSchema.keywords.separator); const optWithAjvCustomOptions: EnvSchemaOpt = { - ajv: { - customOptions(ajvInstance: Ajv): Ajv { - return new Ajv(); - } - } + ajv: { + customOptions(ajvInstance: Ajv): Ajv { + return new Ajv(); + }, + }, }; -expectType(optWithAjvCustomOptions) +expectType(optWithAjvCustomOptions); expectError({ - ajv: { - customOptions(ajvInstance: Ajv) { - } - } + ajv: { + customOptions(ajvInstance: Ajv) {}, + }, }); -const envSchemaDefaultsToEnvSchemaData = envSchema({ schema: schema }); -expectType(envSchemaDefaultsToEnvSchemaData) +const envSchemaWithType = envSchema({ schema: schemaWithType }); +expectType(envSchemaWithType); -interface EnvData { - PORT: string -} -const envSchemaAllowsToSpecifyType = envSchema({ schema }); -expectType(envSchemaAllowsToSpecifyType) +const envSchemaTypebox = envSchema({ schema: schemaTypebox }); +expectType(envSchemaTypebox);