@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,533 @@
1
+ import type {
2
+ Cell,
3
+ ColumnInfo,
4
+ DatabaseErrorCode,
5
+ DatabaseMethod,
6
+ DatabaseParams,
7
+ DatabaseRequest,
8
+ DatabaseResponse,
9
+ DatabaseResult,
10
+ DockerContainer,
11
+ ForeignKeyInfo,
12
+ IndexInfo,
13
+ ResultColumn,
14
+ RowKey,
15
+ RowsResult,
16
+ ServerInfo,
17
+ StatementResult,
18
+ TableKind,
19
+ Value
20
+ } from '../protocol/index.ts';
21
+ import type { DatabaseTransport } from '../client/types.ts';
22
+
23
+ export interface FakeTable {
24
+ /* `table` when left out. A view cannot be edited. */
25
+ readonly kind?: TableKind;
26
+ readonly columns: readonly ColumnInfo[];
27
+ /* Also the row key. A table without one is read only. */
28
+ readonly primaryKey?: readonly string[];
29
+ /* One row per entry, its values in the order of `columns`. */
30
+ readonly rows: readonly (readonly Value[])[];
31
+ readonly indexes?: readonly IndexInfo[];
32
+ readonly foreignKeys?: readonly ForeignKeyInfo[];
33
+ readonly ddl?: string;
34
+ }
35
+
36
+ export interface FakeDatabase {
37
+ /* Schema name to table name to table. SQLite uses `main`. */
38
+ readonly schemas: Readonly<Record<string, Readonly<Record<string, FakeTable>>>>;
39
+ }
40
+
41
+ export interface FakeDatabaseTransportOptions {
42
+ /* Keyed by the `path` of a SQLite connection or the `host` of a MySQL one. The transport works on a copy, so the data passed in never changes. */
43
+ readonly databases: Readonly<Record<string, FakeDatabase>>;
44
+ /* Delays every answer. A `cancel` for a request that is still waiting answers it with `cancelled`. */
45
+ readonly latencyMs?: number;
46
+ /* What every `open` and `test` reports. Derived from the engine when left out. */
47
+ readonly server?: ServerInfo;
48
+ /* What `discover` lists. Nothing when left out. */
49
+ readonly containers?: readonly DockerContainer[];
50
+ }
51
+
52
+ interface Table {
53
+ kind: TableKind;
54
+ columns: ColumnInfo[];
55
+ primaryKey: string[];
56
+ rows: Value[][];
57
+ indexes: IndexInfo[];
58
+ foreignKeys: ForeignKeyInfo[];
59
+ ddl: string | null;
60
+ }
61
+
62
+ interface Session {
63
+ readonly schemas: Map<string, Map<string, Table>>;
64
+ readonly readOnly: boolean;
65
+ schema: string | null;
66
+ inTransaction: boolean;
67
+ }
68
+
69
+ class FakeError extends Error {
70
+ readonly code: DatabaseErrorCode;
71
+ readonly sqlState: string | undefined;
72
+ readonly change: number | undefined;
73
+
74
+ constructor(code: DatabaseErrorCode, message: string, sqlState?: string, change?: number) {
75
+ super(message);
76
+ this.code = code;
77
+ this.sqlState = sqlState;
78
+ this.change = change;
79
+ }
80
+ }
81
+
82
+ const SYSTEM_SCHEMAS = ['information_schema', 'mysql', 'performance_schema', 'sys'];
83
+ const DEFAULT_ROWS_CELL_LIMIT = 1024;
84
+ const DEFAULT_EXECUTE_LIMIT = 500;
85
+ const DEFAULT_EXECUTE_CELL_LIMIT = 65536;
86
+
87
+ const toTables = (database: FakeDatabase): Map<string, Map<string, Table>> =>
88
+ new Map(
89
+ Object.entries(database.schemas).map(([schema, tables]) => [
90
+ schema,
91
+ new Map(
92
+ Object.entries(tables).map(([name, table]) => [
93
+ name,
94
+ {
95
+ kind: table.kind ?? 'table',
96
+ columns: structuredClone([...table.columns]),
97
+ primaryKey: [...(table.primaryKey ?? [])],
98
+ rows: structuredClone(table.rows.map((row) => [...row])),
99
+ indexes: structuredClone([...(table.indexes ?? [])]),
100
+ foreignKeys: structuredClone([...(table.foreignKeys ?? [])]),
101
+ ddl: table.ddl ?? null
102
+ } satisfies Table
103
+ ])
104
+ )
105
+ ])
106
+ );
107
+
108
+ const findTable = (session: Session, schema: string, name: string): Table => {
109
+ const tables = session.schemas.get(schema);
110
+
111
+ if (tables === undefined) {
112
+ throw new FakeError('query-failed', `Unknown database '${schema}'`, '42000');
113
+ }
114
+
115
+ const table = tables.get(name);
116
+
117
+ if (table === undefined) {
118
+ throw new FakeError('query-failed', `Table '${schema}.${name}' doesn't exist`, '42S02');
119
+ }
120
+
121
+ return table;
122
+ };
123
+
124
+ /* The columns that pick out one row: the primary key, or else the first unique index over columns that cannot be null. */
125
+ const rowKeyOf = (table: Table): string[] | null => {
126
+ if (table.primaryKey.length > 0) {
127
+ return table.primaryKey;
128
+ }
129
+
130
+ const unique = table.indexes.find(
131
+ (index) => index.unique && index.columns.every((name) => table.columns.find((column) => column.name === name)?.nullable === false)
132
+ );
133
+ return unique === undefined ? null : [...unique.columns];
134
+ };
135
+
136
+ const columnIndex = (table: Table, name: string): number => {
137
+ const index = table.columns.findIndex((column) => column.name === name);
138
+
139
+ if (index === -1) {
140
+ throw new FakeError('query-failed', `Unknown column '${name}' in 'field list'`, '42S22');
141
+ }
142
+
143
+ return index;
144
+ };
145
+
146
+ const isSameValue = (left: Value, right: Value): boolean => {
147
+ if (left !== null && right !== null && typeof left === 'object' && typeof right === 'object') {
148
+ return left.hex === right.hex;
149
+ }
150
+
151
+ return left === right;
152
+ };
153
+
154
+ const matches = (table: Table, row: readonly Value[], key: RowKey): boolean =>
155
+ Object.entries(key).every(([name, value]) => isSameValue(row[columnIndex(table, name)]!, value));
156
+
157
+ const toCell = (value: Value, cellLimit: number): Cell => {
158
+ if (typeof value === 'string' && value.length > cellLimit) {
159
+ return { kind: 'longText', preview: value.slice(0, cellLimit), length: value.length };
160
+ }
161
+
162
+ if (value !== null && typeof value === 'object') {
163
+ const length = value.hex.length / 2;
164
+ return { kind: 'binary', hex: value.hex.slice(0, Math.min(length, cellLimit) * 2), length };
165
+ }
166
+
167
+ return value;
168
+ };
169
+
170
+ const toResultColumns = (table: Table): ResultColumn[] => table.columns.map(({ name, type, kind }) => ({ name, type, kind }));
171
+
172
+ const toRowsResult = (table: Table, rows: readonly (readonly Value[])[], offset: number, limit: number, cellLimit: number): RowsResult => ({
173
+ columns: toResultColumns(table),
174
+ rows: rows.slice(offset, offset + limit).map((row) => row.map((value) => toCell(value, cellLimit))),
175
+ hasMore: rows.length > offset + limit,
176
+ elapsedMs: 0
177
+ });
178
+
179
+ /* Evaluates the default of a column for a row an insert or an update leaves it out of. */
180
+ const defaultOf = (column: ColumnInfo, table: Table): Value => {
181
+ if (column.autoIncrement) {
182
+ const index = table.columns.indexOf(column);
183
+ return table.rows.reduce((highest, row) => (typeof row[index] === 'number' ? Math.max(highest, row[index]) : highest), 0) + 1;
184
+ }
185
+
186
+ const expression = column.defaultValue?.trim() ?? null;
187
+
188
+ if (expression === null) {
189
+ if (column.nullable || column.generated) {
190
+ return null;
191
+ }
192
+
193
+ throw new FakeError('query-failed', `Field '${column.name}' doesn't have a default value`, 'HY000');
194
+ }
195
+
196
+ if (/^null$/i.test(expression)) {
197
+ return null;
198
+ }
199
+
200
+ if (/^current_timestamp(?:\(\d*\))?$/i.test(expression)) {
201
+ return new Date().toISOString().slice(0, 19).replace('T', ' ');
202
+ }
203
+
204
+ if (/^-?\d+(?:\.\d+)?$/.test(expression)) {
205
+ return Number(expression);
206
+ }
207
+
208
+ if (/^'.*'$/s.test(expression)) {
209
+ return expression.slice(1, -1).replace(/''/g, "'");
210
+ }
211
+
212
+ return expression;
213
+ };
214
+
215
+ const resolveEdit = (column: ColumnInfo, table: Table, value: Value | { readonly kind: 'default' }): Value => {
216
+ const resolved = value !== null && typeof value === 'object' && value.kind === 'default' ? defaultOf(column, table) : (value as Value);
217
+
218
+ if (resolved === null && !column.nullable && !column.autoIncrement) {
219
+ throw new FakeError('query-failed', `Column '${column.name}' cannot be null`, '23000');
220
+ }
221
+
222
+ return resolved;
223
+ };
224
+
225
+ /* Applies the changes to a copy of the rows, so a failed change leaves the table as it was. */
226
+ const applyChanges = (table: Table, changes: DatabaseParams<'apply'>['changes']): number => {
227
+ if (table.kind === 'view') {
228
+ throw new FakeError('unsupported', 'A view cannot be edited.');
229
+ }
230
+
231
+ const key = rowKeyOf(table);
232
+ const draft = { ...table, rows: table.rows.map((row) => [...row]) };
233
+
234
+ changes.forEach((change, position) => {
235
+ if (change.kind === 'insert') {
236
+ for (const name of Object.keys(change.values)) {
237
+ columnIndex(table, name);
238
+ }
239
+
240
+ const row = table.columns.map((column) => {
241
+ const given = Object.hasOwn(change.values, column.name) ? change.values[column.name]! : { kind: 'default' as const };
242
+ return resolveEdit(column, draft, given);
243
+ });
244
+
245
+ const primary = table.primaryKey.map((name) => row[columnIndex(table, name)]!);
246
+
247
+ if (
248
+ primary.length > 0 &&
249
+ draft.rows.some((existing) => table.primaryKey.every((name, i) => isSameValue(existing[columnIndex(table, name)]!, primary[i]!)))
250
+ ) {
251
+ throw new FakeError('query-failed', `Duplicate entry for key 'PRIMARY'`, '23000');
252
+ }
253
+
254
+ draft.rows.push(row);
255
+ return;
256
+ }
257
+
258
+ if (key === null) {
259
+ throw new FakeError('no-row-key', 'The table has no primary key or unique key to pick a row with.');
260
+ }
261
+
262
+ const found = draft.rows.filter((row) => matches(table, row, change.key));
263
+
264
+ if (found.length !== 1) {
265
+ throw new FakeError('conflict', `The ${change.kind} matched ${found.length === 0 ? 'no row' : 'more than one row'}.`, undefined, position);
266
+ }
267
+
268
+ const row = found[0]!;
269
+
270
+ if (change.kind === 'delete') {
271
+ draft.rows.splice(draft.rows.indexOf(row), 1);
272
+ return;
273
+ }
274
+
275
+ for (const [name, value] of Object.entries(change.values)) {
276
+ const index = columnIndex(table, name);
277
+ row[index] = resolveEdit(table.columns[index]!, draft, value);
278
+ }
279
+ });
280
+
281
+ table.rows = draft.rows;
282
+ return changes.length;
283
+ };
284
+
285
+ const SELECT_ALL = /^select\s+\*\s+from\s+(\S+)$/i;
286
+
287
+ /* The table of a `SELECT * FROM <table>`, the only statement the fake runs. */
288
+ const selectedTable = (session: Session, sql: string): Table => {
289
+ const name = SELECT_ALL.exec(sql.trim().replace(/;+$/, ''))?.[1]?.replace(/^[`"]|[`"]$/g, '');
290
+
291
+ if (name === undefined) {
292
+ throw new FakeError('unsupported', 'The fake database only runs SELECT * FROM <table>.');
293
+ }
294
+
295
+ return findTable(session, session.schema ?? session.schemas.keys().next().value ?? '', name);
296
+ };
297
+
298
+ const runStatement = (session: Session, sql: string, limit: number, cellLimit: number): StatementResult => {
299
+ try {
300
+ const table = selectedTable(session, sql);
301
+ return { kind: 'rows', sql, ...toRowsResult(table, table.rows, 0, limit, cellLimit) };
302
+ } catch (error) {
303
+ if (error instanceof FakeError) {
304
+ return {
305
+ kind: 'error',
306
+ sql,
307
+ error: { code: error.code, message: error.message, ...(error.sqlState === undefined ? {} : { sqlState: error.sqlState }) },
308
+ elapsedMs: 0
309
+ };
310
+ }
311
+
312
+ throw error;
313
+ }
314
+ };
315
+
316
+ /* Makes a schema the selected one, as a call that names `schema` does. */
317
+ const selectSchema = (session: Session, schema: string | undefined): void => {
318
+ if (schema === undefined) {
319
+ return;
320
+ }
321
+
322
+ if (!session.schemas.has(schema)) {
323
+ throw new FakeError('query-failed', `Unknown database '${schema}'`, '42000');
324
+ }
325
+
326
+ session.schema = schema;
327
+ };
328
+
329
+ type Handlers = { readonly [M in DatabaseMethod]: (params: DatabaseParams<M>) => DatabaseResult<M> };
330
+
331
+ /*
332
+ * An in-memory database server behind the transport of the client, for demos and for tests of an app.
333
+ * It answers the whole protocol, with these limits: `where` and `orderBy` are not interpreted, so
334
+ * `rows` and `count` return every row in stored order; `execute` and `page` run `SELECT * FROM <table>`
335
+ * on the selected schema (the first one until a call names another) and answer every other statement
336
+ * with an `unsupported` error; and, as in the protocol, the first failed statement ends the list.
337
+ * `transaction` only raises a flag that `execute` reports: a rollback does not undo a change. There
338
+ * are no files, so `export`, `import` and `sample` answer `unsupported`.
339
+ */
340
+ export const fakeDatabaseTransport = (options: FakeDatabaseTransportOptions): DatabaseTransport => {
341
+ const databases = new Map(Object.entries(options.databases).map(([name, database]) => [name, toTables(database)]));
342
+ const sessions = new Map<string, Session>();
343
+ const waiting = new Map<string, () => void>();
344
+ let counter = 0;
345
+
346
+ const serverOf = (config: { readonly engine: 'sqlite' | 'mysql' }): ServerInfo =>
347
+ options.server ?? (config.engine === 'sqlite' ? { flavor: 'sqlite', version: '3.50.4' } : { flavor: 'mysql', version: '8.4.0' });
348
+
349
+ const connect = (config: DatabaseParams<'open'>['connection']): Session => {
350
+ const name = config.engine === 'sqlite' ? config.path : config.host;
351
+ const schemas = databases.get(name);
352
+
353
+ if (schemas === undefined) {
354
+ throw new FakeError('connect-failed', config.engine === 'sqlite' ? `Unable to open database file "${name}"` : `Unknown MySQL server "${name}"`);
355
+ }
356
+
357
+ return { schemas, readOnly: config.readOnly === true, schema: config.engine === 'mysql' ? config.database || null : null, inTransaction: false };
358
+ };
359
+
360
+ const sessionOf = (id: string): Session => {
361
+ const session = sessions.get(id);
362
+
363
+ if (session === undefined) {
364
+ throw new FakeError('unknown-session', `There is no open session "${id}".`);
365
+ }
366
+
367
+ return session;
368
+ };
369
+
370
+ const handlers: Handlers = {
371
+ open: ({ connection }) => {
372
+ const session = connect(connection);
373
+ const id = `fake-${++counter}`;
374
+ sessions.set(id, session);
375
+ return { session: id, server: serverOf(connection) };
376
+ },
377
+ close: ({ session }) => {
378
+ sessionOf(session);
379
+ sessions.delete(session);
380
+ return null;
381
+ },
382
+ test: ({ connection }) => {
383
+ connect(connection);
384
+ return { server: serverOf(connection) };
385
+ },
386
+ schemas: ({ session }) => ({
387
+ schemas: [...sessionOf(session).schemas.keys()].map((name) => ({ name, system: SYSTEM_SCHEMAS.includes(name) }))
388
+ }),
389
+ tables: ({ session, schema }) => {
390
+ const tables = sessionOf(session).schemas.get(schema);
391
+
392
+ if (tables === undefined) {
393
+ throw new FakeError('query-failed', `Unknown database '${schema}'`, '42000');
394
+ }
395
+
396
+ return { tables: [...tables].map(([name, table]) => ({ name, kind: table.kind, rowEstimate: table.rows.length, comment: null })) };
397
+ },
398
+ structure: ({ session, schema, table: name }) => {
399
+ const table = findTable(sessionOf(session), schema, name);
400
+ return {
401
+ schema,
402
+ name,
403
+ kind: table.kind,
404
+ columns: table.columns,
405
+ primaryKey: table.primaryKey,
406
+ rowKey: rowKeyOf(table),
407
+ indexes: table.indexes,
408
+ foreignKeys: table.foreignKeys,
409
+ ddl: table.ddl
410
+ };
411
+ },
412
+ rows: ({ session, schema, table: name, offset, limit, cellLimit }) => {
413
+ const table = findTable(sessionOf(session), schema, name);
414
+ return toRowsResult(table, table.rows, offset, limit, cellLimit ?? DEFAULT_ROWS_CELL_LIMIT);
415
+ },
416
+ count: ({ session, schema, table: name }) => ({ count: findTable(sessionOf(session), schema, name).rows.length }),
417
+ cell: ({ session, schema, table: name, key, column }) => {
418
+ const table = findTable(sessionOf(session), schema, name);
419
+ const index = columnIndex(table, column);
420
+
421
+ if (rowKeyOf(table) === null) {
422
+ throw new FakeError('no-row-key', 'The table has no primary key or unique key to pick a row with.');
423
+ }
424
+
425
+ const row = table.rows.find((candidate) => matches(table, candidate, key));
426
+
427
+ if (row === undefined) {
428
+ throw new FakeError('query-failed', 'No row matches the key.');
429
+ }
430
+
431
+ return { value: row[index]! };
432
+ },
433
+ apply: ({ session, schema, table: name, changes }) => {
434
+ const target = sessionOf(session);
435
+
436
+ if (target.readOnly) {
437
+ throw new FakeError('read-only', 'The connection is read only.');
438
+ }
439
+
440
+ return { affected: applyChanges(findTable(target, schema, name), changes) };
441
+ },
442
+ execute: ({ session, sql, schema, limit, cellLimit }) => {
443
+ const target = sessionOf(session);
444
+ selectSchema(target, schema);
445
+
446
+ const results: StatementResult[] = [];
447
+
448
+ for (const statement of sql
449
+ .split(';')
450
+ .map((part) => part.trim())
451
+ .filter((part) => part.length > 0)) {
452
+ const result = runStatement(target, statement, limit ?? DEFAULT_EXECUTE_LIMIT, cellLimit ?? DEFAULT_EXECUTE_CELL_LIMIT);
453
+ results.push(result);
454
+
455
+ if (result.kind === 'error') {
456
+ break;
457
+ }
458
+ }
459
+
460
+ return { results, inTransaction: target.inTransaction };
461
+ },
462
+ page: ({ session, sql, schema, offset, limit, cellLimit }) => {
463
+ const target = sessionOf(session);
464
+ selectSchema(target, schema);
465
+ const table = selectedTable(target, sql);
466
+ return toRowsResult(table, table.rows, offset, limit, cellLimit ?? DEFAULT_EXECUTE_CELL_LIMIT);
467
+ },
468
+ transaction: ({ session, action }) => {
469
+ const target = sessionOf(session);
470
+ target.inTransaction = action === 'begin';
471
+ return { active: target.inTransaction };
472
+ },
473
+ export: ({ session }) => {
474
+ sessionOf(session);
475
+ throw new FakeError('unsupported', 'The fake database has no files to export to.');
476
+ },
477
+ sample: () => {
478
+ throw new FakeError('unsupported', 'The fake database has no files to read.');
479
+ },
480
+ import: ({ session }) => {
481
+ sessionOf(session);
482
+ throw new FakeError('unsupported', 'The fake database has no files to import from.');
483
+ },
484
+ discover: () => ({ containers: structuredClone([...(options.containers ?? [])]) }),
485
+ cancel: ({ request }) => {
486
+ const stop = waiting.get(request);
487
+ stop?.();
488
+ return { cancelled: stop !== undefined };
489
+ }
490
+ };
491
+
492
+ const answer = (request: DatabaseRequest): DatabaseResponse => {
493
+ try {
494
+ const handler = handlers[request.method] as (params: unknown) => unknown;
495
+ return { id: request.id, ok: true, result: handler(request.params) } as DatabaseResponse;
496
+ } catch (error) {
497
+ if (error instanceof FakeError) {
498
+ return {
499
+ id: request.id,
500
+ ok: false,
501
+ error: {
502
+ code: error.code,
503
+ message: error.message,
504
+ ...(error.sqlState === undefined ? {} : { sqlState: error.sqlState }),
505
+ ...(error.change === undefined ? {} : { change: error.change })
506
+ }
507
+ };
508
+ }
509
+
510
+ throw error;
511
+ }
512
+ };
513
+
514
+ return (request) => {
515
+ // `cancel` answers at once, since delaying it would let the request it targets finish first.
516
+ if (!options.latencyMs || request.method === 'cancel') {
517
+ return Promise.resolve(answer(request));
518
+ }
519
+
520
+ return new Promise<DatabaseResponse>((resolve) => {
521
+ const timer = setTimeout(() => {
522
+ waiting.delete(request.id);
523
+ resolve(answer(request));
524
+ }, options.latencyMs);
525
+
526
+ waiting.set(request.id, () => {
527
+ clearTimeout(timer);
528
+ waiting.delete(request.id);
529
+ resolve({ id: request.id, ok: false, error: { code: 'cancelled', message: 'The request was cancelled.' } });
530
+ });
531
+ });
532
+ };
533
+ };
@@ -0,0 +1 @@
1
+ export { fakeDatabaseTransport, type FakeDatabase, type FakeDatabaseTransportOptions, type FakeTable } from './fake.ts';
@@ -0,0 +1,27 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { isInFloatingLayer } from '@adecore/ui';
3
+
4
+ /*
5
+ * Catches the click that follows a press in a popup, menu or dialog. When a popup closes on that
6
+ * press, the browser lands the click on whatever lies under the pointer, which is not something the
7
+ * person clicked. The returned check is true once for such a click.
8
+ */
9
+ export function usePopupPress(): () => boolean {
10
+ const pressed = useRef(false);
11
+
12
+ useEffect(() => {
13
+ const press = (e: PointerEvent): void => {
14
+ pressed.current = isInFloatingLayer(e.target);
15
+ };
16
+ document.addEventListener('pointerdown', press, true);
17
+ return () => {
18
+ document.removeEventListener('pointerdown', press, true);
19
+ };
20
+ }, []);
21
+
22
+ return () => {
23
+ const was = pressed.current;
24
+ pressed.current = false;
25
+ return was;
26
+ };
27
+ }
@@ -0,0 +1,19 @@
1
+ import { useEffect } from 'react';
2
+ import { useDatabaseClient } from './client-context.ts';
3
+ import { useStableCallback } from './use-stable-callback.ts';
4
+
5
+ /* Calls `onChange` when the shape of this schema changed, or of its connection when the change names no schema. */
6
+ export function useSchemaChange(connectionId: string, schema: string, onChange: () => void): void {
7
+ const client = useDatabaseClient();
8
+ const notify = useStableCallback(onChange);
9
+
10
+ useEffect(
11
+ () =>
12
+ client.onSchemaChange((change) => {
13
+ if (change.connectionId === connectionId && (change.schema === undefined || change.schema === schema)) {
14
+ notify();
15
+ }
16
+ }),
17
+ [client, connectionId, schema, notify]
18
+ );
19
+ }
@@ -0,0 +1,12 @@
1
+ import { useCallback, useLayoutEffect, useRef } from 'react';
2
+
3
+ /* A function that never changes identity and always calls the latest `callback`, so a provider value built on it does not rebuild on each render. */
4
+ export function useStableCallback<Args extends unknown[], Result>(callback: (...args: Args) => Result): (...args: Args) => Result {
5
+ const latest = useRef(callback);
6
+
7
+ useLayoutEffect(() => {
8
+ latest.current = callback;
9
+ });
10
+
11
+ return useCallback((...args: Args) => latest.current(...args), []);
12
+ }