@walkthru-earth/objex-utils 1.3.0 → 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.
- package/LICENSE +5 -0
- package/README.md +36 -19
- package/dist/index.cjs +3020 -524
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1462 -82
- package/dist/index.d.ts +1462 -82
- package/dist/index.js +2938 -525
- package/dist/index.js.map +1 -1
- package/docs/README.md +17 -6
- package/docs/app-config.md +237 -0
- package/docs/channel-composite.md +127 -0
- package/docs/cog-asset.md +164 -0
- package/docs/cog.md +2 -2
- package/docs/errors.md +1 -1
- package/docs/file-sort.md +1 -1
- package/docs/formatting.md +4 -4
- package/docs/geometry.md +3 -3
- package/docs/local-storage.md +1 -1
- package/docs/lru.md +76 -0
- package/docs/map-pixel-inspect.md +132 -0
- package/docs/markdown-sql.md +1 -1
- package/docs/parquet-metadata.md +1 -1
- package/docs/stac-facets.md +198 -0
- package/docs/stac-hydrate.md +181 -0
- package/docs/stac-pushdown.md +145 -0
- package/docs/stac-source.md +224 -0
- package/docs/stac-storage-extension.md +146 -0
- package/docs/stac.md +425 -0
- package/docs/storage.md +2 -2
- package/package.json +10 -3
package/dist/index.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ declare const COPY_FEEDBACK_MS = 2000;
|
|
|
39
39
|
* Single source of truth for extension → icon, color, label, category,
|
|
40
40
|
* viewer, DuckDB read function, MIME type, and queryability.
|
|
41
41
|
*
|
|
42
|
-
* Used by:
|
|
42
|
+
* Used by: FileTreeSidebar, ViewerRouter, TableViewer, WasmQueryEngine, etc.
|
|
43
43
|
*/
|
|
44
44
|
type FileCategory = 'data' | 'geo' | 'code' | 'document' | 'config' | 'image' | 'video' | 'audio' | 'archive' | 'database' | '3d' | 'other';
|
|
45
45
|
type ViewerKind = 'table' | 'image' | 'video' | 'audio' | 'markdown' | 'code' | 'cog' | 'pmtiles' | 'flatgeobuf' | 'pdf' | '3d' | 'archive' | 'database' | 'zarr' | 'copc' | 'notebook' | 'raw';
|
|
@@ -149,6 +149,16 @@ interface QueryEngine {
|
|
|
149
149
|
crs: string | null;
|
|
150
150
|
}>;
|
|
151
151
|
queryCancellable?(connId: string, sql: string): QueryHandle;
|
|
152
|
+
/**
|
|
153
|
+
* Streaming variant of `queryCancellable`. Yields one `QueryResult`-shaped
|
|
154
|
+
* chunk per Arrow RecordBatch instead of materializing the full row set
|
|
155
|
+
* before returning. Peak memory tracks one batch (~64 KiB rows) rather
|
|
156
|
+
* than the full result, which avoids DuckDB-WASM OOMs on large parquet
|
|
157
|
+
* scans. The first chunk carries the schema (`columns` / `types`); later
|
|
158
|
+
* chunks repeat them so consumers don't need to track first-batch state.
|
|
159
|
+
* Cancellation aborts the inner DuckDB cursor.
|
|
160
|
+
*/
|
|
161
|
+
queryStream?(connId: string, sql: string, signal?: AbortSignal): AsyncIterable<QueryResult>;
|
|
152
162
|
queryForMapCancellable?(connId: string, sql: string, geomCol: string, geomColType: string, sourceCrs?: string | null): MapQueryHandle;
|
|
153
163
|
forceCancel?(): Promise<void>;
|
|
154
164
|
/** Register a file buffer in DuckDB-WASM's virtual filesystem for ATTACH. */
|
|
@@ -344,6 +354,431 @@ declare class UrlAdapter implements StorageAdapter {
|
|
|
344
354
|
copy(): Promise<WriteResult>;
|
|
345
355
|
}
|
|
346
356
|
|
|
357
|
+
/** A basemap option a host can offer. Consumed in Phase 2. */
|
|
358
|
+
interface BasemapConfig {
|
|
359
|
+
id: string;
|
|
360
|
+
label: string;
|
|
361
|
+
type: 'vector' | 'raster';
|
|
362
|
+
url: string;
|
|
363
|
+
variant?: 'light' | 'dark';
|
|
364
|
+
}
|
|
365
|
+
/** A preloaded connection definition. Never carries secrets. Consumed in Phase 2. */
|
|
366
|
+
interface ConnectionSeed {
|
|
367
|
+
name: string;
|
|
368
|
+
provider: string;
|
|
369
|
+
bucket: string;
|
|
370
|
+
region?: string;
|
|
371
|
+
endpoint?: string;
|
|
372
|
+
anonymous?: boolean;
|
|
373
|
+
authMethod?: 'sigv4' | 'sas-token';
|
|
374
|
+
rootPrefix?: string;
|
|
375
|
+
}
|
|
376
|
+
interface AppConfigDefaults {
|
|
377
|
+
theme: Theme;
|
|
378
|
+
locale: string;
|
|
379
|
+
featureLimit: number;
|
|
380
|
+
mosaicItemLimit: number;
|
|
381
|
+
}
|
|
382
|
+
interface AppConfigUi {
|
|
383
|
+
showConnectionRail: boolean;
|
|
384
|
+
showFileTree: boolean;
|
|
385
|
+
showSettings: boolean;
|
|
386
|
+
}
|
|
387
|
+
interface AppConfig {
|
|
388
|
+
defaults: AppConfigDefaults;
|
|
389
|
+
ui: AppConfigUi;
|
|
390
|
+
basemaps: BasemapConfig[];
|
|
391
|
+
defaultBasemap: {
|
|
392
|
+
light?: string;
|
|
393
|
+
dark?: string;
|
|
394
|
+
};
|
|
395
|
+
connections: ConnectionSeed[];
|
|
396
|
+
}
|
|
397
|
+
/** Hardcoded fallback. Matches current app behaviour when no config is present. */
|
|
398
|
+
declare const DEFAULT_APP_CONFIG: AppConfig;
|
|
399
|
+
declare function coerceTheme(v: unknown): Theme | undefined;
|
|
400
|
+
declare function coerceString(v: unknown): string | undefined;
|
|
401
|
+
declare function coercePositiveInt(v: unknown): number | undefined;
|
|
402
|
+
declare function coerceBool(v: unknown): boolean | undefined;
|
|
403
|
+
/** First defined (non-null, non-undefined) candidate wins. Encodes the precedence chain. */
|
|
404
|
+
declare function resolveSetting<T>(...candidates: (T | null | undefined)[]): T | undefined;
|
|
405
|
+
/** Maps the ?sidebar / ?tree visibility param to a boolean, or undefined when absent or invalid. */
|
|
406
|
+
declare function parseVisibilityParam(value: string | null): boolean | undefined;
|
|
407
|
+
/**
|
|
408
|
+
* Pick the basemap a map should render. Precedence: explicit user pick (when it
|
|
409
|
+
* still exists in the configured list) > the configured default for the theme
|
|
410
|
+
* variant > the first basemap matching the variant > the first basemap of any
|
|
411
|
+
* variant. Returns undefined when no basemaps are configured, signalling the
|
|
412
|
+
* caller to fall back to its hardcoded default.
|
|
413
|
+
*/
|
|
414
|
+
declare function resolveBasemap(config: AppConfig, variant: 'light' | 'dark', userId: string | undefined): BasemapConfig | undefined;
|
|
415
|
+
/**
|
|
416
|
+
* Merge an untrusted JSON value over a base config, field by field.
|
|
417
|
+
* Unknown fields are ignored. Malformed values fall back to the base.
|
|
418
|
+
* Never reads secrets.
|
|
419
|
+
*/
|
|
420
|
+
declare function mergeAppConfig(base: AppConfig, override: unknown): AppConfig;
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* STAC (SpatioTemporal Asset Catalog) detection and parsing.
|
|
424
|
+
*
|
|
425
|
+
* Pure TypeScript helpers shared by ViewerRouter, StacMosaicViewer, and
|
|
426
|
+
* MultiCogViewer. No Svelte dependency, publishable via objex-utils.
|
|
427
|
+
*/
|
|
428
|
+
/** STAC Link (shared by Catalog/Collection/Item). */
|
|
429
|
+
interface StacLink {
|
|
430
|
+
rel: string;
|
|
431
|
+
href: string;
|
|
432
|
+
type?: string;
|
|
433
|
+
title?: string;
|
|
434
|
+
}
|
|
435
|
+
/** STAC Item (GeoJSON Feature shape with stac_version). */
|
|
436
|
+
interface StacItem {
|
|
437
|
+
type: 'Feature';
|
|
438
|
+
stac_version: string;
|
|
439
|
+
id: string;
|
|
440
|
+
bbox?: [number, number, number, number];
|
|
441
|
+
geometry?: unknown;
|
|
442
|
+
properties?: Record<string, unknown>;
|
|
443
|
+
assets?: Record<string, StacAsset>;
|
|
444
|
+
collection?: string;
|
|
445
|
+
links?: StacLink[];
|
|
446
|
+
}
|
|
447
|
+
/** STAC FeatureCollection (collection of Items). Also used for STAC API responses. */
|
|
448
|
+
interface StacFeatureCollection {
|
|
449
|
+
type: 'FeatureCollection';
|
|
450
|
+
stac_version?: string;
|
|
451
|
+
features: StacItem[];
|
|
452
|
+
links?: StacLink[];
|
|
453
|
+
}
|
|
454
|
+
/** STAC Collection (a grouping of Items with its own metadata and links). */
|
|
455
|
+
interface StacCollection {
|
|
456
|
+
type: 'Collection';
|
|
457
|
+
stac_version: string;
|
|
458
|
+
id: string;
|
|
459
|
+
description?: string;
|
|
460
|
+
extent?: {
|
|
461
|
+
spatial?: {
|
|
462
|
+
bbox?: number[][];
|
|
463
|
+
};
|
|
464
|
+
temporal?: unknown;
|
|
465
|
+
};
|
|
466
|
+
links: StacLink[];
|
|
467
|
+
}
|
|
468
|
+
/** STAC Catalog (a directory-like grouping of Catalogs/Collections/Items via links). */
|
|
469
|
+
interface StacCatalog {
|
|
470
|
+
type: 'Catalog';
|
|
471
|
+
stac_version: string;
|
|
472
|
+
id: string;
|
|
473
|
+
description?: string;
|
|
474
|
+
links: StacLink[];
|
|
475
|
+
}
|
|
476
|
+
/** Single asset entry within a STAC item. */
|
|
477
|
+
interface StacAsset {
|
|
478
|
+
href: string;
|
|
479
|
+
type?: string;
|
|
480
|
+
title?: string;
|
|
481
|
+
roles?: string[];
|
|
482
|
+
/** Set when the asset carries `eo:bands`. */
|
|
483
|
+
'eo:bands'?: {
|
|
484
|
+
name?: string;
|
|
485
|
+
common_name?: string;
|
|
486
|
+
}[];
|
|
487
|
+
}
|
|
488
|
+
/** Sentinel-2 band slot identifier, shared with utils/cog.ts composites. */
|
|
489
|
+
type BandSlot = 'red' | 'green' | 'blue' | 'nir' | 'swir1' | 'swir2' | 'rededge';
|
|
490
|
+
/** Parsed band map: slot name → HTTPS asset URL. */
|
|
491
|
+
type BandMap = Partial<Record<BandSlot, string>>;
|
|
492
|
+
/** Asset keys providers use for the single "display COG" asset, in priority order. */
|
|
493
|
+
declare const STAC_COG_ASSET_KEYS: readonly ["visual", "image", "data", "rendered_preview"];
|
|
494
|
+
/** Shape-check a parsed JSON object as a STAC Item. */
|
|
495
|
+
declare function isStacItem(json: unknown): json is StacItem;
|
|
496
|
+
/** Shape-check a parsed JSON object as a STAC FeatureCollection. */
|
|
497
|
+
declare function isStacFeatureCollection(json: unknown): json is StacFeatureCollection;
|
|
498
|
+
/** STAC Collection detection: `type === 'Collection'` + stac_version + links array. */
|
|
499
|
+
declare function isStacCollection(json: unknown): json is StacCollection;
|
|
500
|
+
/** STAC Catalog detection: `type === 'Catalog'` + stac_version + links array. */
|
|
501
|
+
declare function isStacCatalog(json: unknown): json is StacCatalog;
|
|
502
|
+
/** Routing verdict for any STAC-shaped JSON payload. */
|
|
503
|
+
type StacRoutableKind = {
|
|
504
|
+
kind: 'item';
|
|
505
|
+
item: StacItem;
|
|
506
|
+
} | {
|
|
507
|
+
kind: 'item-collection';
|
|
508
|
+
fc: StacFeatureCollection;
|
|
509
|
+
} | {
|
|
510
|
+
kind: 'collection';
|
|
511
|
+
payload: StacCollection;
|
|
512
|
+
} | {
|
|
513
|
+
kind: 'catalog';
|
|
514
|
+
payload: StacCatalog;
|
|
515
|
+
} | {
|
|
516
|
+
kind: 'none';
|
|
517
|
+
};
|
|
518
|
+
/** Classify an arbitrary parsed JSON into one of the STAC routing buckets. */
|
|
519
|
+
declare function classifyStac(json: unknown): StacRoutableKind;
|
|
520
|
+
/**
|
|
521
|
+
* Pick the COG-ish asset href from a STAC Item. Returns the href of the named
|
|
522
|
+
* asset when `preferred` is given and present, else scans STAC_COG_ASSET_KEYS,
|
|
523
|
+
* else falls back to any asset whose `type` contains "tiff". Returns null when
|
|
524
|
+
* nothing matches.
|
|
525
|
+
*/
|
|
526
|
+
declare function pickCogAssetHref(item: StacItem, preferred?: string): string | null;
|
|
527
|
+
/** True when a single STAC Item exposes a COG-ish asset and a bbox. */
|
|
528
|
+
declare function detectMosaicCapable(item: StacItem): boolean;
|
|
529
|
+
/** True when a single STAC Item exposes ≥3 single-band raster COG assets,
|
|
530
|
+
* whether tagged with S2-style common names or not. The MultiCogViewer's
|
|
531
|
+
* band picker fills in the gap when presets don't auto-resolve. */
|
|
532
|
+
declare function detectMultiCogCapable(item: StacItem): boolean;
|
|
533
|
+
/** WGS84 bbox helper. Returns `null` if no bbox can be derived. */
|
|
534
|
+
declare function stacItemBbox(item: StacItem): [number, number, number, number] | null;
|
|
535
|
+
/** Normalized mosaic source entry consumed by MosaicLayer. */
|
|
536
|
+
interface MosaicSourceMeta {
|
|
537
|
+
id: string;
|
|
538
|
+
bbox: [number, number, number, number];
|
|
539
|
+
href: string;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Normalize a STAC Item or a plain `{id?, bbox, href}` record into a
|
|
543
|
+
* MosaicSourceMeta. Returns null when essentials (bbox / href) are missing.
|
|
544
|
+
*/
|
|
545
|
+
declare function buildMosaicSourceMeta(input: StacItem | {
|
|
546
|
+
id?: string;
|
|
547
|
+
bbox: [number, number, number, number] | number[];
|
|
548
|
+
href: string;
|
|
549
|
+
}, assetKey?: string): MosaicSourceMeta | null;
|
|
550
|
+
/**
|
|
551
|
+
* Stable spatial cell key for an item, used to dedupe revisits when the
|
|
552
|
+
* caller wants only the freshest scene per footprint. STAC providers
|
|
553
|
+
* default to descending-datetime sort, so the first item per key is the
|
|
554
|
+
* newest. Prefers STAC `grid:code`, then Sentinel-2 MGRS triplet, then
|
|
555
|
+
* `s2:mgrs_tile`, then a rounded bbox so non-S2 providers still dedupe.
|
|
556
|
+
*/
|
|
557
|
+
declare function spatialCellKey(item: StacItem, bbox: [number, number, number, number]): string;
|
|
558
|
+
/**
|
|
559
|
+
* Map Sentinel-2 STAC item assets to a BandMap. Recognizes:
|
|
560
|
+
* - `eo:bands[0].common_name` (preferred, stable across providers)
|
|
561
|
+
* - asset key heuristics for Microsoft PC / Element 84 / AWS S2 L2A buckets
|
|
562
|
+
* Returns an empty map when no bands are identifiable so callers can fall
|
|
563
|
+
* back to a different viewer.
|
|
564
|
+
*/
|
|
565
|
+
declare function extractSentinelBandAssets(item: StacItem): BandMap;
|
|
566
|
+
/** True when the band map has enough channels for a True Color composite. */
|
|
567
|
+
declare function hasRgbBands(map: BandMap): boolean;
|
|
568
|
+
/**
|
|
569
|
+
* Generic raster band asset description, vendor-neutral. Carries the bits a
|
|
570
|
+
* MultiCOG band picker needs to populate dropdowns and resolve presets.
|
|
571
|
+
*
|
|
572
|
+
* `key` is the STAC asset key as it appears in `item.assets` (`red`, `B04`,
|
|
573
|
+
* `image`, `analytic`, ...). It is the natural identifier and lines up 1:1
|
|
574
|
+
* with `MultiCOGLayer.sources`'s record key.
|
|
575
|
+
*/
|
|
576
|
+
interface RasterBandAsset {
|
|
577
|
+
key: string;
|
|
578
|
+
href: string;
|
|
579
|
+
commonName?: string;
|
|
580
|
+
bandCount?: number;
|
|
581
|
+
roles?: string[];
|
|
582
|
+
mediaType?: string;
|
|
583
|
+
title?: string;
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Enumerate every asset on a STAC Item that looks like a single-band raster
|
|
587
|
+
* COG suitable for compositing in MultiCOGLayer. Used by the band picker UI.
|
|
588
|
+
*
|
|
589
|
+
* Inclusion: `media_type` matches `image/tiff*` (drops JP2 mirrors, PNGs,
|
|
590
|
+
* thumbnails) AND it is not a thumbnail / overview / metadata role. Pre-baked
|
|
591
|
+
* multi-band visuals (`raster:bands.length > 1` or `eo:bands.length > 1`) are
|
|
592
|
+
* dropped because compositing needs single-band sources, not a 3-band visual
|
|
593
|
+
* COG; that asset belongs in `CogViewer`.
|
|
594
|
+
*
|
|
595
|
+
* `bandCount` is reported as `1` when neither tag is present and the asset
|
|
596
|
+
* media type is COG-like, so vendor catalogs that omit `eo:bands` are still
|
|
597
|
+
* pickable. Callers wanting to be strict can filter on `bandCount === 1`.
|
|
598
|
+
*/
|
|
599
|
+
declare function extractRasterBandAssets(item: StacItem): RasterBandAsset[];
|
|
600
|
+
/**
|
|
601
|
+
* Resolve a semantic band slot to an asset key on this Item.
|
|
602
|
+
*
|
|
603
|
+
* Priority:
|
|
604
|
+
* 1. First asset whose `eo:bands[0].common_name` (lowercased) equals the slot.
|
|
605
|
+
* 2. First asset whose key appears in `BAND_KEY_FALLBACKS[slot]` (vendor key
|
|
606
|
+
* conventions: `B04`, `red-jp2`, etc.).
|
|
607
|
+
*
|
|
608
|
+
* Returns the asset key (NOT the href) so callers can plumb it into
|
|
609
|
+
* `composite: { r, g, b }` and look it up in `extractRasterBandAssets()`.
|
|
610
|
+
*/
|
|
611
|
+
declare function resolveBandSlotAssetKey(assets: RasterBandAsset[], slot: BandSlot): string | undefined;
|
|
612
|
+
/**
|
|
613
|
+
* Resolve a preset's R/G/B BandSlot triple into asset keys for this Item.
|
|
614
|
+
* Returns null when any of the three required slots cannot be resolved, so
|
|
615
|
+
* the caller can disable the preset rather than half-apply it.
|
|
616
|
+
*/
|
|
617
|
+
declare function resolvePresetComposite(assets: RasterBandAsset[], composite: {
|
|
618
|
+
r: BandSlot;
|
|
619
|
+
g: BandSlot;
|
|
620
|
+
b: BandSlot;
|
|
621
|
+
}): {
|
|
622
|
+
r: string;
|
|
623
|
+
g: string;
|
|
624
|
+
b: string;
|
|
625
|
+
} | null;
|
|
626
|
+
/** True when ≥3 single-band raster COG assets exist, so manual RGB pick is viable. */
|
|
627
|
+
declare function hasCompositableBands(assets: RasterBandAsset[]): boolean;
|
|
628
|
+
/**
|
|
629
|
+
* Same shape as `extractRasterBandAssets`, but does NOT drop multi-band assets.
|
|
630
|
+
* Used by the mosaic asset picker, where a 3-band pre-baked `visual` TCI is a
|
|
631
|
+
* legitimate choice alongside the per-band single-band COGs.
|
|
632
|
+
*
|
|
633
|
+
* `bandCount` is reported when known (`raster:bands.length` or `eo:bands.length`),
|
|
634
|
+
* else left undefined so the consuming UI can fall back to probing the COG.
|
|
635
|
+
*/
|
|
636
|
+
declare function extractMosaicAssets(item: StacItem): RasterBandAsset[];
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Generic per-channel asset descriptor for the unified RGB picker.
|
|
640
|
+
*
|
|
641
|
+
* Pure TypeScript. No Svelte dependency. Publishable via objex-utils.
|
|
642
|
+
*
|
|
643
|
+
* `CogAsset` is the canonical shape every viewer (CogViewer, MultiCogViewer,
|
|
644
|
+
* StacMosaicViewer) hands to the shared ChannelPicker UI. Each entry records
|
|
645
|
+
* the STAC asset key (`red`, `B04`, `image`, `visual`, ... or `self` when the
|
|
646
|
+
* viewer is a single bare COG file with no STAC context), the href, the band
|
|
647
|
+
* count (from `raster:bands.length` when STAC populates it, lazily probed from
|
|
648
|
+
* the COG header otherwise), and the optional `eo:bands` common name.
|
|
649
|
+
*/
|
|
650
|
+
|
|
651
|
+
interface CogAsset {
|
|
652
|
+
/** STAC asset key, or `self` for a single bare COG without STAC context. */
|
|
653
|
+
key: string;
|
|
654
|
+
/** Absolute or relative href as it appears in the STAC item / URL. */
|
|
655
|
+
href: string;
|
|
656
|
+
/** Number of bands in the asset. 1 by default until probed. */
|
|
657
|
+
bandCount: number;
|
|
658
|
+
/** True when bandCount came from STAC metadata or a probe; false → trust default. */
|
|
659
|
+
bandCountKnown: boolean;
|
|
660
|
+
/** `raster:bands[0].data_type` if known. */
|
|
661
|
+
dtype?: string;
|
|
662
|
+
/** `eo:bands[].common_name` lowercased, aligned to bandIndex order. */
|
|
663
|
+
eoCommon: string[];
|
|
664
|
+
/** STAC asset roles (`data`, `visual`, `reflectance`, ...). */
|
|
665
|
+
roles: string[];
|
|
666
|
+
/** Optional human title. */
|
|
667
|
+
title?: string;
|
|
668
|
+
/** Asset media_type as advertised by STAC. */
|
|
669
|
+
mediaType?: string;
|
|
670
|
+
}
|
|
671
|
+
/** Per-channel pixel coordinate inside a STAC item. */
|
|
672
|
+
interface ChannelRef {
|
|
673
|
+
assetKey: string;
|
|
674
|
+
bandIndex: number;
|
|
675
|
+
}
|
|
676
|
+
/** RGB(A) composite expressed as `(asset, bandIndex)` per channel. */
|
|
677
|
+
interface ChannelComposite {
|
|
678
|
+
r: ChannelRef;
|
|
679
|
+
g: ChannelRef;
|
|
680
|
+
b: ChannelRef;
|
|
681
|
+
a?: ChannelRef;
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Enumerate every TIFF/COG asset on a STAC Item, keeping multi-band assets
|
|
685
|
+
* (NAIP `image`, S2 `visual` TCI) alongside single-band per-band assets.
|
|
686
|
+
*
|
|
687
|
+
* Reads band metadata from (in priority order):
|
|
688
|
+
* 1. `asset.bands` (STAC 1.1 unified bands array)
|
|
689
|
+
* 2. `asset['raster:bands']` (STAC 1.0 raster extension)
|
|
690
|
+
* 3. `asset['eo:bands']` (STAC 1.0 eo extension)
|
|
691
|
+
* 4. `item.properties.bands` (STAC 1.1 item-level bands, applies to all
|
|
692
|
+
* assets that do not override) — covers catalogs like the Hamilton
|
|
693
|
+
* NAIP-style 3-inch where each item has 4 bands but the single `data`
|
|
694
|
+
* asset carries no band metadata of its own.
|
|
695
|
+
*
|
|
696
|
+
* `bandCount` is set when any of the above provides one; otherwise defaults
|
|
697
|
+
* to 1 with `bandCountKnown: false` so callers can lazily probe on first pick.
|
|
698
|
+
*/
|
|
699
|
+
declare function extractCogAssets(item: StacItem): CogAsset[];
|
|
700
|
+
/**
|
|
701
|
+
* For `CogViewer` (single bare COG file, no STAC context). Returns one synthetic
|
|
702
|
+
* asset with key `self` so the same ChannelPicker UI works without special-casing.
|
|
703
|
+
* `bandCount` defaults to 1, set to the probed `geotiff.count` once known.
|
|
704
|
+
*/
|
|
705
|
+
declare function syntheticSelfAsset(href: string, probedBandCount?: number): CogAsset;
|
|
706
|
+
/**
|
|
707
|
+
* Pick the most natural and most performant default composite for an item.
|
|
708
|
+
*
|
|
709
|
+
* Priority (first match wins):
|
|
710
|
+
* 1. A 3-band uint8 pre-baked visual asset (`visual` / `image` / `tci` etc):
|
|
711
|
+
* all three channels bind to it, bands 0/1/2. Single COGLayer path,
|
|
712
|
+
* one decoder, fastest.
|
|
713
|
+
* 2. Common-name red/green/blue resolvable across separate single-band assets.
|
|
714
|
+
* MultiCOGLayer path.
|
|
715
|
+
* 3. Fallback: first three raster assets, band 0 each.
|
|
716
|
+
*
|
|
717
|
+
* Returns null when no raster assets exist.
|
|
718
|
+
*/
|
|
719
|
+
declare function pickNaturalColorComposite(assets: CogAsset[]): {
|
|
720
|
+
composite: ChannelComposite;
|
|
721
|
+
source: 'visual-asset' | 'rgb-bands' | 'fallback';
|
|
722
|
+
} | null;
|
|
723
|
+
/** True when all three RGB channels target the same asset key. */
|
|
724
|
+
declare function isSingleAssetComposite(c: ChannelComposite): boolean;
|
|
725
|
+
/** True when all three channels are at band index 0 (the MultiCOGLayer-compatible case). */
|
|
726
|
+
declare function allChannelsBand0(c: ChannelComposite): boolean;
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* Preset definitions, URL round-trip, and preset application for the unified
|
|
730
|
+
* RGB picker. Pure TypeScript, publishable via objex-utils.
|
|
731
|
+
*
|
|
732
|
+
* Presets describe a SEMANTIC band slot triple (`red`/`green`/`blue` for
|
|
733
|
+
* Natural Color, `nir`/`red`/`green` for False-Color IR, ...). Resolving a
|
|
734
|
+
* preset against a specific item walks `BAND_KEY_FALLBACKS` in `utils/stac.ts`
|
|
735
|
+
* to map slots to actual asset keys on that item. NDVI and other single-band
|
|
736
|
+
* derived presets are intentionally NOT in this list for this slice.
|
|
737
|
+
*/
|
|
738
|
+
|
|
739
|
+
interface PresetDef {
|
|
740
|
+
id: string;
|
|
741
|
+
labelKey: string;
|
|
742
|
+
slots: {
|
|
743
|
+
r: BandSlot;
|
|
744
|
+
g: BandSlot;
|
|
745
|
+
b: BandSlot;
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
declare const PRESETS: PresetDef[];
|
|
749
|
+
/** Subset of PRESETS whose slot triple resolves on this item. */
|
|
750
|
+
declare function availablePresets(assets: CogAsset[]): PresetDef[];
|
|
751
|
+
/** Resolve a preset to a ChannelComposite for this item. Returns null when not applicable. */
|
|
752
|
+
declare function applyPreset(assets: CogAsset[], preset: PresetDef): ChannelComposite | null;
|
|
753
|
+
/** True when the preset's resolved composite still matches the user's current picks. */
|
|
754
|
+
declare function presetMatchesComposite(preset: PresetDef, c: ChannelComposite, assets: CogAsset[]): boolean;
|
|
755
|
+
/**
|
|
756
|
+
* Decode a `URLSearchParams` chunk into a ChannelComposite.
|
|
757
|
+
*
|
|
758
|
+
* Format: `r=<asset>&g=<asset>&b=<asset>&band_r=<n>&band_g=<n>&band_b=<n>` plus
|
|
759
|
+
* optional `a=<asset>&band_a=<n>`. `band_*` defaults to 0 when absent so
|
|
760
|
+
* legacy MultiCog URLs (`?r=red&g=green&b=blue&preset=true-color`) keep
|
|
761
|
+
* round-tripping. Returns null when any required asset key is missing from
|
|
762
|
+
* the current item's asset list.
|
|
763
|
+
*/
|
|
764
|
+
declare function compositeFromUrl(params: URLSearchParams, assets: CogAsset[]): ChannelComposite | null;
|
|
765
|
+
/** Encode a composite + active preset id into URLSearchParams for the hash. */
|
|
766
|
+
declare function compositeToUrl(c: ChannelComposite, presetId: string | null): URLSearchParams;
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Copy text to clipboard and run a feedback callback for COPY_FEEDBACK_MS.
|
|
770
|
+
* Silently catches clipboard errors (e.g. insecure context).
|
|
771
|
+
*
|
|
772
|
+
* @returns true if copy succeeded, false otherwise.
|
|
773
|
+
*/
|
|
774
|
+
declare function copyToClipboard(text: string, onFeedback?: (copied: boolean) => void): Promise<boolean>;
|
|
775
|
+
/**
|
|
776
|
+
* Wire click-to-copy on all elements matching `selector` inside `root`.
|
|
777
|
+
* Each element must have `data-code` (URI-encoded) with the text to copy.
|
|
778
|
+
* Adds/removes a `copied` CSS class for visual feedback.
|
|
779
|
+
*/
|
|
780
|
+
declare function wireCodeCopyButtons(root: Element, selector: string): void;
|
|
781
|
+
|
|
347
782
|
/**
|
|
348
783
|
* Cloud storage protocol URL utilities — pure TS, no Svelte dependency.
|
|
349
784
|
*
|
|
@@ -404,9 +839,69 @@ declare function typeColor(category: TypeCategory): string;
|
|
|
404
839
|
declare function typeBadgeClass(category: TypeCategory): string;
|
|
405
840
|
declare function typeLabel(category: TypeCategory): string;
|
|
406
841
|
|
|
842
|
+
/**
|
|
843
|
+
* Canonical connection identity.
|
|
844
|
+
*
|
|
845
|
+
* Single source of truth for deciding when two connection configs point at
|
|
846
|
+
* the same bucket. Used by the connections store to deduplicate auto-detect,
|
|
847
|
+
* manual add, and edit flows, so one physical bucket never ends up with
|
|
848
|
+
* multiple competing local records.
|
|
849
|
+
*
|
|
850
|
+
* Identity rules, per provider:
|
|
851
|
+
*
|
|
852
|
+
* azure → (provider, endpoint, bucket) endpoint carries the account
|
|
853
|
+
* gcs → (provider, bucket) GCS bucket names are global
|
|
854
|
+
* s3 → (provider, bucket, region) AWS native: same bucket name
|
|
855
|
+
* can exist in exactly one region,
|
|
856
|
+
* but the region is load-bearing
|
|
857
|
+
* for signing, so a paste with a
|
|
858
|
+
* different region is a distinct
|
|
859
|
+
* connection until the user merges
|
|
860
|
+
* other → (provider, endpoint, bucket) r2, b2, minio, wasabi, storj,
|
|
861
|
+
* digitalocean, contabo, hetzner,
|
|
862
|
+
* linode, ovhcloud, custom
|
|
863
|
+
*
|
|
864
|
+
* Endpoint normalization is aggressive: scheme + host + non-default port +
|
|
865
|
+
* pathname, with trailing slashes and default ports stripped, host lowercased.
|
|
866
|
+
* That collapses the common trip hazards — http vs https, :443 vs empty,
|
|
867
|
+
* trailing slash drift, mixed case host.
|
|
868
|
+
*/
|
|
869
|
+
|
|
870
|
+
interface ConnectionIdentityInput {
|
|
871
|
+
provider: string;
|
|
872
|
+
endpoint: string;
|
|
873
|
+
bucket: string;
|
|
874
|
+
region: string;
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Normalize an endpoint URL to a canonical form suitable for equality checks.
|
|
878
|
+
* Empty / whitespace-only input returns `''` (the "no endpoint" sentinel).
|
|
879
|
+
* Non-URL strings are lowercased and stripped of trailing slashes as a best
|
|
880
|
+
* effort so the comparison is still deterministic.
|
|
881
|
+
*/
|
|
882
|
+
declare function normalizeEndpoint(raw: string | undefined | null): string;
|
|
883
|
+
/** Collapse unknown / empty providers to `'s3'`; otherwise lowercase. */
|
|
884
|
+
declare function normalizeProvider(provider: string | undefined | null): ProviderId;
|
|
885
|
+
/**
|
|
886
|
+
* Produce a canonical key for a connection's identity. Two connection
|
|
887
|
+
* configs with the same identity key point at the same physical bucket.
|
|
888
|
+
* Returns `''` when the config is too incomplete to identify a bucket.
|
|
889
|
+
*/
|
|
890
|
+
declare function connectionIdentityKey(input: ConnectionIdentityInput): string;
|
|
891
|
+
/** Convenience: true when both inputs share the same non-empty identity. */
|
|
892
|
+
declare function isSameConnectionIdentity(a: ConnectionIdentityInput, b: ConnectionIdentityInput): boolean;
|
|
893
|
+
|
|
407
894
|
/**
|
|
408
895
|
* Shared error handling for async viewer load operations.
|
|
409
896
|
*/
|
|
897
|
+
/**
|
|
898
|
+
* True for any abort cascade. Recognizes raw `DOMException(AbortError)`,
|
|
899
|
+
* objects whose `.name` is `AbortError`, deck.gl's `_SourceError("Failed
|
|
900
|
+
* to fetch")` wrapper whose `cause` is an AbortError, and free-text errors
|
|
901
|
+
* from `@developmentseed/geotiff` that mention "aborted". Used to silence
|
|
902
|
+
* cancellation noise without swallowing real failures.
|
|
903
|
+
*/
|
|
904
|
+
declare function isAbortError(err: unknown): boolean;
|
|
410
905
|
/**
|
|
411
906
|
* Extract an error message from an unknown caught value.
|
|
412
907
|
* Returns null for AbortError (caller should silently return).
|
|
@@ -515,6 +1010,59 @@ declare function buildGeoArrowTables(wkbArrays: Uint8Array[], attributes: Map<st
|
|
|
515
1010
|
type: string;
|
|
516
1011
|
}>, knownGeomType?: GeoArrowGeomType): GeoArrowResult[];
|
|
517
1012
|
|
|
1013
|
+
/**
|
|
1014
|
+
* Helpers for parsing DuckDB v1.5 parameterized GEOMETRY type strings.
|
|
1015
|
+
*
|
|
1016
|
+
* DuckDB v1.5 made GEOMETRY a core type with an optional CRS parameter:
|
|
1017
|
+
* GEOMETRY — no CRS attached
|
|
1018
|
+
* GEOMETRY('EPSG:4326') — EPSG form
|
|
1019
|
+
* GEOMETRY('OGC:CRS84') — OGC form (canonical for GeoParquet 1.1+)
|
|
1020
|
+
* GEOMETRY('EPSG:27700') — projected CRS
|
|
1021
|
+
*
|
|
1022
|
+
* Type strings may come from `DESCRIBE`, from the Arrow schema, or from a
|
|
1023
|
+
* legacy code path that still reports `BLOB`. Use these helpers everywhere
|
|
1024
|
+
* instead of ad-hoc regex so behaviour stays consistent.
|
|
1025
|
+
*/
|
|
1026
|
+
interface GeometryTypeInfo {
|
|
1027
|
+
/** True if the type is some form of GEOMETRY (with or without CRS). */
|
|
1028
|
+
isGeometry: boolean;
|
|
1029
|
+
/** True if the type carries a CRS parameter, e.g. GEOMETRY('EPSG:4326'). */
|
|
1030
|
+
hasCrs: boolean;
|
|
1031
|
+
/** The CRS string if present, otherwise null. Raw value, including WGS84. */
|
|
1032
|
+
rawCrs: string | null;
|
|
1033
|
+
/**
|
|
1034
|
+
* The CRS string if present and NOT a WGS84 variant (EPSG:4326, EPSG:4979,
|
|
1035
|
+
* OGC:CRS84). Returns null for WGS84 so callers can skip ST_Transform.
|
|
1036
|
+
*/
|
|
1037
|
+
nonWgs84Crs: string | null;
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* Parse a DuckDB type string and report whether it is a GEOMETRY type, and
|
|
1041
|
+
* whether a CRS parameter is attached.
|
|
1042
|
+
*/
|
|
1043
|
+
declare function parseGeometryTypeCrs(typeStr: string | null | undefined): GeometryTypeInfo;
|
|
1044
|
+
/** True for EPSG:4326, EPSG:4979, OGC:CRS84 and equivalent strings. */
|
|
1045
|
+
declare function isWgs84Crs(crs: string | null | undefined): boolean;
|
|
1046
|
+
/**
|
|
1047
|
+
* Build a `ST_Transform(...)` SQL expression choosing the 2-arg form when the
|
|
1048
|
+
* input already carries its CRS in the GEOMETRY type (DuckDB v1.5), and the
|
|
1049
|
+
* 3-arg form otherwise.
|
|
1050
|
+
*
|
|
1051
|
+
* `geometry_always_xy` is set globally at DB init, so no per-call `always_xy`
|
|
1052
|
+
* argument is needed.
|
|
1053
|
+
*/
|
|
1054
|
+
declare function buildTransformExpr(innerExpr: string, sourceType: string, sourceCrs: string, targetCrs: string): string;
|
|
1055
|
+
/**
|
|
1056
|
+
* Wrap a bare WKB expression with `ST_SetCRS(ST_GeomFromWKB(...))` so that the
|
|
1057
|
+
* resulting GEOMETRY value carries a CRS through the rest of the pipeline.
|
|
1058
|
+
* Used in the legacy GeoParquet fallback where we read the geometry column as
|
|
1059
|
+
* BLOB but still know the source CRS from hyparquet metadata or the GeoParquet
|
|
1060
|
+
* footer.
|
|
1061
|
+
*
|
|
1062
|
+
* If `sourceCrs` is null/empty, returns a plain `ST_GeomFromWKB(...)`.
|
|
1063
|
+
*/
|
|
1064
|
+
declare function wrapWkbWithCrs(wkbExpr: string, sourceCrs: string | null | undefined): string;
|
|
1065
|
+
|
|
518
1066
|
interface HexRow {
|
|
519
1067
|
offset: string;
|
|
520
1068
|
hex: string[];
|
|
@@ -526,6 +1074,142 @@ interface HexRow {
|
|
|
526
1074
|
*/
|
|
527
1075
|
declare function generateHexDump(data: Uint8Array, bytesPerRow?: number): HexRow[];
|
|
528
1076
|
|
|
1077
|
+
/**
|
|
1078
|
+
* Universal cloud storage URL / bucket parser.
|
|
1079
|
+
*
|
|
1080
|
+
* Accepts the many URI/URL formats that users commonly paste and extracts
|
|
1081
|
+
* the correct bucket, region, endpoint, and provider.
|
|
1082
|
+
*
|
|
1083
|
+
* Supported URI schemes:
|
|
1084
|
+
* s3:// s3a:// s3n:// aws:// — Amazon S3 / S3-compatible
|
|
1085
|
+
* r2:// — Cloudflare R2
|
|
1086
|
+
* gs:// gcs:// — Google Cloud Storage
|
|
1087
|
+
* azure:// az:// — Azure Blob Storage
|
|
1088
|
+
* abfs:// abfss:// — Azure Data Lake (ADLS Gen2)
|
|
1089
|
+
* wasbs:// — Azure Blob (Hadoop WASB driver)
|
|
1090
|
+
* swift:// — OpenStack Swift
|
|
1091
|
+
* file:// filesystem:// — Local filesystem
|
|
1092
|
+
*
|
|
1093
|
+
* Supported HTTPS URL patterns:
|
|
1094
|
+
* https://<bucket>.s3.<region>.amazonaws.com[/prefix] — AWS virtual-hosted
|
|
1095
|
+
* https://s3.<region>.amazonaws.com/<bucket>[/prefix] — AWS path-style
|
|
1096
|
+
* https://s3.amazonaws.com/<bucket> — AWS global
|
|
1097
|
+
* https://<account>.r2.cloudflarestorage.com/<bucket> — Cloudflare R2
|
|
1098
|
+
* https://storage.googleapis.com/<bucket> — Google Cloud Storage
|
|
1099
|
+
* https://<bucket>.storage.googleapis.com[/prefix] — GCS virtual-hosted
|
|
1100
|
+
* https://<bucket>.<region>.digitaloceanspaces.com — DigitalOcean Spaces
|
|
1101
|
+
* https://<region>.digitaloceanspaces.com/<bucket> — DO Spaces path-style
|
|
1102
|
+
* https://s3.<region>.wasabisys.com/<bucket> — Wasabi
|
|
1103
|
+
* https://f<id>.backblazeb2.com/file/<bucket> — Backblaze B2
|
|
1104
|
+
* https://<bucket>.s3.<region>.backblazeb2.com — B2 S3-compatible
|
|
1105
|
+
* https://<bucket>.oss-<region>.aliyuncs.com — Alibaba Cloud OSS
|
|
1106
|
+
* https://<bucket>.cos.<region>.myqcloud.com — Tencent COS
|
|
1107
|
+
* https://storage.yandexcloud.net/<bucket> — Yandex Cloud
|
|
1108
|
+
* https://gateway.storjshare.io/<bucket> — Storj S3 gateway
|
|
1109
|
+
* https://link.storjshare.io/raw/<access>/<bucket> — Storj linksharing
|
|
1110
|
+
* https://<custom-endpoint>/<bucket> — Generic S3-compatible
|
|
1111
|
+
*
|
|
1112
|
+
* Also handles plain bucket names (no protocol).
|
|
1113
|
+
*/
|
|
1114
|
+
type StorageProvider = string;
|
|
1115
|
+
interface ParsedStorageUrl {
|
|
1116
|
+
bucket: string;
|
|
1117
|
+
region: string;
|
|
1118
|
+
endpoint: string;
|
|
1119
|
+
provider: StorageProvider;
|
|
1120
|
+
/** Original prefix/path after bucket, if any */
|
|
1121
|
+
prefix: string;
|
|
1122
|
+
}
|
|
1123
|
+
/** STAC API path test, one source of truth. Tests pathname only. */
|
|
1124
|
+
declare const STAC_API_PATH_RE: RegExp;
|
|
1125
|
+
/**
|
|
1126
|
+
* Returns true when the host matches any of the provider host patterns
|
|
1127
|
+
* that `parseStorageUrl` recognizes on the HTTPS branch.
|
|
1128
|
+
*/
|
|
1129
|
+
declare function isKnownBucketHost(host: string): boolean;
|
|
1130
|
+
interface Defaults {
|
|
1131
|
+
region?: string;
|
|
1132
|
+
endpoint?: string;
|
|
1133
|
+
provider?: StorageProvider;
|
|
1134
|
+
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Parse a user-provided bucket/URL string into structured storage connection parts.
|
|
1137
|
+
*/
|
|
1138
|
+
declare function parseStorageUrl(input: string, defaults?: Defaults): ParsedStorageUrl;
|
|
1139
|
+
/**
|
|
1140
|
+
* Returns true if the input looks like a URL/URI rather than a plain bucket name.
|
|
1141
|
+
* Covers all recognized cloud storage URI schemes.
|
|
1142
|
+
*/
|
|
1143
|
+
declare function looksLikeUrl(input: string): boolean;
|
|
1144
|
+
/**
|
|
1145
|
+
* Given a parsed URL result, build a human-readable summary of what was detected.
|
|
1146
|
+
*/
|
|
1147
|
+
declare function describeParseResult(parsed: ParsedStorageUrl): string;
|
|
1148
|
+
type UrlClassification = {
|
|
1149
|
+
kind: 'scheme';
|
|
1150
|
+
parsed: ParsedStorageUrl;
|
|
1151
|
+
} | {
|
|
1152
|
+
kind: 'object-storage';
|
|
1153
|
+
parsed: ParsedStorageUrl;
|
|
1154
|
+
} | {
|
|
1155
|
+
kind: 'stac-api';
|
|
1156
|
+
url: URL;
|
|
1157
|
+
} | {
|
|
1158
|
+
kind: 'remote-file';
|
|
1159
|
+
url: URL;
|
|
1160
|
+
};
|
|
1161
|
+
/**
|
|
1162
|
+
* Classify a user-supplied URL/URI into one of four buckets. Unparseable or
|
|
1163
|
+
* plain inputs fall through to `remote-file` with a best-effort URL parse,
|
|
1164
|
+
* returning a synthetic `https://` URL when `new URL()` would throw.
|
|
1165
|
+
*/
|
|
1166
|
+
declare function classifyUrl(input: string): UrlClassification;
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* Auto-detect hosting bucket from URL search params and window.location.
|
|
1170
|
+
*
|
|
1171
|
+
* Detection priority:
|
|
1172
|
+
* 1. `?url=<storage-url>` query parameter (highest priority)
|
|
1173
|
+
* 2. `window.location.hostname` pattern matching (fallback)
|
|
1174
|
+
*
|
|
1175
|
+
* Also extracts `rootPrefix` when the app is hosted inside a subfolder.
|
|
1176
|
+
*/
|
|
1177
|
+
|
|
1178
|
+
interface DetectedHost {
|
|
1179
|
+
provider: StorageProvider;
|
|
1180
|
+
bucket: string;
|
|
1181
|
+
region: string;
|
|
1182
|
+
endpoint: string;
|
|
1183
|
+
rootPrefix: string;
|
|
1184
|
+
bucketUrl: string;
|
|
1185
|
+
}
|
|
1186
|
+
/**
|
|
1187
|
+
* Detect hosting bucket from current URL.
|
|
1188
|
+
* Returns null when no hosting bucket can be determined.
|
|
1189
|
+
*/
|
|
1190
|
+
declare function detectHostBucket(): DetectedHost | null;
|
|
1191
|
+
/**
|
|
1192
|
+
* Enrich an in-progress connection-config draft with hints from a STAC Item
|
|
1193
|
+
* that declares the Storage Extension (`storage:region`,
|
|
1194
|
+
* `storage:requester_pays`, `storage:platform`, v2 `storage:schemes`).
|
|
1195
|
+
*
|
|
1196
|
+
* Modular by design, callers opt in. The existing `detectHostBucket` flow
|
|
1197
|
+
* does NOT know about STAC, so call sites that already hold a representative
|
|
1198
|
+
* `StacItem` (e.g. classification just after fetching a Catalog / Collection /
|
|
1199
|
+
* ItemCollection) should funnel through this helper before handing the draft
|
|
1200
|
+
* to `connectionStore.saveHostConnection` / `connectionStore.save`. Existing
|
|
1201
|
+
* non-empty fields on the draft are preserved (this never clobbers a
|
|
1202
|
+
* user-set value).
|
|
1203
|
+
*
|
|
1204
|
+
* Returns a shallow copy of `input` with hint fields filled in. Safe to call
|
|
1205
|
+
* with an unrelated item, returns `input` untouched when the extension is
|
|
1206
|
+
* absent or unparseable.
|
|
1207
|
+
*/
|
|
1208
|
+
declare function applyStacItemStorageHints<T extends {
|
|
1209
|
+
region?: string;
|
|
1210
|
+
endpoint?: string;
|
|
1211
|
+
}>(input: T, item: StacItem): T;
|
|
1212
|
+
|
|
529
1213
|
/**
|
|
530
1214
|
* Generic localStorage helpers with SSR safety.
|
|
531
1215
|
*
|
|
@@ -543,6 +1227,93 @@ declare function loadFromStorage<T>(key: string, defaultValue: T): T;
|
|
|
543
1227
|
*/
|
|
544
1228
|
declare function persistToStorage(key: string, value: unknown): void;
|
|
545
1229
|
|
|
1230
|
+
/**
|
|
1231
|
+
* Tiny insertion-order LRU built on top of `Map`. `get` / `has` move the entry
|
|
1232
|
+
* to the most-recent slot, `set` evicts the oldest (and runs `onEvict`) once
|
|
1233
|
+
* the cap is exceeded.
|
|
1234
|
+
*
|
|
1235
|
+
* Keeps the implementation deliberately small. Used by viewer modules that
|
|
1236
|
+
* cache per-source resources (GeoTIFF headers, presigned URLs) which would
|
|
1237
|
+
* otherwise leak across long pan / viewport-reload sessions.
|
|
1238
|
+
*/
|
|
1239
|
+
interface LruCacheOptions<K, V> {
|
|
1240
|
+
/** Maximum number of entries. Must be > 0. */
|
|
1241
|
+
max: number;
|
|
1242
|
+
/** Called when an entry is evicted (LRU overflow or `delete()`). */
|
|
1243
|
+
onEvict?: (key: K, value: V) => void;
|
|
1244
|
+
}
|
|
1245
|
+
declare class LruCache<K, V> {
|
|
1246
|
+
private map;
|
|
1247
|
+
readonly max: number;
|
|
1248
|
+
private onEvict?;
|
|
1249
|
+
constructor(opts: LruCacheOptions<K, V>);
|
|
1250
|
+
get size(): number;
|
|
1251
|
+
has(key: K): boolean;
|
|
1252
|
+
get(key: K): V | undefined;
|
|
1253
|
+
set(key: K, value: V): void;
|
|
1254
|
+
delete(key: K): boolean;
|
|
1255
|
+
clear(): void;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
/**
|
|
1259
|
+
* Framework-agnostic helper that wires a "click on map, run a probe, surface the
|
|
1260
|
+
* result" flow used by every COG-style viewer (`CogViewer`, `StacMosaicViewer`,
|
|
1261
|
+
* `MultiCogViewer`).
|
|
1262
|
+
*
|
|
1263
|
+
* The viewers differ in what the probe returns (a single pixel sample, a per-
|
|
1264
|
+
* channel fan-out, or a topmost-source bbox hit), but they share the same
|
|
1265
|
+
* boilerplate: subscribe to `click`, mark inspecting, await the probe, surface
|
|
1266
|
+
* the payload, abort the previous probe if a new click arrives mid-flight, and
|
|
1267
|
+
* tear the listener down on cleanup.
|
|
1268
|
+
*
|
|
1269
|
+
* No dependency on Svelte, MapLibre, or deck.gl. The `MapLike` shape captures
|
|
1270
|
+
* only what the helper needs from the underlying map so this can be unit-tested
|
|
1271
|
+
* with a tiny stub if needed.
|
|
1272
|
+
*/
|
|
1273
|
+
interface PixelInspectClickEvent {
|
|
1274
|
+
lngLat: {
|
|
1275
|
+
lng: number;
|
|
1276
|
+
lat: number;
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
type PixelInspectClickHandler = (event: PixelInspectClickEvent) => void;
|
|
1280
|
+
/**
|
|
1281
|
+
* Minimal subset of MapLibre's map API used by the inspector. Anything that
|
|
1282
|
+
* dispatches a `click` event with `{lngLat}` and supports symmetric on/off
|
|
1283
|
+
* registration plugs in.
|
|
1284
|
+
*/
|
|
1285
|
+
interface MapLike {
|
|
1286
|
+
on(type: 'click', handler: PixelInspectClickHandler): unknown;
|
|
1287
|
+
off(type: 'click', handler: PixelInspectClickHandler): unknown;
|
|
1288
|
+
}
|
|
1289
|
+
interface PixelInspectProbeRequest {
|
|
1290
|
+
lng: number;
|
|
1291
|
+
lat: number;
|
|
1292
|
+
signal: AbortSignal;
|
|
1293
|
+
}
|
|
1294
|
+
type PixelInspectProbe<T> = (req: PixelInspectProbeRequest) => Promise<T | null>;
|
|
1295
|
+
interface PixelInspectCallbacks<T> {
|
|
1296
|
+
/** Called synchronously when a click is accepted, before the probe is awaited. */
|
|
1297
|
+
onStart(): void;
|
|
1298
|
+
/**
|
|
1299
|
+
* Called once per click after the probe settles. Receives `null` when the
|
|
1300
|
+
* probe returned `null` or threw a non-helper-driven error (including an
|
|
1301
|
+
* `AbortError` that did not originate from this helper's own controller).
|
|
1302
|
+
*/
|
|
1303
|
+
onResult(result: T | null): void;
|
|
1304
|
+
}
|
|
1305
|
+
interface AttachPixelInspectorOptions<T> {
|
|
1306
|
+
probe: PixelInspectProbe<T>;
|
|
1307
|
+
onStart: PixelInspectCallbacks<T>['onStart'];
|
|
1308
|
+
onResult: PixelInspectCallbacks<T>['onResult'];
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Wire a click-to-inspect probe onto `map`. Returns a `detach()` function that
|
|
1312
|
+
* removes the listener AND aborts any in-flight probe. Subsequent clicks abort
|
|
1313
|
+
* the previous probe so a fast double-click never leaves a stale result behind.
|
|
1314
|
+
*/
|
|
1315
|
+
declare function attachPixelInspector<T>(map: MapLike, { probe, onStart, onResult }: AttachPixelInspectorOptions<T>): () => void;
|
|
1316
|
+
|
|
546
1317
|
interface SqlBlock {
|
|
547
1318
|
name: string;
|
|
548
1319
|
sql: string;
|
|
@@ -574,6 +1345,92 @@ declare function interpolateTemplates(text: string, queryResults: Map<string, Re
|
|
|
574
1345
|
*/
|
|
575
1346
|
declare function markSqlBlocks(content: string): string;
|
|
576
1347
|
|
|
1348
|
+
/**
|
|
1349
|
+
* Executes the SQL blocks parsed out of a markdown document (Evidence.dev style)
|
|
1350
|
+
* against an injected query engine, and caches the results by block name. Pairs
|
|
1351
|
+
* with `markdown-sql.ts` (the parser). Pure TypeScript, the engine is supplied by
|
|
1352
|
+
* the host so this module never imports DuckDB or any other heavy dependency.
|
|
1353
|
+
*/
|
|
1354
|
+
declare class MarkdownSqlContext {
|
|
1355
|
+
private engine;
|
|
1356
|
+
private connId;
|
|
1357
|
+
private prefix;
|
|
1358
|
+
private results;
|
|
1359
|
+
constructor(engine: QueryEngine, connId: string, prefix?: string);
|
|
1360
|
+
/** Execute a SQL query and store the result under the given name. */
|
|
1361
|
+
executeSql(sql: string, queryName: string): Promise<Record<string, any>[]>;
|
|
1362
|
+
/**
|
|
1363
|
+
* Transform relative file paths in SQL to full S3 URLs.
|
|
1364
|
+
* e.g. read_parquet('data.parquet') becomes read_parquet('s3://bucket/prefix/data.parquet').
|
|
1365
|
+
*/
|
|
1366
|
+
private transformPaths;
|
|
1367
|
+
getResult(queryName: string): {
|
|
1368
|
+
result: QueryResult;
|
|
1369
|
+
rows: Record<string, any>[];
|
|
1370
|
+
} | undefined;
|
|
1371
|
+
getAllResults(): Map<string, Record<string, any>[]>;
|
|
1372
|
+
getColumns(queryName: string): string[];
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
/**
|
|
1376
|
+
* Lightweight browser-native Jupyter notebook renderer.
|
|
1377
|
+
* Replaces `notebookjs` which depends on jsdom/Buffer (Node.js only).
|
|
1378
|
+
* Handles nbformat 2, 3, 4, and 5.
|
|
1379
|
+
*/
|
|
1380
|
+
interface NotebookConfig {
|
|
1381
|
+
markdown: (md: string) => string;
|
|
1382
|
+
ansi: (text: string) => string;
|
|
1383
|
+
highlighter: (code: string, lang: string) => string;
|
|
1384
|
+
}
|
|
1385
|
+
interface RawNotebook {
|
|
1386
|
+
nbformat: number;
|
|
1387
|
+
nbformat_minor?: number;
|
|
1388
|
+
metadata?: Record<string, any>;
|
|
1389
|
+
cells?: RawCell[];
|
|
1390
|
+
worksheets?: {
|
|
1391
|
+
cells: RawCell[];
|
|
1392
|
+
}[];
|
|
1393
|
+
}
|
|
1394
|
+
interface RawCell {
|
|
1395
|
+
cell_type: string;
|
|
1396
|
+
source?: string | string[];
|
|
1397
|
+
input?: string | string[];
|
|
1398
|
+
outputs?: RawOutput[];
|
|
1399
|
+
prompt_number?: number;
|
|
1400
|
+
execution_count?: number | null;
|
|
1401
|
+
level?: number;
|
|
1402
|
+
language?: string;
|
|
1403
|
+
}
|
|
1404
|
+
interface RawOutput {
|
|
1405
|
+
output_type: string;
|
|
1406
|
+
data?: Record<string, string | string[]>;
|
|
1407
|
+
text?: string | string[];
|
|
1408
|
+
stream?: string;
|
|
1409
|
+
name?: string;
|
|
1410
|
+
png?: string;
|
|
1411
|
+
jpeg?: string;
|
|
1412
|
+
svg?: string;
|
|
1413
|
+
html?: string;
|
|
1414
|
+
latex?: string;
|
|
1415
|
+
traceback?: string[];
|
|
1416
|
+
ename?: string;
|
|
1417
|
+
evalue?: string;
|
|
1418
|
+
[key: string]: any;
|
|
1419
|
+
}
|
|
1420
|
+
interface NotebookMeta {
|
|
1421
|
+
kernelName: string;
|
|
1422
|
+
language: string;
|
|
1423
|
+
cellCount: number;
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Parse and render a Jupyter notebook JSON to a DOM element.
|
|
1427
|
+
* Returns the rendered element and metadata.
|
|
1428
|
+
*/
|
|
1429
|
+
declare function renderNotebook(raw: RawNotebook, config: NotebookConfig): {
|
|
1430
|
+
element: HTMLElement;
|
|
1431
|
+
meta: NotebookMeta;
|
|
1432
|
+
};
|
|
1433
|
+
|
|
577
1434
|
/**
|
|
578
1435
|
* Lightweight Parquet metadata reader using hyparquet.
|
|
579
1436
|
*
|
|
@@ -640,42 +1497,185 @@ declare function extractGeometryTypes(geo: GeoParquetMeta): GeoArrowGeomType[];
|
|
|
640
1497
|
declare function extractBounds(geo: GeoParquetMeta): [number, number, number, number] | null;
|
|
641
1498
|
|
|
642
1499
|
/**
|
|
643
|
-
* STAC
|
|
1500
|
+
* STAC item facets, filtering, and sorting. Pure TS, framework-agnostic.
|
|
644
1501
|
*
|
|
645
|
-
*
|
|
646
|
-
*
|
|
1502
|
+
* Inputs are STAC Items (or any subset compatible with `StacItem`). Outputs
|
|
1503
|
+
* are slim views, auto-detected facet descriptors, and filtered/sorted view
|
|
1504
|
+
* arrays. No DOM, no Svelte, no fetch, no maplibre, safe to publish via
|
|
1505
|
+
* `@walkthru-earth/objex-utils`.
|
|
1506
|
+
*
|
|
1507
|
+
* The flow is intentionally one-directional:
|
|
1508
|
+
* StacItem[] --extractItemView--> StacItemView[]
|
|
1509
|
+
* StacItemView[] --buildFacets--> FacetSet
|
|
1510
|
+
* StacItemView[] + FacetState --applyFacets--> StacItemView[]
|
|
1511
|
+
* StacItemView[] + FacetSort --sortViews--> StacItemView[]
|
|
1512
|
+
*
|
|
1513
|
+
* Callers (any framework) can hold the views array, derive a FacetSet on
|
|
1514
|
+
* change, and reactively filter / sort it without re-touching the original
|
|
1515
|
+
* StacItems.
|
|
647
1516
|
*/
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
interface StacItem {
|
|
657
|
-
type: 'Feature';
|
|
658
|
-
stac_version: string;
|
|
1517
|
+
|
|
1518
|
+
/**
|
|
1519
|
+
* Compact, render-ready projection of a STAC Item. Keeps only the fields
|
|
1520
|
+
* needed for facet UI, sorting, footprint rendering, and the inspector
|
|
1521
|
+
* panel. The full original item is retained on `raw` for callers that need
|
|
1522
|
+
* to inspect arbitrary properties without a re-extract pass.
|
|
1523
|
+
*/
|
|
1524
|
+
interface StacItemView {
|
|
659
1525
|
id: string;
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
1526
|
+
collection: string | null;
|
|
1527
|
+
bbox: [number, number, number, number] | null;
|
|
1528
|
+
/** ISO 8601 datetime, or `start_datetime` when only an interval is given. */
|
|
1529
|
+
datetime: string | null;
|
|
1530
|
+
/** End of `start_datetime` / `end_datetime` interval, when present. */
|
|
1531
|
+
endDatetime: string | null;
|
|
1532
|
+
/** `eo:cloud_cover` percent (0-100), null when absent. */
|
|
1533
|
+
cloudCover: number | null;
|
|
1534
|
+
/** Ground sample distance in meters, null when absent. */
|
|
1535
|
+
gsd: number | null;
|
|
1536
|
+
platform: string | null;
|
|
1537
|
+
constellation: string | null;
|
|
1538
|
+
instruments: string[];
|
|
1539
|
+
/** EPSG code from `proj:epsg`, null when absent or non-numeric. */
|
|
1540
|
+
epsg: number | null;
|
|
1541
|
+
/** Best-effort thumbnail / overview href, null when no preview asset. */
|
|
1542
|
+
thumbnailHref: string | null;
|
|
1543
|
+
/** Asset role set across all assets on the item. */
|
|
1544
|
+
assetRoles: string[];
|
|
1545
|
+
/** Original item, retained so the inspector can show the raw JSON. */
|
|
1546
|
+
raw: StacItem;
|
|
666
1547
|
}
|
|
667
|
-
/**
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
1548
|
+
/** Project a STAC Item into a `StacItemView`. Always succeeds. */
|
|
1549
|
+
declare function extractItemView(item: StacItem): StacItemView;
|
|
1550
|
+
/**
|
|
1551
|
+
* Numeric facet, e.g. cloud cover. `min`/`max` are derived from the loaded
|
|
1552
|
+
* views so the UI can use them as slider bounds. `count` is how many of the
|
|
1553
|
+
* input views had this field at all.
|
|
1554
|
+
*/
|
|
1555
|
+
interface NumericFacet {
|
|
1556
|
+
kind: 'numeric';
|
|
1557
|
+
field: NumericFacetField;
|
|
1558
|
+
min: number;
|
|
1559
|
+
max: number;
|
|
1560
|
+
count: number;
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* Enum facet, e.g. platform. `values` is sorted by descending count so the
|
|
1564
|
+
* most common values surface first in chip lists.
|
|
1565
|
+
*/
|
|
1566
|
+
interface EnumFacet {
|
|
1567
|
+
kind: 'enum';
|
|
1568
|
+
field: EnumFacetField;
|
|
1569
|
+
values: {
|
|
1570
|
+
value: string;
|
|
1571
|
+
count: number;
|
|
677
1572
|
}[];
|
|
678
1573
|
}
|
|
1574
|
+
/**
|
|
1575
|
+
* Calendar-aligned bin granularity for the datetime histogram. Picked
|
|
1576
|
+
* automatically from the loaded items' time span so callers never see a
|
|
1577
|
+
* span-vs-resolution mismatch (e.g. month bins on a 30-day window or day bins
|
|
1578
|
+
* on a 20-year archive). See `pickGranularity` for the breakpoints.
|
|
1579
|
+
*/
|
|
1580
|
+
type DatetimeGranularity = 'day' | 'week' | 'month' | 'year';
|
|
1581
|
+
/**
|
|
1582
|
+
* Datetime facet, with min/max for slider bounds and a calendar-aligned
|
|
1583
|
+
* histogram the UI can render under a range slider. Each bin spans one
|
|
1584
|
+
* `granularity` unit (UTC day, ISO week starting Monday, calendar month, or
|
|
1585
|
+
* calendar year). `bins[i]` is the count of items whose `datetime` falls
|
|
1586
|
+
* inside the bin starting at `binEdges[i]` (epoch ms, UTC). `bins.length ===
|
|
1587
|
+
* binEdges.length`, capped at `DATETIME_HISTOGRAM_BINS_MAX`.
|
|
1588
|
+
*/
|
|
1589
|
+
interface DatetimeFacet {
|
|
1590
|
+
kind: 'datetime';
|
|
1591
|
+
field: 'datetime';
|
|
1592
|
+
/** Earliest datetime, ISO 8601. */
|
|
1593
|
+
min: string;
|
|
1594
|
+
/** Latest datetime, ISO 8601. */
|
|
1595
|
+
max: string;
|
|
1596
|
+
count: number;
|
|
1597
|
+
/** Per-bin counts. Length matches `binEdges.length`. */
|
|
1598
|
+
bins: number[];
|
|
1599
|
+
/** Auto-picked calendar granularity for each histogram bucket. */
|
|
1600
|
+
granularity: DatetimeGranularity;
|
|
1601
|
+
/** Epoch ms (UTC) of each bin's start boundary. Same length as `bins`. */
|
|
1602
|
+
binEdges: number[];
|
|
1603
|
+
}
|
|
1604
|
+
type Facet = NumericFacet | EnumFacet | DatetimeFacet;
|
|
1605
|
+
type NumericFacetField = 'cloudCover' | 'gsd';
|
|
1606
|
+
type EnumFacetField = 'collection' | 'platform' | 'constellation' | 'instruments' | 'assetRoles';
|
|
1607
|
+
/**
|
|
1608
|
+
* Soft cap on histogram bin count. The actual count is derived from the span
|
|
1609
|
+
* + granularity (e.g. a 5-year span at month granularity emits 60 bins, a
|
|
1610
|
+
* 90-day span at day granularity emits 90 bins). Spans that would exceed the
|
|
1611
|
+
* cap are clamped here, the next coarser granularity should already have been
|
|
1612
|
+
* picked by `pickGranularity` so this only protects against pathological
|
|
1613
|
+
* inputs.
|
|
1614
|
+
*/
|
|
1615
|
+
declare const DATETIME_HISTOGRAM_BINS_MAX = 64;
|
|
1616
|
+
/** @deprecated Retained for backward compatibility. Use `DATETIME_HISTOGRAM_BINS_MAX`. */
|
|
1617
|
+
declare const DATETIME_HISTOGRAM_BINS = 32;
|
|
1618
|
+
/** Result of `buildFacets`: every facet that has variance in the input set. */
|
|
1619
|
+
interface FacetSet {
|
|
1620
|
+
datetime: DatetimeFacet | null;
|
|
1621
|
+
numeric: NumericFacet[];
|
|
1622
|
+
enums: EnumFacet[];
|
|
1623
|
+
/** Total number of views the facet set was built from. */
|
|
1624
|
+
total: number;
|
|
1625
|
+
}
|
|
1626
|
+
/**
|
|
1627
|
+
* Pick a calendar granularity from the time span. Inspired by lazycogs's
|
|
1628
|
+
* `_TemporalGrouper` family: short windows surface daily / weekly cadence,
|
|
1629
|
+
* long archives roll up to month / year so each bin still represents a
|
|
1630
|
+
* meaningful slice of data.
|
|
1631
|
+
*/
|
|
1632
|
+
declare function pickGranularity(spanMs: number): DatetimeGranularity;
|
|
1633
|
+
/**
|
|
1634
|
+
* Scan a list of views and emit only those facets that have meaningful
|
|
1635
|
+
* variance. A facet is omitted when:
|
|
1636
|
+
* - numeric: fewer than two distinct finite values
|
|
1637
|
+
* - enum: fewer than two distinct values
|
|
1638
|
+
* - datetime: fewer than two parseable timestamps with distinct values
|
|
1639
|
+
*
|
|
1640
|
+
* The intent is "render only the controls that will narrow this dataset",
|
|
1641
|
+
* so callers can map each returned facet to a UI component without further
|
|
1642
|
+
* checks.
|
|
1643
|
+
*/
|
|
1644
|
+
declare function buildFacets(views: StacItemView[]): FacetSet;
|
|
1645
|
+
/**
|
|
1646
|
+
* Mutable filter state, intended to be held by the UI layer. Each entry is
|
|
1647
|
+
* optional, omitting an entry means "no filter on this field". Numeric
|
|
1648
|
+
* ranges are inclusive on both ends. Enum sets are union-match (any value
|
|
1649
|
+
* in the set passes).
|
|
1650
|
+
*/
|
|
1651
|
+
interface FacetState {
|
|
1652
|
+
datetime?: {
|
|
1653
|
+
min?: string;
|
|
1654
|
+
max?: string;
|
|
1655
|
+
};
|
|
1656
|
+
numeric?: Partial<Record<NumericFacetField, {
|
|
1657
|
+
min?: number;
|
|
1658
|
+
max?: number;
|
|
1659
|
+
}>>;
|
|
1660
|
+
enums?: Partial<Record<EnumFacetField, string[]>>;
|
|
1661
|
+
}
|
|
1662
|
+
/**
|
|
1663
|
+
* Filter views by `state`. Empty / missing entries are no-ops. Returns a new
|
|
1664
|
+
* array, the input is never mutated. Order is preserved, run `sortViews`
|
|
1665
|
+
* afterwards if a different order is needed.
|
|
1666
|
+
*/
|
|
1667
|
+
declare function applyFacets(views: StacItemView[], state: FacetState | null | undefined): StacItemView[];
|
|
1668
|
+
type FacetSort = 'datetime-desc' | 'datetime-asc' | 'cloud-asc' | 'cloud-desc' | 'gsd-asc' | 'gsd-desc' | 'id-asc';
|
|
1669
|
+
/**
|
|
1670
|
+
* Sort views by one of a fixed set of strategies. Items missing the sort
|
|
1671
|
+
* field always sink to the bottom (regardless of asc/desc) so a `cloud-asc`
|
|
1672
|
+
* sort never surfaces "items with no cloud cover" above the cleanest scenes.
|
|
1673
|
+
*/
|
|
1674
|
+
declare function sortViews(views: StacItemView[], sort: FacetSort): StacItemView[];
|
|
1675
|
+
/** True when any filter in `state` would actually narrow the input. */
|
|
1676
|
+
declare function hasActiveFilters(state: FacetState | null | undefined): boolean;
|
|
1677
|
+
/** Return `state` with every filter cleared. Useful for reset buttons. */
|
|
1678
|
+
declare function emptyFacetState(): FacetState;
|
|
679
1679
|
|
|
680
1680
|
/**
|
|
681
1681
|
* stac-geoparquet helpers.
|
|
@@ -769,69 +1769,449 @@ declare function pickStacPrimaryAsset(assets: Record<string, StacAsset> | null |
|
|
|
769
1769
|
declare function stacRowToItem(row: StacGeoparquetRow, baseUrl: string, opts?: StacRowToItemOptions): StacItem;
|
|
770
1770
|
|
|
771
1771
|
/**
|
|
772
|
-
*
|
|
1772
|
+
* STAC link-following hydrator. Walks `links[rel=item]` (Collection),
|
|
1773
|
+
* `links[rel=child]` → `links[rel=item]` (Catalog), and `links[rel=next]`
|
|
1774
|
+
* (paginated FeatureCollection / STAC API) into a flat list of StacItems.
|
|
1775
|
+
*/
|
|
1776
|
+
|
|
1777
|
+
interface HydrateOptions {
|
|
1778
|
+
signal: AbortSignal;
|
|
1779
|
+
/** Max parallel fetches. Default 12. */
|
|
1780
|
+
concurrency?: number;
|
|
1781
|
+
/** Hard cap on items; catalogs larger than this are truncated. Default 2000. */
|
|
1782
|
+
limit?: number;
|
|
1783
|
+
/** Follow `links[rel=next]` pagination in FeatureCollections. Default true. */
|
|
1784
|
+
followPagination?: boolean;
|
|
1785
|
+
/** Emit fetched items in batches for progressive rendering. */
|
|
1786
|
+
onBatch?: (items: StacItem[]) => void;
|
|
1787
|
+
/** Emit progress totals for UI. */
|
|
1788
|
+
onProgress?: (fetched: number, totalHinted: number | undefined) => void;
|
|
1789
|
+
/**
|
|
1790
|
+
* Map an absolute HTTPS URL to a bucket-relative key when it belongs to the
|
|
1791
|
+
* caller's connection. When provided and it returns a non-null string,
|
|
1792
|
+
* `fetchJson` routes through the storage adapter (which handles SigV4) instead
|
|
1793
|
+
* of a raw cross-origin `fetch`, so private-bucket catalogs can be walked.
|
|
1794
|
+
*/
|
|
1795
|
+
urlToKey?: (absoluteUrl: string) => string | null;
|
|
1796
|
+
/**
|
|
1797
|
+
* Optional native STAC API filters appended to the `rel="items"` endpoint
|
|
1798
|
+
* (and applied to `links[rel=next]` pages). Lets callers narrow a
|
|
1799
|
+
* collection by spatial / temporal extent before hydration.
|
|
1800
|
+
*/
|
|
1801
|
+
itemsQuery?: StacItemsQuery;
|
|
1802
|
+
}
|
|
1803
|
+
/** Native filters supported by OGC API Features / STAC API on `/items`. */
|
|
1804
|
+
interface StacItemsQuery {
|
|
1805
|
+
/** WGS84 bbox `[west, south, east, north]`. */
|
|
1806
|
+
bbox?: [number, number, number, number];
|
|
1807
|
+
/** RFC 3339 instant or interval `start/end` (use `..` for open ends). */
|
|
1808
|
+
datetime?: string;
|
|
1809
|
+
/** Per-page item count hint, the server may cap this. */
|
|
1810
|
+
limit?: number;
|
|
1811
|
+
/**
|
|
1812
|
+
* CQL2-JSON filter expression (STAC API Filter extension). When set, gets
|
|
1813
|
+
* appended as `?filter=<json>&filter-lang=cql2-json` and re-stamped onto
|
|
1814
|
+
* every `rel="next"` page so cursor URLs cannot strip it.
|
|
1815
|
+
*/
|
|
1816
|
+
filter?: unknown;
|
|
1817
|
+
}
|
|
1818
|
+
interface HydrateResult {
|
|
1819
|
+
items: StacItem[];
|
|
1820
|
+
truncated: boolean;
|
|
1821
|
+
rootBaseHref: string;
|
|
1822
|
+
}
|
|
1823
|
+
declare function hydrateStacItems(root: StacRoutableKind, baseHref: string, adapter: StorageAdapter, opts: HydrateOptions): Promise<HydrateResult>;
|
|
1824
|
+
/**
|
|
1825
|
+
* True when the payload exposes a `rel="items"` link (OGC API Features /
|
|
1826
|
+
* STAC API convention). Lets callers switch to viewport-scoped fetching
|
|
1827
|
+
* instead of walking every page.
|
|
1828
|
+
*/
|
|
1829
|
+
declare function hasStacItemsEndpoint(payload: StacCollection | StacCatalog): boolean;
|
|
1830
|
+
/**
|
|
1831
|
+
* Resolve a possibly-relative href against a base. STAC catalogs commonly use
|
|
1832
|
+
* `./child/foo.json` or `../foo.json`. `new URL(relative, base)` handles both.
|
|
1833
|
+
*/
|
|
1834
|
+
declare function absolutizeHref(href: string, baseHref: string): string;
|
|
1835
|
+
|
|
1836
|
+
/**
|
|
1837
|
+
* Translate a `FacetState` into native STAC API parameters and CQL2 filters,
|
|
1838
|
+
* gated by what the endpoint advertises in `conformsTo`. Pure TS, framework
|
|
1839
|
+
* and transport agnostic.
|
|
773
1840
|
*
|
|
774
|
-
*
|
|
775
|
-
*
|
|
1841
|
+
* The split between this module and `stac-facets.ts` is intentional. Facets
|
|
1842
|
+
* own discovery and client-side filtering (works for any source). This
|
|
1843
|
+
* module owns *server-side push-down*, which only makes sense when the
|
|
1844
|
+
* source is a STAC API that supports OGC API Features query params or the
|
|
1845
|
+
* STAC API filter extension.
|
|
776
1846
|
*
|
|
777
|
-
*
|
|
778
|
-
*
|
|
779
|
-
*
|
|
780
|
-
*
|
|
781
|
-
*
|
|
782
|
-
*
|
|
783
|
-
*
|
|
784
|
-
*
|
|
785
|
-
*
|
|
1847
|
+
* Callers that want push-down:
|
|
1848
|
+
* 1. Fetch the API root, read `conformsTo`.
|
|
1849
|
+
* 2. `caps = sniffApiCapabilities(conformsTo)` once per session.
|
|
1850
|
+
* 3. On each pan / filter change:
|
|
1851
|
+
* const native = toNativeQuery(state, caps);
|
|
1852
|
+
* const filter = caps.cql2 ? toCql2Filter(state, caps) : null;
|
|
1853
|
+
* Pass `native` to the items endpoint and apply `filter` via
|
|
1854
|
+
* `?filter=<json-encoded>` when present. Anything that could not be
|
|
1855
|
+
* pushed down stays in `state` and is filtered client-side via
|
|
1856
|
+
* `applyFacets`.
|
|
1857
|
+
*/
|
|
1858
|
+
|
|
1859
|
+
/**
|
|
1860
|
+
* Subset of STAC API capabilities relevant to filter push-down. Read once
|
|
1861
|
+
* per session from the API root's `conformsTo` array.
|
|
1862
|
+
*/
|
|
1863
|
+
interface StacApiCapabilities {
|
|
1864
|
+
/** Supports `bbox=` query param (OGC API Features core). */
|
|
1865
|
+
bbox: boolean;
|
|
1866
|
+
/** Supports `datetime=` query param (OGC API Features core). */
|
|
1867
|
+
datetime: boolean;
|
|
1868
|
+
/** Supports `collections=` filter (STAC API Item Search). */
|
|
1869
|
+
collections: boolean;
|
|
1870
|
+
/** Supports the STAC API Filter extension via `filter=` + `filter-lang=cql2-json`. */
|
|
1871
|
+
cql2: boolean;
|
|
1872
|
+
/** Queryables endpoint advertised, lets clients sniff filterable property names. */
|
|
1873
|
+
queryables: boolean;
|
|
1874
|
+
}
|
|
1875
|
+
/**
|
|
1876
|
+
* Parse a STAC API `conformsTo` array into a capability flag set. Tolerant
|
|
1877
|
+
* of unknown URIs, missing entries, and casing differences. Defaults to all
|
|
1878
|
+
* `false` when given an empty / non-array input, so a caller that hasn't
|
|
1879
|
+
* fetched the root yet never accidentally pushes down something the API
|
|
1880
|
+
* cannot honor.
|
|
1881
|
+
*/
|
|
1882
|
+
declare function sniffApiCapabilities(conformsTo: unknown): StacApiCapabilities;
|
|
1883
|
+
/**
|
|
1884
|
+
* Generic STAC items query, compatible with both OGC API Features
|
|
1885
|
+
* (`/collections/{id}/items`) and STAC API Item Search (`/search`). Mirrors
|
|
1886
|
+
* the shape `stac-hydrate.ts::StacItemsQuery` expects, plus optional
|
|
1887
|
+
* `collections` and `filter` for the search endpoint.
|
|
1888
|
+
*/
|
|
1889
|
+
interface StacNativeQuery {
|
|
1890
|
+
bbox?: [number, number, number, number];
|
|
1891
|
+
datetime?: string;
|
|
1892
|
+
collections?: string[];
|
|
1893
|
+
limit?: number;
|
|
1894
|
+
/** CQL2-JSON object, encode with `JSON.stringify` when serializing. */
|
|
1895
|
+
filter?: unknown;
|
|
1896
|
+
'filter-lang'?: 'cql2-json';
|
|
1897
|
+
}
|
|
1898
|
+
interface ToNativeQueryOptions {
|
|
1899
|
+
bbox?: [number, number, number, number];
|
|
1900
|
+
limit?: number;
|
|
1901
|
+
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Translate `state` into the subset of native query params the API supports.
|
|
1904
|
+
* Anything that can't be pushed down is silently dropped here, the caller is
|
|
1905
|
+
* expected to keep applying it client-side via `applyFacets`. This is safe
|
|
1906
|
+
* because client filtering is always a superset, never a contradiction.
|
|
786
1907
|
*
|
|
787
|
-
*
|
|
788
|
-
*
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
*
|
|
793
|
-
*
|
|
794
|
-
*
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
*
|
|
799
|
-
*
|
|
800
|
-
*
|
|
801
|
-
* https://storage.yandexcloud.net/<bucket> — Yandex Cloud
|
|
802
|
-
* https://gateway.storjshare.io/<bucket> — Storj S3 gateway
|
|
803
|
-
* https://link.storjshare.io/raw/<access>/<bucket> — Storj linksharing
|
|
804
|
-
* https://<custom-endpoint>/<bucket> — Generic S3-compatible
|
|
1908
|
+
* `bbox` / `limit` are accepted as overrides because they typically come
|
|
1909
|
+
* from the viewer's viewport + user setting, not from `state`.
|
|
1910
|
+
*/
|
|
1911
|
+
declare function toNativeQuery(state: FacetState | null | undefined, caps: StacApiCapabilities, opts?: ToNativeQueryOptions): StacNativeQuery;
|
|
1912
|
+
/**
|
|
1913
|
+
* CQL2-JSON expression node (very loose typing because the spec allows
|
|
1914
|
+
* arbitrary nesting and we only emit a small subset). Use `unknown` at the
|
|
1915
|
+
* boundary, cast inside this module.
|
|
1916
|
+
*/
|
|
1917
|
+
type Cql2Node = unknown;
|
|
1918
|
+
/**
|
|
1919
|
+
* Build a CQL2-JSON `and` expression from a `FacetState`, covering the
|
|
1920
|
+
* filters that aren't already handled by native params. Returns `null` when
|
|
1921
|
+
* nothing in `state` requires CQL2 (so the caller can omit `filter=`).
|
|
805
1922
|
*
|
|
806
|
-
*
|
|
1923
|
+
* Currently emits:
|
|
1924
|
+
* - eo:cloud_cover (between)
|
|
1925
|
+
* - gsd (between)
|
|
1926
|
+
* - proj:epsg (=)
|
|
1927
|
+
* - platform (in)
|
|
1928
|
+
* - constellation (in)
|
|
1929
|
+
* - instruments (a_overlaps)
|
|
1930
|
+
*
|
|
1931
|
+
* `collection` and `datetime` are skipped here when the corresponding native
|
|
1932
|
+
* cap is set, since those are cheaper to push as plain query params. They
|
|
1933
|
+
* fall through to CQL2 only when the API advertises CQL2 but not the
|
|
1934
|
+
* matching native capability (rare but legal).
|
|
807
1935
|
*/
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
1936
|
+
declare function toCql2Filter(state: FacetState | null | undefined, caps: StacApiCapabilities): Cql2Node | null;
|
|
1937
|
+
/**
|
|
1938
|
+
* Subtract everything that was pushed down from `state`, returning the
|
|
1939
|
+
* remaining state that the caller still has to apply client-side. Lets the
|
|
1940
|
+
* UI avoid double-filtering (which would just be a no-op but wastes work).
|
|
1941
|
+
*
|
|
1942
|
+
* This is a structural diff, not a deep clone, the input is not mutated.
|
|
1943
|
+
*/
|
|
1944
|
+
declare function residualState(state: FacetState | null | undefined, caps: StacApiCapabilities): FacetState;
|
|
1945
|
+
|
|
1946
|
+
/**
|
|
1947
|
+
* StacSource contract. Unified interface for the three STAC ingestion paths
|
|
1948
|
+
* (STAC API, stac-geoparquet, self-contained static catalog) so the viewer
|
|
1949
|
+
* has a single orchestration loop and the UI can branch on capability flags
|
|
1950
|
+
* instead of hard-coded discovery modes.
|
|
1951
|
+
*
|
|
1952
|
+
* Pure TypeScript. No Svelte / maplibre / deck.gl / DuckDB on this import
|
|
1953
|
+
* graph. The DuckDB-bound parquet implementation lives under `query/` so the
|
|
1954
|
+
* `utils/` side stays publishable via `@walkthru-earth/objex-utils` (slice 6).
|
|
1955
|
+
*
|
|
1956
|
+
* Per-batch `pushedDown` / `residual` reporting lets the caller skip
|
|
1957
|
+
* client-side filtering for dimensions the engine already narrowed, and lets
|
|
1958
|
+
* the UI render capability badges. A parquet file with a STRUCT
|
|
1959
|
+
* `properties` column can push `eo:cloud_cover` while a sibling file with
|
|
1960
|
+
* an opaque `properties` cannot, so the report is per batch, not per source.
|
|
1961
|
+
*/
|
|
1962
|
+
|
|
1963
|
+
/** Which underlying engine drives this source. Used by the viewer to pick
|
|
1964
|
+
* atomic-swap-vs-append, by the UI to choose copy / badges, and by tests. */
|
|
1965
|
+
type StacSourceKind = 'api' | 'parquet' | 'static';
|
|
1966
|
+
/**
|
|
1967
|
+
* Per-source capability surface. Read by the viewer at construction (no await
|
|
1968
|
+
* — sources are synchronous to construct so the orchestrator can branch on
|
|
1969
|
+
* `kind` before any I/O) and by the filter UI to decide which controls to
|
|
1970
|
+
* disable / badge as "client-side only".
|
|
1971
|
+
*
|
|
1972
|
+
* The `pushdown` map is exhaustive: every facet field listed in
|
|
1973
|
+
* `FacetState` has a flag here, so adding a new facet is a compile-time
|
|
1974
|
+
* error in every consumer until they handle it.
|
|
1975
|
+
*/
|
|
1976
|
+
interface StacSourceCapabilities {
|
|
1977
|
+
kind: StacSourceKind;
|
|
1978
|
+
/** Human-readable label for HUD copy. e.g. "STAC API", "stac-geoparquet". */
|
|
1979
|
+
label: string;
|
|
1980
|
+
/** True when count(filter, bbox) is cheap. UI surfaces "Y of X". */
|
|
1981
|
+
countAvailable: boolean;
|
|
1982
|
+
/** True when query() yields multiple batches before completing. */
|
|
1983
|
+
streaming: boolean;
|
|
1984
|
+
/**
|
|
1985
|
+
* True when the underlying source is a hive-partitioned parquet directory
|
|
1986
|
+
* (e.g. `s3://bucket/prefix/year=2023/month=01/...`). Set by the parquet
|
|
1987
|
+
* source when the factory detects a directory layout (or the SDK passes
|
|
1988
|
+
* `hivePartitioned: true`). Lets the viewer surface a HUD hint without
|
|
1989
|
+
* inspecting `kind === 'parquet'` alone, since the same `kind` covers
|
|
1990
|
+
* single-file stac-geoparquet.
|
|
1991
|
+
*/
|
|
1992
|
+
hivePartitioned?: boolean;
|
|
1993
|
+
pushdown: {
|
|
1994
|
+
bbox: boolean;
|
|
1995
|
+
datetime: boolean;
|
|
1996
|
+
collection: boolean;
|
|
1997
|
+
cloudCover: boolean;
|
|
1998
|
+
gsd: boolean;
|
|
1999
|
+
epsg: boolean;
|
|
2000
|
+
platform: boolean;
|
|
2001
|
+
constellation: boolean;
|
|
2002
|
+
instruments: boolean;
|
|
2003
|
+
assetRoles: boolean;
|
|
2004
|
+
};
|
|
816
2005
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
2006
|
+
/** Per-query inputs. The signal is required, sources MUST throw
|
|
2007
|
+
* `DOMException("Aborted", "AbortError")` on abort, never silently complete. */
|
|
2008
|
+
interface StacSourceRequest {
|
|
2009
|
+
/** WGS84 viewport bbox `[west, south, east, north]`. Required. Sources that
|
|
2010
|
+
* cannot push down bbox still receive it so they can stream the whole set
|
|
2011
|
+
* and rely on the caller's residual filter. */
|
|
2012
|
+
bbox: [number, number, number, number];
|
|
2013
|
+
filter: FacetState;
|
|
2014
|
+
limit: number;
|
|
2015
|
+
/** Per-page hint for sources that paginate. Server may ignore. */
|
|
2016
|
+
pageSize?: number;
|
|
2017
|
+
signal: AbortSignal;
|
|
2018
|
+
}
|
|
2019
|
+
/** One yielded batch of items. */
|
|
2020
|
+
interface StacSourceBatch {
|
|
2021
|
+
items: StacItem[];
|
|
2022
|
+
/** Subset of filter the source / engine applied. UI reports as "pushed". */
|
|
2023
|
+
pushedDown: FacetState;
|
|
2024
|
+
/** Subset of filter the caller still has to apply via applyFacets(). */
|
|
2025
|
+
residual: FacetState;
|
|
2026
|
+
/** True when no more batches will arrive for this request. The async
|
|
2027
|
+
* iterator's own end-of-iteration also signals done; this flag lets a
|
|
2028
|
+
* caller break the loop at the moment a single-yield source completes. */
|
|
2029
|
+
done: boolean;
|
|
2030
|
+
/** Best-effort hint of total matching items, when the source knows. */
|
|
2031
|
+
totalHinted?: number;
|
|
821
2032
|
}
|
|
2033
|
+
interface StacSource {
|
|
2034
|
+
capabilities: StacSourceCapabilities;
|
|
2035
|
+
query(req: StacSourceRequest): AsyncIterable<StacSourceBatch>;
|
|
2036
|
+
/** Optional cheap count(filter, bbox). Surfaced as "Y of X" when set. */
|
|
2037
|
+
count?(filter: FacetState, bbox: StacSourceRequest['bbox'], signal: AbortSignal): Promise<number>;
|
|
2038
|
+
}
|
|
2039
|
+
/** All-false push-down flags. Helper to keep capability declarations terse. */
|
|
2040
|
+
declare function emptyPushdown(): StacSourceCapabilities['pushdown'];
|
|
2041
|
+
|
|
822
2042
|
/**
|
|
823
|
-
*
|
|
2043
|
+
* STAC API implementation of the StacSource contract. Wraps `hydrateStacItems`
|
|
2044
|
+
* link-walking with `itemsQuery: {bbox, datetime, limit, filter}` push-down and
|
|
2045
|
+
* yields each `onBatch` as a `StacSourceBatch`.
|
|
2046
|
+
*
|
|
2047
|
+
* Slice 2 sniffs the catalog/collection's `conformsTo` array once per source
|
|
2048
|
+
* instance, builds a CQL2-JSON filter (cloud cover / gsd / platform /
|
|
2049
|
+
* constellation / instruments / collection) via `toCql2Filter`, and reports
|
|
2050
|
+
* the actually-pushed subset of `FacetState` plus the residual the caller still
|
|
2051
|
+
* has to apply via `applyFacets`. When the sniff fails or `conformsTo` lacks
|
|
2052
|
+
* the Filter extension, behavior degrades gracefully to slice-1 (bbox+datetime
|
|
2053
|
+
* only).
|
|
2054
|
+
*
|
|
2055
|
+
* Pure TypeScript. No DuckDB / Svelte / maplibre / deck.gl import. The
|
|
2056
|
+
* `StorageAdapter` import is structural (an interface), and the actual
|
|
2057
|
+
* adapter is injected via `deps`.
|
|
824
2058
|
*/
|
|
825
|
-
|
|
2059
|
+
|
|
2060
|
+
interface StacApiSourceDeps {
|
|
2061
|
+
adapter: StorageAdapter;
|
|
2062
|
+
baseHref: string;
|
|
2063
|
+
urlToKey?: (absoluteUrl: string) => string | null;
|
|
2064
|
+
concurrency?: number;
|
|
2065
|
+
}
|
|
826
2066
|
/**
|
|
827
|
-
*
|
|
828
|
-
*
|
|
2067
|
+
* Construct a STAC API source. `kind` is the classified payload from
|
|
2068
|
+
* `classifyStac` (Collection / Catalog with `rel="items"`, or a STAC API
|
|
2069
|
+
* `item-collection` page). The factory checks before dispatching here, this
|
|
2070
|
+
* function does not re-validate.
|
|
2071
|
+
*
|
|
2072
|
+
* The advertised `capabilities.pushdown` flags reflect the *ceiling* of what a
|
|
2073
|
+
* STAC API can push (everything CQL2 covers). The actual push-down per request
|
|
2074
|
+
* depends on the `conformsTo` sniff and is reported per-batch in
|
|
2075
|
+
* `pushedDown` / `residual` so the caller can re-filter only what's left.
|
|
829
2076
|
*/
|
|
830
|
-
declare function
|
|
2077
|
+
declare function createApiSource(kind: StacRoutableKind, deps: StacApiSourceDeps): StacSource;
|
|
2078
|
+
|
|
831
2079
|
/**
|
|
832
|
-
*
|
|
2080
|
+
* Self-contained static catalog implementation of the StacSource contract.
|
|
2081
|
+
* Wraps `hydrateStacItems` link-walking with no `itemsQuery`, so the entire
|
|
2082
|
+
* advertised tree is fetched and the caller filters client-side.
|
|
2083
|
+
*
|
|
2084
|
+
* Slice 1 reports zero push-down. Slice 4 adds extent-pruning (skip child
|
|
2085
|
+
* links whose `extent.spatial` / `extent.temporal` does not intersect the
|
|
2086
|
+
* request bbox / datetime), which lifts `bbox` and `datetime` to true.
|
|
2087
|
+
*
|
|
2088
|
+
* Pure TypeScript. No DuckDB / Svelte / maplibre / deck.gl import.
|
|
833
2089
|
*/
|
|
834
|
-
|
|
2090
|
+
|
|
2091
|
+
interface StacStaticSourceDeps {
|
|
2092
|
+
adapter: StorageAdapter;
|
|
2093
|
+
baseHref: string;
|
|
2094
|
+
urlToKey?: (absoluteUrl: string) => string | null;
|
|
2095
|
+
concurrency?: number;
|
|
2096
|
+
}
|
|
2097
|
+
declare function createStaticSource(kind: StacRoutableKind, deps: StacStaticSourceDeps): StacSource;
|
|
2098
|
+
|
|
2099
|
+
/**
|
|
2100
|
+
* STAC Storage Extension parser.
|
|
2101
|
+
*
|
|
2102
|
+
* Detects the Storage Extension version on a STAC Item and extracts
|
|
2103
|
+
* connection-relevant hints (region, requester-pays, custom-S3 endpoint).
|
|
2104
|
+
*
|
|
2105
|
+
* Inspired by lazycogs's `_storage_ext.py`. Pure TypeScript, no fetch, no
|
|
2106
|
+
* Svelte dependency. Suitable for `@walkthru-earth/objex-utils` re-export.
|
|
2107
|
+
*
|
|
2108
|
+
* Supported schema URLs:
|
|
2109
|
+
* - https://stac-extensions.github.io/storage/v1.0.0/schema.json
|
|
2110
|
+
* - https://stac-extensions.github.io/storage/v2.0.0/schema.json
|
|
2111
|
+
*
|
|
2112
|
+
* v1 (item / asset properties)
|
|
2113
|
+
* storage:platform e.g. "AWS", "GCP", "AZURE"
|
|
2114
|
+
* storage:region
|
|
2115
|
+
* storage:requester_pays boolean
|
|
2116
|
+
* storage:tier (ignored — no obstore equivalent)
|
|
2117
|
+
*
|
|
2118
|
+
* v2 (item-level scheme map + asset-level refs)
|
|
2119
|
+
* properties.storage:schemes = {
|
|
2120
|
+
* <ref>: { type, platform, region?, requester_pays?, endpoint? }
|
|
2121
|
+
* }
|
|
2122
|
+
* asset.storage:refs = ["primary", ...] (first matching ref wins)
|
|
2123
|
+
*
|
|
2124
|
+
* Asset-level fields take precedence over item-level fields in v1.
|
|
2125
|
+
*/
|
|
2126
|
+
|
|
2127
|
+
/** Recognized Storage Extension schema versions. */
|
|
2128
|
+
type StorageExtensionVersion = '1.0.0' | '2.0.0';
|
|
2129
|
+
/**
|
|
2130
|
+
* Connection-relevant hints extracted from the Storage Extension. All fields
|
|
2131
|
+
* are nullable so callers can merge selectively into existing config without
|
|
2132
|
+
* clobbering user-set values.
|
|
2133
|
+
*/
|
|
2134
|
+
interface StorageHints {
|
|
2135
|
+
/** e.g. "AWS", "GCP", "AZURE", "MINIO". Uppercased. Null when absent. */
|
|
2136
|
+
platform: string | null;
|
|
2137
|
+
/** Region code, e.g. "us-west-2". Null when absent. */
|
|
2138
|
+
region: string | null;
|
|
2139
|
+
/** True when requester-pays must be set. False when absent or false. */
|
|
2140
|
+
requesterPays: boolean;
|
|
2141
|
+
/** Concrete S3-compatible endpoint URL. Null unless v2 `custom-s3` with
|
|
2142
|
+
* a non-templated `platform` value. */
|
|
2143
|
+
endpoint: string | null;
|
|
2144
|
+
}
|
|
2145
|
+
/** Empty hints record. Returned when extension absent or unparseable. */
|
|
2146
|
+
declare function emptyStorageHints(): StorageHints;
|
|
2147
|
+
/**
|
|
2148
|
+
* Scan `item.stac_extensions[]` for the Storage Extension schema URL and
|
|
2149
|
+
* return its parsed version. Returns null when the extension is absent or
|
|
2150
|
+
* the version is not one we recognize.
|
|
2151
|
+
*/
|
|
2152
|
+
declare function detectStorageExtensionVersion(item: StacItem): StorageExtensionVersion | null;
|
|
2153
|
+
/**
|
|
2154
|
+
* Extract connection hints from a STAC Item. Dispatches on the detected
|
|
2155
|
+
* Storage Extension version. Returns `emptyStorageHints()` when the
|
|
2156
|
+
* extension is absent or fails to parse.
|
|
2157
|
+
*
|
|
2158
|
+
* `assetKey` is optional. When given:
|
|
2159
|
+
* - v1: that asset's overrides take precedence over item-level fields.
|
|
2160
|
+
* - v2: that asset's `storage:refs[0]` resolves the item scheme.
|
|
2161
|
+
* When omitted in v2, the first scheme found in any asset's refs wins.
|
|
2162
|
+
*/
|
|
2163
|
+
declare function extractStorageHints(item: StacItem, assetKey?: string): StorageHints;
|
|
2164
|
+
/**
|
|
2165
|
+
* TODO(host-detection): a future PR should call this from the connection
|
|
2166
|
+
* auto-fill path so a STAC Item carrying Storage Extension metadata can
|
|
2167
|
+
* pre-populate `region` and (for `custom-s3`) `endpoint` on a new
|
|
2168
|
+
* connection.
|
|
2169
|
+
*
|
|
2170
|
+
* Today this lives next to the parser so consumers can opt-in without
|
|
2171
|
+
* touching `host-detection.ts` or the connection store. The function is
|
|
2172
|
+
* intentionally generic — it takes any object with `region` / `endpoint`
|
|
2173
|
+
* keys and returns a shallow copy with hint fields filled only when the
|
|
2174
|
+
* existing value is empty.
|
|
2175
|
+
*/
|
|
2176
|
+
declare function applyStorageHintsToConnection<T extends {
|
|
2177
|
+
region?: string;
|
|
2178
|
+
endpoint?: string;
|
|
2179
|
+
}>(conn: T, hints: StorageHints): T;
|
|
2180
|
+
|
|
2181
|
+
/**
|
|
2182
|
+
* Open-time storage probe. Issues a single ranged GET against a presigned
|
|
2183
|
+
* asset URL to surface auth, CORS, and bucket-misconfiguration errors at
|
|
2184
|
+
* viewer load time, before any tile read kicks off.
|
|
2185
|
+
*
|
|
2186
|
+
* Inspired by lazycogs `_smoketest_store` (developmentseed/lazycogs). The
|
|
2187
|
+
* Python library calls `store.head()` on a representative asset during
|
|
2188
|
+
* `open()` so credential or region misconfiguration fails in <1s instead of
|
|
2189
|
+
* mid-mosaic when the first COG range fetch errors out.
|
|
2190
|
+
*
|
|
2191
|
+
* We use ranged GET instead of HEAD because:
|
|
2192
|
+
* - Many private buckets allow GET but block HEAD via CORS.
|
|
2193
|
+
* - Range `bytes=0-0` is one byte, cheaper than a full body fetch.
|
|
2194
|
+
* - Successful 206 / 200 confirms BOTH auth and CORS headers are correct,
|
|
2195
|
+
* which a HEAD response sometimes lies about under bucket policies that
|
|
2196
|
+
* return mismatched `Access-Control-Expose-Headers`.
|
|
2197
|
+
*
|
|
2198
|
+
* Pure TS, no Svelte / framework deps. Safe to publish via objex-utils.
|
|
2199
|
+
*/
|
|
2200
|
+
type SmokeTestResult = {
|
|
2201
|
+
ok: true;
|
|
2202
|
+
} | {
|
|
2203
|
+
ok: false;
|
|
2204
|
+
status: number | null;
|
|
2205
|
+
reason: string;
|
|
2206
|
+
};
|
|
2207
|
+
/**
|
|
2208
|
+
* Probe a presigned URL with a one-byte Range GET. Resolves to `{ ok: true }`
|
|
2209
|
+
* on 200 / 206, otherwise returns a structured failure with the HTTP status
|
|
2210
|
+
* (or null when the request never reached a server, e.g. CORS preflight
|
|
2211
|
+
* failure or DNS error). AbortError is re-thrown so callers can distinguish
|
|
2212
|
+
* intentional cancellation from real failures.
|
|
2213
|
+
*/
|
|
2214
|
+
declare function smokeTestHref(href: string, signal?: AbortSignal): Promise<SmokeTestResult>;
|
|
835
2215
|
|
|
836
2216
|
/**
|
|
837
2217
|
* Lightweight WKB (Well-Known Binary) parser for extracting coordinates.
|
|
@@ -877,4 +2257,4 @@ declare function findGeoColumnFromRows(rows: Record<string, unknown>[], schema:
|
|
|
877
2257
|
type: string;
|
|
878
2258
|
}[]): string | null;
|
|
879
2259
|
|
|
880
|
-
export { type AccessMode, type AccessModeInput, COPY_FEEDBACK_MS, type CogInfo, type Connection, type ConnectionConfig, DEFAULT_TARGET_CRS, DUCKDB_INIT_TIMEOUT_MS, type Defaults, type DuckDbReadFn, type FileCategory, type FileEntry, type FileTypeInfo, type GeoArrowGeomType, type GeoArrowResult, type GeoBounds, type GeoColumnMeta, type GeoParquetMeta, type GeoType, type HexRow, LAYER_HUE_MULTIPLIER, type ListPage, MAX_QUERY_HISTORY_ENTRIES, type MapQueryHandle, type MapQueryResult, PROVIDERS, PROVIDER_IDS, type ParquetFileMetadata, type ParsedGeometry, type ParsedMarkdownDocument, type ParsedStorageUrl, type ProviderDef, type ProviderId, type ProviderRegion, QueryCancelledError, type QueryEngine, type QueryHandle, type QueryResult, type QuerySource, SF_LABELS, SQL_PREVIEW_LENGTH, STAC_GEOPARQUET_REQUIRED_COLUMNS, STORAGE_KEYS, type SchemaField, type SortConfig, type SortDirection, type SortField, type SqlBlock, type StacBboxStruct, type StacGeoparquetRow, type StacGeoparquetSchemaColumn, type StacRowToItemOptions, type StorageAdapter, type StorageProvider, type Tab, type Theme, type TypeCategory, UrlAdapter, VIEWER_DIR_EXTENSIONS, type ViewerKind, WGS84_CODES, type WriteResult, buildDataTypeLabel, buildDuckDbSource, buildEndpointFromTemplate, buildGeoArrowTables, buildProviderBaseUrl, clampBounds, classifyType, describeParseResult, escapeCsvField, exportToCsv, exportToJson, extractBounds, extractEpsgFromGeoMeta, extractGeometryTypes, findGeoColumn, findGeoColumnFromRows, flattenStacBbox, formatDate, formatFileSize, formatValue, generateHexDump, getAccessMode, getDuckDbReadFn, getFileExtension, getFileTypeInfo, getMimeType, getNativeScheme, getProvider, getViewerKind, handleLoadError, interpolateTemplates, isCloudNativeFormat, isGcsProvider, isPubliclyStreamable, isQueryable, isStacGeoparquetSchema, jsonReplacerBigInt, loadFromStorage, looksLikeUrl, markSqlBlocks, normalizeGeomType, parseMarkdownDocument, parseStorageUrl, parseWKB, persistToStorage, pickStacPrimaryAsset, readParquetMetadata, resolveCloudUrl, resolveProviderEndpoint, resolveStacAssetHref, safeClamp, safeDecodeURIComponent, serializeToCsv, serializeToJson, sortFileEntries, stacRowToItem, toBinary, toggleSortField, typeBadgeClass, typeColor, typeLabel };
|
|
2260
|
+
export { type AccessMode, type AccessModeInput, type AppConfig, type AppConfigDefaults, type AppConfigUi, type AttachPixelInspectorOptions, type BandMap, type BandSlot, type BasemapConfig, COPY_FEEDBACK_MS, type ChannelComposite, type ChannelRef, type CogAsset, type CogInfo, type Connection, type ConnectionConfig, type ConnectionIdentityInput, type ConnectionSeed, DATETIME_HISTOGRAM_BINS, DATETIME_HISTOGRAM_BINS_MAX, DEFAULT_APP_CONFIG, DEFAULT_TARGET_CRS, DUCKDB_INIT_TIMEOUT_MS, type DatetimeFacet, type DatetimeGranularity, type Defaults, type DetectedHost, type DuckDbReadFn, type EnumFacet, type EnumFacetField, type Facet, type FacetSet, type FacetSort, type FacetState, type FileCategory, type FileEntry, type FileTypeInfo, type GeoArrowGeomType, type GeoArrowResult, type GeoBounds, type GeoColumnMeta, type GeoParquetMeta, type GeoType, type GeometryTypeInfo, type HexRow, type HydrateOptions, type HydrateResult, LAYER_HUE_MULTIPLIER, type ListPage, LruCache, type LruCacheOptions, MAX_QUERY_HISTORY_ENTRIES, type MapLike, type MapQueryHandle, type MapQueryResult, MarkdownSqlContext, type MosaicSourceMeta, type NotebookConfig, type NotebookMeta, type NumericFacet, type NumericFacetField, PRESETS, PROVIDERS, PROVIDER_IDS, type ParquetFileMetadata, type ParsedGeometry, type ParsedMarkdownDocument, type ParsedStorageUrl, type PixelInspectCallbacks, type PixelInspectClickEvent, type PixelInspectClickHandler, type PixelInspectProbe, type PixelInspectProbeRequest, type PresetDef, type ProviderDef, type ProviderId, type ProviderRegion, QueryCancelledError, type QueryEngine, type QueryHandle, type QueryResult, type QuerySource, type RasterBandAsset, SF_LABELS, SQL_PREVIEW_LENGTH, STAC_API_PATH_RE, STAC_COG_ASSET_KEYS, STAC_GEOPARQUET_REQUIRED_COLUMNS, STORAGE_KEYS, type SchemaField, type SmokeTestResult, type SortConfig, type SortDirection, type SortField, type SqlBlock, type StacApiCapabilities, type StacApiSourceDeps, type StacAsset, type StacBboxStruct, type StacCatalog, type StacCollection, type StacFeatureCollection, type StacGeoparquetRow, type StacGeoparquetSchemaColumn, type StacItem, type StacItemView, type StacItemsQuery, type StacLink, type StacNativeQuery, type StacRoutableKind, type StacRowToItemOptions, type StacSource, type StacSourceBatch, type StacSourceCapabilities, type StacSourceKind, type StacSourceRequest, type StacStaticSourceDeps, type StorageAdapter, type StorageExtensionVersion, type StorageHints, type StorageProvider, type Tab, type Theme, type ToNativeQueryOptions, type TypeCategory, UrlAdapter, type UrlClassification, VIEWER_DIR_EXTENSIONS, type ViewerKind, WGS84_CODES, type WriteResult, absolutizeHref, allChannelsBand0, applyFacets, applyPreset, applyStacItemStorageHints, applyStorageHintsToConnection, attachPixelInspector, availablePresets, buildDataTypeLabel, buildDuckDbSource, buildEndpointFromTemplate, buildFacets, buildGeoArrowTables, buildMosaicSourceMeta, buildProviderBaseUrl, buildTransformExpr, clampBounds, classifyStac, classifyType, classifyUrl, coerceBool, coercePositiveInt, coerceString, coerceTheme, compositeFromUrl, compositeToUrl, connectionIdentityKey, copyToClipboard, createApiSource, createStaticSource, describeParseResult, detectHostBucket, detectMosaicCapable, detectMultiCogCapable, detectStorageExtensionVersion, emptyFacetState, emptyPushdown, emptyStorageHints, escapeCsvField, exportToCsv, exportToJson, extractBounds, extractCogAssets, extractEpsgFromGeoMeta, extractGeometryTypes, extractItemView, extractMosaicAssets, extractRasterBandAssets, extractSentinelBandAssets, extractStorageHints, findGeoColumn, findGeoColumnFromRows, flattenStacBbox, formatDate, formatFileSize, formatValue, generateHexDump, getAccessMode, getDuckDbReadFn, getFileExtension, getFileTypeInfo, getMimeType, getNativeScheme, getProvider, getViewerKind, handleLoadError, hasActiveFilters, hasCompositableBands, hasRgbBands, hasStacItemsEndpoint, hydrateStacItems, interpolateTemplates, isAbortError, isCloudNativeFormat, isGcsProvider, isKnownBucketHost, isPubliclyStreamable, isQueryable, isSameConnectionIdentity, isSingleAssetComposite, isStacCatalog, isStacCollection, isStacFeatureCollection, isStacGeoparquetSchema, isStacItem, isWgs84Crs, jsonReplacerBigInt, loadFromStorage, looksLikeUrl, markSqlBlocks, mergeAppConfig, normalizeEndpoint, normalizeGeomType, normalizeProvider, parseGeometryTypeCrs, parseMarkdownDocument, parseStorageUrl, parseVisibilityParam, parseWKB, persistToStorage, pickCogAssetHref, pickGranularity, pickNaturalColorComposite, pickStacPrimaryAsset, presetMatchesComposite, readParquetMetadata, renderNotebook, residualState, resolveBandSlotAssetKey, resolveBasemap, resolveCloudUrl, resolvePresetComposite, resolveProviderEndpoint, resolveSetting, resolveStacAssetHref, safeClamp, safeDecodeURIComponent, serializeToCsv, serializeToJson, smokeTestHref, sniffApiCapabilities, sortFileEntries, sortViews, spatialCellKey, stacItemBbox, stacRowToItem, syntheticSelfAsset, toBinary, toCql2Filter, toNativeQuery, toggleSortField, typeBadgeClass, typeColor, typeLabel, wireCodeCopyButtons, wrapWkbWithCrs };
|