@adecore/database 0.0.1 → 0.16.0-beta.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 (400) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +152 -1
  3. package/dist/DatabaseProvider.d.ts +13 -0
  4. package/dist/DatabaseProvider.js +17 -0
  5. package/dist/actions.d.ts +40 -0
  6. package/dist/actions.js +1 -0
  7. package/dist/client/client.d.ts +5 -0
  8. package/dist/client/client.js +245 -0
  9. package/dist/client/index.d.ts +2 -0
  10. package/dist/client/index.js +2 -0
  11. package/dist/client/types.d.ts +97 -0
  12. package/dist/client/types.js +13 -0
  13. package/dist/client-context.d.ts +16 -0
  14. package/dist/client-context.js +17 -0
  15. package/dist/connections/ConnectionForm.d.ts +10 -0
  16. package/dist/connections/ConnectionForm.js +145 -0
  17. package/dist/connections/ConnectionManager.d.ts +12 -0
  18. package/dist/connections/ConnectionManager.js +93 -0
  19. package/dist/connections/EngineIcon.d.ts +9 -0
  20. package/dist/connections/EngineIcon.js +7 -0
  21. package/dist/connections/connection-config.d.ts +16 -0
  22. package/dist/connections/connection-config.js +107 -0
  23. package/dist/connections/docker.d.ts +9 -0
  24. package/dist/connections/docker.js +38 -0
  25. package/dist/connections/engine-icons.d.ts +3 -0
  26. package/dist/connections/engine-icons.js +3 -0
  27. package/dist/connections/use-docker-containers.d.ts +17 -0
  28. package/dist/connections/use-docker-containers.js +26 -0
  29. package/dist/console/DestructiveDialog.d.ts +9 -0
  30. package/dist/console/DestructiveDialog.js +9 -0
  31. package/dist/console/HistoryPanel.d.ts +10 -0
  32. package/dist/console/HistoryPanel.js +15 -0
  33. package/dist/console/QueryConsole.d.ts +14 -0
  34. package/dist/console/QueryConsole.js +261 -0
  35. package/dist/console/SchemaPicker.d.ts +11 -0
  36. package/dist/console/SchemaPicker.js +8 -0
  37. package/dist/console/SqlEditor.d.ts +12 -0
  38. package/dist/console/SqlEditor.js +61 -0
  39. package/dist/console/StatementResultView.d.ts +18 -0
  40. package/dist/console/StatementResultView.js +32 -0
  41. package/dist/console/TransactionControls.d.ts +13 -0
  42. package/dist/console/TransactionControls.js +12 -0
  43. package/dist/console/destructive.d.ts +7 -0
  44. package/dist/console/destructive.js +43 -0
  45. package/dist/console/editing.d.ts +12 -0
  46. package/dist/console/editing.js +60 -0
  47. package/dist/console/history.d.ts +13 -0
  48. package/dist/console/history.js +43 -0
  49. package/dist/console/labels.d.ts +1 -0
  50. package/dist/console/labels.js +6 -0
  51. package/dist/console/outcome.d.ts +6 -0
  52. package/dist/console/outcome.js +11 -0
  53. package/dist/console/schemas.d.ts +2 -0
  54. package/dist/console/schemas.js +5 -0
  55. package/dist/console/shortcuts.d.ts +2 -0
  56. package/dist/console/shortcuts.js +3 -0
  57. package/dist/console/useConsoleHistory.d.ts +7 -0
  58. package/dist/console/useConsoleHistory.js +22 -0
  59. package/dist/ddl/alter-mysql.d.ts +4 -0
  60. package/dist/ddl/alter-mysql.js +79 -0
  61. package/dist/ddl/alter-sqlite.d.ts +4 -0
  62. package/dist/ddl/alter-sqlite.js +80 -0
  63. package/dist/ddl/alter.d.ts +4 -0
  64. package/dist/ddl/alter.js +24 -0
  65. package/dist/ddl/column.d.ts +9 -0
  66. package/dist/ddl/column.js +62 -0
  67. package/dist/ddl/create.d.ts +12 -0
  68. package/dist/ddl/create.js +74 -0
  69. package/dist/ddl/dialect.d.ts +12 -0
  70. package/dist/ddl/dialect.js +34 -0
  71. package/dist/ddl/diff.d.ts +34 -0
  72. package/dist/ddl/diff.js +111 -0
  73. package/dist/ddl/draft.d.ts +46 -0
  74. package/dist/ddl/draft.js +70 -0
  75. package/dist/ddl/index.d.ts +10 -0
  76. package/dist/ddl/index.js +10 -0
  77. package/dist/ddl/parse.d.ts +17 -0
  78. package/dist/ddl/parse.js +131 -0
  79. package/dist/ddl/statements.d.ts +6 -0
  80. package/dist/ddl/statements.js +12 -0
  81. package/dist/ddl/types.d.ts +6 -0
  82. package/dist/ddl/types.js +52 -0
  83. package/dist/ddl/validate.d.ts +7 -0
  84. package/dist/ddl/validate.js +38 -0
  85. package/dist/designer/ColumnsEditor.d.ts +2 -0
  86. package/dist/designer/ColumnsEditor.js +24 -0
  87. package/dist/designer/DesignerView.d.ts +25 -0
  88. package/dist/designer/DesignerView.js +33 -0
  89. package/dist/designer/ForeignKeysEditor.d.ts +7 -0
  90. package/dist/designer/ForeignKeysEditor.js +24 -0
  91. package/dist/designer/IndexesEditor.d.ts +2 -0
  92. package/dist/designer/IndexesEditor.js +13 -0
  93. package/dist/designer/NamePicker.d.ts +11 -0
  94. package/dist/designer/NamePicker.js +13 -0
  95. package/dist/designer/OptionsEditor.d.ts +2 -0
  96. package/dist/designer/OptionsEditor.js +17 -0
  97. package/dist/designer/SqlPreview.d.ts +12 -0
  98. package/dist/designer/SqlPreview.js +17 -0
  99. package/dist/designer/TableDesigner.d.ts +10 -0
  100. package/dist/designer/TableDesigner.js +103 -0
  101. package/dist/designer/edit.d.ts +17 -0
  102. package/dist/designer/edit.js +115 -0
  103. package/dist/designer/editor-props.d.ts +10 -0
  104. package/dist/designer/editor-props.js +1 -0
  105. package/dist/designer/plan.d.ts +9 -0
  106. package/dist/designer/plan.js +13 -0
  107. package/dist/designer/recover.d.ts +2 -0
  108. package/dist/designer/recover.js +18 -0
  109. package/dist/designer/useReferenceColumns.d.ts +3 -0
  110. package/dist/designer/useReferenceColumns.js +36 -0
  111. package/dist/explorer/DatabaseExplorer.d.ts +13 -0
  112. package/dist/explorer/DatabaseExplorer.js +264 -0
  113. package/dist/explorer/RowMenu.d.ts +11 -0
  114. package/dist/explorer/RowMenu.js +89 -0
  115. package/dist/explorer/TableDialog.d.ts +15 -0
  116. package/dist/explorer/TableDialog.js +50 -0
  117. package/dist/explorer/expansion.d.ts +7 -0
  118. package/dist/explorer/expansion.js +31 -0
  119. package/dist/explorer/table-statements.d.ts +5 -0
  120. package/dist/explorer/table-statements.js +12 -0
  121. package/dist/explorer/tree.d.ts +125 -0
  122. package/dist/explorer/tree.js +295 -0
  123. package/dist/explorer/use-explorer-loads.d.ts +12 -0
  124. package/dist/explorer/use-explorer-loads.js +109 -0
  125. package/dist/explorer/use-stored-expansion.d.ts +9 -0
  126. package/dist/explorer/use-stored-expansion.js +47 -0
  127. package/dist/grid/CellEditor.d.ts +9 -0
  128. package/dist/grid/CellEditor.js +29 -0
  129. package/dist/grid/CopyAsMenu.d.ts +7 -0
  130. package/dist/grid/CopyAsMenu.js +10 -0
  131. package/dist/grid/DataGrid.d.ts +36 -0
  132. package/dist/grid/DataGrid.js +512 -0
  133. package/dist/grid/EnumPicker.d.ts +17 -0
  134. package/dist/grid/EnumPicker.js +53 -0
  135. package/dist/grid/GridHeaderCell.d.ts +36 -0
  136. package/dist/grid/GridHeaderCell.js +69 -0
  137. package/dist/grid/RecordView.d.ts +20 -0
  138. package/dist/grid/RecordView.js +126 -0
  139. package/dist/grid/ValueDock.d.ts +11 -0
  140. package/dist/grid/ValueDock.js +28 -0
  141. package/dist/grid/aggregates.d.ts +22 -0
  142. package/dist/grid/aggregates.js +62 -0
  143. package/dist/grid/cell-range.d.ts +14 -0
  144. package/dist/grid/cell-range.js +11 -0
  145. package/dist/grid/column-layout.d.ts +18 -0
  146. package/dist/grid/column-layout.js +49 -0
  147. package/dist/grid/column-selection.d.ts +11 -0
  148. package/dist/grid/column-selection.js +28 -0
  149. package/dist/grid/column-view.d.ts +10 -0
  150. package/dist/grid/column-view.js +23 -0
  151. package/dist/grid/copy-formats.d.ts +15 -0
  152. package/dist/grid/copy-formats.js +80 -0
  153. package/dist/grid/display.d.ts +20 -0
  154. package/dist/grid/display.js +81 -0
  155. package/dist/grid/edit-value.d.ts +5 -0
  156. package/dist/grid/edit-value.js +55 -0
  157. package/dist/grid/enum-type.d.ts +7 -0
  158. package/dist/grid/enum-type.js +52 -0
  159. package/dist/grid/focused-value.d.ts +4 -0
  160. package/dist/grid/focused-value.js +19 -0
  161. package/dist/grid/layout.d.ts +22 -0
  162. package/dist/grid/layout.js +74 -0
  163. package/dist/grid/navigation.d.ts +15 -0
  164. package/dist/grid/navigation.js +52 -0
  165. package/dist/grid/row-selection.d.ts +9 -0
  166. package/dist/grid/row-selection.js +24 -0
  167. package/dist/grid/sort.d.ts +7 -0
  168. package/dist/grid/sort.js +7 -0
  169. package/dist/grid/types.d.ts +30 -0
  170. package/dist/grid/types.js +9 -0
  171. package/dist/host/helper-path.d.ts +7 -0
  172. package/dist/host/helper-path.js +15 -0
  173. package/dist/host/host.d.ts +2 -0
  174. package/dist/host/host.js +343 -0
  175. package/dist/host/index.d.ts +5 -0
  176. package/dist/host/index.js +4 -0
  177. package/dist/host/spawn.d.ts +7 -0
  178. package/dist/host/spawn.js +98 -0
  179. package/dist/host/types.d.ts +19 -0
  180. package/dist/host/types.js +1 -0
  181. package/dist/host/validate.d.ts +10 -0
  182. package/dist/host/validate.js +339 -0
  183. package/dist/index.d.ts +14 -0
  184. package/dist/index.js +13 -0
  185. package/dist/locales/en.json +577 -0
  186. package/dist/locales/nl.json +577 -0
  187. package/dist/locales.d.ts +4 -0
  188. package/dist/locales.js +14 -0
  189. package/dist/protocol/connection.d.ts +56 -0
  190. package/dist/protocol/connection.js +1 -0
  191. package/dist/protocol/errors.d.ts +7 -0
  192. package/dist/protocol/errors.js +1 -0
  193. package/dist/protocol/index.d.ts +5 -0
  194. package/dist/protocol/index.js +3 -0
  195. package/dist/protocol/messages.d.ts +250 -0
  196. package/dist/protocol/messages.js +2 -0
  197. package/dist/protocol/schema.d.ts +53 -0
  198. package/dist/protocol/schema.js +1 -0
  199. package/dist/protocol/values.d.ts +21 -0
  200. package/dist/protocol/values.js +15 -0
  201. package/dist/sql-split.d.ts +15 -0
  202. package/dist/sql-split.js +201 -0
  203. package/dist/sql.d.ts +29 -0
  204. package/dist/sql.js +104 -0
  205. package/dist/structure/StructureView.d.ts +14 -0
  206. package/dist/structure/StructureView.js +99 -0
  207. package/dist/structure/structure-text.d.ts +2 -0
  208. package/dist/structure/structure-text.js +2 -0
  209. package/dist/table/CommandField.d.ts +14 -0
  210. package/dist/table/CommandField.js +157 -0
  211. package/dist/table/ImportDialog.d.ts +27 -0
  212. package/dist/table/ImportDialog.js +28 -0
  213. package/dist/table/TableStatusBar.d.ts +19 -0
  214. package/dist/table/TableStatusBar.js +22 -0
  215. package/dist/table/TableToolbar.d.ts +43 -0
  216. package/dist/table/TableToolbar.js +16 -0
  217. package/dist/table/TableView.d.ts +13 -0
  218. package/dist/table/TableView.js +508 -0
  219. package/dist/table/command-field.d.ts +57 -0
  220. package/dist/table/command-field.js +203 -0
  221. package/dist/table/grid-rows.d.ts +23 -0
  222. package/dist/table/grid-rows.js +55 -0
  223. package/dist/table/import-mapping.d.ts +7 -0
  224. package/dist/table/import-mapping.js +28 -0
  225. package/dist/table/layout-store.d.ts +13 -0
  226. package/dist/table/layout-store.js +43 -0
  227. package/dist/table/paging.d.ts +12 -0
  228. package/dist/table/paging.js +20 -0
  229. package/dist/table/pending.d.ts +32 -0
  230. package/dist/table/pending.js +94 -0
  231. package/dist/table/references.d.ts +12 -0
  232. package/dist/table/references.js +21 -0
  233. package/dist/table/useLoaded.d.ts +7 -0
  234. package/dist/table/useLoaded.js +36 -0
  235. package/dist/table/useStoredLayout.d.ts +8 -0
  236. package/dist/table/useStoredLayout.js +39 -0
  237. package/dist/table/useTableTransfer.d.ts +51 -0
  238. package/dist/table/useTableTransfer.js +125 -0
  239. package/dist/testing/fake.d.ts +21 -0
  240. package/dist/testing/fake.js +372 -0
  241. package/dist/testing/index.d.ts +1 -0
  242. package/dist/testing/index.js +1 -0
  243. package/dist/use-popup-press.d.ts +1 -0
  244. package/dist/use-popup-press.js +24 -0
  245. package/dist/use-schema-change.d.ts +1 -0
  246. package/dist/use-schema-change.js +13 -0
  247. package/dist/use-stable-callback.d.ts +1 -0
  248. package/dist/use-stable-callback.js +9 -0
  249. package/dist/value/ValuePanel.d.ts +14 -0
  250. package/dist/value/ValuePanel.js +103 -0
  251. package/dist/value/draft.d.ts +33 -0
  252. package/dist/value/draft.js +44 -0
  253. package/dist/value/hex.d.ts +14 -0
  254. package/dist/value/hex.js +46 -0
  255. package/dist/value/json.d.ts +9 -0
  256. package/dist/value/json.js +71 -0
  257. package/dist/value/views.d.ts +22 -0
  258. package/dist/value/views.js +54 -0
  259. package/dist/workbench/DatabaseWorkbench.d.ts +9 -0
  260. package/dist/workbench/DatabaseWorkbench.js +179 -0
  261. package/dist/workbench/DesignerTab.d.ts +10 -0
  262. package/dist/workbench/DesignerTab.js +22 -0
  263. package/dist/workbench/TableTab.d.ts +10 -0
  264. package/dist/workbench/TableTab.js +8 -0
  265. package/dist/workbench/tabs.d.ts +42 -0
  266. package/dist/workbench/tabs.js +139 -0
  267. package/package.json +79 -3
  268. package/src/DatabaseProvider.tsx +37 -0
  269. package/src/actions.ts +34 -0
  270. package/src/client/client.ts +324 -0
  271. package/src/client/index.ts +18 -0
  272. package/src/client/types.ts +161 -0
  273. package/src/client-context.ts +35 -0
  274. package/src/connections/ConnectionForm.tsx +474 -0
  275. package/src/connections/ConnectionManager.tsx +224 -0
  276. package/src/connections/EngineIcon.tsx +18 -0
  277. package/src/connections/connection-config.ts +143 -0
  278. package/src/connections/docker.ts +50 -0
  279. package/src/connections/engine-icons.ts +5 -0
  280. package/src/connections/use-docker-containers.ts +41 -0
  281. package/src/console/DestructiveDialog.tsx +40 -0
  282. package/src/console/HistoryPanel.tsx +67 -0
  283. package/src/console/QueryConsole.tsx +439 -0
  284. package/src/console/SchemaPicker.tsx +32 -0
  285. package/src/console/SqlEditor.tsx +95 -0
  286. package/src/console/StatementResultView.tsx +176 -0
  287. package/src/console/TransactionControls.tsx +53 -0
  288. package/src/console/destructive.ts +60 -0
  289. package/src/console/editing.ts +77 -0
  290. package/src/console/history.ts +62 -0
  291. package/src/console/labels.ts +7 -0
  292. package/src/console/outcome.ts +14 -0
  293. package/src/console/schemas.ts +7 -0
  294. package/src/console/shortcuts.ts +4 -0
  295. package/src/console/useConsoleHistory.ts +39 -0
  296. package/src/ddl/alter-mysql.ts +90 -0
  297. package/src/ddl/alter-sqlite.ts +93 -0
  298. package/src/ddl/alter.ts +29 -0
  299. package/src/ddl/column.ts +74 -0
  300. package/src/ddl/create.ts +92 -0
  301. package/src/ddl/dialect.ts +53 -0
  302. package/src/ddl/diff.ts +164 -0
  303. package/src/ddl/draft.ts +136 -0
  304. package/src/ddl/index.ts +10 -0
  305. package/src/ddl/parse.ts +161 -0
  306. package/src/ddl/statements.ts +20 -0
  307. package/src/ddl/types.ts +60 -0
  308. package/src/ddl/validate.ts +47 -0
  309. package/src/designer/ColumnsEditor.tsx +204 -0
  310. package/src/designer/DesignerView.tsx +184 -0
  311. package/src/designer/ForeignKeysEditor.tsx +132 -0
  312. package/src/designer/IndexesEditor.tsx +63 -0
  313. package/src/designer/NamePicker.tsx +44 -0
  314. package/src/designer/OptionsEditor.tsx +101 -0
  315. package/src/designer/SqlPreview.tsx +56 -0
  316. package/src/designer/TableDesigner.tsx +186 -0
  317. package/src/designer/edit.ts +137 -0
  318. package/src/designer/editor-props.ts +13 -0
  319. package/src/designer/plan.ts +25 -0
  320. package/src/designer/recover.ts +20 -0
  321. package/src/designer/useReferenceColumns.ts +54 -0
  322. package/src/explorer/DatabaseExplorer.tsx +450 -0
  323. package/src/explorer/RowMenu.tsx +134 -0
  324. package/src/explorer/TableDialog.tsx +80 -0
  325. package/src/explorer/expansion.ts +47 -0
  326. package/src/explorer/table-statements.ts +18 -0
  327. package/src/explorer/tree.ts +453 -0
  328. package/src/explorer/use-explorer-loads.ts +157 -0
  329. package/src/explorer/use-stored-expansion.ts +66 -0
  330. package/src/grid/CellEditor.tsx +53 -0
  331. package/src/grid/CopyAsMenu.tsx +33 -0
  332. package/src/grid/DataGrid.tsx +871 -0
  333. package/src/grid/EnumPicker.tsx +108 -0
  334. package/src/grid/GridHeaderCell.tsx +243 -0
  335. package/src/grid/RecordView.tsx +293 -0
  336. package/src/grid/ValueDock.tsx +52 -0
  337. package/src/grid/aggregates.ts +94 -0
  338. package/src/grid/cell-range.ts +26 -0
  339. package/src/grid/column-layout.ts +71 -0
  340. package/src/grid/column-selection.ts +42 -0
  341. package/src/grid/column-view.ts +35 -0
  342. package/src/grid/copy-formats.ts +108 -0
  343. package/src/grid/display.ts +109 -0
  344. package/src/grid/edit-value.ts +60 -0
  345. package/src/grid/enum-type.ts +62 -0
  346. package/src/grid/focused-value.ts +23 -0
  347. package/src/grid/layout.ts +91 -0
  348. package/src/grid/navigation.ts +74 -0
  349. package/src/grid/row-selection.ts +35 -0
  350. package/src/grid/sort.ts +12 -0
  351. package/src/grid/types.ts +59 -0
  352. package/src/host/helper-path.ts +41 -0
  353. package/src/host/host.ts +456 -0
  354. package/src/host/index.ts +5 -0
  355. package/src/host/spawn.ts +124 -0
  356. package/src/host/types.ts +46 -0
  357. package/src/host/validate.ts +402 -0
  358. package/src/index.ts +37 -0
  359. package/src/locales/en.json +577 -0
  360. package/src/locales/nl.json +577 -0
  361. package/src/locales.ts +18 -0
  362. package/src/protocol/connection.ts +86 -0
  363. package/src/protocol/errors.ts +38 -0
  364. package/src/protocol/index.ts +33 -0
  365. package/src/protocol/messages.ts +168 -0
  366. package/src/protocol/schema.ts +76 -0
  367. package/src/protocol/values.ts +52 -0
  368. package/src/sql-split.ts +220 -0
  369. package/src/sql.ts +152 -0
  370. package/src/structure/StructureView.tsx +269 -0
  371. package/src/structure/structure-text.ts +4 -0
  372. package/src/table/CommandField.tsx +318 -0
  373. package/src/table/ImportDialog.tsx +168 -0
  374. package/src/table/TableStatusBar.tsx +91 -0
  375. package/src/table/TableToolbar.tsx +217 -0
  376. package/src/table/TableView.tsx +880 -0
  377. package/src/table/command-field.ts +285 -0
  378. package/src/table/grid-rows.ts +80 -0
  379. package/src/table/import-mapping.ts +37 -0
  380. package/src/table/layout-store.ts +62 -0
  381. package/src/table/paging.ts +33 -0
  382. package/src/table/pending.ts +143 -0
  383. package/src/table/references.ts +38 -0
  384. package/src/table/useLoaded.ts +58 -0
  385. package/src/table/useStoredLayout.ts +53 -0
  386. package/src/table/useTableTransfer.ts +187 -0
  387. package/src/testing/fake.ts +533 -0
  388. package/src/testing/index.ts +1 -0
  389. package/src/use-popup-press.ts +27 -0
  390. package/src/use-schema-change.ts +19 -0
  391. package/src/use-stable-callback.ts +12 -0
  392. package/src/value/ValuePanel.tsx +254 -0
  393. package/src/value/draft.ts +72 -0
  394. package/src/value/hex.ts +62 -0
  395. package/src/value/json.ts +74 -0
  396. package/src/value/views.ts +68 -0
  397. package/src/workbench/DatabaseWorkbench.tsx +348 -0
  398. package/src/workbench/DesignerTab.tsx +37 -0
  399. package/src/workbench/TableTab.tsx +27 -0
  400. package/src/workbench/tabs.ts +184 -0
@@ -0,0 +1,439 @@
1
+ import { useEffect, useId, useMemo, useRef, useState, type Ref } from 'react';
2
+ import clsx from 'clsx';
3
+ import { Check, CircleAlert, History, Play, Square } from 'lucide-react';
4
+ import type { TFunction } from 'i18next';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Banner, Button, Icon, messageOf, PromptDialog, Spinner, Tabs, Tooltip } from '@adecore/ui';
7
+ import { formatNumber } from '@adecore/ui/format';
8
+ import { DatabaseRequestError, type Connection } from '../client/types.ts';
9
+ import { useDatabaseClient, useDatabaseFiles } from '../client-context.ts';
10
+ import type { FileFormat, RowsResult, SchemaInfo, StatementResult } from '../protocol/index.ts';
11
+ import { splitStatements, statementAt } from '../sql-split.ts';
12
+ import { DestructiveDialog } from './DestructiveDialog.tsx';
13
+ import { findDestructive, type DestructiveStatement } from './destructive.ts';
14
+ import type { HistoryEntry } from './history.ts';
15
+ import { HistoryPanel } from './HistoryPanel.tsx';
16
+ import { statementLabel } from './labels.ts';
17
+ import { isPageable, outcomeOf } from './outcome.ts';
18
+ import { SchemaPicker } from './SchemaPicker.tsx';
19
+ import { orderSchemas } from './schemas.ts';
20
+ import { RUN_ALL_SHORTCUT, RUN_SHORTCUT } from './shortcuts.ts';
21
+ import { SqlEditor, type RunScope } from './SqlEditor.tsx';
22
+ import { StatementResultView, type ResultPager } from './StatementResultView.tsx';
23
+ import { TransactionControls, type TransactionMode } from './TransactionControls.tsx';
24
+ import { useConsoleHistory } from './useConsoleHistory.ts';
25
+
26
+ export interface QueryConsoleProps {
27
+ connection: Connection;
28
+ /* The schema the statements run in. */
29
+ schema?: string;
30
+ /* The SQL, when the app keeps it (a tab that survives a reload). Without it the console keeps the text itself, starting from `defaultValue`. */
31
+ value?: string;
32
+ defaultValue?: string;
33
+ onValueChange?(sql: string): void;
34
+ /* Opens the history list beside the editor from the start. */
35
+ defaultHistoryOpen?: boolean;
36
+ /* Puts the caret in the editor when the console mounts, for one a person just opened. */
37
+ autoFocus?: boolean;
38
+ className?: string;
39
+ ref?: Ref<HTMLDivElement>;
40
+ }
41
+
42
+ type Run =
43
+ | { readonly status: 'idle' }
44
+ | { readonly status: 'running' }
45
+ | { readonly status: 'done'; readonly results: readonly StatementResult[] }
46
+ | { readonly status: 'failed'; readonly message: string }
47
+ | { readonly status: 'cancelled' };
48
+
49
+ /* What a run is about to execute, and how the toolbar names it afterwards. */
50
+ interface RunTarget {
51
+ readonly sql: string;
52
+ readonly ran: Ran;
53
+ }
54
+
55
+ type Ran = { readonly kind: 'statement'; readonly label: string } | { readonly kind: 'selection' } | { readonly kind: 'all'; readonly count: number };
56
+
57
+ interface PageState {
58
+ readonly offset: number;
59
+ readonly rows: RowsResult;
60
+ }
61
+
62
+ interface Notice {
63
+ readonly tone: 'error' | 'neutral';
64
+ readonly message: string;
65
+ }
66
+
67
+ /* Rows in the first page of a result, and in every page after it. */
68
+ const PAGE_SIZE = 500;
69
+
70
+ const ranText = (ran: Ran, t: TFunction<'database'>): string => {
71
+ switch (ran.kind) {
72
+ case 'statement':
73
+ return t('console.ranStatement', { statement: ran.label });
74
+ case 'selection':
75
+ return t('console.ranSelection');
76
+ case 'all':
77
+ return t('console.ranAll', { count: ran.count, formatted: formatNumber(ran.count) });
78
+ }
79
+ };
80
+
81
+ /* Type SQL, run it, and read what each statement did. Several statements give a tab each. */
82
+ export function QueryConsole({
83
+ connection,
84
+ schema,
85
+ value,
86
+ defaultValue = '',
87
+ onValueChange,
88
+ defaultHistoryOpen = false,
89
+ autoFocus = false,
90
+ className,
91
+ ref
92
+ }: QueryConsoleProps) {
93
+ const { t } = useTranslation('database');
94
+ const client = useDatabaseClient();
95
+ const files = useDatabaseFiles();
96
+ // A channel of its own, so a transaction a person starts here stays out of the table views and the designer.
97
+ const channel = `console:${useId()}`;
98
+ const session = useMemo(() => client.session(connection, channel), [client, connection, channel]);
99
+ const engine = connection.config.engine;
100
+ useEffect(
101
+ () => () => {
102
+ void session.close();
103
+ },
104
+ [session]
105
+ );
106
+ const history = useConsoleHistory(connection.id);
107
+ const running = useRef<AbortController | null>(null);
108
+ const generation = useRef(0);
109
+ const editor = useRef<HTMLTextAreaElement>(null);
110
+ const [own, setOwn] = useState(defaultValue);
111
+ const [run, setRun] = useState<Run>({ status: 'idle' });
112
+ const [ran, setRan] = useState<Ran | null>(null);
113
+ const [tab, setTab] = useState(0);
114
+ const [pages, setPages] = useState<Readonly<Record<number, PageState>>>({});
115
+ const [paging, setPaging] = useState(false);
116
+ const [panelOpen, setPanelOpen] = useState(false);
117
+ const [historyOpen, setHistoryOpen] = useState(defaultHistoryOpen);
118
+ const [mode, setMode] = useState<TransactionMode>('auto');
119
+ const [inTransaction, setInTransaction] = useState(false);
120
+ const [settling, setSettling] = useState(false);
121
+ const [endingManual, setEndingManual] = useState(false);
122
+ const [confirming, setConfirming] = useState<{ readonly target: RunTarget; readonly statements: readonly DestructiveStatement[] } | null>(null);
123
+ const [schemas, setSchemas] = useState<readonly SchemaInfo[]>([]);
124
+ const [seededSchema, setSeededSchema] = useState(schema);
125
+ const [pickedSchema, setPickedSchema] = useState<string | undefined>(undefined);
126
+ const [exporting, setExporting] = useState(false);
127
+ const [notice, setNotice] = useState<Notice | null>(null);
128
+ const sql = value ?? own;
129
+ const blank = sql.trim() === '';
130
+ const busy = run.status === 'running' || settling;
131
+ const activeSchema = pickedSchema ?? schema;
132
+
133
+ if (seededSchema !== schema) {
134
+ setSeededSchema(schema);
135
+ setPickedSchema(undefined);
136
+ }
137
+
138
+ useEffect(() => () => running.current?.abort(), []);
139
+
140
+ useEffect(() => {
141
+ if (autoFocus) {
142
+ editor.current?.focus();
143
+ }
144
+ // Only on mount: a console that later becomes active keeps the focus where the person put it.
145
+ // eslint-disable-next-line react/exhaustive-deps
146
+ }, []);
147
+
148
+ useEffect(() => {
149
+ if (engine !== 'mysql') {
150
+ return undefined;
151
+ }
152
+ const controller = new AbortController();
153
+ session
154
+ .schemas({ signal: controller.signal })
155
+ .then((list) => setSchemas(orderSchemas(list)))
156
+ .catch(() => undefined);
157
+ return () => controller.abort();
158
+ }, [engine, session]);
159
+
160
+ const changeSql = (next: string): void => {
161
+ if (value === undefined) {
162
+ setOwn(next);
163
+ }
164
+ onValueChange?.(next);
165
+ };
166
+
167
+ const targetOf = (scope: RunScope): RunTarget | null => {
168
+ if (scope === 'all') {
169
+ const statements = splitStatements(sql, engine);
170
+ const [only] = statements;
171
+ if (only === undefined) {
172
+ return null;
173
+ }
174
+ return { sql, ran: statements.length > 1 ? { kind: 'all', count: statements.length } : { kind: 'statement', label: statementLabel(only.text) } };
175
+ }
176
+ const start = editor.current?.selectionStart ?? 0;
177
+ const end = editor.current?.selectionEnd ?? 0;
178
+ const selected = sql.slice(start, end);
179
+ if (splitStatements(selected, engine).length > 0) {
180
+ return { sql: selected, ran: { kind: 'selection' } };
181
+ }
182
+ const statement = statementAt(sql, start, engine);
183
+ return statement === null ? null : { sql: statement.text, ran: { kind: 'statement', label: statementLabel(statement.text) } };
184
+ };
185
+
186
+ const requestRun = (target: RunTarget | null): void => {
187
+ if (target === null || busy) {
188
+ return;
189
+ }
190
+ const statements = findDestructive(target.sql, engine);
191
+ if (statements.length > 0) {
192
+ setConfirming({ target, statements });
193
+ return;
194
+ }
195
+ void execute(target);
196
+ };
197
+
198
+ const execute = async (target: RunTarget): Promise<void> => {
199
+ const controller = new AbortController();
200
+ const mine = ++generation.current;
201
+ running.current = controller;
202
+ setRun({ status: 'running' });
203
+ setRan(target.ran);
204
+ setNotice(null);
205
+ const record = (outcome: Pick<HistoryEntry, 'ok' | 'rows'>): void =>
206
+ history.record({ sql: target.sql, at: Date.now(), connection: connection.name, ...outcome });
207
+ try {
208
+ if (mode === 'manual' && !inTransaction) {
209
+ setInTransaction(await session.transaction('begin', { signal: controller.signal }));
210
+ }
211
+ const executed = await session.execute(target.sql, { schema: activeSchema, limit: PAGE_SIZE, signal: controller.signal });
212
+ if (controller.signal.aborted) {
213
+ setRun({ status: 'cancelled' });
214
+ return;
215
+ }
216
+ setInTransaction(executed.inTransaction);
217
+ setTab(0);
218
+ setPages({});
219
+ setRun({ status: 'done', results: executed.results });
220
+ record(outcomeOf(executed.results));
221
+ } catch (error) {
222
+ if (controller.signal.aborted || (error instanceof DatabaseRequestError && error.code === 'cancelled')) {
223
+ setRun({ status: 'cancelled' });
224
+ } else {
225
+ setRun({ status: 'failed', message: messageOf(error) });
226
+ record({ ok: false, rows: null });
227
+ }
228
+ } finally {
229
+ if (generation.current === mine) {
230
+ running.current = null;
231
+ }
232
+ }
233
+ };
234
+
235
+ const pickHistory = (entry: HistoryEntry, runIt: boolean): void => {
236
+ changeSql(entry.sql);
237
+ if (runIt) {
238
+ requestRun({ sql: entry.sql, ran: { kind: 'statement', label: statementLabel(entry.sql) } });
239
+ }
240
+ };
241
+
242
+ const settle = async (action: 'commit' | 'rollback'): Promise<boolean> => {
243
+ setSettling(true);
244
+ try {
245
+ setInTransaction(await session.transaction(action));
246
+ return true;
247
+ } catch (error) {
248
+ setNotice({ tone: 'error', message: messageOf(error) });
249
+ return false;
250
+ } finally {
251
+ setSettling(false);
252
+ }
253
+ };
254
+
255
+ const settleAndGoAuto = async (action: 'commit' | 'rollback'): Promise<void> => {
256
+ if (await settle(action)) {
257
+ setMode('auto');
258
+ }
259
+ setEndingManual(false);
260
+ };
261
+
262
+ const changeMode = (next: TransactionMode): void => {
263
+ if (next === 'auto' && inTransaction) {
264
+ setEndingManual(true);
265
+ } else {
266
+ setMode(next);
267
+ }
268
+ };
269
+
270
+ const loadPage = async (index: number, result: StatementResult, offset: number): Promise<void> => {
271
+ const mine = generation.current;
272
+ setPaging(true);
273
+ setNotice(null);
274
+ try {
275
+ const rows = await session.page(result.sql, { schema: activeSchema, offset, limit: PAGE_SIZE });
276
+ if (generation.current === mine) {
277
+ setPages((current) => ({ ...current, [index]: { offset, rows } }));
278
+ }
279
+ } catch (error) {
280
+ setNotice({ tone: 'error', message: messageOf(error) });
281
+ } finally {
282
+ setPaging(false);
283
+ }
284
+ };
285
+
286
+ const exportResult = async (result: StatementResult, format: FileFormat): Promise<void> => {
287
+ if (files === undefined) {
288
+ return;
289
+ }
290
+ setExporting(true);
291
+ try {
292
+ const path = await files.save({ suggestedName: `result.${format}`, format });
293
+ if (path !== null) {
294
+ const exported = await session.export({ source: { kind: 'query', sql: result.sql, schema: activeSchema }, format, path });
295
+ setNotice({ tone: 'neutral', message: t('console.exported', { count: exported.rows, formatted: formatNumber(exported.rows) }) });
296
+ }
297
+ } catch (error) {
298
+ setNotice({ tone: 'error', message: messageOf(error) });
299
+ } finally {
300
+ setExporting(false);
301
+ }
302
+ };
303
+
304
+ const results = run.status === 'done' ? run.results : [];
305
+ const index = Math.min(tab, results.length - 1);
306
+ const base = results[index];
307
+ const page = pages[index];
308
+ const shown: StatementResult | undefined = base?.kind === 'rows' && page !== undefined ? { kind: 'rows', sql: base.sql, ...page.rows } : base;
309
+ const offset = page?.offset ?? 0;
310
+ const pager: ResultPager | undefined =
311
+ base?.kind === 'rows' && shown?.kind === 'rows' && isPageable(base.sql, engine) && (shown.hasMore || offset > 0)
312
+ ? {
313
+ page: offset / PAGE_SIZE + 1,
314
+ loading: paging,
315
+ onPrevious: () => void loadPage(index, base, Math.max(0, offset - PAGE_SIZE)),
316
+ onNext: () => void loadPage(index, base, offset + PAGE_SIZE)
317
+ }
318
+ : undefined;
319
+ const exportBase = files !== undefined && base?.kind === 'rows' ? base : undefined;
320
+
321
+ return (
322
+ <div ref={ref} className={clsx('flex min-h-0 flex-col bg-surface text-text', className)}>
323
+ <div className="flex shrink-0 border-b border-border">
324
+ <div className="flex min-w-0 flex-1 flex-col gap-2 p-3">
325
+ <SqlEditor
326
+ ref={editor}
327
+ value={sql}
328
+ onValueChange={changeSql}
329
+ onRun={(scope) => requestRun(targetOf(scope))}
330
+ label={t('console.editor')}
331
+ placeholder={t('console.placeholder')}
332
+ />
333
+ <div className="flex flex-wrap items-center gap-2">
334
+ {run.status === 'running' ? (
335
+ <>
336
+ <Button size="sm" onClick={() => running.current?.abort()}>
337
+ <Icon icon={Square} size={12} />
338
+ {t('console.cancel')}
339
+ </Button>
340
+ <Spinner size={14} label={t('console.running')} />
341
+ </>
342
+ ) : (
343
+ <>
344
+ <Tooltip label={t('console.run')} kbd={RUN_SHORTCUT}>
345
+ <Button variant="primary" size="sm" disabled={blank || busy} onClick={() => requestRun(targetOf('selection-or-statement'))}>
346
+ <Icon icon={Play} size={12} />
347
+ {t('console.run')}
348
+ </Button>
349
+ </Tooltip>
350
+ <Tooltip label={t('console.runAll')} kbd={RUN_ALL_SHORTCUT}>
351
+ <Button variant="secondary" size="sm" disabled={blank || busy} onClick={() => requestRun(targetOf('all'))}>
352
+ {t('console.runAll')}
353
+ </Button>
354
+ </Tooltip>
355
+ </>
356
+ )}
357
+ <TransactionControls
358
+ mode={mode}
359
+ onModeChange={changeMode}
360
+ open={inTransaction}
361
+ busy={busy}
362
+ onCommit={() => void settle('commit')}
363
+ onRollback={() => void settle('rollback')}
364
+ />
365
+ {ran !== null && run.status !== 'idle' && <span className="min-w-0 truncate text-xs text-text-muted">{ranText(ran, t)}</span>}
366
+ <div className="ml-auto flex items-center gap-2">
367
+ {schemas.length > 0 && (
368
+ <SchemaPicker schemas={schemas} value={activeSchema ?? null} disabled={busy} onValueChange={setPickedSchema} />
369
+ )}
370
+ <Button
371
+ size="sm"
372
+ aria-pressed={historyOpen}
373
+ className="aria-pressed:bg-surface-active aria-pressed:text-text"
374
+ onClick={() => setHistoryOpen(!historyOpen)}
375
+ >
376
+ <Icon icon={History} size={12} />
377
+ {t('console.history.title')}
378
+ </Button>
379
+ </div>
380
+ </div>
381
+ </div>
382
+ {historyOpen && <HistoryPanel entries={history.entries} onPick={pickHistory} onClear={history.clear} />}
383
+ </div>
384
+ {run.status === 'failed' && <Banner icon={CircleAlert} tone="error" message={run.message} className="shrink-0 pt-2" />}
385
+ {run.status === 'cancelled' && <Banner icon={CircleAlert} tone="neutral" message={t('console.cancelled')} className="shrink-0 pt-2" />}
386
+ {notice !== null && (
387
+ <Banner icon={notice.tone === 'error' ? CircleAlert : Check} tone={notice.tone} message={notice.message} className="shrink-0 pt-2">
388
+ <Button size="xs" onClick={() => setNotice(null)}>
389
+ {t('console.dismiss')}
390
+ </Button>
391
+ </Banner>
392
+ )}
393
+ {results.length > 1 && (
394
+ <Tabs.Root value={tab} onValueChange={(next) => setTab(Number(next))}>
395
+ <Tabs.List className="shrink-0 px-3">
396
+ {results.map((result, position) => (
397
+ <Tabs.Tab key={position} value={position}>
398
+ {statementLabel(result.sql)}
399
+ </Tabs.Tab>
400
+ ))}
401
+ </Tabs.List>
402
+ </Tabs.Root>
403
+ )}
404
+ {shown !== undefined && (
405
+ <StatementResultView
406
+ key={`${index}:${offset}`}
407
+ result={shown}
408
+ offset={offset}
409
+ pager={pager}
410
+ onExport={exportBase === undefined ? undefined : (format) => void exportResult(exportBase, format)}
411
+ exporting={exporting}
412
+ engine={engine}
413
+ valuePanelOpen={panelOpen}
414
+ onValuePanelOpenChange={setPanelOpen}
415
+ />
416
+ )}
417
+ <DestructiveDialog
418
+ statements={confirming?.statements ?? []}
419
+ onCancel={() => setConfirming(null)}
420
+ onConfirm={() => {
421
+ const target = confirming?.target;
422
+ setConfirming(null);
423
+ if (target !== undefined) {
424
+ void execute(target);
425
+ }
426
+ }}
427
+ />
428
+ <PromptDialog
429
+ open={endingManual}
430
+ title={t('console.transaction.endTitle')}
431
+ description={t('console.transaction.endDescription')}
432
+ confirmLabel={t('console.transaction.commit')}
433
+ secondary={{ label: t('console.transaction.rollback'), onClick: () => void settleAndGoAuto('rollback') }}
434
+ onConfirm={() => settleAndGoAuto('commit')}
435
+ onOpenChange={() => setEndingManual(false)}
436
+ />
437
+ </div>
438
+ );
439
+ }
@@ -0,0 +1,32 @@
1
+ import type { Ref } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Select } from '@adecore/ui';
4
+ import type { SchemaInfo } from '../protocol/index.ts';
5
+
6
+ export interface SchemaPickerProps {
7
+ schemas: readonly SchemaInfo[];
8
+ /* `null` while the statements run in whatever the session has selected. */
9
+ value: string | null;
10
+ onValueChange(schema: string): void;
11
+ disabled?: boolean;
12
+ className?: string;
13
+ ref?: Ref<HTMLButtonElement>;
14
+ }
15
+
16
+ /* Picks the schema the console's statements run in. */
17
+ export function SchemaPicker({ schemas, value, onValueChange, disabled, className, ref }: SchemaPickerProps) {
18
+ const { t } = useTranslation('database');
19
+ return (
20
+ <Select
21
+ ref={ref}
22
+ size="sm"
23
+ label={t('console.schema')}
24
+ placeholder={t('console.schemaPlaceholder')}
25
+ value={value}
26
+ disabled={disabled}
27
+ className={className}
28
+ items={schemas.map((schema) => ({ value: schema.name, label: schema.name }))}
29
+ onValueChange={onValueChange}
30
+ />
31
+ );
32
+ }
@@ -0,0 +1,95 @@
1
+ import { useEffect, useRef, type KeyboardEvent, type Ref } from 'react';
2
+ import { isApplePlatform, matchesShortcut, TextArea } from '@adecore/ui';
3
+ import { applyEdit, indentEdit, newlineEdit, outdentEdit, type TextEdit } from './editing.ts';
4
+ import { RUN_ALL_SHORTCUT, RUN_SHORTCUT } from './shortcuts.ts';
5
+
6
+ /* What the person asked for: the selection or the statement under the caret, or the whole script. */
7
+ export type RunScope = 'selection-or-statement' | 'all';
8
+
9
+ export interface SqlEditorProps {
10
+ value: string;
11
+ onValueChange(value: string): void;
12
+ /* Mod+Enter and Mod+Shift+Enter. */
13
+ onRun(scope: RunScope): void;
14
+ label: string;
15
+ placeholder?: string;
16
+ className?: string;
17
+ /* The text area, which holds the selection and the caret the console reads when it runs. */
18
+ ref?: Ref<HTMLTextAreaElement>;
19
+ }
20
+
21
+ /*
22
+ * Where the SQL is typed. A plain text area for now, kept to itself so an editor with highlighting
23
+ * and completion can take its place without the console noticing: it needs only these props.
24
+ */
25
+ export function SqlEditor({ value, onValueChange, onRun, label, placeholder, className, ref }: SqlEditorProps) {
26
+ const pendingSelection = useRef<{ area: HTMLTextAreaElement; start: number; end: number } | null>(null);
27
+
28
+ useEffect(() => {
29
+ const pending = pendingSelection.current;
30
+ if (pending !== null) {
31
+ pendingSelection.current = null;
32
+ pending.area.setSelectionRange(pending.start, pending.end);
33
+ }
34
+ }, [value]);
35
+
36
+ // Through the browser's own insert, so Cmd+Z undoes the edit; the fallback sets the value itself.
37
+ const apply = (area: HTMLTextAreaElement, edit: TextEdit): void => {
38
+ const before = area.value;
39
+ area.setSelectionRange(edit.from, edit.to);
40
+ const inserted = document.execCommand('insertText', false, edit.insert);
41
+ if (inserted && area.value === applyEdit(before, edit)) {
42
+ area.setSelectionRange(edit.selectionStart, edit.selectionEnd);
43
+ return;
44
+ }
45
+ pendingSelection.current = { area, start: edit.selectionStart, end: edit.selectionEnd };
46
+ onValueChange(applyEdit(before, edit));
47
+ };
48
+
49
+ const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>): void => {
50
+ const apple = isApplePlatform();
51
+ if (matchesShortcut(RUN_ALL_SHORTCUT, event, apple)) {
52
+ event.preventDefault();
53
+ onRun('all');
54
+ return;
55
+ }
56
+ if (matchesShortcut(RUN_SHORTCUT, event, apple)) {
57
+ event.preventDefault();
58
+ onRun('selection-or-statement');
59
+ return;
60
+ }
61
+ if (event.nativeEvent.isComposing || event.metaKey || event.ctrlKey || event.altKey) {
62
+ return;
63
+ }
64
+ const area = event.currentTarget;
65
+ const { selectionStart, selectionEnd } = area;
66
+ if (event.key === 'Tab') {
67
+ event.preventDefault();
68
+ const edit = event.shiftKey ? outdentEdit(area.value, selectionStart, selectionEnd) : indentEdit(area.value, selectionStart, selectionEnd);
69
+ if (edit !== null) {
70
+ apply(area, edit);
71
+ }
72
+ } else if (event.key === 'Enter' && !event.shiftKey) {
73
+ event.preventDefault();
74
+ apply(area, newlineEdit(area.value, selectionStart, selectionEnd));
75
+ }
76
+ };
77
+
78
+ return (
79
+ <TextArea
80
+ ref={ref}
81
+ mono
82
+ resize="vertical"
83
+ aria-label={label}
84
+ placeholder={placeholder}
85
+ spellCheck={false}
86
+ autoCapitalize="off"
87
+ autoCorrect="off"
88
+ rows={8}
89
+ value={value}
90
+ className={className}
91
+ onChange={(event) => onValueChange(event.target.value)}
92
+ onKeyDown={handleKeyDown}
93
+ />
94
+ );
95
+ }