sqlite-hub 0.4.0 → 0.6.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 (56) hide show
  1. package/README.md +2 -2
  2. package/changelog.md +15 -0
  3. package/frontend/assets/mockups/connections.png +0 -0
  4. package/frontend/assets/mockups/data.png +0 -0
  5. package/frontend/assets/mockups/data_row_editor.png +0 -0
  6. package/frontend/assets/mockups/home.png +0 -0
  7. package/frontend/assets/mockups/sql_editor.png +0 -0
  8. package/frontend/assets/mockups/sql_editor_querydetail.png +0 -0
  9. package/frontend/assets/mockups/structure.png +0 -0
  10. package/frontend/assets/mockups/structure_inspector.png +0 -0
  11. package/frontend/js/api.js +114 -5
  12. package/frontend/js/app.js +368 -18
  13. package/frontend/js/components/bottomTabs.js +1 -1
  14. package/frontend/js/components/dataGrid.js +3 -3
  15. package/frontend/js/components/queryEditor.js +33 -55
  16. package/frontend/js/components/queryHistoryDetail.js +263 -0
  17. package/frontend/js/components/queryHistoryPanel.js +228 -0
  18. package/frontend/js/components/queryResults.js +32 -46
  19. package/frontend/js/components/rowEditorPanel.js +73 -14
  20. package/frontend/js/components/sidebar.js +1 -0
  21. package/frontend/js/components/tableDesignerEditor.js +356 -0
  22. package/frontend/js/components/tableDesignerSidebar.js +126 -0
  23. package/frontend/js/components/tableDesignerSqlPreview.js +40 -0
  24. package/frontend/js/router.js +10 -0
  25. package/frontend/js/store.js +841 -22
  26. package/frontend/js/utils/format.js +23 -0
  27. package/frontend/js/utils/tableDesigner.js +1192 -0
  28. package/frontend/js/views/data.js +273 -250
  29. package/frontend/js/views/editor.js +34 -10
  30. package/frontend/js/views/overview.js +15 -0
  31. package/frontend/js/views/tableDesigner.js +37 -0
  32. package/frontend/styles/base.css +87 -73
  33. package/frontend/styles/components.css +841 -188
  34. package/frontend/styles/views.css +40 -0
  35. package/package.json +1 -1
  36. package/server/routes/data.js +2 -0
  37. package/server/routes/export.js +4 -1
  38. package/server/routes/overview.js +12 -0
  39. package/server/routes/sql.js +163 -5
  40. package/server/routes/tableDesigner.js +60 -0
  41. package/server/server.js +5 -1
  42. package/server/services/sqlite/dataBrowserService.js +4 -16
  43. package/server/services/sqlite/exportService.js +4 -16
  44. package/server/services/sqlite/overviewService.js +34 -0
  45. package/server/services/sqlite/sqlExecutor.js +83 -63
  46. package/server/services/sqlite/tableDesigner/changeAnalysis.js +295 -0
  47. package/server/services/sqlite/tableDesigner/schemaMapping.js +233 -0
  48. package/server/services/sqlite/tableDesigner/sql.js +63 -0
  49. package/server/services/sqlite/tableDesigner/validation.js +245 -0
  50. package/server/services/sqlite/tableDesignerService.js +181 -0
  51. package/server/services/sqlite/tableSort.js +63 -0
  52. package/server/services/storage/appStateStore.js +674 -1
  53. package/server/services/storage/queryHistoryUtils.js +169 -0
  54. package/frontend/assets/mockups/data_edit.png +0 -0
  55. package/frontend/assets/mockups/graph_visualize.png +0 -0
  56. package/frontend/assets/mockups/overview.png +0 -0
@@ -0,0 +1,233 @@
1
+ const { quoteIdentifier } = require("../../../utils/identifier");
2
+ const { getRawStructureEntries } = require("../introspection");
3
+
4
+ const SUPPORTED_TABLE_DESIGNER_TYPES = [
5
+ "TEXT",
6
+ "INTEGER",
7
+ "REAL",
8
+ "BLOB",
9
+ "NUMERIC",
10
+ "BOOLEAN",
11
+ "DATE",
12
+ "DATETIME",
13
+ ];
14
+
15
+ function normalizeDesignerType(value) {
16
+ const normalized = String(value ?? "").trim().toUpperCase();
17
+ return normalized || "TEXT";
18
+ }
19
+
20
+ function createDesignerWarning({
21
+ code,
22
+ title,
23
+ message,
24
+ tone = "alert",
25
+ blocking = false,
26
+ }) {
27
+ return {
28
+ code,
29
+ title,
30
+ message,
31
+ tone,
32
+ blocking,
33
+ };
34
+ }
35
+
36
+ function buildSingleColumnUniqueSet(indexes = []) {
37
+ const uniqueColumns = new Set();
38
+
39
+ indexes.forEach((index) => {
40
+ if (!index?.unique || index.partial || !Array.isArray(index.columns) || index.columns.length !== 1) {
41
+ return;
42
+ }
43
+
44
+ const columnName = index.columns[0]?.name;
45
+
46
+ if (columnName) {
47
+ uniqueColumns.add(columnName);
48
+ }
49
+ });
50
+
51
+ return uniqueColumns;
52
+ }
53
+
54
+ function buildSimpleForeignKeyMap(foreignKeys = []) {
55
+ const map = new Map();
56
+
57
+ foreignKeys.forEach((foreignKey) => {
58
+ if (!Array.isArray(foreignKey?.mappings) || foreignKey.mappings.length !== 1) {
59
+ return;
60
+ }
61
+
62
+ const mapping = foreignKey.mappings[0];
63
+
64
+ if (!mapping?.from || !mapping?.to || map.has(mapping.from)) {
65
+ return;
66
+ }
67
+
68
+ map.set(mapping.from, {
69
+ table: foreignKey.referencedTable,
70
+ column: mapping.to,
71
+ });
72
+ });
73
+
74
+ return map;
75
+ }
76
+
77
+ function buildSchemaWarnings(tableDetail) {
78
+ const warnings = [];
79
+ const generatedColumns = (tableDetail.columns ?? []).filter((column) => column.generated);
80
+ const compositePrimaryKeyColumns = (tableDetail.columns ?? []).filter(
81
+ (column) => Number(column.primaryKeyPosition ?? 0) > 0
82
+ );
83
+ const complexForeignKeys = (tableDetail.foreignKeys ?? []).filter(
84
+ (foreignKey) => (foreignKey.mappings?.length ?? 0) !== 1
85
+ );
86
+ const complexUniqueIndexes = (tableDetail.indexes ?? []).filter(
87
+ (index) => index.unique && (index.partial || (index.columns?.length ?? 0) !== 1)
88
+ );
89
+
90
+ if (generatedColumns.length) {
91
+ warnings.push(
92
+ createDesignerWarning({
93
+ code: "GENERATED_COLUMNS_PRESENT",
94
+ title: "Generated Columns Detected",
95
+ message:
96
+ "Generated or hidden columns are not editable in Table Designer v1. Safe operations like table rename or adding simple columns still work.",
97
+ })
98
+ );
99
+ }
100
+
101
+ if (compositePrimaryKeyColumns.length > 1) {
102
+ warnings.push(
103
+ createDesignerWarning({
104
+ code: "COMPOSITE_PRIMARY_KEY_PRESENT",
105
+ title: "Composite Primary Key Detected",
106
+ message:
107
+ "This table uses more than one primary key column. Table Designer v1 preserves it, but changing primary key structure requires a manual table rebuild.",
108
+ })
109
+ );
110
+ }
111
+
112
+ if (complexForeignKeys.length) {
113
+ warnings.push(
114
+ createDesignerWarning({
115
+ code: "COMPLEX_FOREIGN_KEYS_PRESENT",
116
+ title: "Complex Foreign Keys Detected",
117
+ message:
118
+ "Composite or multi-mapping foreign keys cannot be edited directly in Table Designer v1. They are preserved until a rebuild is done manually.",
119
+ })
120
+ );
121
+ }
122
+
123
+ if (complexUniqueIndexes.length) {
124
+ warnings.push(
125
+ createDesignerWarning({
126
+ code: "COMPLEX_UNIQUE_CONSTRAINTS_PRESENT",
127
+ title: "Complex Unique Constraints Detected",
128
+ message:
129
+ "Multi-column or partial UNIQUE constraints are outside Table Designer v1. They remain untouched unless you rebuild the table manually.",
130
+ })
131
+ );
132
+ }
133
+
134
+ if (tableDetail.strict) {
135
+ warnings.push(
136
+ createDesignerWarning({
137
+ code: "STRICT_TABLE_PRESENT",
138
+ title: "STRICT Table",
139
+ message:
140
+ "STRICT tables can be renamed and extended with supported columns, but rebuild-style schema edits should be reviewed carefully.",
141
+ tone: "muted",
142
+ })
143
+ );
144
+ }
145
+
146
+ if (tableDetail.withoutRowId) {
147
+ warnings.push(
148
+ createDesignerWarning({
149
+ code: "WITHOUT_ROWID_PRESENT",
150
+ title: "WITHOUT ROWID Table",
151
+ message:
152
+ "WITHOUT ROWID tables can be inspected here, but rebuild-style changes are intentionally blocked in v1.",
153
+ tone: "muted",
154
+ })
155
+ );
156
+ }
157
+
158
+ return warnings;
159
+ }
160
+
161
+ function mapTableColumnToDraft(column, { uniqueColumns, foreignKeyMap }) {
162
+ const foreignKey = foreignKeyMap.get(column.name);
163
+ const type = normalizeDesignerType(column.declaredType || column.affinity || "TEXT");
164
+ const defaultValue = column.defaultValue ?? "";
165
+
166
+ return {
167
+ id: `existing:${column.cid}:${column.name}`,
168
+ isNew: false,
169
+ deleted: false,
170
+ name: column.name,
171
+ type,
172
+ notNull: Boolean(column.notNull),
173
+ unique: uniqueColumns.has(column.name),
174
+ primaryKey: Number(column.primaryKeyPosition ?? 0) > 0,
175
+ defaultValue,
176
+ referencesTable: foreignKey?.table ?? "",
177
+ referencesColumn: foreignKey?.column ?? "",
178
+ originalName: column.name,
179
+ originalType: type,
180
+ originalNotNull: Boolean(column.notNull),
181
+ originalUnique: uniqueColumns.has(column.name),
182
+ originalPrimaryKey: Number(column.primaryKeyPosition ?? 0) > 0,
183
+ originalDefaultValue: defaultValue,
184
+ originalReferencesTable: foreignKey?.table ?? "",
185
+ originalReferencesColumn: foreignKey?.column ?? "",
186
+ };
187
+ }
188
+
189
+ function buildTableDesignerDraft(tableDetail) {
190
+ const uniqueColumns = buildSingleColumnUniqueSet(tableDetail.indexes);
191
+ const foreignKeyMap = buildSimpleForeignKeyMap(tableDetail.foreignKeys);
192
+ const columns = (tableDetail.columns ?? [])
193
+ .filter((column) => column.visible !== false && !column.generated)
194
+ .map((column) => mapTableColumnToDraft(column, { uniqueColumns, foreignKeyMap }));
195
+ const schemaWarnings = buildSchemaWarnings(tableDetail);
196
+
197
+ return {
198
+ mode: "edit",
199
+ originalTableName: tableDetail.name,
200
+ tableName: tableDetail.name,
201
+ columns,
202
+ dirty: false,
203
+ schemaWarnings,
204
+ warnings: [...schemaWarnings],
205
+ };
206
+ }
207
+
208
+ function listDesignerTables(db) {
209
+ return getRawStructureEntries(db)
210
+ .filter((entry) => entry.type === "table")
211
+ .map((entry) => {
212
+ const columns = db
213
+ .prepare(`PRAGMA table_xinfo(${quoteIdentifier(entry.name)})`)
214
+ .all()
215
+ .filter((column) => Number(column.hidden ?? 0) === 0)
216
+ .map((column) => column.name);
217
+
218
+ return {
219
+ name: entry.name,
220
+ columnCount: columns.length,
221
+ columns,
222
+ };
223
+ })
224
+ .sort((left, right) => left.name.localeCompare(right.name, undefined, { sensitivity: "base" }));
225
+ }
226
+
227
+ module.exports = {
228
+ SUPPORTED_TABLE_DESIGNER_TYPES,
229
+ buildTableDesignerDraft,
230
+ createDesignerWarning,
231
+ listDesignerTables,
232
+ normalizeDesignerType,
233
+ };
@@ -0,0 +1,63 @@
1
+ const { quoteIdentifier, quoteIdentifierList } = require("../../../utils/identifier");
2
+ const { normalizeSqlFragment } = require("./validation");
3
+
4
+ function buildColumnDefinition(column) {
5
+ const parts = [quoteIdentifier(column.name)];
6
+ const type = normalizeSqlFragment(column.type);
7
+ const defaultValue = normalizeSqlFragment(column.defaultValue);
8
+
9
+ if (type) {
10
+ parts.push(type);
11
+ }
12
+
13
+ if (column.primaryKey) {
14
+ parts.push("PRIMARY KEY");
15
+ }
16
+
17
+ if (column.notNull) {
18
+ parts.push("NOT NULL");
19
+ }
20
+
21
+ if (column.unique) {
22
+ parts.push("UNIQUE");
23
+ }
24
+
25
+ if (defaultValue) {
26
+ parts.push(`DEFAULT ${defaultValue}`);
27
+ }
28
+
29
+ if (column.referencesTable && column.referencesColumn) {
30
+ parts.push(
31
+ `REFERENCES ${quoteIdentifier(column.referencesTable)}(${quoteIdentifier(column.referencesColumn)})`
32
+ );
33
+ }
34
+
35
+ return parts.join(" ");
36
+ }
37
+
38
+ function buildCreateTableSql(draft) {
39
+ const columnSql = draft.columns.map((column) => ` ${buildColumnDefinition(column)}`).join(",\n");
40
+
41
+ return `CREATE TABLE ${quoteIdentifier(draft.tableName)} (\n${columnSql}\n);`;
42
+ }
43
+
44
+ function buildAlterTableRenameSql(fromName, toName) {
45
+ return `ALTER TABLE ${quoteIdentifier(fromName)} RENAME TO ${quoteIdentifier(toName)};`;
46
+ }
47
+
48
+ function buildAlterTableAddColumnSql(tableName, column) {
49
+ return `ALTER TABLE ${quoteIdentifier(tableName)} ADD COLUMN ${buildColumnDefinition(column)};`;
50
+ }
51
+
52
+ function buildInsertRowsSql(tableName, columns) {
53
+ const placeholders = columns.map(() => "?").join(", ");
54
+ return `INSERT INTO ${quoteIdentifier(tableName)} (${quoteIdentifierList(columns)}) VALUES (${placeholders});`;
55
+ }
56
+
57
+ module.exports = {
58
+ buildAlterTableAddColumnSql,
59
+ buildAlterTableRenameSql,
60
+ buildColumnDefinition,
61
+ buildCreateTableSql,
62
+ buildInsertRowsSql,
63
+ };
@@ -0,0 +1,245 @@
1
+ const { ValidationError } = require("../../../utils/errors");
2
+ const { assertValidIdentifier } = require("../../../utils/identifier");
3
+ const { normalizeDesignerType } = require("./schemaMapping");
4
+
5
+ function normalizeBoolean(value) {
6
+ return value === true || value === "true" || value === 1 || value === "1";
7
+ }
8
+
9
+ function normalizeSqlFragment(value) {
10
+ return String(value ?? "").trim();
11
+ }
12
+
13
+ function assertSafeSqlFragment(value, label, { allowEmpty = true } = {}) {
14
+ const normalized = normalizeSqlFragment(value);
15
+
16
+ if (!normalized) {
17
+ if (allowEmpty) {
18
+ return "";
19
+ }
20
+
21
+ throw new ValidationError(`${label} is required.`);
22
+ }
23
+
24
+ if (
25
+ normalized.includes("\0") ||
26
+ normalized.includes(";") ||
27
+ normalized.includes("--") ||
28
+ normalized.includes("/*") ||
29
+ normalized.includes("*/")
30
+ ) {
31
+ throw new ValidationError(
32
+ `${label} must be a single SQL fragment without statement separators or comments.`
33
+ );
34
+ }
35
+
36
+ return normalized;
37
+ }
38
+
39
+ function normalizeColumnPayload(column = {}, index = 0) {
40
+ const importedValueIndex =
41
+ column.importedValueIndex === null || column.importedValueIndex === undefined
42
+ ? null
43
+ : Number(column.importedValueIndex);
44
+
45
+ return {
46
+ id: String(column.id ?? `column:${index}`),
47
+ isNew: normalizeBoolean(column.isNew),
48
+ deleted: normalizeBoolean(column.deleted),
49
+ name: String(column.name ?? "").trim(),
50
+ type: normalizeDesignerType(column.type),
51
+ notNull: normalizeBoolean(column.notNull),
52
+ unique: normalizeBoolean(column.unique),
53
+ primaryKey: normalizeBoolean(column.primaryKey),
54
+ defaultValue: assertSafeSqlFragment(column.defaultValue, "Default value"),
55
+ referencesTable: String(column.referencesTable ?? "").trim(),
56
+ referencesColumn: String(column.referencesColumn ?? "").trim(),
57
+ originalName: String(column.originalName ?? "").trim(),
58
+ originalType: normalizeDesignerType(column.originalType || column.type),
59
+ originalNotNull: normalizeBoolean(column.originalNotNull),
60
+ originalUnique: normalizeBoolean(column.originalUnique),
61
+ originalPrimaryKey: normalizeBoolean(column.originalPrimaryKey),
62
+ originalDefaultValue: assertSafeSqlFragment(column.originalDefaultValue, "Original default value"),
63
+ originalReferencesTable: String(column.originalReferencesTable ?? "").trim(),
64
+ originalReferencesColumn: String(column.originalReferencesColumn ?? "").trim(),
65
+ importedValueIndex: Number.isInteger(importedValueIndex) ? importedValueIndex : null,
66
+ };
67
+ }
68
+
69
+ function normalizeDraftPayload(payload = {}) {
70
+ const mode = String(payload.mode ?? "create").trim() === "edit" ? "edit" : "create";
71
+
72
+ return {
73
+ mode,
74
+ originalTableName: String(payload.originalTableName ?? "").trim(),
75
+ tableName: String(payload.tableName ?? "").trim(),
76
+ columns: Array.isArray(payload.columns)
77
+ ? payload.columns.map((column, index) => normalizeColumnPayload(column, index))
78
+ : [],
79
+ schemaWarnings: Array.isArray(payload.schemaWarnings) ? payload.schemaWarnings : [],
80
+ warnings: Array.isArray(payload.warnings) ? payload.warnings : [],
81
+ fillImportedRows: normalizeBoolean(payload.fillImportedRows),
82
+ importedCsvFileName: String(payload.importedCsvFileName ?? "").trim(),
83
+ importedCsvDelimiter: String(payload.importedCsvDelimiter ?? "").trim(),
84
+ importedCsvRows: Array.isArray(payload.importedCsvRows)
85
+ ? payload.importedCsvRows.map((row) =>
86
+ Array.isArray(row) ? row.map((cell) => String(cell ?? "")) : []
87
+ )
88
+ : [],
89
+ dirty: normalizeBoolean(payload.dirty),
90
+ };
91
+ }
92
+
93
+ function normalizeIdentifierKey(value) {
94
+ return String(value ?? "").trim().toLowerCase();
95
+ }
96
+
97
+ function buildSelfReferenceColumns(draft) {
98
+ return draft.columns
99
+ .filter((column) => !column.deleted)
100
+ .map((column) => column.name)
101
+ .filter(Boolean);
102
+ }
103
+
104
+ function validatePrimaryKeys(draft, originalDraft) {
105
+ const currentPrimaryKeyColumns = draft.columns.filter(
106
+ (column) => !column.deleted && column.primaryKey
107
+ );
108
+
109
+ if (currentPrimaryKeyColumns.length <= 1) {
110
+ return;
111
+ }
112
+
113
+ if (!originalDraft) {
114
+ throw new ValidationError(
115
+ "Table Designer v1 supports a single primary key column when creating a table."
116
+ );
117
+ }
118
+
119
+ const originalPrimaryKeyNames = new Set(
120
+ originalDraft.columns
121
+ .filter((column) => column.primaryKey)
122
+ .map((column) => normalizeIdentifierKey(column.originalName || column.name))
123
+ );
124
+ const currentPrimaryKeyNames = new Set(
125
+ currentPrimaryKeyColumns.map((column) =>
126
+ normalizeIdentifierKey(column.originalName || column.name)
127
+ )
128
+ );
129
+
130
+ const isUnchangedCompositePrimaryKey =
131
+ originalPrimaryKeyNames.size > 1 &&
132
+ originalPrimaryKeyNames.size === currentPrimaryKeyNames.size &&
133
+ [...currentPrimaryKeyNames].every((name) => originalPrimaryKeyNames.has(name));
134
+
135
+ if (!isUnchangedCompositePrimaryKey) {
136
+ throw new ValidationError(
137
+ "Table Designer v1 does not support editing composite primary keys."
138
+ );
139
+ }
140
+ }
141
+
142
+ function validateTableDesignerDraft(draft, { catalogTables = [], originalDraft = null } = {}) {
143
+ if (!["create", "edit"].includes(draft.mode)) {
144
+ throw new ValidationError("Draft mode must be create or edit.");
145
+ }
146
+
147
+ assertValidIdentifier(draft.tableName, "Table name");
148
+
149
+ if (draft.mode === "edit") {
150
+ assertValidIdentifier(draft.originalTableName, "Original table name");
151
+ }
152
+
153
+ if (!draft.columns.length) {
154
+ throw new ValidationError("At least one column is required.");
155
+ }
156
+
157
+ const activeColumns = draft.columns.filter((column) => !column.deleted);
158
+
159
+ if (!activeColumns.length) {
160
+ throw new ValidationError("At least one column is required.");
161
+ }
162
+
163
+ const catalogByName = new Map(
164
+ catalogTables.map((table) => [normalizeIdentifierKey(table.name), table])
165
+ );
166
+ const normalizedTableName = normalizeIdentifierKey(draft.tableName);
167
+ const normalizedOriginalTableName = normalizeIdentifierKey(draft.originalTableName);
168
+
169
+ if (
170
+ catalogByName.has(normalizedTableName) &&
171
+ (draft.mode === "create" || normalizedTableName !== normalizedOriginalTableName)
172
+ ) {
173
+ throw new ValidationError(`A table named ${draft.tableName} already exists.`);
174
+ }
175
+
176
+ const seenColumns = new Set();
177
+
178
+ activeColumns.forEach((column) => {
179
+ assertValidIdentifier(column.name, "Column name");
180
+ assertSafeSqlFragment(column.type, `Type for ${column.name}`, { allowEmpty: false });
181
+
182
+ const normalizedColumnName = normalizeIdentifierKey(column.name);
183
+
184
+ if (seenColumns.has(normalizedColumnName)) {
185
+ throw new ValidationError(`Duplicate column name: ${column.name}`);
186
+ }
187
+
188
+ seenColumns.add(normalizedColumnName);
189
+
190
+ const hasReferenceTable = Boolean(column.referencesTable);
191
+ const hasReferenceColumn = Boolean(column.referencesColumn);
192
+
193
+ if (hasReferenceTable !== hasReferenceColumn) {
194
+ throw new ValidationError(
195
+ `Column ${column.name} must define both a referenced table and a referenced column.`
196
+ );
197
+ }
198
+
199
+ if (!hasReferenceTable || !hasReferenceColumn) {
200
+ return;
201
+ }
202
+
203
+ const normalizedReferenceTable = normalizeIdentifierKey(column.referencesTable);
204
+ const referencedTable =
205
+ normalizedReferenceTable === normalizedTableName
206
+ ? {
207
+ name: draft.tableName,
208
+ columns: buildSelfReferenceColumns(draft),
209
+ }
210
+ : catalogByName.get(normalizedReferenceTable);
211
+
212
+ if (!referencedTable) {
213
+ throw new ValidationError(
214
+ `Referenced table ${column.referencesTable} does not exist in the current SQLite schema.`
215
+ );
216
+ }
217
+
218
+ const hasReferencedColumn = (referencedTable.columns ?? []).some(
219
+ (candidate) => normalizeIdentifierKey(candidate) === normalizeIdentifierKey(column.referencesColumn)
220
+ );
221
+
222
+ if (!hasReferencedColumn) {
223
+ throw new ValidationError(
224
+ `Referenced column ${column.referencesColumn} does not exist on ${referencedTable.name}.`
225
+ );
226
+ }
227
+ });
228
+
229
+ validatePrimaryKeys(draft, originalDraft);
230
+
231
+ if (draft.mode === "edit" && draft.fillImportedRows) {
232
+ throw new ValidationError("Imported row fill is only available when creating a table.");
233
+ }
234
+
235
+ if (draft.fillImportedRows && !draft.importedCsvRows.length) {
236
+ throw new ValidationError("Fill requires imported CSV rows.");
237
+ }
238
+ }
239
+
240
+ module.exports = {
241
+ normalizeDraftPayload,
242
+ normalizeIdentifierKey,
243
+ normalizeSqlFragment,
244
+ validateTableDesignerDraft,
245
+ };