ultraenv 1.0.0

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 (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2058 -0
  3. package/bin/ultraenv.mjs +3 -0
  4. package/dist/chunk-2USZPWLZ.js +288 -0
  5. package/dist/chunk-3UV2QNJL.js +270 -0
  6. package/dist/chunk-3VYXPTYV.js +179 -0
  7. package/dist/chunk-4XUYMRK5.js +366 -0
  8. package/dist/chunk-5G2DU52U.js +189 -0
  9. package/dist/chunk-6KS56D6E.js +172 -0
  10. package/dist/chunk-AWN6ADV7.js +328 -0
  11. package/dist/chunk-CHVO6NWI.js +203 -0
  12. package/dist/chunk-CIFMBJ4H.js +3975 -0
  13. package/dist/chunk-GC7RXHLA.js +253 -0
  14. package/dist/chunk-HFXQGJY3.js +445 -0
  15. package/dist/chunk-IGFVP24Q.js +91 -0
  16. package/dist/chunk-IKPTKALB.js +78 -0
  17. package/dist/chunk-JB7RKV3C.js +66 -0
  18. package/dist/chunk-MNVFG7H4.js +611 -0
  19. package/dist/chunk-MSXMESFP.js +1910 -0
  20. package/dist/chunk-N5PAV4NM.js +127 -0
  21. package/dist/chunk-NBOABPHM.js +158 -0
  22. package/dist/chunk-OMAOROL4.js +49 -0
  23. package/dist/chunk-R7PZRSZ7.js +105 -0
  24. package/dist/chunk-TE7HPLA6.js +73 -0
  25. package/dist/chunk-TMT5KCO3.js +101 -0
  26. package/dist/chunk-UEWYFN6A.js +189 -0
  27. package/dist/chunk-WMHN5RW2.js +128 -0
  28. package/dist/chunk-XC65ORJ5.js +70 -0
  29. package/dist/chunk-YMMP4VQL.js +118 -0
  30. package/dist/chunk-YN2KGTCB.js +33 -0
  31. package/dist/chunk-YTICOB5M.js +65 -0
  32. package/dist/chunk-YVWLXFUT.js +107 -0
  33. package/dist/ci-check-sync-VBMSVWIV.js +48 -0
  34. package/dist/ci-scan-24MT5XGS.js +41 -0
  35. package/dist/ci-setup-C2NKEFRD.js +135 -0
  36. package/dist/ci-validate-7AW24LSQ.js +57 -0
  37. package/dist/cli/index.cjs +9217 -0
  38. package/dist/cli/index.d.cts +9 -0
  39. package/dist/cli/index.d.ts +9 -0
  40. package/dist/cli/index.js +339 -0
  41. package/dist/comparator-RDKX3OI7.js +13 -0
  42. package/dist/completion-MW35C2XO.js +168 -0
  43. package/dist/config-O5YRQP5Z.js +13 -0
  44. package/dist/debug-PTPXAF3K.js +131 -0
  45. package/dist/declaration-LEME4AFZ.js +10 -0
  46. package/dist/doctor-FZAUPKHS.js +129 -0
  47. package/dist/envs-compare-5K3HESX5.js +49 -0
  48. package/dist/envs-create-2XXHXMGA.js +58 -0
  49. package/dist/envs-list-NQM5252B.js +59 -0
  50. package/dist/envs-switch-6L2AQYID.js +50 -0
  51. package/dist/envs-validate-FL73Q76T.js +89 -0
  52. package/dist/fs-VH7ATUS3.js +31 -0
  53. package/dist/generator-LFZBMZZS.js +14 -0
  54. package/dist/git-BZS4DPAI.js +30 -0
  55. package/dist/help-3XJBXEHE.js +121 -0
  56. package/dist/index.cjs +12907 -0
  57. package/dist/index.d.cts +2562 -0
  58. package/dist/index.d.ts +2562 -0
  59. package/dist/index.js +3212 -0
  60. package/dist/init-Y7JQ2KYJ.js +146 -0
  61. package/dist/install-hook-SKXIV6NV.js +111 -0
  62. package/dist/json-schema-I26YNQBH.js +10 -0
  63. package/dist/key-manager-O3G55WPU.js +25 -0
  64. package/dist/middleware/express.cjs +103 -0
  65. package/dist/middleware/express.d.cts +115 -0
  66. package/dist/middleware/express.d.ts +115 -0
  67. package/dist/middleware/express.js +8 -0
  68. package/dist/middleware/fastify.cjs +91 -0
  69. package/dist/middleware/fastify.d.cts +111 -0
  70. package/dist/middleware/fastify.d.ts +111 -0
  71. package/dist/middleware/fastify.js +8 -0
  72. package/dist/module-IDIZPP4M.js +10 -0
  73. package/dist/protect-NCWPM6VC.js +161 -0
  74. package/dist/scan-TRLY36TT.js +58 -0
  75. package/dist/schema/index.cjs +4074 -0
  76. package/dist/schema/index.d.cts +1244 -0
  77. package/dist/schema/index.d.ts +1244 -0
  78. package/dist/schema/index.js +152 -0
  79. package/dist/sync-TMHMTLH2.js +186 -0
  80. package/dist/typegen-SQOSXBWM.js +80 -0
  81. package/dist/validate-IOAM5HWS.js +100 -0
  82. package/dist/vault-decrypt-U6HJZNBV.js +111 -0
  83. package/dist/vault-diff-B3ZOQTWI.js +132 -0
  84. package/dist/vault-encrypt-GUSLCSKS.js +112 -0
  85. package/dist/vault-init-GUBOTOUL.js +106 -0
  86. package/dist/vault-rekey-DAHT7JCN.js +132 -0
  87. package/dist/vault-status-GDLRU2OK.js +90 -0
  88. package/dist/vault-verify-CD76FJSF.js +102 -0
  89. package/package.json +106 -0
@@ -0,0 +1,127 @@
1
+ import {
2
+ EncryptionError
3
+ } from "./chunk-5G2DU52U.js";
4
+
5
+ // src/vault/integrity.ts
6
+ import { createHmac, timingSafeEqual as cryptoTimingSafeEqual } from "crypto";
7
+ var HMAC_ALGORITHM = "sha256";
8
+ var HMAC_DIGEST_LENGTH = 64;
9
+ function computeIntegrity(data, key) {
10
+ if (key.length === 0) {
11
+ throw new EncryptionError(
12
+ "Cannot compute integrity with an empty key",
13
+ { hint: "Provide a valid encryption key for integrity computation." }
14
+ );
15
+ }
16
+ if (data.length === 0) {
17
+ throw new EncryptionError(
18
+ "Cannot compute integrity for empty data",
19
+ { hint: "Provide non-empty data for integrity computation." }
20
+ );
21
+ }
22
+ const hmac = createHmac(HMAC_ALGORITHM, key);
23
+ hmac.update(data, "utf-8");
24
+ return hmac.digest("hex");
25
+ }
26
+ function verifyIntegrity(data, key, expected) {
27
+ if (key.length === 0) {
28
+ throw new EncryptionError(
29
+ "Cannot verify integrity with an empty key"
30
+ );
31
+ }
32
+ if (expected.length !== HMAC_DIGEST_LENGTH) {
33
+ throw new EncryptionError(
34
+ `Invalid expected digest length: expected ${HMAC_DIGEST_LENGTH} hex characters, got ${expected.length}`,
35
+ { hint: "The integrity digest should be a 64-character lowercase hex string from computeIntegrity()." }
36
+ );
37
+ }
38
+ if (!/^[0-9a-f]{64}$/.test(expected)) {
39
+ throw new EncryptionError(
40
+ "Invalid expected digest format: must be a lowercase hex string",
41
+ { hint: "Ensure the stored digest is a 64-character lowercase hex string." }
42
+ );
43
+ }
44
+ try {
45
+ const computed = computeIntegrity(data, key);
46
+ return timingSafeEqualHex(computed, expected);
47
+ } catch (error) {
48
+ if (error instanceof EncryptionError) throw error;
49
+ throw new EncryptionError("Integrity verification failed", {
50
+ cause: error instanceof Error ? error : void 0
51
+ });
52
+ }
53
+ }
54
+ function computeVaultChecksum(environments, key) {
55
+ if (key.length === 0) {
56
+ throw new EncryptionError(
57
+ "Cannot compute vault checksum with an empty key"
58
+ );
59
+ }
60
+ if (environments.size === 0) {
61
+ throw new EncryptionError(
62
+ "Cannot compute vault checksum for empty environment set"
63
+ );
64
+ }
65
+ const sortedNames = Array.from(environments.keys()).sort();
66
+ const parts = [];
67
+ for (const name of sortedNames) {
68
+ const entry = environments.get(name);
69
+ if (entry === void 0) continue;
70
+ const encrypted = entry["encrypted"];
71
+ const encryptedStr = typeof encrypted === "string" ? encrypted : "";
72
+ parts.push(`${name}:${encryptedStr}:${entry.varCount}:${entry.lastModified}`);
73
+ }
74
+ const combinedData = parts.join("\n");
75
+ return computeIntegrity(combinedData, key);
76
+ }
77
+ function verifyVaultChecksum(environments, key, expected) {
78
+ if (key.length === 0) {
79
+ throw new EncryptionError(
80
+ "Cannot verify vault checksum with an empty key"
81
+ );
82
+ }
83
+ if (expected.length !== HMAC_DIGEST_LENGTH) {
84
+ throw new EncryptionError(
85
+ `Invalid vault checksum length: expected ${HMAC_DIGEST_LENGTH} hex characters, got ${expected.length}`
86
+ );
87
+ }
88
+ try {
89
+ const computed = computeVaultChecksum(environments, key);
90
+ return timingSafeEqualHex(computed, expected);
91
+ } catch (error) {
92
+ if (error instanceof EncryptionError) throw error;
93
+ throw new EncryptionError("Vault checksum verification failed", {
94
+ cause: error instanceof Error ? error : void 0
95
+ });
96
+ }
97
+ }
98
+ function timingSafeEqualHex(a, b) {
99
+ if (a === b) return true;
100
+ const bufA = Buffer.from(a, "hex");
101
+ const bufB = Buffer.from(b, "hex");
102
+ if (bufA.length !== bufB.length) {
103
+ const maxLen = Math.max(bufA.length, bufB.length);
104
+ const padA = Buffer.alloc(maxLen);
105
+ const padB = Buffer.alloc(maxLen);
106
+ bufA.copy(padA, maxLen - bufA.length);
107
+ bufB.copy(padB, maxLen - bufB.length);
108
+ try {
109
+ cryptoTimingSafeEqual(padA, padB);
110
+ } catch {
111
+ return false;
112
+ }
113
+ return false;
114
+ }
115
+ try {
116
+ return cryptoTimingSafeEqual(bufA, bufB);
117
+ } catch {
118
+ return false;
119
+ }
120
+ }
121
+
122
+ export {
123
+ computeIntegrity,
124
+ verifyIntegrity,
125
+ computeVaultChecksum,
126
+ verifyVaultChecksum
127
+ };
@@ -0,0 +1,158 @@
1
+ import {
2
+ readFile,
3
+ writeFile
4
+ } from "./chunk-3VYXPTYV.js";
5
+ import {
6
+ VaultError
7
+ } from "./chunk-5G2DU52U.js";
8
+
9
+ // src/vault/vault-file.ts
10
+ var VAULT_VAR_PREFIX = "ULTRAENV_VAULT_";
11
+ function parseVaultFile(content) {
12
+ const result = /* @__PURE__ */ new Map();
13
+ if (content.trim().length === 0) {
14
+ return result;
15
+ }
16
+ const lines = content.split("\n");
17
+ for (let i = 0; i < lines.length; i++) {
18
+ const lineNumber = i + 1;
19
+ const rawLine = lines[i];
20
+ if (rawLine === void 0) continue;
21
+ const line = rawLine.trim();
22
+ if (line.length === 0 || line.startsWith("#")) continue;
23
+ const match = line.match(
24
+ new RegExp(`^${VAULT_VAR_PREFIX}([A-Za-z0-9_]+)="(.*)"$`)
25
+ );
26
+ if (!match) {
27
+ const plainMatch = line.match(
28
+ new RegExp(`^${VAULT_VAR_PREFIX}([A-Za-z0-9_]+)=(.*)$`)
29
+ );
30
+ if (!plainMatch) {
31
+ throw new VaultError(
32
+ `Invalid vault file format at line ${lineNumber}: "${line}"`,
33
+ {
34
+ operation: "parse",
35
+ hint: `Each environment entry should be in the format: ${VAULT_VAR_PREFIX}{ENVIRONMENT}="encrypted:..."`
36
+ }
37
+ );
38
+ }
39
+ const envName2 = plainMatch[1].toLowerCase();
40
+ const encryptedValue2 = plainMatch[2];
41
+ const varCount2 = countEncryptedVars(encryptedValue2);
42
+ result.set(envName2, {
43
+ name: envName2,
44
+ varCount: varCount2,
45
+ lastModified: (/* @__PURE__ */ new Date()).toISOString(),
46
+ keyIds: extractKeyIds(encryptedValue2),
47
+ encrypted: encryptedValue2
48
+ });
49
+ continue;
50
+ }
51
+ const envName = match[1].toLowerCase();
52
+ const encryptedValue = match[2];
53
+ const varCount = countEncryptedVars(encryptedValue);
54
+ result.set(envName, {
55
+ name: envName,
56
+ varCount,
57
+ lastModified: (/* @__PURE__ */ new Date()).toISOString(),
58
+ keyIds: extractKeyIds(encryptedValue),
59
+ encrypted: encryptedValue
60
+ });
61
+ }
62
+ return result;
63
+ }
64
+ function serializeVaultFile(environments) {
65
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
66
+ const envNames = Array.from(environments.keys()).sort();
67
+ const lines = [
68
+ "# ultraenv encrypted vault \u2014 safe to commit",
69
+ `# Generated: ${timestamp}`,
70
+ `# Environments: ${envNames.join(", ")}`,
71
+ ""
72
+ ];
73
+ for (const envName of envNames) {
74
+ const entry = environments.get(envName);
75
+ if (entry === void 0) continue;
76
+ const varName = `${VAULT_VAR_PREFIX}${envName.toUpperCase()}`;
77
+ lines.push(`${varName}="${entry.encrypted}"`);
78
+ }
79
+ if (lines[lines.length - 1] !== "") {
80
+ lines.push("");
81
+ }
82
+ return lines.join("\n");
83
+ }
84
+ async function readVaultFile(path) {
85
+ try {
86
+ const content = await readFile(path, "utf-8");
87
+ return parseVaultFile(content);
88
+ } catch (error) {
89
+ if (error instanceof VaultError) throw error;
90
+ throw new VaultError(
91
+ `Failed to read vault file "${path}"`,
92
+ {
93
+ operation: "read",
94
+ /* v8 ignore start */
95
+ cause: error instanceof Error ? error : void 0
96
+ /* v8 ignore stop */
97
+ }
98
+ );
99
+ }
100
+ }
101
+ async function writeVaultFile(path, environments) {
102
+ if (environments.size === 0) {
103
+ throw new VaultError(
104
+ "Cannot write an empty vault file",
105
+ {
106
+ operation: "write",
107
+ hint: "Add at least one environment to the vault before writing."
108
+ }
109
+ );
110
+ }
111
+ try {
112
+ const content = serializeVaultFile(environments);
113
+ await writeFile(path, content, "utf-8");
114
+ } catch (error) {
115
+ if (error instanceof VaultError) throw error;
116
+ throw new VaultError(
117
+ `Failed to write vault file "${path}"`,
118
+ {
119
+ operation: "write",
120
+ /* v8 ignore start */
121
+ cause: error instanceof Error ? error : void 0
122
+ /* v8 ignore stop */
123
+ }
124
+ );
125
+ }
126
+ }
127
+ function getEnvironmentData(vault, env) {
128
+ return vault.get(env.toLowerCase());
129
+ }
130
+ function getVaultEnvironments(vault) {
131
+ return Array.from(vault.keys()).sort();
132
+ }
133
+ function countEncryptedVars(encrypted) {
134
+ if (!encrypted.startsWith("encrypted:")) {
135
+ return encrypted.split("\n").filter((line) => line.includes("=")).length || 1;
136
+ }
137
+ return 1;
138
+ }
139
+ function extractKeyIds(encrypted) {
140
+ if (!encrypted.startsWith("encrypted:")) {
141
+ return ["unknown"];
142
+ }
143
+ const colonIndex = encrypted.indexOf(":", "encrypted:".length);
144
+ if (colonIndex === -1) {
145
+ return ["unknown"];
146
+ }
147
+ const version = encrypted.slice("encrypted:".length, colonIndex);
148
+ return [`v${version === "v1" ? "1" : version}`];
149
+ }
150
+
151
+ export {
152
+ parseVaultFile,
153
+ serializeVaultFile,
154
+ readVaultFile,
155
+ writeVaultFile,
156
+ getEnvironmentData,
157
+ getVaultEnvironments
158
+ };
@@ -0,0 +1,49 @@
1
+ // src/cli/ui/colors.ts
2
+ var ESC = "\x1B[";
3
+ var CODES = {
4
+ reset: `${ESC}0m`,
5
+ bold: `${ESC}1m`,
6
+ dim: `${ESC}2m`,
7
+ underline: `${ESC}4m`,
8
+ red: `${ESC}31m`,
9
+ green: `${ESC}32m`,
10
+ yellow: `${ESC}33m`,
11
+ blue: `${ESC}34m`,
12
+ magenta: `${ESC}35m`,
13
+ cyan: `${ESC}36m`,
14
+ white: `${ESC}37m`,
15
+ gray: `${ESC}90m`
16
+ };
17
+ var bold = (text) => colorEnabled ? `${CODES.bold}${text}${CODES.reset}` : text;
18
+ var dim = (text) => colorEnabled ? `${CODES.dim}${text}${CODES.reset}` : text;
19
+ var red = (text) => colorEnabled ? `${CODES.red}${text}${CODES.reset}` : text;
20
+ var green = (text) => colorEnabled ? `${CODES.green}${text}${CODES.reset}` : text;
21
+ var yellow = (text) => colorEnabled ? `${CODES.yellow}${text}${CODES.reset}` : text;
22
+ var cyan = (text) => colorEnabled ? `${CODES.cyan}${text}${CODES.reset}` : text;
23
+ var colorEnabled = true;
24
+ function supportsColor() {
25
+ if (process.env.NO_COLOR !== void 0 && process.env.NO_COLOR !== "") {
26
+ return false;
27
+ }
28
+ if (process.stdout !== void 0 && !process.stdout.isTTY) {
29
+ return false;
30
+ }
31
+ return true;
32
+ }
33
+ function initColorSupport(noColorFlag = false) {
34
+ if (noColorFlag || !supportsColor()) {
35
+ colorEnabled = false;
36
+ } else {
37
+ colorEnabled = true;
38
+ }
39
+ }
40
+
41
+ export {
42
+ bold,
43
+ dim,
44
+ red,
45
+ green,
46
+ yellow,
47
+ cyan,
48
+ initColorSupport
49
+ };
@@ -0,0 +1,105 @@
1
+ // src/utils/git.ts
2
+ import { execFile } from "child_process";
3
+ import { promisify } from "util";
4
+ var execFileAsync = promisify(execFile);
5
+ async function git(args, cwd) {
6
+ try {
7
+ const { stdout } = await execFileAsync("git", [...args], {
8
+ cwd: cwd ?? process.cwd(),
9
+ maxBuffer: 10 * 1024 * 1024,
10
+ // 10MB
11
+ encoding: "utf-8",
12
+ timeout: 3e4
13
+ });
14
+ return stdout.trim();
15
+ } catch {
16
+ return "";
17
+ }
18
+ }
19
+ async function gitSuccess(args, cwd) {
20
+ try {
21
+ await execFileAsync("git", [...args], {
22
+ cwd: cwd ?? process.cwd(),
23
+ maxBuffer: 10 * 1024 * 1024,
24
+ encoding: "utf-8",
25
+ timeout: 3e4
26
+ });
27
+ return true;
28
+ } catch {
29
+ return false;
30
+ }
31
+ }
32
+ async function isGitRepository(cwd) {
33
+ return gitSuccess(["rev-parse", "--is-inside-work-tree"], cwd);
34
+ }
35
+ async function getGitRoot(cwd) {
36
+ const result = await git(["rev-parse", "--show-toplevel"], cwd);
37
+ return result !== "" ? result : null;
38
+ }
39
+ async function getStagedFiles(cwd) {
40
+ const result = await git(["diff", "--cached", "--name-only", "--diff-filter=ACMR"], cwd);
41
+ if (result === "") return [];
42
+ return result.split("\n").filter((f) => f.length > 0);
43
+ }
44
+ async function getDiffFiles(from, to, cwd) {
45
+ const args = ["diff", "--name-only", from];
46
+ if (to !== void 0) {
47
+ args.push(to);
48
+ }
49
+ const result = await git(args, cwd);
50
+ if (result === "") return [];
51
+ return result.split("\n").filter((f) => f.length > 0);
52
+ }
53
+ async function isGitIgnored(path, cwd) {
54
+ return gitSuccess(["check-ignore", "--quiet", path], cwd);
55
+ }
56
+ async function getCommitHash(cwd) {
57
+ const result = await git(["rev-parse", "--short", "HEAD"], cwd);
58
+ return result !== "" ? result : null;
59
+ }
60
+ async function getFullCommitHash(cwd) {
61
+ const result = await git(["rev-parse", "HEAD"], cwd);
62
+ return result !== "" ? result : null;
63
+ }
64
+ async function getConfig(key, cwd) {
65
+ const result = await git(["config", "--get", key], cwd);
66
+ return result !== "" ? result : null;
67
+ }
68
+ async function getConfigAll(key, cwd) {
69
+ const result = await git(["config", "--get-all", key], cwd);
70
+ if (result === "") return [];
71
+ return result.split("\n").filter((v) => v.length > 0);
72
+ }
73
+ async function getCurrentBranch(cwd) {
74
+ const result = await git(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
75
+ if (result === "" || result === "HEAD") return null;
76
+ return result;
77
+ }
78
+ async function getRemoteUrl(remote = "origin", cwd) {
79
+ return getConfig(`remote.${remote}.url`, cwd);
80
+ }
81
+ async function isDirty(cwd) {
82
+ const result = await git(["status", "--porcelain"], cwd);
83
+ return result !== "";
84
+ }
85
+ async function getTrackedFiles(cwd) {
86
+ const result = await git(["ls-files"], cwd);
87
+ if (result === "") return [];
88
+ return result.split("\n").filter((f) => f.length > 0);
89
+ }
90
+
91
+ export {
92
+ isGitRepository,
93
+ getGitRoot,
94
+ getStagedFiles,
95
+ getDiffFiles,
96
+ isGitIgnored,
97
+ getCommitHash,
98
+ getFullCommitHash,
99
+ getConfig,
100
+ getConfigAll,
101
+ getCurrentBranch,
102
+ getRemoteUrl,
103
+ isDirty,
104
+ getTrackedFiles
105
+ };
@@ -0,0 +1,73 @@
1
+ // src/utils/mask.ts
2
+ var DEFAULT_MASK_OPTIONS = {
3
+ visibleStart: 3,
4
+ visibleEnd: 4,
5
+ maskChar: "*",
6
+ minLength: 8
7
+ };
8
+ function maskValue(value, options) {
9
+ const opts = { ...DEFAULT_MASK_OPTIONS, ...options };
10
+ if (value.length === 0) return "";
11
+ if (value.length < opts.minLength) return value;
12
+ const startLen = Math.min(opts.visibleStart, value.length - opts.visibleEnd - 1);
13
+ const endLen = Math.min(opts.visibleEnd, value.length - startLen - 1);
14
+ const maskedLen = Math.max(4, value.length - startLen - endLen);
15
+ const start = value.slice(0, startLen);
16
+ const end = value.slice(-endLen);
17
+ const mask = opts.maskChar.repeat(maskedLen);
18
+ return `${start}${mask}${end}`;
19
+ }
20
+ var SECRET_INDICATORS = [
21
+ /^[a-zA-Z0-9]{20,}$/,
22
+ // Long alphanumeric strings (20+ chars)
23
+ /^[a-f0-9]{32,}$/,
24
+ // Hex strings (32+ chars, likely hashes/keys)
25
+ /^sk-[a-zA-Z0-9]{20,}$/,
26
+ // Stripe-style API keys
27
+ /^sk_live_[a-zA-Z0-9]{20,}$/,
28
+ // Stripe live keys
29
+ /^sk_test_[a-zA-Z0-9]{20,}$/,
30
+ // Stripe test keys
31
+ /^key-[a-zA-Z0-9]{16,}$/,
32
+ // Generic key- prefixed
33
+ /^token-[a-zA-Z0-9]{16,}$/,
34
+ // Generic token- prefixed
35
+ /^pk_[a-zA-Z0-9]{20,}$/,
36
+ // Public key patterns
37
+ /^secret_[a-zA-Z0-9]{16,}$/,
38
+ // Secret key patterns
39
+ /^[A-Za-z0-9+/]{40,}={0,2}$/,
40
+ // Base64-like strings (40+ chars)
41
+ /^[A-Za-z0-9_-]{40,}$/
42
+ // Base64url-like strings (40+ chars)
43
+ ];
44
+ var SECRET_KEY_PATTERNS = [
45
+ /password/i,
46
+ /passwd/i,
47
+ /secret/i,
48
+ /token/i,
49
+ /api[_-]?key/i,
50
+ /private[_-]?key/i,
51
+ /access[_-]?key/i,
52
+ /auth/i,
53
+ /credential/i,
54
+ /certificate/i,
55
+ /private/i
56
+ ];
57
+ function isSecretLike(value) {
58
+ if (value.length === 0) return false;
59
+ if (value.length < 16) return false;
60
+ for (const pattern of SECRET_INDICATORS) {
61
+ if (pattern.test(value)) return true;
62
+ }
63
+ return false;
64
+ }
65
+ function isSecretKey(key) {
66
+ return SECRET_KEY_PATTERNS.some((pattern) => pattern.test(key));
67
+ }
68
+
69
+ export {
70
+ maskValue,
71
+ isSecretLike,
72
+ isSecretKey
73
+ };
@@ -0,0 +1,101 @@
1
+ // src/cli/ui/box.ts
2
+ var DEFAULT_BOX_OPTIONS = {
3
+ title: "",
4
+ border: "single",
5
+ padding: 1,
6
+ width: 0,
7
+ titleAlign: "left"
8
+ };
9
+ var BORDERS = {
10
+ single: {
11
+ topLeft: "\u250C",
12
+ topRight: "\u2510",
13
+ bottomLeft: "\u2514",
14
+ bottomRight: "\u2518",
15
+ horizontal: "\u2500",
16
+ vertical: "\u2502",
17
+ titleLeft: "\u251C",
18
+ titleRight: "\u2524",
19
+ titleHorizontal: "\u2500"
20
+ },
21
+ double: {
22
+ topLeft: "\u2554",
23
+ topRight: "\u2557",
24
+ bottomLeft: "\u255A",
25
+ bottomRight: "\u255D",
26
+ horizontal: "\u2550",
27
+ vertical: "\u2551",
28
+ titleLeft: "\u2560",
29
+ titleRight: "\u2563",
30
+ titleHorizontal: "\u2550"
31
+ },
32
+ rounded: {
33
+ topLeft: "\u256D",
34
+ topRight: "\u256E",
35
+ bottomLeft: "\u2570",
36
+ bottomRight: "\u256F",
37
+ horizontal: "\u2500",
38
+ vertical: "\u2502",
39
+ titleLeft: "\u251C",
40
+ titleRight: "\u2524",
41
+ titleHorizontal: "\u2500"
42
+ }
43
+ };
44
+ function visibleWidth(text) {
45
+ return text.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").length;
46
+ }
47
+ function padVisible(text, width) {
48
+ const visible = visibleWidth(text);
49
+ const diff = width - visible;
50
+ if (diff <= 0) return text;
51
+ return text + " ".repeat(diff);
52
+ }
53
+ function drawBox(lines, options) {
54
+ const opts = { ...DEFAULT_BOX_OPTIONS, ...options };
55
+ const chars = BORDERS[opts.border];
56
+ let contentWidth = 0;
57
+ for (const line of lines) {
58
+ const w = visibleWidth(line);
59
+ if (w > contentWidth) contentWidth = w;
60
+ }
61
+ const innerWidth = contentWidth + opts.padding * 2;
62
+ const boxInnerWidth = opts.width > 0 ? opts.width - 2 : innerWidth;
63
+ const boxContentWidth = boxInnerWidth - opts.padding * 2;
64
+ if (boxContentWidth < contentWidth) {
65
+ contentWidth = boxContentWidth;
66
+ }
67
+ const finalInnerWidth = contentWidth + opts.padding * 2;
68
+ const result = [];
69
+ if (opts.title && opts.title.length > 0) {
70
+ const titleDisplay = ` ${opts.title} `;
71
+ const titleVisible = visibleWidth(titleDisplay);
72
+ const leftWidth = Math.max(2, Math.floor((finalInnerWidth - titleVisible) / 2));
73
+ let titleLine;
74
+ if (opts.titleAlign === "center") {
75
+ const left = chars.titleHorizontal.repeat(leftWidth);
76
+ const right = chars.titleHorizontal.repeat(finalInnerWidth - titleVisible - leftWidth);
77
+ titleLine = `${chars.topLeft}${left}${titleDisplay}${right}${chars.topRight}`;
78
+ } else if (opts.titleAlign === "right") {
79
+ const right = chars.titleHorizontal.repeat(2);
80
+ const left = chars.titleHorizontal.repeat(finalInnerWidth - titleVisible - 2);
81
+ titleLine = `${chars.topLeft}${left}${titleDisplay}${right}${chars.topRight}`;
82
+ } else {
83
+ const right = chars.titleHorizontal.repeat(Math.max(2, finalInnerWidth - titleVisible));
84
+ titleLine = `${chars.topLeft}${titleDisplay}${right}${chars.topRight}`;
85
+ }
86
+ result.push(titleLine);
87
+ } else {
88
+ result.push(`${chars.topLeft}${chars.horizontal.repeat(finalInnerWidth)}${chars.topRight}`);
89
+ }
90
+ for (const line of lines) {
91
+ const paddedLine = padVisible(line, boxContentWidth);
92
+ const paddingStr = " ".repeat(opts.padding);
93
+ result.push(`${chars.vertical}${paddingStr}${paddedLine}${paddingStr}${chars.vertical}`);
94
+ }
95
+ result.push(`${chars.bottomLeft}${chars.horizontal.repeat(finalInnerWidth)}${chars.bottomRight}`);
96
+ return result.join("\n");
97
+ }
98
+
99
+ export {
100
+ drawBox
101
+ };