@pitininja/envious 3.3.5 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,23 +10,69 @@ npm i @pitininja/envious
10
10
 
11
11
  ## Usage
12
12
 
13
- * Environment variables are automatically loaded using [Dotenv](https://github.com/motdotla/dotenv)
14
- * Refer to the [official Typebox documentation](https://github.com/sinclairzx81/typebox) for how to write a Typebox schema
13
+ Environment variables are automatically loaded using [Dotenv](https://github.com/motdotla/dotenv).
14
+
15
+ Refer to the [official Typebox documentation](https://github.com/sinclairzx81/typebox) for how to write a Typebox schema.
15
16
 
16
17
  ```typescript
18
+ import { envious } from '@pitininja/envious';
17
19
  import { Type } from '@sinclair/typebox';
20
+
21
+ const env = envious(
22
+ Type.Object({
23
+ STRING_VAR: Type.String(),
24
+ NUMBER_VAR: Type.Integer(),
25
+ BOOLEAN_VAR_WITH_DEFAULT: Type.Boolean({ default: false }),
26
+ OPTIONAL_VAR: Type.Optional(Type.String())
27
+ })
28
+ );
29
+ ```
30
+
31
+ ## Formats
32
+
33
+ String formats can be loaded in the `formats` property of the options parameter, as an object with the format name as key and a regex or a validation function as value.
34
+
35
+ ```typescript
18
36
  import { envious } from '@pitininja/envious';
37
+ import { Type } from '@sinclair/typebox';
38
+ import dayjs from 'dayjs';
39
+
40
+ const env = envious(
41
+ Type.Object({
42
+ EXAMPLE_URI: Type.String({ format: 'uri' }),
43
+ EXAMPLE_DATE: Type.String({ format: 'date' })
44
+ }),
45
+ {
46
+ formats: {
47
+ uri: /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/)?[^\s]*$/i,
48
+ date: (val) => dayjs(val).isValid()
49
+ }
50
+ }
51
+ );
52
+ ```
53
+
54
+ ## Errors
55
+
56
+ If something goes wrong, Envious will throw an error of class `EnviousError`.
57
+
58
+ An `EnviousError` error contains a message and a list of `ValueError` (an error class taken from Typebox).
59
+
60
+ Here is a simple example of how to handle Envious errors :
61
+
62
+ ```typescript
63
+ import { envious, EnviousError } from '@pitininja/envious';
64
+ import { Type } from '@sinclair/typebox';
19
65
 
20
- export const schema = Type.Object({
21
- STRING_VAR: Type.String(),
22
- NUMBER_VAR: Type.Integer(),
23
- BOOLEAN_VAR_WITH_DEFAULT: Type.Boolean({ default: false }),
24
- OPTIONAL_VAR: Type.Optional(Type.String())
25
- });
26
-
27
- /**
28
- Parse your environment variables.
29
- If the environment variables don't match the schema, an error will be thrown.
30
- */
31
- const env = envious(schema);
66
+ try {
67
+ const env = envious(
68
+ Type.Object({
69
+ STRING_VAR: Type.String()
70
+ })
71
+ );
72
+ } catch (err: unknown) {
73
+ if (err instanceof EnviousError) {
74
+ console.log('Message:', err.message); // Error message
75
+ console.log('Errors:', err.errors); // Typebox's ValueError array
76
+ }
77
+ }
32
78
  ```
package/dist/index.d.ts CHANGED
@@ -1,3 +1,14 @@
1
1
  import 'dotenv/config';
2
- import type { Static, TObject } from '@sinclair/typebox';
3
- export declare const envious: <T extends TObject>(schema: T) => Static<T>;
2
+ import { type Static, type TObject } from '@sinclair/typebox';
3
+ import { type ValueError } from '@sinclair/typebox/value';
4
+ export type EnviousFormats = {
5
+ [name: string]: RegExp | ((val: unknown) => boolean);
6
+ };
7
+ export type EnviousOptions = {
8
+ formats: EnviousFormats;
9
+ };
10
+ export declare class EnviousError extends Error {
11
+ errors: ValueError[];
12
+ constructor(message: string, errors?: ValueError[]);
13
+ }
14
+ export declare const envious: <T extends TObject>(schema: T, options?: EnviousOptions) => Static<T>;
package/dist/index.js CHANGED
@@ -1,34 +1,48 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.envious = void 0;
3
+ exports.envious = exports.EnviousError = void 0;
7
4
  require("dotenv/config");
8
- const node_os_1 = __importDefault(require("node:os"));
5
+ const typebox_1 = require("@sinclair/typebox");
9
6
  const value_1 = require("@sinclair/typebox/value");
10
- const envious = (schema) => {
11
- const parsed = value_1.Value.Parse(schema, process.env);
12
- const errors = [...value_1.Value.Errors(schema, parsed)];
13
- if (errors.length) {
14
- const computedErrorMessages = {};
15
- for (const { path, message } of errors) {
16
- const envVarName = path.replace(/^\//, '');
17
- if (!computedErrorMessages[envVarName]) {
18
- computedErrorMessages[envVarName] = [];
19
- }
20
- computedErrorMessages[envVarName].push(message);
7
+ class EnviousError extends Error {
8
+ errors;
9
+ constructor(message, errors) {
10
+ super(message);
11
+ this.errors = errors ?? [];
12
+ }
13
+ }
14
+ exports.EnviousError = EnviousError;
15
+ const setFormats = (formats) => {
16
+ for (const name of Object.keys(formats)) {
17
+ const format = formats[name];
18
+ typebox_1.FormatRegistry.Set(name, format instanceof RegExp ? (val) => format.test(val) : format);
19
+ }
20
+ };
21
+ const envious = (schema, options) => {
22
+ const invalidEnvVarMessage = 'Invalid environment variables';
23
+ try {
24
+ if (options?.formats) {
25
+ setFormats(options.formats);
26
+ }
27
+ const parsed = value_1.Value.Parse(schema, process.env);
28
+ const errors = [...value_1.Value.Errors(schema, parsed)];
29
+ if (errors.length) {
30
+ throw new EnviousError(invalidEnvVarMessage, errors);
31
+ }
32
+ return value_1.Value.Cast({
33
+ ...schema,
34
+ additionalProperties: false
35
+ }, parsed);
36
+ }
37
+ catch (err) {
38
+ if (err instanceof EnviousError) {
39
+ throw err;
40
+ }
41
+ if (err instanceof value_1.AssertError) {
42
+ throw new EnviousError(invalidEnvVarMessage, err.error ? [err.error] : []);
21
43
  }
22
- const errorTextParts = [
23
- 'Invalid environment variables',
24
- ...Object.entries(computedErrorMessages).map(([varName, messages]) => ` ${varName} : ${messages.join(', ')}`)
25
- ];
26
- throw new Error(errorTextParts.join(node_os_1.default.EOL));
44
+ throw new EnviousError(`Unexpected error while parsing environment variables${err instanceof Error ? ` (${err.message})` : ''}`);
27
45
  }
28
- return value_1.Value.Cast({
29
- ...schema,
30
- additionalProperties: false
31
- }, parsed);
32
46
  };
33
47
  exports.envious = envious;
34
48
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,yBAAuB;AACvB,sDAAyB;AAEzB,mDAAgD;AAEzC,MAAM,OAAO,GAAG,CAAoB,MAAS,EAAa,EAAE;IAC/D,MAAM,MAAM,GAAG,aAAK,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,CAAC,GAAG,aAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACjD,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAChB,MAAM,qBAAqB,GAA6B,EAAE,CAAC;QAC3D,KAAK,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,MAAM,EAAE,CAAC;YACrC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC3C,IAAI,CAAC,qBAAqB,CAAC,UAAU,CAAC,EAAE,CAAC;gBACrC,qBAAqB,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC;YAC3C,CAAC;YACD,qBAAqB,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpD,CAAC;QACD,MAAM,cAAc,GAAa;YAC7B,+BAA+B;YAC/B,GAAG,MAAM,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,GAAG,CACxC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,KAAK,OAAO,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACnE;SACJ,CAAC;QACF,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,iBAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,aAAK,CAAC,IAAI,CACb;QACI,GAAG,MAAM;QACT,oBAAoB,EAAE,KAAK;KAC9B,EACD,MAAM,CACT,CAAC;AACN,CAAC,CAAC;AA3BW,QAAA,OAAO,WA2BlB"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yBAAuB;AACvB,+CAA8E;AAC9E,mDAA8E;AAU9E,MAAa,YAAa,SAAQ,KAAK;IACnC,MAAM,CAAe;IACrB,YAAY,OAAe,EAAE,MAAqB;QAC9C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC;IAC/B,CAAC;CACJ;AAND,oCAMC;AAED,MAAM,UAAU,GAAG,CAAC,OAAuB,EAAE,EAAE;IAC3C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7B,wBAAc,CAAC,GAAG,CACd,IAAI,EACJ,MAAM,YAAY,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAChE,CAAC;IACN,CAAC;AACL,CAAC,CAAC;AAEK,MAAM,OAAO,GAAG,CACnB,MAAS,EACT,OAAwB,EACf,EAAE;IACX,MAAM,oBAAoB,GAAG,+BAA+B,CAAC;IAC7D,IAAI,CAAC;QACD,IAAI,OAAO,EAAE,OAAO,EAAE,CAAC;YACnB,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QACD,MAAM,MAAM,GAAG,aAAK,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,CAAC,GAAG,aAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QACjD,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,IAAI,YAAY,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,aAAK,CAAC,IAAI,CACb;YACI,GAAG,MAAM;YACT,oBAAoB,EAAE,KAAK;SAC9B,EACD,MAAM,CACT,CAAC;IACN,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,IAAI,GAAG,YAAY,YAAY,EAAE,CAAC;YAC9B,MAAM,GAAG,CAAC;QACd,CAAC;QACD,IAAI,GAAG,YAAY,mBAAW,EAAE,CAAC;YAC7B,MAAM,IAAI,YAAY,CAClB,oBAAoB,EACpB,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAC/B,CAAC;QACN,CAAC;QACD,MAAM,IAAI,YAAY,CAClB,uDAAuD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3G,CAAC;IACN,CAAC;AACL,CAAC,CAAC;AAnCW,QAAA,OAAO,WAmClB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pitininja/envious",
3
- "version": "3.3.5",
3
+ "version": "4.0.0",
4
4
  "license": "AGPL-3.0-or-later",
5
5
  "homepage": "https://github.com/pitininja/envious",
6
6
  "repository": {
@@ -24,7 +24,7 @@
24
24
  "devDependencies": {
25
25
  "@biomejs/biome": "^1.9.4",
26
26
  "@tsconfig/recommended": "^1.0.8",
27
- "@types/node": "^22.8.4",
27
+ "@types/node": "^22.8.7",
28
28
  "@vitest/coverage-istanbul": "^2.1.4",
29
29
  "husky": "^9.1.6",
30
30
  "typescript": "^5.6.3",