@walkthru-earth/objex 1.1.0 → 1.2.1

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 (89) hide show
  1. package/README.md +3 -1
  2. package/dist/components/browser/FileBrowser.svelte +25 -14
  3. package/dist/components/browser/FileTreeSidebar.svelte +43 -7
  4. package/dist/components/layout/ConnectionDialog.svelte +100 -1
  5. package/dist/components/layout/Sidebar.svelte +70 -25
  6. package/dist/components/viewers/ArchiveViewer.svelte +4 -4
  7. package/dist/components/viewers/CodeViewer.svelte +44 -5
  8. package/dist/components/viewers/CogControls.svelte +208 -0
  9. package/dist/components/viewers/CogControls.svelte.d.ts +12 -0
  10. package/dist/components/viewers/CogViewer.svelte +373 -1162
  11. package/dist/components/viewers/CogViewer.svelte.d.ts +1 -1
  12. package/dist/components/viewers/CopcViewer.svelte +20 -2
  13. package/dist/components/viewers/DatabaseViewer.svelte +345 -37
  14. package/dist/components/viewers/FlatGeobufViewer.svelte +15 -9
  15. package/dist/components/viewers/MarkdownViewer.svelte +1 -1
  16. package/dist/components/viewers/PmtilesViewer.svelte +2 -2
  17. package/dist/components/viewers/StacMapViewer.svelte +25 -9
  18. package/dist/components/viewers/TableViewer.svelte +162 -51
  19. package/dist/components/viewers/ZarrMapViewer.svelte +33 -4
  20. package/dist/components/viewers/ZarrViewer.svelte +3 -6
  21. package/dist/components/viewers/pmtiles/PmtilesMapView.svelte +0 -1
  22. package/dist/constants.d.ts +6 -2
  23. package/dist/constants.js +6 -2
  24. package/dist/file-icons/index.d.ts +1 -1
  25. package/dist/file-icons/index.js +12 -2
  26. package/dist/i18n/ar.js +25 -0
  27. package/dist/i18n/en.js +25 -0
  28. package/dist/i18n/index.svelte.d.ts +0 -1
  29. package/dist/i18n/index.svelte.js +0 -3
  30. package/dist/index.d.ts +2 -0
  31. package/dist/index.js +1 -0
  32. package/dist/query/engine.d.ts +20 -4
  33. package/dist/query/index.d.ts +2 -1
  34. package/dist/query/index.js +1 -0
  35. package/dist/query/source.d.ts +42 -0
  36. package/dist/query/source.js +54 -0
  37. package/dist/query/wasm.d.ts +7 -5
  38. package/dist/query/wasm.js +267 -107
  39. package/dist/storage/adapter.d.ts +9 -0
  40. package/dist/storage/adapter.js +13 -1
  41. package/dist/storage/browser-azure.d.ts +1 -1
  42. package/dist/storage/browser-azure.js +4 -0
  43. package/dist/storage/browser-cloud.d.ts +1 -1
  44. package/dist/storage/browser-cloud.js +7 -0
  45. package/dist/storage/presign.d.ts +13 -0
  46. package/dist/storage/presign.js +55 -0
  47. package/dist/storage/providers.d.ts +53 -0
  48. package/dist/storage/providers.js +171 -0
  49. package/dist/stores/browser.svelte.d.ts +2 -0
  50. package/dist/stores/browser.svelte.js +17 -1
  51. package/dist/stores/files.svelte.d.ts +1 -2
  52. package/dist/stores/files.svelte.js +1 -2
  53. package/dist/stores/tabs.svelte.d.ts +9 -2
  54. package/dist/stores/tabs.svelte.js +11 -2
  55. package/dist/types.d.ts +11 -0
  56. package/dist/utils/cog.d.ts +244 -0
  57. package/dist/utils/cog.js +1039 -0
  58. package/dist/utils/deck.d.ts +0 -18
  59. package/dist/utils/deck.js +0 -36
  60. package/dist/utils/geometry-type.d.ts +52 -0
  61. package/dist/utils/geometry-type.js +76 -0
  62. package/dist/utils/markdown-sql.d.ts +1 -1
  63. package/dist/utils/markdown-sql.js +3 -4
  64. package/dist/utils/pmtiles-tile.d.ts +0 -2
  65. package/dist/utils/pmtiles-tile.js +0 -8
  66. package/dist/utils/url-state.d.ts +6 -0
  67. package/dist/utils/url-state.js +34 -26
  68. package/dist/utils/url.d.ts +26 -9
  69. package/dist/utils/url.js +52 -25
  70. package/dist/utils/wkb.js +22 -8
  71. package/dist/utils/zarr-tab.d.ts +22 -0
  72. package/dist/utils/zarr-tab.js +30 -0
  73. package/dist/utils/zarr.d.ts +0 -2
  74. package/dist/utils/zarr.js +73 -44
  75. package/package.json +47 -43
  76. package/dist/components/ui/tabs/index.d.ts +0 -5
  77. package/dist/components/ui/tabs/index.js +0 -7
  78. package/dist/components/ui/tabs/tabs-content.svelte +0 -17
  79. package/dist/components/ui/tabs/tabs-content.svelte.d.ts +0 -4
  80. package/dist/components/ui/tabs/tabs-list.svelte +0 -16
  81. package/dist/components/ui/tabs/tabs-list.svelte.d.ts +0 -4
  82. package/dist/components/ui/tabs/tabs-trigger.svelte +0 -20
  83. package/dist/components/ui/tabs/tabs-trigger.svelte.d.ts +0 -4
  84. package/dist/components/ui/tabs/tabs.svelte +0 -19
  85. package/dist/components/ui/tabs/tabs.svelte.d.ts +0 -4
  86. package/dist/components/viewers/MapViewer.svelte +0 -234
  87. package/dist/components/viewers/MapViewer.svelte.d.ts +0 -7
  88. package/dist/components/viewers/StyleEditorOverlay.svelte +0 -27
  89. package/dist/components/viewers/StyleEditorOverlay.svelte.d.ts +0 -7
package/dist/i18n/en.js CHANGED
@@ -30,6 +30,14 @@ export const en = {
30
30
  'connection.accessKey': 'Access Key',
31
31
  'connection.secretKey': 'Secret Key',
32
32
  'connection.credentialNotice': "Your credentials are stored only in your browser's password manager if you choose to save them. They are never sent to any external server or stored in local storage.",
33
+ 'connection.corsTitle': 'CORS Configuration',
34
+ 'connection.corsRequired': 'Browser access requires CORS to be enabled on your bucket. Without it, requests will be blocked.',
35
+ 'connection.corsDefault': 'CORS is enabled by default. No configuration needed.',
36
+ 'connection.corsDocs': 'Official CORS docs',
37
+ 'connection.corsCliTitle': 'Enable CORS via CLI',
38
+ 'connection.readOnlyTitle': 'Read-Only Access',
39
+ 'connection.readOnlyDocs': 'Official permissions docs',
40
+ 'connection.readOnlyCliTitle': 'Restrict via CLI',
33
41
  'connection.testSuccess': 'Connection successful',
34
42
  'connection.testFail': 'Connection failed. Check your settings and try again.',
35
43
  'connection.testButton': 'Test Connection',
@@ -194,6 +202,12 @@ export const en = {
194
202
  'database.tablesHeader': 'Tables',
195
203
  'database.loadingTable': 'Loading table...',
196
204
  'database.selectTable': 'Select a table to browse',
205
+ // DuckLake
206
+ 'ducklake.loading': 'Attaching DuckLake catalog...',
207
+ 'ducklake.snapshot': 'Snapshot',
208
+ 'ducklake.snapshots': 'snapshots',
209
+ 'ducklake.noTables': 'No tables in this schema',
210
+ 'ducklake.extensionHint': 'The DuckLake extension may not be available for this DuckDB-WASM version.',
197
211
  // PDF Viewer
198
212
  'pdf.badge': 'PDF',
199
213
  'pdf.loading': 'Loading PDF...',
@@ -317,6 +331,7 @@ export const en = {
317
331
  'map.loadingFgb': 'Loading FlatGeobuf...',
318
332
  'map.loadingCog': 'Loading COG...',
319
333
  'map.loadingZarr': 'Loading Zarr data...',
334
+ 'map.zarrTooLarge': 'Array too large for map view ({shape}, ~{tiles} tiles). This dataset needs a multiscale pyramid for tiled rendering.',
320
335
  'map.features': 'features',
321
336
  'map.limit': '(limit)',
322
337
  'map.of': 'of',
@@ -327,6 +342,7 @@ export const en = {
327
342
  'map.flatgeobufInfo': 'FlatGeobuf Info',
328
343
  'map.cogInfo': 'COG Info',
329
344
  'map.cogCorsError': 'Cannot load COG: the server does not allow cross-origin requests (CORS). The file must be hosted with CORS headers enabled.',
345
+ 'map.cogInvalidTiff': 'This file is not a valid TIFF. The server advertises image/tiff but the bytes do not match the TIFF signature, the file may be corrupt, encrypted, or mislabeled.',
330
346
  'map.cogUnsupportedFormat': 'This COG uses {{type}} format which is not supported for map rendering. Only RGB COGs can be displayed.',
331
347
  'map.noGeoColumn': 'No geometry column detected in schema',
332
348
  'map.noData': 'No data available for map view',
@@ -366,6 +382,15 @@ export const en = {
366
382
  'mapInfo.columns': 'Columns',
367
383
  'mapInfo.size': 'Size',
368
384
  'mapInfo.bands': 'Bands',
385
+ // COG Controls
386
+ 'cog.style': 'Style',
387
+ 'cog.band': 'Band',
388
+ 'cog.singleBand': 'Single',
389
+ 'cog.colorRamp': 'Color ramp',
390
+ 'cog.pixelValue': 'Pixel Value',
391
+ 'cog.reading': 'Reading pixel...',
392
+ 'cog.rescale': 'Rescale',
393
+ 'cog.rescaleReset': 'Reset',
369
394
  // PMTiles Viewer
370
395
  'pmtiles.mapView': 'Map',
371
396
  'pmtiles.archiveView': 'Archive',
@@ -1,5 +1,4 @@
1
1
  export type Locale = 'en' | 'ar';
2
- export declare function getLocale(): Locale;
3
2
  export declare function setLocale(l: Locale): void;
4
3
  export declare function getDir(): 'ltr' | 'rtl';
5
4
  /**
@@ -3,9 +3,6 @@ import { en } from './en.js';
3
3
  const translations = { en, ar };
4
4
  const RTL_LOCALES = new Set(['ar']);
5
5
  let currentLocale = $state('en');
6
- export function getLocale() {
7
- return currentLocale;
8
- }
9
6
  export function setLocale(l) {
10
7
  currentLocale = l;
11
8
  }
package/dist/index.d.ts CHANGED
@@ -10,6 +10,8 @@ export { UrlAdapter } from './storage/url-adapter.js';
10
10
  export type { Connection, ConnectionConfig, FileEntry, Tab, Theme, WriteResult } from './types.js';
11
11
  export { copyToClipboard, wireCodeCopyButtons } from './utils/clipboard.js';
12
12
  export { getNativeScheme, resolveCloudUrl, safeDecodeURIComponent } from './utils/cloud-url.js';
13
+ export type { CogInfo, GeoBounds } from './utils/cog.js';
14
+ export { buildDataTypeLabel, clampBounds, SF_LABELS, safeClamp } from './utils/cog.js';
13
15
  export type { TypeCategory } from './utils/column-types.js';
14
16
  export { classifyType, typeBadgeClass, typeColor, typeLabel } from './utils/column-types.js';
15
17
  export { handleLoadError } from './utils/error.js';
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ export { UrlAdapter } from './storage/url-adapter.js';
10
10
  export { copyToClipboard, wireCodeCopyButtons } from './utils/clipboard.js';
11
11
  // Cloud URL resolution
12
12
  export { getNativeScheme, resolveCloudUrl, safeDecodeURIComponent } from './utils/cloud-url.js';
13
+ export { buildDataTypeLabel, clampBounds, SF_LABELS, safeClamp } from './utils/cog.js';
13
14
  export { classifyType, typeBadgeClass, typeColor, typeLabel } from './utils/column-types.js';
14
15
  // Error handling
15
16
  export { handleLoadError } from './utils/error.js';
@@ -35,15 +35,27 @@ export interface SchemaField {
35
35
  type: string;
36
36
  nullable: boolean;
37
37
  }
38
+ /**
39
+ * Abstraction over a DuckDB query source. Decouples schema / CRS / count
40
+ * helpers from assuming a file-backed path. `ref` is the FROM-clause target
41
+ * inserted into generated SQL (e.g. `read_parquet('url')` for files, or
42
+ * `attached_db."schema"."table"` for attached databases). `filePath` is
43
+ * optional and only used as a shortcut for Parquet file-level metadata
44
+ * queries (`parquet_kv_metadata`, `parquet_file_metadata`), not for SQL.
45
+ */
46
+ export interface QuerySource {
47
+ ref: string;
48
+ filePath?: string;
49
+ }
38
50
  export interface QueryEngine {
39
51
  query(connId: string, sql: string): Promise<QueryResult>;
40
52
  queryForMap(connId: string, sql: string, geomCol: string, geomColType: string, sourceCrs?: string | null): Promise<MapQueryResult>;
41
- getSchema(connId: string, path: string): Promise<SchemaField[]>;
42
- getRowCount(connId: string, path: string): Promise<number>;
53
+ getSchema(connId: string, source: QuerySource): Promise<SchemaField[]>;
54
+ getRowCount(connId: string, source: QuerySource): Promise<number>;
43
55
  /** Detect CRS from GeoParquet metadata. Returns e.g. 'EPSG:27700' or null if WGS84/unknown. */
44
- detectCrs(connId: string, path: string, geomCol: string): Promise<string | null>;
56
+ detectCrs(connId: string, source: QuerySource, geomCol: string): Promise<string | null>;
45
57
  /** Combined schema + CRS detection in a single connection (fewer web worker round-trips). */
46
- getSchemaAndCrs?(connId: string, path: string, findGeoCol: (schema: SchemaField[]) => string | null): Promise<{
58
+ getSchemaAndCrs?(connId: string, source: QuerySource, findGeoCol: (schema: SchemaField[]) => string | null): Promise<{
47
59
  schema: SchemaField[];
48
60
  geomCol: string | null;
49
61
  crs: string | null;
@@ -51,6 +63,10 @@ export interface QueryEngine {
51
63
  queryCancellable?(connId: string, sql: string): QueryHandle;
52
64
  queryForMapCancellable?(connId: string, sql: string, geomCol: string, geomColType: string, sourceCrs?: string | null): MapQueryHandle;
53
65
  forceCancel?(): Promise<void>;
66
+ /** Register a file buffer in DuckDB-WASM's virtual filesystem for ATTACH. */
67
+ registerFileBuffer?(name: string, buffer: Uint8Array): Promise<void>;
68
+ /** Drop a previously registered file from DuckDB-WASM's virtual filesystem. */
69
+ dropFile?(name: string): Promise<void>;
54
70
  releaseMemory(): Promise<void>;
55
71
  dispose(): Promise<void>;
56
72
  }
@@ -1,4 +1,5 @@
1
1
  import type { QueryEngine } from './engine';
2
2
  export declare function getQueryEngine(): Promise<QueryEngine>;
3
- export type { MapQueryHandle, MapQueryResult, QueryEngine, QueryHandle, QueryResult, SchemaField } from './engine';
3
+ export type { MapQueryHandle, MapQueryResult, QueryEngine, QueryHandle, QueryResult, QuerySource, SchemaField } from './engine';
4
4
  export { QueryCancelledError } from './engine';
5
+ export { type ResolvedTableSource, resolveTableSource, resolveTableSourceAsync } from './source.js';
@@ -17,3 +17,4 @@ export async function getQueryEngine() {
17
17
  return enginePromise;
18
18
  }
19
19
  export { QueryCancelledError } from './engine';
20
+ export { resolveTableSource, resolveTableSourceAsync } from './source.js';
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Resolve a Tab to a QuerySource — the abstraction every engine helper
3
+ * consumes. A tab is either file-backed (path → `read_parquet('url')`) or
4
+ * SQL-backed (`tab.sourceRef` is a pre-built FROM-clause target, such as an
5
+ * attached DuckLake table).
6
+ *
7
+ * This module is the only place that knows how to map a Tab to both a SQL
8
+ * FROM target AND a resolved file URL, so TableViewer and DatabaseViewer
9
+ * stay free of ad-hoc branching.
10
+ */
11
+ import type { Tab } from '../types.js';
12
+ import type { QuerySource } from './engine.js';
13
+ /**
14
+ * True when a source ref points at a self-authenticating HTTPS URL (e.g. a
15
+ * presigned `read_parquet('https://...?X-Amz-Signature=...')`). Used to decide
16
+ * whether DuckDB needs S3 credential config — presigned URLs don't.
17
+ */
18
+ export declare function isHttpsSourceRef(ref: string): boolean;
19
+ export interface ResolvedTableSource extends QuerySource {
20
+ /**
21
+ * True when the tab is file-backed and hyparquet / parquet metadata
22
+ * shortcuts apply. False for SQL-backed sources like attached DuckLake
23
+ * tables.
24
+ */
25
+ isFileSource: boolean;
26
+ /** File URL used for hyparquet metadata fetches. Null for SQL-backed sources. */
27
+ fileUrl: string | null;
28
+ /** Display label, typically the tab name. */
29
+ label: string;
30
+ }
31
+ /**
32
+ * Resolve a tab to its QuerySource. Must be called lazily (inside reactive
33
+ * expressions or functions) because `tab.sourceRef` and `tab.path` can change
34
+ * over a tab's lifetime.
35
+ */
36
+ export declare function resolveTableSource(tab: Tab): ResolvedTableSource;
37
+ /**
38
+ * Async counterpart of `resolveTableSource`. Returns a presigned HTTPS URL
39
+ * for `signed-s3` connections so DuckDB httpfs can fetch without the
40
+ * `Authorization` header preflight.
41
+ */
42
+ export declare function resolveTableSourceAsync(tab: Tab): Promise<ResolvedTableSource>;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Resolve a Tab to a QuerySource — the abstraction every engine helper
3
+ * consumes. A tab is either file-backed (path → `read_parquet('url')`) or
4
+ * SQL-backed (`tab.sourceRef` is a pre-built FROM-clause target, such as an
5
+ * attached DuckLake table).
6
+ *
7
+ * This module is the only place that knows how to map a Tab to both a SQL
8
+ * FROM target AND a resolved file URL, so TableViewer and DatabaseViewer
9
+ * stay free of ad-hoc branching.
10
+ */
11
+ import { buildDuckDbSource } from '../file-icons/index.js';
12
+ import { buildDuckDbUrl, buildDuckDbUrlAsync } from '../utils/url.js';
13
+ /**
14
+ * True when a source ref points at a self-authenticating HTTPS URL (e.g. a
15
+ * presigned `read_parquet('https://...?X-Amz-Signature=...')`). Used to decide
16
+ * whether DuckDB needs S3 credential config — presigned URLs don't.
17
+ */
18
+ export function isHttpsSourceRef(ref) {
19
+ return /(?:^|\(\s*['"])https:\/\//.test(ref);
20
+ }
21
+ function toResolved(tab, fileUrl) {
22
+ if (tab.sourceRef) {
23
+ return {
24
+ ref: tab.sourceRef,
25
+ filePath: undefined,
26
+ isFileSource: false,
27
+ fileUrl: null,
28
+ label: tab.name
29
+ };
30
+ }
31
+ return {
32
+ ref: buildDuckDbSource(tab.path, fileUrl ?? ''),
33
+ filePath: tab.path,
34
+ isFileSource: true,
35
+ fileUrl,
36
+ label: tab.name
37
+ };
38
+ }
39
+ /**
40
+ * Resolve a tab to its QuerySource. Must be called lazily (inside reactive
41
+ * expressions or functions) because `tab.sourceRef` and `tab.path` can change
42
+ * over a tab's lifetime.
43
+ */
44
+ export function resolveTableSource(tab) {
45
+ return toResolved(tab, tab.sourceRef ? null : buildDuckDbUrl(tab));
46
+ }
47
+ /**
48
+ * Async counterpart of `resolveTableSource`. Returns a presigned HTTPS URL
49
+ * for `signed-s3` connections so DuckDB httpfs can fetch without the
50
+ * `Authorization` header preflight.
51
+ */
52
+ export async function resolveTableSourceAsync(tab) {
53
+ return toResolved(tab, tab.sourceRef ? null : await buildDuckDbUrlAsync(tab));
54
+ }
@@ -1,20 +1,22 @@
1
- import { type MapQueryHandle, type MapQueryResult, type QueryEngine, type QueryHandle, type QueryResult, type SchemaField } from './engine';
1
+ import { type MapQueryHandle, type MapQueryResult, type QueryEngine, type QueryHandle, type QueryResult, type QuerySource, type SchemaField } from './engine';
2
2
  export declare class WasmQueryEngine implements QueryEngine {
3
3
  query(connId: string, sql: string): Promise<QueryResult>;
4
4
  queryForMap(connId: string, sql: string, geomCol: string, geomColType: string, sourceCrs?: string | null): Promise<MapQueryResult>;
5
- getSchema(connId: string, path: string): Promise<SchemaField[]>;
6
- getRowCount(connId: string, path: string): Promise<number>;
7
- getSchemaAndCrs(connId: string, path: string, findGeoCol: (schema: SchemaField[]) => string | null): Promise<{
5
+ getSchema(connId: string, source: QuerySource): Promise<SchemaField[]>;
6
+ getRowCount(connId: string, source: QuerySource): Promise<number>;
7
+ getSchemaAndCrs(connId: string, source: QuerySource, findGeoCol: (schema: SchemaField[]) => string | null): Promise<{
8
8
  schema: SchemaField[];
9
9
  geomCol: string | null;
10
10
  crs: string | null;
11
11
  }>;
12
12
  private configureStorage;
13
- detectCrs(connId: string, path: string, geomCol: string): Promise<string | null>;
13
+ detectCrs(connId: string, source: QuerySource, geomCol: string): Promise<string | null>;
14
14
  private detectCrsWithConn;
15
15
  queryCancellable(connId: string, sql: string): QueryHandle;
16
16
  queryForMapCancellable(connId: string, sql: string, geomCol: string, geomColType: string, sourceCrs?: string | null): MapQueryHandle;
17
17
  forceCancel(): Promise<void>;
18
+ registerFileBuffer(name: string, buffer: Uint8Array): Promise<void>;
19
+ dropFile(name: string): Promise<void>;
18
20
  releaseMemory(): Promise<void>;
19
21
  dispose(): Promise<void>;
20
22
  }