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