@walkthru-earth/objex 0.1.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 (367) hide show
  1. package/LICENSE +396 -0
  2. package/README.md +114 -0
  3. package/dist/assets/favicon.svg +17 -0
  4. package/dist/components/CLAUDE.md +44 -0
  5. package/dist/components/browser/Breadcrumb.svelte +50 -0
  6. package/dist/components/browser/Breadcrumb.svelte.d.ts +7 -0
  7. package/dist/components/browser/CreateFolderDialog.svelte +98 -0
  8. package/dist/components/browser/CreateFolderDialog.svelte.d.ts +6 -0
  9. package/dist/components/browser/DeleteConfirmDialog.svelte +90 -0
  10. package/dist/components/browser/DeleteConfirmDialog.svelte.d.ts +8 -0
  11. package/dist/components/browser/DropZone.svelte +83 -0
  12. package/dist/components/browser/DropZone.svelte.d.ts +7 -0
  13. package/dist/components/browser/FileBrowser.svelte +229 -0
  14. package/dist/components/browser/FileBrowser.svelte.d.ts +3 -0
  15. package/dist/components/browser/FileRow.svelte +112 -0
  16. package/dist/components/browser/FileRow.svelte.d.ts +9 -0
  17. package/dist/components/browser/FileTreeSidebar.svelte +559 -0
  18. package/dist/components/browser/FileTreeSidebar.svelte.d.ts +8 -0
  19. package/dist/components/browser/RenameDialog.svelte +101 -0
  20. package/dist/components/browser/RenameDialog.svelte.d.ts +8 -0
  21. package/dist/components/browser/SearchBar.svelte +40 -0
  22. package/dist/components/browser/SearchBar.svelte.d.ts +6 -0
  23. package/dist/components/browser/UploadButton.svelte +65 -0
  24. package/dist/components/browser/UploadButton.svelte.d.ts +3 -0
  25. package/dist/components/editor/CodeMirrorEditor.svelte +404 -0
  26. package/dist/components/editor/CodeMirrorEditor.svelte.d.ts +12 -0
  27. package/dist/components/editor/MilkdownEditor.svelte +98 -0
  28. package/dist/components/editor/MilkdownEditor.svelte.d.ts +9 -0
  29. package/dist/components/editor/SqlEditor.svelte +173 -0
  30. package/dist/components/editor/SqlEditor.svelte.d.ts +7 -0
  31. package/dist/components/editor/SqlResultBlock.svelte +199 -0
  32. package/dist/components/editor/SqlResultBlock.svelte.d.ts +9 -0
  33. package/dist/components/layout/ConnectionDialog.svelte +439 -0
  34. package/dist/components/layout/ConnectionDialog.svelte.d.ts +9 -0
  35. package/dist/components/layout/LocaleToggle.svelte +32 -0
  36. package/dist/components/layout/LocaleToggle.svelte.d.ts +3 -0
  37. package/dist/components/layout/SafeLockToggle.svelte +37 -0
  38. package/dist/components/layout/SafeLockToggle.svelte.d.ts +18 -0
  39. package/dist/components/layout/Sidebar.svelte +314 -0
  40. package/dist/components/layout/Sidebar.svelte.d.ts +3 -0
  41. package/dist/components/layout/StatusBar.svelte +73 -0
  42. package/dist/components/layout/StatusBar.svelte.d.ts +3 -0
  43. package/dist/components/layout/TabBar.svelte +102 -0
  44. package/dist/components/layout/TabBar.svelte.d.ts +7 -0
  45. package/dist/components/layout/ThemeToggle.svelte +52 -0
  46. package/dist/components/layout/ThemeToggle.svelte.d.ts +3 -0
  47. package/dist/components/ui/badge/badge.svelte +49 -0
  48. package/dist/components/ui/badge/badge.svelte.d.ts +32 -0
  49. package/dist/components/ui/badge/index.d.ts +1 -0
  50. package/dist/components/ui/badge/index.js +1 -0
  51. package/dist/components/ui/button/button.svelte +82 -0
  52. package/dist/components/ui/button/button.svelte.d.ts +64 -0
  53. package/dist/components/ui/button/index.d.ts +2 -0
  54. package/dist/components/ui/button/index.js +4 -0
  55. package/dist/components/ui/context-menu/context-menu-checkbox-item.svelte +40 -0
  56. package/dist/components/ui/context-menu/context-menu-checkbox-item.svelte.d.ts +9 -0
  57. package/dist/components/ui/context-menu/context-menu-content.svelte +28 -0
  58. package/dist/components/ui/context-menu/context-menu-content.svelte.d.ts +10 -0
  59. package/dist/components/ui/context-menu/context-menu-group-heading.svelte +21 -0
  60. package/dist/components/ui/context-menu/context-menu-group-heading.svelte.d.ts +7 -0
  61. package/dist/components/ui/context-menu/context-menu-group.svelte +7 -0
  62. package/dist/components/ui/context-menu/context-menu-group.svelte.d.ts +4 -0
  63. package/dist/components/ui/context-menu/context-menu-item.svelte +27 -0
  64. package/dist/components/ui/context-menu/context-menu-item.svelte.d.ts +8 -0
  65. package/dist/components/ui/context-menu/context-menu-label.svelte +24 -0
  66. package/dist/components/ui/context-menu/context-menu-label.svelte.d.ts +8 -0
  67. package/dist/components/ui/context-menu/context-menu-portal.svelte +7 -0
  68. package/dist/components/ui/context-menu/context-menu-portal.svelte.d.ts +3 -0
  69. package/dist/components/ui/context-menu/context-menu-radio-group.svelte +16 -0
  70. package/dist/components/ui/context-menu/context-menu-radio-group.svelte.d.ts +4 -0
  71. package/dist/components/ui/context-menu/context-menu-radio-item.svelte +33 -0
  72. package/dist/components/ui/context-menu/context-menu-radio-item.svelte.d.ts +4 -0
  73. package/dist/components/ui/context-menu/context-menu-separator.svelte +17 -0
  74. package/dist/components/ui/context-menu/context-menu-separator.svelte.d.ts +4 -0
  75. package/dist/components/ui/context-menu/context-menu-shortcut.svelte +20 -0
  76. package/dist/components/ui/context-menu/context-menu-shortcut.svelte.d.ts +5 -0
  77. package/dist/components/ui/context-menu/context-menu-sub-content.svelte +20 -0
  78. package/dist/components/ui/context-menu/context-menu-sub-content.svelte.d.ts +4 -0
  79. package/dist/components/ui/context-menu/context-menu-sub-trigger.svelte +29 -0
  80. package/dist/components/ui/context-menu/context-menu-sub-trigger.svelte.d.ts +8 -0
  81. package/dist/components/ui/context-menu/context-menu-sub.svelte +7 -0
  82. package/dist/components/ui/context-menu/context-menu-sub.svelte.d.ts +3 -0
  83. package/dist/components/ui/context-menu/context-menu-trigger.svelte +7 -0
  84. package/dist/components/ui/context-menu/context-menu-trigger.svelte.d.ts +4 -0
  85. package/dist/components/ui/context-menu/context-menu.svelte +7 -0
  86. package/dist/components/ui/context-menu/context-menu.svelte.d.ts +3 -0
  87. package/dist/components/ui/context-menu/index.d.ts +17 -0
  88. package/dist/components/ui/context-menu/index.js +19 -0
  89. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
  90. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte.d.ts +4 -0
  91. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +43 -0
  92. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte.d.ts +9 -0
  93. package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte +29 -0
  94. package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte.d.ts +10 -0
  95. package/dist/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
  96. package/dist/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte.d.ts +8 -0
  97. package/dist/components/ui/dropdown-menu/dropdown-menu-group.svelte +7 -0
  98. package/dist/components/ui/dropdown-menu/dropdown-menu-group.svelte.d.ts +4 -0
  99. package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte +27 -0
  100. package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte.d.ts +8 -0
  101. package/dist/components/ui/dropdown-menu/dropdown-menu-label.svelte +24 -0
  102. package/dist/components/ui/dropdown-menu/dropdown-menu-label.svelte.d.ts +8 -0
  103. package/dist/components/ui/dropdown-menu/dropdown-menu-portal.svelte +7 -0
  104. package/dist/components/ui/dropdown-menu/dropdown-menu-portal.svelte.d.ts +3 -0
  105. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
  106. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +4 -0
  107. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +33 -0
  108. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte.d.ts +4 -0
  109. package/dist/components/ui/dropdown-menu/dropdown-menu-separator.svelte +17 -0
  110. package/dist/components/ui/dropdown-menu/dropdown-menu-separator.svelte.d.ts +4 -0
  111. package/dist/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +20 -0
  112. package/dist/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte.d.ts +5 -0
  113. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +20 -0
  114. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte.d.ts +4 -0
  115. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
  116. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte.d.ts +7 -0
  117. package/dist/components/ui/dropdown-menu/dropdown-menu-sub.svelte +7 -0
  118. package/dist/components/ui/dropdown-menu/dropdown-menu-sub.svelte.d.ts +3 -0
  119. package/dist/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
  120. package/dist/components/ui/dropdown-menu/dropdown-menu-trigger.svelte.d.ts +4 -0
  121. package/dist/components/ui/dropdown-menu/dropdown-menu.svelte +7 -0
  122. package/dist/components/ui/dropdown-menu/dropdown-menu.svelte.d.ts +3 -0
  123. package/dist/components/ui/dropdown-menu/index.d.ts +18 -0
  124. package/dist/components/ui/dropdown-menu/index.js +18 -0
  125. package/dist/components/ui/input/index.d.ts +2 -0
  126. package/dist/components/ui/input/index.js +4 -0
  127. package/dist/components/ui/input/input.svelte +52 -0
  128. package/dist/components/ui/input/input.svelte.d.ts +13 -0
  129. package/dist/components/ui/resizable/index.d.ts +4 -0
  130. package/dist/components/ui/resizable/index.js +6 -0
  131. package/dist/components/ui/resizable/resizable-handle.svelte +30 -0
  132. package/dist/components/ui/resizable/resizable-handle.svelte.d.ts +8 -0
  133. package/dist/components/ui/resizable/resizable-pane-group.svelte +20 -0
  134. package/dist/components/ui/resizable/resizable-pane-group.svelte.d.ts +7 -0
  135. package/dist/components/ui/scroll-area/index.d.ts +3 -0
  136. package/dist/components/ui/scroll-area/index.js +5 -0
  137. package/dist/components/ui/scroll-area/scroll-area-scrollbar.svelte +31 -0
  138. package/dist/components/ui/scroll-area/scroll-area-scrollbar.svelte.d.ts +4 -0
  139. package/dist/components/ui/scroll-area/scroll-area.svelte +47 -0
  140. package/dist/components/ui/scroll-area/scroll-area.svelte.d.ts +11 -0
  141. package/dist/components/ui/separator/index.d.ts +2 -0
  142. package/dist/components/ui/separator/index.js +4 -0
  143. package/dist/components/ui/separator/separator.svelte +21 -0
  144. package/dist/components/ui/separator/separator.svelte.d.ts +4 -0
  145. package/dist/components/ui/sheet/index.d.ts +11 -0
  146. package/dist/components/ui/sheet/index.js +13 -0
  147. package/dist/components/ui/sheet/sheet-close.svelte +7 -0
  148. package/dist/components/ui/sheet/sheet-close.svelte.d.ts +4 -0
  149. package/dist/components/ui/sheet/sheet-content.svelte +62 -0
  150. package/dist/components/ui/sheet/sheet-content.svelte.d.ts +37 -0
  151. package/dist/components/ui/sheet/sheet-description.svelte +17 -0
  152. package/dist/components/ui/sheet/sheet-description.svelte.d.ts +4 -0
  153. package/dist/components/ui/sheet/sheet-footer.svelte +20 -0
  154. package/dist/components/ui/sheet/sheet-footer.svelte.d.ts +5 -0
  155. package/dist/components/ui/sheet/sheet-header.svelte +20 -0
  156. package/dist/components/ui/sheet/sheet-header.svelte.d.ts +5 -0
  157. package/dist/components/ui/sheet/sheet-overlay.svelte +20 -0
  158. package/dist/components/ui/sheet/sheet-overlay.svelte.d.ts +4 -0
  159. package/dist/components/ui/sheet/sheet-portal.svelte +7 -0
  160. package/dist/components/ui/sheet/sheet-portal.svelte.d.ts +3 -0
  161. package/dist/components/ui/sheet/sheet-title.svelte +13 -0
  162. package/dist/components/ui/sheet/sheet-title.svelte.d.ts +4 -0
  163. package/dist/components/ui/sheet/sheet-trigger.svelte +7 -0
  164. package/dist/components/ui/sheet/sheet-trigger.svelte.d.ts +4 -0
  165. package/dist/components/ui/sheet/sheet.svelte +7 -0
  166. package/dist/components/ui/sheet/sheet.svelte.d.ts +3 -0
  167. package/dist/components/ui/switch/index.d.ts +2 -0
  168. package/dist/components/ui/switch/index.js +4 -0
  169. package/dist/components/ui/switch/switch.svelte +28 -0
  170. package/dist/components/ui/switch/switch.svelte.d.ts +8 -0
  171. package/dist/components/ui/tabs/index.d.ts +5 -0
  172. package/dist/components/ui/tabs/index.js +7 -0
  173. package/dist/components/ui/tabs/tabs-content.svelte +17 -0
  174. package/dist/components/ui/tabs/tabs-content.svelte.d.ts +4 -0
  175. package/dist/components/ui/tabs/tabs-list.svelte +16 -0
  176. package/dist/components/ui/tabs/tabs-list.svelte.d.ts +4 -0
  177. package/dist/components/ui/tabs/tabs-trigger.svelte +20 -0
  178. package/dist/components/ui/tabs/tabs-trigger.svelte.d.ts +4 -0
  179. package/dist/components/ui/tabs/tabs.svelte +19 -0
  180. package/dist/components/ui/tabs/tabs.svelte.d.ts +4 -0
  181. package/dist/components/ui/tooltip/index.d.ts +6 -0
  182. package/dist/components/ui/tooltip/index.js +8 -0
  183. package/dist/components/ui/tooltip/tooltip-content.svelte +52 -0
  184. package/dist/components/ui/tooltip/tooltip-content.svelte.d.ts +11 -0
  185. package/dist/components/ui/tooltip/tooltip-portal.svelte +7 -0
  186. package/dist/components/ui/tooltip/tooltip-portal.svelte.d.ts +4 -0
  187. package/dist/components/ui/tooltip/tooltip-provider.svelte +7 -0
  188. package/dist/components/ui/tooltip/tooltip-provider.svelte.d.ts +4 -0
  189. package/dist/components/ui/tooltip/tooltip-trigger.svelte +7 -0
  190. package/dist/components/ui/tooltip/tooltip-trigger.svelte.d.ts +4 -0
  191. package/dist/components/ui/tooltip/tooltip.svelte +7 -0
  192. package/dist/components/ui/tooltip/tooltip.svelte.d.ts +4 -0
  193. package/dist/components/viewers/ArchiveViewer.svelte +586 -0
  194. package/dist/components/viewers/ArchiveViewer.svelte.d.ts +7 -0
  195. package/dist/components/viewers/CLAUDE.md +60 -0
  196. package/dist/components/viewers/CodeViewer.svelte +553 -0
  197. package/dist/components/viewers/CodeViewer.svelte.d.ts +7 -0
  198. package/dist/components/viewers/CogViewer.svelte +1345 -0
  199. package/dist/components/viewers/CogViewer.svelte.d.ts +7 -0
  200. package/dist/components/viewers/CopcViewer.svelte +25 -0
  201. package/dist/components/viewers/CopcViewer.svelte.d.ts +7 -0
  202. package/dist/components/viewers/DatabaseViewer.svelte +169 -0
  203. package/dist/components/viewers/DatabaseViewer.svelte.d.ts +7 -0
  204. package/dist/components/viewers/FileInfo.svelte +174 -0
  205. package/dist/components/viewers/FileInfo.svelte.d.ts +10 -0
  206. package/dist/components/viewers/FlatGeobufViewer.svelte +755 -0
  207. package/dist/components/viewers/FlatGeobufViewer.svelte.d.ts +7 -0
  208. package/dist/components/viewers/GeoParquetMapViewer.svelte +278 -0
  209. package/dist/components/viewers/GeoParquetMapViewer.svelte.d.ts +17 -0
  210. package/dist/components/viewers/ImageViewer.svelte +233 -0
  211. package/dist/components/viewers/ImageViewer.svelte.d.ts +7 -0
  212. package/dist/components/viewers/LoadProgress.svelte +93 -0
  213. package/dist/components/viewers/LoadProgress.svelte.d.ts +15 -0
  214. package/dist/components/viewers/MapViewer.svelte +234 -0
  215. package/dist/components/viewers/MapViewer.svelte.d.ts +7 -0
  216. package/dist/components/viewers/MarkdownViewer.svelte +478 -0
  217. package/dist/components/viewers/MarkdownViewer.svelte.d.ts +7 -0
  218. package/dist/components/viewers/MediaViewer.svelte +121 -0
  219. package/dist/components/viewers/MediaViewer.svelte.d.ts +7 -0
  220. package/dist/components/viewers/ModelViewer.svelte +164 -0
  221. package/dist/components/viewers/ModelViewer.svelte.d.ts +7 -0
  222. package/dist/components/viewers/NotebookViewer.svelte +389 -0
  223. package/dist/components/viewers/NotebookViewer.svelte.d.ts +7 -0
  224. package/dist/components/viewers/PdfViewer.svelte +278 -0
  225. package/dist/components/viewers/PdfViewer.svelte.d.ts +7 -0
  226. package/dist/components/viewers/PmtilesViewer.svelte +191 -0
  227. package/dist/components/viewers/PmtilesViewer.svelte.d.ts +7 -0
  228. package/dist/components/viewers/QueryHistoryPanel.svelte +159 -0
  229. package/dist/components/viewers/QueryHistoryPanel.svelte.d.ts +8 -0
  230. package/dist/components/viewers/RawViewer.svelte +117 -0
  231. package/dist/components/viewers/RawViewer.svelte.d.ts +7 -0
  232. package/dist/components/viewers/StacMapViewer.svelte +20 -0
  233. package/dist/components/viewers/StacMapViewer.svelte.d.ts +7 -0
  234. package/dist/components/viewers/StyleEditorOverlay.svelte +27 -0
  235. package/dist/components/viewers/StyleEditorOverlay.svelte.d.ts +7 -0
  236. package/dist/components/viewers/TableGrid.svelte +355 -0
  237. package/dist/components/viewers/TableGrid.svelte.d.ts +12 -0
  238. package/dist/components/viewers/TableStatusBar.svelte +92 -0
  239. package/dist/components/viewers/TableStatusBar.svelte.d.ts +11 -0
  240. package/dist/components/viewers/TableToolbar.svelte +382 -0
  241. package/dist/components/viewers/TableToolbar.svelte.d.ts +25 -0
  242. package/dist/components/viewers/TableViewer.svelte +923 -0
  243. package/dist/components/viewers/TableViewer.svelte.d.ts +7 -0
  244. package/dist/components/viewers/ViewerRouter.svelte +70 -0
  245. package/dist/components/viewers/ViewerRouter.svelte.d.ts +7 -0
  246. package/dist/components/viewers/ZarrMapViewer.svelte +288 -0
  247. package/dist/components/viewers/ZarrMapViewer.svelte.d.ts +17 -0
  248. package/dist/components/viewers/ZarrViewer.svelte +256 -0
  249. package/dist/components/viewers/ZarrViewer.svelte.d.ts +7 -0
  250. package/dist/components/viewers/map/AttributeTable.svelte +52 -0
  251. package/dist/components/viewers/map/AttributeTable.svelte.d.ts +8 -0
  252. package/dist/components/viewers/map/MapContainer.svelte +158 -0
  253. package/dist/components/viewers/map/MapContainer.svelte.d.ts +12 -0
  254. package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte +389 -0
  255. package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte.d.ts +10 -0
  256. package/dist/components/viewers/pmtiles/PmtilesMapView.svelte +332 -0
  257. package/dist/components/viewers/pmtiles/PmtilesMapView.svelte.d.ts +11 -0
  258. package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte +373 -0
  259. package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte.d.ts +12 -0
  260. package/dist/components/viewers/pmtiles/SvgTileRenderer.svelte +112 -0
  261. package/dist/components/viewers/pmtiles/SvgTileRenderer.svelte.d.ts +10 -0
  262. package/dist/file-icons/CLAUDE.md +21 -0
  263. package/dist/file-icons/FileTypeIcon.svelte +74 -0
  264. package/dist/file-icons/FileTypeIcon.svelte.d.ts +9 -0
  265. package/dist/file-icons/index.d.ts +56 -0
  266. package/dist/file-icons/index.js +1070 -0
  267. package/dist/i18n/CLAUDE.md +19 -0
  268. package/dist/i18n/ar.d.ts +1 -0
  269. package/dist/i18n/ar.js +404 -0
  270. package/dist/i18n/en.d.ts +1 -0
  271. package/dist/i18n/en.js +404 -0
  272. package/dist/i18n/index.svelte.d.ts +9 -0
  273. package/dist/i18n/index.svelte.js +27 -0
  274. package/dist/index.d.ts +20 -0
  275. package/dist/index.js +13 -0
  276. package/dist/query/CLAUDE.md +22 -0
  277. package/dist/query/engine.d.ts +56 -0
  278. package/dist/query/engine.js +6 -0
  279. package/dist/query/index.d.ts +4 -0
  280. package/dist/query/index.js +19 -0
  281. package/dist/query/wasm.d.ts +20 -0
  282. package/dist/query/wasm.js +890 -0
  283. package/dist/storage/CLAUDE.md +23 -0
  284. package/dist/storage/adapter.d.ts +21 -0
  285. package/dist/storage/adapter.js +1 -0
  286. package/dist/storage/browser-azure.d.ts +25 -0
  287. package/dist/storage/browser-azure.js +271 -0
  288. package/dist/storage/browser-cloud.d.ts +32 -0
  289. package/dist/storage/browser-cloud.js +293 -0
  290. package/dist/storage/index.d.ts +11 -0
  291. package/dist/storage/index.js +37 -0
  292. package/dist/storage/url-adapter.d.ts +19 -0
  293. package/dist/storage/url-adapter.js +51 -0
  294. package/dist/stores/CLAUDE.md +29 -0
  295. package/dist/stores/browser.svelte.d.ts +28 -0
  296. package/dist/stores/browser.svelte.js +160 -0
  297. package/dist/stores/connections.svelte.d.ts +56 -0
  298. package/dist/stores/connections.svelte.js +272 -0
  299. package/dist/stores/credentials.svelte.d.ts +56 -0
  300. package/dist/stores/credentials.svelte.js +79 -0
  301. package/dist/stores/files.svelte.d.ts +20 -0
  302. package/dist/stores/files.svelte.js +76 -0
  303. package/dist/stores/query-history.svelte.d.ts +16 -0
  304. package/dist/stores/query-history.svelte.js +57 -0
  305. package/dist/stores/safelock.svelte.d.ts +8 -0
  306. package/dist/stores/safelock.svelte.js +52 -0
  307. package/dist/stores/settings.svelte.d.ts +11 -0
  308. package/dist/stores/settings.svelte.js +101 -0
  309. package/dist/stores/tab-resources.svelte.d.ts +25 -0
  310. package/dist/stores/tab-resources.svelte.js +61 -0
  311. package/dist/stores/tabs.svelte.d.ts +17 -0
  312. package/dist/stores/tabs.svelte.js +110 -0
  313. package/dist/types/notebookjs.d.ts +14 -0
  314. package/dist/types.d.ts +47 -0
  315. package/dist/types.js +1 -0
  316. package/dist/utils/CLAUDE.md +54 -0
  317. package/dist/utils/analytics.d.ts +10 -0
  318. package/dist/utils/analytics.js +38 -0
  319. package/dist/utils/archive.d.ts +70 -0
  320. package/dist/utils/archive.js +333 -0
  321. package/dist/utils/column-types.d.ts +5 -0
  322. package/dist/utils/column-types.js +137 -0
  323. package/dist/utils/deck.d.ts +98 -0
  324. package/dist/utils/deck.js +208 -0
  325. package/dist/utils/evidence-context.d.ts +22 -0
  326. package/dist/utils/evidence-context.js +56 -0
  327. package/dist/utils/export.d.ts +2 -0
  328. package/dist/utils/export.js +51 -0
  329. package/dist/utils/format.d.ts +14 -0
  330. package/dist/utils/format.js +56 -0
  331. package/dist/utils/geoarrow.d.ts +32 -0
  332. package/dist/utils/geoarrow.js +672 -0
  333. package/dist/utils/hex.d.ts +10 -0
  334. package/dist/utils/hex.js +27 -0
  335. package/dist/utils/host-detection.d.ts +23 -0
  336. package/dist/utils/host-detection.js +289 -0
  337. package/dist/utils/map-selection.d.ts +12 -0
  338. package/dist/utils/map-selection.js +45 -0
  339. package/dist/utils/markdown-sql.d.ts +30 -0
  340. package/dist/utils/markdown-sql.js +73 -0
  341. package/dist/utils/markdown.d.ts +18 -0
  342. package/dist/utils/markdown.js +146 -0
  343. package/dist/utils/model3d.d.ts +13 -0
  344. package/dist/utils/model3d.js +62 -0
  345. package/dist/utils/parquet-metadata.d.ts +58 -0
  346. package/dist/utils/parquet-metadata.js +228 -0
  347. package/dist/utils/pdf.d.ts +8 -0
  348. package/dist/utils/pdf.js +28 -0
  349. package/dist/utils/pmtiles-tile.d.ts +38 -0
  350. package/dist/utils/pmtiles-tile.js +64 -0
  351. package/dist/utils/pmtiles.d.ts +46 -0
  352. package/dist/utils/pmtiles.js +135 -0
  353. package/dist/utils/shiki.d.ts +8 -0
  354. package/dist/utils/shiki.js +98 -0
  355. package/dist/utils/storage-url.d.ts +64 -0
  356. package/dist/utils/storage-url.js +374 -0
  357. package/dist/utils/url-state.d.ts +40 -0
  358. package/dist/utils/url-state.js +113 -0
  359. package/dist/utils/url.d.ts +27 -0
  360. package/dist/utils/url.js +115 -0
  361. package/dist/utils/wkb.d.ts +43 -0
  362. package/dist/utils/wkb.js +345 -0
  363. package/dist/utils/zarr.d.ts +39 -0
  364. package/dist/utils/zarr.js +204 -0
  365. package/dist/utils.d.ts +12 -0
  366. package/dist/utils.js +5 -0
  367. package/package.json +203 -0
@@ -0,0 +1,890 @@
1
+ import { buildDuckDbSource } from '../file-icons/index.js';
2
+ import { credentialStore } from '../stores/credentials.svelte.js';
3
+ import { QueryCancelledError } from './engine';
4
+ const DUCKDB_VERSION = __DUCKDB_WASM_VERSION__;
5
+ const CDN_BASE = `https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@${DUCKDB_VERSION}/dist`;
6
+ const duckdb_wasm = `${CDN_BASE}/duckdb-mvp.wasm`;
7
+ const mvp_worker = `${CDN_BASE}/duckdb-browser-mvp.worker.js`;
8
+ const duckdb_wasm_eh = `${CDN_BASE}/duckdb-eh.wasm`;
9
+ const eh_worker = `${CDN_BASE}/duckdb-browser-eh.worker.js`;
10
+ const INIT_TIMEOUT_MS = 30_000;
11
+ // ─── Performance & diagnostic logging ────────────────────────────────
12
+ const LOG_PREFIX = '[DuckDB]';
13
+ function log(...args) {
14
+ console.log(LOG_PREFIX, ...args);
15
+ }
16
+ function logWarn(...args) {
17
+ console.warn(LOG_PREFIX, ...args);
18
+ }
19
+ function elapsed(start) {
20
+ return `${(performance.now() - start).toFixed(1)}ms`;
21
+ }
22
+ let dbPromise = null;
23
+ function withTimeout(promise, ms, label) {
24
+ return new Promise((resolve, reject) => {
25
+ const timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
26
+ promise.then((v) => {
27
+ clearTimeout(timer);
28
+ resolve(v);
29
+ }, (e) => {
30
+ clearTimeout(timer);
31
+ reject(e);
32
+ });
33
+ });
34
+ }
35
+ async function getDB() {
36
+ if (dbPromise) {
37
+ log('getDB → cached');
38
+ return dbPromise;
39
+ }
40
+ dbPromise = (async () => {
41
+ const t0 = performance.now();
42
+ log('getDB → initializing...');
43
+ const duckdb = await import('@duckdb/duckdb-wasm');
44
+ const MANUAL_BUNDLES = {
45
+ mvp: {
46
+ mainModule: duckdb_wasm,
47
+ mainWorker: mvp_worker
48
+ },
49
+ eh: {
50
+ mainModule: duckdb_wasm_eh,
51
+ mainWorker: eh_worker
52
+ }
53
+ };
54
+ const bundle = await duckdb.selectBundle(MANUAL_BUNDLES);
55
+ // Fetch the worker script as a blob to bypass cross-origin worker restrictions
56
+ const workerScript = await fetch(bundle.mainWorker).then((r) => r.blob());
57
+ const workerUrl = URL.createObjectURL(workerScript);
58
+ const worker = new Worker(workerUrl);
59
+ URL.revokeObjectURL(workerUrl);
60
+ const logger = new duckdb.ConsoleLogger();
61
+ const db = new duckdb.AsyncDuckDB(logger, worker);
62
+ await withTimeout(db.instantiate(bundle.mainModule, bundle.pthreadWorker), INIT_TIMEOUT_MS, 'DuckDB WASM instantiation');
63
+ log(`getDB → instantiated in ${elapsed(t0)}`);
64
+ // Load httpfs for remote file access and spatial for ST_ReadSHP
65
+ const conn = await db.connect();
66
+ try {
67
+ const tExt = performance.now();
68
+ await withTimeout(conn.query('INSTALL httpfs; LOAD httpfs; INSTALL spatial; LOAD spatial;'), INIT_TIMEOUT_MS, 'extension install (httpfs + spatial)');
69
+ // Disable auto-conversion of GeoParquet metadata → GEOMETRY type.
70
+ // Some files use legacy GeoParquet metadata (schema_version 0.x without
71
+ // "version" field) which causes DuckDB's spatial extension to throw
72
+ // "Geoparquet metadata does not have a version". We handle geometry
73
+ // detection, CRS, and WKB conversion ourselves via hyparquet metadata
74
+ // and explicit ST_GeomFromWKB() calls, so auto-conversion is not needed.
75
+ // SET GLOBAL applies to all future connections (no per-connection overhead).
76
+ try {
77
+ await conn.query('SET GLOBAL enable_geoparquet_conversion = false');
78
+ geoConversionGlobal = true;
79
+ }
80
+ catch {
81
+ // SET GLOBAL not supported — fall back to per-connection SET
82
+ await conn.query('SET enable_geoparquet_conversion = false');
83
+ }
84
+ log(`getDB → extensions loaded in ${elapsed(tExt)}`);
85
+ }
86
+ finally {
87
+ await conn.close();
88
+ }
89
+ log(`getDB → ready (total ${elapsed(t0)})`);
90
+ return db;
91
+ })();
92
+ // If init fails, clear the promise so it can be retried
93
+ dbPromise.catch(() => {
94
+ dbPromise = null;
95
+ });
96
+ return dbPromise;
97
+ }
98
+ let geoConversionGlobal = false;
99
+ /**
100
+ * Ensure GeoParquet auto-conversion is disabled on this connection.
101
+ * If SET GLOBAL succeeded during init, this is a no-op.
102
+ * Otherwise falls back to per-connection SET.
103
+ */
104
+ async function ensureGeoConversionDisabled(conn) {
105
+ if (geoConversionGlobal)
106
+ return;
107
+ await conn.query('SET enable_geoparquet_conversion = false');
108
+ }
109
+ // ─── CRS detection helpers ───────────────────────────────────────────
110
+ const WGS84_CODES = new Set([4326, 4979]);
111
+ /** Extract EPSG code from a PROJJSON object. Returns null for WGS84/CRS84. */
112
+ function extractEpsgFromProjjson(crs) {
113
+ if (!crs)
114
+ return null;
115
+ // OGC CRS84 is lon/lat WGS84
116
+ if (crs.type === 'name' && crs.properties?.name?.includes('CRS84'))
117
+ return null;
118
+ // PROJJSON: { "id": { "authority": "EPSG", "code": 27700 } }
119
+ if (crs.id?.authority === 'EPSG') {
120
+ const code = crs.id.code;
121
+ if (WGS84_CODES.has(code))
122
+ return null;
123
+ return `EPSG:${code}`;
124
+ }
125
+ return null;
126
+ }
127
+ /**
128
+ * Extract CRS from Parquet Format 2.11+ logical_type string.
129
+ * Handles these patterns:
130
+ * GeometryType(crs={...PROJJSON...}) — inline PROJJSON
131
+ * GeometryType(crs=projjson:key_name) — reference to KV metadata key
132
+ * GeometryType(crs=srid:5070) — direct SRID
133
+ * GeometryType(crs=<null>) — no CRS (WGS84)
134
+ */
135
+ async function extractCrsFromLogicalType(logicalType, conn, path) {
136
+ if (!logicalType.startsWith('GeometryType(') && !logicalType.startsWith('GeographyType('))
137
+ return null;
138
+ // Extract the crs= value from inside the parentheses
139
+ const crsMatch = logicalType.match(/crs=(.+?)(?:,\s*\w+=|\))/s);
140
+ if (!crsMatch)
141
+ return null;
142
+ const crsValue = crsMatch[1].trim();
143
+ // Null CRS — assume WGS84
144
+ if (crsValue === '<null>' || crsValue === 'null')
145
+ return null;
146
+ // Pattern: srid:NNNN — direct SRID
147
+ const sridMatch = crsValue.match(/^srid:(\d+)$/);
148
+ if (sridMatch) {
149
+ const code = Number(sridMatch[1]);
150
+ if (WGS84_CODES.has(code))
151
+ return null;
152
+ return `EPSG:${code}`;
153
+ }
154
+ // Pattern: projjson:key_name — reference to a KV metadata key
155
+ const refMatch = crsValue.match(/^projjson:(.+)$/);
156
+ if (refMatch) {
157
+ try {
158
+ const kvResult = await conn.query(`SELECT value FROM parquet_kv_metadata('${path}') WHERE decode(key) = '${refMatch[1]}'`);
159
+ const kvRows = kvResult.toArray();
160
+ if (kvRows.length > 0) {
161
+ const raw = kvRows[0].value;
162
+ const text = typeof raw === 'string' ? raw : new TextDecoder().decode(raw);
163
+ return extractEpsgFromProjjson(JSON.parse(text));
164
+ }
165
+ }
166
+ catch {
167
+ /* metadata lookup failed */
168
+ }
169
+ return null;
170
+ }
171
+ // Pattern: {... PROJJSON ...} — inline JSON (find balanced braces)
172
+ if (crsValue.startsWith('{')) {
173
+ const jsonStart = logicalType.indexOf('{');
174
+ if (jsonStart === -1)
175
+ return null;
176
+ let depth = 0;
177
+ let jsonEnd = -1;
178
+ for (let i = jsonStart; i < logicalType.length; i++) {
179
+ if (logicalType[i] === '{')
180
+ depth++;
181
+ else if (logicalType[i] === '}') {
182
+ depth--;
183
+ if (depth === 0) {
184
+ jsonEnd = i;
185
+ break;
186
+ }
187
+ }
188
+ }
189
+ if (jsonEnd === -1)
190
+ return null;
191
+ try {
192
+ const crs = JSON.parse(logicalType.substring(jsonStart, jsonEnd + 1));
193
+ return extractEpsgFromProjjson(crs);
194
+ }
195
+ catch {
196
+ return null;
197
+ }
198
+ }
199
+ return null;
200
+ }
201
+ // ─── Type-aware column extraction ────────────────────────────────────
202
+ // DuckDB Arrow type strings that represent binary/blob data — not useful
203
+ // for map tooltips and expensive to extract row-by-row.
204
+ const BINARY_TYPES = new Set(['BLOB', 'BYTEA', 'BINARY', 'LARGEBINARY', 'WKB_BLOB']);
205
+ /** True if the Arrow type string represents a numeric primitive (zero-copy .toArray()). */
206
+ function isNumericArrowType(typeStr) {
207
+ const t = typeStr.toUpperCase();
208
+ return (t.includes('INT') ||
209
+ t.includes('FLOAT') ||
210
+ t.includes('DOUBLE') ||
211
+ t.includes('DECIMAL') ||
212
+ t === 'TINYINT' ||
213
+ t === 'SMALLINT' ||
214
+ t === 'BIGINT' ||
215
+ t === 'HUGEINT' ||
216
+ t === 'UBIGINT' ||
217
+ t === 'UINTEGER' ||
218
+ t === 'USMALLINT' ||
219
+ t === 'UTINYINT');
220
+ }
221
+ /**
222
+ * Extract column values using the fastest available method:
223
+ * - Numeric primitives → .toArray() returns a typed array view (zero-copy),
224
+ * then Array.from() to convert to a plain JS array for downstream compat.
225
+ * - Other types → per-element .get(i) for correctness (strings, structs, etc.)
226
+ */
227
+ function extractColumnBulk(col, numRows, typeStr) {
228
+ if (isNumericArrowType(typeStr)) {
229
+ // .toArray() returns a TypedArray (Float64Array, Int32Array, etc.)
230
+ // which is a zero-copy view over the Arrow buffer.
231
+ return Array.from(col.toArray());
232
+ }
233
+ const values = new Array(numRows);
234
+ for (let i = 0; i < numRows; i++) {
235
+ values[i] = col.get(i);
236
+ }
237
+ return values;
238
+ }
239
+ /**
240
+ * Append column values from a streaming batch to an existing values array.
241
+ * Same optimisation as extractColumnBulk but appends instead of creating new.
242
+ */
243
+ function appendColumnBulk(target, col, numRows, typeStr) {
244
+ if (isNumericArrowType(typeStr)) {
245
+ const arr = col.toArray();
246
+ for (let i = 0; i < arr.length; i++) {
247
+ target.push(arr[i]);
248
+ }
249
+ return;
250
+ }
251
+ for (let i = 0; i < numRows; i++) {
252
+ target.push(col.get(i));
253
+ }
254
+ }
255
+ /** Should this column type be skipped for map attribute extraction? */
256
+ function isBinaryType(typeStr) {
257
+ return BINARY_TYPES.has(typeStr.toUpperCase());
258
+ }
259
+ export class WasmQueryEngine {
260
+ async query(connId, sql) {
261
+ const t0 = performance.now();
262
+ const sqlPreview = sql.length > 120 ? `${sql.slice(0, 120)}…` : sql;
263
+ log(`query → ${sqlPreview}`);
264
+ const db = await getDB();
265
+ const conn = await db.connect();
266
+ await ensureGeoConversionDisabled(conn);
267
+ const tConn = performance.now();
268
+ log(`query → connected in ${elapsed(t0)}`);
269
+ try {
270
+ if (connId) {
271
+ await this.configureStorage(conn, connId);
272
+ log(`query → storage configured in ${elapsed(tConn)}`);
273
+ }
274
+ const tQuery = performance.now();
275
+ const result = await conn.query(sql);
276
+ log(`query → executed in ${elapsed(tQuery)}, rows: ${result.numRows}`);
277
+ // DuckDB WASM returns an Arrow Table (bundled apache-arrow@17).
278
+ // Our project uses apache-arrow@21 — cross-version tableToIPC/tableFromIPC
279
+ // loses data rows. Extract rows directly from DuckDB's own Arrow Table.
280
+ const numRows = result.numRows;
281
+ const cols = result.schema.fields.map((f) => f.name);
282
+ const types = result.schema.fields.map((f) => String(f.type));
283
+ if (numRows === 0) {
284
+ log(`query → done (empty) in ${elapsed(t0)}`);
285
+ return {
286
+ columns: cols,
287
+ types,
288
+ rowCount: 0,
289
+ rows: []
290
+ };
291
+ }
292
+ // Extract rows directly — avoids Arrow version mismatch
293
+ const rows = result.toArray().map((row) => {
294
+ if (typeof row.toJSON === 'function')
295
+ return row.toJSON();
296
+ // Fallback: manually build row object
297
+ const obj = {};
298
+ for (const col of cols)
299
+ obj[col] = row[col];
300
+ return obj;
301
+ });
302
+ log(`query → done in ${elapsed(t0)}, ${numRows} rows, ${cols.length} cols`);
303
+ return { columns: cols, types, rowCount: numRows, rows };
304
+ }
305
+ catch (err) {
306
+ logWarn(`query → failed after ${elapsed(t0)}:`, err?.message ?? err);
307
+ throw err;
308
+ }
309
+ finally {
310
+ await conn.close();
311
+ }
312
+ }
313
+ async queryForMap(connId, sql, geomCol, geomColType, sourceCrs) {
314
+ const t0 = performance.now();
315
+ log(`queryForMap → geomCol: ${geomCol}, type: ${geomColType}, crs: ${sourceCrs ?? 'WGS84'}`);
316
+ const db = await getDB();
317
+ const conn = await db.connect();
318
+ await ensureGeoConversionDisabled(conn);
319
+ try {
320
+ if (connId) {
321
+ await this.configureStorage(conn, connId);
322
+ }
323
+ // Build geometry expression based on column type:
324
+ // - Native spatial types (GEOMETRY, WKB_BLOB, POINT, etc.) → use directly
325
+ // - BLOB/BINARY → DuckDB implicitly casts BLOB→GEOMETRY, use directly
326
+ // - Everything else (VARCHAR, JSON, STRUCT, ...) → GeoJSON text
327
+ const quoted = `"${geomCol}"`;
328
+ const upper = geomColType.toUpperCase();
329
+ // Spatial types that ST_AsWKB accepts directly (GEOMETRY, WKB_BLOB, etc.).
330
+ // Includes Arrow "Binary"/"LargeBinary" — DuckDB GEOMETRY columns from
331
+ // ST_ReadSHP/ST_Read appear as Arrow Binary but are NOT WKB blobs.
332
+ const isSpatialType = upper === 'GEOMETRY' ||
333
+ upper === 'GEOGRAPHY' ||
334
+ upper === 'WKB_BLOB' ||
335
+ upper.includes('POINT') ||
336
+ upper.includes('LINESTRING') ||
337
+ upper.includes('POLYGON') ||
338
+ upper.includes('BINARY'); // Arrow serialization of DuckDB GEOMETRY
339
+ // Actual WKB BLOB columns (e.g. GeoParquet) need explicit ST_GeomFromWKB
340
+ // because DuckDB has no implicit BLOB→GEOMETRY cast.
341
+ const isWkbBlob = upper === 'BLOB' || upper === 'BYTEA';
342
+ let wkbExpr;
343
+ let geomExpr;
344
+ if (isWkbBlob && !sourceCrs) {
345
+ // Already WKB — use directly, no spatial function calls needed.
346
+ // Avoids ST_GeomFromWKB which can fail if DuckDB auto-converted
347
+ // the column to GEOMETRY despite enable_geoparquet_conversion=false.
348
+ wkbExpr = quoted;
349
+ geomExpr = null; // geometry type detected client-side from WKB headers
350
+ }
351
+ else {
352
+ geomExpr = isSpatialType
353
+ ? quoted
354
+ : isWkbBlob
355
+ ? `ST_GeomFromWKB(${quoted})`
356
+ : `ST_GeomFromGeoJSON(${quoted})`;
357
+ // Re-project to WGS84 if the source CRS is not EPSG:4326/CRS84.
358
+ // always_xy := true forces lon/lat (x/y) axis order for both source and
359
+ // target, matching the GeoParquet convention regardless of CRS authority.
360
+ if (sourceCrs) {
361
+ geomExpr = `ST_Transform(${geomExpr}, '${sourceCrs}', 'EPSG:4326', always_xy := true)`;
362
+ }
363
+ // ST_AsWKB needed — DuckDB GEOMETRY columns (from ST_ReadSHP, ST_Read)
364
+ // use an internal binary format, not WKB, even though Arrow reports Binary type.
365
+ wkbExpr = `ST_AsWKB(${geomExpr})`;
366
+ }
367
+ // Wrap query: WKB for binary geometry, optional ST_GeometryType
368
+ const mapSql = geomExpr
369
+ ? `SELECT *, ${wkbExpr} AS __wkb, ST_GeometryType(${geomExpr}) AS __geom_type FROM (${sql}) __src`
370
+ : `SELECT *, ${wkbExpr} AS __wkb FROM (${sql}) __src`;
371
+ const result = await conn.query(mapSql);
372
+ // Extract raw WKB binary column — .get(i) returns Uint8Array from Arrow v17
373
+ const wkbCol = result.getChild('__wkb');
374
+ const wkbArrays = [];
375
+ for (let i = 0; i < wkbCol.length; i++) {
376
+ const v = wkbCol.get(i);
377
+ // Copy: Arrow .get() returns buffer views that may be invalidated
378
+ if (v)
379
+ wkbArrays.push(v instanceof Uint8Array ? v.slice() : new Uint8Array(v));
380
+ }
381
+ // Detect geometry type from first non-null row (if available)
382
+ const typeCol = result.getChild('__geom_type');
383
+ let geometryType = 'POINT';
384
+ if (typeCol) {
385
+ for (let i = 0; i < typeCol.length; i++) {
386
+ const t = typeCol.get(i);
387
+ if (t) {
388
+ geometryType = String(t);
389
+ break;
390
+ }
391
+ }
392
+ }
393
+ // Extract attribute columns (skip geometry, helper, and binary columns)
394
+ const skipCols = new Set([geomCol, '__wkb', '__geom_type']);
395
+ const attributes = new Map();
396
+ for (const field of result.schema.fields) {
397
+ if (skipCols.has(field.name))
398
+ continue;
399
+ const typeStr = String(field.type);
400
+ // Skip binary/blob columns — not useful for map tooltips, expensive to extract
401
+ if (isBinaryType(typeStr))
402
+ continue;
403
+ const col = result.getChild(field.name);
404
+ const values = extractColumnBulk(col, col.length, typeStr);
405
+ attributes.set(field.name, { values, type: typeStr });
406
+ }
407
+ log(`queryForMap → done in ${elapsed(t0)}, ${wkbArrays.length} geometries (${geometryType}), ${attributes.size} attrs`);
408
+ return { wkbArrays, geometryType, attributes, rowCount: wkbArrays.length };
409
+ }
410
+ catch (err) {
411
+ logWarn(`queryForMap → failed after ${elapsed(t0)}:`, err?.message ?? err);
412
+ throw err;
413
+ }
414
+ finally {
415
+ await conn.close();
416
+ }
417
+ }
418
+ async getSchema(connId, path) {
419
+ const t0 = performance.now();
420
+ log('getSchema →', path);
421
+ const db = await getDB();
422
+ const conn = await db.connect();
423
+ await ensureGeoConversionDisabled(conn);
424
+ try {
425
+ if (connId) {
426
+ await this.configureStorage(conn, connId);
427
+ }
428
+ const source = buildDuckDbSource(path, path);
429
+ const result = await conn.query(`DESCRIBE SELECT * FROM ${source}`);
430
+ const rows = result.toArray();
431
+ const schema = rows.map((row) => ({
432
+ name: row.column_name,
433
+ type: row.column_type,
434
+ nullable: row.null === 'YES'
435
+ }));
436
+ log(`getSchema → done in ${elapsed(t0)}, ${schema.length} fields`);
437
+ return schema;
438
+ }
439
+ catch (err) {
440
+ logWarn('getSchema → failed:', err?.message ?? err);
441
+ throw err;
442
+ }
443
+ finally {
444
+ await conn.close();
445
+ }
446
+ }
447
+ async getRowCount(connId, path) {
448
+ const t0 = performance.now();
449
+ log('getRowCount →', path);
450
+ const db = await getDB();
451
+ const conn = await db.connect();
452
+ await ensureGeoConversionDisabled(conn);
453
+ try {
454
+ if (connId) {
455
+ await this.configureStorage(conn, connId);
456
+ }
457
+ // For Parquet files, try reading row count from file footer metadata first.
458
+ // This avoids parsing column types (which can fail on exotic geometry types)
459
+ // and is faster than SELECT COUNT(*) since it reads only footer bytes.
460
+ const isParquet = /\.parquet$/i.test(path);
461
+ if (isParquet) {
462
+ try {
463
+ const metaResult = await conn.query(`SELECT SUM(num_rows)::BIGINT as cnt FROM parquet_file_metadata('${path}')`);
464
+ const metaRows = metaResult.toArray();
465
+ const count = Number(metaRows[0].cnt);
466
+ log(`getRowCount → ${count} via parquet_file_metadata in ${elapsed(t0)}`);
467
+ return count;
468
+ }
469
+ catch (metaErr) {
470
+ logWarn('getRowCount → parquet_file_metadata failed, falling back to COUNT(*):', metaErr?.message ?? metaErr);
471
+ }
472
+ }
473
+ const source = buildDuckDbSource(path, path);
474
+ const result = await conn.query(`SELECT COUNT(*) as cnt FROM ${source}`);
475
+ const rows = result.toArray();
476
+ const count = Number(rows[0].cnt);
477
+ log(`getRowCount → ${count} via COUNT(*) in ${elapsed(t0)}`);
478
+ return count;
479
+ }
480
+ catch (err) {
481
+ logWarn('getRowCount → failed:', err?.message ?? err);
482
+ throw err;
483
+ }
484
+ finally {
485
+ await conn.close();
486
+ }
487
+ }
488
+ async getSchemaAndCrs(connId, path, findGeoCol) {
489
+ const t0 = performance.now();
490
+ log('getSchemaAndCrs →', path);
491
+ const db = await getDB();
492
+ const conn = await db.connect();
493
+ await ensureGeoConversionDisabled(conn);
494
+ try {
495
+ if (connId) {
496
+ await this.configureStorage(conn, connId);
497
+ }
498
+ // Schema detection
499
+ const tSchema = performance.now();
500
+ const source = buildDuckDbSource(path, path);
501
+ const result = await conn.query(`DESCRIBE SELECT * FROM ${source}`);
502
+ const schemaRows = result.toArray();
503
+ const schema = schemaRows.map((row) => ({
504
+ name: row.column_name,
505
+ type: row.column_type,
506
+ nullable: row.null === 'YES'
507
+ }));
508
+ log(`getSchemaAndCrs → schema: ${schema.length} fields in ${elapsed(tSchema)}`);
509
+ // Geo column detection via callback (avoids importing wkb utils here)
510
+ const geomCol = findGeoCol(schema);
511
+ if (!geomCol) {
512
+ log(`getSchemaAndCrs → no geo column, done in ${elapsed(t0)}`);
513
+ return { schema, geomCol: null, crs: null };
514
+ }
515
+ // CRS detection reusing the same connection
516
+ log(`getSchemaAndCrs → geo column: ${geomCol}, detecting CRS...`);
517
+ const tCrs = performance.now();
518
+ const crs = await this.detectCrsWithConn(conn, path, geomCol);
519
+ log(`getSchemaAndCrs → CRS: ${crs ?? 'WGS84/null'} in ${elapsed(tCrs)}, total ${elapsed(t0)}`);
520
+ return { schema, geomCol, crs };
521
+ }
522
+ catch (err) {
523
+ logWarn('getSchemaAndCrs → failed:', err?.message ?? err);
524
+ throw err;
525
+ }
526
+ finally {
527
+ await conn.close();
528
+ }
529
+ }
530
+ async configureStorage(conn, connId) {
531
+ try {
532
+ // Read connection metadata from localStorage
533
+ const stored = localStorage.getItem('obstore-explore-connections');
534
+ if (!stored) {
535
+ log('configureStorage → no connections in localStorage');
536
+ return;
537
+ }
538
+ const connections = JSON.parse(stored);
539
+ const connection = connections.find((c) => c.id === connId);
540
+ if (!connection) {
541
+ logWarn(`configureStorage → connection "${connId}" not found`);
542
+ return;
543
+ }
544
+ // Azure uses direct HTTPS URLs with SAS token — no S3 config needed
545
+ if (connection.provider === 'azure') {
546
+ log('configureStorage → Azure provider, skipping S3 config');
547
+ return;
548
+ }
549
+ // Batch all SET commands into a single query to minimize web worker round-trips
550
+ const sets = [];
551
+ // Set S3 credentials from in-memory credential store
552
+ const creds = credentialStore.get(connId);
553
+ if (creds && creds.type === 'sigv4') {
554
+ sets.push(`SET s3_access_key_id = '${creds.accessKey}'`);
555
+ sets.push(`SET s3_secret_access_key = '${creds.secretKey}'`);
556
+ }
557
+ if (connection.region) {
558
+ sets.push(`SET s3_region = '${connection.region}'`);
559
+ }
560
+ if (connection.endpoint) {
561
+ const endpoint = connection.endpoint.replace(/^https?:\/\//, '');
562
+ sets.push(`SET s3_endpoint = '${endpoint}'`);
563
+ if (connection.endpoint.startsWith('http://')) {
564
+ sets.push(`SET s3_use_ssl = false`);
565
+ }
566
+ }
567
+ // Always use path-style — virtual-hosted breaks for buckets with dots
568
+ sets.push(`SET s3_url_style = 'path'`);
569
+ if (sets.length > 0) {
570
+ const t0 = performance.now();
571
+ await conn.query(`${sets.join('; ')};`);
572
+ log(`configureStorage → ${sets.length} SETs batched in ${elapsed(t0)} (provider: ${connection.provider ?? 's3'})`);
573
+ }
574
+ }
575
+ catch (err) {
576
+ console.error(LOG_PREFIX, 'configureStorage error:', err);
577
+ }
578
+ }
579
+ async detectCrs(connId, path, geomCol) {
580
+ const t0 = performance.now();
581
+ log(`detectCrs → standalone call for "${geomCol}"`, path);
582
+ const db = await getDB();
583
+ const conn = await db.connect();
584
+ await ensureGeoConversionDisabled(conn);
585
+ try {
586
+ if (connId) {
587
+ await this.configureStorage(conn, connId);
588
+ }
589
+ const crs = await this.detectCrsWithConn(conn, path, geomCol);
590
+ log(`detectCrs → ${crs ?? 'WGS84/null'} in ${elapsed(t0)}`);
591
+ return crs;
592
+ }
593
+ catch (err) {
594
+ logWarn('detectCrs → failed:', err);
595
+ return null;
596
+ }
597
+ finally {
598
+ await conn.close();
599
+ }
600
+ }
601
+ async detectCrsWithConn(conn, path, geomCol) {
602
+ // Strategy 1: GeoParquet file-level metadata (geo key in KV metadata)
603
+ try {
604
+ const t1 = performance.now();
605
+ const kvResult = await conn.query(`SELECT value FROM parquet_kv_metadata('${path}') WHERE CAST(key AS VARCHAR) = 'geo'`);
606
+ const kvRows = kvResult.toArray();
607
+ log(`detectCrs strategy 1 (kv_metadata) → ${kvRows.length} rows in ${elapsed(t1)}`);
608
+ if (kvRows.length > 0) {
609
+ const raw = kvRows[0].value;
610
+ const text = typeof raw === 'string' ? raw : new TextDecoder().decode(raw);
611
+ const geo = JSON.parse(text);
612
+ const colMeta = geo.columns?.[geomCol] ?? (geo.columns ? Object.values(geo.columns)[0] : null);
613
+ if (colMeta) {
614
+ // GeoParquet "geo" metadata is authoritative:
615
+ // has crs → extract EPSG; no crs or crs: null → WGS84 per spec
616
+ const result = colMeta.crs ? extractEpsgFromProjjson(colMeta.crs) : null;
617
+ log(`detectCrs strategy 1 → authoritative: ${result ?? 'WGS84/null'} (skipping strategy 2)`);
618
+ return result;
619
+ }
620
+ log('detectCrs strategy 1 → "geo" key found but no column metadata for', geomCol);
621
+ }
622
+ }
623
+ catch (err) {
624
+ log('detectCrs strategy 1 → skipped (not GeoParquet or no KV metadata):', err?.message ?? err);
625
+ }
626
+ // Strategy 2: Native Parquet GEOMETRY type (Parquet Format 2.11+)
627
+ // CRS is in the schema logical_type: GeometryType(crs=...)
628
+ try {
629
+ const t2 = performance.now();
630
+ const schemaResult = await conn.query(`SELECT logical_type FROM parquet_schema('${path}') WHERE name = '${geomCol}'`);
631
+ const schemaRows = schemaResult.toArray();
632
+ log(`detectCrs strategy 2 (parquet_schema) → ${schemaRows.length} rows in ${elapsed(t2)}`);
633
+ if (schemaRows.length > 0) {
634
+ const logicalType = String(schemaRows[0].logical_type ?? '');
635
+ log(`detectCrs strategy 2 → logical_type: "${logicalType}"`);
636
+ const epsg = await extractCrsFromLogicalType(logicalType, conn, path);
637
+ if (epsg) {
638
+ log(`detectCrs strategy 2 → found: ${epsg}`);
639
+ return epsg;
640
+ }
641
+ }
642
+ }
643
+ catch (err) {
644
+ log('detectCrs strategy 2 → skipped:', err?.message ?? err);
645
+ }
646
+ log('detectCrs → no CRS found, assuming WGS84');
647
+ return null;
648
+ }
649
+ queryCancellable(connId, sql) {
650
+ let cancelled = false;
651
+ let conn = null;
652
+ const result = (async () => {
653
+ const t0 = performance.now();
654
+ const sqlPreview = sql.length > 120 ? `${sql.slice(0, 120)}…` : sql;
655
+ log(`queryCancellable → ${sqlPreview}`);
656
+ const db = await getDB();
657
+ conn = await db.connect();
658
+ await ensureGeoConversionDisabled(conn);
659
+ log(`queryCancellable → connected in ${elapsed(t0)}`);
660
+ try {
661
+ if (connId) {
662
+ await this.configureStorage(conn, connId);
663
+ }
664
+ const tQuery = performance.now();
665
+ const reader = await conn.send(sql);
666
+ log(`queryCancellable → send() in ${elapsed(tQuery)}`);
667
+ const rows = [];
668
+ let cols = [];
669
+ let types = [];
670
+ const batches = reader[Symbol.asyncIterator]();
671
+ let first = true;
672
+ while (true) {
673
+ if (cancelled)
674
+ throw new QueryCancelledError();
675
+ const { value: batch, done } = await batches.next();
676
+ if (done)
677
+ break;
678
+ if (first && batch.schema) {
679
+ cols = batch.schema.fields.map((f) => f.name);
680
+ types = batch.schema.fields.map((f) => String(f.type));
681
+ first = false;
682
+ }
683
+ for (const row of batch.toArray()) {
684
+ const json = typeof row.toJSON === 'function' ? row.toJSON() : { ...row };
685
+ // Binary columns return Uint8Array views into the Arrow
686
+ // RecordBatch buffer. The streaming reader can reclaim/reuse
687
+ // buffers between batches (especially for cached queries),
688
+ // making stale views. Copy them so row data survives.
689
+ for (const key in json) {
690
+ if (json[key] instanceof Uint8Array) {
691
+ json[key] = json[key].slice();
692
+ }
693
+ }
694
+ rows.push(json);
695
+ }
696
+ }
697
+ log(`queryCancellable → done in ${elapsed(t0)}, ${rows.length} rows`);
698
+ return { columns: cols, types, rowCount: rows.length, rows };
699
+ }
700
+ catch (err) {
701
+ if (cancelled || err instanceof QueryCancelledError) {
702
+ log(`queryCancellable → cancelled after ${elapsed(t0)}`);
703
+ throw new QueryCancelledError();
704
+ }
705
+ logWarn(`queryCancellable → failed after ${elapsed(t0)}:`, err?.message ?? err);
706
+ throw err;
707
+ }
708
+ finally {
709
+ await conn?.close();
710
+ conn = null;
711
+ }
712
+ })();
713
+ const cancel = async () => {
714
+ cancelled = true;
715
+ try {
716
+ if (conn)
717
+ await conn.cancelSent();
718
+ return true;
719
+ }
720
+ catch {
721
+ return false;
722
+ }
723
+ };
724
+ return { result, cancel };
725
+ }
726
+ queryForMapCancellable(connId, sql, geomCol, geomColType, sourceCrs) {
727
+ let cancelled = false;
728
+ let conn = null;
729
+ const result = (async () => {
730
+ const t0 = performance.now();
731
+ log(`queryForMapCancellable → geomCol: ${geomCol}, type: ${geomColType}, crs: ${sourceCrs ?? 'WGS84'}`);
732
+ const db = await getDB();
733
+ conn = await db.connect();
734
+ await ensureGeoConversionDisabled(conn);
735
+ try {
736
+ if (connId) {
737
+ await this.configureStorage(conn, connId);
738
+ }
739
+ // Build geometry expression (same logic as queryForMap)
740
+ const quoted = `"${geomCol}"`;
741
+ const upper = geomColType.toUpperCase();
742
+ const isSpatialType = upper === 'GEOMETRY' ||
743
+ upper === 'GEOGRAPHY' ||
744
+ upper === 'WKB_BLOB' ||
745
+ upper.includes('POINT') ||
746
+ upper.includes('LINESTRING') ||
747
+ upper.includes('POLYGON') ||
748
+ upper.includes('BINARY');
749
+ const isWkbBlob = upper === 'BLOB' || upper === 'BYTEA';
750
+ let wkbExpr;
751
+ let geomExpr;
752
+ if (isWkbBlob && !sourceCrs) {
753
+ // Already WKB — use directly, no spatial function calls needed.
754
+ wkbExpr = quoted;
755
+ geomExpr = null; // geometry type detected client-side from WKB headers
756
+ }
757
+ else {
758
+ geomExpr = isSpatialType
759
+ ? quoted
760
+ : isWkbBlob
761
+ ? `ST_GeomFromWKB(${quoted})`
762
+ : `ST_GeomFromGeoJSON(${quoted})`;
763
+ if (sourceCrs) {
764
+ geomExpr = `ST_Transform(${geomExpr}, '${sourceCrs}', 'EPSG:4326', always_xy := true)`;
765
+ }
766
+ wkbExpr = `ST_AsWKB(${geomExpr})`;
767
+ }
768
+ const mapSql = geomExpr
769
+ ? `SELECT *, ${wkbExpr} AS __wkb, ST_GeometryType(${geomExpr}) AS __geom_type FROM (${sql}) __src`
770
+ : `SELECT *, ${wkbExpr} AS __wkb FROM (${sql}) __src`;
771
+ const reader = await conn.send(mapSql);
772
+ const wkbArrays = [];
773
+ let geometryType = 'POINT';
774
+ let geometryTypeDetected = false;
775
+ const skipCols = new Set([geomCol, '__wkb', '__geom_type']);
776
+ const attributes = new Map();
777
+ let fieldsInitialized = false;
778
+ const fieldNames = [];
779
+ const fieldTypes = new Map();
780
+ const batches = reader[Symbol.asyncIterator]();
781
+ while (true) {
782
+ if (cancelled)
783
+ throw new QueryCancelledError();
784
+ const { value: batch, done } = await batches.next();
785
+ if (done)
786
+ break;
787
+ if (!fieldsInitialized && batch.schema) {
788
+ for (const field of batch.schema.fields) {
789
+ const typeStr = String(field.type);
790
+ if (skipCols.has(field.name))
791
+ continue;
792
+ // Skip binary/blob columns — not useful for map tooltips
793
+ if (isBinaryType(typeStr))
794
+ continue;
795
+ fieldNames.push(field.name);
796
+ fieldTypes.set(field.name, typeStr);
797
+ attributes.set(field.name, { values: [], type: typeStr });
798
+ }
799
+ fieldsInitialized = true;
800
+ }
801
+ // Extract WKB and geometry type from batch
802
+ const wkbCol = batch.getChild('__wkb');
803
+ const typeCol = batch.getChild('__geom_type');
804
+ for (let i = 0; i < batch.numRows; i++) {
805
+ const v = wkbCol?.get(i);
806
+ // .get(i) returns a Uint8Array view into the batch buffer —
807
+ // copy it so data survives buffer reuse across batches.
808
+ if (v)
809
+ wkbArrays.push(v instanceof Uint8Array ? v.slice() : new Uint8Array(v));
810
+ if (!geometryTypeDetected && typeCol) {
811
+ const t = typeCol.get(i);
812
+ if (t) {
813
+ geometryType = String(t);
814
+ geometryTypeDetected = true;
815
+ }
816
+ }
817
+ }
818
+ // Extract attribute columns — type-aware bulk extraction
819
+ for (const name of fieldNames) {
820
+ const col = batch.getChild(name);
821
+ if (!col)
822
+ continue;
823
+ const attr = attributes.get(name);
824
+ appendColumnBulk(attr.values, col, batch.numRows, fieldTypes.get(name));
825
+ }
826
+ }
827
+ log(`queryForMapCancellable → done in ${elapsed(t0)}, ${wkbArrays.length} geometries (${geometryType})`);
828
+ return { wkbArrays, geometryType, attributes, rowCount: wkbArrays.length };
829
+ }
830
+ catch (err) {
831
+ if (cancelled || err instanceof QueryCancelledError) {
832
+ log(`queryForMapCancellable → cancelled after ${elapsed(t0)}`);
833
+ throw new QueryCancelledError();
834
+ }
835
+ logWarn(`queryForMapCancellable → failed after ${elapsed(t0)}:`, err?.message ?? err);
836
+ throw err;
837
+ }
838
+ finally {
839
+ await conn?.close();
840
+ conn = null;
841
+ }
842
+ })();
843
+ const cancel = async () => {
844
+ cancelled = true;
845
+ try {
846
+ if (conn)
847
+ await conn.cancelSent();
848
+ return true;
849
+ }
850
+ catch {
851
+ return false;
852
+ }
853
+ };
854
+ return { result, cancel };
855
+ }
856
+ async forceCancel() {
857
+ log('forceCancel → terminating worker');
858
+ try {
859
+ if (dbPromise) {
860
+ const db = await dbPromise;
861
+ await db.terminate();
862
+ }
863
+ }
864
+ catch (err) {
865
+ logWarn('forceCancel → terminate error:', err);
866
+ }
867
+ finally {
868
+ dbPromise = null;
869
+ geoConversionGlobal = false;
870
+ log('forceCancel → done, next getDB() will reinitialize');
871
+ }
872
+ }
873
+ async releaseMemory() {
874
+ const db = await getDB();
875
+ const conn = await db.connect();
876
+ try {
877
+ await conn.query('CALL pragma_database_size()');
878
+ await conn.query('CHECKPOINT');
879
+ }
880
+ catch {
881
+ // Ignore — checkpoint may fail on read-only/in-memory DBs
882
+ }
883
+ finally {
884
+ await conn.close();
885
+ }
886
+ }
887
+ async dispose() {
888
+ await this.releaseMemory();
889
+ }
890
+ }