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