envprism 0.3.0 → 0.3.1

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.
@@ -1,676 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { loadConfig } from "c12";
3
- import { dirname, join, basename } from "pathe";
4
- import consola from "consola";
5
- import { defu } from "defu";
6
- import { stat, readdir, readFile } from "node:fs/promises";
7
- const SECRET_TOKENS = [
8
- "SECRET",
9
- "TOKEN",
10
- "PASSWORD",
11
- "PASSWD",
12
- "PWD",
13
- "KEY",
14
- "PRIVATE",
15
- "CREDENTIAL",
16
- "AUTH",
17
- "DSN"
18
- ];
19
- function isSecretKey(key, tokens = SECRET_TOKENS) {
20
- const upper = key.toUpperCase();
21
- const segments = upper.split("_").filter(Boolean);
22
- if (segments.length === 0) return false;
23
- if (segments[segments.length - 1] === "ID") return false;
24
- if (segments[0] === "PUBLIC") return false;
25
- if (segments.includes("PUBLIC")) return false;
26
- return segments.some(
27
- (seg) => tokens.some((token) => seg === token || seg.endsWith(token))
28
- );
29
- }
30
- function maskValue(value) {
31
- if (value.length === 0) return "••••";
32
- const dots = "•".repeat(Math.min(value.length, 8));
33
- return value.length > 8 ? `${dots} (${value.length})` : dots;
34
- }
35
- const DEFAULT_CONFIG = {
36
- discovery: {
37
- paths: ["."],
38
- skipSuffixes: [".swp", "~", ".bak"],
39
- skipSuffixesExtra: [],
40
- exampleFirst: true
41
- },
42
- base: {
43
- name: ".env.example",
44
- priority: []
45
- },
46
- heuristics: {
47
- secretTokens: [
48
- "SECRET",
49
- "TOKEN",
50
- "PASSWORD",
51
- "PASSWD",
52
- "PWD",
53
- "KEY",
54
- "PRIVATE",
55
- "CREDENTIAL",
56
- "AUTH",
57
- "DSN"
58
- ],
59
- secretTokensExtra: [],
60
- placeholders: [
61
- "todo",
62
- "fixme",
63
- "changeme",
64
- "placeholder",
65
- "tbd",
66
- "x{3,}",
67
- "your[_-]?(secret|key|token|password|api[_-]?key)(_here)?",
68
- "replace[_-]?me"
69
- ],
70
- placeholdersExtra: [],
71
- grouping: "auto"
72
- },
73
- diff: {
74
- json: false,
75
- checkExitCode: 1
76
- },
77
- tui: {
78
- theme: {},
79
- layout: {
80
- keyColWidth: 22,
81
- valueColMin: 18,
82
- sidebarWidth: 30,
83
- rowGap: 0,
84
- cellPadX: 1
85
- },
86
- undoLimit: 50,
87
- maskSecrets: true
88
- }
89
- };
90
- const PLACEHOLDER_RE = /^(todo|fixme|changeme|placeholder|tbd|x{3,}|your[_-]?(secret|key|token|password|api[_-]?key)(_here)?|replace[_-]?me)$/i;
91
- function isPlaceholderValue(value, re = PLACEHOLDER_RE) {
92
- const v = value.trim();
93
- if (v.length === 0) return false;
94
- return re.test(v);
95
- }
96
- function formatValue(value, secret) {
97
- if (value === void 0) return "";
98
- if (secret) return maskValue(value);
99
- return value;
100
- }
101
- function matchesFilter(key, filter) {
102
- if (!filter) return true;
103
- return key.toLowerCase().includes(filter.toLowerCase());
104
- }
105
- function truncate(text, width) {
106
- if (width <= 0) return "";
107
- if (text.length <= width) return text;
108
- if (width <= 1) return "…";
109
- return `${text.slice(0, width - 1)}…`;
110
- }
111
- function findKvEntry(file, key) {
112
- for (const e of file.entries) {
113
- if (e.kind === "kv" && e.key === key) return e;
114
- }
115
- return void 0;
116
- }
117
- const DEFAULT_THEME_HEX = {
118
- fg: "#cccccc",
119
- fgDim: "#666666",
120
- fgHeader: "#ffffff",
121
- fgBase: "#82aaff",
122
- fgSection: "#82aaff",
123
- differs: "#ffd866",
124
- extra: "#ffd866",
125
- placeholder: "#ffd866",
126
- modified: "#7fce6a",
127
- fgDirty: "#7fce6a",
128
- missing: "#ff6b6b",
129
- focusBg: "#3a3f4b"
130
- };
131
- const HEX_RE = /^#[0-9a-fA-F]{6}$/;
132
- function resolveThemeHex(theme = {}, warn = consola.warn) {
133
- const out = { ...DEFAULT_THEME_HEX };
134
- for (const key of Object.keys(DEFAULT_THEME_HEX)) {
135
- const override = theme[key];
136
- if (override === void 0) continue;
137
- if (HEX_RE.test(override)) {
138
- out[key] = override;
139
- } else {
140
- warn(`envprism: ignoring invalid hex for theme.${key}: "${override}"`);
141
- }
142
- }
143
- return out;
144
- }
145
- function pickList(replace, extra, fallback) {
146
- const base = replace ?? fallback;
147
- return [.../* @__PURE__ */ new Set([...base, ...extra ?? []])];
148
- }
149
- function mergeConfig(user = {}) {
150
- return {
151
- discovery: {
152
- paths: user.discovery?.paths ?? DEFAULT_CONFIG.discovery.paths,
153
- skipSuffixes: pickList(
154
- user.discovery?.skipSuffixes,
155
- user.discovery?.skipSuffixesExtra,
156
- DEFAULT_CONFIG.discovery.skipSuffixes
157
- ),
158
- skipSuffixesExtra: [],
159
- exampleFirst: user.discovery?.exampleFirst ?? DEFAULT_CONFIG.discovery.exampleFirst
160
- },
161
- base: {
162
- name: user.base?.name ?? DEFAULT_CONFIG.base.name,
163
- priority: user.base?.priority ?? DEFAULT_CONFIG.base.priority
164
- },
165
- heuristics: {
166
- secretTokens: pickList(
167
- user.heuristics?.secretTokens,
168
- user.heuristics?.secretTokensExtra,
169
- DEFAULT_CONFIG.heuristics.secretTokens
170
- ),
171
- secretTokensExtra: [],
172
- placeholders: pickList(
173
- user.heuristics?.placeholders,
174
- user.heuristics?.placeholdersExtra,
175
- DEFAULT_CONFIG.heuristics.placeholders
176
- ),
177
- placeholdersExtra: [],
178
- grouping: user.heuristics?.grouping ?? DEFAULT_CONFIG.heuristics.grouping
179
- },
180
- diff: defu(user.diff, DEFAULT_CONFIG.diff),
181
- tui: {
182
- theme: defu(user.tui?.theme, DEFAULT_CONFIG.tui.theme),
183
- layout: defu(user.tui?.layout, DEFAULT_CONFIG.tui.layout),
184
- undoLimit: user.tui?.undoLimit ?? DEFAULT_CONFIG.tui.undoLimit,
185
- maskSecrets: user.tui?.maskSecrets ?? DEFAULT_CONFIG.tui.maskSecrets
186
- }
187
- };
188
- }
189
- function resolveHeuristics(c) {
190
- const tokens = c.heuristics.secretTokens.map((t) => t.toUpperCase());
191
- const placeholderRe = new RegExp(
192
- `^(${c.heuristics.placeholders.join("|")})$`,
193
- "i"
194
- );
195
- return {
196
- isSecretKey: (key) => isSecretKey(key, tokens),
197
- isPlaceholderValue: (value) => isPlaceholderValue(value, placeholderRe),
198
- grouping: c.heuristics.grouping
199
- };
200
- }
201
- const CONFIG_EXTS = ["ts", "js", "mjs", "json"];
202
- function findConfigUp(start) {
203
- let dir = start;
204
- for (; ; ) {
205
- for (const ext of CONFIG_EXTS) {
206
- const candidate = join(dir, `envprism.config.${ext}`);
207
- if (existsSync(candidate)) return candidate;
208
- }
209
- const parent = dirname(dir);
210
- if (parent === dir) return void 0;
211
- dir = parent;
212
- }
213
- }
214
- async function loadEnvprismConfig(options = {}) {
215
- const envConfig = process.env.ENVPRISM_CONFIG;
216
- const startCwd = options.cwd ?? process.cwd();
217
- const resolved = options.configFile ?? envConfig ?? findConfigUp(startCwd);
218
- const cwd = resolved ? dirname(resolved) : startCwd;
219
- if (!resolved) {
220
- return { config: mergeConfig({}), configFile: void 0, cwd };
221
- }
222
- const { config } = await loadConfig({
223
- name: "envprism",
224
- cwd,
225
- configFile: resolved,
226
- rcFile: false,
227
- globalRc: false,
228
- dotenv: false
229
- });
230
- return {
231
- config: mergeConfig(config ?? {}),
232
- configFile: resolved,
233
- cwd
234
- };
235
- }
236
- function resolveBase(files, override, options = {}) {
237
- if (files.length === 0) return null;
238
- if (override) {
239
- const match = files.find(
240
- (f) => f.path === override || basename(f.path) === override
241
- );
242
- if (!match) {
243
- throw new Error(
244
- `--base ${override} did not match any discovered env file`
245
- );
246
- }
247
- return match;
248
- }
249
- const name = options.name ?? ".env.example";
250
- const example = files.find((f) => basename(f.path) === name);
251
- if (example) return example;
252
- for (const candidate of options.priority ?? []) {
253
- const match = files.find((f) => basename(f.path) === candidate);
254
- if (match) return match;
255
- }
256
- return files[0] ?? null;
257
- }
258
- const DRIFT_STATES = /* @__PURE__ */ new Set([
259
- "differs",
260
- "missing",
261
- "extra"
262
- ]);
263
- function computeDiff(matrix) {
264
- const others = matrix.files.filter((f) => f !== matrix.base);
265
- const files = others.map((f) => buildFileReport(matrix, f));
266
- return {
267
- base: matrix.base.path,
268
- files,
269
- inSync: files.every((f) => f.drift === 0)
270
- };
271
- }
272
- function buildFileReport(matrix, file) {
273
- const keys = {};
274
- let drift = 0;
275
- for (const key of matrix.keys) {
276
- const { state } = matrix.cell(key, file);
277
- keys[key] = state;
278
- if (DRIFT_STATES.has(state)) drift++;
279
- }
280
- return { path: file.path, keys, drift };
281
- }
282
- function formatDiffText(report) {
283
- const baseName = basename(report.base);
284
- const otherNames = report.files.map((f) => basename(f.path));
285
- const lines = [];
286
- lines.push(`Base: ${baseName} (vs. ${otherNames.join(", ")})`);
287
- lines.push("");
288
- if (report.files.length === 0) {
289
- lines.push("No other env files to compare.");
290
- return lines.join("\n") + "\n";
291
- }
292
- const driftKeys = /* @__PURE__ */ new Set();
293
- for (const f of report.files) {
294
- for (const [k, s] of Object.entries(f.keys)) {
295
- if (DRIFT_STATES.has(s)) driftKeys.add(k);
296
- }
297
- }
298
- if (driftKeys.size === 0) {
299
- lines.push("All env files are in sync with the base.");
300
- return lines.join("\n") + "\n";
301
- }
302
- const keyWidth = Math.max(3, ...[...driftKeys].map((k) => k.length));
303
- const colWidth = Math.max(12, ...otherNames.map((n) => n.length));
304
- lines.push(
305
- formatRow("KEY", otherNames, keyWidth, colWidth, (n) => n.padEnd(colWidth))
306
- );
307
- for (const key of [...driftKeys].sort()) {
308
- const cells = report.files.map((f) => stateLabel(f.keys[key] ?? "missing"));
309
- lines.push(
310
- formatRow(key, cells, keyWidth, colWidth, (n) => n.padEnd(colWidth))
311
- );
312
- }
313
- lines.push("");
314
- const totalDrift = report.files.reduce((sum, f) => sum + f.drift, 0);
315
- lines.push(
316
- `${driftKeys.size} key(s) differ across ${report.files.length} file(s) (${totalDrift} cell drift).`
317
- );
318
- return lines.join("\n") + "\n";
319
- }
320
- function formatRow(key, cells, keyWidth, colWidth, pad) {
321
- return [key.padEnd(keyWidth), ...cells.map(pad)].join(" ");
322
- }
323
- function stateLabel(state) {
324
- switch (state) {
325
- case "same":
326
- return "— same";
327
- case "differs":
328
- return "≠ differs";
329
- case "missing":
330
- return "✗ missing";
331
- case "extra":
332
- return "★ extra";
333
- case "base":
334
- return "· base";
335
- }
336
- }
337
- const KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
338
- function parseEnv(source, path = "") {
339
- const trailingNewline = source.endsWith("\n");
340
- const body = trailingNewline ? source.slice(0, -1) : source;
341
- const lines = body.length === 0 ? [] : body.split("\n");
342
- const entries = [];
343
- for (const raw of lines) {
344
- entries.push(parseLine(raw));
345
- }
346
- return { path, entries, trailingNewline };
347
- }
348
- function parseLine(raw) {
349
- if (raw.trim().length === 0) {
350
- return { kind: "blank", raw };
351
- }
352
- const trimmedStart = raw.replace(/^\s+/, "");
353
- if (trimmedStart.startsWith("#")) {
354
- return { kind: "comment", raw };
355
- }
356
- const kv = tryParseKv(raw);
357
- if (kv) return kv;
358
- return { kind: "comment", raw };
359
- }
360
- function tryParseKv(raw) {
361
- let rest = raw;
362
- const leadingWs = rest.match(/^[ \t]*/)?.[0] ?? "";
363
- rest = rest.slice(leadingWs.length);
364
- let exportPrefix = false;
365
- if (rest.startsWith("export ")) {
366
- exportPrefix = true;
367
- rest = rest.slice("export ".length).replace(/^[ \t]*/, "");
368
- }
369
- const eqIdx = rest.indexOf("=");
370
- if (eqIdx < 0) return null;
371
- const key = rest.slice(0, eqIdx).trimEnd();
372
- if (!KEY_RE.test(key)) return null;
373
- const after = rest.slice(eqIdx + 1);
374
- const valueStart = after.replace(/^[ \t]*/, "");
375
- after.slice(0, after.length - valueStart.length);
376
- const parsed = parseValue(valueStart);
377
- if (!parsed) return null;
378
- return {
379
- kind: "kv",
380
- key,
381
- rawValue: parsed.rawValue,
382
- value: parsed.value,
383
- quoting: parsed.quoting,
384
- exportPrefix,
385
- inlineComment: parsed.inlineComment,
386
- raw
387
- };
388
- }
389
- function parseValue(input) {
390
- if (input.length === 0) {
391
- return { rawValue: "", value: "", quoting: "none", inlineComment: "" };
392
- }
393
- const first = input[0];
394
- if (first === '"' || first === "'") {
395
- const close = findClosingQuote(input, first);
396
- if (close < 0) return null;
397
- const rawValue = input.slice(1, close);
398
- const value = first === '"' ? decodeDoubleQuoted(rawValue) : rawValue;
399
- const tail = input.slice(close + 1);
400
- const inlineComment2 = extractInlineComment(tail);
401
- return {
402
- rawValue,
403
- value,
404
- quoting: first === '"' ? "double" : "single",
405
- inlineComment: inlineComment2
406
- };
407
- }
408
- const hashIdx = findUnquotedCommentStart(input);
409
- const valuePart = (hashIdx < 0 ? input : input.slice(0, hashIdx)).trimEnd();
410
- const inlineComment = hashIdx < 0 ? "" : input.slice(valuePart.length);
411
- return {
412
- rawValue: valuePart,
413
- value: valuePart,
414
- quoting: "none",
415
- inlineComment
416
- };
417
- }
418
- function findClosingQuote(input, quote) {
419
- for (let i = 1; i < input.length; i++) {
420
- const ch = input[i];
421
- if (quote === '"' && ch === "\\") {
422
- i++;
423
- continue;
424
- }
425
- if (ch === quote) return i;
426
- }
427
- return -1;
428
- }
429
- function findUnquotedCommentStart(input) {
430
- for (let i = 0; i < input.length; i++) {
431
- if (input[i] !== "#") continue;
432
- if (i === 0) return i;
433
- const prev = input[i - 1];
434
- if (prev === " " || prev === " ") return i;
435
- }
436
- return -1;
437
- }
438
- function extractInlineComment(tail) {
439
- return tail;
440
- }
441
- function decodeDoubleQuoted(raw) {
442
- return raw.replace(/\\(.)/g, (_, ch) => {
443
- switch (ch) {
444
- case "n":
445
- return "\n";
446
- case "r":
447
- return "\r";
448
- case "t":
449
- return " ";
450
- case "\\":
451
- return "\\";
452
- case '"':
453
- return '"';
454
- default:
455
- return `\\${ch}`;
456
- }
457
- });
458
- }
459
- const SKIP_SUFFIXES = [".swp", "~", ".bak"];
460
- async function discoverEnvFiles(paths, options = {}) {
461
- const skipSuffixes = options.skipSuffixes ?? SKIP_SUFFIXES;
462
- const exampleFirst = options.exampleFirst ?? true;
463
- const filePaths = /* @__PURE__ */ new Set();
464
- for (const p of paths) {
465
- const info = await stat(p);
466
- if (info.isDirectory()) {
467
- const entries = await readdir(p);
468
- for (const name of entries) {
469
- if (!looksLikeEnvFile(name, skipSuffixes)) continue;
470
- filePaths.add(join(p, name));
471
- }
472
- } else {
473
- filePaths.add(p);
474
- }
475
- }
476
- const sorted = [...filePaths].sort(
477
- (a, b) => envPathOrder(a, b, exampleFirst)
478
- );
479
- const files = [];
480
- for (const filePath of sorted) {
481
- const source = await readFile(filePath, "utf8");
482
- files.push(parseEnv(source, filePath));
483
- }
484
- return files;
485
- }
486
- function looksLikeEnvFile(name, skipSuffixes) {
487
- if (!name.startsWith(".env")) return false;
488
- if (skipSuffixes.some((s) => name.endsWith(s))) return false;
489
- return true;
490
- }
491
- function envPathOrder(a, b, exampleFirst) {
492
- const an = basename(a);
493
- const bn = basename(b);
494
- if (exampleFirst) {
495
- if (an === ".env.example") return -1;
496
- if (bn === ".env.example") return 1;
497
- }
498
- return an.localeCompare(bn);
499
- }
500
- const SEP_CHARS = /* @__PURE__ */ new Set(["=", "-", "#", "~", "*"]);
501
- const SPACE_CHARS = /* @__PURE__ */ new Set([" ", " "]);
502
- function computeSections(base) {
503
- const out = /* @__PURE__ */ new Map();
504
- let current = null;
505
- for (let i = 0; i < base.entries.length; i++) {
506
- const name = detectSectionName(base.entries, i);
507
- if (name !== null) current = name;
508
- const e = base.entries[i];
509
- if (e.kind === "kv" && current) out.set(e.key, current);
510
- }
511
- return out;
512
- }
513
- function detectSectionName(entries, idx) {
514
- const e = entries[idx];
515
- if (!e || e.kind !== "comment") return null;
516
- if (isDecorativeLine(e.raw)) return null;
517
- const inline = parseInlineBanner(e.raw);
518
- if (inline !== null) return inline;
519
- const text = stripCommentPrefix(e.raw);
520
- if (!text) return null;
521
- if (isDecorative(entries[idx - 1]) || isDecorative(entries[idx + 1])) {
522
- return text;
523
- }
524
- return null;
525
- }
526
- function isDecorative(e) {
527
- if (!e || e.kind !== "comment") return false;
528
- return isDecorativeLine(e.raw);
529
- }
530
- function isDecorativeLine(raw) {
531
- const start = skipSpaces(raw, 0);
532
- if (start >= raw.length || raw[start] !== "#") return false;
533
- let i = start + 1;
534
- let sawSeparator = false;
535
- while (i < raw.length) {
536
- const ch = raw[i];
537
- if (SEP_CHARS.has(ch)) sawSeparator = true;
538
- else if (!SPACE_CHARS.has(ch)) return false;
539
- i++;
540
- }
541
- return sawSeparator;
542
- }
543
- function parseInlineBanner(raw) {
544
- const start = skipSpaces(raw, 0);
545
- if (start >= raw.length || raw[start] !== "#") return null;
546
- let i = skipSpaces(raw, start + 1);
547
- if (i >= raw.length || !SEP_CHARS.has(raw[i])) return null;
548
- let leadCount = 0;
549
- while (i < raw.length && SEP_CHARS.has(raw[i])) {
550
- leadCount++;
551
- i++;
552
- }
553
- if (leadCount < 2) return null;
554
- let j = raw.length;
555
- while (j > i && SPACE_CHARS.has(raw[j - 1])) j--;
556
- if (j <= i || !SEP_CHARS.has(raw[j - 1])) return null;
557
- let trailCount = 0;
558
- while (j > i && SEP_CHARS.has(raw[j - 1])) {
559
- trailCount++;
560
- j--;
561
- }
562
- if (trailCount < 2) return null;
563
- const inner = raw.slice(i, j).trim();
564
- return inner.length > 0 ? inner : null;
565
- }
566
- function stripCommentPrefix(raw) {
567
- let i = skipSpaces(raw, 0);
568
- while (i < raw.length && raw[i] === "#") i++;
569
- i = skipSpaces(raw, i);
570
- let j = raw.length;
571
- while (j > i && (SPACE_CHARS.has(raw[j - 1]) || SEP_CHARS.has(raw[j - 1]))) {
572
- j--;
573
- }
574
- return raw.slice(i, j);
575
- }
576
- function skipSpaces(raw, from) {
577
- let i = from;
578
- while (i < raw.length && SPACE_CHARS.has(raw[i])) i++;
579
- return i;
580
- }
581
- function buildMatrix(files, base) {
582
- const keys = collectKeys(files, base);
583
- const lookups = /* @__PURE__ */ new Map();
584
- for (const file of files) {
585
- lookups.set(file, indexKv(file));
586
- }
587
- const baseIndex = lookups.get(base);
588
- if (!baseIndex) {
589
- throw new Error("base file is not in the files list");
590
- }
591
- const sections = computeSections(base);
592
- return {
593
- keys,
594
- files,
595
- base,
596
- sectionOf(key) {
597
- return sections.get(key);
598
- },
599
- cell(key, file) {
600
- const ownIndex = lookups.get(file);
601
- if (!ownIndex) {
602
- throw new Error(`file not in matrix: ${file.path}`);
603
- }
604
- const own = ownIndex.get(key);
605
- const baseEntry = baseIndex.get(key);
606
- if (file === base) {
607
- return {
608
- state: own ? "base" : "missing",
609
- value: own?.value
610
- };
611
- }
612
- if (!own && !baseEntry) {
613
- return { state: "missing", value: void 0 };
614
- }
615
- if (!own) {
616
- return { state: "missing", value: void 0 };
617
- }
618
- if (!baseEntry) {
619
- return { state: "extra", value: own.value };
620
- }
621
- return {
622
- state: own.value === baseEntry.value ? "same" : "differs",
623
- value: own.value
624
- };
625
- }
626
- };
627
- }
628
- function collectKeys(files, base) {
629
- const seen = /* @__PURE__ */ new Set();
630
- const out = [];
631
- for (const e of base.entries) {
632
- if (e.kind === "kv" && !seen.has(e.key)) {
633
- seen.add(e.key);
634
- out.push(e.key);
635
- }
636
- }
637
- const extras = /* @__PURE__ */ new Set();
638
- for (const file of files) {
639
- if (file === base) continue;
640
- for (const e of file.entries) {
641
- if (e.kind === "kv" && !seen.has(e.key)) {
642
- extras.add(e.key);
643
- }
644
- }
645
- }
646
- for (const k of [...extras].sort()) out.push(k);
647
- return out;
648
- }
649
- function indexKv(file) {
650
- const m = /* @__PURE__ */ new Map();
651
- for (const e of file.entries) {
652
- if (e.kind === "kv") m.set(e.key, e);
653
- }
654
- return m;
655
- }
656
- export {
657
- DEFAULT_CONFIG as D,
658
- DEFAULT_THEME_HEX as a,
659
- buildMatrix as b,
660
- computeDiff as c,
661
- computeSections as d,
662
- discoverEnvFiles as e,
663
- findKvEntry as f,
664
- formatDiffText as g,
665
- formatValue as h,
666
- isSecretKey as i,
667
- matchesFilter as j,
668
- resolveHeuristics as k,
669
- loadEnvprismConfig as l,
670
- maskValue as m,
671
- resolveThemeHex as n,
672
- parseEnv as p,
673
- resolveBase as r,
674
- truncate as t
675
- };
676
- //# sourceMappingURL=matrix-C1pErf9Z.mjs.map