@triflare/fx 2.1.0-beta.4 → 2.1.0-beta.5
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/package.json +1 -1
- package/product.yml +1 -1
- package/src/cli/commands/vendor/outdated.js +1 -1
- package/src/cli/reporter.js +26 -18
- package/src/terminal.js +27 -0
- package/src/vendor.js +9 -7
package/package.json
CHANGED
package/product.yml
CHANGED
|
@@ -34,7 +34,7 @@ export const vendorOutdatedCommand = {
|
|
|
34
34
|
: result.status === "error"
|
|
35
35
|
? "danger"
|
|
36
36
|
: "success";
|
|
37
|
-
reporter.item(result.name,
|
|
37
|
+
reporter.item(result.name, label, { width: 24, tone });
|
|
38
38
|
if (result.error) reporter.warn(result.error);
|
|
39
39
|
}
|
|
40
40
|
if (outdated > 0)
|
package/src/cli/reporter.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { styleText } from "node:util";
|
|
10
10
|
import { brandText } from "../brand.js";
|
|
11
|
+
import { sanitizeTerminalText } from "../terminal.js";
|
|
11
12
|
import { formatColumns } from "./format.js";
|
|
12
13
|
import { createTheme } from "./theme.js";
|
|
13
14
|
|
|
@@ -60,7 +61,7 @@ export function createReporter({
|
|
|
60
61
|
const glyph = mark(symbol);
|
|
61
62
|
const stream = type === "success" ? stdout : stderr;
|
|
62
63
|
const theme = themeFor(stream);
|
|
63
|
-
const text =
|
|
64
|
+
const text = sanitizeTerminalText(message);
|
|
64
65
|
const output = text
|
|
65
66
|
.split("\n")
|
|
66
67
|
.map((line, index) => {
|
|
@@ -102,48 +103,54 @@ export function createReporter({
|
|
|
102
103
|
|
|
103
104
|
info(message) {
|
|
104
105
|
if (!human) return;
|
|
105
|
-
stdout.write(`${message}\n`);
|
|
106
|
+
stdout.write(`${sanitizeTerminalText(message)}\n`);
|
|
106
107
|
},
|
|
107
108
|
|
|
108
109
|
bullet(message) {
|
|
109
110
|
if (!human) return;
|
|
110
111
|
const theme = themeFor(stdout);
|
|
111
112
|
const glyph = mark("bullet");
|
|
112
|
-
|
|
113
|
+
const text = sanitizeTerminalText(message);
|
|
114
|
+
stdout.write(` ${glyph ? `${theme.accent(glyph)} ` : ""}${text}\n`);
|
|
113
115
|
},
|
|
114
116
|
|
|
115
117
|
step(message) {
|
|
116
118
|
if (!human) return;
|
|
117
119
|
const theme = themeFor(stdout);
|
|
118
120
|
const glyph = mark("progress");
|
|
119
|
-
|
|
121
|
+
const text = sanitizeTerminalText(message);
|
|
122
|
+
stdout.write(`${glyph ? `${theme.accent(glyph)} ` : ""}${text}\n`);
|
|
120
123
|
},
|
|
121
124
|
|
|
122
125
|
heading(message) {
|
|
123
126
|
if (!human) return;
|
|
124
|
-
stdout.write(
|
|
127
|
+
stdout.write(
|
|
128
|
+
`${themeFor(stdout).heading(sanitizeTerminalText(message))}\n`,
|
|
129
|
+
);
|
|
125
130
|
},
|
|
126
131
|
|
|
127
132
|
/** Aligned `label value` row used by command summaries. */
|
|
128
133
|
detail(label, value, { width = 13 } = {}) {
|
|
129
134
|
if (!human) return;
|
|
130
135
|
const theme = themeFor(stdout);
|
|
131
|
-
const text =
|
|
136
|
+
const text = sanitizeTerminalText(value);
|
|
132
137
|
const rendered = text === "(none)" ? theme.muted(text) : theme.bold(text);
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
);
|
|
138
|
+
const name = sanitizeTerminalText(label);
|
|
139
|
+
stdout.write(` ${theme.muted(name.padEnd(width))} ${rendered}\n`);
|
|
136
140
|
},
|
|
137
141
|
|
|
138
142
|
/** Aligned name + description row used by listings. */
|
|
139
|
-
item(label, value = "", { width = 24, suffix = "" } = {}) {
|
|
143
|
+
item(label, value = "", { width = 24, suffix = "", tone } = {}) {
|
|
140
144
|
if (!human) return;
|
|
141
145
|
const theme = themeFor(stdout);
|
|
142
|
-
const text =
|
|
146
|
+
const text = sanitizeTerminalText(label);
|
|
143
147
|
const padded = text.padEnd(Math.max(width, text.length));
|
|
144
|
-
const detail = value ? ` ${value}` : "";
|
|
145
|
-
const note = suffix
|
|
146
|
-
|
|
148
|
+
const detail = value ? ` ${sanitizeTerminalText(value)}` : "";
|
|
149
|
+
const note = suffix
|
|
150
|
+
? ` ${theme.muted(sanitizeTerminalText(suffix))}`
|
|
151
|
+
: "";
|
|
152
|
+
const rendered = tone ? theme.tone(tone, detail) : detail;
|
|
153
|
+
stdout.write(` ${theme.accent(padded)}${rendered}${note}\n`);
|
|
147
154
|
},
|
|
148
155
|
|
|
149
156
|
/** Compact multi-column rows for long block and file listings. */
|
|
@@ -171,7 +178,9 @@ export function createReporter({
|
|
|
171
178
|
/** Quiet, de-emphasized line for incidental notes. */
|
|
172
179
|
note(message) {
|
|
173
180
|
if (!human) return;
|
|
174
|
-
stdout.write(
|
|
181
|
+
stdout.write(
|
|
182
|
+
`${themeFor(stdout).muted(sanitizeTerminalText(message))}\n`,
|
|
183
|
+
);
|
|
175
184
|
},
|
|
176
185
|
|
|
177
186
|
warn(message) {
|
|
@@ -191,9 +200,8 @@ export function createReporter({
|
|
|
191
200
|
hint(message) {
|
|
192
201
|
if (!human) return;
|
|
193
202
|
const theme = themeFor(stdout);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
);
|
|
203
|
+
const text = sanitizeTerminalText(message);
|
|
204
|
+
stdout.write(` ${theme.accent("↳")} ${theme.muted(`hint: ${text}`)}\n`);
|
|
197
205
|
},
|
|
198
206
|
|
|
199
207
|
/** One JSON document, used for the command result envelope. */
|
package/src/terminal.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal output safety.
|
|
3
|
+
*
|
|
4
|
+
* Names, paths and source text can come from a repository the user did not
|
|
5
|
+
* write, so untrusted text is rendered with visible `\xNN` escapes instead of
|
|
6
|
+
* letting the terminal interpret control characters and escape sequences.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Replace terminal control characters with visible `\xNN` escapes.
|
|
11
|
+
*
|
|
12
|
+
* Tabs and newlines are kept because callers print multi-line blocks.
|
|
13
|
+
* @param {unknown} value
|
|
14
|
+
*/
|
|
15
|
+
export function sanitizeTerminalText(value) {
|
|
16
|
+
let clean = "";
|
|
17
|
+
for (const char of String(value)) {
|
|
18
|
+
const code = char.codePointAt(0);
|
|
19
|
+
const printable =
|
|
20
|
+
code === 0x09 ||
|
|
21
|
+
code === 0x0a ||
|
|
22
|
+
(code >= 0x20 && code <= 0x7e) ||
|
|
23
|
+
code >= 0xa0;
|
|
24
|
+
clean += printable ? char : `\\x${code.toString(16).padStart(2, "0")}`;
|
|
25
|
+
}
|
|
26
|
+
return clean;
|
|
27
|
+
}
|
package/src/vendor.js
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
readExtensionDataUri,
|
|
24
24
|
resolveSource,
|
|
25
25
|
} from "./extensions.js";
|
|
26
|
+
import { sanitizeTerminalText } from "./terminal.js";
|
|
26
27
|
|
|
27
28
|
export const LOCK_FILENAME = "fx.lock";
|
|
28
29
|
export const LOCK_VERSION = 1;
|
|
@@ -1146,13 +1147,14 @@ export async function loadVendorExtensions({
|
|
|
1146
1147
|
|
|
1147
1148
|
/** A compact line diff, enough to review an extension or config change. */
|
|
1148
1149
|
export function formatDiff(before, after, filename = "") {
|
|
1150
|
+
const header = sanitizeTerminalText(filename);
|
|
1149
1151
|
const a = String(before).split(/\r?\n/);
|
|
1150
1152
|
const b = String(after).split(/\r?\n/);
|
|
1151
1153
|
const rows = a.length;
|
|
1152
1154
|
const columns = b.length;
|
|
1153
1155
|
// Keep very large extensions from allocating an enormous matrix.
|
|
1154
1156
|
if (rows * columns > 4_000_000) {
|
|
1155
|
-
return `--- ${
|
|
1157
|
+
return `--- ${header}\n+++ ${header}\n@@ source changed @@\n`;
|
|
1156
1158
|
}
|
|
1157
1159
|
const table = Array.from(
|
|
1158
1160
|
{ length: rows + 1 },
|
|
@@ -1166,24 +1168,24 @@ export function formatDiff(before, after, filename = "") {
|
|
|
1166
1168
|
: Math.max(table[i + 1][j], table[i][j + 1]);
|
|
1167
1169
|
}
|
|
1168
1170
|
}
|
|
1169
|
-
const lines = [`--- ${
|
|
1171
|
+
const lines = [`--- ${header}`, `+++ ${header}`];
|
|
1170
1172
|
let i = 0;
|
|
1171
1173
|
let j = 0;
|
|
1172
1174
|
while (i < rows && j < columns) {
|
|
1173
1175
|
if (a[i] === b[j]) {
|
|
1174
|
-
lines.push(` ${a[i]}`);
|
|
1176
|
+
lines.push(` ${sanitizeTerminalText(a[i])}`);
|
|
1175
1177
|
i += 1;
|
|
1176
1178
|
j += 1;
|
|
1177
1179
|
} else if (table[i + 1][j] >= table[i][j + 1]) {
|
|
1178
|
-
lines.push(`-${a[i]}`);
|
|
1180
|
+
lines.push(`-${sanitizeTerminalText(a[i])}`);
|
|
1179
1181
|
i += 1;
|
|
1180
1182
|
} else {
|
|
1181
|
-
lines.push(`+${b[j]}`);
|
|
1183
|
+
lines.push(`+${sanitizeTerminalText(b[j])}`);
|
|
1182
1184
|
j += 1;
|
|
1183
1185
|
}
|
|
1184
1186
|
}
|
|
1185
|
-
while (i < rows) lines.push(`-${a[i++]}`);
|
|
1186
|
-
while (j < columns) lines.push(`+${b[j++]}`);
|
|
1187
|
+
while (i < rows) lines.push(`-${sanitizeTerminalText(a[i++])}`);
|
|
1188
|
+
while (j < columns) lines.push(`+${sanitizeTerminalText(b[j++])}`);
|
|
1187
1189
|
return `${lines.join("\n")}\n`;
|
|
1188
1190
|
}
|
|
1189
1191
|
|