@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,110 @@
1
+ import { tabResources } from './tab-resources.svelte.js';
2
+ /** Maximum number of viewer instances kept alive (mounted but hidden). */
3
+ const MAX_ALIVE = 5;
4
+ function releaseDuckDbMemory() {
5
+ import('../query/index.js')
6
+ .then(({ getQueryEngine }) => getQueryEngine().then((engine) => engine.releaseMemory()))
7
+ .catch(() => { });
8
+ }
9
+ function createTabsStore() {
10
+ let tabs = $state([]);
11
+ let activeTabId = $state(null);
12
+ // LRU order: most recently activated tab IDs first.
13
+ // Used to decide which tabs stay alive in the DOM.
14
+ let recentOrder = $state([]);
15
+ function touchRecent(id) {
16
+ recentOrder = [id, ...recentOrder.filter((r) => r !== id)];
17
+ // Dispose resources for tabs that fell out of the alive window.
18
+ // This fires asynchronously to avoid blocking the tab switch.
19
+ const evicted = recentOrder.slice(MAX_ALIVE);
20
+ for (const evictedId of evicted) {
21
+ if (tabResources.has(evictedId)) {
22
+ tabResources.dispose(evictedId);
23
+ }
24
+ }
25
+ }
26
+ function pruneRecent() {
27
+ // Remove IDs that no longer exist in tabs
28
+ const ids = new Set(tabs.map((t) => t.id));
29
+ recentOrder = recentOrder.filter((r) => ids.has(r));
30
+ }
31
+ return {
32
+ get items() {
33
+ return tabs;
34
+ },
35
+ get activeTabId() {
36
+ return activeTabId;
37
+ },
38
+ get active() {
39
+ if (!activeTabId)
40
+ return undefined;
41
+ return tabs.find((t) => t.id === activeTabId);
42
+ },
43
+ /**
44
+ * Tabs that should be kept alive in the DOM (mounted but hidden if inactive).
45
+ * Returns up to MAX_ALIVE tabs in MRU order. The active tab is always included.
46
+ */
47
+ get aliveTabs() {
48
+ const aliveIds = recentOrder.slice(0, MAX_ALIVE);
49
+ // Ensure active tab is always included
50
+ if (activeTabId && !aliveIds.includes(activeTabId)) {
51
+ aliveIds.pop();
52
+ aliveIds.unshift(activeTabId);
53
+ }
54
+ const idSet = new Set(aliveIds);
55
+ return tabs.filter((t) => idSet.has(t.id));
56
+ },
57
+ open(tab) {
58
+ // If a tab with the same id is already open, just activate it
59
+ const existing = tabs.find((t) => t.id === tab.id);
60
+ if (existing) {
61
+ activeTabId = tab.id;
62
+ touchRecent(tab.id);
63
+ return;
64
+ }
65
+ tabs = [...tabs, tab];
66
+ activeTabId = tab.id;
67
+ touchRecent(tab.id);
68
+ },
69
+ async close(id) {
70
+ const index = tabs.findIndex((t) => t.id === id);
71
+ if (index === -1)
72
+ return;
73
+ // Dispose viewer resources (maps, abort controllers, cached data)
74
+ await tabResources.dispose(id);
75
+ tabs = tabs.filter((t) => t.id !== id);
76
+ pruneRecent();
77
+ // If we closed the active tab, activate an adjacent one
78
+ if (activeTabId === id) {
79
+ if (tabs.length === 0) {
80
+ activeTabId = null;
81
+ }
82
+ else {
83
+ // Prefer the most recently used tab, or the tab at the same index
84
+ const nextId = recentOrder[0] ?? tabs[Math.min(index, tabs.length - 1)]?.id;
85
+ activeTabId = nextId ?? null;
86
+ }
87
+ }
88
+ releaseDuckDbMemory();
89
+ },
90
+ async closeOthers(id) {
91
+ // Dispose resources for all tabs except the kept one
92
+ await tabResources.disposeAllExcept(id);
93
+ tabs = tabs.filter((t) => t.id === id);
94
+ activeTabId = id;
95
+ recentOrder = [id];
96
+ releaseDuckDbMemory();
97
+ },
98
+ setActive(id) {
99
+ if (tabs.find((t) => t.id === id)) {
100
+ activeTabId = id;
101
+ touchRecent(id);
102
+ }
103
+ },
104
+ update(id, partial) {
105
+ tabs = tabs.map((t) => (t.id === id ? { ...t, ...partial } : t));
106
+ }
107
+ };
108
+ }
109
+ export const tabStore = createTabsStore();
110
+ export { tabStore as tabs };
@@ -0,0 +1,14 @@
1
+ declare module 'notebookjs' {
2
+ interface Notebook {
3
+ render(): HTMLElement;
4
+ }
5
+ const nb: {
6
+ prefix: string;
7
+ markdown: (md: string) => string;
8
+ ansi: (text: string) => string;
9
+ highlighter: (code: string, lang: string) => string;
10
+ sanitizer: (html: string) => string;
11
+ parse(json: unknown): Notebook;
12
+ };
13
+ export default nb;
14
+ }
@@ -0,0 +1,47 @@
1
+ export interface FileEntry {
2
+ name: string;
3
+ path: string;
4
+ is_dir: boolean;
5
+ size: number;
6
+ modified: number;
7
+ extension: string;
8
+ }
9
+ export interface Connection {
10
+ id: string;
11
+ name: string;
12
+ provider: 's3' | 'gcs' | 'r2' | 'minio' | 'azure' | 'storj';
13
+ endpoint: string;
14
+ bucket: string;
15
+ region: string;
16
+ anonymous: boolean;
17
+ authMethod?: 'sigv4' | 'sas-token';
18
+ rootPrefix?: string;
19
+ }
20
+ export interface ConnectionConfig {
21
+ name: string;
22
+ provider: 's3' | 'gcs' | 'r2' | 'minio' | 'azure' | 'storj';
23
+ endpoint: string;
24
+ bucket: string;
25
+ region: string;
26
+ access_key?: string;
27
+ secret_key?: string;
28
+ sas_token?: string;
29
+ anonymous: boolean;
30
+ authMethod?: 'sigv4' | 'sas-token';
31
+ rootPrefix?: string;
32
+ }
33
+ export interface Tab {
34
+ id: string;
35
+ name: string;
36
+ path: string;
37
+ source: 'remote' | 'url';
38
+ connectionId?: string;
39
+ extension: string;
40
+ size?: number;
41
+ }
42
+ export interface WriteResult {
43
+ key: string;
44
+ size: number;
45
+ e_tag?: string;
46
+ }
47
+ export type Theme = 'light' | 'dark' | 'system';
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,54 @@
1
+ # utils/
2
+
3
+ Pure utility modules. No Svelte dependency except `url-state.ts` and `analytics.ts`.
4
+
5
+ ```mermaid
6
+ graph TD
7
+ subgraph "Published (npm)"
8
+ WKB[wkb.ts<br/>parseWKB, findGeoColumn]
9
+ GA[geoarrow.ts<br/>buildGeoArrowTables]
10
+ SU[storage-url.ts<br/>parseStorageUrl]
11
+ PM[parquet-metadata.ts<br/>readParquetMetadata]
12
+ FMT[format.ts<br/>formatFileSize, formatDate]
13
+ HEX[hex.ts<br/>generateHexDump]
14
+ CT[column-types.ts<br/>classifyType]
15
+ end
16
+ subgraph "Internal only"
17
+ DECK[deck.ts<br/>createDeckOverlay, createGeoArrowOverlay]
18
+ URL[url.ts<br/>buildHttpsUrl, buildDuckDbUrl]
19
+ US[url-state.ts<br/>syncUrlParam — uses $app]
20
+ ARC[archive.ts<br/>streamZip/Tar]
21
+ PMT[pmtiles.ts<br/>loadPmtiles]
22
+ SHIKI[shiki.ts<br/>highlightCode]
23
+ ZARR[zarr.ts<br/>fetchConsolidated]
24
+ end
25
+ WKB --> GA
26
+ PM --> GA
27
+ GA --> DECK
28
+ ```
29
+
30
+ | File | Key Exports | Used by |
31
+ |------|-------------|---------|
32
+ | `wkb.ts` | `parseWKB()`, `toBinary()`, `findGeoColumn()`, `findGeoColumnFromRows()` | TableViewer, GeoParquetMapViewer, lib/index.ts |
33
+ | `geoarrow.ts` | `buildGeoArrowTables()`, `normalizeGeomType()` | TableViewer, GeoParquetMapViewer, lib/index.ts |
34
+ | `storage-url.ts` | `parseStorageUrl()`, `looksLikeUrl()`, `Defaults` | ConnectionDialog, Sidebar, lib/index.ts |
35
+ | `parquet-metadata.ts` | `readParquetMetadata()`, `extractEpsgFromGeoMeta()`, `extractBounds()` | TableViewer, lib/index.ts |
36
+ | `format.ts` | `formatFileSize()`, `formatDate()`, `getFileExtension()` | StatusBar, FileRow, ArchiveViewer, RawViewer, lib/index.ts |
37
+ | `hex.ts` | `generateHexDump()` | RawViewer, lib/index.ts |
38
+ | `column-types.ts` | `classifyType()`, `typeColor()`, `typeLabel()` | TableGrid, lib/index.ts |
39
+ | `deck.ts` | `createDeckOverlay()`, `createGeoArrowOverlay()`, `createGeoArrowLayers()` | FlatGeobufViewer, GeoParquetMapViewer |
40
+ | `url.ts` | `buildHttpsUrl()`, `buildDuckDbUrl()`, `canStreamDirectly()` | TabBar, FileTreeSidebar, CogViewer, TableViewer, FlatGeobufViewer, ArchiveViewer, MediaViewer, CopcViewer, PdfViewer, ZarrMapViewer, StacMapViewer, ZarrViewer, CodeViewer, ImageViewer, PmtilesViewer, TableToolbar |
41
+ | `archive.ts` | `streamZipEntriesFromUrl()`, `streamTarEntriesFromUrl()`, `listContents()` | ArchiveViewer |
42
+ | `pmtiles.ts` | `loadPmtiles()`, `buildPmtilesLayers()` | PmtilesMapView, PmtilesArchiveView, PmtilesViewer |
43
+ | `shiki.ts` | `highlightCode()`, `extensionToShikiLang()` | PmtilesArchiveView, NotebookViewer, CodeViewer |
44
+ | `zarr.ts` | `fetchConsolidated()`, `probeWithZarrita()` | ZarrViewer |
45
+ | `url-state.ts` | `syncUrlParam()`, `updateUrlView()` | Sidebar, FileTreeSidebar, TableViewer, ZarrViewer, CodeViewer, PmtilesViewer, +page.svelte |
46
+ | `analytics.ts` | `initAnalytics()`, `capturePageview()` | +layout.svelte |
47
+ | `export.ts` | `exportToCsv()`, `exportToJson()` | TableStatusBar |
48
+ | `pdf.ts` | `loadPdfDocument()` | PdfViewer |
49
+ | `model3d.ts` | `createModelScene()`, `loadModel()` | ModelViewer |
50
+ | `markdown.ts` | `renderMarkdown()`, `detectRTL()` | MarkdownViewer |
51
+ | `map-selection.ts` | `setupSelectionLayer()`, `updateSelection()` | PmtilesMapView, MapViewer |
52
+ | `host-detection.ts` | `detectHostBucket()` | stores/connections, Sidebar |
53
+ | `markdown-sql.ts` | `parseMarkdownDocument()`, `interpolateTemplates()` | MarkdownViewer |
54
+ | `evidence-context.ts` | `EvidenceContext` | MarkdownViewer |
@@ -0,0 +1,10 @@
1
+ /**
2
+ * PostHog analytics — cookieless by default (memory persistence).
3
+ *
4
+ * Env vars (PUBLIC_ prefix = available client-side in SvelteKit):
5
+ * PUBLIC_POSTHOG_KEY — project API key
6
+ * PUBLIC_POSTHOG_HOST — ingestion endpoint (default: EU cloud)
7
+ */
8
+ export declare function initAnalytics(): void;
9
+ export declare function capturePageview(url: string): void;
10
+ export declare function capturePageleave(): void;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * PostHog analytics — cookieless by default (memory persistence).
3
+ *
4
+ * Env vars (PUBLIC_ prefix = available client-side in SvelteKit):
5
+ * PUBLIC_POSTHOG_KEY — project API key
6
+ * PUBLIC_POSTHOG_HOST — ingestion endpoint (default: EU cloud)
7
+ */
8
+ import posthog from 'posthog-js';
9
+ import { browser } from '$app/environment';
10
+ import { env } from '$env/dynamic/public';
11
+ let initialized = false;
12
+ export function initAnalytics() {
13
+ if (!browser || initialized)
14
+ return;
15
+ const key = env.PUBLIC_POSTHOG_KEY;
16
+ if (!key)
17
+ return; // silently skip if no key configured
18
+ const host = env.PUBLIC_POSTHOG_HOST || 'https://eu.i.posthog.com';
19
+ posthog.init(key, {
20
+ api_host: host,
21
+ person_profiles: 'identified_only',
22
+ capture_pageview: false, // we track manually via afterNavigate
23
+ capture_pageleave: true,
24
+ persistence: 'memory', // cookieless — no consent banner needed
25
+ autocapture: true
26
+ });
27
+ initialized = true;
28
+ }
29
+ export function capturePageview(url) {
30
+ if (!initialized)
31
+ return;
32
+ posthog.capture('$pageview', { $current_url: url });
33
+ }
34
+ export function capturePageleave() {
35
+ if (!initialized)
36
+ return;
37
+ posthog.capture('$pageleave');
38
+ }
@@ -0,0 +1,70 @@
1
+ import { type Entry } from '@zip.js/zip.js';
2
+ export interface ArchiveEntry {
3
+ filename: string;
4
+ directory: boolean;
5
+ compressedSize: number;
6
+ uncompressedSize: number;
7
+ lastModified: Date;
8
+ /** TAR only: byte offset of the file data within the archive */
9
+ dataOffset?: number;
10
+ }
11
+ export type ArchiveFormat = 'zip' | 'tar' | 'tar.gz' | 'unsupported';
12
+ export declare function detectArchiveFormat(filename: string): ArchiveFormat;
13
+ /**
14
+ * Stream ZIP entries from a URL using HTTP range requests.
15
+ * Uses getEntriesGenerator() so the UI can render progressively.
16
+ */
17
+ export declare function streamZipEntriesFromUrl(url: string, signal?: AbortSignal): AsyncGenerator<{
18
+ zipEntries: Entry[];
19
+ archiveEntries: ArchiveEntry[];
20
+ }>;
21
+ /**
22
+ * Read ZIP entries from an in-memory buffer (non-streaming, fast).
23
+ */
24
+ export declare function readZipEntriesFromBuffer(data: Uint8Array): Promise<{
25
+ entries: Entry[];
26
+ entryList: ArchiveEntry[];
27
+ }>;
28
+ /**
29
+ * Stream TAR entries from a URL using batched HTTP range requests.
30
+ * Yields a batch of entries after each chunk is processed,
31
+ * so the UI can render progressively while scanning continues.
32
+ */
33
+ export declare function streamTarEntriesFromUrl(url: string, signal?: AbortSignal): AsyncGenerator<ArchiveEntry[]>;
34
+ /**
35
+ * Read TAR entries from an in-memory buffer (non-streaming, fast).
36
+ */
37
+ export declare function readTarEntriesFromBuffer(data: Uint8Array): {
38
+ entryList: ArchiveEntry[];
39
+ };
40
+ /**
41
+ * Decompress gzip data using the browser's DecompressionStream.
42
+ * Requires full download — gzip does not support random access.
43
+ */
44
+ export declare function decompressGzip(data: Uint8Array): Promise<Uint8Array>;
45
+ /**
46
+ * Stream tar.gz entries by fetching the URL, piping through DecompressionStream,
47
+ * and parsing tar headers progressively as decompressed chunks arrive.
48
+ *
49
+ * Gzip doesn't support random access, so we must download sequentially,
50
+ * but entries appear immediately — no waiting for the full download.
51
+ *
52
+ * Yields { entries, chunk } so the caller can optionally accumulate raw
53
+ * decompressed data for later file extraction.
54
+ */
55
+ export declare function streamTarGzEntriesFromUrl(url: string, signal?: AbortSignal): AsyncGenerator<{
56
+ entries: ArchiveEntry[];
57
+ chunk: Uint8Array;
58
+ }>;
59
+ /**
60
+ * List directory contents at a given prefix.
61
+ * Returns immediate children: directories (as path strings) and files.
62
+ */
63
+ export declare function listContents(entryList: ArchiveEntry[], prefix: string): {
64
+ directories: string[];
65
+ files: ArchiveEntry[];
66
+ };
67
+ export declare function clampPrefix(prefix: string, maxDepth?: number): string;
68
+ export declare function downloadZipEntry(entry: Entry): Promise<void>;
69
+ export declare function downloadTarEntryFromUrl(url: string, entry: ArchiveEntry): Promise<void>;
70
+ export declare function downloadTarEntryFromBuffer(data: Uint8Array, entry: ArchiveEntry): void;
@@ -0,0 +1,333 @@
1
+ import { BlobReader, configure, HttpReader, ZipReader } from '@zip.js/zip.js';
2
+ // Enable web workers for non-blocking ZIP parsing
3
+ configure({ useWebWorkers: true });
4
+ // ── Format Detection ───────────────────────────────────────────────────
5
+ export function detectArchiveFormat(filename) {
6
+ const lower = filename.toLowerCase();
7
+ if (lower.endsWith('.zip'))
8
+ return 'zip';
9
+ if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz'))
10
+ return 'tar.gz';
11
+ if (lower.endsWith('.tar'))
12
+ return 'tar';
13
+ if (lower.endsWith('.gz'))
14
+ return 'tar.gz';
15
+ return 'unsupported';
16
+ }
17
+ // ── ZIP (streaming) ────────────────────────────────────────────────────
18
+ const ZIP_BATCH_SIZE = 500;
19
+ /**
20
+ * Stream ZIP entries from a URL using HTTP range requests.
21
+ * Uses getEntriesGenerator() so the UI can render progressively.
22
+ */
23
+ export async function* streamZipEntriesFromUrl(url, signal) {
24
+ const httpReader = new HttpReader(url, { forceRangeRequests: true });
25
+ const reader = new ZipReader(httpReader);
26
+ let zBatch = [];
27
+ let aBatch = [];
28
+ try {
29
+ for await (const entry of reader.getEntriesGenerator()) {
30
+ if (signal?.aborted)
31
+ return;
32
+ zBatch.push(entry);
33
+ aBatch.push({
34
+ filename: entry.filename,
35
+ directory: entry.directory,
36
+ compressedSize: entry.compressedSize,
37
+ uncompressedSize: entry.uncompressedSize,
38
+ lastModified: entry.lastModDate
39
+ });
40
+ if (zBatch.length >= ZIP_BATCH_SIZE) {
41
+ yield { zipEntries: zBatch, archiveEntries: aBatch };
42
+ zBatch = [];
43
+ aBatch = [];
44
+ }
45
+ }
46
+ if (zBatch.length > 0) {
47
+ yield { zipEntries: zBatch, archiveEntries: aBatch };
48
+ }
49
+ }
50
+ finally {
51
+ await reader.close();
52
+ }
53
+ }
54
+ /**
55
+ * Read ZIP entries from an in-memory buffer (non-streaming, fast).
56
+ */
57
+ export async function readZipEntriesFromBuffer(data) {
58
+ const blob = new Blob([data]);
59
+ const reader = new ZipReader(new BlobReader(blob));
60
+ const entries = await reader.getEntries();
61
+ return {
62
+ entries,
63
+ entryList: entries.map((e) => ({
64
+ filename: e.filename,
65
+ directory: e.directory,
66
+ compressedSize: e.compressedSize,
67
+ uncompressedSize: e.uncompressedSize,
68
+ lastModified: e.lastModDate
69
+ }))
70
+ };
71
+ }
72
+ // ── TAR (streaming) ────────────────────────────────────────────────────
73
+ const TAR_HEADER_SIZE = 512;
74
+ const TAR_CHUNK_SIZE = 256 * 1024; // 256 KB per range request
75
+ /**
76
+ * Parse a single 512-byte TAR header.
77
+ * Returns null for end-of-archive (all-zero block).
78
+ */
79
+ function parseTarHeader(header) {
80
+ if (header.every((b) => b === 0))
81
+ return null;
82
+ const dec = new TextDecoder('ascii');
83
+ const name = dec.decode(header.slice(0, 100)).replace(/\0+$/, '');
84
+ const prefix = dec.decode(header.slice(345, 500)).replace(/\0+$/, '');
85
+ let fullName = prefix ? `${prefix}/${name}` : name;
86
+ // Normalize: strip leading ./ (common in tarballs created with `tar -czf`)
87
+ if (fullName.startsWith('./'))
88
+ fullName = fullName.slice(2);
89
+ const sizeStr = dec.decode(header.slice(124, 136)).replace(/\0+$/, '').trim();
90
+ const size = parseInt(sizeStr, 8) || 0;
91
+ const mtimeStr = dec.decode(header.slice(136, 148)).replace(/\0+$/, '').trim();
92
+ const mtime = new Date((parseInt(mtimeStr, 8) || 0) * 1000);
93
+ const typeFlag = String.fromCharCode(header[156]);
94
+ const isDir = typeFlag === '5' || fullName.endsWith('/');
95
+ return {
96
+ filename: fullName,
97
+ directory: isDir,
98
+ compressedSize: size,
99
+ uncompressedSize: size,
100
+ lastModified: mtime,
101
+ dataOffset: 0
102
+ };
103
+ }
104
+ /** Returns true for bare root entries (e.g. ".", "") that should be skipped. */
105
+ function isRootEntry(entry) {
106
+ const f = entry.filename.replace(/\/+$/, '');
107
+ return f === '' || f === '.';
108
+ }
109
+ /**
110
+ * Stream TAR entries from a URL using batched HTTP range requests.
111
+ * Yields a batch of entries after each chunk is processed,
112
+ * so the UI can render progressively while scanning continues.
113
+ */
114
+ export async function* streamTarEntriesFromUrl(url, signal) {
115
+ const head = await fetch(url, { method: 'HEAD', signal });
116
+ const totalSize = Number(head.headers.get('content-length'));
117
+ if (!totalSize || totalSize <= 0)
118
+ throw new Error('Cannot determine TAR file size');
119
+ let offset = 0;
120
+ while (offset < totalSize) {
121
+ if (signal?.aborted)
122
+ return;
123
+ const end = Math.min(offset + TAR_CHUNK_SIZE - 1, totalSize - 1);
124
+ const res = await fetch(url, {
125
+ headers: { Range: `bytes=${offset}-${end}` },
126
+ signal
127
+ });
128
+ const chunk = new Uint8Array(await res.arrayBuffer());
129
+ const batch = [];
130
+ let localOffset = 0;
131
+ while (localOffset + TAR_HEADER_SIZE <= chunk.length) {
132
+ const header = chunk.slice(localOffset, localOffset + TAR_HEADER_SIZE);
133
+ const entry = parseTarHeader(header);
134
+ if (!entry) {
135
+ if (batch.length > 0)
136
+ yield batch;
137
+ return;
138
+ }
139
+ const dataBlocks = Math.ceil(entry.uncompressedSize / TAR_HEADER_SIZE);
140
+ const entryTotalSize = TAR_HEADER_SIZE + dataBlocks * TAR_HEADER_SIZE;
141
+ if (!isRootEntry(entry)) {
142
+ entry.dataOffset = offset + localOffset + TAR_HEADER_SIZE;
143
+ batch.push(entry);
144
+ }
145
+ if (localOffset + entryTotalSize > chunk.length) {
146
+ offset += localOffset + entryTotalSize;
147
+ localOffset = chunk.length;
148
+ }
149
+ else {
150
+ localOffset += entryTotalSize;
151
+ }
152
+ }
153
+ if (batch.length > 0)
154
+ yield batch;
155
+ if (localOffset <= chunk.length) {
156
+ offset += localOffset || TAR_CHUNK_SIZE;
157
+ }
158
+ }
159
+ }
160
+ /**
161
+ * Read TAR entries from an in-memory buffer (non-streaming, fast).
162
+ */
163
+ export function readTarEntriesFromBuffer(data) {
164
+ const entries = [];
165
+ let offset = 0;
166
+ while (offset + TAR_HEADER_SIZE <= data.length) {
167
+ const header = data.slice(offset, offset + TAR_HEADER_SIZE);
168
+ const entry = parseTarHeader(header);
169
+ if (!entry)
170
+ break;
171
+ const dataBlocks = Math.ceil(entry.uncompressedSize / TAR_HEADER_SIZE);
172
+ if (!isRootEntry(entry)) {
173
+ entry.dataOffset = offset + TAR_HEADER_SIZE;
174
+ entries.push(entry);
175
+ }
176
+ offset += TAR_HEADER_SIZE + dataBlocks * TAR_HEADER_SIZE;
177
+ }
178
+ return { entryList: entries };
179
+ }
180
+ // ── TAR.GZ ─────────────────────────────────────────────────────────────
181
+ /**
182
+ * Decompress gzip data using the browser's DecompressionStream.
183
+ * Requires full download — gzip does not support random access.
184
+ */
185
+ export async function decompressGzip(data) {
186
+ const ds = new DecompressionStream('gzip');
187
+ const decompressedStream = new Blob([data]).stream().pipeThrough(ds);
188
+ const buf = await new Response(decompressedStream).arrayBuffer();
189
+ return new Uint8Array(buf);
190
+ }
191
+ const TAR_GZ_BATCH_SIZE = 200;
192
+ /**
193
+ * Stream tar.gz entries by fetching the URL, piping through DecompressionStream,
194
+ * and parsing tar headers progressively as decompressed chunks arrive.
195
+ *
196
+ * Gzip doesn't support random access, so we must download sequentially,
197
+ * but entries appear immediately — no waiting for the full download.
198
+ *
199
+ * Yields { entries, chunk } so the caller can optionally accumulate raw
200
+ * decompressed data for later file extraction.
201
+ */
202
+ export async function* streamTarGzEntriesFromUrl(url, signal) {
203
+ const response = await fetch(url, { signal });
204
+ if (!response.body)
205
+ throw new Error('No response body');
206
+ const ds = new DecompressionStream('gzip');
207
+ const decompressed = response.body.pipeThrough(ds);
208
+ const reader = decompressed.getReader();
209
+ let buffer = new Uint8Array(0);
210
+ let tarOffset = 0; // absolute byte offset in decompressed tar stream
211
+ while (true) {
212
+ if (signal?.aborted)
213
+ return;
214
+ const { done, value } = await reader.read();
215
+ if (done)
216
+ break;
217
+ // Append new decompressed chunk to working buffer
218
+ const prev = buffer;
219
+ buffer = new Uint8Array(prev.length + value.length);
220
+ buffer.set(prev);
221
+ buffer.set(value, prev.length);
222
+ const batch = [];
223
+ let consumed = 0;
224
+ // Parse as many complete tar entries as possible
225
+ while (buffer.length - consumed >= TAR_HEADER_SIZE) {
226
+ const header = buffer.slice(consumed, consumed + TAR_HEADER_SIZE);
227
+ const entry = parseTarHeader(header);
228
+ if (!entry) {
229
+ // End-of-archive marker (two 512-byte zero blocks)
230
+ if (batch.length > 0) {
231
+ yield { entries: batch, chunk: buffer.slice(0, consumed) };
232
+ }
233
+ return;
234
+ }
235
+ const dataBlocks = Math.ceil(entry.uncompressedSize / TAR_HEADER_SIZE);
236
+ const entryTotalSize = TAR_HEADER_SIZE + dataBlocks * TAR_HEADER_SIZE;
237
+ if (buffer.length - consumed < entryTotalSize) {
238
+ // Not enough data yet for header + file data, wait for more
239
+ break;
240
+ }
241
+ if (!isRootEntry(entry)) {
242
+ entry.dataOffset = tarOffset + consumed + TAR_HEADER_SIZE;
243
+ batch.push(entry);
244
+ }
245
+ consumed += entryTotalSize;
246
+ if (batch.length >= TAR_GZ_BATCH_SIZE) {
247
+ yield { entries: batch.splice(0), chunk: buffer.slice(0, consumed) };
248
+ }
249
+ }
250
+ // Yield any remaining entries in this chunk
251
+ if (batch.length > 0) {
252
+ yield { entries: batch, chunk: buffer.slice(0, consumed) };
253
+ }
254
+ tarOffset += consumed;
255
+ buffer = buffer.slice(consumed);
256
+ }
257
+ }
258
+ // ── Listing (universal) ────────────────────────────────────────────────
259
+ const MAX_BROWSE_DEPTH = 10;
260
+ /**
261
+ * List directory contents at a given prefix.
262
+ * Returns immediate children: directories (as path strings) and files.
263
+ */
264
+ export function listContents(entryList, prefix) {
265
+ const canonical = prefix.length > 0 && !prefix.endsWith('/') ? `${prefix}/` : prefix;
266
+ const files = [];
267
+ const directories = new Set();
268
+ for (const entry of entryList) {
269
+ if (!entry.filename.startsWith(canonical))
270
+ continue;
271
+ const remainder = entry.filename.slice(canonical.length);
272
+ if (remainder.length === 0)
273
+ continue;
274
+ const slashIndex = remainder.indexOf('/');
275
+ if (slashIndex === -1) {
276
+ if (!entry.directory)
277
+ files.push(entry);
278
+ }
279
+ else {
280
+ const dirName = remainder.slice(0, slashIndex);
281
+ const dirPath = `${canonical}${dirName}`;
282
+ if (dirName.length > 0)
283
+ directories.add(dirPath);
284
+ }
285
+ }
286
+ return {
287
+ directories: [...directories].sort(),
288
+ files: files.sort((a, b) => a.filename.localeCompare(b.filename))
289
+ };
290
+ }
291
+ export function clampPrefix(prefix, maxDepth = MAX_BROWSE_DEPTH) {
292
+ const parts = prefix.split('/').filter(Boolean);
293
+ if (parts.length <= maxDepth)
294
+ return prefix;
295
+ return parts.slice(0, maxDepth).join('/');
296
+ }
297
+ // ── Download ───────────────────────────────────────────────────────────
298
+ export async function downloadZipEntry(entry) {
299
+ if (entry.directory || !entry.getData)
300
+ return;
301
+ const { BlobWriter, getMimeType } = await import('@zip.js/zip.js');
302
+ const mimeType = getMimeType(entry.filename);
303
+ const blob = await entry.getData(new BlobWriter(mimeType));
304
+ triggerBlobDownload(blob, entry.filename.split('/').pop() || 'file');
305
+ }
306
+ export async function downloadTarEntryFromUrl(url, entry) {
307
+ if (entry.directory || entry.dataOffset == null)
308
+ return;
309
+ const res = await fetch(url, {
310
+ headers: {
311
+ Range: `bytes=${entry.dataOffset}-${entry.dataOffset + entry.uncompressedSize - 1}`
312
+ }
313
+ });
314
+ const blob = await res.blob();
315
+ triggerBlobDownload(blob, entry.filename.split('/').pop() || 'file');
316
+ }
317
+ export function downloadTarEntryFromBuffer(data, entry) {
318
+ if (entry.directory || entry.dataOffset == null)
319
+ return;
320
+ const slice = data.slice(entry.dataOffset, entry.dataOffset + entry.uncompressedSize);
321
+ const blob = new Blob([slice]);
322
+ triggerBlobDownload(blob, entry.filename.split('/').pop() || 'file');
323
+ }
324
+ function triggerBlobDownload(blob, filename) {
325
+ const url = URL.createObjectURL(blob);
326
+ const a = document.createElement('a');
327
+ a.href = url;
328
+ a.download = filename;
329
+ document.body.appendChild(a);
330
+ a.click();
331
+ a.remove();
332
+ setTimeout(() => URL.revokeObjectURL(url), 1000);
333
+ }