@fire-spark/cli 0.0.0-stage → 0.13.2

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.
@@ -0,0 +1,71 @@
1
+ export const REDACTED = "[redacted]";
2
+
3
+ const SECRET_NAME =
4
+ /(KEY|TOKEN|SECRET|PASSWORD|PASSWD|PRIVATE|CREDENTIAL|AUTH)/i;
5
+
6
+ // Values shorter than this are too likely to collide with ordinary words.
7
+ const MIN_SECRET_LENGTH = 6;
8
+
9
+ const PATTERNS: RegExp[] = [
10
+ /\beyJ[\w-]{6,}\.[\w-]{6,}\.[\w-]{6,}\b/g,
11
+ /\b(Bearer|Basic)\s+[\w.~+/=-]{8,}/gi,
12
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
13
+ /\b(sk|pk|rk)_(live|test)_[\w]{8,}\b/g,
14
+ /\bAIza[\w-]{30,}\b/g,
15
+ /\bgh[pousr]_[\w]{20,}\b/g,
16
+ /\bsb_(secret|publishable)_[\w-]{8,}\b/g,
17
+ /\bp\.ey[\w.-]{20,}\b/g
18
+ ];
19
+
20
+ const URL_CREDENTIALS = /(\b[a-z][\w+.-]*:\/\/)([^\s/:@]+):([^\s/@]+)@/gi;
21
+ const QUERY_SECRET =
22
+ /([?&](?:key|token|apikey|api_key|secret|password|sig|signature|access_token)=)[^&\s"']+/gi;
23
+
24
+ export const isSecretName = (name: string) => SECRET_NAME.test(name);
25
+
26
+ const escapeRegExp = (value: string) => {
27
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
28
+ };
29
+
30
+ export interface Redactor {
31
+ add: (value: string | undefined) => void;
32
+ redact: (text: string) => string;
33
+ size: () => number;
34
+ }
35
+
36
+ export const createRedactor = (initial: Iterable<string> = []): Redactor => {
37
+ const secrets = new Set<string>();
38
+ const add = (value: string | undefined) => {
39
+ if (!value) return;
40
+ const trimmed = value.trim();
41
+ if (trimmed.length < MIN_SECRET_LENGTH) return;
42
+ secrets.add(trimmed);
43
+ const encoded = encodeURIComponent(trimmed);
44
+ if (encoded !== trimmed) secrets.add(encoded);
45
+ };
46
+ for (const value of initial) add(value);
47
+ const redact = (text: string) => {
48
+ let out = text;
49
+ const ordered = [...secrets].sort((a, b) => b.length - a.length);
50
+ for (const secret of ordered) {
51
+ out = out.replace(new RegExp(escapeRegExp(secret), "g"), REDACTED);
52
+ }
53
+ for (const pattern of PATTERNS) out = out.replace(pattern, REDACTED);
54
+ out = out.replace(URL_CREDENTIALS, `$1${REDACTED}@`);
55
+ return out.replace(QUERY_SECRET, `$1${REDACTED}`);
56
+ };
57
+ return { add, redact, size: () => secrets.size };
58
+ };
59
+
60
+ export const secretsFromEnv = (env: NodeJS.ProcessEnv) => {
61
+ const values: string[] = [];
62
+ for (const [name, value] of Object.entries(env)) {
63
+ if (value && isSecretName(name)) values.push(value);
64
+ }
65
+ return values;
66
+ };
67
+
68
+ export const redactError = (error: unknown, redactor: Redactor) => {
69
+ const message = error instanceof Error ? error.message : String(error);
70
+ return redactor.redact(message);
71
+ };
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { run } from "./main";
2
+ export type { RunIo } from "./main";
3
+ export { COMMANDS, findCommand } from "./commands";
4
+ export { createRedactor } from "./core/redact";
package/src/main.ts ADDED
@@ -0,0 +1,113 @@
1
+ import { COMMANDS, findCommand } from "./commands";
2
+ import type { Command } from "./core/command.types";
3
+ import { renderCommandHelp, renderRootHelp } from "./core/help";
4
+ import { resolveLocale, translate } from "./core/i18n";
5
+ import type { Locale } from "./core/i18n";
6
+ import { GLOBAL_OPTIONS, UsageError, parseOptions } from "./core/options";
7
+ import { resolveOptions, secretValues } from "./core/options";
8
+ import { createOutput } from "./core/output";
9
+ import type { Writer } from "./core/output";
10
+ import { createRedactor, redactError, secretsFromEnv } from "./core/redact";
11
+ import type { Redactor } from "./core/redact";
12
+
13
+ export interface RunIo {
14
+ env: NodeJS.ProcessEnv;
15
+ cwd: string;
16
+ stdout: Writer;
17
+ stderr: Writer;
18
+ isTty: boolean;
19
+ ask?: (question: string) => Promise<string>;
20
+ }
21
+
22
+ const UNKNOWN_COMMAND = {
23
+ "es-EC": "Comando desconocido: {name}",
24
+ "en-US": "Unknown command: {name}",
25
+ "pt-BR": "Comando desconhecido: {name}"
26
+ };
27
+
28
+ const USAGE_ERROR = {
29
+ "es-EC": "Error de uso: {message}",
30
+ "en-US": "Usage error: {message}",
31
+ "pt-BR": "Erro de uso: {message}"
32
+ };
33
+
34
+ const UNEXPECTED = {
35
+ "es-EC": "Error inesperado: {message}",
36
+ "en-US": "Unexpected error: {message}",
37
+ "pt-BR": "Erro inesperado: {message}"
38
+ };
39
+
40
+ const defaultIo = (): RunIo => {
41
+ return {
42
+ env: process.env,
43
+ cwd: process.cwd(),
44
+ stdout: process.stdout,
45
+ stderr: process.stderr,
46
+ isTty: Boolean(process.stdout.isTTY)
47
+ };
48
+ };
49
+
50
+ const peekLocale = (argv: string[], env: NodeJS.ProcessEnv) => {
51
+ const index = argv.findIndex(arg => arg === "--locale");
52
+ const inline = argv.find(arg => arg.startsWith("--locale="));
53
+ const flag = inline ? inline.slice("--locale=".length) : argv[index + 1];
54
+ return resolveLocale(index >= 0 || inline ? flag : undefined, env);
55
+ };
56
+
57
+ interface CommandRun {
58
+ command: Command;
59
+ argv: string[];
60
+ io: RunIo;
61
+ locale: Locale;
62
+ redactor: Redactor;
63
+ }
64
+
65
+ const runCommand = async (input: CommandRun) => {
66
+ const { command, argv, io, locale, redactor } = input;
67
+ const specs = [...command.options, ...GLOBAL_OPTIONS];
68
+ const { values, positionals } = parseOptions(argv, specs);
69
+ const options = resolveOptions(specs, values, io.env);
70
+ for (const secret of secretValues(specs, options)) redactor.add(secret);
71
+ const colors = io.isTty && !options["no-color"];
72
+ const output = createOutput(io.stdout, io.stderr, redactor, colors);
73
+ if (options.help) {
74
+ output.line(renderCommandHelp(command, locale));
75
+ return 0;
76
+ }
77
+ const { env, cwd, ask, isTty } = io;
78
+ const context = { locale, options, positionals, env, cwd, output, redactor };
79
+ return command.run({ ...context, isTty, ask });
80
+ };
81
+
82
+ // `pnpm run doctor -- --live` forwards the separator itself.
83
+ const dropSeparators = (argv: string[]) => argv.filter(arg => arg !== "--");
84
+
85
+ export const run = async (input: string[], io: RunIo = defaultIo()) => {
86
+ const argv = dropSeparators(input);
87
+ const locale = peekLocale(argv, io.env);
88
+ const redactor = createRedactor(secretsFromEnv(io.env));
89
+ const output = createOutput(io.stdout, io.stderr, redactor, false);
90
+ const [name, ...rest] = argv;
91
+ if (!name || name === "--help" || name === "-h" || name === "help") {
92
+ output.line(renderRootHelp(COMMANDS, locale));
93
+ return 0;
94
+ }
95
+ const command = findCommand(name);
96
+ if (!command) {
97
+ output.error(translate(locale, UNKNOWN_COMMAND, { name }));
98
+ output.error(renderRootHelp(COMMANDS, locale));
99
+ return 2;
100
+ }
101
+ try {
102
+ const input = { command, argv: rest, io, locale, redactor };
103
+ return await runCommand(input);
104
+ } catch (error) {
105
+ const message = redactError(error, redactor);
106
+ if (error instanceof UsageError) {
107
+ output.error(translate(locale, USAGE_ERROR, { message }));
108
+ return 2;
109
+ }
110
+ output.error(translate(locale, UNEXPECTED, { message }));
111
+ return 1;
112
+ }
113
+ };
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.