@profullstack/readm3 0.19.0 → 0.24.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.
- package/README.md +59 -6
- package/dist/blob-store.d.ts +6 -1
- package/dist/blob-store.d.ts.map +1 -1
- package/dist/blob-store.js +6 -1
- package/dist/blob-store.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/code.d.ts.map +1 -1
- package/dist/code.js +6 -1
- package/dist/code.js.map +1 -1
- package/dist/csv.d.ts +174 -0
- package/dist/csv.d.ts.map +1 -0
- package/dist/csv.js +489 -0
- package/dist/csv.js.map +1 -0
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +48 -2
- package/dist/mcp.js.map +1 -1
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +4 -1
- package/dist/print.js.map +1 -1
- package/dist/sync-schema.d.ts.map +1 -1
- package/dist/sync-schema.js +3 -3
- package/dist/sync-schema.js.map +1 -1
- package/dist/tree.d.ts +3 -1
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +8 -5
- package/dist/tree.js.map +1 -1
- package/dist/viewer.d.ts.map +1 -1
- package/dist/viewer.js +346 -56
- package/dist/viewer.js.map +1 -1
- package/package.json +3 -2
package/dist/csv.js
ADDED
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tables: CSV, TSV and their cousins, read into rows of strings and viewed as a grid.
|
|
3
|
+
*
|
|
4
|
+
* One module for every surface. The terminal reader draws the result with hqtui's data
|
|
5
|
+
* grid, the web viewer builds a <table> from it, `--print` lays it out as text, and the
|
|
6
|
+
* paste service uses `looksLikeTable` to recognise a pasted CSV. Nothing here touches a
|
|
7
|
+
* DOM, a terminal or a file, so all of it is tested without either.
|
|
8
|
+
*
|
|
9
|
+
* Inspired by tabiew (github.com/shshemi/tabiew): sort by any column, filter rows,
|
|
10
|
+
* open one row as a sheet, and per-column statistics. Querying is a small filter
|
|
11
|
+
* language rather than SQL: `city:london age>30 !draft`.
|
|
12
|
+
*/
|
|
13
|
+
export const TABLE_LANGUAGE = "csv";
|
|
14
|
+
export const DELIMITERS = [",", "\t", ";", "|"];
|
|
15
|
+
export const TABLE_EXTENSIONS = ["csv", "tsv", "tab", "psv"];
|
|
16
|
+
const TABLE_NAME = /\.(?:csv|tsv|tab|psv)$/i;
|
|
17
|
+
/** Whether a file name is one the table view opens. */
|
|
18
|
+
export function isTableName(name) {
|
|
19
|
+
return !!name && TABLE_NAME.test(name);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* RFC 4180 records: quoted fields may hold the delimiter, quotes ("" escapes one) and
|
|
23
|
+
* line breaks; CRLF, LF and lone CR all end a record; a leading BOM is dropped. One
|
|
24
|
+
* pass, no regex per character, so a 4 MB file parses in milliseconds.
|
|
25
|
+
*/
|
|
26
|
+
export function parseRecords(source, delimiter, limit = Infinity) {
|
|
27
|
+
const text = source.charCodeAt(0) === 0xfeff ? source.slice(1) : source;
|
|
28
|
+
const records = [];
|
|
29
|
+
let record = [];
|
|
30
|
+
let field = "";
|
|
31
|
+
let quoted = false;
|
|
32
|
+
let i = 0;
|
|
33
|
+
const n = text.length;
|
|
34
|
+
while (i < n) {
|
|
35
|
+
const ch = text[i];
|
|
36
|
+
if (quoted) {
|
|
37
|
+
if (ch === '"') {
|
|
38
|
+
if (text[i + 1] === '"') {
|
|
39
|
+
field += '"';
|
|
40
|
+
i += 2;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
quoted = false;
|
|
44
|
+
i++;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
field += ch;
|
|
48
|
+
i++;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (ch === '"' && field === "") {
|
|
52
|
+
quoted = true;
|
|
53
|
+
i++;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (ch === delimiter) {
|
|
57
|
+
record.push(field);
|
|
58
|
+
field = "";
|
|
59
|
+
i++;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (ch === "\n" || ch === "\r") {
|
|
63
|
+
record.push(field);
|
|
64
|
+
field = "";
|
|
65
|
+
records.push(record);
|
|
66
|
+
record = [];
|
|
67
|
+
if (records.length >= limit)
|
|
68
|
+
return records;
|
|
69
|
+
i += ch === "\r" && text[i + 1] === "\n" ? 2 : 1;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
field += ch;
|
|
73
|
+
i++;
|
|
74
|
+
}
|
|
75
|
+
if (field !== "" || record.length > 0) {
|
|
76
|
+
record.push(field);
|
|
77
|
+
records.push(record);
|
|
78
|
+
}
|
|
79
|
+
// Blank lines are not empty records; a trailing newline is not a last record.
|
|
80
|
+
return records.filter((r) => r.length > 1 || r[0] !== "");
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The delimiter a text uses: the candidate that splits the first lines into the same
|
|
84
|
+
* number of fields (more than one) most consistently. Ties go to the order of
|
|
85
|
+
* DELIMITERS, so a comma wins over a semicolon that merely appears in the data.
|
|
86
|
+
*/
|
|
87
|
+
export function sniffDelimiter(source, name) {
|
|
88
|
+
if (name && /\.(?:tsv|tab)$/i.test(name))
|
|
89
|
+
return "\t";
|
|
90
|
+
if (name && /\.psv$/i.test(name))
|
|
91
|
+
return "|";
|
|
92
|
+
return scoreDelimiters(source)[0]?.delimiter ?? ",";
|
|
93
|
+
}
|
|
94
|
+
function scoreDelimiters(source) {
|
|
95
|
+
const sample = source.slice(0, 64 * 1024);
|
|
96
|
+
const scored = DELIMITERS.map((delimiter) => {
|
|
97
|
+
const records = parseRecords(sample, delimiter, 50);
|
|
98
|
+
// The last sampled record may be cut off by the sample boundary.
|
|
99
|
+
const usable = sample.length < source.length && records.length > 2 ? records.slice(0, -1) : records;
|
|
100
|
+
const counts = new Map();
|
|
101
|
+
for (const record of usable)
|
|
102
|
+
counts.set(record.length, (counts.get(record.length) ?? 0) + 1);
|
|
103
|
+
let fields = 1;
|
|
104
|
+
let most = 0;
|
|
105
|
+
for (const [count, times] of counts)
|
|
106
|
+
if (times > most || (times === most && count > fields)) {
|
|
107
|
+
fields = count;
|
|
108
|
+
most = times;
|
|
109
|
+
}
|
|
110
|
+
return { delimiter, fields, consistency: usable.length ? most / usable.length : 0 };
|
|
111
|
+
});
|
|
112
|
+
return scored
|
|
113
|
+
.filter((s) => s.fields > 1)
|
|
114
|
+
// Ties go to more fields (a TSV whose cells each hold one comma), then to DELIMITERS order.
|
|
115
|
+
.sort((a, b) => b.consistency - a.consistency || b.fields - a.fields);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Whether pasted text with no file name is a table. Strict on purpose, because prose
|
|
119
|
+
* and code have commas too: at least three records, every sampled one with the same
|
|
120
|
+
* field count of two or more, and no line ending the way code does.
|
|
121
|
+
*/
|
|
122
|
+
export function looksLikeTable(source) {
|
|
123
|
+
const lines = source.slice(0, 64 * 1024).split(/\r?\n/).filter((line) => line.trim()).slice(0, 50);
|
|
124
|
+
if (lines.length < 3)
|
|
125
|
+
return false;
|
|
126
|
+
if (lines.some((line) => /[{};]\s*$|^\s*(?:\/\/|#|--)/.test(line) && !/\t/.test(line)))
|
|
127
|
+
return false;
|
|
128
|
+
const best = scoreDelimiters(source)[0];
|
|
129
|
+
if (!best || best.consistency < 1)
|
|
130
|
+
return false;
|
|
131
|
+
// Comma-separated prose ("Hello, world") has long cells full of spaces; data does not.
|
|
132
|
+
if (best.delimiter === ",") {
|
|
133
|
+
const records = parseRecords(source.slice(0, 16 * 1024), ",", 20);
|
|
134
|
+
const wordy = records.flat().filter((cell) => cell.trim().split(/\s+/).length > 6).length;
|
|
135
|
+
if (wordy > records.length)
|
|
136
|
+
return false;
|
|
137
|
+
// So does a sentence with one comma per line: the text after it starts with a space and a word.
|
|
138
|
+
const later = records.flatMap((record) => record.slice(1));
|
|
139
|
+
if (later.length && later.filter((cell) => /^ [A-Za-z]/.test(cell)).length / later.length >= 0.5)
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
const NUMBER = /^[-+]?(?:\d{1,3}(?:,\d{3})+|\d+)?(?:\.\d+)?(?:[eE][-+]?\d+)?%?$/;
|
|
145
|
+
const CURRENCY = /^[-+]?[$€£¥]\s?/;
|
|
146
|
+
const DATE = /^\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?$|^\d{1,2}\/\d{1,2}\/\d{2,4}$/;
|
|
147
|
+
const BOOLEAN = /^(?:true|false|yes|no|y|n|t|f)$/i;
|
|
148
|
+
/** A cell as a number, or NaN: thousands commas, a currency sign and a % are allowed. */
|
|
149
|
+
export function toNumber(cell) {
|
|
150
|
+
const trimmed = cell.trim().replace(CURRENCY, "");
|
|
151
|
+
if (!trimmed || !/\d/.test(trimmed) || !NUMBER.test(trimmed))
|
|
152
|
+
return NaN;
|
|
153
|
+
return Number(trimmed.replace(/,/g, "").replace(/%$/, ""));
|
|
154
|
+
}
|
|
155
|
+
function typeOfCell(cell) {
|
|
156
|
+
const value = cell.trim();
|
|
157
|
+
if (!value)
|
|
158
|
+
return null;
|
|
159
|
+
if (!Number.isNaN(toNumber(value)))
|
|
160
|
+
return "number";
|
|
161
|
+
if (DATE.test(value) && !Number.isNaN(Date.parse(value)))
|
|
162
|
+
return "date";
|
|
163
|
+
if (BOOLEAN.test(value))
|
|
164
|
+
return "boolean";
|
|
165
|
+
return "text";
|
|
166
|
+
}
|
|
167
|
+
/** A column's type: what at least 95% of its non-empty sampled cells are. */
|
|
168
|
+
export function inferType(cells) {
|
|
169
|
+
const counts = { number: 0, date: 0, boolean: 0, text: 0 };
|
|
170
|
+
let seen = 0;
|
|
171
|
+
const step = Math.max(1, Math.floor(cells.length / 2000));
|
|
172
|
+
for (let i = 0; i < cells.length; i += step) {
|
|
173
|
+
const type = typeOfCell(cells[i] ?? "");
|
|
174
|
+
if (!type)
|
|
175
|
+
continue;
|
|
176
|
+
counts[type]++;
|
|
177
|
+
seen++;
|
|
178
|
+
}
|
|
179
|
+
if (!seen)
|
|
180
|
+
return "text";
|
|
181
|
+
for (const type of ["number", "date", "boolean"])
|
|
182
|
+
if (counts[type] / seen >= 0.95)
|
|
183
|
+
return type;
|
|
184
|
+
return "text";
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Whether the first record names the columns. It does unless it looks like data: a
|
|
188
|
+
* value typed the way the rest of its column is (a number over numbers), a blank, or
|
|
189
|
+
* a repeated name.
|
|
190
|
+
*/
|
|
191
|
+
export function detectHeader(records) {
|
|
192
|
+
const [first, ...rest] = records;
|
|
193
|
+
if (!first || rest.length === 0)
|
|
194
|
+
return true;
|
|
195
|
+
if (first.some((cell) => !cell.trim()))
|
|
196
|
+
return false;
|
|
197
|
+
if (new Set(first.map((cell) => cell.trim().toLowerCase())).size !== first.length)
|
|
198
|
+
return false;
|
|
199
|
+
for (let c = 0; c < first.length; c++) {
|
|
200
|
+
const below = inferType(rest.slice(0, 200).map((record) => record[c] ?? ""));
|
|
201
|
+
if (below !== "text" && typeOfCell(first[c]) === below)
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
/** Spreadsheet column names: A … Z, AA, AB … for a file with no header. */
|
|
207
|
+
export function letterName(index) {
|
|
208
|
+
let name = "";
|
|
209
|
+
for (let n = index + 1; n > 0; n = Math.floor((n - 1) / 26))
|
|
210
|
+
name = String.fromCharCode(65 + ((n - 1) % 26)) + name;
|
|
211
|
+
return name;
|
|
212
|
+
}
|
|
213
|
+
export function parseTable(source, options = {}) {
|
|
214
|
+
const delimiter = options.delimiter ?? sniffDelimiter(source, options.name);
|
|
215
|
+
const records = parseRecords(source, delimiter);
|
|
216
|
+
const header = options.header ?? detectHeader(records);
|
|
217
|
+
const head = header ? records[0] ?? [] : [];
|
|
218
|
+
const body = header ? records.slice(1) : records;
|
|
219
|
+
const width = Math.max(head.length, ...body.slice(0, 5000).map((record) => record.length), 0);
|
|
220
|
+
// Ragged means "not the shape the header promised"; with no header, not the widest shape.
|
|
221
|
+
const expected = header ? head.length : width;
|
|
222
|
+
let ragged = 0;
|
|
223
|
+
const rows = body.map((record) => {
|
|
224
|
+
if (record.length !== expected)
|
|
225
|
+
ragged++;
|
|
226
|
+
if (record.length === width)
|
|
227
|
+
return record;
|
|
228
|
+
return record.length > width ? record.slice(0, width) : [...record, ...Array(width - record.length).fill("")];
|
|
229
|
+
});
|
|
230
|
+
const columns = [];
|
|
231
|
+
for (let c = 0; c < width; c++) {
|
|
232
|
+
const raw = head[c]?.trim();
|
|
233
|
+
columns.push({ name: raw || letterName(c), type: inferType(rows.map((row) => row[c])) });
|
|
234
|
+
}
|
|
235
|
+
return { delimiter, header, columns, rows, ragged };
|
|
236
|
+
}
|
|
237
|
+
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
|
|
238
|
+
/** Compares two cells the way their column's type reads; empty cells always sort last. */
|
|
239
|
+
export function compareCells(a, b, type) {
|
|
240
|
+
const ea = !a.trim();
|
|
241
|
+
const eb = !b.trim();
|
|
242
|
+
if (ea || eb)
|
|
243
|
+
return ea === eb ? 0 : ea ? 1 : -1;
|
|
244
|
+
if (type === "number") {
|
|
245
|
+
const d = toNumber(a) - toNumber(b);
|
|
246
|
+
if (!Number.isNaN(d) && d !== 0)
|
|
247
|
+
return d;
|
|
248
|
+
}
|
|
249
|
+
else if (type === "date") {
|
|
250
|
+
const d = Date.parse(a) - Date.parse(b);
|
|
251
|
+
if (!Number.isNaN(d) && d !== 0)
|
|
252
|
+
return d;
|
|
253
|
+
}
|
|
254
|
+
else if (type === "boolean") {
|
|
255
|
+
const truthy = (v) => (/^(?:true|yes|y|t)$/i.test(v.trim()) ? 1 : 0);
|
|
256
|
+
const d = truthy(b) - truthy(a);
|
|
257
|
+
if (d)
|
|
258
|
+
return d;
|
|
259
|
+
}
|
|
260
|
+
return collator.compare(a, b);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Parses a filter. Terms are separated by spaces and must all match. A term is free
|
|
264
|
+
* text (any cell contains it), `column:text` (that column contains it), or a
|
|
265
|
+
* comparison such as `age>30`, `price<=9.99`, `status=open`, `country!=US`. A leading
|
|
266
|
+
* `!` negates. Quotes keep spaces: `city:"new york"`. A column is named, or numbered
|
|
267
|
+
* from 1 (`#3>10`). Unknown column names fall back to free text.
|
|
268
|
+
*/
|
|
269
|
+
export function parseFilter(query, columns) {
|
|
270
|
+
const terms = [];
|
|
271
|
+
const tokens = query.match(/[^\s"]*"[^"]*"?\S*|\S+/g) ?? [];
|
|
272
|
+
const byName = new Map(columns.map((column, index) => [column.name.toLowerCase(), index]));
|
|
273
|
+
for (let token of tokens) {
|
|
274
|
+
let negate = false;
|
|
275
|
+
if (token.startsWith("!") && token.length > 1) {
|
|
276
|
+
negate = true;
|
|
277
|
+
token = token.slice(1);
|
|
278
|
+
}
|
|
279
|
+
const match = token.match(/^("[^"]+"|#\d+|[^:=!<>]+)(:|!=|>=|<=|=|>|<)(.*)$/);
|
|
280
|
+
const unquote = (s) => s.replace(/^"|"$/g, "");
|
|
281
|
+
if (match) {
|
|
282
|
+
const name = unquote(match[1]).toLowerCase();
|
|
283
|
+
const column = /^#\d+$/.test(name) ? Number(name.slice(1)) - 1 : byName.get(name);
|
|
284
|
+
if (column !== undefined && column >= 0 && column < columns.length) {
|
|
285
|
+
terms.push({ column, op: match[2], value: unquote(match[3]), negate });
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
terms.push({ column: null, op: ":", value: unquote(token), negate });
|
|
290
|
+
}
|
|
291
|
+
return terms.filter((term) => term.value !== "" || term.op !== ":");
|
|
292
|
+
}
|
|
293
|
+
function termMatches(row, term, columns) {
|
|
294
|
+
const needle = term.value.toLowerCase();
|
|
295
|
+
if (term.column === null)
|
|
296
|
+
return row.some((cell) => cell.toLowerCase().includes(needle));
|
|
297
|
+
const cell = row[term.column] ?? "";
|
|
298
|
+
const type = columns[term.column].type;
|
|
299
|
+
switch (term.op) {
|
|
300
|
+
case ":":
|
|
301
|
+
return cell.toLowerCase().includes(needle);
|
|
302
|
+
case "=":
|
|
303
|
+
return type === "number" && !Number.isNaN(toNumber(term.value))
|
|
304
|
+
? toNumber(cell) === toNumber(term.value)
|
|
305
|
+
: cell.trim().toLowerCase() === needle;
|
|
306
|
+
case "!=":
|
|
307
|
+
return type === "number" && !Number.isNaN(toNumber(term.value))
|
|
308
|
+
? toNumber(cell) !== toNumber(term.value)
|
|
309
|
+
: cell.trim().toLowerCase() !== needle;
|
|
310
|
+
default: {
|
|
311
|
+
if (!cell.trim())
|
|
312
|
+
return false;
|
|
313
|
+
// Two numbers compare as numbers whatever the column was typed as.
|
|
314
|
+
const numeric = !Number.isNaN(toNumber(cell)) && !Number.isNaN(toNumber(term.value));
|
|
315
|
+
const order = compareCells(cell, term.value, numeric ? "number" : type);
|
|
316
|
+
return term.op === ">" ? order > 0 : term.op === ">=" ? order >= 0 : term.op === "<" ? order < 0 : order <= 0;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* The rows to show, as indices into `table.rows`: filtered, then sorted. The sort is
|
|
322
|
+
* stable, so sorting by one column and then another keeps ties in the earlier order.
|
|
323
|
+
*/
|
|
324
|
+
export function viewRows(table, options = {}) {
|
|
325
|
+
const terms = options.filter ? parseFilter(options.filter, table.columns) : [];
|
|
326
|
+
const indices = [];
|
|
327
|
+
for (let i = 0; i < table.rows.length; i++) {
|
|
328
|
+
const row = table.rows[i];
|
|
329
|
+
if (terms.every((term) => termMatches(row, term, table.columns) !== term.negate))
|
|
330
|
+
indices.push(i);
|
|
331
|
+
}
|
|
332
|
+
const sort = options.sort;
|
|
333
|
+
if (sort && sort.column >= 0 && sort.column < table.columns.length) {
|
|
334
|
+
const type = table.columns[sort.column].type;
|
|
335
|
+
const sign = sort.descending ? -1 : 1;
|
|
336
|
+
indices.sort((a, b) => {
|
|
337
|
+
const ca = table.rows[a][sort.column];
|
|
338
|
+
const cb = table.rows[b][sort.column];
|
|
339
|
+
// Empties stay last whichever way the sort runs.
|
|
340
|
+
if (!ca.trim() !== !cb.trim())
|
|
341
|
+
return ca.trim() ? -1 : 1;
|
|
342
|
+
return sign * compareCells(ca, cb, type) || a - b;
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
return indices;
|
|
346
|
+
}
|
|
347
|
+
/** The next sort after a click on `column`: ascending, then descending, then none. */
|
|
348
|
+
export function nextSort(current, column) {
|
|
349
|
+
if (!current || current.column !== column)
|
|
350
|
+
return { column };
|
|
351
|
+
if (!current.descending)
|
|
352
|
+
return { column, descending: true };
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
/** Statistics for one column over the given rows (the filtered view, by default all). */
|
|
356
|
+
export function columnStats(table, column, indices) {
|
|
357
|
+
const info = table.columns[column];
|
|
358
|
+
const rows = indices ?? table.rows.map((_, i) => i);
|
|
359
|
+
const counts = new Map();
|
|
360
|
+
const values = [];
|
|
361
|
+
let empty = 0;
|
|
362
|
+
for (const index of rows) {
|
|
363
|
+
const cell = table.rows[index][column] ?? "";
|
|
364
|
+
if (!cell.trim()) {
|
|
365
|
+
empty++;
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
values.push(cell);
|
|
369
|
+
counts.set(cell, (counts.get(cell) ?? 0) + 1);
|
|
370
|
+
}
|
|
371
|
+
const top = [...counts].sort((a, b) => b[1] - a[1] || collator.compare(a[0], b[0])).slice(0, 5).map(([value, count]) => ({ value, count }));
|
|
372
|
+
const stats = { name: info.name, type: info.type, count: rows.length, empty, unique: counts.size, top };
|
|
373
|
+
if (!values.length)
|
|
374
|
+
return stats;
|
|
375
|
+
const sorted = [...values].sort((a, b) => compareCells(a, b, info.type));
|
|
376
|
+
stats.min = sorted[0];
|
|
377
|
+
stats.max = sorted.at(-1);
|
|
378
|
+
if (info.type === "number") {
|
|
379
|
+
const numbers = values.map(toNumber).filter((n) => !Number.isNaN(n)).sort((a, b) => a - b);
|
|
380
|
+
if (numbers.length) {
|
|
381
|
+
stats.sum = numbers.reduce((total, n) => total + n, 0);
|
|
382
|
+
stats.mean = stats.sum / numbers.length;
|
|
383
|
+
const mid = numbers.length >> 1;
|
|
384
|
+
stats.median = numbers.length % 2 ? numbers[mid] : (numbers[mid - 1] + numbers[mid]) / 2;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return stats;
|
|
388
|
+
}
|
|
389
|
+
/** A number as people read it: grouped, at most four decimals. */
|
|
390
|
+
export function formatNumber(n) {
|
|
391
|
+
return n.toLocaleString("en-US", { maximumFractionDigits: 4 });
|
|
392
|
+
}
|
|
393
|
+
/** Stats as label/value pairs, for a dialog or a modal. */
|
|
394
|
+
export function statsRows(stats) {
|
|
395
|
+
const rows = [
|
|
396
|
+
{ label: "type", value: stats.type },
|
|
397
|
+
{ label: "rows", value: formatNumber(stats.count) },
|
|
398
|
+
{ label: "empty", value: formatNumber(stats.empty) },
|
|
399
|
+
{ label: "distinct", value: formatNumber(stats.unique) },
|
|
400
|
+
];
|
|
401
|
+
if (stats.min !== undefined)
|
|
402
|
+
rows.push({ label: "min", value: stats.min });
|
|
403
|
+
if (stats.max !== undefined)
|
|
404
|
+
rows.push({ label: "max", value: stats.max });
|
|
405
|
+
if (stats.sum !== undefined)
|
|
406
|
+
rows.push({ label: "sum", value: formatNumber(stats.sum) });
|
|
407
|
+
if (stats.mean !== undefined)
|
|
408
|
+
rows.push({ label: "mean", value: formatNumber(stats.mean) });
|
|
409
|
+
if (stats.median !== undefined)
|
|
410
|
+
rows.push({ label: "median", value: formatNumber(stats.median) });
|
|
411
|
+
for (const { value, count } of stats.top)
|
|
412
|
+
rows.push({ label: `× ${formatNumber(count)}`, value });
|
|
413
|
+
return rows;
|
|
414
|
+
}
|
|
415
|
+
/** The delimiter as people name it. */
|
|
416
|
+
export function delimiterName(delimiter) {
|
|
417
|
+
return delimiter === "," ? "comma" : delimiter === "\t" ? "tab" : delimiter === ";" ? "semicolon" : "pipe";
|
|
418
|
+
}
|
|
419
|
+
/** A field written back out, quoted only when it has to be. */
|
|
420
|
+
export function quoteField(cell, delimiter) {
|
|
421
|
+
return cell.includes(delimiter) || /["\r\n]/.test(cell) || /^\s|\s$/.test(cell) ? `"${cell.replace(/"/g, '""')}"` : cell;
|
|
422
|
+
}
|
|
423
|
+
/** The view (filtered, sorted) back as delimited text, header first. */
|
|
424
|
+
export function toDelimited(table, indices, delimiter = table.delimiter) {
|
|
425
|
+
const line = (cells) => cells.map((cell) => quoteField(cell, delimiter)).join(delimiter);
|
|
426
|
+
const out = [line(table.columns.map((column) => column.name)), ...indices.map((index) => line(table.rows[index]))];
|
|
427
|
+
return `${out.join("\n")}\n`;
|
|
428
|
+
}
|
|
429
|
+
/** A cell for one line of a fixed-width layout: first line only, tabs as spaces. */
|
|
430
|
+
export function displayCell(cell) {
|
|
431
|
+
const at = cell.search(/[\r\n]/);
|
|
432
|
+
return (at < 0 ? cell : `${cell.slice(0, at)}↵`).replace(/\t/g, " ");
|
|
433
|
+
}
|
|
434
|
+
/** Numbers and dates read right-aligned, the way a spreadsheet shows them. */
|
|
435
|
+
export function alignOf(type) {
|
|
436
|
+
return type === "number" ? "right" : "left";
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* The table as a GFM pipe table, every cell escaped so nothing in the data turns into
|
|
440
|
+
* formatting. `--print` renders a CSV through this, so it shares the Markdown
|
|
441
|
+
* renderer's column fitting instead of growing a second one.
|
|
442
|
+
*/
|
|
443
|
+
export function toMarkdownTable(table, indices = table.rows.map((_, i) => i)) {
|
|
444
|
+
const escape = (cell) => displayCell(cell).replace(/[\\`*_{}[\]()<>#+\-.!|~]/g, "\\$&") || " ";
|
|
445
|
+
const line = (cells) => `| ${cells.map(escape).join(" | ")} |`;
|
|
446
|
+
const rule = `|${table.columns.map((column) => (alignOf(column.type) === "right" ? " ---: " : " --- ")).join("|")}|`;
|
|
447
|
+
return [line(table.columns.map((column) => column.name)), rule, ...indices.map((index) => line(table.rows[index]))].join("\n") + "\n";
|
|
448
|
+
}
|
|
449
|
+
/** A column by name (case-insensitive) or 1-based number, or -1. */
|
|
450
|
+
export function columnIndex(table, ref) {
|
|
451
|
+
if (ref === undefined || ref === "")
|
|
452
|
+
return -1;
|
|
453
|
+
if (typeof ref === "number" || /^#?\d+$/.test(ref)) {
|
|
454
|
+
const n = Number(String(ref).replace("#", "")) - 1;
|
|
455
|
+
return n >= 0 && n < table.columns.length ? n : -1;
|
|
456
|
+
}
|
|
457
|
+
return table.columns.findIndex((column) => column.name.toLowerCase() === ref.toLowerCase());
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* One call's worth of a table for an agent: the typed columns, the rows that match,
|
|
461
|
+
* sorted and paged, as objects keyed by column name, and optionally one column's
|
|
462
|
+
* statistics over the matching rows. What the MCP `table_query` tool returns.
|
|
463
|
+
*/
|
|
464
|
+
export function queryTable(source, query = {}) {
|
|
465
|
+
const table = parseTable(source, { name: query.name });
|
|
466
|
+
const sortColumn = columnIndex(table, query.sort);
|
|
467
|
+
if (query.sort !== undefined && sortColumn < 0)
|
|
468
|
+
throw new Error(`No column ${JSON.stringify(query.sort)}. Columns: ${table.columns.map((c) => c.name).join(", ")}`);
|
|
469
|
+
const indices = viewRows(table, { filter: query.filter, sort: sortColumn >= 0 ? { column: sortColumn, descending: query.descending } : null });
|
|
470
|
+
const limit = Math.max(0, Math.min(1000, Math.floor(query.limit ?? 50)));
|
|
471
|
+
const offset = Math.max(0, Math.floor(query.offset ?? 0));
|
|
472
|
+
const statsColumn = columnIndex(table, query.stats);
|
|
473
|
+
if (query.stats !== undefined && statsColumn < 0)
|
|
474
|
+
throw new Error(`No column ${JSON.stringify(query.stats)}.`);
|
|
475
|
+
return {
|
|
476
|
+
delimiter: delimiterName(table.delimiter),
|
|
477
|
+
header: table.header,
|
|
478
|
+
columns: table.columns,
|
|
479
|
+
total: table.rows.length,
|
|
480
|
+
matched: indices.length,
|
|
481
|
+
offset,
|
|
482
|
+
rows: indices.slice(offset, offset + limit).map((index) => ({
|
|
483
|
+
row: index + 1,
|
|
484
|
+
...Object.fromEntries(table.columns.map((column, c) => [column.name, table.rows[index][c] ?? ""])),
|
|
485
|
+
})),
|
|
486
|
+
...(statsColumn >= 0 ? { stats: columnStats(table, statsColumn, indices) } : {}),
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
//# sourceMappingURL=csv.js.map
|
package/dist/csv.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.js","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AA6SA,wBAAsB,MAAM,kBAiE3B"}
|
package/dist/mcp.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
4
5
|
import { cloudAction, cloudRequest, pasteLocation, sharedLocation } from "./cloud-client.js";
|
|
6
|
+
import { queryTable } from "./csv.js";
|
|
5
7
|
const string = { type: "string" };
|
|
6
8
|
const role = { type: "string", enum: ["view", "edit"] };
|
|
7
9
|
const definitions = [
|
|
8
10
|
["paste_create", "Create an anonymous paste: any text file behind a secret link, no account needed. Markdown renders as a document; code (JSON, JavaScript, Python, YAML, SQL and 30 more) is highlighted with folding. The language is detected from the title's extension, else from the content; pass language to force one. Expires after 7 days by default (1h, 1d, 7d or 30d), 256 KB at most. Returns the URL and a raw URL; keep them, the server cannot show them again. The result's sensitive field lists any key, token, password, email address, public IP or named home directory in what the link exposes (secrets masked); when its level is \"secret\", tell the user before passing the link on.", { source: string, title: { type: "string", description: "A file name; its extension decides the language." }, language: { type: "string", description: "A highlight.js name such as json, javascript, python, bash, yaml or markdown. Detected when omitted." }, expiresIn: { type: "string", enum: ["1h", "1d", "7d", "30d"] } }, ["source"]],
|
|
11
|
+
["table_query", "Read a CSV or TSV the way the readm3 table view does, without loading it all into context. Give exactly one of source (the text), path (a local file) or url (a readm3 paste link or a raw http(s) file URL). The delimiter, header row and column types (number, date, boolean, text) are detected. filter keeps rows matching every space-separated term: free text, col:text, col=v, col!=v, col>n, col>=n, col<n, col<=n, #3 for the third column, ! to negate, quotes for spaces (city:\"new york\"). sort orders by a column name or number, empties last. Returns columns, total and matched counts, up to limit rows (default 50, max 1000) from offset as objects keyed by column name with their 1-based row number, and with stats a column's count, empty, distinct, min, max, sum, mean, median and top values over the matched rows.", { source: string, path: string, url: string, filter: string, sort: string, descending: { type: "boolean" }, limit: { type: "integer", minimum: 0, maximum: 1000 }, offset: { type: "integer", minimum: 0 }, stats: string }, []],
|
|
9
12
|
["paste_get", "Read an anonymous paste by its URL. Returns the source with its title, language and mime type.", { url: string }, ["url"]],
|
|
10
13
|
["paste_delete", "Delete an anonymous paste by its URL. The link stops working for everyone.", { url: string }, ["url"]],
|
|
11
14
|
["settings_get", "Read the verified account's latest settings and Markdown workspace snapshot, including its revision.", {}, []],
|
|
@@ -257,6 +260,47 @@ const definitions = [
|
|
|
257
260
|
["url", "source", "baseVersion"],
|
|
258
261
|
],
|
|
259
262
|
];
|
|
263
|
+
/** table_query: the text from wherever it was pointed at, then src/csv.ts does the rest. */
|
|
264
|
+
async function tableQuery(args) {
|
|
265
|
+
const given = ["source", "path", "url"].filter((key) => typeof args[key] === "string" && args[key] !== "");
|
|
266
|
+
if (given.length !== 1)
|
|
267
|
+
throw new Error("Give exactly one of source, path or url.");
|
|
268
|
+
let source;
|
|
269
|
+
let name;
|
|
270
|
+
if (typeof args.source === "string" && args.source)
|
|
271
|
+
source = args.source;
|
|
272
|
+
else if (typeof args.path === "string" && args.path) {
|
|
273
|
+
source = readFileSync(args.path, "utf8");
|
|
274
|
+
name = args.path;
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
const url = String(args.url);
|
|
278
|
+
if (/\/p\/[A-Za-z0-9_-]{43}/.test(url)) {
|
|
279
|
+
const { token, config } = pasteLocation(url);
|
|
280
|
+
const paste = await cloudRequest(`pastes/${token}`, "GET", undefined, config);
|
|
281
|
+
source = paste.source;
|
|
282
|
+
name = paste.title;
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
if (!/^https?:\/\//.test(url))
|
|
286
|
+
throw new Error("url must be http(s).");
|
|
287
|
+
const response = await fetch(url);
|
|
288
|
+
if (!response.ok)
|
|
289
|
+
throw new Error(`${url} answered ${response.status}.`);
|
|
290
|
+
source = await response.text();
|
|
291
|
+
name = new URL(url).pathname;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return queryTable(source, {
|
|
295
|
+
name,
|
|
296
|
+
filter: typeof args.filter === "string" ? args.filter : undefined,
|
|
297
|
+
sort: typeof args.sort === "string" || typeof args.sort === "number" ? args.sort : undefined,
|
|
298
|
+
descending: args.descending === true,
|
|
299
|
+
limit: typeof args.limit === "number" ? args.limit : undefined,
|
|
300
|
+
offset: typeof args.offset === "number" ? args.offset : undefined,
|
|
301
|
+
stats: typeof args.stats === "string" || typeof args.stats === "number" ? args.stats : undefined,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
260
304
|
export async function runMcp() {
|
|
261
305
|
const server = new Server({ name: "readm3", version: "0.6.0" }, { capabilities: { tools: {} } });
|
|
262
306
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
@@ -270,7 +314,7 @@ export async function runMcp() {
|
|
|
270
314
|
additionalProperties: false,
|
|
271
315
|
},
|
|
272
316
|
annotations: {
|
|
273
|
-
readOnlyHint: /_(list|get|me|users|revisions)$/.test(name),
|
|
317
|
+
readOnlyHint: /_(list|get|me|users|revisions|query)$/.test(name),
|
|
274
318
|
destructiveHint: /_(delete|remove|revoke|transfer)$/.test(name),
|
|
275
319
|
openWorldHint: true,
|
|
276
320
|
},
|
|
@@ -283,7 +327,9 @@ export async function runMcp() {
|
|
|
283
327
|
if (!definitions.some(([operation]) => operation === name))
|
|
284
328
|
throw new Error("Unknown tool.");
|
|
285
329
|
let result;
|
|
286
|
-
if (name
|
|
330
|
+
if (name === "table_query")
|
|
331
|
+
result = await tableQuery(args);
|
|
332
|
+
else if (name.startsWith("settings_")) {
|
|
287
333
|
if (name === "settings_save" && (!Number.isSafeInteger(args.ifRevision) || Number(args.ifRevision) < 0 || !args.snapshot))
|
|
288
334
|
throw new Error("snapshot and a nonnegative ifRevision are required.");
|
|
289
335
|
result = await cloudRequest(name === "settings_revisions" ? "settings/revisions" : "settings", name === "settings_save" ? "PUT" : "GET", name === "settings_save" ? { snapshot: args.snapshot, ifRevision: args.ifRevision } : undefined);
|