sqlite-hub 0.11.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -13
- package/database.sqlite +0 -0
- package/frontend/js/api.js +64 -12
- package/frontend/js/app.js +723 -37
- package/frontend/js/components/emptyState.js +42 -46
- package/frontend/js/components/modal.js +526 -2
- package/frontend/js/components/queryEditor.js +12 -3
- package/frontend/js/components/queryResults.js +79 -17
- package/frontend/js/components/rowEditorPanel.js +345 -12
- package/frontend/js/components/sidebar.js +106 -23
- package/frontend/js/components/tableDesignerEditor.js +69 -11
- package/frontend/js/store.js +437 -26
- package/frontend/js/utils/copyColumnExport.js +117 -0
- package/frontend/js/utils/exportFilenames.js +32 -0
- package/frontend/js/utils/filePathPreview.js +315 -0
- package/frontend/js/utils/format.js +1 -1
- package/frontend/js/utils/rowEditorJson.js +65 -0
- package/frontend/js/utils/sqlFormatter.js +691 -0
- package/frontend/js/utils/tableDesigner.js +178 -6
- package/frontend/js/utils/textCellStats.js +20 -0
- package/frontend/js/utils/timestampPreview.js +264 -0
- package/frontend/js/views/charts.js +3 -1
- package/frontend/js/views/data.js +39 -4
- package/frontend/js/views/editor.js +50 -1
- package/frontend/js/views/settings.js +1 -4
- package/frontend/js/views/structure.js +154 -212
- package/frontend/styles/base.css +6 -0
- package/frontend/styles/components.css +463 -2
- package/frontend/styles/structure-graph.css +0 -3
- package/frontend/styles/tailwind.generated.css +1 -1
- package/frontend/styles/tokens.css +95 -95
- package/package.json +2 -3
- package/server/routes/export.js +97 -12
- package/server/services/sqlite/dataBrowserService.js +2 -68
- package/server/services/sqlite/exportService.js +74 -15
- package/server/services/sqlite/introspection.js +209 -1
- package/server/services/sqlite/sqlExecutor.js +31 -0
- package/server/services/sqlite/tableDesigner/changeAnalysis.js +25 -1
- package/server/services/sqlite/tableDesigner/schemaMapping.js +105 -10
- package/server/services/sqlite/tableDesigner/validation.js +60 -2
- package/server/services/sqlite/tableDesignerService.js +1 -1
- package/server/services/sqlite/tableFilter.js +75 -0
- package/server/utils/csv.js +30 -4
- package/tests/check-constraint-options.test.js +90 -0
- package/tests/export-filenames.test.js +34 -0
- package/tests/file-path-preview.test.js +165 -0
- package/tests/row-editor-json.test.js +82 -0
- package/tests/row-editor-timestamp-preview.test.js +192 -0
- package/tests/sql-formatter.test.js +173 -0
- package/tests/sql-highlight.test.js +38 -0
- package/tests/table-designer-v2-unique-constraints.test.js +78 -0
- package/tests/text-cell-stats.test.js +38 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const Database = require("better-sqlite3");
|
|
2
|
+
const assert = require("node:assert/strict");
|
|
3
|
+
const test = require("node:test");
|
|
4
|
+
const { getTableDetail } = require("../server/services/sqlite/introspection");
|
|
5
|
+
const { analyzeTableDesignerChanges } = require("../server/services/sqlite/tableDesigner/changeAnalysis");
|
|
6
|
+
const { buildTableDesignerDraft } = require("../server/services/sqlite/tableDesigner/schemaMapping");
|
|
7
|
+
|
|
8
|
+
test("table designer draft exposes v2 complex unique constraints", () => {
|
|
9
|
+
const db = new Database(":memory:");
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
db.exec(`
|
|
13
|
+
CREATE TABLE companies (
|
|
14
|
+
company_id INTEGER NOT NULL,
|
|
15
|
+
ticker TEXT NOT NULL,
|
|
16
|
+
deleted_at TEXT,
|
|
17
|
+
name TEXT,
|
|
18
|
+
UNIQUE (company_id, ticker)
|
|
19
|
+
);
|
|
20
|
+
CREATE UNIQUE INDEX uniq_active_company_ticker
|
|
21
|
+
ON companies (ticker)
|
|
22
|
+
WHERE deleted_at IS NULL;
|
|
23
|
+
`);
|
|
24
|
+
|
|
25
|
+
const tableDetail = getTableDetail(db, "companies", { includeRowCount: false });
|
|
26
|
+
const draft = buildTableDesignerDraft(tableDetail);
|
|
27
|
+
|
|
28
|
+
assert.equal(draft.designerVersion, 2);
|
|
29
|
+
assert.equal(draft.uniqueConstraints.length, 2);
|
|
30
|
+
|
|
31
|
+
const multiColumnConstraint = draft.uniqueConstraints.find(
|
|
32
|
+
(constraint) => constraint.columns.length === 2
|
|
33
|
+
);
|
|
34
|
+
assert.ok(multiColumnConstraint);
|
|
35
|
+
assert.equal(multiColumnConstraint.partial, false);
|
|
36
|
+
assert.deepEqual(
|
|
37
|
+
multiColumnConstraint.columns.map((column) => column.name),
|
|
38
|
+
["company_id", "ticker"]
|
|
39
|
+
);
|
|
40
|
+
assert.equal(multiColumnConstraint.expression, 'UNIQUE ("company_id", "ticker")');
|
|
41
|
+
assert.equal(multiColumnConstraint.originalExpression, multiColumnConstraint.expression);
|
|
42
|
+
assert.equal(multiColumnConstraint.editable, true);
|
|
43
|
+
assert.equal(multiColumnConstraint.preserved, true);
|
|
44
|
+
|
|
45
|
+
const partialConstraint = draft.uniqueConstraints.find((constraint) => constraint.partial);
|
|
46
|
+
assert.ok(partialConstraint);
|
|
47
|
+
assert.equal(partialConstraint.name, "uniq_active_company_ticker");
|
|
48
|
+
assert.match(partialConstraint.sql, /CREATE UNIQUE INDEX uniq_active_company_ticker/i);
|
|
49
|
+
assert.match(partialConstraint.sql, /WHERE deleted_at IS NULL/i);
|
|
50
|
+
assert.equal(partialConstraint.expression, partialConstraint.sql);
|
|
51
|
+
|
|
52
|
+
const changedDraft = {
|
|
53
|
+
...draft,
|
|
54
|
+
uniqueConstraints: draft.uniqueConstraints.map((constraint) =>
|
|
55
|
+
constraint.id === multiColumnConstraint.id
|
|
56
|
+
? {
|
|
57
|
+
...constraint,
|
|
58
|
+
expression: 'UNIQUE ("company_id", "ticker", "name")',
|
|
59
|
+
}
|
|
60
|
+
: constraint
|
|
61
|
+
),
|
|
62
|
+
};
|
|
63
|
+
const analysis = analyzeTableDesignerChanges({ draft: changedDraft, originalDraft: draft });
|
|
64
|
+
assert.equal(analysis.dirty, true);
|
|
65
|
+
assert.equal(analysis.executable, false);
|
|
66
|
+
assert.match(analysis.warnings[0].title, /Constraint Change Requires Rebuild/);
|
|
67
|
+
|
|
68
|
+
const uniqueWarning = draft.schemaWarnings.find(
|
|
69
|
+
(warning) => warning.code === "COMPLEX_UNIQUE_CONSTRAINTS_PRESENT"
|
|
70
|
+
);
|
|
71
|
+
assert.ok(uniqueWarning);
|
|
72
|
+
assert.equal(uniqueWarning.title, "Table Designer v2 Unique Constraints");
|
|
73
|
+
assert.equal(uniqueWarning.tone, "muted");
|
|
74
|
+
assert.doesNotMatch(uniqueWarning.message, /outside Table Designer v1/i);
|
|
75
|
+
} finally {
|
|
76
|
+
db.close();
|
|
77
|
+
}
|
|
78
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const assert = require("node:assert/strict");
|
|
2
|
+
const { readFileSync } = require("node:fs");
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
const test = require("node:test");
|
|
5
|
+
|
|
6
|
+
let textCellStatsModulePromise = null;
|
|
7
|
+
|
|
8
|
+
function loadTextCellStatsModule() {
|
|
9
|
+
if (!textCellStatsModulePromise) {
|
|
10
|
+
const source = readFileSync(
|
|
11
|
+
path.resolve(__dirname, "../frontend/js/utils/textCellStats.js"),
|
|
12
|
+
"utf8"
|
|
13
|
+
);
|
|
14
|
+
const url = `data:text/javascript;base64,${Buffer.from(source).toString("base64")}`;
|
|
15
|
+
|
|
16
|
+
textCellStatsModulePromise = import(url);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return textCellStatsModulePromise;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
test("text cell character count only returns counts for non-empty strings", async () => {
|
|
23
|
+
const { getTextCellCharacterCount } = await loadTextCellStatsModule();
|
|
24
|
+
|
|
25
|
+
assert.equal(getTextCellCharacterCount(""), null);
|
|
26
|
+
assert.equal(getTextCellCharacterCount(null), null);
|
|
27
|
+
assert.equal(getTextCellCharacterCount(123), null);
|
|
28
|
+
assert.equal(getTextCellCharacterCount("hello"), 5);
|
|
29
|
+
assert.equal(getTextCellCharacterCount("a\nb"), 3);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("text cell character count label is readable", async () => {
|
|
33
|
+
const { formatTextCellCharacterCount } = await loadTextCellStatsModule();
|
|
34
|
+
|
|
35
|
+
assert.equal(formatTextCellCharacterCount(1), "1 char");
|
|
36
|
+
assert.equal(formatTextCellCharacterCount(25), "25 chars");
|
|
37
|
+
assert.equal(formatTextCellCharacterCount(1200), "1,200 chars");
|
|
38
|
+
});
|