@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,261 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useId, useMemo, useRef, useState } from 'react';
3
+ import clsx from 'clsx';
4
+ import { Check, CircleAlert, History, Play, Square } from 'lucide-react';
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 } from '../client/types.js';
9
+ import { useDatabaseClient, useDatabaseFiles } from '../client-context.js';
10
+ import { splitStatements, statementAt } from '../sql-split.js';
11
+ import { DestructiveDialog } from './DestructiveDialog.js';
12
+ import { findDestructive } from './destructive.js';
13
+ import { HistoryPanel } from './HistoryPanel.js';
14
+ import { statementLabel } from './labels.js';
15
+ import { isPageable, outcomeOf } from './outcome.js';
16
+ import { SchemaPicker } from './SchemaPicker.js';
17
+ import { orderSchemas } from './schemas.js';
18
+ import { RUN_ALL_SHORTCUT, RUN_SHORTCUT } from './shortcuts.js';
19
+ import { SqlEditor } from './SqlEditor.js';
20
+ import { StatementResultView } from './StatementResultView.js';
21
+ import { TransactionControls } from './TransactionControls.js';
22
+ import { useConsoleHistory } from './useConsoleHistory.js';
23
+ /* Rows in the first page of a result, and in every page after it. */
24
+ const PAGE_SIZE = 500;
25
+ const ranText = (ran, t) => {
26
+ switch (ran.kind) {
27
+ case 'statement':
28
+ return t('console.ranStatement', { statement: ran.label });
29
+ case 'selection':
30
+ return t('console.ranSelection');
31
+ case 'all':
32
+ return t('console.ranAll', { count: ran.count, formatted: formatNumber(ran.count) });
33
+ }
34
+ };
35
+ /* Type SQL, run it, and read what each statement did. Several statements give a tab each. */
36
+ export function QueryConsole({ connection, schema, value, defaultValue = '', onValueChange, defaultHistoryOpen = false, autoFocus = false, className, ref }) {
37
+ const { t } = useTranslation('database');
38
+ const client = useDatabaseClient();
39
+ const files = useDatabaseFiles();
40
+ // A channel of its own, so a transaction a person starts here stays out of the table views and the designer.
41
+ const channel = `console:${useId()}`;
42
+ const session = useMemo(() => client.session(connection, channel), [client, connection, channel]);
43
+ const engine = connection.config.engine;
44
+ useEffect(() => () => {
45
+ void session.close();
46
+ }, [session]);
47
+ const history = useConsoleHistory(connection.id);
48
+ const running = useRef(null);
49
+ const generation = useRef(0);
50
+ const editor = useRef(null);
51
+ const [own, setOwn] = useState(defaultValue);
52
+ const [run, setRun] = useState({ status: 'idle' });
53
+ const [ran, setRan] = useState(null);
54
+ const [tab, setTab] = useState(0);
55
+ const [pages, setPages] = useState({});
56
+ const [paging, setPaging] = useState(false);
57
+ const [panelOpen, setPanelOpen] = useState(false);
58
+ const [historyOpen, setHistoryOpen] = useState(defaultHistoryOpen);
59
+ const [mode, setMode] = useState('auto');
60
+ const [inTransaction, setInTransaction] = useState(false);
61
+ const [settling, setSettling] = useState(false);
62
+ const [endingManual, setEndingManual] = useState(false);
63
+ const [confirming, setConfirming] = useState(null);
64
+ const [schemas, setSchemas] = useState([]);
65
+ const [seededSchema, setSeededSchema] = useState(schema);
66
+ const [pickedSchema, setPickedSchema] = useState(undefined);
67
+ const [exporting, setExporting] = useState(false);
68
+ const [notice, setNotice] = useState(null);
69
+ const sql = value ?? own;
70
+ const blank = sql.trim() === '';
71
+ const busy = run.status === 'running' || settling;
72
+ const activeSchema = pickedSchema ?? schema;
73
+ if (seededSchema !== schema) {
74
+ setSeededSchema(schema);
75
+ setPickedSchema(undefined);
76
+ }
77
+ useEffect(() => () => running.current?.abort(), []);
78
+ useEffect(() => {
79
+ if (autoFocus) {
80
+ editor.current?.focus();
81
+ }
82
+ // Only on mount: a console that later becomes active keeps the focus where the person put it.
83
+ // eslint-disable-next-line react/exhaustive-deps
84
+ }, []);
85
+ useEffect(() => {
86
+ if (engine !== 'mysql') {
87
+ return undefined;
88
+ }
89
+ const controller = new AbortController();
90
+ session
91
+ .schemas({ signal: controller.signal })
92
+ .then((list) => setSchemas(orderSchemas(list)))
93
+ .catch(() => undefined);
94
+ return () => controller.abort();
95
+ }, [engine, session]);
96
+ const changeSql = (next) => {
97
+ if (value === undefined) {
98
+ setOwn(next);
99
+ }
100
+ onValueChange?.(next);
101
+ };
102
+ const targetOf = (scope) => {
103
+ if (scope === 'all') {
104
+ const statements = splitStatements(sql, engine);
105
+ const [only] = statements;
106
+ if (only === undefined) {
107
+ return null;
108
+ }
109
+ return { sql, ran: statements.length > 1 ? { kind: 'all', count: statements.length } : { kind: 'statement', label: statementLabel(only.text) } };
110
+ }
111
+ const start = editor.current?.selectionStart ?? 0;
112
+ const end = editor.current?.selectionEnd ?? 0;
113
+ const selected = sql.slice(start, end);
114
+ if (splitStatements(selected, engine).length > 0) {
115
+ return { sql: selected, ran: { kind: 'selection' } };
116
+ }
117
+ const statement = statementAt(sql, start, engine);
118
+ return statement === null ? null : { sql: statement.text, ran: { kind: 'statement', label: statementLabel(statement.text) } };
119
+ };
120
+ const requestRun = (target) => {
121
+ if (target === null || busy) {
122
+ return;
123
+ }
124
+ const statements = findDestructive(target.sql, engine);
125
+ if (statements.length > 0) {
126
+ setConfirming({ target, statements });
127
+ return;
128
+ }
129
+ void execute(target);
130
+ };
131
+ const execute = async (target) => {
132
+ const controller = new AbortController();
133
+ const mine = ++generation.current;
134
+ running.current = controller;
135
+ setRun({ status: 'running' });
136
+ setRan(target.ran);
137
+ setNotice(null);
138
+ const record = (outcome) => history.record({ sql: target.sql, at: Date.now(), connection: connection.name, ...outcome });
139
+ try {
140
+ if (mode === 'manual' && !inTransaction) {
141
+ setInTransaction(await session.transaction('begin', { signal: controller.signal }));
142
+ }
143
+ const executed = await session.execute(target.sql, { schema: activeSchema, limit: PAGE_SIZE, signal: controller.signal });
144
+ if (controller.signal.aborted) {
145
+ setRun({ status: 'cancelled' });
146
+ return;
147
+ }
148
+ setInTransaction(executed.inTransaction);
149
+ setTab(0);
150
+ setPages({});
151
+ setRun({ status: 'done', results: executed.results });
152
+ record(outcomeOf(executed.results));
153
+ }
154
+ catch (error) {
155
+ if (controller.signal.aborted || (error instanceof DatabaseRequestError && error.code === 'cancelled')) {
156
+ setRun({ status: 'cancelled' });
157
+ }
158
+ else {
159
+ setRun({ status: 'failed', message: messageOf(error) });
160
+ record({ ok: false, rows: null });
161
+ }
162
+ }
163
+ finally {
164
+ if (generation.current === mine) {
165
+ running.current = null;
166
+ }
167
+ }
168
+ };
169
+ const pickHistory = (entry, runIt) => {
170
+ changeSql(entry.sql);
171
+ if (runIt) {
172
+ requestRun({ sql: entry.sql, ran: { kind: 'statement', label: statementLabel(entry.sql) } });
173
+ }
174
+ };
175
+ const settle = async (action) => {
176
+ setSettling(true);
177
+ try {
178
+ setInTransaction(await session.transaction(action));
179
+ return true;
180
+ }
181
+ catch (error) {
182
+ setNotice({ tone: 'error', message: messageOf(error) });
183
+ return false;
184
+ }
185
+ finally {
186
+ setSettling(false);
187
+ }
188
+ };
189
+ const settleAndGoAuto = async (action) => {
190
+ if (await settle(action)) {
191
+ setMode('auto');
192
+ }
193
+ setEndingManual(false);
194
+ };
195
+ const changeMode = (next) => {
196
+ if (next === 'auto' && inTransaction) {
197
+ setEndingManual(true);
198
+ }
199
+ else {
200
+ setMode(next);
201
+ }
202
+ };
203
+ const loadPage = async (index, result, offset) => {
204
+ const mine = generation.current;
205
+ setPaging(true);
206
+ setNotice(null);
207
+ try {
208
+ const rows = await session.page(result.sql, { schema: activeSchema, offset, limit: PAGE_SIZE });
209
+ if (generation.current === mine) {
210
+ setPages((current) => ({ ...current, [index]: { offset, rows } }));
211
+ }
212
+ }
213
+ catch (error) {
214
+ setNotice({ tone: 'error', message: messageOf(error) });
215
+ }
216
+ finally {
217
+ setPaging(false);
218
+ }
219
+ };
220
+ const exportResult = async (result, format) => {
221
+ if (files === undefined) {
222
+ return;
223
+ }
224
+ setExporting(true);
225
+ try {
226
+ const path = await files.save({ suggestedName: `result.${format}`, format });
227
+ if (path !== null) {
228
+ const exported = await session.export({ source: { kind: 'query', sql: result.sql, schema: activeSchema }, format, path });
229
+ setNotice({ tone: 'neutral', message: t('console.exported', { count: exported.rows, formatted: formatNumber(exported.rows) }) });
230
+ }
231
+ }
232
+ catch (error) {
233
+ setNotice({ tone: 'error', message: messageOf(error) });
234
+ }
235
+ finally {
236
+ setExporting(false);
237
+ }
238
+ };
239
+ const results = run.status === 'done' ? run.results : [];
240
+ const index = Math.min(tab, results.length - 1);
241
+ const base = results[index];
242
+ const page = pages[index];
243
+ const shown = base?.kind === 'rows' && page !== undefined ? { kind: 'rows', sql: base.sql, ...page.rows } : base;
244
+ const offset = page?.offset ?? 0;
245
+ const pager = base?.kind === 'rows' && shown?.kind === 'rows' && isPageable(base.sql, engine) && (shown.hasMore || offset > 0)
246
+ ? {
247
+ page: offset / PAGE_SIZE + 1,
248
+ loading: paging,
249
+ onPrevious: () => void loadPage(index, base, Math.max(0, offset - PAGE_SIZE)),
250
+ onNext: () => void loadPage(index, base, offset + PAGE_SIZE)
251
+ }
252
+ : undefined;
253
+ const exportBase = files !== undefined && base?.kind === 'rows' ? base : undefined;
254
+ return (_jsxs("div", { ref: ref, className: clsx('flex min-h-0 flex-col bg-surface text-text', className), children: [_jsxs("div", { className: "flex shrink-0 border-b border-border", children: [_jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-2 p-3", children: [_jsx(SqlEditor, { ref: editor, value: sql, onValueChange: changeSql, onRun: (scope) => requestRun(targetOf(scope)), label: t('console.editor'), placeholder: t('console.placeholder') }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [run.status === 'running' ? (_jsxs(_Fragment, { children: [_jsxs(Button, { size: "sm", onClick: () => running.current?.abort(), children: [_jsx(Icon, { icon: Square, size: 12 }), t('console.cancel')] }), _jsx(Spinner, { size: 14, label: t('console.running') })] })) : (_jsxs(_Fragment, { children: [_jsx(Tooltip, { label: t('console.run'), kbd: RUN_SHORTCUT, children: _jsxs(Button, { variant: "primary", size: "sm", disabled: blank || busy, onClick: () => requestRun(targetOf('selection-or-statement')), children: [_jsx(Icon, { icon: Play, size: 12 }), t('console.run')] }) }), _jsx(Tooltip, { label: t('console.runAll'), kbd: RUN_ALL_SHORTCUT, children: _jsx(Button, { variant: "secondary", size: "sm", disabled: blank || busy, onClick: () => requestRun(targetOf('all')), children: t('console.runAll') }) })] })), _jsx(TransactionControls, { mode: mode, onModeChange: changeMode, open: inTransaction, busy: busy, onCommit: () => void settle('commit'), onRollback: () => void settle('rollback') }), ran !== null && run.status !== 'idle' && _jsx("span", { className: "min-w-0 truncate text-xs text-text-muted", children: ranText(ran, t) }), _jsxs("div", { className: "ml-auto flex items-center gap-2", children: [schemas.length > 0 && (_jsx(SchemaPicker, { schemas: schemas, value: activeSchema ?? null, disabled: busy, onValueChange: setPickedSchema })), _jsxs(Button, { size: "sm", "aria-pressed": historyOpen, className: "aria-pressed:bg-surface-active aria-pressed:text-text", onClick: () => setHistoryOpen(!historyOpen), children: [_jsx(Icon, { icon: History, size: 12 }), t('console.history.title')] })] })] })] }), historyOpen && _jsx(HistoryPanel, { entries: history.entries, onPick: pickHistory, onClear: history.clear })] }), run.status === 'failed' && _jsx(Banner, { icon: CircleAlert, tone: "error", message: run.message, className: "shrink-0 pt-2" }), run.status === 'cancelled' && _jsx(Banner, { icon: CircleAlert, tone: "neutral", message: t('console.cancelled'), className: "shrink-0 pt-2" }), notice !== null && (_jsx(Banner, { icon: notice.tone === 'error' ? CircleAlert : Check, tone: notice.tone, message: notice.message, className: "shrink-0 pt-2", children: _jsx(Button, { size: "xs", onClick: () => setNotice(null), children: t('console.dismiss') }) })), results.length > 1 && (_jsx(Tabs.Root, { value: tab, onValueChange: (next) => setTab(Number(next)), children: _jsx(Tabs.List, { className: "shrink-0 px-3", children: results.map((result, position) => (_jsx(Tabs.Tab, { value: position, children: statementLabel(result.sql) }, position))) }) })), shown !== undefined && (_jsx(StatementResultView, { result: shown, offset: offset, pager: pager, onExport: exportBase === undefined ? undefined : (format) => void exportResult(exportBase, format), exporting: exporting, engine: engine, valuePanelOpen: panelOpen, onValuePanelOpenChange: setPanelOpen }, `${index}:${offset}`)), _jsx(DestructiveDialog, { statements: confirming?.statements ?? [], onCancel: () => setConfirming(null), onConfirm: () => {
255
+ const target = confirming?.target;
256
+ setConfirming(null);
257
+ if (target !== undefined) {
258
+ void execute(target);
259
+ }
260
+ } }), _jsx(PromptDialog, { open: endingManual, title: t('console.transaction.endTitle'), description: t('console.transaction.endDescription'), confirmLabel: t('console.transaction.commit'), secondary: { label: t('console.transaction.rollback'), onClick: () => void settleAndGoAuto('rollback') }, onConfirm: () => settleAndGoAuto('commit'), onOpenChange: () => setEndingManual(false) })] }));
261
+ }
@@ -0,0 +1,11 @@
1
+ import type { Ref } from 'react';
2
+ import type { SchemaInfo } from '../protocol/index.ts';
3
+ export interface SchemaPickerProps {
4
+ schemas: readonly SchemaInfo[];
5
+ value: string | null;
6
+ onValueChange(schema: string): void;
7
+ disabled?: boolean;
8
+ className?: string;
9
+ ref?: Ref<HTMLButtonElement>;
10
+ }
11
+ export declare function SchemaPicker({ schemas, value, onValueChange, disabled, className, ref }: SchemaPickerProps): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Select } from '@adecore/ui';
4
+ /* Picks the schema the console's statements run in. */
5
+ export function SchemaPicker({ schemas, value, onValueChange, disabled, className, ref }) {
6
+ const { t } = useTranslation('database');
7
+ return (_jsx(Select, { ref: ref, size: "sm", label: t('console.schema'), placeholder: t('console.schemaPlaceholder'), value: value, disabled: disabled, className: className, items: schemas.map((schema) => ({ value: schema.name, label: schema.name })), onValueChange: onValueChange }));
8
+ }
@@ -0,0 +1,12 @@
1
+ import { type Ref } from 'react';
2
+ export type RunScope = 'selection-or-statement' | 'all';
3
+ export interface SqlEditorProps {
4
+ value: string;
5
+ onValueChange(value: string): void;
6
+ onRun(scope: RunScope): void;
7
+ label: string;
8
+ placeholder?: string;
9
+ className?: string;
10
+ ref?: Ref<HTMLTextAreaElement>;
11
+ }
12
+ export declare function SqlEditor({ value, onValueChange, onRun, label, placeholder, className, ref }: SqlEditorProps): import("react").JSX.Element;
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from 'react';
3
+ import { isApplePlatform, matchesShortcut, TextArea } from '@adecore/ui';
4
+ import { applyEdit, indentEdit, newlineEdit, outdentEdit } from './editing.js';
5
+ import { RUN_ALL_SHORTCUT, RUN_SHORTCUT } from './shortcuts.js';
6
+ /*
7
+ * Where the SQL is typed. A plain text area for now, kept to itself so an editor with highlighting
8
+ * and completion can take its place without the console noticing: it needs only these props.
9
+ */
10
+ export function SqlEditor({ value, onValueChange, onRun, label, placeholder, className, ref }) {
11
+ const pendingSelection = useRef(null);
12
+ useEffect(() => {
13
+ const pending = pendingSelection.current;
14
+ if (pending !== null) {
15
+ pendingSelection.current = null;
16
+ pending.area.setSelectionRange(pending.start, pending.end);
17
+ }
18
+ }, [value]);
19
+ // Through the browser's own insert, so Cmd+Z undoes the edit; the fallback sets the value itself.
20
+ const apply = (area, edit) => {
21
+ const before = area.value;
22
+ area.setSelectionRange(edit.from, edit.to);
23
+ const inserted = document.execCommand('insertText', false, edit.insert);
24
+ if (inserted && area.value === applyEdit(before, edit)) {
25
+ area.setSelectionRange(edit.selectionStart, edit.selectionEnd);
26
+ return;
27
+ }
28
+ pendingSelection.current = { area, start: edit.selectionStart, end: edit.selectionEnd };
29
+ onValueChange(applyEdit(before, edit));
30
+ };
31
+ const handleKeyDown = (event) => {
32
+ const apple = isApplePlatform();
33
+ if (matchesShortcut(RUN_ALL_SHORTCUT, event, apple)) {
34
+ event.preventDefault();
35
+ onRun('all');
36
+ return;
37
+ }
38
+ if (matchesShortcut(RUN_SHORTCUT, event, apple)) {
39
+ event.preventDefault();
40
+ onRun('selection-or-statement');
41
+ return;
42
+ }
43
+ if (event.nativeEvent.isComposing || event.metaKey || event.ctrlKey || event.altKey) {
44
+ return;
45
+ }
46
+ const area = event.currentTarget;
47
+ const { selectionStart, selectionEnd } = area;
48
+ if (event.key === 'Tab') {
49
+ event.preventDefault();
50
+ const edit = event.shiftKey ? outdentEdit(area.value, selectionStart, selectionEnd) : indentEdit(area.value, selectionStart, selectionEnd);
51
+ if (edit !== null) {
52
+ apply(area, edit);
53
+ }
54
+ }
55
+ else if (event.key === 'Enter' && !event.shiftKey) {
56
+ event.preventDefault();
57
+ apply(area, newlineEdit(area.value, selectionStart, selectionEnd));
58
+ }
59
+ };
60
+ return (_jsx(TextArea, { ref: ref, mono: true, resize: "vertical", "aria-label": label, placeholder: placeholder, spellCheck: false, autoCapitalize: "off", autoCorrect: "off", rows: 8, value: value, className: className, onChange: (event) => onValueChange(event.target.value), onKeyDown: handleKeyDown }));
61
+ }
@@ -0,0 +1,18 @@
1
+ import type { Engine, FileFormat, StatementResult } from '../protocol/index.ts';
2
+ export interface ResultPager {
3
+ readonly page: number;
4
+ readonly loading: boolean;
5
+ onPrevious(): void;
6
+ onNext(): void;
7
+ }
8
+ export interface StatementResultViewProps {
9
+ result: StatementResult;
10
+ offset?: number;
11
+ pager?: ResultPager;
12
+ onExport?(format: FileFormat): void;
13
+ exporting?: boolean;
14
+ engine?: Engine;
15
+ valuePanelOpen?: boolean;
16
+ onValuePanelOpenChange?(open: boolean): void;
17
+ }
18
+ export declare function StatementResultView({ result, offset, pager, onExport, exporting, engine, valuePanelOpen, onValuePanelOpenChange }: StatementResultViewProps): import("react").JSX.Element;
@@ -0,0 +1,32 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState } from 'react';
3
+ import { ChevronLeft, ChevronRight, CircleAlert, Download, PanelRight } from 'lucide-react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Banner, IconButton, Menu, Spinner } from '@adecore/ui';
6
+ import { formatDecimal, formatNumber } from '@adecore/ui/format';
7
+ import { DataGrid } from '../grid/DataGrid.js';
8
+ import { previewValueOf } from '../grid/focused-value.js';
9
+ import { ValueDock } from '../grid/ValueDock.js';
10
+ import { ValuePanel } from '../value/ValuePanel.js';
11
+ /* What "Copy as SQL INSERT" names the table of a result that comes from no table. */
12
+ const RESULT_TABLE = 'result';
13
+ const EXPORT_FORMATS = ['csv', 'tsv', 'json', 'sql'];
14
+ /* One statement's outcome: a result set in a read-only grid, a count of affected rows, or the error the server gave. */
15
+ export function StatementResultView({ result, offset = 0, pager, onExport, exporting = false, engine, valuePanelOpen = false, onValuePanelOpenChange }) {
16
+ const { t } = useTranslation('database');
17
+ const [focus, setFocus] = useState(null);
18
+ const rows = useMemo(() => (result.kind === 'rows' ? result.rows.map((cells, index) => ({ key: `row:${index}`, number: offset + index + 1, cells })) : []), [result, offset]);
19
+ const sqlTarget = useMemo(() => (engine === undefined ? undefined : { engine, table: RESULT_TABLE }), [engine]);
20
+ const elapsed = t('console.elapsed', { value: formatDecimal(result.elapsedMs) });
21
+ if (result.kind === 'error') {
22
+ return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-3 p-3", children: [_jsx(Banner, { icon: CircleAlert, tone: "error", className: "min-w-0", message: _jsxs(_Fragment, { children: [result.error.message, result.error.sqlState !== undefined && (_jsx("span", { className: "ml-2 font-mono text-xs text-text-muted", children: t('console.sqlState', { state: result.error.sqlState }) }))] }) }), _jsx("p", { className: "px-1 text-xs text-text-muted tabular-nums", children: elapsed })] }));
23
+ }
24
+ if (result.kind === 'done') {
25
+ return (_jsxs("div", { className: "flex flex-col gap-1 p-4 text-sm text-text", children: [_jsx("p", { children: t('console.affected', { count: result.affected, formatted: formatNumber(result.affected) }) }), result.lastInsertId !== null && _jsx("p", { className: "text-xs text-text-muted", children: t('console.lastInsertId', { id: String(result.lastInsertId) }) }), _jsx("p", { className: "text-xs text-text-muted tabular-nums", children: elapsed })] }));
26
+ }
27
+ const focusedRow = valuePanelOpen && focus !== null ? rows.find((row) => row.key === focus.rowKey) : undefined;
28
+ const focusedCell = focus === null ? undefined : focusedRow?.cells[focus.column];
29
+ return (_jsxs(_Fragment, { children: [_jsx(ValueDock, { open: valuePanelOpen, panel: _jsx(ValuePanel, { column: focus === null ? null : (result.columns[focus.column] ?? null), value: focusedCell === undefined ? undefined : previewValueOf(focusedCell), loading: false, editable: false, onClose: () => onValuePanelOpenChange?.(false), className: "min-w-0 flex-1" }), children: _jsx(DataGrid, { label: t('console.result'), columns: result.columns, rows: rows, empty: t('console.noRows'), sqlTarget: sqlTarget, onFocusedCellChange: setFocus }) }), _jsxs("footer", { className: "flex h-10 shrink-0 items-center gap-4 border-t border-border px-3 text-xs text-text-muted", children: [_jsx("span", { className: "text-text tabular-nums", children: offset > 0 || pager !== undefined
30
+ ? t('console.rowRange', { from: formatNumber(offset + Math.min(1, rows.length)), to: formatNumber(offset + rows.length) })
31
+ : t('console.rows', { rows: formatNumber(result.rows.length) }) }), pager === undefined && result.hasMore && _jsx("span", { children: t('console.hasMore') }), _jsx("span", { className: "tabular-nums", children: elapsed }), _jsxs("span", { className: "ml-auto flex items-center gap-1", children: [pager !== undefined && (_jsxs(_Fragment, { children: [pager.loading && _jsx(Spinner, { size: 14, label: t('console.loading') }), _jsx(IconButton, { icon: ChevronLeft, size: "sm", label: t('console.previousPage'), disabled: pager.page <= 1 || pager.loading, onClick: pager.onPrevious }), _jsx("span", { className: "min-w-12 text-center tabular-nums", children: t('console.page', { page: formatNumber(pager.page) }) }), _jsx(IconButton, { icon: ChevronRight, size: "sm", label: t('console.nextPage'), disabled: !result.hasMore || pager.loading, onClick: pager.onNext })] })), onExport !== undefined && (_jsxs(Menu.Root, { children: [_jsx(IconButton, { icon: Download, size: "sm", label: t('console.export'), disabled: exporting, render: _jsx(Menu.Trigger, {}) }), _jsx(Menu.Popup, { children: EXPORT_FORMATS.map((format) => (_jsx(Menu.Item, { onClick: () => onExport(format), children: t('console.exportAs', { format: format.toUpperCase() }) }, format))) })] })), onValuePanelOpenChange !== undefined && (_jsx(IconButton, { icon: PanelRight, size: "sm", label: t('console.valuePanel'), "aria-pressed": valuePanelOpen, onClick: () => onValuePanelOpenChange(!valuePanelOpen) }))] })] })] }));
32
+ }
@@ -0,0 +1,13 @@
1
+ import type { Ref } from 'react';
2
+ export type TransactionMode = 'auto' | 'manual';
3
+ export interface TransactionControlsProps {
4
+ mode: TransactionMode;
5
+ onModeChange(mode: TransactionMode): void;
6
+ open: boolean;
7
+ busy: boolean;
8
+ onCommit(): void;
9
+ onRollback(): void;
10
+ className?: string;
11
+ ref?: Ref<HTMLDivElement>;
12
+ }
13
+ export declare function TransactionControls({ mode, onModeChange, open, busy, onCommit, onRollback, className, ref }: TransactionControlsProps): import("react").JSX.Element;
@@ -0,0 +1,12 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import clsx from 'clsx';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { Button, Pill, Segmented, Tooltip } from '@adecore/ui';
5
+ /* Auto or manual commits, and while a transaction is open, the way to end it. */
6
+ export function TransactionControls({ mode, onModeChange, open, busy, onCommit, onRollback, className, ref }) {
7
+ const { t } = useTranslation('database');
8
+ return (_jsxs("div", { ref: ref, className: clsx('flex items-center gap-2', className), children: [_jsx(Tooltip, { label: t('console.transaction.hint'), children: _jsx("div", { className: "flex", children: _jsx(Segmented, { value: mode, onValueChange: onModeChange, disabled: busy, label: t('console.transaction.mode'), options: [
9
+ { id: 'auto', label: t('console.transaction.auto') },
10
+ { id: 'manual', label: t('console.transaction.manual') }
11
+ ] }) }) }), open && (_jsxs(_Fragment, { children: [_jsx(Pill, { tone: "needsYou", children: t('console.transaction.open') }), _jsx(Button, { size: "xs", variant: "secondary", disabled: busy, onClick: onCommit, children: t('console.transaction.commit') }), _jsx(Button, { size: "xs", variant: "danger-outline", disabled: busy, onClick: onRollback, children: t('console.transaction.rollback') })] }))] }));
12
+ }
@@ -0,0 +1,7 @@
1
+ import type { Engine } from '../protocol/index.ts';
2
+ export type DestructiveKind = 'drop' | 'truncate' | 'delete' | 'update' | 'alterDrop';
3
+ export interface DestructiveStatement {
4
+ readonly sql: string;
5
+ readonly kind: DestructiveKind;
6
+ }
7
+ export declare const findDestructive: (sql: string, engine: Engine) => DestructiveStatement[];
@@ -0,0 +1,43 @@
1
+ import { splitStatements, tokenize } from '../sql-split.js';
2
+ const wordsOf = (sql, engine) => {
3
+ const words = [];
4
+ let depth = 0;
5
+ for (const token of tokenize(sql, engine)) {
6
+ const text = sql.slice(token.start, token.end);
7
+ if (token.kind === 'word') {
8
+ words.push({ word: text.toUpperCase(), depth });
9
+ }
10
+ else if (token.kind === 'punct' && text === '(') {
11
+ depth += 1;
12
+ }
13
+ else if (token.kind === 'punct' && text === ')') {
14
+ depth = Math.max(0, depth - 1);
15
+ }
16
+ }
17
+ return words;
18
+ };
19
+ const kindOf = (sql, engine) => {
20
+ const words = wordsOf(sql, engine);
21
+ const first = words[0]?.word;
22
+ if (first === 'DROP') {
23
+ return 'drop';
24
+ }
25
+ if (first === 'TRUNCATE') {
26
+ return 'truncate';
27
+ }
28
+ if (first === 'ALTER') {
29
+ return words.some(({ word }) => word === 'DROP') ? 'alterDrop' : null;
30
+ }
31
+ // A statement behind a `WITH` is the first DELETE or UPDATE outside any parentheses.
32
+ const writer = first === 'WITH' ? words.find(({ word, depth }) => depth === 0 && (word === 'DELETE' || word === 'UPDATE')) : words[0];
33
+ if (writer?.word !== 'DELETE' && writer?.word !== 'UPDATE') {
34
+ return null;
35
+ }
36
+ const filtered = words.some(({ word, depth }) => word === 'WHERE' && depth === 0);
37
+ return filtered ? null : writer.word === 'DELETE' ? 'delete' : 'update';
38
+ };
39
+ /* The statements of a script that drop, truncate, or delete or update without a `WHERE`. */
40
+ export const findDestructive = (sql, engine) => splitStatements(sql, engine).flatMap((statement) => {
41
+ const kind = kindOf(statement.text, engine);
42
+ return kind === null ? [] : [{ sql: statement.text, kind }];
43
+ });
@@ -0,0 +1,12 @@
1
+ export interface TextEdit {
2
+ readonly from: number;
3
+ readonly to: number;
4
+ readonly insert: string;
5
+ readonly selectionStart: number;
6
+ readonly selectionEnd: number;
7
+ }
8
+ export declare const INDENT = " ";
9
+ export declare const applyEdit: (value: string, edit: TextEdit) => string;
10
+ export declare const indentEdit: (value: string, start: number, end: number) => TextEdit;
11
+ export declare const outdentEdit: (value: string, start: number, end: number) => TextEdit | null;
12
+ export declare const newlineEdit: (value: string, start: number, end: number) => TextEdit;
@@ -0,0 +1,60 @@
1
+ export const INDENT = ' ';
2
+ export const applyEdit = (value, edit) => value.slice(0, edit.from) + edit.insert + value.slice(edit.to);
3
+ const lineStartOf = (value, at) => value.lastIndexOf('\n', at - 1) + 1;
4
+ const lineEndOf = (value, at) => {
5
+ const newline = value.indexOf('\n', at);
6
+ return newline === -1 ? value.length : newline;
7
+ };
8
+ /* The whitespace a line begins with. */
9
+ const indentOf = (line) => /^[ \t]*/.exec(line)[0];
10
+ /* The lines a selection touches. A selection that ends at the start of a line leaves that line out. */
11
+ const touchedLines = (value, start, end) => {
12
+ const from = lineStartOf(value, start);
13
+ const last = end > start && value[end - 1] === '\n' ? end - 1 : end;
14
+ return { from, to: lineEndOf(value, last) };
15
+ };
16
+ /* Tab: four spaces at the caret, or one indent level on every touched line when the selection spans lines. */
17
+ export const indentEdit = (value, start, end) => {
18
+ if (!value.slice(start, end).includes('\n')) {
19
+ return { from: start, to: end, insert: INDENT, selectionStart: start + INDENT.length, selectionEnd: start + INDENT.length };
20
+ }
21
+ const { from, to } = touchedLines(value, start, end);
22
+ const lines = value.slice(from, to).split('\n');
23
+ return {
24
+ from,
25
+ to,
26
+ insert: lines.map((line) => (line === '' ? line : INDENT + line)).join('\n'),
27
+ selectionStart: start + (lines[0] === '' ? 0 : INDENT.length),
28
+ selectionEnd: end + lines.filter((line) => line !== '').length * INDENT.length
29
+ };
30
+ };
31
+ /* Shift+Tab: takes one level of indentation off every touched line, four spaces or one tab. */
32
+ export const outdentEdit = (value, start, end) => {
33
+ const { from, to } = touchedLines(value, start, end);
34
+ const lines = value.slice(from, to).split('\n');
35
+ const removed = lines.map((line) => {
36
+ if (line.startsWith('\t')) {
37
+ return 1;
38
+ }
39
+ return Math.min(INDENT.length, /^ */.exec(line)[0].length);
40
+ });
41
+ const total = removed.reduce((sum, count) => sum + count, 0);
42
+ if (total === 0) {
43
+ return null;
44
+ }
45
+ const firstLineStart = from;
46
+ return {
47
+ from,
48
+ to,
49
+ insert: lines.map((line, index) => line.slice(removed[index])).join('\n'),
50
+ selectionStart: Math.max(firstLineStart, start - removed[0]),
51
+ selectionEnd: Math.max(firstLineStart, end - total)
52
+ };
53
+ };
54
+ /* Enter: a new line that starts with the indentation of the line the caret is on, up to the caret. */
55
+ export const newlineEdit = (value, start, end) => {
56
+ const lineStart = lineStartOf(value, start);
57
+ const indent = indentOf(value.slice(lineStart, start));
58
+ const insert = `\n${indent}`;
59
+ return { from: start, to: end, insert, selectionStart: start + insert.length, selectionEnd: start + insert.length };
60
+ };
@@ -0,0 +1,13 @@
1
+ export interface HistoryEntry {
2
+ readonly sql: string;
3
+ readonly at: number;
4
+ readonly connection: string;
5
+ readonly ok: boolean;
6
+ readonly rows: number | null;
7
+ }
8
+ export declare const HISTORY_LIMIT = 200;
9
+ export declare const historyKey: (connectionId: string) => string;
10
+ export declare const parseHistory: (raw: string | null) => HistoryEntry[];
11
+ export declare const serializeHistory: (entries: readonly HistoryEntry[]) => string;
12
+ export declare const addEntry: (entries: readonly HistoryEntry[], entry: HistoryEntry) => HistoryEntry[];
13
+ export declare const searchHistory: (entries: readonly HistoryEntry[], query: string) => readonly HistoryEntry[];
@@ -0,0 +1,43 @@
1
+ export const HISTORY_LIMIT = 200;
2
+ export const historyKey = (connectionId) => `database:console-history:${connectionId}`;
3
+ const isEntry = (value) => {
4
+ if (value === null || typeof value !== 'object') {
5
+ return false;
6
+ }
7
+ const entry = value;
8
+ return (typeof entry.sql === 'string' &&
9
+ typeof entry.at === 'number' &&
10
+ typeof entry.connection === 'string' &&
11
+ typeof entry.ok === 'boolean' &&
12
+ (entry.rows === null || typeof entry.rows === 'number'));
13
+ };
14
+ /* Reads what `serializeHistory` wrote. Anything that does not fit gives an empty list, or leaves out the entries that do not fit. */
15
+ export const parseHistory = (raw) => {
16
+ if (raw === null) {
17
+ return [];
18
+ }
19
+ try {
20
+ const parsed = JSON.parse(raw);
21
+ return Array.isArray(parsed) ? parsed.filter(isEntry).slice(0, HISTORY_LIMIT) : [];
22
+ }
23
+ catch {
24
+ return [];
25
+ }
26
+ };
27
+ export const serializeHistory = (entries) => JSON.stringify(entries);
28
+ /* The list with the run in front. A run of the same SQL as the latest one replaces it. */
29
+ export const addEntry = (entries, entry) => {
30
+ const rest = entries[0]?.sql.trim() === entry.sql.trim() ? entries.slice(1) : entries;
31
+ return [entry, ...rest].slice(0, HISTORY_LIMIT);
32
+ };
33
+ /* The entries whose SQL holds every word of the query, in any case. */
34
+ export const searchHistory = (entries, query) => {
35
+ const words = query.toLowerCase().split(/\s+/).filter(Boolean);
36
+ if (words.length === 0) {
37
+ return entries;
38
+ }
39
+ return entries.filter((entry) => {
40
+ const sql = entry.sql.toLowerCase();
41
+ return words.every((word) => sql.includes(word));
42
+ });
43
+ };
@@ -0,0 +1 @@
1
+ export declare const statementLabel: (sql: string) => string;
@@ -0,0 +1,6 @@
1
+ const LABEL_LENGTH = 32;
2
+ /* The tab of a statement: its SQL on one line, cut where it gets long. */
3
+ export const statementLabel = (sql) => {
4
+ const line = sql.replace(/\s+/g, ' ').trim();
5
+ return line.length > LABEL_LENGTH ? `${line.slice(0, LABEL_LENGTH - 1).trimEnd()}…` : line;
6
+ };