|
| 1 | diff --git a/internal/webui/blossom-encryption.js b/internal/webui/blossom-encryption.js | | 2 | new file mode 100644 | | 3 | index 0000000..0524813 | | 4 | --- /dev/null | | 5 | +++ b/internal/webui/blossom-encryption.js | | 6 | @@ -0,0 +1,73 @@ | | 7 | +// BUD-15 draft (June 2026), BUD-15: Client-side CHK encrypted blobs. | | 8 | +// https://raw.githubusercontent.com/mmalmi/blossom/codex/bud-15-chk-encryption/buds/15.md | | 9 | +// Keys are client-only values. Callers must not include k in requests to a server. | | 10 | +(() => { | | 11 | + "use strict"; | | 12 | + | | 13 | + const encoder = new TextEncoder(); | | 14 | + const zeroNonce = new Uint8Array(12); | | 15 | + const salt = encoder.encode("hashtree-chk"); | | 16 | + const info = encoder.encode("encryption-key"); | | 17 | + | | 18 | + const hex = bytes => Array.from(bytes, byte => byte.toString(16).padStart(2, "0")).join(""); | | 19 | + const bytes = value => { | | 20 | + if (value instanceof Uint8Array) return value; | | 21 | + if (ArrayBuffer.isView(value)) return new Uint8Array(value.buffer, value.byteOffset, value.byteLength); | | 22 | + throw new TypeError("expected Uint8Array"); | | 23 | + }; | | 24 | + const fromHex = value => { | | 25 | + if (typeof value !== "string" || !/^[0-9a-f]{64}$/.test(value)) throw new Error("expected a 64-character lowercase hex key"); | | 26 | + const result = new Uint8Array(32); | | 27 | + for (let index = 0; index < result.length; index++) result[index] = parseInt(value.slice(index * 2, index * 2 + 2), 16); | | 28 | + return result; | | 29 | + }; | | 30 | + const equal = (left, right) => left.length === right.length && left.every((value, index) => value === right[index]); | | 31 | + const digest = async value => new Uint8Array(await crypto.subtle.digest("SHA-256", bytes(value))); | | 32 | + const aesKey = async key => { | | 33 | + const material = await crypto.subtle.importKey("raw", key, "HKDF", false, ["deriveKey"]); | | 34 | + return crypto.subtle.deriveKey({name: "HKDF", hash: "SHA-256", salt, info}, material, | | 35 | + {name: "AES-GCM", length: 256}, false, ["encrypt", "decrypt"]); | | 36 | + }; | | 37 | + | | 38 | + const encryptCHK = async plaintext => { | | 39 | + const input = bytes(plaintext); | | 40 | + const key = await digest(input); | | 41 | + const encrypted = new Uint8Array(await crypto.subtle.encrypt({name: "AES-GCM", iv: zeroNonce}, await aesKey(key), input)); | | 42 | + return {ciphertext: encrypted, key: hex(key), hash: hex(await digest(encrypted))}; | | 43 | + }; | | 44 | + | | 45 | + const decryptCHK = async (ciphertext, key, hash) => { | | 46 | + const input = bytes(ciphertext); | | 47 | + const chkKey = fromHex(key); | | 48 | + if (typeof hash !== "string" || !/^[0-9a-f]{64}$/.test(hash)) throw new Error("expected a 64-character lowercase blob hash"); | | 49 | + if (hex(await digest(input)) !== hash) throw new Error("ciphertext hash mismatch"); | | 50 | + let plain; | | 51 | + try { | | 52 | + plain = new Uint8Array(await crypto.subtle.decrypt({name: "AES-GCM", iv: zeroNonce}, await aesKey(chkKey), input)); | | 53 | + } catch { | | 54 | + throw new Error("CHK decryption failed"); | | 55 | + } | | 56 | + if (!equal(await digest(plain), chkKey)) throw new Error("plaintext hash mismatch"); | | 57 | + return plain; | | 58 | + }; | | 59 | + | | 60 | + const createURI = (hash, extension, key) => { | | 61 | + if (typeof hash !== "string" || !/^[0-9a-f]{64}$/.test(hash)) throw new Error("expected a 64-character lowercase blob hash"); | | 62 | + const path = "blossom:" + hash + (extension ? "." + String(extension).replace(/^\.+/, "") : ""); | | 63 | + fromHex(key); | | 64 | + const params = new URLSearchParams({enc: "chk-v1", k: key}); | | 65 | + return path + "?" + params; | | 66 | + }; | | 67 | + | | 68 | + const parseURI = value => { | | 69 | + if (typeof value !== "string" || !value.startsWith("blossom:")) throw new Error("expected a Blossom URI"); | | 70 | + const parsed = new URL(value.slice("blossom:".length), "https://blossom.invalid"); | | 71 | + const match = parsed.pathname.match(/^\/([0-9a-f]{64})(?:\.([A-Za-z0-9]+))?$/); | | 72 | + if (!match || parsed.searchParams.get("enc") !== "chk-v1") throw new Error("invalid CHK Blossom URI"); | | 73 | + const key = parsed.searchParams.get("k"); | | 74 | + fromHex(key); | | 75 | + return {hash: match[1], extension: match[2] || "", key}; | | 76 | + }; | | 77 | + | | 78 | + globalThis.TinyBlossomEncryption = Object.freeze({encryptCHK, decryptCHK, createURI, parseURI}); | | 79 | +})(); | | 80 | diff --git a/internal/webui/blossom-manifests.js b/internal/webui/blossom-manifests.js | | 81 | new file mode 100644 | | 82 | index 0000000..3390f32 | | 83 | --- /dev/null | | 84 | +++ b/internal/webui/blossom-manifests.js | | 85 | @@ -0,0 +1,466 @@ | | 86 | +(() => { | | 87 | + (() => { | | 88 | + "use strict"; | | 89 | + const CHUNK_SIZE = 2 * 1024 * 1024; | | 90 | + const MAX_LINKS = 174; | | 91 | + const MAX_DEPTH = 64; | | 92 | + const MAX_MANIFEST_BYTES = 16 * 1024 * 1024; | | 93 | + const te = new TextEncoder(), td = new TextDecoder("utf-8", { fatal: true }); | | 94 | + const asBytes = (v) => v instanceof Uint8Array ? v : ArrayBuffer.isView(v) ? new Uint8Array(v.buffer, v.byteOffset, v.byteLength) : (() => { | | 95 | + throw new TypeError("expected bytes"); | | 96 | + })(); | | 97 | + const hashBytes = (h) => { | | 98 | + if (typeof h !== "string" || !/^[0-9a-f]{64}$/.test(h)) throw new Error("expected lowercase SHA-256 hash"); | | 99 | + return Uint8Array.from(h.match(/../g), (x) => parseInt(x, 16)); | | 100 | + }; | | 101 | + const keyBytes = (k) => { | | 102 | + if (k === void 0) return; | | 103 | + const b = asBytes(k); | | 104 | + if (b.length !== 32) throw new Error("manifest keys must be 32 bytes"); | | 105 | + return b; | | 106 | + }; | | 107 | + const wellFormed = (value) => { | | 108 | + for (let i = 0; i < value.length; i++) { | | 109 | + const c = value.charCodeAt(i); | | 110 | + if (c >= 55296 && c <= 56319) { | | 111 | + if (++i >= value.length || value.charCodeAt(i) < 56320 || value.charCodeAt(i) > 57343) return false; | | 112 | + } else if (c >= 56320 && c <= 57343) return false; | | 113 | + } | | 114 | + return true; | | 115 | + }; | | 116 | + const nameOK = (n) => typeof n === "string" && n.length > 0 && n !== "." && n !== ".." && wellFormed(n) && !n.includes("/") && !n.includes("\0") && te.encode(n).length <= 1024; | | 117 | + const int = (n) => Number.isSafeInteger(n) && n >= 0; | | 118 | + const utf8cmp = (a, b) => { | | 119 | + const x = te.encode(a), y = te.encode(b); | | 120 | + for (let i = 0; i < Math.min(x.length, y.length); i++) if (x[i] !== y[i]) return x[i] - y[i]; | | 121 | + return x.length - y.length; | | 122 | + }; | | 123 | + class Writer { | | 124 | + constructor() { | | 125 | + this.a = []; | | 126 | + } | | 127 | + push(...x) { | | 128 | + this.a.push(...x); | | 129 | + } | | 130 | + u8(n) { | | 131 | + this.a.push(n & 255); | | 132 | + } | | 133 | + bytes() { | | 134 | + return Uint8Array.from(this.a); | | 135 | + } | | 136 | + } | | 137 | + const putInt = (w, n) => { | | 138 | + if (!Number.isSafeInteger(n)) throw new Error("manifest integers must be safe integers"); | | 139 | + if (n >= 0) { | | 140 | + if (n < 128) w.u8(n); | | 141 | + else if (n < 256) w.push(204, n); | | 142 | + else if (n < 65536) w.push(205, n >> 8, n); | | 143 | + else if (n < 4294967296) w.push(206, n >>> 24, n >>> 16, n >>> 8, n); | | 144 | + else { | | 145 | + w.u8(207); | | 146 | + let x = BigInt(n); | | 147 | + for (let i = 7; i >= 0; i--) w.u8(Number(x >> BigInt(i * 8) & 255n)); | | 148 | + } | | 149 | + } else if (n >= -32) w.u8(256 + n); | | 150 | + else if (n >= -128) w.push(208, n & 255); | | 151 | + else if (n >= -32768) w.push(209, n >> 8 & 255, n & 255); | | 152 | + else if (n >= -2147483648) w.push(210, n >>> 24, n >>> 16, n >>> 8, n); | | 153 | + else { | | 154 | + w.u8(211); | | 155 | + let x = BigInt.asUintN(64, BigInt(n)); | | 156 | + for (let i = 7; i >= 0; i--) w.u8(Number(x >> BigInt(i * 8) & 255n)); | | 157 | + } | | 158 | + }; | | 159 | + const put = (w, v) => { | | 160 | + if (v === null) return w.u8(192); | | 161 | + if (v === false) return w.u8(194); | | 162 | + if (v === true) return w.u8(195); | | 163 | + if (typeof v === "number") { | | 164 | + if (Number.isInteger(v)) return putInt(w, v); | | 165 | + if (!Number.isFinite(v)) throw Error("metadata number must be finite"); | | 166 | + w.u8(203); | | 167 | + const b = new DataView(new ArrayBuffer(8)); | | 168 | + b.setFloat64(0, v); | | 169 | + for (let i = 0; i < 8; i++) w.u8(b.getUint8(i)); | | 170 | + return; | | 171 | + } | | 172 | + if (typeof v === "string") { | | 173 | + const b = te.encode(v); | | 174 | + if (b.length < 32) w.u8(160 | b.length); | | 175 | + else if (b.length < 256) w.push(217, b.length); | | 176 | + else if (b.length < 65536) w.push(218, b.length >> 8, b.length); | | 177 | + else throw new Error("string too large"); | | 178 | + w.push(...b); | | 179 | + return; | | 180 | + } | | 181 | + if (v instanceof Uint8Array) { | | 182 | + if (v.length < 256) w.push(196, v.length); | | 183 | + else if (v.length < 65536) w.push(197, v.length >> 8, v.length); | | 184 | + else throw new Error("binary value too large"); | | 185 | + w.push(...v); | | 186 | + return; | | 187 | + } | | 188 | + if (Array.isArray(v)) { | | 189 | + if (v.length < 16) w.u8(144 | v.length); | | 190 | + else if (v.length < 65536) w.push(220, v.length >> 8, v.length); | | 191 | + else throw new Error("array too large"); | | 192 | + v.forEach((x) => put(w, x)); | | 193 | + return; | | 194 | + } | | 195 | + if (v && typeof v === "object") { | | 196 | + const keys = Object.keys(v).sort(utf8cmp); | | 197 | + if (keys.length < 16) w.u8(128 | keys.length); | | 198 | + else if (keys.length < 65536) w.push(222, keys.length >> 8, keys.length); | | 199 | + else throw new Error("map too large"); | | 200 | + keys.forEach((k) => { | | 201 | + put(w, k); | | 202 | + put(w, v[k]); | | 203 | + }); | | 204 | + return; | | 205 | + } | | 206 | + throw new Error("unsupported manifest value"); | | 207 | + }; | | 208 | + class Reader { | | 209 | + constructor(b) { | | 210 | + this.b = asBytes(b); | | 211 | + this.i = 0; | | 212 | + } | | 213 | + need(n) { | | 214 | + if (this.i + n > this.b.length) throw new Error("truncated MessagePack"); | | 215 | + } | | 216 | + u8() { | | 217 | + this.need(1); | | 218 | + return this.b[this.i++]; | | 219 | + } | | 220 | + n(len) { | | 221 | + this.need(len); | | 222 | + const x = this.b.slice(this.i, this.i + len); | | 223 | + this.i += len; | | 224 | + return x; | | 225 | + } | | 226 | + } | | 227 | + const readMap = (r, n, depth) => { | | 228 | + const o = /* @__PURE__ */ Object.create(null); | | 229 | + for (let i = 0; i < n; i++) { | | 230 | + const k = get(r, depth + 1); | | 231 | + if (typeof k !== "string" || Object.prototype.hasOwnProperty.call(o, k)) throw Error("invalid map key"); | | 232 | + o[k] = get(r, depth + 1); | | 233 | + } | | 234 | + return o; | | 235 | + }; | | 236 | + const get = (r, depth = 0) => { | | 237 | + if (depth > MAX_DEPTH) throw Error("MessagePack recursion limit exceeded"); | | 238 | + const t = r.u8(); | | 239 | + if (t <= 127) return t; | | 240 | + if (t >= 224) return t - 256; | | 241 | + if ((t & 224) === 160) return td.decode(r.n(t & 31)); | | 242 | + if ((t & 240) === 144) return Array.from({ length: t & 15 }, () => get(r, depth + 1)); | | 243 | + if ((t & 240) === 128) return readMap(r, t & 15, depth); | | 244 | + if (t === 192) return null; | | 245 | + if (t === 194) return false; | | 246 | + if (t === 195) return true; | | 247 | + let n; | | 248 | + if (t === 204) n = r.u8(); | | 249 | + else if (t === 205) n = r.u8() << 8 | r.u8(); | | 250 | + else if (t === 206) n = r.u8() * 16777216 + (r.u8() << 16) + (r.u8() << 8) + r.u8(); | | 251 | + else if (t === 207) { | | 252 | + let x = 0n; | | 253 | + for (let i = 0; i < 8; i++) x = x << 8n | BigInt(r.u8()); | | 254 | + if (x > BigInt(Number.MAX_SAFE_INTEGER)) throw Error("integer exceeds safe range"); | | 255 | + n = Number(x); | | 256 | + } else if (t === 208) n = r.u8() << 24 >> 24; | | 257 | + else if (t === 209) { | | 258 | + n = r.u8() << 8 | r.u8(); | | 259 | + if (n & 32768) n -= 65536; | | 260 | + } else if (t === 210) { | | 261 | + n = r.u8() * 16777216 + (r.u8() << 16) + (r.u8() << 8) + r.u8(); | | 262 | + if (n >= 2147483648) n -= 4294967296; | | 263 | + } else if (t === 211) { | | 264 | + let x = 0n; | | 265 | + for (let i = 0; i < 8; i++) x = x << 8n | BigInt(r.u8()); | | 266 | + if (x & 0x8000000000000000n) x -= 0x10000000000000000n; | | 267 | + if (x > BigInt(Number.MAX_SAFE_INTEGER) || x < BigInt(Number.MIN_SAFE_INTEGER)) throw Error("integer exceeds safe range"); | | 268 | + n = Number(x); | | 269 | + } else if (t === 202) { | | 270 | + const b = new ArrayBuffer(4), d = new DataView(b); | | 271 | + for (let i = 0; i < 4; i++) d.setUint8(i, r.u8()); | | 272 | + n = d.getFloat32(0); | | 273 | + } else if (t === 203) { | | 274 | + const b = new ArrayBuffer(8), d = new DataView(b); | | 275 | + for (let i = 0; i < 8; i++) d.setUint8(i, r.u8()); | | 276 | + n = d.getFloat64(0); | | 277 | + } else if (t === 217) n = td.decode(r.n(r.u8())); | | 278 | + else if (t === 218) n = td.decode(r.n(r.u8() << 8 | r.u8())); | | 279 | + else if (t === 196) n = r.n(r.u8()); | | 280 | + else if (t === 197) n = r.n(r.u8() << 8 | r.u8()); | | 281 | + else if (t === 220) { | | 282 | + n = r.u8() << 8 | r.u8(); | | 283 | + return Array.from({ length: n }, () => get(r, depth + 1)); | | 284 | + } else if (t === 222) { | | 285 | + n = r.u8() << 8 | r.u8(); | | 286 | + return readMap(r, n, depth); | | 287 | + } else throw Error("unsupported MessagePack type"); | | 288 | + return n; | | 289 | + }; | | 290 | + const validateMeta = (m, depth = 0) => { | | 291 | + if (m === void 0) return; | | 292 | + if (depth > MAX_DEPTH || !m || Array.isArray(m) || typeof m !== "object" || m instanceof Uint8Array) throw Error("metadata must be a JSON map"); | | 293 | + const keys = Object.keys(m); | | 294 | + if (keys.length > 64) throw Error("too many metadata keys"); | | 295 | + for (const [k, v] of Object.entries(m)) { | | 296 | + if (typeof k !== "string" || te.encode(k).length > 256) throw Error("invalid metadata key"); | | 297 | + if (v === void 0 || typeof v === "bigint" || typeof v === "function" || typeof v === "symbol" || typeof v === "number" && !Number.isFinite(v)) throw Error("metadata is not JSON-compatible"); | | 298 | + if (v && typeof v === "object") { | | 299 | + if (Array.isArray(v)) { | | 300 | + if (v.length > 256) throw Error("metadata array too large"); | | 301 | + v.forEach((x) => validateMeta({ x }, depth + 1)); | | 302 | + } else validateMeta(v, depth + 1); | | 303 | + } | | 304 | + } | | 305 | + }; | | 306 | + const validateLink = (x, nodeType) => { | | 307 | + if (!x || typeof x !== "object" || Array.isArray(x)) throw Error("invalid manifest link"); | | 308 | + if (Object.keys(x).some((k) => !["h", "k", "m", "n", "s", "t"].includes(k))) throw Error("unknown manifest link field"); | | 309 | + if (!(x.h instanceof Uint8Array) || x.h.length !== 32) throw Error("link hash must be 32 bytes"); | | 310 | + if (x.k !== void 0) keyBytes(x.k); | | 311 | + if (x.m !== void 0) validateMeta(x.m); | | 312 | + if (!int(x.s)) throw Error("invalid link size"); | | 313 | + if (!int(x.t) || x.t < 0 || x.t > 3) throw Error("unsupported link type"); | | 314 | + if (nodeType === 1 && x.t !== 0 && x.t !== 1) throw Error("invalid file link type"); | | 315 | + if (nodeType === 1 && x.n !== void 0) throw Error("file links cannot have names"); | | 316 | + if (nodeType === 3 && x.n !== void 0) throw Error("fanout links cannot have names"); | | 317 | + if (nodeType === 3 && x.t !== 2 && x.t !== 3) throw Error("invalid fanout child type"); | | 318 | + if (nodeType === 2 && !nameOK(x.n)) throw Error("invalid directory entry name"); | | 319 | + }; | | 320 | + const validateNode = (node, depth = 0) => { | | 321 | + if (depth > MAX_DEPTH) throw Error("manifest recursion limit exceeded"); | | 322 | + if (!node || typeof node !== "object" || Array.isArray(node) || !Array.isArray(node.l) || !int(node.t) || ![1, 2, 3].includes(node.t) || Object.keys(node).some((k) => k !== "l" && k !== "t")) throw Error("invalid manifest node"); | | 323 | + if (node.l.length > MAX_LINKS) throw Error("too many manifest links"); | | 324 | + const seen = /* @__PURE__ */ new Set(), links = node.l; | | 325 | + for (const x of links) { | | 326 | + validateLink(x, node.t); | | 327 | + if (node.t === 2) { | | 328 | + if (seen.has(x.n)) throw Error("duplicate directory entry"); | | 329 | + seen.add(x.n); | | 330 | + } | | 331 | + if (node.t === 3) { | | 332 | + if (!x.m || !int(x.m.count) || x.m.count < 1 || !nameOK(x.m.first) || !nameOK(x.m.last) || utf8cmp(x.m.first, x.m.last) > 0) throw Error("invalid fanout bounds"); | | 333 | + } | | 334 | + } | | 335 | + if (node.t === 2 && links.some((x, i) => i && utf8cmp(links[i - 1].n, x.n) >= 0)) throw Error("directory links are not canonical"); | | 336 | + return node; | | 337 | + }; | | 338 | + const encodeManifest = (node) => { | | 339 | + validateNode(node); | | 340 | + const w = new Writer(); | | 341 | + w.u8(130); | | 342 | + put(w, "l"); | | 343 | + if (node.l.length < 16) w.u8(144 | node.l.length); | | 344 | + else w.push(220, node.l.length >> 8, node.l.length); | | 345 | + for (const x of node.l) { | | 346 | + const fields = ["h", ...x.k !== void 0 ? ["k"] : [], ...x.m !== void 0 ? ["m"] : [], ...x.n !== void 0 ? ["n"] : [], "s", "t"]; | | 347 | + if (fields.length < 16) w.u8(128 | fields.length); | | 348 | + else w.push(222, fields.length >> 8, fields.length); | | 349 | + for (const k of fields) { | | 350 | + put(w, k); | | 351 | + put(w, k === "h" || k === "k" ? asBytes(x[k]) : x[k]); | | 352 | + } | | 353 | + } | | 354 | + put(w, "t"); | | 355 | + put(w, node.t); | | 356 | + const out = w.bytes(); | | 357 | + if (out.length > MAX_MANIFEST_BYTES) throw Error("manifest too large"); | | 358 | + return out; | | 359 | + }; | | 360 | + const decodeManifest = (bytes, options = {}) => { | | 361 | + const b = asBytes(bytes); | | 362 | + if (b.length > MAX_MANIFEST_BYTES) throw Error("manifest too large"); | | 363 | + const r = new Reader(b), node = get(r); | | 364 | + if (r.i !== b.length) throw Error("trailing MessagePack data"); | | 365 | + validateNode(node); | | 366 | + if (node.t === 3) for (const x of node.l) { | | 367 | + if (x.m.count > Number(options.maxEntries || 1e9)) throw Error("fanout count limit exceeded"); | | 368 | + } | | 369 | + return node; | | 370 | + }; | | 371 | + const sha256 = async (b) => new Uint8Array(await crypto.subtle.digest("SHA-256", asBytes(b))); | | 372 | + const hex = (b) => Array.from(asBytes(b), (x) => x.toString(16).padStart(2, "0")).join(""); | | 373 | + const manifestHash = async (node) => hex(await sha256(encodeManifest(node))); | | 374 | + const leaf = (hash, size, key) => { | | 375 | + const x = { h: hashBytes(hash), s: size, t: 0 }; | | 376 | + if (key !== void 0) x.k = keyBytes(key); | | 377 | + return x; | | 378 | + }; | | 379 | + const storeNode = async (node, options, manifests) => { | | 380 | + const bytes = encodeManifest(node), computed = hex(await sha256(bytes)); | | 381 | + let result = { hash: computed }; | | 382 | + if (options.store) { | | 383 | + const stored = await options.store({ hash: computed, bytes, node }); | | 384 | + if (stored) { | | 385 | + if (typeof stored === "string") result.hash = stored; | | 386 | + else result = { ...result, ...stored }; | | 387 | + } | | 388 | + } | | 389 | + if (!/^[0-9a-f]{64}$/.test(result.hash)) throw Error("store returned invalid manifest hash"); | | 390 | + if (result.key !== void 0) keyBytes(result.key); | | 391 | + manifests.push({ hash: result.hash, bytes, node, key: result.key }); | | 392 | + return result; | | 393 | + }; | | 394 | + const buildFile = async (chunks, options = {}) => { | | 395 | + const max = options.maxLinks ?? MAX_LINKS; | | 396 | + if (!Number.isInteger(max) || max < 2 || max > MAX_LINKS) throw Error("invalid maxLinks"); | | 397 | + const leaves = chunks.map((c) => typeof c === "string" ? leaf(c, options.chunkSize || 0) : leaf(c.hash, c.size, c.key)); | | 398 | + if (leaves.some((x) => x.s < 0 || x.s > CHUNK_SIZE)) throw Error("invalid chunk size"); | | 399 | + const manifests = []; | | 400 | + let level = leaves; | | 401 | + while (level.length > max) { | | 402 | + const next = []; | | 403 | + for (let i = 0; i < level.length; i += max) { | | 404 | + const group = level.slice(i, i + max), child = { l: group, t: 1 }, stored = await storeNode(child, options, manifests); | | 405 | + next.push({ h: hashBytes(stored.hash), ...stored.key ? { k: keyBytes(stored.key) } : {}, s: group.reduce((n, x) => n + x.s, 0), t: 1 }); | | 406 | + } | | 407 | + level = next; | | 408 | + } | | 409 | + const root = { l: level, t: 1 }; | | 410 | + await storeNode(root, options, manifests); | | 411 | + return options.returnDetails ? { root, manifests } : root; | | 412 | + }; | | 413 | + const buildDirectory = async (entries, options = {}) => { | | 414 | + if (!Array.isArray(entries)) throw Error("entries must be an array"); | | 415 | + const max = options.maxLinks ?? MAX_LINKS; | | 416 | + if (!Number.isInteger(max) || max < 2 || max > MAX_LINKS) throw Error("invalid maxLinks"); | | 417 | + const sorted = entries.map((e) => { | | 418 | + const x = { h: hashBytes(e.hash), n: e.name, s: e.size || 0, t: e.type || 0 }; | | 419 | + if (e.key !== void 0) x.k = keyBytes(e.key); | | 420 | + if (e.metadata !== void 0) x.m = e.metadata; | | 421 | + return x; | | 422 | + }).sort((a, b) => utf8cmp(a.n, b.n)); | | 423 | + for (let i = 1; i < sorted.length; i++) if (sorted[i - 1].n === sorted[i].n) throw Error("duplicate directory entry"); | | 424 | + const manifests = []; | | 425 | + if (sorted.length <= max) { | | 426 | + const root2 = { l: sorted, t: 2 }; | | 427 | + await storeNode(root2, options, manifests); | | 428 | + return options.returnDetails ? { root: root2, manifests } : root2; | | 429 | + } | | 430 | + let level = []; | | 431 | + for (let i = 0; i < sorted.length; i += max) { | | 432 | + const l = sorted.slice(i, i + max), child = { l, t: 2 }, stored = await storeNode(child, options, manifests); | | 433 | + level.push({ h: hashBytes(stored.hash), ...stored.key ? { k: keyBytes(stored.key) } : {}, m: { count: l.length, first: l[0].n, last: l[l.length - 1].n }, s: l.reduce((n, x) => n + x.s, 0), t: 2 }); | | 434 | + } | | 435 | + while (level.length > max) { | | 436 | + const next = []; | | 437 | + for (let i = 0; i < level.length; i += max) { | | 438 | + const l = level.slice(i, i + max), child = { l, t: 3 }, stored = await storeNode(child, options, manifests); | | 439 | + next.push({ h: hashBytes(stored.hash), ...stored.key ? { k: keyBytes(stored.key) } : {}, m: { count: l.reduce((n, x) => n + x.m.count, 0), first: l[0].m.first, last: l[l.length - 1].m.last }, s: l.reduce((n, x) => n + x.s, 0), t: 3 }); | | 440 | + } | | 441 | + level = next; | | 442 | + } | | 443 | + const root = { l: level, t: 3 }; | | 444 | + await storeNode(root, options, manifests); | | 445 | + return options.returnDetails ? { root, manifests } : root; | | 446 | + }; | | 447 | + const flattenDirectory = (node, out = []) => { | | 448 | + validateNode(node); | | 449 | + if (node.t === 2) { | | 450 | + out.push(...node.l); | | 451 | + return out; | | 452 | + } | | 453 | + if (node.t !== 3) throw Error("not a directory"); | | 454 | + for (const x of node.l) if (x.child) flattenDirectory(x.child, out); | | 455 | + return out; | | 456 | + }; | | 457 | + const resolveDirectory = async (root, fetchNode, options = {}) => { | | 458 | + if (typeof fetchNode !== "function") throw Error("fetchNode is required"); | | 459 | + const maxDepth = options.maxDepth ?? MAX_DEPTH, maxEntries = options.maxEntries ?? 1e5, maxManifests = options.maxManifests ?? 1e4, maxBytes = options.maxBytes ?? 64 * 1024 * 1024; | | 460 | + if ([maxDepth, maxEntries, maxManifests, maxBytes].some(value => !Number.isSafeInteger(value) || value < 0)) throw Error("invalid directory limits"); | | 461 | + let count = 0, manifestCount = 0, fetched = 0; | | 462 | + const active = /* @__PURE__ */ new Set(); | | 463 | + const walk = async (node, depth) => { | | 464 | + if (depth > maxDepth) throw Error("directory recursion limit exceeded"); | | 465 | + if (++manifestCount > maxManifests) throw Error("manifest count limit exceeded"); | | 466 | + validateNode(node); | | 467 | + if (node.t === 2) { | | 468 | + count += node.l.length; | | 469 | + if (count > maxEntries) throw Error("directory entry limit exceeded"); | | 470 | + return node.l.slice(); | | 471 | + } | | 472 | + if (node.t !== 3) throw Error("not a directory"); | | 473 | + const result = []; | | 474 | + let previous = ""; | | 475 | + for (const link of node.l) { | | 476 | + if (previous && utf8cmp(previous, link.m.first) >= 0) throw Error("fanout bounds are not ordered"); | | 477 | + previous = link.m.last; | | 478 | + const linkHash = hex(link.h); | | 479 | + if (active.has(linkHash)) throw Error("manifest cycle detected"); | | 480 | + active.add(linkHash); | | 481 | + let raw = asBytes(await fetchNode(linkHash, link)); | | 482 | + fetched += raw.length; | | 483 | + if (fetched > maxBytes) throw Error("directory byte limit exceeded"); | | 484 | + if (hex(await sha256(raw)) !== linkHash) throw Error("manifest hash mismatch"); | | 485 | + if (link.k) { | | 486 | + if (typeof options.decrypt !== "function") throw Error("encrypted manifest requires decrypt callback"); | | 487 | + raw = asBytes(await options.decrypt(raw, link)); | | 488 | + } | | 489 | + const child = decodeManifest(raw); | | 490 | + if (child.t !== link.t) throw Error("fanout child type mismatch"); | | 491 | + const links = await walk(child, depth + 1); | | 492 | + active.delete(linkHash); | | 493 | + if (links.length !== link.m.count || links[0]?.n !== link.m.first || links.at(-1)?.n !== link.m.last) throw Error("fanout bounds mismatch"); | | 494 | + result.push(...links); | | 495 | + } | | 496 | + for (let i = 1; i < result.length; i++) if (utf8cmp(result[i - 1].n, result[i].n) >= 0) throw Error("duplicate or unordered directory entry"); | | 497 | + return result; | | 498 | + }; | | 499 | + return walk(root, 0); | | 500 | + }; | | 501 | + const readFile = async (root, fetchBlob, options = {}) => { | | 502 | + if (typeof fetchBlob !== "function") throw Error("fetchBlob is required"); | | 503 | + const maxDepth = options.maxDepth ?? MAX_DEPTH, maxBytes = options.maxBytes ?? 512 * 1024 * 1024, maxManifests = options.maxManifests ?? 1e4; | | 504 | + const maxFetchedBytes = options.maxFetchedBytes ?? maxBytes + 16 * 1024 * 1024; | | 505 | + if ([maxDepth, maxBytes, maxManifests, maxFetchedBytes].some(value => !Number.isSafeInteger(value) || value < 0)) throw Error("invalid file limits"); | | 506 | + let used = 0, fetched = 0, manifests = 0; | | 507 | + const active = /* @__PURE__ */ new Set(), parts = []; | | 508 | + const readNode = async (node, depth) => { | | 509 | + if (depth > maxDepth) throw Error("file recursion limit exceeded"); | | 510 | + validateNode(node); | | 511 | + if (node.t !== 1) throw Error("not a file manifest"); | | 512 | + if (++manifests > maxManifests) throw Error("manifest count limit exceeded"); | | 513 | + for (const link of node.l) { | | 514 | + const hash = hex(link.h); | | 515 | + if (active.has(hash)) throw Error("manifest cycle detected"); | | 516 | + active.add(hash); | | 517 | + let raw = asBytes(await fetchBlob(hash, link)); | | 518 | + fetched += raw.length; | | 519 | + if (fetched > maxFetchedBytes) throw Error("file fetched byte limit exceeded"); | | 520 | + if (hex(await sha256(raw)) !== hash) throw Error("blob hash mismatch"); | | 521 | + if (link.t === 1) { | | 522 | + if (typeof options.decrypt !== "function" && link.k) throw Error("encrypted manifest requires decrypt callback"); | | 523 | + if (link.k) raw = asBytes(await options.decrypt(raw, link)); | | 524 | + const child = decodeManifest(raw); | | 525 | + if (child.t !== 1) throw Error("file child type mismatch"); | | 526 | + const before = used; | | 527 | + await readNode(child, depth + 1); | | 528 | + if (used - before !== link.s) throw Error("file manifest size mismatch"); | | 529 | + } else { | | 530 | + if (link.k && typeof options.decrypt !== "function") throw Error("encrypted chunk requires decrypt callback"); | | 531 | + const data = link.k ? asBytes(await options.decrypt(raw, link)) : raw; | | 532 | + if (data.length !== link.s) throw Error("file chunk size mismatch"); | | 533 | + used += data.length; | | 534 | + if (used > maxBytes) throw Error("file byte limit exceeded"); | | 535 | + parts.push(data); | | 536 | + } | | 537 | + active.delete(hash); | | 538 | + } | | 539 | + }; | | 540 | + await readNode(root, 0); | | 541 | + const out = new Uint8Array(used); | | 542 | + let at = 0; | | 543 | + for (const p of parts) { | | 544 | + out.set(p, at); | | 545 | + at += p.length; | | 546 | + } | | 547 | + return out; | | 548 | + }; | | 549 | + globalThis.TinyBlossomManifests = Object.freeze({ CHUNK_SIZE, MAX_LINKS, encodeManifest, decodeManifest, encode: encodeManifest, decode: decodeManifest, manifestHash, buildFile, buildDirectory, flattenDirectory, resolveDirectory, readFile, sha256, hex }); | | 550 | + })(); | | 551 | +})(); | | 552 | diff --git a/internal/webui/blossom-upload.js b/internal/webui/blossom-upload.js | | 553 | new file mode 100644 | | 554 | index 0000000..a5b8acb | | 555 | --- /dev/null | | 556 | +++ b/internal/webui/blossom-upload.js | | 557 | @@ -0,0 +1,160 @@ | | 558 | +// BUD-14 draft multipart uploads. | | 559 | +// https://raw.githubusercontent.com/hzrd149/blossom/multi-part-uploads-2/buds/14.md | | 560 | +// Upload state is deliberately held by the caller. It does not survive a page | | 561 | +// navigation because BUD-14 has no offset discovery protocol. | | 562 | +(() => { | | 563 | + "use strict"; | | 564 | + | | 565 | + const sha256 = async bytes => new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)); | | 566 | + const hex = bytes => Array.from(bytes, value => value.toString(16).padStart(2, "0")).join(""); | | 567 | + const asBytes = async value => { | | 568 | + if (value instanceof Uint8Array) return value; | | 569 | + if (ArrayBuffer.isView(value)) return new Uint8Array(value.buffer, value.byteOffset, value.byteLength); | | 570 | + if (value instanceof ArrayBuffer) return new Uint8Array(value); | | 571 | + if (value instanceof Blob) return new Uint8Array(await value.arrayBuffer()); | | 572 | + throw new TypeError("expected Blob, ArrayBuffer, or Uint8Array"); | | 573 | + }; | | 574 | + const responseBody = async response => { | | 575 | + try { return await response.json(); } catch { return null; } | | 576 | + }; | | 577 | + const abortError = () => { const error = new Error("upload canceled"); error.name = "AbortError"; return error; }; | | 578 | + const verifyDescriptor = (descriptor, hash, size) => { | | 579 | + if (!descriptor || typeof descriptor !== "object" || descriptor.sha256 !== hash || descriptor.size !== size) throw new Error("invalid Blossom upload descriptor"); | | 580 | + return descriptor; | | 581 | + }; | | 582 | + | | 583 | + const uploadTask = async (source, options = {}, controller) => { | | 584 | + const bytes = await asBytes(source); | | 585 | + const hash = options.hash || hex(await sha256(bytes)); | | 586 | + if (!/^[0-9a-f]{64}$/.test(hash)) throw new Error("expected a lowercase SHA-256 hash"); | | 587 | + if (options.hash && hash !== hex(await sha256(bytes))) throw new Error("upload hash mismatch"); | | 588 | + const type = options.type || source.type || "application/octet-stream"; | | 589 | + const base = options.url || options.endpoint; | | 590 | + if (typeof base !== "string" || base === "") throw new Error("upload URL is required"); | | 591 | + let parsed; | | 592 | + try { parsed = new URL(base, globalThis.location?.href || "https://blossom.invalid"); } catch { throw new Error("invalid upload URL"); } | | 593 | + if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error("upload URL must use HTTP or HTTPS"); | | 594 | + if (parsed.username || parsed.password || parsed.hash || parsed.search) throw new Error("upload URL must not contain credentials, query, or fragment"); | | 595 | + const extension = options.extension ? String(options.extension).replace(/^\.+/, "") : ""; | | 596 | + if (extension && !/^[a-z0-9]{1,8}$/.test(extension)) throw new Error("invalid file extension"); | | 597 | + const path = parsed.pathname; | | 598 | + const target = new RegExp("/" + hash + "(?:\\.[a-z0-9]{1,8})?/?$").test(path) ? base : base.replace(/\/$/, "") + "/" + hash + (extension ? "." + extension : ""); | | 599 | + const url = target; | | 600 | + const fetcher = options.fetch || globalThis.fetch; | | 601 | + if (typeof fetcher !== "function") throw new Error("fetch is unavailable"); | | 602 | + const chunkSize = Number.isSafeInteger(options.chunkSize) && options.chunkSize > 0 ? options.chunkSize : 5 * 1024 * 1024; | | 603 | + const timeoutMs = Number.isSafeInteger(options.timeoutMs) && options.timeoutMs > 0 ? options.timeoutMs : 30000; | | 604 | + const retries = Number.isSafeInteger(options.retries) && options.retries >= 0 ? options.retries : 0; | | 605 | + const state = options.state instanceof Map ? options.state : new Map(); | | 606 | + const notify = (offset, length, status) => options.onState?.({offset, length, status, state}); | | 607 | + const progress = sent => options.onProgress?.({sent, total: bytes.byteLength, fraction: bytes.byteLength ? sent / bytes.byteLength : 1}); | | 608 | + const request = async (method, body, headers, authBody = body) => { | | 609 | + if (controller.signal.aborted) throw abortError(); | | 610 | + const requestController = new AbortController(); | | 611 | + let timer, rejectCanceled; | | 612 | + const cancel = () => { requestController.abort(); rejectCanceled?.(abortError()); }; | | 613 | + controller.signal.addEventListener("abort", cancel, {once: true}); | | 614 | + try { | | 615 | + return await Promise.race([ | | 616 | + (async () => { | | 617 | + const requestHeaders = {...headers}; | | 618 | + if (options.authorize && method !== "OPTIONS") { | | 619 | + const value = await options.authorize(url, method, authBody, {hash, type}); | | 620 | + if (typeof value === "string") requestHeaders.authorization = value; | | 621 | + else if (value && typeof value === "object") Object.assign(requestHeaders, value); | | 622 | + } | | 623 | + if (requestController.signal.aborted) throw abortError(); | | 624 | + const response = await fetcher(url, {method, headers: requestHeaders, body: method === "OPTIONS" ? undefined : body, signal: requestController.signal}); | | 625 | + // Keep the request deadline and cancellation active until the | | 626 | + // descriptor body has arrived, not only until headers arrive. | | 627 | + const descriptor = response.status === 200 || response.status === 201 ? await responseBody(response) : null; | | 628 | + if (!descriptor) await response.body?.cancel?.(); | | 629 | + return {status: response.status, ok: response.ok, headers: response.headers, json: async () => descriptor}; | | 630 | + })(), | | 631 | + new Promise((_, reject) => { | | 632 | + timer = setTimeout(() => { requestController.abort(); reject(Error("upload request timed out")); }, timeoutMs); | | 633 | + }), | | 634 | + new Promise((_, reject) => { rejectCanceled = reject; }) | | 635 | + ]); | | 636 | + } finally { | | 637 | + clearTimeout(timer); | | 638 | + controller.signal.removeEventListener("abort", cancel); | | 639 | + requestController.abort(); | | 640 | + } | | 641 | + }; | | 642 | + const retry = async (action, offset, length) => { | | 643 | + let attempt = 0; | | 644 | + while (true) { | | 645 | + if (controller.signal.aborted) throw abortError(); | | 646 | + try { | | 647 | + const result = await action(); | | 648 | + if (!result.ok && result.status >= 500 && attempt++ < retries) { notify(offset, length, "retrying"); continue; } | | 649 | + return result; | | 650 | + } catch (error) { | | 651 | + if (controller.signal.aborted) throw abortError(); | | 652 | + if (attempt++ >= retries) throw error; | | 653 | + notify(offset, length, "retrying"); | | 654 | + } | | 655 | + } | | 656 | + }; | | 657 | + | | 658 | + let supportsPatch = options.probe === false; | | 659 | + if (options.probe !== false) { | | 660 | + try { | | 661 | + const response = await request("OPTIONS", null, {}); | | 662 | + supportsPatch = /(?:^|,|\s)PATCH(?:,|\s|$)/i.test(response.headers?.get?.("allow") || ""); | | 663 | + } catch { supportsPatch = false; } | | 664 | + } | | 665 | + if (!supportsPatch) { | | 666 | + const response = await retry(() => request("PUT", bytes, {"content-type": type}), 0, bytes.byteLength); | | 667 | + if (!response.ok) throw new Error("Blossom upload failed: " + response.status); | | 668 | + return {hash, descriptor: verifyDescriptor(await responseBody(response), hash, bytes.byteLength), state}; | | 669 | + } | | 670 | + | | 671 | + const metadata = state.meta; | | 672 | + if (!metadata || metadata.hash !== hash || metadata.size !== bytes.byteLength || metadata.chunkSize !== chunkSize || metadata.url !== url || metadata.type !== type || Date.now() - (metadata.lastAcceptedAt || 0) >= 60000) { | | 673 | + state.clear(); | | 674 | + state.meta = {hash, size: bytes.byteLength, chunkSize, url, type, lastAcceptedAt: 0}; | | 675 | + } | | 676 | + let sent = 0; | | 677 | + let replayed = false; | | 678 | + while (true) { | | 679 | + for (let offset = 0; offset < bytes.byteLength || (bytes.byteLength === 0 && offset === 0); offset += chunkSize) { | | 680 | + const end = Math.min(offset + chunkSize, bytes.byteLength); | | 681 | + const chunk = bytes.slice(offset, end); | | 682 | + const length = chunk.byteLength; | | 683 | + const prior = state.get(offset); | | 684 | + if (!replayed && prior?.status === 204 && Date.now() - (state.meta.lastAcceptedAt || 0) < 60000) { sent = end; progress(sent); continue; } | | 685 | + const result = await retry(() => request("PATCH", chunk, { | | 686 | + "upload-type": type, | | 687 | + "upload-length": String(bytes.byteLength), | | 688 | + "upload-offset": String(offset), | | 689 | + "content-type": "application/octet-stream" | | 690 | + }, options.authorizationMode === "final-hash" ? bytes : chunk), offset, length); | | 691 | + if (![200, 201, 204].includes(result.status)) throw new Error("Blossom chunk upload failed: " + result.status); | | 692 | + state.set(offset, {offset, length, status: result.status}); | | 693 | + state.meta.lastAcceptedAt = Date.now(); | | 694 | + sent = end; | | 695 | + notify(offset, length, result.status === 204 ? "uploaded" : "complete"); | | 696 | + progress(sent); | | 697 | + if (result.status !== 204) return {hash, descriptor: verifyDescriptor(await responseBody(result), hash, bytes.byteLength), state}; | | 698 | + if (bytes.byteLength === 0) break; | | 699 | + } | | 700 | + if (!replayed) { replayed = true; continue; } | | 701 | + throw new Error("multipart upload ended without completion"); | | 702 | + } | | 703 | + }; | | 704 | + | | 705 | + const upload = (source, options = {}) => { | | 706 | + const controller = new AbortController(); | | 707 | + if (options.signal) { | | 708 | + if (options.signal.aborted) controller.abort(); | | 709 | + else options.signal.addEventListener("abort", () => controller.abort(), {once: true}); | | 710 | + } | | 711 | + const task = uploadTask(source, options, controller); | | 712 | + task.cancel = () => controller.abort(); | | 713 | + return task; | | 714 | + }; | | 715 | + | | 716 | + globalThis.TinyBlossomUpload = Object.freeze({upload}); | | 717 | +})(); | | 718 | diff --git a/internal/webui/bridge.js b/internal/webui/bridge.js | | 719 | index c3fc81f..c193ea9 100644 | | 720 | --- a/internal/webui/bridge.js | | 721 | +++ b/internal/webui/bridge.js | | 722 | @@ -34,7 +34,7 @@ | | 723 | const event = await window.nostr.signEvent({ | | 724 | kind: 27235, | | 725 | created_at: Math.floor(Date.now() / 1000), | | 726 | - tags: [["u", url], ["method", method], ["payload", await sha256hex(bytes)]], | | 727 | + tags: [["u", url], ["method", method], ["payload", await sha256hex(bytes)], ["nonce", randomHex()]], | | 728 | content: "" | | 729 | }); | | 730 | return "Nostr " + btoa(JSON.stringify(event)); | | 731 | diff --git a/internal/webui/browse.html b/internal/webui/browse.html | | 732 | index c760f71..eaecf22 100644 | | 733 | --- a/internal/webui/browse.html | | 734 | +++ b/internal/webui/browse.html | | 735 | @@ -14,6 +14,8 @@ | | 736 | | | 737 | {{define "files"}}<section> | | 738 | <h1>Files</h1> | | 739 | +<form method="get" action="/files" id="file-search"><label>Search <input name="q" value="{{.Query.Get "q"}}" placeholder="hash, type or uploader" autocomplete="off"></label> <button>Search</button>{{if .Query.Get "q"}} <a href="/files">clear</a>{{end}}</form> | | 740 | +{{if .Actor}}<div id="file-actions"><h2>Store a file</h2><signed-form action="/upload" method="PUT"><label>File <input type="file" name="file" required></label><button>Upload</button></signed-form><file-mirror action="/mirror"></file-mirror><file-tools></file-tools><file-workspace></file-workspace><p><small>Encrypted uploads keep the file contents and key in your browser. The relay stores only encrypted bytes. Keep the complete share link to decrypt the file later.</small></p><noscript><p>Connect a signer and enable JavaScript to authorize uploads. Folder manifests require JavaScript.</p></noscript></div>{{end}} | | 741 | {{if .Error}}{{template "alert" .Error}} | | 742 | {{else if .Feed}}<table id="rows"> | | 743 | <thead><tr><th>hash</th><th>type</th><th>uploaded</th></tr></thead> | | 744 | @@ -36,6 +38,9 @@ | | 745 | {{with index $file "blossom_uri"}}<tr><th>blossom</th><td><code>{{.}}</code></td></tr>{{end}} | | 746 | <tr><th>download</th><td><a fx-ignore href="/files/raw?hash={{urlquery (str (index $file "sha256"))}}">raw</a></td></tr> | | 747 | </table> | | 748 | +<file-workspace-root hash="{{str (index $file "sha256")}}" type="{{str (index $file "type")}}" hidden></file-workspace-root> | | 749 | +<noscript><p>Enable JavaScript to decrypt files and browse folder manifests locally. Raw downloads remain available above.</p></noscript> | | 750 | +<file-tools hash="{{str (index $file "sha256")}}" type="{{str (index $file "type")}}" size="{{str (index $file "size")}}" name="{{shortID (index $file "sha256")}}"></file-tools> | | 751 | {{$type := str (index $file "type")}}{{$raw := printf "/files/raw?hash=%s" (urlquery (str (index $file "sha256")))}}{{if hasPrefix $type "image/"}}<div id="media"><img src="{{$raw}}" alt=""></div> | | 752 | {{else if hasPrefix $type "video/"}}<div id="media"><video controls src="{{$raw}}"></video></div> | | 753 | {{else if hasPrefix $type "audio/"}}<div id="media"><audio controls src="{{$raw}}"></audio></div> | | 754 | diff --git a/internal/webui/components.js b/internal/webui/components.js | | 755 | index 3e91365..af07cd6 100644 | | 756 | --- a/internal/webui/components.js | | 757 | +++ b/internal/webui/components.js | | 758 | @@ -18,6 +18,12 @@ | | 759 | // Tables sit inside a <scroll-box> so wide rows scroll sideways. | | 760 | // <nostr-key hex="…"> | | 761 | // Shortened key with the full hex as title; click copies it. | | 762 | +// <file-mirror action="/mirror"> | | 763 | +// Signed BUD-04 mirror of a URL or Blossom URI into this relay. | | 764 | +// <file-tools [hash="…"] [type="…"]> | | 765 | +// Copies Blossom links, encrypts and uploads a file, decrypts a share | | 766 | +// link fragment and sends a random-key file as a NIP-17 message. | | 767 | +// <file-workspace> and <file-workspace-root> live in file-workspace.js. | | 768 | (() => { | | 769 | "use strict"; | | 770 | | | 771 | @@ -167,6 +173,222 @@ | | 772 | } | | 773 | } | | 774 | | | 775 | + class FileMirror extends FormElement { | | 776 | + async submit(form) { | | 777 | + let source = form.elements.namedItem("url").value.trim(); | | 778 | + if (!source) throw Error("Enter a URL or Blossom URI."); | | 779 | + if (/^blossom:/i.test(source) && new URLSearchParams(source.split("?", 2)[1] || "").has("k")) throw Error("Encrypted Blossom URIs keep k client side; paste the public URI without its key."); | | 780 | + if (/^https?:/i.test(source)) { const url = new URL(source); url.hash = ""; source = url.href; } | | 781 | + const action = this.getAttribute("action") || "/mirror"; | | 782 | + const body = JSON.stringify({url: source}); | | 783 | + this.report("Signing…"); | | 784 | + const response = await tiny.signedFetch(action, "PUT", body, {contentType: "application/json"}); | | 785 | + let result; | | 786 | + try { result = await response.json(); } catch { result = await response.text(); } | | 787 | + this.report("Stored."); | | 788 | + const view = el("json-view"); | | 789 | + view.value = result; | | 790 | + this.append(view); | | 791 | + } | | 792 | + | | 793 | + connectedCallback() { | | 794 | + if (this.form) return; | | 795 | + this.innerHTML = '<form><label>URL or Blossom URI <input name="url" type="text" placeholder="https://… or blossom:…" required></label><button>Mirror</button></form>'; | | 796 | + super.connectedCallback(); | | 797 | + } | | 798 | + } | | 799 | + | | 800 | + const b64url = bytes => { | | 801 | + let binary = ""; | | 802 | + bytes.forEach(byte => { binary += String.fromCharCode(byte); }); | | 803 | + return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); | | 804 | + }; | | 805 | + const fromB64url = value => { | | 806 | + const text = value.replace(/-/g, "+").replace(/_/g, "/") + "===".slice((value.length + 3) % 4); | | 807 | + return Uint8Array.from(atob(text), char => char.charCodeAt(0)); | | 808 | + }; | | 809 | + | | 810 | + class FileTools extends HTMLElement { | | 811 | + disconnectedCallback() { | | 812 | + if (this.downloadURL) URL.revokeObjectURL(this.downloadURL); | | 813 | + this.uploadTask?.cancel?.(); | | 814 | + this.uploadController?.abort(); | | 815 | + this.uploadCanceled = true; | | 816 | + } | | 817 | + | | 818 | + connectedCallback() { | | 819 | + if (this.bound) return; | | 820 | + this.bound = true; | | 821 | + this.innerHTML = '<h3>File tools</h3><p><button type="button" data-copy>Copy Blossom URI</button> <button type="button" data-link>Copy share link</button></p><form><label>Encrypt a file <input type="file" name="file"></label><label>Encryption <select name="scheme"><option value="random">Random key</option><option value="chk">Deduplicated key</option></select></label><button>Encrypt and upload</button></form><p><button type="button" data-upload-cancel disabled>Cancel upload</button> <button type="button" data-upload-retry disabled>Retry upload</button></p><p><small>Random keys keep identical files distinct. Deduplication reveals matching files and allows guesses about predictable content.</small></p><form data-share><label>Send to <input name="recipient" inputmode="text" placeholder="npub or hex pubkey"></label><button>Send privately</button></form><label>Share link <input data-share-url readonly></label><output role="status"></output>'; | | 822 | + this.output = this.querySelector("output"); | | 823 | + this.querySelector("[data-copy]").addEventListener("click", () => this.copy(this.blossomURI())); | | 824 | + this.querySelector("[data-link]").addEventListener("click", () => this.copy(this.shareLink())); | | 825 | + this.querySelector("form:not([data-share])").addEventListener("submit", event => { event.preventDefault(); this.encryptUpload(event.currentTarget); }); | | 826 | + this.querySelector("[data-upload-cancel]").addEventListener("click", () => { this.uploadCanceled = true; this.uploadTask?.cancel?.(); this.uploadController?.abort(); }); | | 827 | + this.querySelector("[data-upload-retry]").addEventListener("click", () => this.retryUpload()); | | 828 | + this.querySelector("[data-share]").addEventListener("submit", event => { event.preventDefault(); this.share(event.currentTarget); }); | | 829 | + this.updateFileControls(); | | 830 | + this.decryptFromFragment(); | | 831 | + } | | 832 | + | | 833 | + hash() { return this.fileHash || this.getAttribute("hash") || ""; } | | 834 | + type() { return this.getAttribute("type") || "application/octet-stream"; } | | 835 | + updateFileControls() { | | 836 | + const available = Boolean(this.hash()); | | 837 | + this.querySelectorAll("[data-copy], [data-link], [data-share] button").forEach(button => { button.disabled = !available; }); | | 838 | + } | | 839 | + blossomURI() { | | 840 | + const params = new URLSearchParams(this.fileFragment || location.hash.slice(1)); | | 841 | + const extension = params.has("manifest") ? (["2", "3"].includes(params.get("node") || "2") ? "bdir" : params.get("node") === "1" ? "bfile" : "bin") : "bin"; | | 842 | + const uri = (params.get("enc") === "chk-v1" || params.has("manifest")) && params.get("key") ? TinyBlossomEncryption.createURI(this.hash(), extension, params.get("key")) : "blossom:" + this.hash(); | | 843 | + return tiny.root ? uri : uri + (uri.includes("?") ? "&" : "?") + "xs=" + encodeURIComponent(new URL(location.href).host); | | 844 | + } | | 845 | + shareLink() { | | 846 | + if (this.fileShareLink) return this.fileShareLink; | | 847 | + const fragment = this.fileFragment || location.hash.slice(1); | | 848 | + const params = new URLSearchParams(fragment); | | 849 | + if ((params.get("key") && params.get("iv")) || ((params.get("enc") === "chk-v1" || params.has("manifest")) && params.get("key"))) return location.href.split("#", 1)[0] + "#" + fragment; | | 850 | + return location.href.split("#", 1)[0] + "#blossom=" + encodeURIComponent(this.hash()); | | 851 | + } | | 852 | + async copy(value) { | | 853 | + try { await navigator.clipboard.writeText(value); this.say("Copied."); } | | 854 | + catch { this.say(value); } | | 855 | + } | | 856 | + say(value, error) { this.output.textContent = value; if (error) this.output.dataset.error = ""; else delete this.output.dataset.error; } | | 857 | + | | 858 | + uploadBusy(value) { | | 859 | + this.busy = value; | | 860 | + this.querySelector("[data-upload-cancel]").disabled = !value; | | 861 | + this.querySelector("[data-upload-retry]").disabled = value || !this.pendingUpload; | | 862 | + } | | 863 | + | | 864 | + async encryptUpload(form) { | | 865 | + if (this.busy) return; | | 866 | + const file = form.elements.namedItem("file").files[0]; | | 867 | + if (!file) { this.say("Choose a file first.", true); return; } | | 868 | + this.uploadBusy(true); | | 869 | + this.uploadCanceled = false; | | 870 | + try { | | 871 | + if (file.size > 256 * 1024 * 1024) throw Error("Encrypted browser uploads are limited to 256 MiB."); | | 872 | + this.say("Encrypting and signing…"); | | 873 | + const scheme = form.elements.namedItem("scheme").value; | | 874 | + const plaintext = new Uint8Array(await file.arrayBuffer()); | | 875 | + const plaintextHash = await tiny.sha256hex(plaintext); | | 876 | + let ciphertext, fragment; | | 877 | + if (scheme === "chk") { | | 878 | + if (!globalThis.TinyBlossomEncryption) throw Error("CHK encryption is unavailable. Reload this page."); | | 879 | + const encrypted = await TinyBlossomEncryption.encryptCHK(plaintext); | | 880 | + ciphertext = encrypted.ciphertext; | | 881 | + fragment = "enc=chk-v1&key=" + encrypted.key; | | 882 | + } else { | | 883 | + const key = await crypto.subtle.generateKey({name: "AES-GCM", length: 256}, true, ["encrypt", "decrypt"]); | | 884 | + const iv = crypto.getRandomValues(new Uint8Array(12)); | | 885 | + ciphertext = new Uint8Array(await crypto.subtle.encrypt({name: "AES-GCM", iv}, key, plaintext)); | | 886 | + const rawKey = new Uint8Array(await crypto.subtle.exportKey("raw", key)); | | 887 | + fragment = "key=" + b64url(rawKey) + "&iv=" + b64url(iv); | | 888 | + } | | 889 | + fragment += "&name=" + encodeURIComponent(file.name) + "&type=" + encodeURIComponent(file.type || "application/octet-stream") + "&ox=" + plaintextHash; | | 890 | + this.pendingUpload = {ciphertext, fragment, file, state: new Map()}; | | 891 | + if (this.uploadCanceled) throw Error("Upload canceled."); | | 892 | + await this.storePreparedUpload(); | | 893 | + } catch (error) { this.say("Upload stopped: " + error.message + (this.pendingUpload ? " Retry while this page stays open." : ""), true); } | | 894 | + finally { this.uploadBusy(false); } | | 895 | + } | | 896 | + | | 897 | + async retryUpload() { | | 898 | + if (this.busy || !this.pendingUpload) return; | | 899 | + this.uploadBusy(true); | | 900 | + this.uploadCanceled = false; | | 901 | + try { await this.storePreparedUpload(); } | | 902 | + catch (error) { this.say("Upload stopped: " + error.message + " Retry while this page stays open.", true); } | | 903 | + finally { this.uploadBusy(false); } | | 904 | + } | | 905 | + | | 906 | + async storePreparedUpload() { | | 907 | + const {ciphertext, fragment, file, state} = this.pendingUpload; | | 908 | + const actualHash = await tiny.sha256hex(ciphertext); | | 909 | + this.uploadController = new AbortController(); | | 910 | + let descriptor; | | 911 | + if (globalThis.TinyBlossomUpload?.upload) { | | 912 | + const task = TinyBlossomUpload.upload(ciphertext, { | | 913 | + url: new URL(tiny.localPath("/"), location.href).href, hash: actualHash, | | 914 | + type: "application/octet-stream", state, signal: this.uploadController.signal, | | 915 | + authorize: (url, verb, body) => tiny.authorization(url, verb, body), | | 916 | + onProgress: progress => this.say("Uploading " + Math.round(progress.fraction * 100) + "%…") | | 917 | + }); | | 918 | + this.uploadTask = task; | | 919 | + descriptor = (await task).descriptor; | | 920 | + } else { | | 921 | + const response = await tiny.signedFetch("/upload", "PUT", ciphertext, {contentType: "application/octet-stream", signal: this.uploadController.signal}); | | 922 | + descriptor = await response.json(); | | 923 | + } | | 924 | + if (descriptor.sha256 !== actualHash) throw Error("relay returned an unexpected hash"); | | 925 | + this.fileHash = actualHash; | | 926 | + this.fileFragment = fragment; | | 927 | + this.setAttribute("hash", actualHash); | | 928 | + this.setAttribute("type", file.type || "application/octet-stream"); | | 929 | + this.setAttribute("size", String(ciphertext.length)); | | 930 | + this.updateFileControls(); | | 931 | + const link = new URL(location.href); | | 932 | + link.pathname = location.pathname.replace(/\/files?$/, "/file"); | | 933 | + link.search = "?hash=" + actualHash; | | 934 | + link.hash = fragment; | | 935 | + this.fileShareLink = link.href; | | 936 | + const share = this.querySelector("[data-share-url]"); | | 937 | + if (share) share.value = link.href; | | 938 | + this.pendingUpload = null; | | 939 | + this.uploadTask = null; | | 940 | + try { | | 941 | + if (!navigator.clipboard?.writeText) throw Error("Clipboard unavailable"); | | 942 | + await navigator.clipboard.writeText(link.href); | | 943 | + this.say("Encrypted file stored. Share link copied; the key is only in its fragment."); | | 944 | + } catch { | | 945 | + if (share) share.select?.(); | | 946 | + this.say("Encrypted file stored. Copy the share link above; the key is only in its fragment."); | | 947 | + } | | 948 | + } | | 949 | + | | 950 | + async decryptFromFragment() { | | 951 | + const params = new URLSearchParams(this.fileFragment || location.hash.slice(1)); | | 952 | + if ((!params.get("key") || !params.get("iv")) && !(params.get("enc") === "chk-v1" && params.get("key"))) return; | | 953 | + try { | | 954 | + this.say("Decrypting…"); | | 955 | + const response = await fetch(tiny.localPath("/files/raw?hash=" + encodeURIComponent(this.hash()))); | | 956 | + if (!response.ok) throw Error("download failed (" + response.status + ")"); | | 957 | + const encrypted = new Uint8Array(await response.arrayBuffer()); | | 958 | + if (await tiny.sha256hex(encrypted) !== this.hash()) throw Error("ciphertext hash mismatch"); | | 959 | + let plain; | | 960 | + if (params.get("enc") === "chk-v1") plain = await TinyBlossomEncryption.decryptCHK(encrypted, params.get("key"), this.hash()); | | 961 | + else { | | 962 | + const key = await crypto.subtle.importKey("raw", fromB64url(params.get("key")), "AES-GCM", false, ["decrypt"]); | | 963 | + plain = await crypto.subtle.decrypt({name: "AES-GCM", iv: fromB64url(params.get("iv"))}, key, encrypted); | | 964 | + } | | 965 | + if (params.get("ox") && await tiny.sha256hex(plain) !== params.get("ox")) throw Error("plaintext hash mismatch"); | | 966 | + if (this.downloadURL) URL.revokeObjectURL(this.downloadURL); | | 967 | + const link = el("a"); this.downloadURL = URL.createObjectURL(new Blob([plain], {type: params.get("type") || this.type()})); link.href = this.downloadURL; link.download = params.get("name") || "decrypted-file"; link.textContent = "download decrypted file"; this.append(link); this.say("Decrypted locally."); | | 968 | + } catch (error) { this.say("Could not decrypt this file: " + error.message, true); } | | 969 | + } | | 970 | + | | 971 | + async share(form) { | | 972 | + if (this.sharing) return; | | 973 | + const recipient = form.elements.namedItem("recipient").value.trim(); | | 974 | + if (!recipient) { this.say("Enter a public key or npub.", true); return; } | | 975 | + const params = new URLSearchParams(this.fileFragment || location.hash.slice(1)); | | 976 | + const signer = globalThis.tinySigner || globalThis.nostr; | | 977 | + if (!globalThis.TinyFileMessages || !signer) { this.say("Connect a NIP-44 signer first. Use the encrypted link, which keeps its key in the URL fragment.", true); return; } | | 978 | + if (!params.get("key") || !params.get("iv")) { this.say("NIP-17 sharing currently requires a random AES-GCM encrypted file.", true); return; } | | 979 | + this.sharing = true; | | 980 | + try { | | 981 | + const key = fromB64url(params.get("key")), nonce = fromB64url(params.get("iv")); | | 982 | + if (key.length !== 32 || nonce.length !== 12) throw Error("Invalid file key or nonce."); | | 983 | + const hex = bytes => Array.from(bytes, value => value.toString(16).padStart(2, "0")).join(""); | | 984 | + this.say("Preparing sealed NIP-17 delivery…"); | | 985 | + await TinyFileMessages.share({recipient, fileURL: new URL(tiny.localPath("/files/raw?hash=" + this.hash()), location.href).href, ciphertextHash: this.hash(), plaintextHash: params.get("ox") || undefined, key: hex(key), nonce: hex(nonce), mimeType: params.get("type") || this.type(), size: Number(this.getAttribute("size") || 0) || undefined}, {signer}); | | 986 | + this.say("Encrypted message delivered; sender copy saved."); | | 987 | + } catch (error) { this.say("NIP-17 delivery failed: " + error.message, true); } finally { this.sharing = false; } | | 988 | + } | | 989 | + } | | 990 | + | | 991 | class PublishList extends FormElement { | | 992 | async submit(form) { | | 993 | if (!window.nostr?.signEvent) throw Error("Connect a signer first."); | | 994 | @@ -549,6 +771,8 @@ | | 995 | customElements.define("connect-list", ConnectList); | | 996 | customElements.define("relay-lists", RelayLists); | | 997 | customElements.define("signed-form", SignedForm); | | 998 | + customElements.define("file-mirror", FileMirror); | | 999 | + customElements.define("file-tools", FileTools); | | 1000 | customElements.define("publish-list", PublishList); | | 1001 | customElements.define("nostr-key", NostrKey); | | 1002 | customElements.define("json-view", JsonView); | | 1003 | diff --git a/internal/webui/file-messages.js b/internal/webui/file-messages.js | | 1004 | new file mode 100644 | | 1005 | index 0000000..c6d9ba2 | | 1006 | --- /dev/null | | 1007 | +++ b/internal/webui/file-messages.js | | 1008 | @@ -0,0 +1,209 @@ | | 1009 | +/* NIP-17 file messages. This module never publishes to ordinary relays. */ | | 1010 | +(function (root) { | | 1011 | + "use strict"; | | 1012 | + | | 1013 | + const MAX_RELAYS = 8; | | 1014 | + const DAY = 86400; | | 1015 | + | | 1016 | + function api() { | | 1017 | + const signer = root.NostrSigner; | | 1018 | + if (!signer || !signer.nip44 || !signer.finalizeEvent || !signer.generateSecretKey) { | | 1019 | + throw new Error("NIP-44 support is unavailable"); | | 1020 | + } | | 1021 | + return signer; | | 1022 | + } | | 1023 | + | | 1024 | + function relayURL(value) { | | 1025 | + if (typeof value !== "string" || value.length > 2048) return null; | | 1026 | + let url; | | 1027 | + try { url = new URL(value); } catch { return null; } | | 1028 | + if (url.protocol !== "wss:" && url.protocol !== "ws:") return null; | | 1029 | + if (url.username || url.password || url.hash) return null; | | 1030 | + return url.toString().replace(/\/$/, ""); | | 1031 | + } | | 1032 | + | | 1033 | + function relayList(event, pubkey) { | | 1034 | + if (Array.isArray(event)) { | | 1035 | + event = event.filter((candidate) => candidate?.kind === 10050 && candidate.pubkey === pubkey) | | 1036 | + .sort((a, b) => b.created_at - a.created_at)[0]; | | 1037 | + } | | 1038 | + if (!event || event.kind !== 10050 || event.pubkey !== pubkey) { | | 1039 | + throw new Error("recipient has no valid kind 10050 relay list"); | | 1040 | + } | | 1041 | + const signer = api(); | | 1042 | + if (!signer.verifyEvent(event)) throw new Error("invalid kind 10050 relay list signature"); | | 1043 | + const relays = [...new Set(event.tags.filter((tag) => tag[0] === "relay").map((tag) => relayURL(tag[1])).filter(Boolean))]; | | 1044 | + if (!relays.length) throw new Error("kind 10050 relay list is empty"); | | 1045 | + return relays.slice(0, MAX_RELAYS); | | 1046 | + } | | 1047 | + | | 1048 | + function normalizePubkey(value) { | | 1049 | + const text = String(value || "").trim(); | | 1050 | + if (/^[0-9a-f]{64}$/i.test(text)) return text.toLowerCase(); | | 1051 | + const decoded = api().decodeNpub?.(text); | | 1052 | + if (typeof decoded === "string" && /^[0-9a-f]{64}$/i.test(decoded)) return decoded.toLowerCase(); | | 1053 | + throw new Error("public key must be a 64 character hex key or npub"); | | 1054 | + } | | 1055 | + | | 1056 | + async function bounded(value, timeout, label) { | | 1057 | + const ms = Number.isFinite(timeout) && timeout > 0 ? timeout : 10000; | | 1058 | + let timer; | | 1059 | + try { | | 1060 | + return await Promise.race([Promise.resolve(value), new Promise((_, reject) => { | | 1061 | + timer = setTimeout(() => reject(new Error(label + " timed out")), ms); | | 1062 | + })]); | | 1063 | + } finally { clearTimeout(timer); } | | 1064 | + } | | 1065 | + | | 1066 | + function sent(result) { | | 1067 | + if (result === false || result == null) return false; | | 1068 | + if (Array.isArray(result)) return result.some(sent); | | 1069 | + if (typeof result === "object" && "ok" in result) return result.ok === true; | | 1070 | + return true; | | 1071 | + } | | 1072 | + | | 1073 | + function past(now) { | | 1074 | + return Math.max(0, Math.floor(now) - Math.floor(Math.random() * (2 * DAY + 1))); | | 1075 | + } | | 1076 | + | | 1077 | + async function publicKey(signer, getPublicKey) { | | 1078 | + const key = getPublicKey ? await getPublicKey() : await signer.getPublicKey(); | | 1079 | + if (typeof key !== "string" || !/^[0-9a-f]{64}$/i.test(key)) throw new Error("invalid signer public key"); | | 1080 | + return key.toLowerCase(); | | 1081 | + } | | 1082 | + | | 1083 | + async function queryRelayList(pubkey, options) { | | 1084 | + if (!root.tiny?.signedFetch) throw new Error("signed local query is unavailable"); | | 1085 | + const response = await root.tiny.signedFetch("/query", "POST", JSON.stringify([{ kinds: [10050], authors: [pubkey], limit: 1 }]), { contentType: "application/json" }); | | 1086 | + return response.json(); | | 1087 | + } | | 1088 | + | | 1089 | + function publishEvent(event, relays, options) { | | 1090 | + const signer = options.signer || root.tinySigner || root.nostr; | | 1091 | + const WebSocketImpl = options.WebSocket || root.WebSocket; | | 1092 | + if (typeof WebSocketImpl !== "function") throw new Error("WebSocket support is unavailable"); | | 1093 | + const timeout = Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? options.timeoutMs : 10000; | | 1094 | + return Promise.all(relays.map((url) => new Promise((resolve) => { | | 1095 | + let socket; | | 1096 | + let timer; | | 1097 | + let settled = false; | | 1098 | + let authID = ""; | | 1099 | + let authAttempts = 0; | | 1100 | + let eventSent = false; | | 1101 | + const finish = (ok) => { if (settled) return; settled = true; clearTimeout(timer); try { socket?.close(); } catch {} resolve(ok); }; | | 1102 | + const sendEvent = () => { if (!settled && !eventSent) { eventSent = true; socket.send(JSON.stringify(["EVENT", event])); } }; | | 1103 | + try { | | 1104 | + socket = new WebSocketImpl(url); | | 1105 | + timer = setTimeout(() => finish(false), timeout); | | 1106 | + socket.onopen = sendEvent; | | 1107 | + socket.onmessage = async (message) => { | | 1108 | + let frame; | | 1109 | + try { frame = JSON.parse(typeof message.data === "string" ? message.data : ""); } catch { return; } | | 1110 | + if (settled || !Array.isArray(frame)) return; | | 1111 | + if (frame[0] === "OK" && frame[1] === authID) { | | 1112 | + if (frame[2] === true) { eventSent = false; sendEvent(); } else finish(false); | | 1113 | + return; | | 1114 | + } | | 1115 | + if (frame[0] === "OK" && frame[1] === event.id) { | | 1116 | + if (frame[2] !== true && String(frame[3] || "").toLowerCase().startsWith("auth-required")) { eventSent = false; return; } | | 1117 | + finish(frame[2] === true); return; | | 1118 | + } | | 1119 | + if (frame[0] !== "AUTH" || typeof signer?.signEvent !== "function") return; | | 1120 | + if (typeof frame[1] !== "string" || frame[1].length > 4096) { finish(false); return; } | | 1121 | + if (authAttempts++ >= 1) { finish(false); return; } | | 1122 | + try { | | 1123 | + const auth = await signer.signEvent({ kind: 22242, created_at: Math.floor(Date.now() / 1000), tags: [["relay", url], ["challenge", frame[1]], ["method", "AUTH"]], content: "" }); | | 1124 | + if (auth && !settled && auth.kind === 22242 && auth.content === "" && auth.tags.length === 3 && auth.tags[0][0] === "relay" && auth.tags[0][1] === url && auth.tags[1][0] === "challenge" && auth.tags[1][1] === frame[1] && auth.tags[2][0] === "method" && auth.tags[2][1] === "AUTH" && api().verifyEvent(auth) && auth.pubkey === await publicKey(signer)) { authID = auth.id; socket.send(JSON.stringify(["AUTH", auth])); } | | 1125 | + else finish(false); | | 1126 | + } catch { finish(false); } | | 1127 | + }; | | 1128 | + socket.onerror = () => finish(false); | | 1129 | + socket.onclose = () => finish(false); | | 1130 | + } catch { finish(false); } | | 1131 | + }))); | | 1132 | + } | | 1133 | + | | 1134 | + function unsignedRumor(input, sender, now) { | | 1135 | + if (!input.fileURL || !/^https?:\/\//i.test(input.fileURL)) throw new Error("fileURL must be an HTTP(S) URL"); | | 1136 | + if (!/^[0-9a-f]{64}$/i.test(input.ciphertextHash || "")) throw new Error("ciphertextHash must be SHA-256 hex"); | | 1137 | + if (!input.key || !input.nonce) throw new Error("encrypted file messages require a decryption key and nonce"); | | 1138 | + const tags = [["p", input.recipient]]; | | 1139 | + if (input.mimeType) tags.push(["file-type", input.mimeType]); | | 1140 | + tags.push(["encryption-algorithm", "aes-gcm"]); | | 1141 | + if (input.key) tags.push(["decryption-key", input.key]); | | 1142 | + if (input.nonce) tags.push(["decryption-nonce", input.nonce]); | | 1143 | + tags.push(["x", input.ciphertextHash.toLowerCase()]); | | 1144 | + if (input.plaintextHash) { | | 1145 | + if (!/^[0-9a-f]{64}$/i.test(input.plaintextHash)) throw new Error("plaintextHash must be SHA-256 hex"); | | 1146 | + tags.push(["ox", input.plaintextHash.toLowerCase()]); | | 1147 | + } | | 1148 | + if (input.size != null) tags.push(["size", String(input.size)]); | | 1149 | + const rumor = { pubkey: sender, created_at: Math.floor(now), kind: 15, tags, content: input.fileURL }; | | 1150 | + rumor.id = api().getEventHash(rumor); | | 1151 | + return rumor; | | 1152 | + } | | 1153 | + | | 1154 | + async function encryptFor(signer, recipient, plaintext) { | | 1155 | + if (typeof signer.nip44Encrypt === "function") return signer.nip44Encrypt(recipient, plaintext); | | 1156 | + if (signer.nip44?.encrypt) return signer.nip44.encrypt(recipient, plaintext); | | 1157 | + throw new Error("signer has no NIP-44 encrypt method"); | | 1158 | + } | | 1159 | + | | 1160 | + async function seal(rumor, recipient, signer, now) { | | 1161 | + const content = await encryptFor(signer, recipient, JSON.stringify(rumor)); | | 1162 | + const unsigned = { kind: 13, tags: [], content, created_at: past(now), pubkey: rumor.pubkey }; | | 1163 | + const event = await signer.signEvent(unsigned); | | 1164 | + if (!event || event.kind !== 13 || event.pubkey !== rumor.pubkey || event.tags.length || event.content !== unsigned.content || event.created_at !== unsigned.created_at) throw new Error("signer returned an invalid NIP-17 seal"); | | 1165 | + if (!api().verifyEvent(event)) throw new Error("signer returned an invalid seal signature"); | | 1166 | + return event; | | 1167 | + } | | 1168 | + | | 1169 | + function wrap(sealEvent, recipient, now) { | | 1170 | + const signer = api(); | | 1171 | + const secret = signer.generateSecretKey(); | | 1172 | + const pubkey = signer.getPublicKey(secret); | | 1173 | + const key = signer.nip44.getConversationKey(secret, recipient); | | 1174 | + const content = signer.nip44.encrypt(JSON.stringify(sealEvent), key); | | 1175 | + const event = signer.finalizeEvent({ kind: 1059, tags: [["p", recipient]], content, created_at: past(now) }, secret); | | 1176 | + if (!signer.verifyEvent(event)) throw new Error("failed to verify gift wrap"); | | 1177 | + return { event, secret, pubkey }; | | 1178 | + } | | 1179 | + | | 1180 | + async function build(input, options = {}) { | | 1181 | + const signer = options.signer || root.tinySigner || root.nostr; | | 1182 | + if (!signer || typeof signer.signEvent !== "function" || (typeof signer.nip44Encrypt !== "function" && !signer.nip44?.encrypt)) throw new Error("a NIP-44 signer is required"); | | 1183 | + const recipient = normalizePubkey(input.recipient); | | 1184 | + const sender = await publicKey(signer, options.getPublicKey); | | 1185 | + const now = (options.now || Date.now() / 1000); | | 1186 | + const rumor = unsignedRumor({ ...input, recipient }, sender, now); | | 1187 | + const receiverSeal = await seal(rumor, recipient, signer, now); | | 1188 | + const senderSeal = await seal(rumor, sender, signer, now); | | 1189 | + const receiver = wrap(receiverSeal, recipient, now).event; | | 1190 | + const own = wrap(senderSeal, sender, now).event; | | 1191 | + return { rumor, events: [{ recipient, event: receiver }, { recipient: sender, event: own }] }; | | 1192 | + } | | 1193 | + | | 1194 | + async function share(input, options = {}) { | | 1195 | + const signer = options.signer || root.tinySigner || root.nostr; | | 1196 | + const query = options.queryRelayList || ((pubkey) => queryRelayList(pubkey, options)); | | 1197 | + const publish = options.publish || ((event, relays) => publishEvent(event, relays, options)); | | 1198 | + if (typeof query !== "function" || typeof publish !== "function") throw new Error("private NIP-17 delivery is unavailable"); | | 1199 | + const sender = await publicKey(signer, options.getPublicKey); | | 1200 | + const recipient = normalizePubkey(input.recipient); | | 1201 | + const timeout = options.timeoutMs; | | 1202 | + const receiverList = relayList(await bounded(query(recipient), timeout, "recipient relay lookup"), recipient); | | 1203 | + const senderList = relayList(await bounded(query(sender), timeout, "sender relay lookup"), sender); | | 1204 | + const built = await build(input, options); | | 1205 | + if (built.rumor.pubkey !== sender) throw new Error("signer changed during file sharing; try again"); | | 1206 | + const deliveries = []; | | 1207 | + for (const target of built.events) { | | 1208 | + const relays = target.recipient === recipient ? receiverList : senderList; | | 1209 | + const delivery = await bounded(publish(target.event, relays), timeout, "NIP-17 delivery"); | | 1210 | + if (!sent(delivery)) throw new Error("NIP-17 delivery failed on every inbox relay"); | | 1211 | + deliveries.push(delivery); | | 1212 | + } | | 1213 | + return { ...built, relays: { recipient: receiverList, sender: senderList }, deliveries }; | | 1214 | + } | | 1215 | + | | 1216 | + root.TinyFileMessages = { build, share, relayList, queryRelayList, publishEvent }; | | 1217 | +})(globalThis); | | 1218 | diff --git a/internal/webui/file-workspace.js b/internal/webui/file-workspace.js | | 1219 | new file mode 100644 | | 1220 | index 0000000..17b9c1c | | 1221 | --- /dev/null | | 1222 | +++ b/internal/webui/file-workspace.js | | 1223 | @@ -0,0 +1,291 @@ | | 1224 | +// Draft BUD-16/17 trees. Every object written here is encrypted with BUD-15. | | 1225 | +(() => { | | 1226 | + "use strict"; | | 1227 | + | | 1228 | + const MAX_FILES = 10000; | | 1229 | + const MAX_DEPTH = 32; | | 1230 | + const MAX_FILE_BYTES = 256 * 1024 * 1024; | | 1231 | + const MAX_FOLDER_BYTES = 1024 * 1024 * 1024; | | 1232 | + const MAX_FETCH_BYTES = 16 * 1024 * 1024 + 16; | | 1233 | + const directoryType = "application/vnd.blossom.directory+msgpack"; | | 1234 | + const codec = () => globalThis.TinyBlossomManifests; | | 1235 | + const hex = value => codec().hex(value); | | 1236 | + const fromHex = value => { | | 1237 | + if (!/^[0-9a-f]{64}$/.test(value || "")) throw Error("Invalid hash or encryption key."); | | 1238 | + return Uint8Array.from(value.match(/../g), byte => parseInt(byte, 16)); | | 1239 | + }; | | 1240 | + const element = (tag, text) => { | | 1241 | + const node = document.createElement(tag); | | 1242 | + if (text !== undefined) node.textContent = text; | | 1243 | + return node; | | 1244 | + }; | | 1245 | + const checkAbort = signal => { | | 1246 | + if (signal?.aborted) throw Object.assign(Error("Upload canceled. Retry while this page stays open."), {name: "AbortError"}); | | 1247 | + }; | | 1248 | + const validName = name => typeof name === "string" && name.length > 0 && name.isWellFormed() && ![".", ".."].includes(name) && !/[\/\u0000]/.test(name) && new TextEncoder().encode(name).length <= 1024; | | 1249 | + const folderNode = () => ({directories: new Map(), files: new Map()}); | | 1250 | + | | 1251 | + function selectionTree(files) { | | 1252 | + if (!files.length || files.length > MAX_FILES) throw Error("Choose a folder with 1 to 10,000 files."); | | 1253 | + const root = folderNode(); | | 1254 | + let total = 0; | | 1255 | + const paths = files.map(file => (file.webkitRelativePath || file.name).split("/")); | | 1256 | + const commonRoot = paths.every(path => path.length > 1 && path[0] === paths[0][0]); | | 1257 | + for (let index = 0; index < files.length; index++) { | | 1258 | + const file = files[index]; | | 1259 | + const path = commonRoot ? paths[index].slice(1) : paths[index]; | | 1260 | + if (path.length > MAX_DEPTH || path.some(name => !validName(name))) throw Error("Invalid folder path."); | | 1261 | + if (!Number.isSafeInteger(file.size) || file.size < 0 || file.size > MAX_FILE_BYTES) throw Error("Files must be at most 256 MiB in the browser workspace."); | | 1262 | + total += file.size; | | 1263 | + if (total > MAX_FOLDER_BYTES) throw Error("The selected folder exceeds 1 GiB."); | | 1264 | + let directory = root; | | 1265 | + for (const name of path.slice(0, -1)) { | | 1266 | + if (directory.files.has(name)) throw Error("A file and folder use the same name."); | | 1267 | + if (!directory.directories.has(name)) directory.directories.set(name, folderNode()); | | 1268 | + directory = directory.directories.get(name); | | 1269 | + } | | 1270 | + const name = path.at(-1); | | 1271 | + if (directory.files.has(name) || directory.directories.has(name)) throw Error("Duplicate folder entry."); | | 1272 | + directory.files.set(name, file); | | 1273 | + } | | 1274 | + return {root, name: commonRoot ? paths[0][0] : "folder"}; | | 1275 | + } | | 1276 | + | | 1277 | + async function storeEncrypted(plaintext, type, signal) { | | 1278 | + checkAbort(signal); | | 1279 | + const encrypted = await TinyBlossomEncryption.encryptCHK(plaintext); | | 1280 | + checkAbort(signal); | | 1281 | + const url = new URL(tiny.localPath("/"), location.href).href; | | 1282 | + const result = await TinyBlossomUpload.upload(encrypted.ciphertext, { | | 1283 | + url, hash: encrypted.hash, type, signal, | | 1284 | + authorize: (target, method, body) => tiny.authorization(target, method, body) | | 1285 | + }); | | 1286 | + if (result.descriptor.sha256 !== encrypted.hash || result.descriptor.size !== encrypted.ciphertext.length) throw Error("Relay returned an unexpected blob descriptor."); | | 1287 | + return {hash: encrypted.hash, key: fromHex(encrypted.key)}; | | 1288 | + } | | 1289 | + | | 1290 | + const manifestStore = signal => async ({bytes, node}) => storeEncrypted(bytes, node.t === 2 || node.t === 3 ? directoryType : "application/octet-stream", signal); | | 1291 | + const rootReference = details => { | | 1292 | + const stored = details.manifests.at(-1); | | 1293 | + return {hash: stored.hash, key: stored.key, type: details.root.t, size: details.root.l.reduce((sum, link) => sum + link.s, 0)}; | | 1294 | + }; | | 1295 | + | | 1296 | + async function storeFile(file, signal, progress) { | | 1297 | + if (file.size > MAX_FILE_BYTES) throw Error("Files must be at most 256 MiB in the browser workspace."); | | 1298 | + const chunks = []; | | 1299 | + for (let offset = 0; offset < file.size || offset === 0; offset += codec().CHUNK_SIZE) { | | 1300 | + checkAbort(signal); | | 1301 | + const plain = new Uint8Array(await file.slice(offset, offset + codec().CHUNK_SIZE).arrayBuffer()); | | 1302 | + const stored = await storeEncrypted(plain, "application/octet-stream", signal); | | 1303 | + chunks.push({...stored, size: plain.length}); | | 1304 | + progress?.(plain.length); | | 1305 | + if (file.size === 0) break; | | 1306 | + } | | 1307 | + if (chunks.length === 1) return {...chunks[0], type: 0}; | | 1308 | + return rootReference(await codec().buildFile(chunks, {store: manifestStore(signal), returnDetails: true})); | | 1309 | + } | | 1310 | + | | 1311 | + async function storeDirectory(directory, signal, progress) { | | 1312 | + const entries = []; | | 1313 | + for (const [name, file] of directory.files) { | | 1314 | + const stored = await storeFile(file, signal, progress); | | 1315 | + entries.push({...stored, name, metadata: {type: file.type || "application/octet-stream"}}); | | 1316 | + } | | 1317 | + for (const [name, child] of directory.directories) { | | 1318 | + const stored = await storeDirectory(child, signal, progress); | | 1319 | + entries.push({...stored, name}); | | 1320 | + } | | 1321 | + return rootReference(await codec().buildDirectory(entries, {store: manifestStore(signal), returnDetails: true})); | | 1322 | + } | | 1323 | + | | 1324 | + const shareURL = (reference, name, type) => { | | 1325 | + const url = new URL(tiny.localPath("/file"), location.href); | | 1326 | + url.searchParams.set("hash", reference.hash); | | 1327 | + url.hash = new URLSearchParams({manifest: reference.hash, key: hex(reference.key), node: String(reference.type), name, type: type || "application/octet-stream"}).toString(); | | 1328 | + return url.href; | | 1329 | + }; | | 1330 | + | | 1331 | + class Workspace extends HTMLElement { | | 1332 | + connectedCallback() { | | 1333 | + if (this.bound) return; | | 1334 | + this.bound = true; | | 1335 | + this.innerHTML = '<h3>Folders and large files</h3><p>Encrypt folders and split large files into portable chunks. Deduplicated encryption reveals matching content and permits guesses about predictable files.</p><form data-folder><label>Folder <input type="file" name="folder" webkitdirectory directory multiple required></label><button>Encrypt and store folder</button></form><form data-chunked><label>File to split into chunks <input type="file" name="file" required></label><button>Encrypt and store file</button></form><p><button type="button" data-cancel disabled>Cancel upload</button> <button type="button" data-retry disabled>Retry upload</button></p><label>Share link <input data-tree-share readonly></label><p><a data-open hidden>Browse stored content</a></p><output role="status"></output>'; | | 1336 | + this.out = this.querySelector("output"); | | 1337 | + for (const form of this.querySelectorAll("form")) form.addEventListener("submit", event => { | | 1338 | + event.preventDefault(); | | 1339 | + this.run(form).catch(error => this.say(error.message, true)); | | 1340 | + }); | | 1341 | + this.querySelector("[data-cancel]").addEventListener("click", () => this.controller?.abort()); | | 1342 | + this.querySelector("[data-retry]").addEventListener("click", () => this.run().catch(error => this.say(error.message, true))); | | 1343 | + } | | 1344 | + disconnectedCallback() { this.controller?.abort(); } | | 1345 | + say(message, error = false) { | | 1346 | + this.out.textContent = message; | | 1347 | + if (error) this.out.dataset.error = ""; | | 1348 | + else delete this.out.dataset.error; | | 1349 | + } | | 1350 | + async run(form) { | | 1351 | + if (this.busy) return; | | 1352 | + if (form) { | | 1353 | + const folder = form.elements.namedItem("folder"); | | 1354 | + const input = folder || form.elements.namedItem("file"); | | 1355 | + this.selection = {files: [...input.files], folder: Boolean(folder)}; | | 1356 | + } | | 1357 | + if (!this.selection?.files.length) throw Error("Choose a file or folder first."); | | 1358 | + this.busy = true; | | 1359 | + this.controller = new AbortController(); | | 1360 | + this.querySelector("[data-cancel]").disabled = false; | | 1361 | + this.querySelector("[data-retry]").disabled = true; | | 1362 | + let completed = false; | | 1363 | + try { | | 1364 | + let reference, name, type; | | 1365 | + let sent = 0; | | 1366 | + const progress = size => { sent += size; this.say("Stored " + sent.toLocaleString() + " bytes. Encrypting the remaining content…"); }; | | 1367 | + this.say("Encrypting selected content…"); | | 1368 | + if (this.selection.folder) { | | 1369 | + const selected = selectionTree(this.selection.files); | | 1370 | + name = selected.name; | | 1371 | + reference = await storeDirectory(selected.root, this.controller.signal, progress); | | 1372 | + } else { | | 1373 | + const file = this.selection.files[0]; | | 1374 | + name = file.name; type = file.type; | | 1375 | + reference = await storeFile(file, this.controller.signal, progress); | | 1376 | + } | | 1377 | + checkAbort(this.controller.signal); | | 1378 | + const url = shareURL(reference, name, type); | | 1379 | + this.querySelector("[data-tree-share]").value = url; | | 1380 | + const open = this.querySelector("[data-open]"); | | 1381 | + open.href = url; open.hidden = false; | | 1382 | + this.say("Stored. Keep the complete share link to decrypt and browse this content."); | | 1383 | + completed = true; | | 1384 | + return url; | | 1385 | + } finally { | | 1386 | + this.busy = false; | | 1387 | + this.querySelector("[data-cancel]").disabled = true; | | 1388 | + this.querySelector("[data-retry]").disabled = completed; | | 1389 | + if (completed) this.selection = null; | | 1390 | + } | | 1391 | + } | | 1392 | + } | | 1393 | + | | 1394 | + async function fetchBlob(hash, signal) { | | 1395 | + fromHex(hash); | | 1396 | + const response = await fetch(tiny.localPath("/files/raw?hash=" + hash), {signal}); | | 1397 | + if (!response.ok) throw Error("Download failed (" + response.status + ")."); | | 1398 | + if (Number(response.headers.get("Content-Length")) > MAX_FETCH_BYTES) throw Error("Linked blob exceeds the browser fetch limit."); | | 1399 | + const reader = response.body?.getReader(); | | 1400 | + if (!reader) { | | 1401 | + const bytes = new Uint8Array(await response.arrayBuffer()); | | 1402 | + if (bytes.length > MAX_FETCH_BYTES) throw Error("Linked blob exceeds the browser fetch limit."); | | 1403 | + return bytes; | | 1404 | + } | | 1405 | + const parts = []; | | 1406 | + let size = 0; | | 1407 | + try { | | 1408 | + for (;;) { | | 1409 | + const {value, done} = await reader.read(); | | 1410 | + if (done) break; | | 1411 | + size += value.length; | | 1412 | + if (size > MAX_FETCH_BYTES) { await reader.cancel(); throw Error("Linked blob exceeds the browser fetch limit."); } | | 1413 | + parts.push(value); | | 1414 | + } | | 1415 | + } finally { reader.releaseLock(); } | | 1416 | + const bytes = new Uint8Array(size); | | 1417 | + let offset = 0; | | 1418 | + for (const part of parts) { bytes.set(part, offset); offset += part.length; } | | 1419 | + return bytes; | | 1420 | + } | | 1421 | + | | 1422 | + async function plaintext(reference, signal) { | | 1423 | + const hash = hex(reference.h); | | 1424 | + const raw = await fetchBlob(hash, signal); | | 1425 | + if (hex(await codec().sha256(raw)) !== hash) throw Error("Blob hash mismatch."); | | 1426 | + return reference.k ? TinyBlossomEncryption.decryptCHK(raw, hex(reference.k), hash) : raw; | | 1427 | + } | | 1428 | + const decrypt = (raw, link) => TinyBlossomEncryption.decryptCHK(raw, hex(link.k), hex(link.h)); | | 1429 | + | | 1430 | + class Root extends HTMLElement { | | 1431 | + connectedCallback() { | | 1432 | + if (this.bound) return; | | 1433 | + this.bound = true; | | 1434 | + const params = new URLSearchParams(location.hash.slice(1)); | | 1435 | + if (!params.has("manifest") && this.getAttribute("type") !== directoryType) { this.hidden = true; return; } | | 1436 | + this.hidden = false; | | 1437 | + this.load().catch(error => this.fail(error.message)); | | 1438 | + } | | 1439 | + disconnectedCallback() { | | 1440 | + this.controller?.abort(); | | 1441 | + if (this.downloadURL) URL.revokeObjectURL(this.downloadURL); | | 1442 | + } | | 1443 | + fail(message) { | | 1444 | + const error = element("p", "Content unavailable: " + message); | | 1445 | + error.setAttribute("role", "alert"); | | 1446 | + this.replaceChildren(error); | | 1447 | + } | | 1448 | + async load() { | | 1449 | + this.controller?.abort(); | | 1450 | + this.controller = new AbortController(); | | 1451 | + const params = new URLSearchParams(location.hash.slice(1)); | | 1452 | + const reference = {h: fromHex(params.get("manifest") || this.getAttribute("hash"))}; | | 1453 | + if (params.has("key")) reference.k = fromHex(params.get("key")); | | 1454 | + const type = params.has("node") ? Number(params.get("node")) : 2; | | 1455 | + if (![0, 1, 2, 3].includes(type)) throw Error("Unsupported manifest type."); | | 1456 | + const data = await plaintext(reference, this.controller.signal); | | 1457 | + const heading = element("h2", params.get("name") || "Stored content"); | | 1458 | + this.replaceChildren(heading); | | 1459 | + if (type === 0) { this.offerDownload(data, params.get("name") || "file", params.get("type")); return; } | | 1460 | + const root = codec().decodeManifest(data); | | 1461 | + if (root.t !== type) throw Error("Manifest type does not match its reference."); | | 1462 | + if (type === 1) { | | 1463 | + const button = element("button", "Download decrypted file"); | | 1464 | + button.type = "button"; | | 1465 | + button.addEventListener("click", () => this.download({...reference, t: 1, s: root.l.reduce((sum, link) => sum + link.s, 0), n: params.get("name") || "file", m: {type: params.get("type")}}).catch(error => this.fail(error.message))); | | 1466 | + this.append(button); return; | | 1467 | + } | | 1468 | + const entries = await codec().resolveDirectory(root, (hash) => fetchBlob(hash, this.controller.signal), {decrypt, maxDepth: MAX_DEPTH, maxEntries: MAX_FILES, maxBytes: 64 * 1024 * 1024}); | | 1469 | + const list = element("ul"); | | 1470 | + if (!entries.length) this.append(element("p", "This folder is empty.")); | | 1471 | + for (const link of entries) { | | 1472 | + const item = element("li"); | | 1473 | + if (link.t === 2 || link.t === 3) { | | 1474 | + const open = element("a", link.n + "/"); | | 1475 | + const url = new URL(tiny.localPath("/file"), location.href); | | 1476 | + url.searchParams.set("hash", hex(link.h)); | | 1477 | + const fragment = new URLSearchParams({manifest: hex(link.h), node: String(link.t), name: link.n}); | | 1478 | + if (link.k) fragment.set("key", hex(link.k)); | | 1479 | + url.hash = fragment.toString(); open.href = url.href; | | 1480 | + item.append(open); | | 1481 | + } else { | | 1482 | + const button = element("button", link.n + " (" + link.s.toLocaleString() + " bytes)"); | | 1483 | + button.type = "button"; | | 1484 | + button.addEventListener("click", () => this.download(link).catch(error => this.fail(error.message))); | | 1485 | + item.append(button); | | 1486 | + } | | 1487 | + list.append(item); | | 1488 | + } | | 1489 | + this.append(list); | | 1490 | + this.entries = entries; | | 1491 | + } | | 1492 | + async download(link) { | | 1493 | + let data; | | 1494 | + if (link.t === 0) data = await plaintext(link, this.controller.signal); | | 1495 | + else if (link.t === 1) { | | 1496 | + const manifest = codec().decodeManifest(await plaintext(link, this.controller.signal)); | | 1497 | + data = await codec().readFile(manifest, hash => fetchBlob(hash, this.controller.signal), {decrypt, maxDepth: MAX_DEPTH, maxBytes: MAX_FILE_BYTES}); | | 1498 | + } else throw Error("Unsupported file link."); | | 1499 | + if (data.length !== link.s) throw Error("File size does not match its directory entry."); | | 1500 | + this.offerDownload(data, link.n, link.m?.type); | | 1501 | + return data; | | 1502 | + } | | 1503 | + offerDownload(data, name, type) { | | 1504 | + if (this.downloadURL) URL.revokeObjectURL(this.downloadURL); | | 1505 | + this.downloadURL = URL.createObjectURL(new Blob([data], {type: type || "application/octet-stream"})); | | 1506 | + const link = element("a", "Save " + name); | | 1507 | + link.href = this.downloadURL; link.download = name; | | 1508 | + this.append(link); | | 1509 | + link.click(); | | 1510 | + } | | 1511 | + } | | 1512 | + customElements.define("file-workspace", Workspace); | | 1513 | + customElements.define("file-workspace-root", Root); | | 1514 | +})(); | | 1515 | diff --git a/internal/webui/page.html b/internal/webui/page.html | | 1516 | index 903431d..5659f1c 100644 | | 1517 | --- a/internal/webui/page.html | | 1518 | +++ b/internal/webui/page.html | | 1519 | @@ -57,6 +57,11 @@ | | 1520 | </tr></tbody></table> | | 1521 | <footer id="footer"><table><tr><td><span id="prompt"><b>{{.Slug}}</b> » {{prompt .Path .Query}}</span></td><td><span id="who">{{if .Private}}private relay{{else if .Actor}}signed in as {{template "nostrKey" .Actor}} (<button id="session-logout" type="button">sign out</button>){{else}}browsing as guest (<a href="/signin">Sign in</a>){{end}}</span> <span id="session-status" role="status"></span><span id="navigation-status" role="status" aria-live="polite"></span><button id="theme" type="button" aria-label="Switch theme">theme</button></td></tr></table></footer> | | 1522 | <script>{{script "bridge.js"}}</script> | | 1523 | +<script>{{script "blossom-encryption.js"}}</script> | | 1524 | +<script>{{script "blossom-manifests.js"}}</script> | | 1525 | +<script>{{script "blossom-upload.js"}}</script> | | 1526 | +<script>{{script "file-messages.js"}}</script> | | 1527 | +<script>{{script "file-workspace.js"}}</script> | | 1528 | <script>{{script "components.js"}}</script> | | 1529 | </body> | | 1530 | </html> | | 1531 | diff --git a/internal/webui/signer.js b/internal/webui/signer.js | | 1532 | index 0900cd3..cad8094 100644 | | 1533 | --- a/internal/webui/signer.js | | 1534 | +++ b/internal/webui/signer.js | | 1535 | @@ -1,5 +1,5 @@ | | 1536 | "use strict"; | | 1537 | -(()=>{var Ri=Object.defineProperty;var _i=(e,t,n)=>t in e?Ri(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var u=(e,t,n)=>_i(e,typeof t!="symbol"?t+"":t,n);function pt(e){return e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name==="Uint8Array"}function xe(e,t=""){if(!Number.isSafeInteger(e)||e<0){let n=t&&`"${t}" `;throw new Error(`${n}expected integer >= 0, got ${e}`)}}function N(e,t,n=""){let r=pt(e),o=e?.length,s=t!==void 0;if(!r||s&&o!==t){let i=n&&`"${n}" `,a=s?` of length ${t}`:"",c=r?`length=${o}`:`type=${typeof e}`;throw new Error(i+"expected Uint8Array"+a+", got "+c)}return e}function Bt(e){if(typeof e!="function"||typeof e.create!="function")throw new Error("Hash must wrapped by utils.createHasher");xe(e.outputLen),xe(e.blockLen)}function nt(e,t=!0){if(e.destroyed)throw new Error("Hash instance has been destroyed");if(t&&e.finished)throw new Error("Hash#digest() has already been called")}function Jr(e,t){N(e,void 0,"digestInto() output");let n=t.outputLen;if(e.length<n)throw new Error('"digestInto() output" expected to be of length >='+n)}function $e(...e){for(let t=0;t<e.length;t++)e[t].fill(0)}function Rt(e){return new DataView(e.buffer,e.byteOffset,e.byteLength)}function be(e,t){return e<<32-t|e>>>t}var Xr=typeof Uint8Array.from([]).toHex=="function"&&typeof Uint8Array.fromHex=="function",ki=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,"0"));function We(e){if(N(e),Xr)return e.toHex();let t="";for(let n=0;n<e.length;n++)t+=ki[e[n]];return t}var Se={_0:48,_9:57,A:65,F:70,a:97,f:102};function Zr(e){if(e>=Se._0&&e<=Se._9)return e-Se._0;if(e>=Se.A&&e<=Se.F)return e-(Se.A-10);if(e>=Se.a&&e<=Se.f)return e-(Se.a-10)}function Ve(e){if(typeof e!="string")throw new Error("hex string expected, got "+typeof e);if(Xr)return Uint8Array.fromHex(e);let t=e.length,n=t/2;if(t%2)throw new Error("hex string expected, got unpadded hex of length "+t);let r=new Uint8Array(n);for(let o=0,s=0;o<n;o++,s+=2){let i=Zr(e.charCodeAt(s)),a=Zr(e.charCodeAt(s+1));if(i===void 0||a===void 0){let c=e[s]+e[s+1];throw new Error('hex string expected, got non-hex character "'+c+'" at index '+s)}r[o]=i*16+a}return r}function ce(...e){let t=0;for(let r=0;r<e.length;r++){let o=e[r];N(o),t+=o.length}let n=new Uint8Array(t);for(let r=0,o=0;r<e.length;r++){let s=e[r];n.set(s,o),o+=s.length}return n}function Yr(e,t={}){let n=(o,s)=>e(s).update(o).digest(),r=e(void 0);return n.outputLen=r.outputLen,n.blockLen=r.blockLen,n.create=o=>e(o),Object.assign(n,t),Object.freeze(n)}function Ge(e=32){let t=typeof globalThis=="object"?globalThis.crypto:null;if(typeof t?.getRandomValues!="function")throw new Error("crypto.getRandomValues must be defined");return t.getRandomValues(new Uint8Array(e))}var Qr=e=>({oid:Uint8Array.from([6,9,96,134,72,1,101,3,4,2,e])});function eo(e,t,n){return e&t^~e&n}function to(e,t,n){return e&t^e&n^t&n}var _t=class{constructor(t,n,r,o){u(this,"blockLen");u(this,"outputLen");u(this,"padOffset");u(this,"isLE");u(this,"buffer");u(this,"view");u(this,"finished",!1);u(this,"length",0);u(this,"pos",0);u(this,"destroyed",!1);this.blockLen=t,this.outputLen=n,this.padOffset=r,this.isLE=o,this.buffer=new Uint8Array(t),this.view=Rt(this.buffer)}update(t){nt(this),N(t);let{view:n,buffer:r,blockLen:o}=this,s=t.length;for(let i=0;i<s;){let a=Math.min(o-this.pos,s-i);if(a===o){let c=Rt(t);for(;o<=s-i;i+=o)this.process(c,i);continue}r.set(t.subarray(i,i+a),this.pos),this.pos+=a,i+=a,this.pos===o&&(this.process(n,0),this.pos=0)}return this.length+=t.length,this.roundClean(),this}digestInto(t){nt(this),Jr(t,this),this.finished=!0;let{buffer:n,view:r,blockLen:o,isLE:s}=this,{pos:i}=this;n[i++]=128,$e(this.buffer.subarray(i)),this.padOffset>o-i&&(this.process(r,0),i=0);for(let h=i;h<o;h++)n[h]=0;r.setBigUint64(o-8,BigInt(this.length*8),s),this.process(r,0);let a=Rt(t),c=this.outputLen;if(c%4)throw new Error("_sha2: outputLen must be aligned to 32bit");let l=c/4,f=this.get();if(l>f.length)throw new Error("_sha2: outputLen bigger than state");for(let h=0;h<l;h++)a.setUint32(4*h,f[h],s)}digest(){let{buffer:t,outputLen:n}=this;this.digestInto(t);let r=t.slice(0,n);return this.destroy(),r}_cloneInto(t){t||(t=new this.constructor),t.set(...this.get());let{blockLen:n,buffer:r,length:o,finished:s,destroyed:i,pos:a}=this;return t.destroyed=i,t.finished=s,t.length=o,t.pos=a,o%n&&t.buffer.set(r),t}clone(){return this._cloneInto()}},Ae=Uint32Array.from([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]);var Li=Uint32Array.from([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),Oe=new Uint32Array(64),yn=class extends _t{constructor(t){super(64,t,8,!1)}get(){let{A:t,B:n,C:r,D:o,E:s,F:i,G:a,H:c}=this;return[t,n,r,o,s,i,a,c]}set(t,n,r,o,s,i,a,c){this.A=t|0,this.B=n|0,this.C=r|0,this.D=o|0,this.E=s|0,this.F=i|0,this.G=a|0,this.H=c|0}process(t,n){for(let h=0;h<16;h++,n+=4)Oe[h]=t.getUint32(n,!1);for(let h=16;h<64;h++){let d=Oe[h-15],p=Oe[h-2],y=be(d,7)^be(d,18)^d>>>3,m=be(p,17)^be(p,19)^p>>>10;Oe[h]=m+Oe[h-7]+y+Oe[h-16]|0}let{A:r,B:o,C:s,D:i,E:a,F:c,G:l,H:f}=this;for(let h=0;h<64;h++){let d=be(a,6)^be(a,11)^be(a,25),p=f+d+eo(a,c,l)+Li[h]+Oe[h]|0,m=(be(r,2)^be(r,13)^be(r,22))+to(r,o,s)|0;f=l,l=c,c=a,a=i+p|0,i=s,s=o,o=r,r=p+m|0}r=r+this.A|0,o=o+this.B|0,s=s+this.C|0,i=i+this.D|0,a=a+this.E|0,c=c+this.F|0,l=l+this.G|0,f=f+this.H|0,this.set(r,o,s,i,a,c,l,f)}roundClean(){$e(Oe)}destroy(){this.set(0,0,0,0,0,0,0,0),$e(this.buffer)}},gn=class extends yn{constructor(){super(32);u(this,"A",Ae[0]|0);u(this,"B",Ae[1]|0);u(this,"C",Ae[2]|0);u(this,"D",Ae[3]|0);u(this,"E",Ae[4]|0);u(this,"F",Ae[5]|0);u(this,"G",Ae[6]|0);u(this,"H",Ae[7]|0)}};var kt=Yr(()=>new gn,Qr(1));var mn=BigInt(0),bn=BigInt(1);function yt(e,t=""){if(typeof e!="boolean"){let n=t&&`"${t}" `;throw new Error(n+"expected boolean, got type="+typeof e)}return e}function no(e){if(typeof e=="bigint"){if(!Lt(e))throw new Error("positive bigint expected, got "+e)}else xe(e);return e}function gt(e){let t=no(e).toString(16);return t.length&1?"0"+t:t}function ro(e){if(typeof e!="string")throw new Error("hex string expected, got "+typeof e);return e===""?mn:BigInt("0x"+e)}function Ue(e){return ro(We(e))}function wn(e){return ro(We(Ci(N(e)).reverse()))}function Ct(e,t){xe(t),e=no(e);let n=Ve(e.toString(16).padStart(t*2,"0"));if(n.length!==t)throw new Error("number too large");return n}function xn(e,t){return Ct(e,t).reverse()}function Ci(e){return Uint8Array.from(e)}function oo(e){return Uint8Array.from(e,(t,n)=>{let r=t.charCodeAt(0);if(t.length!==1||r>127)throw new Error(`string contains non-ASCII character "${e[n]}" with code ${r} at position ${n}`);return r})}var Lt=e=>typeof e=="bigint"&&mn<=e;function Ii(e,t,n){return Lt(e)&&Lt(t)&&Lt(n)&&t<=e&&e<n}function so(e,t,n,r){if(!Ii(t,n,r))throw new Error("expected valid "+e+": "+n+" <= n < "+r+", got "+t)}function vn(e){let t;for(t=0;e>mn;e>>=bn,t+=1);return t}var bt=e=>(bn<<BigInt(e))-bn;function io(e,t,n){if(xe(e,"hashLen"),xe(t,"qByteLen"),typeof n!="function")throw new Error("hmacFn must be a function");let r=w=>new Uint8Array(w),o=Uint8Array.of(),s=Uint8Array.of(0),i=Uint8Array.of(1),a=1e3,c=r(e),l=r(e),f=0,h=()=>{c.fill(1),l.fill(0),f=0},d=(...w)=>n(l,ce(c,...w)),p=(w=o)=>{l=d(s,w),c=d(),w.length!==0&&(l=d(i,w),c=d())},y=()=>{if(f++>=a)throw new Error("drbg: tried max amount of iterations");let w=0,x=[];for(;w<t;){c=d();let A=c.slice();x.push(A),w+=c.length}return ce(...x)};return(w,x)=>{h(),p(w);let A;for(;!(A=x(y()));)p();return h(),A}}function mt(e,t={},n={}){if(!e||typeof e!="object")throw new Error("expected valid options object");function r(s,i,a){let c=e[s];if(a&&c===void 0)return;let l=typeof c;if(l!==i||c===null)throw new Error(`param "${s}" is invalid: expected ${i}, got ${l}`)}let o=(s,i)=>Object.entries(s).forEach(([a,c])=>r(a,c,i));o(t,!1),o(n,!0)}function En(e){let t=new WeakMap;return(n,...r)=>{let o=t.get(n);if(o!==void 0)return o;let s=e(n,...r);return t.set(n,s),s}}var ae=BigInt(0),se=BigInt(1),Ke=BigInt(2),lo=BigInt(3),uo=BigInt(4),fo=BigInt(5),Hi=BigInt(7),ho=BigInt(8),Oi=BigInt(9),po=BigInt(16);function me(e,t){let n=e%t;return n>=ae?n:t+n}function fe(e,t,n){let r=e;for(;t-- >ae;)r*=r,r%=n;return r}function ao(e,t){if(e===ae)throw new Error("invert: expected non-zero number");if(t<=ae)throw new Error("invert: expected positive modulus, got "+t);let n=me(e,t),r=t,o=ae,s=se,i=se,a=ae;for(;n!==ae;){let l=r/n,f=r%n,h=o-i*l,d=s-a*l;r=n,n=f,o=i,s=a,i=h,a=d}if(r!==se)throw new Error("invert: does not exist");return me(o,t)}function An(e,t,n){if(!e.eql(e.sqr(t),n))throw new Error("Cannot find square root")}function yo(e,t){let n=(e.ORDER+se)/uo,r=e.pow(t,n);return An(e,r,t),r}function Ui(e,t){let n=(e.ORDER-fo)/ho,r=e.mul(t,Ke),o=e.pow(r,n),s=e.mul(t,o),i=e.mul(e.mul(s,Ke),o),a=e.mul(s,e.sub(i,e.ONE));return An(e,a,t),a}function Pi(e){let t=rt(e),n=go(e),r=n(t,t.neg(t.ONE)),o=n(t,r),s=n(t,t.neg(r)),i=(e+Hi)/po;return(a,c)=>{let l=a.pow(c,i),f=a.mul(l,r),h=a.mul(l,o),d=a.mul(l,s),p=a.eql(a.sqr(f),c),y=a.eql(a.sqr(h),c);l=a.cmov(l,f,p),f=a.cmov(d,h,y);let m=a.eql(a.sqr(f),c),w=a.cmov(l,f,m);return An(a,w,c),w}}function go(e){if(e<lo)throw new Error("sqrt is not defined for small field");let t=e-se,n=0;for(;t%Ke===ae;)t/=Ke,n++;let r=Ke,o=rt(e);for(;co(o,r)===1;)if(r++>1e3)throw new Error("Cannot find square root: probably non-prime P");if(n===1)return yo;let s=o.pow(r,t),i=(t+se)/Ke;return function(c,l){if(c.is0(l))return l;if(co(c,l)!==1)throw new Error("Cannot find square root");let f=n,h=c.mul(c.ONE,s),d=c.pow(l,t),p=c.pow(l,i);for(;!c.eql(d,c.ONE);){if(c.is0(d))return c.ZERO;let y=1,m=c.sqr(d);for(;!c.eql(m,c.ONE);)if(y++,m=c.sqr(m),y===f)throw new Error("Cannot find square root");let w=se<<BigInt(f-y-1),x=c.pow(h,w);f=y,h=c.sqr(x),d=c.mul(d,h),p=c.mul(p,x)}return p}}function Ni(e){return e%uo===lo?yo:e%ho===fo?Ui:e%po===Oi?Pi(e):go(e)}var Mi=["create","isValid","is0","neg","inv","sqrt","sqr","eql","add","sub","mul","pow","div","addN","subN","mulN","sqrN"];function Tn(e){let t={ORDER:"bigint",BYTES:"number",BITS:"number"},n=Mi.reduce((r,o)=>(r[o]="function",r),t);return mt(e,n),e}function qi(e,t,n){if(n<ae)throw new Error("invalid exponent, negatives unsupported");if(n===ae)return e.ONE;if(n===se)return t;let r=e.ONE,o=t;for(;n>ae;)n&se&&(r=e.mul(r,o)),o=e.sqr(o),n>>=se;return r}function It(e,t,n=!1){let r=new Array(t.length).fill(n?e.ZERO:void 0),o=t.reduce((i,a,c)=>e.is0(a)?i:(r[c]=i,e.mul(i,a)),e.ONE),s=e.inv(o);return t.reduceRight((i,a,c)=>e.is0(a)?i:(r[c]=e.mul(i,r[c]),e.mul(i,a)),s),r}function co(e,t){let n=(e.ORDER-se)/Ke,r=e.pow(t,n),o=e.eql(r,e.ONE),s=e.eql(r,e.ZERO),i=e.eql(r,e.neg(e.ONE));if(!o&&!s&&!i)throw new Error("invalid Legendre symbol result");return o?1:s?0:-1}function Di(e,t){t!==void 0&&xe(t);let n=t!==void 0?t:e.toString(2).length,r=Math.ceil(n/8);return{nBitLength:n,nByteLength:r}}var Sn=class{constructor(t,n={}){u(this,"ORDER");u(this,"BITS");u(this,"BYTES");u(this,"isLE");u(this,"ZERO",ae);u(this,"ONE",se);u(this,"_lengths");u(this,"_sqrt");u(this,"_mod");if(t<=ae)throw new Error("invalid field: expected ORDER > 0, got "+t);let r;this.isLE=!1,n!=null&&typeof n=="object"&&(typeof n.BITS=="number"&&(r=n.BITS),typeof n.sqrt=="function"&&(this.sqrt=n.sqrt),typeof n.isLE=="boolean"&&(this.isLE=n.isLE),n.allowedLengths&&(this._lengths=n.allowedLengths?.slice()),typeof n.modFromBytes=="boolean"&&(this._mod=n.modFromBytes));let{nBitLength:o,nByteLength:s}=Di(t,r);if(s>2048)throw new Error("invalid field: expected ORDER of <= 2048 bytes");this.ORDER=t,this.BITS=o,this.BYTES=s,this._sqrt=void 0,Object.preventExtensions(this)}create(t){return me(t,this.ORDER)}isValid(t){if(typeof t!="bigint")throw new Error("invalid field element: expected bigint, got "+typeof t);return ae<=t&&t<this.ORDER}is0(t){return t===ae}isValidNot0(t){return!this.is0(t)&&this.isValid(t)}isOdd(t){return(t&se)===se}neg(t){return me(-t,this.ORDER)}eql(t,n){return t===n}sqr(t){return me(t*t,this.ORDER)}add(t,n){return me(t+n,this.ORDER)}sub(t,n){return me(t-n,this.ORDER)}mul(t,n){return me(t*n,this.ORDER)}pow(t,n){return qi(this,t,n)}div(t,n){return me(t*ao(n,this.ORDER),this.ORDER)}sqrN(t){return t*t}addN(t,n){return t+n}subN(t,n){return t-n}mulN(t,n){return t*n}inv(t){return ao(t,this.ORDER)}sqrt(t){return this._sqrt||(this._sqrt=Ni(this.ORDER)),this._sqrt(this,t)}toBytes(t){return this.isLE?xn(t,this.BYTES):Ct(t,this.BYTES)}fromBytes(t,n=!1){N(t);let{_lengths:r,BYTES:o,isLE:s,ORDER:i,_mod:a}=this;if(r){if(!r.includes(t.length)||t.length>o)throw new Error("Field.fromBytes: expected "+r+" bytes, got "+t.length);let l=new Uint8Array(o);l.set(t,s?0:l.length-t.length),t=l}if(t.length!==o)throw new Error("Field.fromBytes: expected "+o+" bytes, got "+t.length);let c=s?wn(t):Ue(t);if(a&&(c=me(c,i)),!n&&!this.isValid(c))throw new Error("invalid field element: outside of range 0..ORDER");return c}invertBatch(t){return It(this,t)}cmov(t,n,r){return r?n:t}};function rt(e,t={}){return new Sn(e,t)}function bo(e){if(typeof e!="bigint")throw new Error("field order must be bigint");let t=e.toString(2).length;return Math.ceil(t/8)}function Bn(e){let t=bo(e);return t+Math.ceil(t/2)}function Ht(e,t,n=!1){N(e);let r=e.length,o=bo(t),s=Bn(t);if(r<16||r<s||r>1024)throw new Error("expected "+s+"-1024 bytes of input, got "+r);let i=n?wn(e):Ue(e),a=me(i,t-se)+se;return n?xn(a,o):Ct(a,o)}var ot=BigInt(0),Fe=BigInt(1);function wt(e,t){let n=t.negate();return e?n:t}function Ln(e,t){let n=It(e.Fp,t.map(r=>r.Z));return t.map((r,o)=>e.fromAffine(r.toAffine(n[o])))}function vo(e,t){if(!Number.isSafeInteger(e)||e<=0||e>t)throw new Error("invalid window size, expected [1.."+t+"], got W="+e)}function Rn(e,t){vo(e,t);let n=Math.ceil(t/e)+1,r=2**(e-1),o=2**e,s=bt(e),i=BigInt(e);return{windows:n,windowSize:r,mask:s,maxNumber:o,shiftBy:i}}function mo(e,t,n){let{windowSize:r,mask:o,maxNumber:s,shiftBy:i}=n,a=Number(e&o),c=e>>i;a>r&&(a-=s,c+=Fe);let l=t*r,f=l+Math.abs(a)-1,h=a===0,d=a<0,p=t%2!==0;return{nextN:c,offset:f,isZero:h,isNeg:d,isNegF:p,offsetF:l}}var _n=new WeakMap,Eo=new WeakMap;function kn(e){return Eo.get(e)||1}function wo(e){if(e!==ot)throw new Error("invalid wNAF")}var Ot=class{constructor(t,n){u(this,"BASE");u(this,"ZERO");u(this,"Fn");u(this,"bits");this.BASE=t.BASE,this.ZERO=t.ZERO,this.Fn=t.Fn,this.bits=n}_unsafeLadder(t,n,r=this.ZERO){let o=t;for(;n>ot;)n&Fe&&(r=r.add(o)),o=o.double(),n>>=Fe;return r}precomputeWindow(t,n){let{windows:r,windowSize:o}=Rn(n,this.bits),s=[],i=t,a=i;for(let c=0;c<r;c++){a=i,s.push(a);for(let l=1;l<o;l++)a=a.add(i),s.push(a);i=a.double()}return s}wNAF(t,n,r){if(!this.Fn.isValid(r))throw new Error("invalid scalar");let o=this.ZERO,s=this.BASE,i=Rn(t,this.bits);for(let a=0;a<i.windows;a++){let{nextN:c,offset:l,isZero:f,isNeg:h,isNegF:d,offsetF:p}=mo(r,a,i);r=c,f?s=s.add(wt(d,n[p])):o=o.add(wt(h,n[l]))}return wo(r),{p:o,f:s}}wNAFUnsafe(t,n,r,o=this.ZERO){let s=Rn(t,this.bits);for(let i=0;i<s.windows&&r!==ot;i++){let{nextN:a,offset:c,isZero:l,isNeg:f}=mo(r,i,s);if(r=a,!l){let h=n[c];o=o.add(f?h.negate():h)}}return wo(r),o}getPrecomputes(t,n,r){let o=_n.get(n);return o||(o=this.precomputeWindow(n,t),t!==1&&(typeof r=="function"&&(o=r(o)),_n.set(n,o))),o}cached(t,n,r){let o=kn(t);return this.wNAF(o,this.getPrecomputes(o,t,r),n)}unsafe(t,n,r,o){let s=kn(t);return s===1?this._unsafeLadder(t,n,o):this.wNAFUnsafe(s,this.getPrecomputes(s,t,r),n,o)}createCache(t,n){vo(n,this.bits),Eo.set(t,n),_n.delete(t)}hasCache(t){return kn(t)!==1}};function So(e,t,n,r){let o=t,s=e.ZERO,i=e.ZERO;for(;n>ot||r>ot;)n&Fe&&(s=s.add(o)),r&Fe&&(i=i.add(o)),o=o.double(),n>>=Fe,r>>=Fe;return{p1:s,p2:i}}function xo(e,t,n){if(t){if(t.ORDER!==e)throw new Error("Field.ORDER must match order: Fp == p, Fn == n");return Tn(t),t}else return rt(e,{isLE:n})}function Ao(e,t,n={},r){if(r===void 0&&(r=e==="edwards"),!t||typeof t!="object")throw new Error(`expected valid ${e} CURVE object`);for(let c of["p","n","h"]){let l=t[c];if(!(typeof l=="bigint"&&l>ot))throw new Error(`CURVE.${c} must be positive bigint`)}let o=xo(t.p,n.Fp,r),s=xo(t.n,n.Fn,r),a=["Gx","Gy","a",e==="weierstrass"?"b":"d"];for(let c of a)if(!o.isValid(t[c]))throw new Error(`CURVE.${c} must be valid field element of CURVE.Fp`);return t=Object.freeze(Object.assign({},t)),{CURVE:t,Fp:o,Fn:s}}function Ut(e,t){return function(r){let o=e(r);return{secretKey:o,publicKey:t(o)}}}var Pt=class{constructor(t,n){u(this,"oHash");u(this,"iHash");u(this,"blockLen");u(this,"outputLen");u(this,"finished",!1);u(this,"destroyed",!1);if(Bt(t),N(n,void 0,"key"),this.iHash=t.create(),typeof this.iHash.update!="function")throw new Error("Expected instance of class which extends utils.Hash");this.blockLen=this.iHash.blockLen,this.outputLen=this.iHash.outputLen;let r=this.blockLen,o=new Uint8Array(r);o.set(n.length>r?t.create().update(n).digest():n);for(let s=0;s<o.length;s++)o[s]^=54;this.iHash.update(o),this.oHash=t.create();for(let s=0;s<o.length;s++)o[s]^=106;this.oHash.update(o),$e(o)}update(t){return nt(this),this.iHash.update(t),this}digestInto(t){nt(this),N(t,this.outputLen,"output"),this.finished=!0,this.iHash.digestInto(t),this.oHash.update(t),this.oHash.digestInto(t),this.destroy()}digest(){let t=new Uint8Array(this.oHash.outputLen);return this.digestInto(t),t}_cloneInto(t){t||(t=Object.create(Object.getPrototypeOf(this),{}));let{oHash:n,iHash:r,finished:o,destroyed:s,blockLen:i,outputLen:a}=this;return t=t,t.finished=o,t.destroyed=s,t.blockLen=i,t.outputLen=a,t.oHash=n._cloneInto(t.oHash),t.iHash=r._cloneInto(t.iHash),t}clone(){return this._cloneInto()}destroy(){this.destroyed=!0,this.oHash.destroy(),this.iHash.destroy()}},Cn=(e,t,n)=>new Pt(e,t).update(n).digest();Cn.create=(e,t)=>new Pt(e,t);var To=(e,t)=>(e+(e>=0?t:-t)/Bo)/t;function $i(e,t,n){let[[r,o],[s,i]]=t,a=To(i*e,n),c=To(-o*e,n),l=e-a*r-c*s,f=-a*o-c*i,h=l<Te,d=f<Te;h&&(l=-l),d&&(f=-f);let p=bt(Math.ceil(vn(n)/2))+st;if(l<Te||l>=p||f<Te||f>=p)throw new Error("splitScalar (endomorphism): failed, k="+e);return{k1neg:h,k1:l,k2neg:d,k2:f}}function Hn(e){if(!["compact","recovered","der"].includes(e))throw new Error('Signature format must be "compact", "recovered", or "der"');return e}function In(e,t){let n={};for(let r of Object.keys(t))n[r]=e[r]===void 0?t[r]:e[r];return yt(n.lowS,"lowS"),yt(n.prehash,"prehash"),n.format!==void 0&&Hn(n.format),n}var On=class extends Error{constructor(t=""){super(t)}},Pe={Err:On,_tlv:{encode:(e,t)=>{let{Err:n}=Pe;if(e<0||e>256)throw new n("tlv.encode: wrong tag");if(t.length&1)throw new n("tlv.encode: unpadded data");let r=t.length/2,o=gt(r);if(o.length/2&128)throw new n("tlv.encode: long form length too big");let s=r>127?gt(o.length/2|128):"";return gt(e)+s+o+t},decode(e,t){let{Err:n}=Pe,r=0;if(e<0||e>256)throw new n("tlv.encode: wrong tag");if(t.length<2||t[r++]!==e)throw new n("tlv.decode: wrong tlv");let o=t[r++],s=!!(o&128),i=0;if(!s)i=o;else{let c=o&127;if(!c)throw new n("tlv.decode(long): indefinite length not supported");if(c>4)throw new n("tlv.decode(long): byte length is too big");let l=t.subarray(r,r+c);if(l.length!==c)throw new n("tlv.decode: length bytes not complete");if(l[0]===0)throw new n("tlv.decode(long): zero leftmost byte");for(let f of l)i=i<<8|f;if(r+=c,i<128)throw new n("tlv.decode(long): not minimal encoding")}let a=t.subarray(r,r+i);if(a.length!==i)throw new n("tlv.decode: wrong value length");return{v:a,l:t.subarray(r+i)}}},_int:{encode(e){let{Err:t}=Pe;if(e<Te)throw new t("integer: negative integers are not allowed");let n=gt(e);if(Number.parseInt(n[0],16)&8&&(n="00"+n),n.length&1)throw new t("unexpected DER parsing assertion: unpadded hex");return n},decode(e){let{Err:t}=Pe;if(e[0]&128)throw new t("invalid signature integer: negative");if(e[0]===0&&!(e[1]&128))throw new t("invalid signature integer: unnecessary leading zero");return Ue(e)}},toSig(e){let{Err:t,_int:n,_tlv:r}=Pe,o=N(e,void 0,"signature"),{v:s,l:i}=r.decode(48,o);if(i.length)throw new t("invalid signature: left bytes after parsing");let{v:a,l:c}=r.decode(2,s),{v:l,l:f}=r.decode(2,c);if(f.length)throw new t("invalid signature: left bytes after parsing");return{r:n.decode(a),s:n.decode(l)}},hexFromSig(e){let{_tlv:t,_int:n}=Pe,r=t.encode(2,n.encode(e.r)),o=t.encode(2,n.encode(e.s)),s=r+o;return t.encode(48,s)}},Te=BigInt(0),st=BigInt(1),Bo=BigInt(2),Nt=BigInt(3),Wi=BigInt(4);function Ro(e,t={}){let n=Ao("weierstrass",e,t),{Fp:r,Fn:o}=n,s=n.CURVE,{h:i,n:a}=s;mt(t,{},{allowInfinityPoint:"boolean",clearCofactor:"function",isTorsionFree:"function",fromBytes:"function",toBytes:"function",endo:"object"});let{endo:c}=t;if(c&&(!r.is0(s.a)||typeof c.beta!="bigint"||!Array.isArray(c.basises)))throw new Error('invalid endo: expected "beta": bigint and "basises": array');let l=ko(r,o);function f(){if(!r.isOdd)throw new Error("compression is not supported: Field does not have .isOdd()")}function h(L,b,g){let{x:E,y:S}=b.toAffine(),B=r.toBytes(E);if(yt(g,"isCompressed"),g){f();let v=!r.isOdd(S);return ce(_o(v),B)}else return ce(Uint8Array.of(4),B,r.toBytes(S))}function d(L){N(L,void 0,"Point");let{publicKey:b,publicKeyUncompressed:g}=l,E=L.length,S=L[0],B=L.subarray(1);if(E===b&&(S===2||S===3)){let v=r.fromBytes(B);if(!r.isValid(v))throw new Error("bad point: is not on curve, wrong x");let R=m(v),T;try{T=r.sqrt(R)}catch(K){let q=K instanceof Error?": "+K.message:"";throw new Error("bad point: is not on curve, sqrt error"+q)}f();let _=r.isOdd(T);return(S&1)===1!==_&&(T=r.neg(T)),{x:v,y:T}}else if(E===g&&S===4){let v=r.BYTES,R=r.fromBytes(B.subarray(0,v)),T=r.fromBytes(B.subarray(v,v*2));if(!w(R,T))throw new Error("bad point: is not on curve");return{x:R,y:T}}else throw new Error(`bad point: got length ${E}, expected compressed=${b} or uncompressed=${g}`)}let p=t.toBytes||h,y=t.fromBytes||d;function m(L){let b=r.sqr(L),g=r.mul(b,L);return r.add(r.add(g,r.mul(L,s.a)),s.b)}function w(L,b){let g=r.sqr(b),E=m(L);return r.eql(g,E)}if(!w(s.Gx,s.Gy))throw new Error("bad curve params: generator point");let x=r.mul(r.pow(s.a,Nt),Wi),A=r.mul(r.sqr(s.b),BigInt(27));if(r.is0(r.add(x,A)))throw new Error("bad curve params: a or b");function I(L,b,g=!1){if(!r.isValid(b)||g&&r.is0(b))throw new Error(`bad point coordinate ${L}`);return b}function P(L){if(!(L instanceof U))throw new Error("Weierstrass Point expected")}function Q(L){if(!c||!c.basises)throw new Error("no endo");return $i(L,c.basises,o.ORDER)}let V=En((L,b)=>{let{X:g,Y:E,Z:S}=L;if(r.eql(S,r.ONE))return{x:g,y:E};let B=L.is0();b==null&&(b=B?r.ONE:r.inv(S));let v=r.mul(g,b),R=r.mul(E,b),T=r.mul(S,b);if(B)return{x:r.ZERO,y:r.ZERO};if(!r.eql(T,r.ONE))throw new Error("invZ was invalid");return{x:v,y:R}}),Z=En(L=>{if(L.is0()){if(t.allowInfinityPoint&&!r.is0(L.Y))return;throw new Error("bad point: ZERO")}let{x:b,y:g}=L.toAffine();if(!r.isValid(b)||!r.isValid(g))throw new Error("bad point: x or y not field elements");if(!w(b,g))throw new Error("bad point: equation left != right");if(!L.isTorsionFree())throw new Error("bad point: not in prime-order subgroup");return!0});function j(L,b,g,E,S){return g=new U(r.mul(g.X,L),g.Y,g.Z),b=wt(E,b),g=wt(S,g),b.add(g)}let C=class C{constructor(b,g,E){u(this,"X");u(this,"Y");u(this,"Z");this.X=I("x",b),this.Y=I("y",g,!0),this.Z=I("z",E),Object.freeze(this)}static CURVE(){return s}static fromAffine(b){let{x:g,y:E}=b||{};if(!b||!r.isValid(g)||!r.isValid(E))throw new Error("invalid affine point");if(b instanceof C)throw new Error("projective point not allowed");return r.is0(g)&&r.is0(E)?C.ZERO:new C(g,E,r.ONE)}static fromBytes(b){let g=C.fromAffine(y(N(b,void 0,"point")));return g.assertValidity(),g}static fromHex(b){return C.fromBytes(Ve(b))}get x(){return this.toAffine().x}get y(){return this.toAffine().y}precompute(b=8,g=!0){return M.createCache(this,b),g||this.multiply(Nt),this}assertValidity(){Z(this)}hasEvenY(){let{y:b}=this.toAffine();if(!r.isOdd)throw new Error("Field doesn't support isOdd");return!r.isOdd(b)}equals(b){P(b);let{X:g,Y:E,Z:S}=this,{X:B,Y:v,Z:R}=b,T=r.eql(r.mul(g,R),r.mul(B,S)),_=r.eql(r.mul(E,R),r.mul(v,S));return T&&_}negate(){return new C(this.X,r.neg(this.Y),this.Z)}double(){let{a:b,b:g}=s,E=r.mul(g,Nt),{X:S,Y:B,Z:v}=this,R=r.ZERO,T=r.ZERO,_=r.ZERO,H=r.mul(S,S),K=r.mul(B,B),q=r.mul(v,v),O=r.mul(S,B);return O=r.add(O,O),_=r.mul(S,v),_=r.add(_,_),R=r.mul(b,_),T=r.mul(E,q),T=r.add(R,T),R=r.sub(K,T),T=r.add(K,T),T=r.mul(R,T),R=r.mul(O,R),_=r.mul(E,_),q=r.mul(b,q),O=r.sub(H,q),O=r.mul(b,O),O=r.add(O,_),_=r.add(H,H),H=r.add(_,H),H=r.add(H,q),H=r.mul(H,O),T=r.add(T,H),q=r.mul(B,v),q=r.add(q,q),H=r.mul(q,O),R=r.sub(R,H),_=r.mul(q,K),_=r.add(_,_),_=r.add(_,_),new C(R,T,_)}add(b){P(b);let{X:g,Y:E,Z:S}=this,{X:B,Y:v,Z:R}=b,T=r.ZERO,_=r.ZERO,H=r.ZERO,K=s.a,q=r.mul(s.b,Nt),O=r.mul(g,B),z=r.mul(E,v),J=r.mul(S,R),Y=r.add(g,E),X=r.add(B,v);Y=r.mul(Y,X),X=r.add(O,z),Y=r.sub(Y,X),X=r.add(g,S);let ie=r.add(B,R);return X=r.mul(X,ie),ie=r.add(O,J),X=r.sub(X,ie),ie=r.add(E,S),T=r.add(v,R),ie=r.mul(ie,T),T=r.add(z,J),ie=r.sub(ie,T),H=r.mul(K,X),T=r.mul(q,J),H=r.add(T,H),T=r.sub(z,H),H=r.add(z,H),_=r.mul(T,H),z=r.add(O,O),z=r.add(z,O),J=r.mul(K,J),X=r.mul(q,X),z=r.add(z,J),J=r.sub(O,J),J=r.mul(K,J),X=r.add(X,J),O=r.mul(z,X),_=r.add(_,O),O=r.mul(ie,X),T=r.mul(Y,T),T=r.sub(T,O),O=r.mul(Y,z),H=r.mul(ie,H),H=r.add(H,O),new C(T,_,H)}subtract(b){return this.add(b.negate())}is0(){return this.equals(C.ZERO)}multiply(b){let{endo:g}=t;if(!o.isValidNot0(b))throw new Error("invalid scalar: out of range");let E,S,B=v=>M.cached(this,v,R=>Ln(C,R));if(g){let{k1neg:v,k1:R,k2neg:T,k2:_}=Q(b),{p:H,f:K}=B(R),{p:q,f:O}=B(_);S=K.add(O),E=j(g.beta,H,q,v,T)}else{let{p:v,f:R}=B(b);E=v,S=R}return Ln(C,[E,S])[0]}multiplyUnsafe(b){let{endo:g}=t,E=this;if(!o.isValid(b))throw new Error("invalid scalar: out of range");if(b===Te||E.is0())return C.ZERO;if(b===st)return E;if(M.hasCache(this))return this.multiply(b);if(g){let{k1neg:S,k1:B,k2neg:v,k2:R}=Q(b),{p1:T,p2:_}=So(C,E,B,R);return j(g.beta,T,_,S,v)}else return M.unsafe(E,b)}toAffine(b){return V(this,b)}isTorsionFree(){let{isTorsionFree:b}=t;return i===st?!0:b?b(C,this):M.unsafe(this,a).is0()}clearCofactor(){let{clearCofactor:b}=t;return i===st?this:b?b(C,this):this.multiplyUnsafe(i)}isSmallOrder(){return this.multiplyUnsafe(i).is0()}toBytes(b=!0){return yt(b,"isCompressed"),this.assertValidity(),p(C,this,b)}toHex(b=!0){return We(this.toBytes(b))}toString(){return`<Point ${this.is0()?"ZERO":this.toHex()}>`}};u(C,"BASE",new C(s.Gx,s.Gy,r.ONE)),u(C,"ZERO",new C(r.ZERO,r.ONE,r.ZERO)),u(C,"Fp",r),u(C,"Fn",o);let U=C,W=o.BITS,M=new Ot(U,t.endo?Math.ceil(W/2):W);return U.BASE.precompute(8),U}function _o(e){return Uint8Array.of(e?2:3)}function ko(e,t){return{secretKey:t.BYTES,publicKey:1+e.BYTES,publicKeyUncompressed:1+2*e.BYTES,publicKeyHasPrefix:!0,signature:2*t.BYTES}}function Vi(e,t={}){let{Fn:n}=e,r=t.randomBytes||Ge,o=Object.assign(ko(e.Fp,n),{seed:Bn(n.ORDER)});function s(p){try{let y=n.fromBytes(p);return n.isValidNot0(y)}catch{return!1}}function i(p,y){let{publicKey:m,publicKeyUncompressed:w}=o;try{let x=p.length;return y===!0&&x!==m||y===!1&&x!==w?!1:!!e.fromBytes(p)}catch{return!1}}function a(p=r(o.seed)){return Ht(N(p,o.seed,"seed"),n.ORDER)}function c(p,y=!0){return e.BASE.multiply(n.fromBytes(p)).toBytes(y)}function l(p){let{secretKey:y,publicKey:m,publicKeyUncompressed:w}=o;if(!pt(p)||"_lengths"in n&&n._lengths||y===m)return;let x=N(p,void 0,"key").length;return x===m||x===w}function f(p,y,m=!0){if(l(p)===!0)throw new Error("first arg must be private key");if(l(y)===!1)throw new Error("second arg must be public key");let w=n.fromBytes(p);return e.fromBytes(y).multiply(w).toBytes(m)}let h={isValidSecretKey:s,isValidPublicKey:i,randomSecretKey:a},d=Ut(a,c);return Object.freeze({getPublicKey:c,getSharedSecret:f,keygen:d,Point:e,utils:h,lengths:o})}function Lo(e,t,n={}){Bt(t),mt(n,{},{hmac:"function",lowS:"boolean",randomBytes:"function",bits2int:"function",bits2int_modN:"function"}),n=Object.assign({},n);let r=n.randomBytes||Ge,o=n.hmac||((b,g)=>Cn(t,b,g)),{Fp:s,Fn:i}=e,{ORDER:a,BITS:c}=i,{keygen:l,getPublicKey:f,getSharedSecret:h,utils:d,lengths:p}=Vi(e,n),y={prehash:!0,lowS:typeof n.lowS=="boolean"?n.lowS:!0,format:"compact",extraEntropy:!1},m=a*Bo<s.ORDER;function w(b){let g=a>>st;return b>g}function x(b,g){if(!i.isValidNot0(g))throw new Error(`invalid signature ${b}: out of range 1..Point.Fn.ORDER`);return g}function A(){if(m)throw new Error('"recovered" sig type is not supported for cofactor >2 curves')}function I(b,g){Hn(g);let E=p.signature,S=g==="compact"?E:g==="recovered"?E+1:void 0;return N(b,S)}class P{constructor(g,E,S){u(this,"r");u(this,"s");u(this,"recovery");if(this.r=x("r",g),this.s=x("s",E),S!=null){if(A(),![0,1,2,3].includes(S))throw new Error("invalid recovery id");this.recovery=S}Object.freeze(this)}static fromBytes(g,E=y.format){I(g,E);let S;if(E==="der"){let{r:T,s:_}=Pe.toSig(N(g));return new P(T,_)}E==="recovered"&&(S=g[0],E="compact",g=g.subarray(1));let B=p.signature/2,v=g.subarray(0,B),R=g.subarray(B,B*2);return new P(i.fromBytes(v),i.fromBytes(R),S)}static fromHex(g,E){return this.fromBytes(Ve(g),E)}assertRecovery(){let{recovery:g}=this;if(g==null)throw new Error("invalid recovery id: must be present");return g}addRecoveryBit(g){return new P(this.r,this.s,g)}recoverPublicKey(g){let{r:E,s:S}=this,B=this.assertRecovery(),v=B===2||B===3?E+a:E;if(!s.isValid(v))throw new Error("invalid recovery id: sig.r+curve.n != R.x");let R=s.toBytes(v),T=e.fromBytes(ce(_o((B&1)===0),R)),_=i.inv(v),H=V(N(g,void 0,"msgHash")),K=i.create(-H*_),q=i.create(S*_),O=e.BASE.multiplyUnsafe(K).add(T.multiplyUnsafe(q));if(O.is0())throw new Error("invalid recovery: point at infinify");return O.assertValidity(),O}hasHighS(){return w(this.s)}toBytes(g=y.format){if(Hn(g),g==="der")return Ve(Pe.hexFromSig(this));let{r:E,s:S}=this,B=i.toBytes(E),v=i.toBytes(S);return g==="recovered"?(A(),ce(Uint8Array.of(this.assertRecovery()),B,v)):ce(B,v)}toHex(g){return We(this.toBytes(g))}}let Q=n.bits2int||function(g){if(g.length>8192)throw new Error("input is too large");let E=Ue(g),S=g.length*8-c;return S>0?E>>BigInt(S):E},V=n.bits2int_modN||function(g){return i.create(Q(g))},Z=bt(c);function j(b){return so("num < 2^"+c,b,Te,Z),i.toBytes(b)}function U(b,g){return N(b,void 0,"message"),g?N(t(b),void 0,"prehashed message"):b}function W(b,g,E){let{lowS:S,prehash:B,extraEntropy:v}=In(E,y);b=U(b,B);let R=V(b),T=i.fromBytes(g);if(!i.isValidNot0(T))throw new Error("invalid private key");let _=[j(T),j(R)];if(v!=null&&v!==!1){let O=v===!0?r(p.secretKey):v;_.push(N(O,void 0,"extraEntropy"))}let H=ce(..._),K=R;function q(O){let z=Q(O);if(!i.isValidNot0(z))return;let J=i.inv(z),Y=e.BASE.multiply(z).toAffine(),X=i.create(Y.x);if(X===Te)return;let ie=i.create(J*i.create(K+X*T));if(ie===Te)return;let jr=(Y.x===X?0:2)|Number(Y.y&st),zr=ie;return S&&w(ie)&&(zr=i.neg(ie),jr^=1),new P(X,zr,m?void 0:jr)}return{seed:H,k2sig:q}}function M(b,g,E={}){let{seed:S,k2sig:B}=W(b,g,E);return io(t.outputLen,i.BYTES,o)(S,B).toBytes(E.format)}function C(b,g,E,S={}){let{lowS:B,prehash:v,format:R}=In(S,y);if(E=N(E,void 0,"publicKey"),g=U(g,v),!pt(b)){let T=b instanceof P?", use sig.toBytes()":"";throw new Error("verify expects Uint8Array signature"+T)}I(b,R);try{let T=P.fromBytes(b,R),_=e.fromBytes(E);if(B&&T.hasHighS())return!1;let{r:H,s:K}=T,q=V(g),O=i.inv(K),z=i.create(q*O),J=i.create(H*O),Y=e.BASE.multiplyUnsafe(z).add(_.multiplyUnsafe(J));return Y.is0()?!1:i.create(Y.x)===H}catch{return!1}}function L(b,g,E={}){let{prehash:S}=In(E,y);return g=U(g,S),P.fromBytes(b,"recovered").recoverPublicKey(g).toBytes()}return Object.freeze({keygen:l,getPublicKey:f,getSharedSecret:h,utils:d,lengths:p,Point:e,sign:M,verify:C,recoverPublicKey:L,Signature:P,hash:t})}var Dt={p:BigInt("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"),n:BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"),h:BigInt(1),a:BigInt(0),b:BigInt(7),Gx:BigInt("0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"),Gy:BigInt("0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8")},Gi={beta:BigInt("0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee"),basises:[[BigInt("0x3086d221a7d46bcde86c90e49284eb15"),-BigInt("0xe4437ed6010e88286f547fa90abfe4c3")],[BigInt("0x114ca50f7a8e2f3f657c1108d9d44cfd8"),BigInt("0x3086d221a7d46bcde86c90e49284eb15")]]},Ki=BigInt(0),Un=BigInt(2);function Fi(e){let t=Dt.p,n=BigInt(3),r=BigInt(6),o=BigInt(11),s=BigInt(22),i=BigInt(23),a=BigInt(44),c=BigInt(88),l=e*e*e%t,f=l*l*e%t,h=fe(f,n,t)*f%t,d=fe(h,n,t)*f%t,p=fe(d,Un,t)*l%t,y=fe(p,o,t)*p%t,m=fe(y,s,t)*y%t,w=fe(m,a,t)*m%t,x=fe(w,c,t)*w%t,A=fe(x,a,t)*m%t,I=fe(A,n,t)*f%t,P=fe(I,i,t)*y%t,Q=fe(P,r,t)*l%t,V=fe(Q,Un,t);if(!Mt.eql(Mt.sqr(V),e))throw new Error("Cannot find square root");return V}var Mt=rt(Dt.p,{sqrt:Fi}),je=Ro(Dt,{Fp:Mt,endo:Gi}),ze=Lo(je,kt),Co={};function qt(e,...t){let n=Co[e];if(n===void 0){let r=kt(oo(e));n=ce(r,r),Co[e]=n}return kt(ce(n,...t))}var Nn=e=>e.toBytes(!0).slice(1),Mn=e=>e%Un===Ki;function Pn(e){let{Fn:t,BASE:n}=je,r=t.fromBytes(e),o=n.multiply(r);return{scalar:Mn(o.y)?r:t.neg(r),bytes:Nn(o)}}function Ho(e){let t=Mt;if(!t.isValidNot0(e))throw new Error("invalid x: Fail if x \u2265 p");let n=t.create(e*e),r=t.create(n*e+BigInt(7)),o=t.sqrt(r);Mn(o)||(o=t.neg(o));let s=je.fromAffine({x:e,y:o});return s.assertValidity(),s}var xt=Ue;function Oo(...e){return je.Fn.create(xt(qt("BIP0340/challenge",...e)))}function Io(e){return Pn(e).bytes}function ji(e,t,n=Ge(32)){let{Fn:r}=je,o=N(e,void 0,"message"),{bytes:s,scalar:i}=Pn(t),a=N(n,32,"auxRand"),c=r.toBytes(i^xt(qt("BIP0340/aux",a))),l=qt("BIP0340/nonce",c,s,o),{bytes:f,scalar:h}=Pn(l),d=Oo(f,s,o),p=new Uint8Array(64);if(p.set(f,0),p.set(r.toBytes(r.create(h+d*i)),32),!Uo(p,o,s))throw new Error("sign: Invalid signature produced");return p}function Uo(e,t,n){let{Fp:r,Fn:o,BASE:s}=je,i=N(e,64,"signature"),a=N(t,void 0,"message"),c=N(n,32,"publicKey");try{let l=Ho(xt(c)),f=xt(i.subarray(0,32));if(!r.isValidNot0(f))return!1;let h=xt(i.subarray(32,64));if(!o.isValidNot0(h))return!1;let d=Oo(o.toBytes(f),Nn(l),a),p=s.multiplyUnsafe(h).add(l.multiplyUnsafe(o.neg(d))),{x:y,y:m}=p.toAffine();return!(p.is0()||!Mn(m)||y!==f)}catch{return!1}}var he=(()=>{let n=(r=Ge(48))=>Ht(r,Dt.n);return{keygen:Ut(n,Io),getPublicKey:Io,sign:ji,verify:Uo,Point:je,utils:{randomSecretKey:n,taggedHash:qt,lift_x:Ho,pointToBytes:Nn},lengths:{secretKey:32,publicKey:32,publicKeyHasPrefix:!1,signature:64,seed:48}}})();function zi(e){return e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name==="Uint8Array"}function $t(e,t=""){if(!Number.isSafeInteger(e)||e<0){let n=t&&`"${t}" `;throw new Error(`${n}expected integer >= 0, got ${e}`)}}function ve(e,t,n=""){let r=zi(e),o=e?.length,s=t!==void 0;if(!r||s&&o!==t){let i=n&&`"${n}" `,a=s?` of length ${t}`:"",c=r?`length=${o}`:`type=${typeof e}`;throw new Error(i+"expected Uint8Array"+a+", got "+c)}return e}function vt(e){if(typeof e!="function"||typeof e.create!="function")throw new Error("Hash must wrapped by utils.createHasher");$t(e.outputLen),$t(e.blockLen)}function it(e,t=!0){if(e.destroyed)throw new Error("Hash instance has been destroyed");if(t&&e.finished)throw new Error("Hash#digest() has already been called")}function No(e,t){ve(e,void 0,"digestInto() output");let n=t.outputLen;if(e.length<n)throw new Error('"digestInto() output" expected to be of length >='+n)}function Re(...e){for(let t=0;t<e.length;t++)e[t].fill(0)}function Wt(e){return new DataView(e.buffer,e.byteOffset,e.byteLength)}function we(e,t){return e<<32-t|e>>>t}var Mo=typeof Uint8Array.from([]).toHex=="function"&&typeof Uint8Array.fromHex=="function",Zi=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,"0"));function D(e){if(ve(e),Mo)return e.toHex();let t="";for(let n=0;n<e.length;n++)t+=Zi[e[n]];return t}var Be={_0:48,_9:57,A:65,F:70,a:97,f:102};function Po(e){if(e>=Be._0&&e<=Be._9)return e-Be._0;if(e>=Be.A&&e<=Be.F)return e-(Be.A-10);if(e>=Be.a&&e<=Be.f)return e-(Be.a-10)}function $(e){if(typeof e!="string")throw new Error("hex string expected, got "+typeof e);if(Mo)return Uint8Array.fromHex(e);let t=e.length,n=t/2;if(t%2)throw new Error("hex string expected, got unpadded hex of length "+t);let r=new Uint8Array(n);for(let o=0,s=0;o<n;o++,s+=2){let i=Po(e.charCodeAt(s)),a=Po(e.charCodeAt(s+1));if(i===void 0||a===void 0){let c=e[s]+e[s+1];throw new Error('hex string expected, got non-hex character "'+c+'" at index '+s)}r[o]=i*16+a}return r}function de(...e){let t=0;for(let r=0;r<e.length;r++){let o=e[r];ve(o),t+=o.length}let n=new Uint8Array(t);for(let r=0,o=0;r<e.length;r++){let s=e[r];n.set(s,o),o+=s.length}return n}function qo(e,t={}){let n=(o,s)=>e(s).update(o).digest(),r=e(void 0);return n.outputLen=r.outputLen,n.blockLen=r.blockLen,n.create=o=>e(o),Object.assign(n,t),Object.freeze(n)}function at(e=32){let t=typeof globalThis=="object"?globalThis.crypto:null;if(typeof t?.getRandomValues!="function")throw new Error("crypto.getRandomValues must be defined");return t.getRandomValues(new Uint8Array(e))}var Do=e=>({oid:Uint8Array.from([6,9,96,134,72,1,101,3,4,2,e])});function $o(e,t,n){return e&t^~e&n}function Wo(e,t,n){return e&t^e&n^t&n}var Vt=class{constructor(t,n,r,o){u(this,"blockLen");u(this,"outputLen");u(this,"padOffset");u(this,"isLE");u(this,"buffer");u(this,"view");u(this,"finished",!1);u(this,"length",0);u(this,"pos",0);u(this,"destroyed",!1);this.blockLen=t,this.outputLen=n,this.padOffset=r,this.isLE=o,this.buffer=new Uint8Array(t),this.view=Wt(this.buffer)}update(t){it(this),ve(t);let{view:n,buffer:r,blockLen:o}=this,s=t.length;for(let i=0;i<s;){let a=Math.min(o-this.pos,s-i);if(a===o){let c=Wt(t);for(;o<=s-i;i+=o)this.process(c,i);continue}r.set(t.subarray(i,i+a),this.pos),this.pos+=a,i+=a,this.pos===o&&(this.process(n,0),this.pos=0)}return this.length+=t.length,this.roundClean(),this}digestInto(t){it(this),No(t,this),this.finished=!0;let{buffer:n,view:r,blockLen:o,isLE:s}=this,{pos:i}=this;n[i++]=128,Re(this.buffer.subarray(i)),this.padOffset>o-i&&(this.process(r,0),i=0);for(let h=i;h<o;h++)n[h]=0;r.setBigUint64(o-8,BigInt(this.length*8),s),this.process(r,0);let a=Wt(t),c=this.outputLen;if(c%4)throw new Error("_sha2: outputLen must be aligned to 32bit");let l=c/4,f=this.get();if(l>f.length)throw new Error("_sha2: outputLen bigger than state");for(let h=0;h<l;h++)a.setUint32(4*h,f[h],s)}digest(){let{buffer:t,outputLen:n}=this;this.digestInto(t);let r=t.slice(0,n);return this.destroy(),r}_cloneInto(t){t||(t=new this.constructor),t.set(...this.get());let{blockLen:n,buffer:r,length:o,finished:s,destroyed:i,pos:a}=this;return t.destroyed=i,t.finished=s,t.length=o,t.pos=a,o%n&&t.buffer.set(r),t}clone(){return this._cloneInto()}},_e=Uint32Array.from([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]);var Ji=Uint32Array.from([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),Ne=new Uint32Array(64),qn=class extends Vt{constructor(t){super(64,t,8,!1)}get(){let{A:t,B:n,C:r,D:o,E:s,F:i,G:a,H:c}=this;return[t,n,r,o,s,i,a,c]}set(t,n,r,o,s,i,a,c){this.A=t|0,this.B=n|0,this.C=r|0,this.D=o|0,this.E=s|0,this.F=i|0,this.G=a|0,this.H=c|0}process(t,n){for(let h=0;h<16;h++,n+=4)Ne[h]=t.getUint32(n,!1);for(let h=16;h<64;h++){let d=Ne[h-15],p=Ne[h-2],y=we(d,7)^we(d,18)^d>>>3,m=we(p,17)^we(p,19)^p>>>10;Ne[h]=m+Ne[h-7]+y+Ne[h-16]|0}let{A:r,B:o,C:s,D:i,E:a,F:c,G:l,H:f}=this;for(let h=0;h<64;h++){let d=we(a,6)^we(a,11)^we(a,25),p=f+d+$o(a,c,l)+Ji[h]+Ne[h]|0,m=(we(r,2)^we(r,13)^we(r,22))+Wo(r,o,s)|0;f=l,l=c,c=a,a=i+p|0,i=s,s=o,o=r,r=p+m|0}r=r+this.A|0,o=o+this.B|0,s=s+this.C|0,i=i+this.D|0,a=a+this.E|0,c=c+this.F|0,l=l+this.G|0,f=f+this.H|0,this.set(r,o,s,i,a,c,l,f)}roundClean(){Re(Ne)}destroy(){this.set(0,0,0,0,0,0,0,0),Re(this.buffer)}},Dn=class extends qn{constructor(){super(32);u(this,"A",_e[0]|0);u(this,"B",_e[1]|0);u(this,"C",_e[2]|0);u(this,"D",_e[3]|0);u(this,"E",_e[4]|0);u(this,"F",_e[5]|0);u(this,"G",_e[6]|0);u(this,"H",_e[7]|0)}};var te=qo(()=>new Dn,Do(1));function Gn(e){return e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name==="Uint8Array"}function Xi(e){if(!Gn(e))throw new Error("Uint8Array expected")}function Fo(e,t){return Array.isArray(t)?t.length===0?!0:e?t.every(n=>typeof n=="string"):t.every(n=>Number.isSafeInteger(n)):!1}function Yi(e){if(typeof e!="function")throw new Error("function expected");return!0}function Ze(e,t){if(typeof t!="string")throw new Error(`${e}: string expected`);return!0}function Kn(e){if(!Number.isSafeInteger(e))throw new Error(`invalid integer: ${e}`)}function $n(e){if(!Array.isArray(e))throw new Error("array expected")}function Kt(e,t){if(!Fo(!0,t))throw new Error(`${e}: array of strings expected`)}function jo(e,t){if(!Fo(!1,t))throw new Error(`${e}: array of numbers expected`)}function zo(...e){let t=s=>s,n=(s,i)=>a=>s(i(a)),r=e.map(s=>s.encode).reduceRight(n,t),o=e.map(s=>s.decode).reduce(n,t);return{encode:r,decode:o}}function Zo(e){let t=typeof e=="string"?e.split(""):e,n=t.length;Kt("alphabet",t);let r=new Map(t.map((o,s)=>[o,s]));return{encode:o=>($n(o),o.map(s=>{if(!Number.isSafeInteger(s)||s<0||s>=n)throw new Error(`alphabet.encode: digit index outside alphabet "${s}". Allowed: ${e}`);return t[s]})),decode:o=>($n(o),o.map(s=>{Ze("alphabet.decode",s);let i=r.get(s);if(i===void 0)throw new Error(`Unknown letter: "${s}". Allowed: ${e}`);return i}))}}function Jo(e=""){return Ze("join",e),{encode:t=>(Kt("join.decode",t),t.join(e)),decode:t=>(Ze("join.decode",t),t.split(e))}}function Qi(e,t="="){return Kn(e),Ze("padding",t),{encode(n){for(Kt("padding.encode",n);n.length*e%8;)n.push(t);return n},decode(n){Kt("padding.decode",n);let r=n.length;if(r*e%8)throw new Error("padding: invalid, string should have whole number of bytes");for(;r>0&&n[r-1]===t;r--)if((r-1)*e%8===0)throw new Error("padding: invalid, string has too much padding");return n.slice(0,r)}}}var Xo=(e,t)=>t===0?e:Xo(t,e%t),Ft=(e,t)=>e+(t-Xo(e,t)),Gt=(()=>{let e=[];for(let t=0;t<40;t++)e.push(2**t);return e})();function Wn(e,t,n,r){if($n(e),t<=0||t>32)throw new Error(`convertRadix2: wrong from=${t}`);if(n<=0||n>32)throw new Error(`convertRadix2: wrong to=${n}`);if(Ft(t,n)>32)throw new Error(`convertRadix2: carry overflow from=${t} to=${n} carryBits=${Ft(t,n)}`);let o=0,s=0,i=Gt[t],a=Gt[n]-1,c=[];for(let l of e){if(Kn(l),l>=i)throw new Error(`convertRadix2: invalid data word=${l} from=${t}`);if(o=o<<t|l,s+t>32)throw new Error(`convertRadix2: carry overflow pos=${s} from=${t}`);for(s+=t;s>=n;s-=n)c.push((o>>s-n&a)>>>0);let f=Gt[s];if(f===void 0)throw new Error("invalid carry");o&=f-1}if(o=o<<n-s&a,!r&&s>=t)throw new Error("Excess padding");if(!r&&o>0)throw new Error(`Non-zero padding: ${o}`);return r&&s>0&&c.push(o>>>0),c}function Yo(e,t=!1){if(Kn(e),e<=0||e>32)throw new Error("radix2: bits should be in (0..32]");if(Ft(8,e)>32||Ft(e,8)>32)throw new Error("radix2: carry overflow");return{encode:n=>{if(!Gn(n))throw new Error("radix2.encode input should be Uint8Array");return Wn(Array.from(n),8,e,!t)},decode:n=>(jo("radix2.decode",n),Uint8Array.from(Wn(n,e,8,t)))}}function Vo(e){return Yi(e),function(...t){try{return e.apply(null,t)}catch{}}}var ea=typeof Uint8Array.from([]).toBase64=="function"&&typeof Uint8Array.fromBase64=="function",ta=(e,t)=>{Ze("base64",e);let n=t?/^[A-Za-z0-9=_-]+$/:/^[A-Za-z0-9=+/]+$/,r=t?"base64url":"base64";if(e.length>0&&!n.test(e))throw new Error("invalid base64");return Uint8Array.fromBase64(e,{alphabet:r,lastChunkHandling:"strict"})},le=ea?{encode(e){return Xi(e),e.toBase64()},decode(e){return ta(e,!1)}}:zo(Yo(6),Zo("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"),Qi(6),Jo(""));var Vn=zo(Zo("qpzry9x8gf2tvdw0s3jn54khce6mua7l"),Jo("")),Go=[996825010,642813549,513874426,1027748829,705979059];function Et(e){let t=e>>25,n=(e&33554431)<<5;for(let r=0;r<Go.length;r++)(t>>r&1)===1&&(n^=Go[r]);return n}function Ko(e,t,n=1){let r=e.length,o=1;for(let s=0;s<r;s++){let i=e.charCodeAt(s);if(i<33||i>126)throw new Error(`Invalid prefix (${e})`);o=Et(o)^i>>5}o=Et(o);for(let s=0;s<r;s++)o=Et(o)^e.charCodeAt(s)&31;for(let s of t)o=Et(o)^s;for(let s=0;s<6;s++)o=Et(o);return o^=n,Vn.encode(Wn([o%Gt[30]],30,5,!1))}function na(e){let t=e==="bech32"?1:734539939,n=Yo(5),r=n.decode,o=n.encode,s=Vo(r);function i(h,d,p=90){Ze("bech32.encode prefix",h),Gn(d)&&(d=Array.from(d)),jo("bech32.encode",d);let y=h.length;if(y===0)throw new TypeError(`Invalid prefix length ${y}`);let m=y+7+d.length;if(p!==!1&&m>p)throw new TypeError(`Length ${m} exceeds limit ${p}`);let w=h.toLowerCase(),x=Ko(w,d,t);return`${w}1${Vn.encode(d)}${x}`}function a(h,d=90){Ze("bech32.decode input",h);let p=h.length;if(p<8||d!==!1&&p>d)throw new TypeError(`invalid string length: ${p} (${h}). Expected (8..${d})`);let y=h.toLowerCase();if(h!==y&&h!==h.toUpperCase())throw new Error("String must be lowercase or uppercase");let m=y.lastIndexOf("1");if(m===0||m===-1)throw new Error('Letter "1" must be present between prefix and data only');let w=y.slice(0,m),x=y.slice(m+1);if(x.length<6)throw new Error("Data must be at least 6 characters long");let A=Vn.decode(x).slice(0,-6),I=Ko(w,A,t);if(!x.endsWith(I))throw new Error(`Invalid checksum in ${h}: expected "${I}"`);return{prefix:w,words:A}}let c=Vo(a);function l(h){let{prefix:d,words:p}=a(h,!1);return{prefix:d,words:p,bytes:r(p)}}function f(h,d){return i(h,o(d))}return{encode:i,decode:a,encodeFromBytes:f,decodeToBytes:l,decodeUnsafe:c,fromWords:r,fromWordsUnsafe:s,toWords:o}}var Me=na("bech32");function ra(e){return e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name==="Uint8Array"}function jt(e){if(typeof e!="boolean")throw new Error(`boolean expected, not ${e}`)}function St(e){if(!Number.isSafeInteger(e)||e<0)throw new Error("positive integer expected, got "+e)}function F(e,t,n=""){let r=ra(e),o=e?.length,s=t!==void 0;if(!r||s&&o!==t){let i=n&&`"${n}" `,a=s?` of length ${t}`:"",c=r?`length=${o}`:`type=${typeof e}`;throw new Error(i+"expected Uint8Array"+a+", got "+c)}return e}function Fn(e,t=!0){if(e.destroyed)throw new Error("Hash instance has been destroyed");if(t&&e.finished)throw new Error("Hash#digest() has already been called")}function Qo(e,t){F(e,void 0,"output");let n=t.outputLen;if(e.length<n)throw new Error("digestInto() expects output buffer of length at least "+n)}function ee(e){return new Uint32Array(e.buffer,e.byteOffset,Math.floor(e.byteLength/4))}function re(...e){for(let t=0;t<e.length;t++)e[t].fill(0)}function es(e){return new DataView(e.buffer,e.byteOffset,e.byteLength)}var oa=new Uint8Array(new Uint32Array([287454020]).buffer)[0]===68;function ts(e,t){return e.buffer===t.buffer&&e.byteOffset<t.byteOffset+t.byteLength&&t.byteOffset<e.byteOffset+e.byteLength}function jn(e,t){if(ts(e,t)&&e.byteOffset<t.byteOffset)throw new Error("complex overlap of input and output is not supported")}function ns(e,t){if(t==null||typeof t!="object")throw new Error("options must be defined");return Object.assign(e,t)}function Je(e,t){if(e.length!==t.length)return!1;let n=0;for(let r=0;r<e.length;r++)n|=e[r]^t[r];return n===0}var At=(e,t)=>{function n(r,...o){if(F(r,void 0,"key"),!oa)throw new Error("Non little-endian hardware is not yet supported");if(e.nonceLength!==void 0){let f=o[0];F(f,e.varSizeNonce?void 0:e.nonceLength,"nonce")}let s=e.tagLength;s&&o[1]!==void 0&&F(o[1],void 0,"AAD");let i=t(r,...o),a=(f,h)=>{if(h!==void 0){if(f!==2)throw new Error("cipher output not supported");F(h,void 0,"output")}},c=!1;return{encrypt(f,h){if(c)throw new Error("cannot encrypt() twice with same key + nonce");return c=!0,F(f),a(i.encrypt.length,h),i.encrypt(f,h)},decrypt(f,h){if(F(f),s&&f.length<s)throw new Error('"ciphertext" expected length bigger than tagLength='+s);return a(i.decrypt.length,h),i.decrypt(f,h)}}}return Object.assign(n,e),n};function ct(e,t,n=!0){if(t===void 0)return new Uint8Array(e);if(t.length!==e)throw new Error('"output" expected Uint8Array of length '+e+", got: "+t.length);if(n&&!Xe(t))throw new Error("invalid output, must be aligned");return t}function zn(e,t,n){jt(n);let r=new Uint8Array(16),o=es(r);return o.setBigUint64(0,BigInt(t),n),o.setBigUint64(8,BigInt(e),n),r}function Xe(e){return e.byteOffset%4===0}function pe(e){return Uint8Array.from(e)}var ne=16;var ia=283;function os(e){if(![16,24,32].includes(e.length))throw new Error('"aes key" expected Uint8Array of length 16/24/32, got length='+e.length)}function Qn(e){return e<<1^ia&-(e>>7)}function lt(e,t){let n=0;for(;t>0;t>>=1)n^=e&-(t&1),e=Qn(e);return n}var Xn=(()=>{let e=new Uint8Array(256);for(let n=0,r=1;n<256;n++,r^=Qn(r))e[n]=r;let t=new Uint8Array(256);t[0]=99;for(let n=0;n<255;n++){let r=e[255-n];r|=r<<8,t[e[n]]=(r^r>>4^r>>5^r>>6^r>>7^99)&255}return re(e),t})(),aa=Xn.map((e,t)=>Xn.indexOf(t)),ca=e=>e<<24|e>>>8,Zn=e=>e<<8|e>>>24;function ss(e,t){if(e.length!==256)throw new Error("Wrong sbox length");let n=new Uint32Array(256).map((l,f)=>t(e[f])),r=n.map(Zn),o=r.map(Zn),s=o.map(Zn),i=new Uint32Array(256*256),a=new Uint32Array(256*256),c=new Uint16Array(256*256);for(let l=0;l<256;l++)for(let f=0;f<256;f++){let h=l*256+f;i[h]=n[l]^r[f],a[h]=o[l]^s[f],c[h]=e[l]<<8|e[f]}return{sbox:e,sbox2:c,T0:n,T1:r,T2:o,T3:s,T01:i,T23:a}}var er=ss(Xn,e=>lt(e,3)<<24|e<<16|e<<8|lt(e,2)),is=ss(aa,e=>lt(e,11)<<24|lt(e,13)<<16|lt(e,9)<<8|lt(e,14)),la=(()=>{let e=new Uint8Array(16);for(let t=0,n=1;t<16;t++,n=Qn(n))e[t]=n;return e})();function tr(e){F(e);let t=e.length;os(e);let{sbox2:n}=er,r=[];Xe(e)||r.push(e=pe(e));let o=ee(e),s=o.length,i=c=>Ee(n,c,c,c,c),a=new Uint32Array(t+28);a.set(o);for(let c=s;c<a.length;c++){let l=a[c-1];c%s===0?l=i(ca(l))^la[c/s-1]:s>6&&c%s===4&&(l=i(l)),a[c]=a[c-s]^l}return re(...r),a}function ua(e){let t=tr(e),n=t.slice(),r=t.length,{sbox2:o}=er,{T0:s,T1:i,T2:a,T3:c}=is;for(let l=0;l<r;l+=4)for(let f=0;f<4;f++)n[l+f]=t[r-l-4+f];re(t);for(let l=4;l<r-4;l++){let f=n[l],h=Ee(o,f,f,f,f);n[l]=s[h&255]^i[h>>>8&255]^a[h>>>16&255]^c[h>>>24]}return n}function qe(e,t,n,r,o,s){return e[n<<8&65280|r>>>8&255]^t[o>>>8&65280|s>>>24&255]}function Ee(e,t,n,r,o){return e[t&255|n&65280]|e[r>>>16&255|o>>>16&65280]<<16}function Yn(e,t,n,r,o){let{sbox2:s,T01:i,T23:a}=er,c=0;t^=e[c++],n^=e[c++],r^=e[c++],o^=e[c++];let l=e.length/4-2;for(let y=0;y<l;y++){let m=e[c++]^qe(i,a,t,n,r,o),w=e[c++]^qe(i,a,n,r,o,t),x=e[c++]^qe(i,a,r,o,t,n),A=e[c++]^qe(i,a,o,t,n,r);t=m,n=w,r=x,o=A}let f=e[c++]^Ee(s,t,n,r,o),h=e[c++]^Ee(s,n,r,o,t),d=e[c++]^Ee(s,r,o,t,n),p=e[c++]^Ee(s,o,t,n,r);return{s0:f,s1:h,s2:d,s3:p}}function fa(e,t,n,r,o){let{sbox2:s,T01:i,T23:a}=is,c=0;t^=e[c++],n^=e[c++],r^=e[c++],o^=e[c++];let l=e.length/4-2;for(let y=0;y<l;y++){let m=e[c++]^qe(i,a,t,o,r,n),w=e[c++]^qe(i,a,n,t,o,r),x=e[c++]^qe(i,a,r,n,t,o),A=e[c++]^qe(i,a,o,r,n,t);t=m,n=w,r=x,o=A}let f=e[c++]^Ee(s,t,o,r,n),h=e[c++]^Ee(s,n,t,o,r),d=e[c++]^Ee(s,r,n,t,o),p=e[c++]^Ee(s,o,r,n,t);return{s0:f,s1:h,s2:d,s3:p}}function ha(e){if(F(e),e.length%ne!==0)throw new Error("aes-(cbc/ecb).decrypt ciphertext should consist of blocks with size "+ne)}function da(e,t,n){F(e);let r=e.length,o=r%ne;if(!t&&o!==0)throw new Error("aec/(cbc-ecb): unpadded plaintext with disabled padding");Xe(e)||(e=pe(e));let s=ee(e);if(t){let a=ne-o;a||(a=ne),r=r+a}n=ct(r,n),jn(e,n);let i=ee(n);return{b:s,o:i,out:n}}function pa(e,t){if(!t)return e;let n=e.length;if(!n)throw new Error("aes/pcks5: empty ciphertext not allowed");let r=e[n-1];if(r<=0||r>16)throw new Error("aes/pcks5: wrong padding");let o=e.subarray(0,-r);for(let s=0;s<r;s++)if(e[n-s-1]!==r)throw new Error("aes/pcks5: wrong padding");return o}function ya(e){let t=new Uint8Array(16),n=ee(t);t.set(e);let r=ne-e.length;for(let o=ne-r;o<ne;o++)t[o]=r;return n}var nr=At({blockSize:16,nonceLength:16},function(t,n,r={}){let o=!r.disablePadding;return{encrypt(s,i){let a=tr(t),{b:c,o:l,out:f}=da(s,o,i),h=n,d=[a];Xe(h)||d.push(h=pe(h));let p=ee(h),y=p[0],m=p[1],w=p[2],x=p[3],A=0;for(;A+4<=c.length;)y^=c[A+0],m^=c[A+1],w^=c[A+2],x^=c[A+3],{s0:y,s1:m,s2:w,s3:x}=Yn(a,y,m,w,x),l[A++]=y,l[A++]=m,l[A++]=w,l[A++]=x;if(o){let I=ya(s.subarray(A*4));y^=I[0],m^=I[1],w^=I[2],x^=I[3],{s0:y,s1:m,s2:w,s3:x}=Yn(a,y,m,w,x),l[A++]=y,l[A++]=m,l[A++]=w,l[A++]=x}return re(...d),f},decrypt(s,i){ha(s);let a=ua(t),c=n,l=[a];Xe(c)||l.push(c=pe(c));let f=ee(c);i=ct(s.length,i),Xe(s)||l.push(s=pe(s)),jn(s,i);let h=ee(s),d=ee(i),p=f[0],y=f[1],m=f[2],w=f[3];for(let x=0;x+4<=h.length;){let A=p,I=y,P=m,Q=w;p=h[x+0],y=h[x+1],m=h[x+2],w=h[x+3];let{s0:V,s1:Z,s2:j,s3:U}=fa(a,p,y,m,w);d[x++]=V^A,d[x++]=Z^I,d[x++]=j^P,d[x++]=U^Q}return re(...l),pa(i,o)}}});function ga(e){return e instanceof Uint32Array||ArrayBuffer.isView(e)&&e.constructor.name==="Uint32Array"}function Jn(e,t){if(F(t,16,"block"),!ga(e))throw new Error("_encryptBlock accepts result of expandKeyLE");let n=ee(t),{s0:r,s1:o,s2:s,s3:i}=Yn(e,n[0],n[1],n[2],n[3]);return n[0]=r,n[1]=o,n[2]=s,n[3]=i,t}function rs(e){let t=0;for(let n=ne-1;n>=0;n--){let r=(e[n]&128)>>>7;e[n]=e[n]<<1|t,t=r}return t&&(e[ne-1]^=135),e}function zt(e,t){if(e.length!==t.length)throw new Error("xorBlock: blocks must have same length");for(let n=0;n<e.length;n++)e[n]=e[n]^t[n];return e}var Zt=class{constructor(t){u(this,"buffer");u(this,"destroyed");u(this,"k1");u(this,"k2");u(this,"xk");F(t),os(t),this.xk=tr(t),this.buffer=new Uint8Array(0),this.destroyed=!1;let n=new Uint8Array(ne);Jn(this.xk,n),this.k1=rs(n),this.k2=rs(new Uint8Array(this.k1))}update(t){let{destroyed:n,buffer:r}=this;if(n)throw new Error("CMAC instance was destroyed");F(t);let o=new Uint8Array(r.length+t.length);return o.set(r),o.set(t,r.length),this.buffer=o,this}digest(){if(this.destroyed)throw new Error("CMAC instance was destroyed");let{buffer:t}=this,n=t.length,r=Math.ceil(n/ne),o;r===0?(r=1,o=!1):o=n%ne===0;let s=(r-1)*ne,i=t.subarray(s),a;if(o)a=zt(new Uint8Array(i),this.k1);else{let l=new Uint8Array(ne);l.set(i),l[i.length]=128,a=zt(l,this.k2)}let c=new Uint8Array(ne);for(let l=0;l<r-1;l++){let f=t.subarray(l*ne,(l+1)*ne);zt(c,f),Jn(this.xk,c)}return zt(c,a),Jn(this.xk,c),re(a),c}destroy(){let{buffer:t,destroyed:n,xk:r,k1:o,k2:s}=this;n||(this.destroyed=!0,re(t,r,o,s))}},ba=(e,t)=>new Zt(e).update(t).digest();ba.create=e=>new Zt(e);var cs=e=>Uint8Array.from(e.split(""),t=>t.charCodeAt(0)),ma=cs("expand 16-byte k"),wa=cs("expand 32-byte k"),xa=ee(ma),va=ee(wa);function k(e,t){return e<<t|e>>>32-t}function rr(e){return e.byteOffset%4===0}var Jt=64,Ea=16,ls=2**32-1,as=Uint32Array.of();function Sa(e,t,n,r,o,s,i,a){let c=o.length,l=new Uint8Array(Jt),f=ee(l),h=rr(o)&&rr(s),d=h?ee(o):as,p=h?ee(s):as;for(let y=0;y<c;i++){if(e(t,n,r,f,i,a),i>=ls)throw new Error("arx: counter overflow");let m=Math.min(Jt,c-y);if(h&&m===Jt){let w=y/4;if(y%4!==0)throw new Error("arx: invalid block position");for(let x=0,A;x<Ea;x++)A=w+x,p[A]=d[A]^f[x];y+=Jt;continue}for(let w=0,x;w<m;w++)x=y+w,s[x]=o[x]^l[w];y+=m}}function or(e,t){let{allowShortKeys:n,extendNonceFn:r,counterLength:o,counterRight:s,rounds:i}=ns({allowShortKeys:!1,counterLength:8,counterRight:!1,rounds:20},t);if(typeof e!="function")throw new Error("core must be a function");return St(o),St(i),jt(s),jt(n),(a,c,l,f,h=0)=>{F(a,void 0,"key"),F(c,void 0,"nonce"),F(l,void 0,"data");let d=l.length;if(f===void 0&&(f=new Uint8Array(d)),F(f,void 0,"output"),St(h),h<0||h>=ls)throw new Error("arx: counter overflow");if(f.length<d)throw new Error(`arx: output (${f.length}) is shorter than data (${d})`);let p=[],y=a.length,m,w;if(y===32)p.push(m=pe(a)),w=va;else if(y===16&&n)m=new Uint8Array(32),m.set(a),m.set(a,16),w=xa,p.push(m);else throw F(a,32,"arx key"),new Error("invalid key size");rr(c)||p.push(c=pe(c));let x=ee(m);if(r){if(c.length!==24)throw new Error("arx: extended nonce must be 24 bytes");r(w,x,ee(c.subarray(0,16)),x),c=c.subarray(16)}let A=16-o;if(A!==c.length)throw new Error(`arx: nonce must be ${A} or 16 bytes`);if(A!==12){let P=new Uint8Array(12);P.set(c,s?0:12-c.length),c=P,p.push(c)}let I=ee(c);return Sa(e,w,x,I,l,f,h,i),re(...p),f}}function oe(e,t){return e[t++]&255|(e[t++]&255)<<8}var sr=class{constructor(t){u(this,"blockLen",16);u(this,"outputLen",16);u(this,"buffer",new Uint8Array(16));u(this,"r",new Uint16Array(10));u(this,"h",new Uint16Array(10));u(this,"pad",new Uint16Array(8));u(this,"pos",0);u(this,"finished",!1);t=pe(F(t,32,"key"));let n=oe(t,0),r=oe(t,2),o=oe(t,4),s=oe(t,6),i=oe(t,8),a=oe(t,10),c=oe(t,12),l=oe(t,14);this.r[0]=n&8191,this.r[1]=(n>>>13|r<<3)&8191,this.r[2]=(r>>>10|o<<6)&7939,this.r[3]=(o>>>7|s<<9)&8191,this.r[4]=(s>>>4|i<<12)&255,this.r[5]=i>>>1&8190,this.r[6]=(i>>>14|a<<2)&8191,this.r[7]=(a>>>11|c<<5)&8065,this.r[8]=(c>>>8|l<<8)&8191,this.r[9]=l>>>5&127;for(let f=0;f<8;f++)this.pad[f]=oe(t,16+2*f)}process(t,n,r=!1){let o=r?0:2048,{h:s,r:i}=this,a=i[0],c=i[1],l=i[2],f=i[3],h=i[4],d=i[5],p=i[6],y=i[7],m=i[8],w=i[9],x=oe(t,n+0),A=oe(t,n+2),I=oe(t,n+4),P=oe(t,n+6),Q=oe(t,n+8),V=oe(t,n+10),Z=oe(t,n+12),j=oe(t,n+14),U=s[0]+(x&8191),W=s[1]+((x>>>13|A<<3)&8191),M=s[2]+((A>>>10|I<<6)&8191),C=s[3]+((I>>>7|P<<9)&8191),L=s[4]+((P>>>4|Q<<12)&8191),b=s[5]+(Q>>>1&8191),g=s[6]+((Q>>>14|V<<2)&8191),E=s[7]+((V>>>11|Z<<5)&8191),S=s[8]+((Z>>>8|j<<8)&8191),B=s[9]+(j>>>5|o),v=0,R=v+U*a+W*(5*w)+M*(5*m)+C*(5*y)+L*(5*p);v=R>>>13,R&=8191,R+=b*(5*d)+g*(5*h)+E*(5*f)+S*(5*l)+B*(5*c),v+=R>>>13,R&=8191;let T=v+U*c+W*a+M*(5*w)+C*(5*m)+L*(5*y);v=T>>>13,T&=8191,T+=b*(5*p)+g*(5*d)+E*(5*h)+S*(5*f)+B*(5*l),v+=T>>>13,T&=8191;let _=v+U*l+W*c+M*a+C*(5*w)+L*(5*m);v=_>>>13,_&=8191,_+=b*(5*y)+g*(5*p)+E*(5*d)+S*(5*h)+B*(5*f),v+=_>>>13,_&=8191;let H=v+U*f+W*l+M*c+C*a+L*(5*w);v=H>>>13,H&=8191,H+=b*(5*m)+g*(5*y)+E*(5*p)+S*(5*d)+B*(5*h),v+=H>>>13,H&=8191;let K=v+U*h+W*f+M*l+C*c+L*a;v=K>>>13,K&=8191,K+=b*(5*w)+g*(5*m)+E*(5*y)+S*(5*p)+B*(5*d),v+=K>>>13,K&=8191;let q=v+U*d+W*h+M*f+C*l+L*c;v=q>>>13,q&=8191,q+=b*a+g*(5*w)+E*(5*m)+S*(5*y)+B*(5*p),v+=q>>>13,q&=8191;let O=v+U*p+W*d+M*h+C*f+L*l;v=O>>>13,O&=8191,O+=b*c+g*a+E*(5*w)+S*(5*m)+B*(5*y),v+=O>>>13,O&=8191;let z=v+U*y+W*p+M*d+C*h+L*f;v=z>>>13,z&=8191,z+=b*l+g*c+E*a+S*(5*w)+B*(5*m),v+=z>>>13,z&=8191;let J=v+U*m+W*y+M*p+C*d+L*h;v=J>>>13,J&=8191,J+=b*f+g*l+E*c+S*a+B*(5*w),v+=J>>>13,J&=8191;let Y=v+U*w+W*m+M*y+C*p+L*d;v=Y>>>13,Y&=8191,Y+=b*h+g*f+E*l+S*c+B*a,v+=Y>>>13,Y&=8191,v=(v<<2)+v|0,v=v+R|0,R=v&8191,v=v>>>13,T+=v,s[0]=R,s[1]=T,s[2]=_,s[3]=H,s[4]=K,s[5]=q,s[6]=O,s[7]=z,s[8]=J,s[9]=Y}finalize(){let{h:t,pad:n}=this,r=new Uint16Array(10),o=t[1]>>>13;t[1]&=8191;for(let a=2;a<10;a++)t[a]+=o,o=t[a]>>>13,t[a]&=8191;t[0]+=o*5,o=t[0]>>>13,t[0]&=8191,t[1]+=o,o=t[1]>>>13,t[1]&=8191,t[2]+=o,r[0]=t[0]+5,o=r[0]>>>13,r[0]&=8191;for(let a=1;a<10;a++)r[a]=t[a]+o,o=r[a]>>>13,r[a]&=8191;r[9]-=8192;let s=(o^1)-1;for(let a=0;a<10;a++)r[a]&=s;s=~s;for(let a=0;a<10;a++)t[a]=t[a]&s|r[a];t[0]=(t[0]|t[1]<<13)&65535,t[1]=(t[1]>>>3|t[2]<<10)&65535,t[2]=(t[2]>>>6|t[3]<<7)&65535,t[3]=(t[3]>>>9|t[4]<<4)&65535,t[4]=(t[4]>>>12|t[5]<<1|t[6]<<14)&65535,t[5]=(t[6]>>>2|t[7]<<11)&65535,t[6]=(t[7]>>>5|t[8]<<8)&65535,t[7]=(t[8]>>>8|t[9]<<5)&65535;let i=t[0]+n[0];t[0]=i&65535;for(let a=1;a<8;a++)i=(t[a]+n[a]|0)+(i>>>16)|0,t[a]=i&65535;re(r)}update(t){Fn(this),F(t),t=pe(t);let{buffer:n,blockLen:r}=this,o=t.length;for(let s=0;s<o;){let i=Math.min(r-this.pos,o-s);if(i===r){for(;r<=o-s;s+=r)this.process(t,s);continue}n.set(t.subarray(s,s+i),this.pos),this.pos+=i,s+=i,this.pos===r&&(this.process(n,0,!1),this.pos=0)}return this}destroy(){re(this.h,this.r,this.buffer,this.pad)}digestInto(t){Fn(this),Qo(t,this),this.finished=!0;let{buffer:n,h:r}=this,{pos:o}=this;if(o){for(n[o++]=1;o<16;o++)n[o]=0;this.process(n,0,!0)}this.finalize();let s=0;for(let i=0;i<8;i++)t[s++]=r[i]>>>0,t[s++]=r[i]>>>8;return t}digest(){let{buffer:t,outputLen:n}=this;this.digestInto(t);let r=t.slice(0,n);return this.destroy(),r}};function Aa(e){let t=(r,o)=>e(o).update(r).digest(),n=e(new Uint8Array(32));return t.outputLen=n.outputLen,t.blockLen=n.blockLen,t.create=r=>e(r),t}var us=Aa(e=>new sr(e));function ds(e,t,n,r,o,s=20){let i=e[0],a=e[1],c=e[2],l=e[3],f=t[0],h=t[1],d=t[2],p=t[3],y=t[4],m=t[5],w=t[6],x=t[7],A=o,I=n[0],P=n[1],Q=n[2],V=i,Z=a,j=c,U=l,W=f,M=h,C=d,L=p,b=y,g=m,E=w,S=x,B=A,v=I,R=P,T=Q;for(let H=0;H<s;H+=2)V=V+W|0,B=k(B^V,16),b=b+B|0,W=k(W^b,12),V=V+W|0,B=k(B^V,8),b=b+B|0,W=k(W^b,7),Z=Z+M|0,v=k(v^Z,16),g=g+v|0,M=k(M^g,12),Z=Z+M|0,v=k(v^Z,8),g=g+v|0,M=k(M^g,7),j=j+C|0,R=k(R^j,16),E=E+R|0,C=k(C^E,12),j=j+C|0,R=k(R^j,8),E=E+R|0,C=k(C^E,7),U=U+L|0,T=k(T^U,16),S=S+T|0,L=k(L^S,12),U=U+L|0,T=k(T^U,8),S=S+T|0,L=k(L^S,7),V=V+M|0,T=k(T^V,16),E=E+T|0,M=k(M^E,12),V=V+M|0,T=k(T^V,8),E=E+T|0,M=k(M^E,7),Z=Z+C|0,B=k(B^Z,16),S=S+B|0,C=k(C^S,12),Z=Z+C|0,B=k(B^Z,8),S=S+B|0,C=k(C^S,7),j=j+L|0,v=k(v^j,16),b=b+v|0,L=k(L^b,12),j=j+L|0,v=k(v^j,8),b=b+v|0,L=k(L^b,7),U=U+W|0,R=k(R^U,16),g=g+R|0,W=k(W^g,12),U=U+W|0,R=k(R^U,8),g=g+R|0,W=k(W^g,7);let _=0;r[_++]=i+V|0,r[_++]=a+Z|0,r[_++]=c+j|0,r[_++]=l+U|0,r[_++]=f+W|0,r[_++]=h+M|0,r[_++]=d+C|0,r[_++]=p+L|0,r[_++]=y+b|0,r[_++]=m+g|0,r[_++]=w+E|0,r[_++]=x+S|0,r[_++]=A+B|0,r[_++]=I+v|0,r[_++]=P+R|0,r[_++]=Q+T|0}function Ta(e,t,n,r){let o=e[0],s=e[1],i=e[2],a=e[3],c=t[0],l=t[1],f=t[2],h=t[3],d=t[4],p=t[5],y=t[6],m=t[7],w=n[0],x=n[1],A=n[2],I=n[3];for(let Q=0;Q<20;Q+=2)o=o+c|0,w=k(w^o,16),d=d+w|0,c=k(c^d,12),o=o+c|0,w=k(w^o,8),d=d+w|0,c=k(c^d,7),s=s+l|0,x=k(x^s,16),p=p+x|0,l=k(l^p,12),s=s+l|0,x=k(x^s,8),p=p+x|0,l=k(l^p,7),i=i+f|0,A=k(A^i,16),y=y+A|0,f=k(f^y,12),i=i+f|0,A=k(A^i,8),y=y+A|0,f=k(f^y,7),a=a+h|0,I=k(I^a,16),m=m+I|0,h=k(h^m,12),a=a+h|0,I=k(I^a,8),m=m+I|0,h=k(h^m,7),o=o+l|0,I=k(I^o,16),y=y+I|0,l=k(l^y,12),o=o+l|0,I=k(I^o,8),y=y+I|0,l=k(l^y,7),s=s+f|0,w=k(w^s,16),m=m+w|0,f=k(f^m,12),s=s+f|0,w=k(w^s,8),m=m+w|0,f=k(f^m,7),i=i+h|0,x=k(x^i,16),d=d+x|0,h=k(h^d,12),i=i+h|0,x=k(x^i,8),d=d+x|0,h=k(h^d,7),a=a+c|0,A=k(A^a,16),p=p+A|0,c=k(c^p,12),a=a+c|0,A=k(A^a,8),p=p+A|0,c=k(c^p,7);let P=0;r[P++]=o,r[P++]=s,r[P++]=i,r[P++]=a,r[P++]=w,r[P++]=x,r[P++]=A,r[P++]=I}var Ye=or(ds,{counterRight:!1,counterLength:4,allowShortKeys:!1}),Ba=or(ds,{counterRight:!1,counterLength:8,extendNonceFn:Ta,allowShortKeys:!1});var Ra=new Uint8Array(16),fs=(e,t)=>{e.update(t);let n=t.length%16;n&&e.update(Ra.subarray(n))},_a=new Uint8Array(32);function hs(e,t,n,r,o){o!==void 0&&F(o,void 0,"AAD");let s=e(t,n,_a),i=zn(r.length,o?o.length:0,!0),a=us.create(s);o&&fs(a,o),fs(a,r),a.update(i);let c=a.digest();return re(s,i),c}var ps=e=>(t,n,r)=>({encrypt(s,i){let a=s.length;i=ct(a+16,i,!1),i.set(s);let c=i.subarray(0,-16);e(t,n,c,c,1);let l=hs(e,t,n,c,r);return i.set(l,a),re(l),i},decrypt(s,i){i=ct(s.length-16,i,!1);let a=s.subarray(0,-16),c=s.subarray(-16),l=hs(e,t,n,a,r);if(!Je(c,l))throw new Error("invalid tag");return i.set(s.subarray(0,-16)),e(t,n,i,i,1),re(l),i}}),$0=At({blockSize:64,nonceLength:12,tagLength:16},ps(Ye)),W0=At({blockSize:64,nonceLength:24,tagLength:16},ps(Ba));var Xt=class{constructor(t,n){u(this,"oHash");u(this,"iHash");u(this,"blockLen");u(this,"outputLen");u(this,"finished",!1);u(this,"destroyed",!1);if(vt(t),ve(n,void 0,"key"),this.iHash=t.create(),typeof this.iHash.update!="function")throw new Error("Expected instance of class which extends utils.Hash");this.blockLen=this.iHash.blockLen,this.outputLen=this.iHash.outputLen;let r=this.blockLen,o=new Uint8Array(r);o.set(n.length>r?t.create().update(n).digest():n);for(let s=0;s<o.length;s++)o[s]^=54;this.iHash.update(o),this.oHash=t.create();for(let s=0;s<o.length;s++)o[s]^=106;this.oHash.update(o),Re(o)}update(t){return it(this),this.iHash.update(t),this}digestInto(t){it(this),ve(t,this.outputLen,"output"),this.finished=!0,this.iHash.digestInto(t),this.oHash.update(t),this.oHash.digestInto(t),this.destroy()}digest(){let t=new Uint8Array(this.oHash.outputLen);return this.digestInto(t),t}_cloneInto(t){t||(t=Object.create(Object.getPrototypeOf(this),{}));let{oHash:n,iHash:r,finished:o,destroyed:s,blockLen:i,outputLen:a}=this;return t=t,t.finished=o,t.destroyed=s,t.blockLen=i,t.outputLen=a,t.oHash=n._cloneInto(t.oHash),t.iHash=r._cloneInto(t.iHash),t}clone(){return this._cloneInto()}destroy(){this.destroyed=!0,this.oHash.destroy(),this.iHash.destroy()}},De=(e,t,n)=>new Xt(e,t).update(n).digest();De.create=(e,t)=>new Xt(e,t);function Yt(e,t,n){return vt(e),n===void 0&&(n=new Uint8Array(e.outputLen)),De(e,n,t)}var ir=Uint8Array.of(0),ys=Uint8Array.of();function Qt(e,t,n,r=32){vt(e),$t(r,"length");let o=e.outputLen;if(r>255*o)throw new Error("Length must be <= 255*HashLen");let s=Math.ceil(r/o);n===void 0?n=ys:ve(n,void 0,"info");let i=new Uint8Array(s*o),a=De.create(e,t),c=a._cloneInto(),l=new Uint8Array(a.outputLen);for(let f=0;f<s;f++)ir[0]=f+1,c.update(f===0?ys:l).update(n).update(ir).digestInto(l),i.set(l,o*f),a._cloneInto(c);return a.destroy(),c.destroy(),Re(l,ir),i.slice(0,r)}var ka=Object.defineProperty,G=(e,t)=>{for(var n in t)ka(e,n,{get:t[n],enumerable:!0})},La={};G(La,{binarySearch:()=>dr,bytesToHex:()=>D,hexToBytes:()=>$,insertEventIntoAscendingList:()=>Ia,insertEventIntoDescendingList:()=>Ca,isHex32:()=>ue,mergeReverseSortedLists:()=>Ha,normalizeURL:()=>et,utf8Decoder:()=>Ce,utf8Encoder:()=>ye});var Ce=new TextDecoder("utf-8"),ye=new TextEncoder;function et(e){try{e.indexOf("://")===-1&&(e="wss://"+e);let t=new URL(e);return t.protocol==="http:"?t.protocol="ws:":t.protocol==="https:"&&(t.protocol="wss:"),t.pathname=t.pathname.replace(/\/+/g,"/"),t.pathname.endsWith("/")&&(t.pathname=t.pathname.slice(0,-1)),(t.port==="80"&&t.protocol==="ws:"||t.port==="443"&&t.protocol==="wss:")&&(t.port=""),t.searchParams.sort(),t.hash="",t.toString()}catch{throw new Error(`Invalid URL: ${e}`)}}function Ca(e,t){let[n,r]=dr(e,o=>t.id===o.id?0:t.created_at===o.created_at?-1:o.created_at-t.created_at);return r||e.splice(n,0,t),e}function Ia(e,t){let[n,r]=dr(e,o=>t.id===o.id?0:t.created_at===o.created_at?-1:t.created_at-o.created_at);return r||e.splice(n,0,t),e}function dr(e,t){let n=0,r=e.length-1;for(;n<=r;){let o=Math.floor((n+r)/2),s=t(e[o]);if(s===0)return[o,!0];s<0?r=o-1:n=o+1}return[n,!1]}function Ha(e,t){let n=new Array(e.length+t.length);n.length=0;let r=0,o=0,s=[];for(;r<e.length&&o<t.length;){let i;if(e[r]?.created_at>t[o]?.created_at?(i=e[r],r++):(i=t[o],o++),n.length>0&&n[n.length-1].created_at===i.created_at){if(s.includes(i.id))continue}else s.length=0;n.push(i),s.push(i.id)}for(;r<e.length;){let i=e[r];if(r++,n.length>0&&n[n.length-1].created_at===i.created_at){if(s.includes(i.id))continue}else s.length=0;n.push(i),s.push(i.id)}for(;o<t.length;){let i=t[o];if(o++,n.length>0&&n[n.length-1].created_at===i.created_at){if(s.includes(i.id))continue}else s.length=0;n.push(i),s.push(i.id)}return n}function ue(e){if(e.length!==64)return!1;for(let t=0;t<64;t++){let n=e.charCodeAt(t);if(isNaN(n)||n<48||n>102||n>57&&n<97)return!1}return!0}var ut=Symbol("verified"),Oa=e=>e instanceof Object;function pr(e){if(!Oa(e)||typeof e.kind!="number"||typeof e.content!="string"||typeof e.created_at!="number"||typeof e.pubkey!="string"||!ue(e.pubkey)||!Array.isArray(e.tags))return!1;for(let t=0;t<e.tags.length;t++){let n=e.tags[t];if(!Array.isArray(n))return!1;for(let r=0;r<n.length;r++)if(typeof n[r]!="string")return!1}return!0}var Ua=class{generateSecretKey(){return he.utils.randomSecretKey()}getPublicKey(e){return D(he.getPublicKey(e))}finalizeEvent(e,t){let n=e;return n.pubkey=D(he.getPublicKey(t)),n.id=tn(n),n.sig=D(he.sign($(tn(n)),t)),n[ut]=!0,n}verifyEvent(e){if(typeof e[ut]=="boolean")return e[ut];try{let t=tn(e);if(t!==e.id)return e[ut]=!1,!1;let n=he.verify($(e.sig),$(t),$(e.pubkey));return e[ut]=n,n}catch{return e[ut]=!1,!1}}};function Pa(e){if(!pr(e))throw new Error("can't serialize event with wrong or missing properties");return JSON.stringify([0,e.pubkey,e.created_at,e.kind,e.tags,e.content])}function tn(e){let t=te(ye.encode(Pa(e)));return D(t)}var an=new Ua,yr=an.generateSecretKey,Tt=an.getPublicKey,ge=an.finalizeEvent,tt=an.verifyEvent,Na={};G(Na,{AIEmbeddings:()=>Cc,AppCurationSet:()=>hu,Application:()=>uu,AuthoredPodcasts:()=>Cl,BadgeAward:()=>Ka,BadgeDefinition:()=>Yl,Bid:()=>ic,BidConfirmation:()=>ac,BlobsAuth:()=>Vl,BlockedRelaysList:()=>dl,BlossomServerList:()=>Bl,BookmarkList:()=>ul,Bookmarksets:()=>jl,Calendar:()=>ku,CalendarEventRSVP:()=>Lu,CashuMintAnnouncement:()=>$u,CashuWalletEvent:()=>ql,CashuWalletHistory:()=>Vc,CashuWalletTokens:()=>Wc,ChannelCreation:()=>Ts,ChannelHideMessage:()=>_s,ChannelMessage:()=>Rs,ChannelMetadata:()=>Bs,ChannelMuteUser:()=>ks,ChatMessage:()=>Fa,Chess:()=>rc,ClassifiedListing:()=>mu,ClientAuth:()=>Ls,CodeSnippet:()=>gc,CoinjoinPool:()=>Oc,Comment:()=>fc,CommunitiesList:()=>fl,CommunityDefinition:()=>Nu,CommunityPostApproval:()=>Nc,ConferenceEvent:()=>yu,Contacts:()=>Wa,CreateOrUpdateProduct:()=>tu,CreateOrUpdateStall:()=>eu,CuratedVideoSets:()=>Zl,Curationsets:()=>zl,Date:()=>Ru,DecoupledEncryptionKeyDistribution:()=>Pc,DecoupledKeyAnnouncement:()=>Sl,DecoupledKeyClientAnnouncement:()=>Uc,DirectMessageRelaysList:()=>Al,DraftClassifiedListing:()=>wu,DraftEvent:()=>Au,DraftLong:()=>su,Emojisets:()=>iu,EncryptedDirectMessage:()=>Va,EventDeletion:()=>Ga,FavoriteFollowSets:()=>vl,FavoritePodcasts:()=>Tl,FavoriteRelays:()=>gl,FedimintAnnouncement:()=>Wu,Feed:()=>Bu,FileMessage:()=>Ja,FileMetadata:()=>lc,FileServerPreference:()=>Rl,Followsets:()=>Gl,ForumThread:()=>za,GenericRepost:()=>xr,Genericlists:()=>Kl,GeocacheListing:()=>qu,GeocacheLog:()=>Gc,GeocacheLogEntry:()=>Du,GeocacheProofOfFind:()=>Kc,GiftWrap:()=>on,GitPullRequest:()=>mc,GitPullRequestUpdate:()=>wc,GoodWikiAuthorList:()=>_l,GoodWikiRelayList:()=>kl,GroupMetadata:()=>Gu,HTTPAuth:()=>vr,Handlerinformation:()=>Hu,Handlerrecommendation:()=>Iu,Highlights:()=>il,InteractiveRoom:()=>pu,InterestsList:()=>ml,Interestsets:()=>Ql,Issue:()=>xc,JobFeedback:()=>Dc,JobRequest:()=>Mc,JobResult:()=>qc,Label:()=>kc,LegacyNsiteFile:()=>Uu,LightningPubRPC:()=>Dl,LinkSet:()=>Tu,LiveChatMessage:()=>yc,LiveEvent:()=>du,LongFormArticle:()=>ou,MarketplaceUI:()=>nu,MediaFollows:()=>xl,MediaStarterPacks:()=>Ju,MergeRequests:()=>oc,Metadata:()=>Da,ModularArticleContent:()=>cu,ModularArticleHeader:()=>au,MuteSets:()=>Jl,Mutelist:()=>al,NWCWalletInfo:()=>Nl,NWCWalletRequest:()=>Cs,NWCWalletResponse:()=>$l,NormalVideo:()=>Qa,NostrConnect:()=>Wl,NsiteNamed:()=>Mu,NsiteRoot:()=>Ml,NutZap:()=>nl,NutZapInfo:()=>wl,OpenTimestamps:()=>cc,Patch:()=>bc,PeerToPeerOrderEvents:()=>Vu,Photo:()=>Ya,Pinlist:()=>cl,PodcastEpisode:()=>nc,PodcastMetadata:()=>Ll,Poll:()=>uc,PollResponse:()=>sc,PrivateDirectMessage:()=>As,PrivateEventRelayList:()=>bl,ProblemTracker:()=>Bc,ProductSoldAsAuction:()=>ru,ProfileBadges:()=>Xl,ProxyAnnouncement:()=>Ul,PublicChatsList:()=>hl,PublicMessage:()=>tc,Reaction:()=>wr,ReactionToWebsite:()=>Xa,RecommendRelay:()=>$a,Redirects:()=>Su,RelayDiscovery:()=>fu,RelayList:()=>ll,RelayMonitorAnnouncement:()=>Il,RelayReview:()=>Cu,RelayReviews:()=>Lc,Relaysets:()=>Fl,ReleaseArtifactSets:()=>lu,Reply:()=>vc,Report:()=>Rc,Reporting:()=>_c,RepositoryAnnouncement:()=>xu,RepositoryState:()=>vu,Repost:()=>mr,ReservedCashuWalletTokens:()=>$c,RoomPresence:()=>Hl,Scroll:()=>dc,Seal:()=>rn,SearchRelaysList:()=>pl,ShortTextNote:()=>Ss,ShortVideo:()=>ec,SimpleGroupAdmins:()=>Ku,SimpleGroupCreateGroup:()=>Jc,SimpleGroupCreateInvite:()=>Yc,SimpleGroupDeleteEvent:()=>Zc,SimpleGroupDeleteGroup:()=>Xc,SimpleGroupEditMetadata:()=>zc,SimpleGroupJoinRequest:()=>Qc,SimpleGroupLeaveRequest:()=>el,SimpleGroupList:()=>yl,SimpleGroupLiveKitParticipants:()=>zu,SimpleGroupMembers:()=>Fu,SimpleGroupPutUser:()=>Fc,SimpleGroupRemoveUser:()=>jc,SimpleGroupReply:()=>Za,SimpleGroupRoles:()=>ju,SimpleGroupThreadedReply:()=>ja,SlideSet:()=>bu,SoftwareApplication:()=>Ou,StarterPacks:()=>Zu,StatusApplied:()=>Sc,StatusClosed:()=>Ac,StatusDraft:()=>Tc,StatusOpen:()=>Ec,TidalLogin:()=>rl,Time:()=>_u,Torrent:()=>Ic,TorrentComment:()=>Hc,TransportMethodAnnouncement:()=>Pl,UserEmojiList:()=>El,UserGraspList:()=>Ol,UserStatuses:()=>gu,VideoViewEvent:()=>Pu,Voice:()=>hc,VoiceComment:()=>pc,WebBookmarks:()=>Xu,WikiArticle:()=>Eu,Zap:()=>sl,ZapGoal:()=>tl,ZapRequest:()=>ol,classifyKind:()=>Ma,isAddressableKind:()=>br,isEphemeralKind:()=>Es,isKind:()=>qa,isRegularKind:()=>vs,isReplaceableKind:()=>gr});function vs(e){return e<1e4&&e!==0&&e!==3}function gr(e){return e===0||e===3||1e4<=e&&e<2e4}function Es(e){return 2e4<=e&&e<3e4}function br(e){return 3e4<=e&&e<4e4}function Ma(e){return vs(e)?"regular":gr(e)?"replaceable":Es(e)?"ephemeral":br(e)?"parameterized":"unknown"}function qa(e,t){let n=t instanceof Array?t:[t];return pr(e)&&n.includes(e.kind)||!1}var Da=0,Ss=1,$a=2,Wa=3,Va=4,Ga=5,mr=6,wr=7,Ka=8,Fa=9,ja=10,za=11,Za=12,rn=13,As=14,Ja=15,xr=16,Xa=17,Ya=20,Qa=21,ec=22,tc=24,Ts=40,Bs=41,Rs=42,_s=43,ks=44,nc=54,rc=64,oc=818,sc=1018,ic=1021,ac=1022,cc=1040,on=1059,lc=1063,uc=1068,fc=1111,hc=1222,dc=1227,pc=1244,yc=1311,gc=1337,bc=1617,mc=1618,wc=1619,xc=1621,vc=1622,Ec=1630,Sc=1631,Ac=1632,Tc=1633,Bc=1971,Rc=1984,_c=1984,kc=1985,Lc=1986,Cc=1987,Ic=2003,Hc=2004,Oc=2022,Uc=4454,Pc=4455,Nc=4550,Mc=5999,qc=6999,Dc=7e3,$c=7374,Wc=7375,Vc=7376,Gc=7516,Kc=7517,Fc=9e3,jc=9001,zc=9002,Zc=9005,Jc=9007,Xc=9008,Yc=9009,Qc=9021,el=9022,tl=9041,nl=9321,rl=9467,ol=9734,sl=9735,il=9802,al=1e4,cl=10001,ll=10002,ul=10003,fl=10004,hl=10005,dl=10006,pl=10007,yl=10009,gl=10012,bl=10013,ml=10015,wl=10019,xl=10020,vl=10021,El=10030,Sl=10044,Al=10050,Tl=10054,Bl=10063,Rl=10096,_l=10101,kl=10102,Ll=10154,Cl=10164,Il=10166,Hl=10312,Ol=10317,Ul=10377,Pl=11111,Nl=13194,Ml=15128,ql=17375,Dl=21e3,Ls=22242,Cs=23194,$l=23195,Wl=24133,Vl=24242,vr=27235,Gl=3e4,Kl=30001,Fl=30002,jl=30003,zl=30004,Zl=30005,Jl=30007,Xl=30008,Yl=30009,Ql=30015,eu=30017,tu=30018,nu=30019,ru=30020,ou=30023,su=30024,iu=30030,au=30040,cu=30041,lu=30063,uu=30078,fu=30166,hu=30267,du=30311,pu=30312,yu=30313,gu=30315,bu=30388,mu=30402,wu=30403,xu=30617,vu=30618,Eu=30818,Su=30819,Au=31234,Tu=31388,Bu=31890,Ru=31922,_u=31923,ku=31924,Lu=31925,Cu=31987,Iu=31989,Hu=31990,Ou=32267,Uu=34128,Pu=34237,Nu=34550,Mu=35128,qu=37515,Du=37516,$u=38172,Wu=38173,Vu=38383,Gu=39e3,Ku=39001,Fu=39002,ju=39003,zu=39004,Zu=39089,Ju=39092,Xu=39701;function Yu(e,t){if(e.ids&&e.ids.indexOf(t.id)===-1||e.kinds&&e.kinds.indexOf(t.kind)===-1||e.authors&&e.authors.indexOf(t.pubkey)===-1)return!1;for(let n in e)if(n[0]==="#"){let r=n.slice(1),o=e[`#${r}`];if(o&&!t.tags.find(([s,i])=>s===n.slice(1)&&o.indexOf(i)!==-1))return!1}return!(e.since&&t.created_at<e.since||e.until&&t.created_at>e.until)}function Qu(e,t){for(let n=0;n<e.length;n++)if(Yu(e[n],t))return!0;return!1}var ef={};G(ef,{getHex64:()=>cn,getInt:()=>Is,getSubscriptionId:()=>Hs,matchEventId:()=>tf,matchEventKind:()=>rf,matchEventPubkey:()=>nf});function cn(e,t){let n=t.length+3,r=e.indexOf(`"${t}":`)+n,o=e.slice(r).indexOf('"')+r+1;return e.slice(o,o+64)}function Is(e,t){let n=t.length,r=e.indexOf(`"${t}":`)+n+3,o=e.slice(r),s=Math.min(o.indexOf(","),o.indexOf("}"));return parseInt(o.slice(0,s),10)}function Hs(e){let t=e.slice(0,22).indexOf('"EVENT"');if(t===-1)return null;let n=e.slice(t+7+1).indexOf('"');if(n===-1)return null;let r=t+7+1+n,o=e.slice(r+1,80).indexOf('"');if(o===-1)return null;let s=r+1+o;return e.slice(r+1,s)}function tf(e,t){return t===cn(e,"id")}function nf(e,t){return t===cn(e,"pubkey")}function rf(e,t){return t===Is(e,"kind")}var of={};G(of,{makeAuthEvent:()=>Os});function Os(e,t){return{kind:Ls,created_at:Math.floor(Date.now()/1e3),tags:[["relay",e],["challenge",t]],content:""}}var hr=class extends Error{constructor(e,t){super(`Tried to send message '${e} on a closed connection to ${t}.`),this.name="SendingOnClosedConnection"}},Us=class{constructor(e,t){u(this,"url");u(this,"_connected",!1);u(this,"onclose",null);u(this,"onnotice",e=>console.debug(`NOTICE from ${this.url}: ${e}`));u(this,"onauth");u(this,"baseEoseTimeout",4400);u(this,"publishTimeout",4400);u(this,"pingFrequency",29e3);u(this,"pingTimeout",2e4);u(this,"resubscribeBackoff",[1e4,1e4,1e4,2e4,2e4,3e4,6e4]);u(this,"openSubs",new Map);u(this,"enablePing");u(this,"enableReconnect");u(this,"idleTimeout",0);u(this,"idleSince",Date.now());u(this,"ongoingOperations",0);u(this,"reconnectTimeoutHandle");u(this,"pingIntervalHandle");u(this,"reconnectAttempts",0);u(this,"skipReconnection",!1);u(this,"idleTimeoutHandle");u(this,"connectionPromise");u(this,"openCountRequests",new Map);u(this,"openEventPublishes",new Map);u(this,"ws");u(this,"challenge");u(this,"authPromise");u(this,"serial",0);u(this,"verifyEvent");u(this,"_WebSocket");this.url=et(e),this.verifyEvent=t.verifyEvent,this._WebSocket=t.websocketImplementation||WebSocket,this.enablePing=t.enablePing,this.enableReconnect=t.enableReconnect||!1,t.idleTimeout&&(this.idleTimeout=t.idleTimeout)}static async connect(e,t){let n=new Us(e,t);return await n.connect(t),n}closeAllSubscriptions(e){for(let[t,n]of this.openSubs)n.close(e);this.openSubs.clear();for(let[t,n]of this.openEventPublishes)n.reject(new Error(e));this.openEventPublishes.clear();for(let[t,n]of this.openCountRequests)n.reject(new Error(e));this.openCountRequests.clear()}get connected(){return this._connected}clearIdleTimeout(){this.idleTimeoutHandle&&(clearTimeout(this.idleTimeoutHandle),this.idleTimeoutHandle=void 0)}scheduleIdleClose(){this.clearIdleTimeout(),this.idleTimeout>0&&(this.idleTimeoutHandle=setTimeout(()=>{this.ongoingOperations===0&&this.idleSince&&this.close()},this.idleTimeout))}async reconnect(){let e=this.resubscribeBackoff[Math.min(this.reconnectAttempts,this.resubscribeBackoff.length-1)];this.reconnectAttempts++,this.reconnectTimeoutHandle=setTimeout(async()=>{try{await this.connect()}catch{}},e)}handleHardClose(e){this.ws&&(this.ws.onopen=null,this.ws.onerror=null,this.ws.onclose=null),this.pingIntervalHandle&&(clearInterval(this.pingIntervalHandle),this.pingIntervalHandle=void 0),this._connected=!1,this.connectionPromise=void 0,this.idleSince=void 0,this.clearIdleTimeout(),this.enableReconnect&&!this.skipReconnection?this.reconnect():(this.onclose?.(),this.closeAllSubscriptions(e))}async connect(e){let t;return this.connectionPromise?this.connectionPromise:(this.challenge=void 0,this.authPromise=void 0,this.skipReconnection=!1,this.connectionPromise=new Promise((n,r)=>{e?.timeout&&(t=setTimeout(()=>{r("connection timed out"),this.connectionPromise=void 0,this.reconnectAttempts===0&&(this.skipReconnection=!0),this.handleHardClose("relay connection timed out")},e.timeout)),e?.abort&&(e.abort.onabort=r);try{this.ws=new this._WebSocket(this.url)}catch(o){clearTimeout(t),r(o);return}this.ws.onopen=()=>{this.reconnectTimeoutHandle&&(clearTimeout(this.reconnectTimeoutHandle),this.reconnectTimeoutHandle=void 0),clearTimeout(t),this._connected=!0;let o=this.reconnectAttempts>0;this.reconnectAttempts=0;for(let s of this.openSubs.values()){if(s.eosed=!1,o)for(let i=0;i<s.filters.length;i++)s.lastEmitted&&(s.filters[i].since=s.lastEmitted+1);s.fire()}this.enablePing&&(this.pingIntervalHandle=setInterval(()=>this.pingpong(),this.pingFrequency)),n()},this.ws.onerror=()=>{clearTimeout(t),r("connection failed"),this.connectionPromise=void 0,this.reconnectAttempts===0&&(this.skipReconnection=!0),this.handleHardClose("relay connection failed")},this.ws.onclose=o=>{clearTimeout(t),r(o.message||"websocket closed"),this.handleHardClose("relay connection closed")},this.ws.onmessage=this._onmessage.bind(this)}),this.connectionPromise)}waitForPingPong(){return new Promise(e=>{this.ws.once("pong",()=>e(!0)),this.ws.ping()})}waitForDummyReq(){return new Promise((e,t)=>{if(!this.connectionPromise)return t(new Error(`no connection to ${this.url}, can't ping`));try{let n=this.subscribe([{ids:["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],limit:0}],{label:"<forced-ping>",oneose:()=>{e(!0),n.close()},onclose(){e(!0)},eoseTimeout:this.pingTimeout+1e3})}catch(n){t(n)}})}async pingpong(){this.ws?.readyState===1&&(await Promise.any([this.ws&&this.ws.ping&&this.ws.once?this.waitForPingPong():this.waitForDummyReq(),new Promise(t=>setTimeout(()=>t(!1),this.pingTimeout))])||this.ws?.readyState===this._WebSocket.OPEN&&this.ws?.close())}async send(e){if(!this.connectionPromise)throw new hr(e,this.url);this.connectionPromise.then(()=>{this.ws?.send(e)})}async auth(e){let t=this.challenge;if(!t)throw new Error("can't perform auth, no challenge was received");return this.authPromise?this.authPromise:(this.authPromise=new Promise(async(n,r)=>{try{let o=await e(Os(this.url,t)),s=setTimeout(()=>{let i=this.openEventPublishes.get(o.id);i&&(i.reject(new Error("auth timed out")),this.openEventPublishes.delete(o.id))},this.publishTimeout);this.openEventPublishes.set(o.id,{resolve:n,reject:r,timeout:s}),this.send('["AUTH",'+JSON.stringify(o)+"]")}catch(o){console.warn("subscribe auth function failed:",o)}}),this.authPromise)}async publish(e){this.idleSince=void 0,this.clearIdleTimeout(),this.ongoingOperations++;let t=new Promise((n,r)=>{let o=setTimeout(()=>{let s=this.openEventPublishes.get(e.id);s&&(s.reject(new Error("publish timed out")),this.openEventPublishes.delete(e.id))},this.publishTimeout);this.openEventPublishes.set(e.id,{resolve:n,reject:r,timeout:o})});try{await this.send('["EVENT",'+JSON.stringify(e)+"]")}catch(n){let r=this.openEventPublishes.get(e.id);r&&(r.reject(n),this.openEventPublishes.delete(e.id))}return this.ongoingOperations--,this.ongoingOperations===0&&(this.idleSince=Date.now(),this.scheduleIdleClose()),t}async count(e,t){return(await this.countWithHLL(e,t)).count}async countWithHLL(e,t){this.serial++;let n=t?.id||"count:"+this.serial,r=new Promise((o,s)=>{this.openCountRequests.set(n,{resolve:o,reject:s})});try{await this.send('["COUNT","'+n+'",'+JSON.stringify(e).substring(1))}catch(o){let s=this.openCountRequests.get(n);s&&(s.reject(o),this.openCountRequests.delete(n))}return r}subscribe(e,t){t.label!=="<forced-ping>"&&(this.idleSince=void 0,this.clearIdleTimeout(),this.ongoingOperations++);let n=this.prepareSubscription(e,t);return n.fire(),t.abort&&(t.abort.onabort=()=>n.close(String(t.abort.reason||"<aborted>"))),n}prepareSubscription(e,t){this.serial++;let n=t.id||(t.label?t.label+":":"sub:")+this.serial,r=new sf(this,n,e,t);return this.openSubs.set(n,r),r}close(){this.skipReconnection=!0,this.reconnectTimeoutHandle&&(clearTimeout(this.reconnectTimeoutHandle),this.reconnectTimeoutHandle=void 0),this.pingIntervalHandle&&(clearInterval(this.pingIntervalHandle),this.pingIntervalHandle=void 0),this.closeAllSubscriptions("relay connection closed by us"),this._connected=!1,this.connectionPromise=void 0,this.idleSince=void 0,this.clearIdleTimeout(),this.onclose?.(),this.ws&&(this.ws.onopen=null,this.ws.onerror=null,this.ws.onclose=null,this.ws.readyState!==this._WebSocket.CLOSING&&this.ws.readyState!==this._WebSocket.CLOSED&&this.ws.close())}_onmessage(e){let t=e.data;if(!t)return;let n=Hs(t);if(n){let r=this.openSubs.get(n);if(!r)return;let o=cn(t,"id"),s=r.alreadyHaveEvent?.(o);if(r.receivedEvent?.(this,o),s)return}try{let r=JSON.parse(t);switch(r[0]){case"EVENT":{let o=this.openSubs.get(r[1]),s=r[2];Qu(o.filters,s)&&this.verifyEvent(s,this.url)?o.onevent(s):o.oninvalidevent?.(s),(!o.lastEmitted||o.lastEmitted<s.created_at)&&(o.lastEmitted=s.created_at);return}case"COUNT":{let o=r[1],s=r[2],i=this.openCountRequests.get(o);i&&(i.resolve(s),this.openCountRequests.delete(o));return}case"EOSE":{let o=this.openSubs.get(r[1]);if(!o)return;o.receivedEose();return}case"OK":{let o=r[1],s=r[2],i=r[3],a=this.openEventPublishes.get(o);a&&(clearTimeout(a.timeout),s?a.resolve(i):a.reject(new Error(i)),this.openEventPublishes.delete(o));return}case"CLOSED":{let o=r[1],s=this.openSubs.get(o);if(!s){let i=this.openCountRequests.get(o);i&&(i.reject(new Error(r[2])),this.openCountRequests.delete(o));return}s.closed=!0,s.close(r[2]);return}case"NOTICE":{this.onnotice(r[1]);return}case"AUTH":{this.challenge=r[1],this.onauth&&this.auth(this.onauth).catch(o=>{if(!(o instanceof hr))throw o});return}default:{this.openSubs.get(r[1])?.oncustom?.(r);return}}}catch(r){try{let[o,s,i]=JSON.parse(t);console.warn(`[nostr] relay ${this.url} error processing message:`,r,i)}catch{console.warn(`[nostr] relay ${this.url} error processing message:`,r)}return}}},sf=class{constructor(e,t,n,r){u(this,"relay");u(this,"id");u(this,"lastEmitted");u(this,"closed",!1);u(this,"eosed",!1);u(this,"filters");u(this,"alreadyHaveEvent");u(this,"receivedEvent");u(this,"onevent");u(this,"oninvalidevent");u(this,"oneose");u(this,"onclose");u(this,"oncustom");u(this,"eoseTimeout");u(this,"eoseTimeoutHandle");if(n.length===0)throw new Error("subscription can't be created with zero filters");this.relay=e,this.filters=n,this.id=t,this.alreadyHaveEvent=r.alreadyHaveEvent,this.receivedEvent=r.receivedEvent,this.eoseTimeout=r.eoseTimeout||e.baseEoseTimeout,this.oneose=r.oneose,this.onclose=r.onclose,this.oninvalidevent=r.oninvalidevent,this.onevent=r.onevent||(o=>{console.warn(`onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,o)})}fire(){this.relay.send('["REQ","'+this.id+'",'+JSON.stringify(this.filters).substring(1)),this.eoseTimeoutHandle=setTimeout(this.receivedEose.bind(this),this.eoseTimeout)}receivedEose(){this.eosed||(clearTimeout(this.eoseTimeoutHandle),this.eosed=!0,this.oneose?.())}close(e="closed by caller"){if(!this.closed&&this.relay.connected){try{this.relay.send('["CLOSE",'+JSON.stringify(this.id)+"]")}catch(t){if(!(t instanceof hr))throw t}this.closed=!0}this.relay.openSubs.delete(this.id),this.id.startsWith("<forced-ping>")||(this.relay.ongoingOperations--,this.relay.ongoingOperations===0&&(this.relay.idleSince=Date.now(),this.relay.scheduleIdleClose())),this.onclose?.(e)}},af;try{af=WebSocket}catch{}var Ie=256,cf=Ie*2,ep=new TextEncoder;function lf(e,t){switch(t){case"reactions":return{"#e":[e],kinds:[7]};case"reposts":return{"#e":[e],kinds:[6]};case"quotes":return{"#q":[e],kinds:[1,1111]};case"replies":return{"#e":[e],kinds:[1]};case"comments":return{"#E":[e],kinds:[1111]};case"followers":return{"#p":[e],kinds:[3]}}}function uf(){return new Uint8Array(Ie)}function ff(e){if(e.length!==cf||!/^[0-9a-f]+$/.test(e))return;let t=new Uint8Array(Ie);for(let n=0;n<Ie;n++)t[n]=parseInt(e.slice(n*2,n*2+2),16);return t}function hf(e){if(e.length!==Ie)throw new Error(`invalid number of registers ${e.length}`);let t="";for(let n=0;n<Ie;n++)t+=e[n].toString(16).padStart(2,"0");return t}function df(e,t){if(e.length===0&&(e=uf()),e.length!==Ie)throw new Error(`invalid number of registers ${e.length}`);if(t.length!==Ie)throw new Error(`invalid number of registers ${t.length}`);for(let n=0;n<Ie;n++)t[n]>e[n]&&(e[n]=t[n]);return e}var pf=class{constructor(e){u(this,"relays",new Map);u(this,"seenOn",new Map);u(this,"trackRelays",!1);u(this,"verifyEvent");u(this,"enablePing");u(this,"enableReconnect");u(this,"idleTimeout",2e4);u(this,"automaticallyAuth");u(this,"onRelayConnectionFailure");u(this,"onRelayConnectionSuccess");u(this,"allowConnectingToRelay");u(this,"maxWaitForConnection");u(this,"_WebSocket");this.verifyEvent=e.verifyEvent,this._WebSocket=e.websocketImplementation,this.enablePing=e.enablePing,this.enableReconnect=e.enableReconnect||!1,e.idleTimeout&&(this.idleTimeout=e.idleTimeout),this.automaticallyAuth=e.automaticallyAuth,this.onRelayConnectionFailure=e.onRelayConnectionFailure,this.onRelayConnectionSuccess=e.onRelayConnectionSuccess,this.allowConnectingToRelay=e.allowConnectingToRelay,this.maxWaitForConnection=e.maxWaitForConnection||3e3}async ensureRelay(e,t){e=et(e);let n=this.relays.get(e);if(n||(n=new Us(e,{verifyEvent:this.verifyEvent,websocketImplementation:this._WebSocket,enablePing:this.enablePing,enableReconnect:this.enableReconnect,idleTimeout:this.idleTimeout}),n.onclose=()=>{this.relays.delete(e)},this.relays.set(e,n)),this.automaticallyAuth){let r=this.automaticallyAuth(e);r&&(n.onauth=r)}try{await n.connect({timeout:t?.connectionTimeout,abort:t?.abort})}catch(r){throw this.relays.delete(e),r}return n}close(e){e.map(et).forEach(t=>{this.relays.get(t)?.close(),this.relays.delete(t)})}subscribe(e,t,n){let r=[],o=[];for(let s=0;s<e.length;s++){let i=et(e[s]);r.find(a=>a.url===i)||o.indexOf(i)===-1&&(o.push(i),r.push({url:i,filter:t}))}return this.subscribeMap(r,n)}subscribeMany(e,t,n){return this.subscribe(e,t,n)}subscribeMap(e,t){let n=new Map;for(let d of e){let{url:p,filter:y}=d;n.has(p)||n.set(p,[]),n.get(p).push(y)}let r=Array.from(n.entries()).map(([d,p])=>({url:d,filters:p}));this.trackRelays&&(t.receivedEvent=(d,p)=>{let y=this.seenOn.get(p);y||(y=new Set,this.seenOn.set(p,y)),y.add(d)});let o=new Set,s=[],i=[],a=d=>{i[d]||(i[d]=!0,i.filter(p=>p).length===r.length&&(t.oneose?.(),a=()=>{}))},c=[],l=(d,p,y)=>{c[d]||(a(d),c[d]={url:p,reason:y},c.filter(m=>m).length===r.length&&(t.onclose?.(c),l=()=>{}))},f=d=>{if(t.alreadyHaveEvent?.(d))return!0;let p=o.has(d);return o.add(d),p},h=Promise.all(r.map(async({url:d,filters:p},y)=>{if(this.allowConnectingToRelay?.(d,["read",p])===!1){l(y,d,"connection skipped by allowConnectingToRelay");return}let m;try{m=await this.ensureRelay(d,{connectionTimeout:this.maxWaitForConnection<(t.maxWait||0)?Math.max(t.maxWait*.8,t.maxWait-1e3):this.maxWaitForConnection,abort:t.abort})}catch(x){this.onRelayConnectionFailure?.(d),l(y,d,x?.message||String(x));return}this.onRelayConnectionSuccess?.(d);let w=m.subscribe(p,{...t,oneose:()=>a(y),onclose:x=>{x.startsWith("auth-required: ")&&t.onauth?m.auth(t.onauth).then(()=>{m.subscribe(p,{...t,oneose:()=>a(y),onclose:A=>{l(y,d,A)},alreadyHaveEvent:f,eoseTimeout:t.maxWait,abort:t.abort})}).catch(A=>{l(y,d,`auth was required and attempted, but failed with: ${A}`)}):l(y,d,x)},alreadyHaveEvent:f,eoseTimeout:t.maxWait,abort:t.abort});s.push(w)}));return{async close(d){await h,s.forEach(p=>{p.close(d)})}}}subscribeEose(e,t,n){let r;return r=this.subscribe(e,t,{...n,oneose(){let o="closed automatically on eose";r?r.close(o):n.onclose?.(e.map(s=>({url:s,reason:o})))}}),r}subscribeManyEose(e,t,n){return this.subscribeEose(e,t,n)}async querySync(e,t,n){return new Promise(async r=>{let o=[];this.subscribeEose(e,t,{...n,onevent(s){o.push(s)},onclose(s){r(o)}})})}async get(e,t,n){t.limit=1;let r=await this.querySync(e,t,n);return r.sort((o,s)=>s.created_at-o.created_at),r[0]||null}async countMany(e,t,n,r){let o=lf(t,n),s=[];for(let l=0;l<e.length;l++){let f=et(e[l]);s.indexOf(f)===-1&&s.push(f)}let i=await Promise.all(s.map(async l=>{if(this.allowConnectingToRelay?.(l,["read",[o]])===!1)return null;let f;try{f=await this.ensureRelay(l,{connectionTimeout:this.maxWaitForConnection<(r?.maxWait||0)?Math.max(r.maxWait*.8,r.maxWait-1e3):this.maxWaitForConnection,abort:r?.abort})}catch{return this.onRelayConnectionFailure?.(l),null}return this.onRelayConnectionSuccess?.(l),f.countWithHLL([o],{id:r?.id}).catch(()=>null)})),a=0,c;for(let l of i){if(!l||(l.count>a&&(a=l.count),!l.hll||l.hll.length!==512))continue;let f=ff(l.hll);f&&(c=df(c||new Uint8Array(0),f))}return c?{count:a,hll:hf(c)}:{count:a}}publish(e,t,n){return e.map(et).map(async(r,o,s)=>{if(s.indexOf(r)!==o)return Promise.reject("duplicate url");if(this.allowConnectingToRelay?.(r,["write",t])===!1)return Promise.reject("connection skipped by allowConnectingToRelay");let i;try{i=await this.ensureRelay(r,{connectionTimeout:this.maxWaitForConnection<(n?.maxWait||0)?Math.max(n.maxWait*.8,n.maxWait-1e3):this.maxWaitForConnection,abort:n?.abort})}catch(a){return this.onRelayConnectionFailure?.(r),Promise.reject("connection failure: "+String(a))}return i.publish(t).catch(async a=>{if(a instanceof Error&&a.message.startsWith("auth-required: ")&&n?.onauth)return await i.auth(n.onauth),i.publish(t);throw a}).then(a=>{if(this.trackRelays){let c=this.seenOn.get(t.id);c||(c=new Set,this.seenOn.set(t.id,c)),c.add(i)}return a})})}listConnectionStatus(){let e=new Map;return this.relays.forEach((t,n)=>e.set(n,t.connected)),e}destroy(){this.relays.forEach(e=>e.close()),this.relays=new Map}pruneIdleRelays(e=1e4){let t=[];for(let[n,r]of this.relays)r.idleSince&&Date.now()-r.idleSince>=e&&(this.relays.delete(n),t.push(n),r.close());return t}},Ps;try{Ps=WebSocket}catch{}var Ns=class extends pf{constructor(e){super({verifyEvent:tt,websocketImplementation:Ps,maxWaitForConnection:3e3,...e})}},Er={};G(Er,{BECH32_REGEX:()=>Ms,Bech32MaxSize:()=>Sr,NostrTypeGuard:()=>yf,decode:()=>ln,decodeNostrURI:()=>bf,encodeBytes:()=>fn,naddrEncode:()=>Sf,neventEncode:()=>Ef,noteEncode:()=>xf,nprofileEncode:()=>vf,npubEncode:()=>wf,nsecEncode:()=>mf});var yf={isNProfile:e=>/^nprofile1[a-z\d]+$/.test(e||""),isNEvent:e=>/^nevent1[a-z\d]+$/.test(e||""),isNAddr:e=>/^naddr1[a-z\d]+$/.test(e||""),isNSec:e=>/^nsec1[a-z\d]{58}$/.test(e||""),isNPub:e=>/^npub1[a-z\d]{58}$/.test(e||""),isNote:e=>/^note1[a-z\d]+$/.test(e||""),isNcryptsec:e=>/^ncryptsec1[a-z\d]+$/.test(e||"")},Sr=5e3,Ms=/[\x21-\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/;function gf(e){let t=new Uint8Array(4);return t[0]=e>>24&255,t[1]=e>>16&255,t[2]=e>>8&255,t[3]=e&255,t}function bf(e){try{return e.startsWith("nostr:")&&(e=e.substring(6)),ln(e)}catch{return{type:"invalid",data:null}}}function ln(e){let{prefix:t,words:n}=Me.decode(e,Sr),r=new Uint8Array(Me.fromWords(n));switch(t){case"nprofile":{let o=ar(r);if(!o[0]?.[0])throw new Error("missing TLV 0 for nprofile");if(o[0][0].length!==32)throw new Error("TLV 0 should be 32 bytes");return{type:"nprofile",data:{pubkey:D(o[0][0]),relays:o[1]?o[1].map(s=>Ce.decode(s)):[]}}}case"nevent":{let o=ar(r);if(!o[0]?.[0])throw new Error("missing TLV 0 for nevent");if(o[0][0].length!==32)throw new Error("TLV 0 should be 32 bytes");if(o[2]&&o[2][0].length!==32)throw new Error("TLV 2 should be 32 bytes");if(o[3]&&o[3][0].length!==4)throw new Error("TLV 3 should be 4 bytes");return{type:"nevent",data:{id:D(o[0][0]),relays:o[1]?o[1].map(s=>Ce.decode(s)):[],author:o[2]?.[0]?D(o[2][0]):void 0,kind:o[3]?.[0]?parseInt(D(o[3][0]),16):void 0}}}case"naddr":{let o=ar(r);if(!o[0]?.[0])throw new Error("missing TLV 0 for naddr");if(!o[2]?.[0])throw new Error("missing TLV 2 for naddr");if(o[2][0].length!==32)throw new Error("TLV 2 should be 32 bytes");if(!o[3]?.[0])throw new Error("missing TLV 3 for naddr");if(o[3][0].length!==4)throw new Error("TLV 3 should be 4 bytes");return{type:"naddr",data:{identifier:Ce.decode(o[0][0]),pubkey:D(o[2][0]),kind:parseInt(D(o[3][0]),16),relays:o[1]?o[1].map(s=>Ce.decode(s)):[]}}}case"nsec":return{type:t,data:r};case"npub":case"note":return{type:t,data:D(r)};default:throw new Error(`unknown prefix ${t}`)}}function ar(e){let t={},n=e;for(;n.length>0;){if(n.length<2)throw new Error("not enough data to read TLV");let r=n[0],o=n[1],s=n.slice(2,2+o);if(n=n.slice(2+o),s.length<o)throw new Error(`not enough data to read on TLV ${r}`);t[r]=t[r]||[],t[r].push(s)}return t}function mf(e){return fn("nsec",e)}function wf(e){return fn("npub",$(e))}function xf(e){return fn("note",$(e))}function un(e,t){let n=Me.toWords(t);return Me.encode(e,n,Sr)}function fn(e,t){return un(e,t)}function vf(e){let t=Ar({0:[$(e.pubkey)],1:(e.relays||[]).map(n=>ye.encode(n))});return un("nprofile",t)}function Ef(e){let t;e.kind!==void 0&&(t=gf(e.kind));let n=Ar({0:[$(e.id)],1:(e.relays||[]).map(r=>ye.encode(r)),2:e.author?[$(e.author)]:[],3:t?[new Uint8Array(t)]:[]});return un("nevent",n)}function Sf(e){let t=new ArrayBuffer(4);new DataView(t).setUint32(0,e.kind,!1);let n=Ar({0:[ye.encode(e.identifier)],1:(e.relays||[]).map(r=>ye.encode(r)),2:[$(e.pubkey)],3:[new Uint8Array(t)]});return un("naddr",n)}function Ar(e){let t=[];return Object.entries(e).reverse().forEach(([n,r])=>{r.forEach(o=>{let s=new Uint8Array(o.length+2);s.set([parseInt(n)],0),s.set([o.length],1),s.set(o,2),t.push(s)})}),de(...t)}var Af={};G(Af,{decrypt:()=>Tf,encrypt:()=>qs});function qs(e,t,n){let r=e instanceof Uint8Array?e:$(e),o=ze.getSharedSecret(r,$("02"+t)),s=Ds(o),i=Uint8Array.from(at(16)),a=ye.encode(n),c=nr(s,i).encrypt(a),l=le.encode(new Uint8Array(c)),f=le.encode(new Uint8Array(i.buffer));return`${l}?iv=${f}`}function Tf(e,t,n){let r=e instanceof Uint8Array?e:$(e),[o,s]=n.split("?iv="),i=ze.getSharedSecret(r,$("02"+t)),a=Ds(i),c=le.decode(s),l=le.decode(o),f=nr(a,c).decrypt(l);return Ce.decode(f)}function Ds(e){return e.slice(1,33)}var Bf={};G(Bf,{NIP05_REGEX:()=>Tr,isNip05:()=>Rf,isValid:()=>Lf,queryProfile:()=>$s,searchDomain:()=>kf,useFetchImplementation:()=>_f});var Tr=/^(?:([\w.+-]+)@)?([\w_-]+(\.[\w_-]+)+)$/,Rf=e=>Tr.test(e||""),hn;try{hn=fetch}catch{}function _f(e){hn=e}async function kf(e,t=""){try{let n=`https://${e}/.well-known/nostr.json?name=${t}`,r=await hn(n,{redirect:"manual"});if(r.status!==200)throw Error("Wrong response code");return(await r.json()).names}catch{return{}}}async function $s(e){let t=e.match(Tr);if(!t)return null;let[,n="_",r]=t;try{let o=`https://${r}/.well-known/nostr.json?name=${n}`,s=await hn(o,{redirect:"manual"});if(s.status!==200)throw Error("Wrong response code");let i=await s.json(),a=i.names[n];return a?{pubkey:a,relays:i.relays?.[a]}:null}catch{return null}}async function Lf(e,t){let n=await $s(t);return n?n.pubkey===e:!1}var Cf={};G(Cf,{parse:()=>If});function If(e){let t={reply:void 0,root:void 0,mentions:[],profiles:[],quotes:[]},n,r;for(let o=e.tags.length-1;o>=0;o--){let s=e.tags[o];if(s[0]==="e"&&s[1]&&ue(s[1])){let[i,a,c,l,f]=s,h={id:a,relays:c?[c]:[],author:f&&ue(f)?f:void 0};if(l==="root"){t.root=h;continue}if(l==="reply"){t.reply=h;continue}if(l==="mention"){t.mentions.push(h);continue}n?r=h:n=h,t.mentions.push(h);continue}if(s[0]==="q"&&s[1]&&ue(s[1])){let[i,a,c]=s;t.quotes.push({id:a,relays:c?[c]:[]})}if(s[0]==="p"&&s[1]&&ue(s[1])){t.profiles.push({pubkey:s[1],relays:s[2]?[s[2]]:[]});continue}}return t.root||(t.root=r||n||t.reply),t.reply||(t.reply=n||t.root),[t.reply,t.root].forEach(o=>{if(!o)return;let s=t.mentions.indexOf(o);if(s!==-1&&t.mentions.splice(s,1),o.author){let i=t.profiles.find(a=>a.pubkey===o.author);i&&i.relays&&(o.relays||(o.relays=[]),i.relays.forEach(a=>{o.relays?.indexOf(a)===-1&&o.relays.push(a)}),i.relays=o.relays)}}),t.mentions.forEach(o=>{if(o.author){let s=t.profiles.find(i=>i.pubkey===o.author);s&&s.relays&&(o.relays||(o.relays=[]),s.relays.forEach(i=>{o.relays.indexOf(i)===-1&&o.relays.push(i)}),s.relays=o.relays)}}),t}var Hf={};G(Hf,{fetchRelayInformation:()=>Uf,useFetchImplementation:()=>Of});var Ws;try{Ws=fetch}catch{}function Of(e){Ws=e}async function Uf(e){return await(await fetch(e.replace("ws://","http://").replace("wss://","https://"),{headers:{Accept:"application/nostr+json"}})).json()}var Pf={};G(Pf,{getPow:()=>Nf,minePow:()=>qf});function Nf(e){let t=0;for(let n=0;n<64;n+=8){let r=parseInt(e.substring(n,n+8),16);if(r===0)t+=32;else{t+=Math.clz32(r);break}}return t}function Mf(e){let t=0;for(let n=0;n<e.length;n++){let r=e[n];if(r===0)t+=8;else{t+=Math.clz32(r)-24;break}}return t}function qf(e,t){let n=0,r=e,o=["nonce",n.toString(),t.toString()];for(r.tags.push(o);;){let s=Math.floor(new Date().getTime()/1e3);s!==r.created_at&&(n=0,r.created_at=s),o[1]=(++n).toString();let i=te(ye.encode(JSON.stringify([0,r.pubkey,r.created_at,r.kind,r.tags,r.content])));if(Mf(i)>=t){r.id=D(i);break}}return r}var Df={};G(Df,{unwrapEvent:()=>Xf,unwrapManyEvents:()=>Yf,wrapEvent:()=>ti,wrapManyEvents:()=>Jf});var $f={};G($f,{createRumor:()=>Xs,createSeal:()=>Ys,createWrap:()=>Qs,unwrapEvent:()=>Hr,unwrapManyEvents:()=>ei,wrapEvent:()=>sn,wrapManyEvents:()=>zf});var Wf={};G(Wf,{decrypt:()=>Ir,encrypt:()=>Cr,getConversationKey:()=>kr,v2:()=>Ff});var Br=1,Rr=4294967295,_r=65536;function kr(e,t){let n=ze.getSharedSecret(e,$("02"+t)).subarray(1,33);return Yt(te,n,ye.encode("nip44-v2"))}function Vs(e,t){let n=Qt(te,e,t,76);return{chacha_key:n.subarray(0,32),chacha_nonce:n.subarray(32,44),hmac_key:n.subarray(44,76)}}function Lr(e){if(!Number.isSafeInteger(e)||e<1)throw new Error("expected positive integer");if(e<=32)return 32;let t=2**(Math.floor(Math.log2(e-1))+1),n=t<=256?32:t/8;return n*(Math.floor((e-1)/n)+1)}function Vf(e){if(!Number.isSafeInteger(e)||e<Br||e>65535)throw new Error("invalid plaintext size: must be between 1 and 65535 bytes");let t=new Uint8Array(2);return new DataView(t.buffer).setUint16(0,e,!1),t}function Gf(e){if(!Number.isSafeInteger(e)||e<_r||e>Rr)throw new Error("invalid plaintext size: must be between 65536 and 4294967295 bytes");let t=new Uint8Array(4);return new DataView(t.buffer).setUint32(0,e,!1),t}function Gs(e){let t=ye.encode(e),n=t.length;if(n<Br||n>Rr)throw new Error("invalid plaintext size: must be between 1 and 4294967295 bytes");let r=n>=_r?de(new Uint8Array([0,0]),Gf(n)):Vf(n),o=new Uint8Array(Lr(n)-n);return de(r,t,o)}function Ks(e){let t=new DataView(e.buffer,e.byteOffset,e.byteLength),n=t.getUint16(0),r,o;if(n===0){if(r=t.getUint32(2),r<_r)throw new Error("invalid padding");o=6}else r=n,o=2;let s=e.subarray(o,o+r);if(r<Br||r>Rr||s.length!==r||e.length!==o+Lr(r))throw new Error("invalid padding");return Ce.decode(s)}function Fs(e,t,n){if(n.length!==32)throw new Error("AAD associated data must be 32 bytes");let r=de(n,t);return De(te,e,r)}function Kf(e){if(typeof e!="string")throw new Error("payload must be a valid string");let t=e.length;if(t<132)throw new Error("invalid payload length: "+t);if(e[0]==="#")throw new Error("unknown encryption version");let n;try{n=le.decode(e)}catch(s){throw new Error("invalid base64: "+s.message)}let r=n.length;if(r<99)throw new Error("invalid data length: "+r);let o=n[0];if(o!==2)throw new Error("unknown encryption version "+o);return{nonce:n.subarray(1,33),ciphertext:n.subarray(33,-32),mac:n.subarray(-32)}}function Cr(e,t,n=at(32)){let{chacha_key:r,chacha_nonce:o,hmac_key:s}=Vs(t,n),i=Gs(e),a=Ye(r,o,i),c=Fs(s,a,n);return le.encode(de(new Uint8Array([2]),n,a,c))}function Ir(e,t){let{nonce:n,ciphertext:r,mac:o}=Kf(e),{chacha_key:s,chacha_nonce:i,hmac_key:a}=Vs(t,n),c=Fs(a,r,n);if(!Je(c,o))throw new Error("invalid MAC");let l=Ye(s,i,r);return Ks(l)}var Ff={utils:{getConversationKey:kr,calcPaddedLen:Lr,pad:Gs,unpad:Ks},encrypt:Cr,decrypt:Ir},jf=2880*60,js=()=>Math.round(Date.now()/1e3),zs=()=>Math.round(js()-Math.random()*jf),Zs=(e,t)=>kr(e,t),Js=(e,t,n)=>Cr(JSON.stringify(e),Zs(t,n)),gs=(e,t)=>JSON.parse(Ir(e.content,Zs(t,e.pubkey)));function Xs(e,t){let n={created_at:js(),content:"",tags:[],...e,pubkey:Tt(t)};return n.id=tn(n),n}function Ys(e,t,n){return ge({kind:rn,content:Js(e,t,n),created_at:zs(),tags:[]},t)}function Qs(e,t){let n=yr();return ge({kind:on,content:Js(e,n,t),created_at:zs(),tags:[["p",t]]},n)}function sn(e,t,n){let r=Xs(e,t),o=Ys(r,t,n);return Qs(o,n)}function zf(e,t,n){if(!n||n.length===0)throw new Error("At least one recipient is required.");let r=Tt(t),o=[sn(e,t,r)];return n.forEach(s=>{o.push(sn(e,t,s))}),o}function Hr(e,t){if(e.kind!==on)throw new Error(`unexpected wrap kind ${e.kind}, expected ${on}`);let n=gs(e,t);if(n.kind!==rn)throw new Error(`unexpected seal kind ${n.kind}, expected ${rn}`);if(!tt(n))throw new Error("seal signature is invalid");let r=gs(n,t);if(r.pubkey!==n.pubkey)throw new Error(`rumor pubkey ${r.pubkey} does not match seal pubkey ${n.pubkey}`);return r}function ei(e,t){let n=[];return e.forEach(r=>{try{n.push(Hr(r,t))}catch{}}),n.sort((r,o)=>r.created_at-o.created_at),n}function Zf(e,t,n,r){let o={created_at:Math.ceil(Date.now()/1e3),kind:As,tags:[],content:t};return(Array.isArray(e)?e:[e]).forEach(({publicKey:i,relayUrl:a})=>{o.tags.push(a?["p",i,a]:["p",i])}),r&&o.tags.push(["e",r.eventId,r.relayUrl||"","reply"]),n&&o.tags.push(["subject",n]),o}function ti(e,t,n,r,o){let s=Zf(t,n,r,o);return sn(s,e,t.publicKey)}function Jf(e,t,n,r,o){if(!t||t.length===0)throw new Error("At least one recipient is required.");return[{publicKey:Tt(e)},...t].map(i=>ti(e,i,n,r,o))}var Xf=Hr,Yf=ei,Qf={};G(Qf,{finishRepostEvent:()=>eh,getRepostedEvent:()=>th,getRepostedEventPointer:()=>ni});function eh(e,t,n,r){let o,s=[...e.tags??[],["e",t.id,n],["p",t.pubkey]];return t.kind===Ss?o=mr:(o=xr,s.push(["k",String(t.kind)])),ge({kind:o,tags:s,content:e.content===""||t.tags?.find(i=>i[0]==="-")?"":JSON.stringify(t),created_at:e.created_at},r)}function ni(e){if(![mr,xr].includes(e.kind))return;let t,n;for(let r=e.tags.length-1;r>=0&&(t===void 0||n===void 0);r--){let o=e.tags[r];o.length>=2&&(o[0]==="e"&&t===void 0?t=o:o[0]==="p"&&n===void 0&&(n=o))}if(t!==void 0)return{id:t[1],relays:[t[2],n?.[2]].filter(r=>typeof r=="string"),author:n?.[1]}}function th(e,{skipVerification:t}={}){let n=ni(e);if(n===void 0||e.content==="")return;let r;try{r=JSON.parse(e.content)}catch{return}if(r.id===n.id&&!(!t&&!tt(r)))return r}var nh={};G(nh,{NOSTR_URI_REGEX:()=>Or,parse:()=>oh,test:()=>rh});var Or=new RegExp(`nostr:(${Ms.source})`);function rh(e){return typeof e=="string"&&new RegExp(`^${Or.source}$`).test(e)}function oh(e){let t=e.match(new RegExp(`^${Or.source}$`));if(!t)throw new Error(`Invalid Nostr URI: ${e}`);return{uri:t[0],value:t[1],decoded:ln(t[1])}}var sh={};G(sh,{parse:()=>ah});function bs(e){if(e)return/^\d+$/.test(e)?parseInt(e,10):e}function ri(e,t){let n=e.indexOf(":"),r=e.indexOf(":",n+1);if(n===-1||r===-1)return;let o=parseInt(e.slice(0,n),10);if(Number.isNaN(o))return;let s=e.slice(n+1,r);if(ue(s))return{kind:o,pubkey:s,identifier:e.slice(r+1),relays:t?[t]:[]}}function ms(e){switch(e[0]){case"E":case"e":return!e[1]||!ue(e[1])?void 0:{id:e[1],relays:e[2]?[e[2]]:[],author:e[3]&&ue(e[3])?e[3]:void 0};case"A":case"a":return e[1]?ri(e[1],e[2]):void 0;case"I":case"i":return e[1]?{value:e[1],hint:e[2]}:void 0}}function ih(e){if(e[1]){if(e[1].includes(":"))return ri(e[1],e[2]);if(ue(e[1]))return{id:e[1],relays:e[2]?[e[2]]:[],author:e[3]&&ue(e[3])?e[3]:void 0}}}function ws(e){return e.findLast(t=>t.tagName==="A"||t.tagName==="a")?.pointer||e.findLast(t=>t.tagName==="I"||t.tagName==="i")?.pointer||e.findLast(t=>t.tagName==="E"||t.tagName==="e")?.pointer}function cr(e,t){if(!e||!("id"in e)||!e.author)return;let n=t.find(r=>r.pubkey===e.author);!n||!n.relays||(e.relays||(e.relays=[]),n.relays.forEach(r=>{e.relays.indexOf(r)===-1&&e.relays.push(r)}),n.relays=e.relays)}function ah(e){let t={root:void 0,rootKind:void 0,reply:void 0,replyKind:void 0,mentions:[],quotes:[],profiles:[]},n=[],r=[];for(let o of e.tags){if((o[0]==="E"||o[0]==="A"||o[0]==="I")&&o[1]){let s=ms(o);s&&n.push({tagName:o[0],pointer:s});continue}if((o[0]==="e"||o[0]==="a"||o[0]==="i")&&o[1]){let s=ms(o);s&&r.push({tagName:o[0],pointer:s});continue}if(o[0]==="K"){t.rootKind=bs(o[1]);continue}if(o[0]==="k"){t.replyKind=bs(o[1]);continue}if(o[0]==="q"){let s=ih(o);s&&t.quotes.push(s);continue}(o[0]==="P"||o[0]==="p")&&o[1]&&ue(o[1])&&t.profiles.push({pubkey:o[1],relays:o[2]?[o[2]]:[]})}return t.root=ws(n),t.reply=ws(r),cr(t.root,t.profiles),cr(t.reply,t.profiles),t.quotes.forEach(o=>cr(o,t.profiles)),t}var ch={};G(ch,{finishReactionEvent:()=>lh,getReactedEventPointer:()=>uh});function lh(e,t,n){let r=t.tags.filter(o=>o.length>=2&&(o[0]==="e"||o[0]==="p"));return ge({...e,kind:wr,tags:[...e.tags??[],...r,["e",t.id],["p",t.pubkey]],content:e.content??"+"},n)}function uh(e){if(e.kind!==wr)return;let t,n;for(let r=e.tags.length-1;r>=0&&(t===void 0||n===void 0);r--){let o=e.tags[r];o.length>=2&&(o[0]==="e"&&t===void 0?t=o:o[0]==="p"&&n===void 0&&(n=o))}if(!(t===void 0||n===void 0))return{id:t[1],relays:[t[2],n[2]].filter(r=>r!==void 0),author:n[1]}}var fh={};G(fh,{parse:()=>dh});var lr=/\W/m,xs=/[^\w\/] |[^\w\/]$|$|,| /m,hh=42;function*dh(e){let t=[];if(typeof e!="string"){for(let s=0;s<e.tags.length;s++){let i=e.tags[s];i[0]==="emoji"&&i.length>=3&&t.push({type:"emoji",shortcode:i[1],url:i[2]})}e=e.content}let n=e.length,r=0,o=0;e:for(;o<n;){let s=e.indexOf(":",o),i=e.indexOf("#",o);if(s===-1&&i===-1)break e;if(s===-1||i>=0&&i<s){if(i===0||e[i-1].match(lr)){let a=e.slice(i+1,i+hh).match(lr),c=a?i+1+a.index:n;yield{type:"text",text:e.slice(r,i),start:r,end:i},yield{type:"hashtag",value:e.slice(i+1,c),start:i,end:c},o=c,r=o;continue e}o=i+1;continue e}if(e.slice(s-5,s)==="nostr"){let a=e.slice(s+60).match(lr),c=a?s+60+a.index:n;try{let l,{data:f,type:h}=ln(e.slice(s+1,c));switch(h){case"npub":l={pubkey:f};break;case"note":l={id:f};break;case"nsec":o=c+1;continue;default:l=f}r!==s-5&&(yield{type:"text",text:e.slice(r,s-5),start:r,end:s-5}),yield{type:"reference",pointer:l,start:s-5,end:c},o=c,r=o;continue e}catch{o=s+1;continue e}}else if(e.slice(s-5,s)==="https"||e.slice(s-4,s)==="http"){let a=e.slice(s+4).match(xs),c=a?s+4+a.index:n,l=e[s-1]==="s"?5:4;try{let f=new URL(e.slice(s-l,c));if(f.hostname.indexOf(".")===-1)throw new Error("invalid url");if(r!==s-l&&(yield{type:"text",text:e.slice(r,s-l),start:r,end:s-l}),/\.(png|jpe?g|gif|webp|heic|svg)$/i.test(f.pathname)){yield{type:"image",url:f.toString(),start:s-l,end:c},o=c,r=o;continue e}if(/\.(mp4|avi|webm|mkv|mov)$/i.test(f.pathname)){yield{type:"video",url:f.toString(),start:s-l,end:c},o=c,r=o;continue e}if(/\.(mp3|aac|ogg|opus|wav|flac)$/i.test(f.pathname)){yield{type:"audio",url:f.toString(),start:s-l,end:c},o=c,r=o;continue e}yield{type:"url",url:f.toString(),start:s-l,end:c},o=c,r=o;continue e}catch{o=c+1;continue e}}else if(e.slice(s-3,s)==="wss"||e.slice(s-2,s)==="ws"){let a=e.slice(s+4).match(xs),c=a?s+4+a.index:n,l=e[s-1]==="s"?3:2;try{let f=new URL(e.slice(s-l,c));if(f.hostname.indexOf(".")===-1)throw new Error("invalid ws url");r!==s-l&&(yield{type:"text",text:e.slice(r,s-l),start:r,end:s-l}),yield{type:"relay",url:f.toString(),start:s-l,end:c},o=c,r=o;continue e}catch{o=c+1;continue e}}else{for(let a=0;a<t.length;a++){let c=t[a];if(e[s+c.shortcode.length+1]===":"&&e.slice(s+1,s+c.shortcode.length+1)===c.shortcode){r!==s&&(yield{type:"text",text:e.slice(r,s),start:r,end:s}),yield{...c,start:s,end:s+c.shortcode.length+2},o=s+c.shortcode.length+2,r=o;continue e}}o=s+1;continue e}}r!==n&&(yield{type:"text",text:e.slice(r),start:r,end:n})}var ph={};G(ph,{channelCreateEvent:()=>yh,channelHideMessageEvent:()=>mh,channelMessageEvent:()=>bh,channelMetadataEvent:()=>gh,channelMuteUserEvent:()=>wh});var yh=(e,t)=>{let n;if(typeof e.content=="object")n=JSON.stringify(e.content);else if(typeof e.content=="string")n=e.content;else return;return ge({kind:Ts,tags:[...e.tags??[]],content:n,created_at:e.created_at},t)},gh=(e,t)=>{let n;if(typeof e.content=="object")n=JSON.stringify(e.content);else if(typeof e.content=="string")n=e.content;else return;return ge({kind:Bs,tags:[["e",e.channel_create_event_id],...e.tags??[]],content:n,created_at:e.created_at},t)},bh=(e,t)=>{let n=[["e",e.channel_create_event_id,e.relay_url,"root"]];return e.reply_to_channel_message_event_id&&n.push(["e",e.reply_to_channel_message_event_id,e.relay_url,"reply"]),ge({kind:Rs,tags:[...n,...e.tags??[]],content:e.content,created_at:e.created_at},t)},mh=(e,t)=>{let n;if(typeof e.content=="object")n=JSON.stringify(e.content);else if(typeof e.content=="string")n=e.content;else return;return ge({kind:_s,tags:[["e",e.channel_message_event_id],...e.tags??[]],content:n,created_at:e.created_at},t)},wh=(e,t)=>{let n;if(typeof e.content=="object")n=JSON.stringify(e.content);else if(typeof e.content=="string")n=e.content;else return;return ge({kind:ks,tags:[["p",e.pubkey_to_mute],...e.tags??[]],content:n,created_at:e.created_at},t)},xh={};G(xh,{EMOJI_SHORTCODE_REGEX:()=>oi,matchAll:()=>vh,regex:()=>Ur,replaceAll:()=>Eh});var oi=/:(\w+):/,Ur=()=>new RegExp(`\\B${oi.source}\\B`,"g");function*vh(e){let t=e.matchAll(Ur());for(let n of t)try{let[r,o]=n;yield{shortcode:r,name:o,start:n.index,end:n.index+r.length}}catch{}}function Eh(e,t){return e.replaceAll(Ur(),(n,r)=>t({shortcode:n,name:r}))}var Sh={};G(Sh,{useFetchImplementation:()=>Ah,validateGithub:()=>Th});var Pr;try{Pr=fetch}catch{}function Ah(e){Pr=e}async function Th(e,t,n){try{return await(await Pr(`https://gist.github.com/${t}/${n}/raw`)).text()===`Verifying that I control the following Nostr public key: ${e}`}catch{return!1}}var Bh={};G(Bh,{makeNwcRequestEvent:()=>_h,parseConnectionString:()=>Rh});function Rh(e){let{host:t,pathname:n,searchParams:r}=new URL(e),o=n||t,s=r.getAll("relay"),i=r.get("secret");if(!o||s.length===0||!i)throw new Error("invalid connection string");return{pubkey:o,relay:s[0],relays:s,secret:i}}async function _h(e,t,n){let o=qs(t,e,JSON.stringify({method:"pay_invoice",params:{invoice:n}})),s={kind:Cs,created_at:Math.round(Date.now()/1e3),content:o,tags:[["p",e]]};return ge(s,t)}var kh={};G(kh,{normalizeIdentifier:()=>Lh});function Lh(e){return e=e.trim().toLowerCase(),e=e.normalize("NFKC"),Array.from(e).map(t=>/\p{Letter}/u.test(t)||/\p{Number}/u.test(t)?t:"-").join("")}var Ch={};G(Ch,{getSatoshisAmountFromBolt11:()=>Nh,getZapEndpoint:()=>Hh,makeZapReceipt:()=>Ph,makeZapRequest:()=>Oh,useFetchImplementation:()=>Ih,validateZapRequest:()=>Uh});var Nr;try{Nr=fetch}catch{}function Ih(e){Nr=e}async function Hh(e){try{let t="",{lud06:n,lud16:r}=JSON.parse(e.content);if(r){let[i,a]=r.split("@");t=new URL(`/.well-known/lnurlp/${i}`,`https://${a}`).toString()}else if(n){let{words:i}=Me.decode(n,1e3),a=Me.fromWords(i);t=Ce.decode(a)}else return null;let s=await(await Nr(t)).json();if(s.allowsNostr&&s.nostrPubkey)return s.callback}catch{}return null}function Oh(e){let t={kind:9734,created_at:Math.round(Date.now()/1e3),content:e.comment||"",tags:[["p","pubkey"in e?e.pubkey:e.event.pubkey],["amount",e.amount.toString()],["relays",...e.relays]]};if("event"in e){if(t.tags.push(["e",e.event.id]),gr(e.event.kind)){let n=["a",`${e.event.kind}:${e.event.pubkey}:`];t.tags.push(n)}else if(br(e.event.kind)){let n=e.event.tags.find(([o,s])=>o==="d"&&s);if(!n)throw new Error("d tag not found or is empty");let r=["a",`${e.event.kind}:${e.event.pubkey}:${n[1]}`];t.tags.push(r)}t.tags.push(["k",e.event.kind.toString()])}return t}function Uh(e){let t;try{t=JSON.parse(e)}catch{return"Invalid zap request JSON."}if(!pr(t))return"Zap request is not a valid Nostr event.";if(!tt(t))return"Invalid signature on zap request.";let n=t.tags.find(([s,i])=>s==="p"&&i);if(!n)return"Zap request doesn't have a 'p' tag.";if(!ue(n[1]))return"Zap request 'p' tag is not valid hex.";let r=t.tags.find(([s,i])=>s==="e"&&i);return r&&!ue(r[1])?"Zap request 'e' tag is not valid hex.":t.tags.find(([s,i])=>s==="relays"&&i)?null:"Zap request doesn't have a 'relays' tag."}function Ph({zapRequest:e,preimage:t,bolt11:n,paidAt:r}){let o=JSON.parse(e),s=o.tags.filter(([a])=>a==="e"||a==="p"||a==="a"),i={kind:9735,created_at:Math.round(r.getTime()/1e3),content:"",tags:[...s,["P",o.pubkey],["bolt11",n],["description",e]]};return t&&i.tags.push(["preimage",t]),i}function Nh(e){if(e.length<50)return 0;e=e.substring(0,50);let t=e.lastIndexOf("1");if(t===-1)return 0;let n=e.substring(0,t);if(!n.startsWith("lnbc"))return 0;let r=n.substring(4);if(r.length<1)return 0;let o=r[r.length-1],s=o.charCodeAt(0)-48,i=s>=0&&s<=9,a=r.length-1;if(i&&a++,a<1)return 0;let c=parseInt(r.substring(0,a));switch(o){case"m":return c*1e5;case"u":return c*100;case"n":return c/10;case"p":return c/1e4;default:return c*1e8}}var Mh={};G(Mh,{Negentropy:()=>ii,NegentropyStorageVector:()=>$h,NegentropySync:()=>Wh});var ur=97,ft=32,si=16,Qe={Skip:0,Fingerprint:1,IdList:2},Le=class{constructor(e){u(this,"_raw");u(this,"length");typeof e=="number"?(this._raw=new Uint8Array(e),this.length=0):e instanceof Uint8Array?(this._raw=new Uint8Array(e),this.length=e.length):(this._raw=new Uint8Array(512),this.length=0)}unwrap(){return this._raw.subarray(0,this.length)}get capacity(){return this._raw.byteLength}extend(e){if(e instanceof Le&&(e=e.unwrap()),typeof e.length!="number")throw Error("bad length");let t=e.length+this.length;if(this.capacity<t){let n=this._raw,r=Math.max(this.capacity*2,t);this._raw=new Uint8Array(r),this._raw.set(n)}this._raw.set(e,this.length),this.length+=e.length}shift(){let e=this._raw[0];return this._raw=this._raw.subarray(1),this.length--,e}shiftN(e=1){let t=this._raw.subarray(0,e);return this._raw=this._raw.subarray(e),this.length-=e,t}};function en(e){let t=0;for(;;){if(e.length===0)throw Error("parse ends prematurely");let n=e.shift();if(t=t<<7|n&127,(n&128)===0)break}return t}function ke(e){if(e===0)return new Le(new Uint8Array([0]));let t=[];for(;e!==0;)t.push(e&127),e>>>=7;t.reverse();for(let n=0;n<t.length-1;n++)t[n]|=128;return new Le(new Uint8Array(t))}function qh(e){return nn(e,1)[0]}function nn(e,t){if(e.length<t)throw Error("parse ends prematurely");return e.shiftN(t)}var Dh=class{constructor(){u(this,"buf");this.setToZero()}setToZero(){this.buf=new Uint8Array(ft)}add(e){let t=0,n=0,r=new DataView(this.buf.buffer),o=new DataView(e.buffer);for(let s=0;s<8;s++){let i=s*4,a=r.getUint32(i,!0),c=o.getUint32(i,!0),l=a;l+=t,l+=c,l>4294967295&&(n=1),r.setUint32(i,l&4294967295,!0),t=n,n=0}}negate(){let e=new DataView(this.buf.buffer);for(let n=0;n<8;n++){let r=n*4;e.setUint32(r,~e.getUint32(r,!0))}let t=new Uint8Array(ft);t[0]=1,this.add(t)}getFingerprint(e){let t=new Le;return t.extend(this.buf),t.extend(ke(e)),te(t.unwrap()).subarray(0,si)}},$h=class{constructor(){u(this,"items");u(this,"sealed");this.items=[],this.sealed=!1}insert(e,t){if(this.sealed)throw Error("already sealed");let n=$(t);if(n.byteLength!==ft)throw Error("bad id size for added item");this.items.push({timestamp:e,id:n})}seal(){if(this.sealed)throw Error("already sealed");this.sealed=!0,this.items.sort(fr);for(let e=1;e<this.items.length;e++)if(fr(this.items[e-1],this.items[e])===0)throw Error("duplicate item inserted")}unseal(){this.sealed=!1}size(){return this._checkSealed(),this.items.length}getItem(e){if(this._checkSealed(),e>=this.items.length)throw Error("out of range");return this.items[e]}iterate(e,t,n){this._checkSealed(),this._checkBounds(e,t);for(let r=e;r<t&&n(this.items[r],r);++r);}findLowerBound(e,t,n){return this._checkSealed(),this._checkBounds(e,t),this._binarySearch(this.items,e,t,r=>fr(r,n)<0)}fingerprint(e,t){let n=new Dh;return n.setToZero(),this.iterate(e,t,r=>(n.add(r.id),!0)),n.getFingerprint(t-e)}_checkSealed(){if(!this.sealed)throw Error("not sealed")}_checkBounds(e,t){if(e>t||t>this.items.length)throw Error("bad range")}_binarySearch(e,t,n,r){let o=n-t;for(;o>0;){let s=t,i=Math.floor(o/2);s+=i,r(e[s])?(t=++s,o-=i+1):o=i}return t}},ii=class{constructor(e,t=6e4){u(this,"storage");u(this,"frameSizeLimit");u(this,"lastTimestampIn");u(this,"lastTimestampOut");if(t<4096)throw Error("frameSizeLimit too small");this.storage=e,this.frameSizeLimit=t,this.lastTimestampIn=0,this.lastTimestampOut=0}_bound(e,t){return{timestamp:e,id:t||new Uint8Array(0)}}initiate(){let e=new Le;return e.extend(new Uint8Array([ur])),this.splitRange(0,this.storage.size(),this._bound(Number.MAX_VALUE),e),D(e.unwrap())}reconcile(e,t,n){let r=new Le($(e));this.lastTimestampIn=this.lastTimestampOut=0;let o=new Le;o.extend(new Uint8Array([ur]));let s=qh(r);if(s<96||s>111)throw Error("invalid negentropy protocol version byte");if(s!==ur)throw Error("unsupported negentropy protocol version requested: "+(s-96));let i=this.storage.size(),a=this._bound(0),c=0,l=!1;for(;r.length!==0;){let f=new Le,h=()=>{l&&(l=!1,f.extend(this.encodeBound(a)),f.extend(ke(Qe.Skip)))},d=this.decodeBound(r),p=en(r),y=c,m=this.storage.findLowerBound(c,i,d);if(p===Qe.Skip)l=!0;else if(p===Qe.Fingerprint){let w=nn(r,si),x=this.storage.fingerprint(y,m);ai(w,x)!==0?(h(),this.splitRange(y,m,d,f)):l=!0}else if(p===Qe.IdList){let w=en(r),x={};for(let A=0;A<w;A++){let I=nn(r,ft);x[D(I)]=I}if(l=!0,this.storage.iterate(y,m,A=>{let I=A.id,P=D(I);return x[P]?delete x[D(I)]:t?.(P),!0}),n)for(let A of Object.values(x))n(D(A))}else throw Error("unexpected mode");if(this.exceededFrameSizeLimit(o.length+f.length)){let w=this.storage.fingerprint(m,i);o.extend(this.encodeBound(this._bound(Number.MAX_VALUE))),o.extend(ke(Qe.Fingerprint)),o.extend(w);break}else o.extend(f);c=m,a=d}return o.length===1?null:D(o.unwrap())}splitRange(e,t,n,r){let o=t-e,s=16;if(o<s*2)r.extend(this.encodeBound(n)),r.extend(ke(Qe.IdList)),r.extend(ke(o)),this.storage.iterate(e,t,i=>(r.extend(i.id),!0));else{let i=Math.floor(o/s),a=o%s,c=e;for(let l=0;l<s;l++){let f=i+(l<a?1:0),h=this.storage.fingerprint(c,c+f);c+=f;let d;if(c===t)d=n;else{let p,y;this.storage.iterate(c-1,c+1,(m,w)=>(w===c-1?p=m:y=m,!0)),d=this.getMinimalBound(p,y)}r.extend(this.encodeBound(d)),r.extend(ke(Qe.Fingerprint)),r.extend(h)}}}exceededFrameSizeLimit(e){return e>this.frameSizeLimit-200}decodeTimestampIn(e){let t=en(e);return t=t===0?Number.MAX_VALUE:t-1,this.lastTimestampIn===Number.MAX_VALUE||t===Number.MAX_VALUE?(this.lastTimestampIn=Number.MAX_VALUE,Number.MAX_VALUE):(t+=this.lastTimestampIn,this.lastTimestampIn=t,t)}decodeBound(e){let t=this.decodeTimestampIn(e),n=en(e);if(n>ft)throw Error("bound key too long");let r=nn(e,n);return{timestamp:t,id:r}}encodeTimestampOut(e){if(e===Number.MAX_VALUE)return this.lastTimestampOut=Number.MAX_VALUE,ke(0);let t=e;return e-=this.lastTimestampOut,this.lastTimestampOut=t,ke(e+1)}encodeBound(e){let t=new Le;return t.extend(this.encodeTimestampOut(e.timestamp)),t.extend(ke(e.id.length)),t.extend(e.id),t}getMinimalBound(e,t){if(t.timestamp!==e.timestamp)return this._bound(t.timestamp);{let n=0,r=t.id,o=e.id;for(let s=0;s<ft&&r[s]===o[s];s++)n++;return this._bound(t.timestamp,t.id.subarray(0,n+1))}}};function ai(e,t){for(let n=0;n<e.byteLength;n++){if(e[n]<t[n])return-1;if(e[n]>t[n])return 1}return e.byteLength>t.byteLength?1:e.byteLength<t.byteLength?-1:0}function fr(e,t){return e.timestamp===t.timestamp?ai(e.id,t.id):e.timestamp-t.timestamp}var Wh=class{constructor(e,t,n,r={}){u(this,"relay");u(this,"storage");u(this,"neg");u(this,"filter");u(this,"subscription");u(this,"onhave");u(this,"onneed");this.relay=e,this.storage=t,this.neg=new ii(t),this.onhave=r.onhave,this.onneed=r.onneed,this.filter=n,this.subscription=this.relay.prepareSubscription([{}],{label:r.label||"negentropy"}),this.subscription.oncustom=o=>{switch(o[0]){case"NEG-MSG":{o.length<3&&console.warn(`got invalid NEG-MSG from ${this.relay.url}: ${o}`);try{let s=this.neg.reconcile(o[2],this.onhave,this.onneed);s?this.relay.send(`["NEG-MSG", "${this.subscription.id}", "${s}"]`):(this.close(),r.onclose?.())}catch(s){console.error("negentropy reconcile error:",s),r?.onclose?.(`reconcile error: ${s}`)}break}case"NEG-CLOSE":{let s=o[2];console.warn("negentropy error:",s),r.onclose?.(s);break}case"NEG-ERR":r.onclose?.()}}}async start(){let e=this.neg.initiate();this.relay.send(`["NEG-OPEN","${this.subscription.id}",${JSON.stringify(this.filter)},"${e}"]`)}close(){this.relay.send(`["NEG-CLOSE","${this.subscription.id}"]`),this.subscription.close()}},Vh={};G(Vh,{getToken:()=>Gh,hashPayload:()=>Mr,unpackEventFromToken:()=>li,validateEvent:()=>yi,validateEventKind:()=>fi,validateEventMethodTag:()=>di,validateEventPayloadTag:()=>pi,validateEventTimestamp:()=>ui,validateEventUrlTag:()=>hi,validateToken:()=>Kh});var ci="Nostr ";async function Gh(e,t,n,r=!1,o){let s={kind:vr,tags:[["u",e],["method",t]],created_at:Math.round(new Date().getTime()/1e3),content:""};o&&s.tags.push(["payload",Mr(o)]);let i=await n(s);return(r?ci:"")+le.encode(ye.encode(JSON.stringify(i)))}async function Kh(e,t,n){let r=await li(e).catch(s=>{throw s});return await yi(r,t,n).catch(s=>{throw s})}async function li(e){if(!e)throw new Error("Missing token");e=e.replace(ci,"");let t=Ce.decode(le.decode(e));if(!t||t.length===0||!t.startsWith("{"))throw new Error("Invalid token");return JSON.parse(t)}function ui(e){return e.created_at?Math.abs(Math.round(new Date().getTime()/1e3)-e.created_at)<60:!1}function fi(e){return e.kind===vr}function hi(e,t){let n=e.tags.find(r=>r[0]==="u");return n?n.length>0&&n[1]===t:!1}function di(e,t){let n=e.tags.find(r=>r[0]==="method");return n?n.length>0&&n[1].toLowerCase()===t.toLowerCase():!1}function Mr(e){let t=te(ye.encode(JSON.stringify(e)));return D(t)}function pi(e,t){let n=e.tags.find(o=>o[0]==="payload");if(!n)return!1;let r=Mr(t);return n.length>0&&n[1]===r}async function yi(e,t,n,r){if(!tt(e))throw new Error("Invalid nostr event, signature invalid");if(!fi(e))throw new Error("Invalid nostr event, kind invalid");if(!ui(e))throw new Error("Invalid nostr event, created_at timestamp invalid");if(!hi(e,t))throw new Error("Invalid nostr event, url tag invalid");if(!di(e,n))throw new Error("Invalid nostr event, method tag invalid");if(r&&typeof r=="object"&&Object.keys(r).length>0&&!pi(e,r))throw new Error("Invalid nostr event, payload tag does not match request body hash");return!0}var Fh=new TextDecoder("utf-8"),Vr=new TextEncoder;function dt(e){try{e.indexOf("://")===-1&&(e="wss://"+e);let t=new URL(e);return t.protocol==="http:"?t.protocol="ws:":t.protocol==="https:"&&(t.protocol="wss:"),t.pathname=t.pathname.replace(/\/+/g,"/"),t.pathname.endsWith("/")&&(t.pathname=t.pathname.slice(0,-1)),(t.port==="80"&&t.protocol==="ws:"||t.port==="443"&&t.protocol==="wss:")&&(t.port=""),t.searchParams.sort(),t.hash="",t.toString()}catch{throw new Error(`Invalid URL: ${e}`)}}function jh(e){if(e.length!==64)return!1;for(let t=0;t<64;t++){let n=e.charCodeAt(t);if(isNaN(n)||n<48||n>102||n>57&&n<97)return!1}return!0}var ht=Symbol("verified"),zh=e=>e instanceof Object;function Zh(e){if(!zh(e)||typeof e.kind!="number"||typeof e.content!="string"||typeof e.created_at!="number"||typeof e.pubkey!="string"||!jh(e.pubkey)||!Array.isArray(e.tags))return!1;for(let t=0;t<e.tags.length;t++){let n=e.tags[t];if(!Array.isArray(n))return!1;for(let r=0;r<n.length;r++)if(typeof n[r]!="string")return!1}return!0}var Jh=class{generateSecretKey(){return he.utils.randomSecretKey()}getPublicKey(e){return D(he.getPublicKey(e))}finalizeEvent(e,t){let n=e;return n.pubkey=D(he.getPublicKey(t)),n.id=qr(n),n.sig=D(he.sign($(qr(n)),t)),n[ht]=!0,n}verifyEvent(e){if(typeof e[ht]=="boolean")return e[ht];try{let t=qr(e);if(t!==e.id)return e[ht]=!1,!1;let n=he.verify($(e.sig),$(t),$(e.pubkey));return e[ht]=n,n}catch{return e[ht]=!1,!1}}};function Xh(e){if(!Zh(e))throw new Error("can't serialize event with wrong or missing properties");return JSON.stringify([0,e.pubkey,e.created_at,e.kind,e.tags,e.content])}function qr(e){let t=te(Vr.encode(Xh(e)));return D(t)}var pn=new Jh,qp=pn.generateSecretKey,gi=pn.getPublicKey,Yh=pn.finalizeEvent,mi=pn.verifyEvent,Gr=1,Kr=4294967295,Fr=65536;function Dr(e,t){let n=ze.getSharedSecret(e,$("02"+t)).subarray(1,33);return Yt(te,n,Vr.encode("nip44-v2"))}function wi(e,t){let n=Qt(te,e,t,76);return{chacha_key:n.subarray(0,32),chacha_nonce:n.subarray(32,44),hmac_key:n.subarray(44,76)}}function xi(e){if(!Number.isSafeInteger(e)||e<1)throw new Error("expected positive integer");if(e<=32)return 32;let t=2**(Math.floor(Math.log2(e-1))+1),n=t<=256?32:t/8;return n*(Math.floor((e-1)/n)+1)}function Qh(e){if(!Number.isSafeInteger(e)||e<Gr||e>65535)throw new Error("invalid plaintext size: must be between 1 and 65535 bytes");let t=new Uint8Array(2);return new DataView(t.buffer).setUint16(0,e,!1),t}function ed(e){if(!Number.isSafeInteger(e)||e<Fr||e>Kr)throw new Error("invalid plaintext size: must be between 65536 and 4294967295 bytes");let t=new Uint8Array(4);return new DataView(t.buffer).setUint32(0,e,!1),t}function td(e){let t=Vr.encode(e),n=t.length;if(n<Gr||n>Kr)throw new Error("invalid plaintext size: must be between 1 and 4294967295 bytes");let r=n>=Fr?de(new Uint8Array([0,0]),ed(n)):Qh(n),o=new Uint8Array(xi(n)-n);return de(r,t,o)}function nd(e){let t=new DataView(e.buffer,e.byteOffset,e.byteLength),n=t.getUint16(0),r,o;if(n===0){if(r=t.getUint32(2),r<Fr)throw new Error("invalid padding");o=6}else r=n,o=2;let s=e.subarray(o,o+r);if(r<Gr||r>Kr||s.length!==r||e.length!==o+xi(r))throw new Error("invalid padding");return Fh.decode(s)}function vi(e,t,n){if(n.length!==32)throw new Error("AAD associated data must be 32 bytes");let r=de(n,t);return De(te,e,r)}function rd(e){if(typeof e!="string")throw new Error("payload must be a valid string");let t=e.length;if(t<132)throw new Error("invalid payload length: "+t);if(e[0]==="#")throw new Error("unknown encryption version");let n;try{n=le.decode(e)}catch(s){throw new Error("invalid base64: "+s.message)}let r=n.length;if(r<99)throw new Error("invalid data length: "+r);let o=n[0];if(o!==2)throw new Error("unknown encryption version "+o);return{nonce:n.subarray(1,33),ciphertext:n.subarray(33,-32),mac:n.subarray(-32)}}function od(e,t,n=at(32)){let{chacha_key:r,chacha_nonce:o,hmac_key:s}=wi(t,n),i=td(e),a=Ye(r,o,i),c=vi(s,a,n);return le.encode(de(new Uint8Array([2]),n,a,c))}function bi(e,t){let{nonce:n,ciphertext:r,mac:o}=rd(e),{chacha_key:s,chacha_nonce:i,hmac_key:a}=wi(t,n),c=vi(a,r,n);if(!Je(c,o))throw new Error("invalid MAC");let l=Ye(s,i,r);return nd(l)}var sd=/^(?:([\w.+-]+)@)?([\w_-]+(\.[\w_-]+)+)$/,id;try{id=fetch}catch{}var ad=22242,$r=24133;function cd(e,t){if(e.ids&&e.ids.indexOf(t.id)===-1||e.kinds&&e.kinds.indexOf(t.kind)===-1||e.authors&&e.authors.indexOf(t.pubkey)===-1)return!1;for(let n in e)if(n[0]==="#"){let r=n.slice(1),o=e[`#${r}`];if(o&&!t.tags.find(([s,i])=>s===n.slice(1)&&o.indexOf(i)!==-1))return!1}return!(e.since&&t.created_at<e.since||e.until&&t.created_at>e.until)}function ld(e,t){for(let n=0;n<e.length;n++)if(cd(e[n],t))return!0;return!1}function ud(e,t){let n=t.length+3,r=e.indexOf(`"${t}":`)+n,o=e.slice(r).indexOf('"')+r+1;return e.slice(o,o+64)}function fd(e){let t=e.slice(0,22).indexOf('"EVENT"');if(t===-1)return null;let n=e.slice(t+7+1).indexOf('"');if(n===-1)return null;let r=t+7+1+n,o=e.slice(r+1,80).indexOf('"');if(o===-1)return null;let s=r+1+o;return e.slice(r+1,s)}function hd(e,t){return{kind:ad,created_at:Math.floor(Date.now()/1e3),tags:[["relay",e],["challenge",t]],content:""}}var Wr=class extends Error{constructor(e,t){super(`Tried to send message '${e} on a closed connection to ${t}.`),this.name="SendingOnClosedConnection"}},Ei=class{constructor(e,t){u(this,"url");u(this,"_connected",!1);u(this,"onclose",null);u(this,"onnotice",e=>console.debug(`NOTICE from ${this.url}: ${e}`));u(this,"onauth");u(this,"baseEoseTimeout",4400);u(this,"publishTimeout",4400);u(this,"pingFrequency",29e3);u(this,"pingTimeout",2e4);u(this,"resubscribeBackoff",[1e4,1e4,1e4,2e4,2e4,3e4,6e4]);u(this,"openSubs",new Map);u(this,"enablePing");u(this,"enableReconnect");u(this,"idleTimeout",0);u(this,"idleSince",Date.now());u(this,"ongoingOperations",0);u(this,"reconnectTimeoutHandle");u(this,"pingIntervalHandle");u(this,"reconnectAttempts",0);u(this,"skipReconnection",!1);u(this,"idleTimeoutHandle");u(this,"connectionPromise");u(this,"openCountRequests",new Map);u(this,"openEventPublishes",new Map);u(this,"ws");u(this,"challenge");u(this,"authPromise");u(this,"serial",0);u(this,"verifyEvent");u(this,"_WebSocket");this.url=dt(e),this.verifyEvent=t.verifyEvent,this._WebSocket=t.websocketImplementation||WebSocket,this.enablePing=t.enablePing,this.enableReconnect=t.enableReconnect||!1,t.idleTimeout&&(this.idleTimeout=t.idleTimeout)}static async connect(e,t){let n=new Ei(e,t);return await n.connect(t),n}closeAllSubscriptions(e){for(let[t,n]of this.openSubs)n.close(e);this.openSubs.clear();for(let[t,n]of this.openEventPublishes)n.reject(new Error(e));this.openEventPublishes.clear();for(let[t,n]of this.openCountRequests)n.reject(new Error(e));this.openCountRequests.clear()}get connected(){return this._connected}clearIdleTimeout(){this.idleTimeoutHandle&&(clearTimeout(this.idleTimeoutHandle),this.idleTimeoutHandle=void 0)}scheduleIdleClose(){this.clearIdleTimeout(),this.idleTimeout>0&&(this.idleTimeoutHandle=setTimeout(()=>{this.ongoingOperations===0&&this.idleSince&&this.close()},this.idleTimeout))}async reconnect(){let e=this.resubscribeBackoff[Math.min(this.reconnectAttempts,this.resubscribeBackoff.length-1)];this.reconnectAttempts++,this.reconnectTimeoutHandle=setTimeout(async()=>{try{await this.connect()}catch{}},e)}handleHardClose(e){this.ws&&(this.ws.onopen=null,this.ws.onerror=null,this.ws.onclose=null),this.pingIntervalHandle&&(clearInterval(this.pingIntervalHandle),this.pingIntervalHandle=void 0),this._connected=!1,this.connectionPromise=void 0,this.idleSince=void 0,this.clearIdleTimeout(),this.enableReconnect&&!this.skipReconnection?this.reconnect():(this.onclose?.(),this.closeAllSubscriptions(e))}async connect(e){let t;return this.connectionPromise?this.connectionPromise:(this.challenge=void 0,this.authPromise=void 0,this.skipReconnection=!1,this.connectionPromise=new Promise((n,r)=>{e?.timeout&&(t=setTimeout(()=>{r("connection timed out"),this.connectionPromise=void 0,this.reconnectAttempts===0&&(this.skipReconnection=!0),this.handleHardClose("relay connection timed out")},e.timeout)),e?.abort&&(e.abort.onabort=r);try{this.ws=new this._WebSocket(this.url)}catch(o){clearTimeout(t),r(o);return}this.ws.onopen=()=>{this.reconnectTimeoutHandle&&(clearTimeout(this.reconnectTimeoutHandle),this.reconnectTimeoutHandle=void 0),clearTimeout(t),this._connected=!0;let o=this.reconnectAttempts>0;this.reconnectAttempts=0;for(let s of this.openSubs.values()){if(s.eosed=!1,o)for(let i=0;i<s.filters.length;i++)s.lastEmitted&&(s.filters[i].since=s.lastEmitted+1);s.fire()}this.enablePing&&(this.pingIntervalHandle=setInterval(()=>this.pingpong(),this.pingFrequency)),n()},this.ws.onerror=()=>{clearTimeout(t),r("connection failed"),this.connectionPromise=void 0,this.reconnectAttempts===0&&(this.skipReconnection=!0),this.handleHardClose("relay connection failed")},this.ws.onclose=o=>{clearTimeout(t),r(o.message||"websocket closed"),this.handleHardClose("relay connection closed")},this.ws.onmessage=this._onmessage.bind(this)}),this.connectionPromise)}waitForPingPong(){return new Promise(e=>{this.ws.once("pong",()=>e(!0)),this.ws.ping()})}waitForDummyReq(){return new Promise((e,t)=>{if(!this.connectionPromise)return t(new Error(`no connection to ${this.url}, can't ping`));try{let n=this.subscribe([{ids:["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],limit:0}],{label:"<forced-ping>",oneose:()=>{e(!0),n.close()},onclose(){e(!0)},eoseTimeout:this.pingTimeout+1e3})}catch(n){t(n)}})}async pingpong(){this.ws?.readyState===1&&(await Promise.any([this.ws&&this.ws.ping&&this.ws.once?this.waitForPingPong():this.waitForDummyReq(),new Promise(t=>setTimeout(()=>t(!1),this.pingTimeout))])||this.ws?.readyState===this._WebSocket.OPEN&&this.ws?.close())}async send(e){if(!this.connectionPromise)throw new Wr(e,this.url);this.connectionPromise.then(()=>{this.ws?.send(e)})}async auth(e){let t=this.challenge;if(!t)throw new Error("can't perform auth, no challenge was received");return this.authPromise?this.authPromise:(this.authPromise=new Promise(async(n,r)=>{try{let o=await e(hd(this.url,t)),s=setTimeout(()=>{let i=this.openEventPublishes.get(o.id);i&&(i.reject(new Error("auth timed out")),this.openEventPublishes.delete(o.id))},this.publishTimeout);this.openEventPublishes.set(o.id,{resolve:n,reject:r,timeout:s}),this.send('["AUTH",'+JSON.stringify(o)+"]")}catch(o){console.warn("subscribe auth function failed:",o)}}),this.authPromise)}async publish(e){this.idleSince=void 0,this.clearIdleTimeout(),this.ongoingOperations++;let t=new Promise((n,r)=>{let o=setTimeout(()=>{let s=this.openEventPublishes.get(e.id);s&&(s.reject(new Error("publish timed out")),this.openEventPublishes.delete(e.id))},this.publishTimeout);this.openEventPublishes.set(e.id,{resolve:n,reject:r,timeout:o})});try{await this.send('["EVENT",'+JSON.stringify(e)+"]")}catch(n){let r=this.openEventPublishes.get(e.id);r&&(r.reject(n),this.openEventPublishes.delete(e.id))}return this.ongoingOperations--,this.ongoingOperations===0&&(this.idleSince=Date.now(),this.scheduleIdleClose()),t}async count(e,t){return(await this.countWithHLL(e,t)).count}async countWithHLL(e,t){this.serial++;let n=t?.id||"count:"+this.serial,r=new Promise((o,s)=>{this.openCountRequests.set(n,{resolve:o,reject:s})});try{await this.send('["COUNT","'+n+'",'+JSON.stringify(e).substring(1))}catch(o){let s=this.openCountRequests.get(n);s&&(s.reject(o),this.openCountRequests.delete(n))}return r}subscribe(e,t){t.label!=="<forced-ping>"&&(this.idleSince=void 0,this.clearIdleTimeout(),this.ongoingOperations++);let n=this.prepareSubscription(e,t);return n.fire(),t.abort&&(t.abort.onabort=()=>n.close(String(t.abort.reason||"<aborted>"))),n}prepareSubscription(e,t){this.serial++;let n=t.id||(t.label?t.label+":":"sub:")+this.serial,r=new dd(this,n,e,t);return this.openSubs.set(n,r),r}close(){this.skipReconnection=!0,this.reconnectTimeoutHandle&&(clearTimeout(this.reconnectTimeoutHandle),this.reconnectTimeoutHandle=void 0),this.pingIntervalHandle&&(clearInterval(this.pingIntervalHandle),this.pingIntervalHandle=void 0),this.closeAllSubscriptions("relay connection closed by us"),this._connected=!1,this.connectionPromise=void 0,this.idleSince=void 0,this.clearIdleTimeout(),this.onclose?.(),this.ws&&(this.ws.onopen=null,this.ws.onerror=null,this.ws.onclose=null,this.ws.readyState!==this._WebSocket.CLOSING&&this.ws.readyState!==this._WebSocket.CLOSED&&this.ws.close())}_onmessage(e){let t=e.data;if(!t)return;let n=fd(t);if(n){let r=this.openSubs.get(n);if(!r)return;let o=ud(t,"id"),s=r.alreadyHaveEvent?.(o);if(r.receivedEvent?.(this,o),s)return}try{let r=JSON.parse(t);switch(r[0]){case"EVENT":{let o=this.openSubs.get(r[1]),s=r[2];ld(o.filters,s)&&this.verifyEvent(s,this.url)?o.onevent(s):o.oninvalidevent?.(s),(!o.lastEmitted||o.lastEmitted<s.created_at)&&(o.lastEmitted=s.created_at);return}case"COUNT":{let o=r[1],s=r[2],i=this.openCountRequests.get(o);i&&(i.resolve(s),this.openCountRequests.delete(o));return}case"EOSE":{let o=this.openSubs.get(r[1]);if(!o)return;o.receivedEose();return}case"OK":{let o=r[1],s=r[2],i=r[3],a=this.openEventPublishes.get(o);a&&(clearTimeout(a.timeout),s?a.resolve(i):a.reject(new Error(i)),this.openEventPublishes.delete(o));return}case"CLOSED":{let o=r[1],s=this.openSubs.get(o);if(!s){let i=this.openCountRequests.get(o);i&&(i.reject(new Error(r[2])),this.openCountRequests.delete(o));return}s.closed=!0,s.close(r[2]);return}case"NOTICE":{this.onnotice(r[1]);return}case"AUTH":{this.challenge=r[1],this.onauth&&this.auth(this.onauth).catch(o=>{if(!(o instanceof Wr))throw o});return}default:{this.openSubs.get(r[1])?.oncustom?.(r);return}}}catch(r){try{let[o,s,i]=JSON.parse(t);console.warn(`[nostr] relay ${this.url} error processing message:`,r,i)}catch{console.warn(`[nostr] relay ${this.url} error processing message:`,r)}return}}},dd=class{constructor(e,t,n,r){u(this,"relay");u(this,"id");u(this,"lastEmitted");u(this,"closed",!1);u(this,"eosed",!1);u(this,"filters");u(this,"alreadyHaveEvent");u(this,"receivedEvent");u(this,"onevent");u(this,"oninvalidevent");u(this,"oneose");u(this,"onclose");u(this,"oncustom");u(this,"eoseTimeout");u(this,"eoseTimeoutHandle");if(n.length===0)throw new Error("subscription can't be created with zero filters");this.relay=e,this.filters=n,this.id=t,this.alreadyHaveEvent=r.alreadyHaveEvent,this.receivedEvent=r.receivedEvent,this.eoseTimeout=r.eoseTimeout||e.baseEoseTimeout,this.oneose=r.oneose,this.onclose=r.onclose,this.oninvalidevent=r.oninvalidevent,this.onevent=r.onevent||(o=>{console.warn(`onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,o)})}fire(){this.relay.send('["REQ","'+this.id+'",'+JSON.stringify(this.filters).substring(1)),this.eoseTimeoutHandle=setTimeout(this.receivedEose.bind(this),this.eoseTimeout)}receivedEose(){this.eosed||(clearTimeout(this.eoseTimeoutHandle),this.eosed=!0,this.oneose?.())}close(e="closed by caller"){if(!this.closed&&this.relay.connected){try{this.relay.send('["CLOSE",'+JSON.stringify(this.id)+"]")}catch(t){if(!(t instanceof Wr))throw t}this.closed=!0}this.relay.openSubs.delete(this.id),this.id.startsWith("<forced-ping>")||(this.relay.ongoingOperations--,this.relay.ongoingOperations===0&&(this.relay.idleSince=Date.now(),this.relay.scheduleIdleClose())),this.onclose?.(e)}},He=256,pd=He*2,zp=new TextEncoder;function yd(e,t){switch(t){case"reactions":return{"#e":[e],kinds:[7]};case"reposts":return{"#e":[e],kinds:[6]};case"quotes":return{"#q":[e],kinds:[1,1111]};case"replies":return{"#e":[e],kinds:[1]};case"comments":return{"#E":[e],kinds:[1111]};case"followers":return{"#p":[e],kinds:[3]}}}function gd(){return new Uint8Array(He)}function bd(e){if(e.length!==pd||!/^[0-9a-f]+$/.test(e))return;let t=new Uint8Array(He);for(let n=0;n<He;n++)t[n]=parseInt(e.slice(n*2,n*2+2),16);return t}function md(e){if(e.length!==He)throw new Error(`invalid number of registers ${e.length}`);let t="";for(let n=0;n<He;n++)t+=e[n].toString(16).padStart(2,"0");return t}function wd(e,t){if(e.length===0&&(e=gd()),e.length!==He)throw new Error(`invalid number of registers ${e.length}`);if(t.length!==He)throw new Error(`invalid number of registers ${t.length}`);for(let n=0;n<He;n++)t[n]>e[n]&&(e[n]=t[n]);return e}var xd=class{constructor(e){u(this,"relays",new Map);u(this,"seenOn",new Map);u(this,"trackRelays",!1);u(this,"verifyEvent");u(this,"enablePing");u(this,"enableReconnect");u(this,"idleTimeout",2e4);u(this,"automaticallyAuth");u(this,"onRelayConnectionFailure");u(this,"onRelayConnectionSuccess");u(this,"allowConnectingToRelay");u(this,"maxWaitForConnection");u(this,"_WebSocket");this.verifyEvent=e.verifyEvent,this._WebSocket=e.websocketImplementation,this.enablePing=e.enablePing,this.enableReconnect=e.enableReconnect||!1,e.idleTimeout&&(this.idleTimeout=e.idleTimeout),this.automaticallyAuth=e.automaticallyAuth,this.onRelayConnectionFailure=e.onRelayConnectionFailure,this.onRelayConnectionSuccess=e.onRelayConnectionSuccess,this.allowConnectingToRelay=e.allowConnectingToRelay,this.maxWaitForConnection=e.maxWaitForConnection||3e3}async ensureRelay(e,t){e=dt(e);let n=this.relays.get(e);if(n||(n=new Ei(e,{verifyEvent:this.verifyEvent,websocketImplementation:this._WebSocket,enablePing:this.enablePing,enableReconnect:this.enableReconnect,idleTimeout:this.idleTimeout}),n.onclose=()=>{this.relays.delete(e)},this.relays.set(e,n)),this.automaticallyAuth){let r=this.automaticallyAuth(e);r&&(n.onauth=r)}try{await n.connect({timeout:t?.connectionTimeout,abort:t?.abort})}catch(r){throw this.relays.delete(e),r}return n}close(e){e.map(dt).forEach(t=>{this.relays.get(t)?.close(),this.relays.delete(t)})}subscribe(e,t,n){let r=[],o=[];for(let s=0;s<e.length;s++){let i=dt(e[s]);r.find(a=>a.url===i)||o.indexOf(i)===-1&&(o.push(i),r.push({url:i,filter:t}))}return this.subscribeMap(r,n)}subscribeMany(e,t,n){return this.subscribe(e,t,n)}subscribeMap(e,t){let n=new Map;for(let d of e){let{url:p,filter:y}=d;n.has(p)||n.set(p,[]),n.get(p).push(y)}let r=Array.from(n.entries()).map(([d,p])=>({url:d,filters:p}));this.trackRelays&&(t.receivedEvent=(d,p)=>{let y=this.seenOn.get(p);y||(y=new Set,this.seenOn.set(p,y)),y.add(d)});let o=new Set,s=[],i=[],a=d=>{i[d]||(i[d]=!0,i.filter(p=>p).length===r.length&&(t.oneose?.(),a=()=>{}))},c=[],l=(d,p,y)=>{c[d]||(a(d),c[d]={url:p,reason:y},c.filter(m=>m).length===r.length&&(t.onclose?.(c),l=()=>{}))},f=d=>{if(t.alreadyHaveEvent?.(d))return!0;let p=o.has(d);return o.add(d),p},h=Promise.all(r.map(async({url:d,filters:p},y)=>{if(this.allowConnectingToRelay?.(d,["read",p])===!1){l(y,d,"connection skipped by allowConnectingToRelay");return}let m;try{m=await this.ensureRelay(d,{connectionTimeout:this.maxWaitForConnection<(t.maxWait||0)?Math.max(t.maxWait*.8,t.maxWait-1e3):this.maxWaitForConnection,abort:t.abort})}catch(x){this.onRelayConnectionFailure?.(d),l(y,d,x?.message||String(x));return}this.onRelayConnectionSuccess?.(d);let w=m.subscribe(p,{...t,oneose:()=>a(y),onclose:x=>{x.startsWith("auth-required: ")&&t.onauth?m.auth(t.onauth).then(()=>{m.subscribe(p,{...t,oneose:()=>a(y),onclose:A=>{l(y,d,A)},alreadyHaveEvent:f,eoseTimeout:t.maxWait,abort:t.abort})}).catch(A=>{l(y,d,`auth was required and attempted, but failed with: ${A}`)}):l(y,d,x)},alreadyHaveEvent:f,eoseTimeout:t.maxWait,abort:t.abort});s.push(w)}));return{async close(d){await h,s.forEach(p=>{p.close(d)})}}}subscribeEose(e,t,n){let r;return r=this.subscribe(e,t,{...n,oneose(){let o="closed automatically on eose";r?r.close(o):n.onclose?.(e.map(s=>({url:s,reason:o})))}}),r}subscribeManyEose(e,t,n){return this.subscribeEose(e,t,n)}async querySync(e,t,n){return new Promise(async r=>{let o=[];this.subscribeEose(e,t,{...n,onevent(s){o.push(s)},onclose(s){r(o)}})})}async get(e,t,n){t.limit=1;let r=await this.querySync(e,t,n);return r.sort((o,s)=>s.created_at-o.created_at),r[0]||null}async countMany(e,t,n,r){let o=yd(t,n),s=[];for(let l=0;l<e.length;l++){let f=dt(e[l]);s.indexOf(f)===-1&&s.push(f)}let i=await Promise.all(s.map(async l=>{if(this.allowConnectingToRelay?.(l,["read",[o]])===!1)return null;let f;try{f=await this.ensureRelay(l,{connectionTimeout:this.maxWaitForConnection<(r?.maxWait||0)?Math.max(r.maxWait*.8,r.maxWait-1e3):this.maxWaitForConnection,abort:r?.abort})}catch{return this.onRelayConnectionFailure?.(l),null}return this.onRelayConnectionSuccess?.(l),f.countWithHLL([o],{id:r?.id}).catch(()=>null)})),a=0,c;for(let l of i){if(!l||(l.count>a&&(a=l.count),!l.hll||l.hll.length!==512))continue;let f=bd(l.hll);f&&(c=wd(c||new Uint8Array(0),f))}return c?{count:a,hll:md(c)}:{count:a}}publish(e,t,n){return e.map(dt).map(async(r,o,s)=>{if(s.indexOf(r)!==o)return Promise.reject("duplicate url");if(this.allowConnectingToRelay?.(r,["write",t])===!1)return Promise.reject("connection skipped by allowConnectingToRelay");let i;try{i=await this.ensureRelay(r,{connectionTimeout:this.maxWaitForConnection<(n?.maxWait||0)?Math.max(n.maxWait*.8,n.maxWait-1e3):this.maxWaitForConnection,abort:n?.abort})}catch(a){return this.onRelayConnectionFailure?.(r),Promise.reject("connection failure: "+String(a))}return i.publish(t).catch(async a=>{if(a instanceof Error&&a.message.startsWith("auth-required: ")&&n?.onauth)return await i.auth(n.onauth),i.publish(t);throw a}).then(a=>{if(this.trackRelays){let c=this.seenOn.get(t.id);c||(c=new Set,this.seenOn.set(t.id,c)),c.add(i)}return a})})}listConnectionStatus(){let e=new Map;return this.relays.forEach((t,n)=>e.set(n,t.connected)),e}destroy(){this.relays.forEach(e=>e.close()),this.relays=new Map}pruneIdleRelays(e=1e4){let t=[];for(let[n,r]of this.relays)r.idleSince&&Date.now()-r.idleSince>=e&&(this.relays.delete(n),t.push(n),r.close());return t}},Si;try{Si=WebSocket}catch{}var vd=class extends xd{constructor(e){super({verifyEvent:mi,websocketImplementation:Si,maxWaitForConnection:3e3,...e})}},Ai;try{Ai=fetch}catch{}var Ed=/^bunker:\/\/([0-9a-f]{64})\??([?\/\w:.=&%-]*)$/;async function Ti(e){let t=e.match(Ed);if(t)try{let n=t[1],r=new URLSearchParams(t[2]);return{pubkey:n,relays:r.getAll("relay"),secret:r.get("secret")}}catch{}return Sd(e)}async function Sd(e){let t=e.match(sd);if(!t)return null;let[n,r="_",o]=t;try{let s=`https://${o}/.well-known/nostr.json?name=${r}`,i=await(await Ai(s,{redirect:"error"})).json(),a=i.names[r],c=i.nip46[a]||[];return{pubkey:a,relays:c,secret:null}}catch{return null}}function Bi(e){let t=new URLSearchParams;return e.relays.forEach(n=>{t.append("relay",n)}),t.append("secret",e.secret),e.perms&&e.perms.length>0&&t.append("perms",e.perms.join(",")),e.name&&t.append("name",e.name),e.url&&t.append("url",e.url),e.image&&t.append("image",e.image),`nostrconnect://${e.clientPubkey}?${t.toString()}`}var dn=class{constructor(e,t){u(this,"params");u(this,"pool");u(this,"subCloser");u(this,"isOpen");u(this,"serial");u(this,"idPrefix");u(this,"listeners");u(this,"waitingForAuth");u(this,"secretKey");u(this,"conversationKey");u(this,"bp");u(this,"cachedPubKey");this.params=t,this.pool=t.pool||new vd,this.secretKey=e,this.isOpen=!1,this.idPrefix=Math.random().toString(36).substring(7),this.serial=0,this.listeners={},this.waitingForAuth={}}static fromBunker(e,t,n={}){if(t.relays.length===0)throw new Error("no relays specified for this bunker");let r=new dn(e,n);return r.conversationKey=Dr(e,t.pubkey),r.bp=t,r.setupSubscription(),r}static async fromURI(e,t,n={},r=3e5){let o=new URL(t),s=o.searchParams.get("secret");if(!s)throw new Error("nostrconnect:// URI has no secret");let i=new dn(e,n),a=gi(e);return new Promise((c,l)=>{let f=!1,h=i.pool.subscribe(o.searchParams.getAll("relay"),{kinds:[$r],"#p":[a],limit:0},{onevent:async d=>{try{let p=Dr(e,d.pubkey),y=bi(d.content,p);JSON.parse(y).result===s&&(h.close(),i.bp={pubkey:d.pubkey,relays:o.searchParams.getAll("relay"),secret:s},i.conversationKey=Dr(e,d.pubkey),i.setupSubscription(),f=!0,n.skipSwitchRelays||await Promise.race([new Promise(w=>setTimeout(w,1e3)),i.switchRelays()]),c(i))}catch(p){console.warn("failed to process potential connection event",p)}},onclose:()=>{f||l(new Error("subscription closed before connection was established."))},maxWait:typeof r=="number"?r:void 0,abort:typeof r!="number"?r:void 0})})}setupSubscription(){let e=this.listeners,t=this.waitingForAuth,n=this.conversationKey;this.subCloser=this.pool.subscribe(this.bp.relays,{kinds:[$r],authors:[this.bp.pubkey],"#p":[gi(this.secretKey)],limit:0},{onevent:async r=>{let o=JSON.parse(bi(r.content,n)),{id:s,result:i,error:a}=o;if(i==="auth_url"&&t[s]){delete t[s],this.params.onauth?this.params.onauth(a):console.warn(`nostr-tools/nip46: remote signer ${this.bp.pubkey} tried to send an "auth_url"='${a}' but there was no onauth() callback configured.`);return}let c=e[s];c&&(a?c.reject(a):i&&c.resolve(i),delete e[s])},onclose:()=>{this.subCloser=void 0}}),this.isOpen=!0}async switchRelays(){try{let e=await this.sendRequest("switch_relays",[]),t=JSON.parse(e);if(!t||JSON.stringify(t.sort())===JSON.stringify(this.bp.relays))return!1;this.bp.relays=t;let n=this.subCloser;return setTimeout(()=>{n.close()},5e3),this.subCloser=void 0,this.setupSubscription(),!0}catch{return!1}}async close(){this.isOpen=!1,this.subCloser?.close(),this.subCloser=void 0}async sendRequest(e,t){return new Promise(async(n,r)=>{try{if(!this.isOpen)throw new Error("this signer is not open anymore, create a new one");this.subCloser||this.setupSubscription(),this.serial++;let o=`${this.idPrefix}-${this.serial}`,s=od(JSON.stringify({id:o,method:e,params:t}),this.conversationKey),i=Yh({kind:$r,tags:[["p",this.bp.pubkey]],content:s,created_at:Math.floor(Date.now()/1e3)},this.secretKey);this.listeners[o]={resolve:n,reject:r},this.waitingForAuth[o]=!0,await Promise.any(this.pool.publish(this.bp.relays,i))}catch(o){r(o)}})}async ping(){let e=await this.sendRequest("ping",[]);if(e!=="pong")throw new Error(`result is not pong: ${e}`)}async connect(e){let t=[this.bp.pubkey,this.bp.secret||""];e&&(t.push(""),t.push(JSON.stringify(e))),await this.sendRequest("connect",t)}async logout(){let e=await this.sendRequest("logout",[]);if(e!=="ack")throw new Error(`result is not ack: ${e}`);await this.close()}async getPublicKey(){return this.cachedPubKey||(this.cachedPubKey=await this.sendRequest("get_public_key",[])),this.cachedPubKey}async signEvent(e){let t=await this.sendRequest("sign_event",[JSON.stringify(e)]),n=JSON.parse(t);if(mi(n))return n;throw new Error(`event returned from bunker is improperly signed: ${JSON.stringify(n)}`)}async nip04Encrypt(e,t){return await this.sendRequest("nip04_encrypt",[e,t])}async nip04Decrypt(e,t){return await this.sendRequest("nip04_decrypt",[e,t])}async nip44Encrypt(e,t){return await this.sendRequest("nip44_encrypt",[e,t])}async nip44Decrypt(e,t){return await this.sendRequest("nip44_decrypt",[e,t])}};globalThis.NostrSigner={generateSecretKey:yr,getPublicKey:Tt,finalizeEvent:ge,verifyEvent:tt,SimplePool:Ns,BunkerSigner:dn,parseBunkerInput:Ti,createNostrConnectURI:Bi,npubEncode:Er.npubEncode,bytesToHex:e=>Array.from(e,t=>t.toString(16).padStart(2,"0")).join(""),hexToBytes:e=>Uint8Array.from(e.match(/.{1,2}/g)??[],t=>parseInt(t,16))};})(); | | 1538 | +(()=>{var _i=Object.defineProperty;var ki=(e,t,n)=>t in e?_i(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var u=(e,t,n)=>ki(e,typeof t!="symbol"?t+"":t,n);function yt(e){return e instanceof Uint8Array||ArrayBuffer.isView(e)&&e.constructor.name==="Uint8Array"}function xe(e,t=""){if(!Number.isSafeInteger(e)||e<0){let n=t&&`"${t}" `;throw new Error(`${n}expected integer >= 0, got ${e}`)}}function N(e,t,n=""){let r=yt(e),o=e?.length,s=t!==void 0;if(!r||s&&o!==t){let i=n&&`"${n}" `,a=s?` of length ${t}`:"",c=r?`length=${o}`:`type=${typeof e}`;throw new Error(i+"expected Uint8Array"+a+", got "+c)}return e}function Rt(e){if(typeof e!="function"||typeof e.create!="function")throw new Error("Hash must wrapped by utils.createHasher");xe(e.outputLen),xe(e.blockLen)}function nt(e,t=!0){if(e.destroyed)throw new Error("Hash instance has been destroyed");if(t&&e.finished)throw new Error("Hash#digest() has already been called")}function Xr(e,t){N(e,void 0,"digestInto() output");let n=t.outputLen;if(e.length<n)throw new Error('"digestInto() output" expected to be of length >='+n)}function $e(...e){for(let t=0;t<e.length;t++)e[t].fill(0)}function _t(e){return new DataView(e.buffer,e.byteOffset,e.byteLength)}function be(e,t){return e<<32-t|e>>>t}var Yr=typeof Uint8Array.from([]).toHex=="function"&&typeof Uint8Array.fromHex=="function",Li=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,"0"));function We(e){if(N(e),Yr)return e.toHex();let t="";for(let n=0;n<e.length;n++)t+=Li[e[n]];return t}var Se={_0:48,_9:57,A:65,F:70,a:97,f:102};function Jr(e){if(e>=Se._0&&e<=Se._9)return e-Se._0;if(e>=Se.A&&e<=Se.F)return e-(Se.A-10);if(e>=Se.a&&e<=Se.f)return e-(Se.a-10)}function Ve(e){if(typeof e!="string")throw new Error("hex string expected, got "+typeof e);if(Yr)return Uint8Array.fromHex(e);let t=e.length,n=t/2;if(t%2)throw new Error("hex string expected, got unpadded hex of length "+t);let r=new Uint8Array(n);for(let o=0,s=0;o<n;o++,s+=2){let i=Jr(e.charCodeAt(s)),a=Jr(e.charCodeAt(s+1));if(i===void 0||a===void 0){let c=e[s]+e[s+1];throw new Error('hex string expected, got non-hex character "'+c+'" at index '+s)}r[o]=i*16+a}return r}function ce(...e){let t=0;for(let r=0;r<e.length;r++){let o=e[r];N(o),t+=o.length}let n=new Uint8Array(t);for(let r=0,o=0;r<e.length;r++){let s=e[r];n.set(s,o),o+=s.length}return n}function Qr(e,t={}){let n=(o,s)=>e(s).update(o).digest(),r=e(void 0);return n.outputLen=r.outputLen,n.blockLen=r.blockLen,n.create=o=>e(o),Object.assign(n,t),Object.freeze(n)}function Ge(e=32){let t=typeof globalThis=="object"?globalThis.crypto:null;if(typeof t?.getRandomValues!="function")throw new Error("crypto.getRandomValues must be defined");return t.getRandomValues(new Uint8Array(e))}var eo=e=>({oid:Uint8Array.from([6,9,96,134,72,1,101,3,4,2,e])});function to(e,t,n){return e&t^~e&n}function no(e,t,n){return e&t^e&n^t&n}var kt=class{constructor(t,n,r,o){u(this,"blockLen");u(this,"outputLen");u(this,"padOffset");u(this,"isLE");u(this,"buffer");u(this,"view");u(this,"finished",!1);u(this,"length",0);u(this,"pos",0);u(this,"destroyed",!1);this.blockLen=t,this.outputLen=n,this.padOffset=r,this.isLE=o,this.buffer=new Uint8Array(t),this.view=_t(this.buffer)}update(t){nt(this),N(t);let{view:n,buffer:r,blockLen:o}=this,s=t.length;for(let i=0;i<s;){let a=Math.min(o-this.pos,s-i);if(a===o){let c=_t(t);for(;o<=s-i;i+=o)this.process(c,i);continue}r.set(t.subarray(i,i+a),this.pos),this.pos+=a,i+=a,this.pos===o&&(this.process(n,0),this.pos=0)}return this.length+=t.length,this.roundClean(),this}digestInto(t){nt(this),Xr(t,this),this.finished=!0;let{buffer:n,view:r,blockLen:o,isLE:s}=this,{pos:i}=this;n[i++]=128,$e(this.buffer.subarray(i)),this.padOffset>o-i&&(this.process(r,0),i=0);for(let h=i;h<o;h++)n[h]=0;r.setBigUint64(o-8,BigInt(this.length*8),s),this.process(r,0);let a=_t(t),c=this.outputLen;if(c%4)throw new Error("_sha2: outputLen must be aligned to 32bit");let l=c/4,f=this.get();if(l>f.length)throw new Error("_sha2: outputLen bigger than state");for(let h=0;h<l;h++)a.setUint32(4*h,f[h],s)}digest(){let{buffer:t,outputLen:n}=this;this.digestInto(t);let r=t.slice(0,n);return this.destroy(),r}_cloneInto(t){t||(t=new this.constructor),t.set(...this.get());let{blockLen:n,buffer:r,length:o,finished:s,destroyed:i,pos:a}=this;return t.destroyed=i,t.finished=s,t.length=o,t.pos=a,o%n&&t.buffer.set(r),t}clone(){return this._cloneInto()}},Ae=Uint32Array.from([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]);var Ci=Uint32Array.from([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),Oe=new Uint32Array(64),gn=class extends kt{constructor(t){super(64,t,8,!1)}get(){let{A:t,B:n,C:r,D:o,E:s,F:i,G:a,H:c}=this;return[t,n,r,o,s,i,a,c]}set(t,n,r,o,s,i,a,c){this.A=t|0,this.B=n|0,this.C=r|0,this.D=o|0,this.E=s|0,this.F=i|0,this.G=a|0,this.H=c|0}process(t,n){for(let h=0;h<16;h++,n+=4)Oe[h]=t.getUint32(n,!1);for(let h=16;h<64;h++){let d=Oe[h-15],p=Oe[h-2],y=be(d,7)^be(d,18)^d>>>3,m=be(p,17)^be(p,19)^p>>>10;Oe[h]=m+Oe[h-7]+y+Oe[h-16]|0}let{A:r,B:o,C:s,D:i,E:a,F:c,G:l,H:f}=this;for(let h=0;h<64;h++){let d=be(a,6)^be(a,11)^be(a,25),p=f+d+to(a,c,l)+Ci[h]+Oe[h]|0,m=(be(r,2)^be(r,13)^be(r,22))+no(r,o,s)|0;f=l,l=c,c=a,a=i+p|0,i=s,s=o,o=r,r=p+m|0}r=r+this.A|0,o=o+this.B|0,s=s+this.C|0,i=i+this.D|0,a=a+this.E|0,c=c+this.F|0,l=l+this.G|0,f=f+this.H|0,this.set(r,o,s,i,a,c,l,f)}roundClean(){$e(Oe)}destroy(){this.set(0,0,0,0,0,0,0,0),$e(this.buffer)}},bn=class extends gn{constructor(){super(32);u(this,"A",Ae[0]|0);u(this,"B",Ae[1]|0);u(this,"C",Ae[2]|0);u(this,"D",Ae[3]|0);u(this,"E",Ae[4]|0);u(this,"F",Ae[5]|0);u(this,"G",Ae[6]|0);u(this,"H",Ae[7]|0)}};var Lt=Qr(()=>new bn,eo(1));var wn=BigInt(0),mn=BigInt(1);function gt(e,t=""){if(typeof e!="boolean"){let n=t&&`"${t}" `;throw new Error(n+"expected boolean, got type="+typeof e)}return e}function ro(e){if(typeof e=="bigint"){if(!Ct(e))throw new Error("positive bigint expected, got "+e)}else xe(e);return e}function bt(e){let t=ro(e).toString(16);return t.length&1?"0"+t:t}function oo(e){if(typeof e!="string")throw new Error("hex string expected, got "+typeof e);return e===""?wn:BigInt("0x"+e)}function Ue(e){return oo(We(e))}function xn(e){return oo(We(Ii(N(e)).reverse()))}function It(e,t){xe(t),e=ro(e);let n=Ve(e.toString(16).padStart(t*2,"0"));if(n.length!==t)throw new Error("number too large");return n}function vn(e,t){return It(e,t).reverse()}function Ii(e){return Uint8Array.from(e)}function so(e){return Uint8Array.from(e,(t,n)=>{let r=t.charCodeAt(0);if(t.length!==1||r>127)throw new Error(`string contains non-ASCII character "${e[n]}" with code ${r} at position ${n}`);return r})}var Ct=e=>typeof e=="bigint"&&wn<=e;function Hi(e,t,n){return Ct(e)&&Ct(t)&&Ct(n)&&t<=e&&e<n}function io(e,t,n,r){if(!Hi(t,n,r))throw new Error("expected valid "+e+": "+n+" <= n < "+r+", got "+t)}function En(e){let t;for(t=0;e>wn;e>>=mn,t+=1);return t}var mt=e=>(mn<<BigInt(e))-mn;function ao(e,t,n){if(xe(e,"hashLen"),xe(t,"qByteLen"),typeof n!="function")throw new Error("hmacFn must be a function");let r=w=>new Uint8Array(w),o=Uint8Array.of(),s=Uint8Array.of(0),i=Uint8Array.of(1),a=1e3,c=r(e),l=r(e),f=0,h=()=>{c.fill(1),l.fill(0),f=0},d=(...w)=>n(l,ce(c,...w)),p=(w=o)=>{l=d(s,w),c=d(),w.length!==0&&(l=d(i,w),c=d())},y=()=>{if(f++>=a)throw new Error("drbg: tried max amount of iterations");let w=0,x=[];for(;w<t;){c=d();let A=c.slice();x.push(A),w+=c.length}return ce(...x)};return(w,x)=>{h(),p(w);let A;for(;!(A=x(y()));)p();return h(),A}}function wt(e,t={},n={}){if(!e||typeof e!="object")throw new Error("expected valid options object");function r(s,i,a){let c=e[s];if(a&&c===void 0)return;let l=typeof c;if(l!==i||c===null)throw new Error(`param "${s}" is invalid: expected ${i}, got ${l}`)}let o=(s,i)=>Object.entries(s).forEach(([a,c])=>r(a,c,i));o(t,!1),o(n,!0)}function Sn(e){let t=new WeakMap;return(n,...r)=>{let o=t.get(n);if(o!==void 0)return o;let s=e(n,...r);return t.set(n,s),s}}var ae=BigInt(0),se=BigInt(1),Ke=BigInt(2),uo=BigInt(3),fo=BigInt(4),ho=BigInt(5),Oi=BigInt(7),po=BigInt(8),Ui=BigInt(9),yo=BigInt(16);function me(e,t){let n=e%t;return n>=ae?n:t+n}function fe(e,t,n){let r=e;for(;t-- >ae;)r*=r,r%=n;return r}function co(e,t){if(e===ae)throw new Error("invert: expected non-zero number");if(t<=ae)throw new Error("invert: expected positive modulus, got "+t);let n=me(e,t),r=t,o=ae,s=se,i=se,a=ae;for(;n!==ae;){let l=r/n,f=r%n,h=o-i*l,d=s-a*l;r=n,n=f,o=i,s=a,i=h,a=d}if(r!==se)throw new Error("invert: does not exist");return me(o,t)}function Tn(e,t,n){if(!e.eql(e.sqr(t),n))throw new Error("Cannot find square root")}function go(e,t){let n=(e.ORDER+se)/fo,r=e.pow(t,n);return Tn(e,r,t),r}function Pi(e,t){let n=(e.ORDER-ho)/po,r=e.mul(t,Ke),o=e.pow(r,n),s=e.mul(t,o),i=e.mul(e.mul(s,Ke),o),a=e.mul(s,e.sub(i,e.ONE));return Tn(e,a,t),a}function Ni(e){let t=rt(e),n=bo(e),r=n(t,t.neg(t.ONE)),o=n(t,r),s=n(t,t.neg(r)),i=(e+Oi)/yo;return(a,c)=>{let l=a.pow(c,i),f=a.mul(l,r),h=a.mul(l,o),d=a.mul(l,s),p=a.eql(a.sqr(f),c),y=a.eql(a.sqr(h),c);l=a.cmov(l,f,p),f=a.cmov(d,h,y);let m=a.eql(a.sqr(f),c),w=a.cmov(l,f,m);return Tn(a,w,c),w}}function bo(e){if(e<uo)throw new Error("sqrt is not defined for small field");let t=e-se,n=0;for(;t%Ke===ae;)t/=Ke,n++;let r=Ke,o=rt(e);for(;lo(o,r)===1;)if(r++>1e3)throw new Error("Cannot find square root: probably non-prime P");if(n===1)return go;let s=o.pow(r,t),i=(t+se)/Ke;return function(c,l){if(c.is0(l))return l;if(lo(c,l)!==1)throw new Error("Cannot find square root");let f=n,h=c.mul(c.ONE,s),d=c.pow(l,t),p=c.pow(l,i);for(;!c.eql(d,c.ONE);){if(c.is0(d))return c.ZERO;let y=1,m=c.sqr(d);for(;!c.eql(m,c.ONE);)if(y++,m=c.sqr(m),y===f)throw new Error("Cannot find square root");let w=se<<BigInt(f-y-1),x=c.pow(h,w);f=y,h=c.sqr(x),d=c.mul(d,h),p=c.mul(p,x)}return p}}function Mi(e){return e%fo===uo?go:e%po===ho?Pi:e%yo===Ui?Ni(e):bo(e)}var qi=["create","isValid","is0","neg","inv","sqrt","sqr","eql","add","sub","mul","pow","div","addN","subN","mulN","sqrN"];function Bn(e){let t={ORDER:"bigint",BYTES:"number",BITS:"number"},n=qi.reduce((r,o)=>(r[o]="function",r),t);return wt(e,n),e}function Di(e,t,n){if(n<ae)throw new Error("invalid exponent, negatives unsupported");if(n===ae)return e.ONE;if(n===se)return t;let r=e.ONE,o=t;for(;n>ae;)n&se&&(r=e.mul(r,o)),o=e.sqr(o),n>>=se;return r}function Ht(e,t,n=!1){let r=new Array(t.length).fill(n?e.ZERO:void 0),o=t.reduce((i,a,c)=>e.is0(a)?i:(r[c]=i,e.mul(i,a)),e.ONE),s=e.inv(o);return t.reduceRight((i,a,c)=>e.is0(a)?i:(r[c]=e.mul(i,r[c]),e.mul(i,a)),s),r}function lo(e,t){let n=(e.ORDER-se)/Ke,r=e.pow(t,n),o=e.eql(r,e.ONE),s=e.eql(r,e.ZERO),i=e.eql(r,e.neg(e.ONE));if(!o&&!s&&!i)throw new Error("invalid Legendre symbol result");return o?1:s?0:-1}function $i(e,t){t!==void 0&&xe(t);let n=t!==void 0?t:e.toString(2).length,r=Math.ceil(n/8);return{nBitLength:n,nByteLength:r}}var An=class{constructor(t,n={}){u(this,"ORDER");u(this,"BITS");u(this,"BYTES");u(this,"isLE");u(this,"ZERO",ae);u(this,"ONE",se);u(this,"_lengths");u(this,"_sqrt");u(this,"_mod");if(t<=ae)throw new Error("invalid field: expected ORDER > 0, got "+t);let r;this.isLE=!1,n!=null&&typeof n=="object"&&(typeof n.BITS=="number"&&(r=n.BITS),typeof n.sqrt=="function"&&(this.sqrt=n.sqrt),typeof n.isLE=="boolean"&&(this.isLE=n.isLE),n.allowedLengths&&(this._lengths=n.allowedLengths?.slice()),typeof n.modFromBytes=="boolean"&&(this._mod=n.modFromBytes));let{nBitLength:o,nByteLength:s}=$i(t,r);if(s>2048)throw new Error("invalid field: expected ORDER of <= 2048 bytes");this.ORDER=t,this.BITS=o,this.BYTES=s,this._sqrt=void 0,Object.preventExtensions(this)}create(t){return me(t,this.ORDER)}isValid(t){if(typeof t!="bigint")throw new Error("invalid field element: expected bigint, got "+typeof t);return ae<=t&&t<this.ORDER}is0(t){return t===ae}isValidNot0(t){return!this.is0(t)&&this.isValid(t)}isOdd(t){return(t&se)===se}neg(t){return me(-t,this.ORDER)}eql(t,n){return t===n}sqr(t){return me(t*t,this.ORDER)}add(t,n){return me(t+n,this.ORDER)}sub(t,n){return me(t-n,this.ORDER)}mul(t,n){return me(t*n,this.ORDER)}pow(t,n){return Di(this,t,n)}div(t,n){return me(t*co(n,this.ORDER),this.ORDER)}sqrN(t){return t*t}addN(t,n){return t+n}subN(t,n){return t-n}mulN(t,n){return t*n}inv(t){return co(t,this.ORDER)}sqrt(t){return this._sqrt||(this._sqrt=Mi(this.ORDER)),this._sqrt(this,t)}toBytes(t){return this.isLE?vn(t,this.BYTES):It(t,this.BYTES)}fromBytes(t,n=!1){N(t);let{_lengths:r,BYTES:o,isLE:s,ORDER:i,_mod:a}=this;if(r){if(!r.includes(t.length)||t.length>o)throw new Error("Field.fromBytes: expected "+r+" bytes, got "+t.length);let l=new Uint8Array(o);l.set(t,s?0:l.length-t.length),t=l}if(t.length!==o)throw new Error("Field.fromBytes: expected "+o+" bytes, got "+t.length);let c=s?xn(t):Ue(t);if(a&&(c=me(c,i)),!n&&!this.isValid(c))throw new Error("invalid field element: outside of range 0..ORDER");return c}invertBatch(t){return Ht(this,t)}cmov(t,n,r){return r?n:t}};function rt(e,t={}){return new An(e,t)}function mo(e){if(typeof e!="bigint")throw new Error("field order must be bigint");let t=e.toString(2).length;return Math.ceil(t/8)}function Rn(e){let t=mo(e);return t+Math.ceil(t/2)}function Ot(e,t,n=!1){N(e);let r=e.length,o=mo(t),s=Rn(t);if(r<16||r<s||r>1024)throw new Error("expected "+s+"-1024 bytes of input, got "+r);let i=n?xn(e):Ue(e),a=me(i,t-se)+se;return n?vn(a,o):It(a,o)}var ot=BigInt(0),Fe=BigInt(1);function xt(e,t){let n=t.negate();return e?n:t}function Cn(e,t){let n=Ht(e.Fp,t.map(r=>r.Z));return t.map((r,o)=>e.fromAffine(r.toAffine(n[o])))}function Eo(e,t){if(!Number.isSafeInteger(e)||e<=0||e>t)throw new Error("invalid window size, expected [1.."+t+"], got W="+e)}function _n(e,t){Eo(e,t);let n=Math.ceil(t/e)+1,r=2**(e-1),o=2**e,s=mt(e),i=BigInt(e);return{windows:n,windowSize:r,mask:s,maxNumber:o,shiftBy:i}}function wo(e,t,n){let{windowSize:r,mask:o,maxNumber:s,shiftBy:i}=n,a=Number(e&o),c=e>>i;a>r&&(a-=s,c+=Fe);let l=t*r,f=l+Math.abs(a)-1,h=a===0,d=a<0,p=t%2!==0;return{nextN:c,offset:f,isZero:h,isNeg:d,isNegF:p,offsetF:l}}var kn=new WeakMap,So=new WeakMap;function Ln(e){return So.get(e)||1}function xo(e){if(e!==ot)throw new Error("invalid wNAF")}var Ut=class{constructor(t,n){u(this,"BASE");u(this,"ZERO");u(this,"Fn");u(this,"bits");this.BASE=t.BASE,this.ZERO=t.ZERO,this.Fn=t.Fn,this.bits=n}_unsafeLadder(t,n,r=this.ZERO){let o=t;for(;n>ot;)n&Fe&&(r=r.add(o)),o=o.double(),n>>=Fe;return r}precomputeWindow(t,n){let{windows:r,windowSize:o}=_n(n,this.bits),s=[],i=t,a=i;for(let c=0;c<r;c++){a=i,s.push(a);for(let l=1;l<o;l++)a=a.add(i),s.push(a);i=a.double()}return s}wNAF(t,n,r){if(!this.Fn.isValid(r))throw new Error("invalid scalar");let o=this.ZERO,s=this.BASE,i=_n(t,this.bits);for(let a=0;a<i.windows;a++){let{nextN:c,offset:l,isZero:f,isNeg:h,isNegF:d,offsetF:p}=wo(r,a,i);r=c,f?s=s.add(xt(d,n[p])):o=o.add(xt(h,n[l]))}return xo(r),{p:o,f:s}}wNAFUnsafe(t,n,r,o=this.ZERO){let s=_n(t,this.bits);for(let i=0;i<s.windows&&r!==ot;i++){let{nextN:a,offset:c,isZero:l,isNeg:f}=wo(r,i,s);if(r=a,!l){let h=n[c];o=o.add(f?h.negate():h)}}return xo(r),o}getPrecomputes(t,n,r){let o=kn.get(n);return o||(o=this.precomputeWindow(n,t),t!==1&&(typeof r=="function"&&(o=r(o)),kn.set(n,o))),o}cached(t,n,r){let o=Ln(t);return this.wNAF(o,this.getPrecomputes(o,t,r),n)}unsafe(t,n,r,o){let s=Ln(t);return s===1?this._unsafeLadder(t,n,o):this.wNAFUnsafe(s,this.getPrecomputes(s,t,r),n,o)}createCache(t,n){Eo(n,this.bits),So.set(t,n),kn.delete(t)}hasCache(t){return Ln(t)!==1}};function Ao(e,t,n,r){let o=t,s=e.ZERO,i=e.ZERO;for(;n>ot||r>ot;)n&Fe&&(s=s.add(o)),r&Fe&&(i=i.add(o)),o=o.double(),n>>=Fe,r>>=Fe;return{p1:s,p2:i}}function vo(e,t,n){if(t){if(t.ORDER!==e)throw new Error("Field.ORDER must match order: Fp == p, Fn == n");return Bn(t),t}else return rt(e,{isLE:n})}function To(e,t,n={},r){if(r===void 0&&(r=e==="edwards"),!t||typeof t!="object")throw new Error(`expected valid ${e} CURVE object`);for(let c of["p","n","h"]){let l=t[c];if(!(typeof l=="bigint"&&l>ot))throw new Error(`CURVE.${c} must be positive bigint`)}let o=vo(t.p,n.Fp,r),s=vo(t.n,n.Fn,r),a=["Gx","Gy","a",e==="weierstrass"?"b":"d"];for(let c of a)if(!o.isValid(t[c]))throw new Error(`CURVE.${c} must be valid field element of CURVE.Fp`);return t=Object.freeze(Object.assign({},t)),{CURVE:t,Fp:o,Fn:s}}function Pt(e,t){return function(r){let o=e(r);return{secretKey:o,publicKey:t(o)}}}var Nt=class{constructor(t,n){u(this,"oHash");u(this,"iHash");u(this,"blockLen");u(this,"outputLen");u(this,"finished",!1);u(this,"destroyed",!1);if(Rt(t),N(n,void 0,"key"),this.iHash=t.create(),typeof this.iHash.update!="function")throw new Error("Expected instance of class which extends utils.Hash");this.blockLen=this.iHash.blockLen,this.outputLen=this.iHash.outputLen;let r=this.blockLen,o=new Uint8Array(r);o.set(n.length>r?t.create().update(n).digest():n);for(let s=0;s<o.length;s++)o[s]^=54;this.iHash.update(o),this.oHash=t.create();for(let s=0;s<o.length;s++)o[s]^=106;this.oHash.update(o),$e(o)}update(t){return nt(this),this.iHash.update(t),this}digestInto(t){nt(this),N(t,this.outputLen,"output"),this.finished=!0,this.iHash.digestInto(t),this.oHash.update(t),this.oHash.digestInto(t),this.destroy()}digest(){let t=new Uint8Array(this.oHash.outputLen);return this.digestInto(t),t}_cloneInto(t){t||(t=Object.create(Object.getPrototypeOf(this),{}));let{oHash:n,iHash:r,finished:o,destroyed:s,blockLen:i,outputLen:a}=this;return t=t,t.finished=o,t.destroyed=s,t.blockLen=i,t.outputLen=a,t.oHash=n._cloneInto(t.oHash),t.iHash=r._cloneInto(t.iHash),t}clone(){return this._cloneInto()}destroy(){this.destroyed=!0,this.oHash.destroy(),this.iHash.destroy()}},In=(e,t,n)=>new Nt(e,t).update(n).digest();In.create=(e,t)=>new Nt(e,t);var Bo=(e,t)=>(e+(e>=0?t:-t)/Ro)/t;function Wi(e,t,n){let[[r,o],[s,i]]=t,a=Bo(i*e,n),c=Bo(-o*e,n),l=e-a*r-c*s,f=-a*o-c*i,h=l<Te,d=f<Te;h&&(l=-l),d&&(f=-f);let p=mt(Math.ceil(En(n)/2))+st;if(l<Te||l>=p||f<Te||f>=p)throw new Error("splitScalar (endomorphism): failed, k="+e);return{k1neg:h,k1:l,k2neg:d,k2:f}}function On(e){if(!["compact","recovered","der"].includes(e))throw new Error('Signature format must be "compact", "recovered", or "der"');return e}function Hn(e,t){let n={};for(let r of Object.keys(t))n[r]=e[r]===void 0?t[r]:e[r];return gt(n.lowS,"lowS"),gt(n.prehash,"prehash"),n.format!==void 0&&On(n.format),n}var Un=class extends Error{constructor(t=""){super(t)}},Pe={Err:Un,_tlv:{encode:(e,t)=>{let{Err:n}=Pe;if(e<0||e>256)throw new n("tlv.encode: wrong tag");if(t.length&1)throw new n("tlv.encode: unpadded data");let r=t.length/2,o=bt(r);if(o.length/2&128)throw new n("tlv.encode: long form length too big");let s=r>127?bt(o.length/2|128):"";return bt(e)+s+o+t},decode(e,t){let{Err:n}=Pe,r=0;if(e<0||e>256)throw new n("tlv.encode: wrong tag");if(t.length<2||t[r++]!==e)throw new n("tlv.decode: wrong tlv");let o=t[r++],s=!!(o&128),i=0;if(!s)i=o;else{let c=o&127;if(!c)throw new n("tlv.decode(long): indefinite length not supported");if(c>4)throw new n("tlv.decode(long): byte length is too big");let l=t.subarray(r,r+c);if(l.length!==c)throw new n("tlv.decode: length bytes not complete");if(l[0]===0)throw new n("tlv.decode(long): zero leftmost byte");for(let f of l)i=i<<8|f;if(r+=c,i<128)throw new n("tlv.decode(long): not minimal encoding")}let a=t.subarray(r,r+i);if(a.length!==i)throw new n("tlv.decode: wrong value length");return{v:a,l:t.subarray(r+i)}}},_int:{encode(e){let{Err:t}=Pe;if(e<Te)throw new t("integer: negative integers are not allowed");let n=bt(e);if(Number.parseInt(n[0],16)&8&&(n="00"+n),n.length&1)throw new t("unexpected DER parsing assertion: unpadded hex");return n},decode(e){let{Err:t}=Pe;if(e[0]&128)throw new t("invalid signature integer: negative");if(e[0]===0&&!(e[1]&128))throw new t("invalid signature integer: unnecessary leading zero");return Ue(e)}},toSig(e){let{Err:t,_int:n,_tlv:r}=Pe,o=N(e,void 0,"signature"),{v:s,l:i}=r.decode(48,o);if(i.length)throw new t("invalid signature: left bytes after parsing");let{v:a,l:c}=r.decode(2,s),{v:l,l:f}=r.decode(2,c);if(f.length)throw new t("invalid signature: left bytes after parsing");return{r:n.decode(a),s:n.decode(l)}},hexFromSig(e){let{_tlv:t,_int:n}=Pe,r=t.encode(2,n.encode(e.r)),o=t.encode(2,n.encode(e.s)),s=r+o;return t.encode(48,s)}},Te=BigInt(0),st=BigInt(1),Ro=BigInt(2),Mt=BigInt(3),Vi=BigInt(4);function _o(e,t={}){let n=To("weierstrass",e,t),{Fp:r,Fn:o}=n,s=n.CURVE,{h:i,n:a}=s;wt(t,{},{allowInfinityPoint:"boolean",clearCofactor:"function",isTorsionFree:"function",fromBytes:"function",toBytes:"function",endo:"object"});let{endo:c}=t;if(c&&(!r.is0(s.a)||typeof c.beta!="bigint"||!Array.isArray(c.basises)))throw new Error('invalid endo: expected "beta": bigint and "basises": array');let l=Lo(r,o);function f(){if(!r.isOdd)throw new Error("compression is not supported: Field does not have .isOdd()")}function h(L,b,g){let{x:E,y:S}=b.toAffine(),B=r.toBytes(E);if(gt(g,"isCompressed"),g){f();let v=!r.isOdd(S);return ce(ko(v),B)}else return ce(Uint8Array.of(4),B,r.toBytes(S))}function d(L){N(L,void 0,"Point");let{publicKey:b,publicKeyUncompressed:g}=l,E=L.length,S=L[0],B=L.subarray(1);if(E===b&&(S===2||S===3)){let v=r.fromBytes(B);if(!r.isValid(v))throw new Error("bad point: is not on curve, wrong x");let R=m(v),T;try{T=r.sqrt(R)}catch(K){let q=K instanceof Error?": "+K.message:"";throw new Error("bad point: is not on curve, sqrt error"+q)}f();let _=r.isOdd(T);return(S&1)===1!==_&&(T=r.neg(T)),{x:v,y:T}}else if(E===g&&S===4){let v=r.BYTES,R=r.fromBytes(B.subarray(0,v)),T=r.fromBytes(B.subarray(v,v*2));if(!w(R,T))throw new Error("bad point: is not on curve");return{x:R,y:T}}else throw new Error(`bad point: got length ${E}, expected compressed=${b} or uncompressed=${g}`)}let p=t.toBytes||h,y=t.fromBytes||d;function m(L){let b=r.sqr(L),g=r.mul(b,L);return r.add(r.add(g,r.mul(L,s.a)),s.b)}function w(L,b){let g=r.sqr(b),E=m(L);return r.eql(g,E)}if(!w(s.Gx,s.Gy))throw new Error("bad curve params: generator point");let x=r.mul(r.pow(s.a,Mt),Vi),A=r.mul(r.sqr(s.b),BigInt(27));if(r.is0(r.add(x,A)))throw new Error("bad curve params: a or b");function I(L,b,g=!1){if(!r.isValid(b)||g&&r.is0(b))throw new Error(`bad point coordinate ${L}`);return b}function P(L){if(!(L instanceof U))throw new Error("Weierstrass Point expected")}function Q(L){if(!c||!c.basises)throw new Error("no endo");return Wi(L,c.basises,o.ORDER)}let V=Sn((L,b)=>{let{X:g,Y:E,Z:S}=L;if(r.eql(S,r.ONE))return{x:g,y:E};let B=L.is0();b==null&&(b=B?r.ONE:r.inv(S));let v=r.mul(g,b),R=r.mul(E,b),T=r.mul(S,b);if(B)return{x:r.ZERO,y:r.ZERO};if(!r.eql(T,r.ONE))throw new Error("invZ was invalid");return{x:v,y:R}}),Z=Sn(L=>{if(L.is0()){if(t.allowInfinityPoint&&!r.is0(L.Y))return;throw new Error("bad point: ZERO")}let{x:b,y:g}=L.toAffine();if(!r.isValid(b)||!r.isValid(g))throw new Error("bad point: x or y not field elements");if(!w(b,g))throw new Error("bad point: equation left != right");if(!L.isTorsionFree())throw new Error("bad point: not in prime-order subgroup");return!0});function j(L,b,g,E,S){return g=new U(r.mul(g.X,L),g.Y,g.Z),b=xt(E,b),g=xt(S,g),b.add(g)}let C=class C{constructor(b,g,E){u(this,"X");u(this,"Y");u(this,"Z");this.X=I("x",b),this.Y=I("y",g,!0),this.Z=I("z",E),Object.freeze(this)}static CURVE(){return s}static fromAffine(b){let{x:g,y:E}=b||{};if(!b||!r.isValid(g)||!r.isValid(E))throw new Error("invalid affine point");if(b instanceof C)throw new Error("projective point not allowed");return r.is0(g)&&r.is0(E)?C.ZERO:new C(g,E,r.ONE)}static fromBytes(b){let g=C.fromAffine(y(N(b,void 0,"point")));return g.assertValidity(),g}static fromHex(b){return C.fromBytes(Ve(b))}get x(){return this.toAffine().x}get y(){return this.toAffine().y}precompute(b=8,g=!0){return M.createCache(this,b),g||this.multiply(Mt),this}assertValidity(){Z(this)}hasEvenY(){let{y:b}=this.toAffine();if(!r.isOdd)throw new Error("Field doesn't support isOdd");return!r.isOdd(b)}equals(b){P(b);let{X:g,Y:E,Z:S}=this,{X:B,Y:v,Z:R}=b,T=r.eql(r.mul(g,R),r.mul(B,S)),_=r.eql(r.mul(E,R),r.mul(v,S));return T&&_}negate(){return new C(this.X,r.neg(this.Y),this.Z)}double(){let{a:b,b:g}=s,E=r.mul(g,Mt),{X:S,Y:B,Z:v}=this,R=r.ZERO,T=r.ZERO,_=r.ZERO,H=r.mul(S,S),K=r.mul(B,B),q=r.mul(v,v),O=r.mul(S,B);return O=r.add(O,O),_=r.mul(S,v),_=r.add(_,_),R=r.mul(b,_),T=r.mul(E,q),T=r.add(R,T),R=r.sub(K,T),T=r.add(K,T),T=r.mul(R,T),R=r.mul(O,R),_=r.mul(E,_),q=r.mul(b,q),O=r.sub(H,q),O=r.mul(b,O),O=r.add(O,_),_=r.add(H,H),H=r.add(_,H),H=r.add(H,q),H=r.mul(H,O),T=r.add(T,H),q=r.mul(B,v),q=r.add(q,q),H=r.mul(q,O),R=r.sub(R,H),_=r.mul(q,K),_=r.add(_,_),_=r.add(_,_),new C(R,T,_)}add(b){P(b);let{X:g,Y:E,Z:S}=this,{X:B,Y:v,Z:R}=b,T=r.ZERO,_=r.ZERO,H=r.ZERO,K=s.a,q=r.mul(s.b,Mt),O=r.mul(g,B),z=r.mul(E,v),J=r.mul(S,R),Y=r.add(g,E),X=r.add(B,v);Y=r.mul(Y,X),X=r.add(O,z),Y=r.sub(Y,X),X=r.add(g,S);let ie=r.add(B,R);return X=r.mul(X,ie),ie=r.add(O,J),X=r.sub(X,ie),ie=r.add(E,S),T=r.add(v,R),ie=r.mul(ie,T),T=r.add(z,J),ie=r.sub(ie,T),H=r.mul(K,X),T=r.mul(q,J),H=r.add(T,H),T=r.sub(z,H),H=r.add(z,H),_=r.mul(T,H),z=r.add(O,O),z=r.add(z,O),J=r.mul(K,J),X=r.mul(q,X),z=r.add(z,J),J=r.sub(O,J),J=r.mul(K,J),X=r.add(X,J),O=r.mul(z,X),_=r.add(_,O),O=r.mul(ie,X),T=r.mul(Y,T),T=r.sub(T,O),O=r.mul(Y,z),H=r.mul(ie,H),H=r.add(H,O),new C(T,_,H)}subtract(b){return this.add(b.negate())}is0(){return this.equals(C.ZERO)}multiply(b){let{endo:g}=t;if(!o.isValidNot0(b))throw new Error("invalid scalar: out of range");let E,S,B=v=>M.cached(this,v,R=>Cn(C,R));if(g){let{k1neg:v,k1:R,k2neg:T,k2:_}=Q(b),{p:H,f:K}=B(R),{p:q,f:O}=B(_);S=K.add(O),E=j(g.beta,H,q,v,T)}else{let{p:v,f:R}=B(b);E=v,S=R}return Cn(C,[E,S])[0]}multiplyUnsafe(b){let{endo:g}=t,E=this;if(!o.isValid(b))throw new Error("invalid scalar: out of range");if(b===Te||E.is0())return C.ZERO;if(b===st)return E;if(M.hasCache(this))return this.multiply(b);if(g){let{k1neg:S,k1:B,k2neg:v,k2:R}=Q(b),{p1:T,p2:_}=Ao(C,E,B,R);return j(g.beta,T,_,S,v)}else return M.unsafe(E,b)}toAffine(b){return V(this,b)}isTorsionFree(){let{isTorsionFree:b}=t;return i===st?!0:b?b(C,this):M.unsafe(this,a).is0()}clearCofactor(){let{clearCofactor:b}=t;return i===st?this:b?b(C,this):this.multiplyUnsafe(i)}isSmallOrder(){return this.multiplyUnsafe(i).is0()}toBytes(b=! | | Diff preview truncated (256 KiB or 10,000 lines). |
|
About| owner | c01503e8035f |
|---|
| visibility | public |
|---|
| head | 8b51870933f8 |
|---|
Maintainers- c01503e8035f owner
- ba1b5beed5b5 agent hermes
Clonehttps://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git
|