@tltdh61/dotenvrtdb 1.260131.11434

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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +526 -0
  3. package/cli.js +736 -0
  4. package/package.json +42 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2025 Jens Claes
2
+
3
+ The MIT License (MIT)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,526 @@
1
+ # dotenvrtdb
2
+
3
+ A simple dotenv CLI for loading environment variables from `.env` files **with remote realtime database support**.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@tolaptrinhdh61-spec/dotenvrtdb.svg)](https://www.npmjs.com/package/@tolaptrinhdh61-spec/dotenvrtdb)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Features
9
+
10
+ ✨ All standard dotenv-cli features
11
+ 🔥 **Pull** environment variables from remote databases (Firebase, custom APIs)
12
+ 🚀 **Push** local .env files to remote databases
13
+ 🔒 Automatic auth token masking in console output
14
+ 📦 Support for multiple file formats and cascading environments
15
+ 🌐 Works with HTTP/HTTPS endpoints
16
+
17
+ ## Installing
18
+
19
+ ### NPM
20
+
21
+ ```bash
22
+ $ npm install -g @tolaptrinhdh61-spec/dotenvrtdb
23
+ ```
24
+
25
+ ### Yarn
26
+
27
+ ```bash
28
+ $ yarn global add @tolaptrinhdh61-spec/dotenvrtdb
29
+ ```
30
+
31
+ ### pnpm
32
+
33
+ ```bash
34
+ $ pnpm add -g @tolaptrinhdh61-spec/dotenvrtdb
35
+ ```
36
+
37
+ ### GitHub Packages
38
+
39
+ To install from GitHub Packages, create a `.npmrc` file:
40
+
41
+ ```
42
+ @tolaptrinhdh61-spec:registry=https://npm.pkg.github.com
43
+ ```
44
+
45
+ Then install:
46
+
47
+ ```bash
48
+ $ npm install -g @tolaptrinhdh61-spec/dotenvrtdb
49
+ ```
50
+
51
+ ## Usage
52
+
53
+ ### Basic Usage
54
+
55
+ ```bash
56
+ $ dotenvrtdb -- <command with arguments>
57
+ ```
58
+
59
+ This will load the variables from the .env file in the current working directory and then run the command (using the new set of environment variables).
60
+
61
+ Alternatively, if you do not need to pass arguments to the command, you can use the shorthand:
62
+
63
+ ```bash
64
+ $ dotenvrtdb <command>
65
+ ```
66
+
67
+ ### 🔥 Remote Database Sync
68
+
69
+ #### Pull environment variables from remote database
70
+
71
+ Download environment variables from a realtime database (Firebase, custom API, etc.) and save to a local `.env` file:
72
+
73
+ ```bash
74
+ # Pull to default .env file
75
+ $ dotenvrtdb --pull https://your-project.firebaseio.com/env.json
76
+
77
+ # Pull to custom file using -e flag
78
+ $ dotenvrtdb --pull https://your-project.firebaseio.com/env.json -e .env.production
79
+
80
+ # Or specify -e flag before --pull
81
+ $ dotenvrtdb -e .env.staging --pull https://your-project.firebaseio.com/env.json
82
+ ```
83
+
84
+ #### Push environment variables to remote database
85
+
86
+ Upload your local `.env` file to a realtime database:
87
+
88
+ ```bash
89
+ # Push from default .env file
90
+ $ dotenvrtdb --push https://your-project.firebaseio.com/env.json
91
+
92
+ # Push from custom file using -e flag
93
+ $ dotenvrtdb --push https://your-project.firebaseio.com/env.json -e .env.production
94
+
95
+ # Or specify -e flag before --push
96
+ $ dotenvrtdb -e .env.staging --push https://your-project.firebaseio.com/env.json
97
+ ```
98
+
99
+ #### Example workflow:
100
+
101
+ ```bash
102
+ # Pull production env from Firebase
103
+ $ dotenvrtdb --pull https://myapp.firebaseio.com/env/prod.json -e .env.production
104
+
105
+ # Run your app with production env
106
+ $ dotenvrtdb -e .env.production -- node app.js
107
+
108
+ # Update local env and push back
109
+ $ dotenvrtdb --push https://myapp.firebaseio.com/env/prod.json -e .env.production
110
+ ```
111
+
112
+ ### Custom .env files
113
+
114
+ Another .env file could be specified using the -e flag (this will replace loading `.env` file):
115
+
116
+ ```bash
117
+ $ dotenvrtdb -e .env2 -- <command with arguments>
118
+ ```
119
+
120
+ Multiple .env files can be specified, and will be processed in order, but only sets variables if they haven't already been set. So the first one wins (existing env variables win over the first file and the first file wins over the second file):
121
+
122
+ ```bash
123
+ $ dotenvrtdb -e .env3 -e .env4 -- <command with arguments>
124
+ ```
125
+
126
+ ### Cascading env variables
127
+
128
+ Some applications load env variables from multiple `.env` files depending on the environment:
129
+
130
+ - `.env`
131
+ - `.env.local`
132
+ - `.env.development`
133
+ - `.env.development.local`
134
+
135
+ dotenvrtdb supports this using the `-c` flag:
136
+
137
+ - `-c` loads `.env` and `.env.local`
138
+ - `-c test` loads `.env`, `.env.local`, `.env.test`, and `.env.test.local`
139
+
140
+ The `-c` flag can be used together with the `-e` flag. The following example will cascade env files located one folder up in the directory tree (`../.env` followed by `../.env.local`):
141
+
142
+ ```bash
143
+ dotenvrtdb -e ../.env -c
144
+ ```
145
+
146
+ ### Setting variable from command line
147
+
148
+ It is possible to set variable directly from command line using the -v flag:
149
+
150
+ ```bash
151
+ $ dotenvrtdb -v VARIABLE=somevalue -- <command with arguments>
152
+ ```
153
+
154
+ Multiple variables can be specified:
155
+
156
+ ```bash
157
+ $ dotenvrtdb -v VARIABLE1=somevalue1 -v VARIABLE2=somevalue2 -- <command with arguments>
158
+ ```
159
+
160
+ Variables set up from command line have higher priority than from env files.
161
+
162
+ > Purpose of this is that standard approach `VARIABLE=somevalue <command with arguments>` doesn't work on Windows. The -v flag works on all the platforms.
163
+
164
+ ### Check env variable
165
+
166
+ If you want to check the value of an environment variable, use the `-p` flag
167
+
168
+ ```bash
169
+ $ dotenvrtdb -p NODE_ENV
170
+ ```
171
+
172
+ ### Flags to the underlying command
173
+
174
+ If you want to pass flags to the inner command use `--` after all the flags to `dotenvrtdb`.
175
+
176
+ E.g. the following command without dotenvrtdb:
177
+
178
+ ```bash
179
+ mvn exec:java -Dexec.args="-g -f"
180
+ ```
181
+
182
+ will become the following command with dotenvrtdb:
183
+
184
+ ```bash
185
+ $ dotenvrtdb -- mvn exec:java -Dexec.args="-g -f"
186
+ ```
187
+
188
+ or in case the env file is at `.my-env`
189
+
190
+ ```bash
191
+ $ dotenvrtdb -e .my-env -- mvn exec:java -Dexec.args="-g -f"
192
+ ```
193
+
194
+ ### Variable expansion
195
+
196
+ We support expanding env variables inside .env files (See [dotenv-expand](https://github.com/motdotla/dotenv-expand) npm package for more information)
197
+
198
+ For example:
199
+
200
+ ```
201
+ IP=127.0.0.1
202
+ PORT=1234
203
+ APP_URL=http://${IP}:${PORT}
204
+ ```
205
+
206
+ Using the above example `.env` file, `process.env.APP_URL` would be `http://127.0.0.1:1234`.
207
+
208
+ #### Disabling variable expansion
209
+
210
+ If your `.env` variables include values that should not be expanded (e.g. `PASSWORD="pas$word"`), you can pass flag `--no-expand` to `dotenvrtdb` to disable variable expansion.
211
+
212
+ For example:
213
+
214
+ ```bash
215
+ dotenvrtdb --no-expand <command>
216
+ ```
217
+
218
+ ### Variable expansion in the command
219
+
220
+ If your `.env` file looks like:
221
+
222
+ ```
223
+ SAY_HI=hello!
224
+ ```
225
+
226
+ you might expect `dotenvrtdb echo "$SAY_HI"` to display `hello!`. In fact, this is not what happens: your shell will first interpret your command before passing it to `dotenvrtdb`, so if `SAY_HI` envvar is set to `""`, the command will be expanded into `dotenvrtdb echo`: that's why `dotenvrtdb` cannot make the expansion you expect.
227
+
228
+ #### Possible solutions
229
+
230
+ 1. Bash and escape
231
+
232
+ One possible way to get the desired result is:
233
+
234
+ ```bash
235
+ $ dotenvrtdb -- bash -c 'echo "$SAY_HI"'
236
+ ```
237
+
238
+ In bash, everything between `'` is not interpreted but passed as is. Since `$SAY_HI` is inside `''` brackets, it's passed as a string literal.
239
+
240
+ Therefore, `dotenvrtdb` will start a child process `bash -c 'echo "$SAY_HI"'` with the env variable `SAY_HI` set correctly which means bash will run `echo "$SAY_HI"` in the right environment which will print correctly `hello`
241
+
242
+ 2. Subscript encapsulation
243
+
244
+ Another solution is simply to encapsulate your script in another subscript.
245
+
246
+ Example here with npm scripts in a package.json
247
+
248
+ ```json
249
+ {
250
+ "scripts": {
251
+ "_print-stuff": "echo $STUFF",
252
+ "print-stuff": "dotenvrtdb -- npm run _print-stuff"
253
+ }
254
+ }
255
+ ```
256
+
257
+ This example is used in a project setting (has a package.json). Should always install locally `npm install -D @tolaptrinhdh61-spec/dotenvrtdb`
258
+
259
+ ### Debugging
260
+
261
+ You can add the `--debug` flag to output the `.env` files that would be processed and exit.
262
+
263
+ ### Override
264
+
265
+ Override any environment variables that have already been set on your machine with values from your .env file.
266
+
267
+ ```bash
268
+ dotenvrtdb -e .env.test -o -- jest
269
+ ```
270
+
271
+ ## Command Reference
272
+
273
+ ```
274
+ Usage: dotenvrtdb [--help] [--debug] [--quiet=false] [-e <path>] [-v <n>=<value>]
275
+ [-p <variable name>] [-c [environment]] [--no-expand] [-- command]
276
+
277
+ Options:
278
+ --help print help
279
+ --debug output the files that would be processed but don't actually parse them
280
+ --quiet, -q suppress debug output from dotenv (default: true)
281
+ -e <path> parses the file <path> as a `.env` file
282
+ -e <path> multiple -e flags are allowed
283
+ -v <n>=<value> put variable <n> into environment using value <value>
284
+ -v <n>=<value> multiple -v flags are allowed
285
+ -p <variable> print value of <variable> to the console
286
+ -c [environment] support cascading env variables from multiple files
287
+ --no-expand skip variable expansion
288
+ -o, --override override system variables. Cannot be used with cascade (-c)
289
+ command command to run with environment variables loaded
290
+
291
+ Remote database commands:
292
+ --pull <url> pull env variables from remote database URL and save to file
293
+ use with -e flag to specify output file (default: .env)
294
+ example: dotenvrtdb --pull <url> -e .env.production
295
+ --push <url> push local .env file to remote database URL
296
+ use with -e flag to specify source file (default: .env)
297
+ example: dotenvrtdb --push <url> -e .env.staging
298
+ ```
299
+
300
+ ## Use Cases
301
+
302
+ ### Team Environment Sync
303
+
304
+ Keep your team's environment variables in sync using Firebase Realtime Database:
305
+
306
+ ```bash
307
+ # Team lead pushes the base config
308
+ $ dotenvrtdb --push https://team-project.firebaseio.com/env/base.json
309
+
310
+ # Team members pull the config
311
+ $ dotenvrtdb --pull https://team-project.firebaseio.com/env/base.json
312
+ ```
313
+
314
+ ### Multi-Environment Deployment
315
+
316
+ Manage different environments easily:
317
+
318
+ ```bash
319
+ # Pull production config
320
+ $ dotenvrtdb --pull https://myapp.firebaseio.com/prod.json -e .env.production
321
+
322
+ # Pull staging config
323
+ $ dotenvrtdb --pull https://myapp.firebaseio.com/staging.json -e .env.staging
324
+
325
+ # Run with specific environment
326
+ $ dotenvrtdb -e .env.production -- node server.js
327
+ ```
328
+
329
+ ### CI/CD Integration
330
+
331
+ Store secrets in Firebase and pull them during deployment:
332
+
333
+ ```yaml
334
+ # .github/workflows/deploy.yml
335
+ - name: Pull environment variables
336
+ run: |
337
+ npm install -g @tolaptrinhdh61-spec/dotenvrtdb
338
+ dotenvrtdb --pull ${{ secrets.FIREBASE_ENV_URL }} -e .env.production
339
+
340
+ - name: Deploy application
341
+ run: dotenvrtdb -e .env.production -- npm run deploy
342
+ ```
343
+
344
+ ### Development Workflow
345
+
346
+ ```bash
347
+ # Developer pulls latest shared config
348
+ $ dotenvrtdb --pull https://dev-db.firebaseio.com/config.json -e .env.development
349
+
350
+ # Make local changes and test
351
+ $ dotenvrtdb -e .env.development -- npm run dev
352
+
353
+ # Push updated config back (if authorized)
354
+ $ dotenvrtdb --push https://dev-db.firebaseio.com/config.json -e .env.development
355
+ ```
356
+
357
+ ## Remote Database Format
358
+
359
+ The remote database should return JSON in the following format:
360
+
361
+ ```json
362
+ {
363
+ "DATABASE_URL": "postgresql://localhost:5432/mydb",
364
+ "API_KEY": "your-api-key-here",
365
+ "NODE_ENV": "production",
366
+ "PORT": "3000"
367
+ }
368
+ ```
369
+
370
+ This will be converted to `.env` format:
371
+
372
+ ```
373
+ DATABASE_URL=postgresql://localhost:5432/mydb
374
+ API_KEY=your-api-key-here
375
+ NODE_ENV=production
376
+ PORT=3000
377
+ ```
378
+
379
+ ## Security Features
380
+
381
+ ### 🔒 Automatic Auth Token Masking
382
+
383
+ dotenvrtdb automatically masks sensitive information in URLs when displaying console output:
384
+
385
+ ```bash
386
+ # Your command
387
+ $ dotenvrtdb --pull "https://myapp.firebaseio.com/env.json?auth=AIzaSyAbc123XYZ"
388
+
389
+ # Console output (auth token is masked)
390
+ Pulling environment variables from https://myapp.firebaseio.com/env.json?auth=******...
391
+ ✓ Successfully pulled environment variables to .env
392
+ ```
393
+
394
+ Masked parameters include:
395
+
396
+ - `auth`
397
+ - `token`
398
+ - `key`
399
+ - `secret`
400
+ - `apikey`
401
+ - `api_key`
402
+
403
+ Username and password in URLs are also automatically masked:
404
+
405
+ ```
406
+ https://user:password@example.com → https://******:******@example.com
407
+ ```
408
+
409
+ ### ⚠️ Important Security Notes
410
+
411
+ - Never commit `.env` files containing sensitive data to version control
412
+ - Use Firebase Security Rules to restrict access to your env database
413
+ - For production secrets, consider using environment-specific databases with proper authentication
414
+ - The `--pull` command requires read access to the database URL
415
+ - The `--push` command requires write access to the database URL
416
+
417
+ Example Firebase Security Rules:
418
+
419
+ ```json
420
+ {
421
+ "rules": {
422
+ "env": {
423
+ ".read": "auth != null",
424
+ ".write": "auth != null && auth.token.admin === true"
425
+ }
426
+ }
427
+ }
428
+ ```
429
+
430
+ ## Supported Databases
431
+
432
+ dotenvrtdb works with any HTTP/HTTPS endpoint that:
433
+
434
+ - Returns JSON in key-value format (for pull)
435
+ - Accepts JSON via PUT/POST request (for push)
436
+
437
+ ### Compatible services:
438
+
439
+ - ✅ Firebase Realtime Database
440
+ - ✅ Custom REST APIs
441
+ - ✅ Any HTTP/HTTPS JSON endpoint
442
+ - ✅ Cloud Functions
443
+ - ✅ Serverless endpoints
444
+
445
+ ## Migration from dotenv-cli
446
+
447
+ If you're currently using `dotenv-cli`, you can switch to `dotenvrtdb` with zero breaking changes:
448
+
449
+ ```bash
450
+ # Replace
451
+ $ dotenv -- node app.js
452
+
453
+ # With
454
+ $ dotenvrtdb -- node app.js
455
+ ```
456
+
457
+ All existing flags and features work exactly the same way!
458
+
459
+ ## Examples
460
+
461
+ ### Basic Examples
462
+
463
+ ```bash
464
+ # Load .env and run node app
465
+ $ dotenvrtdb -- node app.js
466
+
467
+ # Load custom env file
468
+ $ dotenvrtdb -e .env.local -- npm start
469
+
470
+ # Print environment variable
471
+ $ dotenvrtdb -p DATABASE_URL
472
+
473
+ # Set variables from command line
474
+ $ dotenvrtdb -v PORT=3000 -v HOST=localhost -- node server.js
475
+ ```
476
+
477
+ ### Remote Database Examples
478
+
479
+ ```bash
480
+ # Pull from Firebase with auth token
481
+ $ dotenvrtdb --pull "https://myapp.firebaseio.com/config.json?auth=YOUR_TOKEN" -e .env
482
+
483
+ # Push to custom API endpoint
484
+ $ dotenvrtdb --push "https://api.myapp.com/env" -e .env.production
485
+
486
+ # Pull and immediately use
487
+ $ dotenvrtdb --pull "https://myapp.firebaseio.com/env.json?auth=TOKEN" -e .env.temp && \
488
+ dotenvrtdb -e .env.temp -- node app.js
489
+ ```
490
+
491
+ ### Advanced Examples
492
+
493
+ ```bash
494
+ # Cascade with remote sync
495
+ $ dotenvrtdb --pull "https://firebase.com/base.json" -e .env
496
+ $ dotenvrtdb -e .env -c production -- node app.js
497
+
498
+ # Multiple env files with priority
499
+ $ dotenvrtdb -e .env.local -e .env.shared -- npm run build
500
+
501
+ # Override system variables
502
+ $ dotenvrtdb -e .env.test -o -- jest
503
+ ```
504
+
505
+ ## Package Information
506
+
507
+ - **Package**: `@tolaptrinhdh61-spec/dotenvrtdb`
508
+ - **Command**: `dotenvrtdb`
509
+ - **Version**: 11.0.3
510
+ - **Repository**: https://github.com/tolaptrinhdh61-spec/dotenvrtdb
511
+
512
+ ## Contributing
513
+
514
+ Issues and pull requests are welcome! Please visit our [GitHub repository](https://github.com/tolaptrinhdh61-spec/dotenvrtdb).
515
+
516
+ ## License
517
+
518
+ [MIT](https://en.wikipedia.org/wiki/MIT_License)
519
+
520
+ ## Credits
521
+
522
+ Based on [dotenv-cli](https://github.com/entropitor/dotenv-cli) with added remote database synchronization features.
523
+
524
+ ---
525
+
526
+ Made with ❤️ by [tolaptrinhdh61-spec](https://github.com/tolaptrinhdh61-spec)
package/cli.js ADDED
@@ -0,0 +1,736 @@
1
+ #!/usr/bin/env node
2
+
3
+ const spawn = require("cross-spawn");
4
+ const path = require("path");
5
+ const https = require("https");
6
+ const http = require("http");
7
+ const fs = require("fs");
8
+ const os = require("os");
9
+
10
+ // ✅ FIX: Custom parsing để handle URLs bị split bởi shell hoặc masked bởi GitHub Actions
11
+ // Case 1: URL không có quotes, shell tách thành nhiều args
12
+ // VD: -eUrl https://example.com?auth=123&key=456
13
+ // Shell tách: ['-eUrl', 'https://example.com?auth=123', 'key=456']
14
+ // → Ghép lại: ['-eUrl', 'https://example.com?auth=123&key=456']
15
+ //
16
+ // Case 2: GitHub Actions mask secret thành ***, không có quotes
17
+ // VD: -eUrl *** -- node script.js
18
+ // GitHub mask trước: ['-eUrl', '***', '--', 'node', 'script.js']
19
+ // → Giữ nguyên '***' và tìm '--' để tách command
20
+ function parseArguments(args) {
21
+ const result = [];
22
+ let i = 0;
23
+
24
+ // Tìm vị trí của '--' trước tiên
25
+ const doubleDashIndex = args.indexOf("--");
26
+
27
+ while (i < args.length) {
28
+ const arg = args[i];
29
+
30
+ // Nếu gặp '--', dừng parsing flags và pass everything sau đó as command
31
+ if (arg === "--") {
32
+ // Không push '--' vào result, nhưng push tất cả args sau nó
33
+ i++;
34
+ while (i < args.length) {
35
+ result.push(args[i]);
36
+ i++;
37
+ }
38
+ break;
39
+ }
40
+
41
+ // Nếu là -eUrl, cần xử lý đặc biệt
42
+ if (arg === "-eUrl" || arg === "--eUrl") {
43
+ result.push(arg);
44
+ i++;
45
+
46
+ // Ghép tất cả args tiếp theo cho đến khi gặp '--' hoặc arg bắt đầu bằng '-'
47
+ let urlParts = [];
48
+ while (i < args.length && args[i] !== "--") {
49
+ const nextArg = args[i];
50
+
51
+ // Nếu gặp flag khác (bắt đầu với '-' nhưng KHÔNG phải '***' hoặc pattern giống)
52
+ // thì dừng lại
53
+ if (nextArg.startsWith("-") && !nextArg.match(/^-[\*\+\.]+$/)) {
54
+ break;
55
+ }
56
+
57
+ urlParts.push(nextArg);
58
+ i++;
59
+ }
60
+
61
+ // Ghép lại thành 1 URL với '&' (trường hợp shell tách bởi &)
62
+ // Hoặc giữ nguyên nếu là masked value như '***'
63
+ if (urlParts.length > 0) {
64
+ result.push(urlParts.join("&"));
65
+ }
66
+ } else {
67
+ result.push(arg);
68
+ i++;
69
+ }
70
+ }
71
+
72
+ return result;
73
+ }
74
+
75
+ const parsedArgs = parseArguments(process.argv.slice(2));
76
+ const argv = require("minimist")(parsedArgs);
77
+ const dotenv = require("dotenv");
78
+
79
+ // ✅ FIX: Xử lý import dotenv-expand cho cả CommonJS và ES modules
80
+ let dotenvExpand;
81
+ try {
82
+ // Thử import theo cách mới (dotenv-expand >= 9.0)
83
+ const dotenvExpandModule = require("dotenv-expand");
84
+ dotenvExpand = dotenvExpandModule.expand || dotenvExpandModule.default || dotenvExpandModule;
85
+ } catch (err) {
86
+ console.error("Failed to load dotenv-expand:", err.message);
87
+ process.exit(1);
88
+ }
89
+
90
+ // Biến lưu danh sách các file tạm cần xóa
91
+ const tempFilesToCleanup = [];
92
+ let isCleanedUp = false; // Prevent double cleanup
93
+
94
+ function printHelp() {
95
+ console.log(
96
+ [
97
+ "Usage: dotenvrtdb [--help] [--debug] [--quiet=false] [-e <path>] [-eUrl <url>] [-v <n>=<value>] [-p <variable name>] [-c [environment]] [--no-expand] [-- command]",
98
+ " --help print help",
99
+ " --debug output the files that would be processed but don't actually parse them or run the `command`",
100
+ " --quiet, -q suppress debug output from dotenv (default: true)",
101
+ " -e <path> parses the file <path> as a `.env` file and adds the variables to the environment",
102
+ " -e <path> multiple -e flags are allowed",
103
+ " -eUrl <url> pull env from remote URL to temp file, use it, then delete temp file",
104
+ " -eUrl <url> multiple -eUrl flags are allowed",
105
+ " -v <n>=<value> put variable <n> into environment using value <value>",
106
+ " -v <n>=<value> multiple -v flags are allowed",
107
+ " -p <variable> print value of <variable> to the console. If you specify this, you do not have to specify a `command`",
108
+ " -c [environment] support cascading env variables from `.env`, `.env.<environment>`, `.env.local`, `.env.<environment>.local` files",
109
+ " --no-expand skip variable expansion",
110
+ " -o, --override override system variables. Cannot be used along with cascade (-c).",
111
+ " command `command` is the actual command you want to run. Best practice is to precede this command with ` -- `. Everything after `--` is considered to be your command. So any flags will not be parsed by this tool but be passed to your command. If you do not do it, this tool will strip those flags",
112
+ "",
113
+ "Remote database commands:",
114
+ " --pull <url> pull env variables from remote database URL and save to file",
115
+ " use with -e flag to specify output file (default: .env)",
116
+ " example: dotenvrtdb --pull <url> -e .env.production",
117
+ " --push <url> push local .env file to remote database URL",
118
+ " use with -e flag to specify source file (default: .env)",
119
+ " example: dotenvrtdb --push <url> -e .env.staging",
120
+ ].join("\n"),
121
+ );
122
+ }
123
+
124
+ // Hàm cleanup để xóa các file tạm
125
+ function cleanupTempFiles() {
126
+ if (isCleanedUp) return;
127
+ isCleanedUp = true;
128
+
129
+ if (tempFilesToCleanup.length === 0) {
130
+ return; // Không có file tạm nào để xóa
131
+ }
132
+
133
+ const isDebug = argv.debug;
134
+ const isQuiet = !(argv.quiet === false || argv.q === false || argv.quiet === "false" || argv.q === "false");
135
+
136
+ // 🔒 ALWAYS show cleanup message for security awareness
137
+ if (!isQuiet) {
138
+ console.log(`\n🧹 Cleaning up ${tempFilesToCleanup.length} temporary file(s)...`);
139
+ }
140
+
141
+ let deletedCount = 0;
142
+ let failedCount = 0;
143
+
144
+ tempFilesToCleanup.forEach((filePath) => {
145
+ try {
146
+ if (fs.existsSync(filePath)) {
147
+ fs.unlinkSync(filePath);
148
+ deletedCount++;
149
+ if (isDebug || !isQuiet) {
150
+ console.log(` ✓ Deleted: ${path.basename(filePath)}`);
151
+ }
152
+ } else {
153
+ if (isDebug) {
154
+ console.log(` ⊘ Already deleted: ${path.basename(filePath)}`);
155
+ }
156
+ }
157
+ } catch (err) {
158
+ failedCount++;
159
+ console.error(` ✗ Failed to delete ${path.basename(filePath)}: ${err.message}`);
160
+ }
161
+ });
162
+
163
+ if (!isQuiet && deletedCount > 0) {
164
+ console.log(`✓ Successfully deleted ${deletedCount} temporary file(s)\n`);
165
+ }
166
+
167
+ if (failedCount > 0) {
168
+ console.error(`⚠️ Warning: ${failedCount} file(s) could not be deleted. Please check manually.`);
169
+ }
170
+ }
171
+
172
+ // Đăng ký cleanup khi process kết thúc
173
+ process.on("exit", cleanupTempFiles);
174
+ process.on("SIGINT", () => {
175
+ cleanupTempFiles();
176
+ process.exit(130);
177
+ });
178
+ process.on("SIGTERM", () => {
179
+ cleanupTempFiles();
180
+ process.exit(143);
181
+ });
182
+ process.on("uncaughtException", (err) => {
183
+ console.error("Uncaught exception:", err);
184
+ cleanupTempFiles();
185
+ process.exit(1);
186
+ });
187
+ process.on("unhandledRejection", (err) => {
188
+ console.error("Unhandled rejection:", err);
189
+ cleanupTempFiles();
190
+ process.exit(1);
191
+ });
192
+
193
+ // Hàm mask URL để ẩn auth token
194
+ function maskUrl(url) {
195
+ try {
196
+ const urlObj = new URL(url);
197
+
198
+ // Mask query parameters chứa auth/token/key
199
+ const params = new URLSearchParams(urlObj.search);
200
+ const maskedParams = new URLSearchParams();
201
+
202
+ for (const [key, value] of params.entries()) {
203
+ const lowerKey = key.toLowerCase();
204
+ if (lowerKey.includes("auth") || lowerKey.includes("token") || lowerKey.includes("key") || lowerKey.includes("secret")) {
205
+ maskedParams.set(key, "******");
206
+ } else {
207
+ maskedParams.set(key, value);
208
+ }
209
+ }
210
+
211
+ urlObj.search = maskedParams.toString();
212
+
213
+ // Mask username/password trong URL
214
+ if (urlObj.username || urlObj.password) {
215
+ urlObj.username = urlObj.username ? "******" : "";
216
+ urlObj.password = urlObj.password ? "******" : "";
217
+ }
218
+
219
+ return urlObj.toString();
220
+ } catch (err) {
221
+ // Nếu không parse được URL, mask theo pattern
222
+ return url.replace(/([?&])(auth|token|key|secret|apikey|api_key)=([^&]+)/gi, "$1$2=******").replace(/\/\/([^:]+):([^@]+)@/gi, "//******:******@");
223
+ }
224
+ }
225
+
226
+ // Hàm fetch dữ liệu từ URL
227
+ function fetchFromUrl(url) {
228
+ return new Promise((resolve, reject) => {
229
+ const protocol = url.startsWith("https") ? https : http;
230
+
231
+ protocol
232
+ .get(url, (res) => {
233
+ let data = "";
234
+
235
+ if (res.statusCode !== 200) {
236
+ reject(new Error(`Failed to fetch from ${maskUrl(url)}. Status code: ${res.statusCode}`));
237
+ return;
238
+ }
239
+
240
+ res.on("data", (chunk) => {
241
+ data += chunk;
242
+ });
243
+
244
+ res.on("end", () => {
245
+ try {
246
+ const jsonData = JSON.parse(data);
247
+ resolve(jsonData);
248
+ } catch (err) {
249
+ reject(new Error(`Failed to parse JSON from ${maskUrl(url)}: ${err.message}`));
250
+ }
251
+ });
252
+ })
253
+ .on("error", (err) => {
254
+ reject(new Error(`Failed to fetch from ${maskUrl(url)}: ${err.message}`));
255
+ });
256
+ });
257
+ }
258
+
259
+ // Hàm push dữ liệu lên URL (PUT request for Firebase Realtime Database)
260
+ function pushToUrl(url, data) {
261
+ return new Promise((resolve, reject) => {
262
+ const urlObj = new URL(url);
263
+ const protocol = url.startsWith("https") ? https : http;
264
+
265
+ const jsonData = JSON.stringify(data);
266
+
267
+ const options = {
268
+ hostname: urlObj.hostname,
269
+ port: urlObj.port,
270
+ path: urlObj.pathname + urlObj.search,
271
+ method: "PUT",
272
+ headers: {
273
+ "Content-Type": "application/json",
274
+ "Content-Length": Buffer.byteLength(jsonData),
275
+ },
276
+ };
277
+
278
+ const req = protocol.request(options, (res) => {
279
+ let responseData = "";
280
+
281
+ res.on("data", (chunk) => {
282
+ responseData += chunk;
283
+ });
284
+
285
+ res.on("end", () => {
286
+ if (res.statusCode === 200 || res.statusCode === 201) {
287
+ resolve(responseData);
288
+ } else {
289
+ reject(new Error(`Failed to push to ${maskUrl(url)}. Status code: ${res.statusCode}`));
290
+ }
291
+ });
292
+ });
293
+
294
+ req.on("error", (err) => {
295
+ reject(new Error(`Failed to push to ${maskUrl(url)}: ${err.message}`));
296
+ });
297
+
298
+ req.write(jsonData);
299
+ req.end();
300
+ });
301
+ }
302
+
303
+ // Hàm chuyển đổi object thành format .env
304
+ function objectToEnvFormat(obj) {
305
+ if (!obj || typeof obj !== "object") {
306
+ return "";
307
+ }
308
+
309
+ return Object.entries(obj)
310
+ .map(([key, value]) => {
311
+ // Escape giá trị nếu chứa ký tự đặc biệt
312
+ const stringValue = String(value);
313
+ if (stringValue.includes("\n") || stringValue.includes('"') || stringValue.includes(" ")) {
314
+ return `${key}="${stringValue.replace(/"/g, '\\"')}"`;
315
+ }
316
+ return `${key}=${stringValue}`;
317
+ })
318
+ .join("\n");
319
+ }
320
+
321
+ // Hàm đọc file .env và chuyển thành object
322
+ function parseEnvFile(filePath) {
323
+ try {
324
+ const content = fs.readFileSync(filePath, "utf-8");
325
+ const lines = content.split("\n");
326
+ const result = {};
327
+
328
+ for (const line of lines) {
329
+ const trimmedLine = line.trim();
330
+
331
+ // Bỏ qua comment và dòng trống
332
+ if (!trimmedLine || trimmedLine.startsWith("#")) {
333
+ continue;
334
+ }
335
+
336
+ // Parse key=value
337
+ const match = trimmedLine.match(/^([^=]+)=(.*)$/);
338
+ if (match) {
339
+ const key = match[1].trim();
340
+ let value = match[2].trim();
341
+
342
+ // Xử lý giá trị trong dấu ngoặc kép
343
+ if (value.startsWith('"') && value.endsWith('"')) {
344
+ value = value.slice(1, -1).replace(/\\"/g, '"');
345
+ } else if (value.startsWith("'") && value.endsWith("'")) {
346
+ value = value.slice(1, -1);
347
+ }
348
+
349
+ result[key] = value;
350
+ }
351
+ }
352
+
353
+ return result;
354
+ } catch (err) {
355
+ throw new Error(`Failed to read or parse ${filePath}: ${err.message}`);
356
+ }
357
+ }
358
+
359
+ // Hàm tạo file tạm từ URL
360
+ async function createTempFileFromUrl(url, index = 0) {
361
+ const isDebug = argv.debug;
362
+ const isQuiet = !(argv.quiet === false || argv.q === false || argv.quiet === "false" || argv.q === "false");
363
+ const timestamp = Date.now();
364
+ const randomSuffix = Math.random().toString(36).substring(7);
365
+ const tempFileName = `.env.temp.${timestamp}.${index}.${randomSuffix}`;
366
+
367
+ // 🔒 CRITICAL SECURITY FIX: Tạo temp file trong OS temp directory
368
+ // KHÔNG tạo trong cwd để tránh:
369
+ // 1. File bị commit vào git
370
+ // 2. File bị publish lên npm package
371
+ // 3. Secrets bị leak ra public
372
+ const tempFilePath = path.join(os.tmpdir(), tempFileName);
373
+
374
+ try {
375
+ if (isDebug || !isQuiet) {
376
+ console.log(`📥 Fetching env vars from ${maskUrl(url)}...`);
377
+ }
378
+
379
+ const data = await fetchFromUrl(url);
380
+ const envContent = objectToEnvFormat(data);
381
+
382
+ fs.writeFileSync(tempFilePath, envContent, "utf-8");
383
+
384
+ if (isDebug || !isQuiet) {
385
+ console.log(` ✓ Created temp file: ${tempFileName}`);
386
+ console.log(` 📍 Location: ${os.tmpdir()}`);
387
+ console.log(` 🔒 Will be auto-deleted after execution`);
388
+ }
389
+
390
+ // Thêm vào danh sách cần cleanup
391
+ tempFilesToCleanup.push(tempFilePath);
392
+
393
+ return tempFilePath;
394
+ } catch (err) {
395
+ throw new Error(`Failed to create temp file from ${maskUrl(url)}: ${err.message}`);
396
+ }
397
+ }
398
+
399
+ // Xử lý lệnh pull
400
+ async function handlePull(url, outputPath) {
401
+ try {
402
+ console.log(`Pulling environment variables from ${maskUrl(url)}...`);
403
+ const data = await fetchFromUrl(url);
404
+ const envContent = objectToEnvFormat(data);
405
+
406
+ fs.writeFileSync(outputPath, envContent, "utf-8");
407
+ console.log(`✓ Successfully pulled environment variables to ${outputPath}`);
408
+ process.exit(0);
409
+ } catch (err) {
410
+ console.error(`✗ Pull failed: ${err.message}`);
411
+ process.exit(1);
412
+ }
413
+ }
414
+
415
+ // Xử lý lệnh push
416
+ async function handlePush(url, sourcePath) {
417
+ try {
418
+ console.log(`Pushing environment variables from ${sourcePath} to ${maskUrl(url)}...`);
419
+
420
+ if (!fs.existsSync(sourcePath)) {
421
+ throw new Error(`Source file ${sourcePath} does not exist`);
422
+ }
423
+
424
+ const envData = parseEnvFile(sourcePath);
425
+ await pushToUrl(url, envData);
426
+
427
+ console.log(`✓ Successfully pushed environment variables to ${maskUrl(url)}`);
428
+ process.exit(0);
429
+ } catch (err) {
430
+ console.error(`✗ Push failed: ${err.message}`);
431
+ process.exit(1);
432
+ }
433
+ }
434
+
435
+ // Xử lý -eUrl: Pull từ URL vào file tạm
436
+ async function processEUrlFlags() {
437
+ // ✅ FIX: Support reading URL from environment variable
438
+ // Nếu -eUrl = "***" (GitHub Actions masked), thử đọc từ env var
439
+ // Priority: 1) argv.eUrl nếu không phải "***", 2) DOTENVRTDB_URL env var
440
+
441
+ let urls = [];
442
+
443
+ if (argv.eUrl) {
444
+ let eUrlValue = typeof argv.eUrl === "string" ? [argv.eUrl] : argv.eUrl;
445
+
446
+ if (!Array.isArray(eUrlValue)) {
447
+ eUrlValue = [eUrlValue];
448
+ }
449
+
450
+ // Check if URL is masked by GitHub Actions
451
+ urls = eUrlValue.map((url) => {
452
+ if (url === "***" || url === "*" || url.match(/^\*+$/)) {
453
+ // URL was masked, try to get from environment variable
454
+ const envUrl = process.env.DOTENVRTDB_URL;
455
+ if (envUrl) {
456
+ console.log("⚠️ Detected masked URL (***), using DOTENVRTDB_URL environment variable");
457
+ return envUrl;
458
+ } else {
459
+ console.error("❌ URL was masked by GitHub Actions but DOTENVRTDB_URL env var not found");
460
+ console.error(" Please set DOTENVRTDB_URL as environment variable:");
461
+ console.error(" env:");
462
+ console.error(" DOTENVRTDB_URL: ${{ secrets.DOTENVRTDB_URL }}");
463
+ process.exit(1);
464
+ }
465
+ }
466
+ return url;
467
+ });
468
+ } else if (process.env.DOTENVRTDB_URL) {
469
+ // Không có -eUrl flag nhưng có env var
470
+ urls = [process.env.DOTENVRTDB_URL];
471
+ console.log("ℹ️ Using DOTENVRTDB_URL from environment variable");
472
+ } else {
473
+ return [];
474
+ }
475
+
476
+ urls = urls.filter((url) => url && typeof url === "string" && url.trim().length > 0);
477
+
478
+ if (urls.length === 0) {
479
+ return [];
480
+ }
481
+
482
+ const tempPaths = [];
483
+
484
+ for (let i = 0; i < urls.length; i++) {
485
+ const url = urls[i];
486
+ try {
487
+ const tempPath = await createTempFileFromUrl(url, i);
488
+ tempPaths.push(tempPath);
489
+ } catch (err) {
490
+ console.error(`✗ Failed to process -eUrl ${maskUrl(url)}: ${err.message}`);
491
+ cleanupTempFiles();
492
+ process.exit(1);
493
+ }
494
+ }
495
+
496
+ return tempPaths;
497
+ }
498
+
499
+ // Main async function để xử lý -eUrl
500
+ async function main() {
501
+ const override = argv.o || argv.override;
502
+
503
+ // Handle quiet flag - default is true (quiet), can be disabled with --quiet=false or -q=false
504
+ const isQuiet = !(argv.quiet === false || argv.q === false || argv.quiet === "false" || argv.q === "false");
505
+
506
+ if (argv.c && override) {
507
+ console.error("Invalid arguments. Cascading env variables conflicts with overrides.");
508
+ process.exit(1);
509
+ }
510
+
511
+ let paths = [];
512
+
513
+ // Xử lý -eUrl trước
514
+ const tempPaths = await processEUrlFlags();
515
+
516
+ if (Array.isArray(tempPaths) && tempPaths.length > 0) {
517
+ paths.push(...tempPaths);
518
+ }
519
+
520
+ // Sau đó xử lý -e như bình thường
521
+ if (argv.e) {
522
+ if (typeof argv.e === "string") {
523
+ paths.push(argv.e);
524
+ } else if (Array.isArray(argv.e)) {
525
+ paths.push(...argv.e);
526
+ }
527
+ }
528
+
529
+ // Nếu không có -e và -eUrl, dùng .env mặc định
530
+ if (paths.length === 0) {
531
+ paths.push(".env");
532
+ }
533
+
534
+ if (argv.c) {
535
+ paths = paths.reduce(
536
+ (accumulator, envPath) =>
537
+ accumulator.concat(
538
+ typeof argv.c === "string"
539
+ ? [`${envPath}.${argv.c}.local`, `${envPath}.local`, `${envPath}.${argv.c}`, envPath]
540
+ : [`${envPath}.local`, envPath],
541
+ ),
542
+ [],
543
+ );
544
+ }
545
+
546
+ function validateCmdVariable(param) {
547
+ const [, key, val] = param.match(/^(\w+)=([\s\S]+)$/m) || [];
548
+ if (!key || !val) {
549
+ console.error(`Invalid variable name. Expected variable in format '-v variable=value', but got: \`-v ${param}\`.`);
550
+ cleanupTempFiles();
551
+ process.exit(1);
552
+ }
553
+
554
+ return [key, val];
555
+ }
556
+
557
+ const variables = [];
558
+ if (argv.v) {
559
+ if (typeof argv.v === "string") {
560
+ variables.push(validateCmdVariable(argv.v));
561
+ } else {
562
+ variables.push(...argv.v.map(validateCmdVariable));
563
+ }
564
+ }
565
+ const parsedVariables = Object.fromEntries(variables);
566
+
567
+ if (argv.debug) {
568
+ console.log("Files to be processed:");
569
+ console.log(paths);
570
+ console.log("\nVariables from command line:");
571
+ console.log(parsedVariables);
572
+ if (tempFilesToCleanup.length > 0) {
573
+ console.log("\nTemp files (will be deleted after execution):");
574
+ console.log(tempFilesToCleanup);
575
+ }
576
+ cleanupTempFiles();
577
+ process.exit();
578
+ }
579
+
580
+ // ✅ FIX: Load và expand từng file với kiểm tra function
581
+ paths.forEach(function (env) {
582
+ const resolvedPath = path.resolve(env);
583
+
584
+ // Debug: Check if file exists
585
+ if (!fs.existsSync(resolvedPath)) {
586
+ if (!isQuiet) {
587
+ console.warn(`Warning: File does not exist: ${resolvedPath}`);
588
+ }
589
+ return; // Skip this file
590
+ }
591
+
592
+ const result = dotenv.config({ path: resolvedPath, override, quiet: isQuiet });
593
+
594
+ // Debug: Check if file was loaded successfully
595
+ if (result.error && !isQuiet) {
596
+ console.error(`Error loading ${resolvedPath}:`, result.error.message);
597
+ } else if (result.parsed && !isQuiet) {
598
+ console.log(`✓ Loaded ${Object.keys(result.parsed).length} variables from ${resolvedPath}`);
599
+ }
600
+
601
+ // Expand variables nếu cần và nếu dotenvExpand là function
602
+ if (argv.expand !== false && result.parsed && typeof dotenvExpand === "function") {
603
+ dotenvExpand(result);
604
+ }
605
+ });
606
+
607
+ // Thêm variables từ command line
608
+ Object.assign(process.env, parsedVariables);
609
+
610
+ if (argv.p) {
611
+ let value = process.env[argv.p];
612
+ if (typeof value === "string") {
613
+ value = `${value}`;
614
+ }
615
+ console.log(value != null ? value : "");
616
+ cleanupTempFiles();
617
+ process.exit();
618
+ }
619
+
620
+ const command = argv._[0];
621
+
622
+ // ✅ FIX: Nếu không có command nhưng có -eUrl hợp lệ
623
+ // GitHub Actions có thể mask secret thành *** khiến parsing bị lỗi
624
+ // Trong trường hợp này, check xem có remaining args sau khi parse không
625
+ if (!command && argv.eUrl) {
626
+ console.error("ERROR: No command provided after arguments.");
627
+ console.error("When using -eUrl, make sure to include the command to run.");
628
+ console.error("");
629
+ console.error("Examples:");
630
+ console.error(' dotenvrtdb -eUrl "${{ secrets.URL }}" -- node script.js');
631
+ console.error(' dotenvrtdb -eUrl "https://example.com" -- npm start');
632
+ console.error("");
633
+ console.error("Note: In GitHub Actions, always quote secrets to prevent parsing issues:");
634
+ console.error(' -eUrl "${{ secrets.DOTENVRTDB_URL }}" -- command');
635
+ console.error("");
636
+ printHelp();
637
+ cleanupTempFiles();
638
+ process.exit(1);
639
+ }
640
+
641
+ if (!command) {
642
+ printHelp();
643
+ cleanupTempFiles();
644
+ process.exit(1);
645
+ }
646
+
647
+ const child = spawn(command, argv._.slice(1), { stdio: "inherit" }).on("exit", function (exitCode, signal) {
648
+ cleanupTempFiles();
649
+ if (typeof exitCode === "number") {
650
+ process.exit(exitCode);
651
+ } else {
652
+ process.kill(process.pid, signal);
653
+ }
654
+ });
655
+
656
+ for (const signal of ["SIGINT", "SIGTERM", "SIGPIPE", "SIGHUP", "SIGBREAK", "SIGWINCH", "SIGUSR1", "SIGUSR2"]) {
657
+ process.on(signal, function () {
658
+ child.kill(signal);
659
+ });
660
+ }
661
+ }
662
+
663
+ // Entry point
664
+ (async function () {
665
+ // ✅ DEBUG: Always log raw arguments để diagnose vấn đề
666
+ const hasEUrl = process.argv.includes("-eUrl") || process.argv.includes("--eUrl");
667
+ const hasDoubleDash = process.argv.includes("--");
668
+ const hasCommand = argv._.length > 0;
669
+
670
+ // Nếu có -eUrl nhưng không có command, có thể là parsing issue
671
+ if (hasEUrl && !hasCommand && !argv.help && !argv.pull && !argv.push && !argv.debug && !argv.p) {
672
+ console.error("=== ARGUMENT PARSING DEBUG ===");
673
+ console.error("Raw process.argv:", process.argv);
674
+ console.error("Parsed args:", parsedArgs);
675
+ console.error("Minimist result:", JSON.stringify(argv, null, 2));
676
+ console.error("Has --:", hasDoubleDash);
677
+ console.error("==============================");
678
+ console.error("");
679
+ console.error("ERROR: No command provided after arguments");
680
+ console.error("");
681
+ console.error("The most common cause is forgetting '--' before the command:");
682
+ console.error(" ❌ Wrong: dotenvrtdb -eUrl ${{ secrets.URL }} node script.js");
683
+ console.error(' ✅ Correct: dotenvrtdb -eUrl "${{ secrets.URL }}" -- node script.js');
684
+ console.error("");
685
+ console.error("In GitHub Actions, ALWAYS quote secrets and use '--':");
686
+ console.error(' run: dotenvrtdb -eUrl "${{ secrets.DOTENVRTDB_URL }}" -- node ./bin/cli.js publish');
687
+ console.error("");
688
+ printHelp();
689
+ process.exit(1);
690
+ }
691
+
692
+ // DEBUG MODE
693
+ const isDebugMode = argv.debug || process.env.DEBUG_DOTENVRTDB === "true";
694
+ if (isDebugMode) {
695
+ console.log("=== DEBUG MODE ===");
696
+ console.log("Raw process.argv:", process.argv);
697
+ console.log("Parsed args:", parsedArgs);
698
+ console.log("Minimist argv:", JSON.stringify(argv, null, 2));
699
+ console.log("==================");
700
+ }
701
+
702
+ if (argv.help) {
703
+ printHelp();
704
+ process.exit();
705
+ }
706
+
707
+ // Xử lý lệnh pull
708
+ if (argv.pull) {
709
+ const pullUrl = argv.pull;
710
+ let outputPath = ".env";
711
+ if (argv.e) {
712
+ outputPath = typeof argv.e === "string" ? argv.e : argv.e[0];
713
+ }
714
+ await handlePull(pullUrl, outputPath);
715
+ return;
716
+ }
717
+
718
+ // Xử lý lệnh push
719
+ if (argv.push) {
720
+ const pushUrl = argv.push;
721
+ let sourcePath = ".env";
722
+ if (argv.e) {
723
+ sourcePath = typeof argv.e === "string" ? argv.e : argv.e[0];
724
+ }
725
+ await handlePush(pushUrl, sourcePath);
726
+ return;
727
+ }
728
+
729
+ // Chạy main function
730
+ await main();
731
+ })().catch((err) => {
732
+ console.error("Fatal error:", err.message);
733
+ console.error(err.stack);
734
+ cleanupTempFiles();
735
+ process.exit(1);
736
+ });
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@tltdh61/dotenvrtdb",
3
+ "version": "1.260131.11434",
4
+ "description": "A CLI tool to load environment variables from .env files with remote database support",
5
+ "main": "cli.js",
6
+ "bin": {
7
+ "dotenvrtdb": "./cli.js"
8
+ },
9
+ "scripts": {
10
+ "publish-o861runners": "dotenv -e .env.dotenvrtdb -- npx --yes @o861runners/nmp publish --debug"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/tolaptrinhdh61-spec/dotenvrtdb.git"
15
+ },
16
+ "keywords": [
17
+ "dotenv",
18
+ "dotenvrtdb",
19
+ "env",
20
+ "environment",
21
+ "variables",
22
+ "config",
23
+ "cli",
24
+ "firebase",
25
+ "realtime-database"
26
+ ],
27
+ "author": "tolaptrinhdh61-spec",
28
+ "license": "MIT",
29
+ "bugs": {
30
+ "url": "https://github.com/tolaptrinhdh61-spec/dotenvrtdb/issues"
31
+ },
32
+ "homepage": "https://github.com/tolaptrinhdh61-spec/dotenvrtdb#readme",
33
+ "dependencies": {
34
+ "cross-spawn": "^7.0.3",
35
+ "dotenv": "^16.0.0",
36
+ "dotenv-expand": "^10.0.0",
37
+ "minimist": "^1.2.8"
38
+ },
39
+ "publishConfig": {
40
+ "registry": "https://registry.npmjs.org/"
41
+ }
42
+ }