@stacksjs/env 0.70.22 → 0.70.25
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 +417 -30
- package/dist/index.js +24 -9
- package/dist/src/cli.d.ts +53 -0
- package/dist/src/crypto.d.ts +13 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/parser.d.ts +16 -0
- package/dist/src/plugin.d.ts +24 -0
- package/dist/src/types.d.ts +142 -0
- package/dist/src/utils.d.ts +24 -0
- package/package.json +19 -12
- package/dist/index.d.ts +0 -11
- package/dist/types.d.ts +0 -97
- package/dist/utils.d.ts +0 -18
package/README.md
CHANGED
|
@@ -1,42 +1,438 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @stacksjs/env
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A secure .env file management package with built-in encryption support for Bun and Node.js.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
7
|
+
- 🔐 **Automatic Encryption/Decryption** - Secure your environment variables with public-key cryptography
|
|
8
|
+
- 🚀 **Bun Plugin** - Seamless integration with Bun's runtime
|
|
9
|
+
- 🔑 **secp256k1 ECIES** - Industry-standard elliptic curve encryption
|
|
10
|
+
- 📝 **Variable Expansion** - Support for `${VAR}`, defaults, and alternates
|
|
11
|
+
- 🔧 **Command Substitution** - Execute commands with `$(command)`
|
|
12
|
+
- 🎯 **Multi-Environment** - Manage multiple .env files for different environments
|
|
13
|
+
- 🛠️ **CLI Tools** - Full-featured CLI via buddy commands
|
|
14
|
+
- 🌍 **Environment Detection** - Native runtime, platform, and CI/CD detection utilities
|
|
9
15
|
|
|
10
|
-
##
|
|
16
|
+
## Installation
|
|
11
17
|
|
|
12
18
|
```bash
|
|
13
|
-
bun
|
|
19
|
+
bun add @stacksjs/env
|
|
14
20
|
```
|
|
15
21
|
|
|
16
|
-
|
|
22
|
+
## Quick Start
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
import { collect } from '@stacksjs/objects'
|
|
24
|
+
### 1. Auto-load .env files
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
pages: 176,
|
|
24
|
-
}, {
|
|
25
|
-
name: 'Fantastic Beasts and Where to Find Them',
|
|
26
|
-
pages: 1096,
|
|
27
|
-
}])
|
|
26
|
+
```typescript
|
|
27
|
+
import { autoLoadEnv } from '@stacksjs/env'
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
// Automatically loads .env files based on NODE_ENV or DOTENV_ENV
|
|
30
|
+
autoLoadEnv()
|
|
31
|
+
|
|
32
|
+
// Now use your environment variables
|
|
33
|
+
console.log(process.env.MY_SECRET)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. Programmatic Usage
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { loadEnv } from '@stacksjs/env'
|
|
40
|
+
|
|
41
|
+
// Load specific .env files
|
|
42
|
+
loadEnv({
|
|
43
|
+
path: ['.env.local', '.env'],
|
|
44
|
+
overload: false,
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 3. Bun Plugin
|
|
49
|
+
|
|
50
|
+
Add to your `bunfig.toml`:
|
|
51
|
+
|
|
52
|
+
```toml
|
|
53
|
+
preload = ["./storage/framework/core/env/plugin.ts"]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Or import in your preloader:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import '@stacksjs/env/plugin'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Encryption
|
|
63
|
+
|
|
64
|
+
### Encrypting .env Files
|
|
65
|
+
|
|
66
|
+
Use the buddy CLI to encrypt your environment variables:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Encrypt .env file
|
|
70
|
+
buddy env:encrypt
|
|
71
|
+
|
|
72
|
+
# Encrypt specific file
|
|
73
|
+
buddy env:encrypt --file .env.production
|
|
74
|
+
|
|
75
|
+
# Encrypt specific keys only
|
|
76
|
+
buddy env:encrypt -k "SECRET**"
|
|
77
|
+
|
|
78
|
+
# Exclude specific keys from encryption
|
|
79
|
+
buddy env:encrypt -ek "PUBLIC**"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This will:
|
|
83
|
+
|
|
84
|
+
1. Generate a public/private keypair
|
|
85
|
+
2. Store keys in `.env.keys` (keep this secure!)
|
|
86
|
+
3. Encrypt values in your .env file
|
|
87
|
+
4. Add `DOTENV_PUBLIC_KEY` to your .env file
|
|
88
|
+
|
|
89
|
+
### Decrypting .env Files
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Decrypt .env file
|
|
93
|
+
buddy env:decrypt
|
|
94
|
+
|
|
95
|
+
# Decrypt specific file
|
|
96
|
+
buddy env:decrypt --file .env.production
|
|
30
97
|
```
|
|
31
98
|
|
|
32
|
-
|
|
99
|
+
### How Encryption Works
|
|
100
|
+
|
|
101
|
+
The encryption uses **secp256k1 ECIES** (Elliptic Curve Integrated Encryption Scheme):
|
|
102
|
+
|
|
103
|
+
1. A keypair is generated using secp256k1 (same as Bitcoin)
|
|
104
|
+
2. Each value is encrypted with AES-256-GCM using an ephemeral key
|
|
105
|
+
3. The ephemeral key is encrypted with the public key
|
|
106
|
+
4. Only the private key can decrypt the values
|
|
107
|
+
|
|
108
|
+
**Example encrypted .env:**
|
|
109
|
+
|
|
110
|
+
```ini
|
|
111
|
+
# /-------------------[DOTENV_PUBLIC_KEY]--------------------/
|
|
112
|
+
# / public-key encryption for .env files /
|
|
113
|
+
# / [how it works](https://stacksjs.com/encryption) /
|
|
114
|
+
# /----------------------------------------------------------/
|
|
115
|
+
DOTENV_PUBLIC_KEY="034af93e93708b994c10f236c96ef88e47291066946cce2e8d98c9e02c741ced45"
|
|
116
|
+
|
|
117
|
+
# .env
|
|
118
|
+
API_KEY="encrypted:BDqDBibm4wsYqMpCjTQ6BsDHmMadg9K3dAt+Z9HPMfLEIRVz50hmLXPXRuDBXaJi..."
|
|
119
|
+
DB_PASSWORD="encrypted:AKx8Bh3m5xtZrNqDkUP7CuEInOcfg9L4eBy/2qt59vbSU0aN9WSmN..."
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## CLI Commands
|
|
123
|
+
|
|
124
|
+
All commands are available through the `buddy` CLI:
|
|
33
125
|
|
|
34
|
-
|
|
126
|
+
### Get Environment Variables
|
|
35
127
|
|
|
36
128
|
```bash
|
|
37
|
-
|
|
129
|
+
# Get a specific variable
|
|
130
|
+
buddy env:get API_KEY
|
|
131
|
+
|
|
132
|
+
# Get all variables as JSON
|
|
133
|
+
buddy env:get --all
|
|
134
|
+
|
|
135
|
+
# Get all variables in shell format
|
|
136
|
+
buddy env:get --all --format shell
|
|
137
|
+
|
|
138
|
+
# Pretty print JSON
|
|
139
|
+
buddy env:get --all --pretty
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Set Environment Variables
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Set a variable (encrypted by default)
|
|
146
|
+
buddy env:set API_KEY "my-secret-value"
|
|
147
|
+
|
|
148
|
+
# Set without encryption
|
|
149
|
+
buddy env:set PUBLIC_URL "https://example.com" --plain
|
|
150
|
+
|
|
151
|
+
# Set in specific file
|
|
152
|
+
buddy env:set API_KEY "value" --file .env.production
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Manage Keypairs
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
# View keypair
|
|
159
|
+
buddy env:keypair
|
|
160
|
+
|
|
161
|
+
# View keypair for specific environment
|
|
162
|
+
buddy env:keypair --file .env.production
|
|
163
|
+
|
|
164
|
+
# Get specific key
|
|
165
|
+
buddy env:keypair DOTENV_PRIVATE_KEY
|
|
166
|
+
|
|
167
|
+
# Output in shell format
|
|
168
|
+
buddy env:keypair --format shell
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Rotate Keys
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# Rotate keypair and re-encrypt all values
|
|
175
|
+
buddy env:rotate
|
|
176
|
+
|
|
177
|
+
# Rotate for specific environment
|
|
178
|
+
buddy env:rotate --file .env.production
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Variable Expansion
|
|
182
|
+
|
|
183
|
+
The env parser supports advanced variable expansion:
|
|
184
|
+
|
|
185
|
+
### Basic Expansion
|
|
186
|
+
|
|
187
|
+
```ini
|
|
188
|
+
USERNAME="john"
|
|
189
|
+
DATABASE_URL="postgres://${USERNAME}@localhost/mydb"
|
|
190
|
+
# Result: postgres://john@localhost/mydb
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Default Values
|
|
194
|
+
|
|
195
|
+
```ini
|
|
196
|
+
# Use default if unset or empty
|
|
197
|
+
DATABASE_HOST=${DB_HOST:-localhost}
|
|
198
|
+
DATABASE_PORT=${DB_PORT:-5432}
|
|
199
|
+
|
|
200
|
+
# Use default only if unset (empty is ok)
|
|
201
|
+
API_URL=${API_BASE_URL-https://api.example.com}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Alternate Values
|
|
205
|
+
|
|
206
|
+
```ini
|
|
207
|
+
NODE_ENV=production
|
|
208
|
+
|
|
209
|
+
# Use alternate if set and non-empty
|
|
210
|
+
DEBUG_MODE=${NODE_ENV:+false}
|
|
211
|
+
LOG_LEVEL=${NODE_ENV:+error}
|
|
212
|
+
|
|
213
|
+
# Use alternate if set (empty is ok)
|
|
214
|
+
CACHE_ENABLED=${NODE_ENV+true}
|
|
38
215
|
```
|
|
39
216
|
|
|
217
|
+
### Command Substitution
|
|
218
|
+
|
|
219
|
+
```ini
|
|
220
|
+
# Execute command and use output
|
|
221
|
+
CURRENT_USER=$(whoami)
|
|
222
|
+
BUILD_TIME=$(date +%s)
|
|
223
|
+
GIT_COMMIT=$(git rev-parse HEAD)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Environment Detection
|
|
227
|
+
|
|
228
|
+
The package includes native utilities to detect runtime, platform, and CI/CD environments:
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
import {
|
|
232
|
+
// Runtime detection
|
|
233
|
+
isBun,
|
|
234
|
+
isNode,
|
|
235
|
+
runtime,
|
|
236
|
+
runtimeInfo,
|
|
237
|
+
|
|
238
|
+
// Platform detection
|
|
239
|
+
platform,
|
|
240
|
+
isWindows,
|
|
241
|
+
isMacOS,
|
|
242
|
+
isLinux,
|
|
243
|
+
|
|
244
|
+
// Environment detection
|
|
245
|
+
hasTTY,
|
|
246
|
+
hasWindow,
|
|
247
|
+
isCI,
|
|
248
|
+
isDebug,
|
|
249
|
+
isMinimal,
|
|
250
|
+
isColorSupported,
|
|
251
|
+
|
|
252
|
+
// Provider detection
|
|
253
|
+
provider,
|
|
254
|
+
providerInfo,
|
|
255
|
+
} from '@stacksjs/env'
|
|
256
|
+
|
|
257
|
+
// Check runtime
|
|
258
|
+
console.log(runtime) // 'bun' | 'node' | 'unknown'
|
|
259
|
+
console.log(runtimeInfo) // { name: 'bun', version: '1.3.2' }
|
|
260
|
+
|
|
261
|
+
// Check platform
|
|
262
|
+
console.log(platform) // 'darwin' | 'linux' | 'win32' | etc.
|
|
263
|
+
console.log(isMacOS) // true/false
|
|
264
|
+
|
|
265
|
+
// Check CI environment
|
|
266
|
+
console.log(isCI) // true/false
|
|
267
|
+
console.log(provider) // 'github' | 'gitlab' | 'vercel' | etc.
|
|
268
|
+
console.log(providerInfo) // { name: 'GitHub Actions', detected: true }
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Supported CI/CD Providers
|
|
272
|
+
|
|
273
|
+
- GitHub Actions
|
|
274
|
+
- GitLab CI
|
|
275
|
+
- CircleCI
|
|
276
|
+
- Travis CI
|
|
277
|
+
- Jenkins
|
|
278
|
+
- Vercel
|
|
279
|
+
- Netlify
|
|
280
|
+
- Heroku
|
|
281
|
+
- AWS
|
|
282
|
+
- Azure
|
|
283
|
+
- Cloudflare Pages
|
|
284
|
+
- Railway
|
|
285
|
+
- Render
|
|
286
|
+
|
|
287
|
+
## Multi-Environment Support
|
|
288
|
+
|
|
289
|
+
Load different .env files based on environment:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# .env.local (highest priority)
|
|
293
|
+
# .env.development
|
|
294
|
+
# .env.production
|
|
295
|
+
# .env (lowest priority)
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
The loader will automatically detect `NODE_ENV` or `DOTENV_ENV` and load the appropriate files.
|
|
299
|
+
|
|
300
|
+
### Environment-Specific Keys
|
|
301
|
+
|
|
302
|
+
Keys are automatically namespaced by environment:
|
|
303
|
+
|
|
304
|
+
```ini
|
|
305
|
+
# .env.keys
|
|
306
|
+
DOTENV_PUBLIC_KEY="..."
|
|
307
|
+
DOTENV_PRIVATE_KEY="..."
|
|
308
|
+
|
|
309
|
+
DOTENV_PUBLIC_KEY_PRODUCTION="..."
|
|
310
|
+
DOTENV_PRIVATE_KEY_PRODUCTION="..."
|
|
311
|
+
|
|
312
|
+
DOTENV_PUBLIC_KEY_CI="..."
|
|
313
|
+
DOTENV_PRIVATE_KEY_CI="..."
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## Security Best Practices
|
|
317
|
+
|
|
318
|
+
1. **Never commit `.env.keys`** - Add to `.gitignore`
|
|
319
|
+
2. **Commit encrypted `.env` files** - They're safe to commit
|
|
320
|
+
3. **Store private keys securely** - Use your CI/CD secrets manager
|
|
321
|
+
4. **Rotate keys regularly** - Use `buddy env:rotate`
|
|
322
|
+
5. **Use environment-specific keys** - Different keys for dev/staging/prod
|
|
323
|
+
|
|
324
|
+
## API Reference
|
|
325
|
+
|
|
326
|
+
### `autoLoadEnv(options?)`
|
|
327
|
+
|
|
328
|
+
Automatically load .env files based on environment.
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
import { autoLoadEnv } from '@stacksjs/env'
|
|
332
|
+
|
|
333
|
+
autoLoadEnv({
|
|
334
|
+
env: 'production', // Override environment detection
|
|
335
|
+
overload: false, // Don't override existing vars
|
|
336
|
+
quiet: true, // Suppress output
|
|
337
|
+
cwd: '/path/to/project'
|
|
338
|
+
})
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### `loadEnv(options)`
|
|
342
|
+
|
|
343
|
+
Load specific .env files.
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
import { loadEnv } from '@stacksjs/env'
|
|
347
|
+
|
|
348
|
+
loadEnv({
|
|
349
|
+
path: ['.env.local', '.env'],
|
|
350
|
+
overload: false,
|
|
351
|
+
privateKey: 'your-private-key',
|
|
352
|
+
keysFile: '.env.keys',
|
|
353
|
+
quiet: false
|
|
354
|
+
})
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### `encryptEnv(options)`
|
|
358
|
+
|
|
359
|
+
Encrypt a .env file.
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
import { encryptEnv } from '@stacksjs/env'
|
|
363
|
+
|
|
364
|
+
const result = encryptEnv({
|
|
365
|
+
file: '.env',
|
|
366
|
+
keysFile: '.env.keys',
|
|
367
|
+
key: 'SECRET**', // Only encrypt keys matching pattern
|
|
368
|
+
excludeKey: 'PUBLIC**', // Exclude keys matching pattern
|
|
369
|
+
stdout: false
|
|
370
|
+
})
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### `decryptEnv(options)`
|
|
374
|
+
|
|
375
|
+
Decrypt a .env file.
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
import { decryptEnv } from '@stacksjs/env'
|
|
379
|
+
|
|
380
|
+
const result = decryptEnv({
|
|
381
|
+
file: '.env',
|
|
382
|
+
keysFile: '.env.keys',
|
|
383
|
+
stdout: false
|
|
384
|
+
})
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### `setEnv(key, value, options)`
|
|
388
|
+
|
|
389
|
+
Set an environment variable.
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
import { setEnv } from '@stacksjs/env'
|
|
393
|
+
|
|
394
|
+
setEnv('API_KEY', 'my-secret', {
|
|
395
|
+
file: '.env',
|
|
396
|
+
keysFile: '.env.keys',
|
|
397
|
+
plain: false // Encrypt by default
|
|
398
|
+
})
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### `getEnv(key?, options)`
|
|
402
|
+
|
|
403
|
+
Get environment variable(s).
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
import { getEnv } from '@stacksjs/env'
|
|
407
|
+
|
|
408
|
+
// Get single value
|
|
409
|
+
const result = getEnv('API_KEY', {
|
|
410
|
+
file: '.env',
|
|
411
|
+
keysFile: '.env.keys'
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
// Get all values
|
|
415
|
+
const result = getEnv(undefined, {
|
|
416
|
+
all: true,
|
|
417
|
+
format: 'json', // or 'shell' or 'eval'
|
|
418
|
+
prettyPrint: true
|
|
419
|
+
})
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Migration from dotenvx
|
|
423
|
+
|
|
424
|
+
This package replaces `@dotenvx/dotenvx` and `bun-plugin-dotenvx` with a native Bun implementation.
|
|
425
|
+
|
|
426
|
+
### Breaking Changes
|
|
427
|
+
|
|
428
|
+
None! The API is designed to be compatible with dotenvx.
|
|
429
|
+
|
|
430
|
+
### Migration Steps
|
|
431
|
+
|
|
432
|
+
1. Update your `bunfig.toml` preload
|
|
433
|
+
2. Update imports from `@dotenvx/dotenvx` to `@stacksjs/env`
|
|
434
|
+
3. buddy commands remain the same
|
|
435
|
+
|
|
40
436
|
## 📈 Changelog
|
|
41
437
|
|
|
42
438
|
Please see our [releases](https://github.com/stacksjs/stacks/releases) page for more information on what has changed recently.
|
|
@@ -55,15 +451,6 @@ For casual chit-chat with others using this package:
|
|
|
55
451
|
|
|
56
452
|
[Join the Stacks Discord Server](https://discord.gg/stacksjs)
|
|
57
453
|
|
|
58
|
-
## 🙏🏼 Credits
|
|
59
|
-
|
|
60
|
-
Many thanks to the following core technologies & people who have contributed to this package:
|
|
61
|
-
|
|
62
|
-
- [Collect.js](https://github.com/ecrmnn/collect.js)
|
|
63
|
-
- [Laravel](https://laravel.com/)
|
|
64
|
-
- [Chris Breuer](https://github.com/chrisbbreuer)
|
|
65
|
-
- [All Contributors](../../contributors)
|
|
66
|
-
|
|
67
454
|
## 📄 License
|
|
68
455
|
|
|
69
456
|
The MIT License (MIT). Please see [LICENSE](https://github.com/stacksjs/stacks/tree/main/LICENSE.md) for more information.
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
|
|
3
|
-
GFS4: `),console.error(q)};if(!B[F]){if(wq=global[F]||[],aq(B,wq),B.close=function(q){function n(E,y){return q.call(B,E,function(J){if(!J)rq();if(typeof y==="function")y.apply(this,arguments)})}return Object.defineProperty(n,Yq,{value:q}),n}(B.close),B.closeSync=function(q){function n(E){q.apply(B,arguments),rq()}return Object.defineProperty(n,Yq,{value:q}),n}(B.closeSync),/\bgfs4\b/i.test(process.env.NODE_DEBUG||""))process.on("exit",function(){e(B[F]),u("node:assert").equal(B[F].length,0)})}var wq;if(!global[F])aq(global,B[F]);Iq.exports=Bq(ny(B));if(process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH&&!B.__patched)Iq.exports=Bq(B),B.__patched=!0;function Bq(q){sE(q),q.gracefulify=Bq,q.createReadStream=gq,q.createWriteStream=bq;var n=q.readFile;q.readFile=E;function E(z,Z,Y){if(typeof Z==="function")Y=Z,Z=null;return M(z,Z,Y);function M(T,I,w,D){return n(T,I,function(H){if(H&&(H.code==="EMFILE"||H.code==="ENFILE"))s([M,[T,I,w],H,D||Date.now(),Date.now()]);else if(typeof w==="function")w.apply(this,arguments)})}}var y=q.writeFile;q.writeFile=J;function J(z,Z,Y,M){if(typeof Y==="function")M=Y,Y=null;return T(z,Z,Y,M);function T(I,w,D,H,k){return y(I,w,D,function(x){if(x&&(x.code==="EMFILE"||x.code==="ENFILE"))s([T,[I,w,D,H],x,k||Date.now(),Date.now()]);else if(typeof H==="function")H.apply(this,arguments)})}}var O=q.appendFile;if(O)q.appendFile=_;function _(z,Z,Y,M){if(typeof Y==="function")M=Y,Y=null;return T(z,Z,Y,M);function T(I,w,D,H,k){return O(I,w,D,function(x){if(x&&(x.code==="EMFILE"||x.code==="ENFILE"))s([T,[I,w,D,H],x,k||Date.now(),Date.now()]);else if(typeof H==="function")H.apply(this,arguments)})}}var U=q.copyFile;if(U)q.copyFile=j;function j(z,Z,Y,M){if(typeof Y==="function")M=Y,Y=0;return T(z,Z,Y,M);function T(I,w,D,H,k){return U(I,w,D,function(x){if(x&&(x.code==="EMFILE"||x.code==="ENFILE"))s([T,[I,w,D,H],x,k||Date.now(),Date.now()]);else if(typeof H==="function")H.apply(this,arguments)})}}var $=q.readdir;q.readdir=A;var V=/^v[0-5]\./;function A(z,Z,Y){if(typeof Z==="function")Y=Z,Z=null;var M=V.test(process.version)?function I(w,D,H,k){return $(w,T(w,D,H,k))}:function I(w,D,H,k){return $(w,D,T(w,D,H,k))};return M(z,Z,Y);function T(I,w,D,H){return function(k,x){if(k&&(k.code==="EMFILE"||k.code==="ENFILE"))s([M,[I,w,D],k,H||Date.now(),Date.now()]);else{if(x&&x.sort)x.sort();if(typeof D==="function")D.call(this,k,x)}}}}if(process.version.substr(0,4)==="v0.8"){var L=qy(q);R=L.ReadStream,m=L.WriteStream}var K=q.ReadStream;if(K)R.prototype=Object.create(K.prototype),R.prototype.open=h;var P=q.WriteStream;if(P)m.prototype=Object.create(P.prototype),m.prototype.open=Jq;Object.defineProperty(q,"ReadStream",{get:function(){return R},set:function(z){R=z},enumerable:!0,configurable:!0}),Object.defineProperty(q,"WriteStream",{get:function(){return m},set:function(z){m=z},enumerable:!0,configurable:!0});var N=R;Object.defineProperty(q,"FileReadStream",{get:function(){return N},set:function(z){N=z},enumerable:!0,configurable:!0});var C=m;Object.defineProperty(q,"FileWriteStream",{get:function(){return C},set:function(z){C=z},enumerable:!0,configurable:!0});function R(z,Z){if(this instanceof R)return K.apply(this,arguments),this;else return R.apply(Object.create(R.prototype),arguments)}function h(){var z=this;Rq(z.path,z.flags,z.mode,function(Z,Y){if(Z){if(z.autoClose)z.destroy();z.emit("error",Z)}else z.fd=Y,z.emit("open",Y),z.read()})}function m(z,Z){if(this instanceof m)return P.apply(this,arguments),this;else return m.apply(Object.create(m.prototype),arguments)}function Jq(){var z=this;Rq(z.path,z.flags,z.mode,function(Z,Y){if(Z)z.destroy(),z.emit("error",Z);else z.fd=Y,z.emit("open",Y)})}function gq(z,Z){return new q.ReadStream(z,Z)}function bq(z,Z){return new q.WriteStream(z,Z)}var SE=q.open;q.open=Rq;function Rq(z,Z,Y,M){if(typeof Y==="function")M=Y,Y=null;return T(z,Z,Y,M);function T(I,w,D,H,k){return SE(I,w,D,function(x,PO){if(x&&(x.code==="EMFILE"||x.code==="ENFILE"))s([T,[I,w,D,H],x,k||Date.now(),Date.now()]);else if(typeof H==="function")H.apply(this,arguments)})}}return q}function s(q){e("ENQUEUE",q[0].name,q[1]),B[F].push(q),Dq()}var Xq;function rq(){var q=Date.now();for(var n=0;n<B[F].length;++n)if(B[F][n].length>2)B[F][n][3]=q,B[F][n][4]=q;Dq()}function Dq(){if(clearTimeout(Xq),Xq=void 0,B[F].length===0)return;var q=B[F].shift(),n=q[0],E=q[1],y=q[2],J=q[3],O=q[4];if(J===void 0)e("RETRY",n.name,E),n.apply(null,E);else if(Date.now()-J>=60000){e("TIMEOUT",n.name,E);var _=E.pop();if(typeof _==="function")_.call(null,y)}else{var U=Date.now()-O,j=Math.max(O-J,1),$=Math.min(j*1.2,100);if(U>=$)e("RETRY",n.name,E),n.apply(null,E.concat([J]));else B[F].push(q)}if(Xq===void 0)Xq=setTimeout(Dq,0)}});var g=X((uq)=>{var eq=Q().fromCallback,S=qq(),yy=["access","appendFile","chmod","chown","close","copyFile","cp","fchmod","fchown","fdatasync","fstat","fsync","ftruncate","futimes","glob","lchmod","lchown","lutimes","link","lstat","mkdir","mkdtemp","open","opendir","readdir","readFile","readlink","realpath","rename","rm","rmdir","stat","statfs","symlink","truncate","unlink","utimes","writeFile"].filter((q)=>{return typeof S[q]==="function"});Object.assign(uq,S);yy.forEach((q)=>{uq[q]=eq(S[q])});uq.exists=function(q,n){if(typeof n==="function")return S.exists(q,n);return new Promise((E)=>{return S.exists(q,E)})};uq.read=function(q,n,E,y,J,O){if(typeof O==="function")return S.read(q,n,E,y,J,O);return new Promise((_,U)=>{S.read(q,n,E,y,J,(j,$,V)=>{if(j)return U(j);_({bytesRead:$,buffer:V})})})};uq.write=function(q,n,...E){if(typeof E[E.length-1]==="function")return S.write(q,n,...E);return new Promise((y,J)=>{S.write(q,n,...E,(O,_,U)=>{if(O)return J(O);y({bytesWritten:_,buffer:U})})})};uq.readv=function(q,n,...E){if(typeof E[E.length-1]==="function")return S.readv(q,n,...E);return new Promise((y,J)=>{S.readv(q,n,...E,(O,_,U)=>{if(O)return J(O);y({bytesRead:_,buffers:U})})})};uq.writev=function(q,n,...E){if(typeof E[E.length-1]==="function")return S.writev(q,n,...E);return new Promise((y,J)=>{S.writev(q,n,...E,(O,_,U)=>{if(O)return J(O);y({bytesWritten:_,buffers:U})})})};if(typeof S.realpath.native==="function")uq.realpath.native=eq(S.realpath.native);else process.emitWarning("fs.realpath.native is not a function. Is fs being monkey-patched?","Warning","fs-extra-WARN0003")});var sq=X((zy,tq)=>{var Uy=u("node:path");zy.checkPath=function q(n){if(process.platform==="win32"){if(/[<>:"|?*]/.test(n.replace(Uy.parse(n).root,""))){let y=new Error(`Path contains invalid characters: ${n}`);throw y.code="EINVAL",y}}}});var yn=X((Py,Qq)=>{var qn=g(),{checkPath:nn}=sq(),En=(q)=>{let n={mode:511};if(typeof q==="number")return q;return{...n,...q}.mode};Py.makeDir=async(q,n)=>{return nn(q),qn.mkdir(q,{mode:En(n),recursive:!0})};Py.makeDirSync=(q,n)=>{return nn(q),qn.mkdirSync(q,{mode:En(n),recursive:!0})}});var l=X((xO,Jn)=>{var Xy=Q().fromPromise,{makeDir:Yy,makeDirSync:Wq}=yn(),Mq=Xy(Yy);Jn.exports={mkdirs:Mq,mkdirsSync:Wq,mkdirp:Mq,mkdirpSync:Wq,ensureDir:Mq,ensureDirSync:Wq}});var d=X((jO,_n)=>{var Zy=Q().fromPromise,On=g();function Cy(q){return On.access(q).then(()=>!0).catch(()=>!1)}_n.exports={pathExists:Zy(Cy),pathExistsSync:On.existsSync}});var Tq=X((RO,Vn)=>{var nq=g(),Hy=Q().fromPromise;async function Ny(q,n,E){let y=await nq.open(q,"r+"),J=null;try{await nq.futimes(y,n,E)}finally{try{await nq.close(y)}catch(O){J=O}}if(J)throw J}function xy(q,n,E){let y=nq.openSync(q,"r+");return nq.futimesSync(y,n,E),nq.closeSync(y)}Vn.exports={utimesMillis:Hy(Ny),utimesMillisSync:xy}});var t=X((wO,Ln)=>{var Eq=g(),W=u("node:path"),Kn=Q().fromPromise;function jy(q,n,E){let y=E.dereference?(J)=>Eq.stat(J,{bigint:!0}):(J)=>Eq.lstat(J,{bigint:!0});return Promise.all([y(q),y(n).catch((J)=>{if(J.code==="ENOENT")return null;throw J})]).then(([J,O])=>({srcStat:J,destStat:O}))}function Ry(q,n,E){let y,J=E.dereference?(_)=>Eq.statSync(_,{bigint:!0}):(_)=>Eq.lstatSync(_,{bigint:!0}),O=J(q);try{y=J(n)}catch(_){if(_.code==="ENOENT")return{srcStat:O,destStat:null};throw _}return{srcStat:O,destStat:y}}async function wy(q,n,E,y){let{srcStat:J,destStat:O}=await jy(q,n,y);if(O){if(Oq(J,O)){let _=W.basename(q),U=W.basename(n);if(E==="move"&&_!==U&&_.toLowerCase()===U.toLowerCase())return{srcStat:J,destStat:O,isChangingCase:!0};throw new Error("Source and destination must not be the same.")}if(J.isDirectory()&&!O.isDirectory())throw new Error(`Cannot overwrite non-directory '${n}' with directory '${q}'.`);if(!J.isDirectory()&&O.isDirectory())throw new Error(`Cannot overwrite directory '${n}' with non-directory '${q}'.`)}if(J.isDirectory()&&kq(q,n))throw new Error(Zq(q,n,E));return{srcStat:J,destStat:O}}function By(q,n,E,y){let{srcStat:J,destStat:O}=Ry(q,n,y);if(O){if(Oq(J,O)){let _=W.basename(q),U=W.basename(n);if(E==="move"&&_!==U&&_.toLowerCase()===U.toLowerCase())return{srcStat:J,destStat:O,isChangingCase:!0};throw new Error("Source and destination must not be the same.")}if(J.isDirectory()&&!O.isDirectory())throw new Error(`Cannot overwrite non-directory '${n}' with directory '${q}'.`);if(!J.isDirectory()&&O.isDirectory())throw new Error(`Cannot overwrite directory '${n}' with non-directory '${q}'.`)}if(J.isDirectory()&&kq(q,n))throw new Error(Zq(q,n,E));return{srcStat:J,destStat:O}}async function Un(q,n,E,y){let J=W.resolve(W.dirname(q)),O=W.resolve(W.dirname(E));if(O===J||O===W.parse(O).root)return;let _;try{_=await Eq.stat(O,{bigint:!0})}catch(U){if(U.code==="ENOENT")return;throw U}if(Oq(n,_))throw new Error(Zq(q,E,y));return Un(q,n,O,y)}function zn(q,n,E,y){let J=W.resolve(W.dirname(q)),O=W.resolve(W.dirname(E));if(O===J||O===W.parse(O).root)return;let _;try{_=Eq.statSync(O,{bigint:!0})}catch(U){if(U.code==="ENOENT")return;throw U}if(Oq(n,_))throw new Error(Zq(q,E,y));return zn(q,n,O,y)}function Oq(q,n){return n.ino&&n.dev&&n.ino===q.ino&&n.dev===q.dev}function kq(q,n){let E=W.resolve(q).split(W.sep).filter((J)=>J),y=W.resolve(n).split(W.sep).filter((J)=>J);return E.every((J,O)=>y[O]===J)}function Zq(q,n,E){return`Cannot ${E} '${q}' to a subdirectory of itself, '${n}'.`}Ln.exports={checkPaths:Kn(wy),checkPathsSync:By,checkParentPaths:Kn(Un),checkParentPathsSync:zn,isSrcSubdir:kq,areIdentical:Oq}});var Yn=X((BO,Xn)=>{var v=g(),_q=u("node:path"),{mkdirs:Dy}=l(),{pathExists:Iy}=d(),{utimesMillis:uy}=Tq(),Vq=t();async function Qy(q,n,E={}){if(typeof E==="function")E={filter:E};if(E.clobber="clobber"in E?!!E.clobber:!0,E.overwrite="overwrite"in E?!!E.overwrite:E.clobber,E.preserveTimestamps&&process.arch==="ia32")process.emitWarning(`Using the preserveTimestamps option in 32-bit node is not recommended;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
import XJ from"process";import{projectPath as jJ}from"@stacksjs/path";import y from"fs";var K={APP_ENV:["local","dev","development","staging","prod","production"],DB_CONNECTION:["mysql","sqlite","postgres","dynamodb"],MAIL_MAILER:["smtp","mailgun","ses","postmark","sendmail","log","sendgrid","mailtrap"],SEARCH_ENGINE_DRIVER:["opensearch","meilisearch","algolia","typesense"],FRONTEND_APP_ENV:["development","staging","production"]};import{createCipheriv as p,createDecipheriv as i,createHash as f,randomBytes as m}from"crypto";function a(J,Q){let $=m(16),z=Q.length===32?Q:Buffer.from(Q.toString("hex").slice(0,64),"hex"),Z=p("aes-256-gcm",z,$),X=Buffer.concat([Z.update(J,"utf8"),Z.final()]),W=Z.getAuthTag();return{ciphertext:X.toString("hex"),iv:$.toString("hex"),authTag:W.toString("hex")}}function n(J,Q,$,z){let Z=Q.length===32?Q:Buffer.from(Q.toString("hex").slice(0,64),"hex");try{let X=i("aes-256-gcm",Z,Buffer.from($,"hex"));return X.setAuthTag(Buffer.from(z,"hex")),Buffer.concat([X.update(Buffer.from(J,"hex")),X.final()]).toString("utf8")}catch(X){throw Error(`Decryption failed (data may be corrupted or key is incorrect): ${X instanceof Error?X.message:String(X)}`)}}function b(){let J=m(32);return{publicKey:f("sha256").update(J).digest().toString("hex"),privateKey:J.toString("hex")}}function S(J,Q){let $=f("sha256").update(Buffer.from(Q,"hex")).digest(),{ciphertext:z,iv:Z,authTag:X}=a(J,$);return`encrypted:${Buffer.concat([Buffer.from(Z,"hex"),Buffer.from(X,"hex"),Buffer.from(z,"hex")]).toString("base64")}`}function x(J,Q){if(!J.startsWith("encrypted:"))return J;let $=J.slice(10),z=Buffer.from($,"base64");if(z.length<32)throw Error("Invalid encrypted data: payload too short (need at least iv + authTag = 32 bytes)");let Z=z.subarray(0,16).toString("hex"),X=z.subarray(16,32).toString("hex"),W=z.subarray(32).toString("hex"),H=f("sha256").update(Buffer.from(Q,"hex")).digest(),G=f("sha256").update(H).digest();return n(W,G,Z,X)}function AJ(J){let Q=J.match(/^DOTENV_PRIVATE_KEY_(.+)$/);return Q&&Q[1]?Q[1].toLowerCase():""}function h(J=""){if(!J)return process.env.DOTENV_PRIVATE_KEY;let Q=`DOTENV_PRIVATE_KEY_${J.toUpperCase()}`;return process.env[Q]}function C(J,Q={}){let $={},z=[],Z=J.split(`
|
|
3
|
+
`);for(let X of Z){let W=X.trim();if(!W||W.startsWith("#"))continue;if(W.startsWith("DOTENV_PUBLIC_KEY=")){let O=W.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(O&&O[1]!==void 0)$.DOTENV_PUBLIC_KEY=O[1];continue}let H=W.match(/^([^=]+)=(.*)$/);if(!H||H[1]===void 0||H[2]===void 0)continue;let G=H[1].trim(),j=H[2].trim();if(j.startsWith('"')&&j.endsWith('"')||j.startsWith("'")&&j.endsWith("'")){if(j=j.slice(1,-1),j.includes("\\n"))j=j.replace(/\\n/g,`
|
|
4
|
+
`)}if(Q.privateKey&&(j.startsWith("encrypted:")||j.startsWith("enc:")))try{let O=j.startsWith("enc:")?`encrypted:${j.slice(4)}`:j;j=x(O,Q.privateKey)}catch(O){z.push(`Failed to decrypt ${G}: ${O instanceof Error?O.message:"Unknown error"}`)}j=r(j,{...Q.processEnv||process.env,...$}),j=s(j),$[G]=j}return{parsed:$,errors:z}}function r(J,Q){return J.replace(/\$\{([^}]+)\}/g,($,z)=>{let Z=z.match(/^([^:\-+]+)(:-|-)(.+)$/);if(Z){let[,W,H,G]=Z,j=Q[W];if(H===":-")return j||G;else return j!==void 0?j:G}let X=z.match(/^([^:\-+]+)(:?\+)(.+)$/);if(X){let[,W,H,G]=X,j=Q[W];if(H===":+")return j?G:"";else return j!==void 0?G:""}return Q[z]||""})}var o=new Set(["date","hostname","whoami","uname","pwd","echo","printf","cat","basename","dirname"]);function s(J){return J.replace(/\$\(([^)]+)\)/g,(Q,$)=>{try{let z=$.trim().split(/\s+/),Z=z[0];if(!Z||!o.has(Z))return console.warn(`[env] Blocked command substitution for disallowed command: ${Z}`),"";let X=Bun.spawnSync(z,{stdout:"pipe",stderr:"pipe"});if(X.exitCode===0)return new TextDecoder().decode(X.stdout).trim();console.warn(`[env] Command substitution failed (exit code ${X.exitCode}): ${Z}`)}catch(z){console.warn(`[env] Command substitution error: ${z instanceof Error?z.message:String(z)}`)}return""})}async function _J(J,Q={}){let $={},z=[];for(let Z of J)try{let X=Bun.file(Z);if(!X.size)continue;let W=await X.text(),{parsed:H,errors:G}=C(W,Q);z.push(...G);for(let[j,O]of Object.entries(H)){if(!Q.overload&&$[j]!==void 0)continue;$[j]=O}}catch(X){if(X?.code==="ENOENT")continue;z.push(`Failed to read ${Z}: ${X instanceof Error?X.message:String(X)}`)}return{parsed:$,errors:z}}import{existsSync as F}from"fs";import{readFileSync as k}from"fs";import{resolve as V}from"path";function d(J={}){let{path:Q=[".env"],overload:$=!1,env:z,privateKey:Z,keysFile:X,quiet:W=!1,cwd:H=process.cwd()}=J,G=Array.isArray(Q)?Q:[Q],j=[],O=0,Y=Z;if(!Y&&X){let U=V(H,X);if(F(U))try{let A=k(U,"utf-8"),{parsed:B}=C(A);if(z){let M=`DOTENV_PRIVATE_KEY_${z.toUpperCase()}`;Y=B[M]}else Y=B.DOTENV_PRIVATE_KEY}catch(A){j.push(`Failed to load keys file: ${A instanceof Error?A.message:"Unknown error"}`)}}if(!Y)Y=h(z||"");for(let U of G){let A=V(H,U);if(!F(A)){if(!W)j.push(`File not found: ${A}`);continue}try{let B=k(A,"utf-8"),{parsed:M,errors:q}=C(B,{privateKey:Y,processEnv:process.env});j.push(...q);for(let[I,R]of Object.entries(M)){if(I==="DOTENV_PUBLIC_KEY")continue;if($||process.env[I]===void 0)process.env[I]=R,O++}if(!W&&!process.env.__ENV_LOADED__)console.log(`[env] loaded ${Object.keys(M).length} variables from ${U}`),process.env.__ENV_LOADED__="1"}catch(B){j.push(`Failed to load ${A}: ${B instanceof Error?B.message:"Unknown error"}`)}}return{loaded:O,errors:j}}function CJ(J={}){return{name:"env-plugin",setup(Q){d(J)}}}function LJ(J={}){let Q=J.env||"development",$=J.cwd||process.cwd(),z=[],Z=`.env.${Q}`,X=`.env.${Q}.local`;if(F(V($,X)))z.push(X);if(F(V($,Z)))z.push(Z);if(F(V($,".env.local")))z.push(".env.local");if(F(V($,".env")))z.push(".env");return d({...J,path:z,env:Q})}import{existsSync as g,readFileSync as L,writeFileSync as E}from"fs";import{resolve as T}from"path";function t(J={}){let Q=J.cwd||process.cwd(),$=T(Q,J.file||".env"),z=T(Q,J.keysFile||".env.keys");if(!g($))return{success:!1,error:`File not found: ${$}`};try{let Z,X;if(g(z)){let U=L(z,"utf-8"),{parsed:A}=C(U),q=((J.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),I=q?`DOTENV_PUBLIC_KEY_${q}`:"DOTENV_PUBLIC_KEY",R=q?`DOTENV_PRIVATE_KEY_${q}`:"DOTENV_PRIVATE_KEY";if(Z=A[I]||"",X=A[R]||"",!Z||!X){let D=b();Z=D.publicKey,X=D.privateKey;let w=`
|
|
5
|
+
${I}="${Z}"
|
|
6
|
+
${R}="${X}"
|
|
7
|
+
`;E(z,U+w,"utf-8")}}else{let U=b();Z=U.publicKey,X=U.privateKey;let M=((J.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),q=M?`DOTENV_PUBLIC_KEY_${M}`:"DOTENV_PUBLIC_KEY",I=M?`DOTENV_PRIVATE_KEY_${M}`:"DOTENV_PRIVATE_KEY",R=`# .env.keys - Keep this file secure and never commit to source control
|
|
8
|
+
${q}="${Z}"
|
|
9
|
+
${I}="${X}"
|
|
10
|
+
`;E(z,R,"utf-8")}let H=L($,"utf-8").split(`
|
|
11
|
+
`),G=[],j=J.file?J.file.replace(/^\.env\./,"").toUpperCase():"",O=j?`DOTENV_PUBLIC_KEY_${j}`:"DOTENV_PUBLIC_KEY";G.push("#/-------------------[DOTENV_PUBLIC_KEY]--------------------/"),G.push("#/ public-key encryption for .env files /"),G.push("#/ [how it works](https://stacksjs.com/encryption) /"),G.push("#/----------------------------------------------------------/"),G.push(`${O}="${Z}"`),G.push("");for(let U of H){let A=U.trim();if(!A||A.startsWith("#")){G.push(U);continue}if(A.startsWith("DOTENV_PUBLIC_KEY"))continue;let B=A.match(/^([^=]+)=(.*)$/);if(!B||B[1]===void 0||B[2]===void 0){G.push(U);continue}let M=B[1].trim(),q=B[2].trim();if(q.startsWith('"')&&q.endsWith('"')||q.startsWith("'")&&q.endsWith("'"))q=q.slice(1,-1);let I=!0;if(J.key&&!M.includes(J.key))I=!1;if(J.excludeKey&&M.includes(J.excludeKey))I=!1;if(q.startsWith("encrypted:"))I=!1;if(I)q=S(q,Z);G.push(`${M}="${q}"`)}let Y=G.join(`
|
|
12
|
+
`);if(J.stdout)return{success:!0,output:Y};return E($,Y,"utf-8"),{success:!0,output:`\u2714 encrypted (${J.file||".env"})
|
|
13
|
+
\u2714 key added to ${J.keysFile||".env.keys"}`}}catch(Z){return{success:!1,error:`Failed to encrypt: ${Z instanceof Error?Z.message:"Unknown error"}`}}}function e(J={}){let Q=J.cwd||process.cwd(),$=T(Q,J.file||".env"),z=T(Q,J.keysFile||".env.keys");if(!g($))return{success:!1,error:`File not found: ${$}`};if(!g(z))return{success:!1,error:`Keys file not found: ${z}`};try{let Z=L(z,"utf-8"),{parsed:X}=C(Z),G=((J.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),j=G?`DOTENV_PRIVATE_KEY_${G}`:"DOTENV_PRIVATE_KEY",O=X[j];if(!O)return{success:!1,error:`Private key not found: ${j}`};let U=L($,"utf-8").split(`
|
|
14
|
+
`),A=[];for(let M of U){let q=M.trim();if(!q||q.startsWith("#")){A.push(M);continue}if(q.startsWith("DOTENV_PUBLIC_KEY"))continue;let I=q.match(/^([^=]+)=(.*)$/);if(!I||I[1]===void 0||I[2]===void 0){A.push(M);continue}let R=I[1].trim(),D=I[2].trim();if(D.startsWith('"')&&D.endsWith('"')||D.startsWith("'")&&D.endsWith("'"))D=D.slice(1,-1);let w=D.startsWith("encrypted:");if(J.key&&!R.includes(J.key))w=!1;if(w)D=x(D,O);A.push(`${R}="${D}"`)}let B=A.join(`
|
|
15
|
+
`);if(J.stdout)return{success:!0,output:B};return E($,B,"utf-8"),{success:!0,output:`\u2714 decrypted (${J.file||".env"})`}}catch(Z){return{success:!1,error:`Failed to decrypt: ${Z instanceof Error?Z.message:"Unknown error"}`}}}function wJ(J,Q,$={}){let z=$.cwd||process.cwd(),Z=T(z,$.file||".env");try{let X="";if(g(Z))X=L(Z,"utf-8");let W=X.split(`
|
|
16
|
+
`),H=!1,G;for(let Y of W){let U=Y.trim();if(U.startsWith("DOTENV_PUBLIC_KEY=")){let A=U.match(/^DOTENV_PUBLIC_KEY=["']?([^"'\n]+)["']?/);if(A)G=A[1];break}}if(!$.plain&&!G){let Y=T(z,$.keysFile||".env.keys"),U=b();G=U.publicKey;let A="";if(g(Y))A=L(Y,"utf-8");let q=(($.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),I=q?`DOTENV_PUBLIC_KEY_${q}`:"DOTENV_PUBLIC_KEY",R=q?`DOTENV_PRIVATE_KEY_${q}`:"DOTENV_PRIVATE_KEY";A+=`
|
|
17
|
+
${I}="${U.publicKey}"
|
|
18
|
+
${R}="${U.privateKey}"
|
|
19
|
+
`,E(Y,A,"utf-8"),W.unshift(`${I}="${G}"`)}let j=Q;if(!$.plain&&G)j=S(Q,G);for(let Y=0;Y<W.length;Y++){let U=W[Y];if(U===void 0)continue;if(U.trim().startsWith(`${J}=`)){W[Y]=`${J}="${j}"`,H=!0;break}}if(!H)W.push(`${J}="${j}"`);let O=W.join(`
|
|
20
|
+
`);return E(Z,O,"utf-8"),{success:!0,output:`set ${J}${$.plain?"":" with encryption"} (${$.file||".env"})`}}catch(X){return{success:!1,error:`Failed to set: ${X instanceof Error?X.message:"Unknown error"}`}}}function fJ(J,Q={}){let $=Q.cwd||process.cwd(),z=T($,Q.file||".env");if(!g(z))return{success:!1,error:`File not found: ${z}`};try{let Z,X=T($,Q.keysFile||".env.keys");if(g(X)){let O=L(X,"utf-8"),{parsed:Y}=C(O),B=((Q.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),M=B?`DOTENV_PRIVATE_KEY_${B}`:"DOTENV_PRIVATE_KEY";Z=Y[M]}let W=L(z,"utf-8"),{parsed:H}=C(W,{privateKey:Z});if(J&&!Q.all){let O=H[J];if(O===void 0)return{success:!1,error:`Key not found: ${J}`};return{success:!0,output:O}}let G=Q.all?{...process.env,...H}:H,j;switch(Q.format){case"shell":j=Object.entries(G).map(([O,Y])=>`${O}=${Y}`).join(" ");break;case"eval":j=Object.entries(G).map(([O,Y])=>`${O}="${Y}"`).join(`
|
|
21
|
+
`);break;case"json":default:j=Q.prettyPrint?JSON.stringify(G,null,2):JSON.stringify(G);break}return{success:!0,output:j}}catch(Z){return{success:!1,error:`Failed to get: ${Z instanceof Error?Z.message:"Unknown error"}`}}}function xJ(J,Q={}){let $=Q.cwd||process.cwd(),z=T($,Q.keysFile||".env.keys");if(!g(z))return{success:!1,error:`Keys file not found: ${z}`};try{let Z=L(z,"utf-8"),{parsed:X}=C(Z),G=((Q.file||".env").split("/").pop()||"").replace(/^\.env\./,"").replace(/^\.env$/,"").toUpperCase(),j=G?`DOTENV_PUBLIC_KEY_${G}`:"DOTENV_PUBLIC_KEY",O=G?`DOTENV_PRIVATE_KEY_${G}`:"DOTENV_PRIVATE_KEY";if(J){let A=X[J];if(!A)return{success:!1,error:`Key not found: ${J}`};return{success:!0,output:A}}let Y={[j]:X[j],[O]:X[O]};return{success:!0,output:Q.format==="shell"?`${j}=${Y[j]} ${O}=${Y[O]}`:JSON.stringify(Y)}}catch(Z){return{success:!1,error:`Failed to get keypair: ${Z instanceof Error?Z.message:"Unknown error"}`}}}function SJ(J={}){let Q=e({file:J.file,keysFile:J.keysFile,stdout:!0,cwd:J.cwd});if(!Q.success)return Q;let $=J.cwd||process.cwd(),z=T($,J.file||".env"),Z=T($,J.keysFile||".env.keys");if(Q.output)E(z,Q.output,"utf-8");let X=b(),H=L(Z,"utf-8").split(`
|
|
22
|
+
`),G=J.file?J.file.replace(/^\.env\./,"").toUpperCase():"",j=G?`DOTENV_PUBLIC_KEY_${G}`:"DOTENV_PUBLIC_KEY",O=G?`DOTENV_PRIVATE_KEY_${G}`:"DOTENV_PRIVATE_KEY";for(let Y=0;Y<H.length;Y++){let U=H[Y];if(U===void 0)continue;if(U.startsWith(`${j}=`))H[Y]=`${j}="${X.publicKey}"`;else if(U.startsWith(`${O}=`))H[Y]=`${O}="${X.privateKey}"`}return E(Z,H.join(`
|
|
23
|
+
`),"utf-8"),t({file:J.file,keysFile:J.keysFile,key:J.key,excludeKey:J.excludeKey,stdout:J.stdout,cwd:J.cwd})}import _ from"process";import{platform as JJ}from"os";var u=typeof Bun<"u",c=typeof _<"u"&&_.versions?.node!==void 0,QJ=u?"bun":c?"node":"unknown",mJ={name:QJ,version:u?Bun.version:c?_.version:void 0},P=JJ(),hJ=P==="win32",kJ=P==="darwin",dJ=P==="linux",v=Boolean(_.stdout?.isTTY),uJ=typeof globalThis.window<"u",ZJ=Boolean(_.env.CI||_.env.CONTINUOUS_INTEGRATION||_.env.BUILD_NUMBER||_.env.RUN_ID),cJ=Boolean(_.env.DEBUG||_.env.VERBOSE||_.argv.includes("--debug")||_.argv.includes("--verbose")),$J=ZJ||!v,vJ=Boolean(!$J&&(v||_.env.COLORTERM||_.env.FORCE_COLOR||_.env.TERM&&_.env.TERM!=="dumb")),N={github:{name:"GitHub Actions",detected:Boolean(_.env.GITHUB_ACTIONS)},gitlab:{name:"GitLab CI",detected:Boolean(_.env.GITLAB_CI)},circle:{name:"CircleCI",detected:Boolean(_.env.CIRCLECI)},travis:{name:"Travis CI",detected:Boolean(_.env.TRAVIS)},jenkins:{name:"Jenkins",detected:Boolean(_.env.JENKINS_URL)},vercel:{name:"Vercel",detected:Boolean(_.env.VERCEL)},netlify:{name:"Netlify",detected:Boolean(_.env.NETLIFY)},heroku:{name:"Heroku",detected:Boolean(_.env.DYNO)},aws:{name:"AWS",detected:Boolean(_.env.AWS_REGION||_.env.AWS_LAMBDA_FUNCTION_NAME)},azure:{name:"Azure",detected:Boolean(_.env.AZURE_HTTP_USER_AGENT)},cloudflare:{name:"Cloudflare",detected:Boolean(_.env.CF_PAGES)},railway:{name:"Railway",detected:Boolean(_.env.RAILWAY_ENVIRONMENT)},render:{name:"Render",detected:Boolean(_.env.RENDER)}},zJ=Object.keys(N).find((J)=>{let Q=N[J];return Q!==void 0&&Q.detected})||"unknown",yJ=N[zJ]||{name:"Unknown",detected:!1};var GJ={get:(J,Q)=>{let $=J[Q],z=["_PORT","_TIMEOUT","_TTL","_SIZE","_LIMIT","_MAX","_MIN","_INTERVAL","_RETRIES","_CONCURRENCY","_WORKERS","_CONNECTIONS"];if(typeof $==="string"&&/^\d+$/.test($)&&!$.startsWith("0")&&z.some((Z)=>Q.endsWith(Z)))return Number($);if(typeof $==="string"){let Z=$.toLowerCase();if(Z==="true")return!0;if(Z==="false")return!1}return $}};function WJ(){return typeof Bun<"u"?Bun.env:XJ.env}var l=new Proxy(WJ(),GJ);function rJ(J,Q,$){let z=$?.path||jJ(".env"),X=y.readFileSync(z,"utf-8").split(`
|
|
24
|
+
`),W=X.findIndex((j)=>j.startsWith(`${J}=`)),G=/[\s"'#$\\]/.test(Q)?`"${Q.replace(/"/g,"\\\"")}"`:Q;if(W!==-1)X[W]=`${J}=${G}`;else X.push(`${J}=${G}`);y.writeFileSync(z,X.join(`
|
|
25
|
+
`))}function oJ(J=l){let Q=[];for(let[$,z]of Object.entries(K)){let Z=J[$];if(Z!==void 0&&Z!==""&&!z.includes(String(Z)))Q.push(`${$}="${Z}" is not valid. Allowed values: ${z.join(", ")}`)}return Q}function sJ(J,Q=l){let $=[];for(let z of J){let Z=Q[z];if(Z===void 0||Z===""||Z===null)$.push(z)}if($.length>0)throw Error(`[env] Missing required environment variable(s): ${$.join(", ")}. Set them in .env or your process environment before booting.`);return Q}export{rJ as writeEnv,oJ as validateEnv,wJ as setEnv,mJ as runtimeInfo,QJ as runtime,SJ as rotateKeypair,sJ as requireEnv,yJ as providerInfo,zJ as provider,WJ as process,P as platform,AJ as parseEnvFromKey,C as parse,_J as loadEnvFiles,d as loadEnv,hJ as isWindows,c as isNode,$J as isMinimal,kJ as isMacOS,dJ as isLinux,cJ as isDebug,vJ as isColorSupported,ZJ as isCI,u as isBun,uJ as hasWindow,v as hasTTY,h as getPrivateKey,xJ as getKeypair,fJ as getEnv,b as generateKeypair,CJ as envPlugin,K as envEnum,l as env,S as encryptValue,t as encryptEnv,x as decryptValue,e as decryptEnv,LJ as autoLoadEnv,a as aesEncrypt,n as aesDecrypt};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encrypt .env file
|
|
3
|
+
*/
|
|
4
|
+
export declare function encryptEnv(options?: EncryptOptions): { success: boolean, output?: string, error?: string };
|
|
5
|
+
/**
|
|
6
|
+
* Decrypt .env file
|
|
7
|
+
*/
|
|
8
|
+
export declare function decryptEnv(options?: DecryptOptions): { success: boolean, output?: string, error?: string };
|
|
9
|
+
/**
|
|
10
|
+
* Set an environment variable
|
|
11
|
+
*/
|
|
12
|
+
export declare function setEnv(key: string, value: string, options?: SetOptions): { success: boolean, output?: string, error?: string };
|
|
13
|
+
/**
|
|
14
|
+
* Get environment variable(s)
|
|
15
|
+
*/
|
|
16
|
+
export declare function getEnv(key?: string, options?: GetOptions): { success: boolean, output?: string, error?: string };
|
|
17
|
+
/**
|
|
18
|
+
* Get keypair for .env file
|
|
19
|
+
*/
|
|
20
|
+
export declare function getKeypair(keyName?: string, options?: { file?: string, keysFile?: string, format?: 'json' | 'shell', cwd?: string }): { success: boolean, output?: string, error?: string };
|
|
21
|
+
/**
|
|
22
|
+
* Rotate keypair and re-encrypt all values
|
|
23
|
+
*/
|
|
24
|
+
export declare function rotateKeypair(options?: { file?: string, keysFile?: string, key?: string, excludeKey?: string, stdout?: boolean, cwd?: string }): { success: boolean, output?: string, error?: string };
|
|
25
|
+
export declare interface EncryptOptions {
|
|
26
|
+
file?: string
|
|
27
|
+
keysFile?: string
|
|
28
|
+
key?: string
|
|
29
|
+
excludeKey?: string
|
|
30
|
+
stdout?: boolean
|
|
31
|
+
cwd?: string
|
|
32
|
+
}
|
|
33
|
+
export declare interface DecryptOptions {
|
|
34
|
+
file?: string
|
|
35
|
+
keysFile?: string
|
|
36
|
+
key?: string
|
|
37
|
+
stdout?: boolean
|
|
38
|
+
cwd?: string
|
|
39
|
+
}
|
|
40
|
+
export declare interface SetOptions {
|
|
41
|
+
file?: string
|
|
42
|
+
keysFile?: string
|
|
43
|
+
plain?: boolean
|
|
44
|
+
cwd?: string
|
|
45
|
+
}
|
|
46
|
+
export declare interface GetOptions {
|
|
47
|
+
file?: string
|
|
48
|
+
keysFile?: string
|
|
49
|
+
all?: boolean
|
|
50
|
+
format?: 'json' | 'shell' | 'eval'
|
|
51
|
+
prettyPrint?: boolean
|
|
52
|
+
cwd?: string
|
|
53
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// AES-256-GCM encryption using Node.js crypto
|
|
2
|
+
export declare function aesEncrypt(plaintext: string, key: Buffer): { ciphertext: string, iv: string, authTag: string };
|
|
3
|
+
export declare function aesDecrypt(ciphertext: string, key: Buffer, iv: string, authTag: string): string;
|
|
4
|
+
// secp256k1 key generation
|
|
5
|
+
export declare function generateKeypair(): { publicKey: string, privateKey: string };
|
|
6
|
+
// Encrypt a value using public key
|
|
7
|
+
export declare function encryptValue(value: string, publicKey: string): string;
|
|
8
|
+
// Decrypt a value using private key
|
|
9
|
+
export declare function decryptValue(encryptedValue: string, privateKey: string): string;
|
|
10
|
+
// Parse environment name from private key variable
|
|
11
|
+
export declare function parseEnvFromKey(keyName: string): string;
|
|
12
|
+
// Get appropriate private key for environment
|
|
13
|
+
export declare function getPrivateKey(env?: string): string | undefined;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { EnvKey } from '../../../env';
|
|
2
|
+
import type { StacksEnv } from './types';
|
|
3
|
+
export declare function process(): StacksEnv;
|
|
4
|
+
// eslint-disable-next-line pickier/no-unused-vars
|
|
5
|
+
export declare function writeEnv(key: EnvKey, value: string, options?: { path: string }): void;
|
|
6
|
+
export declare const env: StacksEnv;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a .env file content
|
|
3
|
+
*/
|
|
4
|
+
export declare function parse(src: string, options?: ParseOptions): ParseResult;
|
|
5
|
+
/**
|
|
6
|
+
* Load and parse multiple .env files
|
|
7
|
+
*/
|
|
8
|
+
export declare function loadEnvFiles(files: string[], options?: ParseOptions & { overload?: boolean }): Promise<ParseResult>;
|
|
9
|
+
export declare interface ParseOptions {
|
|
10
|
+
privateKey?: string
|
|
11
|
+
processEnv?: Record<string, string>
|
|
12
|
+
}
|
|
13
|
+
export declare interface ParseResult {
|
|
14
|
+
parsed: Record<string, string>
|
|
15
|
+
errors: string[]
|
|
16
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BunPlugin } from 'bun';
|
|
2
|
+
/**
|
|
3
|
+
* Load .env files and inject into process.env
|
|
4
|
+
*/
|
|
5
|
+
export declare function loadEnv(options?: EnvPluginOptions): { loaded: number, errors: string[] };
|
|
6
|
+
/**
|
|
7
|
+
* Bun plugin for automatic .env loading
|
|
8
|
+
*/
|
|
9
|
+
export declare function envPlugin(options?: EnvPluginOptions): BunPlugin;
|
|
10
|
+
/**
|
|
11
|
+
* Auto-detect and load .env files based on environment
|
|
12
|
+
*/
|
|
13
|
+
export declare function autoLoadEnv(options?: Omit<EnvPluginOptions, 'path'>): { loaded: number, errors: string[] };
|
|
14
|
+
export declare interface EnvPluginOptions {
|
|
15
|
+
path?: string | string[]
|
|
16
|
+
overload?: boolean
|
|
17
|
+
env?: string
|
|
18
|
+
privateKey?: string
|
|
19
|
+
keysFile?: string
|
|
20
|
+
quiet?: boolean
|
|
21
|
+
cwd?: string
|
|
22
|
+
}
|
|
23
|
+
// Default export for easy usage
|
|
24
|
+
export default envPlugin;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { BooleanValidatorType, EnumValidatorType, NumberValidatorType, StringValidatorType } from '@stacksjs/ts-validation';
|
|
2
|
+
import type { EnvKey } from '../../../env';
|
|
3
|
+
export declare const envEnum: EnumObject;
|
|
4
|
+
declare interface EnumObject {
|
|
5
|
+
[key: string]: string[]
|
|
6
|
+
}
|
|
7
|
+
declare interface StringEnvConfig {
|
|
8
|
+
validation: StringValidatorType
|
|
9
|
+
default: string
|
|
10
|
+
}
|
|
11
|
+
declare interface NumberEnvConfig {
|
|
12
|
+
validation: NumberValidatorType
|
|
13
|
+
default: number
|
|
14
|
+
}
|
|
15
|
+
declare interface BooleanEnvConfig {
|
|
16
|
+
validation: BooleanValidatorType
|
|
17
|
+
default: boolean
|
|
18
|
+
}
|
|
19
|
+
declare interface EnumEnvConfig {
|
|
20
|
+
validation: EnumValidatorType
|
|
21
|
+
default: string
|
|
22
|
+
}
|
|
23
|
+
export declare interface StacksEnv {
|
|
24
|
+
APP_NAME: string | undefined
|
|
25
|
+
APP_ENV: 'local' | 'dev' | 'stage' | 'prod' | undefined
|
|
26
|
+
APP_KEY: string | undefined
|
|
27
|
+
APP_URL: string | undefined
|
|
28
|
+
APP_DOMAIN: string | undefined
|
|
29
|
+
APP_MAINTENANCE: boolean | undefined
|
|
30
|
+
APP_MAINTENANCE_SECRET: string | undefined
|
|
31
|
+
APP_COMING_SOON: boolean | undefined
|
|
32
|
+
APP_COMING_SOON_SECRET: string | undefined
|
|
33
|
+
APP_ROOT: string | undefined
|
|
34
|
+
DEBUG: boolean | undefined
|
|
35
|
+
PORT: number | undefined
|
|
36
|
+
PORT_BACKEND: number | undefined
|
|
37
|
+
PORT_ADMIN: number | undefined
|
|
38
|
+
PORT_LIBRARY: number | undefined
|
|
39
|
+
PORT_DESKTOP: number | undefined
|
|
40
|
+
PORT_EMAIL: number | undefined
|
|
41
|
+
PORT_DOCS: number | undefined
|
|
42
|
+
PORT_INSPECT: number | undefined
|
|
43
|
+
PORT_API: number | undefined
|
|
44
|
+
PORT_SYSTEM_TRAY: number | undefined
|
|
45
|
+
API_PREFIX: string | undefined
|
|
46
|
+
DOCS_PREFIX: string | undefined
|
|
47
|
+
DB_CONNECTION: 'mysql' | 'sqlite' | 'postgres' | undefined
|
|
48
|
+
DB_HOST: string | undefined
|
|
49
|
+
DB_PORT: number | undefined
|
|
50
|
+
DB_DATABASE: string | undefined
|
|
51
|
+
DB_DATABASE_PATH: string | undefined
|
|
52
|
+
DB_USERNAME: string | undefined
|
|
53
|
+
DB_PASSWORD: string | undefined
|
|
54
|
+
DB_PREFIX: string | undefined
|
|
55
|
+
DB_SCHEMA: string | undefined
|
|
56
|
+
DB_QUERY_LOGGING_ENABLED: boolean | undefined
|
|
57
|
+
DB_QUERY_LOGGING_SLOW_THRESHOLD: number | undefined
|
|
58
|
+
DB_QUERY_LOGGING_RETENTION_DAYS: number | undefined
|
|
59
|
+
DB_QUERY_LOGGING_PRUNE_FREQUENCY: number | undefined
|
|
60
|
+
DB_QUERY_LOGGING_ANALYSIS_ENABLED: boolean | undefined
|
|
61
|
+
DB_QUERY_LOGGING_ANALYZE_ALL: boolean | undefined
|
|
62
|
+
DB_QUERY_LOGGING_EXPLAIN_PLAN: boolean | undefined
|
|
63
|
+
DB_QUERY_LOGGING_SUGGESTIONS: boolean | undefined
|
|
64
|
+
DATABASE_URL: string | undefined
|
|
65
|
+
AWS_ACCOUNT_ID: string | undefined
|
|
66
|
+
AWS_ACCESS_KEY_ID: string | undefined
|
|
67
|
+
AWS_SECRET_ACCESS_KEY: string | undefined
|
|
68
|
+
AWS_DEFAULT_REGION: string | undefined
|
|
69
|
+
AWS_DEFAULT_PASSWORD: string | undefined
|
|
70
|
+
AWS_REGION: string | undefined
|
|
71
|
+
AWS_HOSTED_ZONE_ID: string | undefined
|
|
72
|
+
AWS_S3_BUCKET: string | undefined
|
|
73
|
+
AWS_S3_PREFIX: string | undefined
|
|
74
|
+
AWS_SES_REGION: string | undefined
|
|
75
|
+
MAIL_MAILER: string | undefined
|
|
76
|
+
MAIL_HOST: string | undefined
|
|
77
|
+
MAIL_PORT: number | undefined
|
|
78
|
+
MAIL_USERNAME: string | undefined
|
|
79
|
+
MAIL_PASSWORD: string | undefined
|
|
80
|
+
MAIL_ENCRYPTION: string | undefined
|
|
81
|
+
MAIL_FROM_NAME: string | undefined
|
|
82
|
+
MAIL_FROM_ADDRESS: string | undefined
|
|
83
|
+
MAIL_DOMAIN: string | undefined
|
|
84
|
+
MAIL_DRIVER: string | undefined
|
|
85
|
+
MAIL_SERVER_MODE: string | undefined
|
|
86
|
+
MAIL_SERVER_PATH: string | undefined
|
|
87
|
+
SEARCH_ENGINE_DRIVER: string | undefined
|
|
88
|
+
MEILISEARCH_HOST: string | undefined
|
|
89
|
+
MEILISEARCH_KEY: string | undefined
|
|
90
|
+
STRIPE_SECRET_KEY: string | undefined
|
|
91
|
+
STRIPE_PUBLISHABLE_KEY: string | undefined
|
|
92
|
+
FRONTEND_APP_ENV: 'development' | 'staging' | 'production' | undefined
|
|
93
|
+
FRONTEND_APP_URL: string | undefined
|
|
94
|
+
REALTIME_MODE: string | undefined
|
|
95
|
+
BROADCAST_DRIVER: string | undefined
|
|
96
|
+
BROADCAST_HOST: string | undefined
|
|
97
|
+
BROADCAST_PORT: number | undefined
|
|
98
|
+
BROADCAST_SCHEME: string | undefined
|
|
99
|
+
BROADCAST_APP_ID: string | undefined
|
|
100
|
+
BROADCAST_APP_KEY: string | undefined
|
|
101
|
+
BROADCAST_APP_SECRET: string | undefined
|
|
102
|
+
BROADCAST_CORS_ORIGIN: string | undefined
|
|
103
|
+
BROADCAST_DEBUG: boolean | undefined
|
|
104
|
+
BROADCAST_REDIS_ENABLED: boolean | undefined
|
|
105
|
+
BROADCAST_REDIS_PREFIX: string | undefined
|
|
106
|
+
BROADCAST_RATE_LIMIT_ENABLED: boolean | undefined
|
|
107
|
+
BROADCAST_METRICS_ENABLED: boolean | undefined
|
|
108
|
+
REDIS_HOST: string | undefined
|
|
109
|
+
REDIS_PORT: number | undefined
|
|
110
|
+
REDIS_PASSWORD: string | undefined
|
|
111
|
+
PUSHER_APP_ID: string | undefined
|
|
112
|
+
PUSHER_APP_KEY: string | undefined
|
|
113
|
+
PUSHER_APP_SECRET: string | undefined
|
|
114
|
+
PUSHER_APP_CLUSTER: string | undefined
|
|
115
|
+
PUSHER_APP_USE_TLS: boolean | undefined
|
|
116
|
+
SSL_DOMAINS: string | undefined
|
|
117
|
+
LETSENCRYPT_EMAIL: string | undefined
|
|
118
|
+
CONNECT_INSTANCE_ALIAS: string | undefined
|
|
119
|
+
PHONE_NOTIFY_EMAIL: string | undefined
|
|
120
|
+
PHONE_FORWARD_NUMBER: string | undefined
|
|
121
|
+
STORAGE_DRIVER: string | undefined
|
|
122
|
+
STORAGE_ROOT: string | undefined
|
|
123
|
+
STORAGE_PUBLIC_URL: string | undefined
|
|
124
|
+
QUEUE_DRIVER: string | undefined
|
|
125
|
+
AUTH_USERNAME_FIELD: string | undefined
|
|
126
|
+
AUTH_PASSWORD_FIELD: string | undefined
|
|
127
|
+
AUTH_TOKEN_EXPIRY: number | undefined
|
|
128
|
+
AUTH_TOKEN_ROTATION: number | undefined
|
|
129
|
+
AUTH_PASSWORD_RESET_EXPIRE: number | undefined
|
|
130
|
+
AUTH_PASSWORD_RESET_THROTTLE: number | undefined
|
|
131
|
+
[key: string]: string | number | boolean | undefined
|
|
132
|
+
}
|
|
133
|
+
export declare interface FrontendEnv {
|
|
134
|
+
FRONTEND_APP_ENV: 'local' | 'development' | 'staging' | 'production'
|
|
135
|
+
FRONTEND_APP_URL: string
|
|
136
|
+
}
|
|
137
|
+
declare type EnvValueConfig = StringEnvConfig | NumberEnvConfig | BooleanEnvConfig | EnumEnvConfig;
|
|
138
|
+
export type EnvConfig = Partial<Record<EnvKey, EnvValueConfig>>;
|
|
139
|
+
/** @deprecated Use `StacksEnv` instead */
|
|
140
|
+
export type Env = StacksEnv;
|
|
141
|
+
export type EnvSchema = any;
|
|
142
|
+
export type FrontendEnvKeys = keyof FrontendEnv;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Runtime detection
|
|
2
|
+
export declare const isBun: boolean;
|
|
3
|
+
export declare const isNode: boolean;
|
|
4
|
+
export declare const runtime: 'bun' | 'node' | 'unknown';
|
|
5
|
+
export declare const runtimeInfo: { name: 'bun' | 'node' | 'unknown', version: string | undefined };
|
|
6
|
+
// Platform detection
|
|
7
|
+
export declare const platform: NodeJS.Platform;
|
|
8
|
+
export declare const isWindows: boolean;
|
|
9
|
+
export declare const isMacOS: boolean;
|
|
10
|
+
export declare const isLinux: boolean;
|
|
11
|
+
// TTY detection
|
|
12
|
+
export declare const hasTTY: boolean;
|
|
13
|
+
// Window detection (browser environment)
|
|
14
|
+
export declare const hasWindow: boolean;
|
|
15
|
+
// CI detection
|
|
16
|
+
export declare const isCI: boolean;
|
|
17
|
+
// Debug mode detection
|
|
18
|
+
export declare const isDebug: boolean;
|
|
19
|
+
// Minimal mode detection (CI or non-interactive)
|
|
20
|
+
export declare const isMinimal: boolean;
|
|
21
|
+
// Color support detection
|
|
22
|
+
export declare const isColorSupported: boolean;
|
|
23
|
+
export declare const provider: string;
|
|
24
|
+
export declare const providerInfo: { name: string, detected: boolean };
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/env",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.70.
|
|
4
|
+
"version": "0.70.25",
|
|
5
5
|
"description": "Stacks env helper methods.",
|
|
6
6
|
"author": "Chris Breuer",
|
|
7
|
-
"contributors": [
|
|
7
|
+
"contributors": [
|
|
8
|
+
"Chris Breuer <chris@stacksjs.com>"
|
|
9
|
+
],
|
|
8
10
|
"license": "MIT",
|
|
9
11
|
"funding": "https://github.com/sponsors/chrisbbreuer",
|
|
10
12
|
"homepage": "https://github.com/stacksjs/stacks/tree/main/storage/framework/core/env#readme",
|
|
@@ -16,9 +18,16 @@
|
|
|
16
18
|
"bugs": {
|
|
17
19
|
"url": "https://github.com/stacksjs/stacks/issues"
|
|
18
20
|
},
|
|
19
|
-
"keywords": [
|
|
21
|
+
"keywords": [
|
|
22
|
+
"env",
|
|
23
|
+
"utilities",
|
|
24
|
+
"functions",
|
|
25
|
+
"stacks"
|
|
26
|
+
],
|
|
20
27
|
"exports": {
|
|
21
28
|
".": {
|
|
29
|
+
"bun": "./src/index.ts",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
22
31
|
"import": "./dist/index.js"
|
|
23
32
|
},
|
|
24
33
|
"./*": {
|
|
@@ -26,20 +35,18 @@
|
|
|
26
35
|
"import": "./dist/*"
|
|
27
36
|
}
|
|
28
37
|
},
|
|
29
|
-
"files": [
|
|
38
|
+
"files": [
|
|
39
|
+
"README.md",
|
|
40
|
+
"dist"
|
|
41
|
+
],
|
|
30
42
|
"scripts": {
|
|
31
43
|
"build": "bun build.ts",
|
|
32
44
|
"typecheck": "bun tsc --noEmit",
|
|
33
45
|
"prepublishOnly": "bun run build"
|
|
34
46
|
},
|
|
35
|
-
"dependencies": {
|
|
36
|
-
"@dotenvx/dotenvx": "^1.39.0"
|
|
37
|
-
},
|
|
38
47
|
"devDependencies": {
|
|
39
|
-
"
|
|
40
|
-
"@stacksjs/path": "0.70.
|
|
41
|
-
"@stacksjs/validation": "0.70.
|
|
42
|
-
"fs-extra": "^11.3.0",
|
|
43
|
-
"std-env": "^3.8.1"
|
|
48
|
+
"better-dx": "^0.2.12",
|
|
49
|
+
"@stacksjs/path": "0.70.23",
|
|
50
|
+
"@stacksjs/validation": "0.70.23"
|
|
44
51
|
}
|
|
45
52
|
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { Env } from './types';
|
|
2
|
-
import type { EnvKey } from '../../../env';
|
|
3
|
-
|
|
4
|
-
declare const handler: {
|
|
5
|
-
get: (target: Env, key: EnvKey) => unknown
|
|
6
|
-
};
|
|
7
|
-
export declare function process(): Env;
|
|
8
|
-
export declare const env: Env;
|
|
9
|
-
export declare function writeEnv(key: EnvKey, value: string, options?: { path: , string }): void;
|
|
10
|
-
|
|
11
|
-
export * from './types'
|
package/dist/types.d.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import type { EnvKey } from '../../../env';
|
|
2
|
-
import type { Infer, VineBoolean, VineEnum, VineNumber, VineString } from '@stacksjs/validation';
|
|
3
|
-
import type { SchemaTypes } from '@vinejs/vine/types';
|
|
4
|
-
|
|
5
|
-
declare interface EnumObject {
|
|
6
|
-
[key: string]: string[]
|
|
7
|
-
}
|
|
8
|
-
export declare const envEnum: EnumObject;
|
|
9
|
-
declare interface StringEnvConfig {
|
|
10
|
-
validation: VineString
|
|
11
|
-
default: string
|
|
12
|
-
}
|
|
13
|
-
declare interface NumberEnvConfig {
|
|
14
|
-
validation: VineNumber
|
|
15
|
-
default: number
|
|
16
|
-
}
|
|
17
|
-
declare interface BooleanEnvConfig {
|
|
18
|
-
validation: VineBoolean
|
|
19
|
-
default: boolean
|
|
20
|
-
}
|
|
21
|
-
declare interface EnumEnvConfig {
|
|
22
|
-
validation: VineEnum<any>
|
|
23
|
-
default: string
|
|
24
|
-
}
|
|
25
|
-
declare type EnvValueConfig = StringEnvConfig | NumberEnvConfig | BooleanEnvConfig | EnumEnvConfig
|
|
26
|
-
|
|
27
|
-
export type EnvConfig = Partial<Record<EnvKey, EnvValueConfig>>
|
|
28
|
-
|
|
29
|
-
type EnvMap = Record<string, SchemaTypes>
|
|
30
|
-
|
|
31
|
-
const envStructure: EnvMap = Object.entries(env).reduce((acc, [key, value]) => {
|
|
32
|
-
if (typeof value === 'object' && value !== null && 'validation' in value) {
|
|
33
|
-
acc[key] = (value as EnvValueConfig).validation
|
|
34
|
-
return acc
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
let validatorType: SchemaTypes
|
|
38
|
-
switch (typeof value) {
|
|
39
|
-
case 'string':
|
|
40
|
-
validatorType = schema.string()
|
|
41
|
-
break
|
|
42
|
-
case 'number':
|
|
43
|
-
validatorType = schema.number()
|
|
44
|
-
break
|
|
45
|
-
case 'boolean':
|
|
46
|
-
validatorType = schema.boolean()
|
|
47
|
-
break
|
|
48
|
-
default:
|
|
49
|
-
if (Array.isArray(value)) {
|
|
50
|
-
validatorType = schema.enum(value as string[])
|
|
51
|
-
break
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (typeof value === 'object' && value !== null) {
|
|
55
|
-
const schemaNameSymbol = Symbol.for('schema_name')
|
|
56
|
-
const schemaName = (value as { [key: symbol]: string })[schemaNameSymbol]
|
|
57
|
-
|
|
58
|
-
if (schemaName === 'vine.string') {
|
|
59
|
-
validatorType = schema.string()
|
|
60
|
-
break
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (schemaName === 'vine.number') {
|
|
64
|
-
validatorType = schema.number()
|
|
65
|
-
break
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (schemaName === 'vine.boolean') {
|
|
69
|
-
validatorType = schema.boolean()
|
|
70
|
-
break
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (!schemaName && key in envEnum) {
|
|
74
|
-
validatorType = schema.enum(envEnum[key as keyof typeof envEnum])
|
|
75
|
-
break
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
console.error('Unknown env value type', typeof value)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
throw new Error(`Invalid env value for ${key}`)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
acc[key] = validatorType
|
|
85
|
-
return acc
|
|
86
|
-
}, {} as EnvMap)
|
|
87
|
-
|
|
88
|
-
export const envSchema: ReturnType<typeof schema.object> = schema.object(envStructure)
|
|
89
|
-
export type Env = Infer<typeof envSchema>
|
|
90
|
-
|
|
91
|
-
export type EnvOptions = Env
|
|
92
|
-
|
|
93
|
-
export interface FrontendEnv {
|
|
94
|
-
FRONTEND_APP_ENV: 'local' | 'development' | 'staging' | 'production'
|
|
95
|
-
FRONTEND_APP_URL: string
|
|
96
|
-
}
|
|
97
|
-
export type FrontendEnvKeys = keyof FrontendEnv
|
package/dist/utils.d.ts
DELETED