@adriangalilea/utils 0.0.10

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 (110) hide show
  1. package/README.md +99 -0
  2. package/dist/browser.d.ts +14 -0
  3. package/dist/browser.d.ts.map +1 -0
  4. package/dist/browser.js +18 -0
  5. package/dist/browser.js.map +1 -0
  6. package/dist/currency/crypto-symbols-data.d.ts +10 -0
  7. package/dist/currency/crypto-symbols-data.d.ts.map +1 -0
  8. package/dist/currency/crypto-symbols-data.js +13765 -0
  9. package/dist/currency/crypto-symbols-data.js.map +1 -0
  10. package/dist/currency/crypto-symbols.d.ts +20 -0
  11. package/dist/currency/crypto-symbols.d.ts.map +1 -0
  12. package/dist/currency/crypto-symbols.js +23 -0
  13. package/dist/currency/crypto-symbols.js.map +1 -0
  14. package/dist/currency/download-crypto-list.d.ts +10 -0
  15. package/dist/currency/download-crypto-list.d.ts.map +1 -0
  16. package/dist/currency/download-crypto-list.js +69 -0
  17. package/dist/currency/download-crypto-list.js.map +1 -0
  18. package/dist/currency/index.d.ts +84 -0
  19. package/dist/currency/index.d.ts.map +1 -0
  20. package/dist/currency/index.js +230 -0
  21. package/dist/currency/index.js.map +1 -0
  22. package/dist/dir.d.ts +40 -0
  23. package/dist/dir.d.ts.map +1 -0
  24. package/dist/dir.js +108 -0
  25. package/dist/dir.js.map +1 -0
  26. package/dist/file.d.ts +53 -0
  27. package/dist/file.d.ts.map +1 -0
  28. package/dist/file.js +211 -0
  29. package/dist/file.js.map +1 -0
  30. package/dist/format.d.ts +40 -0
  31. package/dist/format.d.ts.map +1 -0
  32. package/dist/format.js +83 -0
  33. package/dist/format.js.map +1 -0
  34. package/dist/index.d.ts +16 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +20 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/kev.d.ts +149 -0
  39. package/dist/kev.d.ts.map +1 -0
  40. package/dist/kev.js +761 -0
  41. package/dist/kev.js.map +1 -0
  42. package/dist/log.d.ts +91 -0
  43. package/dist/log.d.ts.map +1 -0
  44. package/dist/log.js +300 -0
  45. package/dist/log.js.map +1 -0
  46. package/dist/logger.d.ts +91 -0
  47. package/dist/logger.d.ts.map +1 -0
  48. package/dist/logger.js +269 -0
  49. package/dist/logger.js.map +1 -0
  50. package/dist/offensive.d.ts +73 -0
  51. package/dist/offensive.d.ts.map +1 -0
  52. package/dist/offensive.js +103 -0
  53. package/dist/offensive.js.map +1 -0
  54. package/dist/path.d.ts +67 -0
  55. package/dist/path.d.ts.map +1 -0
  56. package/dist/path.js +107 -0
  57. package/dist/path.js.map +1 -0
  58. package/dist/platform/dir.d.ts +40 -0
  59. package/dist/platform/dir.d.ts.map +1 -0
  60. package/dist/platform/dir.js +108 -0
  61. package/dist/platform/dir.js.map +1 -0
  62. package/dist/platform/file.d.ts +53 -0
  63. package/dist/platform/file.d.ts.map +1 -0
  64. package/dist/platform/file.js +211 -0
  65. package/dist/platform/file.js.map +1 -0
  66. package/dist/platform/kev.d.ts +149 -0
  67. package/dist/platform/kev.d.ts.map +1 -0
  68. package/dist/platform/kev.js +762 -0
  69. package/dist/platform/kev.js.map +1 -0
  70. package/dist/platform/path.d.ts +67 -0
  71. package/dist/platform/path.d.ts.map +1 -0
  72. package/dist/platform/path.js +108 -0
  73. package/dist/platform/path.js.map +1 -0
  74. package/dist/platform/project.d.ts +35 -0
  75. package/dist/platform/project.d.ts.map +1 -0
  76. package/dist/platform/project.js +155 -0
  77. package/dist/platform/project.js.map +1 -0
  78. package/dist/project.d.ts +35 -0
  79. package/dist/project.d.ts.map +1 -0
  80. package/dist/project.js +154 -0
  81. package/dist/project.js.map +1 -0
  82. package/dist/runtime.d.ts +65 -0
  83. package/dist/runtime.d.ts.map +1 -0
  84. package/dist/runtime.js +193 -0
  85. package/dist/runtime.js.map +1 -0
  86. package/dist/universal/currency/crypto-symbols-data.d.ts +10 -0
  87. package/dist/universal/currency/crypto-symbols-data.d.ts.map +1 -0
  88. package/dist/universal/currency/crypto-symbols-data.js +13765 -0
  89. package/dist/universal/currency/crypto-symbols-data.js.map +1 -0
  90. package/dist/universal/currency/crypto-symbols.d.ts +20 -0
  91. package/dist/universal/currency/crypto-symbols.d.ts.map +1 -0
  92. package/dist/universal/currency/crypto-symbols.js +23 -0
  93. package/dist/universal/currency/crypto-symbols.js.map +1 -0
  94. package/dist/universal/currency/download-crypto-list.d.ts +10 -0
  95. package/dist/universal/currency/download-crypto-list.d.ts.map +1 -0
  96. package/dist/universal/currency/download-crypto-list.js +69 -0
  97. package/dist/universal/currency/download-crypto-list.js.map +1 -0
  98. package/dist/universal/currency/index.d.ts +90 -0
  99. package/dist/universal/currency/index.d.ts.map +1 -0
  100. package/dist/universal/currency/index.js +276 -0
  101. package/dist/universal/currency/index.js.map +1 -0
  102. package/dist/universal/format.d.ts +40 -0
  103. package/dist/universal/format.d.ts.map +1 -0
  104. package/dist/universal/format.js +83 -0
  105. package/dist/universal/format.js.map +1 -0
  106. package/dist/universal/log.d.ts +91 -0
  107. package/dist/universal/log.d.ts.map +1 -0
  108. package/dist/universal/log.js +309 -0
  109. package/dist/universal/log.js.map +1 -0
  110. package/package.json +75 -0
package/dist/kev.js ADDED
@@ -0,0 +1,761 @@
1
+ /**
2
+ * KEV - A Redis-style KV store for environment variables
3
+ *
4
+ * DEFAULT USAGE (no namespaces needed!):
5
+ * apiKey = KEV.mustGet("API_KEY") // Panics if not found (required config)
6
+ * apiKey = KEV.get("API_KEY") // Returns "" if not found
7
+ * apiKey = KEV.get("API_KEY", "dev") // Returns "dev" if not found
8
+ * port = KEV.int("PORT", 8080) // With type conversion
9
+ * KEV.set("DEBUG", "true") // Sets in memory (fast)
10
+ *
11
+ * KEV.get("DATABASE_URL") // memory → process.env → .env → cache result
12
+ * KEV.get("DATABASE_URL") // memory (cached!) ✓
13
+ *
14
+ * CUSTOMIZE THE SEARCH ORDER:
15
+ * KEV.source.remove("os") // Ignore OS env (perfect for tests!)
16
+ * KEV.source.add(".env.local") // Add more fallbacks
17
+ * KEV.source.set(".env.test") // Or replace entirely
18
+ *
19
+ * REDIS-STYLE NAMESPACING (when you need control):
20
+ * KEV.get("os:PATH") // ONLY from OS, no fallback
21
+ * KEV.get(".env:API_KEY") // ONLY from .env file
22
+ * KEV.set("os:DEBUG", "true") // Write directly to OS
23
+ * KEV.set(".env:API_KEY", "secret") // Update .env file
24
+ *
25
+ * // Pattern matching
26
+ * KEV.keys("API_*") // Find all API_ keys
27
+ * KEV.all("os:*") // Get all OS vars
28
+ * KEV.clear("TEMP_*") // Clean up temp vars
29
+ *
30
+ * SOURCE TRACKING & OBSERVABILITY:
31
+ * const [value, source] = KEV.getWithSource("API_KEY") // Returns value + where it came from
32
+ * source = KEV.sourceOf("API_KEY") // "/path/to/project/.env"
33
+ * KEV.debug = true // Shows lookup chain
34
+ * KEV.export("backup.env") // Includes # from: comments
35
+ */
36
+ import { panic } from './offensive.js';
37
+ import { file } from './file.js';
38
+ import { path } from './path.js';
39
+ import { findProjectRoot, findMonorepoRoot } from './project.js';
40
+ class SourceOps {
41
+ kev;
42
+ constructor(kev) {
43
+ this.kev = kev;
44
+ }
45
+ /**
46
+ * Replace all sources
47
+ */
48
+ set(...sources) {
49
+ this.kev.sources = sources;
50
+ }
51
+ /**
52
+ * Add sources to the search list
53
+ */
54
+ add(...sources) {
55
+ this.kev.sources.push(...sources);
56
+ }
57
+ /**
58
+ * Remove specific sources
59
+ */
60
+ remove(...sources) {
61
+ this.kev.sources = this.kev.sources.filter(s => !sources.includes(s));
62
+ }
63
+ /**
64
+ * List current sources
65
+ */
66
+ list() {
67
+ return [...this.kev.sources];
68
+ }
69
+ /**
70
+ * Clear all sources
71
+ */
72
+ clear() {
73
+ this.kev.sources = [];
74
+ }
75
+ }
76
+ export class KevOps {
77
+ memory = new Map();
78
+ sources = ['os', '.env'];
79
+ source;
80
+ debug = false;
81
+ constructor() {
82
+ this.source = new SourceOps(this);
83
+ this.initializeSmartDefaults();
84
+ }
85
+ initializeSmartDefaults() {
86
+ // Check for monorepo root first (turborepo)
87
+ const monorepoRoot = findMonorepoRoot();
88
+ if (monorepoRoot) {
89
+ const monorepoEnv = path.join(monorepoRoot, '.env');
90
+ this.sources.push(monorepoEnv);
91
+ if (this.debug) {
92
+ console.log('KEV: Auto-discovered monorepo root:', monorepoRoot);
93
+ console.log('KEV: Added monorepo .env to sources:', monorepoEnv);
94
+ }
95
+ }
96
+ // Then check for project root
97
+ const projectRoot = findProjectRoot();
98
+ if (projectRoot) {
99
+ const projectEnv = path.join(projectRoot, '.env');
100
+ // Only add if it's different from monorepo env
101
+ if (!monorepoRoot || projectEnv !== path.join(monorepoRoot, '.env')) {
102
+ this.sources.push(projectEnv);
103
+ if (this.debug) {
104
+ console.log('KEV: Auto-discovered project root:', projectRoot);
105
+ console.log('KEV: Added project .env to sources:', projectEnv);
106
+ }
107
+ }
108
+ }
109
+ if (this.debug) {
110
+ if (!monorepoRoot && !projectRoot) {
111
+ console.log('KEV: No project or monorepo root found, using standard sources:', this.sources);
112
+ }
113
+ else {
114
+ console.log('KEV: Default sources:', this.sources);
115
+ }
116
+ }
117
+ }
118
+ parseKey(key) {
119
+ if (key.startsWith(':')) {
120
+ panic('invalid key format - starts with colon:', key);
121
+ }
122
+ if (key.includes('::')) {
123
+ panic('invalid key format - double colon:', key);
124
+ }
125
+ const parts = key.split(':', 2);
126
+ if (parts.length === 2) {
127
+ if (parts[0] === '' || parts[1] === '') {
128
+ panic('invalid key format - empty namespace or key:', key);
129
+ }
130
+ return [parts[0], parts[1]];
131
+ }
132
+ return ['', key];
133
+ }
134
+ /**
135
+ * Get environment variable with optional default.
136
+ */
137
+ get(key, defaultValue) {
138
+ const [namespace, realKey] = this.parseKey(key);
139
+ const debug = this.debug && key !== 'LOG_LEVEL';
140
+ if (debug) {
141
+ console.log(`KEV: Looking for ${key}`);
142
+ }
143
+ // Namespaced - direct access
144
+ if (namespace) {
145
+ const val = this.getFromNamespace(namespace, realKey);
146
+ if (val !== '') {
147
+ if (debug) {
148
+ console.log(` ✓ ${namespace}: found ${val}`);
149
+ }
150
+ return val;
151
+ }
152
+ if (debug) {
153
+ console.log(` ✗ ${namespace}: not found`);
154
+ }
155
+ if (defaultValue !== undefined) {
156
+ if (debug) {
157
+ console.log(` → using default: ${defaultValue}`);
158
+ }
159
+ return defaultValue;
160
+ }
161
+ return '';
162
+ }
163
+ // Unnamespaced - check memory first
164
+ const entry = this.memory.get(realKey);
165
+ if (entry) {
166
+ if (debug) {
167
+ console.log(` ✓ memory: ${entry.value} (from ${entry.source})`);
168
+ }
169
+ return entry.value;
170
+ }
171
+ if (debug) {
172
+ console.log(' ✗ memory: not found');
173
+ }
174
+ // Search through sources
175
+ for (const source of this.sources) {
176
+ const val = this.getFromNamespace(source, realKey);
177
+ if (val !== '') {
178
+ if (debug) {
179
+ console.log(` ✓ ${source}: found ${val} (caching)`);
180
+ }
181
+ // Cache in memory for next time with source info
182
+ const absoluteSource = source !== 'os' && source !== 'default' && source !== 'set'
183
+ ? path.absolute(source)
184
+ : source;
185
+ this.memory.set(realKey, { value: val, source: absoluteSource });
186
+ return val;
187
+ }
188
+ if (debug) {
189
+ console.log(` ✗ ${source}: not found`);
190
+ }
191
+ }
192
+ // Use default and cache it
193
+ if (defaultValue !== undefined) {
194
+ if (debug) {
195
+ console.log(` → using default: ${defaultValue} (caching)`);
196
+ }
197
+ this.memory.set(realKey, { value: defaultValue, source: 'default' });
198
+ return defaultValue;
199
+ }
200
+ if (debug) {
201
+ console.log(' → not found, returning empty');
202
+ }
203
+ return '';
204
+ }
205
+ /**
206
+ * Get environment variable or panic if not found
207
+ */
208
+ mustGet(key) {
209
+ const val = this.get(key);
210
+ if (val === '') {
211
+ panic('required key not found:', key);
212
+ }
213
+ return val;
214
+ }
215
+ /**
216
+ * Get where a cached key came from
217
+ */
218
+ sourceOf(key) {
219
+ const entry = this.memory.get(key);
220
+ return entry ? entry.source : '';
221
+ }
222
+ /**
223
+ * Get both value and its source
224
+ */
225
+ getWithSource(key, defaultValue) {
226
+ const value = this.get(key, defaultValue);
227
+ if (value !== '') {
228
+ let source = this.sourceOf(key);
229
+ // If not in cache but has value, it might be a namespaced get
230
+ if (!source) {
231
+ const [namespace] = this.parseKey(key);
232
+ if (namespace) {
233
+ source = namespace;
234
+ }
235
+ }
236
+ return [value, source];
237
+ }
238
+ return ['', ''];
239
+ }
240
+ getFromNamespace(namespace, key) {
241
+ switch (namespace) {
242
+ case 'os':
243
+ return process.env[key] || '';
244
+ default:
245
+ // File namespace (.env, .env.local, etc)
246
+ if (namespace.startsWith('.') || namespace.includes('/')) {
247
+ return this.getFromFile(namespace, key);
248
+ }
249
+ }
250
+ return '';
251
+ }
252
+ getFromFile(filePath, key) {
253
+ if (!file.exists(filePath)) {
254
+ return '';
255
+ }
256
+ try {
257
+ const content = file.readText(filePath);
258
+ const lines = content.split('\n');
259
+ for (const line of lines) {
260
+ const trimmed = line.trim();
261
+ if (trimmed === '' || trimmed.startsWith('#')) {
262
+ continue;
263
+ }
264
+ const parts = trimmed.split('=', 2);
265
+ if (parts.length === 2) {
266
+ const fileKey = parts[0].trim();
267
+ if (fileKey === key) {
268
+ let value = parts[1].trim();
269
+ // Remove quotes if present
270
+ if ((value.startsWith('"') && value.endsWith('"')) ||
271
+ (value.startsWith("'") && value.endsWith("'"))) {
272
+ value = value.slice(1, -1);
273
+ }
274
+ return value;
275
+ }
276
+ }
277
+ }
278
+ }
279
+ catch {
280
+ // File read error
281
+ }
282
+ return '';
283
+ }
284
+ /**
285
+ * Set environment variable
286
+ */
287
+ set(key, value) {
288
+ const [namespace, realKey] = this.parseKey(key);
289
+ // Namespaced - direct write
290
+ if (namespace) {
291
+ this.setToNamespace(namespace, realKey, value);
292
+ return;
293
+ }
294
+ // Unnamespaced - memory only
295
+ this.memory.set(realKey, { value, source: 'set' });
296
+ }
297
+ setToNamespace(namespace, key, value) {
298
+ switch (namespace) {
299
+ case 'os':
300
+ process.env[key] = value;
301
+ break;
302
+ default:
303
+ // File namespace - update or append to file
304
+ if (namespace.startsWith('.') || namespace.includes('/')) {
305
+ this.setToFile(namespace, key, value);
306
+ }
307
+ }
308
+ }
309
+ setToFile(path, key, value) {
310
+ const lines = [];
311
+ let found = false;
312
+ if (file.exists(path)) {
313
+ const content = file.readText(path);
314
+ const existingLines = content.split('\n');
315
+ for (const line of existingLines) {
316
+ const trimmed = line.trim();
317
+ // Keep empty lines and comments
318
+ if (trimmed === '' || trimmed.startsWith('#')) {
319
+ lines.push(line);
320
+ continue;
321
+ }
322
+ // Check if this is the key we're updating
323
+ const parts = trimmed.split('=', 2);
324
+ if (parts.length >= 1) {
325
+ const fileKey = parts[0].trim();
326
+ if (fileKey === key) {
327
+ // Update existing key
328
+ const quotedValue = value.includes(' ') || value.includes('\t') || value.includes('\n')
329
+ ? `"${value}"`
330
+ : value;
331
+ lines.push(`${key}=${quotedValue}`);
332
+ found = true;
333
+ }
334
+ else {
335
+ lines.push(line);
336
+ }
337
+ }
338
+ else {
339
+ lines.push(line);
340
+ }
341
+ }
342
+ }
343
+ // If key wasn't found, append it
344
+ if (!found) {
345
+ const quotedValue = value.includes(' ') || value.includes('\t') || value.includes('\n')
346
+ ? `"${value}"`
347
+ : value;
348
+ lines.push(`${key}=${quotedValue}`);
349
+ }
350
+ // Write back
351
+ file.write(path, lines.join('\n'));
352
+ }
353
+ /**
354
+ * Check if key exists
355
+ */
356
+ has(key) {
357
+ const [namespace, realKey] = this.parseKey(key);
358
+ // Namespaced - check directly
359
+ if (namespace) {
360
+ return this.hasInNamespace(namespace, realKey);
361
+ }
362
+ // Unnamespaced - check memory then sources
363
+ if (this.memory.has(realKey)) {
364
+ return true;
365
+ }
366
+ // Check sources
367
+ for (const source of this.sources) {
368
+ if (this.hasInNamespace(source, realKey)) {
369
+ return true;
370
+ }
371
+ }
372
+ return false;
373
+ }
374
+ hasInNamespace(namespace, key) {
375
+ switch (namespace) {
376
+ case 'os':
377
+ return key in process.env;
378
+ default:
379
+ // File namespace
380
+ if (namespace.startsWith('.') || namespace.includes('/')) {
381
+ return this.getFromFile(namespace, key) !== '';
382
+ }
383
+ }
384
+ return false;
385
+ }
386
+ /**
387
+ * Get all keys matching pattern
388
+ */
389
+ keys(pattern = '*') {
390
+ const seen = new Set();
391
+ const result = [];
392
+ const patterns = Array.isArray(pattern) ? pattern : [pattern];
393
+ for (const pat of patterns) {
394
+ const [namespace, keyPattern] = this.parseKey(pat);
395
+ if (namespace) {
396
+ // Namespaced pattern
397
+ const nsKeys = this.keysFromNamespace(namespace, keyPattern);
398
+ for (const key of nsKeys) {
399
+ const fullKey = `${namespace}:${key}`;
400
+ if (!seen.has(fullKey)) {
401
+ result.push(fullKey);
402
+ seen.add(fullKey);
403
+ }
404
+ }
405
+ }
406
+ else {
407
+ // Unnamespaced - get from memory and sources
408
+ for (const [key] of this.memory) {
409
+ if (this.matchPattern(key, keyPattern) && !seen.has(key)) {
410
+ result.push(key);
411
+ seen.add(key);
412
+ }
413
+ }
414
+ // Also check sources
415
+ for (const source of this.sources) {
416
+ const nsKeys = this.keysFromNamespace(source, keyPattern);
417
+ for (const key of nsKeys) {
418
+ if (!seen.has(key)) {
419
+ result.push(key);
420
+ seen.add(key);
421
+ }
422
+ }
423
+ }
424
+ }
425
+ }
426
+ return result;
427
+ }
428
+ keysFromNamespace(namespace, pattern) {
429
+ const data = this.getNamespaceData(namespace, pattern, true);
430
+ return Object.keys(data);
431
+ }
432
+ getNamespaceData(namespace, pattern, keysOnly) {
433
+ const result = {};
434
+ switch (namespace) {
435
+ case 'os':
436
+ for (const [key, value] of Object.entries(process.env)) {
437
+ if (this.matchPattern(key, pattern)) {
438
+ result[key] = keysOnly ? '' : value || '';
439
+ }
440
+ }
441
+ break;
442
+ default:
443
+ // File namespace
444
+ if (namespace.startsWith('.') || namespace.includes('/')) {
445
+ this.parseEnvFile(namespace, pattern, result, keysOnly);
446
+ }
447
+ }
448
+ return result;
449
+ }
450
+ parseEnvFile(path, pattern, result, keysOnly) {
451
+ if (!file.exists(path)) {
452
+ return;
453
+ }
454
+ try {
455
+ const content = file.readText(path);
456
+ const lines = content.split('\n');
457
+ for (const line of lines) {
458
+ const trimmed = line.trim();
459
+ if (trimmed === '' || trimmed.startsWith('#')) {
460
+ continue;
461
+ }
462
+ const parts = trimmed.split('=', 2);
463
+ if (parts.length >= 1) {
464
+ const key = parts[0].trim();
465
+ if (this.matchPattern(key, pattern)) {
466
+ if (keysOnly) {
467
+ result[key] = '';
468
+ }
469
+ else if (parts.length === 2) {
470
+ let value = parts[1].trim();
471
+ // Remove quotes
472
+ if ((value.startsWith('"') && value.endsWith('"')) ||
473
+ (value.startsWith("'") && value.endsWith("'"))) {
474
+ value = value.slice(1, -1);
475
+ }
476
+ result[key] = value;
477
+ }
478
+ }
479
+ }
480
+ }
481
+ }
482
+ catch {
483
+ // File read error
484
+ }
485
+ }
486
+ matchPattern(key, pattern) {
487
+ if (pattern === '*')
488
+ return true;
489
+ if (pattern.endsWith('*')) {
490
+ const prefix = pattern.slice(0, -1);
491
+ return key.startsWith(prefix);
492
+ }
493
+ if (pattern.startsWith('*')) {
494
+ const suffix = pattern.slice(1);
495
+ return key.endsWith(suffix);
496
+ }
497
+ if (pattern.includes('*')) {
498
+ const parts = pattern.split('*');
499
+ if (parts.length === 2) {
500
+ return key.startsWith(parts[0]) && key.endsWith(parts[1]);
501
+ }
502
+ }
503
+ return key === pattern;
504
+ }
505
+ /**
506
+ * Get all variables matching patterns
507
+ */
508
+ all(pattern) {
509
+ const result = {};
510
+ const patterns = pattern ? (Array.isArray(pattern) ? pattern : [pattern]) : [];
511
+ if (patterns.length === 0) {
512
+ // Default - return memory only with source info
513
+ if (this.memory.size > 0) {
514
+ const memCopy = {};
515
+ for (const [key, entry] of this.memory) {
516
+ memCopy[key] = `${entry.value} [from: ${entry.source}]`;
517
+ }
518
+ result.memory = memCopy;
519
+ }
520
+ return result;
521
+ }
522
+ // Check if any pattern has namespace
523
+ const hasNamespace = patterns.some(p => p.includes(':'));
524
+ if (!hasNamespace) {
525
+ // No namespaces - get from memory and all sources
526
+ const memMatches = {};
527
+ for (const [key, entry] of this.memory) {
528
+ for (const pat of patterns) {
529
+ if (this.matchPattern(key, pat)) {
530
+ memMatches[key] = `${entry.value} [from: ${entry.source}]`;
531
+ break;
532
+ }
533
+ }
534
+ }
535
+ if (Object.keys(memMatches).length > 0) {
536
+ result.memory = memMatches;
537
+ }
538
+ // Check all sources
539
+ for (const source of this.sources) {
540
+ const sourceMatches = {};
541
+ const sourceVars = this.getAllFromNamespace(source, '*');
542
+ for (const [key, val] of Object.entries(sourceVars)) {
543
+ for (const pat of patterns) {
544
+ if (this.matchPattern(key, pat)) {
545
+ sourceMatches[key] = val;
546
+ break;
547
+ }
548
+ }
549
+ }
550
+ if (Object.keys(sourceMatches).length > 0) {
551
+ result[source] = sourceMatches;
552
+ }
553
+ }
554
+ return result;
555
+ }
556
+ // Special case for *:*
557
+ if (patterns.length === 1 && patterns[0] === '*:*') {
558
+ // Add memory
559
+ if (this.memory.size > 0) {
560
+ const memCopy = {};
561
+ for (const [key, entry] of this.memory) {
562
+ memCopy[key] = `${entry.value} [from: ${entry.source}]`;
563
+ }
564
+ result.memory = memCopy;
565
+ }
566
+ // Add all sources
567
+ for (const source of this.sources) {
568
+ const sourceVars = this.getAllFromNamespace(source, '*');
569
+ if (Object.keys(sourceVars).length > 0) {
570
+ result[source] = sourceVars;
571
+ }
572
+ }
573
+ }
574
+ else {
575
+ // Process specific patterns
576
+ for (const pat of patterns) {
577
+ const [namespace, keyPattern] = this.parseKey(pat);
578
+ if (namespace) {
579
+ const nsVars = this.getAllFromNamespace(namespace, keyPattern);
580
+ if (Object.keys(nsVars).length > 0) {
581
+ if (!result[namespace]) {
582
+ result[namespace] = {};
583
+ }
584
+ Object.assign(result[namespace], nsVars);
585
+ }
586
+ }
587
+ }
588
+ }
589
+ return result;
590
+ }
591
+ getAllFromNamespace(namespace, pattern) {
592
+ return this.getNamespaceData(namespace, pattern, false);
593
+ }
594
+ /**
595
+ * Clear variables from memory
596
+ */
597
+ clear(...patterns) {
598
+ // Only allow memory clearing for safety
599
+ for (const pattern of patterns) {
600
+ if (pattern.includes(':')) {
601
+ panic('Clear() with namespace is dangerous! Use clearUnsafe() if you really need this.');
602
+ }
603
+ }
604
+ if (patterns.length === 0) {
605
+ // Clear all memory
606
+ this.memory.clear();
607
+ return;
608
+ }
609
+ // Clear patterns from memory only
610
+ for (const pattern of patterns) {
611
+ for (const key of this.memory.keys()) {
612
+ if (this.matchPattern(key, pattern)) {
613
+ this.memory.delete(key);
614
+ }
615
+ }
616
+ }
617
+ }
618
+ /**
619
+ * Clear from namespaces (dangerous!)
620
+ */
621
+ clearUnsafe(...patterns) {
622
+ for (const pattern of patterns) {
623
+ const [namespace, keyPattern] = this.parseKey(pattern);
624
+ if (!namespace) {
625
+ // No namespace - just use regular clear
626
+ this.clear(pattern);
627
+ continue;
628
+ }
629
+ switch (namespace) {
630
+ case 'os':
631
+ if (keyPattern === '*') {
632
+ panic('clearUnsafe("os:*") would destroy system! This is never allowed.');
633
+ }
634
+ const keys = this.keysFromNamespace('os', keyPattern);
635
+ for (const key of keys) {
636
+ delete process.env[key];
637
+ }
638
+ break;
639
+ default:
640
+ panic('Clearing from files not yet implemented');
641
+ }
642
+ }
643
+ }
644
+ /**
645
+ * Remove specific keys
646
+ */
647
+ unset(...keys) {
648
+ for (const key of keys) {
649
+ const [namespace, realKey] = this.parseKey(key);
650
+ if (namespace) {
651
+ // Namespaced unset
652
+ switch (namespace) {
653
+ case 'os':
654
+ delete process.env[realKey];
655
+ break;
656
+ default:
657
+ panic('Unsetting from files not yet implemented');
658
+ }
659
+ }
660
+ else {
661
+ // Unnamespaced - remove from memory
662
+ this.memory.delete(realKey);
663
+ }
664
+ }
665
+ }
666
+ /**
667
+ * Get as integer with default
668
+ */
669
+ int(key, defaultValue) {
670
+ const val = this.get(key);
671
+ if (val === '') {
672
+ return defaultValue;
673
+ }
674
+ const i = parseInt(val, 10);
675
+ if (isNaN(i)) {
676
+ panic(`invalid int value for ${key}: ${val}`);
677
+ }
678
+ return i;
679
+ }
680
+ /**
681
+ * Get as boolean with default
682
+ */
683
+ bool(key, defaultValue) {
684
+ const val = this.get(key).toLowerCase();
685
+ if (val === '') {
686
+ return defaultValue;
687
+ }
688
+ switch (val) {
689
+ case 'true':
690
+ case '1':
691
+ case 'yes':
692
+ case 'on':
693
+ return true;
694
+ case 'false':
695
+ case '0':
696
+ case 'no':
697
+ case 'off':
698
+ return false;
699
+ default:
700
+ panic(`invalid bool value for ${key}: ${val}`);
701
+ }
702
+ }
703
+ /**
704
+ * Get as float with default
705
+ */
706
+ float(key, defaultValue) {
707
+ const val = this.get(key);
708
+ if (val === '') {
709
+ return defaultValue;
710
+ }
711
+ const f = parseFloat(val);
712
+ if (isNaN(f)) {
713
+ panic(`invalid float value for ${key}: ${val}`);
714
+ }
715
+ return f;
716
+ }
717
+ /**
718
+ * Export all memory variables to a file
719
+ */
720
+ export(path) {
721
+ const lines = [];
722
+ for (const [key, entry] of this.memory) {
723
+ let val = entry.value;
724
+ // Escape values with spaces or special chars
725
+ if (val.includes(' ') || val.includes('\t') || val.includes('\n')) {
726
+ val = `"${val}"`;
727
+ }
728
+ // Add comment with source info
729
+ lines.push(`${key}=${val} # from: ${entry.source}`);
730
+ }
731
+ file.write(path, lines.join('\n'));
732
+ }
733
+ /**
734
+ * Print all environment variables (masks sensitive keys)
735
+ */
736
+ dump() {
737
+ const all = this.all();
738
+ for (const [namespace, vars] of Object.entries(all)) {
739
+ for (const [key, val] of Object.entries(vars)) {
740
+ let maskedVal = val;
741
+ // Mask sensitive values
742
+ const lower = key.toLowerCase();
743
+ if (lower.includes('key') ||
744
+ lower.includes('secret') ||
745
+ lower.includes('password') ||
746
+ lower.includes('token')) {
747
+ if (val.length > 4) {
748
+ maskedVal = val.slice(0, 4) + '****';
749
+ }
750
+ else {
751
+ maskedVal = '****';
752
+ }
753
+ }
754
+ console.log(`${namespace}:${key}=${maskedVal}`);
755
+ }
756
+ }
757
+ }
758
+ }
759
+ // Create singleton instance
760
+ export const kev = new KevOps();
761
+ //# sourceMappingURL=kev.js.map