tova 0.8.2 → 0.9.4
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/bin/tova.js +1128 -137
- package/package.json +14 -2
- package/src/analyzer/analyzer.js +405 -9
- package/src/analyzer/browser-analyzer.js +56 -8
- package/src/analyzer/scope.js +7 -0
- package/src/analyzer/server-analyzer.js +33 -1
- package/src/codegen/base-codegen.js +137 -13
- package/src/codegen/browser-codegen.js +725 -20
- package/src/codegen/codegen.js +67 -5
- package/src/codegen/server-codegen.js +54 -6
- package/src/codegen/theme-codegen.js +69 -0
- package/src/config/module-path.js +34 -2
- package/src/config/resolve.js +9 -0
- package/src/config/toml.js +13 -1
- package/src/deploy/provision.js +6 -2
- package/src/diagnostics/security-scorecard.js +111 -0
- package/src/lexer/lexer.js +18 -3
- package/src/parser/animate-ast.js +45 -0
- package/src/parser/ast.js +15 -0
- package/src/parser/browser-ast.js +19 -1
- package/src/parser/browser-parser.js +221 -4
- package/src/parser/parser.js +21 -2
- package/src/parser/theme-ast.js +29 -0
- package/src/parser/theme-parser.js +70 -0
- package/src/registry/plugins/theme-plugin.js +20 -0
- package/src/registry/register-all.js +2 -0
- package/src/runtime/charts.js +547 -0
- package/src/runtime/embedded.js +6 -2
- package/src/runtime/reactivity.js +60 -0
- package/src/runtime/router.js +703 -295
- package/src/runtime/table.js +606 -33
- package/src/stdlib/inline.js +330 -7
- package/src/stdlib/string.js +84 -2
- package/src/stdlib/validation.js +1 -1
- package/src/version.js +1 -1
package/src/stdlib/inline.js
CHANGED
|
@@ -60,7 +60,39 @@ export const PROPAGATE = `function __propagate(val) {
|
|
|
60
60
|
|
|
61
61
|
// Individual builtin functions for tree-shaking
|
|
62
62
|
export const BUILTIN_FUNCTIONS = {
|
|
63
|
-
print: `function print(...args) {
|
|
63
|
+
print: `function print(...args) {
|
|
64
|
+
var _noColor = typeof process !== 'undefined' && (process.env.NO_COLOR || (process.stdout && !process.stdout.isTTY));
|
|
65
|
+
var _codes = { red: '31', green: '32', yellow: '33', blue: '34', magenta: '35', cyan: '36', gray: '90', bold: '1', dim: '2', underline: '4' };
|
|
66
|
+
var _styled = function(s) {
|
|
67
|
+
if (typeof s !== 'string') return s;
|
|
68
|
+
return s.replace(/\\{(\\/|red|green|yellow|blue|magenta|cyan|gray|bold|dim|underline)\\}/g, function(_, tag) {
|
|
69
|
+
if (_noColor) return '';
|
|
70
|
+
if (tag === '/') return '\\x1b[0m';
|
|
71
|
+
return '\\x1b[' + _codes[tag] + 'm';
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
var _pretty = function(v) {
|
|
75
|
+
if (v === null || v === undefined) return v;
|
|
76
|
+
if (typeof v !== 'object') return v;
|
|
77
|
+
if (Array.isArray(v) && v.length > 0 && typeof v[0] === 'object' && v[0] !== null && !Array.isArray(v[0])) {
|
|
78
|
+
var headers = Object.keys(v[0]);
|
|
79
|
+
var rows = v.map(function(r) { return headers.map(function(h) { return String(r[h] != null ? r[h] : ''); }); });
|
|
80
|
+
var widths = headers.map(function(h, i) { return Math.max(h.length, Math.max.apply(null, rows.map(function(r) { return r[i].length; }))); });
|
|
81
|
+
var line = widths.map(function(w) { return '-'.repeat(w + 2); }).join('+');
|
|
82
|
+
var head = headers.map(function(h, i) { return ' ' + h.padEnd(widths[i]) + ' '; }).join('|');
|
|
83
|
+
var body = rows.map(function(r) { return r.map(function(c, i) { return ' ' + c.padEnd(widths[i]) + ' '; }).join('|'); }).join('\\n');
|
|
84
|
+
return head + '\\n' + line + '\\n' + body;
|
|
85
|
+
}
|
|
86
|
+
return JSON.stringify(v, null, 2);
|
|
87
|
+
};
|
|
88
|
+
if (args.length === 1) {
|
|
89
|
+
var processed = _styled(_pretty(args[0]));
|
|
90
|
+
console.log(processed);
|
|
91
|
+
} else {
|
|
92
|
+
var processed = args.map(function(a) { return _styled(a); });
|
|
93
|
+
console.log.apply(console, processed);
|
|
94
|
+
}
|
|
95
|
+
}`,
|
|
64
96
|
len: `function len(v) { if (v == null) return 0; if (typeof v === 'string' || Array.isArray(v) || ArrayBuffer.isView(v)) return v.length; if (typeof v === 'object') return Object.keys(v).length; return 0; }`,
|
|
65
97
|
range: `function range(s, e, st) { if (e === undefined) { e = s; s = 0; } if (st === undefined) st = s < e ? 1 : -1; if (st === 0) return []; const r = []; if (st > 0) { for (let i = s; i < e; i += st) r.push(i); } else { for (let i = s; i > e; i += st) r.push(i); } return r; }`,
|
|
66
98
|
enumerate: `function enumerate(a) { return a.map((v, i) => [i, v]); }`,
|
|
@@ -196,7 +228,7 @@ export const BUILTIN_FUNCTIONS = {
|
|
|
196
228
|
atan2: `function atan2(y, x) { return Math.atan2(y, x); }`,
|
|
197
229
|
|
|
198
230
|
// ── Logarithmic / Exponential ─────────────────────────
|
|
199
|
-
|
|
231
|
+
ln: `function ln(n) { return Math.log(n); }`,
|
|
200
232
|
log2: `function log2(n) { return Math.log2(n); }`,
|
|
201
233
|
log10: `function log10(n) { return Math.log10(n); }`,
|
|
202
234
|
exp: `function exp(n) { return Math.exp(n); }`,
|
|
@@ -240,7 +272,7 @@ Table.prototype = { get rows() { return this._rows.length; }, get columns() { re
|
|
|
240
272
|
table_agg: `function table_agg(grouped, aggregations) { if (!grouped || !grouped.__grouped) throw new Error('agg() must be called after group_by()'); const rows = []; for (const [, { key, rows: gr }] of grouped.groups) { const row = typeof key === 'object' && key !== null ? { ...key } : { _group: key }; for (const [n, fn] of Object.entries(aggregations)) { row[n] = fn(gr); } rows.push(row); } return Table(rows, rows.length > 0 ? Object.keys(rows[0]) : []); }`,
|
|
241
273
|
table_sort_by: `function table_sort_by(table, keyFn, opts) { const desc = opts && opts.desc; const rows = [...table._rows].sort((a, b) => { const ka = typeof keyFn === 'function' ? keyFn(a) : a[keyFn]; const kb = typeof keyFn === 'function' ? keyFn(b) : b[keyFn]; let c = ka < kb ? -1 : ka > kb ? 1 : 0; return desc ? -c : c; }); return Table(rows, table._columns); }`,
|
|
242
274
|
table_limit: `function table_limit(table, n) { return Table(table._rows.slice(0, n), table._columns); }`,
|
|
243
|
-
table_join: `function table_join(table, other, opts) { const { left, right, how } = opts || {}; if (!left || !right) throw new Error('join() requires left and right key functions'); const rows = []; const ri = new Map(); for (const r of other._rows) { const k = typeof right === 'function' ? right(r) : r[right]; const ks = String(k); if (!ri.has(ks)) ri.set(ks, []); ri.get(ks).push(r); } const cc = [...new Set([...table._columns, ...other._columns])]; for (const lr of table._rows) { const k = typeof left === 'function' ? left(lr) : lr[left]; const ms = ri.get(String(k)) || []; if (ms.length > 0) { for (const rr of ms) rows.push({ ...lr, ...rr }); } else if (how === 'left' || how === 'outer') { const row = { ...lr }; for (const c of other._columns) { if (!(c in row)) row[c] = null; } rows.push(row); } } return Table(rows, cc); }`,
|
|
275
|
+
table_join: `function table_join(table, other, opts) { const { left, right, how } = opts || {}; if (how === 'cross') { const rows = []; const cc = [...new Set([...table._columns, ...other._columns])]; for (const lr of table._rows) { for (const rr of other._rows) { rows.push({ ...lr, ...rr }); } } return Table(rows, cc); } if (!left || !right) throw new Error('join() requires left and right key functions'); if (how === 'anti') { const ri = new Set(); for (const r of other._rows) { ri.add(String(typeof right === 'function' ? right(r) : r[right])); } const rows = []; for (const lr of table._rows) { const k = typeof left === 'function' ? left(lr) : lr[left]; if (!ri.has(String(k))) rows.push({ ...lr }); } return Table(rows, [...table._columns]); } if (how === 'semi') { const ri = new Set(); for (const r of other._rows) { ri.add(String(typeof right === 'function' ? right(r) : r[right])); } const rows = []; for (const lr of table._rows) { const k = typeof left === 'function' ? left(lr) : lr[left]; if (ri.has(String(k))) rows.push({ ...lr }); } return Table(rows, [...table._columns]); } if (how === 'right') { const sw = table_join(other, table, { left: right, right: left, how: 'left' }); const cc = [...new Set([...table._columns, ...other._columns])]; return Table(sw._rows, cc); } const ri = new Map(); for (const r of other._rows) { const k = typeof right === 'function' ? right(r) : r[right]; const ks = String(k); if (!ri.has(ks)) ri.set(ks, []); ri.get(ks).push(r); } const cc = [...new Set([...table._columns, ...other._columns])]; const rows = []; const mrk = how === 'outer' ? new Set() : null; for (const lr of table._rows) { const k = typeof left === 'function' ? left(lr) : lr[left]; const ms = ri.get(String(k)) || []; if (ms.length > 0) { for (const rr of ms) rows.push({ ...lr, ...rr }); if (mrk) mrk.add(String(k)); } else if (how === 'left' || how === 'outer') { const row = { ...lr }; for (const c of other._columns) { if (!(c in row)) row[c] = null; } rows.push(row); } } if (how === 'outer') { for (const r of other._rows) { const k = typeof right === 'function' ? right(r) : r[right]; if (!mrk.has(String(k))) { const row = { ...r }; for (const c of table._columns) { if (!(c in row)) row[c] = null; } rows.push(row); } } } return Table(rows, cc); }`,
|
|
244
276
|
table_pivot: `function table_pivot(table, opts) { const { index, columns: colFn, values: valFn } = opts || {}; const pm = new Map(); const ac = new Set(); for (const row of table._rows) { const ik = typeof index === 'function' ? index(row) : row[index]; const col = typeof colFn === 'function' ? colFn(row) : row[colFn]; const val = typeof valFn === 'function' ? valFn(row) : row[valFn]; const ks = String(ik); if (!pm.has(ks)) pm.set(ks, { _index: ik }); pm.get(ks)[String(col)] = val; ac.add(String(col)); } return Table([...pm.values()], ['_index', ...ac]); }`,
|
|
245
277
|
table_unpivot: `function table_unpivot(table, opts) { const { id, columns: uc } = opts || {}; const cn = uc.filter(c => typeof c === 'string'); const rows = []; for (const row of table._rows) { const iv = typeof id === 'function' ? id(row) : row[id]; for (const col of cn) rows.push({ id: iv, variable: col, value: row[col] }); } return Table(rows, ['id', 'variable', 'value']); }`,
|
|
246
278
|
table_explode: `function table_explode(table, colFn) { const rows = []; for (const row of table._rows) { const arr = typeof colFn === 'function' ? colFn(row) : row[colFn]; if (Array.isArray(arr)) { const cn = typeof colFn === 'string' ? colFn : null; for (const val of arr) { const r = { ...row }; if (cn) r[cn] = val; rows.push(r); } } else { rows.push({ ...row }); } } return Table(rows, table._columns); }`,
|
|
@@ -352,6 +384,24 @@ Table.prototype = { get rows() { return this._rows.length; }, get columns() { re
|
|
|
352
384
|
agg_min: `function agg_min(fn) { return (rows) => { if (rows.length === 0) return null; let m = typeof fn === 'function' ? fn(rows[0]) : rows[0][fn]; for (let i = 1; i < rows.length; i++) { const v = typeof fn === 'function' ? fn(rows[i]) : rows[i][fn]; if (v < m) m = v; } return m; }; }`,
|
|
353
385
|
agg_max: `function agg_max(fn) { return (rows) => { if (rows.length === 0) return null; let m = typeof fn === 'function' ? fn(rows[0]) : rows[0][fn]; for (let i = 1; i < rows.length; i++) { const v = typeof fn === 'function' ? fn(rows[i]) : rows[i][fn]; if (v > m) m = v; } return m; }; }`,
|
|
354
386
|
|
|
387
|
+
// ── Window functions ─────────────────────────────────
|
|
388
|
+
win_row_number: `function win_row_number() { return (_rows, index) => index + 1; }`,
|
|
389
|
+
win_rank: `function win_rank() { return (_rows, index, ctx) => { if (index === 0) return 1; const cur = ctx.orderValues[index]; for (let i = index - 1; i >= 0; i--) { if (ctx.orderValues[i] !== cur) return i + 2; } return 1; }; }`,
|
|
390
|
+
win_dense_rank: `function win_dense_rank() { return (_rows, index, ctx) => { if (index === 0) return 1; let rank = 1; for (let i = 1; i <= index; i++) { if (ctx.orderValues[i] !== ctx.orderValues[i - 1]) rank++; } return rank; }; }`,
|
|
391
|
+
win_percent_rank: `function win_percent_rank() { return (rows, index, ctx) => { const n = ctx.partitionSize; if (n <= 1) return 0; const cur = ctx.orderValues[index]; let r = 1; if (index > 0) { for (let i = index - 1; i >= 0; i--) { if (ctx.orderValues[i] !== cur) { r = i + 2; break; } } } return (r - 1) / (n - 1); }; }`,
|
|
392
|
+
win_ntile: `function win_ntile(buckets) { return (_rows, index, ctx) => Math.floor(index * buckets / ctx.partitionSize) + 1; }`,
|
|
393
|
+
win_lag: `function win_lag(colFn, offset, defaultVal) { if (offset === undefined) offset = 1; if (defaultVal === undefined) defaultVal = null; return (rows, index) => { const t = index - offset; if (t < 0 || t >= rows.length) return defaultVal; return typeof colFn === 'function' ? colFn(rows[t]) : rows[t][colFn]; }; }`,
|
|
394
|
+
win_lead: `function win_lead(colFn, offset, defaultVal) { if (offset === undefined) offset = 1; if (defaultVal === undefined) defaultVal = null; return (rows, index) => { const t = index + offset; if (t < 0 || t >= rows.length) return defaultVal; return typeof colFn === 'function' ? colFn(rows[t]) : rows[t][colFn]; }; }`,
|
|
395
|
+
win_first_value: `function win_first_value(colFn) { return (rows) => { if (rows.length === 0) return null; return typeof colFn === 'function' ? colFn(rows[0]) : rows[0][colFn]; }; }`,
|
|
396
|
+
win_last_value: `function win_last_value(colFn) { return (rows) => { if (rows.length === 0) return null; const last = rows[rows.length - 1]; return typeof colFn === 'function' ? colFn(last) : last[colFn]; }; }`,
|
|
397
|
+
win_running_sum: `function win_running_sum(colFn) { return (rows, index) => { let s = 0; for (let i = 0; i <= index; i++) s += typeof colFn === 'function' ? colFn(rows[i]) : rows[i][colFn]; return s; }; }`,
|
|
398
|
+
win_running_count: `function win_running_count() { return (_rows, index) => index + 1; }`,
|
|
399
|
+
win_running_avg: `function win_running_avg(colFn) { return (rows, index) => { let s = 0; for (let i = 0; i <= index; i++) s += typeof colFn === 'function' ? colFn(rows[i]) : rows[i][colFn]; return s / (index + 1); }; }`,
|
|
400
|
+
win_running_min: `function win_running_min(colFn) { return (rows, index) => { let m = typeof colFn === 'function' ? colFn(rows[0]) : rows[0][colFn]; for (let i = 1; i <= index; i++) { const v = typeof colFn === 'function' ? colFn(rows[i]) : rows[i][colFn]; if (v < m) m = v; } return m; }; }`,
|
|
401
|
+
win_running_max: `function win_running_max(colFn) { return (rows, index) => { let m = typeof colFn === 'function' ? colFn(rows[0]) : rows[0][colFn]; for (let i = 1; i <= index; i++) { const v = typeof colFn === 'function' ? colFn(rows[i]) : rows[i][colFn]; if (v > m) m = v; } return m; }; }`,
|
|
402
|
+
win_moving_avg: `function win_moving_avg(colFn, windowSize) { return (rows, index) => { const start = Math.max(0, index - windowSize + 1); let s = 0; for (let i = start; i <= index; i++) s += typeof colFn === 'function' ? colFn(rows[i]) : rows[i][colFn]; return s / (index - start + 1); }; }`,
|
|
403
|
+
table_window: `function table_window(table, opts, windowFns) { const partFn = opts.partition || null; const ordFn = opts.order || null; const desc = opts.desc || false; const parts = new Map(); for (let i = 0; i < table._rows.length; i++) { const r = table._rows[i]; const k = partFn ? String(typeof partFn === 'function' ? partFn(r) : r[partFn]) : '__all__'; if (!parts.has(k)) parts.set(k, []); parts.get(k).push({ row: r, oi: i }); } if (ordFn) { for (const [, items] of parts) { items.sort((a, b) => { const ka = typeof ordFn === 'function' ? ordFn(a.row) : a.row[ordFn]; const kb = typeof ordFn === 'function' ? ordFn(b.row) : b.row[ordFn]; let c = 0; if (ka < kb) c = -1; else if (ka > kb) c = 1; return desc ? -c : c; }); } } const res = new Array(table._rows.length); for (let i = 0; i < res.length; i++) res[i] = {}; for (const [, items] of parts) { const pr = items.map(it => it.row); const ov = ordFn ? pr.map(r => typeof ordFn === 'function' ? ordFn(r) : r[ordFn]) : pr.map((_, i) => i); const ctx = { orderValues: ov, partitionSize: pr.length }; for (const [cn, wf] of Object.entries(windowFns)) { for (let idx = 0; idx < pr.length; idx++) { res[items[idx].oi][cn] = wf(pr, idx, ctx); } } } const nc = [...table._columns]; for (const cn of Object.keys(windowFns)) { if (!nc.includes(cn)) nc.push(cn); } return new Table(table._rows.map((r, i) => Object.assign({}, r, res[i])), nc); }`,
|
|
404
|
+
|
|
355
405
|
// ── Data exploration ────────────────────────────────
|
|
356
406
|
peek: `function peek(table, opts) { const o = typeof opts === 'object' ? opts : {}; console.log(table._format ? table._format(o.n || 10, o.title) : String(table)); return table; }`,
|
|
357
407
|
describe: `function describe(table) { const stats = []; for (const col of table._columns) { const vals = table._rows.map(r => r[col]).filter(v => v != null); const st = { Column: col, Type: 'Unknown', 'Non-Null': vals.length }; if (vals.length > 0) { const s = vals[0]; if (typeof s === 'number') { st.Type = Number.isInteger(s) ? 'Int' : 'Float'; st.Mean = vals.reduce((a, b) => a + b, 0) / vals.length; let mn = vals[0], mx = vals[0]; for (let i = 1; i < vals.length; i++) { if (vals[i] < mn) mn = vals[i]; if (vals[i] > mx) mx = vals[i]; } st.Min = mn; st.Max = mx; } else if (typeof s === 'string') { st.Type = 'String'; st.Unique = new Set(vals).size; } else if (typeof s === 'boolean') { st.Type = 'Bool'; } } stats.push(st); } const dt = Table(stats); console.log(dt._format(100, 'describe()')); return dt; }`,
|
|
@@ -381,7 +431,10 @@ Table.prototype = { get rows() { return this._rows.length; }, get columns() { re
|
|
|
381
431
|
try { const data = JSON.parse(text); if (Array.isArray(data)) return Table(data); return data; } catch { return __parseCSV(text, options); }
|
|
382
432
|
}
|
|
383
433
|
const fs = await import('fs'); const path = await import('path');
|
|
384
|
-
const ext = path.extname(source).toLowerCase();
|
|
434
|
+
const ext = path.extname(source).toLowerCase();
|
|
435
|
+
if (ext === '.parquet') return readParquet(source);
|
|
436
|
+
if (ext === '.xlsx') return readExcel(source, options);
|
|
437
|
+
const text = fs.readFileSync(source, 'utf-8');
|
|
385
438
|
if (ext === '.csv') return __parseCSV(text, options);
|
|
386
439
|
if (ext === '.tsv') return __parseCSV(text, { ...options, delimiter: '\\t' });
|
|
387
440
|
if (ext === '.json') { const data = JSON.parse(text); if (Array.isArray(data)) return Table(data); return data; }
|
|
@@ -391,6 +444,8 @@ Table.prototype = { get rows() { return this._rows.length; }, get columns() { re
|
|
|
391
444
|
write: `async function write(data, destination, opts) {
|
|
392
445
|
const fs = await import('fs'); const path = await import('path');
|
|
393
446
|
const ext = path.extname(destination).toLowerCase();
|
|
447
|
+
if (ext === '.parquet') { return writeParquet(data, destination, opts); }
|
|
448
|
+
if (ext === '.xlsx') { return writeExcel(data, destination, opts); }
|
|
394
449
|
const isTable = data && data._rows && data._columns;
|
|
395
450
|
const td = isTable ? data : (Array.isArray(data) ? Table(data) : null);
|
|
396
451
|
let content;
|
|
@@ -516,7 +571,7 @@ Table.prototype = { get rows() { return this._rows.length; }, get columns() { re
|
|
|
516
571
|
// ── Validation ─────────────────────────────────────────
|
|
517
572
|
is_email: `function is_email(s) { return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(s); }`,
|
|
518
573
|
is_url: `function is_url(s) { try { new URL(s); return true; } catch { return false; } }`,
|
|
519
|
-
is_numeric: `function is_numeric(s) { return typeof s === 'string' && s.length > 0 && !isNaN(Number(s)); }`,
|
|
574
|
+
is_numeric: `function is_numeric(s) { return typeof s === 'string' && s.length > 0 && s.trim().length > 0 && !isNaN(Number(s)); }`,
|
|
520
575
|
is_alpha: `function is_alpha(s) { return /^[a-zA-Z]+$/.test(s); }`,
|
|
521
576
|
is_alphanumeric: `function is_alphanumeric(s) { return /^[a-zA-Z0-9]+$/.test(s); }`,
|
|
522
577
|
is_uuid: `function is_uuid(s) { return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(s); }`,
|
|
@@ -581,11 +636,11 @@ Table.prototype = { get rows() { return this._rows.length; }, get columns() { re
|
|
|
581
636
|
flip: `function flip(fn) { return function(a, b, ...rest) { return fn(b, a, ...rest); }; }`,
|
|
582
637
|
|
|
583
638
|
// ── Encoding (extended) ────────────────────────────────
|
|
584
|
-
hex_encode: `function hex_encode(s) { let r = ''; for (let i = 0; i < s.length; i++) r += s.charCodeAt(i).toString(16).padStart(2, '0'); return r; }`,
|
|
639
|
+
hex_encode: `function hex_encode(s) { let r = ''; if (ArrayBuffer.isView(s) || s instanceof ArrayBuffer) { const u = ArrayBuffer.isView(s) ? s : new Uint8Array(s); for (let i = 0; i < u.length; i++) r += u[i].toString(16).padStart(2, '0'); return r; } for (let i = 0; i < s.length; i++) r += s.charCodeAt(i).toString(16).padStart(2, '0'); return r; }`,
|
|
585
640
|
hex_decode: `function hex_decode(s) { let r = ''; for (let i = 0; i < s.length; i += 2) r += String.fromCharCode(parseInt(s.substr(i, 2), 16)); return r; }`,
|
|
586
641
|
|
|
587
642
|
// ── String (extended) ──────────────────────────────────
|
|
588
|
-
fmt: `function fmt(template, ...args) {
|
|
643
|
+
fmt: `function fmt(template, ...args) { var named = args.length === 1 && args[0] !== null && typeof args[0] === 'object' && !Array.isArray(args[0]) && !(args[0] instanceof Date); var obj = named ? args[0] : null; var ai = 0, result = '', pos = 0, len = template.length; while (pos < len) { if (pos + 1 < len && template[pos] === '{' && template[pos + 1] === '{') { result += '{'; pos += 2; continue; } if (pos + 1 < len && template[pos] === '}' && template[pos + 1] === '}') { result += '}'; pos += 2; continue; } if (template[pos] === '{') { var close = template.indexOf('}', pos + 1); if (close === -1) { result += template[pos]; pos++; continue; } var inner = template.substring(pos + 1, close); pos = close + 1; var colonIdx = inner.indexOf(':'); var key = colonIdx >= 0 ? inner.substring(0, colonIdx) : inner; var spec = colonIdx >= 0 ? inner.substring(colonIdx + 1) : ''; var val; if (named && key.length > 0) { val = obj[key]; if (val === undefined) { result += '{' + inner + '}'; continue; } } else if (key.length === 0) { if (ai < args.length) { val = args[ai++]; } else { result += '{}'; continue; } } else { if (ai < args.length) { val = args[ai++]; } else { result += '{' + inner + '}'; continue; } } if (!spec) { result += String(val); continue; } var si = 0, fill = ' ', align = '', sign = '', comma = false, width = 0, hasWidth = false, precision = -1, type = ''; if (si + 1 < spec.length && (spec[si + 1] === '<' || spec[si + 1] === '>' || spec[si + 1] === '^')) { fill = spec[si]; align = spec[si + 1]; si += 2; } else if (si < spec.length && (spec[si] === '<' || spec[si] === '>' || spec[si] === '^')) { align = spec[si]; si += 1; } if (si < spec.length && (spec[si] === '+' || spec[si] === '-' || spec[si] === ' ')) { sign = spec[si]; si += 1; } while (si < spec.length && spec[si] >= '0' && spec[si] <= '9') { width = width * 10 + (spec.charCodeAt(si) - 48); hasWidth = true; si += 1; } if (si < spec.length && spec[si] === ',') { comma = true; si += 1; } if (si < spec.length && spec[si] === '.') { si += 1; precision = 0; while (si < spec.length && spec[si] >= '0' && spec[si] <= '9') { precision = precision * 10 + (spec.charCodeAt(si) - 48); si += 1; } } if (si < spec.length) { type = spec[si]; } var formatted, numVal = typeof val === 'number' ? val : Number(val); switch (type) { case 'b': formatted = (numVal >>> 0).toString(2); if (numVal === 0) formatted = '0'; break; case 'o': formatted = (numVal >>> 0).toString(8); if (numVal === 0) formatted = '0'; break; case 'x': formatted = (numVal >>> 0).toString(16); if (numVal === 0) formatted = '0'; break; case 'X': formatted = (numVal >>> 0).toString(16).toUpperCase(); if (numVal === 0) formatted = '0'; break; case 'f': formatted = numVal.toFixed(precision >= 0 ? precision : 6); break; case '%': { var pct = numVal * 100; formatted = precision >= 0 ? pct.toFixed(precision) + '%' : (Math.round(pct * 1e10) / 1e10).toString() + '%'; break; } case '$': { var abs = Math.abs(numVal), dollars = abs.toFixed(2), dotIdx2 = dollars.indexOf('.'), intPart2 = dollars.substring(0, dotIdx2), decPart2 = dollars.substring(dotIdx2); var withCommas2 = ''; for (var j2 = 0; j2 < intPart2.length; j2++) { if (j2 > 0 && (intPart2.length - j2) % 3 === 0) withCommas2 += ','; withCommas2 += intPart2[j2]; } formatted = (numVal < 0 ? '-' : '') + '$' + withCommas2 + decPart2; break; } case 's': formatted = String(val); if (precision >= 0) formatted = formatted.substring(0, precision); break; default: if (typeof val === 'number' && precision >= 0 && !type) formatted = numVal.toFixed(precision); else formatted = String(val); break; } if (comma && type !== '$') { var dIdx = formatted.indexOf('.'), iPart = dIdx >= 0 ? formatted.substring(0, dIdx) : formatted, dPart = dIdx >= 0 ? formatted.substring(dIdx) : ''; var signChar = '', digits = iPart; if (digits[0] === '-' || digits[0] === '+') { signChar = digits[0]; digits = digits.substring(1); } var withC = ''; for (var j = 0; j < digits.length; j++) { if (j > 0 && (digits.length - j) % 3 === 0) withC += ','; withC += digits[j]; } formatted = signChar + withC + dPart; } if (sign && type !== '$' && type !== '%') { if (typeof val === 'number') { var s = formatted; if (s[0] === '-' || s[0] === '+') { s = s.substring(1); } if (numVal >= 0) { if (sign === '+') formatted = '+' + s; else if (sign === ' ') formatted = ' ' + s; else formatted = s; } else { formatted = '-' + s; } } } if (hasWidth && formatted.length < width) { var pad = width - formatted.length, a = align || '>'; if (a === '<') formatted = formatted + fill.repeat(pad); else if (a === '^') { var left = Math.floor(pad / 2); formatted = fill.repeat(left) + formatted + fill.repeat(pad - left); } else formatted = fill.repeat(pad) + formatted; } result += formatted; continue; } result += template[pos]; pos++; } return result; }`,
|
|
589
644
|
|
|
590
645
|
// ── Scripting: Environment & CLI ──────────────────────
|
|
591
646
|
env: `function env(key, fallback) { if (key === undefined) return { ...process.env }; const v = process.env[key]; return v !== undefined ? v : (fallback !== undefined ? fallback : null); }`,
|
|
@@ -992,6 +1047,19 @@ Table.prototype = { get rows() { return this._rows.length; }, get columns() { re
|
|
|
992
1047
|
pretty(v) { return JSON.stringify(v, null, 2); }
|
|
993
1048
|
});`,
|
|
994
1049
|
|
|
1050
|
+
crypto: `const crypto = Object.freeze({
|
|
1051
|
+
sha256(data) { const c = require('crypto'); return c.createHash('sha256').update(typeof data === 'string' ? data : Buffer.from(data)).digest('hex'); },
|
|
1052
|
+
sha512(data) { const c = require('crypto'); return c.createHash('sha512').update(typeof data === 'string' ? data : Buffer.from(data)).digest('hex'); },
|
|
1053
|
+
hmac(algo, key, data) { const c = require('crypto'); return c.createHmac(algo, key).update(data).digest('hex'); },
|
|
1054
|
+
random_bytes(n) { return new Uint8Array(require('crypto').randomBytes(n)); },
|
|
1055
|
+
random_int(min, max) { return min + Math.floor(Math.random() * (max - min + 1)); },
|
|
1056
|
+
hash_password(password) { try { const c = require('crypto'); const salt = c.randomBytes(16); const hash = c.scryptSync(password, salt, 64); return Ok(salt.toString('hex') + ':' + hash.toString('hex')); } catch (e) { return Err(e.message); } },
|
|
1057
|
+
verify_password(password, stored) { try { const c = require('crypto'); const [saltHex, hashHex] = stored.split(':'); const salt = Buffer.from(saltHex, 'hex'); const hash = Buffer.from(hashHex, 'hex'); const derived = c.scryptSync(password, salt, 64); return c.timingSafeEqual(hash, derived); } catch { return false; } },
|
|
1058
|
+
encrypt(plaintext, key) { try { const c = require('crypto'); const iv = c.randomBytes(12); const kb = typeof key === 'string' ? Buffer.from(key, 'utf-8').slice(0, 32) : key; const cipher = c.createCipheriv('aes-256-gcm', kb, iv); let enc = cipher.update(plaintext, 'utf8', 'hex'); enc += cipher.final('hex'); const tag = cipher.getAuthTag().toString('hex'); return Ok(iv.toString('hex') + ':' + tag + ':' + enc); } catch (e) { return Err(e.message); } },
|
|
1059
|
+
decrypt(ciphertext, key) { try { const c = require('crypto'); const [ivHex, tagHex, enc] = ciphertext.split(':'); const iv = Buffer.from(ivHex, 'hex'); const tag = Buffer.from(tagHex, 'hex'); const kb = typeof key === 'string' ? Buffer.from(key, 'utf-8').slice(0, 32) : key; const decipher = c.createDecipheriv('aes-256-gcm', kb, iv); decipher.setAuthTag(tag); let dec = decipher.update(enc, 'hex', 'utf8'); dec += decipher.final('utf8'); return Ok(dec); } catch (e) { return Err(e.message); } },
|
|
1060
|
+
constant_time_equal(a, b) { try { const ba = Buffer.from(a); const bb = Buffer.from(b); if (ba.length !== bb.length) return false; return require('crypto').timingSafeEqual(ba, bb); } catch { return false; } }
|
|
1061
|
+
});`,
|
|
1062
|
+
|
|
995
1063
|
fs: `const fs = Object.freeze({
|
|
996
1064
|
read_text(path, enc) { try { return Ok(require('fs').readFileSync(path, enc || 'utf-8')); } catch (e) { return Err(e.message); } },
|
|
997
1065
|
write_text(path, content, opts) { try { const f = require('fs'); if (opts && opts.append) f.appendFileSync(path, content); else f.writeFileSync(path, content); return Ok(path); } catch (e) { return Err(e.message); } },
|
|
@@ -1013,6 +1081,48 @@ Table.prototype = { get rows() { return this._rows.length; }, get columns() { re
|
|
|
1013
1081
|
build_query(obj) { return Object.entries(obj).map(([k, v]) => encodeURIComponent(k) + '=' + encodeURIComponent(v)).join('&'); }
|
|
1014
1082
|
});`,
|
|
1015
1083
|
|
|
1084
|
+
http: `const http = Object.freeze({
|
|
1085
|
+
async _request(method, url, body, opts) {
|
|
1086
|
+
var o = opts || {};
|
|
1087
|
+
var headers = o.headers ? { ...o.headers } : {};
|
|
1088
|
+
if (o.bearer) headers['Authorization'] = 'Bearer ' + o.bearer;
|
|
1089
|
+
if (o.params) { var _qs = new URLSearchParams(o.params).toString(); if (_qs) url += (url.includes('?') ? '&' : '?') + _qs; }
|
|
1090
|
+
if (typeof FormData !== 'undefined' && body instanceof FormData) { delete headers['Content-Type']; }
|
|
1091
|
+
else if (body && typeof body === 'object' && body.__form) { var _fd = new FormData(); Object.keys(body).forEach(function(k) { if (k !== '__form') _fd.append(k, body[k]); }); body = _fd; delete headers['Content-Type']; }
|
|
1092
|
+
else if (body && typeof body === 'object' && !(body instanceof ArrayBuffer) && !(body instanceof Uint8Array) && !headers['Content-Type']) { headers['Content-Type'] = 'application/json'; body = JSON.stringify(body); }
|
|
1093
|
+
var timeout = o.timeout || 30000;
|
|
1094
|
+
var retries = o.retries || 0;
|
|
1095
|
+
var retryDelay = o.retry_delay || 1000;
|
|
1096
|
+
var attempt = 0;
|
|
1097
|
+
while (true) {
|
|
1098
|
+
try {
|
|
1099
|
+
var controller = new AbortController();
|
|
1100
|
+
var timer = setTimeout(function() { controller.abort(); }, timeout);
|
|
1101
|
+
var resp = await fetch(url, { method: method, headers: headers, body: method !== 'GET' && method !== 'HEAD' ? body : undefined, signal: controller.signal, redirect: o.follow_redirects === false ? 'manual' : 'follow' });
|
|
1102
|
+
clearTimeout(timer);
|
|
1103
|
+
var respHeaders = {};
|
|
1104
|
+
resp.headers.forEach(function(v, k) { respHeaders[k] = v; });
|
|
1105
|
+
if (o.stream) { return Ok({ status: resp.status, headers: respHeaders, body: resp.body, ok: resp.ok }); }
|
|
1106
|
+
var respBody;
|
|
1107
|
+
var ct = resp.headers.get('content-type') || '';
|
|
1108
|
+
if (ct.includes('application/json')) { try { respBody = await resp.json(); } catch(e) { respBody = await resp.text(); } }
|
|
1109
|
+
else { respBody = await resp.text(); }
|
|
1110
|
+
return Ok({ status: resp.status, headers: respHeaders, body: respBody, ok: resp.ok, json: function() { try { return Ok(typeof respBody === 'string' ? JSON.parse(respBody) : respBody); } catch (e) { return Err(e.message); } } });
|
|
1111
|
+
} catch (e) {
|
|
1112
|
+
if (attempt < retries) { attempt++; await new Promise(function(r) { setTimeout(r, retryDelay * attempt); }); continue; }
|
|
1113
|
+
return Err(e.name === 'AbortError' ? 'Timeout after ' + timeout + 'ms' : e.message);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
},
|
|
1117
|
+
get(url, opts) { return http._request('GET', url, null, opts); },
|
|
1118
|
+
post(url, body, opts) { return http._request('POST', url, body, opts); },
|
|
1119
|
+
put(url, body, opts) { return http._request('PUT', url, body, opts); },
|
|
1120
|
+
patch(url, body, opts) { return http._request('PATCH', url, body, opts); },
|
|
1121
|
+
delete(url, opts) { return http._request('DELETE', url, null, opts); },
|
|
1122
|
+
head(url, opts) { return http._request('HEAD', url, null, opts); },
|
|
1123
|
+
get_stream(url, opts) { return http._request('GET', url, null, Object.assign({}, opts, { stream: true })); }
|
|
1124
|
+
})`,
|
|
1125
|
+
|
|
1016
1126
|
// ── Channel-based async ───────────────────────────────
|
|
1017
1127
|
Channel: `class Channel {
|
|
1018
1128
|
constructor(capacity) {
|
|
@@ -1227,6 +1337,24 @@ Table.prototype = { get rows() { return this._rows.length; }, get columns() { re
|
|
|
1227
1337
|
OrderedDict, DefaultDict, Counter, Deque
|
|
1228
1338
|
});`,
|
|
1229
1339
|
|
|
1340
|
+
// ── LRU Cache ─────────────────────────────────────────
|
|
1341
|
+
LRUCache: `class LRUCache {
|
|
1342
|
+
constructor(maxSize, ttl) { this._max = maxSize; this._ttl = ttl || 0; this._map = new Map(); this._hits = 0; this._misses = 0; }
|
|
1343
|
+
get(key) { var entry = this._map.get(key); if (\!entry) { this._misses++; return None; } if (this._ttl > 0 && Date.now() - entry.t > this._ttl) { this._map.delete(key); this._misses++; return None; } this._map.delete(key); this._map.set(key, entry); this._hits++; return Some(entry.v); }
|
|
1344
|
+
set(key, value) { if (this._map.has(key)) this._map.delete(key); else if (this._map.size >= this._max) { var first = this._map.keys().next().value; this._map.delete(first); } this._map.set(key, { v: value, t: Date.now() }); }
|
|
1345
|
+
has(key) { if (\!this._map.has(key)) return false; if (this._ttl > 0) { var entry = this._map.get(key); if (Date.now() - entry.t > this._ttl) { this._map.delete(key); return false; } } return true; }
|
|
1346
|
+
delete(key) { return this._map.delete(key); }
|
|
1347
|
+
clear() { this._map.clear(); this._hits = 0; this._misses = 0; }
|
|
1348
|
+
size() { return this._map.size; }
|
|
1349
|
+
keys() { return [...this._map.keys()]; }
|
|
1350
|
+
stats() { var total = this._hits + this._misses; return { hits: this._hits, misses: this._misses, hit_rate: total > 0 ? this._hits / total : 0 }; }
|
|
1351
|
+
}`,
|
|
1352
|
+
|
|
1353
|
+
cache: `const cache = Object.freeze({
|
|
1354
|
+
lru(maxSize) { return new LRUCache(maxSize, 0); },
|
|
1355
|
+
ttl(maxSize, ttlMs) { return new LRUCache(maxSize, ttlMs); }
|
|
1356
|
+
})`,
|
|
1357
|
+
|
|
1230
1358
|
// ─── Typed numeric array functions for @fast mode ───────────────
|
|
1231
1359
|
|
|
1232
1360
|
typed_zeros: `function typed_zeros(n, Type) {
|
|
@@ -1322,6 +1450,172 @@ Table.prototype = { get rows() { return this._rows.length; }, get columns() { re
|
|
|
1322
1450
|
out.sort((a, b) => a - b);
|
|
1323
1451
|
return out;
|
|
1324
1452
|
}`,
|
|
1453
|
+
|
|
1454
|
+
// ── Logging Namespace ──────────────────────────────────
|
|
1455
|
+
log: `var log = (function() {
|
|
1456
|
+
var _level = 1;
|
|
1457
|
+
var _levels = { debug: 0, info: 1, warn: 2, error: 3, silent: 4 };
|
|
1458
|
+
var _format = 'pretty';
|
|
1459
|
+
var _noColor = typeof process !== 'undefined' && (process.env.NO_COLOR || (process.stdout && !process.stdout.isTTY));
|
|
1460
|
+
var _colors = { debug: '36', info: '32', warn: '33', error: '31' };
|
|
1461
|
+
var _labels = { debug: 'DBG', info: 'INF', warn: 'WRN', error: 'ERR' };
|
|
1462
|
+
function _emit(lvl, context, msg, data) {
|
|
1463
|
+
if (_levels[lvl] < _level) return;
|
|
1464
|
+
var consoleFn = lvl === 'error' ? console.error : lvl === 'warn' ? console.warn : lvl === 'debug' ? (console.debug || console.log) : console.log;
|
|
1465
|
+
if (_format === 'json') {
|
|
1466
|
+
var obj = { level: lvl, msg: msg, timestamp: new Date().toISOString() };
|
|
1467
|
+
if (context) Object.assign(obj, context);
|
|
1468
|
+
if (data && typeof data === 'object') Object.assign(obj, data);
|
|
1469
|
+
consoleFn(JSON.stringify(obj));
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
var ts = new Date().toISOString().slice(11, 19);
|
|
1473
|
+
var label = _labels[lvl];
|
|
1474
|
+
if (!_noColor) label = '\\x1b[' + _colors[lvl] + 'm' + label + '\\x1b[0m';
|
|
1475
|
+
var parts = [ts, label, msg];
|
|
1476
|
+
if (data !== undefined) parts.push(typeof data === 'object' ? JSON.stringify(data) : String(data));
|
|
1477
|
+
if (context) parts.push(JSON.stringify(context));
|
|
1478
|
+
consoleFn(parts.join(' '));
|
|
1479
|
+
}
|
|
1480
|
+
function _makeLogger(ctx) {
|
|
1481
|
+
return {
|
|
1482
|
+
debug: function(msg, data) { _emit('debug', ctx, msg, data); },
|
|
1483
|
+
info: function(msg, data) { _emit('info', ctx, msg, data); },
|
|
1484
|
+
warn: function(msg, data) { _emit('warn', ctx, msg, data); },
|
|
1485
|
+
error: function(msg, data) { _emit('error', ctx, msg, data); },
|
|
1486
|
+
level: function(l) { _level = _levels[l] !== undefined ? _levels[l] : 1; },
|
|
1487
|
+
format: function(f) { _format = f; },
|
|
1488
|
+
with: function(extra) { var merged = ctx ? Object.assign({}, ctx, extra) : extra; return _makeLogger(merged); }
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
return _makeLogger(null);
|
|
1492
|
+
})()`,
|
|
1493
|
+
// ── Sampling ──────────────────────────────────────────
|
|
1494
|
+
__xorshift128: `function __xorshift128(seed) { let s = [seed, seed ^ 0xDEADBEEF, seed ^ 0x12345678, seed ^ 0x87654321]; return function() { let t = s[3]; t ^= t << 11; t ^= t >>> 8; s[3] = s[2]; s[2] = s[1]; s[1] = s[0]; t ^= s[0]; t ^= s[0] >>> 19; s[0] = t; return (t >>> 0) / 4294967296; }; }`,
|
|
1495
|
+
table_sample: `function table_sample(table, n, opts) { var total = table._rows.length; var k = n < 1 ? Math.floor(n * total) : Math.min(n, total); if (k <= 0) return Table([], table._columns); if (k >= total) return Table([].concat(table._rows), table._columns); var rng = opts && opts.seed != null ? __xorshift128(opts.seed) : function() { return Math.random(); }; var indices = Array.from({ length: total }, function(_, i) { return i; }); for (var i = 0; i < k; i++) { var j = i + Math.floor(rng() * (total - i)); var tmp = indices[i]; indices[i] = indices[j]; indices[j] = tmp; } var rows = []; for (var i = 0; i < k; i++) rows.push(table._rows[indices[i]]); return Table(rows, table._columns); }`,
|
|
1496
|
+
table_stratified_sample: `function table_stratified_sample(table, keyFn, n, opts) { var groups = new Map(); for (var ri = 0; ri < table._rows.length; ri++) { var row = table._rows[ri]; var key = String(typeof keyFn === 'function' ? keyFn(row) : row[keyFn]); if (!groups.has(key)) groups.set(key, []); groups.get(key).push(row); } var allRows = []; var gi = 0; groups.forEach(function(gr) { var gt = Table(gr, table._columns); var go = opts && opts.seed != null ? { seed: opts.seed + gi * 7919 } : {}; var s = table_sample(gt, n, go); for (var si = 0; si < s._rows.length; si++) allRows.push(s._rows[si]); gi++; }); return Table(allRows, table._columns); }`,
|
|
1497
|
+
|
|
1498
|
+
// ── SQLite Connector ──────────────────────────────────
|
|
1499
|
+
sqlite: `function sqlite(path) { var Database; try { Database = typeof Bun !== 'undefined' ? require('bun:sqlite').Database : require('better-sqlite3'); } catch(e) { throw new Error('SQLite requires Bun (built-in) or "better-sqlite3" package under Node'); } var db = new Database(path); function _inferType(v) { if (v === null || v === undefined) return 'TEXT'; if (typeof v === 'boolean') return 'INTEGER'; if (typeof v === 'number') return Number.isInteger(v) ? 'INTEGER' : 'REAL'; return 'TEXT'; } return { _isTovaSqlite: true, query: function(sql, params) { var stmt = db.prepare(sql); var rows = params ? stmt.all.apply(stmt, params) : stmt.all(); return Table(rows); }, exec: function(sql, params) { var stmt = db.prepare(sql); var result = params ? stmt.run.apply(stmt, params) : stmt.run(); return { changes: result.changes }; }, writeTable: function(tableData, tableName, opts) { var t = tableData._rows ? tableData : Table(tableData); if (t._rows.length === 0) return; if (!opts || !opts.append) { db.run('DROP TABLE IF EXISTS "' + tableName + '"'); var colDefs = t._columns.map(function(c) { return '"' + c + '" ' + _inferType(t._rows[0][c]); }).join(', '); db.run('CREATE TABLE "' + tableName + '" (' + colDefs + ')'); } var ph = t._columns.map(function() { return '?'; }).join(', '); var cols = t._columns.map(function(c) { return '"' + c + '"'; }).join(', '); var ins = db.prepare('INSERT INTO "' + tableName + '" (' + cols + ') VALUES (' + ph + ')'); var tx = db.transaction(function(rows) { for (var i = 0; i < rows.length; i++) { var vals = t._columns.map(function(c) { var v = rows[i][c]; if (v === undefined || v === null) return null; if (typeof v === 'boolean') return v ? 1 : 0; return v; }); ins.run.apply(ins, vals); } }); tx(t._rows); }, close: function() { db.close(); } }; }`,
|
|
1500
|
+
|
|
1501
|
+
// ── SVG Charting ──────────────────────────────────────────
|
|
1502
|
+
__chart_helpers: `var __chart_PALETTE = ['#4f46e5', '#059669', '#d97706', '#dc2626', '#7c3aed', '#0891b2', '#be185d', '#65a30d'];
|
|
1503
|
+
var __chart_DEFAULT_MARGIN = { top: 40, right: 20, bottom: 60, left: 70 };
|
|
1504
|
+
function __chart_esc(s) { return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); }
|
|
1505
|
+
function __chart_getRows(data) { if (data && data._rows) return data._rows; if (Array.isArray(data)) return data; return []; }
|
|
1506
|
+
function __chart_niceTicks(mn, mx, count) { if (count === undefined) count = 5; if (mn === mx) { mn = mn - 1; mx = mx + 1; } var range = mx - mn; var roughStep = range / count; var mag = Math.pow(10, Math.floor(Math.log10(roughStep))); var cands = [1, 2, 5, 10]; var step = mag; for (var i = 0; i < cands.length; i++) { if (cands[i] * mag >= roughStep) { step = cands[i] * mag; break; } } var start = Math.floor(mn / step) * step; var ticks = []; for (var v = start; v <= mx + step * 0.5; v += step) { ticks.push(Math.round(v * 1e10) / 1e10); } return ticks; }
|
|
1507
|
+
function __chart_formatNum(n) { if (Number.isInteger(n)) return String(n); if (Math.abs(n) >= 1000) return String(Math.round(n)); return n.toFixed(1); }
|
|
1508
|
+
function __chart_empty(w, h, msg) { return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + w + ' ' + h + '" width="' + w + '" height="' + h + '" style="font-family:system-ui,sans-serif"><text x="' + (w / 2) + '" y="' + (h / 2) + '" text-anchor="middle" fill="#888" font-size="14">' + __chart_esc(msg || 'No data') + '</text></svg>'; }`,
|
|
1509
|
+
|
|
1510
|
+
bar_chart: `function bar_chart(data, opts) { if (!opts) opts = {}; var rows = __chart_getRows(data); var width = opts.width || 600; var height = opts.height || 400; if (rows.length === 0) return __chart_empty(width, height, 'No data'); var xFn = opts.x; var yFn = opts.y; var title = opts.title || ''; var clr = opts.color || __chart_PALETTE[0]; var margin = { top: title ? 50 : 40, right: 20, bottom: 60, left: 70 }; var labels = rows.map(function(r) { return String(xFn(r)); }); var values = rows.map(function(r) { return Number(yFn(r)); }); var plotW = width - margin.left - margin.right; var plotH = height - margin.top - margin.bottom; var yMax = Math.max.apply(null, values); var ticks = __chart_niceTicks(0, yMax); var scaleMax = ticks[ticks.length - 1]; var barW = plotW / labels.length; var innerW = barW * 0.85; var p = []; p.push('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + width + ' ' + height + '" width="' + width + '" height="' + height + '" style="font-family:system-ui,sans-serif">'); if (title) p.push('<text x="' + (width / 2) + '" y="24" text-anchor="middle" font-size="16" font-weight="bold" fill="#111">' + __chart_esc(title) + '</text>'); for (var ti = 0; ti < ticks.length; ti++) { var t = ticks[ti]; var ty = margin.top + plotH - (t / scaleMax) * plotH; p.push('<line x1="' + margin.left + '" y1="' + ty + '" x2="' + (margin.left + plotW) + '" y2="' + ty + '" stroke="#e5e7eb" stroke-width="1"/>'); p.push('<text x="' + (margin.left - 8) + '" y="' + (ty + 4) + '" text-anchor="end" font-size="11" fill="#666">' + __chart_formatNum(t) + '</text>'); } for (var i = 0; i < labels.length; i++) { var barH = scaleMax > 0 ? (values[i] / scaleMax) * plotH : 0; var bx = margin.left + i * barW + (barW - innerW) / 2; var by = margin.top + plotH - barH; var bc = Array.isArray(clr) ? clr[i % clr.length] : (opts.colors ? opts.colors[i % opts.colors.length] : clr); p.push('<rect x="' + bx + '" y="' + by + '" width="' + innerW + '" height="' + barH + '" fill="' + bc + '" rx="2"/>'); } p.push('<line x1="' + margin.left + '" y1="' + (margin.top + plotH) + '" x2="' + (margin.left + plotW) + '" y2="' + (margin.top + plotH) + '" stroke="#9ca3af" stroke-width="1"/>'); var rot = labels.length > 6; for (var i = 0; i < labels.length; i++) { var lx = margin.left + i * barW + barW / 2; var ly = margin.top + plotH + 16; if (rot) { p.push('<text x="' + lx + '" y="' + ly + '" text-anchor="end" font-size="11" fill="#666" transform="rotate(-45 ' + lx + ' ' + ly + ')">' + __chart_esc(labels[i]) + '</text>'); } else { p.push('<text x="' + lx + '" y="' + ly + '" text-anchor="middle" font-size="11" fill="#666">' + __chart_esc(labels[i]) + '</text>'); } } p.push('<line x1="' + margin.left + '" y1="' + margin.top + '" x2="' + margin.left + '" y2="' + (margin.top + plotH) + '" stroke="#9ca3af" stroke-width="1"/>'); p.push('</svg>'); return p.join('\\n'); }`,
|
|
1511
|
+
|
|
1512
|
+
line_chart: `function line_chart(data, opts) { if (!opts) opts = {}; var rows = __chart_getRows(data); var width = opts.width || 600; var height = opts.height || 400; if (rows.length === 0) return __chart_empty(width, height, 'No data'); var xFn = opts.x; var yFn = opts.y; var title = opts.title || ''; var clr = opts.color || __chart_PALETTE[0]; var showPts = opts.points || false; var margin = { top: title ? 50 : 40, right: 20, bottom: 60, left: 70 }; var xVals = rows.map(function(r) { return xFn(r); }); var yVals = rows.map(function(r) { return Number(yFn(r)); }); var plotW = width - margin.left - margin.right; var plotH = height - margin.top - margin.bottom; var xNum = xVals.every(function(v) { return typeof v === 'number' && !isNaN(v); }); var xPos; if (xNum) { var xMn = Math.min.apply(null, xVals); var xMx = Math.max.apply(null, xVals); var xR = xMx - xMn || 1; xPos = xVals.map(function(v) { return margin.left + ((v - xMn) / xR) * plotW; }); } else { var n = xVals.length; xPos = xVals.map(function(_, i) { return margin.left + (n > 1 ? (i / (n - 1)) * plotW : plotW / 2); }); } var yMn = Math.min.apply(null, yVals); var yMx = Math.max.apply(null, yVals); var ticks = __chart_niceTicks(yMn > 0 ? 0 : yMn, yMx); var sMn = ticks[0]; var sMx = ticks[ticks.length - 1]; var sR = sMx - sMn || 1; var yPos = yVals.map(function(v) { return margin.top + plotH - ((v - sMn) / sR) * plotH; }); var p = []; p.push('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + width + ' ' + height + '" width="' + width + '" height="' + height + '" style="font-family:system-ui,sans-serif">'); if (title) p.push('<text x="' + (width / 2) + '" y="24" text-anchor="middle" font-size="16" font-weight="bold" fill="#111">' + __chart_esc(title) + '</text>'); for (var ti = 0; ti < ticks.length; ti++) { var t = ticks[ti]; var ty = margin.top + plotH - ((t - sMn) / sR) * plotH; p.push('<line x1="' + margin.left + '" y1="' + ty + '" x2="' + (margin.left + plotW) + '" y2="' + ty + '" stroke="#e5e7eb" stroke-width="1"/>'); p.push('<text x="' + (margin.left - 8) + '" y="' + (ty + 4) + '" text-anchor="end" font-size="11" fill="#666">' + __chart_formatNum(t) + '</text>'); } var pts = xPos.map(function(x, i) { return x + ',' + yPos[i]; }).join(' '); p.push('<polyline points="' + pts + '" fill="none" stroke="' + clr + '" stroke-width="2" stroke-linejoin="round"/>'); if (showPts) { for (var i = 0; i < xPos.length; i++) { p.push('<circle cx="' + xPos[i] + '" cy="' + yPos[i] + '" r="4" fill="' + clr + '"/>'); } } p.push('<line x1="' + margin.left + '" y1="' + (margin.top + plotH) + '" x2="' + (margin.left + plotW) + '" y2="' + (margin.top + plotH) + '" stroke="#9ca3af" stroke-width="1"/>'); var lStep = Math.max(1, Math.floor(xVals.length / 8)); for (var i = 0; i < xVals.length; i += lStep) { var lx = xPos[i]; var ly = margin.top + plotH + 16; p.push('<text x="' + lx + '" y="' + ly + '" text-anchor="middle" font-size="11" fill="#666">' + __chart_esc(String(xVals[i])) + '</text>'); } p.push('<line x1="' + margin.left + '" y1="' + margin.top + '" x2="' + margin.left + '" y2="' + (margin.top + plotH) + '" stroke="#9ca3af" stroke-width="1"/>'); p.push('</svg>'); return p.join('\\n'); }`,
|
|
1513
|
+
|
|
1514
|
+
scatter_chart: `function scatter_chart(data, opts) { if (!opts) opts = {}; var rows = __chart_getRows(data); var width = opts.width || 600; var height = opts.height || 400; if (rows.length === 0) return __chart_empty(width, height, 'No data'); var xFn = opts.x; var yFn = opts.y; var title = opts.title || ''; var clr = opts.color || __chart_PALETTE[0]; var rad = opts.r || 5; var margin = { top: title ? 50 : 40, right: 20, bottom: 60, left: 70 }; var xVals = rows.map(function(r) { return Number(xFn(r)); }); var yVals = rows.map(function(r) { return Number(yFn(r)); }); var plotW = width - margin.left - margin.right; var plotH = height - margin.top - margin.bottom; var xTk = __chart_niceTicks(Math.min.apply(null, xVals), Math.max.apply(null, xVals)); var yTk = __chart_niceTicks(Math.min.apply(null, yVals), Math.max.apply(null, yVals)); var xMn = xTk[0]; var xMx = xTk[xTk.length - 1]; var yMn = yTk[0]; var yMx = yTk[yTk.length - 1]; var xR = xMx - xMn || 1; var yR = yMx - yMn || 1; var p = []; p.push('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + width + ' ' + height + '" width="' + width + '" height="' + height + '" style="font-family:system-ui,sans-serif">'); if (title) p.push('<text x="' + (width / 2) + '" y="24" text-anchor="middle" font-size="16" font-weight="bold" fill="#111">' + __chart_esc(title) + '</text>'); for (var i = 0; i < yTk.length; i++) { var t = yTk[i]; var ty = margin.top + plotH - ((t - yMn) / yR) * plotH; p.push('<line x1="' + margin.left + '" y1="' + ty + '" x2="' + (margin.left + plotW) + '" y2="' + ty + '" stroke="#e5e7eb" stroke-width="1"/>'); p.push('<text x="' + (margin.left - 8) + '" y="' + (ty + 4) + '" text-anchor="end" font-size="11" fill="#666">' + __chart_formatNum(t) + '</text>'); } for (var i = 0; i < xTk.length; i++) { var t = xTk[i]; var tx = margin.left + ((t - xMn) / xR) * plotW; p.push('<line x1="' + tx + '" y1="' + margin.top + '" x2="' + tx + '" y2="' + (margin.top + plotH) + '" stroke="#e5e7eb" stroke-width="1"/>'); p.push('<text x="' + tx + '" y="' + (margin.top + plotH + 16) + '" text-anchor="middle" font-size="11" fill="#666">' + __chart_formatNum(t) + '</text>'); } for (var i = 0; i < rows.length; i++) { var cx = margin.left + ((xVals[i] - xMn) / xR) * plotW; var cy = margin.top + plotH - ((yVals[i] - yMn) / yR) * plotH; var cc = Array.isArray(clr) ? clr[i % clr.length] : clr; p.push('<circle cx="' + cx + '" cy="' + cy + '" r="' + rad + '" fill="' + cc + '" opacity="0.7"/>'); } p.push('<line x1="' + margin.left + '" y1="' + (margin.top + plotH) + '" x2="' + (margin.left + plotW) + '" y2="' + (margin.top + plotH) + '" stroke="#9ca3af" stroke-width="1"/>'); p.push('<line x1="' + margin.left + '" y1="' + margin.top + '" x2="' + margin.left + '" y2="' + (margin.top + plotH) + '" stroke="#9ca3af" stroke-width="1"/>'); p.push('</svg>'); return p.join('\\n'); }`,
|
|
1515
|
+
|
|
1516
|
+
histogram: `function histogram(data, opts) { if (!opts) opts = {}; var rows = __chart_getRows(data); var width = opts.width || 600; var height = opts.height || 400; if (rows.length === 0) return __chart_empty(width, height, 'No data'); var colFn = opts.col; var title = opts.title || ''; var clr = opts.color || __chart_PALETTE[0]; var numBins = opts.bins || 20; var margin = { top: title ? 50 : 40, right: 20, bottom: 60, left: 70 }; var values = rows.map(function(r) { return Number(colFn(r)); }).filter(function(v) { return !isNaN(v); }); if (values.length === 0) return __chart_empty(width, height, 'No data'); var dMn = Math.min.apply(null, values); var dMx = Math.max.apply(null, values); var bw = (dMx - dMn) / numBins || 1; var bins = []; for (var i = 0; i < numBins; i++) bins.push({ lo: dMn + i * bw, hi: dMn + (i + 1) * bw, count: 0 }); for (var i = 0; i < values.length; i++) { var idx = Math.floor((values[i] - dMn) / bw); if (idx >= numBins) idx = numBins - 1; if (idx < 0) idx = 0; bins[idx].count++; } var maxC = Math.max.apply(null, bins.map(function(b) { return b.count; })); var ticks = __chart_niceTicks(0, maxC); var scaleMax = ticks[ticks.length - 1]; var plotW = width - margin.left - margin.right; var plotH = height - margin.top - margin.bottom; var barW = plotW / numBins; var p = []; p.push('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + width + ' ' + height + '" width="' + width + '" height="' + height + '" style="font-family:system-ui,sans-serif">'); if (title) p.push('<text x="' + (width / 2) + '" y="24" text-anchor="middle" font-size="16" font-weight="bold" fill="#111">' + __chart_esc(title) + '</text>'); for (var ti = 0; ti < ticks.length; ti++) { var t = ticks[ti]; var ty = margin.top + plotH - (scaleMax > 0 ? (t / scaleMax) * plotH : 0); p.push('<line x1="' + margin.left + '" y1="' + ty + '" x2="' + (margin.left + plotW) + '" y2="' + ty + '" stroke="#e5e7eb" stroke-width="1"/>'); p.push('<text x="' + (margin.left - 8) + '" y="' + (ty + 4) + '" text-anchor="end" font-size="11" fill="#666">' + __chart_formatNum(t) + '</text>'); } for (var i = 0; i < bins.length; i++) { var barH = scaleMax > 0 ? (bins[i].count / scaleMax) * plotH : 0; var bx = margin.left + i * barW; var by = margin.top + plotH - barH; p.push('<rect x="' + bx + '" y="' + by + '" width="' + barW + '" height="' + barH + '" fill="' + clr + '" stroke="#fff" stroke-width="0.5"/>'); } p.push('<line x1="' + margin.left + '" y1="' + (margin.top + plotH) + '" x2="' + (margin.left + plotW) + '" y2="' + (margin.top + plotH) + '" stroke="#9ca3af" stroke-width="1"/>'); var lc = Math.min(numBins + 1, 8); var ls = Math.max(1, Math.floor(numBins / (lc - 1))); for (var i = 0; i <= numBins; i += ls) { var val = dMn + i * bw; var lx = margin.left + i * barW; var ly = margin.top + plotH + 16; p.push('<text x="' + lx + '" y="' + ly + '" text-anchor="middle" font-size="10" fill="#666">' + __chart_formatNum(val) + '</text>'); } p.push('<line x1="' + margin.left + '" y1="' + margin.top + '" x2="' + margin.left + '" y2="' + (margin.top + plotH) + '" stroke="#9ca3af" stroke-width="1"/>'); p.push('</svg>'); return p.join('\\n'); }`,
|
|
1517
|
+
|
|
1518
|
+
pie_chart: `function pie_chart(data, opts) { if (!opts) opts = {}; var rows = __chart_getRows(data); var width = opts.width || 400; var height = opts.height || 400; if (rows.length === 0) return __chart_empty(width, height, 'No data'); var labelFn = opts.label; var valueFn = opts.value; var title = opts.title || ''; var colors = opts.colors || __chart_PALETTE; var labels = rows.map(function(r) { return String(labelFn(r)); }); var values = rows.map(function(r) { return Number(valueFn(r)); }); var total = values.reduce(function(a, b) { return a + b; }, 0); if (total === 0) return __chart_empty(width, height, 'No data'); var cx = width / 2; var cy = title ? (height + 30) / 2 : height / 2; var r = Math.min(cx, cy) - 50; var p = []; p.push('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + width + ' ' + height + '" width="' + width + '" height="' + height + '" style="font-family:system-ui,sans-serif">'); if (title) p.push('<text x="' + (width / 2) + '" y="24" text-anchor="middle" font-size="16" font-weight="bold" fill="#111">' + __chart_esc(title) + '</text>'); var sa = -Math.PI / 2; for (var i = 0; i < values.length; i++) { var sl = (values[i] / total) * 2 * Math.PI; var ea = sa + sl; var x1 = cx + r * Math.cos(sa); var y1 = cy + r * Math.sin(sa); var x2 = cx + r * Math.cos(ea); var y2 = cy + r * Math.sin(ea); var la = sl > Math.PI ? 1 : 0; var c = colors[i % colors.length]; if (values.length === 1) { p.push('<circle cx="' + cx + '" cy="' + cy + '" r="' + r + '" fill="' + c + '"/>'); } else { p.push('<path d="M ' + cx + ' ' + cy + ' L ' + x1 + ' ' + y1 + ' A ' + r + ' ' + r + ' 0 ' + la + ' 1 ' + x2 + ' ' + y2 + ' Z" fill="' + c + '" stroke="#fff" stroke-width="1.5"/>'); } var ma = sa + sl / 2; var lr = r * 0.7; var lx = cx + lr * Math.cos(ma); var ly = cy + lr * Math.sin(ma); var pct = ((values[i] / total) * 100).toFixed(1); p.push('<text x="' + lx + '" y="' + ly + '" text-anchor="middle" font-size="11" fill="#fff" font-weight="bold">' + __chart_esc(labels[i]) + '</text>'); p.push('<text x="' + lx + '" y="' + (ly + 13) + '" text-anchor="middle" font-size="10" fill="#fff">' + pct + '%</text>'); sa = ea; } p.push('</svg>'); return p.join('\\n'); }`,
|
|
1519
|
+
|
|
1520
|
+
// ── Parquet Read/Write ────────────────────────────────────────
|
|
1521
|
+
readParquet: `async function readParquet(path) {
|
|
1522
|
+
var fs = await import('fs'); var arrow = await import('apache-arrow'); var pw = await import('parquet-wasm/node');
|
|
1523
|
+
var fileBytes = new Uint8Array(fs.readFileSync(path));
|
|
1524
|
+
var wasmTable = pw.readParquet(fileBytes);
|
|
1525
|
+
var ipcBytes = wasmTable.intoIPCStream();
|
|
1526
|
+
var arrowTable = arrow.tableFromIPC(ipcBytes);
|
|
1527
|
+
var columns = arrowTable.schema.fields.map(function(f) { return f.name; });
|
|
1528
|
+
var rows = [];
|
|
1529
|
+
for (var i = 0; i < arrowTable.numRows; i++) {
|
|
1530
|
+
var row = {};
|
|
1531
|
+
for (var ci = 0; ci < columns.length; ci++) { var col = columns[ci]; var val = arrowTable.getChild(col).get(i); row[col] = val === undefined ? null : val; }
|
|
1532
|
+
rows.push(row);
|
|
1533
|
+
}
|
|
1534
|
+
return Table(rows, columns);
|
|
1535
|
+
}`,
|
|
1536
|
+
|
|
1537
|
+
writeParquet: `async function writeParquet(tableData, path, opts) {
|
|
1538
|
+
var fs = await import('fs'); var arrow = await import('apache-arrow'); var pw = await import('parquet-wasm/node');
|
|
1539
|
+
var t = tableData && tableData._rows ? tableData : Table(Array.isArray(tableData) ? tableData : []);
|
|
1540
|
+
var columns = t._columns;
|
|
1541
|
+
var COMP_MAP = { snappy: 1, gzip: 2, brotli: 3, zstd: 5, lz4: 6, uncompressed: 0, none: 0 };
|
|
1542
|
+
function _inferType(values) { for (var i = 0; i < values.length; i++) { var v = values[i]; if (v === null || v === undefined) continue; if (typeof v === 'boolean') return 'bool'; if (typeof v === 'number') return Number.isInteger(v) ? 'int' : 'float'; if (typeof v === 'string') return 'string'; } return 'string'; }
|
|
1543
|
+
var colVecs = {};
|
|
1544
|
+
for (var ci = 0; ci < columns.length; ci++) {
|
|
1545
|
+
var col = columns[ci]; var values = t._rows.map(function(r) { var v = r[col]; return v === undefined ? null : v; });
|
|
1546
|
+
var tp = _inferType(values);
|
|
1547
|
+
if (tp === 'int') colVecs[col] = arrow.vectorFromArray(values, new arrow.Int32());
|
|
1548
|
+
else if (tp === 'float') colVecs[col] = arrow.vectorFromArray(values, new arrow.Float64());
|
|
1549
|
+
else if (tp === 'bool') colVecs[col] = arrow.vectorFromArray(values, new arrow.Bool());
|
|
1550
|
+
else colVecs[col] = arrow.vectorFromArray(values, new arrow.Utf8());
|
|
1551
|
+
}
|
|
1552
|
+
var arrowTable = new arrow.Table(colVecs);
|
|
1553
|
+
var ipcBytes = arrow.tableToIPC(arrowTable, 'stream');
|
|
1554
|
+
var wasmTable = pw.Table.fromIPCStream(ipcBytes);
|
|
1555
|
+
var writerProps = null;
|
|
1556
|
+
var comp = (opts && opts.compression) || 'snappy';
|
|
1557
|
+
var compCode = COMP_MAP[comp.toLowerCase()];
|
|
1558
|
+
if (compCode !== undefined) { writerProps = new pw.WriterPropertiesBuilder().setCompression(compCode).build(); }
|
|
1559
|
+
var parquetBytes = pw.writeParquet(wasmTable, writerProps);
|
|
1560
|
+
fs.writeFileSync(path, parquetBytes);
|
|
1561
|
+
}`,
|
|
1562
|
+
|
|
1563
|
+
readExcel: `async function readExcel(path, opts) {
|
|
1564
|
+
var ExcelJS = require('exceljs');
|
|
1565
|
+
var workbook = new ExcelJS.Workbook();
|
|
1566
|
+
await workbook.xlsx.readFile(path);
|
|
1567
|
+
var worksheet;
|
|
1568
|
+
if (opts && typeof opts.sheet === 'string') { worksheet = workbook.getWorksheet(opts.sheet); if (!worksheet) throw new Error('Sheet "' + opts.sheet + '" not found'); }
|
|
1569
|
+
else if (opts && typeof opts.sheet === 'number') { var sheets = workbook.worksheets; if (opts.sheet < 1 || opts.sheet > sheets.length) throw new Error('Sheet index out of range'); worksheet = sheets[opts.sheet - 1]; }
|
|
1570
|
+
else { worksheet = workbook.worksheets[0]; }
|
|
1571
|
+
if (!worksheet) throw new Error('No worksheets found');
|
|
1572
|
+
var headerRow = worksheet.getRow(1);
|
|
1573
|
+
var colArr = [];
|
|
1574
|
+
headerRow.eachCell({ includeEmpty: false }, function(cell, colNumber) { colArr[colNumber] = __excelCellValue(cell); });
|
|
1575
|
+
var colMap = [];
|
|
1576
|
+
for (var i = 1; i < colArr.length; i++) { if (colArr[i] !== undefined && colArr[i] !== null) colMap.push({ colNumber: i, name: String(colArr[i]) }); }
|
|
1577
|
+
if (colMap.length === 0) return Table([], []);
|
|
1578
|
+
var columnNames = colMap.map(function(c) { return c.name; });
|
|
1579
|
+
var rows = [];
|
|
1580
|
+
var rowCount = worksheet.rowCount;
|
|
1581
|
+
for (var r = 2; r <= rowCount; r++) {
|
|
1582
|
+
var excelRow = worksheet.getRow(r);
|
|
1583
|
+
var row = {};
|
|
1584
|
+
for (var ci = 0; ci < colMap.length; ci++) { var cm = colMap[ci]; var cell = excelRow.getCell(cm.colNumber); row[cm.name] = __excelCellValue(cell); }
|
|
1585
|
+
rows.push(row);
|
|
1586
|
+
}
|
|
1587
|
+
return Table(rows, columnNames);
|
|
1588
|
+
}`,
|
|
1589
|
+
|
|
1590
|
+
writeExcel: `async function writeExcel(tableData, path, opts) {
|
|
1591
|
+
var ExcelJS = require('exceljs');
|
|
1592
|
+
var t = tableData && tableData._rows ? tableData : Table(Array.isArray(tableData) ? tableData : []);
|
|
1593
|
+
var workbook = new ExcelJS.Workbook();
|
|
1594
|
+
var sheetName = (opts && opts.sheet) || 'Sheet1';
|
|
1595
|
+
var worksheet = workbook.addWorksheet(sheetName);
|
|
1596
|
+
var cols = t._columns;
|
|
1597
|
+
if (cols.length > 0) worksheet.addRow(cols);
|
|
1598
|
+
for (var i = 0; i < t._rows.length; i++) {
|
|
1599
|
+
var row = t._rows[i];
|
|
1600
|
+
var values = [];
|
|
1601
|
+
for (var ci = 0; ci < cols.length; ci++) { var v = row[cols[ci]]; values.push(v === undefined ? null : v); }
|
|
1602
|
+
worksheet.addRow(values);
|
|
1603
|
+
}
|
|
1604
|
+
await workbook.xlsx.writeFile(path);
|
|
1605
|
+
}`,
|
|
1606
|
+
|
|
1607
|
+
__excelCellValue: `function __excelCellValue(cell) {
|
|
1608
|
+
if (!cell || cell.type === 0) return null;
|
|
1609
|
+
var val = cell.value;
|
|
1610
|
+
if (val === null || val === undefined) return null;
|
|
1611
|
+
if (typeof val === 'object' && val.formula !== undefined) { var result = val.result; return (result === null || result === undefined) ? null : result; }
|
|
1612
|
+
if (typeof val === 'object' && val.richText) { return val.richText.map(function(p) { return p.text; }).join(''); }
|
|
1613
|
+
if (typeof val === 'object' && val.hyperlink) { return val.text || val.hyperlink; }
|
|
1614
|
+
if (typeof val === 'object' && val.error) { return null; }
|
|
1615
|
+
return val;
|
|
1616
|
+
}`,
|
|
1617
|
+
|
|
1618
|
+
heatmap: `function heatmap(data, opts) { if (!opts) opts = {}; var rows = __chart_getRows(data); var width = opts.width || 600; var height = opts.height || 400; if (rows.length === 0) return __chart_empty(width, height, 'No data'); var xFn = opts.x; var yFn = opts.y; var valueFn = opts.value; var title = opts.title || ''; var margin = { top: title ? 50 : 40, right: 40, bottom: 60, left: 80 }; var xCats = []; var yCats = []; var xSet = new Set(); var ySet = new Set(); for (var i = 0; i < rows.length; i++) { var xv = String(xFn(rows[i])); var yv = String(yFn(rows[i])); if (!xSet.has(xv)) { xSet.add(xv); xCats.push(xv); } if (!ySet.has(yv)) { ySet.add(yv); yCats.push(yv); } } var grid = {}; var vMn = Infinity; var vMx = -Infinity; for (var i = 0; i < rows.length; i++) { var xv = String(xFn(rows[i])); var yv = String(yFn(rows[i])); var val = Number(valueFn(rows[i])); grid[xv + '|' + yv] = val; if (val < vMn) vMn = val; if (val > vMx) vMx = val; } var vR = vMx - vMn || 1; var plotW = width - margin.left - margin.right; var plotH = height - margin.top - margin.bottom; var cellW = plotW / xCats.length; var cellH = plotH / yCats.length; function hc(val) { var t = (val - vMn) / vR; var r = Math.round(255 - t * (255 - 79)); var g = Math.round(255 - t * (255 - 70)); var b = Math.round(255 - t * (255 - 229)); return 'rgb(' + r + ',' + g + ',' + b + ')'; } var p = []; p.push('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + width + ' ' + height + '" width="' + width + '" height="' + height + '" style="font-family:system-ui,sans-serif">'); if (title) p.push('<text x="' + (width / 2) + '" y="24" text-anchor="middle" font-size="16" font-weight="bold" fill="#111">' + __chart_esc(title) + '</text>'); for (var xi = 0; xi < xCats.length; xi++) { for (var yi = 0; yi < yCats.length; yi++) { var key = xCats[xi] + '|' + yCats[yi]; var val = grid[key]; var rx = margin.left + xi * cellW; var ry = margin.top + yi * cellH; var fill = val !== undefined ? hc(val) : '#f3f4f6'; p.push('<rect x="' + rx + '" y="' + ry + '" width="' + cellW + '" height="' + cellH + '" fill="' + fill + '" stroke="#fff" stroke-width="1"/>'); if (val !== undefined) { var tc = ((val - vMn) / vR) > 0.5 ? '#fff' : '#111'; p.push('<text x="' + (rx + cellW / 2) + '" y="' + (ry + cellH / 2 + 4) + '" text-anchor="middle" font-size="11" fill="' + tc + '">' + __chart_formatNum(val) + '</text>'); } } } for (var xi = 0; xi < xCats.length; xi++) { var lx = margin.left + xi * cellW + cellW / 2; var ly = margin.top + plotH + 16; p.push('<text x="' + lx + '" y="' + ly + '" text-anchor="middle" font-size="11" fill="#666">' + __chart_esc(xCats[xi]) + '</text>'); } for (var yi = 0; yi < yCats.length; yi++) { var lx = margin.left - 8; var ly = margin.top + yi * cellH + cellH / 2 + 4; p.push('<text x="' + lx + '" y="' + ly + '" text-anchor="end" font-size="11" fill="#666">' + __chart_esc(yCats[yi]) + '</text>'); } p.push('</svg>'); return p.join('\\n'); }`,
|
|
1325
1619
|
};
|
|
1326
1620
|
|
|
1327
1621
|
// All known builtin names for matching
|
|
@@ -1341,10 +1635,12 @@ export const STDLIB_DEPS = {
|
|
|
1341
1635
|
// These are provided by RESULT_OPTION, not the builtin map, so no dep here
|
|
1342
1636
|
// Namespace modules that use builtins internally
|
|
1343
1637
|
json: ['Ok', 'Err'],
|
|
1638
|
+
crypto: ['Ok', 'Err'],
|
|
1344
1639
|
re: ['Ok', 'Err'],
|
|
1345
1640
|
dt: ['Ok', 'Err'],
|
|
1346
1641
|
fs: ['Ok', 'Err'],
|
|
1347
1642
|
url: ['Ok', 'Err'],
|
|
1643
|
+
http: ['Ok', 'Err'],
|
|
1348
1644
|
parse_url: ['Ok', 'Err'],
|
|
1349
1645
|
regex_test: ['__regex_cache'],
|
|
1350
1646
|
regex_match: ['Ok', 'Err', '__regex_cache'],
|
|
@@ -1363,6 +1659,8 @@ export const STDLIB_DEPS = {
|
|
|
1363
1659
|
LazyTable: ['Table', 'table_where', 'table_group_by'],
|
|
1364
1660
|
// Seq uses Some/None
|
|
1365
1661
|
Seq: ['Some', 'None'],
|
|
1662
|
+
// Channel uses Some/None
|
|
1663
|
+
Channel: ['Some', 'None'],
|
|
1366
1664
|
// compare family
|
|
1367
1665
|
compare: ['Less', 'Equal', 'Greater'],
|
|
1368
1666
|
compare_by: ['Less', 'Equal', 'Greater'],
|
|
@@ -1376,6 +1674,31 @@ export const STDLIB_DEPS = {
|
|
|
1376
1674
|
cyan: ['color'],
|
|
1377
1675
|
magenta: ['color'],
|
|
1378
1676
|
gray: ['color'],
|
|
1677
|
+
// LRU cache namespace
|
|
1678
|
+
LRUCache: ['Some', 'None'],
|
|
1679
|
+
cache: ['LRUCache', 'Some', 'None'],
|
|
1680
|
+
// Window functions require Table
|
|
1681
|
+
table_window: ['Table'],
|
|
1682
|
+
// Sampling functions
|
|
1683
|
+
table_sample: ['Table', '__xorshift128'],
|
|
1684
|
+
table_stratified_sample: ['Table', 'table_sample', '__xorshift128'],
|
|
1685
|
+
// SQLite connector
|
|
1686
|
+
sqlite: ['Table'],
|
|
1687
|
+
// Parquet read/write
|
|
1688
|
+
readParquet: ['Table'],
|
|
1689
|
+
writeParquet: ['Table'],
|
|
1690
|
+
readExcel: ['Table', '__excelCellValue'],
|
|
1691
|
+
writeExcel: ['Table'],
|
|
1692
|
+
read: ['Table', '__parseCSV', '__parseJSONL', 'readParquet', 'readExcel', '__excelCellValue'],
|
|
1693
|
+
write: ['Table', 'writeParquet', 'writeExcel'],
|
|
1694
|
+
// SVG Charting
|
|
1695
|
+
__chart_helpers: ['Table'],
|
|
1696
|
+
bar_chart: ['Table', '__chart_helpers'],
|
|
1697
|
+
line_chart: ['Table', '__chart_helpers'],
|
|
1698
|
+
scatter_chart: ['Table', '__chart_helpers'],
|
|
1699
|
+
histogram: ['Table', '__chart_helpers'],
|
|
1700
|
+
pie_chart: ['Table', '__chart_helpers'],
|
|
1701
|
+
heatmap: ['Table', '__chart_helpers'],
|
|
1379
1702
|
};
|
|
1380
1703
|
|
|
1381
1704
|
// Resolve all transitive dependencies for a set of used names
|