@adecore/database 0.38.0 → 0.40.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 (131) hide show
  1. package/dist/console/QueryConsole.js +2 -10
  2. package/dist/console/SchemaPicker.js +0 -1
  3. package/dist/console/StatementResultView.js +1 -1
  4. package/dist/ddl/diff.js +1 -1
  5. package/dist/explorer/tree.js +0 -1
  6. package/dist/export-formats.d.ts +2 -0
  7. package/dist/export-formats.js +1 -0
  8. package/dist/grid/CopyAsMenu.js +0 -1
  9. package/dist/grid/DataGrid.js +10 -10
  10. package/dist/grid/column-layout.d.ts +0 -1
  11. package/dist/grid/column-layout.js +1 -2
  12. package/dist/grid/column-view.js +3 -2
  13. package/dist/grid/display.js +1 -2
  14. package/dist/host/host.js +1 -2
  15. package/dist/host/validate.d.ts +3 -0
  16. package/dist/host/validate.js +18 -15
  17. package/dist/same-list.d.ts +1 -0
  18. package/dist/same-list.js +1 -0
  19. package/dist/stored-size.d.ts +1 -0
  20. package/dist/stored-size.js +10 -0
  21. package/dist/table/TableToolbar.js +1 -1
  22. package/dist/table/TableView.js +2 -10
  23. package/package.json +15 -15
  24. package/src/DatabaseProvider.tsx +2 -0
  25. package/src/KeyIcon.tsx +1 -0
  26. package/src/client/client.ts +13 -0
  27. package/src/client-context.ts +2 -0
  28. package/src/connections/ConnectionForm.tsx +11 -0
  29. package/src/connections/ConnectionManager.tsx +9 -0
  30. package/src/connections/connection-config.ts +21 -0
  31. package/src/connections/docker.ts +2 -0
  32. package/src/connections/use-docker-containers.ts +3 -0
  33. package/src/console/DestructiveDialog.tsx +1 -0
  34. package/src/console/QueryConsole.tsx +38 -10
  35. package/src/console/SchemaPicker.tsx +1 -1
  36. package/src/console/SqlEditor.tsx +10 -0
  37. package/src/console/StatementResultView.tsx +7 -2
  38. package/src/console/TransactionControls.tsx +1 -0
  39. package/src/console/destructive.ts +11 -0
  40. package/src/console/editing.ts +10 -0
  41. package/src/console/history.ts +8 -0
  42. package/src/console/labels.ts +1 -0
  43. package/src/console/outcome.ts +1 -0
  44. package/src/console/result-keys.ts +16 -0
  45. package/src/console/shortcuts.ts +2 -0
  46. package/src/ddl/alter-mysql.ts +16 -0
  47. package/src/ddl/alter-sqlite.ts +21 -0
  48. package/src/ddl/alter.ts +2 -0
  49. package/src/ddl/column.ts +11 -0
  50. package/src/ddl/create.ts +15 -0
  51. package/src/ddl/dialect.ts +4 -0
  52. package/src/ddl/diff.ts +22 -2
  53. package/src/ddl/draft.ts +1 -0
  54. package/src/ddl/parse.ts +31 -0
  55. package/src/ddl/types.ts +3 -0
  56. package/src/ddl/validate.ts +12 -0
  57. package/src/designer/ColumnsEditor.tsx +3 -0
  58. package/src/designer/DesignerView.tsx +2 -0
  59. package/src/designer/ForeignKeysEditor.tsx +4 -0
  60. package/src/designer/IndexesEditor.tsx +1 -0
  61. package/src/designer/NamePicker.tsx +2 -0
  62. package/src/designer/OptionsEditor.tsx +4 -0
  63. package/src/designer/SqlPreview.tsx +2 -0
  64. package/src/designer/TableDesigner.tsx +14 -0
  65. package/src/designer/edit.ts +13 -0
  66. package/src/designer/plan.ts +3 -0
  67. package/src/designer/recover.ts +1 -0
  68. package/src/designer/useReferenceColumns.ts +5 -0
  69. package/src/explorer/DatabaseExplorer.tsx +40 -0
  70. package/src/explorer/RowMenu.tsx +2 -0
  71. package/src/explorer/TableDialog.tsx +6 -0
  72. package/src/explorer/expansion.ts +8 -0
  73. package/src/explorer/tree.ts +73 -2
  74. package/src/explorer/use-explorer-loads.ts +15 -0
  75. package/src/explorer/use-stored-expansion.ts +4 -0
  76. package/src/export-formats.ts +3 -0
  77. package/src/grid/CellEditor.tsx +1 -0
  78. package/src/grid/CopyAsMenu.tsx +1 -1
  79. package/src/grid/DataGrid.tsx +93 -23
  80. package/src/grid/EnumPicker.tsx +6 -0
  81. package/src/grid/GridHeaderCell.tsx +11 -0
  82. package/src/grid/GridRowView.tsx +3 -0
  83. package/src/grid/RecordView.tsx +19 -0
  84. package/src/grid/ValueDock.tsx +2 -0
  85. package/src/grid/aggregates.ts +12 -0
  86. package/src/grid/column-layout.ts +12 -4
  87. package/src/grid/column-selection.ts +9 -0
  88. package/src/grid/column-view.ts +6 -2
  89. package/src/grid/copy-formats.ts +13 -0
  90. package/src/grid/display.ts +17 -3
  91. package/src/grid/edit-value.ts +11 -0
  92. package/src/grid/enum-type.ts +15 -0
  93. package/src/grid/focused-value.ts +4 -0
  94. package/src/grid/layout.ts +12 -0
  95. package/src/grid/navigation.ts +5 -0
  96. package/src/grid/row-selection.ts +7 -0
  97. package/src/grid/sort.ts +1 -0
  98. package/src/grid/whole-cells.ts +8 -0
  99. package/src/host/host.ts +16 -3
  100. package/src/host/spawn.ts +2 -0
  101. package/src/host/validate.ts +37 -15
  102. package/src/same-list.ts +2 -0
  103. package/src/sql-split.ts +28 -0
  104. package/src/sql.ts +20 -0
  105. package/src/stored-size.ts +10 -0
  106. package/src/structure/StructureView.tsx +18 -0
  107. package/src/table/CommandField.tsx +11 -0
  108. package/src/table/ImportDialog.tsx +4 -0
  109. package/src/table/TableStatusBar.tsx +1 -0
  110. package/src/table/TableToolbar.tsx +2 -2
  111. package/src/table/TableView.tsx +50 -10
  112. package/src/table/command-field.ts +40 -0
  113. package/src/table/grid-rows.ts +13 -0
  114. package/src/table/import-mapping.ts +5 -0
  115. package/src/table/layout-store.ts +7 -0
  116. package/src/table/paging.ts +1 -0
  117. package/src/table/pending.ts +18 -0
  118. package/src/table/references.ts +4 -0
  119. package/src/table/useLoaded.ts +3 -0
  120. package/src/table/useStoredLayout.ts +2 -0
  121. package/src/table/useTableTransfer.ts +17 -1
  122. package/src/testing/fake.ts +24 -0
  123. package/src/use-database-resources.ts +1 -0
  124. package/src/use-popup-press.ts +3 -0
  125. package/src/value/ValueEditor.tsx +6 -0
  126. package/src/value/draft.ts +7 -0
  127. package/src/value/hex.ts +8 -0
  128. package/src/value/json.ts +16 -0
  129. package/src/value/views.ts +14 -0
  130. package/src/workbench/DatabaseWorkbench.tsx +22 -0
  131. package/src/workbench/tabs.ts +34 -0
@@ -87,13 +87,17 @@ export function DatabaseWorkbench({ connections, onConnectionsChange, className,
87
87
  const known = useMemo(() => new Set(connections.map((connection) => connection.id)), [connections]);
88
88
  // While there are no connections they may still be loading, so nothing is dropped then.
89
89
  const pruned = known.size > 0 ? pruneTabs(state, known) : state;
90
+
90
91
  if (pruned !== state) {
91
92
  setState(pruned);
92
93
  }
94
+
93
95
  const isOpen = (id: string): boolean => state.tabs.some((tab) => tab.id === id);
96
+
94
97
  if (![...dirty].every(isOpen)) {
95
98
  setDirty(new Set([...dirty].filter(isOpen)));
96
99
  }
100
+
97
101
  const tabs = state.tabs.filter((tab) => known.has(connectionIdOf(tab)));
98
102
  const active = tabs.find((tab) => tab.id === state.activeId) ?? tabs.at(-1) ?? null;
99
103
  const canManage = onConnectionsChange !== undefined;
@@ -104,33 +108,42 @@ export function DatabaseWorkbench({ connections, onConnectionsChange, className,
104
108
 
105
109
  const onAction = useStableCallback((action: DatabaseAction): void => {
106
110
  const connectionId = action.kind === 'open-table' || action.kind === 'edit-table' ? action.ref.connectionId : action.connectionId;
111
+
107
112
  if (!known.has(connectionId) || (action.kind === 'manage-connection' && !canManage)) {
108
113
  outer?.(action);
114
+
109
115
  return;
110
116
  }
117
+
111
118
  switch (action.kind) {
112
119
  case 'open-table':
113
120
  setSelection(action.ref);
114
121
  setState((current) => openTable(current, action.ref, action.view, action.where));
122
+
115
123
  break;
116
124
  case 'open-console':
117
125
  setState((current) => openConsole(current, action.connectionId, action.schema, action.sql));
126
+
118
127
  break;
119
128
  case 'new-table':
120
129
  setState((current) => openDesigner(current, action.connectionId, action.schema));
130
+
121
131
  break;
122
132
  case 'edit-table':
123
133
  setState((current) => openDesigner(current, action.ref.connectionId, action.ref.schema, action.ref.table));
134
+
124
135
  break;
125
136
  case 'manage-connection':
126
137
  setManaged(action.connectionId);
127
138
  setManaging(true);
139
+
128
140
  break;
129
141
  }
130
142
  });
131
143
 
132
144
  function newConsole(): void {
133
145
  const context = consoleContext();
146
+
134
147
  if (context !== null) {
135
148
  setState((current) => openConsole(current, context.connectionId, context.schema));
136
149
  }
@@ -141,10 +154,13 @@ export function DatabaseWorkbench({ connections, onConnectionsChange, className,
141
154
  if (active !== null) {
142
155
  return { connectionId: connectionIdOf(active), schema: active.kind === 'table' ? active.ref.schema : active.schema };
143
156
  }
157
+
144
158
  if (selection !== null && known.has(selection.connectionId)) {
145
159
  return { connectionId: selection.connectionId, schema: selection.schema };
146
160
  }
161
+
147
162
  const first = connections[0];
163
+
148
164
  return first === undefined ? null : { connectionId: first.id };
149
165
  }
150
166
 
@@ -179,12 +195,15 @@ export function DatabaseWorkbench({ connections, onConnectionsChange, className,
179
195
  if (current.has(id) === isDirty) {
180
196
  return current;
181
197
  }
198
+
182
199
  const next = new Set(current);
200
+
183
201
  if (isDirty) {
184
202
  next.add(id);
185
203
  } else {
186
204
  next.delete(id);
187
205
  }
206
+
188
207
  return next;
189
208
  });
190
209
  }
@@ -202,9 +221,11 @@ export function DatabaseWorkbench({ connections, onConnectionsChange, className,
202
221
 
203
222
  const viewOf = (tab: WorkbenchTab) => {
204
223
  const connection = connections.find((entry) => entry.id === connectionIdOf(tab));
224
+
205
225
  if (connection === undefined) {
206
226
  return null;
207
227
  }
228
+
208
229
  switch (tab.kind) {
209
230
  case 'table':
210
231
  return <TableTab connection={connection} tab={tab} onDirtyChange={(isDirty) => markDirty(tab.id, isDirty)} />;
@@ -321,6 +342,7 @@ export function DatabaseWorkbench({ connections, onConnectionsChange, className,
321
342
  if (closing !== null) {
322
343
  closeById(closing);
323
344
  }
345
+
324
346
  setClosing(null);
325
347
  }}
326
348
  onOpenChange={(open) => {
@@ -32,14 +32,18 @@ const add = (state: WorkbenchState, tab: WorkbenchTab): WorkbenchState => ({ ...
32
32
 
33
33
  const update = (state: WorkbenchState, id: string, change: (tab: WorkbenchTab) => WorkbenchTab): WorkbenchState => {
34
34
  let changed = false;
35
+
35
36
  const tabs = state.tabs.map((tab) => {
36
37
  if (tab.id !== id) {
37
38
  return tab;
38
39
  }
40
+
39
41
  const next = change(tab);
40
42
  changed ||= next !== tab;
43
+
41
44
  return next;
42
45
  });
46
+
43
47
  return changed ? { ...state, tabs } : state;
44
48
  };
45
49
 
@@ -51,27 +55,34 @@ export const openTable = (state: WorkbenchState, ref: TableRef, view: TableViewM
51
55
  if (where !== undefined && where.trim() !== '') {
52
56
  return add({ ...state, serial: state.serial + 1 }, { id: `filtered:${state.serial + 1}`, kind: 'table', ref, view, where });
53
57
  }
58
+
54
59
  const id = tableTabId(ref);
55
60
  const existing = state.tabs.find((tab) => tab.id === id);
61
+
56
62
  if (existing === undefined) {
57
63
  return add(state, { id, kind: 'table', ref, view });
58
64
  }
65
+
59
66
  return focusTab(setTableView(state, id, view), id);
60
67
  };
61
68
 
62
69
  export const openConsole = (state: WorkbenchState, connectionId: string, schema?: string, sql = ''): WorkbenchState => {
63
70
  const serial = state.serial + 1;
64
71
  const consoles = state.consoles + 1;
72
+
65
73
  return add({ ...state, serial, consoles }, { id: `console:${serial}`, kind: 'console', connectionId, schema, sql, number: consoles });
66
74
  };
67
75
 
68
76
  /* A table is designed in one tab; a new table gets a tab each time. */
69
77
  export const openDesigner = (state: WorkbenchState, connectionId: string, schema: string, table?: string): WorkbenchState => {
70
78
  const existing = table === undefined ? undefined : state.tabs.find((tab) => isDesignerOf(tab, connectionId, schema, table));
79
+
71
80
  if (existing !== undefined) {
72
81
  return focusTab(state, existing.id);
73
82
  }
83
+
74
84
  const serial = state.serial + 1;
85
+
75
86
  return add({ ...state, serial }, { id: `designer:${serial}`, kind: 'designer', connectionId, schema, table });
76
87
  };
77
88
 
@@ -84,29 +95,37 @@ export const setConsoleSql = (state: WorkbenchState, id: string, sql: string): W
84
95
  /* The designer of a new table became the designer of that table, in the same tab. Closes any other designer of it. */
85
96
  export const setDesignerTable = (state: WorkbenchState, id: string, table: string): WorkbenchState => {
86
97
  const subject = state.tabs.find((tab) => tab.id === id);
98
+
87
99
  if (subject?.kind !== 'designer' || subject.table === table) {
88
100
  return state;
89
101
  }
102
+
90
103
  const tabs = state.tabs.filter((tab) => tab.id === id || !isDesignerOf(tab, subject.connectionId, subject.schema, table));
104
+
91
105
  return update({ ...state, tabs }, id, (tab) => (tab.kind === 'designer' ? { ...tab, table } : tab));
92
106
  };
93
107
 
94
108
  /* The tab next to the closed one comes forward when the closed one was in front. */
95
109
  export const closeTab = (state: WorkbenchState, id: string): WorkbenchState => {
96
110
  const at = state.tabs.findIndex((tab) => tab.id === id);
111
+
97
112
  if (at < 0) {
98
113
  return state;
99
114
  }
115
+
100
116
  const tabs = state.tabs.filter((tab) => tab.id !== id);
117
+
101
118
  return { ...state, tabs, activeId: state.activeId === id ? (tabs[Math.min(at, tabs.length - 1)]?.id ?? null) : state.activeId };
102
119
  };
103
120
 
104
121
  /* Drops the tabs of connections that are gone. */
105
122
  export const pruneTabs = (state: WorkbenchState, connectionIds: ReadonlySet<string>): WorkbenchState => {
106
123
  const tabs = state.tabs.filter((tab) => connectionIds.has(connectionIdOf(tab)));
124
+
107
125
  if (tabs.length === state.tabs.length) {
108
126
  return state;
109
127
  }
128
+
110
129
  return { ...state, tabs, activeId: tabs.some((tab) => tab.id === state.activeId) ? state.activeId : (tabs.at(-1)?.id ?? null) };
111
130
  };
112
131
 
@@ -118,6 +137,7 @@ const isOptionalText = (value: unknown): value is string | undefined => value ==
118
137
 
119
138
  const refOf = (value: unknown): TableRef | null => {
120
139
  const ref = value as Partial<Record<keyof TableRef, unknown>> | null;
140
+
121
141
  return typeof ref === 'object' && ref !== null && isText(ref.connectionId) && isText(ref.schema) && isText(ref.table)
122
142
  ? { connectionId: ref.connectionId, schema: ref.schema, table: ref.table }
123
143
  : null;
@@ -125,14 +145,18 @@ const refOf = (value: unknown): TableRef | null => {
125
145
 
126
146
  const tabOf = (value: unknown): WorkbenchTab | null => {
127
147
  const raw = value as Record<string, unknown> | null;
148
+
128
149
  if (typeof raw !== 'object' || raw === null || !isText(raw.id)) {
129
150
  return null;
130
151
  }
152
+
131
153
  const id = raw.id;
154
+
132
155
  switch (raw.kind) {
133
156
  case 'table': {
134
157
  const ref = refOf(raw.ref);
135
158
  const view = raw.view === 'structure' ? 'structure' : 'data';
159
+
136
160
  return ref === null || !isOptionalText(raw.where) ? null : { id, kind: 'table', ref, view, where: raw.where };
137
161
  }
138
162
  case 'console':
@@ -153,25 +177,35 @@ export const parseState = (raw: string | null): WorkbenchState => {
153
177
  if (raw === null) {
154
178
  return emptyState;
155
179
  }
180
+
156
181
  let data: Record<string, unknown>;
182
+
157
183
  try {
158
184
  data = JSON.parse(raw) as Record<string, unknown>;
159
185
  } catch {
160
186
  return emptyState;
161
187
  }
188
+
162
189
  if (typeof data !== 'object' || data === null || data.version !== 1 || !Array.isArray(data.tabs)) {
163
190
  return emptyState;
164
191
  }
192
+
165
193
  const seen = new Set<string>();
194
+
166
195
  const tabs = data.tabs.map(tabOf).filter((tab): tab is WorkbenchTab => {
167
196
  if (tab === null || seen.has(tab.id)) {
168
197
  return false;
169
198
  }
199
+
170
200
  seen.add(tab.id);
201
+
171
202
  return true;
172
203
  });
204
+
173
205
  const activeId = typeof data.activeId === 'string' && seen.has(data.activeId) ? data.activeId : (tabs.at(-1)?.id ?? null);
206
+
174
207
  const counter = (value: unknown): number => (typeof value === 'number' && Number.isFinite(value) && value >= 0 ? Math.floor(value) : 0);
208
+
175
209
  return {
176
210
  tabs,
177
211
  activeId,