Skip to content

Commit decfe04

Browse files
committed
v5.0.0 🚀 - ReScript Schema V9 & BigInt support
1 parent aeac501 commit decfe04

9 files changed

+1922
-1009
lines changed

README.md

+28-30
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,7 @@ Heavily inspired by the great project [envsafe](https://github.com/KATT/envsafe)
2020
- Built for node.js **and** the browser
2121
- **Composable** parsers with **[rescript-schema](https://github.com/DZakh/rescript-schema)**
2222

23-
## How to use
24-
25-
### Install
26-
27-
```sh
28-
npm install rescript-envsafe rescript-schema
29-
```
30-
31-
Then add `rescript-envsafe` and `rescript-schema` to `bs-dependencies` in your `rescript.json`:
32-
33-
```diff
34-
{
35-
...
36-
+ "bs-dependencies": ["rescript-envsafe", "rescript-schema"],
37-
+ "bsc-flags": ["-open RescriptSchema"],
38-
}
39-
```
40-
41-
### Basic usage
23+
## Basic usage
4224

4325
```rescript
4426
%%private(let envSafe = EnvSafe.make())
@@ -52,15 +34,31 @@ let nodeEnv = envSafe->EnvSafe.get(
5234
]),
5335
~devFallback=#development,
5436
)
55-
let port = envSafe->EnvSafe.get("PORT", S.int->S.Int.port, ~fallback=3000)
56-
let apiUrl = envSafe->EnvSafe.get("API_URL", S.string->S.String.url, ~devFallback="https://example.com/graphql")
37+
let port = envSafe->EnvSafe.get("PORT", S.int->S.port, ~fallback=3000)
38+
let apiUrl = envSafe->EnvSafe.get("API_URL", S.string->S.url, ~devFallback="https://example.com/graphql")
5739
let auth0ClientId = envSafe->EnvSafe.get("AUTH0_CLIENT_ID", S.string)
5840
let auth0Domain = envSafe->EnvSafe.get("AUTH0_DOMAIN", S.string)
5941
6042
// 🧠 If you forget to close `envSafe` then invalid vars end up being `undefined` leading to an expected runtime error.
6143
envSafe->EnvSafe.close
6244
```
6345

46+
## Install
47+
48+
```sh
49+
npm install rescript-envsafe rescript-schema
50+
```
51+
52+
Then add `rescript-envsafe` and `rescript-schema` to `bs-dependencies` in your `rescript.json`:
53+
54+
```diff
55+
{
56+
...
57+
+ "bs-dependencies": ["rescript-envsafe", "rescript-schema"],
58+
+ "bsc-flags": ["-open RescriptSchema"],
59+
}
60+
```
61+
6462
## API Reference
6563

6664
### **`EnvSafe.make`**
@@ -78,21 +76,21 @@ Creates `envSafe` to start working with environment variables. By default it use
7876
`(EnvSafe.t, string, S.t<'value>, ~allowEmpty: bool=?, ~fallback: 'value=?, ~devFallback: 'value=?, ~input: option<string>=?) => 'value`
7977

8078
```rescript
81-
let port = envSafe->EnvSafe.get("PORT", S.int->S.Int.port, ~fallback=3000)
79+
let port = envSafe->EnvSafe.get("PORT", S.int->S.port, ~fallback=3000)
8280
```
8381

8482
Gets an environment variable from `envSafe` applying coercion and parsing logic of `schema`.
8583

8684
#### Possible options
8785

88-
| Name | Type | Description |
89-
| ------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
90-
| `name` | `string` | Name of the environment variable |
91-
| `schema` | `S.t<'value>` | A schema created with **[rescript-schema](https://github.com/DZakh/rescript-schema)**. It's used for coercion and parsing. For bool schemas coerces `"0", "1", "true", "false", "t", "f"` to boolean values. For int and float schemas coerces string to number. For other non-string schemas the value is coerced using `JSON.parse` before being validated. |
92-
| `fallback` | `'value=?` | A fallback value when the environment variable is missing. |
93-
| `devFallback` | `'value=?` | A fallback value to use only when `NODE_ENV` is not `production`. This is handy for env vars that are required for production environments, but optional for development and testing. |
94-
| `input` | `string=?` | As some environments don't allow you to dynamically read env vars, we can manually put it in as well. Example: `input=%raw("process.env.NEXT_PUBLIC_API_URL")`. |
95-
| `allowEmpty` | `bool=false` | Default behavior is `false` which treats empty strings as the value is missing. if explicit empty strings are OK, pass in `true`. |
86+
| Name | Type | Description |
87+
| ------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
88+
| `name` | `string` | Name of the environment variable |
89+
| `schema` | `S.t<'value>` | A schema created with **[rescript-schema](https://github.com/DZakh/rescript-schema)**. It's used for coercion and parsing. For bool schemas coerces `"0", "1", "true", "false", "t", "f"` to boolean values. For int, float and bigint schemas coerces string to number. For other non-string schemas the value is coerced using `JSON.parse` before being validated. |
90+
| `fallback` | `'value=?` | A fallback value when the environment variable is missing. |
91+
| `devFallback` | `'value=?` | A fallback value to use only when `NODE_ENV` is not `production`. This is handy for env vars that are required for production environments, but optional for development and testing. |
92+
| `input` | `string=?` | As some environments don't allow you to dynamically read env vars, we can manually put it in as well. Example: `input=%raw("process.env.NEXT_PUBLIC_API_URL")`. |
93+
| `allowEmpty` | `bool=false` | Default behavior is `false` which treats empty strings as the value is missing. if explicit empty strings are OK, pass in `true`. |
9694

9795
### **`EnvSafe.close`**
9896

__tests__/EnvSafe_bool_test.res

+1-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ test(`Fails to get Bool value when the env is "2"`, t => {
172172
name: "TypeError",
173173
message: `========================================
174174
❌ Invalid environment variables:
175-
BOOL_ENV: Failed parsing at root. Reason: Expected Bool, received "2"
175+
BOOL_ENV: Failed parsing at root. Reason: Expected boolean, received "2"
176176
========================================`,
177177
},
178178
)

__tests__/EnvSafe_json_test.res

+3-3
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ test(`Uses JSON parsing with JSON schema`, t => {
5252
})
5353
})
5454

55-
test(`Doens't use JSON parsing with never schema`, t => {
55+
test(`Doesn't use JSON parsing with never schema`, t => {
5656
let envSafe = EnvSafe.make(
5757
~env=Obj.magic({
5858
"ENV": `[1, 2]`,
@@ -67,7 +67,7 @@ test(`Doens't use JSON parsing with never schema`, t => {
6767
~expectations={
6868
message: `========================================
6969
❌ Invalid environment variables:
70-
ENV: Failed parsing at root. Reason: Expected Never, received "[1, 2]"
70+
ENV: Failed parsing at root. Reason: Expected never, received "[1, 2]"
7171
========================================`,
7272
},
7373
)
@@ -88,7 +88,7 @@ test(`Fails with invalid json string`, t => {
8888
~expectations={
8989
message: `========================================
9090
❌ Invalid environment variables:
91-
ENV: Failed parsing at root. Reason: Expected Array(Int), received "[1, 2],"
91+
ENV: Failed parsing at root. Reason: Expected array<int32>, received "[1, 2],"
9292
========================================`,
9393
},
9494
)

__tests__/EnvSafe_number_test.res

+27-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,32 @@ test(`Successfully get Literal Float value when the env is "1"`, t => {
5252
})
5353
})
5454

55+
test(`Successfully get Literal BigInt value when the env is "1"`, t => {
56+
let envSafe = EnvSafe.make(
57+
~env=Obj.magic({
58+
"INT_ENV": "1",
59+
}),
60+
)
61+
62+
t->Assert.is(envSafe->EnvSafe.get("INT_ENV", S.literal(1n)), 1n)
63+
t->Assert.notThrows(() => {
64+
envSafe->EnvSafe.close
65+
})
66+
})
67+
68+
test(`Successfully get BigInt value when the env is "1"`, t => {
69+
let envSafe = EnvSafe.make(
70+
~env=Obj.magic({
71+
"INT_ENV": "1",
72+
}),
73+
)
74+
75+
t->Assert.is(envSafe->EnvSafe.get("INT_ENV", S.bigint), 1n)
76+
t->Assert.notThrows(() => {
77+
envSafe->EnvSafe.close
78+
})
79+
})
80+
5581
test(`Fails to get invalid number`, t => {
5682
let envSafe = EnvSafe.make(
5783
~env=Obj.magic({
@@ -68,7 +94,7 @@ test(`Fails to get invalid number`, t => {
6894
name: "TypeError",
6995
message: `========================================
7096
❌ Invalid environment variables:
71-
INT_ENV: Failed parsing at root. Reason: Expected Int, received "1_000"
97+
INT_ENV: Failed parsing at root. Reason: Expected int32, received "1_000"
7298
========================================`,
7399
},
74100
)

__tests__/EnvSafe_test.res

+7-7
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ test("Uses devFallback value when env is missing", t => {
140140
t->Assert.is(
141141
envSafe->EnvSafe.get(
142142
"MISSING_ENV",
143-
S.literal("invalid")->S.variant(_ => #polymorphicToTestFunctionType2),
143+
S.literal("invalid")->S.to(_ => #polymorphicToTestFunctionType2),
144144
~devFallback=#polymorphicToTestFunctionType,
145145
),
146146
#polymorphicToTestFunctionType,
@@ -160,7 +160,7 @@ test("Uses fallback value when env is missing", t => {
160160
t->Assert.is(
161161
envSafe->EnvSafe.get(
162162
"MISSING_ENV",
163-
S.literal("invalid")->S.variant(_ => #polymorphicToTestFunctionType2),
163+
S.literal("invalid")->S.to(_ => #polymorphicToTestFunctionType2),
164164
~fallback=#polymorphicToTestFunctionType,
165165
),
166166
#polymorphicToTestFunctionType,
@@ -226,7 +226,7 @@ test("Doesn't use devFallback value when NODE_ENV is production", t => {
226226
t->Assert.is(
227227
envSafe->EnvSafe.get(
228228
"MISSING_ENV",
229-
S.literal("invalid")->S.variant(_ => #polymorphicToTestFunctionType2),
229+
S.literal("invalid")->S.to(_ => #polymorphicToTestFunctionType2),
230230
~devFallback=#polymorphicToTestFunctionType,
231231
),
232232
%raw(`undefined`),
@@ -305,7 +305,7 @@ test("Closes with 1 valid, 3 missing and 2 invalid environment variables", t =>
305305
name: "TypeError",
306306
message: `========================================
307307
❌ Invalid environment variables:
308-
BOOL_ENV1: Failed parsing at root. Reason: Expected Int, received "true"
308+
BOOL_ENV1: Failed parsing at root. Reason: Expected int32, received "true"
309309
BOOL_ENV2: Failed parsing at root. Reason: Expected true, received false
310310
💨 Missing environment variables:
311311
MISSING_ENV1: Missing value
@@ -343,9 +343,9 @@ test(`Doesn't show input value when it's missing for invalid env`, t => {
343343

344344
test("Applies preprocessor logic for union schemas separately", t => {
345345
let schema = S.union([
346-
S.bool->S.variant(bool => #Bool(bool)),
347-
S.string->S.variant(string => #String(string)),
348-
S.union([S.int->S.variant(int => #Int(int)), S.string->S.variant(string => #String(string))]),
346+
S.bool->S.to(bool => #Bool(bool)),
347+
S.string->S.to(string => #String(string)),
348+
S.union([S.int->S.to(int => #Int(int)), S.string->S.to(string => #String(string))]),
349349
])
350350

351351
let envSafe = EnvSafe.make(~env=Obj.magic(Js.Dict.empty()))

0 commit comments

Comments
 (0)