Comparing version 0.3.0 to 0.3.1
@@ -1,4 +0,4 @@ | ||
export * as z from "zod"; | ||
export { z } from "zod"; | ||
export * from "./parse-env"; | ||
export * from "./preprocessors"; | ||
export * from "./extra-schemas"; |
@@ -9,14 +9,2 @@ "use strict"; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
@@ -27,3 +15,4 @@ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
exports.z = void 0; | ||
exports.z = __importStar(require("zod")); | ||
var zod_1 = require("zod"); | ||
Object.defineProperty(exports, "z", { enumerable: true, get: function () { return zod_1.z; } }); | ||
__exportStar(require("./parse-env"), exports); | ||
@@ -30,0 +19,0 @@ __exportStar(require("./preprocessors"), exports); |
@@ -162,7 +162,12 @@ "use strict"; | ||
throw new Error(`Zod type not yet supported: "${typeName}" (PRs welcome)`); | ||
case TypeName.ZodAny: | ||
case TypeName.ZodUnknown: | ||
throw new Error([ | ||
`Zod type not supported: ${typeName}`, | ||
"You can use `z.string()` or `z.string().optional()` instead of the above type.", | ||
"(Environment variables are already constrained to `string | undefined`.)", | ||
].join("\n")); | ||
// some of these types could maybe be supported (if only via the identity | ||
// function), but don't necessarily represent something meaningful as a | ||
// top-level schema passed to znv. | ||
case TypeName.ZodAny: | ||
case TypeName.ZodUnknown: | ||
case TypeName.ZodVoid: | ||
@@ -169,0 +174,0 @@ case TypeName.ZodNever: |
{ | ||
"name": "znv", | ||
"version": "0.3.0", | ||
"version": "0.3.1", | ||
"description": "Parse your environment with Zod schemas", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
105
README.md
# znv | ||
<p align="center"> | ||
<img src="logo.svg" height="90"> | ||
<img src="logo.svg" height="90" alt="znv logo"> | ||
</p> | ||
@@ -43,18 +43,45 @@ | ||
import { parseEnv } from "znv"; | ||
import * as z from "zod"; | ||
import { z } from "zod"; | ||
export const { LLAMA_COUNT, COLOR } = parseEnv(process.env, { | ||
export const { NICKNAME, LLAMA_COUNT, COLOR, SHINY } = parseEnv(process.env, { | ||
NICKNAME: z.string().nonempty(), | ||
LLAMA_COUNT: z.number().int().positive(), | ||
COLOR: z.enum(["red", "blue"]), | ||
SHINY: z.boolean().default(true), | ||
}); | ||
console.log([NICKNAME, LLAMA_COUNT, COLOR, SHINY].join(", ")); | ||
``` | ||
In the above example, the exported values `LLAMA_COUNT` and `COLOR` are | ||
guaranteed to be defined and will be typed as `number` and `'red' | 'blue'` | ||
respectively. | ||
Let's run this with [ts-node](https://github.com/TypeStrong/ts-node): | ||
``` | ||
$ LLAMA_COUNT=huge COLOR=cyan ts-node env.ts | ||
``` | ||
<img src="example.png" width="658" alt="A screenshot showing error output, with parsing errors aggregated and grouped by env var."> | ||
Oops! Let's fix those issues: | ||
``` | ||
$ LLAMA_COUNT=24 COLOR=red NICKNAME=coolguy ts-node env.ts | ||
``` | ||
Now we see the expected output: | ||
``` | ||
coolguy, 24, red, true | ||
``` | ||
Since `parseEnv` didn't throw, our exported values are guaranteed to be defined. | ||
Their TypeScript types will be inferred based on the schemas we used — `COLOR` | ||
will be even be typed to the union of literal strings `'red' | 'blue'` rather | ||
than just `string`. | ||
--- | ||
A more elaborate example: | ||
```ts | ||
// znv re-exports zod as 'z' to save you a few keystrokes. | ||
// znv re-exports zod as 'z' to save a few keystrokes. | ||
import { parseEnv, z, port } from "znv"; | ||
@@ -93,6 +120,4 @@ | ||
// using zod arrays or objects as a spec will attempt to `JSON.parse` the | ||
// env var if it's present. remember, with great power comes great | ||
// responsibility! if you're passing large amounts of data in as an env var, | ||
// you may be doing something wrong. | ||
// using a zod `array()` or `object()` as a spec will make znv attempt to | ||
// `JSON.parse` the env var if it's present. | ||
EDITORS: z.array(z.string().nonempty()), | ||
@@ -228,3 +253,3 @@ | ||
`defaults` accepts a special token as a key: `_`. This is like the `default` | ||
clause in a `switch` case — its value will be used it `NODE_ENV` doesn't match | ||
clause in a `switch` case — its value will be used if `NODE_ENV` doesn't match | ||
any other key in `defaults`. | ||
@@ -246,3 +271,3 @@ | ||
However, `_` still lets you express a few interesting scenarios: | ||
Caveats aside, `_` lets you express a few interesting scenarios: | ||
@@ -254,7 +279,9 @@ ```ts | ||
// default for all environments, but require the var to be passed in in prod. | ||
// default for all non-production environments, but require the var to be | ||
// passed in for production. | ||
{ production: undefined, _: "dev default" } | ||
// unconditional default. equivalent to adding `.default("some default")` | ||
// to the zod schema. | ||
// to the zod schema, but this might be more stylistically consistent with | ||
// your other specs if they use the `defaults` field. | ||
{ _: "unconditional default" } | ||
@@ -264,3 +291,3 @@ ``` | ||
Some testing tools like [Jest](https://jestjs.io/) set `NODE_ENV` to `test`, | ||
so you can also use `defaults` to override env vars for testing. | ||
so you can also use `defaults` to provide default env vars for testing. | ||
@@ -289,8 +316,17 @@ `parseEnv` doesn't restrict or validate `NODE_ENV` to any particular values, | ||
znv tries to do as little work as possible to coerce env vars to the input types | ||
of your schemas. If a string value doesn't look like the input type, znv will | ||
pass it to the validator as-is, with the assumption that the validator will | ||
throw. For example, if your schema is `z.number()` and you pass in "banana", znv | ||
won't coerce it to `NaN`, it'll let your schema say "hey, this is a banana!" | ||
znv tries to do as little work as possible to coerce env vars (which are always | ||
strings when they're present) to the [input | ||
types](https://github.com/colinhacks/zod#what-about-transforms) of your schemas. | ||
If the env var doesn't look like the input type, znv will pass it to the | ||
validator as-is with the assumption that the validator will throw. For example, | ||
if your schema is `z.number()`, znv will test it against a numeric regex first, | ||
rather than unconditionally wrap it in `Number()` or `parseFloat()` (and thus | ||
coerce it to `NaN`). | ||
By modifying as little as possible, znv tries to get out of Zod's way and let it | ||
do the heavy lifting of validation. This also lets us produce less confusing | ||
error messages: if you pass the string "banana" to your number schema, it should | ||
be able to say "you gave me 'banana' instead of a number!" rather than "you gave | ||
me NaN instead of a number!" | ||
**Coercions only happen at the top level of a schema**. If you define an object | ||
@@ -304,4 +340,22 @@ with nested schemas, no coercions will be applied to the keys. | ||
will be passed through. | ||
> Some CLI tool conventions dictate that a variable simply being present in | ||
> the environment (even with no value, eg. setting `MY_VALUE=` with no | ||
> right-hand side) should be interpreted as `true`. However, this convention | ||
> doesn't seem to be in widespread use in Node, probably because it causes the | ||
> var to evaluate to the empty string (which is falsy). znv demands a little | ||
> more specificity by default, while still hedging a bit for some common | ||
> true/false equivalents. If you want the "any defined value" behaviour, you | ||
> can use | ||
> `z.string().optional().transform(v => v === undefined ? false : true)`. | ||
- If your schema's input is an object or array (or record or tuple), znv will | ||
attempt to `JSON.parse` the input value if it's not `undefined` or empty. | ||
> **Remember, with great power comes great responsibility!** If you're using | ||
> an object or array schema to pass in dozens or hundreds of kilobytes of data | ||
> as an env var, you may be doing something wrong. (Certain platforms also | ||
> [impose limits on environment variable | ||
> length](https://devblogs.microsoft.com/oldnewthing/20100203-00/?p=15083).) | ||
- If your schema's input is a Date, znv will call `new Date()` with the input | ||
@@ -313,6 +367,9 @@ value. This has a number of pitfalls, since the `Date()` constructor is | ||
`invalid date` or a completely nonsensical date.) _You should only pass in ISO | ||
8601 date strings_, such as those returned by `Date.prototype.toISOString()`. | ||
8601 date strings_, such as those returned by | ||
[`Date.prototype.toISOString()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString). | ||
Improved validation for Date schemas could be added in a future version. | ||
- Zod defines "nullable" as distinct from "optional". If your schema is | ||
nullable, znv will coerce `undefined` to `null`. Generally it's preferred to | ||
simply use optional. | ||
`nullable`, znv will coerce `undefined` to `null`. Generally it's preferred to | ||
simply use `optional`. | ||
@@ -319,0 +376,0 @@ ## Comparison to other libraries |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
52551
408
509