@walkthru-earth/objex-utils 1.2.1 → 1.3.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/README.md CHANGED
@@ -29,6 +29,13 @@ import {
29
29
  extractEpsgFromGeoMeta,
30
30
  extractBounds,
31
31
 
32
+ // stac-geoparquet (detection + row → STAC Item)
33
+ isStacGeoparquetSchema,
34
+ stacRowToItem,
35
+ flattenStacBbox,
36
+ pickStacPrimaryAsset,
37
+ resolveStacAssetHref,
38
+
32
39
  // Storage URLs
33
40
  parseStorageUrl,
34
41
  resolveCloudUrl,
@@ -67,6 +74,7 @@ Full per-module developer reference lives in [`docs/`](./docs/README.md). Each p
67
74
  | [`docs/geometry.md`](./docs/geometry.md) | WKB parser, GeoArrow builder, geometry-column detection |
68
75
  | [`docs/cog.md`](./docs/cog.md) | Cloud-Optimized GeoTIFF pipeline helpers, band configs, color ramps |
69
76
  | [`docs/parquet-metadata.md`](./docs/parquet-metadata.md) | `readParquetMetadata` + CRS / bounds / geometry-type extractors |
77
+ | [`docs/stac-geoparquet.md`](./docs/stac-geoparquet.md) | stac-geoparquet detection + row → STAC Item transforms |
70
78
  | [`docs/storage.md`](./docs/storage.md) | URL parsing, provider registry, `StorageAdapter`, `UrlAdapter` |
71
79
  | [`docs/query-engine.md`](./docs/query-engine.md) | `QueryEngine` interface + handle / result types |
72
80
  | [`docs/file-types.md`](./docs/file-types.md) | File-type registry: `getFileTypeInfo`, `getViewerKind`, `getDuckDbReadFn`, … |
package/dist/index.cjs CHANGED
@@ -2477,6 +2477,15 @@ async function readParquetMetadata(url) {
2477
2477
  name: col.name,
2478
2478
  type: mapParquetType(col)
2479
2479
  }));
2480
+ const topLevelColumns = [];
2481
+ const root = metadata.schema[0];
2482
+ const rootChildren = root?.num_children ?? 0;
2483
+ let cursor = 1;
2484
+ for (let i = 0; i < rootChildren && cursor < metadata.schema.length; i++) {
2485
+ const el = metadata.schema[cursor];
2486
+ if (el?.name) topLevelColumns.push(el.name);
2487
+ cursor += countSubtree(metadata.schema, cursor);
2488
+ }
2480
2489
  let geo = null;
2481
2490
  let legacyGeoParquet = false;
2482
2491
  const geoKv = metadata.key_value_metadata?.find((kv) => kv.key === "geo");
@@ -2518,7 +2527,26 @@ async function readParquetMetadata(url) {
2518
2527
  compression = [...codecs].join(", ");
2519
2528
  }
2520
2529
  }
2521
- return { rowCount, schema, geo, legacyGeoParquet, createdBy, numRowGroups, compression };
2530
+ return {
2531
+ rowCount,
2532
+ schema,
2533
+ topLevelColumns,
2534
+ geo,
2535
+ legacyGeoParquet,
2536
+ createdBy,
2537
+ numRowGroups,
2538
+ compression
2539
+ };
2540
+ }
2541
+ function countSubtree(schema, start) {
2542
+ const el = schema[start];
2543
+ if (!el) return 0;
2544
+ const n = el.num_children ?? 0;
2545
+ let cursor = start + 1;
2546
+ for (let i = 0; i < n; i++) {
2547
+ cursor += countSubtree(schema, cursor);
2548
+ }
2549
+ return cursor - start;
2522
2550
  }
2523
2551
  function extractEpsgFromGeoMeta(geo) {
2524
2552
  const primaryCol = geo.columns[geo.primaryColumn];
@@ -2557,6 +2585,144 @@ function extractBounds(geo) {
2557
2585
  return [primaryCol.bbox[0], primaryCol.bbox[1], primaryCol.bbox[2], primaryCol.bbox[3]];
2558
2586
  }
2559
2587
 
2588
+ // ../../src/lib/utils/stac-geoparquet.ts
2589
+ var STAC_GEOPARQUET_REQUIRED_COLUMNS = [
2590
+ "stac_version",
2591
+ "type",
2592
+ "geometry",
2593
+ "assets"
2594
+ ];
2595
+ function isStacGeoparquetSchema(schema) {
2596
+ if (!Array.isArray(schema) || schema.length === 0) return false;
2597
+ const names = new Set(schema.map((c) => c.name));
2598
+ return STAC_GEOPARQUET_REQUIRED_COLUMNS.every((c) => names.has(c));
2599
+ }
2600
+ function flattenStacBbox(bbox) {
2601
+ if (!bbox) return null;
2602
+ if (Array.isArray(bbox)) {
2603
+ if (bbox.length < 4) return null;
2604
+ const [minX, minY, maxX, maxY] = bbox;
2605
+ if (![minX, minY, maxX, maxY].every((v) => Number.isFinite(v))) return null;
2606
+ return [minX, minY, maxX, maxY];
2607
+ }
2608
+ if (typeof bbox === "object") {
2609
+ const { xmin, ymin, xmax, ymax } = bbox;
2610
+ if (![xmin, ymin, xmax, ymax].every((v) => Number.isFinite(v))) return null;
2611
+ return [xmin, ymin, xmax, ymax];
2612
+ }
2613
+ return null;
2614
+ }
2615
+ function resolveStacAssetHref(href, baseUrl) {
2616
+ if (!href) return href;
2617
+ if (/^[a-z][a-z0-9+\-.]*:\/\//i.test(href)) return href;
2618
+ try {
2619
+ return new URL(href, baseUrl).toString();
2620
+ } catch {
2621
+ return href;
2622
+ }
2623
+ }
2624
+ function pickStacPrimaryAsset(assets, preferredKeys) {
2625
+ if (!assets || typeof assets !== "object") return null;
2626
+ const entries = Object.entries(assets).filter(
2627
+ ([, a]) => a && typeof a === "object" && typeof a.href === "string"
2628
+ );
2629
+ if (entries.length === 0) return null;
2630
+ if (preferredKeys) {
2631
+ for (const key of preferredKeys) {
2632
+ const match = entries.find(([k]) => k === key);
2633
+ if (match) return { key: match[0], asset: match[1] };
2634
+ }
2635
+ }
2636
+ const data = entries.find(([k]) => k === "data");
2637
+ if (data) return { key: data[0], asset: data[1] };
2638
+ const byRole = entries.find(([, a]) => Array.isArray(a.roles) && a.roles.includes("data"));
2639
+ if (byRole) return { key: byRole[0], asset: byRole[1] };
2640
+ return { key: entries[0][0], asset: entries[0][1] };
2641
+ }
2642
+ function normalizeAssetsField(value, baseUrl) {
2643
+ if (!value || typeof value !== "object") return void 0;
2644
+ const out = {};
2645
+ for (const [key, raw] of Object.entries(value)) {
2646
+ if (!raw || typeof raw !== "object") continue;
2647
+ const asset = raw;
2648
+ if (typeof asset.href !== "string" || !asset.href) continue;
2649
+ out[key] = {
2650
+ ...asset,
2651
+ href: resolveStacAssetHref(asset.href, baseUrl)
2652
+ };
2653
+ }
2654
+ return Object.keys(out).length > 0 ? out : void 0;
2655
+ }
2656
+ function normalizeLinksField(value, baseUrl) {
2657
+ if (!Array.isArray(value)) return void 0;
2658
+ const links = [];
2659
+ for (const raw of value) {
2660
+ if (!raw || typeof raw !== "object") continue;
2661
+ const link = raw;
2662
+ if (typeof link.href !== "string" || typeof link.rel !== "string") continue;
2663
+ links.push({ ...link, href: resolveStacAssetHref(link.href, baseUrl) });
2664
+ }
2665
+ return links.length > 0 ? links : void 0;
2666
+ }
2667
+ function stacRowToItem(row, baseUrl, opts = {}) {
2668
+ const { wkbParser, wkbColumn = "geom_wkb", geometryColumn = "geometry" } = opts;
2669
+ let geometry = row[geometryColumn];
2670
+ if (!geometry) {
2671
+ const wkb = row[wkbColumn];
2672
+ if (wkb && wkbParser) {
2673
+ const bytes = wkb instanceof Uint8Array ? wkb : toUint8Array(wkb);
2674
+ if (bytes) {
2675
+ try {
2676
+ geometry = wkbParser(bytes) ?? void 0;
2677
+ } catch {
2678
+ geometry = void 0;
2679
+ }
2680
+ }
2681
+ }
2682
+ }
2683
+ const bbox = flattenStacBbox(row.bbox) ?? void 0;
2684
+ const assets = normalizeAssetsField(row.assets, baseUrl);
2685
+ const links = normalizeLinksField(row.links, baseUrl);
2686
+ const properties = {};
2687
+ for (const [key, value] of Object.entries(row)) {
2688
+ if (value === null || value === void 0) continue;
2689
+ if (key.startsWith("proj:") || key.startsWith("raster:") || key.startsWith("eo:")) {
2690
+ properties[key] = value;
2691
+ }
2692
+ if (key === "datetime") {
2693
+ properties.datetime = value instanceof Date ? value.toISOString() : String(value);
2694
+ }
2695
+ if (key === "bands") {
2696
+ properties.bands = value;
2697
+ }
2698
+ }
2699
+ const item = {
2700
+ type: "Feature",
2701
+ stac_version: typeof row.stac_version === "string" ? row.stac_version : "1.0.0",
2702
+ id: typeof row.id === "string" ? row.id : String(row.id ?? ""),
2703
+ properties
2704
+ };
2705
+ if (typeof row.collection === "string") item.collection = row.collection;
2706
+ if (Array.isArray(row.stac_extensions)) {
2707
+ item.stac_extensions = row.stac_extensions;
2708
+ }
2709
+ if (bbox) item.bbox = bbox;
2710
+ if (geometry) item.geometry = geometry;
2711
+ if (assets) item.assets = assets;
2712
+ if (links) item.links = links;
2713
+ return item;
2714
+ }
2715
+ function toUint8Array(value) {
2716
+ if (value instanceof Uint8Array) return value;
2717
+ if (value instanceof ArrayBuffer) return new Uint8Array(value);
2718
+ if (ArrayBuffer.isView(value)) {
2719
+ const view = value;
2720
+ return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
2721
+ }
2722
+ if (Array.isArray(value)) return new Uint8Array(value);
2723
+ return null;
2724
+ }
2725
+
2560
2726
  // ../../src/lib/utils/storage-url.ts
2561
2727
  function buildSchemeMap() {
2562
2728
  const map = {};
@@ -2570,6 +2736,32 @@ function buildSchemeMap() {
2570
2736
  return map;
2571
2737
  }
2572
2738
  var SCHEME_MAP = buildSchemeMap();
2739
+ var AWS_VHOST_RE = /^(.+)\.s3[.-]([a-z0-9-]+)\.amazonaws\.com$/;
2740
+ var AWS_PATH_RE = /^s3[.-]([a-z0-9-]+)\.amazonaws\.com$/;
2741
+ var AWS_GLOBAL_HOST = "s3.amazonaws.com";
2742
+ var R2_RE = /^([a-z0-9]+)\.r2\.cloudflarestorage\.com$/;
2743
+ var GCS_GLOBAL_HOST = "storage.googleapis.com";
2744
+ var GCS_VHOST_RE = /^(.+)\.storage\.googleapis\.com$/;
2745
+ var DO_VHOST_RE = /^(.+)\.([a-z0-9-]+)\.digitaloceanspaces\.com$/;
2746
+ var DO_PATH_RE = /^([a-z0-9-]+)\.digitaloceanspaces\.com$/;
2747
+ var WASABI_RE = /^s3\.([a-z0-9-]+)\.wasabisys\.com$/;
2748
+ var B2_S3_RE = /^(.+)\.s3\.([a-z0-9-]+)\.backblazeb2\.com$/;
2749
+ var B2_NATIVE_RE = /^f[a-z0-9]+\.backblazeb2\.com$/;
2750
+ var OSS_RE = /^(.+)\.(oss-[a-z0-9-]+)\.aliyuncs\.com$/;
2751
+ var COS_RE = /^(.+)\.cos\.([a-z0-9-]+)\.myqcloud\.com$/;
2752
+ var YANDEX_HOST = "storage.yandexcloud.net";
2753
+ var CONTABO_RE = /^([a-z0-9]+)\.contabostorage\.com$/;
2754
+ var HETZNER_RE = /^([a-z0-9]+)\.your-objectstorage\.com$/;
2755
+ var LINODE_VHOST_RE = /^(.+)\.([a-z0-9-]+)\.linodeobjects\.com$/;
2756
+ var LINODE_PATH_RE = /^([a-z0-9-]+)\.linodeobjects\.com$/;
2757
+ var OVH_RE = /^s3\.([a-z0-9-]+)\.io\.cloud\.ovh\.(?:net|us)$/;
2758
+ var AZURE_BLOB_RE = /^([a-z0-9]+)\.blob\.core\.windows\.net$/;
2759
+ var STORJ_GATEWAY_RE = /^gateway\.(?:([a-z0-9]+)\.)?storjshare\.io$/;
2760
+ var STORJ_LINK_RE = /^link\.(?:([a-z0-9]+)\.)?storjshare\.io$/;
2761
+ function isMinioLikeHost(host) {
2762
+ return host.includes("minio") || host === "localhost" || host === "127.0.0.1" || host.startsWith("192.168.") || host.startsWith("10.");
2763
+ }
2764
+ var STAC_API_PATH_RE = /\/(collections|items|catalogs|search)(\/|\?|$)/i;
2573
2765
  function defaultResult(defaults) {
2574
2766
  return {
2575
2767
  bucket: "",
@@ -2610,7 +2802,7 @@ function parseStorageUrl(input, defaults = {}) {
2610
2802
  const url = new URL(trimmed);
2611
2803
  const host = url.hostname;
2612
2804
  const pathParts = url.pathname.replace(/^\//, "").split("/").filter(Boolean);
2613
- const awsVhost = host.match(/^(.+)\.s3[.-]([a-z0-9-]+)\.amazonaws\.com$/);
2805
+ const awsVhost = host.match(AWS_VHOST_RE);
2614
2806
  if (awsVhost) {
2615
2807
  return {
2616
2808
  bucket: awsVhost[1],
@@ -2620,7 +2812,7 @@ function parseStorageUrl(input, defaults = {}) {
2620
2812
  prefix: pathParts.join("/")
2621
2813
  };
2622
2814
  }
2623
- const awsPath = host.match(/^s3[.-]([a-z0-9-]+)\.amazonaws\.com$/);
2815
+ const awsPath = host.match(AWS_PATH_RE);
2624
2816
  if (awsPath && pathParts.length > 0) {
2625
2817
  return {
2626
2818
  bucket: pathParts[0],
@@ -2630,7 +2822,7 @@ function parseStorageUrl(input, defaults = {}) {
2630
2822
  prefix: pathParts.slice(1).join("/")
2631
2823
  };
2632
2824
  }
2633
- if (host === "s3.amazonaws.com" && pathParts.length > 0) {
2825
+ if (host === AWS_GLOBAL_HOST && pathParts.length > 0) {
2634
2826
  return {
2635
2827
  bucket: pathParts[0],
2636
2828
  region: defaults.region || "us-east-1",
@@ -2639,7 +2831,7 @@ function parseStorageUrl(input, defaults = {}) {
2639
2831
  prefix: pathParts.slice(1).join("/")
2640
2832
  };
2641
2833
  }
2642
- const r2Match = host.match(/^([a-z0-9]+)\.r2\.cloudflarestorage\.com$/);
2834
+ const r2Match = host.match(R2_RE);
2643
2835
  if (r2Match && pathParts.length > 0) {
2644
2836
  return {
2645
2837
  bucket: pathParts[0],
@@ -2649,7 +2841,7 @@ function parseStorageUrl(input, defaults = {}) {
2649
2841
  prefix: pathParts.slice(1).join("/")
2650
2842
  };
2651
2843
  }
2652
- if (host === "storage.googleapis.com" && pathParts.length > 0) {
2844
+ if (host === GCS_GLOBAL_HOST && pathParts.length > 0) {
2653
2845
  return {
2654
2846
  bucket: pathParts[0],
2655
2847
  region: defaults.region || "us",
@@ -2658,7 +2850,7 @@ function parseStorageUrl(input, defaults = {}) {
2658
2850
  prefix: pathParts.slice(1).join("/")
2659
2851
  };
2660
2852
  }
2661
- const gcsVhost = host.match(/^(.+)\.storage\.googleapis\.com$/);
2853
+ const gcsVhost = host.match(GCS_VHOST_RE);
2662
2854
  if (gcsVhost) {
2663
2855
  return {
2664
2856
  bucket: gcsVhost[1],
@@ -2668,7 +2860,7 @@ function parseStorageUrl(input, defaults = {}) {
2668
2860
  prefix: pathParts.join("/")
2669
2861
  };
2670
2862
  }
2671
- const doVhost = host.match(/^(.+)\.([a-z0-9-]+)\.digitaloceanspaces\.com$/);
2863
+ const doVhost = host.match(DO_VHOST_RE);
2672
2864
  if (doVhost) {
2673
2865
  return {
2674
2866
  bucket: doVhost[1],
@@ -2678,7 +2870,7 @@ function parseStorageUrl(input, defaults = {}) {
2678
2870
  prefix: pathParts.join("/")
2679
2871
  };
2680
2872
  }
2681
- const doPath = host.match(/^([a-z0-9-]+)\.digitaloceanspaces\.com$/);
2873
+ const doPath = host.match(DO_PATH_RE);
2682
2874
  if (doPath && pathParts.length > 0) {
2683
2875
  return {
2684
2876
  bucket: pathParts[0],
@@ -2688,7 +2880,7 @@ function parseStorageUrl(input, defaults = {}) {
2688
2880
  prefix: pathParts.slice(1).join("/")
2689
2881
  };
2690
2882
  }
2691
- const wasabiMatch = host.match(/^s3\.([a-z0-9-]+)\.wasabisys\.com$/);
2883
+ const wasabiMatch = host.match(WASABI_RE);
2692
2884
  if (wasabiMatch && pathParts.length > 0) {
2693
2885
  return {
2694
2886
  bucket: pathParts[0],
@@ -2698,7 +2890,7 @@ function parseStorageUrl(input, defaults = {}) {
2698
2890
  prefix: pathParts.slice(1).join("/")
2699
2891
  };
2700
2892
  }
2701
- const b2S3 = host.match(/^(.+)\.s3\.([a-z0-9-]+)\.backblazeb2\.com$/);
2893
+ const b2S3 = host.match(B2_S3_RE);
2702
2894
  if (b2S3) {
2703
2895
  return {
2704
2896
  bucket: b2S3[1],
@@ -2708,7 +2900,7 @@ function parseStorageUrl(input, defaults = {}) {
2708
2900
  prefix: pathParts.join("/")
2709
2901
  };
2710
2902
  }
2711
- const b2Native = host.match(/^f[a-z0-9]+\.backblazeb2\.com$/);
2903
+ const b2Native = host.match(B2_NATIVE_RE);
2712
2904
  if (b2Native && pathParts[0] === "file" && pathParts.length > 1) {
2713
2905
  return {
2714
2906
  bucket: pathParts[1],
@@ -2718,7 +2910,7 @@ function parseStorageUrl(input, defaults = {}) {
2718
2910
  prefix: pathParts.slice(2).join("/")
2719
2911
  };
2720
2912
  }
2721
- const ossMatch = host.match(/^(.+)\.(oss-[a-z0-9-]+)\.aliyuncs\.com$/);
2913
+ const ossMatch = host.match(OSS_RE);
2722
2914
  if (ossMatch) {
2723
2915
  return {
2724
2916
  bucket: ossMatch[1],
@@ -2728,7 +2920,7 @@ function parseStorageUrl(input, defaults = {}) {
2728
2920
  prefix: pathParts.join("/")
2729
2921
  };
2730
2922
  }
2731
- const cosMatch = host.match(/^(.+)\.cos\.([a-z0-9-]+)\.myqcloud\.com$/);
2923
+ const cosMatch = host.match(COS_RE);
2732
2924
  if (cosMatch) {
2733
2925
  return {
2734
2926
  bucket: cosMatch[1],
@@ -2738,7 +2930,7 @@ function parseStorageUrl(input, defaults = {}) {
2738
2930
  prefix: pathParts.join("/")
2739
2931
  };
2740
2932
  }
2741
- if (host === "storage.yandexcloud.net" && pathParts.length > 0) {
2933
+ if (host === YANDEX_HOST && pathParts.length > 0) {
2742
2934
  return {
2743
2935
  bucket: pathParts[0],
2744
2936
  region: defaults.region || "ru-central1",
@@ -2747,7 +2939,7 @@ function parseStorageUrl(input, defaults = {}) {
2747
2939
  prefix: pathParts.slice(1).join("/")
2748
2940
  };
2749
2941
  }
2750
- const contaboMatch = host.match(/^([a-z0-9]+)\.contabostorage\.com$/);
2942
+ const contaboMatch = host.match(CONTABO_RE);
2751
2943
  if (contaboMatch && pathParts.length > 0) {
2752
2944
  return {
2753
2945
  bucket: pathParts[0],
@@ -2757,7 +2949,7 @@ function parseStorageUrl(input, defaults = {}) {
2757
2949
  prefix: pathParts.slice(1).join("/")
2758
2950
  };
2759
2951
  }
2760
- const hetznerMatch = host.match(/^([a-z0-9]+)\.your-objectstorage\.com$/);
2952
+ const hetznerMatch = host.match(HETZNER_RE);
2761
2953
  if (hetznerMatch && pathParts.length > 0) {
2762
2954
  return {
2763
2955
  bucket: pathParts[0],
@@ -2767,7 +2959,7 @@ function parseStorageUrl(input, defaults = {}) {
2767
2959
  prefix: pathParts.slice(1).join("/")
2768
2960
  };
2769
2961
  }
2770
- const linodeVhost = host.match(/^(.+)\.([a-z0-9-]+)\.linodeobjects\.com$/);
2962
+ const linodeVhost = host.match(LINODE_VHOST_RE);
2771
2963
  if (linodeVhost) {
2772
2964
  return {
2773
2965
  bucket: linodeVhost[1],
@@ -2777,7 +2969,7 @@ function parseStorageUrl(input, defaults = {}) {
2777
2969
  prefix: pathParts.join("/")
2778
2970
  };
2779
2971
  }
2780
- const linodePath = host.match(/^([a-z0-9-]+)\.linodeobjects\.com$/);
2972
+ const linodePath = host.match(LINODE_PATH_RE);
2781
2973
  if (linodePath && pathParts.length > 0) {
2782
2974
  return {
2783
2975
  bucket: pathParts[0],
@@ -2787,7 +2979,7 @@ function parseStorageUrl(input, defaults = {}) {
2787
2979
  prefix: pathParts.slice(1).join("/")
2788
2980
  };
2789
2981
  }
2790
- const ovhMatch = host.match(/^s3\.([a-z0-9-]+)\.io\.cloud\.ovh\.(?:net|us)$/);
2982
+ const ovhMatch = host.match(OVH_RE);
2791
2983
  if (ovhMatch && pathParts.length > 0) {
2792
2984
  return {
2793
2985
  bucket: pathParts[0],
@@ -2797,8 +2989,7 @@ function parseStorageUrl(input, defaults = {}) {
2797
2989
  prefix: pathParts.slice(1).join("/")
2798
2990
  };
2799
2991
  }
2800
- const isMinioLike = host.includes("minio") || host === "localhost" || host === "127.0.0.1" || host.startsWith("192.168.") || host.startsWith("10.");
2801
- if (isMinioLike && pathParts.length > 0) {
2992
+ if (isMinioLikeHost(host) && pathParts.length > 0) {
2802
2993
  return {
2803
2994
  bucket: pathParts[0],
2804
2995
  region: defaults.region || "us-east-1",
@@ -2807,7 +2998,7 @@ function parseStorageUrl(input, defaults = {}) {
2807
2998
  prefix: pathParts.slice(1).join("/")
2808
2999
  };
2809
3000
  }
2810
- const azureBlob = host.match(/^([a-z0-9]+)\.blob\.core\.windows\.net$/);
3001
+ const azureBlob = host.match(AZURE_BLOB_RE);
2811
3002
  if (azureBlob && pathParts.length > 0) {
2812
3003
  return {
2813
3004
  bucket: pathParts[0],
@@ -2817,7 +3008,7 @@ function parseStorageUrl(input, defaults = {}) {
2817
3008
  prefix: pathParts.slice(1).join("/")
2818
3009
  };
2819
3010
  }
2820
- const storjGateway = host.match(/^gateway\.(?:([a-z0-9]+)\.)?storjshare\.io$/);
3011
+ const storjGateway = host.match(STORJ_GATEWAY_RE);
2821
3012
  if (storjGateway && pathParts.length > 0) {
2822
3013
  return {
2823
3014
  bucket: pathParts[0],
@@ -2827,7 +3018,7 @@ function parseStorageUrl(input, defaults = {}) {
2827
3018
  prefix: pathParts.slice(1).join("/")
2828
3019
  };
2829
3020
  }
2830
- const storjLink = host.match(/^link\.(?:([a-z0-9]+)\.)?storjshare\.io$/);
3021
+ const storjLink = host.match(STORJ_LINK_RE);
2831
3022
  if (storjLink && pathParts.length >= 3 && (pathParts[0] === "raw" || pathParts[0] === "s")) {
2832
3023
  return {
2833
3024
  bucket: pathParts[2],
@@ -2837,6 +3028,12 @@ function parseStorageUrl(input, defaults = {}) {
2837
3028
  prefix: pathParts.slice(3).join("/")
2838
3029
  };
2839
3030
  }
3031
+ if (STAC_API_PATH_RE.test(url.pathname)) {
3032
+ return {
3033
+ ...defaultResult(defaults),
3034
+ endpoint: `${url.protocol}//${url.host}`
3035
+ };
3036
+ }
2840
3037
  if (pathParts.length > 0) {
2841
3038
  const endpoint = `${url.protocol}//${url.host}`;
2842
3039
  return {
@@ -3146,6 +3343,7 @@ exports.PROVIDER_IDS = PROVIDER_IDS;
3146
3343
  exports.QueryCancelledError = QueryCancelledError;
3147
3344
  exports.SF_LABELS = SF_LABELS;
3148
3345
  exports.SQL_PREVIEW_LENGTH = SQL_PREVIEW_LENGTH;
3346
+ exports.STAC_GEOPARQUET_REQUIRED_COLUMNS = STAC_GEOPARQUET_REQUIRED_COLUMNS;
3149
3347
  exports.STORAGE_KEYS = STORAGE_KEYS;
3150
3348
  exports.UrlAdapter = UrlAdapter;
3151
3349
  exports.VIEWER_DIR_EXTENSIONS = VIEWER_DIR_EXTENSIONS;
@@ -3166,6 +3364,7 @@ exports.extractEpsgFromGeoMeta = extractEpsgFromGeoMeta;
3166
3364
  exports.extractGeometryTypes = extractGeometryTypes;
3167
3365
  exports.findGeoColumn = findGeoColumn;
3168
3366
  exports.findGeoColumnFromRows = findGeoColumnFromRows;
3367
+ exports.flattenStacBbox = flattenStacBbox;
3169
3368
  exports.formatDate = formatDate;
3170
3369
  exports.formatFileSize = formatFileSize;
3171
3370
  exports.formatValue = formatValue;
@@ -3184,6 +3383,7 @@ exports.isCloudNativeFormat = isCloudNativeFormat;
3184
3383
  exports.isGcsProvider = isGcsProvider;
3185
3384
  exports.isPubliclyStreamable = isPubliclyStreamable;
3186
3385
  exports.isQueryable = isQueryable;
3386
+ exports.isStacGeoparquetSchema = isStacGeoparquetSchema;
3187
3387
  exports.jsonReplacerBigInt = jsonReplacerBigInt;
3188
3388
  exports.loadFromStorage = loadFromStorage;
3189
3389
  exports.looksLikeUrl = looksLikeUrl;
@@ -3193,14 +3393,17 @@ exports.parseMarkdownDocument = parseMarkdownDocument;
3193
3393
  exports.parseStorageUrl = parseStorageUrl;
3194
3394
  exports.parseWKB = parseWKB;
3195
3395
  exports.persistToStorage = persistToStorage;
3396
+ exports.pickStacPrimaryAsset = pickStacPrimaryAsset;
3196
3397
  exports.readParquetMetadata = readParquetMetadata;
3197
3398
  exports.resolveCloudUrl = resolveCloudUrl;
3198
3399
  exports.resolveProviderEndpoint = resolveProviderEndpoint;
3400
+ exports.resolveStacAssetHref = resolveStacAssetHref;
3199
3401
  exports.safeClamp = safeClamp;
3200
3402
  exports.safeDecodeURIComponent = safeDecodeURIComponent;
3201
3403
  exports.serializeToCsv = serializeToCsv;
3202
3404
  exports.serializeToJson = serializeToJson;
3203
3405
  exports.sortFileEntries = sortFileEntries;
3406
+ exports.stacRowToItem = stacRowToItem;
3204
3407
  exports.toBinary = toBinary;
3205
3408
  exports.toggleSortField = toggleSortField;
3206
3409
  exports.typeBadgeClass = typeBadgeClass;