@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/dist/index.cjs CHANGED
@@ -1,10 +1,5 @@
1
1
  'use strict';
2
2
 
3
- require('@developmentseed/epsg/all');
4
- require('@developmentseed/epsg/all.csv.gz?url');
5
- require('@developmentseed/geotiff');
6
- require('@developmentseed/proj');
7
- require('proj4');
8
3
  var apacheArrow = require('apache-arrow');
9
4
 
10
5
  // ../../src/lib/constants.ts
@@ -1155,15 +1150,15 @@ var PROVIDERS = {
1155
1150
  schemes: ["azure", "az", "abfs", "abfss", "wasbs", "adl"]
1156
1151
  },
1157
1152
  minio: {
1158
- label: "MinIO",
1159
- description: "Self-hosted MinIO or S3-compatible",
1153
+ label: "MinIO / RustFS / Custom",
1154
+ description: "MinIO, RustFS, or any custom S3-compatible endpoint",
1160
1155
  authMethod: "sigv4",
1161
1156
  needsRegion: false,
1162
1157
  needsEndpoint: true,
1163
1158
  defaultRegion: "us-east-1",
1164
1159
  endpointTemplate: null,
1165
1160
  regions: [],
1166
- endpointPlaceholder: "https://minio.example.com or http://localhost:9000",
1161
+ endpointPlaceholder: "https://s3.example.com or http://localhost:9000",
1167
1162
  schemes: []
1168
1163
  },
1169
1164
  storj: {
@@ -1431,7 +1426,460 @@ var UrlAdapter = class {
1431
1426
  }
1432
1427
  };
1433
1428
 
1434
- // ../../src/lib/utils/cloud-url.ts
1429
+ // src/app-config.ts
1430
+ var DEFAULT_APP_CONFIG = {
1431
+ defaults: { theme: "system", locale: "en", featureLimit: 1e3, mosaicItemLimit: 2e3 },
1432
+ ui: { showConnectionRail: true, showFileTree: true, showSettings: true },
1433
+ basemaps: [],
1434
+ defaultBasemap: {},
1435
+ connections: []
1436
+ };
1437
+ function asObject(v) {
1438
+ return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
1439
+ }
1440
+ function coerceTheme(v) {
1441
+ return v === "light" || v === "dark" || v === "system" ? v : void 0;
1442
+ }
1443
+ function coerceString(v) {
1444
+ return typeof v === "string" && v.trim().length > 0 ? v.trim() : void 0;
1445
+ }
1446
+ function coercePositiveInt(v) {
1447
+ return typeof v === "number" && Number.isFinite(v) && v >= 1 ? Math.floor(v) : void 0;
1448
+ }
1449
+ function coerceBool(v) {
1450
+ return typeof v === "boolean" ? v : void 0;
1451
+ }
1452
+ function resolveSetting(...candidates) {
1453
+ for (const c of candidates) {
1454
+ if (c !== null && c !== void 0) return c;
1455
+ }
1456
+ return void 0;
1457
+ }
1458
+ function parseVisibilityParam(value) {
1459
+ if (value === "hide") return false;
1460
+ if (value === "show") return true;
1461
+ return void 0;
1462
+ }
1463
+ function coerceBasemaps(v) {
1464
+ if (!Array.isArray(v)) return void 0;
1465
+ const out = [];
1466
+ for (const raw of v) {
1467
+ const o = asObject(raw);
1468
+ if (!o) continue;
1469
+ const id = coerceString(o.id);
1470
+ const label = coerceString(o.label);
1471
+ const url = coerceString(o.url);
1472
+ const type = o.type === "vector" || o.type === "raster" ? o.type : void 0;
1473
+ if (!id || !label || !url || !type) continue;
1474
+ const variant = o.variant === "light" || o.variant === "dark" ? o.variant : void 0;
1475
+ out.push({ id, label, url, type, ...variant ? { variant } : {} });
1476
+ }
1477
+ return out;
1478
+ }
1479
+ function coerceConnections(v) {
1480
+ if (!Array.isArray(v)) return void 0;
1481
+ const out = [];
1482
+ for (const raw of v) {
1483
+ const o = asObject(raw);
1484
+ if (!o) continue;
1485
+ const name = coerceString(o.name);
1486
+ const bucket = coerceString(o.bucket);
1487
+ if (!name || !bucket) continue;
1488
+ const region = coerceString(o.region);
1489
+ const endpoint = coerceString(o.endpoint);
1490
+ const anonymous = coerceBool(o.anonymous);
1491
+ const authMethod = o.authMethod === "sigv4" || o.authMethod === "sas-token" ? o.authMethod : void 0;
1492
+ const rootPrefix = coerceString(o.rootPrefix);
1493
+ out.push({
1494
+ name,
1495
+ bucket,
1496
+ provider: coerceString(o.provider) ?? "s3",
1497
+ ...region !== void 0 ? { region } : {},
1498
+ ...endpoint !== void 0 ? { endpoint } : {},
1499
+ ...anonymous !== void 0 ? { anonymous } : {},
1500
+ ...authMethod !== void 0 ? { authMethod } : {},
1501
+ ...rootPrefix !== void 0 ? { rootPrefix } : {}
1502
+ });
1503
+ }
1504
+ return out;
1505
+ }
1506
+ function resolveBasemap(config, variant, userId) {
1507
+ const list = config.basemaps;
1508
+ if (list.length === 0) return void 0;
1509
+ if (userId) {
1510
+ const picked = list.find((b) => b.id === userId);
1511
+ if (picked) return picked;
1512
+ }
1513
+ const defaultId = config.defaultBasemap[variant];
1514
+ if (defaultId) {
1515
+ const byDefault = list.find((b) => b.id === defaultId);
1516
+ if (byDefault) return byDefault;
1517
+ }
1518
+ return list.find((b) => b.variant === variant) ?? list[0];
1519
+ }
1520
+ function mergeAppConfig(base, override) {
1521
+ const o = asObject(override);
1522
+ if (!o) return base;
1523
+ const d = asObject(o.defaults) ?? {};
1524
+ const u = asObject(o.ui) ?? {};
1525
+ const db = asObject(o.defaultBasemap) ?? {};
1526
+ return {
1527
+ defaults: {
1528
+ theme: coerceTheme(d.theme) ?? base.defaults.theme,
1529
+ locale: coerceString(d.locale) ?? base.defaults.locale,
1530
+ featureLimit: coercePositiveInt(d.featureLimit) ?? base.defaults.featureLimit,
1531
+ mosaicItemLimit: coercePositiveInt(d.mosaicItemLimit) ?? base.defaults.mosaicItemLimit
1532
+ },
1533
+ ui: {
1534
+ showConnectionRail: coerceBool(u.showConnectionRail) ?? base.ui.showConnectionRail,
1535
+ showFileTree: coerceBool(u.showFileTree) ?? base.ui.showFileTree,
1536
+ showSettings: coerceBool(u.showSettings) ?? base.ui.showSettings
1537
+ },
1538
+ basemaps: coerceBasemaps(o.basemaps) ?? base.basemaps,
1539
+ defaultBasemap: (() => {
1540
+ const light = coerceString(db.light) ?? base.defaultBasemap.light;
1541
+ const dark = coerceString(db.dark) ?? base.defaultBasemap.dark;
1542
+ return { ...light !== void 0 ? { light } : {}, ...dark !== void 0 ? { dark } : {} };
1543
+ })(),
1544
+ connections: coerceConnections(o.connections) ?? base.connections
1545
+ };
1546
+ }
1547
+
1548
+ // src/stac.ts
1549
+ var STAC_COG_ASSET_KEYS = ["visual", "image", "data", "rendered_preview"];
1550
+ function isStacItem(json) {
1551
+ if (!json || typeof json !== "object") return false;
1552
+ const obj = json;
1553
+ return obj.type === "Feature" && typeof obj.stac_version === "string";
1554
+ }
1555
+ function isStacFeatureCollection(json) {
1556
+ if (!json || typeof json !== "object") return false;
1557
+ const obj = json;
1558
+ if (obj.type !== "FeatureCollection") return false;
1559
+ if (!Array.isArray(obj.features) || obj.features.length === 0) return false;
1560
+ if (typeof obj.stac_version === "string") return true;
1561
+ return isStacItem(obj.features[0]);
1562
+ }
1563
+ function isStacCollection(json) {
1564
+ if (!json || typeof json !== "object") return false;
1565
+ const obj = json;
1566
+ return obj.type === "Collection" && typeof obj.stac_version === "string" && Array.isArray(obj.links);
1567
+ }
1568
+ function isStacCatalog(json) {
1569
+ if (!json || typeof json !== "object") return false;
1570
+ const obj = json;
1571
+ return obj.type === "Catalog" && typeof obj.stac_version === "string" && Array.isArray(obj.links);
1572
+ }
1573
+ function classifyStac(json) {
1574
+ if (isStacItem(json)) return { kind: "item", item: json };
1575
+ if (isStacFeatureCollection(json)) return { kind: "item-collection", fc: json };
1576
+ if (isStacCollection(json)) return { kind: "collection", payload: json };
1577
+ if (isStacCatalog(json)) return { kind: "catalog", payload: json };
1578
+ return { kind: "none" };
1579
+ }
1580
+ function pickCogAssetHref(item, preferred) {
1581
+ const assets = item.assets ?? {};
1582
+ if (preferred && assets[preferred]?.href) return assets[preferred].href;
1583
+ for (const key of STAC_COG_ASSET_KEYS) {
1584
+ if (assets[key]?.href) return assets[key].href;
1585
+ }
1586
+ for (const asset of Object.values(assets)) {
1587
+ const t = typeof asset?.type === "string" ? asset.type.toLowerCase() : "";
1588
+ if (asset?.href && t.includes("tiff")) return asset.href;
1589
+ }
1590
+ return null;
1591
+ }
1592
+ function detectMosaicCapable(item) {
1593
+ return stacItemBbox(item) !== null && pickCogAssetHref(item) !== null;
1594
+ }
1595
+ function detectMultiCogCapable(item) {
1596
+ if (hasRgbBands(extractSentinelBandAssets(item))) return true;
1597
+ return hasCompositableBands(extractRasterBandAssets(item));
1598
+ }
1599
+ function stacItemBbox(item) {
1600
+ if (Array.isArray(item.bbox) && item.bbox.length >= 4) {
1601
+ return [Number(item.bbox[0]), Number(item.bbox[1]), Number(item.bbox[2]), Number(item.bbox[3])];
1602
+ }
1603
+ return null;
1604
+ }
1605
+ function buildMosaicSourceMeta(input, assetKey) {
1606
+ if (!input || typeof input !== "object") return null;
1607
+ if (isStacItem(input)) {
1608
+ const bbox = stacItemBbox(input);
1609
+ if (!bbox) return null;
1610
+ const href = pickCogAssetHref(input, assetKey);
1611
+ if (!href) return null;
1612
+ return {
1613
+ id: String(input.id ?? href),
1614
+ bbox,
1615
+ href
1616
+ };
1617
+ }
1618
+ const raw = input;
1619
+ if (Array.isArray(raw.bbox) && raw.bbox.length >= 4 && typeof raw.href === "string") {
1620
+ return {
1621
+ id: String(raw.id ?? raw.href),
1622
+ bbox: [Number(raw.bbox[0]), Number(raw.bbox[1]), Number(raw.bbox[2]), Number(raw.bbox[3])],
1623
+ href: raw.href
1624
+ };
1625
+ }
1626
+ return null;
1627
+ }
1628
+ function spatialCellKey(item, bbox) {
1629
+ const props = item.properties ?? {};
1630
+ const grid = props["grid:code"];
1631
+ if (typeof grid === "string" && grid) return `g:${grid}`;
1632
+ const utm = props["mgrs:utm_zone"];
1633
+ const band = props["mgrs:latitude_band"];
1634
+ const sq = props["mgrs:grid_square"];
1635
+ if (utm != null && band != null && sq != null) return `m:${utm}${band}${sq}`;
1636
+ const s2 = props["s2:mgrs_tile"];
1637
+ if (typeof s2 === "string" && s2) return `m:${s2}`;
1638
+ return `b:${bbox[0].toFixed(3)},${bbox[1].toFixed(3)},${bbox[2].toFixed(3)},${bbox[3].toFixed(3)}`;
1639
+ }
1640
+ var BAND_KEY_FALLBACKS = {
1641
+ red: ["red", "B04", "B4", "visual-red"],
1642
+ green: ["green", "B03", "B3", "visual-green"],
1643
+ blue: ["blue", "B02", "B2", "visual-blue"],
1644
+ nir: ["nir", "nir08", "B08", "B8", "B8A"],
1645
+ swir1: ["swir16", "swir1", "B11"],
1646
+ swir2: ["swir22", "swir2", "B12"],
1647
+ rededge: ["rededge1", "rededge", "B05", "B5"]
1648
+ };
1649
+ function extractSentinelBandAssets(item) {
1650
+ const out = {};
1651
+ const assets = item.assets ?? {};
1652
+ for (const [key, asset] of Object.entries(assets)) {
1653
+ if (!asset?.href) continue;
1654
+ const bands = asset["eo:bands"];
1655
+ if (Array.isArray(bands) && bands.length >= 1) {
1656
+ const common = bands[0]?.common_name?.toLowerCase();
1657
+ if (common && isBandSlot(common)) {
1658
+ if (!out[common]) out[common] = asset.href;
1659
+ continue;
1660
+ }
1661
+ }
1662
+ for (const slot of Object.keys(BAND_KEY_FALLBACKS)) {
1663
+ if (BAND_KEY_FALLBACKS[slot].includes(key) && !out[slot]) {
1664
+ out[slot] = asset.href;
1665
+ break;
1666
+ }
1667
+ }
1668
+ }
1669
+ return out;
1670
+ }
1671
+ function isBandSlot(value) {
1672
+ return value === "red" || value === "green" || value === "blue" || value === "nir" || value === "swir1" || value === "swir2" || value === "rededge";
1673
+ }
1674
+ function hasRgbBands(map) {
1675
+ return Boolean(map.red && map.green && map.blue);
1676
+ }
1677
+ function extractRasterBandAssets(item) {
1678
+ const out = [];
1679
+ const assets = item.assets ?? {};
1680
+ for (const [key, asset] of Object.entries(assets)) {
1681
+ if (!asset?.href) continue;
1682
+ const mediaType = typeof asset.type === "string" ? asset.type : void 0;
1683
+ if (mediaType && !/^image\/(tiff|geotiff)\b/i.test(mediaType)) continue;
1684
+ const roles = Array.isArray(asset.roles) ? asset.roles : void 0;
1685
+ if (roles && (roles.includes("thumbnail") || roles.includes("overview") || roles.includes("metadata"))) {
1686
+ continue;
1687
+ }
1688
+ const eoBands = Array.isArray(asset["eo:bands"]) ? asset["eo:bands"] : void 0;
1689
+ const assetExt = asset;
1690
+ const rasterBands = Array.isArray(assetExt["raster:bands"]) ? assetExt["raster:bands"] : void 0;
1691
+ const bandCount = rasterBands?.length ?? eoBands?.length;
1692
+ if (typeof bandCount === "number" && bandCount > 1) continue;
1693
+ const commonName = eoBands?.[0]?.common_name?.toLowerCase();
1694
+ out.push({
1695
+ key,
1696
+ href: asset.href,
1697
+ commonName,
1698
+ bandCount: typeof bandCount === "number" ? bandCount : 1,
1699
+ roles,
1700
+ mediaType,
1701
+ title: typeof asset.title === "string" ? asset.title : void 0
1702
+ });
1703
+ }
1704
+ return out;
1705
+ }
1706
+ function resolveBandSlotAssetKey(assets, slot) {
1707
+ const byCommon = assets.find((a) => a.commonName === slot);
1708
+ if (byCommon) return byCommon.key;
1709
+ const fallbacks = BAND_KEY_FALLBACKS[slot] ?? [];
1710
+ const byKey = assets.find((a) => fallbacks.includes(a.key));
1711
+ return byKey?.key;
1712
+ }
1713
+ function resolvePresetComposite(assets, composite) {
1714
+ const r = resolveBandSlotAssetKey(assets, composite.r);
1715
+ const g = resolveBandSlotAssetKey(assets, composite.g);
1716
+ const b = resolveBandSlotAssetKey(assets, composite.b);
1717
+ if (!r || !g || !b) return null;
1718
+ return { r, g, b };
1719
+ }
1720
+ function hasCompositableBands(assets) {
1721
+ return assets.length >= 3;
1722
+ }
1723
+ function extractMosaicAssets(item) {
1724
+ const out = [];
1725
+ const assets = item.assets ?? {};
1726
+ for (const [key, asset] of Object.entries(assets)) {
1727
+ if (!asset?.href) continue;
1728
+ const mediaType = typeof asset.type === "string" ? asset.type : void 0;
1729
+ if (mediaType && !/^image\/(tiff|geotiff)\b/i.test(mediaType)) continue;
1730
+ const roles = Array.isArray(asset.roles) ? asset.roles : void 0;
1731
+ if (roles && (roles.includes("thumbnail") || roles.includes("overview") || roles.includes("metadata"))) {
1732
+ continue;
1733
+ }
1734
+ const eoBands = Array.isArray(asset["eo:bands"]) ? asset["eo:bands"] : void 0;
1735
+ const assetExt = asset;
1736
+ const rasterBands = Array.isArray(assetExt["raster:bands"]) ? assetExt["raster:bands"] : void 0;
1737
+ const bandCount = rasterBands?.length ?? eoBands?.length;
1738
+ const commonName = eoBands?.[0]?.common_name?.toLowerCase();
1739
+ out.push({
1740
+ key,
1741
+ href: asset.href,
1742
+ commonName,
1743
+ bandCount: typeof bandCount === "number" ? bandCount : void 0,
1744
+ roles,
1745
+ mediaType,
1746
+ title: typeof asset.title === "string" ? asset.title : void 0
1747
+ });
1748
+ }
1749
+ return out;
1750
+ }
1751
+
1752
+ // src/channel-composite.ts
1753
+ var PRESETS = [
1754
+ {
1755
+ id: "natural-color",
1756
+ labelKey: "map.multiCogPreset.trueColor",
1757
+ slots: { r: "red", g: "green", b: "blue" }
1758
+ },
1759
+ {
1760
+ id: "false-color-ir",
1761
+ labelKey: "map.multiCogPreset.falseColorIR",
1762
+ slots: { r: "nir", g: "red", b: "green" }
1763
+ },
1764
+ {
1765
+ id: "swir",
1766
+ labelKey: "map.multiCogPreset.swir",
1767
+ slots: { r: "swir2", g: "swir1", b: "red" }
1768
+ },
1769
+ {
1770
+ id: "vegetation",
1771
+ labelKey: "map.multiCogPreset.vegetation",
1772
+ slots: { r: "nir", g: "swir1", b: "red" }
1773
+ },
1774
+ {
1775
+ id: "agriculture",
1776
+ labelKey: "map.multiCogPreset.agriculture",
1777
+ slots: { r: "swir1", g: "nir", b: "blue" }
1778
+ }
1779
+ ];
1780
+ function toRasterBandAssets(assets) {
1781
+ return assets.map((a) => ({
1782
+ key: a.key,
1783
+ href: a.href,
1784
+ commonName: a.eoCommon[0],
1785
+ bandCount: a.bandCount,
1786
+ roles: a.roles,
1787
+ mediaType: a.mediaType,
1788
+ title: a.title
1789
+ }));
1790
+ }
1791
+ function availablePresets(assets) {
1792
+ const rba = toRasterBandAssets(assets);
1793
+ return PRESETS.filter((p) => resolvePresetComposite(rba, p.slots) !== null);
1794
+ }
1795
+ function applyPreset(assets, preset) {
1796
+ const rba = toRasterBandAssets(assets);
1797
+ const r = resolvePresetComposite(rba, preset.slots);
1798
+ if (!r) return null;
1799
+ return {
1800
+ r: { assetKey: r.r, bandIndex: 0 },
1801
+ g: { assetKey: r.g, bandIndex: 0 },
1802
+ b: { assetKey: r.b, bandIndex: 0 }
1803
+ };
1804
+ }
1805
+ function presetMatchesComposite(preset, c, assets) {
1806
+ const resolved = applyPreset(assets, preset);
1807
+ if (!resolved) return false;
1808
+ return resolved.r.assetKey === c.r.assetKey && resolved.g.assetKey === c.g.assetKey && resolved.b.assetKey === c.b.assetKey && c.r.bandIndex === 0 && c.g.bandIndex === 0 && c.b.bandIndex === 0;
1809
+ }
1810
+ function compositeFromUrl(params, assets) {
1811
+ const r = params.get("r");
1812
+ const g = params.get("g");
1813
+ const b = params.get("b");
1814
+ if (!r || !g || !b) return null;
1815
+ const known = new Map(assets.map((a2) => [a2.key, a2]));
1816
+ const ra = known.get(r);
1817
+ const ga = known.get(g);
1818
+ const ba = known.get(b);
1819
+ if (!ra || !ga || !ba) return null;
1820
+ const out = {
1821
+ r: { assetKey: r, bandIndex: clampBand(params.get("band_r"), ra.bandCount) },
1822
+ g: { assetKey: g, bandIndex: clampBand(params.get("band_g"), ga.bandCount) },
1823
+ b: { assetKey: b, bandIndex: clampBand(params.get("band_b"), ba.bandCount) }
1824
+ };
1825
+ const a = params.get("a");
1826
+ if (a) {
1827
+ const aa = known.get(a);
1828
+ if (aa) out.a = { assetKey: a, bandIndex: clampBand(params.get("band_a"), aa.bandCount) };
1829
+ }
1830
+ return out;
1831
+ }
1832
+ function compositeToUrl(c, presetId) {
1833
+ const p = new URLSearchParams();
1834
+ p.set("r", c.r.assetKey);
1835
+ p.set("g", c.g.assetKey);
1836
+ p.set("b", c.b.assetKey);
1837
+ if (c.r.bandIndex !== 0) p.set("band_r", String(c.r.bandIndex));
1838
+ if (c.g.bandIndex !== 0) p.set("band_g", String(c.g.bandIndex));
1839
+ if (c.b.bandIndex !== 0) p.set("band_b", String(c.b.bandIndex));
1840
+ if (c.a) {
1841
+ p.set("a", c.a.assetKey);
1842
+ if (c.a.bandIndex !== 0) p.set("band_a", String(c.a.bandIndex));
1843
+ }
1844
+ if (presetId) p.set("preset", presetId);
1845
+ return p;
1846
+ }
1847
+ function clampBand(raw, bandCount) {
1848
+ if (!raw) return 0;
1849
+ const n = Number(raw);
1850
+ if (!Number.isFinite(n)) return 0;
1851
+ const i = Math.floor(n);
1852
+ if (i < 0) return 0;
1853
+ if (i >= bandCount) return Math.max(0, bandCount - 1);
1854
+ return i;
1855
+ }
1856
+
1857
+ // src/clipboard.ts
1858
+ async function copyToClipboard(text, onFeedback) {
1859
+ try {
1860
+ await navigator.clipboard.writeText(text);
1861
+ onFeedback?.(true);
1862
+ setTimeout(() => onFeedback?.(false), COPY_FEEDBACK_MS);
1863
+ return true;
1864
+ } catch {
1865
+ return false;
1866
+ }
1867
+ }
1868
+ function wireCodeCopyButtons(root, selector) {
1869
+ for (const btn of root.querySelectorAll(selector)) {
1870
+ btn.addEventListener("click", async () => {
1871
+ const code = decodeURIComponent(btn.dataset.code ?? "");
1872
+ try {
1873
+ await navigator.clipboard.writeText(code);
1874
+ btn.classList.add("copied");
1875
+ setTimeout(() => btn.classList.remove("copied"), COPY_FEEDBACK_MS);
1876
+ } catch {
1877
+ }
1878
+ });
1879
+ }
1880
+ }
1881
+
1882
+ // src/cloud-url.ts
1435
1883
  var AWS_REGION_RE = /^(us|eu|ap|sa|ca|me|af|il)-(north|south|east|west|central|northeast|southeast|northwest|southwest)-\d+/;
1436
1884
  function getNativeScheme(provider) {
1437
1885
  const def = PROVIDERS[provider];
@@ -1446,7 +1894,7 @@ function safeDecodeURIComponent(s) {
1446
1894
  }
1447
1895
  }
1448
1896
  function resolveCloudUrl(url) {
1449
- const s3Match = url.match(/^s3[an]?:\/\/([^/]+)\/?(.*)$/);
1897
+ const s3Match = url.match(/^s3[an]?:\/\/([^/]+)(?:\/(.*))?$/);
1450
1898
  if (s3Match) {
1451
1899
  const [, bucket, key] = s3Match;
1452
1900
  const regionMatch = bucket.match(AWS_REGION_RE);
@@ -1454,7 +1902,7 @@ function resolveCloudUrl(url) {
1454
1902
  const base = buildProviderBaseUrl("s3", "", bucket, region);
1455
1903
  return key ? `${base}/${key}` : base;
1456
1904
  }
1457
- const gcsMatch = url.match(/^gcs?:\/\/([^/]+)\/?(.*)$/);
1905
+ const gcsMatch = url.match(/^gcs?:\/\/([^/]+)(?:\/(.*))?$/);
1458
1906
  if (gcsMatch) {
1459
1907
  const [, bucket, key] = gcsMatch;
1460
1908
  const base = buildProviderBaseUrl("gcs", "", bucket, "");
@@ -1462,6 +1910,122 @@ function resolveCloudUrl(url) {
1462
1910
  }
1463
1911
  return url;
1464
1912
  }
1913
+
1914
+ // src/cog-asset.ts
1915
+ var TIFF_MEDIA = /^image\/(tiff|geotiff)\b/i;
1916
+ var NON_DATA_ROLES = /* @__PURE__ */ new Set(["thumbnail", "overview", "metadata"]);
1917
+ function extractCogAssets(item) {
1918
+ const out = [];
1919
+ const assets = item.assets ?? {};
1920
+ const props = item.properties ?? {};
1921
+ const itemBands = Array.isArray(props.bands) ? props.bands : void 0;
1922
+ for (const [key, asset] of Object.entries(assets)) {
1923
+ if (!asset?.href) continue;
1924
+ const mediaType = typeof asset.type === "string" ? asset.type : void 0;
1925
+ if (mediaType && !TIFF_MEDIA.test(mediaType)) continue;
1926
+ const roles = Array.isArray(asset.roles) ? asset.roles : [];
1927
+ if (roles.some((r) => NON_DATA_ROLES.has(r))) continue;
1928
+ const eoBands = Array.isArray(asset["eo:bands"]) ? asset["eo:bands"] : void 0;
1929
+ const assetExt = asset;
1930
+ const rasterBands = Array.isArray(assetExt["raster:bands"]) ? assetExt["raster:bands"] : void 0;
1931
+ const assetBands11 = Array.isArray(assetExt.bands) ? assetExt.bands : void 0;
1932
+ const bandCount = assetBands11?.length ?? rasterBands?.length ?? eoBands?.length ?? itemBands?.length;
1933
+ const bandCountKnown = typeof bandCount === "number" && bandCount > 0;
1934
+ const commonSource = eoBands ?? assetBands11 ?? itemBands;
1935
+ const eoCommon = commonSource ? commonSource.map((b) => {
1936
+ const c = b?.common_name;
1937
+ return typeof c === "string" ? c.toLowerCase() : "";
1938
+ }) : [];
1939
+ const dtypeSource = rasterBands ?? assetBands11 ?? itemBands;
1940
+ const dtype = dtypeSource?.[0]?.data_type;
1941
+ out.push({
1942
+ key,
1943
+ href: asset.href,
1944
+ bandCount: bandCountKnown ? bandCount : 1,
1945
+ bandCountKnown,
1946
+ dtype: typeof dtype === "string" ? dtype : void 0,
1947
+ eoCommon,
1948
+ roles,
1949
+ title: typeof asset.title === "string" ? asset.title : void 0,
1950
+ mediaType
1951
+ });
1952
+ }
1953
+ return out;
1954
+ }
1955
+ function syntheticSelfAsset(href, probedBandCount) {
1956
+ const known = typeof probedBandCount === "number" && probedBandCount > 0;
1957
+ return {
1958
+ key: "self",
1959
+ href,
1960
+ bandCount: known ? probedBandCount : 1,
1961
+ bandCountKnown: known,
1962
+ eoCommon: [],
1963
+ roles: []
1964
+ };
1965
+ }
1966
+ function pickNaturalColorComposite(assets) {
1967
+ if (assets.length === 0) return null;
1968
+ for (const a of assets) {
1969
+ if (a.bandCount === 3 && (a.roles.includes("visual") || hasRgbInEoCommon(a.eoCommon))) {
1970
+ return {
1971
+ composite: {
1972
+ r: { assetKey: a.key, bandIndex: indexOfCommon(a.eoCommon, "red", 0) },
1973
+ g: { assetKey: a.key, bandIndex: indexOfCommon(a.eoCommon, "green", 1) },
1974
+ b: { assetKey: a.key, bandIndex: indexOfCommon(a.eoCommon, "blue", 2) }
1975
+ },
1976
+ source: "visual-asset"
1977
+ };
1978
+ }
1979
+ }
1980
+ const red = assets.find((a) => a.eoCommon[0] === "red");
1981
+ const green = assets.find((a) => a.eoCommon[0] === "green");
1982
+ const blue = assets.find((a) => a.eoCommon[0] === "blue");
1983
+ if (red && green && blue) {
1984
+ return {
1985
+ composite: {
1986
+ r: { assetKey: red.key, bandIndex: 0 },
1987
+ g: { assetKey: green.key, bandIndex: 0 },
1988
+ b: { assetKey: blue.key, bandIndex: 0 }
1989
+ },
1990
+ source: "rgb-bands"
1991
+ };
1992
+ }
1993
+ if (assets.length >= 3) {
1994
+ return {
1995
+ composite: {
1996
+ r: { assetKey: assets[0].key, bandIndex: 0 },
1997
+ g: { assetKey: assets[1].key, bandIndex: 0 },
1998
+ b: { assetKey: assets[2].key, bandIndex: 0 }
1999
+ },
2000
+ source: "fallback"
2001
+ };
2002
+ }
2003
+ const only = assets[0];
2004
+ const last = Math.max(0, only.bandCount - 1);
2005
+ return {
2006
+ composite: {
2007
+ r: { assetKey: only.key, bandIndex: 0 },
2008
+ g: { assetKey: only.key, bandIndex: Math.min(1, last) },
2009
+ b: { assetKey: only.key, bandIndex: Math.min(2, last) }
2010
+ },
2011
+ source: "fallback"
2012
+ };
2013
+ }
2014
+ function hasRgbInEoCommon(eo) {
2015
+ return eo.includes("red") && eo.includes("green") && eo.includes("blue");
2016
+ }
2017
+ function indexOfCommon(eo, name, fallback) {
2018
+ const i = eo.indexOf(name);
2019
+ return i >= 0 ? i : fallback;
2020
+ }
2021
+ function isSingleAssetComposite(c) {
2022
+ return c.r.assetKey === c.g.assetKey && c.g.assetKey === c.b.assetKey;
2023
+ }
2024
+ function allChannelsBand0(c) {
2025
+ return c.r.bandIndex === 0 && c.g.bandIndex === 0 && c.b.bandIndex === 0;
2026
+ }
2027
+
2028
+ // src/cog-info.ts
1465
2029
  var SF_LABELS = {
1466
2030
  1: "uint",
1467
2031
  2: "int",
@@ -1485,7 +2049,7 @@ function buildDataTypeLabel(sampleFormat, bitsPerSample) {
1485
2049
  return `${SF_LABELS[sampleFormat] ?? `sf${sampleFormat}`}${bitsPerSample ?? ""}`;
1486
2050
  }
1487
2051
 
1488
- // ../../src/lib/utils/column-types.ts
2052
+ // src/column-types.ts
1489
2053
  var NUMBER_TYPES = [
1490
2054
  "TINYINT",
1491
2055
  "SMALLINT",
@@ -1539,7 +2103,9 @@ var BINARY_TYPES = ["BLOB", "BYTEA", "BINARY", "VARBINARY"];
1539
2103
  var JSON_TYPES = ["JSON", "JSONB"];
1540
2104
  function classifyType(duckdbType) {
1541
2105
  const upper = duckdbType.toUpperCase().trim();
1542
- const base = upper.replace(/\(.*\)/, "").trim();
2106
+ const openIdx = upper.indexOf("(");
2107
+ const closeIdx = upper.lastIndexOf(")");
2108
+ const base = (openIdx >= 0 && closeIdx > openIdx ? upper.slice(0, openIdx) + upper.slice(closeIdx + 1) : upper).trim();
1543
2109
  if (NUMBER_TYPES.includes(base)) return "number";
1544
2110
  if (STRING_TYPES.includes(base)) return "string";
1545
2111
  if (DATE_TYPES.includes(base)) return "date";
@@ -1601,13 +2167,84 @@ function typeLabel(category) {
1601
2167
  return TYPE_LABELS[category];
1602
2168
  }
1603
2169
 
1604
- // ../../src/lib/utils/error.ts
2170
+ // src/connection-identity.ts
2171
+ var DEFAULT_PORTS = {
2172
+ "https:": "443",
2173
+ "http:": "80"
2174
+ };
2175
+ var SLASH = 47;
2176
+ function stripTrailingSlashes(s) {
2177
+ let end = s.length;
2178
+ while (end > 0 && s.charCodeAt(end - 1) === SLASH) end--;
2179
+ return s.slice(0, end);
2180
+ }
2181
+ function stripEdgeSlashes(s) {
2182
+ let start = 0;
2183
+ let end = s.length;
2184
+ while (start < end && s.charCodeAt(start) === SLASH) start++;
2185
+ while (end > start && s.charCodeAt(end - 1) === SLASH) end--;
2186
+ return s.slice(start, end);
2187
+ }
2188
+ function normalizeEndpoint(raw) {
2189
+ if (!raw) return "";
2190
+ const trimmed = raw.trim();
2191
+ if (!trimmed) return "";
2192
+ try {
2193
+ const url = new URL(trimmed);
2194
+ const scheme = url.protocol.toLowerCase();
2195
+ const host = url.hostname.toLowerCase();
2196
+ const defaultPort = DEFAULT_PORTS[scheme] ?? "";
2197
+ const port = url.port && url.port !== defaultPort ? `:${url.port}` : "";
2198
+ const path = stripTrailingSlashes(url.pathname);
2199
+ return `${scheme}//${host}${port}${path}`;
2200
+ } catch {
2201
+ return stripTrailingSlashes(trimmed.toLowerCase());
2202
+ }
2203
+ }
2204
+ function normalizeProvider(provider) {
2205
+ if (!provider) return "s3";
2206
+ const p = provider.trim().toLowerCase();
2207
+ if (!p || p === "unknown") return "s3";
2208
+ return p;
2209
+ }
2210
+ function normalizeBucket(bucket) {
2211
+ return stripEdgeSlashes((bucket ?? "").trim());
2212
+ }
2213
+ function normalizeRegion(region) {
2214
+ return (region ?? "").trim().toLowerCase();
2215
+ }
2216
+ function connectionIdentityKey(input) {
2217
+ const provider = normalizeProvider(input.provider);
2218
+ const bucket = normalizeBucket(input.bucket);
2219
+ if (!bucket) return "";
2220
+ const endpoint = normalizeEndpoint(input.endpoint);
2221
+ const region = normalizeRegion(input.region);
2222
+ if (provider === "azure") return `azure|${endpoint}|${bucket}`;
2223
+ if (provider === "gcs") return `gcs|${bucket}`;
2224
+ if (provider === "s3" && !endpoint) return `s3|${bucket}|${region}`;
2225
+ return `${provider}|${endpoint}|${bucket}`;
2226
+ }
2227
+ function isSameConnectionIdentity(a, b) {
2228
+ const key = connectionIdentityKey(a);
2229
+ return key !== "" && key === connectionIdentityKey(b);
2230
+ }
2231
+
2232
+ // src/error.ts
2233
+ function isAbortError(err) {
2234
+ if (!err) return false;
2235
+ if (err instanceof DOMException && err.name === "AbortError") return true;
2236
+ const e = err;
2237
+ if (e.name === "AbortError") return true;
2238
+ if (typeof e.message === "string" && /\baborted?\b/i.test(e.message)) return true;
2239
+ if (e.cause && isAbortError(e.cause)) return true;
2240
+ return false;
2241
+ }
1605
2242
  function handleLoadError(err) {
1606
- if (err instanceof DOMException && err.name === "AbortError") return null;
2243
+ if (isAbortError(err)) return null;
1607
2244
  return err instanceof Error ? err.message : String(err);
1608
2245
  }
1609
2246
 
1610
- // ../../src/lib/utils/format.ts
2247
+ // src/format.ts
1611
2248
  function formatFileSize(bytes) {
1612
2249
  if (bytes < 0) return "0 B";
1613
2250
  if (bytes === 0) return "0 B";
@@ -1653,7 +2290,7 @@ function formatValue(value) {
1653
2290
  return String(value);
1654
2291
  }
1655
2292
 
1656
- // ../../src/lib/utils/export.ts
2293
+ // src/export.ts
1657
2294
  function triggerDownload(content, filename, mimeType) {
1658
2295
  const blob = new Blob([content], { type: mimeType });
1659
2296
  const url = URL.createObjectURL(blob);
@@ -1714,7 +2351,7 @@ function exportToJson(columns, rows, filename) {
1714
2351
  );
1715
2352
  }
1716
2353
 
1717
- // ../../src/lib/utils/file-sort.ts
2354
+ // src/file-sort.ts
1718
2355
  function sortFileEntries(entries, config) {
1719
2356
  const sorted = [...entries];
1720
2357
  const dir = config.direction === "asc" ? 1 : -1;
@@ -2319,7 +2956,42 @@ function buildGeoArrowTables(wkbArrays, attributes, knownGeomType) {
2319
2956
  return results;
2320
2957
  }
2321
2958
 
2322
- // ../../src/lib/utils/hex.ts
2959
+ // src/geometry-type.ts
2960
+ var GEOMETRY_PREFIX = /^GEOMETRY(\s*\(\s*'?([^')]+)'?\s*\))?/i;
2961
+ function parseGeometryTypeCrs(typeStr) {
2962
+ if (!typeStr) return { isGeometry: false, hasCrs: false, rawCrs: null, nonWgs84Crs: null };
2963
+ const match = typeStr.match(GEOMETRY_PREFIX);
2964
+ if (!match) return { isGeometry: false, hasCrs: false, rawCrs: null, nonWgs84Crs: null };
2965
+ const rawCrs = match[2]?.trim() ?? null;
2966
+ if (!rawCrs) return { isGeometry: true, hasCrs: false, rawCrs: null, nonWgs84Crs: null };
2967
+ return {
2968
+ isGeometry: true,
2969
+ hasCrs: true,
2970
+ rawCrs,
2971
+ nonWgs84Crs: isWgs84Crs(rawCrs) ? null : rawCrs
2972
+ };
2973
+ }
2974
+ function isWgs84Crs(crs) {
2975
+ if (!crs) return true;
2976
+ const trimmed = crs.trim();
2977
+ if (trimmed === "OGC:CRS84" || trimmed === "OGC:CRS83") return true;
2978
+ const epsgMatch = trimmed.match(/^EPSG:(\d+)$/i);
2979
+ if (epsgMatch && WGS84_CODES.has(Number(epsgMatch[1]))) return true;
2980
+ return false;
2981
+ }
2982
+ function buildTransformExpr(innerExpr, sourceType, sourceCrs, targetCrs) {
2983
+ const info = parseGeometryTypeCrs(sourceType);
2984
+ if (info.hasCrs) {
2985
+ return `ST_Transform(${innerExpr}, '${targetCrs}')`;
2986
+ }
2987
+ return `ST_Transform(${innerExpr}, '${sourceCrs}', '${targetCrs}')`;
2988
+ }
2989
+ function wrapWkbWithCrs(wkbExpr, sourceCrs) {
2990
+ if (!sourceCrs) return `ST_GeomFromWKB(${wkbExpr})`;
2991
+ return `ST_SetCRS(ST_GeomFromWKB(${wkbExpr}), '${sourceCrs}')`;
2992
+ }
2993
+
2994
+ // src/hex.ts
2323
2995
  function generateHexDump(data, bytesPerRow = 16) {
2324
2996
  const rows = [];
2325
2997
  for (let i = 0; i < data.length; i += bytesPerRow) {
@@ -2343,458 +3015,233 @@ function generateHexDump(data, bytesPerRow = 16) {
2343
3015
  return rows;
2344
3016
  }
2345
3017
 
2346
- // ../../src/lib/utils/local-storage.ts
2347
- function loadFromStorage(key, defaultValue) {
2348
- if (typeof window === "undefined") return defaultValue;
2349
- try {
2350
- const raw = localStorage.getItem(key);
2351
- if (raw) return JSON.parse(raw);
2352
- } catch {
2353
- }
2354
- return defaultValue;
3018
+ // src/stac-storage-extension.ts
3019
+ function emptyStorageHints() {
3020
+ return { platform: null, region: null, requesterPays: false, endpoint: null };
2355
3021
  }
2356
- function persistToStorage(key, value) {
2357
- if (typeof window === "undefined") return;
2358
- try {
2359
- localStorage.setItem(key, JSON.stringify(value));
2360
- } catch {
3022
+ function detectStorageExtensionVersion(item) {
3023
+ const exts = item.stac_extensions;
3024
+ if (!Array.isArray(exts)) return null;
3025
+ for (const raw of exts) {
3026
+ if (typeof raw !== "string") continue;
3027
+ if (!raw.includes("stac-extensions.github.io/storage")) continue;
3028
+ const trimmed = raw.endsWith("/schema.json") ? raw.slice(0, -"/schema.json".length) : raw;
3029
+ const last = trimmed.slice(trimmed.lastIndexOf("/") + 1);
3030
+ const version = last.startsWith("v") ? last.slice(1) : last;
3031
+ if (version === "1.0.0" || version === "2.0.0") return version;
3032
+ const major = version.split(".")[0];
3033
+ if (major === "1") return "1.0.0";
3034
+ if (major === "2") return "2.0.0";
2361
3035
  }
3036
+ return null;
2362
3037
  }
2363
-
2364
- // ../../src/lib/utils/markdown-sql.ts
2365
- async function parseMarkdownDocument(markdown) {
2366
- let frontmatter = {};
2367
- let content = markdown;
2368
- const fmMatch = markdown.match(/^---\n([\s\S]*?)\n---\n/);
2369
- if (fmMatch) {
2370
- try {
2371
- const { default: YAML } = await import('yaml');
2372
- frontmatter = YAML.parse(fmMatch[1]) || {};
2373
- } catch {
3038
+ function extractStorageHints(item, assetKey) {
3039
+ const version = detectStorageExtensionVersion(item);
3040
+ if (version === "1.0.0") return extractV1Hints(item, assetKey);
3041
+ if (version === "2.0.0") return extractV2Hints(item, assetKey);
3042
+ return emptyStorageHints();
3043
+ }
3044
+ function extractV1Hints(item, assetKey) {
3045
+ const props = item.properties ?? {};
3046
+ const asset = assetKey && item.assets && item.assets[assetKey] ? item.assets[assetKey] : null;
3047
+ const platformRaw = pickFirstString([asset?.["storage:platform"], props["storage:platform"]]);
3048
+ const region = pickFirstString([asset?.["storage:region"], props["storage:region"]]);
3049
+ const requesterPays = pickFirstBoolean([
3050
+ asset?.["storage:requester_pays"],
3051
+ props["storage:requester_pays"]
3052
+ ]);
3053
+ return {
3054
+ platform: platformRaw ? platformRaw.toUpperCase() : null,
3055
+ region: region ?? null,
3056
+ requesterPays: requesterPays === true,
3057
+ endpoint: null
3058
+ };
3059
+ }
3060
+ function extractV2Hints(item, assetKey) {
3061
+ const props = item.properties ?? {};
3062
+ const schemes = props["storage:schemes"] ?? {};
3063
+ if (!schemes || typeof schemes !== "object") return emptyStorageHints();
3064
+ const refs = collectAssetRefs(item, assetKey);
3065
+ const scheme = pickFirstScheme(schemes, refs);
3066
+ if (!scheme) return emptyStorageHints();
3067
+ const storeType = typeof scheme.type === "string" ? scheme.type : "";
3068
+ const platform = typeof scheme.platform === "string" ? scheme.platform : null;
3069
+ const region = typeof scheme.region === "string" ? scheme.region : null;
3070
+ const requesterPays = scheme.requester_pays === true;
3071
+ let endpoint = null;
3072
+ if (storeType === "custom-s3") {
3073
+ if (typeof scheme.endpoint === "string" && scheme.endpoint && !scheme.endpoint.includes("{")) {
3074
+ endpoint = scheme.endpoint;
3075
+ } else if (platform && !platform.includes("{")) {
3076
+ endpoint = platform;
2374
3077
  }
2375
- content = markdown.slice(fmMatch[0].length);
2376
3078
  }
2377
- const sqlBlocks = [];
2378
- const lines = content.split("\n");
2379
- let i = 0;
2380
- while (i < lines.length) {
2381
- const line = lines[i];
2382
- const match = line.match(/^```sql\s+(\w[\w-]*)\s*$/);
2383
- if (match) {
2384
- const name = match[1];
2385
- const startLine = i;
2386
- const sqlLines = [];
2387
- i++;
2388
- while (i < lines.length && lines[i] !== "```") {
2389
- sqlLines.push(lines[i]);
2390
- i++;
2391
- }
2392
- sqlBlocks.push({
2393
- name,
2394
- sql: sqlLines.join("\n"),
2395
- startLine,
2396
- endLine: i
2397
- });
2398
- }
2399
- i++;
3079
+ return {
3080
+ platform: platform ? platform.toUpperCase() : null,
3081
+ region,
3082
+ requesterPays,
3083
+ endpoint
3084
+ };
3085
+ }
3086
+ function collectAssetRefs(item, assetKey) {
3087
+ const assets = item.assets ?? {};
3088
+ if (assetKey) {
3089
+ const a = assets[assetKey];
3090
+ const refs = a?.["storage:refs"];
3091
+ if (Array.isArray(refs)) return refs.filter((r) => typeof r === "string");
3092
+ return [];
2400
3093
  }
2401
- return { frontmatter, content, sqlBlocks };
3094
+ const out = [];
3095
+ for (const a of Object.values(assets)) {
3096
+ if (!a || typeof a !== "object") continue;
3097
+ const refs = a["storage:refs"];
3098
+ if (!Array.isArray(refs)) continue;
3099
+ for (const r of refs) if (typeof r === "string") out.push(r);
3100
+ }
3101
+ return out;
2402
3102
  }
2403
- function interpolateTemplates(text, queryResults) {
2404
- return text.replace(/\{(\w+)\.rows\[(\d+)\]\.(\w+)\}/g, (match, queryName, rowIdx, colName) => {
2405
- const rows = queryResults.get(queryName);
2406
- if (!rows) return match;
2407
- const row = rows[parseInt(rowIdx, 10)];
2408
- if (!row) return match;
2409
- const value = row[colName];
2410
- return value !== void 0 ? String(value) : match;
2411
- });
3103
+ function pickFirstScheme(schemes, refs) {
3104
+ for (const r of refs) {
3105
+ const s = schemes[r];
3106
+ if (s && typeof s === "object") return s;
3107
+ }
3108
+ return null;
2412
3109
  }
2413
- function markSqlBlocks(content) {
2414
- return content.replace(
2415
- /```sql\s+(\w[\w-]*)\s*\n([\s\S]*?)```/g,
2416
- (_, name) => `<div data-sql-block="${name}"></div>`
2417
- );
3110
+ function pickFirstString(candidates) {
3111
+ for (const c of candidates) {
3112
+ if (typeof c === "string" && c.length > 0) return c;
3113
+ }
3114
+ return null;
3115
+ }
3116
+ function pickFirstBoolean(candidates) {
3117
+ for (const c of candidates) {
3118
+ if (typeof c === "boolean") return c;
3119
+ }
3120
+ return null;
3121
+ }
3122
+ function applyStorageHintsToConnection(conn, hints) {
3123
+ const out = { ...conn };
3124
+ if (hints.region && !out.region) {
3125
+ out.region = hints.region;
3126
+ }
3127
+ if (hints.endpoint && !out.endpoint) {
3128
+ out.endpoint = hints.endpoint;
3129
+ }
3130
+ return out;
2418
3131
  }
2419
3132
 
2420
- // ../../src/lib/utils/parquet-metadata.ts
2421
- function mapParquetType(col) {
2422
- const lt = col.logical_type;
2423
- if (lt) {
2424
- if (lt.type === "GEOMETRY" || lt.type === "GEOGRAPHY") return "GEOMETRY";
2425
- if (lt.type === "STRING" || lt.type === "UTF8") return "VARCHAR";
2426
- if (lt.type === "JSON") return "JSON";
2427
- if (lt.type === "UUID") return "UUID";
2428
- if (lt.type === "ENUM") return "VARCHAR";
2429
- if (lt.type === "INT" || lt.type === "INTEGER") {
2430
- const bits = lt.bitWidth ?? 32;
2431
- const signed = lt.isSigned !== false;
2432
- if (bits <= 8) return signed ? "TINYINT" : "UTINYINT";
2433
- if (bits <= 16) return signed ? "SMALLINT" : "USMALLINT";
2434
- if (bits <= 32) return signed ? "INTEGER" : "UINTEGER";
2435
- return signed ? "BIGINT" : "UBIGINT";
3133
+ // src/storage-url.ts
3134
+ function buildSchemeMap() {
3135
+ const map = {};
3136
+ for (const [id, def] of Object.entries(PROVIDERS)) {
3137
+ for (const scheme of def.schemes) {
3138
+ const key = `${scheme}://`;
3139
+ map[key] = { provider: id, strip: key.length };
2436
3140
  }
2437
- if (lt.type === "DECIMAL") return `DECIMAL(${lt.precision ?? 18},${lt.scale ?? 0})`;
2438
- if (lt.type === "DATE") return "DATE";
2439
- if (lt.type === "TIME") return "TIME";
2440
- if (lt.type === "TIMESTAMP") return "TIMESTAMP";
2441
- if (lt.type === "BSON") return "BLOB";
2442
3141
  }
2443
- const ct = col.converted_type;
2444
- if (ct === "UTF8") return "VARCHAR";
2445
- if (ct === "JSON") return "JSON";
2446
- if (ct === "DATE") return "DATE";
2447
- if (ct === "TIMESTAMP_MILLIS" || ct === "TIMESTAMP_MICROS") return "TIMESTAMP";
2448
- if (ct === "DECIMAL") return `DECIMAL(${col.precision ?? 18},${col.scale ?? 0})`;
2449
- if (ct === "INT_8") return "TINYINT";
2450
- if (ct === "INT_16") return "SMALLINT";
2451
- if (ct === "INT_32") return "INTEGER";
2452
- if (ct === "INT_64") return "BIGINT";
2453
- if (ct === "UINT_8") return "UTINYINT";
2454
- if (ct === "UINT_16") return "USMALLINT";
2455
- if (ct === "UINT_32") return "UINTEGER";
2456
- if (ct === "UINT_64") return "UBIGINT";
2457
- const pt = col.type;
2458
- if (pt === "BOOLEAN") return "BOOLEAN";
2459
- if (pt === "INT32") return "INTEGER";
2460
- if (pt === "INT64") return "BIGINT";
2461
- if (pt === "INT96") return "TIMESTAMP";
2462
- if (pt === "FLOAT") return "FLOAT";
2463
- if (pt === "DOUBLE") return "DOUBLE";
2464
- if (pt === "BYTE_ARRAY") return "BLOB";
2465
- if (pt === "FIXED_LEN_BYTE_ARRAY") return "BLOB";
2466
- return "VARCHAR";
2467
- }
2468
- async function readParquetMetadata(url) {
2469
- const { parquetMetadataAsync, asyncBufferFromUrl } = await import('hyparquet');
2470
- const file = await asyncBufferFromUrl({ url });
2471
- const metadata = await parquetMetadataAsync(file);
2472
- const rowCount = metadata.row_groups.reduce(
2473
- (sum, rg) => sum + Number(rg.num_rows),
2474
- 0
2475
- );
2476
- const schema = metadata.schema.slice(1).filter((col) => col.num_children === void 0).map((col) => ({
2477
- name: col.name,
2478
- type: mapParquetType(col)
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
- }
2489
- let geo = null;
2490
- let legacyGeoParquet = false;
2491
- const geoKv = metadata.key_value_metadata?.find((kv) => kv.key === "geo");
2492
- if (geoKv) {
2493
- try {
2494
- const geoJson = JSON.parse(geoKv.value ?? "");
2495
- if (geoJson.schema_version && !geoJson.version) {
2496
- legacyGeoParquet = true;
2497
- }
2498
- geo = {
2499
- primaryColumn: geoJson.primary_column ?? "geometry",
2500
- columns: {}
2501
- };
2502
- if (geoJson.columns) {
2503
- for (const [colName, colMeta] of Object.entries(geoJson.columns)) {
2504
- geo.columns[colName] = {
2505
- encoding: colMeta.encoding ?? "WKB",
2506
- geometryTypes: colMeta.geometry_types ?? [],
2507
- crs: colMeta.crs ?? null,
2508
- bbox: colMeta.bbox
2509
- };
2510
- }
2511
- }
2512
- } catch {
2513
- }
2514
- }
2515
- const createdBy = metadata.created_by ?? null;
2516
- const numRowGroups = metadata.row_groups.length;
2517
- let compression = null;
2518
- if (numRowGroups > 0 && metadata.row_groups[0].columns) {
2519
- const codecs = /* @__PURE__ */ new Set();
2520
- for (const col of metadata.row_groups[0].columns) {
2521
- const codec = col.meta_data?.codec;
2522
- if (codec) codecs.add(codec);
2523
- }
2524
- if (codecs.size === 1) {
2525
- compression = [...codecs][0];
2526
- } else if (codecs.size > 1) {
2527
- compression = [...codecs].join(", ");
2528
- }
2529
- }
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;
3142
+ map["swift://"] = { provider: "unknown", strip: 8 };
3143
+ return map;
2550
3144
  }
2551
- function extractEpsgFromGeoMeta(geo) {
2552
- const primaryCol = geo.columns[geo.primaryColumn];
2553
- if (!primaryCol?.crs) return null;
2554
- const crs = primaryCol.crs;
2555
- if (crs.type === "name" && crs.properties?.name?.includes("CRS84")) return null;
2556
- if (crs.id?.authority === "EPSG") {
2557
- const code = crs.id.code;
2558
- if (WGS84_CODES.has(code)) return null;
2559
- return `EPSG:${code}`;
2560
- }
2561
- return null;
3145
+ var SCHEME_MAP = buildSchemeMap();
3146
+ var SLASH2 = 47;
3147
+ function stripTrailingSlashes2(s) {
3148
+ let end = s.length;
3149
+ while (end > 0 && s.charCodeAt(end - 1) === SLASH2) end--;
3150
+ return s.slice(0, end);
2562
3151
  }
2563
- function extractGeometryTypes(geo) {
2564
- const primaryCol = geo.columns[geo.primaryColumn];
2565
- if (!primaryCol?.geometryTypes?.length) return [];
2566
- const typeMap = {
2567
- Point: "point",
2568
- LineString: "linestring",
2569
- Polygon: "polygon",
2570
- MultiPoint: "multipoint",
2571
- MultiLineString: "multilinestring",
2572
- MultiPolygon: "multipolygon"
2573
- };
2574
- const types = [];
2575
- for (const raw of primaryCol.geometryTypes) {
2576
- const base = raw.split(" ")[0];
2577
- const mapped = typeMap[base];
2578
- if (mapped && !types.includes(mapped)) types.push(mapped);
2579
- }
2580
- return types;
3152
+ function stripEdgeSlashes2(s) {
3153
+ let start = 0;
3154
+ let end = s.length;
3155
+ while (start < end && s.charCodeAt(start) === SLASH2) start++;
3156
+ while (end > start && s.charCodeAt(end - 1) === SLASH2) end--;
3157
+ return s.slice(start, end);
2581
3158
  }
2582
- function extractBounds(geo) {
2583
- const primaryCol = geo.columns[geo.primaryColumn];
2584
- if (!primaryCol?.bbox || primaryCol.bbox.length < 4) return null;
2585
- return [primaryCol.bbox[0], primaryCol.bbox[1], primaryCol.bbox[2], primaryCol.bbox[3]];
3159
+ var AWS_VHOST_RE = /^(.+)\.s3[.-]([a-z0-9-]+)\.amazonaws\.com$/;
3160
+ var AWS_PATH_RE = /^s3[.-]([a-z0-9-]+)\.amazonaws\.com$/;
3161
+ var AWS_GLOBAL_HOST = "s3.amazonaws.com";
3162
+ var R2_RE = /^([a-z0-9]+)\.r2\.cloudflarestorage\.com$/;
3163
+ var GCS_GLOBAL_HOST = "storage.googleapis.com";
3164
+ var GCS_VHOST_RE = /^(.+)\.storage\.googleapis\.com$/;
3165
+ var DO_VHOST_RE = /^(.+)\.([a-z0-9-]+)\.digitaloceanspaces\.com$/;
3166
+ var DO_PATH_RE = /^([a-z0-9-]+)\.digitaloceanspaces\.com$/;
3167
+ var WASABI_RE = /^s3\.([a-z0-9-]+)\.wasabisys\.com$/;
3168
+ var B2_S3_RE = /^(.+)\.s3\.([a-z0-9-]+)\.backblazeb2\.com$/;
3169
+ var B2_NATIVE_RE = /^f[a-z0-9]+\.backblazeb2\.com$/;
3170
+ var OSS_RE = /^(.+)\.(oss-[a-z0-9-]+)\.aliyuncs\.com$/;
3171
+ var COS_RE = /^(.+)\.cos\.([a-z0-9-]+)\.myqcloud\.com$/;
3172
+ var YANDEX_HOST = "storage.yandexcloud.net";
3173
+ var CONTABO_RE = /^([a-z0-9]+)\.contabostorage\.com$/;
3174
+ var HETZNER_RE = /^([a-z0-9]+)\.your-objectstorage\.com$/;
3175
+ var LINODE_VHOST_RE = /^(.+)\.([a-z0-9-]+)\.linodeobjects\.com$/;
3176
+ var LINODE_PATH_RE = /^([a-z0-9-]+)\.linodeobjects\.com$/;
3177
+ var OVH_RE = /^s3\.([a-z0-9-]+)\.io\.cloud\.ovh\.(?:net|us)$/;
3178
+ var AZURE_BLOB_RE = /^([a-z0-9]+)\.blob\.core\.windows\.net$/;
3179
+ var STORJ_GATEWAY_RE = /^gateway\.(?:([a-z0-9]+)\.)?storjshare\.io$/;
3180
+ var STORJ_LINK_RE = /^link\.(?:([a-z0-9]+)\.)?storjshare\.io$/;
3181
+ function isMinioLikeHost(host) {
3182
+ return host.includes("minio") || host === "localhost" || host === "127.0.0.1" || host.startsWith("192.168.") || host.startsWith("10.");
2586
3183
  }
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));
3184
+ var STAC_API_PATH_RE = /\/(collections|items|catalogs|search)(\/|\?|$)/i;
3185
+ function isKnownBucketHost(host) {
3186
+ if (!host) return false;
3187
+ if (AWS_VHOST_RE.test(host)) return true;
3188
+ if (AWS_PATH_RE.test(host)) return true;
3189
+ if (host === AWS_GLOBAL_HOST) return true;
3190
+ if (R2_RE.test(host)) return true;
3191
+ if (host === GCS_GLOBAL_HOST) return true;
3192
+ if (GCS_VHOST_RE.test(host)) return true;
3193
+ if (DO_VHOST_RE.test(host)) return true;
3194
+ if (DO_PATH_RE.test(host)) return true;
3195
+ if (WASABI_RE.test(host)) return true;
3196
+ if (B2_S3_RE.test(host)) return true;
3197
+ if (B2_NATIVE_RE.test(host)) return true;
3198
+ if (OSS_RE.test(host)) return true;
3199
+ if (COS_RE.test(host)) return true;
3200
+ if (host === YANDEX_HOST) return true;
3201
+ if (CONTABO_RE.test(host)) return true;
3202
+ if (HETZNER_RE.test(host)) return true;
3203
+ if (LINODE_VHOST_RE.test(host)) return true;
3204
+ if (LINODE_PATH_RE.test(host)) return true;
3205
+ if (OVH_RE.test(host)) return true;
3206
+ if (AZURE_BLOB_RE.test(host)) return true;
3207
+ if (STORJ_GATEWAY_RE.test(host)) return true;
3208
+ if (STORJ_LINK_RE.test(host)) return true;
3209
+ if (isMinioLikeHost(host)) return true;
3210
+ return false;
2599
3211
  }
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;
3212
+ function defaultResult(defaults) {
3213
+ return {
3214
+ bucket: "",
3215
+ region: defaults.region || "us-east-1",
3216
+ endpoint: defaults.endpoint || "",
3217
+ provider: defaults.provider || "s3",
3218
+ prefix: ""
3219
+ };
2614
3220
  }
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;
3221
+ function splitBucketPrefix(rest) {
3222
+ const slashIdx = rest.indexOf("/");
3223
+ if (slashIdx >= 0) {
3224
+ return {
3225
+ bucket: rest.slice(0, slashIdx),
3226
+ prefix: stripTrailingSlashes2(rest.slice(slashIdx + 1))
3227
+ };
2622
3228
  }
3229
+ return { bucket: rest, prefix: "" };
2623
3230
  }
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
-
2726
- // ../../src/lib/utils/storage-url.ts
2727
- function buildSchemeMap() {
2728
- const map = {};
2729
- for (const [id, def] of Object.entries(PROVIDERS)) {
2730
- for (const scheme of def.schemes) {
2731
- const key = `${scheme}://`;
2732
- map[key] = { provider: id, strip: key.length };
2733
- }
2734
- }
2735
- map["swift://"] = { provider: "unknown", strip: 8 };
2736
- return map;
2737
- }
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;
2765
- function defaultResult(defaults) {
2766
- return {
2767
- bucket: "",
2768
- region: defaults.region || "us-east-1",
2769
- endpoint: defaults.endpoint || "",
2770
- provider: defaults.provider || "s3",
2771
- prefix: ""
2772
- };
2773
- }
2774
- function splitBucketPrefix(rest) {
2775
- const slashIdx = rest.indexOf("/");
2776
- if (slashIdx >= 0) {
2777
- return {
2778
- bucket: rest.slice(0, slashIdx),
2779
- prefix: rest.slice(slashIdx + 1).replace(/\/+$/, "")
2780
- };
2781
- }
2782
- return { bucket: rest, prefix: "" };
2783
- }
2784
- function parseStorageUrl(input, defaults = {}) {
2785
- const trimmed = input.trim();
2786
- const lower = trimmed.toLowerCase();
2787
- for (const [scheme, { provider, strip }] of Object.entries(SCHEME_MAP)) {
2788
- if (lower.startsWith(scheme)) {
2789
- const rest = trimmed.slice(strip);
2790
- const { bucket, prefix } = splitBucketPrefix(rest);
2791
- return {
2792
- bucket,
2793
- region: defaults.region || "us-east-1",
2794
- endpoint: defaults.endpoint || "",
2795
- provider,
2796
- prefix
2797
- };
3231
+ function parseStorageUrl(input, defaults = {}) {
3232
+ const trimmed = input.trim();
3233
+ const lower = trimmed.toLowerCase();
3234
+ for (const [scheme, { provider, strip }] of Object.entries(SCHEME_MAP)) {
3235
+ if (lower.startsWith(scheme)) {
3236
+ const rest = trimmed.slice(strip);
3237
+ const { bucket, prefix } = splitBucketPrefix(rest);
3238
+ return {
3239
+ bucket,
3240
+ region: defaults.region || "us-east-1",
3241
+ endpoint: defaults.endpoint || "",
3242
+ provider,
3243
+ prefix
3244
+ };
2798
3245
  }
2799
3246
  }
2800
3247
  if (lower.startsWith("http://") || lower.startsWith("https://")) {
@@ -2989,96 +3436,2062 @@ function parseStorageUrl(input, defaults = {}) {
2989
3436
  prefix: pathParts.slice(1).join("/")
2990
3437
  };
2991
3438
  }
2992
- if (isMinioLikeHost(host) && pathParts.length > 0) {
2993
- return {
2994
- bucket: pathParts[0],
2995
- region: defaults.region || "us-east-1",
2996
- endpoint: `${url.protocol}//${url.host}`,
2997
- provider: "minio",
2998
- prefix: pathParts.slice(1).join("/")
2999
- };
3439
+ if (isMinioLikeHost(host) && pathParts.length > 0) {
3440
+ return {
3441
+ bucket: pathParts[0],
3442
+ region: defaults.region || "us-east-1",
3443
+ endpoint: `${url.protocol}//${url.host}`,
3444
+ provider: "minio",
3445
+ prefix: pathParts.slice(1).join("/")
3446
+ };
3447
+ }
3448
+ const azureBlob = host.match(AZURE_BLOB_RE);
3449
+ if (azureBlob && pathParts.length > 0) {
3450
+ return {
3451
+ bucket: pathParts[0],
3452
+ region: defaults.region || "",
3453
+ endpoint: `${url.protocol}//${url.host}`,
3454
+ provider: "azure",
3455
+ prefix: pathParts.slice(1).join("/")
3456
+ };
3457
+ }
3458
+ const storjGateway = host.match(STORJ_GATEWAY_RE);
3459
+ if (storjGateway && pathParts.length > 0) {
3460
+ return {
3461
+ bucket: pathParts[0],
3462
+ region: storjGateway[1] || defaults.region || "us1",
3463
+ endpoint: `${url.protocol}//${url.host}`,
3464
+ provider: "storj",
3465
+ prefix: pathParts.slice(1).join("/")
3466
+ };
3467
+ }
3468
+ const storjLink = host.match(STORJ_LINK_RE);
3469
+ if (storjLink && pathParts.length >= 3 && (pathParts[0] === "raw" || pathParts[0] === "s")) {
3470
+ return {
3471
+ bucket: pathParts[2],
3472
+ region: storjLink[1] || defaults.region || "us1",
3473
+ endpoint: `${url.protocol}//${url.host}/${pathParts[0]}/${pathParts[1]}`,
3474
+ provider: "storj",
3475
+ prefix: pathParts.slice(3).join("/")
3476
+ };
3477
+ }
3478
+ if (STAC_API_PATH_RE.test(url.pathname)) {
3479
+ return {
3480
+ ...defaultResult(defaults),
3481
+ endpoint: `${url.protocol}//${url.host}`
3482
+ };
3483
+ }
3484
+ if (pathParts.length > 0) {
3485
+ const endpoint = `${url.protocol}//${url.host}`;
3486
+ return {
3487
+ bucket: pathParts[0],
3488
+ region: defaults.region || "us-east-1",
3489
+ endpoint,
3490
+ provider: defaults.provider || "s3",
3491
+ prefix: pathParts.slice(1).join("/")
3492
+ };
3493
+ }
3494
+ return {
3495
+ ...defaultResult(defaults),
3496
+ endpoint: `${url.protocol}//${url.host}`
3497
+ };
3498
+ } catch {
3499
+ }
3500
+ }
3501
+ const cleaned = stripEdgeSlashes2(trimmed);
3502
+ return {
3503
+ bucket: cleaned,
3504
+ region: defaults.region || "us-east-1",
3505
+ endpoint: defaults.endpoint || "",
3506
+ provider: defaults.provider || "s3",
3507
+ prefix: ""
3508
+ };
3509
+ }
3510
+ function looksLikeUrl(input) {
3511
+ const lower = input.trim().toLowerCase();
3512
+ if (lower.startsWith("http://") || lower.startsWith("https://")) return true;
3513
+ for (const scheme of Object.keys(SCHEME_MAP)) {
3514
+ if (lower.startsWith(scheme)) return true;
3515
+ }
3516
+ return false;
3517
+ }
3518
+ function describeParseResult(parsed) {
3519
+ const parts = [];
3520
+ if (parsed.bucket) parts.push(`bucket="${parsed.bucket}"`);
3521
+ if (parsed.endpoint) parts.push(`endpoint="${parsed.endpoint}"`);
3522
+ if (parsed.region && parsed.region !== "us-east-1") parts.push(`region="${parsed.region}"`);
3523
+ if (parsed.provider !== "s3") parts.push(`provider=${parsed.provider}`);
3524
+ if (parsed.prefix) parts.push(`prefix="${parsed.prefix}"`);
3525
+ return parts.length > 0 ? `Detected: ${parts.join(", ")}` : "";
3526
+ }
3527
+ function classifyUrl(input) {
3528
+ const trimmed = input.trim();
3529
+ const lower = trimmed.toLowerCase();
3530
+ for (const scheme of Object.keys(SCHEME_MAP)) {
3531
+ if (lower.startsWith(scheme)) {
3532
+ return { kind: "scheme", parsed: parseStorageUrl(trimmed) };
3533
+ }
3534
+ }
3535
+ if (lower.startsWith("http://") || lower.startsWith("https://")) {
3536
+ let url;
3537
+ try {
3538
+ url = new URL(trimmed);
3539
+ } catch {
3540
+ return {
3541
+ kind: "remote-file",
3542
+ url: new URL(`https://${trimmed.replace(/^https?:\/\//i, "")}`)
3543
+ };
3544
+ }
3545
+ const parsed = parseStorageUrl(trimmed);
3546
+ if (parsed.bucket && isKnownBucketHost(url.hostname)) {
3547
+ return { kind: "object-storage", parsed };
3548
+ }
3549
+ if (STAC_API_PATH_RE.test(url.pathname)) {
3550
+ return { kind: "stac-api", url };
3551
+ }
3552
+ return { kind: "remote-file", url };
3553
+ }
3554
+ try {
3555
+ return { kind: "remote-file", url: new URL(trimmed) };
3556
+ } catch {
3557
+ return { kind: "remote-file", url: new URL(`https://${trimmed}`) };
3558
+ }
3559
+ }
3560
+
3561
+ // src/host-detection.ts
3562
+ function extractRootPrefix(pathname) {
3563
+ let clean = pathname.replace(/\/[^/]*\.[^/]*$/, "/");
3564
+ clean = clean.replace(/^\//, "");
3565
+ if (!clean || clean === "/") return "";
3566
+ if (!clean.endsWith("/")) clean += "/";
3567
+ return clean;
3568
+ }
3569
+ function buildBucketUrl(provider, endpoint, bucket, region) {
3570
+ return buildProviderBaseUrl(
3571
+ provider === "unknown" ? "s3" : provider,
3572
+ endpoint,
3573
+ bucket,
3574
+ region || ""
3575
+ );
3576
+ }
3577
+ function parsedToHost(parsed, host, rootPrefix) {
3578
+ if (!parsed.bucket || !isKnownBucketHost(host)) return null;
3579
+ const provider = parsed.provider === "unknown" ? "s3" : parsed.provider;
3580
+ return {
3581
+ provider,
3582
+ bucket: parsed.bucket,
3583
+ region: parsed.region,
3584
+ endpoint: parsed.endpoint,
3585
+ rootPrefix,
3586
+ bucketUrl: buildBucketUrl(parsed.provider, parsed.endpoint, parsed.bucket, parsed.region)
3587
+ };
3588
+ }
3589
+ function detectHostBucket() {
3590
+ if (typeof window === "undefined") return null;
3591
+ const url = new URL(window.location.href);
3592
+ const urlParam = url.searchParams.get("url");
3593
+ if (urlParam) {
3594
+ try {
3595
+ const paramUrl = new URL(urlParam);
3596
+ const parsed2 = parseStorageUrl(urlParam);
3597
+ const host = parsedToHost(parsed2, paramUrl.hostname, "");
3598
+ if (host) return host;
3599
+ } catch {
3600
+ }
3601
+ }
3602
+ const parsed = parseStorageUrl(window.location.href);
3603
+ if (!parsed.bucket || !isKnownBucketHost(url.hostname)) return null;
3604
+ const prefixPath = parsed.prefix ? `/${parsed.prefix}` : "";
3605
+ const rootPrefix = extractRootPrefix(prefixPath || "/");
3606
+ const provider = parsed.provider === "unknown" ? "s3" : parsed.provider;
3607
+ return {
3608
+ provider,
3609
+ bucket: parsed.bucket,
3610
+ region: parsed.region,
3611
+ endpoint: parsed.endpoint,
3612
+ rootPrefix,
3613
+ bucketUrl: buildBucketUrl(parsed.provider, parsed.endpoint, parsed.bucket, parsed.region)
3614
+ };
3615
+ }
3616
+ function applyStacItemStorageHints(input, item) {
3617
+ const hints = extractStorageHints(item);
3618
+ const merged = applyStorageHintsToConnection(input, hints);
3619
+ if (undefined?.DEV && (hints.region || hints.endpoint || hints.requesterPays)) {
3620
+ console.debug("[host-detection] applied STAC storage hints", hints);
3621
+ }
3622
+ return merged;
3623
+ }
3624
+
3625
+ // src/local-storage.ts
3626
+ function loadFromStorage(key, defaultValue) {
3627
+ if (typeof window === "undefined") return defaultValue;
3628
+ try {
3629
+ const raw = localStorage.getItem(key);
3630
+ if (raw) return JSON.parse(raw);
3631
+ } catch {
3632
+ }
3633
+ return defaultValue;
3634
+ }
3635
+ function persistToStorage(key, value) {
3636
+ if (typeof window === "undefined") return;
3637
+ try {
3638
+ localStorage.setItem(key, JSON.stringify(value));
3639
+ } catch {
3640
+ }
3641
+ }
3642
+
3643
+ // src/lru.ts
3644
+ var LruCache = class {
3645
+ map = /* @__PURE__ */ new Map();
3646
+ max;
3647
+ onEvict;
3648
+ constructor(opts) {
3649
+ if (opts.max <= 0) throw new Error("LruCache: max must be > 0");
3650
+ this.max = opts.max;
3651
+ this.onEvict = opts.onEvict;
3652
+ }
3653
+ get size() {
3654
+ return this.map.size;
3655
+ }
3656
+ has(key) {
3657
+ return this.map.has(key);
3658
+ }
3659
+ get(key) {
3660
+ const v = this.map.get(key);
3661
+ if (v === void 0) return void 0;
3662
+ this.map.delete(key);
3663
+ this.map.set(key, v);
3664
+ return v;
3665
+ }
3666
+ set(key, value) {
3667
+ if (this.map.has(key)) {
3668
+ this.map.delete(key);
3669
+ }
3670
+ this.map.set(key, value);
3671
+ while (this.map.size > this.max) {
3672
+ const oldest = this.map.keys().next().value;
3673
+ if (oldest === void 0) break;
3674
+ const evicted = this.map.get(oldest);
3675
+ this.map.delete(oldest);
3676
+ this.onEvict?.(oldest, evicted);
3677
+ }
3678
+ }
3679
+ delete(key) {
3680
+ const v = this.map.get(key);
3681
+ if (v === void 0) return false;
3682
+ this.map.delete(key);
3683
+ this.onEvict?.(key, v);
3684
+ return true;
3685
+ }
3686
+ clear() {
3687
+ if (this.onEvict) {
3688
+ for (const [k, v] of this.map) this.onEvict(k, v);
3689
+ }
3690
+ this.map.clear();
3691
+ }
3692
+ };
3693
+
3694
+ // src/map-pixel-inspect.ts
3695
+ function attachPixelInspector(map, { probe, onStart, onResult }) {
3696
+ let active = null;
3697
+ let detached = false;
3698
+ const handler = (event) => {
3699
+ if (detached) return;
3700
+ if (active) active.abort();
3701
+ const ctrl = new AbortController();
3702
+ active = ctrl;
3703
+ onStart();
3704
+ void (async () => {
3705
+ let payload = null;
3706
+ try {
3707
+ payload = await probe({
3708
+ lng: event.lngLat.lng,
3709
+ lat: event.lngLat.lat,
3710
+ signal: ctrl.signal
3711
+ });
3712
+ } catch (err) {
3713
+ if (isHelperAbort(err, ctrl.signal)) return;
3714
+ payload = null;
3715
+ }
3716
+ if (ctrl.signal.aborted && active === ctrl) {
3717
+ return;
3718
+ }
3719
+ if (active === ctrl) active = null;
3720
+ onResult(payload);
3721
+ })();
3722
+ };
3723
+ map.on("click", handler);
3724
+ return function detach() {
3725
+ if (detached) return;
3726
+ detached = true;
3727
+ map.off("click", handler);
3728
+ if (active) {
3729
+ active.abort();
3730
+ active = null;
3731
+ }
3732
+ };
3733
+ }
3734
+ function isHelperAbort(err, signal) {
3735
+ if (!signal.aborted) return false;
3736
+ if (err instanceof DOMException && err.name === "AbortError") return true;
3737
+ if (err && typeof err === "object" && err.name === "AbortError") {
3738
+ return true;
3739
+ }
3740
+ return false;
3741
+ }
3742
+
3743
+ // src/markdown-sql.ts
3744
+ async function parseMarkdownDocument(markdown) {
3745
+ let frontmatter = {};
3746
+ let content = markdown;
3747
+ const fmMatch = markdown.match(/^---\n([\s\S]*?)\n---\n/);
3748
+ if (fmMatch) {
3749
+ try {
3750
+ const { default: YAML } = await import('yaml');
3751
+ frontmatter = YAML.parse(fmMatch[1]) || {};
3752
+ } catch {
3753
+ }
3754
+ content = markdown.slice(fmMatch[0].length);
3755
+ }
3756
+ const sqlBlocks = [];
3757
+ const lines = content.split("\n");
3758
+ let i = 0;
3759
+ while (i < lines.length) {
3760
+ const line = lines[i];
3761
+ const match = line.match(/^```sql\s+(\w[\w-]*)\s*$/);
3762
+ if (match) {
3763
+ const name = match[1];
3764
+ const startLine = i;
3765
+ const sqlLines = [];
3766
+ i++;
3767
+ while (i < lines.length && lines[i] !== "```") {
3768
+ sqlLines.push(lines[i]);
3769
+ i++;
3770
+ }
3771
+ sqlBlocks.push({
3772
+ name,
3773
+ sql: sqlLines.join("\n"),
3774
+ startLine,
3775
+ endLine: i
3776
+ });
3777
+ }
3778
+ i++;
3779
+ }
3780
+ return { frontmatter, content, sqlBlocks };
3781
+ }
3782
+ function interpolateTemplates(text, queryResults) {
3783
+ return text.replace(/\{(\w+)\.rows\[(\d+)\]\.(\w+)\}/g, (match, queryName, rowIdx, colName) => {
3784
+ const rows = queryResults.get(queryName);
3785
+ if (!rows) return match;
3786
+ const row = rows[parseInt(rowIdx, 10)];
3787
+ if (!row) return match;
3788
+ const value = row[colName];
3789
+ return value !== void 0 ? String(value) : match;
3790
+ });
3791
+ }
3792
+ function markSqlBlocks(content) {
3793
+ return content.replace(
3794
+ /```sql[ \t]+(\w[\w-]*)[ \t]*\n([\s\S]*?)```/g,
3795
+ (_, name) => `<div data-sql-block="${name}"></div>`
3796
+ );
3797
+ }
3798
+
3799
+ // src/markdown-sql-context.ts
3800
+ var MarkdownSqlContext = class {
3801
+ engine;
3802
+ connId;
3803
+ prefix;
3804
+ results = /* @__PURE__ */ new Map();
3805
+ constructor(engine, connId, prefix = "") {
3806
+ this.engine = engine;
3807
+ this.connId = connId;
3808
+ this.prefix = prefix;
3809
+ }
3810
+ /** Execute a SQL query and store the result under the given name. */
3811
+ async executeSql(sql, queryName) {
3812
+ const transformedSql = this.transformPaths(sql);
3813
+ const result = await this.engine.query(this.connId, transformedSql);
3814
+ const rows = result.rows ?? [];
3815
+ this.results.set(queryName, { result, rows });
3816
+ return rows;
3817
+ }
3818
+ /**
3819
+ * Transform relative file paths in SQL to full S3 URLs.
3820
+ * e.g. read_parquet('data.parquet') becomes read_parquet('s3://bucket/prefix/data.parquet').
3821
+ */
3822
+ transformPaths(sql) {
3823
+ if (!this.connId || !this.prefix) return sql;
3824
+ return sql.replace(/(read_(?:parquet|csv|json|csv_auto))\('([^']+)'\)/g, (match, fn, path) => {
3825
+ if (path.startsWith("s3://") || path.startsWith("http") || path.startsWith("/")) {
3826
+ return match;
3827
+ }
3828
+ const fullPath = `s3://${this.prefix}/${path}`;
3829
+ return `${fn}('${fullPath}')`;
3830
+ });
3831
+ }
3832
+ getResult(queryName) {
3833
+ return this.results.get(queryName);
3834
+ }
3835
+ getAllResults() {
3836
+ const map = /* @__PURE__ */ new Map();
3837
+ for (const [name, { rows }] of this.results) {
3838
+ map.set(name, rows);
3839
+ }
3840
+ return map;
3841
+ }
3842
+ getColumns(queryName) {
3843
+ const entry = this.results.get(queryName);
3844
+ if (!entry) return [];
3845
+ return entry.result.columns;
3846
+ }
3847
+ };
3848
+
3849
+ // src/notebook.ts
3850
+ var PREFIX = "nb-";
3851
+ function el(tag, classNames = []) {
3852
+ const e = document.createElement(tag);
3853
+ if (classNames.length) e.className = classNames.map((c) => PREFIX + c).join(" ");
3854
+ return e;
3855
+ }
3856
+ function escapeHTML(raw) {
3857
+ return raw.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3858
+ }
3859
+ function joinText(text) {
3860
+ if (Array.isArray(text)) return text.join("");
3861
+ return text ?? "";
3862
+ }
3863
+ var IMAGE_FORMATS = ["image/png", "image/jpeg", "image/gif", "image/webp"];
3864
+ function renderImage(format, data) {
3865
+ const img = el("img", ["image-output"]);
3866
+ img.src = `data:${format};base64,${joinText(data).replace(/\n/g, "")}`;
3867
+ return img;
3868
+ }
3869
+ function renderDisplayData(output, config) {
3870
+ const getData = (mime) => output.data?.[mime] ?? output[mime];
3871
+ for (const fmt of IMAGE_FORMATS) {
3872
+ const d = getData(fmt);
3873
+ if (d) return renderImage(fmt, d);
3874
+ }
3875
+ const svg = getData("image/svg+xml") ?? getData("text/svg+xml");
3876
+ if (svg) {
3877
+ const wrapper = el("div", ["svg-output"]);
3878
+ wrapper.innerHTML = joinText(svg);
3879
+ return wrapper;
3880
+ }
3881
+ const html = getData("text/html");
3882
+ if (html) {
3883
+ const wrapper = el("div", ["html-output"]);
3884
+ wrapper.innerHTML = joinText(html);
3885
+ return wrapper;
3886
+ }
3887
+ const md = getData("text/markdown");
3888
+ if (md) {
3889
+ const wrapper = el("div", ["html-output"]);
3890
+ wrapper.innerHTML = config.markdown(joinText(md));
3891
+ return wrapper;
3892
+ }
3893
+ const latex = getData("text/latex");
3894
+ if (latex) {
3895
+ const wrapper = el("div", ["latex-output"]);
3896
+ wrapper.textContent = joinText(latex);
3897
+ return wrapper;
3898
+ }
3899
+ const plain = getData("text/plain");
3900
+ if (plain) {
3901
+ const pre = el("pre", ["text-output"]);
3902
+ pre.innerHTML = config.ansi(escapeHTML(joinText(plain)));
3903
+ return pre;
3904
+ }
3905
+ return el("div", ["empty-output"]);
3906
+ }
3907
+ function renderStream(output, config) {
3908
+ const streamName = output.stream ?? output.name ?? "stdout";
3909
+ const pre = el("pre", [streamName]);
3910
+ pre.innerHTML = config.ansi(escapeHTML(joinText(output.text ?? "")));
3911
+ return pre;
3912
+ }
3913
+ function renderError(output, config) {
3914
+ const pre = el("pre", ["pyerr"]);
3915
+ const raw = (output.traceback ?? []).join("\n");
3916
+ pre.innerHTML = config.ansi(escapeHTML(raw));
3917
+ return pre;
3918
+ }
3919
+ function coalesceStreams(outputs) {
3920
+ if (!outputs.length) return outputs;
3921
+ const result = [{ ...outputs[0] }];
3922
+ for (let i = 1; i < outputs.length; i++) {
3923
+ const o = outputs[i];
3924
+ const last = result[result.length - 1];
3925
+ if (o.output_type === "stream" && last.output_type === "stream" && (o.stream ?? o.name) === (last.stream ?? last.name)) {
3926
+ const lastText = Array.isArray(last.text) ? last.text : [last.text ?? ""];
3927
+ const oText = Array.isArray(o.text) ? o.text : [o.text ?? ""];
3928
+ last.text = lastText.concat(oText);
3929
+ } else {
3930
+ result.push({ ...o });
3931
+ }
3932
+ }
3933
+ return result;
3934
+ }
3935
+ function renderCodeCell(cell, lang, config) {
3936
+ const cellEl = el("div", ["cell", "code-cell"]);
3937
+ const source = joinText(cell.source ?? cell.input ?? "");
3938
+ if (source) {
3939
+ const holder = el("div", ["input"]);
3940
+ const num = cell.prompt_number ?? cell.execution_count;
3941
+ if (typeof num === "number" && num > -1) {
3942
+ holder.setAttribute("data-prompt-number", String(num));
3943
+ }
3944
+ const pre = el("pre");
3945
+ const code = document.createElement("code");
3946
+ code.setAttribute("data-language", lang);
3947
+ code.className = `lang-${lang}`;
3948
+ code.innerHTML = config.highlighter(escapeHTML(source), lang);
3949
+ pre.appendChild(code);
3950
+ holder.appendChild(pre);
3951
+ cellEl.appendChild(holder);
3952
+ }
3953
+ const rawOutputs = coalesceStreams(cell.outputs ?? []);
3954
+ for (const output of rawOutputs) {
3955
+ const outer = el("div", ["output"]);
3956
+ let inner;
3957
+ switch (output.output_type) {
3958
+ case "display_data":
3959
+ case "execute_result":
3960
+ case "pyout":
3961
+ inner = renderDisplayData(output, config);
3962
+ break;
3963
+ case "stream":
3964
+ inner = renderStream(output, config);
3965
+ break;
3966
+ case "error":
3967
+ case "pyerr":
3968
+ inner = renderError(output, config);
3969
+ break;
3970
+ default:
3971
+ inner = el("div", ["empty-output"]);
3972
+ }
3973
+ outer.appendChild(inner);
3974
+ cellEl.appendChild(outer);
3975
+ }
3976
+ return cellEl;
3977
+ }
3978
+ function renderMarkdownCell(cell, config) {
3979
+ const cellEl = el("div", ["cell", "markdown-cell"]);
3980
+ const source = joinText(cell.source ?? cell.input ?? "");
3981
+ cellEl.innerHTML = config.markdown(source);
3982
+ return cellEl;
3983
+ }
3984
+ function renderHeadingCell(cell) {
3985
+ const level = Math.min(Math.max(cell.level ?? 1, 1), 6);
3986
+ const heading = el(`h${level}`, ["cell", "heading-cell"]);
3987
+ heading.innerHTML = escapeHTML(joinText(cell.source ?? cell.input ?? ""));
3988
+ return heading;
3989
+ }
3990
+ function renderRawCell(cell) {
3991
+ const cellEl = el("div", ["cell", "raw-cell"]);
3992
+ cellEl.innerHTML = escapeHTML(joinText(cell.source ?? cell.input ?? ""));
3993
+ return cellEl;
3994
+ }
3995
+ function renderNotebook(raw, config) {
3996
+ const meta = raw.metadata ?? {};
3997
+ const lang = meta.kernelspec?.language ?? meta.language_info?.name ?? meta.language ?? "python";
3998
+ const kernelName = meta.kernelspec?.display_name ?? meta.language_info?.name ?? "";
3999
+ let allCells = [];
4000
+ if (Array.isArray(raw.cells)) {
4001
+ allCells = raw.cells;
4002
+ } else if (Array.isArray(raw.worksheets)) {
4003
+ for (const ws of raw.worksheets) {
4004
+ if (Array.isArray(ws.cells)) allCells.push(...ws.cells);
4005
+ }
4006
+ }
4007
+ const notebook = el("div", ["notebook"]);
4008
+ for (const cell of allCells) {
4009
+ let rendered;
4010
+ switch (cell.cell_type) {
4011
+ case "code":
4012
+ rendered = renderCodeCell(cell, cell.language ?? lang, config);
4013
+ break;
4014
+ case "markdown":
4015
+ rendered = renderMarkdownCell(cell, config);
4016
+ break;
4017
+ case "heading":
4018
+ rendered = renderHeadingCell(cell);
4019
+ break;
4020
+ case "raw":
4021
+ rendered = renderRawCell(cell);
4022
+ break;
4023
+ default:
4024
+ rendered = renderRawCell(cell);
4025
+ }
4026
+ notebook.appendChild(rendered);
4027
+ }
4028
+ return {
4029
+ element: notebook,
4030
+ meta: { kernelName, language: lang, cellCount: allCells.length }
4031
+ };
4032
+ }
4033
+
4034
+ // src/parquet-metadata.ts
4035
+ function mapParquetType(col) {
4036
+ const lt = col.logical_type;
4037
+ if (lt) {
4038
+ if (lt.type === "GEOMETRY" || lt.type === "GEOGRAPHY") return "GEOMETRY";
4039
+ if (lt.type === "STRING" || lt.type === "UTF8") return "VARCHAR";
4040
+ if (lt.type === "JSON") return "JSON";
4041
+ if (lt.type === "UUID") return "UUID";
4042
+ if (lt.type === "ENUM") return "VARCHAR";
4043
+ if (lt.type === "INT" || lt.type === "INTEGER") {
4044
+ const bits = lt.bitWidth ?? 32;
4045
+ const signed = lt.isSigned !== false;
4046
+ if (bits <= 8) return signed ? "TINYINT" : "UTINYINT";
4047
+ if (bits <= 16) return signed ? "SMALLINT" : "USMALLINT";
4048
+ if (bits <= 32) return signed ? "INTEGER" : "UINTEGER";
4049
+ return signed ? "BIGINT" : "UBIGINT";
4050
+ }
4051
+ if (lt.type === "DECIMAL") return `DECIMAL(${lt.precision ?? 18},${lt.scale ?? 0})`;
4052
+ if (lt.type === "DATE") return "DATE";
4053
+ if (lt.type === "TIME") return "TIME";
4054
+ if (lt.type === "TIMESTAMP") return "TIMESTAMP";
4055
+ if (lt.type === "BSON") return "BLOB";
4056
+ }
4057
+ const ct = col.converted_type;
4058
+ if (ct === "UTF8") return "VARCHAR";
4059
+ if (ct === "JSON") return "JSON";
4060
+ if (ct === "DATE") return "DATE";
4061
+ if (ct === "TIMESTAMP_MILLIS" || ct === "TIMESTAMP_MICROS") return "TIMESTAMP";
4062
+ if (ct === "DECIMAL") return `DECIMAL(${col.precision ?? 18},${col.scale ?? 0})`;
4063
+ if (ct === "INT_8") return "TINYINT";
4064
+ if (ct === "INT_16") return "SMALLINT";
4065
+ if (ct === "INT_32") return "INTEGER";
4066
+ if (ct === "INT_64") return "BIGINT";
4067
+ if (ct === "UINT_8") return "UTINYINT";
4068
+ if (ct === "UINT_16") return "USMALLINT";
4069
+ if (ct === "UINT_32") return "UINTEGER";
4070
+ if (ct === "UINT_64") return "UBIGINT";
4071
+ const pt = col.type;
4072
+ if (pt === "BOOLEAN") return "BOOLEAN";
4073
+ if (pt === "INT32") return "INTEGER";
4074
+ if (pt === "INT64") return "BIGINT";
4075
+ if (pt === "INT96") return "TIMESTAMP";
4076
+ if (pt === "FLOAT") return "FLOAT";
4077
+ if (pt === "DOUBLE") return "DOUBLE";
4078
+ if (pt === "BYTE_ARRAY") return "BLOB";
4079
+ if (pt === "FIXED_LEN_BYTE_ARRAY") return "BLOB";
4080
+ return "VARCHAR";
4081
+ }
4082
+ async function readParquetMetadata(url) {
4083
+ const { parquetMetadataAsync, asyncBufferFromUrl } = await import('hyparquet');
4084
+ const file = await asyncBufferFromUrl({ url });
4085
+ const metadata = await parquetMetadataAsync(file);
4086
+ const rowCount = metadata.row_groups.reduce(
4087
+ (sum, rg) => sum + Number(rg.num_rows),
4088
+ 0
4089
+ );
4090
+ const schema = metadata.schema.slice(1).filter((col) => col.num_children === void 0).map((col) => ({
4091
+ name: col.name,
4092
+ type: mapParquetType(col)
4093
+ }));
4094
+ const topLevelColumns = [];
4095
+ const root = metadata.schema[0];
4096
+ const rootChildren = root?.num_children ?? 0;
4097
+ let cursor = 1;
4098
+ for (let i = 0; i < rootChildren && cursor < metadata.schema.length; i++) {
4099
+ const el2 = metadata.schema[cursor];
4100
+ if (el2?.name) topLevelColumns.push(el2.name);
4101
+ cursor += countSubtree(metadata.schema, cursor);
4102
+ }
4103
+ let geo = null;
4104
+ let legacyGeoParquet = false;
4105
+ const geoKv = metadata.key_value_metadata?.find((kv) => kv.key === "geo");
4106
+ if (geoKv) {
4107
+ try {
4108
+ const geoJson = JSON.parse(geoKv.value ?? "");
4109
+ if (geoJson.schema_version && !geoJson.version) {
4110
+ legacyGeoParquet = true;
4111
+ }
4112
+ geo = {
4113
+ primaryColumn: geoJson.primary_column ?? "geometry",
4114
+ columns: {}
4115
+ };
4116
+ if (geoJson.columns) {
4117
+ for (const [colName, colMeta] of Object.entries(geoJson.columns)) {
4118
+ geo.columns[colName] = {
4119
+ encoding: colMeta.encoding ?? "WKB",
4120
+ geometryTypes: colMeta.geometry_types ?? [],
4121
+ crs: colMeta.crs ?? null,
4122
+ bbox: colMeta.bbox
4123
+ };
4124
+ }
4125
+ }
4126
+ } catch {
4127
+ }
4128
+ }
4129
+ const createdBy = metadata.created_by ?? null;
4130
+ const numRowGroups = metadata.row_groups.length;
4131
+ let compression = null;
4132
+ if (numRowGroups > 0 && metadata.row_groups[0].columns) {
4133
+ const codecs = /* @__PURE__ */ new Set();
4134
+ for (const col of metadata.row_groups[0].columns) {
4135
+ const codec = col.meta_data?.codec;
4136
+ if (codec) codecs.add(codec);
4137
+ }
4138
+ if (codecs.size === 1) {
4139
+ compression = [...codecs][0];
4140
+ } else if (codecs.size > 1) {
4141
+ compression = [...codecs].join(", ");
4142
+ }
4143
+ }
4144
+ return {
4145
+ rowCount,
4146
+ schema,
4147
+ topLevelColumns,
4148
+ geo,
4149
+ legacyGeoParquet,
4150
+ createdBy,
4151
+ numRowGroups,
4152
+ compression
4153
+ };
4154
+ }
4155
+ function countSubtree(schema, start) {
4156
+ const el2 = schema[start];
4157
+ if (!el2) return 0;
4158
+ const n = el2.num_children ?? 0;
4159
+ let cursor = start + 1;
4160
+ for (let i = 0; i < n; i++) {
4161
+ cursor += countSubtree(schema, cursor);
4162
+ }
4163
+ return cursor - start;
4164
+ }
4165
+ function extractEpsgFromGeoMeta(geo) {
4166
+ const primaryCol = geo.columns[geo.primaryColumn];
4167
+ if (!primaryCol?.crs) return null;
4168
+ const crs = primaryCol.crs;
4169
+ if (crs.type === "name" && crs.properties?.name?.includes("CRS84")) return null;
4170
+ if (crs.id?.authority === "EPSG") {
4171
+ const code = crs.id.code;
4172
+ if (WGS84_CODES.has(code)) return null;
4173
+ return `EPSG:${code}`;
4174
+ }
4175
+ return null;
4176
+ }
4177
+ function extractGeometryTypes(geo) {
4178
+ const primaryCol = geo.columns[geo.primaryColumn];
4179
+ if (!primaryCol?.geometryTypes?.length) return [];
4180
+ const typeMap = {
4181
+ Point: "point",
4182
+ LineString: "linestring",
4183
+ Polygon: "polygon",
4184
+ MultiPoint: "multipoint",
4185
+ MultiLineString: "multilinestring",
4186
+ MultiPolygon: "multipolygon"
4187
+ };
4188
+ const types = [];
4189
+ for (const raw of primaryCol.geometryTypes) {
4190
+ const base = raw.split(" ")[0];
4191
+ const mapped = typeMap[base];
4192
+ if (mapped && !types.includes(mapped)) types.push(mapped);
4193
+ }
4194
+ return types;
4195
+ }
4196
+ function extractBounds(geo) {
4197
+ const primaryCol = geo.columns[geo.primaryColumn];
4198
+ if (!primaryCol?.bbox || primaryCol.bbox.length < 4) return null;
4199
+ return [primaryCol.bbox[0], primaryCol.bbox[1], primaryCol.bbox[2], primaryCol.bbox[3]];
4200
+ }
4201
+
4202
+ // src/stac-facets.ts
4203
+ var THUMBNAIL_ROLES = ["thumbnail", "overview", "visual"];
4204
+ var THUMBNAIL_KEYS = ["thumbnail", "preview", "overview", "rendered_preview", "visual"];
4205
+ var BROWSER_IMAGE_MIME_RE = /^image\/(png|jpe?g|webp|gif|avif|svg\+xml|svg)\b/i;
4206
+ function isBrowserRenderableAsset(asset) {
4207
+ if (!asset.type) return true;
4208
+ return BROWSER_IMAGE_MIME_RE.test(asset.type);
4209
+ }
4210
+ function extractThumbnailHref(item) {
4211
+ const assets = item.assets ?? {};
4212
+ for (const role of THUMBNAIL_ROLES) {
4213
+ for (const asset of Object.values(assets)) {
4214
+ if (Array.isArray(asset?.roles) && asset.roles.includes(role) && asset.href && isBrowserRenderableAsset(asset)) {
4215
+ return asset.href;
4216
+ }
4217
+ }
4218
+ }
4219
+ for (const key of THUMBNAIL_KEYS) {
4220
+ const asset = assets[key];
4221
+ if (asset?.href && isBrowserRenderableAsset(asset)) return asset.href;
4222
+ }
4223
+ return null;
4224
+ }
4225
+ function collectAssetRoles(item) {
4226
+ const out = /* @__PURE__ */ new Set();
4227
+ for (const asset of Object.values(item.assets ?? {})) {
4228
+ if (Array.isArray(asset?.roles)) {
4229
+ for (const role of asset.roles) {
4230
+ if (typeof role === "string") out.add(role);
4231
+ }
4232
+ }
4233
+ }
4234
+ return [...out].sort();
4235
+ }
4236
+ function readNumber(props, key) {
4237
+ const v = props[key];
4238
+ if (typeof v === "number" && Number.isFinite(v)) return v;
4239
+ if (typeof v === "string") {
4240
+ const n = Number(v);
4241
+ return Number.isFinite(n) ? n : null;
4242
+ }
4243
+ return null;
4244
+ }
4245
+ function readString(props, key) {
4246
+ const v = props[key];
4247
+ return typeof v === "string" && v.length > 0 ? v : null;
4248
+ }
4249
+ function readStringArray(props, key) {
4250
+ const v = props[key];
4251
+ if (!Array.isArray(v)) return [];
4252
+ return v.filter((x) => typeof x === "string");
4253
+ }
4254
+ function extractItemView(item) {
4255
+ const props = item.properties ?? {};
4256
+ const bbox = Array.isArray(item.bbox) && item.bbox.length >= 4 ? [
4257
+ Number(item.bbox[0]),
4258
+ Number(item.bbox[1]),
4259
+ Number(item.bbox[2]),
4260
+ Number(item.bbox[3])
4261
+ ] : null;
4262
+ return {
4263
+ id: String(item.id ?? ""),
4264
+ collection: typeof item.collection === "string" ? item.collection : null,
4265
+ bbox,
4266
+ datetime: readString(props, "datetime") ?? readString(props, "start_datetime"),
4267
+ endDatetime: readString(props, "end_datetime"),
4268
+ cloudCover: readNumber(props, "eo:cloud_cover"),
4269
+ gsd: readNumber(props, "gsd"),
4270
+ platform: readString(props, "platform"),
4271
+ constellation: readString(props, "constellation"),
4272
+ instruments: readStringArray(props, "instruments"),
4273
+ epsg: readNumber(props, "proj:epsg"),
4274
+ thumbnailHref: extractThumbnailHref(item),
4275
+ assetRoles: collectAssetRoles(item),
4276
+ raw: item
4277
+ };
4278
+ }
4279
+ var DATETIME_HISTOGRAM_BINS_MAX = 64;
4280
+ var DATETIME_HISTOGRAM_BINS = 32;
4281
+ var MS_PER_DAY = 864e5;
4282
+ var MS_PER_YEAR = MS_PER_DAY * 365.25;
4283
+ var NUMERIC_FIELDS = ["cloudCover", "gsd"];
4284
+ var ENUM_FIELDS = [
4285
+ "collection",
4286
+ "platform",
4287
+ "constellation",
4288
+ "instruments",
4289
+ "assetRoles"
4290
+ ];
4291
+ function buildNumericFacet(views, field) {
4292
+ let min = Number.POSITIVE_INFINITY;
4293
+ let max = Number.NEGATIVE_INFINITY;
4294
+ let count = 0;
4295
+ for (const v of views) {
4296
+ const value = v[field];
4297
+ if (value == null || !Number.isFinite(value)) continue;
4298
+ if (value < min) min = value;
4299
+ if (value > max) max = value;
4300
+ count++;
4301
+ }
4302
+ if (count === 0 || min === max) return null;
4303
+ return { kind: "numeric", field, min, max, count };
4304
+ }
4305
+ function buildEnumFacet(views, field) {
4306
+ const counts = /* @__PURE__ */ new Map();
4307
+ for (const v of views) {
4308
+ const raw = v[field];
4309
+ if (raw == null) continue;
4310
+ if (Array.isArray(raw)) {
4311
+ for (const item of raw) counts.set(item, (counts.get(item) ?? 0) + 1);
4312
+ } else if (typeof raw === "string" && raw.length > 0) {
4313
+ counts.set(raw, (counts.get(raw) ?? 0) + 1);
4314
+ }
4315
+ }
4316
+ if (counts.size < 2) return null;
4317
+ const values = [...counts.entries()].map(([value, count]) => ({ value, count })).sort((a, b) => b.count - a.count || a.value.localeCompare(b.value));
4318
+ return { kind: "enum", field, values };
4319
+ }
4320
+ function pickGranularity(spanMs) {
4321
+ if (spanMs <= 90 * MS_PER_DAY) return "day";
4322
+ if (spanMs <= 2 * MS_PER_YEAR) return "week";
4323
+ if (spanMs <= 20 * MS_PER_YEAR) return "month";
4324
+ return "year";
4325
+ }
4326
+ function floorUtcDay(t) {
4327
+ const d = new Date(t);
4328
+ return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
4329
+ }
4330
+ function floorIsoWeek(t) {
4331
+ const dayStart = floorUtcDay(t);
4332
+ const dow = new Date(dayStart).getUTCDay();
4333
+ const offsetDays = (dow + 6) % 7;
4334
+ return dayStart - offsetDays * MS_PER_DAY;
4335
+ }
4336
+ function floorUtcMonth(t) {
4337
+ const d = new Date(t);
4338
+ return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1);
4339
+ }
4340
+ function floorUtcYear(t) {
4341
+ const d = new Date(t);
4342
+ return Date.UTC(d.getUTCFullYear(), 0, 1);
4343
+ }
4344
+ function floorBin(t, g) {
4345
+ switch (g) {
4346
+ case "day":
4347
+ return floorUtcDay(t);
4348
+ case "week":
4349
+ return floorIsoWeek(t);
4350
+ case "month":
4351
+ return floorUtcMonth(t);
4352
+ case "year":
4353
+ return floorUtcYear(t);
4354
+ }
4355
+ }
4356
+ function nextBin(t, g) {
4357
+ const d = new Date(t);
4358
+ switch (g) {
4359
+ case "day":
4360
+ return t + MS_PER_DAY;
4361
+ case "week":
4362
+ return t + 7 * MS_PER_DAY;
4363
+ case "month":
4364
+ return Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + 1, 1);
4365
+ case "year":
4366
+ return Date.UTC(d.getUTCFullYear() + 1, 0, 1);
4367
+ }
4368
+ }
4369
+ function buildDatetimeFacet(views) {
4370
+ const timestamps = [];
4371
+ for (const v of views) {
4372
+ if (!v.datetime) continue;
4373
+ const t = Date.parse(v.datetime);
4374
+ if (Number.isFinite(t)) timestamps.push(t);
4375
+ }
4376
+ if (timestamps.length < 2) return null;
4377
+ let min = timestamps[0];
4378
+ let max = timestamps[0];
4379
+ for (const t of timestamps) {
4380
+ if (t < min) min = t;
4381
+ if (t > max) max = t;
4382
+ }
4383
+ if (min === max) return null;
4384
+ let granularity = pickGranularity(max - min);
4385
+ let binEdges = computeBinEdges(min, max, granularity);
4386
+ while (binEdges.length > DATETIME_HISTOGRAM_BINS_MAX && granularity !== "year") {
4387
+ granularity = coarsenGranularity(granularity);
4388
+ binEdges = computeBinEdges(min, max, granularity);
4389
+ }
4390
+ const bins = new Array(binEdges.length).fill(0);
4391
+ for (const t of timestamps) {
4392
+ const idx = findBinIndex(binEdges, t);
4393
+ bins[idx]++;
4394
+ }
4395
+ return {
4396
+ kind: "datetime",
4397
+ field: "datetime",
4398
+ min: new Date(min).toISOString(),
4399
+ max: new Date(max).toISOString(),
4400
+ count: timestamps.length,
4401
+ bins,
4402
+ granularity,
4403
+ binEdges
4404
+ };
4405
+ }
4406
+ function coarsenGranularity(g) {
4407
+ switch (g) {
4408
+ case "day":
4409
+ return "week";
4410
+ case "week":
4411
+ return "month";
4412
+ case "month":
4413
+ case "year":
4414
+ return "year";
4415
+ }
4416
+ }
4417
+ function computeBinEdges(min, max, g) {
4418
+ const edges = [];
4419
+ let cursor = floorBin(min, g);
4420
+ const stop = max;
4421
+ for (let i = 0; i < DATETIME_HISTOGRAM_BINS_MAX * 4 && cursor <= stop; i++) {
4422
+ edges.push(cursor);
4423
+ cursor = nextBin(cursor, g);
4424
+ }
4425
+ return edges;
4426
+ }
4427
+ function findBinIndex(edges, t) {
4428
+ if (t <= edges[0]) return 0;
4429
+ if (t >= edges[edges.length - 1]) return edges.length - 1;
4430
+ let lo = 0;
4431
+ let hi = edges.length - 1;
4432
+ while (lo < hi) {
4433
+ const mid = lo + hi + 1 >>> 1;
4434
+ if (edges[mid] <= t) lo = mid;
4435
+ else hi = mid - 1;
4436
+ }
4437
+ return lo;
4438
+ }
4439
+ function buildFacets(views) {
4440
+ const numeric = [];
4441
+ for (const field of NUMERIC_FIELDS) {
4442
+ const f = buildNumericFacet(views, field);
4443
+ if (f) numeric.push(f);
4444
+ }
4445
+ const enums = [];
4446
+ for (const field of ENUM_FIELDS) {
4447
+ const f = buildEnumFacet(views, field);
4448
+ if (f) enums.push(f);
4449
+ }
4450
+ return {
4451
+ datetime: buildDatetimeFacet(views),
4452
+ numeric,
4453
+ enums,
4454
+ total: views.length
4455
+ };
4456
+ }
4457
+ function inNumericRange(value, range) {
4458
+ if (value == null || !Number.isFinite(value)) return false;
4459
+ if (range.min != null && value < range.min) return false;
4460
+ if (range.max != null && value > range.max) return false;
4461
+ return true;
4462
+ }
4463
+ function inEnumSet(value, allow) {
4464
+ if (allow.length === 0) return true;
4465
+ if (value == null) return false;
4466
+ if (Array.isArray(value)) return value.some((v) => allow.includes(v));
4467
+ return allow.includes(value);
4468
+ }
4469
+ function inDatetimeRange(value, range) {
4470
+ if (!value) return false;
4471
+ const t = Date.parse(value);
4472
+ if (!Number.isFinite(t)) return false;
4473
+ if (range.min) {
4474
+ const m = Date.parse(range.min);
4475
+ if (Number.isFinite(m) && t < m) return false;
4476
+ }
4477
+ if (range.max) {
4478
+ const m = Date.parse(range.max);
4479
+ if (Number.isFinite(m) && t > m) return false;
4480
+ }
4481
+ return true;
4482
+ }
4483
+ function applyFacets(views, state) {
4484
+ if (!state) return views;
4485
+ const dt = state.datetime;
4486
+ const num = state.numeric;
4487
+ const enums = state.enums;
4488
+ const hasDatetime = dt && (dt.min || dt.max);
4489
+ const hasNumeric = num && Object.values(num).some((r) => r && (r.min != null || r.max != null));
4490
+ const hasEnums = enums && Object.values(enums).some((a) => Array.isArray(a) && a.length > 0);
4491
+ if (!hasDatetime && !hasNumeric && !hasEnums) return views;
4492
+ return views.filter((view) => {
4493
+ if (hasDatetime && dt && !inDatetimeRange(view.datetime, dt)) return false;
4494
+ if (hasNumeric && num) {
4495
+ for (const field of NUMERIC_FIELDS) {
4496
+ const range = num[field];
4497
+ if (!range || range.min == null && range.max == null) continue;
4498
+ if (!inNumericRange(view[field], range)) return false;
4499
+ }
4500
+ }
4501
+ if (hasEnums && enums) {
4502
+ for (const field of ENUM_FIELDS) {
4503
+ const allow = enums[field];
4504
+ if (!Array.isArray(allow) || allow.length === 0) continue;
4505
+ if (!inEnumSet(view[field], allow)) return false;
4506
+ }
4507
+ }
4508
+ return true;
4509
+ });
4510
+ }
4511
+ function sortViews(views, sort) {
4512
+ const arr = views.slice();
4513
+ const cmp = getComparator(sort);
4514
+ arr.sort(cmp);
4515
+ return arr;
4516
+ }
4517
+ function getComparator(sort) {
4518
+ switch (sort) {
4519
+ case "datetime-desc":
4520
+ return (a, b) => compareNullableNumber(parseTime(b.datetime), parseTime(a.datetime));
4521
+ case "datetime-asc":
4522
+ return (a, b) => compareNullableNumber(parseTime(a.datetime), parseTime(b.datetime));
4523
+ case "cloud-asc":
4524
+ return (a, b) => compareNullableNumber(a.cloudCover, b.cloudCover);
4525
+ case "cloud-desc":
4526
+ return (a, b) => compareNullableNumber(b.cloudCover, a.cloudCover);
4527
+ case "gsd-asc":
4528
+ return (a, b) => compareNullableNumber(a.gsd, b.gsd);
4529
+ case "gsd-desc":
4530
+ return (a, b) => compareNullableNumber(b.gsd, a.gsd);
4531
+ case "id-asc":
4532
+ return (a, b) => a.id.localeCompare(b.id);
4533
+ }
4534
+ }
4535
+ function parseTime(s) {
4536
+ if (!s) return null;
4537
+ const t = Date.parse(s);
4538
+ return Number.isFinite(t) ? t : null;
4539
+ }
4540
+ function compareNullableNumber(a, b) {
4541
+ const aMissing = a == null || !Number.isFinite(a);
4542
+ const bMissing = b == null || !Number.isFinite(b);
4543
+ if (aMissing && bMissing) return 0;
4544
+ if (aMissing) return 1;
4545
+ if (bMissing) return -1;
4546
+ return a - b;
4547
+ }
4548
+ function hasActiveFilters(state) {
4549
+ if (!state) return false;
4550
+ if (state.datetime && (state.datetime.min || state.datetime.max)) return true;
4551
+ if (state.numeric) {
4552
+ for (const r of Object.values(state.numeric)) {
4553
+ if (r && (r.min != null || r.max != null)) return true;
4554
+ }
4555
+ }
4556
+ if (state.enums) {
4557
+ for (const a of Object.values(state.enums)) {
4558
+ if (Array.isArray(a) && a.length > 0) return true;
4559
+ }
4560
+ }
4561
+ return false;
4562
+ }
4563
+ function emptyFacetState() {
4564
+ return {};
4565
+ }
4566
+
4567
+ // src/stac-geoparquet.ts
4568
+ function plainifyArrowValue(value) {
4569
+ if (value === null || value === void 0) return value;
4570
+ const t = typeof value;
4571
+ if (t === "string" || t === "number" || t === "boolean" || t === "bigint") return value;
4572
+ if (value instanceof Uint8Array || value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
4573
+ return value;
4574
+ }
4575
+ if (Array.isArray(value)) return value.map(plainifyArrowValue);
4576
+ const obj = value;
4577
+ if (typeof obj.toJSON === "function") {
4578
+ return plainifyArrowValue(obj.toJSON());
4579
+ }
4580
+ if (typeof obj.toArray === "function") {
4581
+ return obj.toArray().map(plainifyArrowValue);
4582
+ }
4583
+ if (t === "object") {
4584
+ const out = {};
4585
+ for (const [k, v] of Object.entries(value)) {
4586
+ out[k] = plainifyArrowValue(v);
4587
+ }
4588
+ return out;
4589
+ }
4590
+ return value;
4591
+ }
4592
+ var STAC_GEOPARQUET_REQUIRED_COLUMNS = [
4593
+ "stac_version",
4594
+ "type",
4595
+ "geometry",
4596
+ "assets"
4597
+ ];
4598
+ function isStacGeoparquetSchema(schema) {
4599
+ if (!Array.isArray(schema) || schema.length === 0) return false;
4600
+ const names = new Set(schema.map((c) => c.name));
4601
+ return STAC_GEOPARQUET_REQUIRED_COLUMNS.every((c) => names.has(c));
4602
+ }
4603
+ function flattenStacBbox(bbox) {
4604
+ if (!bbox) return null;
4605
+ if (Array.isArray(bbox)) {
4606
+ if (bbox.length < 4) return null;
4607
+ const [minX, minY, maxX, maxY] = bbox;
4608
+ if (![minX, minY, maxX, maxY].every((v) => Number.isFinite(v))) return null;
4609
+ return [minX, minY, maxX, maxY];
4610
+ }
4611
+ if (typeof bbox === "object") {
4612
+ const { xmin, ymin, xmax, ymax } = bbox;
4613
+ if (![xmin, ymin, xmax, ymax].every((v) => Number.isFinite(v))) return null;
4614
+ return [xmin, ymin, xmax, ymax];
4615
+ }
4616
+ return null;
4617
+ }
4618
+ function resolveStacAssetHref(href, baseUrl) {
4619
+ if (!href) return href;
4620
+ if (/^[a-z][a-z0-9+\-.]*:\/\//i.test(href)) return href;
4621
+ try {
4622
+ return new URL(href, baseUrl).toString();
4623
+ } catch {
4624
+ return href;
4625
+ }
4626
+ }
4627
+ function pickStacPrimaryAsset(assets, preferredKeys) {
4628
+ if (!assets || typeof assets !== "object") return null;
4629
+ const entries = Object.entries(assets).filter(
4630
+ ([, a]) => a && typeof a === "object" && typeof a.href === "string"
4631
+ );
4632
+ if (entries.length === 0) return null;
4633
+ if (preferredKeys) {
4634
+ for (const key of preferredKeys) {
4635
+ const match = entries.find(([k]) => k === key);
4636
+ if (match) return { key: match[0], asset: match[1] };
4637
+ }
4638
+ }
4639
+ const data = entries.find(([k]) => k === "data");
4640
+ if (data) return { key: data[0], asset: data[1] };
4641
+ const byRole = entries.find(([, a]) => Array.isArray(a.roles) && a.roles.includes("data"));
4642
+ if (byRole) return { key: byRole[0], asset: byRole[1] };
4643
+ return { key: entries[0][0], asset: entries[0][1] };
4644
+ }
4645
+ function normalizeAssetsField(value, baseUrl) {
4646
+ if (!value || typeof value !== "object") return void 0;
4647
+ const out = {};
4648
+ for (const [key, raw] of Object.entries(value)) {
4649
+ if (!raw || typeof raw !== "object") continue;
4650
+ const asset = raw;
4651
+ if (typeof asset.href !== "string" || !asset.href) continue;
4652
+ out[key] = {
4653
+ ...asset,
4654
+ href: resolveStacAssetHref(asset.href, baseUrl)
4655
+ };
4656
+ }
4657
+ return Object.keys(out).length > 0 ? out : void 0;
4658
+ }
4659
+ function coerceDatetimeToIso(value) {
4660
+ if (value == null) return void 0;
4661
+ if (value instanceof Date) {
4662
+ const ms = value.getTime();
4663
+ return Number.isFinite(ms) ? new Date(ms).toISOString() : void 0;
4664
+ }
4665
+ if (typeof value === "bigint") {
4666
+ const ms = Number(value / 1000n);
4667
+ return Number.isFinite(ms) ? new Date(ms).toISOString() : void 0;
4668
+ }
4669
+ if (typeof value === "number" && Number.isFinite(value)) {
4670
+ const ms = Math.abs(value) > 1e14 ? Math.floor(value / 1e3) : value;
4671
+ return new Date(ms).toISOString();
4672
+ }
4673
+ if (typeof value === "string") {
4674
+ const t = Date.parse(value);
4675
+ return Number.isFinite(t) ? new Date(t).toISOString() : void 0;
4676
+ }
4677
+ return void 0;
4678
+ }
4679
+ function normalizeLinksField(value, baseUrl) {
4680
+ if (!Array.isArray(value)) return void 0;
4681
+ const links = [];
4682
+ for (const raw of value) {
4683
+ if (!raw || typeof raw !== "object") continue;
4684
+ const link = raw;
4685
+ if (typeof link.href !== "string" || typeof link.rel !== "string") continue;
4686
+ links.push({ ...link, href: resolveStacAssetHref(link.href, baseUrl) });
4687
+ }
4688
+ return links.length > 0 ? links : void 0;
4689
+ }
4690
+ function stacRowToItem(row, baseUrl, opts = {}) {
4691
+ const { wkbParser, wkbColumn = "geom_wkb", geometryColumn = "geometry" } = opts;
4692
+ for (const key of ["bbox", "assets", "links", "bands", "stac_extensions"]) {
4693
+ if (row[key] !== void 0 && row[key] !== null) {
4694
+ row[key] = plainifyArrowValue(row[key]);
4695
+ }
4696
+ }
4697
+ let geometry = row[geometryColumn];
4698
+ if (!geometry) {
4699
+ const wkb = row[wkbColumn];
4700
+ if (wkb && wkbParser) {
4701
+ const bytes = wkb instanceof Uint8Array ? wkb : toUint8Array(wkb);
4702
+ if (bytes) {
4703
+ try {
4704
+ geometry = wkbParser(bytes) ?? void 0;
4705
+ } catch {
4706
+ geometry = void 0;
4707
+ }
4708
+ }
4709
+ }
4710
+ }
4711
+ const bbox = flattenStacBbox(row.bbox) ?? void 0;
4712
+ const assets = normalizeAssetsField(row.assets, baseUrl);
4713
+ const links = normalizeLinksField(row.links, baseUrl);
4714
+ const properties = {};
4715
+ const SCALAR_PROP_KEYS = /* @__PURE__ */ new Set([
4716
+ "gsd",
4717
+ "platform",
4718
+ "constellation",
4719
+ "instruments",
4720
+ "mission",
4721
+ "license"
4722
+ ]);
4723
+ const TIMESTAMP_PROP_KEYS = /* @__PURE__ */ new Set([
4724
+ "datetime",
4725
+ "start_datetime",
4726
+ "end_datetime",
4727
+ "created",
4728
+ "updated"
4729
+ ]);
4730
+ for (const [key, value] of Object.entries(row)) {
4731
+ if (value === null || value === void 0) continue;
4732
+ if (key.startsWith("proj:") || key.startsWith("raster:") || key.startsWith("eo:")) {
4733
+ properties[key] = value;
4734
+ continue;
4735
+ }
4736
+ if (TIMESTAMP_PROP_KEYS.has(key)) {
4737
+ const iso = coerceDatetimeToIso(value);
4738
+ if (iso) properties[key] = iso;
4739
+ continue;
4740
+ }
4741
+ if (SCALAR_PROP_KEYS.has(key)) {
4742
+ properties[key] = value;
4743
+ continue;
4744
+ }
4745
+ if (key === "bands") {
4746
+ properties.bands = value;
4747
+ }
4748
+ }
4749
+ const item = {
4750
+ type: "Feature",
4751
+ stac_version: typeof row.stac_version === "string" ? row.stac_version : "1.0.0",
4752
+ id: typeof row.id === "string" ? row.id : String(row.id ?? ""),
4753
+ properties
4754
+ };
4755
+ if (typeof row.collection === "string") item.collection = row.collection;
4756
+ if (Array.isArray(row.stac_extensions)) {
4757
+ item.stac_extensions = row.stac_extensions;
4758
+ }
4759
+ if (bbox) item.bbox = bbox;
4760
+ if (geometry) item.geometry = geometry;
4761
+ if (assets) item.assets = assets;
4762
+ if (links) item.links = links;
4763
+ return item;
4764
+ }
4765
+ function toUint8Array(value) {
4766
+ if (value instanceof Uint8Array) return value;
4767
+ if (value instanceof ArrayBuffer) return new Uint8Array(value);
4768
+ if (ArrayBuffer.isView(value)) {
4769
+ const view = value;
4770
+ return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
4771
+ }
4772
+ if (Array.isArray(value)) return new Uint8Array(value);
4773
+ return null;
4774
+ }
4775
+
4776
+ // src/stac-hydrate.ts
4777
+ async function hydrateStacItems(root, baseHref, adapter, opts) {
4778
+ const limit = opts.limit ?? 2e3;
4779
+ const concurrency = opts.concurrency ?? 12;
4780
+ const followPagination = opts.followPagination ?? true;
4781
+ const signal = opts.signal;
4782
+ const urlToKey = opts.urlToKey;
4783
+ const items = [];
4784
+ let truncated = false;
4785
+ const emit = (batch) => {
4786
+ if (batch.length === 0) return;
4787
+ items.push(...batch);
4788
+ opts.onBatch?.(batch);
4789
+ opts.onProgress?.(items.length, void 0);
4790
+ };
4791
+ if (root.kind === "item") {
4792
+ emit([absolutizeItemAssets(root.item, baseHref)]);
4793
+ return { items, truncated: false, rootBaseHref: baseHref };
4794
+ }
4795
+ if (root.kind === "item-collection") {
4796
+ await consumeFeatureCollection(root.fc, baseHref, adapter, {
4797
+ signal,
4798
+ concurrency,
4799
+ limit,
4800
+ followPagination,
4801
+ urlToKey,
4802
+ itemsQuery: opts.itemsQuery,
4803
+ onAccept: (batch) => emit(batch),
4804
+ stopCheck: () => items.length >= limit,
4805
+ onTruncate: () => {
4806
+ truncated = true;
4807
+ }
4808
+ });
4809
+ return { items: items.slice(0, limit), truncated, rootBaseHref: baseHref };
4810
+ }
4811
+ if (root.kind === "collection" || root.kind === "catalog") {
4812
+ const itemLinks = collectItemLinks(root.payload, baseHref);
4813
+ const childLinks = root.kind === "catalog" || itemLinks.length === 0 ? collectChildLinks(root.payload, baseHref) : [];
4814
+ const itemsEndpoint = itemLinks.length === 0 ? collectItemsEndpoint(root.payload, baseHref, opts.itemsQuery) : null;
4815
+ if (itemLinks.length > 0) {
4816
+ await fetchItems(itemLinks, adapter, baseHref, {
4817
+ signal,
4818
+ concurrency,
4819
+ urlToKey,
4820
+ onBatch: (batch) => emit(batch),
4821
+ stopCheck: () => items.length >= limit,
4822
+ onTruncate: () => {
4823
+ truncated = true;
4824
+ }
4825
+ });
4826
+ } else if (itemsEndpoint) {
4827
+ try {
4828
+ const json = await fetchJson(adapter, itemsEndpoint, baseHref, signal, urlToKey);
4829
+ if (isStacFeatureCollection(json)) {
4830
+ await consumeFeatureCollection(json, itemsEndpoint, adapter, {
4831
+ signal,
4832
+ concurrency,
4833
+ limit,
4834
+ followPagination,
4835
+ urlToKey,
4836
+ itemsQuery: opts.itemsQuery,
4837
+ onAccept: (batch) => emit(batch),
4838
+ stopCheck: () => items.length >= limit,
4839
+ onTruncate: () => {
4840
+ truncated = true;
4841
+ }
4842
+ });
4843
+ }
4844
+ } catch {
3000
4845
  }
3001
- const azureBlob = host.match(AZURE_BLOB_RE);
3002
- if (azureBlob && pathParts.length > 0) {
3003
- return {
3004
- bucket: pathParts[0],
3005
- region: defaults.region || "",
3006
- endpoint: `${url.protocol}//${url.host}`,
3007
- provider: "azure",
3008
- prefix: pathParts.slice(1).join("/")
3009
- };
4846
+ }
4847
+ if (!truncated && items.length < limit && childLinks.length > 0) {
4848
+ const queue = [...childLinks];
4849
+ const stopCheck = () => items.length >= limit || signal.aborted;
4850
+ const workerCount = Math.min(concurrency, queue.length);
4851
+ const workers = [];
4852
+ for (let i = 0; i < workerCount; i++) {
4853
+ workers.push(
4854
+ (async () => {
4855
+ while (queue.length > 0) {
4856
+ if (stopCheck()) {
4857
+ if (items.length >= limit) truncated = true;
4858
+ queue.length = 0;
4859
+ return;
4860
+ }
4861
+ const childHref = queue.shift();
4862
+ if (!childHref) return;
4863
+ try {
4864
+ const childJson = await fetchJson(adapter, childHref, baseHref, signal, urlToKey);
4865
+ if (stopCheck()) {
4866
+ if (items.length >= limit) truncated = true;
4867
+ queue.length = 0;
4868
+ return;
4869
+ }
4870
+ const childKind = classifyFetchedJson(childJson);
4871
+ if (childKind.kind === "none") continue;
4872
+ const sub = await hydrateStacItems(childKind, childHref, adapter, {
4873
+ ...opts,
4874
+ limit: limit - items.length,
4875
+ onBatch: (batch) => emit(batch),
4876
+ onProgress: void 0
4877
+ });
4878
+ if (sub.truncated) truncated = true;
4879
+ } catch {
4880
+ }
4881
+ }
4882
+ })()
4883
+ );
3010
4884
  }
3011
- const storjGateway = host.match(STORJ_GATEWAY_RE);
3012
- if (storjGateway && pathParts.length > 0) {
3013
- return {
3014
- bucket: pathParts[0],
3015
- region: storjGateway[1] || defaults.region || "us1",
3016
- endpoint: `${url.protocol}//${url.host}`,
3017
- provider: "storj",
3018
- prefix: pathParts.slice(1).join("/")
3019
- };
4885
+ await Promise.all(workers);
4886
+ }
4887
+ return { items: items.slice(0, limit), truncated, rootBaseHref: baseHref };
4888
+ }
4889
+ return { items, truncated: false, rootBaseHref: baseHref };
4890
+ }
4891
+ function collectItemLinks(payload, baseHref) {
4892
+ return (payload.links ?? []).filter((l) => !!l && typeof l.href === "string" && l.rel === "item").map((l) => absolutizeHref(l.href, baseHref));
4893
+ }
4894
+ function collectChildLinks(payload, baseHref) {
4895
+ return (payload.links ?? []).filter((l) => !!l && typeof l.href === "string" && l.rel === "child").map((l) => absolutizeHref(l.href, baseHref));
4896
+ }
4897
+ function hasStacItemsEndpoint(payload) {
4898
+ return (payload.links ?? []).some((l) => !!l && typeof l.href === "string" && l.rel === "items");
4899
+ }
4900
+ function collectItemsEndpoint(payload, baseHref, query) {
4901
+ const link = (payload.links ?? []).find(
4902
+ (l) => !!l && typeof l.href === "string" && l.rel === "items"
4903
+ );
4904
+ if (!link) return null;
4905
+ return applyItemsQuery(absolutizeHref(link.href, baseHref), query);
4906
+ }
4907
+ function applyItemsQuery(absolute, query) {
4908
+ if (!query) return absolute;
4909
+ try {
4910
+ const url = new URL(absolute);
4911
+ if (query.bbox && query.bbox.length === 4 && !url.searchParams.has("bbox")) {
4912
+ url.searchParams.set("bbox", query.bbox.join(","));
4913
+ }
4914
+ if (query.datetime && !url.searchParams.has("datetime")) {
4915
+ url.searchParams.set("datetime", query.datetime);
4916
+ }
4917
+ if (typeof query.limit === "number" && query.limit > 0 && !url.searchParams.has("limit")) {
4918
+ url.searchParams.set("limit", String(Math.floor(query.limit)));
4919
+ }
4920
+ if (query.filter !== void 0 && query.filter !== null && !url.searchParams.has("filter")) {
4921
+ try {
4922
+ url.searchParams.set("filter", JSON.stringify(query.filter));
4923
+ if (!url.searchParams.has("filter-lang")) {
4924
+ url.searchParams.set("filter-lang", "cql2-json");
4925
+ }
4926
+ } catch {
3020
4927
  }
3021
- const storjLink = host.match(STORJ_LINK_RE);
3022
- if (storjLink && pathParts.length >= 3 && (pathParts[0] === "raw" || pathParts[0] === "s")) {
3023
- return {
3024
- bucket: pathParts[2],
3025
- region: storjLink[1] || defaults.region || "us1",
3026
- endpoint: `${url.protocol}//${url.host}/${pathParts[0]}/${pathParts[1]}`,
3027
- provider: "storj",
3028
- prefix: pathParts.slice(3).join("/")
3029
- };
4928
+ }
4929
+ return url.toString();
4930
+ } catch {
4931
+ return absolute;
4932
+ }
4933
+ }
4934
+ async function consumeFeatureCollection(fc, baseHref, adapter, ctx) {
4935
+ const accepted = fc.features.filter(isStacItem).map((item) => absolutizeItemAssets(item, baseHref));
4936
+ ctx.onAccept(accepted);
4937
+ if (ctx.stopCheck()) {
4938
+ ctx.onTruncate();
4939
+ return;
4940
+ }
4941
+ if (!ctx.followPagination) return;
4942
+ const next = (fc.links ?? []).find((l) => l && l.rel === "next" && typeof l.href === "string");
4943
+ if (!next) return;
4944
+ const nextHref = applyItemsQuery(absolutizeHref(next.href, baseHref), ctx.itemsQuery);
4945
+ if (ctx.signal.aborted) return;
4946
+ try {
4947
+ const json = await fetchJson(adapter, nextHref, baseHref, ctx.signal, ctx.urlToKey);
4948
+ if (!isStacFeatureCollection(json)) return;
4949
+ await consumeFeatureCollection(json, nextHref, adapter, ctx);
4950
+ } catch {
4951
+ }
4952
+ }
4953
+ async function fetchItems(hrefs, adapter, baseHref, ctx) {
4954
+ const queue = [...hrefs];
4955
+ const workers = [];
4956
+ const workerCount = Math.min(ctx.concurrency, queue.length);
4957
+ for (let i = 0; i < workerCount; i++) {
4958
+ workers.push(
4959
+ (async () => {
4960
+ while (queue.length > 0) {
4961
+ if (ctx.signal.aborted) return;
4962
+ if (ctx.stopCheck()) {
4963
+ ctx.onTruncate();
4964
+ queue.length = 0;
4965
+ return;
4966
+ }
4967
+ const href = queue.shift();
4968
+ if (!href) return;
4969
+ try {
4970
+ const json = await fetchJson(adapter, href, baseHref, ctx.signal, ctx.urlToKey);
4971
+ if (isStacItem(json)) ctx.onBatch([absolutizeItemAssets(json, href)]);
4972
+ } catch {
4973
+ }
4974
+ }
4975
+ })()
4976
+ );
4977
+ }
4978
+ await Promise.all(workers);
4979
+ }
4980
+ async function fetchJson(adapter, href, _baseHref, signal, urlToKey) {
4981
+ if (/^https?:/i.test(href)) {
4982
+ const ownKey = urlToKey ? urlToKey(href) : null;
4983
+ if (ownKey !== null) {
4984
+ const buf2 = await adapter.read(ownKey, void 0, void 0, signal);
4985
+ return JSON.parse(new TextDecoder().decode(buf2));
4986
+ }
4987
+ const res = await fetch(href, { signal });
4988
+ if (!res.ok) throw new Error(`HTTP ${res.status} for ${href}`);
4989
+ return await res.json();
4990
+ }
4991
+ const buf = await adapter.read(href, void 0, void 0, signal);
4992
+ return JSON.parse(new TextDecoder().decode(buf));
4993
+ }
4994
+ function classifyFetchedJson(json) {
4995
+ if (isStacItem(json)) return { kind: "item", item: json };
4996
+ if (isStacFeatureCollection(json)) return { kind: "item-collection", fc: json };
4997
+ if (isStacCollection(json)) return { kind: "collection", payload: json };
4998
+ if (isStacCatalog(json)) return { kind: "catalog", payload: json };
4999
+ return { kind: "none" };
5000
+ }
5001
+ function absolutizeHref(href, baseHref) {
5002
+ if (/^https?:/i.test(href) || /^s3:/i.test(href) || /^azure:/i.test(href)) return href;
5003
+ try {
5004
+ return new URL(href, baseHref).toString();
5005
+ } catch {
5006
+ return href;
5007
+ }
5008
+ }
5009
+ function absolutizeItemAssets(item, itemHref) {
5010
+ const assets = item.assets;
5011
+ if (!assets) return item;
5012
+ let changed = false;
5013
+ const resolved = {};
5014
+ for (const [key, asset] of Object.entries(assets)) {
5015
+ if (asset?.href && typeof asset.href === "string") {
5016
+ const abs = absolutizeHref(asset.href, itemHref);
5017
+ if (abs !== asset.href) changed = true;
5018
+ resolved[key] = { ...asset, href: abs };
5019
+ } else {
5020
+ resolved[key] = asset;
5021
+ }
5022
+ }
5023
+ return changed ? { ...item, assets: resolved } : item;
5024
+ }
5025
+
5026
+ // src/stac-pushdown.ts
5027
+ var CONFORMANCE_SIGNATURES = [
5028
+ {
5029
+ key: "bbox",
5030
+ matchers: [/ogc-api[/-]features.*\/conf\/core/i, /stac-api.*\/item-search/i]
5031
+ },
5032
+ {
5033
+ key: "datetime",
5034
+ matchers: [/ogc-api[/-]features.*\/conf\/core/i, /stac-api.*\/item-search/i]
5035
+ },
5036
+ {
5037
+ key: "collections",
5038
+ matchers: [/stac-api.*\/item-search/i]
5039
+ },
5040
+ {
5041
+ key: "cql2",
5042
+ matchers: [/stac-api.*\/item-search.*\/filter/i, /ogc-api[/-]features.*\/cql2/i, /cql2-json/i]
5043
+ },
5044
+ {
5045
+ key: "queryables",
5046
+ matchers: [/stac-api.*\/item-search.*\/queryables/i, /ogc-api[/-]features.*\/queryables/i]
5047
+ }
5048
+ ];
5049
+ function sniffApiCapabilities(conformsTo) {
5050
+ const caps = {
5051
+ bbox: false,
5052
+ datetime: false,
5053
+ collections: false,
5054
+ cql2: false,
5055
+ queryables: false
5056
+ };
5057
+ if (!Array.isArray(conformsTo)) return caps;
5058
+ const normalised = conformsTo.filter((s) => typeof s === "string");
5059
+ for (const sig of CONFORMANCE_SIGNATURES) {
5060
+ if (normalised.some((uri) => sig.matchers.some((re) => re.test(uri)))) {
5061
+ caps[sig.key] = true;
5062
+ }
5063
+ }
5064
+ return caps;
5065
+ }
5066
+ function toNativeQuery(state, caps, opts = {}) {
5067
+ const out = {};
5068
+ if (opts.bbox && caps.bbox) out.bbox = opts.bbox;
5069
+ if (typeof opts.limit === "number" && opts.limit > 0) out.limit = Math.floor(opts.limit);
5070
+ if (!state) return out;
5071
+ if (caps.datetime && state.datetime && (state.datetime.min || state.datetime.max)) {
5072
+ out.datetime = formatDatetimeInterval(state.datetime.min, state.datetime.max);
5073
+ }
5074
+ if (caps.collections && state.enums?.collection?.length) {
5075
+ out.collections = [...state.enums.collection];
5076
+ }
5077
+ if (caps.cql2) {
5078
+ const filter = toCql2Filter(state, caps);
5079
+ if (filter) {
5080
+ out.filter = filter;
5081
+ out["filter-lang"] = "cql2-json";
5082
+ }
5083
+ }
5084
+ return out;
5085
+ }
5086
+ function formatDatetimeInterval(min, max) {
5087
+ const lo = min ?? "..";
5088
+ const hi = max ?? "..";
5089
+ if (lo === ".." && hi === "..") return "..";
5090
+ if (lo === hi) return lo;
5091
+ return `${lo}/${hi}`;
5092
+ }
5093
+ function toCql2Filter(state, caps) {
5094
+ if (!state) return null;
5095
+ const clauses = [];
5096
+ if (state.numeric) {
5097
+ const n = state.numeric;
5098
+ pushBetween(clauses, "eo:cloud_cover", n.cloudCover);
5099
+ pushBetween(clauses, "gsd", n.gsd);
5100
+ }
5101
+ if (state.enums) {
5102
+ const e = state.enums;
5103
+ pushIn(clauses, "platform", e.platform);
5104
+ pushIn(clauses, "constellation", e.constellation);
5105
+ pushOverlap(clauses, "instruments", e.instruments);
5106
+ if (!caps.collections) pushIn(clauses, "collection", e.collection);
5107
+ }
5108
+ if (!caps.datetime && state.datetime && (state.datetime.min || state.datetime.max)) {
5109
+ const lo = state.datetime.min;
5110
+ const hi = state.datetime.max;
5111
+ if (lo && hi) {
5112
+ clauses.push({
5113
+ op: "t_intersects",
5114
+ args: [{ property: "datetime" }, { interval: [lo, hi] }]
5115
+ });
5116
+ } else if (lo) {
5117
+ clauses.push({ op: ">=", args: [{ property: "datetime" }, { timestamp: lo }] });
5118
+ } else if (hi) {
5119
+ clauses.push({ op: "<=", args: [{ property: "datetime" }, { timestamp: hi }] });
5120
+ }
5121
+ }
5122
+ if (clauses.length === 0) return null;
5123
+ if (clauses.length === 1) return clauses[0];
5124
+ return { op: "and", args: clauses };
5125
+ }
5126
+ function pushBetween(out, property, range) {
5127
+ if (!range) return;
5128
+ const { min, max } = range;
5129
+ if (min == null && max == null) return;
5130
+ if (min != null && max != null) {
5131
+ out.push({ op: "between", args: [{ property }, [min, max]] });
5132
+ return;
5133
+ }
5134
+ if (min != null) out.push({ op: ">=", args: [{ property }, min] });
5135
+ if (max != null) out.push({ op: "<=", args: [{ property }, max] });
5136
+ }
5137
+ function pushIn(out, property, values) {
5138
+ if (!values || values.length === 0) return;
5139
+ if (values.length === 1) {
5140
+ out.push({ op: "=", args: [{ property }, values[0]] });
5141
+ return;
5142
+ }
5143
+ out.push({ op: "in", args: [{ property }, values] });
5144
+ }
5145
+ function pushOverlap(out, property, values) {
5146
+ if (!values || values.length === 0) return;
5147
+ out.push({ op: "a_overlaps", args: [{ property }, values] });
5148
+ }
5149
+ function residualState(state, caps) {
5150
+ if (!state) return {};
5151
+ const out = {};
5152
+ if (state.datetime && !caps.datetime && !caps.cql2) out.datetime = state.datetime;
5153
+ else if (state.datetime && (caps.datetime || caps.cql2)) ;
5154
+ if (state.numeric) {
5155
+ const remaining = {};
5156
+ for (const [field, range] of Object.entries(state.numeric)) {
5157
+ if (!range) continue;
5158
+ if (caps.cql2) continue;
5159
+ remaining[field] = range;
5160
+ }
5161
+ if (Object.keys(remaining).length > 0) out.numeric = remaining;
5162
+ }
5163
+ if (state.enums) {
5164
+ const remaining = {};
5165
+ for (const [field, values] of Object.entries(state.enums)) {
5166
+ if (!Array.isArray(values) || values.length === 0) continue;
5167
+ if (field === "collection" && caps.collections) continue;
5168
+ if (caps.cql2 && (field === "platform" || field === "constellation" || field === "instruments")) {
5169
+ continue;
3030
5170
  }
3031
- if (STAC_API_PATH_RE.test(url.pathname)) {
3032
- return {
3033
- ...defaultResult(defaults),
3034
- endpoint: `${url.protocol}//${url.host}`
5171
+ remaining[field] = values;
5172
+ }
5173
+ if (Object.keys(remaining).length > 0) out.enums = remaining;
5174
+ }
5175
+ return out;
5176
+ }
5177
+
5178
+ // src/stac-source.ts
5179
+ function emptyPushdown() {
5180
+ return {
5181
+ bbox: false,
5182
+ datetime: false,
5183
+ collection: false,
5184
+ cloudCover: false,
5185
+ gsd: false,
5186
+ epsg: false,
5187
+ platform: false,
5188
+ constellation: false,
5189
+ instruments: false,
5190
+ assetRoles: false
5191
+ };
5192
+ }
5193
+
5194
+ // src/stac-source-api.ts
5195
+ function subtractState(full, residual) {
5196
+ if (!full) return {};
5197
+ const out = {};
5198
+ if (full.datetime && !residual.datetime) out.datetime = full.datetime;
5199
+ if (full.numeric) {
5200
+ const kept = {};
5201
+ const residualNumeric = residual.numeric ?? {};
5202
+ for (const [k, v] of Object.entries(full.numeric)) {
5203
+ if (!v) continue;
5204
+ if (residualNumeric[k]) continue;
5205
+ kept[k] = v;
5206
+ }
5207
+ if (Object.keys(kept).length > 0) out.numeric = kept;
5208
+ }
5209
+ if (full.enums) {
5210
+ const kept = {};
5211
+ const residualEnums = residual.enums ?? {};
5212
+ for (const [k, v] of Object.entries(full.enums)) {
5213
+ if (!Array.isArray(v) || v.length === 0) continue;
5214
+ if (Array.isArray(residualEnums[k])) continue;
5215
+ kept[k] = v;
5216
+ }
5217
+ if (Object.keys(kept).length > 0) out.enums = kept;
5218
+ }
5219
+ return out;
5220
+ }
5221
+ function datetimeFacetToRfc3339(dt) {
5222
+ if (!dt) return void 0;
5223
+ const lo = dt.min;
5224
+ const hi = dt.max;
5225
+ if (!lo && !hi) return void 0;
5226
+ if (lo && hi) return lo === hi ? lo : `${lo}/${hi}`;
5227
+ if (lo) return `${lo}/..`;
5228
+ return `../${hi}`;
5229
+ }
5230
+ function createApiSource(kind, deps) {
5231
+ const capabilities = {
5232
+ kind: "api",
5233
+ label: "STAC API",
5234
+ countAvailable: false,
5235
+ streaming: true,
5236
+ pushdown: {
5237
+ ...emptyPushdown(),
5238
+ bbox: true,
5239
+ datetime: true,
5240
+ collection: true,
5241
+ cloudCover: true,
5242
+ gsd: true,
5243
+ platform: true,
5244
+ constellation: true,
5245
+ instruments: true
5246
+ }
5247
+ };
5248
+ let capsPromise = null;
5249
+ const getCaps = (signal) => {
5250
+ if (!capsPromise) {
5251
+ capsPromise = sniffSourceCapabilities(kind, deps, signal).catch(() => SLICE_1_CAPS);
5252
+ }
5253
+ return capsPromise;
5254
+ };
5255
+ return {
5256
+ capabilities,
5257
+ query(req) {
5258
+ return apiQueryIterable(kind, deps, req, getCaps);
5259
+ }
5260
+ };
5261
+ }
5262
+ var SLICE_1_CAPS = {
5263
+ bbox: true,
5264
+ datetime: true,
5265
+ collections: false,
5266
+ cql2: false,
5267
+ queryables: false
5268
+ };
5269
+ async function sniffSourceCapabilities(kind, deps, signal) {
5270
+ const inline = readConformsTo(kind);
5271
+ if (inline && inline.length > 0) return sniffApiCapabilities(inline);
5272
+ const json = await fetchRootJson(deps, signal);
5273
+ if (json && typeof json === "object") {
5274
+ const conformsTo = json.conformsTo;
5275
+ if (Array.isArray(conformsTo)) return sniffApiCapabilities(conformsTo);
5276
+ }
5277
+ return SLICE_1_CAPS;
5278
+ }
5279
+ function readConformsTo(kind) {
5280
+ if (kind.kind === "catalog" || kind.kind === "collection") {
5281
+ const ct = kind.payload.conformsTo;
5282
+ return Array.isArray(ct) ? ct : null;
5283
+ }
5284
+ if (kind.kind === "item-collection") {
5285
+ const ct = kind.fc.conformsTo;
5286
+ return Array.isArray(ct) ? ct : null;
5287
+ }
5288
+ return null;
5289
+ }
5290
+ async function fetchRootJson(deps, signal) {
5291
+ const href = deps.baseHref;
5292
+ if (/^https?:/i.test(href)) {
5293
+ const ownKey = deps.urlToKey ? deps.urlToKey(href) : null;
5294
+ if (ownKey !== null) {
5295
+ const buf2 = await deps.adapter.read(ownKey, void 0, void 0, signal);
5296
+ return JSON.parse(new TextDecoder().decode(buf2));
5297
+ }
5298
+ const res = await fetch(href, { signal });
5299
+ if (!res.ok) throw new Error(`HTTP ${res.status} for ${href}`);
5300
+ return await res.json();
5301
+ }
5302
+ const buf = await deps.adapter.read(href, void 0, void 0, signal);
5303
+ return JSON.parse(new TextDecoder().decode(buf));
5304
+ }
5305
+ async function* apiQueryIterable(kind, deps, req, getCaps) {
5306
+ if (req.signal.aborted) throw new DOMException("Aborted", "AbortError");
5307
+ const caps = await getCaps(req.signal);
5308
+ if (req.signal.aborted) throw new DOMException("Aborted", "AbortError");
5309
+ const native = toNativeQuery(req.filter, caps, {
5310
+ bbox: req.bbox,
5311
+ limit: req.pageSize ?? req.limit
5312
+ });
5313
+ const datetime = native.datetime ?? datetimeFacetToRfc3339(req.filter.datetime);
5314
+ const itemsQuery = {
5315
+ bbox: native.bbox ?? req.bbox,
5316
+ datetime,
5317
+ limit: native.limit ?? req.pageSize ?? req.limit,
5318
+ filter: native.filter
5319
+ };
5320
+ const residual = residualState(req.filter, caps);
5321
+ const pushedDown = subtractState(req.filter, residual);
5322
+ if (!caps.cql2) {
5323
+ itemsQuery.filter = void 0;
5324
+ } else if (itemsQuery.filter === void 0) {
5325
+ const cql = toCql2Filter(req.filter, caps);
5326
+ if (cql) itemsQuery.filter = cql;
5327
+ }
5328
+ const buffer = [];
5329
+ let pendingResolve = null;
5330
+ const push = (s) => {
5331
+ if (pendingResolve) {
5332
+ const r = pendingResolve;
5333
+ pendingResolve = null;
5334
+ r(s);
5335
+ } else {
5336
+ buffer.push(s);
5337
+ }
5338
+ };
5339
+ const next = () => {
5340
+ if (buffer.length > 0) return Promise.resolve(buffer.shift());
5341
+ return new Promise((resolve) => {
5342
+ pendingResolve = resolve;
5343
+ });
5344
+ };
5345
+ const onAbort = () => push({ kind: "error", error: new DOMException("Aborted", "AbortError") });
5346
+ req.signal.addEventListener("abort", onAbort, { once: true });
5347
+ void (async () => {
5348
+ try {
5349
+ await hydrateStacItems(kind, deps.baseHref, deps.adapter, {
5350
+ signal: req.signal,
5351
+ concurrency: deps.concurrency ?? 12,
5352
+ limit: req.limit,
5353
+ urlToKey: deps.urlToKey,
5354
+ itemsQuery,
5355
+ onBatch: (batch) => {
5356
+ if (batch.length > 0) push({ kind: "value", batch });
5357
+ }
5358
+ });
5359
+ push({ kind: "done" });
5360
+ } catch (err) {
5361
+ push({ kind: "error", error: err });
5362
+ }
5363
+ })();
5364
+ try {
5365
+ while (true) {
5366
+ const state = await next();
5367
+ if (state.kind === "value") {
5368
+ yield {
5369
+ items: state.batch,
5370
+ pushedDown,
5371
+ residual,
5372
+ done: false
3035
5373
  };
5374
+ continue;
3036
5375
  }
3037
- if (pathParts.length > 0) {
3038
- const endpoint = `${url.protocol}//${url.host}`;
3039
- return {
3040
- bucket: pathParts[0],
3041
- region: defaults.region || "us-east-1",
3042
- endpoint,
3043
- provider: defaults.provider || "s3",
3044
- prefix: pathParts.slice(1).join("/")
5376
+ if (state.kind === "done") {
5377
+ yield {
5378
+ items: [],
5379
+ pushedDown,
5380
+ residual,
5381
+ done: true
3045
5382
  };
5383
+ return;
3046
5384
  }
3047
- return {
3048
- ...defaultResult(defaults),
3049
- endpoint: `${url.protocol}//${url.host}`
3050
- };
3051
- } catch {
5385
+ throw state.error;
3052
5386
  }
5387
+ } finally {
5388
+ req.signal.removeEventListener("abort", onAbort);
3053
5389
  }
3054
- const cleaned = trimmed.replace(/^\/+|\/+$/g, "");
5390
+ }
5391
+
5392
+ // src/stac-source-static.ts
5393
+ function createStaticSource(kind, deps) {
5394
+ const capabilities = {
5395
+ kind: "static",
5396
+ label: "Static catalog",
5397
+ countAvailable: false,
5398
+ streaming: true,
5399
+ pushdown: { ...emptyPushdown() }
5400
+ };
3055
5401
  return {
3056
- bucket: cleaned,
3057
- region: defaults.region || "us-east-1",
3058
- endpoint: defaults.endpoint || "",
3059
- provider: defaults.provider || "s3",
3060
- prefix: ""
5402
+ capabilities,
5403
+ query(req) {
5404
+ return staticQueryIterable(kind, deps, req);
5405
+ }
3061
5406
  };
3062
5407
  }
3063
- function looksLikeUrl(input) {
3064
- const lower = input.trim().toLowerCase();
3065
- if (lower.startsWith("http://") || lower.startsWith("https://")) return true;
3066
- for (const scheme of Object.keys(SCHEME_MAP)) {
3067
- if (lower.startsWith(scheme)) return true;
5408
+ async function* staticQueryIterable(kind, deps, req) {
5409
+ if (req.signal.aborted) throw new DOMException("Aborted", "AbortError");
5410
+ const pushedDown = {};
5411
+ const residual = req.filter;
5412
+ const buffer = [];
5413
+ let pendingResolve = null;
5414
+ const push = (s) => {
5415
+ if (pendingResolve) {
5416
+ const r = pendingResolve;
5417
+ pendingResolve = null;
5418
+ r(s);
5419
+ } else {
5420
+ buffer.push(s);
5421
+ }
5422
+ };
5423
+ const next = () => {
5424
+ if (buffer.length > 0) return Promise.resolve(buffer.shift());
5425
+ return new Promise((resolve) => {
5426
+ pendingResolve = resolve;
5427
+ });
5428
+ };
5429
+ const onAbort = () => push({ kind: "error", error: new DOMException("Aborted", "AbortError") });
5430
+ req.signal.addEventListener("abort", onAbort, { once: true });
5431
+ void (async () => {
5432
+ try {
5433
+ await hydrateStacItems(kind, deps.baseHref, deps.adapter, {
5434
+ signal: req.signal,
5435
+ concurrency: deps.concurrency ?? 12,
5436
+ limit: req.limit,
5437
+ urlToKey: deps.urlToKey,
5438
+ onBatch: (batch) => {
5439
+ if (batch.length > 0) push({ kind: "value", batch });
5440
+ }
5441
+ });
5442
+ push({ kind: "done" });
5443
+ } catch (err) {
5444
+ push({ kind: "error", error: err });
5445
+ }
5446
+ })();
5447
+ try {
5448
+ while (true) {
5449
+ const state = await next();
5450
+ if (state.kind === "value") {
5451
+ yield {
5452
+ items: state.batch,
5453
+ pushedDown,
5454
+ residual,
5455
+ done: false
5456
+ };
5457
+ continue;
5458
+ }
5459
+ if (state.kind === "done") {
5460
+ yield { items: [], pushedDown, residual, done: true };
5461
+ return;
5462
+ }
5463
+ throw state.error;
5464
+ }
5465
+ } finally {
5466
+ req.signal.removeEventListener("abort", onAbort);
3068
5467
  }
3069
- return false;
3070
5468
  }
3071
- function describeParseResult(parsed) {
3072
- const parts = [];
3073
- if (parsed.bucket) parts.push(`bucket="${parsed.bucket}"`);
3074
- if (parsed.endpoint) parts.push(`endpoint="${parsed.endpoint}"`);
3075
- if (parsed.region && parsed.region !== "us-east-1") parts.push(`region="${parsed.region}"`);
3076
- if (parsed.provider !== "s3") parts.push(`provider=${parsed.provider}`);
3077
- if (parsed.prefix) parts.push(`prefix="${parsed.prefix}"`);
3078
- return parts.length > 0 ? `Detected: ${parts.join(", ")}` : "";
5469
+
5470
+ // src/storage-smoketest.ts
5471
+ async function smokeTestHref(href, signal) {
5472
+ let response;
5473
+ try {
5474
+ response = await fetch(href, {
5475
+ method: "GET",
5476
+ headers: { Range: "bytes=0-0" },
5477
+ signal,
5478
+ redirect: "follow"
5479
+ });
5480
+ } catch (err) {
5481
+ if (err instanceof DOMException && err.name === "AbortError") throw err;
5482
+ const reason2 = err instanceof Error ? err.message : String(err);
5483
+ return { ok: false, status: null, reason: reason2 };
5484
+ }
5485
+ if (response.ok || response.status === 206) {
5486
+ await response.body?.cancel().catch(() => {
5487
+ });
5488
+ return { ok: true };
5489
+ }
5490
+ const reason = `${response.status} ${response.statusText || ""}`.trim();
5491
+ return { ok: false, status: response.status, reason };
3079
5492
  }
3080
5493
 
3081
- // ../../src/lib/utils/wkb.ts
5494
+ // src/wkb.ts
3082
5495
  var WKB_POINT = 1;
3083
5496
  var WKB_LINESTRING = 2;
3084
5497
  var WKB_POLYGON = 3;
@@ -3334,34 +5747,78 @@ function isWKT(value) {
3334
5747
  }
3335
5748
 
3336
5749
  exports.COPY_FEEDBACK_MS = COPY_FEEDBACK_MS;
5750
+ exports.DATETIME_HISTOGRAM_BINS = DATETIME_HISTOGRAM_BINS;
5751
+ exports.DATETIME_HISTOGRAM_BINS_MAX = DATETIME_HISTOGRAM_BINS_MAX;
5752
+ exports.DEFAULT_APP_CONFIG = DEFAULT_APP_CONFIG;
3337
5753
  exports.DEFAULT_TARGET_CRS = DEFAULT_TARGET_CRS;
3338
5754
  exports.DUCKDB_INIT_TIMEOUT_MS = DUCKDB_INIT_TIMEOUT_MS;
3339
5755
  exports.LAYER_HUE_MULTIPLIER = LAYER_HUE_MULTIPLIER;
5756
+ exports.LruCache = LruCache;
3340
5757
  exports.MAX_QUERY_HISTORY_ENTRIES = MAX_QUERY_HISTORY_ENTRIES;
5758
+ exports.MarkdownSqlContext = MarkdownSqlContext;
5759
+ exports.PRESETS = PRESETS;
3341
5760
  exports.PROVIDERS = PROVIDERS;
3342
5761
  exports.PROVIDER_IDS = PROVIDER_IDS;
3343
5762
  exports.QueryCancelledError = QueryCancelledError;
3344
5763
  exports.SF_LABELS = SF_LABELS;
3345
5764
  exports.SQL_PREVIEW_LENGTH = SQL_PREVIEW_LENGTH;
5765
+ exports.STAC_API_PATH_RE = STAC_API_PATH_RE;
5766
+ exports.STAC_COG_ASSET_KEYS = STAC_COG_ASSET_KEYS;
3346
5767
  exports.STAC_GEOPARQUET_REQUIRED_COLUMNS = STAC_GEOPARQUET_REQUIRED_COLUMNS;
3347
5768
  exports.STORAGE_KEYS = STORAGE_KEYS;
3348
5769
  exports.UrlAdapter = UrlAdapter;
3349
5770
  exports.VIEWER_DIR_EXTENSIONS = VIEWER_DIR_EXTENSIONS;
3350
5771
  exports.WGS84_CODES = WGS84_CODES;
5772
+ exports.absolutizeHref = absolutizeHref;
5773
+ exports.allChannelsBand0 = allChannelsBand0;
5774
+ exports.applyFacets = applyFacets;
5775
+ exports.applyPreset = applyPreset;
5776
+ exports.applyStacItemStorageHints = applyStacItemStorageHints;
5777
+ exports.applyStorageHintsToConnection = applyStorageHintsToConnection;
5778
+ exports.attachPixelInspector = attachPixelInspector;
5779
+ exports.availablePresets = availablePresets;
3351
5780
  exports.buildDataTypeLabel = buildDataTypeLabel;
3352
5781
  exports.buildDuckDbSource = buildDuckDbSource;
3353
5782
  exports.buildEndpointFromTemplate = buildEndpointFromTemplate;
5783
+ exports.buildFacets = buildFacets;
3354
5784
  exports.buildGeoArrowTables = buildGeoArrowTables;
5785
+ exports.buildMosaicSourceMeta = buildMosaicSourceMeta;
3355
5786
  exports.buildProviderBaseUrl = buildProviderBaseUrl;
5787
+ exports.buildTransformExpr = buildTransformExpr;
3356
5788
  exports.clampBounds = clampBounds;
5789
+ exports.classifyStac = classifyStac;
3357
5790
  exports.classifyType = classifyType;
5791
+ exports.classifyUrl = classifyUrl;
5792
+ exports.coerceBool = coerceBool;
5793
+ exports.coercePositiveInt = coercePositiveInt;
5794
+ exports.coerceString = coerceString;
5795
+ exports.coerceTheme = coerceTheme;
5796
+ exports.compositeFromUrl = compositeFromUrl;
5797
+ exports.compositeToUrl = compositeToUrl;
5798
+ exports.connectionIdentityKey = connectionIdentityKey;
5799
+ exports.copyToClipboard = copyToClipboard;
5800
+ exports.createApiSource = createApiSource;
5801
+ exports.createStaticSource = createStaticSource;
3358
5802
  exports.describeParseResult = describeParseResult;
5803
+ exports.detectHostBucket = detectHostBucket;
5804
+ exports.detectMosaicCapable = detectMosaicCapable;
5805
+ exports.detectMultiCogCapable = detectMultiCogCapable;
5806
+ exports.detectStorageExtensionVersion = detectStorageExtensionVersion;
5807
+ exports.emptyFacetState = emptyFacetState;
5808
+ exports.emptyPushdown = emptyPushdown;
5809
+ exports.emptyStorageHints = emptyStorageHints;
3359
5810
  exports.escapeCsvField = escapeCsvField;
3360
5811
  exports.exportToCsv = exportToCsv;
3361
5812
  exports.exportToJson = exportToJson;
3362
5813
  exports.extractBounds = extractBounds;
5814
+ exports.extractCogAssets = extractCogAssets;
3363
5815
  exports.extractEpsgFromGeoMeta = extractEpsgFromGeoMeta;
3364
5816
  exports.extractGeometryTypes = extractGeometryTypes;
5817
+ exports.extractItemView = extractItemView;
5818
+ exports.extractMosaicAssets = extractMosaicAssets;
5819
+ exports.extractRasterBandAssets = extractRasterBandAssets;
5820
+ exports.extractSentinelBandAssets = extractSentinelBandAssets;
5821
+ exports.extractStorageHints = extractStorageHints;
3365
5822
  exports.findGeoColumn = findGeoColumn;
3366
5823
  exports.findGeoColumnFromRows = findGeoColumnFromRows;
3367
5824
  exports.flattenStacBbox = flattenStacBbox;
@@ -3378,36 +5835,75 @@ exports.getNativeScheme = getNativeScheme;
3378
5835
  exports.getProvider = getProvider;
3379
5836
  exports.getViewerKind = getViewerKind;
3380
5837
  exports.handleLoadError = handleLoadError;
5838
+ exports.hasActiveFilters = hasActiveFilters;
5839
+ exports.hasCompositableBands = hasCompositableBands;
5840
+ exports.hasRgbBands = hasRgbBands;
5841
+ exports.hasStacItemsEndpoint = hasStacItemsEndpoint;
5842
+ exports.hydrateStacItems = hydrateStacItems;
3381
5843
  exports.interpolateTemplates = interpolateTemplates;
5844
+ exports.isAbortError = isAbortError;
3382
5845
  exports.isCloudNativeFormat = isCloudNativeFormat;
3383
5846
  exports.isGcsProvider = isGcsProvider;
5847
+ exports.isKnownBucketHost = isKnownBucketHost;
3384
5848
  exports.isPubliclyStreamable = isPubliclyStreamable;
3385
5849
  exports.isQueryable = isQueryable;
5850
+ exports.isSameConnectionIdentity = isSameConnectionIdentity;
5851
+ exports.isSingleAssetComposite = isSingleAssetComposite;
5852
+ exports.isStacCatalog = isStacCatalog;
5853
+ exports.isStacCollection = isStacCollection;
5854
+ exports.isStacFeatureCollection = isStacFeatureCollection;
3386
5855
  exports.isStacGeoparquetSchema = isStacGeoparquetSchema;
5856
+ exports.isStacItem = isStacItem;
5857
+ exports.isWgs84Crs = isWgs84Crs;
3387
5858
  exports.jsonReplacerBigInt = jsonReplacerBigInt;
3388
5859
  exports.loadFromStorage = loadFromStorage;
3389
5860
  exports.looksLikeUrl = looksLikeUrl;
3390
5861
  exports.markSqlBlocks = markSqlBlocks;
5862
+ exports.mergeAppConfig = mergeAppConfig;
5863
+ exports.normalizeEndpoint = normalizeEndpoint;
3391
5864
  exports.normalizeGeomType = normalizeGeomType;
5865
+ exports.normalizeProvider = normalizeProvider;
5866
+ exports.parseGeometryTypeCrs = parseGeometryTypeCrs;
3392
5867
  exports.parseMarkdownDocument = parseMarkdownDocument;
3393
5868
  exports.parseStorageUrl = parseStorageUrl;
5869
+ exports.parseVisibilityParam = parseVisibilityParam;
3394
5870
  exports.parseWKB = parseWKB;
3395
5871
  exports.persistToStorage = persistToStorage;
5872
+ exports.pickCogAssetHref = pickCogAssetHref;
5873
+ exports.pickGranularity = pickGranularity;
5874
+ exports.pickNaturalColorComposite = pickNaturalColorComposite;
3396
5875
  exports.pickStacPrimaryAsset = pickStacPrimaryAsset;
5876
+ exports.presetMatchesComposite = presetMatchesComposite;
3397
5877
  exports.readParquetMetadata = readParquetMetadata;
5878
+ exports.renderNotebook = renderNotebook;
5879
+ exports.residualState = residualState;
5880
+ exports.resolveBandSlotAssetKey = resolveBandSlotAssetKey;
5881
+ exports.resolveBasemap = resolveBasemap;
3398
5882
  exports.resolveCloudUrl = resolveCloudUrl;
5883
+ exports.resolvePresetComposite = resolvePresetComposite;
3399
5884
  exports.resolveProviderEndpoint = resolveProviderEndpoint;
5885
+ exports.resolveSetting = resolveSetting;
3400
5886
  exports.resolveStacAssetHref = resolveStacAssetHref;
3401
5887
  exports.safeClamp = safeClamp;
3402
5888
  exports.safeDecodeURIComponent = safeDecodeURIComponent;
3403
5889
  exports.serializeToCsv = serializeToCsv;
3404
5890
  exports.serializeToJson = serializeToJson;
5891
+ exports.smokeTestHref = smokeTestHref;
5892
+ exports.sniffApiCapabilities = sniffApiCapabilities;
3405
5893
  exports.sortFileEntries = sortFileEntries;
5894
+ exports.sortViews = sortViews;
5895
+ exports.spatialCellKey = spatialCellKey;
5896
+ exports.stacItemBbox = stacItemBbox;
3406
5897
  exports.stacRowToItem = stacRowToItem;
5898
+ exports.syntheticSelfAsset = syntheticSelfAsset;
3407
5899
  exports.toBinary = toBinary;
5900
+ exports.toCql2Filter = toCql2Filter;
5901
+ exports.toNativeQuery = toNativeQuery;
3408
5902
  exports.toggleSortField = toggleSortField;
3409
5903
  exports.typeBadgeClass = typeBadgeClass;
3410
5904
  exports.typeColor = typeColor;
3411
5905
  exports.typeLabel = typeLabel;
5906
+ exports.wireCodeCopyButtons = wireCodeCopyButtons;
5907
+ exports.wrapWkbWithCrs = wrapWkbWithCrs;
3412
5908
  //# sourceMappingURL=index.cjs.map
3413
5909
  //# sourceMappingURL=index.cjs.map