kirograph 0.16.0 → 0.17.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.
Files changed (64) hide show
  1. package/README.md +227 -8
  2. package/dist/bin/commands/affected.js +40 -5
  3. package/dist/bin/commands/affected.js.map +2 -2
  4. package/dist/bin/commands/data.js +665 -0
  5. package/dist/bin/commands/data.js.map +7 -0
  6. package/dist/bin/commands/help.js +5 -0
  7. package/dist/bin/commands/help.js.map +2 -2
  8. package/dist/bin/installer/config-prompt.js +28 -1
  9. package/dist/bin/installer/config-prompt.js.map +2 -2
  10. package/dist/bin/installer/index.js +28 -1
  11. package/dist/bin/installer/index.js.map +2 -2
  12. package/dist/bin/installer/steering.js +33 -0
  13. package/dist/bin/installer/steering.js.map +2 -2
  14. package/dist/bin/installer/targets/index.js.map +1 -1
  15. package/dist/bin/installer/targets/kiro.js +2 -2
  16. package/dist/bin/installer/targets/kiro.js.map +2 -2
  17. package/dist/bin/kirograph.js +3 -1
  18. package/dist/bin/kirograph.js.map +3 -3
  19. package/dist/bin/progress.js +30 -0
  20. package/dist/bin/progress.js.map +2 -2
  21. package/dist/compression/naive-cost.js +25 -0
  22. package/dist/compression/naive-cost.js.map +2 -2
  23. package/dist/compression/tracker.js +11 -2
  24. package/dist/compression/tracker.js.map +2 -2
  25. package/dist/compression/types.js.map +1 -1
  26. package/dist/config.js +37 -1
  27. package/dist/config.js.map +2 -2
  28. package/dist/core/pipeline.js +56 -2
  29. package/dist/core/pipeline.js.map +2 -2
  30. package/dist/data/filters.js +105 -0
  31. package/dist/data/filters.js.map +7 -0
  32. package/dist/data/indexer.js +225 -0
  33. package/dist/data/indexer.js.map +7 -0
  34. package/dist/data/linker.js +150 -0
  35. package/dist/data/linker.js.map +7 -0
  36. package/dist/data/lint.js +109 -0
  37. package/dist/data/lint.js.map +7 -0
  38. package/dist/data/parsers/csv.js +132 -0
  39. package/dist/data/parsers/csv.js.map +7 -0
  40. package/dist/data/parsers/excel.js +88 -0
  41. package/dist/data/parsers/excel.js.map +7 -0
  42. package/dist/data/parsers/index.js +79 -0
  43. package/dist/data/parsers/index.js.map +7 -0
  44. package/dist/data/parsers/json-array.js +89 -0
  45. package/dist/data/parsers/json-array.js.map +7 -0
  46. package/dist/data/parsers/jsonl.js +84 -0
  47. package/dist/data/parsers/jsonl.js.map +7 -0
  48. package/dist/data/parsers/parquet.js +95 -0
  49. package/dist/data/parsers/parquet.js.map +7 -0
  50. package/dist/data/profiler.js +182 -0
  51. package/dist/data/profiler.js.map +7 -0
  52. package/dist/data/queries.js +512 -0
  53. package/dist/data/queries.js.map +7 -0
  54. package/dist/data/types.js +17 -0
  55. package/dist/data/types.js.map +7 -0
  56. package/dist/db/data-schema.sql +61 -0
  57. package/dist/db/database.js +11 -0
  58. package/dist/db/database.js.map +2 -2
  59. package/dist/mcp/tool-names.js +9 -1
  60. package/dist/mcp/tool-names.js.map +2 -2
  61. package/dist/mcp/tools.js +423 -0
  62. package/dist/mcp/tools.js.map +2 -2
  63. package/dist/types.js.map +1 -1
  64. package/package.json +4 -2
@@ -0,0 +1,512 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var queries_exports = {};
20
+ __export(queries_exports, {
21
+ DataQueries: () => DataQueries
22
+ });
23
+ module.exports = __toCommonJS(queries_exports);
24
+ var import_filters = require("./filters");
25
+ class DataQueries {
26
+ constructor(db) {
27
+ this.db = db;
28
+ }
29
+ // ── List ────────────────────────────────────────────────────────────────────
30
+ listDatasets() {
31
+ const rows = this.db.all("SELECT * FROM data_datasets ORDER BY file_path");
32
+ return rows.map((r) => ({
33
+ id: r.id,
34
+ filePath: r.file_path,
35
+ format: r.format,
36
+ rowCount: r.row_count,
37
+ columnCount: r.column_count,
38
+ fileSize: r.file_size,
39
+ contentHash: r.content_hash,
40
+ summary: r.summary,
41
+ indexedAt: r.indexed_at
42
+ }));
43
+ }
44
+ // ── Describe ────────────────────────────────────────────────────────────────
45
+ describeDataset(datasetId) {
46
+ const ds = this.db.get("SELECT * FROM data_datasets WHERE id = ?", [datasetId]);
47
+ if (!ds) return null;
48
+ const cols = this.db.all(
49
+ "SELECT * FROM data_columns WHERE dataset_id = ? ORDER BY position",
50
+ [datasetId]
51
+ );
52
+ return {
53
+ dataset: {
54
+ id: ds.id,
55
+ filePath: ds.file_path,
56
+ format: ds.format,
57
+ rowCount: ds.row_count,
58
+ columnCount: ds.column_count,
59
+ fileSize: ds.file_size,
60
+ contentHash: ds.content_hash,
61
+ summary: ds.summary,
62
+ indexedAt: ds.indexed_at
63
+ },
64
+ columns: cols.map((c) => ({
65
+ id: c.id,
66
+ datasetId: c.dataset_id,
67
+ name: c.name,
68
+ position: c.position,
69
+ inferredType: c.inferred_type,
70
+ nullable: !!c.nullable,
71
+ nullCount: c.null_count,
72
+ nullPct: c.null_pct,
73
+ cardinality: c.cardinality,
74
+ minValue: c.min_value,
75
+ maxValue: c.max_value,
76
+ meanValue: c.mean_value,
77
+ sampleValues: c.sample_values ? JSON.parse(c.sample_values) : [],
78
+ summary: c.summary,
79
+ updatedAt: c.updated_at
80
+ }))
81
+ };
82
+ }
83
+ describeColumn(datasetId, columnName) {
84
+ const col = this.db.get(
85
+ "SELECT * FROM data_columns WHERE dataset_id = ? AND name = ?",
86
+ [datasetId, columnName]
87
+ );
88
+ if (!col) return null;
89
+ return {
90
+ id: col.id,
91
+ datasetId: col.dataset_id,
92
+ name: col.name,
93
+ position: col.position,
94
+ inferredType: col.inferred_type,
95
+ nullable: !!col.nullable,
96
+ nullCount: col.null_count,
97
+ nullPct: col.null_pct,
98
+ cardinality: col.cardinality,
99
+ minValue: col.min_value,
100
+ maxValue: col.max_value,
101
+ meanValue: col.mean_value,
102
+ sampleValues: col.sample_values ? JSON.parse(col.sample_values) : [],
103
+ summary: col.summary,
104
+ updatedAt: col.updated_at
105
+ };
106
+ }
107
+ // ── Query (filtered rows) ───────────────────────────────────────────────────
108
+ queryRows(datasetId, opts) {
109
+ const ds = this.db.get("SELECT * FROM data_datasets WHERE id = ?", [datasetId]);
110
+ if (!ds) return null;
111
+ const tableName = `data_rows_${datasetId.replace(/[^a-zA-Z0-9_]/g, "_")}`;
112
+ const validCols = new Set(
113
+ this.db.all("SELECT name FROM data_columns WHERE dataset_id = ?", [datasetId]).map((r) => r.name)
114
+ );
115
+ const { where, params } = (0, import_filters.buildWhereClause)(opts?.filters ?? [], validCols);
116
+ let selectCols = "*";
117
+ if (opts?.columns && opts.columns.length > 0) {
118
+ for (const c of opts.columns) {
119
+ if (!validCols.has(c)) throw new Error(`Invalid column: "${c}"`);
120
+ }
121
+ selectCols = opts.columns.map((c) => `"${c.replace(/"/g, '""')}"`).join(", ");
122
+ }
123
+ const limit = Math.min(opts?.limit ?? 500, 500);
124
+ const offset = opts?.offset ?? 0;
125
+ const countRow = this.db.get(`SELECT COUNT(*) as cnt FROM "${tableName}" ${where}`, params);
126
+ const totalMatching = countRow?.cnt ?? 0;
127
+ const rows = this.db.all(
128
+ `SELECT ${selectCols} FROM "${tableName}" ${where} LIMIT ? OFFSET ?`,
129
+ [...params, limit, offset]
130
+ );
131
+ return { rows, totalMatching };
132
+ }
133
+ // ── Aggregate ───────────────────────────────────────────────────────────────
134
+ aggregate(datasetId, opts) {
135
+ const ds = this.db.get("SELECT * FROM data_datasets WHERE id = ?", [datasetId]);
136
+ if (!ds) return null;
137
+ const tableName = `data_rows_${datasetId.replace(/[^a-zA-Z0-9_]/g, "_")}`;
138
+ const validCols = new Set(
139
+ this.db.all("SELECT name FROM data_columns WHERE dataset_id = ?", [datasetId]).map((r) => r.name)
140
+ );
141
+ for (const col of opts.groupBy) {
142
+ if (!validCols.has(col)) throw new Error(`Invalid group-by column: "${col}"`);
143
+ }
144
+ const selectParts = [];
145
+ for (const col of opts.groupBy) {
146
+ selectParts.push(`"${col.replace(/"/g, '""')}"`);
147
+ }
148
+ for (const metric of opts.metrics) {
149
+ if (!validCols.has(metric.column) && metric.op !== "count") {
150
+ throw new Error(`Invalid metric column: "${metric.column}"`);
151
+ }
152
+ const colRef = `"${metric.column.replace(/"/g, '""')}"`;
153
+ const alias = metric.alias ?? `${metric.op}_${metric.column}`;
154
+ switch (metric.op) {
155
+ case "count":
156
+ selectParts.push(`COUNT(${colRef}) as "${alias}"`);
157
+ break;
158
+ case "count_distinct":
159
+ selectParts.push(`COUNT(DISTINCT ${colRef}) as "${alias}"`);
160
+ break;
161
+ case "sum":
162
+ selectParts.push(`SUM(CAST(${colRef} AS REAL)) as "${alias}"`);
163
+ break;
164
+ case "avg":
165
+ selectParts.push(`AVG(CAST(${colRef} AS REAL)) as "${alias}"`);
166
+ break;
167
+ case "min":
168
+ selectParts.push(`MIN(${colRef}) as "${alias}"`);
169
+ break;
170
+ case "max":
171
+ selectParts.push(`MAX(${colRef}) as "${alias}"`);
172
+ break;
173
+ }
174
+ }
175
+ const { where, params } = (0, import_filters.buildWhereClause)(opts.filters ?? [], validCols);
176
+ const groupByClause = opts.groupBy.length > 0 ? `GROUP BY ${opts.groupBy.map((c) => `"${c.replace(/"/g, '""')}"`).join(", ")}` : "";
177
+ const sql = `SELECT ${selectParts.join(", ")} FROM "${tableName}" ${where} ${groupByClause} LIMIT 1000`;
178
+ const rows = this.db.all(sql, params);
179
+ return { rows };
180
+ }
181
+ // ── Search ──────────────────────────────────────────────────────────────────
182
+ searchColumns(datasetId, query) {
183
+ const pattern = `%${query.toLowerCase()}%`;
184
+ const cols = this.db.all(`
185
+ SELECT * FROM data_columns
186
+ WHERE dataset_id = ? AND (LOWER(name) LIKE ? OR LOWER(sample_values) LIKE ? OR LOWER(summary) LIKE ?)
187
+ ORDER BY position
188
+ `, [datasetId, pattern, pattern, pattern]);
189
+ return cols.map((c) => ({
190
+ id: c.id,
191
+ datasetId: c.dataset_id,
192
+ name: c.name,
193
+ position: c.position,
194
+ inferredType: c.inferred_type,
195
+ nullable: !!c.nullable,
196
+ nullCount: c.null_count,
197
+ nullPct: c.null_pct,
198
+ cardinality: c.cardinality,
199
+ minValue: c.min_value,
200
+ maxValue: c.max_value,
201
+ meanValue: c.mean_value,
202
+ sampleValues: c.sample_values ? JSON.parse(c.sample_values) : [],
203
+ summary: c.summary,
204
+ updatedAt: c.updated_at
205
+ }));
206
+ }
207
+ // ── Stats ───────────────────────────────────────────────────────────────────
208
+ getStats() {
209
+ const datasets = this.db.get("SELECT COUNT(*) as cnt FROM data_datasets")?.cnt ?? 0;
210
+ const totalRows = this.db.get("SELECT SUM(row_count) as total FROM data_datasets")?.total ?? 0;
211
+ const totalColumns = this.db.get("SELECT COUNT(*) as cnt FROM data_columns")?.cnt ?? 0;
212
+ return { datasets, totalRows, totalColumns };
213
+ }
214
+ // ── Join ────────────────────────────────────────────────────────────────────
215
+ join(opts) {
216
+ const leftDs = this.db.get("SELECT * FROM data_datasets WHERE id = ?", [opts.left]);
217
+ const rightDs = this.db.get("SELECT * FROM data_datasets WHERE id = ?", [opts.right]);
218
+ if (!leftDs) return null;
219
+ if (!rightDs) return null;
220
+ const leftTable = `data_rows_${opts.left.replace(/[^a-zA-Z0-9_]/g, "_")}`;
221
+ const rightTable = `data_rows_${opts.right.replace(/[^a-zA-Z0-9_]/g, "_")}`;
222
+ const leftCols = new Set(
223
+ this.db.all("SELECT name FROM data_columns WHERE dataset_id = ?", [opts.left]).map((r) => r.name)
224
+ );
225
+ const rightCols = new Set(
226
+ this.db.all("SELECT name FROM data_columns WHERE dataset_id = ?", [opts.right]).map((r) => r.name)
227
+ );
228
+ if (!leftCols.has(opts.leftColumn)) throw new Error(`Column "${opts.leftColumn}" not found in left dataset "${opts.left}"`);
229
+ if (!rightCols.has(opts.rightColumn)) throw new Error(`Column "${opts.rightColumn}" not found in right dataset "${opts.right}"`);
230
+ const joinType = opts.type ?? "inner";
231
+ const joinKeyword = joinType === "left" ? "LEFT JOIN" : joinType === "right" ? "RIGHT JOIN" : "INNER JOIN";
232
+ let selectCols;
233
+ if (opts.columns && opts.columns.length > 0) {
234
+ selectCols = opts.columns.map((c) => {
235
+ if (c.startsWith(`${opts.left}.`)) {
236
+ const col = c.slice(opts.left.length + 1);
237
+ if (!leftCols.has(col)) throw new Error(`Invalid column: "${c}"`);
238
+ return `L."${col.replace(/"/g, '""')}" as "${opts.left}.${col}"`;
239
+ } else if (c.startsWith(`${opts.right}.`)) {
240
+ const col = c.slice(opts.right.length + 1);
241
+ if (!rightCols.has(col)) throw new Error(`Invalid column: "${c}"`);
242
+ return `R."${col.replace(/"/g, '""')}" as "${opts.right}.${col}"`;
243
+ }
244
+ if (leftCols.has(c)) return `L."${c.replace(/"/g, '""')}" as "${opts.left}.${c}"`;
245
+ if (rightCols.has(c)) return `R."${c.replace(/"/g, '""')}" as "${opts.right}.${c}"`;
246
+ throw new Error(`Column "${c}" not found in either dataset`);
247
+ }).join(", ");
248
+ } else {
249
+ const leftSelect = Array.from(leftCols).map((c) => `L."${c.replace(/"/g, '""')}" as "${opts.left}.${c}"`);
250
+ const rightSelect = Array.from(rightCols).map((c) => `R."${c.replace(/"/g, '""')}" as "${opts.right}.${c}"`);
251
+ selectCols = [...leftSelect, ...rightSelect].join(", ");
252
+ }
253
+ const limit = Math.min(opts.limit ?? 100, 500);
254
+ const leftCol = `L."${opts.leftColumn.replace(/"/g, '""')}"`;
255
+ const rightCol = `R."${opts.rightColumn.replace(/"/g, '""')}"`;
256
+ const countSql = `SELECT COUNT(*) as cnt FROM "${leftTable}" L ${joinKeyword} "${rightTable}" R ON ${leftCol} = ${rightCol}`;
257
+ const countRow = this.db.get(countSql);
258
+ const totalMatching = countRow?.cnt ?? 0;
259
+ const sql = `SELECT ${selectCols} FROM "${leftTable}" L ${joinKeyword} "${rightTable}" R ON ${leftCol} = ${rightCol} LIMIT ?`;
260
+ const rows = this.db.all(sql, [limit]);
261
+ return { rows, totalMatching };
262
+ }
263
+ // ── Correlations ────────────────────────────────────────────────────────────
264
+ correlations(datasetId, threshold = 0.3) {
265
+ const ds = this.db.get("SELECT * FROM data_datasets WHERE id = ?", [datasetId]);
266
+ if (!ds) return null;
267
+ const tableName = `data_rows_${datasetId.replace(/[^a-zA-Z0-9_]/g, "_")}`;
268
+ const numericCols = this.db.all(
269
+ `SELECT name FROM data_columns WHERE dataset_id = ? AND inferred_type IN ('integer', 'float')`,
270
+ [datasetId]
271
+ ).map((r) => r.name);
272
+ if (numericCols.length < 2) return [];
273
+ const results = [];
274
+ for (let i = 0; i < numericCols.length; i++) {
275
+ for (let j = i + 1; j < numericCols.length; j++) {
276
+ const col1 = numericCols[i];
277
+ const col2 = numericCols[j];
278
+ const c1 = `"${col1.replace(/"/g, '""')}"`;
279
+ const c2 = `"${col2.replace(/"/g, '""')}"`;
280
+ const row = this.db.get(`
281
+ SELECT
282
+ COUNT(*) as n,
283
+ SUM(CAST(${c1} AS REAL) * CAST(${c2} AS REAL)) as sum_xy,
284
+ SUM(CAST(${c1} AS REAL)) as sum_x,
285
+ SUM(CAST(${c2} AS REAL)) as sum_y,
286
+ SUM(CAST(${c1} AS REAL) * CAST(${c1} AS REAL)) as sum_x2,
287
+ SUM(CAST(${c2} AS REAL) * CAST(${c2} AS REAL)) as sum_y2
288
+ FROM "${tableName}"
289
+ WHERE ${c1} IS NOT NULL AND ${c2} IS NOT NULL
290
+ AND typeof(${c1}) != 'text' AND typeof(${c2}) != 'text'
291
+ `);
292
+ if (!row || row.n < 3) continue;
293
+ const n = row.n;
294
+ const numerator = n * row.sum_xy - row.sum_x * row.sum_y;
295
+ const denomX = n * row.sum_x2 - row.sum_x * row.sum_x;
296
+ const denomY = n * row.sum_y2 - row.sum_y * row.sum_y;
297
+ const denom = Math.sqrt(denomX * denomY);
298
+ if (denom === 0) continue;
299
+ const correlation = numerator / denom;
300
+ const absCorr = Math.abs(correlation);
301
+ if (absCorr < threshold) continue;
302
+ const strength = absCorr >= 0.7 ? "strong" : absCorr >= 0.4 ? "moderate" : absCorr >= 0.2 ? "weak" : "negligible";
303
+ results.push({ column1: col1, column2: col2, correlation: Math.round(correlation * 1e4) / 1e4, strength });
304
+ }
305
+ }
306
+ results.sort((a, b) => Math.abs(b.correlation) - Math.abs(a.correlation));
307
+ return results;
308
+ }
309
+ // ── History ──────────────────────────────────────────────────────────────────
310
+ getHistory(datasetId, limit = 20) {
311
+ const ds = this.db.get("SELECT id FROM data_datasets WHERE id = ?", [datasetId]);
312
+ if (!ds) return null;
313
+ const rows = this.db.all(
314
+ "SELECT * FROM data_dataset_history WHERE dataset_id = ? ORDER BY snapshot_at DESC LIMIT ?",
315
+ [datasetId, limit]
316
+ );
317
+ return rows.map((r) => ({
318
+ id: r.id,
319
+ datasetId: r.dataset_id,
320
+ snapshotAt: r.snapshot_at,
321
+ rowCount: r.row_count,
322
+ columnCount: r.column_count,
323
+ columns: JSON.parse(r.columns_json),
324
+ contentHash: r.content_hash
325
+ }));
326
+ }
327
+ // ── Drift Detection ─────────────────────────────────────────────────────────
328
+ detectDrift(datasetId) {
329
+ const ds = this.db.get("SELECT id FROM data_datasets WHERE id = ?", [datasetId]);
330
+ if (!ds) return null;
331
+ const snapshots = this.db.all(
332
+ "SELECT * FROM data_dataset_history WHERE dataset_id = ? ORDER BY snapshot_at DESC LIMIT 2",
333
+ [datasetId]
334
+ );
335
+ if (snapshots.length < 2) {
336
+ return { hasDrift: false, addedColumns: [], removedColumns: [], changedColumns: [], rowCountDelta: 0 };
337
+ }
338
+ const latest = snapshots[0];
339
+ const previous = snapshots[1];
340
+ const latestCols = JSON.parse(latest.columns_json);
341
+ const prevCols = JSON.parse(previous.columns_json);
342
+ const latestMap = new Map(latestCols.map((c) => [c.name, c]));
343
+ const prevMap = new Map(prevCols.map((c) => [c.name, c]));
344
+ const addedColumns = [];
345
+ const removedColumns = [];
346
+ const changedColumns = [];
347
+ for (const col of latestCols) {
348
+ if (!prevMap.has(col.name)) addedColumns.push(col.name);
349
+ }
350
+ for (const col of prevCols) {
351
+ if (!latestMap.has(col.name)) removedColumns.push(col.name);
352
+ }
353
+ for (const col of latestCols) {
354
+ const prev = prevMap.get(col.name);
355
+ if (!prev) continue;
356
+ const changes = [];
357
+ if (col.type !== prev.type) changes.push(`type: ${prev.type} \u2192 ${col.type}`);
358
+ if (col.cardinality !== prev.cardinality) changes.push(`cardinality: ${prev.cardinality} \u2192 ${col.cardinality}`);
359
+ if (Math.abs(col.nullPct - prev.nullPct) > 0.01) changes.push(`nullPct: ${prev.nullPct.toFixed(1)}% \u2192 ${col.nullPct.toFixed(1)}%`);
360
+ if (changes.length > 0) changedColumns.push({ name: col.name, changes });
361
+ }
362
+ const rowCountDelta = latest.row_count - previous.row_count;
363
+ const hasDrift = addedColumns.length > 0 || removedColumns.length > 0 || changedColumns.length > 0;
364
+ return { hasDrift, addedColumns, removedColumns, changedColumns, rowCountDelta };
365
+ }
366
+ // ── Quality ─────────────────────────────────────────────────────────────────
367
+ quality(datasetId) {
368
+ const ds = this.db.get("SELECT * FROM data_datasets WHERE id = ?", [datasetId]);
369
+ if (!ds) return null;
370
+ const cols = this.db.all(
371
+ "SELECT * FROM data_columns WHERE dataset_id = ? ORDER BY position",
372
+ [datasetId]
373
+ );
374
+ if (cols.length === 0) return [];
375
+ const results = [];
376
+ for (const col of cols) {
377
+ const issues = [];
378
+ let nullRisk = 0;
379
+ let cardinalityRisk = 0;
380
+ let typeRisk = 0;
381
+ const nullPct = col.null_pct ?? 0;
382
+ if (nullPct > 50) {
383
+ issues.push(`High null rate (${nullPct.toFixed(1)}%)`);
384
+ nullRisk = 0.4;
385
+ } else if (nullPct > 20) {
386
+ issues.push(`Moderate null rate (${nullPct.toFixed(1)}%)`);
387
+ nullRisk = 0.2;
388
+ } else if (nullPct > 5) {
389
+ issues.push(`Some nulls (${nullPct.toFixed(1)}%)`);
390
+ nullRisk = 0.1;
391
+ }
392
+ const cardinality = col.cardinality ?? 0;
393
+ const rowCount = ds.row_count ?? 1;
394
+ if (cardinality === 1 && rowCount > 10) {
395
+ issues.push("Constant value (cardinality=1)");
396
+ cardinalityRisk = 0.3;
397
+ } else if (cardinality / rowCount > 0.95 && rowCount > 100) {
398
+ issues.push("Near-unique (>95% distinct)");
399
+ cardinalityRisk = 0.1;
400
+ }
401
+ if (col.inferred_type === "string" && col.cardinality < 20 && rowCount > 100) {
402
+ issues.push("Low-cardinality string (consider enum/category)");
403
+ typeRisk = 0.05;
404
+ }
405
+ const riskScore = Math.min(nullRisk + cardinalityRisk + typeRisk, 1);
406
+ if (issues.length > 0) {
407
+ results.push({ column: col.name, riskScore, nullRisk, cardinalityRisk, typeRisk, issues });
408
+ }
409
+ }
410
+ results.sort((a, b) => b.riskScore - a.riskScore);
411
+ return results;
412
+ }
413
+ // ── Validation Rules ────────────────────────────────────────────────────────
414
+ /**
415
+ * Infer validation rules from column profiles.
416
+ * Returns rules the code should apply when reading/writing this dataset.
417
+ */
418
+ validationRules(datasetId) {
419
+ const info = this.describeDataset(datasetId);
420
+ if (!info) return null;
421
+ const results = [];
422
+ for (const col of info.columns) {
423
+ const rules = [];
424
+ if (!col.nullable) {
425
+ rules.push("required (never null)");
426
+ } else if (col.nullPct < 0.01) {
427
+ rules.push("rarely null (<1%) \u2014 consider required");
428
+ }
429
+ rules.push(`type: ${col.inferredType}`);
430
+ if ((col.inferredType === "integer" || col.inferredType === "float") && col.minValue != null && col.maxValue != null) {
431
+ rules.push(`range: ${col.minValue} to ${col.maxValue}`);
432
+ }
433
+ if (col.inferredType === "string" && col.cardinality <= 20 && info.dataset.rowCount > 50) {
434
+ rules.push(`enum (${col.cardinality} distinct values): ${col.sampleValues.join(", ")}`);
435
+ }
436
+ if (col.cardinality === info.dataset.rowCount && info.dataset.rowCount > 10) {
437
+ rules.push("unique (all values distinct)");
438
+ }
439
+ if (rules.length > 1) {
440
+ results.push({ column: col.name, rules });
441
+ }
442
+ }
443
+ return results;
444
+ }
445
+ // ── Sample Data Generation Hints ────────────────────────────────────────────
446
+ /**
447
+ * From column profiles, provide hints for generating realistic test data.
448
+ */
449
+ sampleHints(datasetId) {
450
+ const info = this.describeDataset(datasetId);
451
+ if (!info) return null;
452
+ const results = [];
453
+ for (const col of info.columns) {
454
+ let hint;
455
+ switch (col.inferredType) {
456
+ case "integer":
457
+ if (col.minValue != null && col.maxValue != null) {
458
+ hint = `Random integer between ${col.minValue} and ${col.maxValue}`;
459
+ } else {
460
+ hint = "Random integer";
461
+ }
462
+ if (col.cardinality === info.dataset.rowCount) hint += " (must be unique)";
463
+ break;
464
+ case "float":
465
+ if (col.minValue != null && col.maxValue != null) {
466
+ hint = `Random float between ${col.minValue} and ${col.maxValue}`;
467
+ if (col.meanValue != null) hint += ` (mean \u2248 ${col.meanValue.toFixed(2)})`;
468
+ } else {
469
+ hint = "Random float";
470
+ }
471
+ break;
472
+ case "boolean":
473
+ hint = "Random boolean (true/false)";
474
+ break;
475
+ case "date":
476
+ if (col.minValue && col.maxValue) {
477
+ hint = `Random date between ${col.minValue} and ${col.maxValue}`;
478
+ } else {
479
+ hint = "Random ISO date string";
480
+ }
481
+ break;
482
+ case "string":
483
+ if (col.cardinality <= 10 && info.dataset.rowCount > 20) {
484
+ hint = `Pick from: ${col.sampleValues.join(", ")}`;
485
+ } else if (col.cardinality === info.dataset.rowCount) {
486
+ const name = col.name.toLowerCase();
487
+ if (name.includes("email")) hint = "Generate unique email (e.g. user{n}@example.com)";
488
+ else if (name.includes("id") || name.includes("uuid")) hint = "Generate unique ID/UUID";
489
+ else if (name.includes("name")) hint = "Generate unique name string";
490
+ else if (name.includes("url") || name.includes("link")) hint = "Generate unique URL";
491
+ else hint = `Generate unique string (${col.cardinality} distinct in source)`;
492
+ } else {
493
+ hint = `Random string (${col.cardinality} distinct values in source)`;
494
+ if (col.sampleValues.length > 0) hint += `. Examples: ${col.sampleValues.slice(0, 3).join(", ")}`;
495
+ }
496
+ break;
497
+ default:
498
+ hint = `Generate ${col.inferredType} value`;
499
+ }
500
+ if (col.nullable && col.nullPct > 0.05) {
501
+ hint += ` \u2014 ${(col.nullPct * 100).toFixed(0)}% chance of null`;
502
+ }
503
+ results.push({ column: col.name, hint });
504
+ }
505
+ return results;
506
+ }
507
+ }
508
+ // Annotate the CommonJS export names for ESM import in node:
509
+ 0 && (module.exports = {
510
+ DataQueries
511
+ });
512
+ //# sourceMappingURL=queries.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/data/queries.ts"],
4
+ "sourcesContent": ["/**\n * Data Queries\n *\n * Read-side helpers for MCP tools and CLI commands.\n * Handles: list, describe, query (with filters), aggregate, search.\n */\n\nimport type { DataSet, DataColumn, QueryFilter, AggregateMetric, CorrelationPair, JoinType, ColumnQuality } from './types';\nimport { buildWhereClause } from './filters';\n\nexport interface ValidationRule {\n column: string;\n rules: string[];\n}\n\nexport interface SampleHint {\n column: string;\n hint: string;\n}\n\nexport class DataQueries {\n private readonly db: any;\n\n constructor(db: any) {\n this.db = db;\n }\n\n // \u2500\u2500 List \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n listDatasets(): DataSet[] {\n const rows = this.db.all('SELECT * FROM data_datasets ORDER BY file_path') as any[];\n return rows.map(r => ({\n id: r.id,\n filePath: r.file_path,\n format: r.format,\n rowCount: r.row_count,\n columnCount: r.column_count,\n fileSize: r.file_size,\n contentHash: r.content_hash,\n summary: r.summary,\n indexedAt: r.indexed_at,\n }));\n }\n\n // \u2500\u2500 Describe \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n describeDataset(datasetId: string): { dataset: DataSet; columns: DataColumn[] } | null {\n const ds = this.db.get('SELECT * FROM data_datasets WHERE id = ?', [datasetId]);\n if (!ds) return null;\n\n const cols = this.db.all(\n 'SELECT * FROM data_columns WHERE dataset_id = ? ORDER BY position',\n [datasetId],\n ) as any[];\n\n return {\n dataset: {\n id: ds.id, filePath: ds.file_path, format: ds.format,\n rowCount: ds.row_count, columnCount: ds.column_count,\n fileSize: ds.file_size, contentHash: ds.content_hash,\n summary: ds.summary, indexedAt: ds.indexed_at,\n },\n columns: cols.map(c => ({\n id: c.id, datasetId: c.dataset_id, name: c.name, position: c.position,\n inferredType: c.inferred_type, nullable: !!c.nullable,\n nullCount: c.null_count, nullPct: c.null_pct, cardinality: c.cardinality,\n minValue: c.min_value, maxValue: c.max_value, meanValue: c.mean_value,\n sampleValues: c.sample_values ? JSON.parse(c.sample_values) : [],\n summary: c.summary, updatedAt: c.updated_at,\n })),\n };\n }\n\n describeColumn(datasetId: string, columnName: string): DataColumn | null {\n const col = this.db.get(\n 'SELECT * FROM data_columns WHERE dataset_id = ? AND name = ?',\n [datasetId, columnName],\n );\n if (!col) return null;\n return {\n id: col.id, datasetId: col.dataset_id, name: col.name, position: col.position,\n inferredType: col.inferred_type, nullable: !!col.nullable,\n nullCount: col.null_count, nullPct: col.null_pct, cardinality: col.cardinality,\n minValue: col.min_value, maxValue: col.max_value, meanValue: col.mean_value,\n sampleValues: col.sample_values ? JSON.parse(col.sample_values) : [],\n summary: col.summary, updatedAt: col.updated_at,\n };\n }\n\n // \u2500\u2500 Query (filtered rows) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n queryRows(\n datasetId: string,\n opts?: { filters?: QueryFilter[]; columns?: string[]; limit?: number; offset?: number },\n ): { rows: any[]; totalMatching: number } | null {\n const ds = this.db.get('SELECT * FROM data_datasets WHERE id = ?', [datasetId]);\n if (!ds) return null;\n\n const tableName = `data_rows_${datasetId.replace(/[^a-zA-Z0-9_]/g, '_')}`;\n\n // Get valid columns\n const validCols = new Set<string>(\n (this.db.all('SELECT name FROM data_columns WHERE dataset_id = ?', [datasetId]) as any[]).map(r => r.name),\n );\n\n // Build WHERE clause\n const { where, params } = buildWhereClause(opts?.filters ?? [], validCols);\n\n // Column projection\n let selectCols = '*';\n if (opts?.columns && opts.columns.length > 0) {\n for (const c of opts.columns) {\n if (!validCols.has(c)) throw new Error(`Invalid column: \"${c}\"`);\n }\n selectCols = opts.columns.map(c => `\"${c.replace(/\"/g, '\"\"')}\"`).join(', ');\n }\n\n const limit = Math.min(opts?.limit ?? 500, 500); // hard cap\n const offset = opts?.offset ?? 0;\n\n // Count total matching\n const countRow = this.db.get(`SELECT COUNT(*) as cnt FROM \"${tableName}\" ${where}`, params);\n const totalMatching = countRow?.cnt ?? 0;\n\n // Fetch rows\n const rows = this.db.all(\n `SELECT ${selectCols} FROM \"${tableName}\" ${where} LIMIT ? OFFSET ?`,\n [...params, limit, offset],\n );\n\n return { rows, totalMatching };\n }\n\n // \u2500\u2500 Aggregate \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n aggregate(\n datasetId: string,\n opts: { groupBy: string[]; metrics: AggregateMetric[]; filters?: QueryFilter[] },\n ): { rows: any[] } | null {\n const ds = this.db.get('SELECT * FROM data_datasets WHERE id = ?', [datasetId]);\n if (!ds) return null;\n\n const tableName = `data_rows_${datasetId.replace(/[^a-zA-Z0-9_]/g, '_')}`;\n\n // Get valid columns\n const validCols = new Set<string>(\n (this.db.all('SELECT name FROM data_columns WHERE dataset_id = ?', [datasetId]) as any[]).map(r => r.name),\n );\n\n // Validate group by columns\n for (const col of opts.groupBy) {\n if (!validCols.has(col)) throw new Error(`Invalid group-by column: \"${col}\"`);\n }\n\n // Build SELECT clause\n const selectParts: string[] = [];\n for (const col of opts.groupBy) {\n selectParts.push(`\"${col.replace(/\"/g, '\"\"')}\"`);\n }\n for (const metric of opts.metrics) {\n if (!validCols.has(metric.column) && metric.op !== 'count') {\n throw new Error(`Invalid metric column: \"${metric.column}\"`);\n }\n const colRef = `\"${metric.column.replace(/\"/g, '\"\"')}\"`;\n const alias = metric.alias ?? `${metric.op}_${metric.column}`;\n switch (metric.op) {\n case 'count': selectParts.push(`COUNT(${colRef}) as \"${alias}\"`); break;\n case 'count_distinct': selectParts.push(`COUNT(DISTINCT ${colRef}) as \"${alias}\"`); break;\n case 'sum': selectParts.push(`SUM(CAST(${colRef} AS REAL)) as \"${alias}\"`); break;\n case 'avg': selectParts.push(`AVG(CAST(${colRef} AS REAL)) as \"${alias}\"`); break;\n case 'min': selectParts.push(`MIN(${colRef}) as \"${alias}\"`); break;\n case 'max': selectParts.push(`MAX(${colRef}) as \"${alias}\"`); break;\n }\n }\n\n // Build WHERE\n const { where, params } = buildWhereClause(opts.filters ?? [], validCols);\n\n // Build GROUP BY\n const groupByClause = opts.groupBy.length > 0\n ? `GROUP BY ${opts.groupBy.map(c => `\"${c.replace(/\"/g, '\"\"')}\"`).join(', ')}`\n : '';\n\n const sql = `SELECT ${selectParts.join(', ')} FROM \"${tableName}\" ${where} ${groupByClause} LIMIT 1000`;\n const rows = this.db.all(sql, params);\n\n return { rows };\n }\n\n // \u2500\u2500 Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n searchColumns(datasetId: string, query: string): DataColumn[] {\n const pattern = `%${query.toLowerCase()}%`;\n const cols = this.db.all(`\n SELECT * FROM data_columns\n WHERE dataset_id = ? AND (LOWER(name) LIKE ? OR LOWER(sample_values) LIKE ? OR LOWER(summary) LIKE ?)\n ORDER BY position\n `, [datasetId, pattern, pattern, pattern]) as any[];\n\n return cols.map(c => ({\n id: c.id, datasetId: c.dataset_id, name: c.name, position: c.position,\n inferredType: c.inferred_type, nullable: !!c.nullable,\n nullCount: c.null_count, nullPct: c.null_pct, cardinality: c.cardinality,\n minValue: c.min_value, maxValue: c.max_value, meanValue: c.mean_value,\n sampleValues: c.sample_values ? JSON.parse(c.sample_values) : [],\n summary: c.summary, updatedAt: c.updated_at,\n }));\n }\n\n // \u2500\u2500 Stats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getStats(): { datasets: number; totalRows: number; totalColumns: number } {\n const datasets = this.db.get('SELECT COUNT(*) as cnt FROM data_datasets')?.cnt ?? 0;\n const totalRows = this.db.get('SELECT SUM(row_count) as total FROM data_datasets')?.total ?? 0;\n const totalColumns = this.db.get('SELECT COUNT(*) as cnt FROM data_columns')?.cnt ?? 0;\n return { datasets, totalRows, totalColumns };\n }\n\n // \u2500\u2500 Join \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n join(opts: {\n left: string;\n right: string;\n leftColumn: string;\n rightColumn: string;\n type?: JoinType;\n columns?: string[];\n limit?: number;\n }): { rows: any[]; totalMatching: number } | null {\n // Verify both datasets exist\n const leftDs = this.db.get('SELECT * FROM data_datasets WHERE id = ?', [opts.left]);\n const rightDs = this.db.get('SELECT * FROM data_datasets WHERE id = ?', [opts.right]);\n if (!leftDs) return null;\n if (!rightDs) return null;\n\n const leftTable = `data_rows_${opts.left.replace(/[^a-zA-Z0-9_]/g, '_')}`;\n const rightTable = `data_rows_${opts.right.replace(/[^a-zA-Z0-9_]/g, '_')}`;\n\n // Validate join columns exist\n const leftCols = new Set<string>(\n (this.db.all('SELECT name FROM data_columns WHERE dataset_id = ?', [opts.left]) as any[]).map(r => r.name),\n );\n const rightCols = new Set<string>(\n (this.db.all('SELECT name FROM data_columns WHERE dataset_id = ?', [opts.right]) as any[]).map(r => r.name),\n );\n\n if (!leftCols.has(opts.leftColumn)) throw new Error(`Column \"${opts.leftColumn}\" not found in left dataset \"${opts.left}\"`);\n if (!rightCols.has(opts.rightColumn)) throw new Error(`Column \"${opts.rightColumn}\" not found in right dataset \"${opts.right}\"`);\n\n // Build join type\n const joinType = opts.type ?? 'inner';\n const joinKeyword = joinType === 'left' ? 'LEFT JOIN' : joinType === 'right' ? 'RIGHT JOIN' : 'INNER JOIN';\n\n // Column projection: prefix with L. or R. for disambiguation\n let selectCols: string;\n if (opts.columns && opts.columns.length > 0) {\n selectCols = opts.columns.map(c => {\n if (c.startsWith(`${opts.left}.`)) {\n const col = c.slice(opts.left.length + 1);\n if (!leftCols.has(col)) throw new Error(`Invalid column: \"${c}\"`);\n return `L.\"${col.replace(/\"/g, '\"\"')}\" as \"${opts.left}.${col}\"`;\n } else if (c.startsWith(`${opts.right}.`)) {\n const col = c.slice(opts.right.length + 1);\n if (!rightCols.has(col)) throw new Error(`Invalid column: \"${c}\"`);\n return `R.\"${col.replace(/\"/g, '\"\"')}\" as \"${opts.right}.${col}\"`;\n }\n // Try left first, then right\n if (leftCols.has(c)) return `L.\"${c.replace(/\"/g, '\"\"')}\" as \"${opts.left}.${c}\"`;\n if (rightCols.has(c)) return `R.\"${c.replace(/\"/g, '\"\"')}\" as \"${opts.right}.${c}\"`;\n throw new Error(`Column \"${c}\" not found in either dataset`);\n }).join(', ');\n } else {\n // Select all columns from both, prefixed\n const leftSelect = Array.from(leftCols).map(c => `L.\"${c.replace(/\"/g, '\"\"')}\" as \"${opts.left}.${c}\"`);\n const rightSelect = Array.from(rightCols).map(c => `R.\"${c.replace(/\"/g, '\"\"')}\" as \"${opts.right}.${c}\"`);\n selectCols = [...leftSelect, ...rightSelect].join(', ');\n }\n\n const limit = Math.min(opts.limit ?? 100, 500);\n const leftCol = `L.\"${opts.leftColumn.replace(/\"/g, '\"\"')}\"`;\n const rightCol = `R.\"${opts.rightColumn.replace(/\"/g, '\"\"')}\"`;\n\n // Count\n const countSql = `SELECT COUNT(*) as cnt FROM \"${leftTable}\" L ${joinKeyword} \"${rightTable}\" R ON ${leftCol} = ${rightCol}`;\n const countRow = this.db.get(countSql);\n const totalMatching = countRow?.cnt ?? 0;\n\n // Fetch\n const sql = `SELECT ${selectCols} FROM \"${leftTable}\" L ${joinKeyword} \"${rightTable}\" R ON ${leftCol} = ${rightCol} LIMIT ?`;\n const rows = this.db.all(sql, [limit]);\n\n return { rows, totalMatching };\n }\n\n // \u2500\u2500 Correlations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n correlations(datasetId: string, threshold = 0.3): CorrelationPair[] | null {\n const ds = this.db.get('SELECT * FROM data_datasets WHERE id = ?', [datasetId]);\n if (!ds) return null;\n\n const tableName = `data_rows_${datasetId.replace(/[^a-zA-Z0-9_]/g, '_')}`;\n\n // Get numeric columns\n const numericCols = (this.db.all(\n `SELECT name FROM data_columns WHERE dataset_id = ? AND inferred_type IN ('integer', 'float')`,\n [datasetId],\n ) as any[]).map(r => r.name);\n\n if (numericCols.length < 2) return [];\n\n // Compute pairwise Pearson correlations using SQL\n const results: CorrelationPair[] = [];\n\n for (let i = 0; i < numericCols.length; i++) {\n for (let j = i + 1; j < numericCols.length; j++) {\n const col1 = numericCols[i]!;\n const col2 = numericCols[j]!;\n const c1 = `\"${col1.replace(/\"/g, '\"\"')}\"`;\n const c2 = `\"${col2.replace(/\"/g, '\"\"')}\"`;\n\n // Pearson correlation via SQL:\n // r = (n*sum(xy) - sum(x)*sum(y)) / sqrt((n*sum(x\u00B2) - sum(x)\u00B2) * (n*sum(y\u00B2) - sum(y)\u00B2))\n const row = this.db.get(`\n SELECT\n COUNT(*) as n,\n SUM(CAST(${c1} AS REAL) * CAST(${c2} AS REAL)) as sum_xy,\n SUM(CAST(${c1} AS REAL)) as sum_x,\n SUM(CAST(${c2} AS REAL)) as sum_y,\n SUM(CAST(${c1} AS REAL) * CAST(${c1} AS REAL)) as sum_x2,\n SUM(CAST(${c2} AS REAL) * CAST(${c2} AS REAL)) as sum_y2\n FROM \"${tableName}\"\n WHERE ${c1} IS NOT NULL AND ${c2} IS NOT NULL\n AND typeof(${c1}) != 'text' AND typeof(${c2}) != 'text'\n `);\n\n if (!row || row.n < 3) continue;\n\n const n = row.n;\n const numerator = n * row.sum_xy - row.sum_x * row.sum_y;\n const denomX = n * row.sum_x2 - row.sum_x * row.sum_x;\n const denomY = n * row.sum_y2 - row.sum_y * row.sum_y;\n const denom = Math.sqrt(denomX * denomY);\n\n if (denom === 0) continue;\n\n const correlation = numerator / denom;\n const absCorr = Math.abs(correlation);\n\n if (absCorr < threshold) continue;\n\n const strength: CorrelationPair['strength'] =\n absCorr >= 0.7 ? 'strong' :\n absCorr >= 0.4 ? 'moderate' :\n absCorr >= 0.2 ? 'weak' : 'negligible';\n\n results.push({ column1: col1, column2: col2, correlation: Math.round(correlation * 10000) / 10000, strength });\n }\n }\n\n // Sort by absolute correlation descending\n results.sort((a, b) => Math.abs(b.correlation) - Math.abs(a.correlation));\n return results;\n }\n\n // \u2500\u2500 History \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getHistory(datasetId: string, limit = 20): Array<{\n id: number;\n datasetId: string;\n snapshotAt: number;\n rowCount: number;\n columnCount: number;\n columns: Array<{ name: string; type: string; cardinality: number; nullPct: number }>;\n contentHash: string;\n }> | null {\n const ds = this.db.get('SELECT id FROM data_datasets WHERE id = ?', [datasetId]);\n if (!ds) return null;\n\n const rows = this.db.all(\n 'SELECT * FROM data_dataset_history WHERE dataset_id = ? ORDER BY snapshot_at DESC LIMIT ?',\n [datasetId, limit],\n ) as any[];\n\n return rows.map(r => ({\n id: r.id,\n datasetId: r.dataset_id,\n snapshotAt: r.snapshot_at,\n rowCount: r.row_count,\n columnCount: r.column_count,\n columns: JSON.parse(r.columns_json),\n contentHash: r.content_hash,\n }));\n }\n\n // \u2500\u2500 Drift Detection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n detectDrift(datasetId: string): {\n hasDrift: boolean;\n addedColumns: string[];\n removedColumns: string[];\n changedColumns: Array<{ name: string; changes: string[] }>;\n rowCountDelta: number;\n } | null {\n const ds = this.db.get('SELECT id FROM data_datasets WHERE id = ?', [datasetId]);\n if (!ds) return null;\n\n const snapshots = this.db.all(\n 'SELECT * FROM data_dataset_history WHERE dataset_id = ? ORDER BY snapshot_at DESC LIMIT 2',\n [datasetId],\n ) as any[];\n\n if (snapshots.length < 2) {\n return { hasDrift: false, addedColumns: [], removedColumns: [], changedColumns: [], rowCountDelta: 0 };\n }\n\n const latest = snapshots[0];\n const previous = snapshots[1];\n const latestCols: Array<{ name: string; type: string; cardinality: number; nullPct: number }> = JSON.parse(latest.columns_json);\n const prevCols: Array<{ name: string; type: string; cardinality: number; nullPct: number }> = JSON.parse(previous.columns_json);\n\n const latestMap = new Map(latestCols.map(c => [c.name, c]));\n const prevMap = new Map(prevCols.map(c => [c.name, c]));\n\n const addedColumns: string[] = [];\n const removedColumns: string[] = [];\n const changedColumns: Array<{ name: string; changes: string[] }> = [];\n\n // Find added columns\n for (const col of latestCols) {\n if (!prevMap.has(col.name)) addedColumns.push(col.name);\n }\n\n // Find removed columns\n for (const col of prevCols) {\n if (!latestMap.has(col.name)) removedColumns.push(col.name);\n }\n\n // Find changed columns\n for (const col of latestCols) {\n const prev = prevMap.get(col.name);\n if (!prev) continue;\n const changes: string[] = [];\n if (col.type !== prev.type) changes.push(`type: ${prev.type} \u2192 ${col.type}`);\n if (col.cardinality !== prev.cardinality) changes.push(`cardinality: ${prev.cardinality} \u2192 ${col.cardinality}`);\n if (Math.abs(col.nullPct - prev.nullPct) > 0.01) changes.push(`nullPct: ${prev.nullPct.toFixed(1)}% \u2192 ${col.nullPct.toFixed(1)}%`);\n if (changes.length > 0) changedColumns.push({ name: col.name, changes });\n }\n\n const rowCountDelta = latest.row_count - previous.row_count;\n const hasDrift = addedColumns.length > 0 || removedColumns.length > 0 || changedColumns.length > 0;\n\n return { hasDrift, addedColumns, removedColumns, changedColumns, rowCountDelta };\n }\n\n // \u2500\u2500 Quality \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n quality(datasetId: string): ColumnQuality[] | null {\n const ds = this.db.get('SELECT * FROM data_datasets WHERE id = ?', [datasetId]);\n if (!ds) return null;\n\n const cols = this.db.all(\n 'SELECT * FROM data_columns WHERE dataset_id = ? ORDER BY position',\n [datasetId],\n ) as any[];\n\n if (cols.length === 0) return [];\n\n const results: ColumnQuality[] = [];\n\n for (const col of cols) {\n const issues: string[] = [];\n let nullRisk = 0;\n let cardinalityRisk = 0;\n let typeRisk = 0;\n\n // Null rate risk\n const nullPct = col.null_pct ?? 0;\n if (nullPct > 50) {\n issues.push(`High null rate (${nullPct.toFixed(1)}%)`);\n nullRisk = 0.4;\n } else if (nullPct > 20) {\n issues.push(`Moderate null rate (${nullPct.toFixed(1)}%)`);\n nullRisk = 0.2;\n } else if (nullPct > 5) {\n issues.push(`Some nulls (${nullPct.toFixed(1)}%)`);\n nullRisk = 0.1;\n }\n\n // Cardinality anomalies\n const cardinality = col.cardinality ?? 0;\n const rowCount = ds.row_count ?? 1;\n\n if (cardinality === 1 && rowCount > 10) {\n issues.push('Constant value (cardinality=1)');\n cardinalityRisk = 0.3;\n } else if (cardinality / rowCount > 0.95 && rowCount > 100) {\n issues.push('Near-unique (>95% distinct)');\n cardinalityRisk = 0.1;\n }\n\n // Type issues\n if (col.inferred_type === 'string' && col.cardinality < 20 && rowCount > 100) {\n issues.push('Low-cardinality string (consider enum/category)');\n typeRisk = 0.05;\n }\n\n const riskScore = Math.min(nullRisk + cardinalityRisk + typeRisk, 1.0);\n\n if (issues.length > 0) {\n results.push({ column: col.name, riskScore, nullRisk, cardinalityRisk, typeRisk, issues });\n }\n }\n\n // Sort by risk score descending\n results.sort((a, b) => b.riskScore - a.riskScore);\n return results;\n }\n\n // \u2500\u2500 Validation Rules \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Infer validation rules from column profiles.\n * Returns rules the code should apply when reading/writing this dataset.\n */\n validationRules(datasetId: string): ValidationRule[] | null {\n const info = this.describeDataset(datasetId);\n if (!info) return null;\n\n const results: ValidationRule[] = [];\n\n for (const col of info.columns) {\n const rules: string[] = [];\n\n // Required/nullable\n if (!col.nullable) {\n rules.push('required (never null)');\n } else if (col.nullPct < 0.01) {\n rules.push('rarely null (<1%) \u2014 consider required');\n }\n\n // Type constraint\n rules.push(`type: ${col.inferredType}`);\n\n // Range constraints for numeric\n if ((col.inferredType === 'integer' || col.inferredType === 'float') && col.minValue != null && col.maxValue != null) {\n rules.push(`range: ${col.minValue} to ${col.maxValue}`);\n }\n\n // Enum constraint for low-cardinality strings\n if (col.inferredType === 'string' && col.cardinality <= 20 && info.dataset.rowCount > 50) {\n rules.push(`enum (${col.cardinality} distinct values): ${col.sampleValues.join(', ')}`);\n }\n\n // Uniqueness\n if (col.cardinality === info.dataset.rowCount && info.dataset.rowCount > 10) {\n rules.push('unique (all values distinct)');\n }\n\n if (rules.length > 1) { // always has at least the type rule\n results.push({ column: col.name, rules });\n }\n }\n\n return results;\n }\n\n // \u2500\u2500 Sample Data Generation Hints \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * From column profiles, provide hints for generating realistic test data.\n */\n sampleHints(datasetId: string): SampleHint[] | null {\n const info = this.describeDataset(datasetId);\n if (!info) return null;\n\n const results: SampleHint[] = [];\n\n for (const col of info.columns) {\n let hint: string;\n\n switch (col.inferredType) {\n case 'integer':\n if (col.minValue != null && col.maxValue != null) {\n hint = `Random integer between ${col.minValue} and ${col.maxValue}`;\n } else {\n hint = 'Random integer';\n }\n if (col.cardinality === info.dataset.rowCount) hint += ' (must be unique)';\n break;\n\n case 'float':\n if (col.minValue != null && col.maxValue != null) {\n hint = `Random float between ${col.minValue} and ${col.maxValue}`;\n if (col.meanValue != null) hint += ` (mean \u2248 ${col.meanValue.toFixed(2)})`;\n } else {\n hint = 'Random float';\n }\n break;\n\n case 'boolean':\n hint = 'Random boolean (true/false)';\n break;\n\n case 'date':\n if (col.minValue && col.maxValue) {\n hint = `Random date between ${col.minValue} and ${col.maxValue}`;\n } else {\n hint = 'Random ISO date string';\n }\n break;\n\n case 'string':\n if (col.cardinality <= 10 && info.dataset.rowCount > 20) {\n hint = `Pick from: ${col.sampleValues.join(', ')}`;\n } else if (col.cardinality === info.dataset.rowCount) {\n // Looks like an ID or email\n const name = col.name.toLowerCase();\n if (name.includes('email')) hint = 'Generate unique email (e.g. user{n}@example.com)';\n else if (name.includes('id') || name.includes('uuid')) hint = 'Generate unique ID/UUID';\n else if (name.includes('name')) hint = 'Generate unique name string';\n else if (name.includes('url') || name.includes('link')) hint = 'Generate unique URL';\n else hint = `Generate unique string (${col.cardinality} distinct in source)`;\n } else {\n hint = `Random string (${col.cardinality} distinct values in source)`;\n if (col.sampleValues.length > 0) hint += `. Examples: ${col.sampleValues.slice(0, 3).join(', ')}`;\n }\n break;\n\n default:\n hint = `Generate ${col.inferredType} value`;\n }\n\n if (col.nullable && col.nullPct > 0.05) {\n hint += ` \u2014 ${(col.nullPct * 100).toFixed(0)}% chance of null`;\n }\n\n results.push({ column: col.name, hint });\n }\n\n return results;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var types_exports = {};
16
+ module.exports = __toCommonJS(types_exports);
17
+ //# sourceMappingURL=types.js.map