@walkthru-earth/objex 1.3.1 → 1.4.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 (184) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +20 -12
  3. package/dist/components/browser/FileTreeSidebar.svelte +32 -17
  4. package/dist/components/layout/AboutSheet.svelte +5 -2
  5. package/dist/components/layout/ConnectionDialog.svelte +1 -1
  6. package/dist/components/layout/SettingsSheet.svelte +237 -0
  7. package/dist/components/layout/SettingsSheet.svelte.d.ts +6 -0
  8. package/dist/components/layout/Sidebar.svelte +73 -6
  9. package/dist/components/layout/Sidebar.svelte.d.ts +4 -1
  10. package/dist/components/layout/StatusBar.svelte +1 -1
  11. package/dist/components/layout/TabBar.svelte +2 -2
  12. package/dist/components/ui/context-menu/context-menu-radio-group.svelte.d.ts +1 -1
  13. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte.d.ts +1 -1
  14. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +1 -1
  15. package/dist/components/ui/input/input.svelte.d.ts +1 -1
  16. package/dist/components/ui/resizable/index.d.ts +1 -1
  17. package/dist/components/ui/resizable/index.js +2 -2
  18. package/dist/components/ui/slider/index.d.ts +3 -0
  19. package/dist/components/ui/slider/index.js +5 -0
  20. package/dist/components/ui/slider/range-slider.svelte +94 -0
  21. package/dist/components/ui/slider/range-slider.svelte.d.ts +21 -0
  22. package/dist/components/ui/slider/slider.svelte +83 -0
  23. package/dist/components/ui/slider/slider.svelte.d.ts +7 -0
  24. package/dist/components/viewers/ArchiveViewer.svelte +2 -2
  25. package/dist/components/viewers/CodeViewer.svelte +31 -22
  26. package/dist/components/viewers/CogControls.svelte +338 -184
  27. package/dist/components/viewers/CogControls.svelte.d.ts +33 -10
  28. package/dist/components/viewers/CogViewer.svelte +263 -112
  29. package/dist/components/viewers/CopcViewer.svelte +1 -1
  30. package/dist/components/viewers/FlatGeobufViewer.svelte +1 -1
  31. package/dist/components/viewers/GeoParquetMapViewer.svelte +6 -6
  32. package/dist/components/viewers/GeoParquetMapViewer.svelte.d.ts +1 -1
  33. package/dist/components/viewers/ImageViewer.svelte +2 -2
  34. package/dist/components/viewers/MarkdownViewer.svelte +12 -9
  35. package/dist/components/viewers/MediaViewer.svelte +2 -2
  36. package/dist/components/viewers/ModelViewer.svelte +1 -1
  37. package/dist/components/viewers/MultiCogViewer.svelte +467 -102
  38. package/dist/components/viewers/MultiCogViewer.svelte.d.ts +1 -1
  39. package/dist/components/viewers/NotebookViewer.svelte +6 -3
  40. package/dist/components/viewers/PdfViewer.svelte +2 -2
  41. package/dist/components/viewers/PmtilesViewer.svelte +3 -6
  42. package/dist/components/viewers/RawViewer.svelte +6 -3
  43. package/dist/components/viewers/StacMapViewer.svelte +1 -1
  44. package/dist/components/viewers/StacMosaicViewer.svelte +1760 -408
  45. package/dist/components/viewers/StacMosaicViewer.svelte.d.ts +1 -1
  46. package/dist/components/viewers/StacTabViewer.svelte +24 -13
  47. package/dist/components/viewers/StacTabViewer.svelte.d.ts +1 -1
  48. package/dist/components/viewers/TableGrid.svelte +4 -4
  49. package/dist/components/viewers/TableStatusBar.svelte +1 -1
  50. package/dist/components/viewers/TableToolbar.svelte +1 -1
  51. package/dist/components/viewers/TableViewer.svelte +25 -17
  52. package/dist/components/viewers/TableViewer.svelte.d.ts +1 -0
  53. package/dist/components/viewers/ViewerRouter.svelte +16 -8
  54. package/dist/components/viewers/ZarrMapViewer.svelte +11 -9
  55. package/dist/components/viewers/ZarrViewer.svelte +4 -4
  56. package/dist/components/viewers/cog/ChannelPicker.svelte +83 -0
  57. package/dist/components/viewers/cog/ChannelPicker.svelte.d.ts +13 -0
  58. package/dist/components/viewers/cog/PixelInspectorPanel.svelte +87 -0
  59. package/dist/components/viewers/cog/PixelInspectorPanel.svelte.d.ts +17 -0
  60. package/dist/components/viewers/cog/buildRgbLayer.d.ts +78 -0
  61. package/dist/components/viewers/cog/buildRgbLayer.js +176 -0
  62. package/dist/components/viewers/map/AttributeTable.svelte +1 -1
  63. package/dist/components/viewers/map/MapContainer.svelte +37 -11
  64. package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte +1 -1
  65. package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte +1 -1
  66. package/dist/components/viewers/stac/StacDatetimeBar.svelte +175 -0
  67. package/dist/components/viewers/stac/StacDatetimeBar.svelte.d.ts +10 -0
  68. package/dist/components/viewers/stac/StacFilterPanel.svelte +243 -0
  69. package/dist/components/viewers/stac/StacFilterPanel.svelte.d.ts +14 -0
  70. package/dist/components/viewers/stac/StacItemInspector.svelte +223 -0
  71. package/dist/components/viewers/stac/StacItemInspector.svelte.d.ts +10 -0
  72. package/dist/components/viewers/stac/StacItemStrip.svelte +228 -0
  73. package/dist/components/viewers/stac/StacItemStrip.svelte.d.ts +12 -0
  74. package/dist/file-icons/index.d.ts +1 -1
  75. package/dist/file-icons/index.js +1 -1
  76. package/dist/i18n/ar.js +110 -2
  77. package/dist/i18n/en.js +110 -2
  78. package/dist/index.d.ts +2 -28
  79. package/dist/index.js +7 -23
  80. package/dist/query/engine.d.ts +10 -0
  81. package/dist/query/source.js +1 -1
  82. package/dist/query/stac-source-factory.d.ts +65 -0
  83. package/dist/query/stac-source-factory.js +77 -0
  84. package/dist/query/stac-source-parquet.d.ts +135 -0
  85. package/dist/query/stac-source-parquet.js +465 -0
  86. package/dist/query/wasm.d.ts +8 -0
  87. package/dist/query/wasm.js +304 -2
  88. package/dist/storage/presign.js +1 -1
  89. package/dist/storage/providers.js +5 -5
  90. package/dist/stores/config.svelte.d.ts +15 -0
  91. package/dist/stores/config.svelte.js +46 -0
  92. package/dist/stores/connections.svelte.d.ts +2 -2
  93. package/dist/stores/connections.svelte.js +1 -2
  94. package/dist/stores/files.svelte.d.ts +1 -1
  95. package/dist/stores/files.svelte.js +1 -1
  96. package/dist/stores/query-history.svelte.js +1 -1
  97. package/dist/stores/settings.svelte.d.ts +16 -1
  98. package/dist/stores/settings.svelte.js +104 -48
  99. package/dist/stores/tabs.svelte.d.ts +3 -0
  100. package/dist/stores/tabs.svelte.js +17 -0
  101. package/dist/utils/cog-histogram.d.ts +121 -0
  102. package/dist/utils/cog-histogram.js +424 -0
  103. package/dist/utils/cog.d.ts +177 -20
  104. package/dist/utils/cog.js +361 -76
  105. package/dist/utils/colormap-sprite.d.ts +0 -9
  106. package/dist/utils/colormap-sprite.js +0 -21
  107. package/dist/utils/deck.d.ts +16 -12
  108. package/dist/utils/deck.js +10 -4
  109. package/dist/utils/pmtiles-tile.js +2 -2
  110. package/dist/utils/{url.d.ts → signed-url.d.ts} +15 -1
  111. package/dist/utils/{url.js → signed-url.js} +32 -10
  112. package/dist/utils/url-state.d.ts +36 -0
  113. package/dist/utils/url-state.js +72 -2
  114. package/dist/utils/zarr-tab.d.ts +1 -2
  115. package/dist/utils/zarr-tab.js +1 -2
  116. package/dist/utils/zarr.d.ts +0 -17
  117. package/dist/utils/zarr.js +1 -45
  118. package/package.json +55 -84
  119. package/dist/components/browser/Breadcrumb.svelte +0 -50
  120. package/dist/components/browser/Breadcrumb.svelte.d.ts +0 -7
  121. package/dist/components/browser/CreateFolderDialog.svelte +0 -98
  122. package/dist/components/browser/CreateFolderDialog.svelte.d.ts +0 -6
  123. package/dist/components/browser/DeleteConfirmDialog.svelte +0 -90
  124. package/dist/components/browser/DeleteConfirmDialog.svelte.d.ts +0 -8
  125. package/dist/components/browser/DropZone.svelte +0 -83
  126. package/dist/components/browser/DropZone.svelte.d.ts +0 -7
  127. package/dist/components/browser/FileBrowser.svelte +0 -252
  128. package/dist/components/browser/FileBrowser.svelte.d.ts +0 -3
  129. package/dist/components/browser/FileRow.svelte +0 -117
  130. package/dist/components/browser/FileRow.svelte.d.ts +0 -9
  131. package/dist/components/browser/RenameDialog.svelte +0 -101
  132. package/dist/components/browser/RenameDialog.svelte.d.ts +0 -8
  133. package/dist/components/browser/SearchBar.svelte +0 -40
  134. package/dist/components/browser/SearchBar.svelte.d.ts +0 -6
  135. package/dist/components/browser/UploadButton.svelte +0 -65
  136. package/dist/components/browser/UploadButton.svelte.d.ts +0 -3
  137. package/dist/query/stac-geoparquet.d.ts +0 -31
  138. package/dist/query/stac-geoparquet.js +0 -136
  139. package/dist/utils/clipboard.d.ts +0 -13
  140. package/dist/utils/clipboard.js +0 -38
  141. package/dist/utils/cloud-url.d.ts +0 -27
  142. package/dist/utils/cloud-url.js +0 -61
  143. package/dist/utils/cog-pure.d.ts +0 -25
  144. package/dist/utils/cog-pure.js +0 -35
  145. package/dist/utils/column-types.d.ts +0 -5
  146. package/dist/utils/column-types.js +0 -137
  147. package/dist/utils/connection-identity.d.ts +0 -51
  148. package/dist/utils/connection-identity.js +0 -97
  149. package/dist/utils/error.d.ts +0 -8
  150. package/dist/utils/error.js +0 -12
  151. package/dist/utils/evidence-context.d.ts +0 -22
  152. package/dist/utils/evidence-context.js +0 -56
  153. package/dist/utils/export.d.ts +0 -22
  154. package/dist/utils/export.js +0 -76
  155. package/dist/utils/file-sort.d.ts +0 -20
  156. package/dist/utils/file-sort.js +0 -41
  157. package/dist/utils/format.d.ts +0 -24
  158. package/dist/utils/format.js +0 -78
  159. package/dist/utils/geoarrow.d.ts +0 -32
  160. package/dist/utils/geoarrow.js +0 -672
  161. package/dist/utils/geometry-type.d.ts +0 -52
  162. package/dist/utils/geometry-type.js +0 -76
  163. package/dist/utils/hex.d.ts +0 -10
  164. package/dist/utils/hex.js +0 -27
  165. package/dist/utils/host-detection.d.ts +0 -23
  166. package/dist/utils/host-detection.js +0 -95
  167. package/dist/utils/local-storage.d.ts +0 -16
  168. package/dist/utils/local-storage.js +0 -37
  169. package/dist/utils/markdown-sql.d.ts +0 -30
  170. package/dist/utils/markdown-sql.js +0 -72
  171. package/dist/utils/notebook.d.ts +0 -59
  172. package/dist/utils/notebook.js +0 -211
  173. package/dist/utils/parquet-metadata.d.ts +0 -64
  174. package/dist/utils/parquet-metadata.js +0 -262
  175. package/dist/utils/stac-geoparquet.d.ts +0 -90
  176. package/dist/utils/stac-geoparquet.js +0 -223
  177. package/dist/utils/stac-hydrate.d.ts +0 -38
  178. package/dist/utils/stac-hydrate.js +0 -243
  179. package/dist/utils/stac.d.ts +0 -136
  180. package/dist/utils/stac.js +0 -176
  181. package/dist/utils/storage-url.d.ts +0 -90
  182. package/dist/utils/storage-url.js +0 -568
  183. package/dist/utils/wkb.d.ts +0 -43
  184. package/dist/utils/wkb.js +0 -359
package/dist/index.js CHANGED
@@ -1,4 +1,11 @@
1
1
  // Core types
2
+ // Clipboard
3
+ // Cloud URL resolution
4
+ // Error handling
5
+ // Data export / serialization
6
+ // localStorage helpers
7
+ // Utilities
8
+ export { allChannelsBand0, applyFacets, applyPreset, applyStorageHintsToConnection, availablePresets, buildDataTypeLabel, buildFacets, buildGeoArrowTables, clampBounds, classifyType, compositeFromUrl, compositeToUrl, connectionIdentityKey, copyToClipboard, DATETIME_HISTOGRAM_BINS, describeParseResult, detectStorageExtensionVersion, emptyFacetState, emptyPushdown, emptyStorageHints, escapeCsvField, extractBounds, extractCogAssets, extractEpsgFromGeoMeta, extractGeometryTypes, extractItemView, extractStorageHints, findGeoColumn, findGeoColumnFromRows, flattenStacBbox, formatDate, formatFileSize, formatValue, generateHexDump, getFileExtension, getNativeScheme, handleLoadError, hasActiveFilters, interpolateTemplates, isAbortError, isSameConnectionIdentity, isSingleAssetComposite, isStacGeoparquetSchema, jsonReplacerBigInt, loadFromStorage, looksLikeUrl, markSqlBlocks, normalizeEndpoint, normalizeGeomType, normalizeProvider, PRESETS, parseMarkdownDocument, parseStorageUrl, parseWKB, persistToStorage, pickNaturalColorComposite, pickStacPrimaryAsset, presetMatchesComposite, readParquetMetadata, residualState, resolveCloudUrl, resolveStacAssetHref, SF_LABELS, STAC_GEOPARQUET_REQUIRED_COLUMNS, safeClamp, safeDecodeURIComponent, serializeToCsv, serializeToJson, sniffApiCapabilities, sortFileEntries, sortViews, stacRowToItem, syntheticSelfAsset, toBinary, toCql2Filter, toggleSortField, toNativeQuery, typeBadgeClass, typeColor, typeLabel, wireCodeCopyButtons } from '@walkthru-earth/objex-utils';
2
9
  // Constants
3
10
  export { COPY_FEEDBACK_MS, DEFAULT_TARGET_CRS, DUCKDB_INIT_TIMEOUT_MS, LAYER_HUE_MULTIPLIER, MAX_QUERY_HISTORY_ENTRIES, SQL_PREVIEW_LENGTH, STORAGE_KEYS, VIEWER_DIR_EXTENSIONS, WGS84_CODES } from './constants.js';
4
11
  // File icons registry
@@ -6,26 +13,3 @@ export { buildDuckDbSource, getDuckDbReadFn, getFileTypeInfo, getMimeType, getVi
6
13
  export { QueryCancelledError } from './query/engine.js';
7
14
  export { buildEndpointFromTemplate, buildProviderBaseUrl, getProvider, isGcsProvider, PROVIDER_IDS, PROVIDERS } from './storage/providers.js';
8
15
  export { UrlAdapter } from './storage/url-adapter.js';
9
- // Clipboard
10
- export { copyToClipboard, wireCodeCopyButtons } from './utils/clipboard.js';
11
- // Cloud URL resolution
12
- export { getNativeScheme, resolveCloudUrl, safeDecodeURIComponent } from './utils/cloud-url.js';
13
- export { buildDataTypeLabel, clampBounds, SF_LABELS, safeClamp } from './utils/cog.js';
14
- export { classifyType, typeBadgeClass, typeColor, typeLabel } from './utils/column-types.js';
15
- export { connectionIdentityKey, isSameConnectionIdentity, normalizeEndpoint, normalizeProvider } from './utils/connection-identity.js';
16
- // Error handling
17
- export { handleLoadError } from './utils/error.js';
18
- // Data export / serialization
19
- export { escapeCsvField, serializeToCsv, serializeToJson } from './utils/export.js';
20
- export { sortFileEntries, toggleSortField } from './utils/file-sort.js';
21
- export { formatDate, formatFileSize, formatValue, getFileExtension, jsonReplacerBigInt } from './utils/format.js';
22
- export { buildGeoArrowTables, normalizeGeomType } from './utils/geoarrow.js';
23
- export { generateHexDump } from './utils/hex.js';
24
- // localStorage helpers
25
- export { loadFromStorage, persistToStorage } from './utils/local-storage.js';
26
- export { interpolateTemplates, markSqlBlocks, parseMarkdownDocument } from './utils/markdown-sql.js';
27
- export { extractBounds, extractEpsgFromGeoMeta, extractGeometryTypes, readParquetMetadata } from './utils/parquet-metadata.js';
28
- export { flattenStacBbox, isStacGeoparquetSchema, pickStacPrimaryAsset, resolveStacAssetHref, STAC_GEOPARQUET_REQUIRED_COLUMNS, stacRowToItem } from './utils/stac-geoparquet.js';
29
- export { describeParseResult, looksLikeUrl, parseStorageUrl } from './utils/storage-url.js';
30
- // Utilities
31
- export { findGeoColumn, findGeoColumnFromRows, parseWKB, toBinary } from './utils/wkb.js';
@@ -61,6 +61,16 @@ export interface QueryEngine {
61
61
  crs: string | null;
62
62
  }>;
63
63
  queryCancellable?(connId: string, sql: string): QueryHandle;
64
+ /**
65
+ * Streaming variant of `queryCancellable`. Yields one `QueryResult`-shaped
66
+ * chunk per Arrow RecordBatch instead of materializing the full row set
67
+ * before returning. Peak memory tracks one batch (~64 KiB rows) rather
68
+ * than the full result, which avoids DuckDB-WASM OOMs on large parquet
69
+ * scans. The first chunk carries the schema (`columns` / `types`); later
70
+ * chunks repeat them so consumers don't need to track first-batch state.
71
+ * Cancellation aborts the inner DuckDB cursor.
72
+ */
73
+ queryStream?(connId: string, sql: string, signal?: AbortSignal): AsyncIterable<QueryResult>;
64
74
  queryForMapCancellable?(connId: string, sql: string, geomCol: string, geomColType: string, sourceCrs?: string | null): MapQueryHandle;
65
75
  forceCancel?(): Promise<void>;
66
76
  /** Register a file buffer in DuckDB-WASM's virtual filesystem for ATTACH. */
@@ -9,7 +9,7 @@
9
9
  * stay free of ad-hoc branching.
10
10
  */
11
11
  import { buildDuckDbSource } from '../file-icons/index.js';
12
- import { buildDuckDbUrl, buildDuckDbUrlAsync } from '../utils/url.js';
12
+ import { buildDuckDbUrl, buildDuckDbUrlAsync } from '../utils/signed-url.js';
13
13
  /**
14
14
  * True when a source ref points at a self-authenticating HTTPS URL (e.g. a
15
15
  * presigned `read_parquet('https://...?X-Amz-Signature=...')`). Used to decide
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Dispatch a `Tab` + classified STAC payload to the appropriate StacSource
3
+ * implementation. The only module allowed to import both `utils/stac-source-*`
4
+ * (API / static) and `query/stac-source-parquet` (DuckDB-bound) together.
5
+ *
6
+ * Keeping this in `query/` rather than `utils/` is deliberate: it imports the
7
+ * parquet source, which transitively pulls in DuckDB-WASM. `utils/` modules
8
+ * must stay DuckDB-free so the objex-utils sub-package doesn't accidentally
9
+ * pull DuckDB into its bundle (slice 6 promotion).
10
+ */
11
+ import type { StacRoutableKind, StacSource } from '@walkthru-earth/objex-utils';
12
+ import type { StorageAdapter } from '../storage/adapter.js';
13
+ import type { Tab } from '../types.js';
14
+ export interface CreateStacSourceDeps {
15
+ adapter: StorageAdapter;
16
+ urlToKey?: (absoluteUrl: string) => string | null;
17
+ baseHref: string;
18
+ /**
19
+ * Connection id resolved at the dispatch site. Threaded into every source
20
+ * so the parquet impl does not have to read `tab.connectionId` itself
21
+ * (asymmetric vs. api/static, would silently break for callers passing a
22
+ * tab whose connectionId is missing).
23
+ */
24
+ connectionId: string;
25
+ /**
26
+ * SDK opt-in: force the parquet source to treat `tab.path` as a
27
+ * hive-partitioned parquet directory and query it with
28
+ * `read_parquet('.../**\/*.parquet', hive_partitioning=true,
29
+ * union_by_name=true)`. Mirrors lazycogs'
30
+ * `DuckdbClient(use_hive_partitioning=True)`. When undefined, the factory
31
+ * also auto-detects directory layouts (see below).
32
+ */
33
+ useHivePartitioning?: boolean;
34
+ /**
35
+ * Debug flag. When true the parquet source runs `EXPLAIN <query>` once per
36
+ * `runQuery()` and logs the plan to the console so we can verify partition
37
+ * stats are pruning files. OFF by default — never enable in shipped UI.
38
+ */
39
+ debugExplain?: boolean;
40
+ }
41
+ /**
42
+ * True when the tab looks like a hive-partitioned parquet directory rather
43
+ * than a single parquet file. Conservative: requires either an explicit SDK
44
+ * opt-in (`deps.useHivePartitioning`) OR a `tab.path` ending in `/` AND a
45
+ * `.parquet`/`.geoparquet` extension. We do NOT auto-promote arbitrary
46
+ * extensionless paths to parquet — that would silently mis-dispatch JSON
47
+ * catalogs whose URL happens to have no extension. The factory defers the
48
+ * actual `adapter.list()` probe to the parquet source so the dispatch stays
49
+ * synchronous.
50
+ */
51
+ export declare function looksLikeHivePartitionedParquet(tab: Tab, deps: CreateStacSourceDeps): boolean;
52
+ /**
53
+ * True when a tab.path looks like a STAC API endpoint (search / items /
54
+ * collections URL pattern). Used to pick the API source for FeatureCollection
55
+ * responses served from API URLs, where the response itself has no
56
+ * `rel="items"` link.
57
+ */
58
+ export declare function tabLooksLikeStacApi(path: string): boolean;
59
+ /**
60
+ * Pick a `StacSource` for the tab. Synchronous: the orchestrator branches on
61
+ * `source.capabilities` immediately, before any I/O. The factory does NOT
62
+ * read the tab payload; it relies on the caller's `classified` argument
63
+ * (from `classifyStac`) plus extension and URL heuristics.
64
+ */
65
+ export declare function createStacSourceForTab(tab: Tab, classified: StacRoutableKind, deps: CreateStacSourceDeps): StacSource;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Dispatch a `Tab` + classified STAC payload to the appropriate StacSource
3
+ * implementation. The only module allowed to import both `utils/stac-source-*`
4
+ * (API / static) and `query/stac-source-parquet` (DuckDB-bound) together.
5
+ *
6
+ * Keeping this in `query/` rather than `utils/` is deliberate: it imports the
7
+ * parquet source, which transitively pulls in DuckDB-WASM. `utils/` modules
8
+ * must stay DuckDB-free so the objex-utils sub-package doesn't accidentally
9
+ * pull DuckDB into its bundle (slice 6 promotion).
10
+ */
11
+ import { createApiSource, createStaticSource, hasStacItemsEndpoint, STAC_API_PATH_RE } from '@walkthru-earth/objex-utils';
12
+ import { createParquetSource } from './stac-source-parquet.js';
13
+ /**
14
+ * True when the tab looks like a hive-partitioned parquet directory rather
15
+ * than a single parquet file. Conservative: requires either an explicit SDK
16
+ * opt-in (`deps.useHivePartitioning`) OR a `tab.path` ending in `/` AND a
17
+ * `.parquet`/`.geoparquet` extension. We do NOT auto-promote arbitrary
18
+ * extensionless paths to parquet — that would silently mis-dispatch JSON
19
+ * catalogs whose URL happens to have no extension. The factory defers the
20
+ * actual `adapter.list()` probe to the parquet source so the dispatch stays
21
+ * synchronous.
22
+ */
23
+ export function looksLikeHivePartitionedParquet(tab, deps) {
24
+ if (deps.useHivePartitioning === true)
25
+ return true;
26
+ const path = tab.path ?? '';
27
+ const ext = (tab.extension ?? '').toLowerCase();
28
+ if (path.endsWith('/') && (ext === 'parquet' || ext === 'geoparquet'))
29
+ return true;
30
+ return false;
31
+ }
32
+ /**
33
+ * True when a tab.path looks like a STAC API endpoint (search / items /
34
+ * collections URL pattern). Used to pick the API source for FeatureCollection
35
+ * responses served from API URLs, where the response itself has no
36
+ * `rel="items"` link.
37
+ */
38
+ export function tabLooksLikeStacApi(path) {
39
+ try {
40
+ return STAC_API_PATH_RE.test(new URL(path).pathname);
41
+ }
42
+ catch {
43
+ return false;
44
+ }
45
+ }
46
+ /**
47
+ * Pick a `StacSource` for the tab. Synchronous: the orchestrator branches on
48
+ * `source.capabilities` immediately, before any I/O. The factory does NOT
49
+ * read the tab payload; it relies on the caller's `classified` argument
50
+ * (from `classifyStac`) plus extension and URL heuristics.
51
+ */
52
+ export function createStacSourceForTab(tab, classified, deps) {
53
+ const ext = (tab.extension ?? '').toLowerCase();
54
+ const hive = looksLikeHivePartitionedParquet(tab, deps);
55
+ if (ext === 'parquet' || ext === 'geoparquet' || hive) {
56
+ return createParquetSource(tab, deps.connectionId, {
57
+ adapter: deps.adapter,
58
+ useHivePartitioning: hive,
59
+ debugExplain: deps.debugExplain
60
+ });
61
+ }
62
+ if (classified.kind === 'collection' || classified.kind === 'catalog') {
63
+ if (hasStacItemsEndpoint(classified.payload)) {
64
+ return createApiSource(classified, deps);
65
+ }
66
+ return createStaticSource(classified, deps);
67
+ }
68
+ if (classified.kind === 'item-collection') {
69
+ if (tabLooksLikeStacApi(tab.path))
70
+ return createApiSource(classified, deps);
71
+ return createStaticSource(classified, deps);
72
+ }
73
+ if (classified.kind === 'item') {
74
+ return createStaticSource(classified, deps);
75
+ }
76
+ throw new Error(`Unsupported STAC kind: ${classified.kind}`);
77
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * stac-geoparquet implementation of the StacSource contract.
3
+ *
4
+ * Reuses:
5
+ * - `getQueryEngine()` + `queryCancellable`/`query` for the single worker
6
+ * - `resolveTableSourceAsync(tab)` for presigned `signed-s3` URL handling
7
+ * - `stacRowToItem` from `@walkthru-earth/objex-utils` for the pure transform
8
+ * - `parseWKB` from `@walkthru-earth/objex-utils` for geometry decoding
9
+ *
10
+ * Push-down: `bbox` (`ST_Intersects` + `ST_MakeEnvelope`) and `datetime`
11
+ * (`datetime BETWEEN TIMESTAMPTZ ...`). Without the datetime push-down,
12
+ * `LIMIT + ORDER BY datetime DESC` silently drops older rows before the
13
+ * client-side filter ever runs, so any window outside the freshest N items
14
+ * returned zero matches. Cloud cover / GSD / platform / etc. still ride
15
+ * along on the residual until slice 3 plumbs them through DuckDB SQL.
16
+ *
17
+ * Hive partitioning: when the factory (or an SDK caller) sets
18
+ * `useHivePartitioning: true`, the FROM target switches to
19
+ * `read_parquet('.../**\/*.parquet', hive_partitioning=true,
20
+ * union_by_name=true)`. Mirrors lazycogs'
21
+ * `DuckdbClient(use_hive_partitioning=True)`. Partition columns appear as
22
+ * virtual columns on the schema, but `buildSelectList` only projects known
23
+ * STAC columns so they never leak into the rendered Items. `union_by_name`
24
+ * is required because partitioned writes can drift schemas across
25
+ * partitions (extra `proj:*` columns added later, etc.).
26
+ *
27
+ * Yields a single batch with `done: true`. Slice 3 turns this into a real
28
+ * stream via `conn.send()` so large catalogs can render progressively.
29
+ */
30
+ import { type StacSource } from '@walkthru-earth/objex-utils';
31
+ import type { StorageAdapter } from '../storage/adapter.js';
32
+ import type { Tab } from '../types.js';
33
+ /**
34
+ * Options for `createParquetSource`. The factory threads adapter +
35
+ * `useHivePartitioning` + `debugExplain` from `CreateStacSourceDeps`, but
36
+ * library consumers can construct a `StacSource` directly.
37
+ */
38
+ export interface CreateParquetSourceOptions {
39
+ /**
40
+ * Storage adapter for the connection backing `tab`. Used solely to probe
41
+ * `tab.path` for `.parquet` children when `useHivePartitioning` is set —
42
+ * never consulted on the per-row read path (DuckDB httpfs handles I/O
43
+ * directly via the presigned / signed URL).
44
+ */
45
+ adapter?: StorageAdapter;
46
+ /**
47
+ * When true, treat `tab.path` as a hive-partitioned parquet directory
48
+ * (e.g. `s3://bucket/year=2023/month=01/...`) and build SQL with
49
+ * `read_parquet('.../**\/*.parquet', hive_partitioning=true,
50
+ * union_by_name=true)` so DuckDB prunes partitions per `bbox` /
51
+ * `datetime` predicate. Mirrors lazycogs'
52
+ * `DuckdbClient(use_hive_partitioning=True)`.
53
+ */
54
+ useHivePartitioning?: boolean;
55
+ /**
56
+ * When true, run `EXPLAIN <query>` once per `runQuery()` and log the plan
57
+ * to the console. Used to verify partition pruning hits parquet stats.
58
+ * Off by default — never enable in shipped UI.
59
+ */
60
+ debugExplain?: boolean;
61
+ /**
62
+ * Mobile / low-memory mode. When true the source caps the effective
63
+ * LIMIT (regardless of the caller's request) and skips
64
+ * `ORDER BY datetime DESC` so DuckDB can stop reading after the first
65
+ * N parquet rows instead of fully materializing every row's STRUCT
66
+ * `assets` column to compute a Top-N. The trade-off: items are
67
+ * returned in file order, not freshness order. Defaults to a
68
+ * mobile-UA detection at module load when undefined.
69
+ */
70
+ lowMemoryMode?: boolean;
71
+ /** Hard cap on `req.limit` when `lowMemoryMode` is on. Default 200. */
72
+ lowMemoryLimit?: number;
73
+ }
74
+ export interface QueryStacGeoparquetOptions {
75
+ signal?: AbortSignal;
76
+ /** Hard cap on rows. Matches `hydrateStacItems` default. */
77
+ limit?: number;
78
+ /**
79
+ * When true, omit `ORDER BY datetime DESC` from the SQL. Sorting forces
80
+ * DuckDB's Top-N operator to read every row's heavy STRUCT `assets`
81
+ * column before the LIMIT engages, which OOMs the WASM heap on mobile
82
+ * Safari (~1.8 GiB cap, no OPFS spill). Without the sort the parquet
83
+ * scan can stop after the first N rows in file order. The trade-off:
84
+ * the user sees the first N items by file order, not the freshest N.
85
+ */
86
+ skipOrderBy?: boolean;
87
+ /**
88
+ * Hive-partitioning context resolved at source construction. When
89
+ * `enabled === true` the runtime FROM target is a `read_parquet` glob
90
+ * over `tab.path` and `union_by_name=true` is applied. The probe lives
91
+ * on the source (`createParquetSource` awaits it once before the first
92
+ * runQuery), not in `runQuery` itself, so the per-pan path stays cheap.
93
+ */
94
+ hive?: {
95
+ enabled: boolean;
96
+ };
97
+ /**
98
+ * Debug flag, propagated from source options. See
99
+ * `CreateParquetSourceOptions.debugExplain`.
100
+ */
101
+ debugExplain?: boolean;
102
+ /**
103
+ * Optional WGS84 viewport bbox `[west, south, east, north]`. When set the
104
+ * query is filtered with `ST_Intersects(geometry, ST_MakeEnvelope(...))` so
105
+ * the parquet path mirrors the API's viewport-scoped behavior. Skipped when
106
+ * the geometry column is missing (already guarded above).
107
+ */
108
+ bbox?: [number, number, number, number];
109
+ /**
110
+ * Optional ISO 8601 datetime range. When set and the parquet has a
111
+ * `datetime` column, the predicate is pushed into the WHERE clause as
112
+ * `datetime BETWEEN TIMESTAMPTZ ... AND TIMESTAMPTZ ...`. Without this
113
+ * push-down the LIMIT + `ORDER BY datetime DESC` would drop older rows
114
+ * before the client-side filter ever ran, so picking an older window
115
+ * returned zero items.
116
+ */
117
+ datetime?: {
118
+ min?: string;
119
+ max?: string;
120
+ };
121
+ }
122
+ /**
123
+ * stac-geoparquet `StacSource`. Slice 1: bbox is the only push-down,
124
+ * single yield with `done: true`. Slice 3 widens push-down (cloud cover /
125
+ * gsd / platform via DuckDB SQL) and turns this into a streaming
126
+ * `conn.send()` cursor.
127
+ *
128
+ * `options.useHivePartitioning` switches the FROM target to a recursive
129
+ * `read_parquet` glob over `tab.path` so DuckDB prunes partitions per
130
+ * `bbox` / `datetime` predicate. The first `query()` call awaits a
131
+ * best-effort `adapter.list()` probe to confirm at least one `.parquet`
132
+ * child exists; if listing fails (e.g. UrlAdapter, AccessDenied) we still
133
+ * attempt the hive query and let DuckDB surface the real error.
134
+ */
135
+ export declare function createParquetSource(tab: Tab, connectionId: string, options?: CreateParquetSourceOptions): StacSource;