@marimo-team/frontend 0.16.1 → 0.16.3

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 (310) hide show
  1. package/dist/assets/{ConnectedDataExplorerComponent-2wVcyvDj.js → ConnectedDataExplorerComponent-Brtw1DxF.js} +1 -1
  2. package/dist/assets/{ImageComparisonComponent-D2j6i0hv.js → ImageComparisonComponent-Dxl-PbZX.js} +1 -1
  3. package/dist/assets/{VegaLite-BckFaf2D.js → VegaLite-BXQF0Cx_.js} +1 -1
  4. package/dist/assets/_baseEach-BjSm9ht3.js +1 -0
  5. package/dist/assets/_baseMap-CV4Ezmtf.js +1 -0
  6. package/dist/assets/_baseUniq-Ci9yZGxz.js +1 -0
  7. package/dist/assets/{_createAggregator-C5CVY-0t.js → _createAggregator-VFK9K2d9.js} +1 -1
  8. package/dist/assets/{agent-panel-RGLNjkYe.js → agent-panel-BoscVLCT.js} +7 -7
  9. package/dist/assets/{any-language-editor-DjuXwGCA.js → any-language-editor-ChaY_VUU.js} +1 -1
  10. package/dist/assets/{architectureDiagram-W76B3OCA-Dyj4ds_R.js → architectureDiagram-W76B3OCA-CueUUFYd.js} +1 -1
  11. package/dist/assets/{between-horizontal-start-Dt2aKpPf.js → between-horizontal-start-DAHqmLYT.js} +1 -1
  12. package/dist/assets/{blockDiagram-QIGZ2CNN-o-i7DDvN.js → blockDiagram-QIGZ2CNN-BYYygyWn.js} +1 -1
  13. package/dist/assets/{c4Diagram-FPNF74CW-DGHEwWrx.js → c4Diagram-FPNF74CW-DAz3xEh1.js} +1 -1
  14. package/dist/assets/channel-6SqQ2U_X.js +1 -0
  15. package/dist/assets/chat-panel-DJkOLrw9.js +3 -0
  16. package/dist/assets/{chunk-4BX2VUAB-BJecb-Ri.js → chunk-4BX2VUAB-8g-RyHdt.js} +1 -1
  17. package/dist/assets/{chunk-55IACEB6-CAATkc4w.js → chunk-55IACEB6-iWZZ8Mt6.js} +1 -1
  18. package/dist/assets/{chunk-FMBD7UC4-DPuNbQ-f.js → chunk-FMBD7UC4-knjss4wk.js} +1 -1
  19. package/dist/assets/{chunk-K7UQS3LO-C8TWVLiH.js → chunk-K7UQS3LO-DVIwPBgZ.js} +1 -1
  20. package/dist/assets/{chunk-QN33PNHL-DiZZ09q4.js → chunk-QN33PNHL-CBU8pN6I.js} +1 -1
  21. package/dist/assets/{chunk-QZHKN3VN-BIUM7usu.js → chunk-QZHKN3VN-5ljElUF4.js} +1 -1
  22. package/dist/assets/{chunk-TVAH2DTR-vGTArPBG.js → chunk-TVAH2DTR-DkIdGINc.js} +1 -1
  23. package/dist/assets/{chunk-TZMSLE5B-D2KRqp_x.js → chunk-TZMSLE5B-CIFOSTqh.js} +1 -1
  24. package/dist/assets/{circle-play-cjeNez0N.js → circle-play-BOdsbq5u.js} +1 -1
  25. package/dist/assets/classDiagram-KNZD7YFC-DVqXcTYf.js +1 -0
  26. package/dist/assets/classDiagram-v2-RKCZMP56-DVqXcTYf.js +1 -0
  27. package/dist/assets/{clear-button-C97JtAez.js → clear-button-GAjXl0CQ.js} +1 -1
  28. package/dist/assets/clone-DSDb0xen.js +1 -0
  29. package/dist/assets/command-palette-BUXkqoLh.js +1 -0
  30. package/dist/assets/{common-Du9rSOwD.js → common-DahoYqdi.js} +1 -1
  31. package/dist/assets/{compile-CZXqyOxa.js → compile-Bg8uJ7vm.js} +1 -1
  32. package/dist/assets/{cose-bilkent-S5V4N54A-CqUN5Y9b.js → cose-bilkent-S5V4N54A-z_0gqD9K.js} +1 -1
  33. package/dist/assets/{dagre-5GWH7T2D-RJqTI9DM.js → dagre-5GWH7T2D-BMt7CNXL.js} +1 -1
  34. package/dist/assets/{data-grid-overlay-editor-DZN0q1LV.js → data-grid-overlay-editor-Ctn4XtXx.js} +1 -1
  35. package/dist/assets/datasources-panel-C7sqRIHs.js +1 -0
  36. package/dist/assets/{dependency-graph-panel-CEog_O7V.js → dependency-graph-panel-DNajptzv.js} +4 -4
  37. package/dist/assets/{diagram-N5W7TBWH-D-l4zZ9d.js → diagram-N5W7TBWH-BzwvLvAy.js} +1 -1
  38. package/dist/assets/{diagram-QEK2KX5R-CCOmBUt-.js → diagram-QEK2KX5R-DRLJ56FS.js} +1 -1
  39. package/dist/assets/{diagram-S2PKOQOG-C_I_9jnZ.js → diagram-S2PKOQOG-Bf8x4KTU.js} +1 -1
  40. package/dist/assets/{documentation-panel-C1BtMZ3M.js → documentation-panel-Dm6Ozl67.js} +1 -1
  41. package/dist/assets/{edit-page-B-oevUZ9.js → edit-page-CGc9EjuG.js} +53 -42
  42. package/dist/assets/{ellipsis-vertical-BEb-J8z6.js → ellipsis-vertical-Bj1YXvZe.js} +1 -1
  43. package/dist/assets/{empty-state-C99UyDE3.js → empty-state-CYev-D31.js} +1 -1
  44. package/dist/assets/{erDiagram-AWTI2OKA-BePOLi5M.js → erDiagram-AWTI2OKA-DmgzgN_I.js} +1 -1
  45. package/dist/assets/{error-panel-Bs34jXFh.js → error-panel-BYG4twCa.js} +1 -1
  46. package/dist/assets/{file-explorer-panel-Ck6UL861.js → file-explorer-panel-BSMiOApi.js} +1 -1
  47. package/dist/assets/{flowDiagram-PVAE7QVJ-BgjFu5l7.js → flowDiagram-PVAE7QVJ-BdRKkajr.js} +1 -1
  48. package/dist/assets/{ganttDiagram-OWAHRB6G-YOPb3XSV.js → ganttDiagram-OWAHRB6G-lfRAMnq_.js} +5 -5
  49. package/dist/assets/{gitGraphDiagram-NY62KEGX-CGhqaDTy.js → gitGraphDiagram-NY62KEGX-CQVTIrHF.js} +1 -1
  50. package/dist/assets/{glide-data-editor-9QUH6iso.js → glide-data-editor-D5A4pou7.js} +4 -4
  51. package/dist/assets/{graph-DQQFGrho.js → graph-CBNo279v.js} +1 -1
  52. package/dist/assets/{home-page-DRKpPCrF.js → home-page-CmdznBJR.js} +2 -2
  53. package/dist/assets/{index-SGLNXrGP.js → index-0dfGh-Gj.js} +1 -1
  54. package/dist/assets/{index-aE43R74q.js → index-BDYVSSzB.js} +1 -1
  55. package/dist/assets/{index-BJNCMUmG.js → index-B_KyDZ94.js} +1 -1
  56. package/dist/assets/{index-BjgnbONl.js → index-Bfy-I_lW.js} +1 -1
  57. package/dist/assets/{index-CUFv_thQ.js → index-Bh98Tp-z.js} +1 -1
  58. package/dist/assets/{index-C2MD0vgD.js → index-BhroIwBL.js} +1 -1
  59. package/dist/assets/{index-DdnKZNxM.js → index-BtQtesaI.js} +1 -1
  60. package/dist/assets/index-C0iXCvyY.css +1 -0
  61. package/dist/assets/index-C1SHFMCp.js +581 -0
  62. package/dist/assets/{index-C_tkBKNO.js → index-C6DWtSls.js} +1 -1
  63. package/dist/assets/{index-Aeo6WiK7.js → index-C71cdkH-.js} +1 -1
  64. package/dist/assets/{index-G5QZppK2.js → index-CT_FTqvK.js} +1 -1
  65. package/dist/assets/{index-B8jXZ12t.js → index-CU5rRr66.js} +1 -1
  66. package/dist/assets/{index-BAbIIxHU.js → index-Cb6duXQm.js} +1 -1
  67. package/dist/assets/{index-BW3k9Gss.js → index-D23e9zQj.js} +1 -1
  68. package/dist/assets/index-DUGecC2Z.js +68 -0
  69. package/dist/assets/{index-CFKO7WXI.js → index-DcGIOAQi.js} +1 -1
  70. package/dist/assets/{index-ClzeQrN7.js → index-PJfa9qXY.js} +1 -1
  71. package/dist/assets/{index-C1ez98sk.js → index-SPslPC2B.js} +1 -1
  72. package/dist/assets/{index-BprjMYH5.js → index-VPQlo4Uz.js} +1 -1
  73. package/dist/assets/{index-CfaDbEdi.js → index-qbTLKWyG.js} +1 -1
  74. package/dist/assets/infoDiagram-STP46IZ2-DBu8p9gd.js +2 -0
  75. package/dist/assets/{isEmpty-D-4c7sMv.js → isEmpty-CnOLuQIv.js} +1 -1
  76. package/dist/assets/{journeyDiagram-BIP6EPQ6-C94u3Mv3.js → journeyDiagram-BIP6EPQ6-6U_vHJBH.js} +1 -1
  77. package/dist/assets/{kanban-definition-6OIFK2YF-BEXYFzz7.js → kanban-definition-6OIFK2YF-DgnR14ys.js} +1 -1
  78. package/dist/assets/{layout-Bz2BJ2ru.js → layout-RHmq4fP9.js} +1 -1
  79. package/dist/assets/{linear-D8s7K76e.js → linear-CLdOVPGV.js} +1 -1
  80. package/dist/assets/links-Dd1icsEk.js +7 -0
  81. package/dist/assets/{logs-panel-DC7wpmPz.js → logs-panel-CjbuhBLx.js} +1 -1
  82. package/dist/assets/{markdown-renderer-DRdSWR9X.js → markdown-renderer-X5YJvAZq.js} +3 -3
  83. package/dist/assets/{mermaid-Y3x4hmD0.js → mermaid-Bl2T5oEC.js} +1 -1
  84. package/dist/assets/{mermaid.core-DzthE35Y.js → mermaid.core-CfukBvGI.js} +4 -4
  85. package/dist/assets/min-BXIes1Za.js +1 -0
  86. package/dist/assets/{mindmap-definition-Q6HEUPPD-DktvuLe1.js → mindmap-definition-Q6HEUPPD-BXCjP4Lu.js} +1 -1
  87. package/dist/assets/{number-overlay-editor-BEfwI1IT.js → number-overlay-editor-BUyqkSes.js} +1 -1
  88. package/dist/assets/{outline-panel-CdsnAy2w.js → outline-panel-BvGcPKdd.js} +1 -1
  89. package/dist/assets/{packages-panel-DiTA-d_D.js → packages-panel-BichDQWG.js} +1 -1
  90. package/dist/assets/{pieDiagram-ADFJNKIX-DQDNQ-de.js → pieDiagram-ADFJNKIX-CMzJFIJM.js} +1 -1
  91. package/dist/assets/{quadrantDiagram-LMRXKWRM-0kgIXc2-.js → quadrantDiagram-LMRXKWRM-CfGssUlO.js} +1 -1
  92. package/dist/assets/{react-plotly-DJqqfM7c.js → react-plotly-DR3hV0HW.js} +1 -1
  93. package/dist/assets/{requirementDiagram-4UW4RH46-B5rb0ypd.js → requirementDiagram-4UW4RH46-CfrFolth.js} +1 -1
  94. package/dist/assets/{run-page-CFmLrv1R.js → run-page-Bqd_4ePD.js} +1 -1
  95. package/dist/assets/{sankeyDiagram-GR3RE2ED-Dom7IlnF.js → sankeyDiagram-GR3RE2ED-D_UttKU0.js} +1 -1
  96. package/dist/assets/{scratchpad-panel-CuHWpHO8.js → scratchpad-panel-D5N15ji1.js} +1 -1
  97. package/dist/assets/secrets-panel-BpbnAO4R.js +1 -0
  98. package/dist/assets/{sequenceDiagram-C3RYC4MD-PNJWXQbw.js → sequenceDiagram-C3RYC4MD-MdfQQApP.js} +1 -1
  99. package/dist/assets/{slides-component-CJgaTRZ0.js → slides-component-C0z7rXmk.js} +1 -1
  100. package/dist/assets/{snippets-panel-B2EC1txM.js → snippets-panel-wlpZ_Wzx.js} +1 -1
  101. package/dist/assets/sortBy-BW_zNHP6.js +1 -0
  102. package/dist/assets/{state-CWict9RU.js → state-CDooX-dk.js} +1 -1
  103. package/dist/assets/{stateDiagram-KXAO66HF-BE58aJnr.js → stateDiagram-KXAO66HF-H7kfw3ot.js} +1 -1
  104. package/dist/assets/stateDiagram-v2-UMBNRL4Z-YMeb9qMR.js +1 -0
  105. package/dist/assets/{storage-DRaR04wR.js → storage-b1QCapTq.js} +6 -6
  106. package/dist/assets/{terminal-BX3Su5q7.js → terminal-CPV44BXz.js} +1 -1
  107. package/dist/assets/{time-hUzZfpNE.js → time-DDy3xv5Y.js} +1 -1
  108. package/dist/assets/{timeline-definition-XQNQX7LJ-CqQP9t51.js → timeline-definition-XQNQX7LJ-J-cPRT2_.js} +1 -1
  109. package/dist/assets/{tracing-B10Q1n-L.js → tracing-3eHHRUiJ.js} +2 -2
  110. package/dist/assets/{tracing-panel-Du8WCnno.js → tracing-panel-BMgy3D7d.js} +2 -2
  111. package/dist/assets/{trash-B81GTiv6.js → trash--tonOuDe.js} +1 -1
  112. package/dist/assets/{tree-6vW2ogkh.js → tree-ouIGEsVg.js} +1 -1
  113. package/dist/assets/{treemap-75Q7IDZK-CdwDwwsz.js → treemap-75Q7IDZK-CzJTJ_3R.js} +20 -20
  114. package/dist/assets/{variable-panel-D5qgJI7k.js → variable-panel-sFTn4Oih.js} +1 -1
  115. package/dist/assets/{vega-component-DJaJWMJM.js → vega-component-BkPkzX9r.js} +1 -1
  116. package/dist/assets/{xychartDiagram-6GGTOJPD-WFtXqaM9.js → xychartDiagram-6GGTOJPD-BZ8WOb_8.js} +1 -1
  117. package/dist/index.html +10 -3
  118. package/package.json +8 -8
  119. package/src/__mocks__/common.ts +5 -3
  120. package/src/__mocks__/notebook.ts +2 -2
  121. package/src/__mocks__/requests.ts +1 -0
  122. package/src/__tests__/main.test.tsx +2 -2
  123. package/src/components/ai/ai-provider-icon.tsx +2 -0
  124. package/src/components/app-config/ai-config.tsx +32 -1
  125. package/src/components/app-config/common.tsx +2 -2
  126. package/src/components/app-config/user-config-form.tsx +26 -0
  127. package/src/components/audio/audio-recorder.tsx +0 -1
  128. package/src/components/chat/acp/blocks.tsx +2 -2
  129. package/src/components/chat/acp/thread.tsx +3 -5
  130. package/src/components/chat/acp/utils.ts +5 -5
  131. package/src/components/chat/chat-panel.tsx +1 -1
  132. package/src/components/data-table/__tests__/columns.test.tsx +38 -0
  133. package/src/components/data-table/__tests__/data-table.test.tsx +2 -2
  134. package/src/components/data-table/cell-hover-template/feature.ts +1 -1
  135. package/src/components/data-table/cell-hover-template/types.ts +1 -1
  136. package/src/components/data-table/charts/__tests__/altair-generator.test.ts +1 -1
  137. package/src/components/data-table/charts/chart-spec/tooltips.ts +3 -3
  138. package/src/components/data-table/charts/components/chart-items.tsx +1 -1
  139. package/src/components/data-table/charts/components/form-fields.tsx +2 -2
  140. package/src/components/data-table/charts/constants.ts +1 -1
  141. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  142. package/src/components/data-table/column-summary/chart-spec-model.tsx +2 -2
  143. package/src/components/data-table/columns.tsx +22 -3
  144. package/src/components/data-table/data-table.tsx +35 -3
  145. package/src/components/data-table/date-popover.tsx +1 -1
  146. package/src/components/data-table/download-actions.tsx +1 -1
  147. package/src/components/data-table/range-focus/__tests__/utils.test.ts +5 -5
  148. package/src/components/data-table/renderers.tsx +22 -13
  149. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +1 -1
  150. package/src/components/data-table/schemas.ts +16 -0
  151. package/src/components/data-table/types.ts +4 -3
  152. package/src/components/datasources/column-preview.tsx +9 -6
  153. package/src/components/debugger/debugger-code.tsx +1 -1
  154. package/src/components/dependency-graph/custom-node.tsx +15 -6
  155. package/src/components/dependency-graph/dependency-graph-minimap.tsx +2 -2
  156. package/src/components/dependency-graph/dependency-graph-tree.tsx +2 -2
  157. package/src/components/dependency-graph/dependency-graph.tsx +1 -1
  158. package/src/components/dependency-graph/elements.ts +7 -7
  159. package/src/components/dependency-graph/utils/changes.ts +4 -4
  160. package/src/components/editor/Cell.tsx +7 -1
  161. package/src/components/editor/ai/transport/chat-transport.tsx +1 -1
  162. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +1 -1
  163. package/src/components/editor/chrome/panels/packages-panel.tsx +1 -1
  164. package/src/components/editor/columns/storage.ts +1 -1
  165. package/src/components/editor/database/__tests__/__snapshots__/as-code.test.ts.snap +36 -0
  166. package/src/components/editor/database/__tests__/as-code.test.ts +30 -7
  167. package/src/components/editor/database/add-database-form.tsx +11 -0
  168. package/src/components/editor/database/as-code.ts +104 -5
  169. package/src/components/editor/database/schemas.ts +36 -18
  170. package/src/components/editor/errors/auto-fix.tsx +12 -2
  171. package/src/components/editor/errors/sql-validation-errors.tsx +40 -0
  172. package/src/components/editor/navigation/clipboard.ts +2 -2
  173. package/src/components/editor/output/ConsoleOutput.tsx +14 -2
  174. package/src/components/editor/output/JsonOutput.tsx +1 -1
  175. package/src/components/editor/output/MarimoErrorOutput.tsx +60 -1
  176. package/src/components/editor/output/MarimoTracebackOutput.tsx +17 -2
  177. package/src/components/editor/renderers/grid-layout/types.ts +2 -2
  178. package/src/components/editor/renderers/plugins.ts +1 -1
  179. package/src/components/editor/renderers/types.ts +1 -1
  180. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +7 -7
  181. package/src/components/forms/form.tsx +5 -5
  182. package/src/components/ui/links.tsx +1 -0
  183. package/src/core/ai/__tests__/model-registry.test.ts +0 -10
  184. package/src/core/ai/context/providers/cell-output.ts +1 -18
  185. package/src/core/ai/context/providers/error.ts +2 -2
  186. package/src/core/ai/ids/ids.ts +1 -0
  187. package/src/core/ai/model-registry.ts +2 -1
  188. package/src/core/cells/cells.ts +5 -5
  189. package/src/core/cells/logs.ts +1 -1
  190. package/src/core/cells/types.ts +1 -1
  191. package/src/core/codemirror/__tests__/format.test.ts +6 -0
  192. package/src/core/codemirror/cells/traceback-decorations.ts +1 -1
  193. package/src/core/codemirror/editing/commands.ts +2 -2
  194. package/src/core/codemirror/find-replace/navigate.ts +1 -1
  195. package/src/core/codemirror/language/__tests__/extension.test.ts +24 -0
  196. package/src/core/codemirror/language/__tests__/sql-validation.test.ts +133 -0
  197. package/src/core/codemirror/language/__tests__/sql.test.ts +764 -79
  198. package/src/core/codemirror/language/languages/markdown.ts +4 -1
  199. package/src/core/codemirror/language/languages/sql/banner-validation-errors.ts +85 -0
  200. package/src/core/codemirror/language/languages/sql/completion-builder.ts +160 -0
  201. package/src/core/codemirror/language/languages/sql/completion-sources.tsx +9 -3
  202. package/src/core/codemirror/language/languages/sql/completion-store.ts +46 -50
  203. package/src/core/codemirror/language/languages/sql/renderers.tsx +485 -0
  204. package/src/core/codemirror/language/languages/sql/sql-mode.ts +20 -0
  205. package/src/core/codemirror/language/languages/sql/sql.ts +218 -4
  206. package/src/core/codemirror/language/languages/sql/utils.ts +4 -1
  207. package/src/core/codemirror/language/panel/panel.tsx +8 -2
  208. package/src/core/codemirror/language/panel/sql.tsx +86 -4
  209. package/src/core/codemirror/language/utils/ast.ts +3 -3
  210. package/src/core/codemirror/lsp/federated-lsp.ts +4 -4
  211. package/src/core/codemirror/lsp/lens.ts +4 -4
  212. package/src/core/codemirror/lsp/notebook-lsp.ts +1 -1
  213. package/src/core/codemirror/lsp/types.ts +1 -1
  214. package/src/core/codemirror/markdown/completions.ts +1 -1
  215. package/src/core/codemirror/reactive-references/analyzer.ts +2 -2
  216. package/src/core/codemirror/rtc/loro/awareness.ts +1 -1
  217. package/src/core/config/config-schema.ts +1 -0
  218. package/src/core/config/feature-flag.tsx +6 -2
  219. package/src/core/datasets/request-registry.ts +24 -1
  220. package/src/core/dom/events.ts +1 -1
  221. package/src/core/dom/outline.ts +2 -2
  222. package/src/core/dom/uiregistry.ts +2 -8
  223. package/src/core/errors/__tests__/errors.test.ts +22 -4
  224. package/src/core/errors/errors.ts +29 -1
  225. package/src/core/errors/state.ts +1 -1
  226. package/src/core/islands/bridge.ts +1 -0
  227. package/src/core/islands/main.ts +3 -2
  228. package/src/core/islands/parse.ts +1 -3
  229. package/src/core/kernel/messages.ts +2 -1
  230. package/src/core/network/CachingRequestRegistry.ts +74 -0
  231. package/src/core/network/DeferredRequestRegistry.ts +3 -1
  232. package/src/core/network/__tests__/CachingRequestRegistry.test.ts +73 -0
  233. package/src/core/network/requests-network.ts +7 -0
  234. package/src/core/network/requests-static.ts +1 -0
  235. package/src/core/network/requests-toasting.ts +1 -0
  236. package/src/core/network/types.ts +3 -1
  237. package/src/core/variables/state.ts +2 -2
  238. package/src/core/wasm/__tests__/state.test.ts +1 -1
  239. package/src/core/wasm/bridge.ts +5 -0
  240. package/src/core/websocket/useMarimoWebSocket.tsx +9 -2
  241. package/src/custom.d.ts +1 -1
  242. package/src/hooks/useCellRenderCount.ts +1 -0
  243. package/src/hooks/useResizeHandle.ts +4 -1
  244. package/src/plugins/core/RenderHTML.tsx +1 -2
  245. package/src/plugins/core/registerReactComponent.tsx +23 -19
  246. package/src/plugins/impl/DataTablePlugin.tsx +18 -6
  247. package/src/plugins/impl/FileUploadPlugin.tsx +1 -1
  248. package/src/plugins/impl/RefreshPlugin.tsx +1 -1
  249. package/src/plugins/impl/SliderPlugin.tsx +4 -0
  250. package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +27 -9
  251. package/src/plugins/impl/anywidget/__tests__/AnyWidgetPlugin.test.tsx +58 -2
  252. package/src/plugins/impl/anywidget/__tests__/model.test.ts +3 -4
  253. package/src/plugins/impl/anywidget/model.ts +2 -3
  254. package/src/plugins/impl/data-editor/types.ts +1 -1
  255. package/src/plugins/impl/data-explorer/components/query-form.tsx +1 -1
  256. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +17 -5
  257. package/src/plugins/impl/data-frames/types.ts +1 -1
  258. package/src/plugins/impl/panel/PanelPlugin.tsx +2 -2
  259. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +3 -3
  260. package/src/plugins/impl/vega/__tests__/loader.test.ts +2 -2
  261. package/src/plugins/impl/vega/loader.ts +1 -1
  262. package/src/plugins/impl/vega/vega-component.tsx +1 -1
  263. package/src/plugins/impl/vega/vega-loader.ts +2 -2
  264. package/src/plugins/layout/NavigationMenuPlugin.tsx +1 -1
  265. package/src/plugins/layout/RoutesPlugin.tsx +1 -2
  266. package/src/plugins/plugins.ts +2 -2
  267. package/src/stories/dataframe.stories.tsx +2 -0
  268. package/src/utils/Logger.ts +1 -1
  269. package/src/utils/__tests__/data-views.test.ts +30 -68
  270. package/src/utils/__tests__/dom.test.ts +167 -0
  271. package/src/utils/__tests__/id-tree.test.ts +49 -1
  272. package/src/utils/__tests__/storage.test.ts +1 -1
  273. package/src/utils/__tests__/traceback.test.ts +13 -2
  274. package/src/utils/arrays.ts +1 -1
  275. package/src/utils/createReducer.ts +1 -5
  276. package/src/utils/data-views.ts +6 -19
  277. package/src/utils/dom.ts +55 -0
  278. package/src/utils/edit-distance.ts +1 -1
  279. package/src/utils/fileToBase64.ts +1 -1
  280. package/src/utils/id-tree.tsx +20 -18
  281. package/src/utils/json/base64.ts +13 -0
  282. package/src/utils/json/json-parser.ts +2 -2
  283. package/src/utils/lru.ts +4 -0
  284. package/src/utils/mergeRefs.ts +1 -1
  285. package/src/utils/objects.ts +3 -3
  286. package/src/utils/pluralize.ts +1 -1
  287. package/src/utils/routes.ts +2 -2
  288. package/src/utils/sets.ts +1 -1
  289. package/src/utils/traceback.ts +45 -15
  290. package/src/utils/tracer.ts +11 -9
  291. package/dist/assets/_baseEach-CvTX9w0Y.js +0 -1
  292. package/dist/assets/_baseMap-CtlwA90f.js +0 -1
  293. package/dist/assets/_baseUniq-BKktIGQ1.js +0 -1
  294. package/dist/assets/channel-Co6iMgWq.js +0 -1
  295. package/dist/assets/chat-panel-9alr8FS4.js +0 -3
  296. package/dist/assets/classDiagram-KNZD7YFC-BbJ0rY3y.js +0 -1
  297. package/dist/assets/classDiagram-v2-RKCZMP56-BbJ0rY3y.js +0 -1
  298. package/dist/assets/clone-BMP0PsTa.js +0 -1
  299. package/dist/assets/command-palette-B93Pjcky.js +0 -1
  300. package/dist/assets/datasources-panel-v7H3cR0p.js +0 -1
  301. package/dist/assets/index-2252nrk6.js +0 -68
  302. package/dist/assets/index-C7CoaNFb.js +0 -578
  303. package/dist/assets/index-DadI618h.css +0 -1
  304. package/dist/assets/infoDiagram-STP46IZ2-CJLOpSAf.js +0 -2
  305. package/dist/assets/links-BpXlz1GG.js +0 -7
  306. package/dist/assets/min-BBO3-1Hg.js +0 -1
  307. package/dist/assets/secrets-panel-CfHc5YD0.js +0 -1
  308. package/dist/assets/sortBy-DZnlX29-.js +0 -1
  309. package/dist/assets/stateDiagram-v2-UMBNRL4Z-CdThjimL.js +0 -1
  310. package/src/__tests__/lru.test.ts +0 -74
@@ -1,6 +1,10 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
- import { NotebookPenIcon, SquareArrowOutUpRightIcon } from "lucide-react";
3
+ import {
4
+ InfoIcon,
5
+ NotebookPenIcon,
6
+ SquareArrowOutUpRightIcon,
7
+ } from "lucide-react";
4
8
  import { Fragment, type JSX } from "react";
5
9
  import {
6
10
  Accordion,
@@ -72,6 +76,8 @@ export const MarimoErrorOutput = ({
72
76
  titleContents = "Ancestor stopped";
73
77
  alertVariant = "default";
74
78
  titleColor = "text-secondary-foreground";
79
+ } else if (errors.some((e) => e.type === "sql-error")) {
80
+ titleContents = "SQL error";
75
81
  } else {
76
82
  // Check for exception type
77
83
  const exceptionError = errors.find((e) => e.type === "exception");
@@ -126,6 +132,10 @@ export const MarimoErrorOutput = ({
126
132
  const unknownErrors = errors.filter(
127
133
  (e): e is Extract<MarimoError, { type: "unknown" }> => e.type === "unknown",
128
134
  );
135
+ const sqlErrors = errors.filter(
136
+ (e): e is Extract<MarimoError, { type: "sql-error" }> =>
137
+ e.type === "sql-error",
138
+ );
129
139
 
130
140
  const openScratchpad = () => {
131
141
  chromeActions.openApplication("scratchpad");
@@ -485,6 +495,55 @@ export const MarimoErrorOutput = ({
485
495
  );
486
496
  }
487
497
 
498
+ if (sqlErrors.length > 0) {
499
+ messages.push(
500
+ <div key="sql-errors">
501
+ {sqlErrors.map((error, idx) => {
502
+ const line =
503
+ error.sql_line == null ? null : Math.trunc(error?.sql_line) + 1;
504
+ const col =
505
+ error.sql_col == null ? null : Math.trunc(error?.sql_col) + 1;
506
+ return (
507
+ <div key={`sql-error-${idx}`} className="space-y-2 mt-2">
508
+ <p className="text-muted-foreground font-medium">{error.msg}</p>
509
+ {error.hint && (
510
+ <div className="flex items-start gap-2">
511
+ <InfoIcon
512
+ size={11}
513
+ className="text-muted-foreground mt-1 flex-shrink-0"
514
+ />
515
+ <p className="whitespace-pre-wrap text-sm text-muted-foreground">
516
+ {error.hint}
517
+ </p>
518
+ </div>
519
+ )}
520
+ {error.sql_statement && (
521
+ <pre
522
+ lang="sql"
523
+ className="text-xs bg-muted/80 rounded whitespace-pre-wrap p-3.5"
524
+ >
525
+ {error.sql_statement.trim()}
526
+ </pre>
527
+ )}
528
+ {line !== null && col !== null && (
529
+ <p className="text-xs text-muted-foreground">
530
+ Error at line {line}, column {col}
531
+ </p>
532
+ )}
533
+ </div>
534
+ );
535
+ })}
536
+ {cellId && (
537
+ <AutoFixButton
538
+ errors={sqlErrors}
539
+ cellId={cellId}
540
+ className="mt-2.5"
541
+ />
542
+ )}
543
+ </div>,
544
+ );
545
+ }
546
+
488
547
  return messages;
489
548
  };
490
549
 
@@ -108,7 +108,7 @@ export const MarimoTracebackOutput = ({
108
108
  Fix with AI
109
109
  </Button>
110
110
  )}
111
- {tracebackInfo && !isWasm() && (
111
+ {tracebackInfo && tracebackInfo.kind === "cell" && !isWasm() && (
112
112
  <Tooltip content={"Attach pdb to the exception point."}>
113
113
  <Button
114
114
  size="xs"
@@ -180,7 +180,7 @@ function lastLine(text: string): string {
180
180
 
181
181
  export const replaceTracebackFilenames = (domNode: DOMNode) => {
182
182
  const info = getTracebackInfo(domNode);
183
- if (info) {
183
+ if (info?.kind === "cell") {
184
184
  const tooltipContent = <InsertBreakpointContent />;
185
185
  return (
186
186
  <span className="nb">
@@ -211,6 +211,21 @@ export const replaceTracebackFilenames = (domNode: DOMNode) => {
211
211
  </span>
212
212
  );
213
213
  }
214
+ if (info?.kind === "file") {
215
+ return (
216
+ <div
217
+ className="inline-block cursor-pointer text-destructive hover:underline"
218
+ onClick={(_) => {
219
+ getRequestClient().openFile({
220
+ path: info.filePath,
221
+ lineNumber: info.lineNumber,
222
+ });
223
+ }}
224
+ >
225
+ <span className="nb">"{info.filePath}"</span>
226
+ </div>
227
+ );
228
+ }
214
229
  };
215
230
 
216
231
  export const replaceTracebackPrefix = (domNode: DOMNode) => {
@@ -68,13 +68,13 @@ export interface GridLayout extends Omit<SerializedGridLayout, "cells"> {
68
68
  /**
69
69
  * The cells in the layout.
70
70
  */
71
- cells: Array<{
71
+ cells: {
72
72
  i: string;
73
73
  x: number;
74
74
  y: number;
75
75
  w: number;
76
76
  h: number;
77
- }>;
77
+ }[];
78
78
 
79
79
  scrollableCells: Set<CellId>;
80
80
 
@@ -8,7 +8,7 @@ import { VerticalLayoutPlugin } from "./vertical-layout/vertical-layout";
8
8
 
9
9
  // If more renderers are added, we may want to consider lazy loading them.
10
10
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
- export const cellRendererPlugins: Array<ICellRendererPlugin<any, any>> = [
11
+ export const cellRendererPlugins: ICellRendererPlugin<any, any>[] = [
12
12
  GridLayoutPlugin,
13
13
  SlidesLayoutPlugin,
14
14
  VerticalLayoutPlugin,
@@ -17,7 +17,7 @@ export interface ICellRendererProps<L> {
17
17
  /**
18
18
  * The cells to render.
19
19
  */
20
- cells: Array<CellRuntimeState & CellData>;
20
+ cells: (CellRuntimeState & CellData)[];
21
21
 
22
22
  /**
23
23
  * The layout configuration.
@@ -198,7 +198,7 @@ const ActionButtons: React.FC<{
198
198
  actions.push(
199
199
  <DropdownMenuItem
200
200
  onSelect={onToggleShowCode}
201
- id="notebook-action-show-code"
201
+ data-testid="notebook-action-show-code"
202
202
  key="show-code"
203
203
  >
204
204
  <Code2Icon className="mr-2" size={14} strokeWidth={1.5} />
@@ -213,7 +213,7 @@ const ActionButtons: React.FC<{
213
213
  actions.push(
214
214
  <DropdownMenuItem
215
215
  onSelect={handleDownloadAsHTML}
216
- id="notebook-action-download-html"
216
+ data-testid="notebook-action-download-html"
217
217
  key="download-html"
218
218
  >
219
219
  <FolderDownIcon className="mr-2" size={14} strokeWidth={1.5} />
@@ -222,7 +222,7 @@ const ActionButtons: React.FC<{
222
222
  <DropdownMenuSeparator key="download-html-separator" />,
223
223
  <DropdownMenuItem
224
224
  onSelect={handleDownloadAsPNG}
225
- id="notebook-action-download-png"
225
+ data-testid="notebook-action-download-png"
226
226
  key="download-png"
227
227
  >
228
228
  <ImageIcon className="mr-2" size={14} strokeWidth={1.5} />
@@ -239,7 +239,7 @@ const ActionButtons: React.FC<{
239
239
  // as this may be used in custom css to hide/show the actions dropdown
240
240
  return (
241
241
  <div
242
- id="notebook-actions-dropdown"
242
+ data-testid="notebook-actions-dropdown"
243
243
  className={cn(
244
244
  "right-0 top-0 z-50 m-4 no-print flex gap-2 print:hidden",
245
245
  // If the notebook is static, we have a banner at the top, so
@@ -419,10 +419,10 @@ export const VerticalLayoutPlugin: ICellRendererPlugin<
419
419
  };
420
420
 
421
421
  export function groupCellsByColumn(
422
- cells: Array<CellRuntimeState & CellData>,
423
- ): Array<[number, Array<CellRuntimeState & CellData>]> {
422
+ cells: (CellRuntimeState & CellData)[],
423
+ ): [number, (CellRuntimeState & CellData)[]][] {
424
424
  // Group cells by column
425
- const cellsByColumn = new Map<number, Array<CellRuntimeState & CellData>>();
425
+ const cellsByColumn = new Map<number, (CellRuntimeState & CellData)[]>();
426
426
  let lastSeenColumn = 0;
427
427
  cells.forEach((cell) => {
428
428
  const column = cell.config.column ?? lastSeenColumn;
@@ -59,7 +59,7 @@ interface Props<T extends FieldValues> {
59
59
  form: UseFormReturn<T>;
60
60
  schema: z.ZodType;
61
61
  path?: Path<T>;
62
- renderers: Array<FormRenderer<T>> | undefined;
62
+ renderers: FormRenderer<T>[] | undefined;
63
63
  children?: React.ReactNode;
64
64
  }
65
65
 
@@ -82,7 +82,7 @@ export function renderZodSchema<T extends FieldValues, S>(
82
82
  schema: z.ZodType<S>,
83
83
  form: UseFormReturn<T>,
84
84
  path: Path<T>,
85
- renderers: Array<FormRenderer<T>>,
85
+ renderers: FormRenderer<T>[],
86
86
  ) {
87
87
  // Try custom renderers first
88
88
  for (const renderer of renderers) {
@@ -330,7 +330,7 @@ export function renderZodSchema<T extends FieldValues, S>(
330
330
  }
331
331
 
332
332
  if (schema instanceof z.ZodDiscriminatedUnion) {
333
- const options = schema._def.options as Array<z.ZodType<unknown>>;
333
+ const options = schema._def.options as z.ZodType<unknown>[];
334
334
  const discriminator = schema._def.discriminator;
335
335
  const optionsMap = schema._def.optionsMap;
336
336
  return (
@@ -392,7 +392,7 @@ export function renderZodSchema<T extends FieldValues, S>(
392
392
  control={form.control}
393
393
  name={path}
394
394
  render={({ field }) => {
395
- const options = schema._def.options as Array<z.ZodType<unknown>>;
395
+ const options = schema._def.options as z.ZodType<unknown>[];
396
396
  let value: string = field.value;
397
397
  const types = options.map((option) => {
398
398
  return getUnionLiteral(option)._def.value;
@@ -812,6 +812,6 @@ const MultiSelectFormField = ({
812
812
  );
813
813
  };
814
814
 
815
- function joinPath<T>(...parts: Array<string | number>): Path<T> {
815
+ function joinPath<T>(...parts: (string | number)[]): Path<T> {
816
816
  return parts.filter((part) => part !== "").join(".") as Path<T>;
817
817
  }
@@ -8,6 +8,7 @@ export const ExternalLink = ({
8
8
  | `https://console.anthropic.com/${string}`
9
9
  | `https://aistudio.google.com/${string}`
10
10
  | `https://github.com/${string}`
11
+ | `https://openrouter.ai/${string}`
11
12
  | `https://docs.marimo.io/${string}`
12
13
  | `https://docs.python.org/${string}`
13
14
  | `https://marimo.io/${string}`
@@ -250,16 +250,6 @@ describe("AiModelRegistry", () => {
250
250
  const providers = listModelsByProvider.map(([provider]) => provider);
251
251
  expect(providers).toEqual(PROVIDER_SORT_ORDER);
252
252
  });
253
-
254
- it("should include custom providers at the top", () => {
255
- const customModels = ["openrouter/custom-gpt"];
256
- const registry = AiModelRegistry.create({ customModels });
257
- const listModelsByProvider = registry.getListModelsByProvider();
258
- expect(listModelsByProvider).toHaveLength(4);
259
-
260
- const providers = listModelsByProvider.map(([provider]) => provider);
261
- expect(providers).toEqual(["openrouter", ...PROVIDER_SORT_ORDER]);
262
- });
263
253
  });
264
254
 
265
255
  describe("getCustomModels", () => {
@@ -9,6 +9,7 @@ import { displayCellName } from "@/core/cells/names";
9
9
  import { isOutputEmpty } from "@/core/cells/outputs";
10
10
  import type { OutputMessage } from "@/core/kernel/messages";
11
11
  import type { JotaiStore } from "@/core/state/jotai";
12
+ import { parseHtmlContent } from "@/utils/dom";
12
13
  import { Logger } from "@/utils/Logger";
13
14
  import { type AIContextItem, AIContextProvider } from "../registry";
14
15
  import { contextToXml } from "../utils";
@@ -64,24 +65,6 @@ function isMediaMimetype(
64
65
  return false;
65
66
  }
66
67
 
67
- function parseHtmlContent(htmlString: string): string {
68
- try {
69
- // Create a temporary DOM element to parse HTML
70
- const tempDiv = document.createElement("div");
71
- tempDiv.innerHTML = htmlString;
72
-
73
- // Extract text content, removing HTML tags
74
- const textContent = tempDiv.textContent || tempDiv.innerText || "";
75
-
76
- // Clean up extra whitespace
77
- return textContent.replaceAll(/\s+/g, " ").trim();
78
- } catch (error) {
79
- Logger.error("Error parsing HTML content:", error);
80
- // If parsing fails, return the original string
81
- return htmlString;
82
- }
83
- }
84
-
85
68
  export class CellOutputContextProvider extends AIContextProvider<CellOutputContextItem> {
86
69
  readonly title = "Cell Outputs";
87
70
  readonly mentionPrefix = "@";
@@ -14,11 +14,11 @@ export interface ErrorContextItem extends AIContextItem {
14
14
  type: "error";
15
15
  data: {
16
16
  type: "all-errors";
17
- errors: Array<{
17
+ errors: {
18
18
  cellId: CellId;
19
19
  cellName: string;
20
20
  errorData: MarimoError[];
21
- }>;
21
+ }[];
22
22
  };
23
23
  }
24
24
 
@@ -11,6 +11,7 @@ export const PROVIDERS = [
11
11
  "deepseek",
12
12
  "azure",
13
13
  "github",
14
+ "openrouter",
14
15
  "marimo",
15
16
  ] as const;
16
17
  export type ProviderId = (typeof PROVIDERS)[number];
@@ -19,6 +19,7 @@ export const PROVIDER_SORT_ORDER: ProviderId[] = [
19
19
  "openai",
20
20
  "google",
21
21
  "github",
22
+ "openrouter",
22
23
  "deepseek",
23
24
  "azure",
24
25
  "bedrock",
@@ -201,7 +202,7 @@ export class AiModelRegistry {
201
202
  return this.modelsByProviderMap;
202
203
  }
203
204
 
204
- getListModelsByProvider(): Array<[ProviderId, AiModel[]]> {
205
+ getListModelsByProvider(): [ProviderId, AiModel[]][] {
205
206
  const modelsByProvider = this.getGroupedModelsByProvider();
206
207
  const arrayModels = [...modelsByProvider.entries()];
207
208
  const providerToOrderIdx = getProviderMap().providerToOrderIdx;
@@ -84,14 +84,14 @@ export interface NotebookState {
84
84
  *
85
85
  * (CodeMirror types the serialized config as any.)
86
86
  */
87
- history: Array<{
87
+ history: {
88
88
  name: string;
89
89
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
90
90
  serializedEditorState: any;
91
91
  column: CellColumnId;
92
92
  index: CellIndex;
93
93
  isSetupCell: boolean;
94
- }>;
94
+ }[];
95
95
  /**
96
96
  * Key of cell to scroll to; typically set by actions that re-order the cell
97
97
  * array. Call the SCROLL_TO_TARGET action to scroll to the specified cell
@@ -1128,10 +1128,10 @@ const {
1128
1128
 
1129
1129
  // Find the start/end of the collapsed ranges
1130
1130
  const nodes = [...column.nodes];
1131
- const rangeIndexes: Array<{
1131
+ const rangeIndexes: {
1132
1132
  start: CellIndex;
1133
1133
  end: CellIndex;
1134
- }> = [];
1134
+ }[] = [];
1135
1135
  const reversedCollapseRanges = [];
1136
1136
 
1137
1137
  // Iterate in reverse order (bottom-up) to process children first
@@ -1668,7 +1668,7 @@ export const getCellEditorView = (cellId: CellId) => {
1668
1668
 
1669
1669
  export function flattenTopLevelNotebookCells(
1670
1670
  state: NotebookState,
1671
- ): Array<CellData & CellRuntimeState> {
1671
+ ): (CellData & CellRuntimeState)[] {
1672
1672
  const { cellIds, cellData, cellRuntime } = state;
1673
1673
  return cellIds.getColumns().flatMap((column) =>
1674
1674
  column.topLevelIds.map((cellId) => ({
@@ -87,7 +87,7 @@ const CellLogLogger = {
87
87
  ? "red"
88
88
  : "orange";
89
89
  const status = payload.level.toUpperCase();
90
- // eslint-disable-next-line no-console
90
+ /** biome-ignore lint/suspicious/noConsole: for debugging */
91
91
  console.log(
92
92
  `%c[${status}]`,
93
93
  `color:${color}; padding:2px 0; border-radius:2px; font-weight:bold`,
@@ -93,7 +93,7 @@ export interface CellRuntimeState {
93
93
  /** TOC outline */
94
94
  outline: Outline | null;
95
95
  /** messages encoding the cell's console outputs. */
96
- consoleOutputs: Array<WithResponse<OutputMessage>>;
96
+ consoleOutputs: WithResponse<OutputMessage>[];
97
97
  /** current status of the cell */
98
98
  status: RuntimeState;
99
99
  /** whether the cell has stale inputs*/
@@ -3,6 +3,7 @@
3
3
  import { python } from "@codemirror/lang-python";
4
4
  import { EditorState } from "@codemirror/state";
5
5
  import { EditorView } from "@codemirror/view";
6
+ import { atom } from "jotai";
6
7
  import { beforeEach, describe, expect, it, vi } from "vitest";
7
8
  import { MockRequestClient } from "@/__mocks__/requests";
8
9
  import type { NotebookState } from "@/core/cells/cells";
@@ -34,6 +35,11 @@ vi.mock("@/core/cells/utils", () => ({
34
35
 
35
36
  vi.mock("@/core/config/config", () => ({
36
37
  getResolvedMarimoConfig: vi.fn(),
38
+ resolvedMarimoConfigAtom: atom({
39
+ display: {
40
+ theme: "light",
41
+ },
42
+ }),
37
43
  }));
38
44
 
39
45
  const updateCellCode = vi.fn();
@@ -29,7 +29,7 @@ function createErrorDecorations(state: EditorState, errors: TracebackInfos) {
29
29
 
30
30
  // Filter and sort errors by line number to ensure they're added in order
31
31
  const relevantErrors = errors
32
- .filter((error) => error.cellId === cellId)
32
+ .filter((error) => error.kind === "cell" && error.cellId === cellId)
33
33
  .sort((a, b) => a.lineNumber - b.lineNumber);
34
34
 
35
35
  for (const error of relevantErrors) {
@@ -3,14 +3,14 @@ import { foldAll, unfoldAll } from "@codemirror/language";
3
3
  import type { Command, EditorView } from "@codemirror/view";
4
4
  import type { Nullable } from "vitest";
5
5
 
6
- export type BulkCommand = (targets: Array<Nullable<EditorView>>) => boolean;
6
+ export type BulkCommand = (targets: Nullable<EditorView>[]) => boolean;
7
7
 
8
8
  /**
9
9
  * Make a bulk command from a single {@type Command} that applies
10
10
  * the given command to all targets.
11
11
  */
12
12
  export function makeBulkCommand(command: Command) {
13
- return (targets: Array<Nullable<EditorView>>) => {
13
+ return (targets: Nullable<EditorView>[]) => {
14
14
  let changed = false;
15
15
  for (const target of targets) {
16
16
  if (target) {
@@ -110,7 +110,7 @@ export const findPrev = findInDirection("prev");
110
110
  */
111
111
  export const replaceAll = searchCommand(({ query }) => {
112
112
  const views = getAllEditorViews();
113
- const undoHandlers: Array<() => void> = [];
113
+ const undoHandlers: (() => void)[] = [];
114
114
  for (const view of views) {
115
115
  if (view.state.readOnly) {
116
116
  continue;
@@ -14,6 +14,7 @@ import {
14
14
  languageAdapterState,
15
15
  switchLanguage,
16
16
  } from "../extension";
17
+ import { exportedForTesting as sqlValidationErrorsForTesting } from "../languages/sql/banner-validation-errors";
17
18
  import { languageMetadataField } from "../metadata";
18
19
 
19
20
  let view: EditorView | null = null;
@@ -258,3 +259,26 @@ describe("switchLanguage", () => {
258
259
  });
259
260
  });
260
261
  });
262
+
263
+ describe("sqlValidationErrors", () => {
264
+ const { splitErrorMessage } = sqlValidationErrorsForTesting;
265
+
266
+ describe("split error message", () => {
267
+ it("should split the error message into error type and error message", () => {
268
+ const error = "SyntaxError: SELECT * FROM df";
269
+ const { errorType, errorMessage } = splitErrorMessage(error);
270
+ expect(errorType).toBe("SyntaxError");
271
+ expect(errorMessage).toBe("SELECT * FROM df");
272
+ });
273
+
274
+ it("should handle multiple colons", () => {
275
+ const error =
276
+ "SyntaxError: SELECT * FROM df:SyntaxError: SELECT * FROM df";
277
+ const { errorType, errorMessage } = splitErrorMessage(error);
278
+ expect(errorType).toBe("SyntaxError");
279
+ expect(errorMessage).toBe(
280
+ "SELECT * FROM df:SyntaxError: SELECT * FROM df",
281
+ );
282
+ });
283
+ });
284
+ });
@@ -0,0 +1,133 @@
1
+ /* Copyright 2024 Marimo. All rights reserved. */
2
+
3
+ import { describe, expect, it } from "vitest";
4
+ import { exportedForTesting } from "../languages/sql/banner-validation-errors";
5
+
6
+ describe("Error Message Splitting", () => {
7
+ it("should handle error message splitting correctly", () => {
8
+ const { splitErrorMessage } = exportedForTesting;
9
+
10
+ const result1 = splitErrorMessage("Syntax error: unexpected token");
11
+ expect(result1.errorType).toBe("Syntax error");
12
+ expect(result1.errorMessage).toBe("unexpected token");
13
+
14
+ const result2 = splitErrorMessage("Multiple: colons: in error");
15
+ expect(result2.errorType).toBe("Multiple");
16
+ expect(result2.errorMessage).toBe("colons: in error");
17
+
18
+ const result3 = splitErrorMessage("No colon error");
19
+ expect(result3.errorType).toBe("No colon error");
20
+ expect(result3.errorMessage).toBe("");
21
+ });
22
+ });
23
+
24
+ describe("DuckDB Error Handling", () => {
25
+ it("should extract codeblock from error with LINE information", () => {
26
+ const { handleDuckdbError } = exportedForTesting;
27
+
28
+ const error =
29
+ 'Binder Error: Referenced column "attacks" not found in FROM clause! Candidate bindings: "Attack", "Total" LINE 1:... from pokemon WHERE \'type_2\' = 32 and attack = 32 and not attacks = \'hi\' ^';
30
+
31
+ const result = handleDuckdbError(error);
32
+
33
+ expect(result.errorType).toBe("Binder Error");
34
+ expect(result.errorMessage).toBe(
35
+ 'Referenced column "attacks" not found in FROM clause! Candidate bindings: "Attack", "Total"',
36
+ );
37
+ expect(result.codeblock).toBe(
38
+ "LINE 1:... from pokemon WHERE 'type_2' = 32 and attack = 32 and not attacks = 'hi' ^",
39
+ );
40
+ });
41
+
42
+ it("should handle error without LINE information", () => {
43
+ const { handleDuckdbError } = exportedForTesting;
44
+
45
+ const error = "Syntax Error: Invalid syntax near WHERE";
46
+
47
+ const result = handleDuckdbError(error);
48
+
49
+ expect(result.errorType).toBe("Syntax Error");
50
+ expect(result.errorMessage).toBe("Invalid syntax near WHERE");
51
+ expect(result.codeblock).toBeUndefined();
52
+ });
53
+
54
+ it("should handle error with LINE at the beginning", () => {
55
+ const { handleDuckdbError } = exportedForTesting;
56
+
57
+ const error = "LINE 1: SELECT * FROM table WHERE invalid_column = 1 ^";
58
+
59
+ const result = handleDuckdbError(error);
60
+
61
+ expect(result.errorType).toBe("LINE 1");
62
+ expect(result.errorMessage).toBe(
63
+ "SELECT * FROM table WHERE invalid_column = 1 ^",
64
+ );
65
+ expect(result.codeblock).toBeUndefined();
66
+ });
67
+
68
+ it("should handle error with multiple LINE occurrences", () => {
69
+ const { handleDuckdbError } = exportedForTesting;
70
+
71
+ const error =
72
+ "Error: Something went wrong LINE 1: SELECT * FROM table WHERE invalid_column = 1 ^";
73
+
74
+ const result = handleDuckdbError(error);
75
+
76
+ expect(result.errorType).toBe("Error");
77
+ expect(result.errorMessage).toBe("Something went wrong");
78
+ expect(result.codeblock).toBe(
79
+ "LINE 1: SELECT * FROM table WHERE invalid_column = 1 ^",
80
+ );
81
+ });
82
+
83
+ it("should handle complex error with nested quotes", () => {
84
+ const { handleDuckdbError } = exportedForTesting;
85
+
86
+ const error =
87
+ "Binder Error: Column \"name\" not found! LINE 1: SELECT * FROM users WHERE name = 'John' AND age > 25 ^";
88
+
89
+ const result = handleDuckdbError(error);
90
+
91
+ expect(result.errorType).toBe("Binder Error");
92
+ expect(result.errorMessage).toBe('Column "name" not found!');
93
+ expect(result.codeblock).toBe(
94
+ "LINE 1: SELECT * FROM users WHERE name = 'John' AND age > 25 ^",
95
+ );
96
+ });
97
+
98
+ it("should handle error with LINE but no caret", () => {
99
+ const { handleDuckdbError } = exportedForTesting;
100
+
101
+ const error = "Error: Invalid query LINE 1: SELECT * FROM table";
102
+
103
+ const result = handleDuckdbError(error);
104
+
105
+ expect(result.errorType).toBe("Error");
106
+ expect(result.errorMessage).toBe("Invalid query");
107
+ expect(result.codeblock).toBe("LINE 1: SELECT * FROM table");
108
+ });
109
+
110
+ it("should trim whitespace from codeblock", () => {
111
+ const { handleDuckdbError } = exportedForTesting;
112
+
113
+ const error = "Error: Something wrong LINE 1: SELECT * FROM table ^ ";
114
+
115
+ const result = handleDuckdbError(error);
116
+
117
+ expect(result.errorType).toBe("Error");
118
+ expect(result.errorMessage).toBe("Something wrong");
119
+ expect(result.codeblock).toBe("LINE 1: SELECT * FROM table ^");
120
+ });
121
+
122
+ it("should handle empty error message", () => {
123
+ const { handleDuckdbError } = exportedForTesting;
124
+
125
+ const error = "";
126
+
127
+ const result = handleDuckdbError(error);
128
+
129
+ expect(result.errorType).toBe("");
130
+ expect(result.errorMessage).toBe("");
131
+ expect(result.codeblock).toBeUndefined();
132
+ });
133
+ });