@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,86 @@
1
+ /* A database file on the machine the helper runs on. */
2
+ export interface SqliteConnectionConfig {
3
+ readonly engine: 'sqlite';
4
+ /* An absolute path. The helper never creates the file unless `create` is set. */
5
+ readonly path: string;
6
+ readonly create?: boolean;
7
+ readonly readOnly?: boolean;
8
+ }
9
+
10
+ /* How the connection to a MySQL or MariaDB server is encrypted. `prefer` falls back to plain text when the server offers no TLS; `verify` also checks the certificate against the host name. */
11
+ export type MysqlTlsMode = 'disable' | 'prefer' | 'require' | 'verify';
12
+
13
+ /*
14
+ * A server reached through a host the helper can SSH into. The helper runs the system's `ssh -W` for
15
+ * every connection, so `~/.ssh/config`, the agent and `ProxyJump` apply, and it never asks for a
16
+ * password: a key or the agent has to answer.
17
+ */
18
+ export interface SshTunnel {
19
+ readonly kind: 'ssh';
20
+ /* A host name, or a `Host` of `~/.ssh/config`. */
21
+ readonly host: string;
22
+ readonly port?: number;
23
+ readonly user?: string;
24
+ readonly identityFile?: string;
25
+ }
26
+
27
+ /*
28
+ * A server in a Docker container. The helper connects to the port the container publishes, or, when it
29
+ * publishes none, through `docker exec`, which needs `bash` in the container.
30
+ */
31
+ export interface DockerTunnel {
32
+ readonly kind: 'docker';
33
+ /* The name or id of the container. */
34
+ readonly container: string;
35
+ /* The port inside the container; the engine's default when left out. */
36
+ readonly port?: number;
37
+ /* A Docker context other than the current one. */
38
+ readonly context?: string;
39
+ }
40
+
41
+ export type Tunnel = SshTunnel | DockerTunnel;
42
+
43
+ /* A MySQL or MariaDB server, over TCP or a Unix socket, directly or through a tunnel. */
44
+ export interface MysqlConnectionConfig {
45
+ readonly engine: 'mysql';
46
+ /* As the far end of the tunnel sees it when there is one; a Docker tunnel ignores it. */
47
+ readonly host: string;
48
+ /* 3306 when left out. */
49
+ readonly port?: number;
50
+ /* A Unix socket to connect through instead of `host` and `port`. */
51
+ readonly socket?: string;
52
+ readonly user: string;
53
+ readonly password?: string;
54
+ /* The schema a session starts in; a session without one sees every schema and has none selected. */
55
+ readonly database?: string;
56
+ readonly tls?: MysqlTlsMode;
57
+ readonly readOnly?: boolean;
58
+ readonly tunnel?: Tunnel;
59
+ }
60
+
61
+ export type ConnectionConfig = SqliteConnectionConfig | MysqlConnectionConfig;
62
+
63
+ export type Engine = ConnectionConfig['engine'];
64
+
65
+ /* What answered: one engine can be served by more than one product, which differ in SQL. */
66
+ export interface ServerInfo {
67
+ readonly flavor: 'sqlite' | 'mysql' | 'mariadb';
68
+ /* The version as the server reports it, such as `11.4.2-MariaDB`. */
69
+ readonly version: string;
70
+ }
71
+
72
+ /* A running container that looks like it serves a database, as `discover` lists it. */
73
+ export interface DockerContainer {
74
+ readonly id: string;
75
+ readonly name: string;
76
+ readonly image: string;
77
+ /* What the image or its ports suggest; `null` when neither says. */
78
+ readonly engine: Engine | null;
79
+ /* The ports inside the container with the host port each is published on, or `null` when it is not. */
80
+ readonly ports: readonly { readonly container: number; readonly host: number | null }[];
81
+ /* From the Compose labels, so a list can group the containers of one project. */
82
+ readonly project: string | null;
83
+ readonly service: string | null;
84
+ /* What the container's environment sets for the image's own variables (`MYSQL_DATABASE`, `MARIADB_USER`, ...), to fill a form with. */
85
+ readonly suggested: { readonly user?: string; readonly password?: string; readonly database?: string };
86
+ }
@@ -0,0 +1,38 @@
1
+ /*
2
+ * Why a request failed. The helper sends the first group; the host adds `helper-exited` and
3
+ * `helper-unavailable`, and `forbidden` when the app's check turns a connection, a file or a discovery down.
4
+ */
5
+ export type DatabaseErrorCode =
6
+ /* The request does not have the shape of the protocol. */
7
+ | 'invalid-request'
8
+ /* The session was closed, or belonged to a helper that has since exited. */
9
+ | 'unknown-session'
10
+ | 'connect-failed'
11
+ | 'auth-failed'
12
+ /* The SSH or Docker tunnel did not come up; `message` holds what `ssh` or `docker` said. */
13
+ | 'tunnel-failed'
14
+ /* The server turned the SQL down; `sqlState` and `message` say why. */
15
+ | 'query-failed'
16
+ /* A write on a connection opened read only. */
17
+ | 'read-only'
18
+ /* An update or a delete on a table without a primary key or a unique key over columns that cannot be null. */
19
+ | 'no-row-key'
20
+ /* An update or a delete matched no row or more than one, so the transaction was rolled back. */
21
+ | 'conflict'
22
+ | 'cancelled'
23
+ | 'unsupported'
24
+ /* A file to export to or import from could not be read or written. */
25
+ | 'file-failed'
26
+ | 'forbidden'
27
+ | 'helper-exited'
28
+ | 'helper-unavailable'
29
+ | 'internal';
30
+
31
+ export interface DatabaseError {
32
+ readonly code: DatabaseErrorCode;
33
+ readonly message: string;
34
+ /* The five characters of SQLSTATE, when the server sent them. */
35
+ readonly sqlState?: string;
36
+ /* For `conflict`: which change of the request it was, counted from zero. */
37
+ readonly change?: number;
38
+ }
@@ -0,0 +1,33 @@
1
+ /* The messages between a page, the host in the app's backend and the helper. Types and pure helpers only, so a page, a preload and a backend can all read it. */
2
+
3
+ export type {
4
+ ConnectionConfig,
5
+ DockerContainer,
6
+ DockerTunnel,
7
+ Engine,
8
+ MysqlConnectionConfig,
9
+ MysqlTlsMode,
10
+ ServerInfo,
11
+ SqliteConnectionConfig,
12
+ SshTunnel,
13
+ Tunnel
14
+ } from './connection.ts';
15
+ export type { DatabaseError, DatabaseErrorCode } from './errors.ts';
16
+ export {
17
+ PROTOCOL_VERSION,
18
+ type DatabaseMethod,
19
+ type DatabaseMethods,
20
+ type DatabaseParams,
21
+ type DatabaseRequest,
22
+ type DatabaseResponse,
23
+ type DatabaseResult,
24
+ type ExportSource,
25
+ type FileFormat,
26
+ type HelperReady,
27
+ type RowChange,
28
+ type RowKey,
29
+ type RowsResult,
30
+ type StatementResult
31
+ } from './messages.ts';
32
+ export type { ColumnInfo, ForeignKeyInfo, IndexInfo, ResultColumn, SchemaInfo, TableInfo, TableKind, TableStructure } from './schema.ts';
33
+ export { valueOfCell, type BinaryCell, type BinaryValue, type Cell, type EditValue, type LongTextCell, type Value, type ValueKind } from './values.ts';
@@ -0,0 +1,168 @@
1
+ import type { ConnectionConfig, DockerContainer, ServerInfo } from './connection.ts';
2
+ import type { DatabaseError } from './errors.ts';
3
+ import type { ResultColumn, SchemaInfo, TableInfo, TableStructure } from './schema.ts';
4
+ import type { Cell, EditValue, Value } from './values.ts';
5
+
6
+ /* Raised whenever a message changes shape, so a host refuses a helper from another release. */
7
+ export const PROTOCOL_VERSION = 2;
8
+
9
+ /* The columns of the row key and their values. */
10
+ export type RowKey = Readonly<Record<string, Value>>;
11
+
12
+ export type RowChange =
13
+ | { readonly kind: 'insert'; readonly values: Readonly<Record<string, EditValue>> }
14
+ | { readonly kind: 'update'; readonly key: RowKey; readonly values: Readonly<Record<string, EditValue>> }
15
+ | { readonly kind: 'delete'; readonly key: RowKey };
16
+
17
+ export interface RowsResult {
18
+ readonly columns: readonly ResultColumn[];
19
+ readonly rows: readonly (readonly Cell[])[];
20
+ /* Whether a row exists past this page, which the helper learns by reading one row more than the limit. */
21
+ readonly hasMore: boolean;
22
+ readonly elapsedMs: number;
23
+ }
24
+
25
+ /* What one statement of an `execute` did. A failed statement ends the list, and the ones after it never ran. */
26
+ export type StatementResult =
27
+ | ({ readonly kind: 'rows'; readonly sql: string } & RowsResult)
28
+ | { readonly kind: 'done'; readonly sql: string; readonly affected: number; readonly lastInsertId: number | string | null; readonly elapsedMs: number }
29
+ | { readonly kind: 'error'; readonly sql: string; readonly error: DatabaseError; readonly elapsedMs: number };
30
+
31
+ /* A file format for `export` and `import`. JSON and SQL only export: an import reads delimited text. */
32
+ export type FileFormat = 'csv' | 'tsv' | 'json' | 'sql';
33
+
34
+ /* What an export writes: a table, filtered and ordered as a table view shows it, or the rows of one statement. */
35
+ export type ExportSource =
36
+ | { readonly kind: 'table'; readonly schema: string; readonly table: string; readonly where?: string; readonly orderBy?: string }
37
+ | { readonly kind: 'query'; readonly sql: string; readonly schema?: string };
38
+
39
+ interface TableTarget {
40
+ readonly session: string;
41
+ readonly schema: string;
42
+ readonly table: string;
43
+ }
44
+
45
+ /*
46
+ * Every method with its params and its result. `where` and `orderBy` are SQL as a person types it
47
+ * after those keywords; they reach the server as written, inside one statement. A connection opened
48
+ * read only stays read only, so neither can write.
49
+ */
50
+ export interface DatabaseMethods {
51
+ open: { params: { readonly connection: ConnectionConfig }; result: { readonly session: string; readonly server: ServerInfo } };
52
+ close: { params: { readonly session: string }; result: null };
53
+ /* Opens a connection and closes it again, for a form that checks what a person filled in. */
54
+ test: { params: { readonly connection: ConnectionConfig }; result: { readonly server: ServerInfo } };
55
+ schemas: { params: { readonly session: string }; result: { readonly schemas: readonly SchemaInfo[] } };
56
+ tables: { params: { readonly session: string; readonly schema: string }; result: { readonly tables: readonly TableInfo[] } };
57
+ structure: { params: TableTarget; result: TableStructure };
58
+ rows: {
59
+ params: TableTarget & {
60
+ readonly where?: string;
61
+ readonly orderBy?: string;
62
+ readonly offset: number;
63
+ /* At most 10000. */
64
+ readonly limit: number;
65
+ /* Characters of text, or bytes of a binary value, before a cell becomes a preview. 1024 when left out. */
66
+ readonly cellLimit?: number;
67
+ };
68
+ result: RowsResult;
69
+ };
70
+ count: { params: TableTarget & { readonly where?: string }; result: { readonly count: number } };
71
+ /* The whole value of one cell, for a cell a read cut off. */
72
+ cell: { params: TableTarget & { readonly key: RowKey; readonly column: string }; result: { readonly value: Value } };
73
+ /* Every change in one transaction: all of them apply, or none does. */
74
+ apply: { params: TableTarget & { readonly changes: readonly RowChange[] }; result: { readonly affected: number } };
75
+ execute: {
76
+ params: {
77
+ readonly session: string;
78
+ /* One statement or several, separated by semicolons. */
79
+ readonly sql: string;
80
+ /* The schema to switch to first, which stays selected for the session. */
81
+ readonly schema?: string;
82
+ /* Rows per result, at most 10000. 500 when left out. */
83
+ readonly limit?: number;
84
+ /* As for `rows`; 65536 when left out. */
85
+ readonly cellLimit?: number;
86
+ };
87
+ /* `inTransaction` says whether a transaction is open after the last statement, also one the SQL itself began or ended. */
88
+ result: { readonly results: readonly StatementResult[]; readonly inTransaction: boolean };
89
+ };
90
+ /*
91
+ * One page of the rows of a single statement that reads, so a console can page through a result
92
+ * larger than its first page. The helper wraps the statement (`SELECT * FROM (<sql>) LIMIT .. OFFSET ..`),
93
+ * so a statement it cannot wrap, such as `SHOW` or `PRAGMA`, fails with `unsupported`.
94
+ */
95
+ page: {
96
+ params: {
97
+ readonly session: string;
98
+ readonly sql: string;
99
+ readonly schema?: string;
100
+ readonly offset: number;
101
+ readonly limit: number;
102
+ readonly cellLimit?: number;
103
+ };
104
+ result: RowsResult;
105
+ };
106
+ /*
107
+ * Starts, commits or rolls back a transaction the person drives. While one is open, `execute` and
108
+ * `apply` run inside it (an `apply` as a savepoint), and closing the session rolls it back.
109
+ */
110
+ transaction: { params: { readonly session: string; readonly action: 'begin' | 'commit' | 'rollback' }; result: { readonly active: boolean } };
111
+ /* Writes every row of the source to a file, streamed, so a table larger than memory still exports. */
112
+ export: {
113
+ params: {
114
+ readonly session: string;
115
+ readonly source: ExportSource;
116
+ readonly format: FileFormat;
117
+ /* An absolute path the app allowed. An existing file is replaced. */
118
+ readonly path: string;
119
+ /* For CSV and TSV: a first line with the column names. True when left out. */
120
+ readonly header?: boolean;
121
+ /* For SQL: the table name the INSERT statements use; the source table, or `result`, when left out. */
122
+ readonly tableName?: string;
123
+ };
124
+ result: { readonly rows: number; readonly bytes: number; readonly elapsedMs: number };
125
+ };
126
+ /* The first lines of a delimited file, so a form can map its columns before an import. */
127
+ sample: {
128
+ params: { readonly path: string; readonly format: 'csv' | 'tsv'; readonly header: boolean; readonly limit?: number };
129
+ result: { readonly columns: readonly string[]; readonly rows: readonly (readonly string[])[] };
130
+ };
131
+ /* Inserts every line of a delimited file in one transaction. A field that is empty, or `\N`, inserts NULL. */
132
+ import: {
133
+ params: TableTarget & {
134
+ readonly path: string;
135
+ readonly format: 'csv' | 'tsv';
136
+ readonly header: boolean;
137
+ /* The column each field of a line goes into, by position; `null` skips that field. */
138
+ readonly columns: readonly (string | null)[];
139
+ };
140
+ result: { readonly rows: number; readonly elapsedMs: number };
141
+ };
142
+ /* The running containers that look like database servers. */
143
+ discover: { params: { readonly kind: 'docker'; readonly context?: string }; result: { readonly containers: readonly DockerContainer[] } };
144
+ /* Stops the request with this id. Answers whether it was still running; the request itself fails with `cancelled`. */
145
+ cancel: { params: { readonly request: string }; result: { readonly cancelled: boolean } };
146
+ }
147
+
148
+ export type DatabaseMethod = keyof DatabaseMethods;
149
+
150
+ export type DatabaseParams<M extends DatabaseMethod> = DatabaseMethods[M]['params'];
151
+
152
+ export type DatabaseResult<M extends DatabaseMethod> = DatabaseMethods[M]['result'];
153
+
154
+ /* A request names its own `id`, which its response repeats and a `cancel` points at. */
155
+ export type DatabaseRequest<M extends DatabaseMethod = DatabaseMethod> = {
156
+ [K in M]: { readonly id: string; readonly method: K; readonly params: DatabaseParams<K> };
157
+ }[M];
158
+
159
+ export type DatabaseResponse<M extends DatabaseMethod = DatabaseMethod> =
160
+ | { readonly id: string; readonly ok: true; readonly result: DatabaseResult<M> }
161
+ | { readonly id: string; readonly ok: false; readonly error: DatabaseError };
162
+
163
+ /* The first line a helper writes, before it reads a request. */
164
+ export interface HelperReady {
165
+ readonly event: 'ready';
166
+ readonly protocol: number;
167
+ readonly version: string;
168
+ }
@@ -0,0 +1,76 @@
1
+ import type { ValueKind } from './values.ts';
2
+
3
+ /* A schema in MySQL terms, which is a database there; SQLite has `main` and one per attached file. */
4
+ export interface SchemaInfo {
5
+ readonly name: string;
6
+ /* A schema the server keeps for itself, such as `information_schema`; a list hides these by default. */
7
+ readonly system: boolean;
8
+ }
9
+
10
+ export type TableKind = 'table' | 'view';
11
+
12
+ export interface TableInfo {
13
+ readonly name: string;
14
+ readonly kind: TableKind;
15
+ /* What the server's statistics say, which can be far off; `null` when it keeps none. */
16
+ readonly rowEstimate: number | null;
17
+ readonly comment: string | null;
18
+ }
19
+
20
+ export interface ColumnInfo {
21
+ readonly name: string;
22
+ /* The type as it was declared, such as `varchar(255)`; an SQLite column may have none, which is an empty string. */
23
+ readonly type: string;
24
+ readonly kind: ValueKind;
25
+ readonly nullable: boolean;
26
+ /* The default as an SQL expression, such as `'draft'` or `CURRENT_TIMESTAMP`. */
27
+ readonly defaultValue: string | null;
28
+ readonly autoIncrement: boolean;
29
+ /* A column the server computes, which an insert or an update leaves alone. */
30
+ readonly generated: boolean;
31
+ readonly comment: string | null;
32
+ }
33
+
34
+ export interface IndexInfo {
35
+ readonly name: string;
36
+ readonly columns: readonly string[];
37
+ readonly unique: boolean;
38
+ readonly primary: boolean;
39
+ }
40
+
41
+ export interface ForeignKeyInfo {
42
+ /* SQLite leaves a foreign key without a name unless the DDL gives it one. */
43
+ readonly name: string | null;
44
+ readonly columns: readonly string[];
45
+ readonly referencedSchema: string;
46
+ readonly referencedTable: string;
47
+ readonly referencedColumns: readonly string[];
48
+ /* The action as SQL, such as `CASCADE`; `null` is the engine's default. */
49
+ readonly onUpdate: string | null;
50
+ readonly onDelete: string | null;
51
+ }
52
+
53
+ export interface TableStructure {
54
+ readonly schema: string;
55
+ readonly name: string;
56
+ readonly kind: TableKind;
57
+ readonly columns: readonly ColumnInfo[];
58
+ readonly primaryKey: readonly string[];
59
+ /*
60
+ * The columns that pick out one row for an update or a delete: the primary key, or else the first
61
+ * unique index over columns that cannot be null. `null` when there is neither, and the table is read only.
62
+ */
63
+ readonly rowKey: readonly string[] | null;
64
+ readonly indexes: readonly IndexInfo[];
65
+ readonly foreignKeys: readonly ForeignKeyInfo[];
66
+ /* The `CREATE` statement as the server writes it. */
67
+ readonly ddl: string | null;
68
+ }
69
+
70
+ /* A column of a result, which may come from a table or from an expression. */
71
+ export interface ResultColumn {
72
+ readonly name: string;
73
+ /* The engine's name for the type of the value, such as `VARCHAR`; empty when it reports none. */
74
+ readonly type: string;
75
+ readonly kind: ValueKind;
76
+ }
@@ -0,0 +1,52 @@
1
+ /*
2
+ * Every value crosses as JSON, so an app can carry it over any channel it already has. An integer
3
+ * beyond `Number.MAX_SAFE_INTEGER`, a decimal, a date and a time arrive as the text the server
4
+ * writes for them, so nothing is rounded on the way.
5
+ */
6
+
7
+ /* Bytes as lowercase hex. */
8
+ export interface BinaryValue {
9
+ readonly kind: 'binary';
10
+ readonly hex: string;
11
+ }
12
+
13
+ /* A whole value, as a key or an edit carries it. */
14
+ export type Value = null | boolean | number | string | BinaryValue;
15
+
16
+ /* What a cell in an edit is set to: a value, or the column's default. */
17
+ export type EditValue = Value | { readonly kind: 'default' };
18
+
19
+ /* Bytes in a result. `hex` holds the first bytes only when `length` is larger than `hex.length / 2`. */
20
+ export interface BinaryCell {
21
+ readonly kind: 'binary';
22
+ readonly hex: string;
23
+ /* The size of the whole value, in bytes. */
24
+ readonly length: number;
25
+ }
26
+
27
+ /* Text cut off at the cell limit of the read. A cell that fits arrives as a plain string. */
28
+ export interface LongTextCell {
29
+ readonly kind: 'longText';
30
+ readonly preview: string;
31
+ /* The length of the whole value, in characters. */
32
+ readonly length: number;
33
+ }
34
+
35
+ /* A value in a result: whole when it fits the cell limit of the read, a preview when it does not. */
36
+ export type Cell = null | boolean | number | string | BinaryCell | LongTextCell;
37
+
38
+ /* What a value is, independent of the engine's name for its type, so a grid can align and edit it. */
39
+ export type ValueKind = 'integer' | 'decimal' | 'float' | 'boolean' | 'text' | 'binary' | 'date' | 'time' | 'datetime' | 'json' | 'other';
40
+
41
+ /* The value a row key or an update can use for a cell, or `undefined` when the cell is only a preview. */
42
+ export const valueOfCell = (cell: Cell): Value | undefined => {
43
+ if (cell === null || typeof cell !== 'object') {
44
+ return cell;
45
+ }
46
+
47
+ if (cell.kind === 'binary') {
48
+ return cell.hex.length === cell.length * 2 ? { kind: 'binary', hex: cell.hex } : undefined;
49
+ }
50
+
51
+ return undefined;
52
+ };
@@ -0,0 +1,220 @@
1
+ import type { Engine } from './protocol/index.ts';
2
+
3
+ /* One piece of SQL text. Whitespace is no token; every other character is part of one. */
4
+ export interface SqlToken {
5
+ readonly kind: 'word' | 'quoted' | 'comment' | 'punct' | 'semicolon';
6
+ /* Offsets into the text, `end` exclusive. */
7
+ readonly start: number;
8
+ readonly end: number;
9
+ }
10
+
11
+ /* A statement of a script, trimmed. `start` and `end` are offsets into the script, `end` exclusive and before the semicolon. */
12
+ export interface SqlStatement {
13
+ readonly text: string;
14
+ readonly start: number;
15
+ readonly end: number;
16
+ }
17
+
18
+ const WHITESPACE = /\s/;
19
+
20
+ const isWordCode = (code: number): boolean =>
21
+ (code >= 48 && code <= 57) || (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || code === 95 || code === 36 || code >= 128;
22
+
23
+ const lineEnd = (text: string, at: number): number => {
24
+ const newline = text.indexOf('\n', at);
25
+ return newline === -1 ? text.length : newline + 1;
26
+ };
27
+
28
+ /* MySQL wants whitespace after the dashes; SQLite does not. */
29
+ const startsLineComment = (text: string, at: number, engine: Engine): boolean => {
30
+ if (text[at] === '-' && text[at + 1] === '-') {
31
+ if (engine === 'sqlite') {
32
+ return true;
33
+ }
34
+ const next = text[at + 2];
35
+ return next === undefined || WHITESPACE.test(next) || next.charCodeAt(0) < 32;
36
+ }
37
+ return text[at] === '#' && engine === 'mysql';
38
+ };
39
+
40
+ /* The index after the closing quote, or the end of the text for one that never closes. */
41
+ const quotedEnd = (text: string, at: number, engine: Engine): number => {
42
+ const quote = text[at]!;
43
+ const backslashEscapes = engine === 'mysql' && quote !== '`';
44
+ let i = at + 1;
45
+ while (i < text.length) {
46
+ if (backslashEscapes && text[i] === '\\') {
47
+ i += 2;
48
+ } else if (text[i] === quote) {
49
+ if (text[i + 1] === quote) {
50
+ i += 2;
51
+ } else {
52
+ return i + 1;
53
+ }
54
+ } else {
55
+ i += 1;
56
+ }
57
+ }
58
+ return text.length;
59
+ };
60
+
61
+ /* Reads SQL the way the helper's splitter does: quotes, comments and escapes decide what a semicolon is. */
62
+ export const tokenize = (text: string, engine: Engine): SqlToken[] => {
63
+ const tokens: SqlToken[] = [];
64
+ let i = 0;
65
+ while (i < text.length) {
66
+ const char = text[i]!;
67
+ const code = text.charCodeAt(i);
68
+ if (WHITESPACE.test(char)) {
69
+ i += 1;
70
+ } else if (char === "'" || char === '"' || char === '`') {
71
+ const end = quotedEnd(text, i, engine);
72
+ tokens.push({ kind: 'quoted', start: i, end });
73
+ i = end;
74
+ } else if (char === '[' && engine === 'sqlite') {
75
+ const close = text.indexOf(']', i);
76
+ const end = close === -1 ? text.length : close + 1;
77
+ tokens.push({ kind: 'quoted', start: i, end });
78
+ i = end;
79
+ } else if (startsLineComment(text, i, engine)) {
80
+ const end = lineEnd(text, i);
81
+ tokens.push({ kind: 'comment', start: i, end });
82
+ i = end;
83
+ } else if (char === '/' && text[i + 1] === '*') {
84
+ const close = text.indexOf('*/', i + 2);
85
+ const end = close === -1 ? text.length : close + 2;
86
+ tokens.push({ kind: 'comment', start: i, end });
87
+ i = end;
88
+ } else if (char === ';') {
89
+ tokens.push({ kind: 'semicolon', start: i, end: i + 1 });
90
+ i += 1;
91
+ } else if (isWordCode(code)) {
92
+ let end = i + 1;
93
+ while (end < text.length && isWordCode(text.charCodeAt(end))) {
94
+ end += 1;
95
+ }
96
+ tokens.push({ kind: 'word', start: i, end });
97
+ i = end;
98
+ } else {
99
+ tokens.push({ kind: 'punct', start: i, end: i + 1 });
100
+ i += 1;
101
+ }
102
+ }
103
+ return tokens;
104
+ };
105
+
106
+ /* Follows `CREATE TRIGGER ... BEGIN ... END` so the semicolons inside its body do not end the statement. */
107
+ class TriggerState {
108
+ private readonly leading: string[] = [];
109
+ private inBody = false;
110
+ private caseDepth = 0;
111
+ private closed = false;
112
+
113
+ word(word: string): void {
114
+ const upper = word.toUpperCase();
115
+ if (this.leading.length < 3) {
116
+ this.leading.push(upper);
117
+ }
118
+ if (!this.isTrigger()) {
119
+ return;
120
+ }
121
+ if (upper === 'BEGIN' && !this.inBody) {
122
+ this.inBody = true;
123
+ } else if (upper === 'CASE' && this.inBody) {
124
+ this.caseDepth += 1;
125
+ } else if (upper === 'END' && this.inBody) {
126
+ if (this.caseDepth > 0) {
127
+ this.caseDepth -= 1;
128
+ } else {
129
+ this.closed = true;
130
+ }
131
+ }
132
+ }
133
+
134
+ splitsHere(): boolean {
135
+ return !this.isTrigger() || !this.inBody || this.closed;
136
+ }
137
+
138
+ private isTrigger(): boolean {
139
+ const [first, second, third] = this.leading;
140
+ return first === 'CREATE' && (second === 'TRIGGER' || ((second === 'TEMP' || second === 'TEMPORARY') && third === 'TRIGGER'));
141
+ }
142
+ }
143
+
144
+ const trimmed = (text: string, from: number, to: number): SqlStatement => {
145
+ let start = from;
146
+ let end = to;
147
+ while (start < end && WHITESPACE.test(text[start]!)) {
148
+ start += 1;
149
+ }
150
+ while (end > start && WHITESPACE.test(text[end - 1]!)) {
151
+ end -= 1;
152
+ }
153
+ return { text: text.slice(start, end), start, end };
154
+ };
155
+
156
+ /*
157
+ * Splits a script at the semicolons outside strings, quoted names and comments, with the rules of the
158
+ * helper's splitter (`helper/src/splitter.rs`). A statement of nothing but comments is dropped, and
159
+ * MySQL's client-side `DELIMITER` command is not understood.
160
+ */
161
+ export const splitStatements = (text: string, engine: Engine): SqlStatement[] => {
162
+ const statements: SqlStatement[] = [];
163
+ let start = 0;
164
+ let hasContent = false;
165
+ let trigger = new TriggerState();
166
+
167
+ for (const token of tokenize(text, engine)) {
168
+ if (token.kind === 'semicolon') {
169
+ if (trigger.splitsHere()) {
170
+ if (hasContent) {
171
+ statements.push(trimmed(text, start, token.start));
172
+ }
173
+ start = token.end;
174
+ hasContent = false;
175
+ trigger = new TriggerState();
176
+ }
177
+ } else if (token.kind !== 'comment') {
178
+ hasContent = true;
179
+ if (token.kind === 'word' && engine === 'sqlite') {
180
+ trigger.word(text.slice(token.start, token.end));
181
+ }
182
+ }
183
+ }
184
+
185
+ if (hasContent) {
186
+ statements.push(trimmed(text, start, text.length));
187
+ }
188
+ return statements;
189
+ };
190
+
191
+ /*
192
+ * The statement the caret belongs to. A caret in the gap after a statement belongs to it while it
193
+ * stays on that line (so right after the semicolon is still that statement), and to the next one
194
+ * from the next line on. `null` when the text holds no statement.
195
+ */
196
+ export const statementAt = (text: string, offset: number, engine: Engine): SqlStatement | null => {
197
+ const statements = splitStatements(text, engine);
198
+ for (const [index, statement] of statements.entries()) {
199
+ if (offset >= statement.start && offset <= statement.end) {
200
+ return statement;
201
+ }
202
+ const next = statements[index + 1];
203
+ if (offset > statement.end && (next === undefined || offset < next.start)) {
204
+ const gap = text.slice(statement.end, offset);
205
+ return gap.includes('\n') && next !== undefined ? next : statement;
206
+ }
207
+ }
208
+ return statements[0] ?? null;
209
+ };
210
+
211
+ /* The first word of a statement in upper case, after comments and opening parentheses. */
212
+ export const firstKeyword = (text: string, engine: Engine): string => {
213
+ for (const token of tokenize(text, engine)) {
214
+ if (token.kind === 'comment' || (token.kind === 'punct' && text[token.start] === '(')) {
215
+ continue;
216
+ }
217
+ return token.kind === 'word' ? (/^[A-Za-z]*/.exec(text.slice(token.start, token.end))?.[0] ?? '').toUpperCase() : '';
218
+ }
219
+ return '';
220
+ };