// node_modules/@noble/ed25519/index.js
/*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
var ed25519_CURVE = {
  p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,
  n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,
  h: 8n,
  a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,
  d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,
  Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,
  Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n
};
var { p: P, n: N, Gx, Gy, a: _a, d: _d, h } = ed25519_CURVE;
var L = 32;
var L2 = 64;
var captureTrace = (...args) => {
  if ("captureStackTrace" in Error && typeof Error.captureStackTrace === "function") {
    Error.captureStackTrace(...args);
  }
};
var err = (message = "") => {
  const e = new Error(message);
  captureTrace(e, err);
  throw e;
};
var isBig = (n) => typeof n === "bigint";
var isStr = (s) => typeof s === "string";
var isBytes = (a) => a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
var abytes = (value, length, title = "") => {
  const bytes = isBytes(value);
  const len = value?.length;
  const needsLen = length !== undefined;
  if (!bytes || needsLen && len !== length) {
    const prefix = title && `"${title}" `;
    const ofLen = needsLen ? ` of length ${length}` : "";
    const got = bytes ? `length=${len}` : `type=${typeof value}`;
    err(prefix + "expected Uint8Array" + ofLen + ", got " + got);
  }
  return value;
};
var u8n = (len) => new Uint8Array(len);
var u8fr = (buf) => Uint8Array.from(buf);
var padh = (n, pad) => n.toString(16).padStart(pad, "0");
var bytesToHex = (b) => Array.from(abytes(b)).map((e) => padh(e, 2)).join("");
var C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };
var _ch = (ch) => {
  if (ch >= C._0 && ch <= C._9)
    return ch - C._0;
  if (ch >= C.A && ch <= C.F)
    return ch - (C.A - 10);
  if (ch >= C.a && ch <= C.f)
    return ch - (C.a - 10);
  return;
};
var hexToBytes = (hex) => {
  const e = "hex invalid";
  if (!isStr(hex))
    return err(e);
  const hl = hex.length;
  const al = hl / 2;
  if (hl % 2)
    return err(e);
  const array = u8n(al);
  for (let ai = 0, hi = 0;ai < al; ai++, hi += 2) {
    const n1 = _ch(hex.charCodeAt(hi));
    const n2 = _ch(hex.charCodeAt(hi + 1));
    if (n1 === undefined || n2 === undefined)
      return err(e);
    array[ai] = n1 * 16 + n2;
  }
  return array;
};
var cr = () => globalThis?.crypto;
var subtle = () => cr()?.subtle ?? err("crypto.subtle must be defined, consider polyfill");
var concatBytes = (...arrs) => {
  const r = u8n(arrs.reduce((sum, a) => sum + abytes(a).length, 0));
  let pad = 0;
  arrs.forEach((a) => {
    r.set(a, pad);
    pad += a.length;
  });
  return r;
};
var big = BigInt;
var assertRange = (n, min, max, msg = "bad number: out of range") => isBig(n) && min <= n && n < max ? n : err(msg);
var M = (a, b = P) => {
  const r = a % b;
  return r >= 0n ? r : b + r;
};
var modN = (a) => M(a, N);
var invert = (num, md) => {
  if (num === 0n || md <= 0n)
    err("no inverse n=" + num + " mod=" + md);
  let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;
  while (a !== 0n) {
    const q = b / a, r = b % a;
    const m = x - u * q, n = y - v * q;
    b = a, a = r, x = u, y = v, u = m, v = n;
  }
  return b === 1n ? M(x, md) : err("no inverse");
};
var apoint = (p) => p instanceof Point ? p : err("Point expected");
var B256 = 2n ** 256n;

class Point {
  static BASE;
  static ZERO;
  X;
  Y;
  Z;
  T;
  constructor(X, Y, Z, T) {
    const max = B256;
    this.X = assertRange(X, 0n, max);
    this.Y = assertRange(Y, 0n, max);
    this.Z = assertRange(Z, 1n, max);
    this.T = assertRange(T, 0n, max);
    Object.freeze(this);
  }
  static CURVE() {
    return ed25519_CURVE;
  }
  static fromAffine(p) {
    return new Point(p.x, p.y, 1n, M(p.x * p.y));
  }
  static fromBytes(hex, zip215 = false) {
    const d = _d;
    const normed = u8fr(abytes(hex, L));
    const lastByte = hex[31];
    normed[31] = lastByte & ~128;
    const y = bytesToNumLE(normed);
    const max = zip215 ? B256 : P;
    assertRange(y, 0n, max);
    const y2 = M(y * y);
    const u = M(y2 - 1n);
    const v = M(d * y2 + 1n);
    let { isValid, value: x } = uvRatio(u, v);
    if (!isValid)
      err("bad point: y not sqrt");
    const isXOdd = (x & 1n) === 1n;
    const isLastByteOdd = (lastByte & 128) !== 0;
    if (!zip215 && x === 0n && isLastByteOdd)
      err("bad point: x==0, isLastByteOdd");
    if (isLastByteOdd !== isXOdd)
      x = M(-x);
    return new Point(x, y, 1n, M(x * y));
  }
  static fromHex(hex, zip215) {
    return Point.fromBytes(hexToBytes(hex), zip215);
  }
  get x() {
    return this.toAffine().x;
  }
  get y() {
    return this.toAffine().y;
  }
  assertValidity() {
    const a = _a;
    const d = _d;
    const p = this;
    if (p.is0())
      return err("bad point: ZERO");
    const { X, Y, Z, T } = p;
    const X2 = M(X * X);
    const Y2 = M(Y * Y);
    const Z2 = M(Z * Z);
    const Z4 = M(Z2 * Z2);
    const aX2 = M(X2 * a);
    const left = M(Z2 * M(aX2 + Y2));
    const right = M(Z4 + M(d * M(X2 * Y2)));
    if (left !== right)
      return err("bad point: equation left != right (1)");
    const XY = M(X * Y);
    const ZT = M(Z * T);
    if (XY !== ZT)
      return err("bad point: equation left != right (2)");
    return this;
  }
  equals(other) {
    const { X: X1, Y: Y1, Z: Z1 } = this;
    const { X: X2, Y: Y2, Z: Z2 } = apoint(other);
    const X1Z2 = M(X1 * Z2);
    const X2Z1 = M(X2 * Z1);
    const Y1Z2 = M(Y1 * Z2);
    const Y2Z1 = M(Y2 * Z1);
    return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
  }
  is0() {
    return this.equals(I);
  }
  negate() {
    return new Point(M(-this.X), this.Y, this.Z, M(-this.T));
  }
  double() {
    const { X: X1, Y: Y1, Z: Z1 } = this;
    const a = _a;
    const A = M(X1 * X1);
    const B = M(Y1 * Y1);
    const C2 = M(2n * M(Z1 * Z1));
    const D = M(a * A);
    const x1y1 = X1 + Y1;
    const E = M(M(x1y1 * x1y1) - A - B);
    const G = D + B;
    const F = G - C2;
    const H = D - B;
    const X3 = M(E * F);
    const Y3 = M(G * H);
    const T3 = M(E * H);
    const Z3 = M(F * G);
    return new Point(X3, Y3, Z3, T3);
  }
  add(other) {
    const { X: X1, Y: Y1, Z: Z1, T: T1 } = this;
    const { X: X2, Y: Y2, Z: Z2, T: T2 } = apoint(other);
    const a = _a;
    const d = _d;
    const A = M(X1 * X2);
    const B = M(Y1 * Y2);
    const C2 = M(T1 * d * T2);
    const D = M(Z1 * Z2);
    const E = M((X1 + Y1) * (X2 + Y2) - A - B);
    const F = M(D - C2);
    const G = M(D + C2);
    const H = M(B - a * A);
    const X3 = M(E * F);
    const Y3 = M(G * H);
    const T3 = M(E * H);
    const Z3 = M(F * G);
    return new Point(X3, Y3, Z3, T3);
  }
  subtract(other) {
    return this.add(apoint(other).negate());
  }
  multiply(n, safe = true) {
    if (!safe && (n === 0n || this.is0()))
      return I;
    assertRange(n, 1n, N);
    if (n === 1n)
      return this;
    if (this.equals(G))
      return wNAF(n).p;
    let p = I;
    let f = G;
    for (let d = this;n > 0n; d = d.double(), n >>= 1n) {
      if (n & 1n)
        p = p.add(d);
      else if (safe)
        f = f.add(d);
    }
    return p;
  }
  multiplyUnsafe(scalar) {
    return this.multiply(scalar, false);
  }
  toAffine() {
    const { X, Y, Z } = this;
    if (this.equals(I))
      return { x: 0n, y: 1n };
    const iz = invert(Z, P);
    if (M(Z * iz) !== 1n)
      err("invalid inverse");
    const x = M(X * iz);
    const y = M(Y * iz);
    return { x, y };
  }
  toBytes() {
    const { x, y } = this.assertValidity().toAffine();
    const b = numTo32bLE(y);
    b[31] |= x & 1n ? 128 : 0;
    return b;
  }
  toHex() {
    return bytesToHex(this.toBytes());
  }
  clearCofactor() {
    return this.multiply(big(h), false);
  }
  isSmallOrder() {
    return this.clearCofactor().is0();
  }
  isTorsionFree() {
    let p = this.multiply(N / 2n, false).double();
    if (N % 2n)
      p = p.add(this);
    return p.is0();
  }
}
var G = new Point(Gx, Gy, 1n, M(Gx * Gy));
var I = new Point(0n, 1n, 1n, 0n);
Point.BASE = G;
Point.ZERO = I;
var numTo32bLE = (num) => hexToBytes(padh(assertRange(num, 0n, B256), L2)).reverse();
var bytesToNumLE = (b) => big("0x" + bytesToHex(u8fr(abytes(b)).reverse()));
var pow2 = (x, power) => {
  let r = x;
  while (power-- > 0n) {
    r *= r;
    r %= P;
  }
  return r;
};
var pow_2_252_3 = (x) => {
  const x2 = x * x % P;
  const b2 = x2 * x % P;
  const b4 = pow2(b2, 2n) * b2 % P;
  const b5 = pow2(b4, 1n) * x % P;
  const b10 = pow2(b5, 5n) * b5 % P;
  const b20 = pow2(b10, 10n) * b10 % P;
  const b40 = pow2(b20, 20n) * b20 % P;
  const b80 = pow2(b40, 40n) * b40 % P;
  const b160 = pow2(b80, 80n) * b80 % P;
  const b240 = pow2(b160, 80n) * b80 % P;
  const b250 = pow2(b240, 10n) * b10 % P;
  const pow_p_5_8 = pow2(b250, 2n) * x % P;
  return { pow_p_5_8, b2 };
};
var RM1 = 0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n;
var uvRatio = (u, v) => {
  const v3 = M(v * v * v);
  const v7 = M(v3 * v3 * v);
  const pow = pow_2_252_3(u * v7).pow_p_5_8;
  let x = M(u * v3 * pow);
  const vx2 = M(v * x * x);
  const root1 = x;
  const root2 = M(x * RM1);
  const useRoot1 = vx2 === u;
  const useRoot2 = vx2 === M(-u);
  const noRoot = vx2 === M(-u * RM1);
  if (useRoot1)
    x = root1;
  if (useRoot2 || noRoot)
    x = root2;
  if ((M(x) & 1n) === 1n)
    x = M(-x);
  return { isValid: useRoot1 || useRoot2, value: x };
};
var modL_LE = (hash) => modN(bytesToNumLE(hash));
var sha512a = (...m) => hashes.sha512Async(concatBytes(...m));
var hash2extK = (hashed) => {
  const head = hashed.slice(0, L);
  head[0] &= 248;
  head[31] &= 127;
  head[31] |= 64;
  const prefix = hashed.slice(L, L2);
  const scalar = modL_LE(head);
  const point = G.multiply(scalar);
  const pointBytes = point.toBytes();
  return { head, prefix, scalar, point, pointBytes };
};
var getExtendedPublicKeyAsync = (secretKey) => sha512a(abytes(secretKey, L)).then(hash2extK);
var getPublicKeyAsync = (secretKey) => getExtendedPublicKeyAsync(secretKey).then((p) => p.pointBytes);
var hashFinishA = (res) => sha512a(res.hashable).then(res.finish);
var _sign = (e, rBytes, msg) => {
  const { pointBytes: P2, scalar: s } = e;
  const r = modL_LE(rBytes);
  const R = G.multiply(r).toBytes();
  const hashable = concatBytes(R, P2, msg);
  const finish = (hashed) => {
    const S = modN(r + modL_LE(hashed) * s);
    return abytes(concatBytes(R, numTo32bLE(S)), L2);
  };
  return { hashable, finish };
};
var signAsync = async (message, secretKey) => {
  const m = abytes(message);
  const e = await getExtendedPublicKeyAsync(secretKey);
  const rBytes = await sha512a(e.prefix, m);
  return hashFinishA(_sign(e, rBytes, m));
};
var hashes = {
  sha512Async: async (message) => {
    const s = subtle();
    const m = concatBytes(message);
    return u8n(await s.digest("SHA-512", m.buffer));
  },
  sha512: undefined
};
var W = 8;
var scalarBits = 256;
var pwindows = Math.ceil(scalarBits / W) + 1;
var pwindowSize = 2 ** (W - 1);
var precompute = () => {
  const points = [];
  let p = G;
  let b = p;
  for (let w = 0;w < pwindows; w++) {
    b = p;
    points.push(b);
    for (let i = 1;i < pwindowSize; i++) {
      b = b.add(p);
      points.push(b);
    }
    p = b.double();
  }
  return points;
};
var Gpows = undefined;
var ctneg = (cnd, p) => {
  const n = p.negate();
  return cnd ? n : p;
};
var wNAF = (n) => {
  const comp = Gpows || (Gpows = precompute());
  let p = I;
  let f = G;
  const pow_2_w = 2 ** W;
  const maxNum = pow_2_w;
  const mask = big(pow_2_w - 1);
  const shiftBy = big(W);
  for (let w = 0;w < pwindows; w++) {
    let wbits = Number(n & mask);
    n >>= shiftBy;
    if (wbits > pwindowSize) {
      wbits -= maxNum;
      n += 1n;
    }
    const off = w * pwindowSize;
    const offF = off;
    const offP = off + Math.abs(wbits) - 1;
    const isEven = w % 2 !== 0;
    const isNeg = wbits < 0;
    if (wbits === 0) {
      f = f.add(ctneg(isEven, comp[offF]));
    } else {
      p = p.add(ctneg(isNeg, comp[offP]));
    }
  }
  if (n !== 0n)
    err("invalid wnaf");
  return { p, f };
};

// src/encoding.ts
function bytesToBase64Url(bytes) {
  const bin = Array.from(bytes).map((b) => String.fromCharCode(b)).join("");
  if (typeof btoa !== "undefined") {
    return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
  }
  const b64 = globalThis.Buffer.from(bytes).toString("base64");
  return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
}
function base64UrlToBytes(input) {
  const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
  const padLen = (4 - normalized.length % 4) % 4;
  const b64 = normalized + "=".repeat(padLen);
  if (typeof atob !== "undefined") {
    const bin = atob(b64);
    const out = new Uint8Array(bin.length);
    for (let i = 0;i < bin.length; i++) {
      out[i] = bin.charCodeAt(i);
    }
    return out;
  }
  return new Uint8Array(globalThis.Buffer.from(b64, "base64"));
}
function textToBytes(value) {
  return new TextEncoder().encode(value);
}

// src/keys.ts
function randomPrivateKey() {
  const out = new Uint8Array(32);
  crypto.getRandomValues(out);
  return out;
}
async function generateKeyPair() {
  const privateKey = randomPrivateKey();
  const publicKey = await getPublicKeyAsync(privateKey);
  return {
    publicKey: bytesToBase64Url(publicKey),
    privateKey: bytesToBase64Url(privateKey)
  };
}
async function getPublicKeyFromPrivate(privateKeyBase64) {
  const privateKey = base64UrlToBytes(privateKeyBase64);
  const publicKey = await getPublicKeyAsync(privateKey);
  return bytesToBase64Url(publicKey);
}
async function signMessage(privateKeyBase64, message) {
  const privateKey = base64UrlToBytes(privateKeyBase64);
  const signature = await signAsync(textToBytes(message), privateKey);
  return bytesToBase64Url(signature);
}

// src/storage.ts
var DEFAULT_KEYS_STORAGE_KEY = "geo_api_keys_v1";
function saveKeys(storage, keys, storageKey = DEFAULT_KEYS_STORAGE_KEY) {
  storage.setItem(storageKey, JSON.stringify(keys));
}
function loadKeys(storage, storageKey = DEFAULT_KEYS_STORAGE_KEY) {
  const raw = storage.getItem(storageKey);
  if (!raw) {
    return null;
  }
  const parsed = JSON.parse(raw);
  if (!parsed.publicKey || !parsed.privateKey) {
    return null;
  }
  return parsed;
}
function clearKeys(storage, storageKey = DEFAULT_KEYS_STORAGE_KEY) {
  storage.removeItem(storageKey);
}

// src/GeoApiClient.ts
class GeoApiClient {
  baseUrl;
  storage;
  storageKey;
  accessToken = null;
  constructor(baseUrl, storage, storageKey = DEFAULT_KEYS_STORAGE_KEY) {
    this.baseUrl = baseUrl.replace(/\/+$/g, "");
    this.storage = storage;
    this.storageKey = storageKey;
  }
  async ensureKeysInStorage() {
    const existing = loadKeys(this.storage, this.storageKey);
    if (existing) {
      return existing;
    }
    const generated = await generateKeyPair();
    saveKeys(this.storage, generated, this.storageKey);
    return generated;
  }
  getStoredKeys() {
    return loadKeys(this.storage, this.storageKey);
  }
  async derivePublicKey(privateKey) {
    return getPublicKeyFromPrivate(privateKey);
  }
  importKeys(keys) {
    saveKeys(this.storage, keys, this.storageKey);
  }
  exportKeys() {
    return loadKeys(this.storage, this.storageKey);
  }
  setAccessToken(token) {
    this.accessToken = token;
  }
  async request(path, init = {}) {
    const headers = new Headers(init.headers ?? {});
    headers.set("Content-Type", "application/json");
    if (this.accessToken) {
      headers.set("Authorization", `Bearer ${this.accessToken}`);
    }
    const body = init.body === undefined ? undefined : JSON.stringify(init.body);
    const res = await fetch(`${this.baseUrl}${path}`, { ...init, headers, body });
    if (!res.ok) {
      const maybeJson = await res.json().catch(() => ({}));
      let msg = maybeJson.error ?? `HTTP ${res.status}`;
      if (maybeJson.hint)
        msg += `. ${maybeJson.hint}`;
      throw new Error(msg);
    }
    if (res.status === 204) {
      return;
    }
    return await res.json();
  }
  async getServicePublicKey() {
    return this.request("/v1/service-key", { method: "GET" });
  }
  async createChallenge(publicKey) {
    return this.request("/v1/auth/challenge", { method: "POST", body: { publicKey } });
  }
  async registerBySigningServiceKey(publicKey, privateKey) {
    const { publicKey: servicePublicKey } = await this.getServicePublicKey();
    const signature = await signMessage(privateKey, servicePublicKey);
    await this.request("/v1/auth/register-by-signature", {
      method: "POST",
      body: { publicKey, signature }
    });
  }
  async loginWithSignature(publicKey, privateKey) {
    const challenge = await this.createChallenge(publicKey);
    const signature = await signMessage(privateKey, challenge.messageToSign);
    const response = await this.request("/v1/auth/login", {
      method: "POST",
      body: {
        publicKey,
        nonce: challenge.nonce,
        signature
      }
    });
    this.accessToken = response.accessToken;
    return response.accessToken;
  }
  async createInvitation(payload, inviterPrivateKey) {
    const payloadStr = JSON.stringify(payload);
    const payloadB64 = bytesToBase64Url(textToBytes(payloadStr));
    const inviteSignature = await signMessage(inviterPrivateKey, `invite:${payloadB64}`);
    await this.request("/v1/invitations", {
      method: "POST",
      body: {
        invitePayloadB64: payloadB64,
        inviteSignature
      }
    });
  }
  async registerWithInvitation(input) {
    const proofSignature = await signMessage(input.privateKey, `register:${input.publicKey}:${input.jti}`);
    await this.request("/v1/auth/register", {
      method: "POST",
      body: {
        publicKey: input.publicKey,
        invitePayloadB64: input.invitePayloadB64,
        inviteSignature: input.inviteSignature,
        proofSignature
      }
    });
  }
  async listCollections() {
    return this.request("/v1/collections", { method: "GET" });
  }
  async createCollection(name) {
    return this.request("/v1/collections", { method: "POST", body: { name } });
  }
  async updateCollection(collectionId, name) {
    return this.request(`/v1/collections/${collectionId}`, {
      method: "PATCH",
      body: { name }
    });
  }
  async deleteCollection(collectionId) {
    return this.request(`/v1/collections/${collectionId}`, { method: "DELETE" });
  }
  async listFeatures(collectionId) {
    return this.request(`/v1/collections/${collectionId}/features`, { method: "GET" });
  }
  async createPointFeature(collectionId, lon, lat, properties) {
    return this.request(`/v1/collections/${collectionId}/features`, {
      method: "POST",
      body: {
        geometry: { type: "Point", coordinates: [lon, lat] },
        properties
      }
    });
  }
  async deleteFeature(featureId) {
    return this.request(`/v1/features/${featureId}`, { method: "DELETE" });
  }
  async createOrLinkAsset(input) {
    return this.request("/v1/assets", { method: "POST", body: input });
  }
  async getAssetSignedUploadUrl(assetId, contentType) {
    const response = await this.request(`/v1/assets/${assetId}/signed-upload`, {
      method: "POST",
      body: { contentType: contentType ?? "application/octet-stream" }
    });
    if (response.url.startsWith("/")) {
      response.url = this.resolveRelativeLink(response.url);
    }
    return response;
  }
  async uploadAssetBinary(assetId, payload, contentType = "application/octet-stream") {
    const upload = await this.getAssetSignedUploadUrl(assetId, contentType);
    const headers = new Headers;
    if (contentType) {
      headers.set("Content-Type", contentType);
    }
    if (this.accessToken) {
      headers.set("Authorization", `Bearer ${this.accessToken}`);
    }
    const res = await fetch(upload.url, {
      method: upload.method || "PUT",
      headers,
      body: payload
    });
    if (!res.ok) {
      const maybeJson = await res.json().catch(() => ({}));
      let msg = maybeJson.error ?? `Upload failed (${res.status})`;
      if (maybeJson.hint)
        msg += `. ${maybeJson.hint}`;
      throw new Error(msg);
    }
  }
  async setAssetVisibility(assetId, isPublic) {
    return this.request(`/v1/assets/${assetId}`, {
      method: "PATCH",
      body: { isPublic }
    });
  }
  resolveRelativeLink(path) {
    if (!path.startsWith("/"))
      return `${this.baseUrl}/${path}`;
    return `${this.baseUrl}${path}`;
  }
}
export {
  signMessage,
  saveKeys,
  loadKeys,
  getPublicKeyFromPrivate,
  generateKeyPair,
  clearKeys,
  GeoApiClient,
  DEFAULT_KEYS_STORAGE_KEY
};
