@achs/env 5.0.0-alpha.0 → 5.0.0-alpha.2

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
@@ -1,818 +1,580 @@
1
- <div id="top" align="center">
2
- <img
3
- alt="logo"
4
- src="https://nodejs.org/static/images/logo.svg"
5
- width="256px"
6
- />
7
-
8
- </br>
9
-
10
- <h1 align="center"><b>env</b></h1>
11
- <h4 align="center">¡Environment variables made easy!</h4>
12
- </div>
13
-
14
- <br />
15
-
16
- <p align="center">
17
- <img
18
- src="https://img.shields.io/badge/version-1.0.0-blue?style=flat-square"
19
- alt="version"
20
- />
21
- &nbsp;
22
- <img
23
- src="https://img.shields.io/badge/TypeScript-007ACC?style=flat-square&logo=typescript&logoColor=white"
24
- alt="typescript"
25
- />
26
- &nbsp;
27
- <img
28
- src="https://img.shields.io/badge/nodejs->=20.0.0-darkgreen?style=flat-square"
29
- alt="nodejs engine"
30
- />
31
- &nbsp;
32
- <img
33
- src="https://img.shields.io/badge/npm->=7.5.6-darkgreen?style=flat-square"
34
- alt="npm engine"
35
- />
36
- </p>
37
-
38
- <br />
39
-
40
- <!-- ABOUT THE PROJECT -->
41
-
42
- ## 📖 **About**
43
-
44
- Eases NodeJS <b>environment variable handling</b>, like [env-cmd](https://www.npmjs.com/package/env-cmd) or [dotenv](https://www.npmjs.com/package/dotenv), but with <b>powerfull features and extensibility</b> for adding custom providers (as plugins) for <u>load</u>, <u>pull</u> and <u>push</u> the variables from different stores.
45
-
46
- <p align="right">(<a href="#top">back to top</a>)</p>
47
-
48
- <!-- REQUIREMENTS -->
49
-
50
- ## 📌 **Requirements**
51
-
52
- First, [download](https://nodejs.org/) and install **NodeJS**. Version `20` or higher is required.
53
-
54
- Validate installed versions of node and npm with:
55
-
56
- ```bash
57
- > node -v
58
- v20.0.0
59
-
60
- > npm -v
61
- 10.0.0
62
- ```
63
-
64
- You can initialize a new npm project using:
65
-
66
- ```bash
67
- > npm init
68
- ```
69
-
70
- <p align="right">(<a href="#top">back to top</a>)</p>
71
-
72
- <!-- ESM NOTE -->
73
-
74
- ## 📦 **ESM only**
75
-
76
- Starting from v5, this package is **ESM-only** (`"type": "module"`). Consumers must use ESM `import` syntax. Custom providers must also be ESM modules that export their provider object as the `default` export.
77
-
78
- ```javascript
79
- // ✅ ESM import
80
- import { MyProvider } from '@achs/env';
81
-
82
- // CommonJS require is not supported
83
- // const { MyProvider } = require('@achs/env');
84
- ```
85
-
86
- <p align="right">(<a href="#top">back to top</a>)</p>
87
-
88
- <!-- QUICK START -->
89
-
90
- ## ⚡️ **Quick start**
91
-
92
- > 🔔 Make sure that you have [NodeJS 20+](https://nodejs.org/) installed on your computer.
93
-
94
- - Installs the package:
95
-
96
- ```bash
97
- > npm install @achs/env
98
-
99
- added 1 packages, and audited 1 packages in 1s
100
-
101
- found 0 vulnerabilities
102
- > _
103
- ```
104
-
105
- - Executes binary directly:
106
-
107
- ```bash
108
- > node_modules/.bin/env --help
109
-
110
- Usage: env [command] [options..] [: subcmd [:]] [options..]
111
-
112
- Commands:
113
- env [options..] [: <subcmd> :]
114
- env pull [options..]
115
- env push [options..]
116
- env schema [options..]
117
- > _
118
- ```
119
-
120
- ```bash
121
- > npx env --help
122
-
123
- Usage: env [command] [options..] [: subcmd [:]] [options..]
124
-
125
- Commands:
126
- env [options..] [: <subcmd> :]
127
- env pull [options..]
128
- env push [options..]
129
- env schema [options..]
130
- > _
131
- ```
132
-
133
- - Or add desired commands in your **npm script** in `package.json`:
134
-
135
- ```javascript
136
- {
137
- ...,
138
- "scripts": {
139
- // starts project injecting "dev" environment variables and debug log level
140
- "start:dev": "env -e dev -m debug : node dist/main.js : --log debug",
141
- // starts project injecting "prod" environment variables
142
- "start:prod": "env -e prod -m debug : node dist/main.js",
143
- ...,
144
- // builds project injecting "prod" environment variables
145
- "build:prod": "env -e prod -m build : tsc",
146
- ...,
147
- "env:schema": "env schema -e dev --ci",
148
- // uploads environment "dev" variables
149
- "env:push:dev": "env push -e dev",
150
- // downloads environment "dev" variables
151
- "env:pull:dev": "env pull -e dev"
152
- },
153
- ...
154
- }
155
- ```
156
-
157
- - Execs your command:
158
-
159
- **file**: _dist/main.js_
160
-
161
- ```javascript
162
- console.log(`My environment loaded is: ${process.env.ENV}`);
163
- ```
164
-
165
- ```bash
166
- > npm run start:dev
167
-
168
- 13:31:59.865 INFO loading dev environment in debug mode
169
- 13:31:59.911 DEBUG using package-json provider
170
- 13:31:59.912 DEBUG using app-settings provider
171
- 13:31:59.914 DEBUG using secrets provider
172
- 13:32:00.109 DEBUG environment loaded:
173
- {
174
- NODE_ENV: 'development',
175
- ENV: 'dev',
176
- VERSION: '1.0.0',
177
- NAME: '@my-app',
178
- VAR1: true,
179
- VAR2: true,
180
- GROUP1__VAR1: 'G1V2',
181
- ARR1: '1,val,true',
182
- SECRET: '***'
183
- }
184
- 13:32:00.116 INFO executing command > node dist/main.js
185
- My environment loaded is: dev
186
- 13:32:00.232 INFO process finished successfully
187
- > _
188
- ```
189
-
190
- <p align="right">(<a href="#top">back to top</a>)</p>
191
-
192
- ## **Structure**
193
-
194
- ```bash
195
- ├── src/
196
- │   ├── commands/ # lib commands handlers
197
- │   │   ├── env.command.ts
198
- │   │   ├── export.command.ts
199
- │   │   ├── pull.command.ts
200
- │   │   ├── push.command.ts
201
- │   │   └── schema.command.ts
202
- │   ├── interfaces/ # provider interfaces
203
- │   ├── providers/ # integrated providers
204
- │   │   ├── package-json.provider.ts
205
- │   │   ├── app-settings.provider.ts
206
- │   │   ├── local.provider.ts
207
- │   │   └── azure-key-vault.provider.ts
208
- │   ├── utils/
209
- │   │   ├── command.util.ts
210
- │   │   ├── interpolate.util.ts
211
- │   │   ├── json.util.ts
212
- │   │   ├── normalize.util.ts
213
- │   │   ├── schema.util.ts
214
- │   │   └── logger.ts
215
- │   ├── arguments.ts # global arguments
216
- │   ├── exec.ts # initialization logic (load config, commands, etc.)
217
- │   └── main.ts
218
- ├── tests/ # integration tests
219
- ├── eslint.config.js
220
- ├── vite.config.ts # build (lib) + vitest (unit & integration)
221
- └── tsconfig.json
222
- ```
223
-
224
- <p align="right">(<a href="#top">back to top</a>)</p>
225
-
226
- <!-- COMMANDS AND OPTIONS -->
227
-
228
- ## ⚙️ **Commands & Options**
229
-
230
- Options handling has the ability of **replace arguments itself**, using `[[` and `]]` as delimiters.
231
- So, in example for define your config file path, you must use your _root_ argument,
232
- supposing root has the value of "config", this definition _`[[root]]/any-config-file.json`_ will be
233
- _`config/any-config-file.json`_, or if your _env_ argument is "dev", this definition
234
- _`[[root]]/config-file.[[env]].json`_ will be _`config/config-file.dev.json`_.
235
-
236
- <div align="center">
237
- <span style="font-size:20px;font-weight:bold" align="center">Options</span>
238
- </div>
239
-
240
- ### Global options
241
-
242
- | Option | Description | Type | Default | Required? |
243
- | ---------------------------------- | ----------------------------------------------- | ---------- | ------- | --------- |
244
- | `--help` | Shows help | `boolean` | | No |
245
- | `--e, --env` | Environment for load | `string` | | Yes |
246
- | `-m, --modes` | Execution modes | `string[]` | `[]` | No |
247
- | `--nd, --nestingDelimiter` | Nesting level delimiter for flatten | `string` | `__` | No |
248
- | `--arrDesc, --arrayDescomposition` | Whether serialize or break down arrays | `boolean` | `false` | No |
249
- | `-x, --expand` | Interpolates environment variables using itself | `boolean` | `false` | No |
250
- | `-ci` | Continuous Integration mode | `boolean` | `false` | No |
251
-
252
- </br>
253
-
254
- ### Workspace options
255
-
256
- | Option | Description | Type | Default | Required? |
257
- | ------------------ | --------------------------------- | -------- | --------------------------------- | --------- |
258
- | `--root` | Default environment folder path | `string` | `env` | No |
259
- | `-c, --configFile` | Config JSON file path | `string` | `[[root]]/settings/settings.json` | No |
260
- | `-s, --schemaFile` | Environment Schema JSON file path | `string` | `[[root]]/settings/schema.json` | No |
261
-
262
- ### JSON Schema options
263
-
264
- | Option | Description | Type | Default | Required? |
265
- | ---------------------- | ---------------------------------------------------------- | ----------------- | ------- | --------- |
266
- | `-r, --resolve` | Whether merges new schema or override | `merge, override` | `merge` | No |
267
- | `--null, --nullable` | Whether variables are nullable by default | `boolean` | `true` | No |
268
- | `--df, --detectFormat` | Whether format of strings variables are included in schema | `boolean` | `false` | No |
269
-
270
- ### Logger options
271
-
272
- | Option | Description | Type | Default | Required? |
273
- | ------------------- | ----------- | ---------------------------------------- | ------- | --------- |
274
- | `--log, --logLevel` | Log level | `silly, trace, debug, info, warn, error` | `info` | No |
275
-
276
- <div align="center">
277
- <span style="font-size:20px;font-weight:bold" align="center">Commands</span>
278
- </div>
279
-
280
- - ## **`env`**
281
-
282
- Inject your environment variables into `process.env` and executes a command.
283
-
284
- ```bash
285
- env -e [env] [options..] [: subcmd [:]] [options..]
286
- ```
287
-
288
- Examples:
289
-
290
- ```bash
291
- > env -e dev -m test unit : npm test
292
- ```
293
-
294
- ```bash
295
- > env -e dev -m debug : npm start : -c [[root]]/[[env]].env.json
296
- ```
297
-
298
- ```bash
299
- > env -e prod -m build optimize : npm build
300
- ```
301
-
302
- - ## **`pull`**
303
-
304
- Pulls environment variables from providers stores.
305
-
306
- ```bash
307
- env pull -e [env] [options..]
308
- ```
309
-
310
- | Option | Description | Type | Default | Required? |
311
- | ----------------- | ------------------------- | --------- | ------- | --------- |
312
- | `-o, --overwrite` | Overwrite local variables | `boolean` | `false` | No |
313
-
314
- Examples:
315
-
316
- ```bash
317
- > env pull -e dev
318
- ```
319
-
320
- - ## **`push`**
321
-
322
- Pushes environment variables to providers stores.
323
-
324
- ```bash
325
- env push -e [env] [options..]
326
- ```
327
-
328
- | Option | Description | Type | Default | Required? |
329
- | ------------- | ------------------------------------ | --------- | ------- | --------- |
330
- | `-f, --force` | Force push for secrets (replace all) | `boolean` | `false` | No |
331
-
332
- Examples:
333
-
334
- ```bash
335
- > env push -e dev
336
- ```
337
-
338
- - ## **`schema`**
339
-
340
- Generates validation schema from providers output variables.
341
-
342
- ```bash
343
- env schema -e [env] -m [modes] [options..]
344
- ```
345
-
346
- Examples:
347
-
348
- ```bash
349
- > env schema -e dev -m build
350
- ```
351
-
352
- - ## **`export`**
353
-
354
- Export unified environment variables to a file from providers.
355
-
356
- ```bash
357
- env export -e [env] -m [modes] [options..]
358
- ```
359
-
360
- | Option | Description | Type | Default | Required? |
361
- | --------------- | ---------------------------------- | -------- | -------- | --------- |
362
- | `-u, -p, --uri` | Uri for export file with variables | `string` | `.env` | No |
363
- | `-f, --format` | Format for export variables | `string` | `dotenv` | No |
364
-
365
- Examples:
366
-
367
- ```bash
368
- > env export -e dev -m build -f json --uri [[env]].env.json
369
- ```
370
-
371
- <p align="right">(<a href="#top">back to top</a>)</p>
372
-
373
- <!-- PROVIDERS -->
374
-
375
- ## 📡 **Providers**
376
-
377
- Main feature of this library is using providers for get and set environment variables.
378
- So, you can define your own provider, but lib came with 3 integrated providers:
379
-
380
- - ## **`package-json`**
381
-
382
- Load some info from your project `package.json`.
383
-
384
- Info read is:
385
-
386
- ```json
387
- {
388
- "version": "1.0.0",
389
- "project": "project-name",
390
- "name": "@package-name",
391
- "title": "app-name",
392
- "description": "any description"
393
- }
394
- ```
395
-
396
- | Option | Description | Type | Default | Required? |
397
- | ------------------- | --------------------------- | -------- | ------- | --------- |
398
- | `--vp, --varPrefix` | Prefix for loaded variables | `string` | `""` | No |
399
-
400
- Examples:
401
-
402
- ```bash
403
- > env -e dev -m build : react-script build : --vp REACT_APP_
404
- ```
405
-
406
- </br>
407
-
408
- - ## **`app-settings`**
409
-
410
- Non secrets loader for `appsettings.json`.
411
-
412
- `appsettings.json` file has the format below:
413
-
414
- ```json
415
- {
416
- "|DEFAULT|": {},
417
- "|MODE|": {},
418
- "|ENV|": {}
419
- }
420
- ```
421
-
422
- In example:
423
-
424
- ```json
425
- {
426
- "|DEFAULT|": {
427
- "VAR1": "v1_default"
428
- },
429
- "|MODE|": {
430
- "build": {
431
- "NODE_ENV": "production"
432
- },
433
- "debug": {
434
- "NODE_ENV": "development"
435
- },
436
- "test": {
437
- "NODE_ENV": "test"
438
- }
439
- },
440
- "|ENV|": {
441
- "dev": {
442
- "C1": "V1",
443
- "C2": "V2",
444
- "C3": 3,
445
- "GROUP1": {
446
- "VAR1": null,
447
- "VAR2": "G1V2",
448
- "VAR3": true,
449
- "GROUP2": {
450
- "VAR1": "G1G2V1"
451
- }
452
- },
453
- "C4": "23"
454
- }
455
- }
456
- }
457
- ```
458
-
459
- | Option | Description | Type | Default | Required? |
460
- | ----------------- | ------------------------------- | -------- | --------------------------- | --------- |
461
- | `--ef, --envFile` | Environment variables file path | `string` | `[[root]]/appsettings.json` | No |
462
-
463
- - ## **`azure-key-vault`**
464
-
465
- Azure Key Vault provider, allows to load secrets from vault store to `env/secrets/[[env]].env.json` per environment.
466
- Also, handles `env/secrets/[[env]].local.env.json` for load local variables with precedence over base.
467
-
468
- | Option | Description | Type | Default | Required? |
469
- | --------------------------------------- | ---------------------------------------------- | ---------- | ----------------------------------------- | --------- |
470
- | `--secretFolder` | Secret variables folder path | `string` | `[[root]]/secrets` | No |
471
- | `--secretFile` | Secret variables file path | `string` | `[[secretFolder]]/[[env]].env.json` | No |
472
- | `--localSecretFile` | Local secret variables file path | `string` | `[[secretFolder]]/[[env]].local.env.json` | No |
473
- | `-k, --keys, --keysFile` | Azure Key Vault SPN credentials files paths | `string[]` | `['../keys.json', '[[root]]/keys.json']` | No |
474
- | `--url, --vaultUrl` | Azure Key Vault server URL | `string` | | Yes |
475
- | `--spn, --clientId, --id` | SPN Client ID | `string` | | Yes |
476
- | `-p --password, --pass, --clientSecret` | SPN Client Secret Password | `string` | | Yes |
477
- | `-t, --tenant` | Azure Tenant ID | `string` | | Yes |
478
- | `--mock` | Mocks Azure Key Vault client (testing purpose) | `string` | `false` | No |
479
-
480
- <p align="right">(<a href="#top">back to top</a>)</p>
481
-
482
- <!-- PROVIDERS -->
483
-
484
- ## **Creating Custom Providers**
485
-
486
- You can create your custom providers, in two ways:
487
-
488
- - **Local Script**: you must create a JavaScript file (.js), exporting by default your "provider" following standard interface exported by this lib.
489
- - **NPM Package**: you must create your custom NPM library and export by default your "provider" using standard interface exported by this lib.
490
-
491
- How to load your provider is shown in Config Section.
492
-
493
- In example, a provider exported by your NPM package written in TypeScript should be like:
494
-
495
- ```typescript
496
- import { CommandArguments, EnvProvider } from '@achs/env';
497
- import { logger, readJson, writeJson } from '@achs/env/utils';
498
-
499
- const KEY = 'my-unique-provider-key';
500
-
501
- interface MyProviderCommandArguments extends CommandArguments {
502
- anyExtraOption: boolean;
503
- }
504
-
505
- export const MyProvider: EnvProvider<MyProviderCommandArguments> = {
506
- // unique identifier for provider
507
- key: KEY,
508
-
509
- // (optional) allows to provider adds new arguments/options
510
- // to commands using yargs for builder
511
- builder: (builder) => {
512
- builder.options({
513
- anyExtraOption: {
514
- group: KEY,
515
- alias: ['a', 'aeo'],
516
- type: 'boolean',
517
- default: false,
518
- describe: 'Any option description',
519
- },
520
- });
521
- },
522
-
523
- // call on environment variables loading,
524
- // may be a Promise
525
- load: ({ env, modes, ...options }) => {
526
- if (env === 'dev')
527
- return {
528
- NODE_ENV: 'development',
529
- };
530
-
531
- // you can return a list of JSON environment variables for merge
532
- return [
533
- {
534
- NODE_ENV: 'production',
535
- },
536
- {
537
- ANY_VAR: 'ANY_VALUE',
538
- ANY_GROUP: {
539
- INNER_VAR: 12,
540
- },
541
- },
542
- ];
543
- },
544
-
545
- // (optional) call on pulling variables from provider store,
546
- // config may pass in your config file
547
- pull: ({ env, modes, ...options }, config) => {
548
- // anyway you want for pulling variables to cache
549
- },
550
-
551
- // (optional) call on pushing/updating variables to provider store,
552
- // config may pass in your config file
553
- push: ({ env, modes, ...options }, config) => {
554
- // anyway you should do for pushing or updating your variables
555
- },
556
- };
557
- ```
558
-
559
- <p align="right">(<a href="#top">back to top</a>)</p>
560
-
561
- <!-- CONFIG -->
562
-
563
- ## 📥 **Config**
564
-
565
- You can configure any config argument inside you config file, but commonly providers are designed for this purpose.
566
-
567
- ```javascript
568
- {
569
- "log": "silly",
570
- // will hide values of keys SECRET and MY_API_KEY in logging
571
- "logMaskValuesOfKeys": ["SECRET", "MY_API_KEY"],
572
- // integrated providers and custom providers together
573
- "providers": [
574
- {
575
- "path": "package-json"
576
- },
577
- {
578
- "path": "app-settings"
579
- },
580
- {
581
- "path": "azure-key-vault",
582
- "config": {
583
- "dev": {
584
- "vaultUrl": "https://kv-desa-ittec-sti.vault.azure.net"
585
- },
586
- "qa": {
587
- "vaultUrl": "https://kv-qa-ittec-sti.vault.azure.net"
588
- }
589
- }
590
- },
591
- {
592
- "path": "local"
593
- },
594
- {
595
- // custom NPM package
596
- "path": "@npm-package",
597
- "type": "module",
598
- "config": {
599
- "any-config": "any value"
600
- }
601
- },
602
- {
603
- // custom script inside project
604
- "path": "scripts/custom-loader.js",
605
- "type": "script"
606
- }
607
- ]
608
- }
609
- ```
610
-
611
- <!-- AZURE KEY VAULT -->
612
-
613
- ## 💽 **Azure Key Vault**
614
-
615
- Allows you to store your secrets in Azure Key Vault.
616
-
617
- #### 1.2. NPM Scripts
618
-
619
- For load desired environment, add you npm script like **`env -e <env> -m <mode1[ mode2]> : <your-command>`**.
620
-
621
- - **mode**: (build|debug|test) execution mode base variables.
622
- - **env**: (dev|qa|stg|prod) environment variables.
623
-
624
- _In example: `env -e dev -m debug : npm start`_
625
-
626
- ## 2. Structure
627
-
628
- #### 2.1. Environments
629
-
630
- Your `env/secrets` folder will contain files below:
631
-
632
- - **dev.env.json**: development environment.
633
- - **dev.local.env.json**: local development environment (takes precedence).
634
- - **qa.env.json**: quality assurance environment.
635
- - **qa.local.env.json**: local qa environment (takes precedence).
636
- - **prod.env.json**: production environment.
637
- - **prod.local.env.json**: local production environment (takes precedence).
638
-
639
- _This folder should contains environment variables files for system environments._
640
-
641
- #### 2.2. Keys (env/keys.json)
642
-
643
- Your `keys.json` file should contains you Azure Key Vault SPN credentials per environment:
644
-
645
- ```json
646
- {
647
- "<env-name>": {
648
- "vaultUrl": "<azure-key-vault-url>", // you can skip this var if present in config
649
- "clientId": "<spn-client-id>",
650
- "clientSecret": "<spn-secret-password>",
651
- "tenantId": "<tenant-id>"
652
- },
653
- ...
654
- }
655
- ```
656
-
657
- In example:
658
-
659
- ```json
660
- {
661
- "dev": {
662
- "clientId": "f176a774-239e-4cd3-8551-88fd9fb9b441",
663
- "clientSecret": "WyBwkmcL8rGQe9B2fvRLDrqDuannE4Ku",
664
- "tenantId": "6d4bbe0a-5654-4c69-a682-bf7dcdaed8e7"
665
- },
666
- "qa": {
667
- "clientId": "5dcd9f45-7067-4387-94d8-e5e7066ba630",
668
- "clientSecret": "60ec5e16430a46eba70dfea80d721b66",
669
- "tenantId": "6d4bbe0a-5654-4c69-a682-bf7dcdaed8e7"
670
- }
671
- }
672
- ```
673
-
674
- Your secrets will be grouped, using your "name" and "project" variables from `package.json` file.
675
-
676
- _This file allows to load environment files locally first run time._
677
-
678
- ## 3. Commands
679
-
680
- You can use two command scripts for refresh your local env files
681
- or publish/updates env files in the azure key vault from your local files.
682
-
683
- - **Pulls Secrets File**: `env pull -e <env> [-o]`. (-o forces to replace your local file).
684
- - **Pushes/Publishes Secrets File**: `env push -e <env>`.
685
-
686
- #### 3.1. Environment Variables for Credentials
687
-
688
- You can set your credentials variables from node environment variables.
689
-
690
- In example:
691
-
692
- ```bash
693
- user@machine:/mnt/c/Users/user$ AZURE_VAULT_URL=https://kv-desa-ittec-sti.vault.azure.net \
694
- AZURE_CLIENT_ID=f176a774-239e-4cd3-8551-88fd9fb9b441 \
695
- AZURE_CLIENT_SECRET=WyBwkmcL8rGQe9B2fvRLDrqDuannE4Ku \
696
- AZURE_TENANT_ID=6d4bbe0a-5654-4c69-a682-bf7dcdaed8e7 \
697
- node_modules/.bin/env pull -e dev -o
698
- ```
699
-
700
- or
701
-
702
- ```bash
703
- user@machine:/mnt/c/Users/user$ npx pull -e dev -o \
704
- --vaultUrl https://kv-desa-ittec-sti.vault.azure.net \
705
- --spn f176a774-239e-4cd3-8551-88fd9fb9b441 \
706
- --password WyBwkmcL8rGQe9B2fvRLDrqDuannE4Ku \
707
- --tenant 6d4bbe0a-5654-4c69-a682-bf7dcdaed8e7
708
- ```
709
-
710
- ## 4. Schema
711
-
712
- This lib uses JSON schema for validate and retrieve secrets from store.
713
-
714
- For each property in the file, loader will retrieve the value from Azure Key
715
- Vault.
716
-
717
- When you push a new variable from any of your environment secrets
718
- file, `env.schema.json` will be updated automatically.
719
-
720
- If you want to ignore to load some variable without delete it, you can remove
721
- the variable from `env.schema.json`.
722
-
723
- ## 5. Shared and Nested Keys
724
-
725
- You can organize your keys in nested objects,
726
- and declare project shared secrets (skips group
727
- separation) prefixing with '\$' like:
728
-
729
- ```json
730
- {
731
- // .dev.env.json
732
- "$SHARED": "sharedValue",
733
- "GROUP1": {
734
- "$SHARED": "sharedValue2",
735
- "VAR": "anyValue1",
736
- ...
737
- },
738
- "GROUP2": {
739
- "VAR": "anyValue2",
740
- "SUBGROUP1": {
741
- "VAR": "anyValue1",
742
- ...
743
- },
744
- ...
745
- },
746
- "VAR3": "anyValue3",
747
- ...
748
- }
749
- ```
750
-
751
- So, in your application you can use the variables as shown below:
752
-
753
- ```javascript
754
- const myVar1 = process.env.GROUP1__VAR;
755
- const myVar2 = process.env.GROUP2__VAR;
756
- const myVar2 = process.env.GROUP2__SUBGROUP1_VAR;
757
- const myVar3 = process.env.VAR3;
758
- // shared vars will load in every project
759
- const mySharedVar1 = process.env.SHARED;
760
- const mySharedNestedVar2 = process.env.GROUP1__SHARED;
761
- ```
762
-
763
- ## 6. Priority
764
-
765
- ### From lowest to highest.
766
-
767
- - `(dev|qa|prod).env.json`
768
- - `(dev|qa|prod).local.env.json` (takes precedence over previous)
769
-
770
- <p align="right">(<a href="#top">back to top</a>)</p>
771
-
772
- <!-- LINTING -->
773
-
774
- ## 🧿 **Linting**
775
-
776
- Project uses ESLint, for code formatting and code styling normalizing.
777
-
778
- - **eslint**: linter integrated with TypeScript.
779
-
780
- For correct interpretation of linters, is recommended to use [Visual Studio Code](https://code.visualstudio.com/) as IDE and install the plugins in .vscode folder at 'extensions.json', as well as use the config provided in 'settings.json'
781
-
782
- <p align="right">(<a href="#top">back to top</a>)</p>
783
-
784
- <!-- CHANGELOG -->
785
-
786
- ## 📋 Changelog
787
-
788
- For last changes see [CHANGELOG.md](CHANGELOG.md) file for details.
789
-
790
- <p align="right">(<a href="#top">back to top</a>)</p>
791
-
792
- <!-- BUILT WITH -->
793
-
794
- ## 🛠️ Built with
795
-
796
- - [yargs](http://yargs.js.org/)
797
- - [tslog v4](https://tslog.js.org/#/)
798
- - [subslate](https://github.com/josh-hemphill/subslate)
799
- - [merge-deep](https://github.com/jonschlinkert/merge-deep)
800
- - [ajv](https://ajv.js.org/)
801
- - [to-json-schema](https://www.npmjs.com/package/to-json-schema)
802
- - [Vite](https://vitejs.dev/) (build)
803
- - [Vitest](https://vitest.dev/) (testing)
804
-
805
- <p align="right">(<a href="#top">back to top</a>)</p>
806
-
807
- ---
808
-
809
- <br />
810
-
811
- <p align="center">
812
- <img
813
- width="15%"
814
- src="https://upload.wikimedia.org/wikipedia/commons/0/09/Logo_ACHS.svg"
815
- />
816
- <h2 align="center">ASOCIACIÓN CHILENA DE SEGURIDAD</h2>
817
- <h3 align="center">Tranformación Digital ▪ Equipo de Desarrollo</h3>
818
- </p>
1
+ <div id="top" align="center">
2
+ <img alt="@achs/env logo" src="assets/logo.png" width="150" />
3
+
4
+ <h1 align="center"><b>env</b></h1>
5
+ <h4 align="center">Environment variables made easy — load, validate, inject.</h4>
6
+
7
+ <p align="center">
8
+ <img src="https://img.shields.io/npm/v/@achs/env?style=flat-square&color=2563eb&label=version" alt="version" />
9
+ &nbsp;
10
+ <img src="https://img.shields.io/badge/module-ESM-f59e0b?style=flat-square" alt="esm" />
11
+ &nbsp;
12
+ <img src="https://img.shields.io/badge/TypeScript-007ACC?style=flat-square&logo=typescript&logoColor=white" alt="typescript" />
13
+ &nbsp;
14
+ <img src="https://img.shields.io/badge/node->=20-339933?style=flat-square&logo=node.js&logoColor=white" alt="node engine" />
15
+ &nbsp;
16
+ <img src="https://img.shields.io/badge/coverage-100%25-22c55e?style=flat-square" alt="coverage" />
17
+ </p>
18
+ </div>
19
+
20
+ <br />
21
+
22
+ <!-- ABOUT -->
23
+
24
+ ## 📖 About
25
+
26
+ `@achs/env` eases **environment variable handling** for NodeJS apps — like
27
+ [env-cmd](https://www.npmjs.com/package/env-cmd) or
28
+ [dotenv](https://www.npmjs.com/package/dotenv), but with **powerful, extensible
29
+ features**: pluggable **providers** to `load`, `pull` and `push` variables from
30
+ different stores, **JSON Schema validation**, value **interpolation**, secret
31
+ **masking** and nested/shared keys.
32
+
33
+ ### ✨ Features
34
+
35
+ - 🧩 **Provider plugins** — bundled `package-json`, `app-settings`,
36
+ `azure-key-vault` and `local` providers, plus your own (NPM package or local
37
+ script).
38
+ - 💉 **Injection** — load variables into `process.env` and run any command.
39
+ - 🔐 **Azure Key Vault** — pull/push secrets per environment.
40
+ - **JSON Schema** — auto-generate and validate variables before injecting.
41
+ - 🪆 **Nested & shared keys** — `GROUP__VAR` flattening and `$`-prefixed shared
42
+ keys.
43
+ - 🧵 **Interpolation** — reference other args/vars with `[[ ]]` delimiters.
44
+ - 🙈 **Masking** hide secret values in logs.
45
+ - 📤 **Export** — write the unified environment to a `.env` or JSON file.
46
+
47
+ <p align="right">(<a href="#top">back to top</a>)</p>
48
+
49
+ <!-- REQUIREMENTS -->
50
+
51
+ ## 📌 Requirements
52
+
53
+ [NodeJS](https://nodejs.org/) **20 or higher**.
54
+
55
+ ```bash
56
+ > node -v
57
+ v20.0.0
58
+ ```
59
+
60
+ <p align="right">(<a href="#top">back to top</a>)</p>
61
+
62
+ <!-- ESM -->
63
+
64
+ ## 📦 ESM only
65
+
66
+ Since **v5** this package is **ESM-only** (`"type": "module"`). Consumers must
67
+ use ESM `import` syntax, and custom providers must be ESM modules that `export`
68
+ their provider object.
69
+
70
+ ```javascript
71
+ // ✅ ESM
72
+ import { EnvProvider } from '@achs/env';
73
+
74
+ // CommonJS require is not supported
75
+ // const { EnvProvider } = require('@achs/env');
76
+ ```
77
+
78
+ <p align="right">(<a href="#top">back to top</a>)</p>
79
+
80
+ <!-- QUICK START -->
81
+
82
+ ## ⚡️ Quick start
83
+
84
+ Install the package:
85
+
86
+ ```bash
87
+ > npm install @achs/env
88
+ ```
89
+
90
+ Run the binary directly:
91
+
92
+ ```bash
93
+ > npx env --help
94
+
95
+ Usage: env [command] [options..] [: subcmd [:]] [options..]
96
+
97
+ Commands:
98
+ env [options..] [: <subcmd> :]
99
+ env pull [options..]
100
+ env push [options..]
101
+ env schema [options..]
102
+ env export [options..]
103
+ ```
104
+
105
+ Or wire it into your **npm scripts**:
106
+
107
+ ```jsonc
108
+ {
109
+ "scripts": {
110
+ // inject "dev" variables (debug mode) and start the app
111
+ "start:dev": "env -e dev -m debug : node dist/main.js",
112
+ // inject "prod" variables for the build
113
+ "build:prod": "env -e prod -m build : tsc",
114
+ // regenerate the validation schema
115
+ "env:schema": "env schema -e dev",
116
+ // pull / push secrets for the "dev" environment
117
+ "env:pull:dev": "env pull -e dev",
118
+ "env:push:dev": "env push -e dev",
119
+ },
120
+ }
121
+ ```
122
+
123
+ Run it:
124
+
125
+ ```bash
126
+ > npm run start:dev
127
+
128
+ 13:31:59.865 INFO loading dev environment in debug mode
129
+ 13:31:59.911 DEBUG using package-json provider
130
+ 13:31:59.912 DEBUG using app-settings provider
131
+ 13:31:59.914 DEBUG using azure-key-vault provider
132
+ 13:32:00.109 DEBUG environment loaded:
133
+ {
134
+ NODE_ENV: 'development',
135
+ ENV: 'dev',
136
+ VERSION: '5.0.0',
137
+ NAME: '@my-app',
138
+ VAR1: true,
139
+ GROUP1__VAR1: 'G1V2',
140
+ ARR1: '1,val,true',
141
+ SECRET: '***'
142
+ }
143
+ 13:32:00.116 INFO executing command > node dist/main.js
144
+ My environment loaded is: dev
145
+ 13:32:00.232 INFO process finished successfully
146
+ ```
147
+
148
+ <p align="right">(<a href="#top">back to top</a>)</p>
149
+
150
+ <!-- COMMANDS AND OPTIONS -->
151
+
152
+ ## ⚙️ Commands & Options
153
+
154
+ > **Interpolation** — any option value can reference other arguments using `[[`
155
+ > and `]]` delimiters. With `root: "config"`, the value `[[root]]/file.json`
156
+ > resolves to `config/file.json`; with `env: "dev"`,
157
+ > `[[root]]/config.[[env]].json` resolves to `config/config.dev.json`.
158
+
159
+ ### Global options
160
+
161
+ | Option | Description | Type | Default |
162
+ | ---------------------------------- | ------------------------------------------------- | ---------- | ------- |
163
+ | `--help` | Show help | `boolean` | |
164
+ | `-e, --env` | Environment to load (i.e. `dev`, `prod`) | `string` | |
165
+ | `-m, --modes` | Execution modes (i.e. `debug`, `test`) | `string[]` | `[]` |
166
+ | `--nd, --nestingDelimiter` | Nesting-level delimiter for flatten | `string` | `__` |
167
+ | `--arrDesc, --arrayDescomposition` | Serialize (`false`) or break down (`true`) arrays | `boolean` | `false` |
168
+ | `-x, --expand` | Interpolate environment variables using itself | `boolean` | `false` |
169
+ | `--ci` | Continuous Integration mode (skips local files) | `boolean` | auto |
170
+
171
+ ### Workspace options
172
+
173
+ | Option | Description | Type | Default |
174
+ | ---------------------- | --------------------------------- | -------- | --------------------------------- |
175
+ | `--root` | Base environment folder path | `string` | `env` |
176
+ | `-c, --configFile` | Config JSON file path | `string` | `[[root]]/settings/settings.json` |
177
+ | `-s, --schemaFile` | Environment schema JSON file path | `string` | `[[root]]/settings/schema.json` |
178
+ | `--pkg, --packageJson` | `package.json` path | `string` | _cwd_ |
179
+
180
+ ### JSON Schema options
181
+
182
+ | Option | Description | Type | Default |
183
+ | ---------------------- | ---------------------------------------------- | ------------------ | ------- |
184
+ | `-r, --resolve` | Merge new schema or override it | `merge`/`override` | `merge` |
185
+ | `--null, --nullable` | Whether variables are nullable by default | `boolean` | `true` |
186
+ | `--df, --detectFormat` | Include string formats in the generated schema | `boolean` | `false` |
187
+
188
+ ### Logger options
189
+
190
+ | Option | Description | Type | Default |
191
+ | ------------------------------ | -------------------------------------- | --------------------------------------------- | ------- |
192
+ | `--log, --logLevel` | Log level | `silly`/`trace`/`debug`/`info`/`warn`/`error` | `info` |
193
+ | `--mvk, --logMaskValuesOfKeys` | Mask values of the given keys in logs | `string[]` | `[]` |
194
+ | `--mrx, --logMaskAnyRegEx` | Mask values matching the given regexes | `string[]` | `[]` |
195
+
196
+ ---
197
+
198
+ ### `env`
199
+
200
+ Inject environment variables into `process.env` and execute a command (the
201
+ command goes after `:`).
202
+
203
+ ```bash
204
+ env -e <env> [options..] [: <subcmd> :] [options..]
205
+ ```
206
+
207
+ ```bash
208
+ > env -e dev -m test unit : npm test
209
+ > env -e dev -m debug : npm start : -c [[root]]/[[env]].env.json
210
+ > env -e prod -m build optimize : npm run build
211
+ ```
212
+
213
+ | Option | Description | Type | Default |
214
+ | ------------------------------ | ------------------------------------------ | --------- | ------- |
215
+ | `--validate, --schemaValidate` | Validate variables against the JSON schema | `boolean` | `true` |
216
+
217
+ ### `pull`
218
+
219
+ Pull environment variables from the providers' stores.
220
+
221
+ ```bash
222
+ env pull -e <env> [options..]
223
+ ```
224
+
225
+ | Option | Description | Type | Default |
226
+ | ----------------- | ------------------------- | --------- | ------- |
227
+ | `-o, --overwrite` | Overwrite local variables | `boolean` | `false` |
228
+
229
+ ### `push`
230
+
231
+ Push environment variables to the providers' stores.
232
+
233
+ ```bash
234
+ env push -e <env> [options..]
235
+ ```
236
+
237
+ | Option | Description | Type | Default |
238
+ | ------------- | ---------------------- | --------- | ------- |
239
+ | `-f, --force` | Force push for secrets | `boolean` | `false` |
240
+
241
+ ### `schema`
242
+
243
+ Generate (or update) the validation schema from the providers' output.
244
+
245
+ ```bash
246
+ > env schema -e dev -m build
247
+ ```
248
+
249
+ ### `export`
250
+
251
+ Export the unified environment to a file.
252
+
253
+ ```bash
254
+ env export -e <env> -m <modes> [options..]
255
+ ```
256
+
257
+ | Option | Description | Type | Default |
258
+ | --------------- | --------------------- | --------------- | -------- |
259
+ | `-u, -p, --uri` | Output file path | `string` | `.env` |
260
+ | `-f, --format` | Output format | `dotenv`/`json` | `dotenv` |
261
+ | `-q, --quotes` | Wrap values in quotes | `boolean` | `false` |
262
+
263
+ ```bash
264
+ > env export -e dev -m build -f json --uri [[env]].env.json
265
+ ```
266
+
267
+ <p align="right">(<a href="#top">back to top</a>)</p>
268
+
269
+ <!-- PROVIDERS -->
270
+
271
+ ## 📡 Providers
272
+
273
+ Providers are the core of this library. It ships with **four integrated
274
+ providers**, and you can add your own.
275
+
276
+ ### `package-json`
277
+
278
+ Loads project info from your `package.json` (`version`, `project`, `name`,
279
+ `title`, `description`) into `ENV`, `VERSION`, `PROJECT`, `NAME`, `TITLE`,
280
+ `DESCRIPTION`.
281
+
282
+ | Option | Description | Type | Default |
283
+ | --------------------------- | ------------------------------- | -------- | ------- |
284
+ | `--vp, --packageInfoPrefix` | Prefix for the loaded variables | `string` | `""` |
285
+
286
+ ```bash
287
+ # i.e. expose them as REACT_APP_* for CRA
288
+ > env -e dev -m build : react-scripts build : --vp REACT_APP_
289
+ ```
290
+
291
+ ### `app-settings`
292
+
293
+ Non-secret loader for `appsettings.json`, organized by sections:
294
+
295
+ ```jsonc
296
+ {
297
+ "|DEFAULT|": { "VAR1": "v1_default" },
298
+ "|MODE|": {
299
+ "build": { "NODE_ENV": "production" },
300
+ "debug": { "NODE_ENV": "development" },
301
+ },
302
+ "|ENV|": {
303
+ "dev": {
304
+ "C1": "V1",
305
+ "GROUP1": { "VAR2": "G1V2", "GROUP2": { "VAR1": "G1G2V1" } },
306
+ },
307
+ },
308
+ "|LOCAL|": { "dev": { "LOCAL_VAR": "only-local" } },
309
+ }
310
+ ```
311
+
312
+ It also merges `appsettings.<env>.json`, `appsettings.<env>.local.json` (skipped
313
+ in `--ci`) and `appsettings.<mode>.json`.
314
+
315
+ | Option | Description | Type | Default |
316
+ | ----------------- | ----------------------------- | -------- | --------------------------- |
317
+ | `--ef, --envFile` | Non-secret settings file path | `string` | `[[root]]/appsettings.json` |
318
+
319
+ ### `local`
320
+
321
+ Loads local-only variables (never loaded in `--ci`). The file is auto-created
322
+ if missing.
323
+
324
+ | Option | Description | Type | Default |
325
+ | ------------------- | ------------------------- | -------- | --------------------------------- |
326
+ | `--lf, --localFile` | Local variables file path | `string` | `[[root]]/[[env]].local.env.json` |
327
+
328
+ ### `azure-key-vault`
329
+
330
+ Loads secrets from an Azure Key Vault store into `[[root]]/[[env]].env.json` per
331
+ environment (see the [Azure Key Vault](#-azure-key-vault) section below).
332
+
333
+ | Option | Description | Type | Default |
334
+ | ---------------------------------------- | --------------------------------- | ---------- | ---------------------------------------- |
335
+ | `--secretsFile` | Secret variables file path | `string` | `[[root]]/[[env]].env.json` |
336
+ | `-k, --keys, --keysFile` | SPN credentials file paths | `string[]` | `['[[root]]/keys.json', '../keys.json']` |
337
+ | `--url, --vaultUrl` | Azure Key Vault URL | `string` | |
338
+ | `--id, --clientId, --spn` | SPN Client ID | `string` | |
339
+ | `-p, --pass, --clientSecret, --password` | SPN Client Secret | `string` | |
340
+ | `-t, --tenant` | Azure Tenant ID | `string` | |
341
+ | `--mock` | Mock the Key Vault client (tests) | `boolean` | `false` |
342
+ | `--dns, --skipDnsCheck` | Skip the DNS reachability check | `boolean` | `false` |
343
+
344
+ <p align="right">(<a href="#top">back to top</a>)</p>
345
+
346
+ <!-- CUSTOM PROVIDERS -->
347
+
348
+ ## ✒️ Creating custom providers
349
+
350
+ Create a provider in two ways:
351
+
352
+ - **Local script** — a `.js` file that `export default`s your provider.
353
+ - **NPM package** — a published module that `export default`s your provider.
354
+
355
+ Both are wired in the [config file](#-config) via the `providers` list.
356
+
357
+ ```typescript
358
+ import type { CommandArguments, EnvProvider } from '@achs/env';
359
+ import { logger, readJson, writeJson } from '@achs/env/utils';
360
+
361
+ const KEY = 'my-unique-provider-key';
362
+
363
+ interface MyProviderArguments extends CommandArguments {
364
+ anyExtraOption: boolean;
365
+ }
366
+
367
+ const MyProvider: EnvProvider<MyProviderArguments> = {
368
+ // unique identifier
369
+ key: KEY,
370
+
371
+ // (optional) add custom options to the CLI via yargs
372
+ builder: (builder) => {
373
+ builder.options({
374
+ anyExtraOption: {
375
+ group: KEY,
376
+ alias: ['a', 'aeo'],
377
+ type: 'boolean',
378
+ default: false,
379
+ describe: 'Any option description',
380
+ },
381
+ });
382
+ },
383
+
384
+ // called on load — may be sync or async, and may return a list to merge
385
+ load: ({ env }) => {
386
+ if (env === 'dev') return { NODE_ENV: 'development' };
387
+
388
+ return [{ NODE_ENV: 'production' }, { ANY_GROUP: { INNER_VAR: 12 } }];
389
+ },
390
+
391
+ // (optional) called on `env pull`
392
+ pull: (argv, config) => {
393
+ /* fetch variables into your local cache */
394
+ },
395
+
396
+ // (optional) called on `env push`
397
+ push: (argv, config) => {
398
+ /* publish/update your variables */
399
+ },
400
+ };
401
+
402
+ export default MyProvider;
403
+ ```
404
+
405
+ <p align="right">(<a href="#top">back to top</a>)</p>
406
+
407
+ <!-- CONFIG -->
408
+
409
+ ## 📥 Config
410
+
411
+ Any CLI argument can be set in your config file
412
+ (`[[root]]/settings/settings.json` by default), but it is mainly used to declare
413
+ **providers**:
414
+
415
+ ```jsonc
416
+ {
417
+ "log": "silly",
418
+ // hide values of these keys in the logs
419
+ "logMaskValuesOfKeys": ["SECRET", "MY_API_KEY"],
420
+ "providers": [
421
+ { "path": "package-json" },
422
+ { "path": "app-settings" },
423
+ {
424
+ "path": "azure-key-vault",
425
+ "config": {
426
+ "dev": {
427
+ "vaultUrl": "https://kv-dev-myproject.vault.azure.net",
428
+ },
429
+ "qa": { "vaultUrl": "https://kv-qa-myproject.vault.azure.net" },
430
+ },
431
+ },
432
+ { "path": "local" },
433
+ // custom NPM package
434
+ { "path": "@my-scope/my-provider", "type": "module", "config": {} },
435
+ // custom local script
436
+ { "path": "scripts/custom-loader.js", "type": "script" },
437
+ ],
438
+ }
439
+ ```
440
+
441
+ > **Provider order matters** — providers are merged in declaration order, so
442
+ > later providers override earlier ones (`package-json` is the base, `local`
443
+ > wins).
444
+
445
+ <p align="right">(<a href="#top">back to top</a>)</p>
446
+
447
+ <!-- AZURE KEY VAULT -->
448
+
449
+ ## 💽 Azure Key Vault
450
+
451
+ Store and retrieve your secrets from Azure Key Vault, grouped by your `name` and
452
+ `project` from `package.json`.
453
+
454
+ ### Keys file (`env/keys.json`)
455
+
456
+ SPN credentials per environment, used to bootstrap the first pull:
457
+
458
+ ```jsonc
459
+ {
460
+ "dev": {
461
+ "vaultUrl": "<azure-key-vault-url>", // optional if set in settings config
462
+ "clientId": "<spn-client-id>",
463
+ "clientSecret": "<spn-client-secret>",
464
+ "tenantId": "<tenant-id>",
465
+ },
466
+ }
467
+ ```
468
+
469
+ > ⚠️ Keep `keys.json` and `*.env.json` out of version control — they hold
470
+ > secrets.
471
+
472
+ ### Credentials via environment variables
473
+
474
+ Alternatively, provide credentials through env vars (they take precedence):
475
+
476
+ ```bash
477
+ AZURE_VAULT_URL=<azure-key-vault-url> \
478
+ AZURE_CLIENT_ID=<spn-client-id> \
479
+ AZURE_CLIENT_SECRET=<spn-client-secret> \
480
+ AZURE_TENANT_ID=<tenant-id> \
481
+ npx env pull -e dev -o
482
+ ```
483
+
484
+ or as CLI flags:
485
+
486
+ ```bash
487
+ npx env pull -e dev -o \
488
+ --vaultUrl <azure-key-vault-url> \
489
+ --spn <spn-client-id> \
490
+ --password <spn-client-secret> \
491
+ --tenant <tenant-id>
492
+ ```
493
+
494
+ ### Workflow
495
+
496
+ - **Pull** secrets to your local file: `env pull -e <env> [-o]` (`-o` overwrites).
497
+ - **Push** local secrets to the vault: `env push -e <env>`.
498
+
499
+ Pushing a new variable updates the schema automatically. To stop loading a
500
+ variable without deleting it from the vault, remove it from the schema file.
501
+
502
+ <p align="right">(<a href="#top">back to top</a>)</p>
503
+
504
+ <!-- SHARED / NESTED -->
505
+
506
+ ## 🪆 Shared & nested keys
507
+
508
+ Organize keys in nested objects. Declare **shared** keys (skipping group
509
+ separation) by prefixing them with `$`:
510
+
511
+ ```jsonc
512
+ {
513
+ "$SHARED": "sharedValue",
514
+ "GROUP1": {
515
+ "$SHARED": "sharedValue2",
516
+ "VAR": "anyValue1",
517
+ },
518
+ "GROUP2": { "SUBGROUP1": { "VAR": "anyValue1" } },
519
+ "VAR3": "anyValue3",
520
+ }
521
+ ```
522
+
523
+ Consumed in your app as flattened keys (`__` separator by default):
524
+
525
+ ```javascript
526
+ process.env.GROUP1__VAR; // "anyValue1"
527
+ process.env.GROUP2__SUBGROUP1__VAR; // "anyValue1"
528
+ process.env.VAR3; // "anyValue3"
529
+ // shared keys drop the `$` and the group prefix
530
+ process.env.SHARED; // "sharedValue"
531
+ process.env.GROUP1__SHARED; // "sharedValue2"
532
+ ```
533
+
534
+ ### Priority (lowest → highest)
535
+
536
+ 1. `<env>.env.json`
537
+ 2. `<env>.local.env.json` (overrides the above)
538
+
539
+ <p align="right">(<a href="#top">back to top</a>)</p>
540
+
541
+ <!-- SCRIPTS -->
542
+
543
+ ## 🧰 Development scripts
544
+
545
+ | Script | Description |
546
+ | -------------------- | ------------------------------------------------- |
547
+ | `pnpm build` | Build the library (Vite, ESM) into `dist/` |
548
+ | `pnpm test` | Run unit tests (Vitest) |
549
+ | `pnpm test:cov` | Run unit tests with coverage (100% threshold) |
550
+ | `pnpm test:int` | Run integration tests against the built binary |
551
+ | `pnpm typecheck` | Type-check with `tsc --noEmit` |
552
+ | `pnpm lint` | Lint with ESLint (flat config) |
553
+ | `pnpm format` | Format with Prettier |
554
+ | `pnpm run pub` | Gate + build + publish to npm (`latest`) |
555
+ | `pnpm run pub:alpha` | Gate + build + publish a prerelease (`alpha` tag) |
556
+
557
+ <p align="right">(<a href="#top">back to top</a>)</p>
558
+
559
+ <!-- BUILT WITH -->
560
+
561
+ ## 🛠️ Built with
562
+
563
+ - [yargs](http://yargs.js.org/) — CLI argument parsing
564
+ - [ajv](https://ajv.js.org/) + [to-json-schema](https://www.npmjs.com/package/to-json-schema) — JSON Schema
565
+ - [tslog](https://tslog.js.org/#/) logging
566
+ - [subslate](https://github.com/josh-hemphill/subslate) — interpolation
567
+ - [merge-deep](https://github.com/jonschlinkert/merge-deep) — deep merge
568
+ - [Vite](https://vite.dev/) — build · [Vitest](https://vitest.dev/) — testing
569
+
570
+ <p align="right">(<a href="#top">back to top</a>)</p>
571
+
572
+ ---
573
+
574
+ <br />
575
+
576
+ <p align="center">
577
+ <img width="25%" src="./assets/logo-achs.png" alt="ACHS" />
578
+ <h3 align="center">ASOCIACIÓN CHILENA DE SEGURIDAD</h3>
579
+ <h4 align="center">Transformación Digital ▪ Equipo de Desarrollo</h4>
580
+ </p>