@maz-ui/node 5.0.0-beta.4 → 5.0.0-beta.44

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.
package/dist/index.d.mts CHANGED
@@ -1,3 +1,4 @@
1
+ import { ExecOptions } from 'node:child_process';
1
2
  import * as consola from 'consola';
2
3
  import { ConsolaOptions, InputLogObject, ConsolaReporter, ConsolaInstance } from 'consola';
3
4
  export { ConsolaOptions } from 'consola';
@@ -51,28 +52,88 @@ interface CustomLogger {
51
52
  warn: (message: string, ...args: any[]) => void;
52
53
  debug: (message: string, ...args: any[]) => void;
53
54
  }
54
- declare function execPromise(command: string, { logger, packageName, noSuccess, noStdout, noStderr, noError, logLevel, cwd, }?: {
55
+ /**
56
+ * Options for {@link execPromise}.
57
+ *
58
+ * The following fields are forwarded as-is to `child_process.exec`:
59
+ * - `cwd` - working directory of the command. Defaults to the current one.
60
+ * - `env` - environment variables, **merged on top of `process.env`**.
61
+ * - `timeout` - maximum run time in milliseconds before the command is killed. No timeout by default.
62
+ * - `maxBuffer` - maximum bytes allowed on stdout/stderr before the command is killed. Defaults to `1024 * 1024` (1 MiB).
63
+ * - `killSignal` - signal used to kill the command on timeout or abort. Defaults to `'SIGTERM'`.
64
+ * - `shell` - shell used to run the command. Defaults to `/bin/sh` (Unix) or `cmd.exe` (Windows).
65
+ * - `signal` - an `AbortSignal` that, once aborted, kills the command and rejects the promise.
66
+ */
67
+ type ExecPromiseOptions = Pick<ExecOptions, 'cwd' | 'timeout' | 'env' | 'maxBuffer' | 'killSignal' | 'shell' | 'signal'> & {
68
+ /**
69
+ * Custom logger used for every output. Falls back to the package logger.
70
+ */
55
71
  logger?: CustomLogger;
72
+ /**
73
+ * Prefix added to every log line, formatted as `[packageName]: `.
74
+ */
56
75
  packageName?: string;
57
76
  /**
58
- * Don't log success message
77
+ * Don't log the success message.
78
+ * @default false
59
79
  */
60
80
  noSuccess?: boolean;
61
81
  /**
62
- * Don't log stdout
82
+ * Don't log stdout.
83
+ * @default false
63
84
  */
64
85
  noStdout?: boolean;
65
86
  /**
66
- * Don't log stderr
87
+ * Don't log stderr.
88
+ * @default false
67
89
  */
68
90
  noStderr?: boolean;
69
91
  /**
70
- * Don't log error
92
+ * Don't log the error when the command fails.
93
+ * @default false
71
94
  */
72
95
  noError?: boolean;
96
+ /**
97
+ * Logging level applied to the default logger before running the command.
98
+ */
73
99
  logLevel?: LogLevel;
74
- cwd?: string;
75
- }): Promise<{
100
+ };
101
+ /**
102
+ * Runs a shell command and resolves with its `stdout` and `stderr`.
103
+ *
104
+ * Secrets (npm auth tokens, `--token`/`--password` flags, provider tokens,
105
+ * JWTs, basic-auth URLs) are automatically masked in every log line and in the
106
+ * rejected error via {@link redactSecrets}. The resolved `stdout`/`stderr` are
107
+ * returned untouched.
108
+ *
109
+ * @param command - The shell command to execute.
110
+ * @returns A promise resolving with the command `stdout` and `stderr`.
111
+ * @throws If the command exits with a non-zero code, times out, is aborted, or
112
+ * exceeds `maxBuffer`. The rejected error is redacted.
113
+ *
114
+ * @example Basic usage
115
+ * ```ts
116
+ * const { stdout } = await execPromise('node --version')
117
+ * ```
118
+ *
119
+ * @example Fail the command after 5 seconds
120
+ * ```ts
121
+ * await execPromise('pnpm install', { timeout: 5000 })
122
+ * ```
123
+ *
124
+ * @example Inject environment variables (merged on top of `process.env`)
125
+ * ```ts
126
+ * await execPromise('node build.mjs', { env: { NODE_ENV: 'production' } })
127
+ * ```
128
+ *
129
+ * @example Cancel the command with an `AbortController`
130
+ * ```ts
131
+ * const controller = new AbortController()
132
+ * const promise = execPromise('long-task', { signal: controller.signal })
133
+ * controller.abort()
134
+ * ```
135
+ */
136
+ declare function execPromise(command: string, { logger, packageName, noSuccess, noStdout, noStderr, noError, logLevel, cwd, timeout, env, maxBuffer, killSignal, shell, signal, }?: ExecPromiseOptions): Promise<{
76
137
  stdout: string;
77
138
  stderr: string;
78
139
  }>;
@@ -88,5 +149,24 @@ declare function printBanner({ name, version, options, }: {
88
149
  };
89
150
  }): void;
90
151
 
91
- export { createLogger, execPromise, logger, printBanner };
92
- export type { LogLevel, Logger, LoggerOptions };
152
+ /**
153
+ * Masks secrets that have a recognizable syntactic context inside a string.
154
+ *
155
+ * Only deterministic patterns are redacted (npm auth tokens, named secret flags
156
+ * such as `--token`/`--password`, prefixed provider tokens like `npm_`/`ghp_`,
157
+ * JWTs, and basic-auth credentials in URLs). Context-free random strings are left
158
+ * untouched because they cannot be detected without false positives.
159
+ *
160
+ * Tokens long enough (>= 16 chars) keep their first and last 4 characters visible
161
+ * (e.g. `npm_***J1iF`) so the right credential can still be recognized; shorter
162
+ * secrets are fully masked.
163
+ */
164
+ declare function redactSecrets(input: string): string;
165
+ /**
166
+ * Returns the given error with its `message`, `cmd` and `stack` redacted via
167
+ * {@link redactSecrets}, so a thrown command never carries secrets to callers.
168
+ */
169
+ declare function redactError<T>(error: T): T;
170
+
171
+ export { createLogger, execPromise, logger, printBanner, redactError, redactSecrets };
172
+ export type { ExecPromiseOptions, LogLevel, Logger, LoggerOptions };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { ExecOptions } from 'node:child_process';
1
2
  import * as consola from 'consola';
2
3
  import { ConsolaOptions, InputLogObject, ConsolaReporter, ConsolaInstance } from 'consola';
3
4
  export { ConsolaOptions } from 'consola';
@@ -51,28 +52,88 @@ interface CustomLogger {
51
52
  warn: (message: string, ...args: any[]) => void;
52
53
  debug: (message: string, ...args: any[]) => void;
53
54
  }
54
- declare function execPromise(command: string, { logger, packageName, noSuccess, noStdout, noStderr, noError, logLevel, cwd, }?: {
55
+ /**
56
+ * Options for {@link execPromise}.
57
+ *
58
+ * The following fields are forwarded as-is to `child_process.exec`:
59
+ * - `cwd` - working directory of the command. Defaults to the current one.
60
+ * - `env` - environment variables, **merged on top of `process.env`**.
61
+ * - `timeout` - maximum run time in milliseconds before the command is killed. No timeout by default.
62
+ * - `maxBuffer` - maximum bytes allowed on stdout/stderr before the command is killed. Defaults to `1024 * 1024` (1 MiB).
63
+ * - `killSignal` - signal used to kill the command on timeout or abort. Defaults to `'SIGTERM'`.
64
+ * - `shell` - shell used to run the command. Defaults to `/bin/sh` (Unix) or `cmd.exe` (Windows).
65
+ * - `signal` - an `AbortSignal` that, once aborted, kills the command and rejects the promise.
66
+ */
67
+ type ExecPromiseOptions = Pick<ExecOptions, 'cwd' | 'timeout' | 'env' | 'maxBuffer' | 'killSignal' | 'shell' | 'signal'> & {
68
+ /**
69
+ * Custom logger used for every output. Falls back to the package logger.
70
+ */
55
71
  logger?: CustomLogger;
72
+ /**
73
+ * Prefix added to every log line, formatted as `[packageName]: `.
74
+ */
56
75
  packageName?: string;
57
76
  /**
58
- * Don't log success message
77
+ * Don't log the success message.
78
+ * @default false
59
79
  */
60
80
  noSuccess?: boolean;
61
81
  /**
62
- * Don't log stdout
82
+ * Don't log stdout.
83
+ * @default false
63
84
  */
64
85
  noStdout?: boolean;
65
86
  /**
66
- * Don't log stderr
87
+ * Don't log stderr.
88
+ * @default false
67
89
  */
68
90
  noStderr?: boolean;
69
91
  /**
70
- * Don't log error
92
+ * Don't log the error when the command fails.
93
+ * @default false
71
94
  */
72
95
  noError?: boolean;
96
+ /**
97
+ * Logging level applied to the default logger before running the command.
98
+ */
73
99
  logLevel?: LogLevel;
74
- cwd?: string;
75
- }): Promise<{
100
+ };
101
+ /**
102
+ * Runs a shell command and resolves with its `stdout` and `stderr`.
103
+ *
104
+ * Secrets (npm auth tokens, `--token`/`--password` flags, provider tokens,
105
+ * JWTs, basic-auth URLs) are automatically masked in every log line and in the
106
+ * rejected error via {@link redactSecrets}. The resolved `stdout`/`stderr` are
107
+ * returned untouched.
108
+ *
109
+ * @param command - The shell command to execute.
110
+ * @returns A promise resolving with the command `stdout` and `stderr`.
111
+ * @throws If the command exits with a non-zero code, times out, is aborted, or
112
+ * exceeds `maxBuffer`. The rejected error is redacted.
113
+ *
114
+ * @example Basic usage
115
+ * ```ts
116
+ * const { stdout } = await execPromise('node --version')
117
+ * ```
118
+ *
119
+ * @example Fail the command after 5 seconds
120
+ * ```ts
121
+ * await execPromise('pnpm install', { timeout: 5000 })
122
+ * ```
123
+ *
124
+ * @example Inject environment variables (merged on top of `process.env`)
125
+ * ```ts
126
+ * await execPromise('node build.mjs', { env: { NODE_ENV: 'production' } })
127
+ * ```
128
+ *
129
+ * @example Cancel the command with an `AbortController`
130
+ * ```ts
131
+ * const controller = new AbortController()
132
+ * const promise = execPromise('long-task', { signal: controller.signal })
133
+ * controller.abort()
134
+ * ```
135
+ */
136
+ declare function execPromise(command: string, { logger, packageName, noSuccess, noStdout, noStderr, noError, logLevel, cwd, timeout, env, maxBuffer, killSignal, shell, signal, }?: ExecPromiseOptions): Promise<{
76
137
  stdout: string;
77
138
  stderr: string;
78
139
  }>;
@@ -88,5 +149,24 @@ declare function printBanner({ name, version, options, }: {
88
149
  };
89
150
  }): void;
90
151
 
91
- export { createLogger, execPromise, logger, printBanner };
92
- export type { LogLevel, Logger, LoggerOptions };
152
+ /**
153
+ * Masks secrets that have a recognizable syntactic context inside a string.
154
+ *
155
+ * Only deterministic patterns are redacted (npm auth tokens, named secret flags
156
+ * such as `--token`/`--password`, prefixed provider tokens like `npm_`/`ghp_`,
157
+ * JWTs, and basic-auth credentials in URLs). Context-free random strings are left
158
+ * untouched because they cannot be detected without false positives.
159
+ *
160
+ * Tokens long enough (>= 16 chars) keep their first and last 4 characters visible
161
+ * (e.g. `npm_***J1iF`) so the right credential can still be recognized; shorter
162
+ * secrets are fully masked.
163
+ */
164
+ declare function redactSecrets(input: string): string;
165
+ /**
166
+ * Returns the given error with its `message`, `cmd` and `stack` redacted via
167
+ * {@link redactSecrets}, so a thrown command never carries secrets to callers.
168
+ */
169
+ declare function redactError<T>(error: T): T;
170
+
171
+ export { createLogger, execPromise, logger, printBanner, redactError, redactSecrets };
172
+ export type { ExecPromiseOptions, LogLevel, Logger, LoggerOptions };
package/dist/index.mjs CHANGED
@@ -53,6 +53,43 @@ function createLogger(options) {
53
53
  }
54
54
  const logger = createLogger();
55
55
 
56
+ const MASK = "***";
57
+ const VISIBLE_PREFIX = 4;
58
+ const MIN_LENGTH_TO_REVEAL = 16;
59
+ const SECRET_FLAG_NAMES = "password|passwd|pwd|pass|token|api[-_]?key|secret|authorization|auth[-_]?token|access[-_]?token|refresh[-_]?token|client[-_]?secret|private[-_]?key|credentials?|auth";
60
+ const keepEnds = (value) => value.length < MIN_LENGTH_TO_REVEAL ? MASK : `${value.slice(0, VISIBLE_PREFIX)}${MASK}${value.slice(-4)}`;
61
+ const keepPrefix = (_match, prefix, value) => `${prefix}${keepEnds(value)}`;
62
+ const REDACTION_RULES = [
63
+ [/(:?_authToken=)([^\s&]+)/gi, keepPrefix],
64
+ [new RegExp(`(--(?:${SECRET_FLAG_NAMES})=)(\\S+)`, "gi"), keepPrefix],
65
+ [new RegExp(`(--(?:${SECRET_FLAG_NAMES})\\s+)(?!-)(\\S+)`, "gi"), keepPrefix],
66
+ [/(\/\/[^/:@\s]+:)([^/@\s]+)@/g, (_match, prefix, value) => `${prefix}${keepEnds(value)}@`],
67
+ [/npm_[A-Za-z0-9]{10,}/g, keepEnds],
68
+ [/gh[posru]_[A-Za-z0-9]{20,}/g, keepEnds],
69
+ [/github_pat_\w{20,}/g, keepEnds],
70
+ [/glpat-[\w-]{15,}/g, keepEnds],
71
+ [/xox[baprs]-[A-Za-z0-9-]{10,}/g, keepEnds],
72
+ [/AKIA[0-9A-Z]{16}/g, keepEnds],
73
+ [/sk-[\w-]{16,}/g, keepEnds],
74
+ [/eyJ[\w-]+\.eyJ[\w-]+\.[\w-]+/g, keepEnds]
75
+ ];
76
+ const REDACTABLE_ERROR_KEYS = ["message", "cmd", "stack"];
77
+ function redactSecrets(input) {
78
+ return REDACTION_RULES.reduce((acc, [pattern, replacement]) => acc.replace(pattern, replacement), input);
79
+ }
80
+ function redactError(error) {
81
+ if (!error || typeof error !== "object") {
82
+ return error;
83
+ }
84
+ for (const key of REDACTABLE_ERROR_KEYS) {
85
+ const value = error[key];
86
+ if (typeof value === "string") {
87
+ error[key] = redactSecrets(value);
88
+ }
89
+ }
90
+ return error;
91
+ }
92
+
56
93
  async function execPromise(command, {
57
94
  logger: logger$1,
58
95
  packageName,
@@ -61,35 +98,52 @@ async function execPromise(command, {
61
98
  noStderr = false,
62
99
  noError = false,
63
100
  logLevel,
64
- cwd
101
+ cwd,
102
+ timeout,
103
+ env,
104
+ maxBuffer,
105
+ killSignal,
106
+ shell,
107
+ signal
65
108
  } = {}) {
66
109
  if (logLevel) {
67
110
  logger.setLevel(logLevel);
68
111
  }
69
112
  const internalLogger = logger$1 ?? logger;
70
113
  const packageNameStr = packageName ? `[${packageName}]: ` : "";
114
+ const safeCommand = redactSecrets(command);
71
115
  return await new Promise((resolve, reject) => {
72
- exec(command, { cwd }, (error, stdout, stderr) => {
116
+ exec(command, {
117
+ cwd,
118
+ timeout,
119
+ maxBuffer,
120
+ killSignal,
121
+ shell,
122
+ signal,
123
+ env: env ? { ...process$1.env, ...env } : void 0
124
+ }, (error, stdout, stderr) => {
73
125
  if (stdout) {
74
- internalLogger.debug(`${command} - stdout output:`, stdout);
126
+ internalLogger.debug(`${safeCommand} - stdout output:`, redactSecrets(stdout));
75
127
  }
76
128
  if (stderr) {
77
- internalLogger.debug(`${command} - stderr output:`, stderr);
129
+ internalLogger.debug(`${safeCommand} - stderr output:`, redactSecrets(stderr));
78
130
  }
79
131
  if (stdout && !noStdout) {
80
- internalLogger.log(`${packageNameStr}stdout -`, stdout.trim());
132
+ internalLogger.log(`${packageNameStr}stdout -`, redactSecrets(stdout.trim()));
81
133
  }
82
134
  if (stderr && !noStderr) {
83
- internalLogger.log(`${packageNameStr}stderr -`, stderr.trim());
135
+ internalLogger.log(`${packageNameStr}stderr -`, redactSecrets(stderr.trim()));
84
136
  }
85
137
  if (error) {
138
+ const safeError = redactError(error);
86
139
  if (!noError) {
87
- internalLogger.error(`${packageNameStr}${command} failed`, error);
140
+ const reason = error.killed && timeout ? `timed out after ${timeout}ms` : "failed";
141
+ internalLogger.error(`${packageNameStr}${safeCommand} ${reason}`, safeError);
88
142
  }
89
- reject(error);
143
+ reject(safeError);
90
144
  } else {
91
145
  if (!noSuccess) {
92
- internalLogger.info(`${packageNameStr}${command} - Success!`);
146
+ internalLogger.info(`${packageNameStr}${safeCommand} - Success!`);
93
147
  }
94
148
  resolve({ stdout, stderr });
95
149
  }
@@ -126,4 +180,4 @@ function printBanner({
126
180
  logger.break();
127
181
  }
128
182
 
129
- export { createLogger, execPromise, logger, printBanner };
183
+ export { createLogger, execPromise, logger, printBanner, redactError, redactSecrets };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@maz-ui/node",
3
3
  "type": "module",
4
- "version": "5.0.0-beta.4",
4
+ "version": "5.0.0-beta.44",
5
5
  "description": "Node utilities for Maz-UI library",
6
6
  "author": "Louis Mazel <me@loicmazuel.com>",
7
7
  "license": "MIT",
@@ -25,7 +25,8 @@
25
25
  ".": {
26
26
  "monorepo:dev": "./src/index.ts",
27
27
  "types": "./dist/index.d.ts",
28
- "import": "./dist/index.mjs"
28
+ "import": "./dist/index.mjs",
29
+ "default": "./dist/index.mjs"
29
30
  }
30
31
  },
31
32
  "main": "./dist/index.mjs",
@@ -41,7 +42,7 @@
41
42
  "dependencies": {
42
43
  "colorette": "^2.0.20",
43
44
  "consola": "^3.4.2",
44
- "figlet": "^1.11.0"
45
+ "figlet": "^1.12.0"
45
46
  },
46
47
  "devDependencies": {
47
48
  "unbuild": "^3.6.1"
@@ -52,7 +53,6 @@
52
53
  ]
53
54
  },
54
55
  "scripts": {
55
- "codegen": "nx build",
56
56
  "build": "unbuild",
57
57
  "dev": "unbuild --stub",
58
58
  "typecheck": "tsc --noEmit --skipLibCheck",