convex-env 1.0.2 → 2.1.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
@@ -3,21 +3,22 @@
3
3
  </p>
4
4
 
5
5
  <h2 align="center">Convex Env</h2>
6
- <p align="center">Typesafe access to environment variables in Convex</p>
6
+ <p align="center">Type-safe access to environment variables in Convex</p>
7
7
 
8
8
  ### Overview
9
9
 
10
- If you've ever used [t3-env](https://github.com/t3-oss/t3-env), it's basically that, but native to [Convex](https://www.convex.dev).
10
+ Similar to [t3-env](https://github.com/t3-oss/t3-env), but native to [Convex](https://www.convex.dev).
11
11
 
12
12
  Validators currently supported:
13
13
 
14
14
  - v.string()
15
15
  - v.number()
16
16
  - v.boolean()
17
+ - v.union() + v.literal() (strings only)
17
18
 
18
- you can use `v.optional()` on _any_ supported validator, see [example](#usage) below
19
+ You can use `v.optional()` on _any_ supported validator, see [examples](#usage) below
19
20
 
20
- <span style="color: red;"><strong>IMPORTANT</strong></span>: The <code>env</code> object from <code>createEnv</code> should only be used in the Convex runtime, the values on it will not be accessible client side.
21
+ <span style="color: red;"><strong>IMPORTANT</strong></span>: The <code>env</code> object from <code>createEnv</code> should only be used in the Convex runtime, the values on it will not be accessible client-side.
21
22
 
22
23
  ### Installation
23
24
 
@@ -50,6 +51,7 @@ import { createEnv } from "convex-env";
50
51
  import { v } from "convex/values";
51
52
 
52
53
  export const env = createEnv({
54
+ ENVIRONMENT: v.union(v.literal("development"), v.literal("production")),
53
55
  CONVEX_SITE_URL: v.string(),
54
56
  BETTER_AUTH_SECRET: v.string(),
55
57
  GOOGLE_CLIENT_ID: v.string(),
@@ -79,7 +81,7 @@ export const createAuth = (ctx: GenericCtx<DataModel>) => {
79
81
  };
80
82
  ```
81
83
 
82
- You can also pass values manually
84
+ You can optionally pass values manually
83
85
 
84
86
  ```typescript
85
87
  // convex/convex.env.ts
@@ -87,24 +89,50 @@ You can also pass values manually
87
89
  import { createEnv } from "convex-env";
88
90
  import { v } from "convex/values";
89
91
 
90
- export const env = createEnv(
91
- {
92
+ export const env = createEnv({
93
+ schema: {
92
94
  CONVEX_SITE_URL: v.string(),
93
- BETTER_AUTH_SECRET: v.string(),
94
- GOOGLE_CLIENT_ID: v.string(),
95
- GOOGLE_CLIENT_SECRET: v.string(),
96
95
  MAX_REQUESTS_PER_USER: v.number(),
97
96
  DEBUG_MODE: v.optional(v.boolean()),
98
97
  },
99
- {
98
+ // optional, defaults to process.env
99
+ values: {
100
100
  CONVEX_SITE_URL: process.env.CONVEX_SITE_URL,
101
- BETTER_AUTH_SECRET: process.env.BETTER_AUTH_SECRET,
102
- GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
103
- GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
104
101
  MAX_REQUESTS_PER_USER: process.env.MAX_REQUESTS_PER_USER,
105
102
  DEBUG_MODE: process.env.DEBUG_MODE,
106
- }
107
- );
103
+ },
104
+ });
105
+ ```
106
+
107
+ If you're running into an issue where it says an environment variable isn't found when you **know for a fact** that it is there, then you may need to use `verifyEnv` to verify the existence and type separately from the creation of the env object.
108
+
109
+ ```typescript
110
+ // convex/convex.env.ts
111
+
112
+ import { createEnv } from "convex-env";
113
+ import { v } from "convex/values";
114
+
115
+ export const schema = {
116
+ CONVEX_SITE_URL: v.string(),
117
+ MAX_REQUESTS_PER_USER: v.number(),
118
+ DEBUG_MODE: v.optional(v.boolean()),
119
+ };
120
+
121
+ export const env = createEnv({
122
+ schema,
123
+ options: {
124
+ skipValidation: true,
125
+ },
126
+ });
127
+ ```
128
+
129
+ ```typescript
130
+ // convex/convex.config.ts
131
+
132
+ import { schema } from "./convex.env";
133
+ import { verifyEnv } from "convex-env";
134
+
135
+ verifyEnv(schema);
108
136
  ```
109
137
 
110
138
  ### Why use it?
package/dist/index.d.mts CHANGED
@@ -1,19 +1,25 @@
1
- import { Infer, VBoolean, VFloat64, VOptional, VString } from "convex/values";
1
+ import { Infer, VBoolean, VFloat64, VLiteral, VOptional, VString, VUnion } from "convex/values";
2
2
 
3
3
  //#region src/types.d.ts
4
- type AllowedPrimitiveValidators = VString | VFloat64 | VBoolean;
4
+ type AllowedBaseValidators = VString | VFloat64 | VBoolean;
5
+ type AllowedLiteralValidators = VLiteral<string>;
6
+ type AllowedUnionValidators = VUnion<any, [AllowedLiteralValidators, AllowedLiteralValidators, ...AllowedLiteralValidators[]]>;
7
+ type AllowedPrimitiveValidators = AllowedBaseValidators | AllowedLiteralValidators | AllowedUnionValidators;
5
8
  type AllowedOptionalValidators = VOptional<AllowedPrimitiveValidators>;
6
9
  type AllowedValidators = AllowedPrimitiveValidators | AllowedOptionalValidators;
7
10
  type InferredOuput<V extends AllowedValidators> = Infer<V>;
11
+ type Values<Schema> = Partial<Record<keyof Schema, string | undefined>>;
12
+ type CreateEnvOptions = {
13
+ skipValidation?: boolean;
14
+ };
8
15
  //#endregion
9
16
  //#region src/index.d.ts
10
17
  /**
11
- * WARNING: The object returned by `createEnv` should only be accessed within the Convex runtime.
12
- * The values on the returned object will NOT be accessible client side.
13
18
  *
14
- * @param entries - The names of the environment variables and their validators
15
- * @param inputEnv (Optional) pass in a record of the values to use, defaults to process.env if not provided
16
- * @returns An object with the same keys as the entries, but with the validated typesafe values
19
+ * WARNING: The object returned by `createEnv` should only be accessed within the Convex runtime. The values on the returned object will NOT be accessible client-side.
20
+ *
21
+ * @param args - You can either pass in the schema object directly, or an object with the schema, values, and options. See examples below.
22
+ * @returns An object with the same keys as the schema, but with validated type-safe values.
17
23
  *
18
24
  * @public
19
25
  *
@@ -26,16 +32,55 @@ type InferredOuput<V extends AllowedValidators> = Infer<V>;
26
32
  *
27
33
  * @example
28
34
  * const env = createEnv({
35
+ * schema: {
36
+ * FOO: v.string(),
37
+ * BAR: v.number(),
38
+ * BAZ: v.optional(v.boolean()),
39
+ * },
40
+ * values: {
41
+ * FOO: process.env.FOO,
42
+ * BAR: "42",
43
+ * BAZ: "true",
44
+ * },
45
+ * options: {
46
+ * skipValidation: true,
47
+ * },
48
+ * });
49
+ *
50
+ */
51
+ declare const createEnv: <Schema extends Record<string, AllowedValidators>>(args: Schema | {
52
+ schema: Schema;
53
+ values?: Values<Schema>;
54
+ options?: CreateEnvOptions;
55
+ }) => { [K in keyof Schema]: InferredOuput<Schema[K]> };
56
+ /**
57
+ *
58
+ * @description You may want to verify the existence and type of the environment variables separately from the creation of the env object. If so, use this function in convex.config.ts, and use the skipValidation option when calling createEnv.
59
+ *
60
+ * @param args - You can either pass in the schema object directly, or an object with the schema and values. See examples below.
61
+ * @returns void
62
+ *
63
+ * @public
64
+ *
65
+ * @example
66
+ * const schema = {
29
67
  * FOO: v.string(),
30
68
  * BAR: v.number(),
31
69
  * BAZ: v.boolean(),
32
- * }, {
33
- * FOO: process.env.FOO,
34
- * BAR: process.env.BAR,
35
- * BAZ: process.env.BAZ,
36
- * });
70
+ * };
71
+ * verifyEnv(schema);
37
72
  *
73
+ * @example
74
+ * const schema = {
75
+ * FOO: v.string(),
76
+ * BAR: v.number(),
77
+ * BAZ: v.boolean(),
78
+ * };
79
+ * verifyEnv({ schema, values: { FOO: process.env.FOO, BAR: "42", BAZ: "true" } });
38
80
  */
39
- declare const createEnv: <T extends Record<string, AllowedValidators>>(entries: T, inputEnv?: Partial<Record<keyof T, string | undefined>>) => { [K in keyof T]: InferredOuput<T[K]> };
81
+ declare const verifyEnv: <Schema extends Record<string, AllowedValidators>>(args: Schema | {
82
+ schema: Schema;
83
+ values?: Values<Schema>;
84
+ }) => void;
40
85
  //#endregion
41
- export { createEnv };
86
+ export { createEnv, verifyEnv };
package/dist/index.mjs CHANGED
@@ -1,13 +1,14 @@
1
1
  import { validate } from "convex-helpers/validators";
2
- import { v } from "convex/values";
3
2
 
4
3
  //#region src/transform.ts
5
4
  const transformed = (value, validator) => {
6
5
  if (value === void 0) return void 0;
7
6
  if (value.trim() === "") throw new Error("Value is empty");
8
- if (validator.kind === v.string().kind) return value;
9
- if (validator.kind === v.number().kind) return validateAndTransformNumber(value);
10
- if (validator.kind === v.boolean().kind) return validateAndTransformBoolean(value);
7
+ if (validator.kind === "union") return value;
8
+ if (validator.kind === "literal") return value;
9
+ if (validator.kind === "string") return value;
10
+ if (validator.kind === "float64") return validateAndTransformNumber(value);
11
+ if (validator.kind === "boolean") return validateAndTransformBoolean(value);
11
12
  throw new Error("Validator is not supported");
12
13
  };
13
14
  const validateAndTransformNumber = (value) => {
@@ -24,12 +25,11 @@ const validateAndTransformBoolean = (value) => {
24
25
  //#endregion
25
26
  //#region src/index.ts
26
27
  /**
27
- * WARNING: The object returned by `createEnv` should only be accessed within the Convex runtime.
28
- * The values on the returned object will NOT be accessible client side.
29
28
  *
30
- * @param entries - The names of the environment variables and their validators
31
- * @param inputEnv (Optional) pass in a record of the values to use, defaults to process.env if not provided
32
- * @returns An object with the same keys as the entries, but with the validated typesafe values
29
+ * WARNING: The object returned by `createEnv` should only be accessed within the Convex runtime. The values on the returned object will NOT be accessible client-side.
30
+ *
31
+ * @param args - You can either pass in the schema object directly, or an object with the schema, values, and options. See examples below.
32
+ * @returns An object with the same keys as the schema, but with validated type-safe values.
33
33
  *
34
34
  * @public
35
35
  *
@@ -42,25 +42,42 @@ const validateAndTransformBoolean = (value) => {
42
42
  *
43
43
  * @example
44
44
  * const env = createEnv({
45
- * FOO: v.string(),
46
- * BAR: v.number(),
47
- * BAZ: v.boolean(),
48
- * }, {
49
- * FOO: process.env.FOO,
50
- * BAR: process.env.BAR,
51
- * BAZ: process.env.BAZ,
45
+ * schema: {
46
+ * FOO: v.string(),
47
+ * BAR: v.number(),
48
+ * BAZ: v.optional(v.boolean()),
49
+ * },
50
+ * values: {
51
+ * FOO: process.env.FOO,
52
+ * BAR: "42",
53
+ * BAZ: "true",
54
+ * },
55
+ * options: {
56
+ * skipValidation: true,
57
+ * },
52
58
  * });
53
59
  *
54
60
  */
55
- const createEnv = (entries, inputEnv) => {
56
- const env = inputEnv ?? process.env;
57
- return Object.keys(entries).map((key) => {
61
+ const createEnv = (args) => {
62
+ let schema;
63
+ let inputValues;
64
+ let options;
65
+ const isSchemaObject = (arg) => {
66
+ return "schema" in arg;
67
+ };
68
+ if (isSchemaObject(args)) {
69
+ schema = args.schema;
70
+ inputValues = args.values;
71
+ options = args.options;
72
+ } else schema = args;
73
+ const values = inputValues ?? process.env;
74
+ return Object.keys(schema).map((key) => {
58
75
  try {
59
- const validator = entries[key];
60
- const envValue = env[key];
61
- if (validator.isOptional === "required" && envValue === void 0) throw new Error("Variable is required but not found in env");
76
+ const validator = schema[key];
77
+ const envValue = values[key];
78
+ if (validator.isOptional === "required" && envValue === void 0 && options?.skipValidation !== true) throw new Error("Variable is required but not found in env");
62
79
  const transformedValue = transformed(envValue, validator);
63
- if (validate(validator, transformedValue) === false) throw new Error(`Variable failed validation`);
80
+ if (validate(validator, transformedValue) === false && options?.skipValidation !== true) throw new Error(`Variable failed validation`);
64
81
  return [key, transformedValue];
65
82
  } catch (error) {
66
83
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -71,6 +88,54 @@ const createEnv = (entries, inputEnv) => {
71
88
  return acc;
72
89
  }, {});
73
90
  };
91
+ /**
92
+ *
93
+ * @description You may want to verify the existence and type of the environment variables separately from the creation of the env object. If so, use this function in convex.config.ts, and use the skipValidation option when calling createEnv.
94
+ *
95
+ * @param args - You can either pass in the schema object directly, or an object with the schema and values. See examples below.
96
+ * @returns void
97
+ *
98
+ * @public
99
+ *
100
+ * @example
101
+ * const schema = {
102
+ * FOO: v.string(),
103
+ * BAR: v.number(),
104
+ * BAZ: v.boolean(),
105
+ * };
106
+ * verifyEnv(schema);
107
+ *
108
+ * @example
109
+ * const schema = {
110
+ * FOO: v.string(),
111
+ * BAR: v.number(),
112
+ * BAZ: v.boolean(),
113
+ * };
114
+ * verifyEnv({ schema, values: { FOO: process.env.FOO, BAR: "42", BAZ: "true" } });
115
+ */
116
+ const verifyEnv = (args) => {
117
+ let schema;
118
+ let inputValues;
119
+ const isSchemaObject = (arg) => {
120
+ return "schema" in arg;
121
+ };
122
+ if (isSchemaObject(args)) {
123
+ schema = args.schema;
124
+ inputValues = args.values;
125
+ } else schema = args;
126
+ const values = inputValues ?? process.env;
127
+ Object.keys(schema).map((key) => {
128
+ try {
129
+ const validator = schema[key];
130
+ const envValue = values[key];
131
+ if (validator.isOptional === "required" && envValue === void 0) throw new Error("Variable is required but not found in env");
132
+ if (validate(validator, transformed(envValue, validator)) === false) throw new Error(`Variable failed validation`);
133
+ } catch (error) {
134
+ const errorMessage = error instanceof Error ? error.message : String(error);
135
+ throw new Error(`Error verifying environment variable ${key}: ${errorMessage}`);
136
+ }
137
+ });
138
+ };
74
139
 
75
140
  //#endregion
76
- export { createEnv };
141
+ export { createEnv, verifyEnv };
package/package.json CHANGED
@@ -1,12 +1,22 @@
1
1
  {
2
2
  "name": "convex-env",
3
3
  "type": "module",
4
- "version": "1.0.2",
5
- "description": "Typesafe access to environment variables in Convex",
4
+ "version": "2.1.0",
5
+ "license": "MIT",
6
+ "description": "Type-safe access to environment variables in Convex",
6
7
  "exports": {
7
8
  ".": "./dist/index.mjs",
8
9
  "./package.json": "./package.json"
9
10
  },
11
+ "keywords": [
12
+ "convex",
13
+ "env",
14
+ "environment variables"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/bentsignal/convex-env.git"
19
+ },
10
20
  "main": "./dist/index.mjs",
11
21
  "module": "./dist/index.mjs",
12
22
  "types": "./dist/index.d.mts",