One row per list that names relays: relay list (10002), DM inbox (10050), search relays (10007), Blossom servers (10063). A row fetches the owner's newest copy from this relay over the NIP-98 bridge and from four indexers over websockets, verifies it when the signer library is loaded, puts this relay first, signs once, and publishes to this relay, the relays the list names and the indexers, showing what each answered. Remove me does the reverse. The signer bundle now exports verifyEvent for the check.
- commit
- f8196a90d13fad2ef4a07180ee5cb1479e4cd44a
- parent
- 682a3ac61421e807bcdc7393cbe02fdc4b735a8b
- author
- Dami <dami.etoile@gmail.com>
| M | scripts/ | 4 | ++-- |
| M | src/ | 6 | +++--- |
| M | src/ | 4 | ++-- |
@@ -2,9 +2,9 @@
22 // build-signer.mjs into src/gen/signer.ts and served at /signer.js.33 // Loaded only when someone picks a remote signer, since NIP-44 and44 // secp256k1 are not in WebCrypto.5-import { generateSecretKey, getPublicKey, finalizeEvent } from "nostr-tools/pure";5+import { generateSecretKey, getPublicKey, finalizeEvent, verifyEvent } from "nostr-tools/pure";66 import { BunkerSigner, parseBunkerInput, createNostrConnectURI } from "nostr-tools/nip46";77 import { npubEncode } from "nostr-tools/nip19";88 import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";99 10-window.NostrSigner = { generateSecretKey, getPublicKey, finalizeEvent, BunkerSigner, parseBunkerInput, createNostrConnectURI, npubEncode, bytesToHex, hexToBytes };10+window.NostrSigner = { generateSecretKey, getPublicKey, finalizeEvent, verifyEvent, BunkerSigner, parseBunkerInput, createNostrConnectURI, npubEncode, bytesToHex, hexToBytes };@@ -16,8 +16,8 @@ export function dashboard(): string {
1616 return page("relay", BODY + '<script>window.SIGNER_URL = "/signer.js?v=' + SIGNER_HASH + '";</script><script>' + JS + "</script>", CSS);1717 }1818 19-const CSS = "main { max-width: 64rem; }\n.mast { display: grid; grid-template-columns: 1fr auto; gap: 1.5rem 2rem; align-items: end; margin-bottom: 2rem; }\n.mast .wordmark { grid-column: 1 / -1; justify-self: center; margin-bottom: .5rem; }\n.mast .right { display: grid; gap: .7rem; justify-items: end; text-align: right; }\n.urlrow, .owner { display: inline-flex; align-items: center; gap: .4rem; color: var(--ink-2); }\n.owner b { color: var(--ink); } .owner em { font-style: normal; color: var(--forest); }\n.urlrow .ib, .owner .ib { width: 26px; height: 26px; margin-left: .2rem; box-shadow: 1px 1px 0 var(--ink); } .urlrow .ib svg, .owner .ib svg { width: 13px; height: 13px; }\n.care { display: flex; gap: .9rem; flex-wrap: wrap; }\n.care .g { display: grid; justify-items: center; gap: .3rem; width: 5.4rem; }\n.care .g i { display: grid; place-items: center; width: 44px; height: 44px; border: 2px solid var(--ink); border-radius: 10px; background: var(--paper); box-shadow: 2px 2px 0 var(--ink); position: relative; }\n.care .g i svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }\n.care .g.off i::after { content: \"\"; position: absolute; width: 3px; height: 40px; background: var(--ink); transform: rotate(45deg); border-radius: 2px; box-shadow: 0 0 0 2px var(--paper); }\n.care .g small { font: 500 10px/1.2 var(--mono); text-transform: uppercase; letter-spacing: .04em; color: var(--ink-2); text-align: center; }\n#unclaimed { background: var(--butter); }\n.doors { display: grid; grid-template-columns: repeat(4, 1fr); gap: .8rem; } @media (max-width: 52rem) { .doors { grid-template-columns: 1fr 1fr; } } @media (max-width: 30rem) { .doors { grid-template-columns: 1fr; } }\n.door { display: grid; grid-template-rows: auto 1fr; align-content: start; gap: .35rem; padding: .8rem .9rem; border: 2px solid var(--ink); border-radius: 12px; background: var(--paper); box-shadow: 3px 3px 0 var(--ink); min-width: 0; }\n.door small { font: 700 11px var(--sans); text-transform: uppercase; letter-spacing: .06em; color: var(--ink-2); }\n.door .v { display: grid; grid-template-columns: 1fr auto; align-items: start; gap: .5rem; font: 500 13px/1.45 var(--mono); } .door .v span { min-width: 0; overflow-wrap: anywhere; }\n.door .ib { width: 24px; height: 24px; box-shadow: 1px 1px 0 var(--ink); flex: 0 0 auto; } .door .ib svg { width: 12px; height: 12px; }\n#peoplesec { background: var(--peach); }\n.dir { display: flex; flex-wrap: wrap; gap: .5rem; }\n.who { display: inline-flex; align-items: center; gap: .1rem; padding: .3rem .7rem .3rem .4rem; border: 2px solid var(--ink); border-radius: 999px; background: var(--paper); box-shadow: 2px 2px 0 var(--ink); font-size: 14px; }\n.who .role { font: 700 10px var(--mono); text-transform: uppercase; letter-spacing: .05em; color: var(--forest); margin-left: .4rem; }\n.who.me { background: var(--sun); }\n#fuelsec { background: var(--mint); }\n.gauges { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }\n.gauge small { display: block; font-weight: 600; font-size: 13px; }\n.gauge .bar { height: 18px; border: 2px solid var(--ink); border-radius: 999px; background: var(--paper); margin: .5rem 0 .35rem; overflow: hidden; padding: 2px; }\n.gauge .bar i { display: block; height: 100%; width: 0; border-radius: 999px; background: repeating-linear-gradient(-45deg, var(--forest) 0 8px, var(--forest-2) 8px 16px); transition: width .4s; }\n.gauge .bar i.over { background: repeating-linear-gradient(-45deg, var(--red) 0 8px, #d4614c 8px 16px); }\n.gauge span { font-size: 13px; color: var(--ink-3); }\n.balance { margin: 1.2rem 0 0; color: var(--ink-2); } .balance b { color: var(--ink); font-weight: 700; }\n.sats { display: inline-flex; align-items: center; width: auto; } .sats input { font: 15px var(--sans); width: 6rem; text-align: right; border: 0; outline: none; padding: 0; background: transparent; color: var(--ink); } .sats span { color: var(--ink-3); padding-left: .4rem; }\n.topup { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-top: .8rem; }\n.invoice { margin-top: 1rem; border: 2px dashed var(--ink); border-radius: 12px; padding: .9rem 1rem; background: var(--paper); }\n.invoice p { margin: 0 0 .6rem; color: var(--ink-2); font-size: 14px; } .invoice .state { font-size: 13px; color: var(--forest); }\n.invoice textarea { margin-top: .7rem; min-height: 3.4rem; font-family: var(--mono); font-size: 11.5px; color: var(--ink-3); }\ndetails.disclosure summary { cursor: pointer; font: 400 1.6rem/1 var(--display); list-style: none; display: flex; align-items: center; gap: .5rem; }\ndetails.disclosure summary::-webkit-details-marker { display: none; }\ndetails.disclosure summary::before { content: \"+\"; font: 700 1.4rem var(--mono); width: 1.4rem; } details.disclosure[open] summary::before { content: \"\u2013\"; }\n.about { display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: .6rem 1.2rem; margin-top: 1rem; font-size: 14px; }\n.about small { display: block; color: var(--ink-3); font-size: 12px; } .about div > div { overflow-wrap: anywhere; }\n.console { margin-top: 3rem; }\n.tabs { display: flex; gap: .45rem; flex-wrap: wrap; align-items: flex-end; margin: 0 0 0 1rem; }\n.tabs a { font: 600 14px var(--sans); padding: .55rem 1rem; border: 2px solid var(--ink); border-bottom: 0; border-radius: 12px 12px 0 0; background: var(--head); color: var(--ink-2); text-decoration: none; margin-bottom: -2px; position: relative; z-index: 1; }\n.tabs a.on { background: var(--paper); color: var(--ink); z-index: 3; padding-top: .7rem; }\n.count { display: inline-grid; place-items: center; min-width: 1.5em; height: 1.5em; padding: 0 .4em; margin-left: .45rem; border: 1.5px solid var(--ink); border-radius: 999px; background: var(--sun); color: var(--ink); font: 700 11px/1 var(--mono); vertical-align: middle; box-shadow: 1px 1px 0 var(--ink); } .count:empty { display: none; } h2 .count { font-size: 12px; vertical-align: .45em; }\n.panel { display: none; margin: 0; border-radius: 0 18px 18px 18px; position: relative; z-index: 2; }\n.panel.on { display: block; }\n.panel h2 { font-size: 2rem; }\n.block { margin-top: 1.8rem; }\n.two { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }\n.actions { display: flex; justify-content: flex-end; margin-top: 1.1rem; } .actions.left { justify-content: flex-start; }\n.addrow { display: flex; gap: .5rem; margin-bottom: .9rem; } .addrow input { flex: 1; min-width: 0; } .addrow .ib { width: 38px; height: 38px; flex: 0 0 auto; border-radius: 10px; }\n.choices { display: grid; gap: .5rem; }\n.choices label { display: grid; grid-template-columns: auto 1fr; gap: 0 .7rem; cursor: pointer; padding: .7rem .8rem; border: 2px solid var(--line-2); border-radius: 12px; background: var(--paper); }\n.choices label:has(:checked) { border-color: var(--ink); box-shadow: 3px 3px 0 var(--ink); }\n.choices input { grid-row: span 2; margin: .2rem 0 0; accent-color: var(--forest); }\n.choices b { font-weight: 600; } .choices small { color: var(--ink-2); font-size: 13px; }\n.limits { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem 1.5rem; margin-top: 1.3rem; }\n.limits label { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: .6rem; } .limits label small { color: var(--ink-3); }\n.form { display: grid; grid-template-columns: 1fr 1fr; gap: .9rem 1.5rem; } .form label { display: grid; gap: .3rem; font-weight: 600; font-size: 14px; } .form .wide { grid-column: 1 / -1; }\n.danger-zone { border: 2px solid var(--red); border-radius: 12px; padding: 1rem 1.1rem; background: var(--red-soft); } .danger-zone h3 { color: var(--red); } .form label.switch { display: flex; flex-direction: row; align-items: center; gap: .6rem; font-weight: 600; font-size: 14px; } .switch input { accent-color: var(--forest); width: 18px; height: 18px; }\ntable { width: 100%; border-collapse: collapse; }\nth { text-align: left; font-size: 12px; font-weight: 600; color: var(--ink-3); padding: .4rem .5rem; border-bottom: 2px solid var(--ink); white-space: nowrap; }\ntd { padding: .5rem .5rem; vertical-align: middle; border-bottom: 1px solid var(--line); }\ntbody tr:nth-child(even) td { background: rgba(255,255,255,.55); }\nth.r, td.r { text-align: right; white-space: nowrap; } td.mono, td.dim { white-space: nowrap; } .events td.c:empty::before { content: \"(no content)\"; color: var(--ink-3); }\ntd .ib { width: 26px; height: 26px; box-shadow: 1px 1px 0 var(--ink); } td .ib svg { width: 13px; height: 13px; }\ntd input.txt { padding: .25rem .5rem; font-size: 14px; }\n.kind { font-family: var(--mono); font-size: 12px; background: var(--sun); border: 1.5px solid var(--ink); padding: 0 .5rem; border-radius: 999px; font-weight: 500; white-space: nowrap; }\n.events td.c { max-width: 0; width: 100%; overflow-x: auto; white-space: nowrap; scrollbar-width: thin; -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 2rem), transparent); mask-image: linear-gradient(90deg, #000 calc(100% - 2rem), transparent); }\n.people-table td.name { width: 12rem; }\n.ev { display: inline-grid; place-items: center; width: 20px; height: 20px; border-radius: 4px; vertical-align: -5px; margin-right: .45rem; font: 500 9px var(--mono); background: var(--line); color: var(--ink-2); font-style: normal; }\n.plain { list-style: none; margin: 0; padding: 0; } .plain li { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: .5rem; padding: .5rem 0; border-bottom: 2px dotted var(--line-2); } .plain li:last-child { border-bottom: 0; } .plain li.empty { grid-template-columns: 1fr; color: var(--ink-3); }\n.kindline { margin: .4rem 0; color: var(--ink-2); display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; }\n.tag { display: inline-flex; align-items: center; gap: .2rem; font-family: var(--mono); font-size: 13px; padding: .05rem .2rem .05rem .55rem; border: 2px solid var(--ink); border-radius: 999px; background: var(--paper); box-shadow: 1px 1px 0 var(--ink); }\n.tag.plain { padding-right: .55rem; box-shadow: none; border-color: var(--line-2); color: var(--ink-3); } .tag.blk { color: var(--red); }\n.tag .ib { width: 18px; height: 18px; border-width: 1.5px; box-shadow: none; margin-left: .2rem; } .tag .ib svg { width: 9px; height: 9px; }\n.counters { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.2rem; margin-bottom: 1.6rem; }\n.counter { display: grid; justify-items: center; align-content: center; gap: .15rem; min-height: 7.2rem; text-align: center; padding: 1rem .8rem; border: 2px solid var(--ink); border-radius: 16px; box-shadow: 4px 4px 0 var(--ink); background: var(--paper); }\n.counter:nth-child(1) { background: var(--sun); } .counter:nth-child(2) { background: var(--peach); } .counter:nth-child(3) { background: var(--sky); } .counter:nth-child(4) { background: var(--mint); }\n.counter .big { font: 400 2.6rem/1 var(--display); white-space: nowrap; } .counter small { display: block; margin-top: .35rem; font: 700 12px var(--sans); text-transform: uppercase; letter-spacing: .08em; } .counter .sub { font-size: 12px; color: var(--ink-2); }\n.kbar { display: flex; width: 100%; height: 22px; border: 2px solid var(--ink); border-radius: 999px; overflow: hidden; background: var(--paper); margin-bottom: .35rem; } .kbar i { display: block; height: 100%; border-right: 2px solid var(--ink); } .kbar i:last-child { border-right: 0; }\n.k1 { background: var(--ink); } .k2 { background: var(--forest); } .k3 { background: var(--id-3); } .k4 { background: var(--id-2); } .k5 { background: var(--line-2); }\n.legend { display: flex; flex-wrap: wrap; justify-content: center; gap: .1rem .6rem; font-family: var(--mono); font-size: 10.5px; text-transform: uppercase; } .legend i { display: inline-block; width: 9px; height: 9px; border: 1.5px solid var(--ink); border-radius: 2px; margin-right: .3rem; vertical-align: -1px; }\n.usage { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: .8rem; } .usage div { padding: .7rem .9rem; border: 2px solid var(--ink); border-radius: 12px; background: var(--paper); } .usage small { display: block; color: var(--ink-3); font-size: 12px; } .usage b { font: 400 1.5rem/1.2 var(--display); }\n@media (max-width: 52rem) { .two, .gauges, .limits, .form, .counters { grid-template-columns: 1fr !important; } .mast { grid-template-columns: 1fr; } .mast .right { justify-items: start; text-align: left; } }\n@media (max-width: 40rem) { .events th:first-child, .events td:first-child { display: none; } }\n\n.slegend { justify-content: flex-start; margin: 0 0 1.2rem; }\n#s-totals { margin-bottom: .4rem; }\ntd.keep { white-space: nowrap; } td.keep input { width: 5.5rem; display: inline-block; margin-right: .3rem; } td.keep input::placeholder { color: var(--ink-3); }\n#kinds tr.any td:first-child { font-weight: 600; }\n#kinds td:first-child .kind { display: inline-block; min-width: 5.2em; text-align: center; margin-right: .35rem; }\n/* Tables keep their columns and scroll inside the card on narrow screens instead of pushing the page sideways. */\n.scroll { overflow-x: auto; max-width: 100%; scrollbar-width: thin; } .scroll table { min-width: 100%; }\nsection, .card { overflow-x: clip; }\n.kind.sys { background: var(--mint); border-color: var(--forest); color: var(--forest); }\ntd.keep.sys { color: var(--ink-3); font-size: 13px; white-space: normal; }\n\n#console.mod .tabs a:not([data-tab=people]):not([data-tab=storage]) { display: none; }\n#console.mod [data-panel=storage] > :not(h2):not(details) { display: none; }\n#members select.role { width: auto; padding: .1rem .3rem; font-size: 12px; margin-left: .3rem; }";19+const CSS = "main { max-width: 64rem; }\n.mast { display: grid; grid-template-columns: 1fr auto; gap: 1.5rem 2rem; align-items: end; margin-bottom: 2rem; }\n.mast .wordmark { grid-column: 1 / -1; justify-self: center; margin-bottom: .5rem; }\n.mast .right { display: grid; gap: .7rem; justify-items: end; text-align: right; }\n.urlrow, .owner { display: inline-flex; align-items: center; gap: .4rem; color: var(--ink-2); }\n.owner b { color: var(--ink); } .owner em { font-style: normal; color: var(--forest); }\n.urlrow .ib, .owner .ib { width: 26px; height: 26px; margin-left: .2rem; box-shadow: 1px 1px 0 var(--ink); } .urlrow .ib svg, .owner .ib svg { width: 13px; height: 13px; }\n.care { display: flex; gap: .9rem; flex-wrap: wrap; }\n.care .g { display: grid; justify-items: center; gap: .3rem; width: 5.4rem; }\n.care .g i { display: grid; place-items: center; width: 44px; height: 44px; border: 2px solid var(--ink); border-radius: 10px; background: var(--paper); box-shadow: 2px 2px 0 var(--ink); position: relative; }\n.care .g i svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }\n.care .g.off i::after { content: \"\"; position: absolute; width: 3px; height: 40px; background: var(--ink); transform: rotate(45deg); border-radius: 2px; box-shadow: 0 0 0 2px var(--paper); }\n.care .g small { font: 500 10px/1.2 var(--mono); text-transform: uppercase; letter-spacing: .04em; color: var(--ink-2); text-align: center; }\n#unclaimed { background: var(--butter); }\n.doors { display: grid; grid-template-columns: repeat(4, 1fr); gap: .8rem; } @media (max-width: 52rem) { .doors { grid-template-columns: 1fr 1fr; } } @media (max-width: 30rem) { .doors { grid-template-columns: 1fr; } }\n.door { display: grid; grid-template-rows: auto 1fr; align-content: start; gap: .35rem; padding: .8rem .9rem; border: 2px solid var(--ink); border-radius: 12px; background: var(--paper); box-shadow: 3px 3px 0 var(--ink); min-width: 0; }\n.door small { font: 700 11px var(--sans); text-transform: uppercase; letter-spacing: .06em; color: var(--ink-2); }\n.door .v { display: grid; grid-template-columns: 1fr auto; align-items: start; gap: .5rem; font: 500 13px/1.45 var(--mono); } .door .v span { min-width: 0; overflow-wrap: anywhere; }\n.door .ib { width: 24px; height: 24px; box-shadow: 1px 1px 0 var(--ink); flex: 0 0 auto; } .door .ib svg { width: 12px; height: 12px; }\n#peoplesec { background: var(--peach); }\n.dir { display: flex; flex-wrap: wrap; gap: .5rem; }\n.who { display: inline-flex; align-items: center; gap: .1rem; padding: .3rem .7rem .3rem .4rem; border: 2px solid var(--ink); border-radius: 999px; background: var(--paper); box-shadow: 2px 2px 0 var(--ink); font-size: 14px; }\n.who .role { font: 700 10px var(--mono); text-transform: uppercase; letter-spacing: .05em; color: var(--forest); margin-left: .4rem; }\n.who.me { background: var(--sun); }\n#fuelsec { background: var(--mint); }\n.gauges { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }\n.gauge small { display: block; font-weight: 600; font-size: 13px; }\n.gauge .bar { height: 18px; border: 2px solid var(--ink); border-radius: 999px; background: var(--paper); margin: .5rem 0 .35rem; overflow: hidden; padding: 2px; }\n.gauge .bar i { display: block; height: 100%; width: 0; border-radius: 999px; background: repeating-linear-gradient(-45deg, var(--forest) 0 8px, var(--forest-2) 8px 16px); transition: width .4s; }\n.gauge .bar i.over { background: repeating-linear-gradient(-45deg, var(--red) 0 8px, #d4614c 8px 16px); }\n.gauge span { font-size: 13px; color: var(--ink-3); }\n.balance { margin: 1.2rem 0 0; color: var(--ink-2); } .balance b { color: var(--ink); font-weight: 700; }\n.sats { display: inline-flex; align-items: center; width: auto; } .sats input { font: 15px var(--sans); width: 6rem; text-align: right; border: 0; outline: none; padding: 0; background: transparent; color: var(--ink); } .sats span { color: var(--ink-3); padding-left: .4rem; }\n.topup { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-top: .8rem; }\n.invoice { margin-top: 1rem; border: 2px dashed var(--ink); border-radius: 12px; padding: .9rem 1rem; background: var(--paper); }\n.invoice p { margin: 0 0 .6rem; color: var(--ink-2); font-size: 14px; } .invoice .state { font-size: 13px; color: var(--forest); }\n.invoice textarea { margin-top: .7rem; min-height: 3.4rem; font-family: var(--mono); font-size: 11.5px; color: var(--ink-3); }\ndetails.disclosure summary { cursor: pointer; font: 400 1.6rem/1 var(--display); list-style: none; display: flex; align-items: center; gap: .5rem; }\ndetails.disclosure summary::-webkit-details-marker { display: none; }\ndetails.disclosure summary::before { content: \"+\"; font: 700 1.4rem var(--mono); width: 1.4rem; } details.disclosure[open] summary::before { content: \"\u2013\"; }\n.about { display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: .6rem 1.2rem; margin-top: 1rem; font-size: 14px; }\n.about small { display: block; color: var(--ink-3); font-size: 12px; } .about div > div { overflow-wrap: anywhere; }\n.console { margin-top: 3rem; }\n.tabs { display: flex; gap: .45rem; flex-wrap: wrap; align-items: flex-end; margin: 0 0 0 1rem; }\n.tabs a { font: 600 14px var(--sans); padding: .55rem 1rem; border: 2px solid var(--ink); border-bottom: 0; border-radius: 12px 12px 0 0; background: var(--head); color: var(--ink-2); text-decoration: none; margin-bottom: -2px; position: relative; z-index: 1; }\n.tabs a.on { background: var(--paper); color: var(--ink); z-index: 3; padding-top: .7rem; }\n.count { display: inline-grid; place-items: center; min-width: 1.5em; height: 1.5em; padding: 0 .4em; margin-left: .45rem; border: 1.5px solid var(--ink); border-radius: 999px; background: var(--sun); color: var(--ink); font: 700 11px/1 var(--mono); vertical-align: middle; box-shadow: 1px 1px 0 var(--ink); } .count:empty { display: none; } h2 .count { font-size: 12px; vertical-align: .45em; }\n.panel { display: none; margin: 0; border-radius: 0 18px 18px 18px; position: relative; z-index: 2; }\n.panel.on { display: block; }\n.panel h2 { font-size: 2rem; }\n.block { margin-top: 1.8rem; }\n.two { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }\n.actions { display: flex; justify-content: flex-end; margin-top: 1.1rem; } .actions.left { justify-content: flex-start; }\n.addrow { display: flex; gap: .5rem; margin-bottom: .9rem; } .addrow input { flex: 1; min-width: 0; } .addrow .ib { width: 38px; height: 38px; flex: 0 0 auto; border-radius: 10px; }\n.choices { display: grid; gap: .5rem; }\n.choices label { display: grid; grid-template-columns: auto 1fr; gap: 0 .7rem; cursor: pointer; padding: .7rem .8rem; border: 2px solid var(--line-2); border-radius: 12px; background: var(--paper); }\n.choices label:has(:checked) { border-color: var(--ink); box-shadow: 3px 3px 0 var(--ink); }\n.choices input { grid-row: span 2; margin: .2rem 0 0; accent-color: var(--forest); }\n.choices b { font-weight: 600; } .choices small { color: var(--ink-2); font-size: 13px; }\n.limits { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem 1.5rem; margin-top: 1.3rem; }\n.limits label { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: .6rem; } .limits label small { color: var(--ink-3); }\n.form { display: grid; grid-template-columns: 1fr 1fr; gap: .9rem 1.5rem; } .form label { display: grid; gap: .3rem; font-weight: 600; font-size: 14px; } .form .wide { grid-column: 1 / -1; }\n.danger-zone { border: 2px solid var(--red); border-radius: 12px; padding: 1rem 1.1rem; background: var(--red-soft); } .danger-zone h3 { color: var(--red); } .form label.switch { display: flex; flex-direction: row; align-items: center; gap: .6rem; font-weight: 600; font-size: 14px; } .switch input { accent-color: var(--forest); width: 18px; height: 18px; }\ntable { width: 100%; border-collapse: collapse; }\nth { text-align: left; font-size: 12px; font-weight: 600; color: var(--ink-3); padding: .4rem .5rem; border-bottom: 2px solid var(--ink); white-space: nowrap; }\ntd { padding: .5rem .5rem; vertical-align: middle; border-bottom: 1px solid var(--line); }\ntbody tr:nth-child(even) td { background: rgba(255,255,255,.55); }\nth.r, td.r { text-align: right; white-space: nowrap; } td.mono, td.dim { white-space: nowrap; } .events td.c:empty::before { content: \"(no content)\"; color: var(--ink-3); }\ntd .ib { width: 26px; height: 26px; box-shadow: 1px 1px 0 var(--ink); } td .ib svg { width: 13px; height: 13px; }\ntd input.txt { padding: .25rem .5rem; font-size: 14px; }\n.kind { font-family: var(--mono); font-size: 12px; background: var(--sun); border: 1.5px solid var(--ink); padding: 0 .5rem; border-radius: 999px; font-weight: 500; white-space: nowrap; }\n.events td.c { max-width: 0; width: 100%; overflow-x: auto; white-space: nowrap; scrollbar-width: thin; -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 2rem), transparent); mask-image: linear-gradient(90deg, #000 calc(100% - 2rem), transparent); }\n.people-table td.name { width: 12rem; }\n.ev { display: inline-grid; place-items: center; width: 20px; height: 20px; border-radius: 4px; vertical-align: -5px; margin-right: .45rem; font: 500 9px var(--mono); background: var(--line); color: var(--ink-2); font-style: normal; }\n.plain { list-style: none; margin: 0; padding: 0; } .plain li { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: .5rem; padding: .5rem 0; border-bottom: 2px dotted var(--line-2); } .plain li:last-child { border-bottom: 0; } .plain li.empty { grid-template-columns: 1fr; color: var(--ink-3); }\n.kindline { margin: .4rem 0; color: var(--ink-2); display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; }\n.tag { display: inline-flex; align-items: center; gap: .2rem; font-family: var(--mono); font-size: 13px; padding: .05rem .2rem .05rem .55rem; border: 2px solid var(--ink); border-radius: 999px; background: var(--paper); box-shadow: 1px 1px 0 var(--ink); }\n.tag.plain { padding-right: .55rem; box-shadow: none; border-color: var(--line-2); color: var(--ink-3); } .tag.blk { color: var(--red); }\n.tag .ib { width: 18px; height: 18px; border-width: 1.5px; box-shadow: none; margin-left: .2rem; } .tag .ib svg { width: 9px; height: 9px; }\n.counters { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.2rem; margin-bottom: 1.6rem; }\n.counter { display: grid; justify-items: center; align-content: center; gap: .15rem; min-height: 7.2rem; text-align: center; padding: 1rem .8rem; border: 2px solid var(--ink); border-radius: 16px; box-shadow: 4px 4px 0 var(--ink); background: var(--paper); }\n.counter:nth-child(1) { background: var(--sun); } .counter:nth-child(2) { background: var(--peach); } .counter:nth-child(3) { background: var(--sky); } .counter:nth-child(4) { background: var(--mint); }\n.counter .big { font: 400 2.6rem/1 var(--display); white-space: nowrap; } .counter small { display: block; margin-top: .35rem; font: 700 12px var(--sans); text-transform: uppercase; letter-spacing: .08em; } .counter .sub { font-size: 12px; color: var(--ink-2); }\n.kbar { display: flex; width: 100%; height: 22px; border: 2px solid var(--ink); border-radius: 999px; overflow: hidden; background: var(--paper); margin-bottom: .35rem; } .kbar i { display: block; height: 100%; border-right: 2px solid var(--ink); } .kbar i:last-child { border-right: 0; }\n.k1 { background: var(--ink); } .k2 { background: var(--forest); } .k3 { background: var(--id-3); } .k4 { background: var(--id-2); } .k5 { background: var(--line-2); }\n.legend { display: flex; flex-wrap: wrap; justify-content: center; gap: .1rem .6rem; font-family: var(--mono); font-size: 10.5px; text-transform: uppercase; } .legend i { display: inline-block; width: 9px; height: 9px; border: 1.5px solid var(--ink); border-radius: 2px; margin-right: .3rem; vertical-align: -1px; }\n.usage { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: .8rem; } .usage div { padding: .7rem .9rem; border: 2px solid var(--ink); border-radius: 12px; background: var(--paper); } .usage small { display: block; color: var(--ink-3); font-size: 12px; } .usage b { font: 400 1.5rem/1.2 var(--display); }\n@media (max-width: 52rem) { .two, .gauges, .limits, .form, .counters { grid-template-columns: 1fr !important; } .mast { grid-template-columns: 1fr; } .mast .right { justify-items: start; text-align: left; } }\n@media (max-width: 40rem) { .events th:first-child, .events td:first-child { display: none; } }\n\n.slegend { justify-content: flex-start; margin: 0 0 1.2rem; }\n#s-totals { margin-bottom: .4rem; }\ntd.keep { white-space: nowrap; } td.keep input { width: 5.5rem; display: inline-block; margin-right: .3rem; } td.keep input::placeholder { color: var(--ink-3); }\n#kinds tr.any td:first-child { font-weight: 600; }\n#kinds td:first-child .kind { display: inline-block; min-width: 5.2em; text-align: center; margin-right: .35rem; }\n/* Tables keep their columns and scroll inside the card on narrow screens instead of pushing the page sideways. */\n.scroll { overflow-x: auto; max-width: 100%; scrollbar-width: thin; } .scroll table { min-width: 100%; }\nsection, .card { overflow-x: clip; }\n.kind.sys { background: var(--mint); border-color: var(--forest); color: var(--forest); }\ntd.keep.sys { color: var(--ink-3); font-size: 13px; white-space: normal; }\n\n#console.mod .tabs a:not([data-tab=people]):not([data-tab=storage]) { display: none; }\n#console.mod [data-panel=storage] > :not(h2):not(details) { display: none; }\n#members select.role { width: auto; padding: .1rem .3rem; font-size: 12px; margin-left: .3rem; }\n#wire .ok { color: var(--forest); } #wire .bad { color: var(--red); } #wire td { vertical-align: top; }\n#presets .btn { margin: 0 .4rem .4rem 0; }";2020 21-const BODY = "<main>\n <header class=\"mast\">\n <div class=\"wordmark\"><h1 id=\"title\"></h1></div>\n <div class=\"left\"><div class=\"care\" id=\"care\"></div></div>\n <div class=\"right\">\n <span class=\"urlrow\"><code class=\"pill\" id=\"url\"></code><button class=\"ib\" id=\"copy\" title=\"Copy relay URL\" aria-label=\"Copy relay URL\"><svg viewBox=\"0 0 24 24\"><rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\"/><path d=\"M5 15V6a2 2 0 0 1 2-2h9\"/></svg></button></span>\n <span class=\"owner\"><i class=\"av hidden\" id=\"owner-av\"></i><span id=\"ownerline\"></span><em id=\"who\"></em>\n <button id=\"signin\" class=\"btn hidden\">Sign in with extension</button>\n <button id=\"signin46\" class=\"btn hidden\">Remote signer</button>\n <button id=\"signout\" class=\"ib hidden\" title=\"Sign out\" aria-label=\"Sign out\"><svg viewBox=\"0 0 24 24\"><path d=\"M10 4H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h4M15 8l5 4-5 4M20 12H9\"/></svg></button>\n </span>\n </div>\n </header>\n\n <section id=\"unclaimed\" class=\"hidden\">\n <h2>Nobody owns this relay yet.</h2>\n <p class=\"lead\">Claim it and it's yours: you decide who can post, who can read, and what stays. One signature with a nostr browser extension; no account, no email, no card.</p>\n <div class=\"row\"><button id=\"claim\" class=\"btn pri\">Claim this relay</button><button class=\"btn remote\" data-note=\"claimnote\">Use a remote signer</button><span class=\"note\" id=\"claimnote\"></span></div>\n </section>\n\n <section id=\"leased\" class=\"hidden\">\n <h2>A temporary relay, for now.</h2>\n <p class=\"lead\">Anyone can read and write here until <b id=\"lease-until\"></b>. Then everything on it is deleted and the name is freed. Claim it and it stays, events and files included: one signature with a nostr browser extension. Or claim a new name and pull this one into it from its Storage tab.</p>\n <div class=\"row\"><button id=\"claimlease\" class=\"btn pri\">Claim this relay</button><button class=\"btn remote\" data-note=\"leasenote\">Use a remote signer</button><span class=\"note\" id=\"leasenote\"></span></div>\n </section>\n\n <section id=\"remotesec\" class=\"hidden\">\n <h2>Connect a remote signer.</h2>\n <p class=\"lead\">Your key stays in a signer app such as Amber or nsec.app; this page asks it to sign. On a phone, open the link and approve there. On a computer, paste the <span class=\"mono\">bunker://</span> URL the app gives you.</p>\n <div class=\"row\"><a id=\"nclink\" class=\"btn pri\" href=\"#\">Open in your signer app</a><button id=\"nccopy\" class=\"btn\">Copy link</button><span class=\"note\" id=\"ncnote\"></span></div>\n <form id=\"bunkerform\" class=\"addrow\"><input class=\"txt mono\" name=\"url\" placeholder=\"bunker://\u2026\" required><button class=\"btn\">Connect</button></form>\n <p class=\"note\" id=\"remotenote\"></p>\n </section>\n\n <section id=\"connectsec\">\n <h2>Connect</h2>\n <p class=\"lead hidden\" id=\"desc\"></p>\n <div class=\"doors\">\n <div class=\"door\"><small>nostr relay</small><div class=\"v\"><span id=\"d-ws\"></span><button class=\"ib\" data-copy=\"ws\" title=\"Copy\" aria-label=\"Copy\"><svg viewBox=\"0 0 24 24\"><rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\"/><path d=\"M5 15V6a2 2 0 0 1 2-2h9\"/></svg></button></div></div>\n <div class=\"door\"><small>Blossom media</small><div class=\"v\"><span id=\"d-blossom\"></span><button class=\"ib\" data-copy=\"blossom\" title=\"Copy\" aria-label=\"Copy\"><svg viewBox=\"0 0 24 24\"><rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\"/><path d=\"M5 15V6a2 2 0 0 1 2-2h9\"/></svg></button></div></div>\n <div class=\"door\"><small>names</small><div class=\"v\"><span id=\"d-nip05\"></span></div></div>\n <div class=\"door\"><small>HTTP bridge, NIP-98</small><div class=\"v\"><span>POST /events, /query, /count</span></div></div>\n </div>\n </section>\n\n <section id=\"peoplesec\" class=\"hidden\">\n <h2>People <span class=\"count\" id=\"people-count\"></span></h2>\n <p class=\"note hidden\" id=\"people-note\">Hidden from visitors. Only you see this list.</p>\n <div class=\"dir\" id=\"dir\"></div>\n </section>\n\n <section id=\"fuelsec\" class=\"hidden\">\n <h2>Fuel</h2>\n <p class=\"lead\">Free allowance first; past it, usage burns sats. Anyone can zap a top-up.</p>\n <div class=\"gauges\">\n <div class=\"gauge\"><small>Events stored</small><div class=\"bar\"><i id=\"g-events\"></i></div><span id=\"t-events\"></span></div>\n <div class=\"gauge\"><small>Files stored</small><div class=\"bar\"><i id=\"g-media\"></i></div><span id=\"t-media\"></span></div>\n <div class=\"gauge\"><small>Awake this month</small><div class=\"bar\"><i id=\"g-active\"></i></div><span id=\"t-active\"></span></div>\n <div class=\"gauge\"><small>Rows written this month</small><div class=\"bar\"><i id=\"g-rows\"></i></div><span id=\"t-rows\"></span></div>\n </div>\n <p class=\"balance\" id=\"fuel-balance\"></p>\n <form id=\"topup\" class=\"topup hidden\"><span class=\"sats\"><input type=\"number\" name=\"sats\" min=\"1\" value=\"1000\"><span>sats</span></span><button class=\"btn pri\">⚡ Zap to top up</button><span class=\"note\" id=\"topup-note\"></span></form>\n <div id=\"invoice\" class=\"invoice hidden\">\n <p>Pay from any lightning wallet. The receipt lands on this relay and credits it automatically.</p>\n <div class=\"row\"><a id=\"inv-link\" class=\"btn\" href=\"#\">Open in wallet</a><button id=\"inv-copy\" class=\"ib\" title=\"Copy invoice\" aria-label=\"Copy invoice\"><svg viewBox=\"0 0 24 24\"><rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\"/><path d=\"M5 15V6a2 2 0 0 1 2-2h9\"/></svg></button><span class=\"state\" id=\"inv-state\"></span></div>\n <textarea id=\"inv-text\" readonly></textarea>\n </div>\n </section>\n\n <section id=\"aboutsec\"><details class=\"disclosure\"><summary>About, for clients</summary><div id=\"about\" class=\"about\"></div></details></section>\n\n <div class=\"console hidden\" id=\"console\">\n <nav class=\"tabs\" id=\"tabs\">\n <a href=\"#people\" data-tab=\"people\">People<span class=\"count\" id=\"tc-people\"></span></a>\n <a href=\"#rules\" data-tab=\"rules\">Rules</a>\n <a href=\"#identity\" data-tab=\"identity\">Identity</a>\n <a href=\"#storage\" data-tab=\"storage\">Storage</a>\n <a href=\"#health\" data-tab=\"health\">Health</a>\n </nav>\n\n <section class=\"panel\" data-panel=\"people\">\n <h2>People</h2>\n <p class=\"lead\">The member list is published as a signed roster; a name makes someone <span class=\"mono\" id=\"nip05-example\"></span>.</p>\n <div class=\"block\">\n <form id=\"addmember\" class=\"addrow\"><input class=\"txt mono\" name=\"pubkey\" placeholder=\"hex pubkey or npub\" required><input class=\"txt\" name=\"name\" placeholder=\"name (optional)\" pattern=\"[a-z0-9._-]{1,64}\" style=\"max-width:9rem\"><input class=\"txt\" name=\"note\" placeholder=\"note\" style=\"max-width:9rem\"><button class=\"ib pri\" title=\"Add member\" aria-label=\"Add member\"><svg viewBox=\"0 0 24 24\"><path d=\"M12 5v14M5 12h14\"/></svg></button></form>\n <div class=\"scroll\"><table id=\"members\" class=\"people-table\"><thead><tr><th>Who</th><th>Name</th><th>Note</th><th>Joined</th><th></th></tr></thead><tbody></tbody></table></div>\n </div>\n <div class=\"block two\">\n <div>\n <h3>Invites</h3>\n <form id=\"mintinvite\" class=\"addrow\"><select class=\"txt\" name=\"ttl\" style=\"max-width:8rem\"><option value=\"3600\">1 hour</option><option value=\"86400\">1 day</option><option value=\"259200\" selected>3 days</option><option value=\"2592000\">30 days</option></select><input class=\"txt num\" type=\"number\" name=\"max\" min=\"0\" value=\"0\" title=\"uses, 0 = unlimited\" style=\"width:5rem;flex:0 0 auto\"><input class=\"txt\" name=\"note\" placeholder=\"note\"><button class=\"ib pri\" title=\"New invite\" aria-label=\"New invite\"><svg viewBox=\"0 0 24 24\"><path d=\"M12 5v14M5 12h14\"/></svg></button></form>\n <ul id=\"invites\" class=\"plain\"></ul>\n </div>\n <div>\n <h3>Bans</h3>\n <form id=\"addban\" class=\"addrow\"><input class=\"txt mono\" name=\"pubkey\" placeholder=\"hex pubkey or npub\" required><input class=\"txt\" name=\"reason\" placeholder=\"reason\" style=\"max-width:8rem\"><button class=\"ib danger\" title=\"Ban\" aria-label=\"Ban\"><svg viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"8.5\"/><path d=\"M6 6l12 12\"/></svg></button></form>\n <ul id=\"bans\" class=\"plain\"></ul>\n <h3>Blocked addresses</h3>\n <form id=\"addblock\" class=\"addrow\"><input class=\"txt mono\" name=\"ip\" placeholder=\"IP address\" required><input class=\"txt\" name=\"reason\" placeholder=\"reason\" style=\"max-width:8rem\"><button class=\"ib danger\" title=\"Block\" aria-label=\"Block\"><svg viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"8.5\"/><path d=\"M6 6l12 12\"/></svg></button></form>\n <ul id=\"blocks\" class=\"plain\"></ul>\n <ul id=\"banned-events\" class=\"plain\"></ul>\n </div>\n </div>\n <div class=\"block\">\n <h3>Reports <span class=\"count\" id=\"reports-count\"></span></h3>\n <p class=\"note\">Reports never show in the feed. Banning also deletes the reported event.</p>\n <div class=\"scroll\"><table id=\"reports\" class=\"events\"><thead><tr><th>Time</th><th>Type</th><th>About</th><th>Reason</th><th></th></tr></thead><tbody></tbody></table></div>\n </div>\n </section>\n\n <section class=\"panel\" data-panel=\"rules\">\n <h2>Rules</h2>\n <p class=\"lead\">Bans apply regardless of these.</p>\n <form id=\"access\">\n <div class=\"two\">\n <div><h3>Writes</h3><div class=\"choices\">\n <label><input type=\"radio\" name=\"writes\" value=\"open\"><b>Anyone</b><small>Every valid event is accepted, unless banned.</small></label>\n <label><input type=\"radio\" name=\"writes\" value=\"allowlist\"><b>Members</b><small>Only people on the member list, and you.</small></label>\n <label><input type=\"radio\" name=\"writes\" value=\"owner\"><b>Only me</b><small>Nobody else can post.</small></label>\n </div></div>\n <div><h3>Reads</h3><div class=\"choices\">\n <label><input type=\"radio\" name=\"reads\" value=\"open\"><b>Anyone</b><small>DMs and gift wraps still go only to their parties.</small></label>\n <label><input type=\"radio\" name=\"reads\" value=\"auth\"><b>Authenticated only</b><small>Every query needs a NIP-42 sign-in first.</small></label>\n <label><input type=\"radio\" name=\"reads\" value=\"members\"><b>Members only</b><small>Removing a member ends their subscriptions.</small></label>\n </div></div>\n </div>\n <div class=\"limits\">\n <label><span>Minimum proof of work <small>bits</small></span><input class=\"txt num\" type=\"number\" name=\"minPow\" min=\"0\" max=\"64\"></label>\n <label><span>Reject events dated in the future by more than <small>seconds</small></span><input class=\"txt num\" type=\"number\" name=\"maxFuture\" min=\"0\"></label>\n <label><span>Max events per query</span><input class=\"txt num\" type=\"number\" name=\"maxLimit\" min=\"1\" max=\"5000\"></label>\n <label><span>Max subscriptions per connection</span><input class=\"txt num\" type=\"number\" name=\"maxSubs\" min=\"1\" max=\"200\"></label>\n <label><span>Events per minute <small>per connection</small></span><input class=\"txt num\" type=\"number\" name=\"eventsPerMinute\" min=\"1\"></label>\n <label><span>Queries per minute <small>per connection</small></span><input class=\"txt num\" type=\"number\" name=\"reqsPerMinute\" min=\"1\"></label>\n <label><span>Max upload size <small>MB</small></span><input class=\"txt num\" type=\"number\" name=\"maxBlobMB\" min=\"1\" max=\"95\"></label>\n </div>\n <div class=\"actions\"><button class=\"btn pri\">Save rules</button></div>\n </form>\n <div class=\"block\">\n <h3>Kinds</h3>\n <p class=\"note\">An empty allow list means every kind. Blocks always win.</p>\n <form id=\"kindform\" class=\"addrow\"><input class=\"txt num\" type=\"number\" name=\"kind\" min=\"0\" max=\"65535\" placeholder=\"kind\" required style=\"width:7rem;flex:0 0 auto\"><button class=\"ib\" name=\"rule\" value=\"allow\" title=\"Allow kind\" aria-label=\"Allow kind\"><svg viewBox=\"0 0 24 24\"><path d=\"M5 12l5 5L20 7\"/></svg></button><button class=\"ib danger\" name=\"rule\" value=\"block\" title=\"Block kind\" aria-label=\"Block kind\"><svg viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"8.5\"/><path d=\"M6 6l12 12\"/></svg></button></form>\n <p class=\"kindline\">Allowed: <span id=\"kinds-allow\"></span></p>\n <p class=\"kindline\">Blocked: <span id=\"kinds-block\"></span></p>\n </div>\n </section>\n\n <section class=\"panel\" data-panel=\"identity\">\n <h2>Identity</h2>\n <form id=\"identity\">\n <div class=\"form\">\n <label><span>Name</span><input class=\"txt\" name=\"name\" maxlength=\"200\"></label>\n <label><span>Contact</span><input class=\"txt\" name=\"contact\" placeholder=\"mailto:you@example.com\" maxlength=\"500\"></label>\n <label class=\"wide\"><span>Description</span><textarea name=\"description\" maxlength=\"2000\"></textarea></label>\n <label class=\"wide\"><span>Icon URL</span><input class=\"txt\" name=\"icon\" placeholder=\"https://\u2026/icon.png\" maxlength=\"2000\"></label>\n <label class=\"wide\"><span>Banner URL</span><input class=\"txt\" name=\"banner\" placeholder=\"https://\u2026/banner.png\" maxlength=\"2000\"></label>\n <label><span>Posting policy URL</span><input class=\"txt\" name=\"postingPolicy\" placeholder=\"https://\u2026/posting\" maxlength=\"2000\"></label>\n <label><span>Privacy policy URL</span><input class=\"txt\" name=\"privacyPolicy\" placeholder=\"https://\u2026/privacy\" maxlength=\"2000\"></label>\n <label><span>Tags</span><input class=\"txt\" name=\"tags\" placeholder=\"bitcoin, cats\"></label>\n <label><span>Languages</span><input class=\"txt\" name=\"languageTags\" placeholder=\"en, pt-BR\"></label>\n <label><span>Countries</span><input class=\"txt\" name=\"relayCountries\" placeholder=\"US, DE\"></label>\n <label class=\"wide\"><span>Shown before someone accepts an invite</span><textarea name=\"joinTerms\" placeholder=\"House rules, expectations, anything a newcomer should agree to.\" maxlength=\"20000\"></textarea></label>\n <label class=\"wide switch\"><input type=\"checkbox\" name=\"directoryPublic\"> Show the people directory to visitors</label>\n </div>\n <div class=\"actions\"><button class=\"btn pri\">Save identity</button></div>\n </form>\n <div class=\"block\">\n <h3>Configuration</h3>\n <p class=\"note\">Rules, identity, members, bans and kind rules as a file. Importing replaces those lists; it never touches events, files, or the owner.</p>\n <div class=\"row\"><button id=\"exportcfg\" class=\"btn\">Export configuration</button><button id=\"importcfg\" class=\"btn\">Import configuration</button><input type=\"file\" id=\"cfgfile\" accept=\"application/json,.json\" class=\"hidden\"></div>\n </div>\n <div class=\"block danger-zone\">\n <h3>Transfer ownership</h3>\n <p class=\"note\">Hands the relay to a member. You stay on as a moderator. The relay's key, events, files and fuel do not change. There is no undo.</p>\n <form id=\"transfer\" class=\"addrow\"><select class=\"txt mono\" name=\"pubkey\" required></select><button class=\"btn danger\">Transfer</button></form>\n </div>\n <div class=\"block danger-zone\">\n <h3>Delete this relay</h3>\n <p class=\"note\">Deletes every event, file, member, invite and setting, closes every connection, and returns the name to unclaimed for anyone to take. There is no undo.</p>\n <button id=\"deleterelay\" class=\"btn danger\">Delete this relay</button>\n </div>\n </section>\n\n <section class=\"panel\" data-panel=\"storage\">\n <h2>Storage</h2>\n <p class=\"lead\">Keep-for rules run once a day. Purges happen now and cannot be undone.</p>\n <div class=\"kbar\" id=\"s-bar\"></div><span class=\"legend slegend\" id=\"s-legend\"></span>\n <div class=\"usage\" id=\"s-totals\"></div>\n <div class=\"block\">\n <h3>By kind</h3>\n <div class=\"scroll\"><table id=\"kinds\" class=\"events\"><thead><tr><th>Kind</th><th class=\"r\">Count</th><th class=\"r\">Size</th><th>Oldest</th><th>Keep for</th><th></th></tr></thead><tbody></tbody></table></div>\n </div>\n <div class=\"block\">\n <h3>Files <span class=\"count\" id=\"blobs-count\"></span></h3>\n <div class=\"scroll\"><table id=\"blobs\" class=\"events\"><thead><tr><th>Time</th><th>File</th><th>Size</th><th>Uploader</th><th></th></tr></thead><tbody></tbody></table></div>\n </div>\n <div class=\"block\">\n <h3>Pull from a relay</h3>\n <p class=\"note\">Copies what the other relay shows to anyone and this one lacks, by NIP-77 sync, so running it again only fetches what is new. Files come along from relays on this host. Bans and kind rules apply; the write rule does not.</p>\n <form id=\"pullform\" class=\"addrow\"><input class=\"txt mono\" name=\"url\" placeholder=\"wss://other.relay\" required><button class=\"btn\">Pull</button></form>\n <p class=\"note\" id=\"pullnote\"></p>\n </div>\n <details class=\"disclosure block\"><summary>Browse recent events</summary>\n <p class=\"note\">Delete removes one thing. Ban also refuses it forever.</p>\n <div class=\"scroll\"><table id=\"events\" class=\"events\"><thead><tr><th>Time</th><th>Kind</th><th>Author</th><th>Content</th><th></th></tr></thead><tbody></tbody></table></div>\n <div class=\"actions left\"><button id=\"more\" class=\"btn\">Load more</button></div>\n </details>\n </section>\n\n <section class=\"panel\" data-panel=\"health\">\n <h2>Health</h2>\n <div class=\"counters\">\n <div class=\"counter\"><b class=\"big\" id=\"h-last\">\u2013</b><small>since last event</small><span class=\"sub\" id=\"h-last-sub\"></span></div>\n <div class=\"counter\"><b class=\"big\" id=\"h-conns\">\u2013</b><small>connected now</small><span class=\"sub\">websockets open</span></div>\n <div class=\"counter\"><b class=\"big\" id=\"h-fuel\">\u2013</b><small id=\"h-fuel-label\">fuel</small><span class=\"sub\" id=\"h-fuel-sub\"></span></div>\n <div class=\"counter\"><div class=\"kbar\" id=\"h-kinds\"></div><small>used for, last 30 days</small><span class=\"sub legend\" id=\"h-kinds-legend\"></span></div>\n </div>\n <div class=\"usage\" id=\"usage\"></div>\n <div class=\"block\">\n <h3>Zaps received</h3>\n <div class=\"scroll\"><table id=\"credits\"><thead><tr><th>When</th><th>From</th><th class=\"r\">Sats</th></tr></thead><tbody></tbody></table></div>\n </div>\n </section>\n </div>\n\n <footer class=\"pg\"><p>a bind.ws relay</p><a href=\"https://github.com/FelineStateMachine/bindws\">source</a></footer>\n</main>\n";21+const BODY = "<main>\n <header class=\"mast\">\n <div class=\"wordmark\"><h1 id=\"title\"></h1></div>\n <div class=\"left\"><div class=\"care\" id=\"care\"></div></div>\n <div class=\"right\">\n <span class=\"urlrow\"><code class=\"pill\" id=\"url\"></code><button class=\"ib\" id=\"copy\" title=\"Copy relay URL\" aria-label=\"Copy relay URL\"><svg viewBox=\"0 0 24 24\"><rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\"/><path d=\"M5 15V6a2 2 0 0 1 2-2h9\"/></svg></button></span>\n <span class=\"owner\"><i class=\"av hidden\" id=\"owner-av\"></i><span id=\"ownerline\"></span><em id=\"who\"></em>\n <button id=\"signin\" class=\"btn hidden\">Sign in with extension</button>\n <button id=\"signin46\" class=\"btn hidden\">Remote signer</button>\n <button id=\"signout\" class=\"ib hidden\" title=\"Sign out\" aria-label=\"Sign out\"><svg viewBox=\"0 0 24 24\"><path d=\"M10 4H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h4M15 8l5 4-5 4M20 12H9\"/></svg></button>\n </span>\n </div>\n </header>\n\n <section id=\"unclaimed\" class=\"hidden\">\n <h2>Nobody owns this relay yet.</h2>\n <p class=\"lead\">Claim it and it's yours: you decide who can post, who can read, and what stays. One signature with a nostr browser extension; no account, no email, no card.</p>\n <div class=\"row\"><button id=\"claim\" class=\"btn pri\">Claim this relay</button><button class=\"btn remote\" data-note=\"claimnote\">Use a remote signer</button><span class=\"note\" id=\"claimnote\"></span></div>\n </section>\n\n <section id=\"leased\" class=\"hidden\">\n <h2>A temporary relay, for now.</h2>\n <p class=\"lead\">Anyone can read and write here until <b id=\"lease-until\"></b>. Then everything on it is deleted and the name is freed. Claim it and it stays, events and files included: one signature with a nostr browser extension. Or claim a new name and pull this one into it from its Storage tab.</p>\n <div class=\"row\"><button id=\"claimlease\" class=\"btn pri\">Claim this relay</button><button class=\"btn remote\" data-note=\"leasenote\">Use a remote signer</button><span class=\"note\" id=\"leasenote\"></span></div>\n </section>\n\n <section id=\"remotesec\" class=\"hidden\">\n <h2>Connect a remote signer.</h2>\n <p class=\"lead\">Your key stays in a signer app such as Amber or nsec.app; this page asks it to sign. On a phone, open the link and approve there. On a computer, paste the <span class=\"mono\">bunker://</span> URL the app gives you.</p>\n <div class=\"row\"><a id=\"nclink\" class=\"btn pri\" href=\"#\">Open in your signer app</a><button id=\"nccopy\" class=\"btn\">Copy link</button><span class=\"note\" id=\"ncnote\"></span></div>\n <form id=\"bunkerform\" class=\"addrow\"><input class=\"txt mono\" name=\"url\" placeholder=\"bunker://\u2026\" required><button class=\"btn\">Connect</button></form>\n <p class=\"note\" id=\"remotenote\"></p>\n </section>\n\n <section id=\"connectsec\">\n <h2>Connect</h2>\n <p class=\"lead hidden\" id=\"desc\"></p>\n <div class=\"doors\">\n <div class=\"door\"><small>nostr relay</small><div class=\"v\"><span id=\"d-ws\"></span><button class=\"ib\" data-copy=\"ws\" title=\"Copy\" aria-label=\"Copy\"><svg viewBox=\"0 0 24 24\"><rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\"/><path d=\"M5 15V6a2 2 0 0 1 2-2h9\"/></svg></button></div></div>\n <div class=\"door\"><small>Blossom media</small><div class=\"v\"><span id=\"d-blossom\"></span><button class=\"ib\" data-copy=\"blossom\" title=\"Copy\" aria-label=\"Copy\"><svg viewBox=\"0 0 24 24\"><rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\"/><path d=\"M5 15V6a2 2 0 0 1 2-2h9\"/></svg></button></div></div>\n <div class=\"door\"><small>names</small><div class=\"v\"><span id=\"d-nip05\"></span></div></div>\n <div class=\"door\"><small>HTTP bridge, NIP-98</small><div class=\"v\"><span>POST /events, /query, /count</span></div></div>\n </div>\n </section>\n\n <section id=\"peoplesec\" class=\"hidden\">\n <h2>People <span class=\"count\" id=\"people-count\"></span></h2>\n <p class=\"note hidden\" id=\"people-note\">Hidden from visitors. Only you see this list.</p>\n <div class=\"dir\" id=\"dir\"></div>\n </section>\n\n <section id=\"fuelsec\" class=\"hidden\">\n <h2>Fuel</h2>\n <p class=\"lead\">Free allowance first; past it, usage burns sats. Anyone can zap a top-up.</p>\n <div class=\"gauges\">\n <div class=\"gauge\"><small>Events stored</small><div class=\"bar\"><i id=\"g-events\"></i></div><span id=\"t-events\"></span></div>\n <div class=\"gauge\"><small>Files stored</small><div class=\"bar\"><i id=\"g-media\"></i></div><span id=\"t-media\"></span></div>\n <div class=\"gauge\"><small>Awake this month</small><div class=\"bar\"><i id=\"g-active\"></i></div><span id=\"t-active\"></span></div>\n <div class=\"gauge\"><small>Rows written this month</small><div class=\"bar\"><i id=\"g-rows\"></i></div><span id=\"t-rows\"></span></div>\n </div>\n <p class=\"balance\" id=\"fuel-balance\"></p>\n <form id=\"topup\" class=\"topup hidden\"><span class=\"sats\"><input type=\"number\" name=\"sats\" min=\"1\" value=\"1000\"><span>sats</span></span><button class=\"btn pri\">⚡ Zap to top up</button><span class=\"note\" id=\"topup-note\"></span></form>\n <div id=\"invoice\" class=\"invoice hidden\">\n <p>Pay from any lightning wallet. The receipt lands on this relay and credits it automatically.</p>\n <div class=\"row\"><a id=\"inv-link\" class=\"btn\" href=\"#\">Open in wallet</a><button id=\"inv-copy\" class=\"ib\" title=\"Copy invoice\" aria-label=\"Copy invoice\"><svg viewBox=\"0 0 24 24\"><rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\"/><path d=\"M5 15V6a2 2 0 0 1 2-2h9\"/></svg></button><span class=\"state\" id=\"inv-state\"></span></div>\n <textarea id=\"inv-text\" readonly></textarea>\n </div>\n </section>\n\n <section id=\"aboutsec\"><details class=\"disclosure\"><summary>About, for clients</summary><div id=\"about\" class=\"about\"></div></details></section>\n\n <div class=\"console hidden\" id=\"console\">\n <nav class=\"tabs\" id=\"tabs\">\n <a href=\"#people\" data-tab=\"people\">People<span class=\"count\" id=\"tc-people\"></span></a>\n <a href=\"#rules\" data-tab=\"rules\">Rules</a>\n <a href=\"#identity\" data-tab=\"identity\">Identity</a>\n <a href=\"#storage\" data-tab=\"storage\">Storage</a>\n <a href=\"#health\" data-tab=\"health\">Health</a>\n </nav>\n\n <section class=\"panel\" data-panel=\"people\">\n <h2>People</h2>\n <p class=\"lead\">The member list is published as a signed roster; a name makes someone <span class=\"mono\" id=\"nip05-example\"></span>.</p>\n <div class=\"block\">\n <form id=\"addmember\" class=\"addrow\"><input class=\"txt mono\" name=\"pubkey\" placeholder=\"hex pubkey or npub\" required><input class=\"txt\" name=\"name\" placeholder=\"name (optional)\" pattern=\"[a-z0-9._-]{1,64}\" style=\"max-width:9rem\"><input class=\"txt\" name=\"note\" placeholder=\"note\" style=\"max-width:9rem\"><button class=\"ib pri\" title=\"Add member\" aria-label=\"Add member\"><svg viewBox=\"0 0 24 24\"><path d=\"M12 5v14M5 12h14\"/></svg></button></form>\n <div class=\"scroll\"><table id=\"members\" class=\"people-table\"><thead><tr><th>Who</th><th>Name</th><th>Note</th><th>Joined</th><th></th></tr></thead><tbody></tbody></table></div>\n </div>\n <div class=\"block two\">\n <div>\n <h3>Invites</h3>\n <form id=\"mintinvite\" class=\"addrow\"><select class=\"txt\" name=\"ttl\" style=\"max-width:8rem\"><option value=\"3600\">1 hour</option><option value=\"86400\">1 day</option><option value=\"259200\" selected>3 days</option><option value=\"2592000\">30 days</option></select><input class=\"txt num\" type=\"number\" name=\"max\" min=\"0\" value=\"0\" title=\"uses, 0 = unlimited\" style=\"width:5rem;flex:0 0 auto\"><input class=\"txt\" name=\"note\" placeholder=\"note\"><button class=\"ib pri\" title=\"New invite\" aria-label=\"New invite\"><svg viewBox=\"0 0 24 24\"><path d=\"M12 5v14M5 12h14\"/></svg></button></form>\n <ul id=\"invites\" class=\"plain\"></ul>\n </div>\n <div>\n <h3>Bans</h3>\n <form id=\"addban\" class=\"addrow\"><input class=\"txt mono\" name=\"pubkey\" placeholder=\"hex pubkey or npub\" required><input class=\"txt\" name=\"reason\" placeholder=\"reason\" style=\"max-width:8rem\"><button class=\"ib danger\" title=\"Ban\" aria-label=\"Ban\"><svg viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"8.5\"/><path d=\"M6 6l12 12\"/></svg></button></form>\n <ul id=\"bans\" class=\"plain\"></ul>\n <h3>Blocked addresses</h3>\n <form id=\"addblock\" class=\"addrow\"><input class=\"txt mono\" name=\"ip\" placeholder=\"IP address\" required><input class=\"txt\" name=\"reason\" placeholder=\"reason\" style=\"max-width:8rem\"><button class=\"ib danger\" title=\"Block\" aria-label=\"Block\"><svg viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"8.5\"/><path d=\"M6 6l12 12\"/></svg></button></form>\n <ul id=\"blocks\" class=\"plain\"></ul>\n <ul id=\"banned-events\" class=\"plain\"></ul>\n </div>\n </div>\n <div class=\"block\">\n <h3>Reports <span class=\"count\" id=\"reports-count\"></span></h3>\n <p class=\"note\">Reports never show in the feed. Banning also deletes the reported event.</p>\n <div class=\"scroll\"><table id=\"reports\" class=\"events\"><thead><tr><th>Time</th><th>Type</th><th>About</th><th>Reason</th><th></th></tr></thead><tbody></tbody></table></div>\n </div>\n </section>\n\n <section class=\"panel\" data-panel=\"rules\">\n <h2>Rules</h2>\n <p class=\"lead\">Bans apply regardless of these.</p>\n <div class=\"block\">\n <h3>Presets</h3>\n <p class=\"note\">One click sets writes, reads, the directory, kinds and keep-for rules together; limits, identity and people stay. Names are cheap: one name per role is how to get an outbox, an inbox, a private relay and a chat without a config file.</p>\n <div id=\"presets\"></div>\n <p class=\"note\" id=\"presetnote\"></p>\n </div>\n <form id=\"access\">\n <div class=\"two\">\n <div><h3>Writes</h3><div class=\"choices\">\n <label><input type=\"radio\" name=\"writes\" value=\"open\"><b>Anyone</b><small>Every valid event is accepted, unless banned.</small></label>\n <label><input type=\"radio\" name=\"writes\" value=\"allowlist\"><b>Members</b><small>Only people on the member list, and you.</small></label>\n <label><input type=\"radio\" name=\"writes\" value=\"owner\"><b>Only me</b><small>Nobody else can post.</small></label>\n </div></div>\n <div><h3>Reads</h3><div class=\"choices\">\n <label><input type=\"radio\" name=\"reads\" value=\"open\"><b>Anyone</b><small>DMs and gift wraps still go only to their parties.</small></label>\n <label><input type=\"radio\" name=\"reads\" value=\"auth\"><b>Authenticated only</b><small>Every query needs a NIP-42 sign-in first.</small></label>\n <label><input type=\"radio\" name=\"reads\" value=\"members\"><b>Members only</b><small>Removing a member ends their subscriptions.</small></label>\n </div></div>\n </div>\n <div class=\"limits\">\n <label><span>Minimum proof of work <small>bits</small></span><input class=\"txt num\" type=\"number\" name=\"minPow\" min=\"0\" max=\"64\"></label>\n <label><span>Reject events dated in the future by more than <small>seconds</small></span><input class=\"txt num\" type=\"number\" name=\"maxFuture\" min=\"0\"></label>\n <label><span>Max events per query</span><input class=\"txt num\" type=\"number\" name=\"maxLimit\" min=\"1\" max=\"5000\"></label>\n <label><span>Max subscriptions per connection</span><input class=\"txt num\" type=\"number\" name=\"maxSubs\" min=\"1\" max=\"200\"></label>\n <label><span>Events per minute <small>per connection</small></span><input class=\"txt num\" type=\"number\" name=\"eventsPerMinute\" min=\"1\"></label>\n <label><span>Queries per minute <small>per connection</small></span><input class=\"txt num\" type=\"number\" name=\"reqsPerMinute\" min=\"1\"></label>\n <label><span>Max upload size <small>MB</small></span><input class=\"txt num\" type=\"number\" name=\"maxBlobMB\" min=\"1\" max=\"95\"></label>\n </div>\n <div class=\"actions\"><button class=\"btn pri\">Save rules</button></div>\n </form>\n <div class=\"block\">\n <h3>Kinds</h3>\n <p class=\"note\">An empty allow list means every kind. Blocks always win.</p>\n <form id=\"kindform\" class=\"addrow\"><input class=\"txt num\" type=\"number\" name=\"kind\" min=\"0\" max=\"65535\" placeholder=\"kind\" required style=\"width:7rem;flex:0 0 auto\"><button class=\"ib\" name=\"rule\" value=\"allow\" title=\"Allow kind\" aria-label=\"Allow kind\"><svg viewBox=\"0 0 24 24\"><path d=\"M5 12l5 5L20 7\"/></svg></button><button class=\"ib danger\" name=\"rule\" value=\"block\" title=\"Block kind\" aria-label=\"Block kind\"><svg viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"8.5\"/><path d=\"M6 6l12 12\"/></svg></button></form>\n <p class=\"kindline\">Allowed: <span id=\"kinds-allow\"></span></p>\n <p class=\"kindline\">Blocked: <span id=\"kinds-block\"></span></p>\n </div>\n </section>\n\n <section class=\"panel\" data-panel=\"identity\">\n <h2>Identity</h2>\n <form id=\"identity\">\n <div class=\"form\">\n <label><span>Name</span><input class=\"txt\" name=\"name\" maxlength=\"200\"></label>\n <label><span>Contact</span><input class=\"txt\" name=\"contact\" placeholder=\"mailto:you@example.com\" maxlength=\"500\"></label>\n <label class=\"wide\"><span>Description</span><textarea name=\"description\" maxlength=\"2000\"></textarea></label>\n <label class=\"wide\"><span>Icon URL</span><input class=\"txt\" name=\"icon\" placeholder=\"https://\u2026/icon.png\" maxlength=\"2000\"></label>\n <label class=\"wide\"><span>Banner URL</span><input class=\"txt\" name=\"banner\" placeholder=\"https://\u2026/banner.png\" maxlength=\"2000\"></label>\n <label><span>Posting policy URL</span><input class=\"txt\" name=\"postingPolicy\" placeholder=\"https://\u2026/posting\" maxlength=\"2000\"></label>\n <label><span>Privacy policy URL</span><input class=\"txt\" name=\"privacyPolicy\" placeholder=\"https://\u2026/privacy\" maxlength=\"2000\"></label>\n <label><span>Tags</span><input class=\"txt\" name=\"tags\" placeholder=\"bitcoin, cats\"></label>\n <label><span>Languages</span><input class=\"txt\" name=\"languageTags\" placeholder=\"en, pt-BR\"></label>\n <label><span>Countries</span><input class=\"txt\" name=\"relayCountries\" placeholder=\"US, DE\"></label>\n <label class=\"wide\"><span>Shown before someone accepts an invite</span><textarea name=\"joinTerms\" placeholder=\"House rules, expectations, anything a newcomer should agree to.\" maxlength=\"20000\"></textarea></label>\n <label class=\"wide switch\"><input type=\"checkbox\" name=\"directoryPublic\"> Show the people directory to visitors</label>\n </div>\n <div class=\"actions\"><button class=\"btn pri\">Save identity</button></div>\n </form>\n <div class=\"block\">\n <h3>Tell your clients</h3>\n <p class=\"note\">Each row fetches your current list from this relay and a few well-known indexers, puts this relay first, and asks for one signature. The merged list then goes here, to the relays it names, and to the indexers, so nothing you had listed is lost.</p>\n <div class=\"scroll\"><table id=\"wire\" class=\"events\"><thead><tr><th>List</th><th>State</th><th>Sent to</th><th></th></tr></thead><tbody></tbody></table></div>\n </div>\n <div class=\"block\">\n <h3>Configuration</h3>\n <p class=\"note\">Rules, identity, members, bans and kind rules as a file. Importing replaces those lists; it never touches events, files, or the owner.</p>\n <div class=\"row\"><button id=\"exportcfg\" class=\"btn\">Export configuration</button><button id=\"importcfg\" class=\"btn\">Import configuration</button><input type=\"file\" id=\"cfgfile\" accept=\"application/json,.json\" class=\"hidden\"></div>\n </div>\n <div class=\"block danger-zone\">\n <h3>Transfer ownership</h3>\n <p class=\"note\">Hands the relay to a member. You stay on as a moderator. The relay's key, events, files and fuel do not change. There is no undo.</p>\n <form id=\"transfer\" class=\"addrow\"><select class=\"txt mono\" name=\"pubkey\" required></select><button class=\"btn danger\">Transfer</button></form>\n </div>\n <div class=\"block danger-zone\">\n <h3>Delete this relay</h3>\n <p class=\"note\">Deletes every event, file, member, invite and setting, closes every connection, and returns the name to unclaimed for anyone to take. There is no undo.</p>\n <button id=\"deleterelay\" class=\"btn danger\">Delete this relay</button>\n </div>\n </section>\n\n <section class=\"panel\" data-panel=\"storage\">\n <h2>Storage</h2>\n <p class=\"lead\">Keep-for rules run once a day. Purges happen now and cannot be undone.</p>\n <div class=\"kbar\" id=\"s-bar\"></div><span class=\"legend slegend\" id=\"s-legend\"></span>\n <div class=\"usage\" id=\"s-totals\"></div>\n <div class=\"block\">\n <h3>By kind</h3>\n <div class=\"scroll\"><table id=\"kinds\" class=\"events\"><thead><tr><th>Kind</th><th class=\"r\">Count</th><th class=\"r\">Size</th><th>Oldest</th><th>Keep for</th><th></th></tr></thead><tbody></tbody></table></div>\n </div>\n <div class=\"block\">\n <h3>Files <span class=\"count\" id=\"blobs-count\"></span></h3>\n <div class=\"scroll\"><table id=\"blobs\" class=\"events\"><thead><tr><th>Time</th><th>File</th><th>Size</th><th>Uploader</th><th></th></tr></thead><tbody></tbody></table></div>\n </div>\n <div class=\"block\">\n <h3>Pull from a relay</h3>\n <p class=\"note\">Copies what the other relay shows to anyone and this one lacks, by NIP-77 sync, so running it again only fetches what is new. Files come along from relays on this host. Bans and kind rules apply; the write rule does not.</p>\n <form id=\"pullform\" class=\"addrow\"><input class=\"txt mono\" name=\"url\" placeholder=\"wss://other.relay\" required><button class=\"btn\">Pull</button></form>\n <p class=\"note\" id=\"pullnote\"></p>\n </div>\n <details class=\"disclosure block\"><summary>Browse recent events</summary>\n <p class=\"note\">Delete removes one thing. Ban also refuses it forever.</p>\n <div class=\"scroll\"><table id=\"events\" class=\"events\"><thead><tr><th>Time</th><th>Kind</th><th>Author</th><th>Content</th><th></th></tr></thead><tbody></tbody></table></div>\n <div class=\"actions left\"><button id=\"more\" class=\"btn\">Load more</button></div>\n </details>\n </section>\n\n <section class=\"panel\" data-panel=\"health\">\n <h2>Health</h2>\n <div class=\"counters\">\n <div class=\"counter\"><b class=\"big\" id=\"h-last\">\u2013</b><small>since last event</small><span class=\"sub\" id=\"h-last-sub\"></span></div>\n <div class=\"counter\"><b class=\"big\" id=\"h-conns\">\u2013</b><small>connected now</small><span class=\"sub\">websockets open</span></div>\n <div class=\"counter\"><b class=\"big\" id=\"h-fuel\">\u2013</b><small id=\"h-fuel-label\">fuel</small><span class=\"sub\" id=\"h-fuel-sub\"></span></div>\n <div class=\"counter\"><div class=\"kbar\" id=\"h-kinds\"></div><small>used for, last 30 days</small><span class=\"sub legend\" id=\"h-kinds-legend\"></span></div>\n </div>\n <div class=\"usage\" id=\"usage\"></div>\n <div class=\"block\">\n <h3>Zaps received</h3>\n <div class=\"scroll\"><table id=\"credits\"><thead><tr><th>When</th><th>From</th><th class=\"r\">Sats</th></tr></thead><tbody></tbody></table></div>\n </div>\n </section>\n </div>\n\n <footer class=\"pg\"><p>a bind.ws relay</p><a href=\"https://github.com/FelineStateMachine/bindws\">source</a></footer>\n</main>\n";2222 23-const JS = "(async () => {\n const $ = (s) => document.querySelector(s);\n const $$ = (s) => document.querySelectorAll(s);\n const host = location.host;\n const wsURL = (location.protocol === \"https:\" ? \"wss://\" : \"ws://\") + host;\n const rpcURL = location.origin + \"/\";\n let info = null, me = null, owner = \"\", policy = null, fuel = null, people = null, myRole = \"\";\n\n const toast = (msg) => { const t = $(\"#toast\"); t.textContent = msg; t.classList.add(\"show\"); clearTimeout(t._t); t._t = setTimeout(() => t.classList.remove(\"show\"), 2600); };\n const short = (hex) => hex ? hex.slice(0, 8) + \"\u2026\" + hex.slice(-4) : \"\";\n const hue = (hex) => (parseInt(hex.slice(0, 2), 16) * 360 / 256).toFixed(0) + \"deg\";\n const av = (hex) => '<i class=\"av\" style=\"--h:' + hue(hex) + '\"></i>';\n const fmtBytes = (n) => n < 1e6 ? (n / 1e3).toFixed(0) + \" KB\" : n < 1e9 ? (n / 1e6).toFixed(1) + \" MB\" : (n / 1e9).toFixed(2) + \" GB\";\n const fmtHours = (ms) => ms < 3600e3 ? Math.round(ms / 60e3) + \" min\" : (ms / 3600e3).toFixed(ms < 36e6 ? 1 : 0) + \" h\";\n const fuelOver = () => !!fuel && (fuel.eventBytes > fuel.freeEventBytes || fuel.mediaBytes > fuel.freeMediaBytes || fuel.activeMs > fuel.freeActiveMs || fuel.rowsWritten > fuel.freeRowsWritten);\n const fmtTime = (t) => t ? new Date(t * 1000).toLocaleString(undefined, { month: \"short\", day: \"numeric\", hour: \"2-digit\", minute: \"2-digit\" }) : \"\u2013\";\n const fmtDay = (t) => t ? new Date(t * 1000).toLocaleDateString(undefined, { month: \"short\", day: \"numeric\" }) : \"\u2013\";\n const ago = (t) => { const s = Math.max(0, Math.floor(Date.now() / 1000) - t); return s < 60 ? \"just now\" : s < 3600 ? Math.floor(s / 60) + \" min\" : s < 86400 ? Math.floor(s / 3600) + \" h\" : Math.floor(s / 86400) + \" d\"; };\n const esc = (s) => String(s ?? \"\").replace(/[&<>\"']/g, (c) => ({ \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \""\", \"'\": \"'\" })[c]);\n\n const IC = {\n x: '<svg viewBox=\"0 0 24 24\"><path d=\"M6 6l12 12M18 6L6 18\"/></svg>',\n undo: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 10h10a5 5 0 0 1 0 10h-3M4 10l4-4M4 10l4 4\"/></svg>',\n check: '<svg viewBox=\"0 0 24 24\"><path d=\"M5 12l5 5L20 7\"/></svg>',\n trash: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 7h16M6 7l1.2 13h9.6L18 7\"/></svg>',\n ban: '<svg viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"8.5\"/><path d=\"M6 6l12 12\"/></svg>',\n banuser: '<svg viewBox=\"0 0 24 24\"><circle cx=\"10\" cy=\"8\" r=\"4\"/><path d=\"M3 21a7 7 0 0 1 11-5.7M15 15l6 6M21 15l-6 6\"/></svg>',\n eye: '<svg viewBox=\"0 0 24 24\"><path d=\"M2 12s4-6 10-6 10 6 10 6-4 6-10 6S2 12 2 12z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></svg>',\n lock: '<svg viewBox=\"0 0 24 24\"><rect x=\"5\" y=\"10\" width=\"14\" height=\"10\" rx=\"2\"/><path d=\"M8 10V7a4 4 0 0 1 8 0v3\"/></svg>',\n pen: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 20l4-1 11-11-3-3L5 16l-1 4zM13 8l3 3\"/></svg>',\n people: '<svg viewBox=\"0 0 24 24\"><circle cx=\"9\" cy=\"8\" r=\"3.5\"/><circle cx=\"17\" cy=\"9\" r=\"2.5\"/><path d=\"M3 20a6 6 0 0 1 12 0M15 20a4.5 4.5 0 0 1 7-3.5\"/></svg>',\n person: '<svg viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"8\" r=\"4\"/><path d=\"M4 21a8 8 0 0 1 16 0\"/></svg>',\n gauge: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 16a8 8 0 0 1 16 0M12 16l4-5\"/><circle cx=\"12\" cy=\"16\" r=\"1.2\"/></svg>',\n bolt: '<svg viewBox=\"0 0 24 24\"><path d=\"M13 3L5 14h6l-1 7 8-11h-6z\"/></svg>',\n copy: '<svg viewBox=\"0 0 24 24\"><rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\"/><path d=\"M5 15V6a2 2 0 0 1 2-2h9\"/></svg>',\n };\n const ib = (icon, label, act, id, extra) => '<button class=\"ib' + (extra ? \" \" + extra : \"\") + '\" title=\"' + label + '\" aria-label=\"' + label + '\" data-act=\"' + act + '\" data-id=\"' + esc(id) + '\">' + IC[icon] + \"</button>\";\n\n const CH = \"qpzry9x8gf2tvdw0s3jn54khce6mua7l\";\n function npubToHex(s) {\n s = s.trim().toLowerCase();\n if (/^[0-9a-f]{64}$/.test(s)) return s;\n if (!s.startsWith(\"npub1\")) return null;\n const data = s.slice(5, -6).split(\"\").map((c) => CH.indexOf(c));\n if (data.some((d) => d < 0)) return null;\n let bits = 0, acc = 0, out = [];\n for (const d of data) { acc = (acc << 5) | d; bits += 5; if (bits >= 8) { bits -= 8; out.push((acc >> bits) & 255); } }\n return out.length === 32 ? out.map((b) => b.toString(16).padStart(2, \"0\")).join(\"\") : null;\n }\n\n async function sha256hex(s) {\n const b = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(s));\n return [...new Uint8Array(b)].map((x) => x.toString(16).padStart(2, \"0\")).join(\"\");\n }\n // ---- signing: a NIP-07 extension, or a NIP-46 remote signer ----\n // The remote path loads the bundled library on first use; its session\n // (client key, signer pubkey, relays) lives in localStorage until sign-out.\n let remote = null, lib = null, pendingNote = null;\n const NO_SIGNER = \"Install a nostr extension (Alby, nos2x, \u2026) or connect a remote signer.\";\n const withTimeout = (p, ms, what) => Promise.race([p, new Promise((_, rej) => setTimeout(() => rej(new Error(what + \" did not answer; is the signer app open?\")), ms))]);\n const signer = {\n ready: () => !!remote || !!window.nostr,\n async getPublicKey() { if (remote) return withTimeout(remote.getPublicKey(), 20000, \"The remote signer\"); if (window.nostr) return window.nostr.getPublicKey(); throw new Error(NO_SIGNER); },\n async signEvent(ev) { if (remote) return withTimeout(remote.signEvent(ev), 60000, \"The remote signer\"); if (window.nostr) return window.nostr.signEvent(ev); throw new Error(NO_SIGNER); },\n };\n async function signerLib() {\n if (lib) return lib;\n if (!window.NostrSigner) await new Promise((res, rej) => { const s = document.createElement(\"script\"); s.src = window.SIGNER_URL || \"/signer.js\"; s.onload = res; s.onerror = () => rej(new Error(\"Could not load the signer library.\")); document.head.appendChild(s); });\n return (lib = window.NostrSigner);\n }\n const onauth = (u) => window.open(u, \"_blank\");\n const saveSession = (sk, s, secret) => localStorage.setItem(\"nip46\", JSON.stringify({ sk: lib.bytesToHex(sk), pubkey: s.bp.pubkey, relays: s.bp.relays, secret: secret || null }));\n async function resumeRemote() {\n const raw = localStorage.getItem(\"nip46\");\n if (!raw) return false;\n try {\n const s = JSON.parse(raw); const L = await signerLib();\n remote = L.BunkerSigner.fromBunker(L.hexToBytes(s.sk), { pubkey: s.pubkey, relays: s.relays, secret: s.secret }, { onauth });\n return true;\n } catch { localStorage.removeItem(\"nip46\"); return false; }\n }\n async function connectBunker(input) {\n const L = await signerLib();\n const bp = await L.parseBunkerInput(input.trim());\n if (!bp) throw new Error(\"That is not a bunker:// URL.\");\n if (!bp.relays.length) throw new Error(\"The bunker URL names no relay.\");\n const sk = L.generateSecretKey();\n const s = L.BunkerSigner.fromBunker(sk, bp, { onauth });\n await withTimeout(s.connect({ name: host, url: location.origin }), 60000, \"The signer\");\n await Promise.race([s.switchRelays(), new Promise((r) => setTimeout(r, 3000))]);\n remote = s; saveSession(sk, s, bp.secret);\n }\n // The nostrconnect:// flow: this relay carries the traffic, so no third relay is involved.\n let ncPending = null;\n async function offerNostrConnect() {\n if (ncPending) return ncPending;\n const L = await signerLib();\n const sk = L.generateSecretKey();\n const secret = L.bytesToHex(crypto.getRandomValues(new Uint8Array(8)));\n const uri = L.createNostrConnectURI({ clientPubkey: L.getPublicKey(sk), relays: [wsURL], secret, name: host, url: location.origin, perms: [\"sign_event:27235\", \"sign_event:9734\"] });\n $(\"#nclink\").href = uri; $(\"#ncnote\").textContent = \"\";\n ncPending = L.BunkerSigner.fromURI(sk, uri, { onauth }, 600000).then((s) => { remote = s; saveSession(sk, s, secret); ncPending = null; return s; }, (e) => { ncPending = null; throw e; });\n return ncPending;\n }\n async function remoteDone() {\n me = await signer.getPublicKey();\n localStorage.setItem(\"me\", me);\n $(\"#remotesec\").classList.add(\"hidden\");\n toast(\"Remote signer connected\");\n if (pendingNote && !owner) { const n = pendingNote; pendingNote = null; await claimNow(n); return; }\n pendingNote = null;\n renderHeader(); await loadAdmin(); await loadPeople();\n }\n function showRemote(note) {\n pendingNote = note || null;\n $(\"#remotesec\").classList.remove(\"hidden\");\n $(\"#remotesec\").scrollIntoView({ behavior: \"smooth\", block: \"start\" });\n offerNostrConnect().then(remoteDone).catch((e) => { $(\"#ncnote\").textContent = e.message; });\n }\n\n async function rpc(method, ...params) {\n if (!signer.ready()) throw new Error(NO_SIGNER);\n const body = JSON.stringify({ method, params });\n const ev = await signer.signEvent({ kind: 27235, created_at: Math.floor(Date.now() / 1000), content: \"\", tags: [[\"u\", rpcURL], [\"method\", \"POST\"], [\"payload\", await sha256hex(body)]] });\n const resp = await fetch(rpcURL, { method: \"POST\", headers: { \"content-type\": \"application/nostr+json+rpc\", authorization: \"Nostr \" + btoa(JSON.stringify(ev)) }, body });\n const json = await resp.json();\n if (json.error) throw new Error(json.error);\n return json.result;\n }\n\n // ---- front of house ----\n function renderHeader() {\n const name = (info && info.name) || host.split(\".\")[0];\n $(\"#title\").textContent = name;\n document.title = name + \" - relay\";\n $(\"#url\").textContent = wsURL;\n $(\"#d-ws\").textContent = wsURL; $(\"#d-blossom\").textContent = location.origin; $(\"#d-nip05\").textContent = \"you@\" + host;\n $(\"#nip05-example\").textContent = \"alice@\" + host;\n const desc = (info && info.description) || \"\";\n $(\"#desc\").textContent = desc; $(\"#desc\").classList.toggle(\"hidden\", !desc);\n owner = (info && info.pubkey) || \"\";\n const isOwner = !!me && me === owner;\n const lease = !owner && info && info.lease ? info.lease : null;\n $(\"#ownerline\").innerHTML = owner ? 'run by <b class=\"mono\" title=\"' + owner + '\">' + short(owner) + \"</b>\" : lease ? \"temporary until \" + fmtDay(lease.expires_at) : \"unclaimed\";\n $(\"#owner-av\").classList.toggle(\"hidden\", !owner);\n if (owner) $(\"#owner-av\").style.setProperty(\"--h\", hue(owner));\n $(\"#who\").textContent = me ? (isOwner ? \"(that's you)\" : \"\") : \"\";\n $(\"#unclaimed\").classList.toggle(\"hidden\", !!owner || !!lease);\n $(\"#leased\").classList.toggle(\"hidden\", !lease);\n if (lease) {\n $(\"#lease-until\").textContent = fmtTime(lease.expires_at);\n $(\"#leasenote\").textContent = lease.holder ? \"Reserved for the key that asked for it; sign with that key.\" : \"\";\n }\n $(\"#signin\").classList.toggle(\"hidden\", !!me || !owner);\n $(\"#signin46\").classList.toggle(\"hidden\", !!me || !owner);\n $(\"#signout\").classList.toggle(\"hidden\", !me);\n $(\"#about\").innerHTML = [\n [\"Name\", info?.name], [\"Description\", info?.description], [\"Contact\", info?.contact], [\"Owner\", owner ? short(owner) : \"\"], [\"Relay key\", info?.self ? short(info.self) : \"\"], [\"Software\", info?.software], [\"Version\", info?.version],\n [\"Max query\", info?.limitation?.max_limit], [\"Auth required\", String(!!info?.limitation?.auth_required)], [\"Restricted writes\", String(!!info?.limitation?.restricted_writes)], [\"Min PoW\", info?.limitation?.min_pow_difficulty || 0],\n [\"NIPs\", info?.supported_nips?.join(\" \")],\n ].map(([k, v]) => \"<div><small>\" + k + \"</small><div\" + ([\"Owner\", \"NIPs\", \"Relay key\"].includes(k) ? ' class=\"mono\"' : \"\") + \">\" + (v === undefined || v === \"\" ? '<span class=\"muted\">\u2013</span>' : esc(v)) + \"</div></div>\").join(\"\");\n renderCare();\n }\n\n function renderCare() {\n const g = (icon, label, off) => '<span class=\"g' + (off ? \" off\" : \"\") + '\" title=\"' + label + '\"><i>' + IC[icon] + \"</i><small>\" + label + \"</small></span>\";\n const writes = policy ? policy.writes : (info?.limitation?.restricted_writes ? \"allowlist\" : \"open\");\n const reads = policy ? policy.reads : (info?.limitation?.auth_required ? \"auth\" : \"open\");\n let out = writes === \"owner\" ? g(\"person\", \"only owner writes\") : writes === \"allowlist\" ? g(\"people\", \"members write\") : g(\"pen\", \"anyone writes\");\n out += reads === \"members\" ? g(\"people\", \"members read\") : reads === \"auth\" ? g(\"lock\", \"sign in to read\") : g(\"eye\", \"anyone reads\");\n if (fuel) {\n const over = fuelOver();\n out += fuel.outOfFuel ? g(\"gauge\", \"out of fuel\", true) : over ? g(\"bolt\", \"burning sats\") : g(\"gauge\", \"on free allowance\");\n }\n $(\"#care\").innerHTML = out;\n }\n\n async function loadPeople() {\n try { people = await (await fetch(\"/people\")).json(); } catch { return; }\n const isOwner = !!me && me === owner;\n let list = people.people || [];\n if (!people.public && isOwner && policy) list = (window.__members || []);\n $(\"#peoplesec\").classList.toggle(\"hidden\", !owner || list.length === 0);\n $(\"#people-note\").classList.toggle(\"hidden\", people.public);\n $(\"#people-count\").textContent = list.length || \"\";\n $(\"#dir\").innerHTML = list.map((m) => '<span class=\"who' + (m.pubkey === me ? \" me\" : \"\") + '\" title=\"' + esc(m.pubkey) + '\">' + av(m.pubkey) + '<span class=\"mono\">' + (m.name ? esc(m.name) + \"@\" + host : short(m.pubkey)) + \"</span>\" + (m.role === \"owner\" ? '<span class=\"role\">owner</span>' : \"\") + \"</span>\").join(\"\");\n }\n\n async function loadInfo() {\n info = await (await fetch(\"/\", { headers: { accept: \"application/nostr+json\" } })).json();\n renderHeader();\n }\n\n async function loadFuel() {\n try { fuel = await (await fetch(\"/fuel\")).json(); } catch { return; }\n $(\"#fuelsec\").classList.toggle(\"hidden\", !owner);\n const pct = (used, free) => Math.min(100, Math.round((used / Math.max(free, 1)) * 100));\n const gauge = (id, used, free, text) => { const g = $(\"#g-\" + id); g.style.width = pct(used, free) + \"%\"; g.classList.toggle(\"over\", used > free); $(\"#t-\" + id).textContent = text; };\n gauge(\"events\", fuel.eventBytes, fuel.freeEventBytes, fmtBytes(fuel.eventBytes) + \" of \" + fmtBytes(fuel.freeEventBytes) + \" free\");\n gauge(\"media\", fuel.mediaBytes, fuel.freeMediaBytes, fmtBytes(fuel.mediaBytes) + \" of \" + fmtBytes(fuel.freeMediaBytes) + \" free\");\n gauge(\"active\", fuel.activeMs, fuel.freeActiveMs, fmtHours(fuel.activeMs) + \" of \" + fmtHours(fuel.freeActiveMs) + \" free\");\n gauge(\"rows\", fuel.rowsWritten, fuel.freeRowsWritten, fuel.rowsWritten.toLocaleString() + \" of \" + fuel.freeRowsWritten.toLocaleString() + \" free\");\n const sats = Math.floor(fuel.balanceMsats / 1000);\n const r = fuel.rates;\n $(\"#fuel-balance\").innerHTML = fuel.outOfFuel\n ? '<span class=\"chip\">out of fuel</span> Writes are paused until someone tops up.'\n : \"Balance <b>\" + sats.toLocaleString() + \" sats</b>. Past the allowances, prices track what the hosting costs: \" + r.satsPerGBMonthEvents.toLocaleString() + \" sats per GB-month of events, \" + r.satsPerGBMonthMedia.toLocaleString() + \" per GB-month of files, \" + r.satsPerActiveHour.toLocaleString() + \" per hour awake, \" + r.satsPerMillionRows.toLocaleString() + \" per million rows written. Traffic is free.\";\n $(\"#topup\").classList.toggle(\"hidden\", !fuel.enabled);\n $(\"#credits tbody\").innerHTML = fuel.credits.length ? fuel.credits.map((c) => '<tr><td class=\"dim\">' + esc(fmtTime(c.at)) + '</td><td class=\"mono\" title=\"' + esc(c.payer) + '\">' + av(c.payer) + short(c.payer) + '</td><td class=\"mono r\">' + Math.floor(c.msats / 1000).toLocaleString() + \"</td></tr>\").join(\"\") : '<tr><td colspan=\"3\" class=\"muted\">no zaps yet</td></tr>';\n if (!fuel.enabled) $(\"#topup-note\").textContent = \"Top-ups are not enabled on this service yet.\";\n renderCare();\n renderFuelTile();\n }\n\n async function topUp(sats) {\n if (!signer.ready()) throw new Error(NO_SIGNER);\n const msats = Math.round(sats * 1000);\n const zapRequest = await signer.signEvent({ kind: 9734, created_at: Math.floor(Date.now() / 1000), content: \"fuel for \" + host, tags: [[\"p\", fuel.servicePubkey], [\"amount\", String(msats)], [\"relays\", wsURL]] });\n const r = await (await fetch(\"/fuel/invoice\", { method: \"POST\", body: JSON.stringify({ zapRequest }) })).json();\n if (r.error) throw new Error(r.error);\n const inv = r.invoice;\n $(\"#invoice\").classList.remove(\"hidden\");\n $(\"#inv-text\").value = inv; $(\"#inv-link\").href = \"lightning:\" + inv; $(\"#inv-state\").textContent = \"waiting for payment\u2026\";\n if (window.webln) { try { await window.webln.enable(); await window.webln.sendPayment(inv); } catch (e) { toast(e.message || \"wallet declined\"); } }\n const before = fuel.creditedMsats;\n for (let i = 0; i < 120; i++) {\n await new Promise((r) => setTimeout(r, 2500));\n await loadFuel();\n if (fuel.creditedMsats > before) { $(\"#inv-state\").textContent = \"paid, thank you\"; toast(\"Fuel credited\"); return; }\n }\n $(\"#inv-state\").textContent = \"no receipt yet; it can take a minute after paying\";\n }\n\n // ---- console ----\n function showTab(name) {\n if (name === \"content\") name = \"storage\";\n const known = myRole === \"moderator\" ? [\"people\", \"storage\"] : [\"people\", \"rules\", \"identity\", \"storage\", \"health\"];\n if (!known.includes(name)) name = \"people\";\n $$(\".tabs a\").forEach((a) => a.classList.toggle(\"on\", a.dataset.tab === name));\n $$(\".panel\").forEach((p) => p.classList.toggle(\"on\", p.dataset.panel === name));\n }\n // Tabs switch in place. A fragment still opens a tab when someone arrives with one,\n // but clicking never writes one, and any fragment already there is dropped.\n $$(\".tabs a\").forEach((a) => a.addEventListener(\"click\", (ev) => { ev.preventDefault(); showTab(a.dataset.tab); if (location.hash) history.replaceState(null, \"\", location.pathname + location.search); }));\n window.addEventListener(\"hashchange\", () => showTab(location.hash.slice(1)));\n\n function renderFuelTile() {\n if (!fuel) return;\n const over = fuelOver();\n const sats = Math.max(0, Math.floor(fuel.balanceMsats / 1000));\n const d = new Date(), end = Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + 1, 1), days = Math.max(1, Math.ceil((end - Date.now()) / 86400000));\n if (fuel.outOfFuel) { $(\"#h-fuel\").textContent = \"0 sats\"; $(\"#h-fuel-label\").textContent = \"out of fuel\"; $(\"#h-fuel-sub\").textContent = \"writes are paused\"; }\n else if (over) { $(\"#h-fuel\").textContent = sats.toLocaleString() + \" sats\"; $(\"#h-fuel-label\").textContent = \"fuel left\"; $(\"#h-fuel-sub\").textContent = \"past the free allowance\"; }\n else { $(\"#h-fuel\").textContent = days + (days === 1 ? \" day\" : \" days\"); $(\"#h-fuel-label\").textContent = \"free allowance left\"; $(\"#h-fuel-sub\").textContent = sats ? \"then \" + sats.toLocaleString() + \" sats\" : \"then a top-up is needed\"; }\n $(\"#usage\").innerHTML = [[\"events stored\", fmtBytes(fuel.eventBytes)], [\"files stored\", fmtBytes(fuel.mediaBytes)], [\"awake this month\", fmtHours(fuel.activeMs)], [\"rows written\", fuel.rowsWritten.toLocaleString()], [\"rows read\", fuel.rowsRead.toLocaleString()], [\"received this month\", fmtBytes(fuel.bytesIn)], [\"served this month\", fmtBytes(fuel.bytesOut)], [\"charged\", Math.floor(fuel.chargedMsats / 1000).toLocaleString() + \" sats\"]]\n .map(([k, v]) => \"<div><small>\" + k + \"</small><b>\" + v + \"</b></div>\").join(\"\");\n }\n\n function renderHealth(stats) {\n $(\"#h-last\").textContent = stats.newest ? ago(stats.newest) : \"none\";\n $(\"#h-last-sub\").textContent = stats.newest ? fmtTime(stats.newest) : \"no events yet\";\n $(\"#h-conns\").textContent = stats.connections;\n const buckets = [[\"notes\", \"k1\", [1]], [\"reactions\", \"k2\", [7]], [\"DMs\", \"k3\", [4, 14, 1059]], [\"long-form\", \"k4\", [30023]], [\"other\", \"k5\", null]];\n const sums = buckets.map(() => 0); let total = 0;\n for (const { kind, n } of stats.kinds || []) { total += n; const i = buckets.findIndex((b) => b[2] && b[2].includes(kind)); sums[i < 0 ? 4 : i] += n; }\n const parts = buckets.map((b, i) => [b[0], b[1], sums[i]]).filter((p) => p[2] > 0);\n $(\"#h-kinds\").innerHTML = total ? parts.map(([name, cls, n]) => '<i class=\"' + cls + '\" style=\"flex:' + n + '\" title=\"' + name + \" \" + Math.round(n * 100 / total) + '%\"></i>').join(\"\") : '<i class=\"k5\" style=\"flex:1\"></i>';\n $(\"#h-kinds-legend\").innerHTML = total ? parts.map(([name, cls, n]) => '<span><i class=\"' + cls + '\"></i>' + name + \" \" + Math.round(n * 100 / total) + \"%</span>\").join(\"\") : \"<span>no events yet</span>\";\n renderFuelTile();\n }\n\n async function loadAdmin() {\n const isOwner = !!me && me === owner;\n myRole = isOwner ? \"owner\" : \"\";\n let stats = null, p = null;\n // A signed-in member may be a moderator; the relay says by answering stats.\n if (me && owner && !isOwner) { try { [stats, p] = await Promise.all([rpc(\"stats\"), rpc(\"getpolicy\")]); myRole = \"moderator\"; } catch { myRole = \"\"; } }\n $(\"#console\").classList.toggle(\"hidden\", !myRole);\n $(\"#console\").classList.toggle(\"mod\", myRole === \"moderator\");\n if (!myRole) { policy = null; renderCare(); return; }\n if (isOwner) [stats, p] = await Promise.all([rpc(\"stats\"), rpc(\"getpolicy\")]);\n policy = p;\n renderHealth(stats);\n renderCare();\n const fa = $(\"#access\"), fi = $(\"#identity\");\n fa.writes.value = p.writes; fa.reads.value = p.reads; fa.minPow.value = p.minPow; fa.maxFuture.value = p.maxFuture; fa.maxLimit.value = p.maxLimit; fa.maxSubs.value = p.maxSubs;\n fa.eventsPerMinute.value = p.eventsPerMinute; fa.reqsPerMinute.value = p.reqsPerMinute; fa.maxBlobMB.value = p.maxBlobMB;\n fi.name.value = p.name; fi.contact.value = p.contact; fi.description.value = p.description; fi.icon.value = p.icon; fi.joinTerms.value = p.joinTerms; fi.directoryPublic.checked = !!p.directoryPublic;\n fi.banner.value = p.banner || \"\"; fi.postingPolicy.value = p.postingPolicy || \"\"; fi.privacyPolicy.value = p.privacyPolicy || \"\";\n fi.tags.value = (p.tags || []).join(\", \"); fi.languageTags.value = (p.languageTags || []).join(\", \"); fi.relayCountries.value = (p.relayCountries || []).join(\", \");\n showTab(location.hash.slice(1));\n await Promise.all([loadLists(), loadEvents(true), ...(isOwner ? [loadStorage()] : [])]);\n }\n\n async function loadLists() {\n const [mem, bans, bannedEvents, allow, block, invites, reports, blobs, blocks] = await Promise.all([rpc(\"listmembers\"), rpc(\"listbannedpubkeys\"), rpc(\"listbannedevents\"), rpc(\"listallowedkinds\"), rpc(\"listblockedkinds\"), rpc(\"listinvites\"), rpc(\"listreports\"), rpc(\"listblobs\", 100), rpc(\"listblockedips\")]);\n const members = mem.members;\n window.__members = members;\n const roleCell = (m) => m.role === \"owner\" ? ' <span class=\"chip\">owner</span>' : myRole === \"owner\" ? ' <select class=\"txt role\" title=\"Role\"><option value=\"member\"' + (m.role === \"member\" ? \" selected\" : \"\") + '>member</option><option value=\"moderator\"' + (m.role === \"moderator\" ? \" selected\" : \"\") + '>moderator</option></select>' : m.role === \"moderator\" ? ' <span class=\"chip\">moderator</span>' : \"\";\n const untouchable = (m) => m.role === \"owner\" || (myRole !== \"owner\" && m.role === \"moderator\");\n $(\"#members tbody\").innerHTML = members.map((m) => '<tr data-pk=\"' + m.pubkey + '\"><td class=\"mono\" title=\"' + esc(m.pubkey) + '\">' + av(m.pubkey) + short(m.pubkey) + roleCell(m) + '</td><td class=\"name\"><input class=\"txt name\" value=\"' + esc(m.name || \"\") + '\" placeholder=\"name\" pattern=\"[a-z0-9._-]{1,64}\"></td><td><input class=\"txt note\" value=\"' + esc(m.note || \"\") + '\" placeholder=\"note\"></td><td class=\"dim\">' + esc(fmtDay(m.joined_at)) + \", \" + esc(m.via) + '</td><td class=\"r\">' + ib(\"check\", \"Save\", \"savemember\", m.pubkey) + (untouchable(m) ? \"\" : ib(\"x\", \"Remove\", \"removemember\", m.pubkey) + ib(\"banuser\", \"Ban\", \"banpubkey\", m.pubkey, \"danger\")) + \"</td></tr>\").join(\"\");\n $(\"#transfer [name=pubkey]\").innerHTML = members.filter((m) => m.role !== \"owner\").map((m) => '<option value=\"' + m.pubkey + '\">' + esc(m.name ? m.name + \"@\" + host : short(m.pubkey)) + \"</option>\").join(\"\");\n $(\"#tc-people\").textContent = members.length;\n const link = (code) => location.origin + \"/invite/\" + code;\n $(\"#invites\").innerHTML = invites.length ? invites.map((i) => '<li><i class=\"ev\">inv</i><span><a class=\"mono\" href=\"/invite/' + i.code + '\">\u2026' + i.code.slice(-8) + \"</a> \" + esc(i.note || \"\") + ' <span class=\"dim\">' + i.uses + (i.max_uses ? \"/\" + i.max_uses : \"\") + \" used, until \" + esc(fmtDay(i.expires_at)) + \"</span></span><span>\" + ib(\"copy\", \"Copy link\", \"copy\", link(i.code)) + ib(\"x\", \"Revoke\", \"revokeinvite\", i.code) + \"</span></li>\").join(\"\") : '<li class=\"empty\">no invites</li>';\n const person = (r, icon, label, act) => \"<li>\" + av(r.pubkey) + '<span><b class=\"mono\" title=\"' + esc(r.pubkey) + '\">' + short(r.pubkey) + \"</b> \" + esc(r.reason || \"\") + \"</span>\" + ib(icon, label, act, r.pubkey) + \"</li>\";\n $(\"#bans\").innerHTML = bans.length ? bans.map((r) => person(r, \"undo\", \"Unban\", \"unrulepubkey\")).join(\"\") : '<li class=\"empty\">nobody banned</li>';\n $(\"#blocks\").innerHTML = blocks.length ? blocks.map((r) => '<li><i class=\"ev\">ip</i><span><b class=\"mono\">' + esc(r.ip) + \"</b> \" + esc(r.reason || \"\") + \"</span>\" + ib(\"undo\", \"Unblock\", \"unblockip\", r.ip) + \"</li>\").join(\"\") : '<li class=\"empty\">no addresses blocked</li>';\n $(\"#banned-events\").innerHTML = bannedEvents.map((r) => '<li><i class=\"ev\">ev</i><span><b class=\"mono\" title=\"' + esc(r.id) + '\">' + short(r.id) + \"</b> \" + esc(r.reason || \"\") + \"</span>\" + ib(\"check\", \"Allow again\", \"allowevent\", r.id) + \"</li>\").join(\"\");\n $(\"#reports tbody\").innerHTML = reports.length ? reports.map((r) => '<tr><td class=\"dim\">' + esc(fmtTime(r.at)) + '</td><td><span class=\"kind\">' + esc(r.type || \"report\") + '</span></td><td class=\"mono\" title=\"' + esc(r.target_pubkey) + '\">' + av(r.target_pubkey) + short(r.target_pubkey) + (r.target_event ? (r.blob ? ' <a class=\"dim\" href=\"/' + esc(r.target_event) + '\" target=\"_blank\" rel=\"noopener\">file ' + short(r.target_event) + \"</a>\" : ' <span class=\"dim\">ev ' + short(r.target_event) + \"</span>\") : \"\") + '</td><td class=\"c\">' + esc(r.content) + '</td><td class=\"r\">' + ib(\"check\", \"Dismiss\", \"resolve:dismiss\", r.id) + ib(\"trash\", r.blob ? \"Delete the file\" : \"Delete the event\", \"resolve:delete\", r.id) + ib(\"banuser\", \"Ban the author\", \"resolve:ban\", r.id, \"danger\") + \"</td></tr>\").join(\"\") : '<tr><td colspan=\"5\" class=\"muted\">nothing reported</td></tr>';\n $(\"#reports-count\").textContent = reports.length || \"\";\n $(\"#blobs tbody\").innerHTML = blobs.length ? blobs.map((b) => '<tr><td class=\"dim\">' + esc(fmtTime(b.uploaded)) + '</td><td class=\"mono\"><a href=\"' + esc(b.url) + '\" target=\"_blank\" rel=\"noopener\">' + short(b.sha256) + '</a> <span class=\"dim\">' + esc(b.type) + '</span></td><td class=\"mono\">' + fmtBytes(b.size) + '</td><td class=\"mono\" title=\"' + esc(b.uploader) + '\">' + av(b.uploader) + short(b.uploader) + '</td><td class=\"r\">' + ib(\"trash\", \"Delete file\", \"deleteblob\", b.sha256, \"danger\") + \"</td></tr>\").join(\"\") : '<tr><td colspan=\"5\" class=\"muted\">no uploads</td></tr>';\n $(\"#blobs-count\").textContent = blobs.length || \"\";\n const tag = (k, cls) => '<span class=\"tag ' + cls + '\">' + k + ib(\"x\", \"Remove rule\", \"unrulekind\", String(k)) + \"</span>\";\n $(\"#kinds-allow\").innerHTML = allow.length ? allow.map((k) => tag(k, \"ok\")).join(\"\") : '<span class=\"tag plain\">all</span>';\n $(\"#kinds-block\").innerHTML = block.length ? block.map((k) => tag(k, \"blk\")).join(\"\") : '<span class=\"tag plain\">none</span>';\n await loadPeople();\n }\n\n const KIND_NAMES = { 0: \"profiles\", 1: \"notes\", 3: \"contacts\", 4: \"DMs\", 5: \"deletions\", 6: \"reposts\", 7: \"reactions\", 16: \"reposts\", 1059: \"gift wraps\", 1063: \"file headers\", 1111: \"comments\", 1984: \"reports\", 9734: \"zap requests\", 9735: \"zap receipts\", 9802: \"highlights\", 10002: \"relay lists\", 13534: \"roster\", 9000: \"group adds\", 9001: \"group removals\", 9021: \"join requests\", 9022: \"leave requests\", 39000: \"group info\", 39001: \"group admins\", 39002: \"group members\", 39003: \"group roles\", 30023: \"articles\", 30024: \"drafts\", 30078: \"app data\", 30311: \"live events\", 30818: \"wiki\" };\n const kindName = (k) => KIND_NAMES[k] || (k >= 20000 && k < 30000 ? \"ephemeral\" : k >= 30000 && k < 40000 ? \"addressable\" : k >= 10000 && k < 20000 ? \"replaceable\" : \"kind \" + k);\n const SYS_KINDS = new Set([0, 3, 10002, 9735, 13534, 8000, 8001, 9000, 9001, 39000, 39001, 39002, 39003]);\n let storage = null;\n\n async function loadStorage() {\n storage = await rpc(\"storagestats\");\n pollPull();\n const st = storage;\n const top = st.kinds.slice(0, 4), rest = st.kinds.slice(4).reduce((a, k) => a + k.bytes, 0);\n const parts = [...top.map((k, i) => [k.kind + \" \" + kindName(k.kind), \"k\" + (i + 1), k.bytes]), ...(rest ? [[\"other\", \"k5\", rest]] : [])];\n const total = st.eventBytes || 1;\n $(\"#s-bar\").innerHTML = parts.length ? parts.map(([n, c, b]) => '<i class=\"' + c + '\" style=\"flex:' + b + '\" title=\"' + esc(n) + \" \" + fmtBytes(b) + '\"></i>').join(\"\") : '<i class=\"k5\" style=\"flex:1\"></i>';\n $(\"#s-legend\").innerHTML = parts.length ? parts.map(([n, c, b]) => '<span><i class=\"' + c + '\"></i>' + esc(n) + \" \" + Math.round(b * 100 / total) + \"%</span>\").join(\"\") : \"<span>no events yet</span>\";\n const overhead = Math.max(0, st.databaseBytes - st.eventBytes);\n $(\"#s-totals\").innerHTML = [[\"events\", st.events.toLocaleString() + \" <small>\" + fmtBytes(st.eventBytes) + \"</small>\"], [\"index and overhead\", fmtBytes(overhead)], [\"files\", st.blobs.toLocaleString() + \" <small>\" + fmtBytes(st.mediaBytes) + \"</small>\"]]\n .map(([k, v]) => \"<div><small>\" + k + \"</small><b>\" + v + \"</b></div>\").join(\"\");\n const any = st.retention.find((r) => r.kind === null);\n const own = (kind) => { const r = st.retention.find((x) => x.kind === kind); return r ? r.days : \"\"; };\n const row = (k) => {\n const key = k.kind === null ? \"\" : String(k.kind);\n const placeholder = k.kind === null ? \"forever\" : k.replaceable || !any ? \"forever\" : any.days + \" (everything else)\";\n const pill = k.kind === null ? \"everything else\" : '<span class=\"kind' + (k.protected ? ' sys\" title=\"required: the relay depends on this kind' : \"\") + '\">' + k.kind + \"</span> \" + esc(kindName(k.kind));\n const cells = k.protected\n ? '<td class=\"keep sys\" colspan=\"2\" title=\"The relay depends on this kind, so it is never expired or purged.\">required</td>'\n : '<td class=\"keep\"><input class=\"txt num days\" type=\"number\" min=\"0\" max=\"36500\" placeholder=\"' + placeholder + '\" value=\"' + own(k.kind) + '\"> days</td><td class=\"r\">' + ib(\"check\", \"Save keep-for rule\", \"saveretention\", key) + ib(\"trash\", \"Purge\", \"purgekind\", key, \"danger\") + \"</td>\";\n return '<tr class=\"' + (k.kind === null ? \"any\" : \"\") + '\" data-kind=\"' + key + '\"><td>' + pill + '</td><td class=\"r mono\">' + (k.n === undefined ? \"\" : k.n.toLocaleString()) + '</td><td class=\"r mono\">' + (k.bytes === undefined ? \"\" : fmtBytes(k.bytes)) + '</td><td class=\"dim\">' + (k.oldest ? esc(fmtDay(k.oldest)) : \"\") + \"</td>\" + cells + \"</tr>\";\n };\n $(\"#kinds tbody\").innerHTML = [...st.kinds, { kind: null, days: any ? any.days : 0 }].map(row).join(\"\");\n }\n\n async function loadEvents(reset) {\n const list = await rpc(\"listrecentevents\", reset ? 50 : 200);\n $(\"#events tbody\").innerHTML = list.length ? list.map((e) => '<tr><td class=\"dim\">' + esc(fmtTime(e.created_at)) + '</td><td><span class=\"kind' + (SYS_KINDS.has(e.kind) ? ' sys\" title=\"required: the relay depends on this kind' : \"\") + '\">' + e.kind + '</span></td><td class=\"mono\" title=\"' + e.pubkey + '\">' + av(e.pubkey) + short(e.pubkey) + '</td><td class=\"c\">' + esc(e.content) + '</td><td class=\"r\">' + ib(\"trash\", \"Delete event\", \"deleteevent\", e.id) + ib(\"ban\", \"Ban event\", \"banevent\", e.id, \"danger\") + ib(\"banuser\", \"Ban author\", \"banpubkey\", e.pubkey, \"danger\") + \"</td></tr>\").join(\"\") : '<tr><td colspan=\"5\" class=\"muted\">no events yet</td></tr>';\n $(\"#more\").classList.toggle(\"hidden\", list.length < 50);\n }\n\n // ---- actions ----\n const guard = (fn) => async (ev) => { ev.preventDefault(); const b = ev.submitter || ev.target; if (b) b.disabled = true; try { await fn(ev); } catch (e) { toast(e.message); } finally { if (b) b.disabled = false; } };\n const refresh = () => Promise.all([loadLists(), loadEvents(true), loadStorage()]);\n\n $(\"#copy\").onclick = async () => { await navigator.clipboard.writeText(wsURL); toast(\"copied \" + wsURL); };\n $(\"#signin\").onclick = guard(async () => {\n if (!window.nostr) throw new Error(\"No nostr extension found. Install one (Alby, nos2x, \u2026) and reload, or use a remote signer.\");\n me = await window.nostr.getPublicKey();\n localStorage.setItem(\"me\", me);\n renderHeader(); await loadAdmin(); await loadPeople();\n });\n $(\"#signin46\").onclick = () => showRemote(null);\n $$(\".remote\").forEach((b) => { b.onclick = () => showRemote($(\"#\" + b.dataset.note)); });\n $(\"#nccopy\").onclick = async () => { await navigator.clipboard.writeText($(\"#nclink\").href); toast(\"Link copied; paste it into your signer app\"); };\n $(\"#bunkerform\").onsubmit = guard(async (ev) => {\n $(\"#remotenote\").textContent = \"Connecting\u2026\";\n try { await connectBunker(ev.target.url.value); } catch (e) { $(\"#remotenote\").textContent = e.message; throw e; }\n $(\"#remotenote\").textContent = \"\"; ev.target.reset();\n await remoteDone();\n });\n $(\"#signout\").onclick = async () => {\n if (remote) { const r = remote; remote = null; try { await withTimeout(r.logout(), 5000, \"The signer\"); } catch { /* the session is gone either way */ } }\n localStorage.removeItem(\"nip46\");\n me = null; localStorage.removeItem(\"me\"); renderHeader(); await loadAdmin(); await loadPeople();\n };\n const claimNow = async (note) => {\n if (!signer.ready()) { note.textContent = \"Sign the claim with a nostr extension or a remote signer.\"; showRemote(note); return; }\n me = await signer.getPublicKey();\n const r = await rpc(\"claim\");\n if (!r.claimed) throw new Error(\"Somebody else claimed it first.\");\n localStorage.setItem(\"me\", me);\n toast(\"It's yours.\");\n await loadInfo(); await loadFuel(); await loadAdmin(); await loadPeople();\n if (r.converted && confirm(\"This relay began as a temporary one: anyone can write, and everything is deleted after 14 days. Switch to the default rules and keep everything from now on? Each rule can be changed later on the Rules and Storage tabs.\")) {\n await rpc(\"resetrules\"); toast(\"Rules reset\"); await loadInfo(); await loadAdmin();\n }\n };\n $(\"#claim\").onclick = guard(() => claimNow($(\"#claimnote\")));\n $(\"#claimlease\").onclick = guard(() => claimNow($(\"#leasenote\")));\n // Pulls run in the background; the note follows them while the tab is open.\n let pullTimer = 0, pullSeen = 0;\n async function pollPull() {\n clearTimeout(pullTimer);\n let st;\n try { st = await rpc(\"pullstatus\"); } catch { return; }\n const el = $(\"#pullnote\");\n if (st.running) {\n const r = st.running;\n el.textContent = \"Pulling from \" + r.url + \": \" + r.stored.toLocaleString() + \" events so far\" + (r.blobs ? \", \" + r.blobs + \" files\" : \"\") + \"\u2026\";\n pullTimer = setTimeout(pollPull, 3000);\n return;\n }\n const l = st.last;\n el.textContent = !l ? \"\" : l.error ? \"Pull from \" + l.url + \" failed: \" + l.error : \"Pulled \" + l.stored.toLocaleString() + \" events\" + (l.blobs ? \" and \" + l.blobs + \" files\" : \"\") + \" from \" + l.url + (l.skipped ? \" (\" + l.skipped + \" skipped)\" : \"\") + \", \" + fmtTime(l.finishedAt) + \".\";\n if (l && l.finishedAt > pullSeen) { const first = pullSeen === 0; pullSeen = l.finishedAt; if (!first && (l.stored || l.blobs)) await refresh(); }\n }\n $(\"#pullform\").onsubmit = guard(async (ev) => {\n const f = ev.target;\n await rpc(\"pullfrom\", f.url.value.trim());\n toast(\"Pull started\"); f.reset(); await pollPull();\n });\n $(\"#access\").onsubmit = guard(async (ev) => {\n const f = ev.target;\n policy = await rpc(\"setpolicy\", { writes: f.writes.value, reads: f.reads.value, minPow: +f.minPow.value, maxFuture: +f.maxFuture.value, maxLimit: +f.maxLimit.value, maxSubs: +f.maxSubs.value, eventsPerMinute: +f.eventsPerMinute.value, reqsPerMinute: +f.reqsPerMinute.value, maxBlobMB: +f.maxBlobMB.value });\n toast(\"Rules saved\"); await loadInfo();\n });\n $(\"#identity\").onsubmit = guard(async (ev) => {\n const f = ev.target;\n const list = (v) => v.split(\",\").map((s) => s.trim()).filter(Boolean);\n policy = await rpc(\"setpolicy\", { name: f.name.value, contact: f.contact.value, description: f.description.value, icon: f.icon.value, joinTerms: f.joinTerms.value, directoryPublic: f.directoryPublic.checked,\n banner: f.banner.value, postingPolicy: f.postingPolicy.value, privacyPolicy: f.privacyPolicy.value, tags: list(f.tags.value), languageTags: list(f.languageTags.value), relayCountries: list(f.relayCountries.value) });\n toast(\"Identity saved\"); await loadInfo(); await loadPeople();\n });\n $(\"#addmember\").onsubmit = guard(async (ev) => {\n const f = ev.target; const pk = npubToHex(f.pubkey.value); if (!pk) throw new Error(\"That is not a pubkey.\");\n await rpc(\"setmember\", pk, { name: f.name.value.trim() || null, note: f.note.value }); f.reset(); toast(\"Member added\"); await loadLists();\n });\n $(\"#addblock\").onsubmit = guard(async (ev) => {\n const f = ev.target;\n await rpc(\"blockip\", f.ip.value.trim(), f.reason.value); f.reset(); toast(\"Blocked\"); await loadLists();\n });\n $(\"#addban\").onsubmit = guard(async (ev) => {\n const pk = npubToHex(ev.target.pubkey.value); if (!pk) throw new Error(\"That is not a pubkey.\");\n await rpc(\"banpubkey\", pk, ev.target.reason.value); ev.target.reset(); toast(\"Banned\"); await loadLists();\n });\n $(\"#mintinvite\").onsubmit = guard(async (ev) => {\n const f = ev.target;\n const inv = await rpc(\"createinvite\", +f.ttl.value, +f.max.value, f.note.value);\n await navigator.clipboard.writeText(location.origin + \"/invite/\" + inv.code).catch(() => {});\n f.note.value = \"\"; toast(\"Invite created and copied\"); await loadLists();\n });\n $(\"#kindform\").onsubmit = guard(async (ev) => {\n const rule = ev.submitter.value; const k = +ev.target.kind.value;\n await rpc(rule === \"allow\" ? \"allowkind\" : \"disallowkind\", k); ev.target.reset(); toast((rule === \"allow\" ? \"Allowed kind \" : \"Blocked kind \") + k); await loadLists();\n });\n $(\"#more\").onclick = guard(() => loadEvents(false));\n $(\"#exportcfg\").onclick = guard(async () => {\n const cfg = await rpc(\"exportconfig\");\n const a = document.createElement(\"a\");\n a.href = URL.createObjectURL(new Blob([JSON.stringify(cfg, null, 2)], { type: \"application/json\" }));\n a.download = host.split(\".\")[0] + \".bind.ws.config.json\";\n a.click(); URL.revokeObjectURL(a.href);\n });\n $(\"#importcfg\").onclick = () => $(\"#cfgfile\").click();\n $(\"#cfgfile\").onchange = guard(async (ev) => {\n const file = ev.target.files[0]; if (!file) return;\n const cfg = JSON.parse(await file.text());\n if (!confirm(\"Replace this relay's rules, identity, members, bans and kind rules with \" + file.name + \"?\")) { ev.target.value = \"\"; return; }\n await rpc(\"importconfig\", cfg); ev.target.value = \"\"; toast(\"Configuration imported\"); await loadInfo(); await loadAdmin(); await loadPeople();\n });\n $(\"#transfer\").onsubmit = guard(async (ev) => {\n const pk = ev.target.pubkey.value; if (!pk) return;\n const name = host.split(\".\")[0];\n const typed = prompt(\"This hands \" + host + \" to \" + short(pk) + \" for good. You stay on as a moderator. Type the relay name (\" + name + \") to confirm.\");\n if (typed === null) return;\n if (typed.trim() !== name) { toast(\"That didn't match; nothing changed.\"); return; }\n await rpc(\"transferowner\", pk);\n toast(\"Transferred\");\n await loadInfo(); await loadAdmin(); await loadPeople();\n });\n $(\"#deleterelay\").onclick = guard(async () => {\n const name = host.split(\".\")[0];\n const typed = prompt(\"This deletes everything on \" + host + \" and gives the name up. Type the relay name (\" + name + \") to confirm.\");\n if (typed === null) return;\n if (typed.trim() !== name) { toast(\"That didn't match; nothing was deleted.\"); return; }\n await rpc(\"deleterelay\", name);\n localStorage.removeItem(\"me\");\n location.href = \"/\";\n });\n $(\"#topup\").onsubmit = guard((ev) => topUp(+ev.target.sats.value));\n $(\"#inv-copy\").onclick = async () => { await navigator.clipboard.writeText($(\"#inv-text\").value); toast(\"invoice copied\"); };\n $$(\"button[data-copy]\").forEach((b) => { b.onclick = async () => { await navigator.clipboard.writeText(b.dataset.copy === \"ws\" ? wsURL : location.origin); toast(\"copied\"); }; });\n document.addEventListener(\"click\", async (ev) => {\n const b = ev.target.closest(\"button[data-act]\"); if (!b) return;\n const act = b.dataset.act, id = b.dataset.id;\n if (act === \"copy\") { await navigator.clipboard.writeText(id); toast(\"copied\"); return; }\n if ((act === \"banpubkey\" || act === \"banevent\" || act === \"resolve:ban\") && !confirm(act === \"banevent\" ? \"Delete this event and refuse it forever?\" : \"Ban this author and refuse everything they post?\")) return;\n if (act === \"deleteblob\" && !confirm(\"Delete this file for good?\")) return;\n if (act === \"purgekind\") {\n const kind = id === \"\" ? null : +id;\n const label = kind === null ? \"everything without its own rule\" : kindName(kind) + \" (kind \" + kind + \")\";\n const typed = prompt(\"Purge \" + label + \" older than how many days? 0 purges all of them. This cannot be undone.\", \"30\");\n if (typed === null) return;\n const days = Math.max(0, Math.floor(+typed || 0));\n b.disabled = true;\n try { const r = await rpc(\"purgekind\", kind, days); toast(\"Purged \" + r.deleted.toLocaleString() + \" events\"); await refresh(); } catch (e) { toast(e.message); } finally { b.disabled = false; }\n return;\n }\n if (act === \"saveretention\") {\n const kind = id === \"\" ? null : +id;\n const days = Math.max(0, Math.floor(+b.closest(\"tr\").querySelector(\".days\").value || 0));\n b.disabled = true;\n try { await rpc(\"setretention\", kind, days); toast(days ? \"Kept for \" + days + \" days\" : \"Kept forever\"); await loadStorage(); await loadInfo(); } catch (e) { toast(e.message); } finally { b.disabled = false; }\n return;\n }\n b.disabled = true;\n try {\n if (act === \"savemember\") { const tr = b.closest(\"tr\"), role = tr.querySelector(\"select.role\"); await rpc(\"setmember\", id, { name: tr.querySelector(\".name\").value.trim() || null, note: tr.querySelector(\".note\").value, ...(role ? { role: role.value } : {}) }); }\n else if (act.startsWith(\"resolve:\")) await rpc(\"resolvereport\", id, act.slice(8));\n else await rpc(act, act === \"unrulekind\" ? +id : id, \"\");\n toast(\"Done\"); await refresh();\n } catch (e) { toast(e.message); } finally { b.disabled = false; }\n });\n\n // ---- boot ----\n try { await loadInfo(); } catch { renderHeader(); }\n await loadFuel();\n me = localStorage.getItem(\"me\");\n if (me && await resumeRemote()) { /* a remote session answers for itself when first used */ }\n else if (me && window.nostr) { try { const pk = await window.nostr.getPublicKey(); if (pk !== me) me = null; } catch { me = null; } }\n else if (me) me = null;\n renderHeader();\n await loadAdmin();\n await loadPeople();\n})();\n";23+const JS = "(async () => {\n const $ = (s) => document.querySelector(s);\n const $$ = (s) => document.querySelectorAll(s);\n const host = location.host;\n const wsURL = (location.protocol === \"https:\" ? \"wss://\" : \"ws://\") + host;\n const rpcURL = location.origin + \"/\";\n let info = null, me = null, owner = \"\", policy = null, fuel = null, people = null, myRole = \"\";\n\n const toast = (msg) => { const t = $(\"#toast\"); t.textContent = msg; t.classList.add(\"show\"); clearTimeout(t._t); t._t = setTimeout(() => t.classList.remove(\"show\"), 2600); };\n const short = (hex) => hex ? hex.slice(0, 8) + \"\u2026\" + hex.slice(-4) : \"\";\n const hue = (hex) => (parseInt(hex.slice(0, 2), 16) * 360 / 256).toFixed(0) + \"deg\";\n const av = (hex) => '<i class=\"av\" style=\"--h:' + hue(hex) + '\"></i>';\n const fmtBytes = (n) => n < 1e6 ? (n / 1e3).toFixed(0) + \" KB\" : n < 1e9 ? (n / 1e6).toFixed(1) + \" MB\" : (n / 1e9).toFixed(2) + \" GB\";\n const fmtHours = (ms) => ms < 3600e3 ? Math.round(ms / 60e3) + \" min\" : (ms / 3600e3).toFixed(ms < 36e6 ? 1 : 0) + \" h\";\n const fuelOver = () => !!fuel && (fuel.eventBytes > fuel.freeEventBytes || fuel.mediaBytes > fuel.freeMediaBytes || fuel.activeMs > fuel.freeActiveMs || fuel.rowsWritten > fuel.freeRowsWritten);\n const fmtTime = (t) => t ? new Date(t * 1000).toLocaleString(undefined, { month: \"short\", day: \"numeric\", hour: \"2-digit\", minute: \"2-digit\" }) : \"\u2013\";\n const fmtDay = (t) => t ? new Date(t * 1000).toLocaleDateString(undefined, { month: \"short\", day: \"numeric\" }) : \"\u2013\";\n const ago = (t) => { const s = Math.max(0, Math.floor(Date.now() / 1000) - t); return s < 60 ? \"just now\" : s < 3600 ? Math.floor(s / 60) + \" min\" : s < 86400 ? Math.floor(s / 3600) + \" h\" : Math.floor(s / 86400) + \" d\"; };\n const esc = (s) => String(s ?? \"\").replace(/[&<>\"']/g, (c) => ({ \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \""\", \"'\": \"'\" })[c]);\n\n const IC = {\n x: '<svg viewBox=\"0 0 24 24\"><path d=\"M6 6l12 12M18 6L6 18\"/></svg>',\n undo: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 10h10a5 5 0 0 1 0 10h-3M4 10l4-4M4 10l4 4\"/></svg>',\n check: '<svg viewBox=\"0 0 24 24\"><path d=\"M5 12l5 5L20 7\"/></svg>',\n trash: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 7h16M6 7l1.2 13h9.6L18 7\"/></svg>',\n ban: '<svg viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"12\" r=\"8.5\"/><path d=\"M6 6l12 12\"/></svg>',\n banuser: '<svg viewBox=\"0 0 24 24\"><circle cx=\"10\" cy=\"8\" r=\"4\"/><path d=\"M3 21a7 7 0 0 1 11-5.7M15 15l6 6M21 15l-6 6\"/></svg>',\n eye: '<svg viewBox=\"0 0 24 24\"><path d=\"M2 12s4-6 10-6 10 6 10 6-4 6-10 6S2 12 2 12z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></svg>',\n lock: '<svg viewBox=\"0 0 24 24\"><rect x=\"5\" y=\"10\" width=\"14\" height=\"10\" rx=\"2\"/><path d=\"M8 10V7a4 4 0 0 1 8 0v3\"/></svg>',\n pen: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 20l4-1 11-11-3-3L5 16l-1 4zM13 8l3 3\"/></svg>',\n people: '<svg viewBox=\"0 0 24 24\"><circle cx=\"9\" cy=\"8\" r=\"3.5\"/><circle cx=\"17\" cy=\"9\" r=\"2.5\"/><path d=\"M3 20a6 6 0 0 1 12 0M15 20a4.5 4.5 0 0 1 7-3.5\"/></svg>',\n person: '<svg viewBox=\"0 0 24 24\"><circle cx=\"12\" cy=\"8\" r=\"4\"/><path d=\"M4 21a8 8 0 0 1 16 0\"/></svg>',\n gauge: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 16a8 8 0 0 1 16 0M12 16l4-5\"/><circle cx=\"12\" cy=\"16\" r=\"1.2\"/></svg>',\n bolt: '<svg viewBox=\"0 0 24 24\"><path d=\"M13 3L5 14h6l-1 7 8-11h-6z\"/></svg>',\n copy: '<svg viewBox=\"0 0 24 24\"><rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\"/><path d=\"M5 15V6a2 2 0 0 1 2-2h9\"/></svg>',\n };\n const ib = (icon, label, act, id, extra) => '<button class=\"ib' + (extra ? \" \" + extra : \"\") + '\" title=\"' + label + '\" aria-label=\"' + label + '\" data-act=\"' + act + '\" data-id=\"' + esc(id) + '\">' + IC[icon] + \"</button>\";\n\n const CH = \"qpzry9x8gf2tvdw0s3jn54khce6mua7l\";\n function npubToHex(s) {\n s = s.trim().toLowerCase();\n if (/^[0-9a-f]{64}$/.test(s)) return s;\n if (!s.startsWith(\"npub1\")) return null;\n const data = s.slice(5, -6).split(\"\").map((c) => CH.indexOf(c));\n if (data.some((d) => d < 0)) return null;\n let bits = 0, acc = 0, out = [];\n for (const d of data) { acc = (acc << 5) | d; bits += 5; if (bits >= 8) { bits -= 8; out.push((acc >> bits) & 255); } }\n return out.length === 32 ? out.map((b) => b.toString(16).padStart(2, \"0\")).join(\"\") : null;\n }\n\n async function sha256hex(s) {\n const b = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(s));\n return [...new Uint8Array(b)].map((x) => x.toString(16).padStart(2, \"0\")).join(\"\");\n }\n // ---- signing: a NIP-07 extension, or a NIP-46 remote signer ----\n // The remote path loads the bundled library on first use; its session\n // (client key, signer pubkey, relays) lives in localStorage until sign-out.\n let remote = null, lib = null, pendingNote = null;\n const NO_SIGNER = \"Install a nostr extension (Alby, nos2x, \u2026) or connect a remote signer.\";\n const withTimeout = (p, ms, what) => Promise.race([p, new Promise((_, rej) => setTimeout(() => rej(new Error(what + \" did not answer; is the signer app open?\")), ms))]);\n const signer = {\n ready: () => !!remote || !!window.nostr,\n async getPublicKey() { if (remote) return withTimeout(remote.getPublicKey(), 20000, \"The remote signer\"); if (window.nostr) return window.nostr.getPublicKey(); throw new Error(NO_SIGNER); },\n async signEvent(ev) { if (remote) return withTimeout(remote.signEvent(ev), 60000, \"The remote signer\"); if (window.nostr) return window.nostr.signEvent(ev); throw new Error(NO_SIGNER); },\n };\n async function signerLib() {\n if (lib) return lib;\n if (!window.NostrSigner) await new Promise((res, rej) => { const s = document.createElement(\"script\"); s.src = window.SIGNER_URL || \"/signer.js\"; s.onload = res; s.onerror = () => rej(new Error(\"Could not load the signer library.\")); document.head.appendChild(s); });\n return (lib = window.NostrSigner);\n }\n const onauth = (u) => window.open(u, \"_blank\");\n const saveSession = (sk, s, secret) => localStorage.setItem(\"nip46\", JSON.stringify({ sk: lib.bytesToHex(sk), pubkey: s.bp.pubkey, relays: s.bp.relays, secret: secret || null }));\n async function resumeRemote() {\n const raw = localStorage.getItem(\"nip46\");\n if (!raw) return false;\n try {\n const s = JSON.parse(raw); const L = await signerLib();\n remote = L.BunkerSigner.fromBunker(L.hexToBytes(s.sk), { pubkey: s.pubkey, relays: s.relays, secret: s.secret }, { onauth });\n return true;\n } catch { localStorage.removeItem(\"nip46\"); return false; }\n }\n async function connectBunker(input) {\n const L = await signerLib();\n const bp = await L.parseBunkerInput(input.trim());\n if (!bp) throw new Error(\"That is not a bunker:// URL.\");\n if (!bp.relays.length) throw new Error(\"The bunker URL names no relay.\");\n const sk = L.generateSecretKey();\n const s = L.BunkerSigner.fromBunker(sk, bp, { onauth });\n await withTimeout(s.connect({ name: host, url: location.origin }), 60000, \"The signer\");\n await Promise.race([s.switchRelays(), new Promise((r) => setTimeout(r, 3000))]);\n remote = s; saveSession(sk, s, bp.secret);\n }\n // The nostrconnect:// flow: this relay carries the traffic, so no third relay is involved.\n let ncPending = null;\n async function offerNostrConnect() {\n if (ncPending) return ncPending;\n const L = await signerLib();\n const sk = L.generateSecretKey();\n const secret = L.bytesToHex(crypto.getRandomValues(new Uint8Array(8)));\n const uri = L.createNostrConnectURI({ clientPubkey: L.getPublicKey(sk), relays: [wsURL], secret, name: host, url: location.origin, perms: [\"sign_event:27235\", \"sign_event:9734\"] });\n $(\"#nclink\").href = uri; $(\"#ncnote\").textContent = \"\";\n ncPending = L.BunkerSigner.fromURI(sk, uri, { onauth }, 600000).then((s) => { remote = s; saveSession(sk, s, secret); ncPending = null; return s; }, (e) => { ncPending = null; throw e; });\n return ncPending;\n }\n async function remoteDone() {\n me = await signer.getPublicKey();\n localStorage.setItem(\"me\", me);\n $(\"#remotesec\").classList.add(\"hidden\");\n toast(\"Remote signer connected\");\n if (pendingNote && !owner) { const n = pendingNote; pendingNote = null; await claimNow(n); return; }\n pendingNote = null;\n renderHeader(); await loadAdmin(); await loadPeople();\n }\n function showRemote(note) {\n pendingNote = note || null;\n $(\"#remotesec\").classList.remove(\"hidden\");\n $(\"#remotesec\").scrollIntoView({ behavior: \"smooth\", block: \"start\" });\n offerNostrConnect().then(remoteDone).catch((e) => { $(\"#ncnote\").textContent = e.message; });\n }\n\n async function rpc(method, ...params) {\n if (!signer.ready()) throw new Error(NO_SIGNER);\n const body = JSON.stringify({ method, params });\n const ev = await signer.signEvent({ kind: 27235, created_at: Math.floor(Date.now() / 1000), content: \"\", tags: [[\"u\", rpcURL], [\"method\", \"POST\"], [\"payload\", await sha256hex(body)]] });\n const resp = await fetch(rpcURL, { method: \"POST\", headers: { \"content-type\": \"application/nostr+json+rpc\", authorization: \"Nostr \" + btoa(JSON.stringify(ev)) }, body });\n const json = await resp.json();\n if (json.error) throw new Error(json.error);\n return json.result;\n }\n\n // ---- front of house ----\n function renderHeader() {\n const name = (info && info.name) || host.split(\".\")[0];\n $(\"#title\").textContent = name;\n document.title = name + \" - relay\";\n $(\"#url\").textContent = wsURL;\n $(\"#d-ws\").textContent = wsURL; $(\"#d-blossom\").textContent = location.origin; $(\"#d-nip05\").textContent = \"you@\" + host;\n $(\"#nip05-example\").textContent = \"alice@\" + host;\n const desc = (info && info.description) || \"\";\n $(\"#desc\").textContent = desc; $(\"#desc\").classList.toggle(\"hidden\", !desc);\n owner = (info && info.pubkey) || \"\";\n const isOwner = !!me && me === owner;\n const lease = !owner && info && info.lease ? info.lease : null;\n $(\"#ownerline\").innerHTML = owner ? 'run by <b class=\"mono\" title=\"' + owner + '\">' + short(owner) + \"</b>\" : lease ? \"temporary until \" + fmtDay(lease.expires_at) : \"unclaimed\";\n $(\"#owner-av\").classList.toggle(\"hidden\", !owner);\n if (owner) $(\"#owner-av\").style.setProperty(\"--h\", hue(owner));\n $(\"#who\").textContent = me ? (isOwner ? \"(that's you)\" : \"\") : \"\";\n $(\"#unclaimed\").classList.toggle(\"hidden\", !!owner || !!lease);\n $(\"#leased\").classList.toggle(\"hidden\", !lease);\n if (lease) {\n $(\"#lease-until\").textContent = fmtTime(lease.expires_at);\n $(\"#leasenote\").textContent = lease.holder ? \"Reserved for the key that asked for it; sign with that key.\" : \"\";\n }\n $(\"#signin\").classList.toggle(\"hidden\", !!me || !owner);\n $(\"#signin46\").classList.toggle(\"hidden\", !!me || !owner);\n $(\"#signout\").classList.toggle(\"hidden\", !me);\n $(\"#about\").innerHTML = [\n [\"Name\", info?.name], [\"Description\", info?.description], [\"Contact\", info?.contact], [\"Owner\", owner ? short(owner) : \"\"], [\"Relay key\", info?.self ? short(info.self) : \"\"], [\"Software\", info?.software], [\"Version\", info?.version],\n [\"Max query\", info?.limitation?.max_limit], [\"Auth required\", String(!!info?.limitation?.auth_required)], [\"Restricted writes\", String(!!info?.limitation?.restricted_writes)], [\"Min PoW\", info?.limitation?.min_pow_difficulty || 0],\n [\"NIPs\", info?.supported_nips?.join(\" \")],\n ].map(([k, v]) => \"<div><small>\" + k + \"</small><div\" + ([\"Owner\", \"NIPs\", \"Relay key\"].includes(k) ? ' class=\"mono\"' : \"\") + \">\" + (v === undefined || v === \"\" ? '<span class=\"muted\">\u2013</span>' : esc(v)) + \"</div></div>\").join(\"\");\n renderCare();\n }\n\n function renderCare() {\n const g = (icon, label, off) => '<span class=\"g' + (off ? \" off\" : \"\") + '\" title=\"' + label + '\"><i>' + IC[icon] + \"</i><small>\" + label + \"</small></span>\";\n const writes = policy ? policy.writes : (info?.limitation?.restricted_writes ? \"allowlist\" : \"open\");\n const reads = policy ? policy.reads : (info?.limitation?.auth_required ? \"auth\" : \"open\");\n let out = writes === \"owner\" ? g(\"person\", \"only owner writes\") : writes === \"allowlist\" ? g(\"people\", \"members write\") : g(\"pen\", \"anyone writes\");\n out += reads === \"members\" ? g(\"people\", \"members read\") : reads === \"auth\" ? g(\"lock\", \"sign in to read\") : g(\"eye\", \"anyone reads\");\n if (fuel) {\n const over = fuelOver();\n out += fuel.outOfFuel ? g(\"gauge\", \"out of fuel\", true) : over ? g(\"bolt\", \"burning sats\") : g(\"gauge\", \"on free allowance\");\n }\n $(\"#care\").innerHTML = out;\n }\n\n async function loadPeople() {\n try { people = await (await fetch(\"/people\")).json(); } catch { return; }\n const isOwner = !!me && me === owner;\n let list = people.people || [];\n if (!people.public && isOwner && policy) list = (window.__members || []);\n $(\"#peoplesec\").classList.toggle(\"hidden\", !owner || list.length === 0);\n $(\"#people-note\").classList.toggle(\"hidden\", people.public);\n $(\"#people-count\").textContent = list.length || \"\";\n $(\"#dir\").innerHTML = list.map((m) => '<span class=\"who' + (m.pubkey === me ? \" me\" : \"\") + '\" title=\"' + esc(m.pubkey) + '\">' + av(m.pubkey) + '<span class=\"mono\">' + (m.name ? esc(m.name) + \"@\" + host : short(m.pubkey)) + \"</span>\" + (m.role === \"owner\" ? '<span class=\"role\">owner</span>' : \"\") + \"</span>\").join(\"\");\n }\n\n async function loadInfo() {\n info = await (await fetch(\"/\", { headers: { accept: \"application/nostr+json\" } })).json();\n renderHeader();\n }\n\n async function loadFuel() {\n try { fuel = await (await fetch(\"/fuel\")).json(); } catch { return; }\n $(\"#fuelsec\").classList.toggle(\"hidden\", !owner);\n const pct = (used, free) => Math.min(100, Math.round((used / Math.max(free, 1)) * 100));\n const gauge = (id, used, free, text) => { const g = $(\"#g-\" + id); g.style.width = pct(used, free) + \"%\"; g.classList.toggle(\"over\", used > free); $(\"#t-\" + id).textContent = text; };\n gauge(\"events\", fuel.eventBytes, fuel.freeEventBytes, fmtBytes(fuel.eventBytes) + \" of \" + fmtBytes(fuel.freeEventBytes) + \" free\");\n gauge(\"media\", fuel.mediaBytes, fuel.freeMediaBytes, fmtBytes(fuel.mediaBytes) + \" of \" + fmtBytes(fuel.freeMediaBytes) + \" free\");\n gauge(\"active\", fuel.activeMs, fuel.freeActiveMs, fmtHours(fuel.activeMs) + \" of \" + fmtHours(fuel.freeActiveMs) + \" free\");\n gauge(\"rows\", fuel.rowsWritten, fuel.freeRowsWritten, fuel.rowsWritten.toLocaleString() + \" of \" + fuel.freeRowsWritten.toLocaleString() + \" free\");\n const sats = Math.floor(fuel.balanceMsats / 1000);\n const r = fuel.rates;\n $(\"#fuel-balance\").innerHTML = fuel.outOfFuel\n ? '<span class=\"chip\">out of fuel</span> Writes are paused until someone tops up.'\n : \"Balance <b>\" + sats.toLocaleString() + \" sats</b>. Past the allowances, prices track what the hosting costs: \" + r.satsPerGBMonthEvents.toLocaleString() + \" sats per GB-month of events, \" + r.satsPerGBMonthMedia.toLocaleString() + \" per GB-month of files, \" + r.satsPerActiveHour.toLocaleString() + \" per hour awake, \" + r.satsPerMillionRows.toLocaleString() + \" per million rows written. Traffic is free.\";\n $(\"#topup\").classList.toggle(\"hidden\", !fuel.enabled);\n $(\"#credits tbody\").innerHTML = fuel.credits.length ? fuel.credits.map((c) => '<tr><td class=\"dim\">' + esc(fmtTime(c.at)) + '</td><td class=\"mono\" title=\"' + esc(c.payer) + '\">' + av(c.payer) + short(c.payer) + '</td><td class=\"mono r\">' + Math.floor(c.msats / 1000).toLocaleString() + \"</td></tr>\").join(\"\") : '<tr><td colspan=\"3\" class=\"muted\">no zaps yet</td></tr>';\n if (!fuel.enabled) $(\"#topup-note\").textContent = \"Top-ups are not enabled on this service yet.\";\n renderCare();\n renderFuelTile();\n }\n\n async function topUp(sats) {\n if (!signer.ready()) throw new Error(NO_SIGNER);\n const msats = Math.round(sats * 1000);\n const zapRequest = await signer.signEvent({ kind: 9734, created_at: Math.floor(Date.now() / 1000), content: \"fuel for \" + host, tags: [[\"p\", fuel.servicePubkey], [\"amount\", String(msats)], [\"relays\", wsURL]] });\n const r = await (await fetch(\"/fuel/invoice\", { method: \"POST\", body: JSON.stringify({ zapRequest }) })).json();\n if (r.error) throw new Error(r.error);\n const inv = r.invoice;\n $(\"#invoice\").classList.remove(\"hidden\");\n $(\"#inv-text\").value = inv; $(\"#inv-link\").href = \"lightning:\" + inv; $(\"#inv-state\").textContent = \"waiting for payment\u2026\";\n if (window.webln) { try { await window.webln.enable(); await window.webln.sendPayment(inv); } catch (e) { toast(e.message || \"wallet declined\"); } }\n const before = fuel.creditedMsats;\n for (let i = 0; i < 120; i++) {\n await new Promise((r) => setTimeout(r, 2500));\n await loadFuel();\n if (fuel.creditedMsats > before) { $(\"#inv-state\").textContent = \"paid, thank you\"; toast(\"Fuel credited\"); return; }\n }\n $(\"#inv-state\").textContent = \"no receipt yet; it can take a minute after paying\";\n }\n\n // ---- console ----\n function showTab(name) {\n if (name === \"content\") name = \"storage\";\n const known = myRole === \"moderator\" ? [\"people\", \"storage\"] : [\"people\", \"rules\", \"identity\", \"storage\", \"health\"];\n if (!known.includes(name)) name = \"people\";\n $$(\".tabs a\").forEach((a) => a.classList.toggle(\"on\", a.dataset.tab === name));\n $$(\".panel\").forEach((p) => p.classList.toggle(\"on\", p.dataset.panel === name));\n }\n // Tabs switch in place. A fragment still opens a tab when someone arrives with one,\n // but clicking never writes one, and any fragment already there is dropped.\n $$(\".tabs a\").forEach((a) => a.addEventListener(\"click\", (ev) => { ev.preventDefault(); showTab(a.dataset.tab); if (location.hash) history.replaceState(null, \"\", location.pathname + location.search); }));\n window.addEventListener(\"hashchange\", () => showTab(location.hash.slice(1)));\n\n function renderFuelTile() {\n if (!fuel) return;\n const over = fuelOver();\n const sats = Math.max(0, Math.floor(fuel.balanceMsats / 1000));\n const d = new Date(), end = Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + 1, 1), days = Math.max(1, Math.ceil((end - Date.now()) / 86400000));\n if (fuel.outOfFuel) { $(\"#h-fuel\").textContent = \"0 sats\"; $(\"#h-fuel-label\").textContent = \"out of fuel\"; $(\"#h-fuel-sub\").textContent = \"writes are paused\"; }\n else if (over) { $(\"#h-fuel\").textContent = sats.toLocaleString() + \" sats\"; $(\"#h-fuel-label\").textContent = \"fuel left\"; $(\"#h-fuel-sub\").textContent = \"past the free allowance\"; }\n else { $(\"#h-fuel\").textContent = days + (days === 1 ? \" day\" : \" days\"); $(\"#h-fuel-label\").textContent = \"free allowance left\"; $(\"#h-fuel-sub\").textContent = sats ? \"then \" + sats.toLocaleString() + \" sats\" : \"then a top-up is needed\"; }\n $(\"#usage\").innerHTML = [[\"events stored\", fmtBytes(fuel.eventBytes)], [\"files stored\", fmtBytes(fuel.mediaBytes)], [\"awake this month\", fmtHours(fuel.activeMs)], [\"rows written\", fuel.rowsWritten.toLocaleString()], [\"rows read\", fuel.rowsRead.toLocaleString()], [\"received this month\", fmtBytes(fuel.bytesIn)], [\"served this month\", fmtBytes(fuel.bytesOut)], [\"charged\", Math.floor(fuel.chargedMsats / 1000).toLocaleString() + \" sats\"]]\n .map(([k, v]) => \"<div><small>\" + k + \"</small><b>\" + v + \"</b></div>\").join(\"\");\n }\n\n function renderHealth(stats) {\n $(\"#h-last\").textContent = stats.newest ? ago(stats.newest) : \"none\";\n $(\"#h-last-sub\").textContent = stats.newest ? fmtTime(stats.newest) : \"no events yet\";\n $(\"#h-conns\").textContent = stats.connections;\n const buckets = [[\"notes\", \"k1\", [1]], [\"reactions\", \"k2\", [7]], [\"DMs\", \"k3\", [4, 14, 1059]], [\"long-form\", \"k4\", [30023]], [\"other\", \"k5\", null]];\n const sums = buckets.map(() => 0); let total = 0;\n for (const { kind, n } of stats.kinds || []) { total += n; const i = buckets.findIndex((b) => b[2] && b[2].includes(kind)); sums[i < 0 ? 4 : i] += n; }\n const parts = buckets.map((b, i) => [b[0], b[1], sums[i]]).filter((p) => p[2] > 0);\n $(\"#h-kinds\").innerHTML = total ? parts.map(([name, cls, n]) => '<i class=\"' + cls + '\" style=\"flex:' + n + '\" title=\"' + name + \" \" + Math.round(n * 100 / total) + '%\"></i>').join(\"\") : '<i class=\"k5\" style=\"flex:1\"></i>';\n $(\"#h-kinds-legend\").innerHTML = total ? parts.map(([name, cls, n]) => '<span><i class=\"' + cls + '\"></i>' + name + \" \" + Math.round(n * 100 / total) + \"%</span>\").join(\"\") : \"<span>no events yet</span>\";\n renderFuelTile();\n }\n\n async function loadAdmin() {\n const isOwner = !!me && me === owner;\n myRole = isOwner ? \"owner\" : \"\";\n let stats = null, p = null;\n // A signed-in member may be a moderator; the relay says by answering stats.\n if (me && owner && !isOwner) { try { [stats, p] = await Promise.all([rpc(\"stats\"), rpc(\"getpolicy\")]); myRole = \"moderator\"; } catch { myRole = \"\"; } }\n $(\"#console\").classList.toggle(\"hidden\", !myRole);\n $(\"#console\").classList.toggle(\"mod\", myRole === \"moderator\");\n if (!myRole) { policy = null; renderCare(); return; }\n if (isOwner) [stats, p] = await Promise.all([rpc(\"stats\"), rpc(\"getpolicy\")]);\n policy = p;\n renderHealth(stats);\n renderCare();\n const fa = $(\"#access\"), fi = $(\"#identity\");\n fa.writes.value = p.writes; fa.reads.value = p.reads; fa.minPow.value = p.minPow; fa.maxFuture.value = p.maxFuture; fa.maxLimit.value = p.maxLimit; fa.maxSubs.value = p.maxSubs;\n fa.eventsPerMinute.value = p.eventsPerMinute; fa.reqsPerMinute.value = p.reqsPerMinute; fa.maxBlobMB.value = p.maxBlobMB;\n fi.name.value = p.name; fi.contact.value = p.contact; fi.description.value = p.description; fi.icon.value = p.icon; fi.joinTerms.value = p.joinTerms; fi.directoryPublic.checked = !!p.directoryPublic;\n fi.banner.value = p.banner || \"\"; fi.postingPolicy.value = p.postingPolicy || \"\"; fi.privacyPolicy.value = p.privacyPolicy || \"\";\n fi.tags.value = (p.tags || []).join(\", \"); fi.languageTags.value = (p.languageTags || []).join(\", \"); fi.relayCountries.value = (p.relayCountries || []).join(\", \");\n showTab(location.hash.slice(1));\n if (isOwner) { await renderPresets(); renderWire(); }\n await Promise.all([loadLists(), loadEvents(true), ...(isOwner ? [loadStorage()] : [])]);\n }\n\n async function loadLists() {\n const [mem, bans, bannedEvents, allow, block, invites, reports, blobs, blocks] = await Promise.all([rpc(\"listmembers\"), rpc(\"listbannedpubkeys\"), rpc(\"listbannedevents\"), rpc(\"listallowedkinds\"), rpc(\"listblockedkinds\"), rpc(\"listinvites\"), rpc(\"listreports\"), rpc(\"listblobs\", 100), rpc(\"listblockedips\")]);\n const members = mem.members;\n window.__members = members;\n const roleCell = (m) => m.role === \"owner\" ? ' <span class=\"chip\">owner</span>' : myRole === \"owner\" ? ' <select class=\"txt role\" title=\"Role\"><option value=\"member\"' + (m.role === \"member\" ? \" selected\" : \"\") + '>member</option><option value=\"moderator\"' + (m.role === \"moderator\" ? \" selected\" : \"\") + '>moderator</option></select>' : m.role === \"moderator\" ? ' <span class=\"chip\">moderator</span>' : \"\";\n const untouchable = (m) => m.role === \"owner\" || (myRole !== \"owner\" && m.role === \"moderator\");\n $(\"#members tbody\").innerHTML = members.map((m) => '<tr data-pk=\"' + m.pubkey + '\"><td class=\"mono\" title=\"' + esc(m.pubkey) + '\">' + av(m.pubkey) + short(m.pubkey) + roleCell(m) + '</td><td class=\"name\"><input class=\"txt name\" value=\"' + esc(m.name || \"\") + '\" placeholder=\"name\" pattern=\"[a-z0-9._-]{1,64}\"></td><td><input class=\"txt note\" value=\"' + esc(m.note || \"\") + '\" placeholder=\"note\"></td><td class=\"dim\">' + esc(fmtDay(m.joined_at)) + \", \" + esc(m.via) + '</td><td class=\"r\">' + ib(\"check\", \"Save\", \"savemember\", m.pubkey) + (untouchable(m) ? \"\" : ib(\"x\", \"Remove\", \"removemember\", m.pubkey) + ib(\"banuser\", \"Ban\", \"banpubkey\", m.pubkey, \"danger\")) + \"</td></tr>\").join(\"\");\n $(\"#transfer [name=pubkey]\").innerHTML = members.filter((m) => m.role !== \"owner\").map((m) => '<option value=\"' + m.pubkey + '\">' + esc(m.name ? m.name + \"@\" + host : short(m.pubkey)) + \"</option>\").join(\"\");\n $(\"#tc-people\").textContent = members.length;\n const link = (code) => location.origin + \"/invite/\" + code;\n $(\"#invites\").innerHTML = invites.length ? invites.map((i) => '<li><i class=\"ev\">inv</i><span><a class=\"mono\" href=\"/invite/' + i.code + '\">\u2026' + i.code.slice(-8) + \"</a> \" + esc(i.note || \"\") + ' <span class=\"dim\">' + i.uses + (i.max_uses ? \"/\" + i.max_uses : \"\") + \" used, until \" + esc(fmtDay(i.expires_at)) + \"</span></span><span>\" + ib(\"copy\", \"Copy link\", \"copy\", link(i.code)) + ib(\"x\", \"Revoke\", \"revokeinvite\", i.code) + \"</span></li>\").join(\"\") : '<li class=\"empty\">no invites</li>';\n const person = (r, icon, label, act) => \"<li>\" + av(r.pubkey) + '<span><b class=\"mono\" title=\"' + esc(r.pubkey) + '\">' + short(r.pubkey) + \"</b> \" + esc(r.reason || \"\") + \"</span>\" + ib(icon, label, act, r.pubkey) + \"</li>\";\n $(\"#bans\").innerHTML = bans.length ? bans.map((r) => person(r, \"undo\", \"Unban\", \"unrulepubkey\")).join(\"\") : '<li class=\"empty\">nobody banned</li>';\n $(\"#blocks\").innerHTML = blocks.length ? blocks.map((r) => '<li><i class=\"ev\">ip</i><span><b class=\"mono\">' + esc(r.ip) + \"</b> \" + esc(r.reason || \"\") + \"</span>\" + ib(\"undo\", \"Unblock\", \"unblockip\", r.ip) + \"</li>\").join(\"\") : '<li class=\"empty\">no addresses blocked</li>';\n $(\"#banned-events\").innerHTML = bannedEvents.map((r) => '<li><i class=\"ev\">ev</i><span><b class=\"mono\" title=\"' + esc(r.id) + '\">' + short(r.id) + \"</b> \" + esc(r.reason || \"\") + \"</span>\" + ib(\"check\", \"Allow again\", \"allowevent\", r.id) + \"</li>\").join(\"\");\n $(\"#reports tbody\").innerHTML = reports.length ? reports.map((r) => '<tr><td class=\"dim\">' + esc(fmtTime(r.at)) + '</td><td><span class=\"kind\">' + esc(r.type || \"report\") + '</span></td><td class=\"mono\" title=\"' + esc(r.target_pubkey) + '\">' + av(r.target_pubkey) + short(r.target_pubkey) + (r.target_event ? (r.blob ? ' <a class=\"dim\" href=\"/' + esc(r.target_event) + '\" target=\"_blank\" rel=\"noopener\">file ' + short(r.target_event) + \"</a>\" : ' <span class=\"dim\">ev ' + short(r.target_event) + \"</span>\") : \"\") + '</td><td class=\"c\">' + esc(r.content) + '</td><td class=\"r\">' + ib(\"check\", \"Dismiss\", \"resolve:dismiss\", r.id) + ib(\"trash\", r.blob ? \"Delete the file\" : \"Delete the event\", \"resolve:delete\", r.id) + ib(\"banuser\", \"Ban the author\", \"resolve:ban\", r.id, \"danger\") + \"</td></tr>\").join(\"\") : '<tr><td colspan=\"5\" class=\"muted\">nothing reported</td></tr>';\n $(\"#reports-count\").textContent = reports.length || \"\";\n $(\"#blobs tbody\").innerHTML = blobs.length ? blobs.map((b) => '<tr><td class=\"dim\">' + esc(fmtTime(b.uploaded)) + '</td><td class=\"mono\"><a href=\"' + esc(b.url) + '\" target=\"_blank\" rel=\"noopener\">' + short(b.sha256) + '</a> <span class=\"dim\">' + esc(b.type) + '</span></td><td class=\"mono\">' + fmtBytes(b.size) + '</td><td class=\"mono\" title=\"' + esc(b.uploader) + '\">' + av(b.uploader) + short(b.uploader) + '</td><td class=\"r\">' + ib(\"trash\", \"Delete file\", \"deleteblob\", b.sha256, \"danger\") + \"</td></tr>\").join(\"\") : '<tr><td colspan=\"5\" class=\"muted\">no uploads</td></tr>';\n $(\"#blobs-count\").textContent = blobs.length || \"\";\n const tag = (k, cls) => '<span class=\"tag ' + cls + '\">' + k + ib(\"x\", \"Remove rule\", \"unrulekind\", String(k)) + \"</span>\";\n $(\"#kinds-allow\").innerHTML = allow.length ? allow.map((k) => tag(k, \"ok\")).join(\"\") : '<span class=\"tag plain\">all</span>';\n $(\"#kinds-block\").innerHTML = block.length ? block.map((k) => tag(k, \"blk\")).join(\"\") : '<span class=\"tag plain\">none</span>';\n await loadPeople();\n }\n\n const KIND_NAMES = { 0: \"profiles\", 1: \"notes\", 3: \"contacts\", 4: \"DMs\", 5: \"deletions\", 6: \"reposts\", 7: \"reactions\", 16: \"reposts\", 1059: \"gift wraps\", 1063: \"file headers\", 1111: \"comments\", 1984: \"reports\", 9734: \"zap requests\", 9735: \"zap receipts\", 9802: \"highlights\", 10002: \"relay lists\", 13534: \"roster\", 9000: \"group adds\", 9001: \"group removals\", 9021: \"join requests\", 9022: \"leave requests\", 39000: \"group info\", 39001: \"group admins\", 39002: \"group members\", 39003: \"group roles\", 30023: \"articles\", 30024: \"drafts\", 30078: \"app data\", 30311: \"live events\", 30818: \"wiki\" };\n const kindName = (k) => KIND_NAMES[k] || (k >= 20000 && k < 30000 ? \"ephemeral\" : k >= 30000 && k < 40000 ? \"addressable\" : k >= 10000 && k < 20000 ? \"replaceable\" : \"kind \" + k);\n const SYS_KINDS = new Set([0, 3, 10002, 9735, 13534, 8000, 8001, 9000, 9001, 39000, 39001, 39002, 39003]);\n let storage = null;\n\n async function loadStorage() {\n storage = await rpc(\"storagestats\");\n pollPull();\n const st = storage;\n const top = st.kinds.slice(0, 4), rest = st.kinds.slice(4).reduce((a, k) => a + k.bytes, 0);\n const parts = [...top.map((k, i) => [k.kind + \" \" + kindName(k.kind), \"k\" + (i + 1), k.bytes]), ...(rest ? [[\"other\", \"k5\", rest]] : [])];\n const total = st.eventBytes || 1;\n $(\"#s-bar\").innerHTML = parts.length ? parts.map(([n, c, b]) => '<i class=\"' + c + '\" style=\"flex:' + b + '\" title=\"' + esc(n) + \" \" + fmtBytes(b) + '\"></i>').join(\"\") : '<i class=\"k5\" style=\"flex:1\"></i>';\n $(\"#s-legend\").innerHTML = parts.length ? parts.map(([n, c, b]) => '<span><i class=\"' + c + '\"></i>' + esc(n) + \" \" + Math.round(b * 100 / total) + \"%</span>\").join(\"\") : \"<span>no events yet</span>\";\n const overhead = Math.max(0, st.databaseBytes - st.eventBytes);\n $(\"#s-totals\").innerHTML = [[\"events\", st.events.toLocaleString() + \" <small>\" + fmtBytes(st.eventBytes) + \"</small>\"], [\"index and overhead\", fmtBytes(overhead)], [\"files\", st.blobs.toLocaleString() + \" <small>\" + fmtBytes(st.mediaBytes) + \"</small>\"]]\n .map(([k, v]) => \"<div><small>\" + k + \"</small><b>\" + v + \"</b></div>\").join(\"\");\n const any = st.retention.find((r) => r.kind === null);\n const own = (kind) => { const r = st.retention.find((x) => x.kind === kind); return r ? r.days : \"\"; };\n const row = (k) => {\n const key = k.kind === null ? \"\" : String(k.kind);\n const placeholder = k.kind === null ? \"forever\" : k.replaceable || !any ? \"forever\" : any.days + \" (everything else)\";\n const pill = k.kind === null ? \"everything else\" : '<span class=\"kind' + (k.protected ? ' sys\" title=\"required: the relay depends on this kind' : \"\") + '\">' + k.kind + \"</span> \" + esc(kindName(k.kind));\n const cells = k.protected\n ? '<td class=\"keep sys\" colspan=\"2\" title=\"The relay depends on this kind, so it is never expired or purged.\">required</td>'\n : '<td class=\"keep\"><input class=\"txt num days\" type=\"number\" min=\"0\" max=\"36500\" placeholder=\"' + placeholder + '\" value=\"' + own(k.kind) + '\"> days</td><td class=\"r\">' + ib(\"check\", \"Save keep-for rule\", \"saveretention\", key) + ib(\"trash\", \"Purge\", \"purgekind\", key, \"danger\") + \"</td>\";\n return '<tr class=\"' + (k.kind === null ? \"any\" : \"\") + '\" data-kind=\"' + key + '\"><td>' + pill + '</td><td class=\"r mono\">' + (k.n === undefined ? \"\" : k.n.toLocaleString()) + '</td><td class=\"r mono\">' + (k.bytes === undefined ? \"\" : fmtBytes(k.bytes)) + '</td><td class=\"dim\">' + (k.oldest ? esc(fmtDay(k.oldest)) : \"\") + \"</td>\" + cells + \"</tr>\";\n };\n $(\"#kinds tbody\").innerHTML = [...st.kinds, { kind: null, days: any ? any.days : 0 }].map(row).join(\"\");\n }\n\n async function loadEvents(reset) {\n const list = await rpc(\"listrecentevents\", reset ? 50 : 200);\n $(\"#events tbody\").innerHTML = list.length ? list.map((e) => '<tr><td class=\"dim\">' + esc(fmtTime(e.created_at)) + '</td><td><span class=\"kind' + (SYS_KINDS.has(e.kind) ? ' sys\" title=\"required: the relay depends on this kind' : \"\") + '\">' + e.kind + '</span></td><td class=\"mono\" title=\"' + e.pubkey + '\">' + av(e.pubkey) + short(e.pubkey) + '</td><td class=\"c\">' + esc(e.content) + '</td><td class=\"r\">' + ib(\"trash\", \"Delete event\", \"deleteevent\", e.id) + ib(\"ban\", \"Ban event\", \"banevent\", e.id, \"danger\") + ib(\"banuser\", \"Ban author\", \"banpubkey\", e.pubkey, \"danger\") + \"</td></tr>\").join(\"\") : '<tr><td colspan=\"5\" class=\"muted\">no events yet</td></tr>';\n $(\"#more\").classList.toggle(\"hidden\", list.length < 50);\n }\n\n // ---- actions ----\n const guard = (fn) => async (ev) => { ev.preventDefault(); const b = ev.submitter || ev.target; if (b) b.disabled = true; try { await fn(ev); } catch (e) { toast(e.message); } finally { if (b) b.disabled = false; } };\n const refresh = () => Promise.all([loadLists(), loadEvents(true), loadStorage()]);\n\n $(\"#copy\").onclick = async () => { await navigator.clipboard.writeText(wsURL); toast(\"copied \" + wsURL); };\n $(\"#signin\").onclick = guard(async () => {\n if (!window.nostr) throw new Error(\"No nostr extension found. Install one (Alby, nos2x, \u2026) and reload, or use a remote signer.\");\n me = await window.nostr.getPublicKey();\n localStorage.setItem(\"me\", me);\n renderHeader(); await loadAdmin(); await loadPeople();\n });\n $(\"#signin46\").onclick = () => showRemote(null);\n $$(\".remote\").forEach((b) => { b.onclick = () => showRemote($(\"#\" + b.dataset.note)); });\n $(\"#nccopy\").onclick = async () => { await navigator.clipboard.writeText($(\"#nclink\").href); toast(\"Link copied; paste it into your signer app\"); };\n $(\"#bunkerform\").onsubmit = guard(async (ev) => {\n $(\"#remotenote\").textContent = \"Connecting\u2026\";\n try { await connectBunker(ev.target.url.value); } catch (e) { $(\"#remotenote\").textContent = e.message; throw e; }\n $(\"#remotenote\").textContent = \"\"; ev.target.reset();\n await remoteDone();\n });\n $(\"#signout\").onclick = async () => {\n if (remote) { const r = remote; remote = null; try { await withTimeout(r.logout(), 5000, \"The signer\"); } catch { /* the session is gone either way */ } }\n localStorage.removeItem(\"nip46\");\n me = null; localStorage.removeItem(\"me\"); renderHeader(); await loadAdmin(); await loadPeople();\n };\n const claimNow = async (note) => {\n if (!signer.ready()) { note.textContent = \"Sign the claim with a nostr extension or a remote signer.\"; showRemote(note); return; }\n me = await signer.getPublicKey();\n const r = await rpc(\"claim\");\n if (!r.claimed) throw new Error(\"Somebody else claimed it first.\");\n localStorage.setItem(\"me\", me);\n toast(\"It's yours.\");\n await loadInfo(); await loadFuel(); await loadAdmin(); await loadPeople();\n if (r.converted && confirm(\"This relay began as a temporary one: anyone can write, and everything is deleted after 14 days. Switch to the default rules and keep everything from now on? Each rule can be changed later on the Rules and Storage tabs.\")) {\n await rpc(\"resetrules\"); toast(\"Rules reset\"); await loadInfo(); await loadAdmin();\n }\n };\n $(\"#claim\").onclick = guard(() => claimNow($(\"#claimnote\")));\n $(\"#claimlease\").onclick = guard(() => claimNow($(\"#leasenote\")));\n // Pulls run in the background; the note follows them while the tab is open.\n let pullTimer = 0, pullSeen = 0;\n async function pollPull() {\n clearTimeout(pullTimer);\n let st;\n try { st = await rpc(\"pullstatus\"); } catch { return; }\n const el = $(\"#pullnote\");\n if (st.running) {\n const r = st.running;\n el.textContent = \"Pulling from \" + r.url + \": \" + r.stored.toLocaleString() + \" events so far\" + (r.blobs ? \", \" + r.blobs + \" files\" : \"\") + \"\u2026\";\n pullTimer = setTimeout(pollPull, 3000);\n return;\n }\n const l = st.last;\n el.textContent = !l ? \"\" : l.error ? \"Pull from \" + l.url + \" failed: \" + l.error : \"Pulled \" + l.stored.toLocaleString() + \" events\" + (l.blobs ? \" and \" + l.blobs + \" files\" : \"\") + \" from \" + l.url + (l.skipped ? \" (\" + l.skipped + \" skipped)\" : \"\") + \", \" + fmtTime(l.finishedAt) + \".\";\n if (l && l.finishedAt > pullSeen) { const first = pullSeen === 0; pullSeen = l.finishedAt; if (!first && (l.stored || l.blobs)) await refresh(); }\n }\n $(\"#pullform\").onsubmit = guard(async (ev) => {\n const f = ev.target;\n await rpc(\"pullfrom\", f.url.value.trim());\n toast(\"Pull started\"); f.reset(); await pollPull();\n });\n // ---- presets: writes, reads, kinds and keep-for rules in one click ----\n let presets = null;\n async function renderPresets() {\n if (!presets) { try { presets = await rpc(\"listpresets\"); } catch { presets = []; } }\n $(\"#presets\").innerHTML = presets.map((p) => '<button class=\"btn\" data-preset=\"' + esc(p.name) + '\" title=\"' + esc(p.about) + '\">' + esc(p.title) + \"</button>\").join(\"\");\n }\n $(\"#presets\").addEventListener(\"click\", async (ev) => {\n const b = ev.target.closest(\"button[data-preset]\"); if (!b) return;\n const p = (presets || []).find((x) => x.name === b.dataset.preset); if (!p) return;\n if (!confirm(p.title + \": \" + p.about + \"\\n\\nThis replaces the writes and reads rules, the directory setting, the kind rules and the keep-for rules. Limits, identity, people and bans stay.\")) return;\n b.disabled = true;\n try { policy = await rpc(\"applypreset\", p.name); $(\"#presetnote\").textContent = \"Now: \" + p.about; toast(p.title + \" applied\"); await loadInfo(); await loadAdmin(); }\n catch (e) { toast(e.message); } finally { b.disabled = false; }\n });\n\n // ---- wire me in: this relay in the owner's own lists ----\n // The lists are replaceable, so a fresh list with only this relay would\n // clobber the real one once it spread. Every publish starts from the\n // newest copy found here or on the indexers, verified when the signer\n // library is around, and adds or removes this relay in it.\n const INDEXERS = [\"wss://purplepag.es\", \"wss://relay.nostr.band\", \"wss://relay.damus.io\", \"wss://nos.lol\"];\n const LISTS = [\n { kind: 10002, tag: \"r\", title: \"Relay list\", nip: \"NIP-65\", about: \"where clients read your notes and send you mentions\" },\n { kind: 10050, tag: \"relay\", title: \"DM inbox\", nip: \"NIP-17\", about: \"where people send you private messages\" },\n { kind: 10007, tag: \"relay\", title: \"Search relays\", nip: \"NIP-51\", about: \"where clients run your searches\" },\n { kind: 10063, tag: \"server\", title: \"Blossom servers\", nip: \"BUD-03\", about: \"where clients upload and look for your files\" },\n ];\n const mineFor = (l) => (l.tag === \"server\" ? location.origin : wsURL);\n const normURL = (u) => { try { const x = new URL(String(u).trim()); return (x.host + x.pathname).replace(/\\/+$/, \"\").toLowerCase(); } catch { return String(u).trim().toLowerCase(); } };\n const isMine = (l, t) => t[0] === l.tag && normURL(t[1] || \"\") === normURL(mineFor(l));\n const listHas = (list, l) => !!list && list.tags.some((t) => isMine(l, t));\n // mergeList is pure: the newest list with this relay put first or taken out, every other tag kept.\n function mergeList(list, l, include) {\n const tags = (list ? list.tags : []).filter((t) => !isMine(l, t));\n if (include) tags.unshift([l.tag, mineFor(l)]);\n return { kind: l.kind, created_at: Math.floor(Date.now() / 1000), content: list ? list.content : \"\", tags };\n }\n const relaysIn = (list) => list.tags.filter((t) => (t[0] === \"r\" || t[0] === \"relay\") && /^wss?:\\/\\//i.test(t[1] || \"\")).map((t) => t[1].trim());\n // overWS opens one socket, sends one message, feeds answers to onMessage until it says done or time runs out.\n function overWS(url, ms, first, onMessage) {\n return new Promise((res) => {\n let ws = null, done = false, out = null;\n const finish = () => { if (done) return; done = true; clearTimeout(timer); try { if (ws) ws.close(); } catch { /* closed */ } res(out); };\n const timer = setTimeout(finish, ms);\n try { ws = new WebSocket(url); } catch { return finish(); }\n ws.onopen = () => ws.send(JSON.stringify(first));\n ws.onmessage = (m) => { let d; try { d = JSON.parse(m.data); } catch { return; } if (!Array.isArray(d)) return; if (onMessage(d, (v) => { out = v; })) finish(); };\n ws.onerror = finish; ws.onclose = finish;\n });\n }\n function fetchNewest(url, filter, ms) {\n let best = null;\n return overWS(url, ms, [\"REQ\", \"w\", filter], (d, set) => {\n if (d[0] === \"EVENT\" && d[1] === \"w\" && d[2] && (!best || d[2].created_at > best.created_at)) { best = d[2]; set(best); }\n return (d[0] === \"EOSE\" || d[0] === \"CLOSED\") && d[1] === \"w\";\n });\n }\n function publishTo(url, event, ms) {\n return overWS(url, ms, [\"EVENT\", event], (d, set) => { if (d[0] === \"OK\" && d[1] === event.id) { set({ url, ok: !!d[2], msg: d[3] || \"\" }); return true; } return false; })\n .then((r) => r || { url, ok: false, msg: \"no answer\" });\n }\n // bridge signs a NIP-98 request to this relay's HTTP door, so the owner's own reads and writes pass any read rule.\n async function bridge(path, body) {\n const url = location.origin + path, raw = JSON.stringify(body);\n const ev = await signer.signEvent({ kind: 27235, created_at: Math.floor(Date.now() / 1000), content: \"\", tags: [[\"u\", url], [\"method\", \"POST\"], [\"payload\", await sha256hex(raw)]] });\n const resp = await fetch(url, { method: \"POST\", headers: { \"content-type\": \"application/json\", authorization: \"Nostr \" + btoa(JSON.stringify(ev)) }, body: raw });\n const json = await resp.json();\n if (!resp.ok) throw new Error(json.error || \"HTTP \" + resp.status);\n return json;\n }\n const wire = {};\n function renderWire() {\n $(\"#wire tbody\").innerHTML = LISTS.map((l) => {\n const w = wire[l.kind] || { state: \"\", results: [], list: null };\n const busy = w.state === \"checking\" || w.state === \"publishing\";\n const btn = (act, cls, label) => '<button class=\"btn ' + cls + '\" data-wire=\"' + l.kind + '\" data-do=\"' + act + '\"' + (busy ? \" disabled\" : \"\") + \">\" + label + \"</button>\";\n const buttons = w.state === \"listed\" ? btn(\"remove\", \"\", \"Remove me\") : btn(\"add\", \"pri\", \"Add me\") + \" \" + btn(\"check\", \"\", w.state ? \"Check again\" : \"Check\");\n const from = w.list ? \"<br><small class=\\\"muted\\\">\" + w.list.tags.filter((t) => t[0] === l.tag).length + \" entries, \" + fmtTime(w.list.created_at) + \"</small>\" : w.state && !busy ? '<br><small class=\"muted\">no list found</small>' : \"\";\n const sent = (w.results || []).map((r) => '<span class=\"' + (r.ok ? \"ok\" : \"bad\") + '\" title=\"' + esc(r.msg) + '\">' + esc(r.url.replace(/^wss?:\\/\\//, \"\")) + (r.ok ? \"\" : \" failed\") + \"</span>\").join(\", \");\n return \"<tr><td><b>\" + l.title + '</b> <small class=\"muted\">' + l.nip + \"</small><br><small>\" + l.about + \"</small></td><td>\" + esc(w.state) + from + \"</td><td>\" + sent + '</td><td class=\"acts\">' + buttons + \"</td></tr>\";\n }).join(\"\");\n }\n async function wireCheck(l) {\n const w = (wire[l.kind] = { state: \"checking\", list: null, results: [] }); renderWire();\n const filter = { kinds: [l.kind], authors: [me], limit: 1 };\n const found = [];\n try { found.push(...(await bridge(\"/query\", [filter]))); } catch { /* the relay may hold none */ }\n const remote = await Promise.all(INDEXERS.map((u) => fetchNewest(u, filter, 4000)));\n let lib = null; try { lib = await signerLib(); } catch { /* unverified lists are still the owner's own, by pubkey */ }\n for (const e of remote) if (e && e.pubkey === me && e.kind === l.kind && Array.isArray(e.tags) && (!lib || lib.verifyEvent(e))) found.push(e);\n w.list = found.sort((a, b) => b.created_at - a.created_at)[0] || null;\n w.state = listHas(w.list, l) ? \"listed\" : \"not listed\"; renderWire();\n }\n async function wirePublish(l, include) {\n if (!wire[l.kind] || !wire[l.kind].state || wire[l.kind].state === \"failed\") await wireCheck(l);\n const w = wire[l.kind]; w.state = \"publishing\"; w.results = []; renderWire();\n try {\n const signed = await signer.signEvent(mergeList(w.list, l, include));\n const here = await bridge(\"/events\", signed).then((r) => ({ url: wsURL, ok: !!r.accepted, msg: r.message || \"\" })).catch((e) => ({ url: wsURL, ok: false, msg: e.message }));\n const targets = [...new Set([...relaysIn(signed), ...INDEXERS])].filter((u) => normURL(u) !== normURL(wsURL));\n const rest = await Promise.all(targets.map((u) => publishTo(u, signed, 6000)));\n w.results = [here, ...rest]; w.list = signed; w.state = include ? \"listed\" : \"not listed\";\n toast((include ? \"Added to your \" : \"Removed from your \") + l.title.toLowerCase());\n } catch (e) { w.state = \"failed\"; w.results = [{ url: wsURL, ok: false, msg: e.message }]; toast(e.message); }\n renderWire();\n }\n $(\"#wire\").addEventListener(\"click\", (ev) => {\n const b = ev.target.closest(\"button[data-wire]\"); if (!b) return;\n const l = LISTS.find((x) => String(x.kind) === b.dataset.wire); if (!l) return;\n if (b.dataset.do === \"check\") wireCheck(l); else wirePublish(l, b.dataset.do === \"add\");\n });\n\n $(\"#access\").onsubmit = guard(async (ev) => {\n const f = ev.target;\n policy = await rpc(\"setpolicy\", { writes: f.writes.value, reads: f.reads.value, minPow: +f.minPow.value, maxFuture: +f.maxFuture.value, maxLimit: +f.maxLimit.value, maxSubs: +f.maxSubs.value, eventsPerMinute: +f.eventsPerMinute.value, reqsPerMinute: +f.reqsPerMinute.value, maxBlobMB: +f.maxBlobMB.value });\n toast(\"Rules saved\"); await loadInfo();\n });\n $(\"#identity\").onsubmit = guard(async (ev) => {\n const f = ev.target;\n const list = (v) => v.split(\",\").map((s) => s.trim()).filter(Boolean);\n policy = await rpc(\"setpolicy\", { name: f.name.value, contact: f.contact.value, description: f.description.value, icon: f.icon.value, joinTerms: f.joinTerms.value, directoryPublic: f.directoryPublic.checked,\n banner: f.banner.value, postingPolicy: f.postingPolicy.value, privacyPolicy: f.privacyPolicy.value, tags: list(f.tags.value), languageTags: list(f.languageTags.value), relayCountries: list(f.relayCountries.value) });\n toast(\"Identity saved\"); await loadInfo(); await loadPeople();\n });\n $(\"#addmember\").onsubmit = guard(async (ev) => {\n const f = ev.target; const pk = npubToHex(f.pubkey.value); if (!pk) throw new Error(\"That is not a pubkey.\");\n await rpc(\"setmember\", pk, { name: f.name.value.trim() || null, note: f.note.value }); f.reset(); toast(\"Member added\"); await loadLists();\n });\n $(\"#addblock\").onsubmit = guard(async (ev) => {\n const f = ev.target;\n await rpc(\"blockip\", f.ip.value.trim(), f.reason.value); f.reset(); toast(\"Blocked\"); await loadLists();\n });\n $(\"#addban\").onsubmit = guard(async (ev) => {\n const pk = npubToHex(ev.target.pubkey.value); if (!pk) throw new Error(\"That is not a pubkey.\");\n await rpc(\"banpubkey\", pk, ev.target.reason.value); ev.target.reset(); toast(\"Banned\"); await loadLists();\n });\n $(\"#mintinvite\").onsubmit = guard(async (ev) => {\n const f = ev.target;\n const inv = await rpc(\"createinvite\", +f.ttl.value, +f.max.value, f.note.value);\n await navigator.clipboard.writeText(location.origin + \"/invite/\" + inv.code).catch(() => {});\n f.note.value = \"\"; toast(\"Invite created and copied\"); await loadLists();\n });\n $(\"#kindform\").onsubmit = guard(async (ev) => {\n const rule = ev.submitter.value; const k = +ev.target.kind.value;\n await rpc(rule === \"allow\" ? \"allowkind\" : \"disallowkind\", k); ev.target.reset(); toast((rule === \"allow\" ? \"Allowed kind \" : \"Blocked kind \") + k); await loadLists();\n });\n $(\"#more\").onclick = guard(() => loadEvents(false));\n $(\"#exportcfg\").onclick = guard(async () => {\n const cfg = await rpc(\"exportconfig\");\n const a = document.createElement(\"a\");\n a.href = URL.createObjectURL(new Blob([JSON.stringify(cfg, null, 2)], { type: \"application/json\" }));\n a.download = host.split(\".\")[0] + \".bind.ws.config.json\";\n a.click(); URL.revokeObjectURL(a.href);\n });\n $(\"#importcfg\").onclick = () => $(\"#cfgfile\").click();\n $(\"#cfgfile\").onchange = guard(async (ev) => {\n const file = ev.target.files[0]; if (!file) return;\n const cfg = JSON.parse(await file.text());\n if (!confirm(\"Replace this relay's rules, identity, members, bans and kind rules with \" + file.name + \"?\")) { ev.target.value = \"\"; return; }\n await rpc(\"importconfig\", cfg); ev.target.value = \"\"; toast(\"Configuration imported\"); await loadInfo(); await loadAdmin(); await loadPeople();\n });\n $(\"#transfer\").onsubmit = guard(async (ev) => {\n const pk = ev.target.pubkey.value; if (!pk) return;\n const name = host.split(\".\")[0];\n const typed = prompt(\"This hands \" + host + \" to \" + short(pk) + \" for good. You stay on as a moderator. Type the relay name (\" + name + \") to confirm.\");\n if (typed === null) return;\n if (typed.trim() !== name) { toast(\"That didn't match; nothing changed.\"); return; }\n await rpc(\"transferowner\", pk);\n toast(\"Transferred\");\n await loadInfo(); await loadAdmin(); await loadPeople();\n });\n $(\"#deleterelay\").onclick = guard(async () => {\n const name = host.split(\".\")[0];\n const typed = prompt(\"This deletes everything on \" + host + \" and gives the name up. Type the relay name (\" + name + \") to confirm.\");\n if (typed === null) return;\n if (typed.trim() !== name) { toast(\"That didn't match; nothing was deleted.\"); return; }\n await rpc(\"deleterelay\", name);\n localStorage.removeItem(\"me\");\n location.href = \"/\";\n });\n $(\"#topup\").onsubmit = guard((ev) => topUp(+ev.target.sats.value));\n $(\"#inv-copy\").onclick = async () => { await navigator.clipboard.writeText($(\"#inv-text\").value); toast(\"invoice copied\"); };\n $$(\"button[data-copy]\").forEach((b) => { b.onclick = async () => { await navigator.clipboard.writeText(b.dataset.copy === \"ws\" ? wsURL : location.origin); toast(\"copied\"); }; });\n document.addEventListener(\"click\", async (ev) => {\n const b = ev.target.closest(\"button[data-act]\"); if (!b) return;\n const act = b.dataset.act, id = b.dataset.id;\n if (act === \"copy\") { await navigator.clipboard.writeText(id); toast(\"copied\"); return; }\n if ((act === \"banpubkey\" || act === \"banevent\" || act === \"resolve:ban\") && !confirm(act === \"banevent\" ? \"Delete this event and refuse it forever?\" : \"Ban this author and refuse everything they post?\")) return;\n if (act === \"deleteblob\" && !confirm(\"Delete this file for good?\")) return;\n if (act === \"purgekind\") {\n const kind = id === \"\" ? null : +id;\n const label = kind === null ? \"everything without its own rule\" : kindName(kind) + \" (kind \" + kind + \")\";\n const typed = prompt(\"Purge \" + label + \" older than how many days? 0 purges all of them. This cannot be undone.\", \"30\");\n if (typed === null) return;\n const days = Math.max(0, Math.floor(+typed || 0));\n b.disabled = true;\n try { const r = await rpc(\"purgekind\", kind, days); toast(\"Purged \" + r.deleted.toLocaleString() + \" events\"); await refresh(); } catch (e) { toast(e.message); } finally { b.disabled = false; }\n return;\n }\n if (act === \"saveretention\") {\n const kind = id === \"\" ? null : +id;\n const days = Math.max(0, Math.floor(+b.closest(\"tr\").querySelector(\".days\").value || 0));\n b.disabled = true;\n try { await rpc(\"setretention\", kind, days); toast(days ? \"Kept for \" + days + \" days\" : \"Kept forever\"); await loadStorage(); await loadInfo(); } catch (e) { toast(e.message); } finally { b.disabled = false; }\n return;\n }\n b.disabled = true;\n try {\n if (act === \"savemember\") { const tr = b.closest(\"tr\"), role = tr.querySelector(\"select.role\"); await rpc(\"setmember\", id, { name: tr.querySelector(\".name\").value.trim() || null, note: tr.querySelector(\".note\").value, ...(role ? { role: role.value } : {}) }); }\n else if (act.startsWith(\"resolve:\")) await rpc(\"resolvereport\", id, act.slice(8));\n else await rpc(act, act === \"unrulekind\" ? +id : id, \"\");\n toast(\"Done\"); await refresh();\n } catch (e) { toast(e.message); } finally { b.disabled = false; }\n });\n\n // ---- boot ----\n try { await loadInfo(); } catch { renderHeader(); }\n await loadFuel();\n me = localStorage.getItem(\"me\");\n if (me && await resumeRemote()) { /* a remote session answers for itself when first used */ }\n else if (me && window.nostr) { try { const pk = await window.nostr.getPublicKey(); if (pk !== me) me = null; } catch { me = null; } }\n else if (me) me = null;\n renderHeader();\n await loadAdmin();\n await loadPeople();\n})();\n";@@ -1,3 +1,3 @@
11 // Generated by scripts/build-signer.mjs from scripts/signer-entry.js. Do not edit; run npm run build:signer.2-export const SIGNER_HASH = "7b981336eb13";3-export const SIGNER_JS = "\"use strict\";(()=>{var bo=Object.defineProperty;var po=(t,e,n)=>e in t?bo(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var u=(t,e,n)=>po(t,typeof e!=\"symbol\"?e+\"\":e,n);function je(t){return t instanceof Uint8Array||ArrayBuffer.isView(t)&&t.constructor.name===\"Uint8Array\"}function fe(t,e=\"\"){if(!Number.isSafeInteger(t)||t<0){let n=e&&`\"${e}\" `;throw new Error(`${n}expected integer >= 0, got ${t}`)}}function N(t,e,n=\"\"){let r=je(t),o=t?.length,s=e!==void 0;if(!r||s&&o!==e){let i=n&&`\"${n}\" `,c=s?` of length ${e}`:\"\",a=r?`length=${o}`:`type=${typeof t}`;throw new Error(i+\"expected Uint8Array\"+c+\", got \"+a)}return t}function Qe(t){if(typeof t!=\"function\"||typeof t.create!=\"function\")throw new Error(\"Hash must wrapped by utils.createHasher\");fe(t.outputLen),fe(t.blockLen)}function Ie(t,e=!0){if(t.destroyed)throw new Error(\"Hash instance has been destroyed\");if(e&&t.finished)throw new Error(\"Hash#digest() has already been called\")}function Rn(t,e){N(t,void 0,\"digestInto() output\");let n=e.outputLen;if(t.length<n)throw new Error('\"digestInto() output\" expected to be of length >='+n)}function Se(...t){for(let e=0;e<t.length;e++)t[e].fill(0)}function et(t){return new DataView(t.buffer,t.byteOffset,t.byteLength)}function se(t,e){return t<<32-e|t>>>e}var On=typeof Uint8Array.from([]).toHex==\"function\"&&typeof Uint8Array.fromHex==\"function\",xo=Array.from({length:256},(t,e)=>e.toString(16).padStart(2,\"0\"));function Le(t){if(N(t),On)return t.toHex();let e=\"\";for(let n=0;n<t.length;n++)e+=xo[t[n]];return e}var le={_0:48,_9:57,A:65,F:70,a:97,f:102};function Hn(t){if(t>=le._0&&t<=le._9)return t-le._0;if(t>=le.A&&t<=le.F)return t-(le.A-10);if(t>=le.a&&t<=le.f)return t-(le.a-10)}function Te(t){if(typeof t!=\"string\")throw new Error(\"hex string expected, got \"+typeof t);if(On)return Uint8Array.fromHex(t);let e=t.length,n=e/2;if(e%2)throw new Error(\"hex string expected, got unpadded hex of length \"+e);let r=new Uint8Array(n);for(let o=0,s=0;o<n;o++,s+=2){let i=Hn(t.charCodeAt(s)),c=Hn(t.charCodeAt(s+1));if(i===void 0||c===void 0){let a=t[s]+t[s+1];throw new Error('hex string expected, got non-hex character \"'+a+'\" at index '+s)}r[o]=i*16+c}return r}function te(...t){let e=0;for(let r=0;r<t.length;r++){let o=t[r];N(o),e+=o.length}let n=new Uint8Array(e);for(let r=0,o=0;r<t.length;r++){let s=t[r];n.set(s,o),o+=s.length}return n}function Un(t,e={}){let n=(o,s)=>t(s).update(o).digest(),r=t(void 0);return n.outputLen=r.outputLen,n.blockLen=r.blockLen,n.create=o=>t(o),Object.assign(n,e),Object.freeze(n)}function He(t=32){let e=typeof globalThis==\"object\"?globalThis.crypto:null;if(typeof e?.getRandomValues!=\"function\")throw new Error(\"crypto.getRandomValues must be defined\");return e.getRandomValues(new Uint8Array(t))}var _n=t=>({oid:Uint8Array.from([6,9,96,134,72,1,101,3,4,2,t])});function In(t,e,n){return t&e^~t&n}function kn(t,e,n){return t&e^t&n^e&n}var tt=class{constructor(e,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=e,this.outputLen=n,this.padOffset=r,this.isLE=o,this.buffer=new Uint8Array(e),this.view=et(this.buffer)}update(e){Ie(this),N(e);let{view:n,buffer:r,blockLen:o}=this,s=e.length;for(let i=0;i<s;){let c=Math.min(o-this.pos,s-i);if(c===o){let a=et(e);for(;o<=s-i;i+=o)this.process(a,i);continue}r.set(e.subarray(i,i+c),this.pos),this.pos+=c,i+=c,this.pos===o&&(this.process(n,0),this.pos=0)}return this.length+=e.length,this.roundClean(),this}digestInto(e){Ie(this),Rn(e,this),this.finished=!0;let{buffer:n,view:r,blockLen:o,isLE:s}=this,{pos:i}=this;n[i++]=128,Se(this.buffer.subarray(i)),this.padOffset>o-i&&(this.process(r,0),i=0);for(let l=i;l<o;l++)n[l]=0;r.setBigUint64(o-8,BigInt(this.length*8),s),this.process(r,0);let c=et(e),a=this.outputLen;if(a%4)throw new Error(\"_sha2: outputLen must be aligned to 32bit\");let f=a/4,h=this.get();if(f>h.length)throw new Error(\"_sha2: outputLen bigger than state\");for(let l=0;l<f;l++)c.setUint32(4*l,h[l],s)}digest(){let{buffer:e,outputLen:n}=this;this.digestInto(e);let r=e.slice(0,n);return this.destroy(),r}_cloneInto(e){e||(e=new this.constructor),e.set(...this.get());let{blockLen:n,buffer:r,length:o,finished:s,destroyed:i,pos:c}=this;return e.destroyed=i,e.finished=s,e.length=o,e.pos=c,o%n&&e.buffer.set(r),e}clone(){return this._cloneInto()}},he=Uint32Array.from([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]);var yo=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]),me=new Uint32Array(64),Rt=class extends tt{constructor(e){super(64,e,8,!1)}get(){let{A:e,B:n,C:r,D:o,E:s,F:i,G:c,H:a}=this;return[e,n,r,o,s,i,c,a]}set(e,n,r,o,s,i,c,a){this.A=e|0,this.B=n|0,this.C=r|0,this.D=o|0,this.E=s|0,this.F=i|0,this.G=c|0,this.H=a|0}process(e,n){for(let l=0;l<16;l++,n+=4)me[l]=e.getUint32(n,!1);for(let l=16;l<64;l++){let x=me[l-15],d=me[l-2],y=se(x,7)^se(x,18)^x>>>3,m=se(d,17)^se(d,19)^d>>>10;me[l]=m+me[l-7]+y+me[l-16]|0}let{A:r,B:o,C:s,D:i,E:c,F:a,G:f,H:h}=this;for(let l=0;l<64;l++){let x=se(c,6)^se(c,11)^se(c,25),d=h+x+In(c,a,f)+yo[l]+me[l]|0,m=(se(r,2)^se(r,13)^se(r,22))+kn(r,o,s)|0;h=f,f=a,a=c,c=i+d|0,i=s,s=o,o=r,r=d+m|0}r=r+this.A|0,o=o+this.B|0,s=s+this.C|0,i=i+this.D|0,c=c+this.E|0,a=a+this.F|0,f=f+this.G|0,h=h+this.H|0,this.set(r,o,s,i,c,a,f,h)}roundClean(){Se(me)}destroy(){this.set(0,0,0,0,0,0,0,0),Se(this.buffer)}},Ot=class extends Rt{constructor(){super(32);u(this,\"A\",he[0]|0);u(this,\"B\",he[1]|0);u(this,\"C\",he[2]|0);u(this,\"D\",he[3]|0);u(this,\"E\",he[4]|0);u(this,\"F\",he[5]|0);u(this,\"G\",he[6]|0);u(this,\"H\",he[7]|0)}};var nt=Un(()=>new Ot,_n(1));var _t=BigInt(0),Ut=BigInt(1);function We(t,e=\"\"){if(typeof t!=\"boolean\"){let n=e&&`\"${e}\" `;throw new Error(n+\"expected boolean, got type=\"+typeof t)}return t}function Cn(t){if(typeof t==\"bigint\"){if(!rt(t))throw new Error(\"positive bigint expected, got \"+t)}else fe(t);return t}function Ge(t){let e=Cn(t).toString(16);return e.length&1?\"0\"+e:e}function Nn(t){if(typeof t!=\"string\")throw new Error(\"hex string expected, got \"+typeof t);return t===\"\"?_t:BigInt(\"0x\"+t)}function Ee(t){return Nn(Le(t))}function It(t){return Nn(Le(go(N(t)).reverse()))}function ot(t,e){fe(e),t=Cn(t);let n=Te(t.toString(16).padStart(e*2,\"0\"));if(n.length!==e)throw new Error(\"number too large\");return n}function kt(t,e){return ot(t,e).reverse()}function go(t){return Uint8Array.from(t)}function Pn(t){return Uint8Array.from(t,(e,n)=>{let r=e.charCodeAt(0);if(e.length!==1||r>127)throw new Error(`string contains non-ASCII character \"${t[n]}\" with code ${r} at position ${n}`);return r})}var rt=t=>typeof t==\"bigint\"&&_t<=t;function wo(t,e,n){return rt(t)&&rt(e)&&rt(n)&&e<=t&&t<n}function qn(t,e,n,r){if(!wo(e,n,r))throw new Error(\"expected valid \"+t+\": \"+n+\" <= n < \"+r+\", got \"+e)}function Ct(t){let e;for(e=0;t>_t;t>>=Ut,e+=1);return e}var Ze=t=>(Ut<<BigInt(t))-Ut;function Dn(t,e,n){if(fe(t,\"hashLen\"),fe(e,\"qByteLen\"),typeof n!=\"function\")throw new Error(\"hmacFn must be a function\");let r=E=>new Uint8Array(E),o=Uint8Array.of(),s=Uint8Array.of(0),i=Uint8Array.of(1),c=1e3,a=r(t),f=r(t),h=0,l=()=>{a.fill(1),f.fill(0),h=0},x=(...E)=>n(f,te(a,...E)),d=(E=o)=>{f=x(s,E),a=x(),E.length!==0&&(f=x(i,E),a=x())},y=()=>{if(h++>=c)throw new Error(\"drbg: tried max amount of iterations\");let E=0,S=[];for(;E<e;){a=x();let U=a.slice();S.push(U),E+=a.length}return te(...S)};return(E,S)=>{l(),d(E);let U;for(;!(U=S(y()));)d();return l(),U}}function ze(t,e={},n={}){if(!t||typeof t!=\"object\")throw new Error(\"expected valid options object\");function r(s,i,c){let a=t[s];if(c&&a===void 0)return;let f=typeof a;if(f!==i||a===null)throw new Error(`param \"${s}\" is invalid: expected ${i}, got ${f}`)}let o=(s,i)=>Object.entries(s).forEach(([c,a])=>r(c,a,i));o(e,!1),o(n,!0)}function Nt(t){let e=new WeakMap;return(n,...r)=>{let o=e.get(n);if(o!==void 0)return o;let s=t(n,...r);return e.set(n,s),s}}var ee=BigInt(0),J=BigInt(1),Re=BigInt(2),Mn=BigInt(3),Kn=BigInt(4),jn=BigInt(5),mo=BigInt(7),Wn=BigInt(8),Eo=BigInt(9),Gn=BigInt(16);function ie(t,e){let n=t%e;return n>=ee?n:e+n}function re(t,e,n){let r=t;for(;e-- >ee;)r*=r,r%=n;return r}function $n(t,e){if(t===ee)throw new Error(\"invert: expected non-zero number\");if(e<=ee)throw new Error(\"invert: expected positive modulus, got \"+e);let n=ie(t,e),r=e,o=ee,s=J,i=J,c=ee;for(;n!==ee;){let f=r/n,h=r%n,l=o-i*f,x=s-c*f;r=n,n=h,o=i,s=c,i=l,c=x}if(r!==J)throw new Error(\"invert: does not exist\");return ie(o,e)}function qt(t,e,n){if(!t.eql(t.sqr(e),n))throw new Error(\"Cannot find square root\")}function Zn(t,e){let n=(t.ORDER+J)/Kn,r=t.pow(e,n);return qt(t,r,e),r}function vo(t,e){let n=(t.ORDER-jn)/Wn,r=t.mul(e,Re),o=t.pow(r,n),s=t.mul(e,o),i=t.mul(t.mul(s,Re),o),c=t.mul(s,t.sub(i,t.ONE));return qt(t,c,e),c}function Bo(t){let e=ke(t),n=zn(t),r=n(e,e.neg(e.ONE)),o=n(e,r),s=n(e,e.neg(r)),i=(t+mo)/Gn;return(c,a)=>{let f=c.pow(a,i),h=c.mul(f,r),l=c.mul(f,o),x=c.mul(f,s),d=c.eql(c.sqr(h),a),y=c.eql(c.sqr(l),a);f=c.cmov(f,h,d),h=c.cmov(x,l,y);let m=c.eql(c.sqr(h),a),E=c.cmov(f,h,m);return qt(c,E,a),E}}function zn(t){if(t<Mn)throw new Error(\"sqrt is not defined for small field\");let e=t-J,n=0;for(;e%Re===ee;)e/=Re,n++;let r=Re,o=ke(t);for(;Vn(o,r)===1;)if(r++>1e3)throw new Error(\"Cannot find square root: probably non-prime P\");if(n===1)return Zn;let s=o.pow(r,e),i=(e+J)/Re;return function(a,f){if(a.is0(f))return f;if(Vn(a,f)!==1)throw new Error(\"Cannot find square root\");let h=n,l=a.mul(a.ONE,s),x=a.pow(f,e),d=a.pow(f,i);for(;!a.eql(x,a.ONE);){if(a.is0(x))return a.ZERO;let y=1,m=a.sqr(x);for(;!a.eql(m,a.ONE);)if(y++,m=a.sqr(m),y===h)throw new Error(\"Cannot find square root\");let E=J<<BigInt(h-y-1),S=a.pow(l,E);h=y,l=a.sqr(S),x=a.mul(x,l),d=a.mul(d,S)}return d}}function Ao(t){return t%Kn===Mn?Zn:t%Wn===jn?vo:t%Gn===Eo?Bo(t):zn(t)}var So=[\"create\",\"isValid\",\"is0\",\"neg\",\"inv\",\"sqrt\",\"sqr\",\"eql\",\"add\",\"sub\",\"mul\",\"pow\",\"div\",\"addN\",\"subN\",\"mulN\",\"sqrN\"];function Dt(t){let e={ORDER:\"bigint\",BYTES:\"number\",BITS:\"number\"},n=So.reduce((r,o)=>(r[o]=\"function\",r),e);return ze(t,n),t}function Lo(t,e,n){if(n<ee)throw new Error(\"invalid exponent, negatives unsupported\");if(n===ee)return t.ONE;if(n===J)return e;let r=t.ONE,o=e;for(;n>ee;)n&J&&(r=t.mul(r,o)),o=t.sqr(o),n>>=J;return r}function st(t,e,n=!1){let r=new Array(e.length).fill(n?t.ZERO:void 0),o=e.reduce((i,c,a)=>t.is0(c)?i:(r[a]=i,t.mul(i,c)),t.ONE),s=t.inv(o);return e.reduceRight((i,c,a)=>t.is0(c)?i:(r[a]=t.mul(i,r[a]),t.mul(i,c)),s),r}function Vn(t,e){let n=(t.ORDER-J)/Re,r=t.pow(e,n),o=t.eql(r,t.ONE),s=t.eql(r,t.ZERO),i=t.eql(r,t.neg(t.ONE));if(!o&&!s&&!i)throw new Error(\"invalid Legendre symbol result\");return o?1:s?0:-1}function To(t,e){e!==void 0&&fe(e);let n=e!==void 0?e:t.toString(2).length,r=Math.ceil(n/8);return{nBitLength:n,nByteLength:r}}var Pt=class{constructor(e,n={}){u(this,\"ORDER\");u(this,\"BITS\");u(this,\"BYTES\");u(this,\"isLE\");u(this,\"ZERO\",ee);u(this,\"ONE\",J);u(this,\"_lengths\");u(this,\"_sqrt\");u(this,\"_mod\");if(e<=ee)throw new Error(\"invalid field: expected ORDER > 0, got \"+e);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}=To(e,r);if(s>2048)throw new Error(\"invalid field: expected ORDER of <= 2048 bytes\");this.ORDER=e,this.BITS=o,this.BYTES=s,this._sqrt=void 0,Object.preventExtensions(this)}create(e){return ie(e,this.ORDER)}isValid(e){if(typeof e!=\"bigint\")throw new Error(\"invalid field element: expected bigint, got \"+typeof e);return ee<=e&&e<this.ORDER}is0(e){return e===ee}isValidNot0(e){return!this.is0(e)&&this.isValid(e)}isOdd(e){return(e&J)===J}neg(e){return ie(-e,this.ORDER)}eql(e,n){return e===n}sqr(e){return ie(e*e,this.ORDER)}add(e,n){return ie(e+n,this.ORDER)}sub(e,n){return ie(e-n,this.ORDER)}mul(e,n){return ie(e*n,this.ORDER)}pow(e,n){return Lo(this,e,n)}div(e,n){return ie(e*$n(n,this.ORDER),this.ORDER)}sqrN(e){return e*e}addN(e,n){return e+n}subN(e,n){return e-n}mulN(e,n){return e*n}inv(e){return $n(e,this.ORDER)}sqrt(e){return this._sqrt||(this._sqrt=Ao(this.ORDER)),this._sqrt(this,e)}toBytes(e){return this.isLE?kt(e,this.BYTES):ot(e,this.BYTES)}fromBytes(e,n=!1){N(e);let{_lengths:r,BYTES:o,isLE:s,ORDER:i,_mod:c}=this;if(r){if(!r.includes(e.length)||e.length>o)throw new Error(\"Field.fromBytes: expected \"+r+\" bytes, got \"+e.length);let f=new Uint8Array(o);f.set(e,s?0:f.length-e.length),e=f}if(e.length!==o)throw new Error(\"Field.fromBytes: expected \"+o+\" bytes, got \"+e.length);let a=s?It(e):Ee(e);if(c&&(a=ie(a,i)),!n&&!this.isValid(a))throw new Error(\"invalid field element: outside of range 0..ORDER\");return a}invertBatch(e){return st(this,e)}cmov(e,n,r){return r?n:e}};function ke(t,e={}){return new Pt(t,e)}function Yn(t){if(typeof t!=\"bigint\")throw new Error(\"field order must be bigint\");let e=t.toString(2).length;return Math.ceil(e/8)}function $t(t){let e=Yn(t);return e+Math.ceil(e/2)}function it(t,e,n=!1){N(t);let r=t.length,o=Yn(e),s=$t(e);if(r<16||r<s||r>1024)throw new Error(\"expected \"+s+\"-1024 bytes of input, got \"+r);let i=n?It(t):Ee(t),c=ie(i,e-J)+J;return n?kt(c,o):ot(c,o)}var Ce=BigInt(0),Oe=BigInt(1);function Ye(t,e){let n=e.negate();return t?n:e}function jt(t,e){let n=st(t.Fp,e.map(r=>r.Z));return e.map((r,o)=>t.fromAffine(r.toAffine(n[o])))}function Qn(t,e){if(!Number.isSafeInteger(t)||t<=0||t>e)throw new Error(\"invalid window size, expected [1..\"+e+\"], got W=\"+t)}function Vt(t,e){Qn(t,e);let n=Math.ceil(e/t)+1,r=2**(t-1),o=2**t,s=Ze(t),i=BigInt(t);return{windows:n,windowSize:r,mask:s,maxNumber:o,shiftBy:i}}function Fn(t,e,n){let{windowSize:r,mask:o,maxNumber:s,shiftBy:i}=n,c=Number(t&o),a=t>>i;c>r&&(c-=s,a+=Oe);let f=e*r,h=f+Math.abs(c)-1,l=c===0,x=c<0,d=e%2!==0;return{nextN:a,offset:h,isZero:l,isNeg:x,isNegF:d,offsetF:f}}var Mt=new WeakMap,er=new WeakMap;function Kt(t){return er.get(t)||1}function Xn(t){if(t!==Ce)throw new Error(\"invalid wNAF\")}var ct=class{constructor(e,n){u(this,\"BASE\");u(this,\"ZERO\");u(this,\"Fn\");u(this,\"bits\");this.BASE=e.BASE,this.ZERO=e.ZERO,this.Fn=e.Fn,this.bits=n}_unsafeLadder(e,n,r=this.ZERO){let o=e;for(;n>Ce;)n&Oe&&(r=r.add(o)),o=o.double(),n>>=Oe;return r}precomputeWindow(e,n){let{windows:r,windowSize:o}=Vt(n,this.bits),s=[],i=e,c=i;for(let a=0;a<r;a++){c=i,s.push(c);for(let f=1;f<o;f++)c=c.add(i),s.push(c);i=c.double()}return s}wNAF(e,n,r){if(!this.Fn.isValid(r))throw new Error(\"invalid scalar\");let o=this.ZERO,s=this.BASE,i=Vt(e,this.bits);for(let c=0;c<i.windows;c++){let{nextN:a,offset:f,isZero:h,isNeg:l,isNegF:x,offsetF:d}=Fn(r,c,i);r=a,h?s=s.add(Ye(x,n[d])):o=o.add(Ye(l,n[f]))}return Xn(r),{p:o,f:s}}wNAFUnsafe(e,n,r,o=this.ZERO){let s=Vt(e,this.bits);for(let i=0;i<s.windows&&r!==Ce;i++){let{nextN:c,offset:a,isZero:f,isNeg:h}=Fn(r,i,s);if(r=c,!f){let l=n[a];o=o.add(h?l.negate():l)}}return Xn(r),o}getPrecomputes(e,n,r){let o=Mt.get(n);return o||(o=this.precomputeWindow(n,e),e!==1&&(typeof r==\"function\"&&(o=r(o)),Mt.set(n,o))),o}cached(e,n,r){let o=Kt(e);return this.wNAF(o,this.getPrecomputes(o,e,r),n)}unsafe(e,n,r,o){let s=Kt(e);return s===1?this._unsafeLadder(e,n,o):this.wNAFUnsafe(s,this.getPrecomputes(s,e,r),n,o)}createCache(e,n){Qn(n,this.bits),er.set(e,n),Mt.delete(e)}hasCache(e){return Kt(e)!==1}};function tr(t,e,n,r){let o=e,s=t.ZERO,i=t.ZERO;for(;n>Ce||r>Ce;)n&Oe&&(s=s.add(o)),r&Oe&&(i=i.add(o)),o=o.double(),n>>=Oe,r>>=Oe;return{p1:s,p2:i}}function Jn(t,e,n){if(e){if(e.ORDER!==t)throw new Error(\"Field.ORDER must match order: Fp == p, Fn == n\");return Dt(e),e}else return ke(t,{isLE:n})}function nr(t,e,n={},r){if(r===void 0&&(r=t===\"edwards\"),!e||typeof e!=\"object\")throw new Error(`expected valid ${t} CURVE object`);for(let a of[\"p\",\"n\",\"h\"]){let f=e[a];if(!(typeof f==\"bigint\"&&f>Ce))throw new Error(`CURVE.${a} must be positive bigint`)}let o=Jn(e.p,n.Fp,r),s=Jn(e.n,n.Fn,r),c=[\"Gx\",\"Gy\",\"a\",t===\"weierstrass\"?\"b\":\"d\"];for(let a of c)if(!o.isValid(e[a]))throw new Error(`CURVE.${a} must be valid field element of CURVE.Fp`);return e=Object.freeze(Object.assign({},e)),{CURVE:e,Fp:o,Fn:s}}function at(t,e){return function(r){let o=t(r);return{secretKey:o,publicKey:e(o)}}}var ft=class{constructor(e,n){u(this,\"oHash\");u(this,\"iHash\");u(this,\"blockLen\");u(this,\"outputLen\");u(this,\"finished\",!1);u(this,\"destroyed\",!1);if(Qe(e),N(n,void 0,\"key\"),this.iHash=e.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?e.create().update(n).digest():n);for(let s=0;s<o.length;s++)o[s]^=54;this.iHash.update(o),this.oHash=e.create();for(let s=0;s<o.length;s++)o[s]^=106;this.oHash.update(o),Se(o)}update(e){return Ie(this),this.iHash.update(e),this}digestInto(e){Ie(this),N(e,this.outputLen,\"output\"),this.finished=!0,this.iHash.digestInto(e),this.oHash.update(e),this.oHash.digestInto(e),this.destroy()}digest(){let e=new Uint8Array(this.oHash.outputLen);return this.digestInto(e),e}_cloneInto(e){e||(e=Object.create(Object.getPrototypeOf(this),{}));let{oHash:n,iHash:r,finished:o,destroyed:s,blockLen:i,outputLen:c}=this;return e=e,e.finished=o,e.destroyed=s,e.blockLen=i,e.outputLen=c,e.oHash=n._cloneInto(e.oHash),e.iHash=r._cloneInto(e.iHash),e}clone(){return this._cloneInto()}destroy(){this.destroyed=!0,this.oHash.destroy(),this.iHash.destroy()}},Wt=(t,e,n)=>new ft(t,e).update(n).digest();Wt.create=(t,e)=>new ft(t,e);var rr=(t,e)=>(t+(t>=0?e:-e)/or)/e;function Ho(t,e,n){let[[r,o],[s,i]]=e,c=rr(i*t,n),a=rr(-o*t,n),f=t-c*r-a*s,h=-c*o-a*i,l=f<de,x=h<de;l&&(f=-f),x&&(h=-h);let d=Ze(Math.ceil(Ct(n)/2))+Ne;if(f<de||f>=d||h<de||h>=d)throw new Error(\"splitScalar (endomorphism): failed, k=\"+t);return{k1neg:l,k1:f,k2neg:x,k2:h}}function Zt(t){if(![\"compact\",\"recovered\",\"der\"].includes(t))throw new Error('Signature format must be \"compact\", \"recovered\", or \"der\"');return t}function Gt(t,e){let n={};for(let r of Object.keys(e))n[r]=t[r]===void 0?e[r]:t[r];return We(n.lowS,\"lowS\"),We(n.prehash,\"prehash\"),n.format!==void 0&&Zt(n.format),n}var zt=class extends Error{constructor(e=\"\"){super(e)}},ve={Err:zt,_tlv:{encode:(t,e)=>{let{Err:n}=ve;if(t<0||t>256)throw new n(\"tlv.encode: wrong tag\");if(e.length&1)throw new n(\"tlv.encode: unpadded data\");let r=e.length/2,o=Ge(r);if(o.length/2&128)throw new n(\"tlv.encode: long form length too big\");let s=r>127?Ge(o.length/2|128):\"\";return Ge(t)+s+o+e},decode(t,e){let{Err:n}=ve,r=0;if(t<0||t>256)throw new n(\"tlv.encode: wrong tag\");if(e.length<2||e[r++]!==t)throw new n(\"tlv.decode: wrong tlv\");let o=e[r++],s=!!(o&128),i=0;if(!s)i=o;else{let a=o&127;if(!a)throw new n(\"tlv.decode(long): indefinite length not supported\");if(a>4)throw new n(\"tlv.decode(long): byte length is too big\");let f=e.subarray(r,r+a);if(f.length!==a)throw new n(\"tlv.decode: length bytes not complete\");if(f[0]===0)throw new n(\"tlv.decode(long): zero leftmost byte\");for(let h of f)i=i<<8|h;if(r+=a,i<128)throw new n(\"tlv.decode(long): not minimal encoding\")}let c=e.subarray(r,r+i);if(c.length!==i)throw new n(\"tlv.decode: wrong value length\");return{v:c,l:e.subarray(r+i)}}},_int:{encode(t){let{Err:e}=ve;if(t<de)throw new e(\"integer: negative integers are not allowed\");let n=Ge(t);if(Number.parseInt(n[0],16)&8&&(n=\"00\"+n),n.length&1)throw new e(\"unexpected DER parsing assertion: unpadded hex\");return n},decode(t){let{Err:e}=ve;if(t[0]&128)throw new e(\"invalid signature integer: negative\");if(t[0]===0&&!(t[1]&128))throw new e(\"invalid signature integer: unnecessary leading zero\");return Ee(t)}},toSig(t){let{Err:e,_int:n,_tlv:r}=ve,o=N(t,void 0,\"signature\"),{v:s,l:i}=r.decode(48,o);if(i.length)throw new e(\"invalid signature: left bytes after parsing\");let{v:c,l:a}=r.decode(2,s),{v:f,l:h}=r.decode(2,a);if(h.length)throw new e(\"invalid signature: left bytes after parsing\");return{r:n.decode(c),s:n.decode(f)}},hexFromSig(t){let{_tlv:e,_int:n}=ve,r=e.encode(2,n.encode(t.r)),o=e.encode(2,n.encode(t.s)),s=r+o;return e.encode(48,s)}},de=BigInt(0),Ne=BigInt(1),or=BigInt(2),ut=BigInt(3),Ro=BigInt(4);function sr(t,e={}){let n=nr(\"weierstrass\",t,e),{Fp:r,Fn:o}=n,s=n.CURVE,{h:i,n:c}=s;ze(e,{},{allowInfinityPoint:\"boolean\",clearCofactor:\"function\",isTorsionFree:\"function\",fromBytes:\"function\",toBytes:\"function\",endo:\"object\"});let{endo:a}=e;if(a&&(!r.is0(s.a)||typeof a.beta!=\"bigint\"||!Array.isArray(a.basises)))throw new Error('invalid endo: expected \"beta\": bigint and \"basises\": array');let f=cr(r,o);function h(){if(!r.isOdd)throw new Error(\"compression is not supported: Field does not have .isOdd()\")}function l(R,p,b){let{x:w,y:v}=p.toAffine(),A=r.toBytes(w);if(We(b,\"isCompressed\"),b){h();let g=!r.isOdd(v);return te(ir(g),A)}else return te(Uint8Array.of(4),A,r.toBytes(v))}function x(R){N(R,void 0,\"Point\");let{publicKey:p,publicKeyUncompressed:b}=f,w=R.length,v=R[0],A=R.subarray(1);if(w===p&&(v===2||v===3)){let g=r.fromBytes(A);if(!r.isValid(g))throw new Error(\"bad point: is not on curve, wrong x\");let L=m(g),B;try{B=r.sqrt(L)}catch(M){let D=M instanceof Error?\": \"+M.message:\"\";throw new Error(\"bad point: is not on curve, sqrt error\"+D)}h();let T=r.isOdd(B);return(v&1)===1!==T&&(B=r.neg(B)),{x:g,y:B}}else if(w===b&&v===4){let g=r.BYTES,L=r.fromBytes(A.subarray(0,g)),B=r.fromBytes(A.subarray(g,g*2));if(!E(L,B))throw new Error(\"bad point: is not on curve\");return{x:L,y:B}}else throw new Error(`bad point: got length ${w}, expected compressed=${p} or uncompressed=${b}`)}let d=e.toBytes||l,y=e.fromBytes||x;function m(R){let p=r.sqr(R),b=r.mul(p,R);return r.add(r.add(b,r.mul(R,s.a)),s.b)}function E(R,p){let b=r.sqr(p),w=m(R);return r.eql(b,w)}if(!E(s.Gx,s.Gy))throw new Error(\"bad curve params: generator point\");let S=r.mul(r.pow(s.a,ut),Ro),U=r.mul(r.sqr(s.b),BigInt(27));if(r.is0(r.add(S,U)))throw new Error(\"bad curve params: a or b\");function C(R,p,b=!1){if(!r.isValid(p)||b&&r.is0(p))throw new Error(`bad point coordinate ${R}`);return p}function P(R){if(!(R instanceof k))throw new Error(\"Weierstrass Point expected\")}function F(R){if(!a||!a.basises)throw new Error(\"no endo\");return Ho(R,a.basises,o.ORDER)}let V=Nt((R,p)=>{let{X:b,Y:w,Z:v}=R;if(r.eql(v,r.ONE))return{x:b,y:w};let A=R.is0();p==null&&(p=A?r.ONE:r.inv(v));let g=r.mul(b,p),L=r.mul(w,p),B=r.mul(v,p);if(A)return{x:r.ZERO,y:r.ZERO};if(!r.eql(B,r.ONE))throw new Error(\"invZ was invalid\");return{x:g,y:L}}),Z=Nt(R=>{if(R.is0()){if(e.allowInfinityPoint&&!r.is0(R.Y))return;throw new Error(\"bad point: ZERO\")}let{x:p,y:b}=R.toAffine();if(!r.isValid(p)||!r.isValid(b))throw new Error(\"bad point: x or y not field elements\");if(!E(p,b))throw new Error(\"bad point: equation left != right\");if(!R.isTorsionFree())throw new Error(\"bad point: not in prime-order subgroup\");return!0});function j(R,p,b,w,v){return b=new k(r.mul(b.X,R),b.Y,b.Z),p=Ye(w,p),b=Ye(v,b),p.add(b)}let O=class O{constructor(p,b,w){u(this,\"X\");u(this,\"Y\");u(this,\"Z\");this.X=C(\"x\",p),this.Y=C(\"y\",b,!0),this.Z=C(\"z\",w),Object.freeze(this)}static CURVE(){return s}static fromAffine(p){let{x:b,y:w}=p||{};if(!p||!r.isValid(b)||!r.isValid(w))throw new Error(\"invalid affine point\");if(p instanceof O)throw new Error(\"projective point not allowed\");return r.is0(b)&&r.is0(w)?O.ZERO:new O(b,w,r.ONE)}static fromBytes(p){let b=O.fromAffine(y(N(p,void 0,\"point\")));return b.assertValidity(),b}static fromHex(p){return O.fromBytes(Te(p))}get x(){return this.toAffine().x}get y(){return this.toAffine().y}precompute(p=8,b=!0){return q.createCache(this,p),b||this.multiply(ut),this}assertValidity(){Z(this)}hasEvenY(){let{y:p}=this.toAffine();if(!r.isOdd)throw new Error(\"Field doesn't support isOdd\");return!r.isOdd(p)}equals(p){P(p);let{X:b,Y:w,Z:v}=this,{X:A,Y:g,Z:L}=p,B=r.eql(r.mul(b,L),r.mul(A,v)),T=r.eql(r.mul(w,L),r.mul(g,v));return B&&T}negate(){return new O(this.X,r.neg(this.Y),this.Z)}double(){let{a:p,b}=s,w=r.mul(b,ut),{X:v,Y:A,Z:g}=this,L=r.ZERO,B=r.ZERO,T=r.ZERO,_=r.mul(v,v),M=r.mul(A,A),D=r.mul(g,g),I=r.mul(v,A);return I=r.add(I,I),T=r.mul(v,g),T=r.add(T,T),L=r.mul(p,T),B=r.mul(w,D),B=r.add(L,B),L=r.sub(M,B),B=r.add(M,B),B=r.mul(L,B),L=r.mul(I,L),T=r.mul(w,T),D=r.mul(p,D),I=r.sub(_,D),I=r.mul(p,I),I=r.add(I,T),T=r.add(_,_),_=r.add(T,_),_=r.add(_,D),_=r.mul(_,I),B=r.add(B,_),D=r.mul(A,g),D=r.add(D,D),_=r.mul(D,I),L=r.sub(L,_),T=r.mul(D,M),T=r.add(T,T),T=r.add(T,T),new O(L,B,T)}add(p){P(p);let{X:b,Y:w,Z:v}=this,{X:A,Y:g,Z:L}=p,B=r.ZERO,T=r.ZERO,_=r.ZERO,M=s.a,D=r.mul(s.b,ut),I=r.mul(b,A),K=r.mul(w,g),W=r.mul(v,L),z=r.add(b,w),G=r.add(A,g);z=r.mul(z,G),G=r.add(I,K),z=r.sub(z,G),G=r.add(b,v);let Q=r.add(A,L);return G=r.mul(G,Q),Q=r.add(I,W),G=r.sub(G,Q),Q=r.add(w,v),B=r.add(g,L),Q=r.mul(Q,B),B=r.add(K,W),Q=r.sub(Q,B),_=r.mul(M,G),B=r.mul(D,W),_=r.add(B,_),B=r.sub(K,_),_=r.add(K,_),T=r.mul(B,_),K=r.add(I,I),K=r.add(K,I),W=r.mul(M,W),G=r.mul(D,G),K=r.add(K,W),W=r.sub(I,W),W=r.mul(M,W),G=r.add(G,W),I=r.mul(K,G),T=r.add(T,I),I=r.mul(Q,G),B=r.mul(z,B),B=r.sub(B,I),I=r.mul(z,K),_=r.mul(Q,_),_=r.add(_,I),new O(B,T,_)}subtract(p){return this.add(p.negate())}is0(){return this.equals(O.ZERO)}multiply(p){let{endo:b}=e;if(!o.isValidNot0(p))throw new Error(\"invalid scalar: out of range\");let w,v,A=g=>q.cached(this,g,L=>jt(O,L));if(b){let{k1neg:g,k1:L,k2neg:B,k2:T}=F(p),{p:_,f:M}=A(L),{p:D,f:I}=A(T);v=M.add(I),w=j(b.beta,_,D,g,B)}else{let{p:g,f:L}=A(p);w=g,v=L}return jt(O,[w,v])[0]}multiplyUnsafe(p){let{endo:b}=e,w=this;if(!o.isValid(p))throw new Error(\"invalid scalar: out of range\");if(p===de||w.is0())return O.ZERO;if(p===Ne)return w;if(q.hasCache(this))return this.multiply(p);if(b){let{k1neg:v,k1:A,k2neg:g,k2:L}=F(p),{p1:B,p2:T}=tr(O,w,A,L);return j(b.beta,B,T,v,g)}else return q.unsafe(w,p)}toAffine(p){return V(this,p)}isTorsionFree(){let{isTorsionFree:p}=e;return i===Ne?!0:p?p(O,this):q.unsafe(this,c).is0()}clearCofactor(){let{clearCofactor:p}=e;return i===Ne?this:p?p(O,this):this.multiplyUnsafe(i)}isSmallOrder(){return this.multiplyUnsafe(i).is0()}toBytes(p=!0){return We(p,\"isCompressed\"),this.assertValidity(),d(O,this,p)}toHex(p=!0){return Le(this.toBytes(p))}toString(){return`<Point ${this.is0()?\"ZERO\":this.toHex()}>`}};u(O,\"BASE\",new O(s.Gx,s.Gy,r.ONE)),u(O,\"ZERO\",new O(r.ZERO,r.ONE,r.ZERO)),u(O,\"Fp\",r),u(O,\"Fn\",o);let k=O,$=o.BITS,q=new ct(k,e.endo?Math.ceil($/2):$);return k.BASE.precompute(8),k}function ir(t){return Uint8Array.of(t?2:3)}function cr(t,e){return{secretKey:e.BYTES,publicKey:1+t.BYTES,publicKeyUncompressed:1+2*t.BYTES,publicKeyHasPrefix:!0,signature:2*e.BYTES}}function Oo(t,e={}){let{Fn:n}=t,r=e.randomBytes||He,o=Object.assign(cr(t.Fp,n),{seed:$t(n.ORDER)});function s(d){try{let y=n.fromBytes(d);return n.isValidNot0(y)}catch{return!1}}function i(d,y){let{publicKey:m,publicKeyUncompressed:E}=o;try{let S=d.length;return y===!0&&S!==m||y===!1&&S!==E?!1:!!t.fromBytes(d)}catch{return!1}}function c(d=r(o.seed)){return it(N(d,o.seed,\"seed\"),n.ORDER)}function a(d,y=!0){return t.BASE.multiply(n.fromBytes(d)).toBytes(y)}function f(d){let{secretKey:y,publicKey:m,publicKeyUncompressed:E}=o;if(!je(d)||\"_lengths\"in n&&n._lengths||y===m)return;let S=N(d,void 0,\"key\").length;return S===m||S===E}function h(d,y,m=!0){if(f(d)===!0)throw new Error(\"first arg must be private key\");if(f(y)===!1)throw new Error(\"second arg must be public key\");let E=n.fromBytes(d);return t.fromBytes(y).multiply(E).toBytes(m)}let l={isValidSecretKey:s,isValidPublicKey:i,randomSecretKey:c},x=at(c,a);return Object.freeze({getPublicKey:a,getSharedSecret:h,keygen:x,Point:t,utils:l,lengths:o})}function ar(t,e,n={}){Qe(e),ze(n,{},{hmac:\"function\",lowS:\"boolean\",randomBytes:\"function\",bits2int:\"function\",bits2int_modN:\"function\"}),n=Object.assign({},n);let r=n.randomBytes||He,o=n.hmac||((p,b)=>Wt(e,p,b)),{Fp:s,Fn:i}=t,{ORDER:c,BITS:a}=i,{keygen:f,getPublicKey:h,getSharedSecret:l,utils:x,lengths:d}=Oo(t,n),y={prehash:!0,lowS:typeof n.lowS==\"boolean\"?n.lowS:!0,format:\"compact\",extraEntropy:!1},m=c*or<s.ORDER;function E(p){let b=c>>Ne;return p>b}function S(p,b){if(!i.isValidNot0(b))throw new Error(`invalid signature ${p}: out of range 1..Point.Fn.ORDER`);return b}function U(){if(m)throw new Error('\"recovered\" sig type is not supported for cofactor >2 curves')}function C(p,b){Zt(b);let w=d.signature,v=b===\"compact\"?w:b===\"recovered\"?w+1:void 0;return N(p,v)}class P{constructor(b,w,v){u(this,\"r\");u(this,\"s\");u(this,\"recovery\");if(this.r=S(\"r\",b),this.s=S(\"s\",w),v!=null){if(U(),![0,1,2,3].includes(v))throw new Error(\"invalid recovery id\");this.recovery=v}Object.freeze(this)}static fromBytes(b,w=y.format){C(b,w);let v;if(w===\"der\"){let{r:B,s:T}=ve.toSig(N(b));return new P(B,T)}w===\"recovered\"&&(v=b[0],w=\"compact\",b=b.subarray(1));let A=d.signature/2,g=b.subarray(0,A),L=b.subarray(A,A*2);return new P(i.fromBytes(g),i.fromBytes(L),v)}static fromHex(b,w){return this.fromBytes(Te(b),w)}assertRecovery(){let{recovery:b}=this;if(b==null)throw new Error(\"invalid recovery id: must be present\");return b}addRecoveryBit(b){return new P(this.r,this.s,b)}recoverPublicKey(b){let{r:w,s:v}=this,A=this.assertRecovery(),g=A===2||A===3?w+c:w;if(!s.isValid(g))throw new Error(\"invalid recovery id: sig.r+curve.n != R.x\");let L=s.toBytes(g),B=t.fromBytes(te(ir((A&1)===0),L)),T=i.inv(g),_=V(N(b,void 0,\"msgHash\")),M=i.create(-_*T),D=i.create(v*T),I=t.BASE.multiplyUnsafe(M).add(B.multiplyUnsafe(D));if(I.is0())throw new Error(\"invalid recovery: point at infinify\");return I.assertValidity(),I}hasHighS(){return E(this.s)}toBytes(b=y.format){if(Zt(b),b===\"der\")return Te(ve.hexFromSig(this));let{r:w,s:v}=this,A=i.toBytes(w),g=i.toBytes(v);return b===\"recovered\"?(U(),te(Uint8Array.of(this.assertRecovery()),A,g)):te(A,g)}toHex(b){return Le(this.toBytes(b))}}let F=n.bits2int||function(b){if(b.length>8192)throw new Error(\"input is too large\");let w=Ee(b),v=b.length*8-a;return v>0?w>>BigInt(v):w},V=n.bits2int_modN||function(b){return i.create(F(b))},Z=Ze(a);function j(p){return qn(\"num < 2^\"+a,p,de,Z),i.toBytes(p)}function k(p,b){return N(p,void 0,\"message\"),b?N(e(p),void 0,\"prehashed message\"):p}function $(p,b,w){let{lowS:v,prehash:A,extraEntropy:g}=Gt(w,y);p=k(p,A);let L=V(p),B=i.fromBytes(b);if(!i.isValidNot0(B))throw new Error(\"invalid private key\");let T=[j(B),j(L)];if(g!=null&&g!==!1){let I=g===!0?r(d.secretKey):g;T.push(N(I,void 0,\"extraEntropy\"))}let _=te(...T),M=L;function D(I){let K=F(I);if(!i.isValidNot0(K))return;let W=i.inv(K),z=t.BASE.multiply(K).toAffine(),G=i.create(z.x);if(G===de)return;let Q=i.create(W*i.create(M+G*B));if(Q===de)return;let Ln=(z.x===G?0:2)|Number(z.y&Ne),Tn=Q;return v&&E(Q)&&(Tn=i.neg(Q),Ln^=1),new P(G,Tn,m?void 0:Ln)}return{seed:_,k2sig:D}}function q(p,b,w={}){let{seed:v,k2sig:A}=$(p,b,w);return Dn(e.outputLen,i.BYTES,o)(v,A).toBytes(w.format)}function O(p,b,w,v={}){let{lowS:A,prehash:g,format:L}=Gt(v,y);if(w=N(w,void 0,\"publicKey\"),b=k(b,g),!je(p)){let B=p instanceof P?\", use sig.toBytes()\":\"\";throw new Error(\"verify expects Uint8Array signature\"+B)}C(p,L);try{let B=P.fromBytes(p,L),T=t.fromBytes(w);if(A&&B.hasHighS())return!1;let{r:_,s:M}=B,D=V(b),I=i.inv(M),K=i.create(D*I),W=i.create(_*I),z=t.BASE.multiplyUnsafe(K).add(T.multiplyUnsafe(W));return z.is0()?!1:i.create(z.x)===_}catch{return!1}}function R(p,b,w={}){let{prehash:v}=Gt(w,y);return b=k(b,v),P.fromBytes(p,\"recovered\").recoverPublicKey(b).toBytes()}return Object.freeze({keygen:f,getPublicKey:h,getSharedSecret:l,utils:x,lengths:d,Point:t,sign:q,verify:O,recoverPublicKey:R,Signature:P,hash:e})}var dt={p:BigInt(\"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f\"),n:BigInt(\"0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141\"),h:BigInt(1),a:BigInt(0),b:BigInt(7),Gx:BigInt(\"0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"),Gy:BigInt(\"0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8\")},Uo={beta:BigInt(\"0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee\"),basises:[[BigInt(\"0x3086d221a7d46bcde86c90e49284eb15\"),-BigInt(\"0xe4437ed6010e88286f547fa90abfe4c3\")],[BigInt(\"0x114ca50f7a8e2f3f657c1108d9d44cfd8\"),BigInt(\"0x3086d221a7d46bcde86c90e49284eb15\")]]},_o=BigInt(0),Yt=BigInt(2);function Io(t){let e=dt.p,n=BigInt(3),r=BigInt(6),o=BigInt(11),s=BigInt(22),i=BigInt(23),c=BigInt(44),a=BigInt(88),f=t*t*t%e,h=f*f*t%e,l=re(h,n,e)*h%e,x=re(l,n,e)*h%e,d=re(x,Yt,e)*f%e,y=re(d,o,e)*d%e,m=re(y,s,e)*y%e,E=re(m,c,e)*m%e,S=re(E,a,e)*E%e,U=re(S,c,e)*m%e,C=re(U,n,e)*h%e,P=re(C,i,e)*y%e,F=re(P,r,e)*f%e,V=re(F,Yt,e);if(!lt.eql(lt.sqr(V),t))throw new Error(\"Cannot find square root\");return V}var lt=ke(dt.p,{sqrt:Io}),Ue=sr(dt,{Fp:lt,endo:Uo}),lr=ar(Ue,nt),fr={};function ht(t,...e){let n=fr[t];if(n===void 0){let r=nt(Pn(t));n=te(r,r),fr[t]=n}return nt(te(n,...e))}var Xt=t=>t.toBytes(!0).slice(1),Jt=t=>t%Yt===_o;function Ft(t){let{Fn:e,BASE:n}=Ue,r=e.fromBytes(t),o=n.multiply(r);return{scalar:Jt(o.y)?r:e.neg(r),bytes:Xt(o)}}function hr(t){let e=lt;if(!e.isValidNot0(t))throw new Error(\"invalid x: Fail if x \\u2265 p\");let n=e.create(t*t),r=e.create(n*t+BigInt(7)),o=e.sqrt(r);Jt(o)||(o=e.neg(o));let s=Ue.fromAffine({x:t,y:o});return s.assertValidity(),s}var Fe=Ee;function dr(...t){return Ue.Fn.create(Fe(ht(\"BIP0340/challenge\",...t)))}function ur(t){return Ft(t).bytes}function ko(t,e,n=He(32)){let{Fn:r}=Ue,o=N(t,void 0,\"message\"),{bytes:s,scalar:i}=Ft(e),c=N(n,32,\"auxRand\"),a=r.toBytes(i^Fe(ht(\"BIP0340/aux\",c))),f=ht(\"BIP0340/nonce\",a,s,o),{bytes:h,scalar:l}=Ft(f),x=dr(h,s,o),d=new Uint8Array(64);if(d.set(h,0),d.set(r.toBytes(r.create(l+x*i)),32),!br(d,o,s))throw new Error(\"sign: Invalid signature produced\");return d}function br(t,e,n){let{Fp:r,Fn:o,BASE:s}=Ue,i=N(t,64,\"signature\"),c=N(e,void 0,\"message\"),a=N(n,32,\"publicKey\");try{let f=hr(Fe(a)),h=Fe(i.subarray(0,32));if(!r.isValidNot0(h))return!1;let l=Fe(i.subarray(32,64));if(!o.isValidNot0(l))return!1;let x=dr(o.toBytes(h),Xt(f),c),d=s.multiplyUnsafe(l).add(f.multiplyUnsafe(o.neg(x))),{x:y,y:m}=d.toAffine();return!(d.is0()||!Jt(m)||y!==h)}catch{return!1}}var oe=(()=>{let n=(r=He(48))=>it(r,dt.n);return{keygen:at(n,ur),getPublicKey:ur,sign:ko,verify:br,Point:Ue,utils:{randomSecretKey:n,taggedHash:ht,lift_x:hr,pointToBytes:Xt},lengths:{secretKey:32,publicKey:32,publicKeyHasPrefix:!1,signature:64,seed:48}}})();function Co(t){return t instanceof Uint8Array||ArrayBuffer.isView(t)&&t.constructor.name===\"Uint8Array\"}function bt(t,e=\"\"){if(!Number.isSafeInteger(t)||t<0){let n=e&&`\"${e}\" `;throw new Error(`${n}expected integer >= 0, got ${t}`)}}function ue(t,e,n=\"\"){let r=Co(t),o=t?.length,s=e!==void 0;if(!r||s&&o!==e){let i=n&&`\"${n}\" `,c=s?` of length ${e}`:\"\",a=r?`length=${o}`:`type=${typeof t}`;throw new Error(i+\"expected Uint8Array\"+c+\", got \"+a)}return t}function Xe(t){if(typeof t!=\"function\"||typeof t.create!=\"function\")throw new Error(\"Hash must wrapped by utils.createHasher\");bt(t.outputLen),bt(t.blockLen)}function Pe(t,e=!0){if(t.destroyed)throw new Error(\"Hash instance has been destroyed\");if(e&&t.finished)throw new Error(\"Hash#digest() has already been called\")}function xr(t,e){ue(t,void 0,\"digestInto() output\");let n=e.outputLen;if(t.length<n)throw new Error('\"digestInto() output\" expected to be of length >='+n)}function pe(...t){for(let e=0;e<t.length;e++)t[e].fill(0)}function pt(t){return new DataView(t.buffer,t.byteOffset,t.byteLength)}function ce(t,e){return t<<32-e|t>>>e}var yr=typeof Uint8Array.from([]).toHex==\"function\"&&typeof Uint8Array.fromHex==\"function\",No=Array.from({length:256},(t,e)=>e.toString(16).padStart(2,\"0\"));function ae(t){if(ue(t),yr)return t.toHex();let e=\"\";for(let n=0;n<t.length;n++)e+=No[t[n]];return e}var be={_0:48,_9:57,A:65,F:70,a:97,f:102};function pr(t){if(t>=be._0&&t<=be._9)return t-be._0;if(t>=be.A&&t<=be.F)return t-(be.A-10);if(t>=be.a&&t<=be.f)return t-(be.a-10)}function ne(t){if(typeof t!=\"string\")throw new Error(\"hex string expected, got \"+typeof t);if(yr)return Uint8Array.fromHex(t);let e=t.length,n=e/2;if(e%2)throw new Error(\"hex string expected, got unpadded hex of length \"+e);let r=new Uint8Array(n);for(let o=0,s=0;o<n;o++,s+=2){let i=pr(t.charCodeAt(s)),c=pr(t.charCodeAt(s+1));if(i===void 0||c===void 0){let a=t[s]+t[s+1];throw new Error('hex string expected, got non-hex character \"'+a+'\" at index '+s)}r[o]=i*16+c}return r}function qe(...t){let e=0;for(let r=0;r<t.length;r++){let o=t[r];ue(o),e+=o.length}let n=new Uint8Array(e);for(let r=0,o=0;r<t.length;r++){let s=t[r];n.set(s,o),o+=s.length}return n}function gr(t,e={}){let n=(o,s)=>t(s).update(o).digest(),r=t(void 0);return n.outputLen=r.outputLen,n.blockLen=r.blockLen,n.create=o=>t(o),Object.assign(n,e),Object.freeze(n)}function wr(t=32){let e=typeof globalThis==\"object\"?globalThis.crypto:null;if(typeof e?.getRandomValues!=\"function\")throw new Error(\"crypto.getRandomValues must be defined\");return e.getRandomValues(new Uint8Array(t))}var mr=t=>({oid:Uint8Array.from([6,9,96,134,72,1,101,3,4,2,t])});function Er(t,e,n){return t&e^~t&n}function vr(t,e,n){return t&e^t&n^e&n}var xt=class{constructor(e,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=e,this.outputLen=n,this.padOffset=r,this.isLE=o,this.buffer=new Uint8Array(e),this.view=pt(this.buffer)}update(e){Pe(this),ue(e);let{view:n,buffer:r,blockLen:o}=this,s=e.length;for(let i=0;i<s;){let c=Math.min(o-this.pos,s-i);if(c===o){let a=pt(e);for(;o<=s-i;i+=o)this.process(a,i);continue}r.set(e.subarray(i,i+c),this.pos),this.pos+=c,i+=c,this.pos===o&&(this.process(n,0),this.pos=0)}return this.length+=e.length,this.roundClean(),this}digestInto(e){Pe(this),xr(e,this),this.finished=!0;let{buffer:n,view:r,blockLen:o,isLE:s}=this,{pos:i}=this;n[i++]=128,pe(this.buffer.subarray(i)),this.padOffset>o-i&&(this.process(r,0),i=0);for(let l=i;l<o;l++)n[l]=0;r.setBigUint64(o-8,BigInt(this.length*8),s),this.process(r,0);let c=pt(e),a=this.outputLen;if(a%4)throw new Error(\"_sha2: outputLen must be aligned to 32bit\");let f=a/4,h=this.get();if(f>h.length)throw new Error(\"_sha2: outputLen bigger than state\");for(let l=0;l<f;l++)c.setUint32(4*l,h[l],s)}digest(){let{buffer:e,outputLen:n}=this;this.digestInto(e);let r=e.slice(0,n);return this.destroy(),r}_cloneInto(e){e||(e=new this.constructor),e.set(...this.get());let{blockLen:n,buffer:r,length:o,finished:s,destroyed:i,pos:c}=this;return e.destroyed=i,e.finished=s,e.length=o,e.pos=c,o%n&&e.buffer.set(r),e}clone(){return this._cloneInto()}},xe=Uint32Array.from([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]);var Po=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]),Be=new Uint32Array(64),Qt=class extends xt{constructor(e){super(64,e,8,!1)}get(){let{A:e,B:n,C:r,D:o,E:s,F:i,G:c,H:a}=this;return[e,n,r,o,s,i,c,a]}set(e,n,r,o,s,i,c,a){this.A=e|0,this.B=n|0,this.C=r|0,this.D=o|0,this.E=s|0,this.F=i|0,this.G=c|0,this.H=a|0}process(e,n){for(let l=0;l<16;l++,n+=4)Be[l]=e.getUint32(n,!1);for(let l=16;l<64;l++){let x=Be[l-15],d=Be[l-2],y=ce(x,7)^ce(x,18)^x>>>3,m=ce(d,17)^ce(d,19)^d>>>10;Be[l]=m+Be[l-7]+y+Be[l-16]|0}let{A:r,B:o,C:s,D:i,E:c,F:a,G:f,H:h}=this;for(let l=0;l<64;l++){let x=ce(c,6)^ce(c,11)^ce(c,25),d=h+x+Er(c,a,f)+Po[l]+Be[l]|0,m=(ce(r,2)^ce(r,13)^ce(r,22))+vr(r,o,s)|0;h=f,f=a,a=c,c=i+d|0,i=s,s=o,o=r,r=d+m|0}r=r+this.A|0,o=o+this.B|0,s=s+this.C|0,i=i+this.D|0,c=c+this.E|0,a=a+this.F|0,f=f+this.G|0,h=h+this.H|0,this.set(r,o,s,i,c,a,f,h)}roundClean(){pe(Be)}destroy(){this.set(0,0,0,0,0,0,0,0),pe(this.buffer)}},en=class extends Qt{constructor(){super(32);u(this,\"A\",xe[0]|0);u(this,\"B\",xe[1]|0);u(this,\"C\",xe[2]|0);u(this,\"D\",xe[3]|0);u(this,\"E\",xe[4]|0);u(this,\"F\",xe[5]|0);u(this,\"G\",xe[6]|0);u(this,\"H\",xe[7]|0)}};var Ae=gr(()=>new en,mr(1));var zi=new TextDecoder(\"utf-8\"),qo=new TextEncoder;function Do(t){if(t.length!==64)return!1;for(let e=0;e<64;e++){let n=t.charCodeAt(e);if(isNaN(n)||n<48||n>102||n>57&&n<97)return!1}return!0}var De=Symbol(\"verified\"),$o=t=>t instanceof Object;function Vo(t){if(!$o(t)||typeof t.kind!=\"number\"||typeof t.content!=\"string\"||typeof t.created_at!=\"number\"||typeof t.pubkey!=\"string\"||!Do(t.pubkey)||!Array.isArray(t.tags))return!1;for(let e=0;e<t.tags.length;e++){let n=t.tags[e];if(!Array.isArray(n))return!1;for(let r=0;r<n.length;r++)if(typeof n[r]!=\"string\")return!1}return!0}var Mo=class{generateSecretKey(){return oe.utils.randomSecretKey()}getPublicKey(t){return ae(oe.getPublicKey(t))}finalizeEvent(t,e){let n=t;return n.pubkey=ae(oe.getPublicKey(e)),n.id=tn(n),n.sig=ae(oe.sign(ne(tn(n)),e)),n[De]=!0,n}verifyEvent(t){if(typeof t[De]==\"boolean\")return t[De];try{let e=tn(t);if(e!==t.id)return t[De]=!1,!1;let n=oe.verify(ne(t.sig),ne(e),ne(t.pubkey));return t[De]=n,n}catch{return t[De]=!1,!1}}};function Ko(t){if(!Vo(t))throw new Error(\"can't serialize event with wrong or missing properties\");return JSON.stringify([0,t.pubkey,t.created_at,t.kind,t.tags,t.content])}function tn(t){let e=Ae(qo.encode(Ko(t)));return ae(e)}var yt=new Mo,Br=yt.generateSecretKey,Ar=yt.getPublicKey,Sr=yt.finalizeEvent,Fi=yt.verifyEvent;function jo(t){return t instanceof Uint8Array||ArrayBuffer.isView(t)&&t.constructor.name===\"Uint8Array\"}function gt(t){if(typeof t!=\"boolean\")throw new Error(`boolean expected, not ${t}`)}function wt(t){if(!Number.isSafeInteger(t)||t<0)throw new Error(\"positive integer expected, got \"+t)}function Y(t,e,n=\"\"){let r=jo(t),o=t?.length,s=e!==void 0;if(!r||s&&o!==e){let i=n&&`\"${n}\" `,c=s?` of length ${e}`:\"\",a=r?`length=${o}`:`type=${typeof t}`;throw new Error(i+\"expected Uint8Array\"+c+\", got \"+a)}return t}function nn(t,e=!0){if(t.destroyed)throw new Error(\"Hash instance has been destroyed\");if(e&&t.finished)throw new Error(\"Hash#digest() has already been called\")}function Lr(t,e){Y(t,void 0,\"output\");let n=e.outputLen;if(t.length<n)throw new Error(\"digestInto() expects output buffer of length at least \"+n)}function ye(t){return new Uint32Array(t.buffer,t.byteOffset,Math.floor(t.byteLength/4))}function ge(...t){for(let e=0;e<t.length;e++)t[e].fill(0)}function Wo(t){return new DataView(t.buffer,t.byteOffset,t.byteLength)}var Go=new Uint8Array(new Uint32Array([287454020]).buffer)[0]===68;function Tr(t,e){if(e==null||typeof e!=\"object\")throw new Error(\"options must be defined\");return Object.assign(t,e)}function mt(t,e){if(t.length!==e.length)return!1;let n=0;for(let r=0;r<t.length;r++)n|=t[r]^e[r];return n===0}var rn=(t,e)=>{function n(r,...o){if(Y(r,void 0,\"key\"),!Go)throw new Error(\"Non little-endian hardware is not yet supported\");if(t.nonceLength!==void 0){let h=o[0];Y(h,t.varSizeNonce?void 0:t.nonceLength,\"nonce\")}let s=t.tagLength;s&&o[1]!==void 0&&Y(o[1],void 0,\"AAD\");let i=e(r,...o),c=(h,l)=>{if(l!==void 0){if(h!==2)throw new Error(\"cipher output not supported\");Y(l,void 0,\"output\")}},a=!1;return{encrypt(h,l){if(a)throw new Error(\"cannot encrypt() twice with same key + nonce\");return a=!0,Y(h),c(i.encrypt.length,l),i.encrypt(h,l)},decrypt(h,l){if(Y(h),s&&h.length<s)throw new Error('\"ciphertext\" expected length bigger than tagLength='+s);return c(i.decrypt.length,l),i.decrypt(h,l)}}}return Object.assign(n,t),n};function on(t,e,n=!0){if(e===void 0)return new Uint8Array(t);if(e.length!==t)throw new Error('\"output\" expected Uint8Array of length '+t+\", got: \"+e.length);if(n&&!Zo(e))throw new Error(\"invalid output, must be aligned\");return e}function Hr(t,e,n){gt(n);let r=new Uint8Array(16),o=Wo(r);return o.setBigUint64(0,BigInt(e),n),o.setBigUint64(8,BigInt(t),n),r}function Zo(t){return t.byteOffset%4===0}function $e(t){return Uint8Array.from(t)}var Or=t=>Uint8Array.from(t.split(\"\"),e=>e.charCodeAt(0)),zo=Or(\"expand 16-byte k\"),Yo=Or(\"expand 32-byte k\"),Fo=ye(zo),Xo=ye(Yo);function H(t,e){return t<<e|t>>>32-e}function sn(t){return t.byteOffset%4===0}var Et=64,Jo=16,Ur=2**32-1,Rr=Uint32Array.of();function Qo(t,e,n,r,o,s,i,c){let a=o.length,f=new Uint8Array(Et),h=ye(f),l=sn(o)&&sn(s),x=l?ye(o):Rr,d=l?ye(s):Rr;for(let y=0;y<a;i++){if(t(e,n,r,h,i,c),i>=Ur)throw new Error(\"arx: counter overflow\");let m=Math.min(Et,a-y);if(l&&m===Et){let E=y/4;if(y%4!==0)throw new Error(\"arx: invalid block position\");for(let S=0,U;S<Jo;S++)U=E+S,d[U]=x[U]^h[S];y+=Et;continue}for(let E=0,S;E<m;E++)S=y+E,s[S]=o[S]^f[E];y+=m}}function cn(t,e){let{allowShortKeys:n,extendNonceFn:r,counterLength:o,counterRight:s,rounds:i}=Tr({allowShortKeys:!1,counterLength:8,counterRight:!1,rounds:20},e);if(typeof t!=\"function\")throw new Error(\"core must be a function\");return wt(o),wt(i),gt(s),gt(n),(c,a,f,h,l=0)=>{Y(c,void 0,\"key\"),Y(a,void 0,\"nonce\"),Y(f,void 0,\"data\");let x=f.length;if(h===void 0&&(h=new Uint8Array(x)),Y(h,void 0,\"output\"),wt(l),l<0||l>=Ur)throw new Error(\"arx: counter overflow\");if(h.length<x)throw new Error(`arx: output (${h.length}) is shorter than data (${x})`);let d=[],y=c.length,m,E;if(y===32)d.push(m=$e(c)),E=Xo;else if(y===16&&n)m=new Uint8Array(32),m.set(c),m.set(c,16),E=Fo,d.push(m);else throw Y(c,32,\"arx key\"),new Error(\"invalid key size\");sn(a)||d.push(a=$e(a));let S=ye(m);if(r){if(a.length!==24)throw new Error(\"arx: extended nonce must be 24 bytes\");r(E,S,ye(a.subarray(0,16)),S),a=a.subarray(16)}let U=16-o;if(U!==a.length)throw new Error(`arx: nonce must be ${U} or 16 bytes`);if(U!==12){let P=new Uint8Array(12);P.set(a,s?0:12-a.length),a=P,d.push(a)}let C=ye(a);return Qo(t,E,S,C,f,h,l,i),ge(...d),h}}function X(t,e){return t[e++]&255|(t[e++]&255)<<8}var an=class{constructor(e){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);e=$e(Y(e,32,\"key\"));let n=X(e,0),r=X(e,2),o=X(e,4),s=X(e,6),i=X(e,8),c=X(e,10),a=X(e,12),f=X(e,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|c<<2)&8191,this.r[7]=(c>>>11|a<<5)&8065,this.r[8]=(a>>>8|f<<8)&8191,this.r[9]=f>>>5&127;for(let h=0;h<8;h++)this.pad[h]=X(e,16+2*h)}process(e,n,r=!1){let o=r?0:2048,{h:s,r:i}=this,c=i[0],a=i[1],f=i[2],h=i[3],l=i[4],x=i[5],d=i[6],y=i[7],m=i[8],E=i[9],S=X(e,n+0),U=X(e,n+2),C=X(e,n+4),P=X(e,n+6),F=X(e,n+8),V=X(e,n+10),Z=X(e,n+12),j=X(e,n+14),k=s[0]+(S&8191),$=s[1]+((S>>>13|U<<3)&8191),q=s[2]+((U>>>10|C<<6)&8191),O=s[3]+((C>>>7|P<<9)&8191),R=s[4]+((P>>>4|F<<12)&8191),p=s[5]+(F>>>1&8191),b=s[6]+((F>>>14|V<<2)&8191),w=s[7]+((V>>>11|Z<<5)&8191),v=s[8]+((Z>>>8|j<<8)&8191),A=s[9]+(j>>>5|o),g=0,L=g+k*c+$*(5*E)+q*(5*m)+O*(5*y)+R*(5*d);g=L>>>13,L&=8191,L+=p*(5*x)+b*(5*l)+w*(5*h)+v*(5*f)+A*(5*a),g+=L>>>13,L&=8191;let B=g+k*a+$*c+q*(5*E)+O*(5*m)+R*(5*y);g=B>>>13,B&=8191,B+=p*(5*d)+b*(5*x)+w*(5*l)+v*(5*h)+A*(5*f),g+=B>>>13,B&=8191;let T=g+k*f+$*a+q*c+O*(5*E)+R*(5*m);g=T>>>13,T&=8191,T+=p*(5*y)+b*(5*d)+w*(5*x)+v*(5*l)+A*(5*h),g+=T>>>13,T&=8191;let _=g+k*h+$*f+q*a+O*c+R*(5*E);g=_>>>13,_&=8191,_+=p*(5*m)+b*(5*y)+w*(5*d)+v*(5*x)+A*(5*l),g+=_>>>13,_&=8191;let M=g+k*l+$*h+q*f+O*a+R*c;g=M>>>13,M&=8191,M+=p*(5*E)+b*(5*m)+w*(5*y)+v*(5*d)+A*(5*x),g+=M>>>13,M&=8191;let D=g+k*x+$*l+q*h+O*f+R*a;g=D>>>13,D&=8191,D+=p*c+b*(5*E)+w*(5*m)+v*(5*y)+A*(5*d),g+=D>>>13,D&=8191;let I=g+k*d+$*x+q*l+O*h+R*f;g=I>>>13,I&=8191,I+=p*a+b*c+w*(5*E)+v*(5*m)+A*(5*y),g+=I>>>13,I&=8191;let K=g+k*y+$*d+q*x+O*l+R*h;g=K>>>13,K&=8191,K+=p*f+b*a+w*c+v*(5*E)+A*(5*m),g+=K>>>13,K&=8191;let W=g+k*m+$*y+q*d+O*x+R*l;g=W>>>13,W&=8191,W+=p*h+b*f+w*a+v*c+A*(5*E),g+=W>>>13,W&=8191;let z=g+k*E+$*m+q*y+O*d+R*x;g=z>>>13,z&=8191,z+=p*l+b*h+w*f+v*a+A*c,g+=z>>>13,z&=8191,g=(g<<2)+g|0,g=g+L|0,L=g&8191,g=g>>>13,B+=g,s[0]=L,s[1]=B,s[2]=T,s[3]=_,s[4]=M,s[5]=D,s[6]=I,s[7]=K,s[8]=W,s[9]=z}finalize(){let{h:e,pad:n}=this,r=new Uint16Array(10),o=e[1]>>>13;e[1]&=8191;for(let c=2;c<10;c++)e[c]+=o,o=e[c]>>>13,e[c]&=8191;e[0]+=o*5,o=e[0]>>>13,e[0]&=8191,e[1]+=o,o=e[1]>>>13,e[1]&=8191,e[2]+=o,r[0]=e[0]+5,o=r[0]>>>13,r[0]&=8191;for(let c=1;c<10;c++)r[c]=e[c]+o,o=r[c]>>>13,r[c]&=8191;r[9]-=8192;let s=(o^1)-1;for(let c=0;c<10;c++)r[c]&=s;s=~s;for(let c=0;c<10;c++)e[c]=e[c]&s|r[c];e[0]=(e[0]|e[1]<<13)&65535,e[1]=(e[1]>>>3|e[2]<<10)&65535,e[2]=(e[2]>>>6|e[3]<<7)&65535,e[3]=(e[3]>>>9|e[4]<<4)&65535,e[4]=(e[4]>>>12|e[5]<<1|e[6]<<14)&65535,e[5]=(e[6]>>>2|e[7]<<11)&65535,e[6]=(e[7]>>>5|e[8]<<8)&65535,e[7]=(e[8]>>>8|e[9]<<5)&65535;let i=e[0]+n[0];e[0]=i&65535;for(let c=1;c<8;c++)i=(e[c]+n[c]|0)+(i>>>16)|0,e[c]=i&65535;ge(r)}update(e){nn(this),Y(e),e=$e(e);let{buffer:n,blockLen:r}=this,o=e.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(e,s);continue}n.set(e.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(){ge(this.h,this.r,this.buffer,this.pad)}digestInto(e){nn(this),Lr(e,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++)e[s++]=r[i]>>>0,e[s++]=r[i]>>>8;return e}digest(){let{buffer:e,outputLen:n}=this;this.digestInto(e);let r=e.slice(0,n);return this.destroy(),r}};function es(t){let e=(r,o)=>t(o).update(r).digest(),n=t(new Uint8Array(32));return e.outputLen=n.outputLen,e.blockLen=n.blockLen,e.create=r=>t(r),e}var _r=es(t=>new an(t));function Cr(t,e,n,r,o,s=20){let i=t[0],c=t[1],a=t[2],f=t[3],h=e[0],l=e[1],x=e[2],d=e[3],y=e[4],m=e[5],E=e[6],S=e[7],U=o,C=n[0],P=n[1],F=n[2],V=i,Z=c,j=a,k=f,$=h,q=l,O=x,R=d,p=y,b=m,w=E,v=S,A=U,g=C,L=P,B=F;for(let _=0;_<s;_+=2)V=V+$|0,A=H(A^V,16),p=p+A|0,$=H($^p,12),V=V+$|0,A=H(A^V,8),p=p+A|0,$=H($^p,7),Z=Z+q|0,g=H(g^Z,16),b=b+g|0,q=H(q^b,12),Z=Z+q|0,g=H(g^Z,8),b=b+g|0,q=H(q^b,7),j=j+O|0,L=H(L^j,16),w=w+L|0,O=H(O^w,12),j=j+O|0,L=H(L^j,8),w=w+L|0,O=H(O^w,7),k=k+R|0,B=H(B^k,16),v=v+B|0,R=H(R^v,12),k=k+R|0,B=H(B^k,8),v=v+B|0,R=H(R^v,7),V=V+q|0,B=H(B^V,16),w=w+B|0,q=H(q^w,12),V=V+q|0,B=H(B^V,8),w=w+B|0,q=H(q^w,7),Z=Z+O|0,A=H(A^Z,16),v=v+A|0,O=H(O^v,12),Z=Z+O|0,A=H(A^Z,8),v=v+A|0,O=H(O^v,7),j=j+R|0,g=H(g^j,16),p=p+g|0,R=H(R^p,12),j=j+R|0,g=H(g^j,8),p=p+g|0,R=H(R^p,7),k=k+$|0,L=H(L^k,16),b=b+L|0,$=H($^b,12),k=k+$|0,L=H(L^k,8),b=b+L|0,$=H($^b,7);let T=0;r[T++]=i+V|0,r[T++]=c+Z|0,r[T++]=a+j|0,r[T++]=f+k|0,r[T++]=h+$|0,r[T++]=l+q|0,r[T++]=x+O|0,r[T++]=d+R|0,r[T++]=y+p|0,r[T++]=m+b|0,r[T++]=E+w|0,r[T++]=S+v|0,r[T++]=U+A|0,r[T++]=C+g|0,r[T++]=P+L|0,r[T++]=F+B|0}function ts(t,e,n,r){let o=t[0],s=t[1],i=t[2],c=t[3],a=e[0],f=e[1],h=e[2],l=e[3],x=e[4],d=e[5],y=e[6],m=e[7],E=n[0],S=n[1],U=n[2],C=n[3];for(let F=0;F<20;F+=2)o=o+a|0,E=H(E^o,16),x=x+E|0,a=H(a^x,12),o=o+a|0,E=H(E^o,8),x=x+E|0,a=H(a^x,7),s=s+f|0,S=H(S^s,16),d=d+S|0,f=H(f^d,12),s=s+f|0,S=H(S^s,8),d=d+S|0,f=H(f^d,7),i=i+h|0,U=H(U^i,16),y=y+U|0,h=H(h^y,12),i=i+h|0,U=H(U^i,8),y=y+U|0,h=H(h^y,7),c=c+l|0,C=H(C^c,16),m=m+C|0,l=H(l^m,12),c=c+l|0,C=H(C^c,8),m=m+C|0,l=H(l^m,7),o=o+f|0,C=H(C^o,16),y=y+C|0,f=H(f^y,12),o=o+f|0,C=H(C^o,8),y=y+C|0,f=H(f^y,7),s=s+h|0,E=H(E^s,16),m=m+E|0,h=H(h^m,12),s=s+h|0,E=H(E^s,8),m=m+E|0,h=H(h^m,7),i=i+l|0,S=H(S^i,16),x=x+S|0,l=H(l^x,12),i=i+l|0,S=H(S^i,8),x=x+S|0,l=H(l^x,7),c=c+a|0,U=H(U^c,16),d=d+U|0,a=H(a^d,12),c=c+a|0,U=H(U^c,8),d=d+U|0,a=H(a^d,7);let P=0;r[P++]=o,r[P++]=s,r[P++]=i,r[P++]=c,r[P++]=E,r[P++]=S,r[P++]=U,r[P++]=C}var vt=cn(Cr,{counterRight:!1,counterLength:4,allowShortKeys:!1}),ns=cn(Cr,{counterRight:!1,counterLength:8,extendNonceFn:ts,allowShortKeys:!1});var rs=new Uint8Array(16),Ir=(t,e)=>{t.update(e);let n=e.length%16;n&&t.update(rs.subarray(n))},os=new Uint8Array(32);function kr(t,e,n,r,o){o!==void 0&&Y(o,void 0,\"AAD\");let s=t(e,n,os),i=Hr(r.length,o?o.length:0,!0),c=_r.create(s);o&&Ir(c,o),Ir(c,r),c.update(i);let a=c.digest();return ge(s,i),a}var Nr=t=>(e,n,r)=>({encrypt(s,i){let c=s.length;i=on(c+16,i,!1),i.set(s);let a=i.subarray(0,-16);t(e,n,a,a,1);let f=kr(t,e,n,a,r);return i.set(f,c),ge(f),i},decrypt(s,i){i=on(s.length-16,i,!1);let c=s.subarray(0,-16),a=s.subarray(-16),f=kr(t,e,n,c,r);if(!mt(a,f))throw new Error(\"invalid tag\");return i.set(s.subarray(0,-16)),t(e,n,i,i,1),ge(f),i}}),pc=rn({blockSize:64,nonceLength:12,tagLength:16},Nr(vt)),xc=rn({blockSize:64,nonceLength:24,tagLength:16},Nr(ns));var Bt=class{constructor(e,n){u(this,\"oHash\");u(this,\"iHash\");u(this,\"blockLen\");u(this,\"outputLen\");u(this,\"finished\",!1);u(this,\"destroyed\",!1);if(Xe(e),ue(n,void 0,\"key\"),this.iHash=e.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?e.create().update(n).digest():n);for(let s=0;s<o.length;s++)o[s]^=54;this.iHash.update(o),this.oHash=e.create();for(let s=0;s<o.length;s++)o[s]^=106;this.oHash.update(o),pe(o)}update(e){return Pe(this),this.iHash.update(e),this}digestInto(e){Pe(this),ue(e,this.outputLen,\"output\"),this.finished=!0,this.iHash.digestInto(e),this.oHash.update(e),this.oHash.digestInto(e),this.destroy()}digest(){let e=new Uint8Array(this.oHash.outputLen);return this.digestInto(e),e}_cloneInto(e){e||(e=Object.create(Object.getPrototypeOf(this),{}));let{oHash:n,iHash:r,finished:o,destroyed:s,blockLen:i,outputLen:c}=this;return e=e,e.finished=o,e.destroyed=s,e.blockLen=i,e.outputLen=c,e.oHash=n._cloneInto(e.oHash),e.iHash=r._cloneInto(e.iHash),e}clone(){return this._cloneInto()}destroy(){this.destroyed=!0,this.oHash.destroy(),this.iHash.destroy()}},Ve=(t,e,n)=>new Bt(t,e).update(n).digest();Ve.create=(t,e)=>new Bt(t,e);function qr(t,e,n){return Xe(t),n===void 0&&(n=new Uint8Array(t.outputLen)),Ve(t,n,e)}var fn=Uint8Array.of(0),Pr=Uint8Array.of();function Dr(t,e,n,r=32){Xe(t),bt(r,\"length\");let o=t.outputLen;if(r>255*o)throw new Error(\"Length must be <= 255*HashLen\");let s=Math.ceil(r/o);n===void 0?n=Pr:ue(n,void 0,\"info\");let i=new Uint8Array(s*o),c=Ve.create(t,e),a=c._cloneInto(),f=new Uint8Array(c.outputLen);for(let h=0;h<s;h++)fn[0]=h+1,a.update(h===0?Pr:f).update(n).update(fn).digestInto(f),i.set(f,o*h),c._cloneInto(a);return c.destroy(),a.destroy(),pe(f,fn),i.slice(0,r)}function dn(t){return t instanceof Uint8Array||ArrayBuffer.isView(t)&&t.constructor.name===\"Uint8Array\"}function ss(t){if(!dn(t))throw new Error(\"Uint8Array expected\")}function Kr(t,e){return Array.isArray(e)?e.length===0?!0:t?e.every(n=>typeof n==\"string\"):e.every(n=>Number.isSafeInteger(n)):!1}function is(t){if(typeof t!=\"function\")throw new Error(\"function expected\");return!0}function _e(t,e){if(typeof e!=\"string\")throw new Error(`${t}: string expected`);return!0}function bn(t){if(!Number.isSafeInteger(t))throw new Error(`invalid integer: ${t}`)}function un(t){if(!Array.isArray(t))throw new Error(\"array expected\")}function St(t,e){if(!Kr(!0,e))throw new Error(`${t}: array of strings expected`)}function jr(t,e){if(!Kr(!1,e))throw new Error(`${t}: array of numbers expected`)}function Wr(...t){let e=s=>s,n=(s,i)=>c=>s(i(c)),r=t.map(s=>s.encode).reduceRight(n,e),o=t.map(s=>s.decode).reduce(n,e);return{encode:r,decode:o}}function Gr(t){let e=typeof t==\"string\"?t.split(\"\"):t,n=e.length;St(\"alphabet\",e);let r=new Map(e.map((o,s)=>[o,s]));return{encode:o=>(un(o),o.map(s=>{if(!Number.isSafeInteger(s)||s<0||s>=n)throw new Error(`alphabet.encode: digit index outside alphabet \"${s}\". Allowed: ${t}`);return e[s]})),decode:o=>(un(o),o.map(s=>{_e(\"alphabet.decode\",s);let i=r.get(s);if(i===void 0)throw new Error(`Unknown letter: \"${s}\". Allowed: ${t}`);return i}))}}function Zr(t=\"\"){return _e(\"join\",t),{encode:e=>(St(\"join.decode\",e),e.join(t)),decode:e=>(_e(\"join.decode\",e),e.split(t))}}function cs(t,e=\"=\"){return bn(t),_e(\"padding\",e),{encode(n){for(St(\"padding.encode\",n);n.length*t%8;)n.push(e);return n},decode(n){St(\"padding.decode\",n);let r=n.length;if(r*t%8)throw new Error(\"padding: invalid, string should have whole number of bytes\");for(;r>0&&n[r-1]===e;r--)if((r-1)*t%8===0)throw new Error(\"padding: invalid, string has too much padding\");return n.slice(0,r)}}}var zr=(t,e)=>e===0?t:zr(e,t%e),Lt=(t,e)=>t+(e-zr(t,e)),At=(()=>{let t=[];for(let e=0;e<40;e++)t.push(2**e);return t})();function ln(t,e,n,r){if(un(t),e<=0||e>32)throw new Error(`convertRadix2: wrong from=${e}`);if(n<=0||n>32)throw new Error(`convertRadix2: wrong to=${n}`);if(Lt(e,n)>32)throw new Error(`convertRadix2: carry overflow from=${e} to=${n} carryBits=${Lt(e,n)}`);let o=0,s=0,i=At[e],c=At[n]-1,a=[];for(let f of t){if(bn(f),f>=i)throw new Error(`convertRadix2: invalid data word=${f} from=${e}`);if(o=o<<e|f,s+e>32)throw new Error(`convertRadix2: carry overflow pos=${s} from=${e}`);for(s+=e;s>=n;s-=n)a.push((o>>s-n&c)>>>0);let h=At[s];if(h===void 0)throw new Error(\"invalid carry\");o&=h-1}if(o=o<<n-s&c,!r&&s>=e)throw new Error(\"Excess padding\");if(!r&&o>0)throw new Error(`Non-zero padding: ${o}`);return r&&s>0&&a.push(o>>>0),a}function Yr(t,e=!1){if(bn(t),t<=0||t>32)throw new Error(\"radix2: bits should be in (0..32]\");if(Lt(8,t)>32||Lt(t,8)>32)throw new Error(\"radix2: carry overflow\");return{encode:n=>{if(!dn(n))throw new Error(\"radix2.encode input should be Uint8Array\");return ln(Array.from(n),8,t,!e)},decode:n=>(jr(\"radix2.decode\",n),Uint8Array.from(ln(n,t,8,e)))}}function $r(t){return is(t),function(...e){try{return t.apply(null,e)}catch{}}}var as=typeof Uint8Array.from([]).toBase64==\"function\"&&typeof Uint8Array.fromBase64==\"function\",fs=(t,e)=>{_e(\"base64\",t);let n=e?/^[A-Za-z0-9=_-]+$/:/^[A-Za-z0-9=+/]+$/,r=e?\"base64url\":\"base64\";if(t.length>0&&!n.test(t))throw new Error(\"invalid base64\");return Uint8Array.fromBase64(t,{alphabet:r,lastChunkHandling:\"strict\"})},pn=as?{encode(t){return ss(t),t.toBase64()},decode(t){return fs(t,!1)}}:Wr(Yr(6),Gr(\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"),cs(6),Zr(\"\"));var hn=Wr(Gr(\"qpzry9x8gf2tvdw0s3jn54khce6mua7l\"),Zr(\"\")),Vr=[996825010,642813549,513874426,1027748829,705979059];function Je(t){let e=t>>25,n=(t&33554431)<<5;for(let r=0;r<Vr.length;r++)(e>>r&1)===1&&(n^=Vr[r]);return n}function Mr(t,e,n=1){let r=t.length,o=1;for(let s=0;s<r;s++){let i=t.charCodeAt(s);if(i<33||i>126)throw new Error(`Invalid prefix (${t})`);o=Je(o)^i>>5}o=Je(o);for(let s=0;s<r;s++)o=Je(o)^t.charCodeAt(s)&31;for(let s of e)o=Je(o)^s;for(let s=0;s<6;s++)o=Je(o);return o^=n,hn.encode(ln([o%At[30]],30,5,!1))}function us(t){let e=t===\"bech32\"?1:734539939,n=Yr(5),r=n.decode,o=n.encode,s=$r(r);function i(l,x,d=90){_e(\"bech32.encode prefix\",l),dn(x)&&(x=Array.from(x)),jr(\"bech32.encode\",x);let y=l.length;if(y===0)throw new TypeError(`Invalid prefix length ${y}`);let m=y+7+x.length;if(d!==!1&&m>d)throw new TypeError(`Length ${m} exceeds limit ${d}`);let E=l.toLowerCase(),S=Mr(E,x,e);return`${E}1${hn.encode(x)}${S}`}function c(l,x=90){_e(\"bech32.decode input\",l);let d=l.length;if(d<8||x!==!1&&d>x)throw new TypeError(`invalid string length: ${d} (${l}). Expected (8..${x})`);let y=l.toLowerCase();if(l!==y&&l!==l.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 E=y.slice(0,m),S=y.slice(m+1);if(S.length<6)throw new Error(\"Data must be at least 6 characters long\");let U=hn.decode(S).slice(0,-6),C=Mr(E,U,e);if(!S.endsWith(C))throw new Error(`Invalid checksum in ${l}: expected \"${C}\"`);return{prefix:E,words:U}}let a=$r(c);function f(l){let{prefix:x,words:d}=c(l,!1);return{prefix:x,words:d,bytes:r(d)}}function h(l,x){return i(l,o(x))}return{encode:i,decode:c,encodeFromBytes:h,decodeToBytes:f,decodeUnsafe:a,fromWords:r,fromWordsUnsafe:s,toWords:o}}var xn=us(\"bech32\");var ls=new TextDecoder(\"utf-8\"),En=new TextEncoder;function Ke(t){try{t.indexOf(\"://\")===-1&&(t=\"wss://\"+t);let e=new URL(t);return e.protocol===\"http:\"?e.protocol=\"ws:\":e.protocol===\"https:\"&&(e.protocol=\"wss:\"),e.pathname=e.pathname.replace(/\\/+/g,\"/\"),e.pathname.endsWith(\"/\")&&(e.pathname=e.pathname.slice(0,-1)),(e.port===\"80\"&&e.protocol===\"ws:\"||e.port===\"443\"&&e.protocol===\"wss:\")&&(e.port=\"\"),e.searchParams.sort(),e.hash=\"\",e.toString()}catch{throw new Error(`Invalid URL: ${t}`)}}function hs(t){if(t.length!==64)return!1;for(let e=0;e<64;e++){let n=t.charCodeAt(e);if(isNaN(n)||n<48||n>102||n>57&&n<97)return!1}return!0}var Me=Symbol(\"verified\"),ds=t=>t instanceof Object;function bs(t){if(!ds(t)||typeof t.kind!=\"number\"||typeof t.content!=\"string\"||typeof t.created_at!=\"number\"||typeof t.pubkey!=\"string\"||!hs(t.pubkey)||!Array.isArray(t.tags))return!1;for(let e=0;e<t.tags.length;e++){let n=t.tags[e];if(!Array.isArray(n))return!1;for(let r=0;r<n.length;r++)if(typeof n[r]!=\"string\")return!1}return!0}var ps=class{generateSecretKey(){return oe.utils.randomSecretKey()}getPublicKey(t){return ae(oe.getPublicKey(t))}finalizeEvent(t,e){let n=t;return n.pubkey=ae(oe.getPublicKey(e)),n.id=yn(n),n.sig=ae(oe.sign(ne(yn(n)),e)),n[Me]=!0,n}verifyEvent(t){if(typeof t[Me]==\"boolean\")return t[Me];try{let e=yn(t);if(e!==t.id)return t[Me]=!1,!1;let n=oe.verify(ne(t.sig),ne(e),ne(t.pubkey));return t[Me]=n,n}catch{return t[Me]=!1,!1}}};function xs(t){if(!bs(t))throw new Error(\"can't serialize event with wrong or missing properties\");return JSON.stringify([0,t.pubkey,t.created_at,t.kind,t.tags,t.content])}function yn(t){let e=Ae(En.encode(xs(t)));return ae(e)}var Ht=new ps,Hc=Ht.generateSecretKey,Fr=Ht.getPublicKey,ys=Ht.finalizeEvent,Jr=Ht.verifyEvent,vn=1,Bn=4294967295,An=65536;function gn(t,e){let n=lr.getSharedSecret(t,ne(\"02\"+e)).subarray(1,33);return qr(Ae,n,En.encode(\"nip44-v2\"))}function Qr(t,e){let n=Dr(Ae,t,e,76);return{chacha_key:n.subarray(0,32),chacha_nonce:n.subarray(32,44),hmac_key:n.subarray(44,76)}}function eo(t){if(!Number.isSafeInteger(t)||t<1)throw new Error(\"expected positive integer\");if(t<=32)return 32;let e=2**(Math.floor(Math.log2(t-1))+1),n=e<=256?32:e/8;return n*(Math.floor((t-1)/n)+1)}function gs(t){if(!Number.isSafeInteger(t)||t<vn||t>65535)throw new Error(\"invalid plaintext size: must be between 1 and 65535 bytes\");let e=new Uint8Array(2);return new DataView(e.buffer).setUint16(0,t,!1),e}function ws(t){if(!Number.isSafeInteger(t)||t<An||t>Bn)throw new Error(\"invalid plaintext size: must be between 65536 and 4294967295 bytes\");let e=new Uint8Array(4);return new DataView(e.buffer).setUint32(0,t,!1),e}function ms(t){let e=En.encode(t),n=e.length;if(n<vn||n>Bn)throw new Error(\"invalid plaintext size: must be between 1 and 4294967295 bytes\");let r=n>=An?qe(new Uint8Array([0,0]),ws(n)):gs(n),o=new Uint8Array(eo(n)-n);return qe(r,e,o)}function Es(t){let e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=e.getUint16(0),r,o;if(n===0){if(r=e.getUint32(2),r<An)throw new Error(\"invalid padding\");o=6}else r=n,o=2;let s=t.subarray(o,o+r);if(r<vn||r>Bn||s.length!==r||t.length!==o+eo(r))throw new Error(\"invalid padding\");return ls.decode(s)}function to(t,e,n){if(n.length!==32)throw new Error(\"AAD associated data must be 32 bytes\");let r=qe(n,e);return Ve(Ae,t,r)}function vs(t){if(typeof t!=\"string\")throw new Error(\"payload must be a valid string\");let e=t.length;if(e<132)throw new Error(\"invalid payload length: \"+e);if(t[0]===\"#\")throw new Error(\"unknown encryption version\");let n;try{n=pn.decode(t)}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 Bs(t,e,n=wr(32)){let{chacha_key:r,chacha_nonce:o,hmac_key:s}=Qr(e,n),i=ms(t),c=vt(r,o,i),a=to(s,c,n);return pn.encode(qe(new Uint8Array([2]),n,c,a))}function Xr(t,e){let{nonce:n,ciphertext:r,mac:o}=vs(t),{chacha_key:s,chacha_nonce:i,hmac_key:c}=Qr(e,n),a=to(c,r,n);if(!mt(a,o))throw new Error(\"invalid MAC\");let f=vt(s,i,r);return Es(f)}var As=/^(?:([\\w.+-]+)@)?([\\w_-]+(\\.[\\w_-]+)+)$/,Ss;try{Ss=fetch}catch{}var Ls=22242,wn=24133;function Ts(t,e){if(t.ids&&t.ids.indexOf(e.id)===-1||t.kinds&&t.kinds.indexOf(e.kind)===-1||t.authors&&t.authors.indexOf(e.pubkey)===-1)return!1;for(let n in t)if(n[0]===\"#\"){let r=n.slice(1),o=t[`#${r}`];if(o&&!e.tags.find(([s,i])=>s===n.slice(1)&&o.indexOf(i)!==-1))return!1}return!(t.since&&e.created_at<t.since||t.until&&e.created_at>t.until)}function Hs(t,e){for(let n=0;n<t.length;n++)if(Ts(t[n],e))return!0;return!1}function Rs(t,e){let n=e.length+3,r=t.indexOf(`\"${e}\":`)+n,o=t.slice(r).indexOf('\"')+r+1;return t.slice(o,o+64)}function Os(t){let e=t.slice(0,22).indexOf('\"EVENT\"');if(e===-1)return null;let n=t.slice(e+7+1).indexOf('\"');if(n===-1)return null;let r=e+7+1+n,o=t.slice(r+1,80).indexOf('\"');if(o===-1)return null;let s=r+1+o;return t.slice(r+1,s)}function Us(t,e){return{kind:Ls,created_at:Math.floor(Date.now()/1e3),tags:[[\"relay\",t],[\"challenge\",e]],content:\"\"}}var mn=class extends Error{constructor(t,e){super(`Tried to send message '${t} on a closed connection to ${e}.`),this.name=\"SendingOnClosedConnection\"}},no=class{constructor(t,e){u(this,\"url\");u(this,\"_connected\",!1);u(this,\"onclose\",null);u(this,\"onnotice\",t=>console.debug(`NOTICE from ${this.url}: ${t}`));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=Ke(t),this.verifyEvent=e.verifyEvent,this._WebSocket=e.websocketImplementation||WebSocket,this.enablePing=e.enablePing,this.enableReconnect=e.enableReconnect||!1,e.idleTimeout&&(this.idleTimeout=e.idleTimeout)}static async connect(t,e){let n=new no(t,e);return await n.connect(e),n}closeAllSubscriptions(t){for(let[e,n]of this.openSubs)n.close(t);this.openSubs.clear();for(let[e,n]of this.openEventPublishes)n.reject(new Error(t));this.openEventPublishes.clear();for(let[e,n]of this.openCountRequests)n.reject(new Error(t));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 t=this.resubscribeBackoff[Math.min(this.reconnectAttempts,this.resubscribeBackoff.length-1)];this.reconnectAttempts++,this.reconnectTimeoutHandle=setTimeout(async()=>{try{await this.connect()}catch{}},t)}handleHardClose(t){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(t))}async connect(t){let e;return this.connectionPromise?this.connectionPromise:(this.challenge=void 0,this.authPromise=void 0,this.skipReconnection=!1,this.connectionPromise=new Promise((n,r)=>{t?.timeout&&(e=setTimeout(()=>{r(\"connection timed out\"),this.connectionPromise=void 0,this.reconnectAttempts===0&&(this.skipReconnection=!0),this.handleHardClose(\"relay connection timed out\")},t.timeout)),t?.abort&&(t.abort.onabort=r);try{this.ws=new this._WebSocket(this.url)}catch(o){clearTimeout(e),r(o);return}this.ws.onopen=()=>{this.reconnectTimeoutHandle&&(clearTimeout(this.reconnectTimeoutHandle),this.reconnectTimeoutHandle=void 0),clearTimeout(e),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(e),r(\"connection failed\"),this.connectionPromise=void 0,this.reconnectAttempts===0&&(this.skipReconnection=!0),this.handleHardClose(\"relay connection failed\")},this.ws.onclose=o=>{clearTimeout(e),r(o.message||\"websocket closed\"),this.handleHardClose(\"relay connection closed\")},this.ws.onmessage=this._onmessage.bind(this)}),this.connectionPromise)}waitForPingPong(){return new Promise(t=>{this.ws.once(\"pong\",()=>t(!0)),this.ws.ping()})}waitForDummyReq(){return new Promise((t,e)=>{if(!this.connectionPromise)return e(new Error(`no connection to ${this.url}, can't ping`));try{let n=this.subscribe([{ids:[\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"],limit:0}],{label:\"<forced-ping>\",oneose:()=>{t(!0),n.close()},onclose(){t(!0)},eoseTimeout:this.pingTimeout+1e3})}catch(n){e(n)}})}async pingpong(){this.ws?.readyState===1&&(await Promise.any([this.ws&&this.ws.ping&&this.ws.once?this.waitForPingPong():this.waitForDummyReq(),new Promise(e=>setTimeout(()=>e(!1),this.pingTimeout))])||this.ws?.readyState===this._WebSocket.OPEN&&this.ws?.close())}async send(t){if(!this.connectionPromise)throw new mn(t,this.url);this.connectionPromise.then(()=>{this.ws?.send(t)})}async auth(t){let e=this.challenge;if(!e)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 t(Us(this.url,e)),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(t){this.idleSince=void 0,this.clearIdleTimeout(),this.ongoingOperations++;let e=new Promise((n,r)=>{let o=setTimeout(()=>{let s=this.openEventPublishes.get(t.id);s&&(s.reject(new Error(\"publish timed out\")),this.openEventPublishes.delete(t.id))},this.publishTimeout);this.openEventPublishes.set(t.id,{resolve:n,reject:r,timeout:o})});try{await this.send('[\"EVENT\",'+JSON.stringify(t)+\"]\")}catch(n){let r=this.openEventPublishes.get(t.id);r&&(r.reject(n),this.openEventPublishes.delete(t.id))}return this.ongoingOperations--,this.ongoingOperations===0&&(this.idleSince=Date.now(),this.scheduleIdleClose()),e}async count(t,e){return(await this.countWithHLL(t,e)).count}async countWithHLL(t,e){this.serial++;let n=e?.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(t).substring(1))}catch(o){let s=this.openCountRequests.get(n);s&&(s.reject(o),this.openCountRequests.delete(n))}return r}subscribe(t,e){e.label!==\"<forced-ping>\"&&(this.idleSince=void 0,this.clearIdleTimeout(),this.ongoingOperations++);let n=this.prepareSubscription(t,e);return n.fire(),e.abort&&(e.abort.onabort=()=>n.close(String(e.abort.reason||\"<aborted>\"))),n}prepareSubscription(t,e){this.serial++;let n=e.id||(e.label?e.label+\":\":\"sub:\")+this.serial,r=new _s(this,n,t,e);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(t){let e=t.data;if(!e)return;let n=Os(e);if(n){let r=this.openSubs.get(n);if(!r)return;let o=Rs(e,\"id\"),s=r.alreadyHaveEvent?.(o);if(r.receivedEvent?.(this,o),s)return}try{let r=JSON.parse(e);switch(r[0]){case\"EVENT\":{let o=this.openSubs.get(r[1]),s=r[2];Hs(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],c=this.openEventPublishes.get(o);c&&(clearTimeout(c.timeout),s?c.resolve(i):c.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 mn))throw o});return}default:{this.openSubs.get(r[1])?.oncustom?.(r);return}}}catch(r){try{let[o,s,i]=JSON.parse(e);console.warn(`[nostr] relay ${this.url} error processing message:`,r,i)}catch{console.warn(`[nostr] relay ${this.url} error processing message:`,r)}return}}},_s=class{constructor(t,e,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=t,this.filters=n,this.id=e,this.alreadyHaveEvent=r.alreadyHaveEvent,this.receivedEvent=r.receivedEvent,this.eoseTimeout=r.eoseTimeout||t.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(t=\"closed by caller\"){if(!this.closed&&this.relay.connected){try{this.relay.send('[\"CLOSE\",'+JSON.stringify(this.id)+\"]\")}catch(e){if(!(e instanceof mn))throw e}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?.(t)}},we=256,Is=we*2,Pc=new TextEncoder;function ks(t,e){switch(e){case\"reactions\":return{\"#e\":[t],kinds:[7]};case\"reposts\":return{\"#e\":[t],kinds:[6]};case\"quotes\":return{\"#q\":[t],kinds:[1,1111]};case\"replies\":return{\"#e\":[t],kinds:[1]};case\"comments\":return{\"#E\":[t],kinds:[1111]};case\"followers\":return{\"#p\":[t],kinds:[3]}}}function Cs(){return new Uint8Array(we)}function Ns(t){if(t.length!==Is||!/^[0-9a-f]+$/.test(t))return;let e=new Uint8Array(we);for(let n=0;n<we;n++)e[n]=parseInt(t.slice(n*2,n*2+2),16);return e}function Ps(t){if(t.length!==we)throw new Error(`invalid number of registers ${t.length}`);let e=\"\";for(let n=0;n<we;n++)e+=t[n].toString(16).padStart(2,\"0\");return e}function qs(t,e){if(t.length===0&&(t=Cs()),t.length!==we)throw new Error(`invalid number of registers ${t.length}`);if(e.length!==we)throw new Error(`invalid number of registers ${e.length}`);for(let n=0;n<we;n++)e[n]>t[n]&&(t[n]=e[n]);return t}var Ds=class{constructor(t){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=t.verifyEvent,this._WebSocket=t.websocketImplementation,this.enablePing=t.enablePing,this.enableReconnect=t.enableReconnect||!1,t.idleTimeout&&(this.idleTimeout=t.idleTimeout),this.automaticallyAuth=t.automaticallyAuth,this.onRelayConnectionFailure=t.onRelayConnectionFailure,this.onRelayConnectionSuccess=t.onRelayConnectionSuccess,this.allowConnectingToRelay=t.allowConnectingToRelay,this.maxWaitForConnection=t.maxWaitForConnection||3e3}async ensureRelay(t,e){t=Ke(t);let n=this.relays.get(t);if(n||(n=new no(t,{verifyEvent:this.verifyEvent,websocketImplementation:this._WebSocket,enablePing:this.enablePing,enableReconnect:this.enableReconnect,idleTimeout:this.idleTimeout}),n.onclose=()=>{this.relays.delete(t)},this.relays.set(t,n)),this.automaticallyAuth){let r=this.automaticallyAuth(t);r&&(n.onauth=r)}try{await n.connect({timeout:e?.connectionTimeout,abort:e?.abort})}catch(r){throw this.relays.delete(t),r}return n}close(t){t.map(Ke).forEach(e=>{this.relays.get(e)?.close(),this.relays.delete(e)})}subscribe(t,e,n){let r=[],o=[];for(let s=0;s<t.length;s++){let i=Ke(t[s]);r.find(c=>c.url===i)||o.indexOf(i)===-1&&(o.push(i),r.push({url:i,filter:e}))}return this.subscribeMap(r,n)}subscribeMany(t,e,n){return this.subscribe(t,e,n)}subscribeMap(t,e){let n=new Map;for(let x of t){let{url:d,filter:y}=x;n.has(d)||n.set(d,[]),n.get(d).push(y)}let r=Array.from(n.entries()).map(([x,d])=>({url:x,filters:d}));this.trackRelays&&(e.receivedEvent=(x,d)=>{let y=this.seenOn.get(d);y||(y=new Set,this.seenOn.set(d,y)),y.add(x)});let o=new Set,s=[],i=[],c=x=>{i[x]||(i[x]=!0,i.filter(d=>d).length===r.length&&(e.oneose?.(),c=()=>{}))},a=[],f=(x,d,y)=>{a[x]||(c(x),a[x]={url:d,reason:y},a.filter(m=>m).length===r.length&&(e.onclose?.(a),f=()=>{}))},h=x=>{if(e.alreadyHaveEvent?.(x))return!0;let d=o.has(x);return o.add(x),d},l=Promise.all(r.map(async({url:x,filters:d},y)=>{if(this.allowConnectingToRelay?.(x,[\"read\",d])===!1){f(y,x,\"connection skipped by allowConnectingToRelay\");return}let m;try{m=await this.ensureRelay(x,{connectionTimeout:this.maxWaitForConnection<(e.maxWait||0)?Math.max(e.maxWait*.8,e.maxWait-1e3):this.maxWaitForConnection,abort:e.abort})}catch(S){this.onRelayConnectionFailure?.(x),f(y,x,S?.message||String(S));return}this.onRelayConnectionSuccess?.(x);let E=m.subscribe(d,{...e,oneose:()=>c(y),onclose:S=>{S.startsWith(\"auth-required: \")&&e.onauth?m.auth(e.onauth).then(()=>{m.subscribe(d,{...e,oneose:()=>c(y),onclose:U=>{f(y,x,U)},alreadyHaveEvent:h,eoseTimeout:e.maxWait,abort:e.abort})}).catch(U=>{f(y,x,`auth was required and attempted, but failed with: ${U}`)}):f(y,x,S)},alreadyHaveEvent:h,eoseTimeout:e.maxWait,abort:e.abort});s.push(E)}));return{async close(x){await l,s.forEach(d=>{d.close(x)})}}}subscribeEose(t,e,n){let r;return r=this.subscribe(t,e,{...n,oneose(){let o=\"closed automatically on eose\";r?r.close(o):n.onclose?.(t.map(s=>({url:s,reason:o})))}}),r}subscribeManyEose(t,e,n){return this.subscribeEose(t,e,n)}async querySync(t,e,n){return new Promise(async r=>{let o=[];this.subscribeEose(t,e,{...n,onevent(s){o.push(s)},onclose(s){r(o)}})})}async get(t,e,n){e.limit=1;let r=await this.querySync(t,e,n);return r.sort((o,s)=>s.created_at-o.created_at),r[0]||null}async countMany(t,e,n,r){let o=ks(e,n),s=[];for(let f=0;f<t.length;f++){let h=Ke(t[f]);s.indexOf(h)===-1&&s.push(h)}let i=await Promise.all(s.map(async f=>{if(this.allowConnectingToRelay?.(f,[\"read\",[o]])===!1)return null;let h;try{h=await this.ensureRelay(f,{connectionTimeout:this.maxWaitForConnection<(r?.maxWait||0)?Math.max(r.maxWait*.8,r.maxWait-1e3):this.maxWaitForConnection,abort:r?.abort})}catch{return this.onRelayConnectionFailure?.(f),null}return this.onRelayConnectionSuccess?.(f),h.countWithHLL([o],{id:r?.id}).catch(()=>null)})),c=0,a;for(let f of i){if(!f||(f.count>c&&(c=f.count),!f.hll||f.hll.length!==512))continue;let h=Ns(f.hll);h&&(a=qs(a||new Uint8Array(0),h))}return a?{count:c,hll:Ps(a)}:{count:c}}publish(t,e,n){return t.map(Ke).map(async(r,o,s)=>{if(s.indexOf(r)!==o)return Promise.reject(\"duplicate url\");if(this.allowConnectingToRelay?.(r,[\"write\",e])===!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(c){return this.onRelayConnectionFailure?.(r),Promise.reject(\"connection failure: \"+String(c))}return i.publish(e).catch(async c=>{if(c instanceof Error&&c.message.startsWith(\"auth-required: \")&&n?.onauth)return await i.auth(n.onauth),i.publish(e);throw c}).then(c=>{if(this.trackRelays){let a=this.seenOn.get(e.id);a||(a=new Set,this.seenOn.set(e.id,a)),a.add(i)}return c})})}listConnectionStatus(){let t=new Map;return this.relays.forEach((e,n)=>t.set(n,e.connected)),t}destroy(){this.relays.forEach(t=>t.close()),this.relays=new Map}pruneIdleRelays(t=1e4){let e=[];for(let[n,r]of this.relays)r.idleSince&&Date.now()-r.idleSince>=t&&(this.relays.delete(n),e.push(n),r.close());return e}},ro;try{ro=WebSocket}catch{}var $s=class extends Ds{constructor(t){super({verifyEvent:Jr,websocketImplementation:ro,maxWaitForConnection:3e3,...t})}},oo;try{oo=fetch}catch{}var Vs=/^bunker:\\/\\/([0-9a-f]{64})\\??([?\\/\\w:.=&%-]*)$/;async function so(t){let e=t.match(Vs);if(e)try{let n=e[1],r=new URLSearchParams(e[2]);return{pubkey:n,relays:r.getAll(\"relay\"),secret:r.get(\"secret\")}}catch{}return Ms(t)}async function Ms(t){let e=t.match(As);if(!e)return null;let[n,r=\"_\",o]=e;try{let s=`https://${o}/.well-known/nostr.json?name=${r}`,i=await(await oo(s,{redirect:\"error\"})).json(),c=i.names[r],a=i.nip46[c]||[];return{pubkey:c,relays:a,secret:null}}catch{return null}}function io(t){let e=new URLSearchParams;return t.relays.forEach(n=>{e.append(\"relay\",n)}),e.append(\"secret\",t.secret),t.perms&&t.perms.length>0&&e.append(\"perms\",t.perms.join(\",\")),t.name&&e.append(\"name\",t.name),t.url&&e.append(\"url\",t.url),t.image&&e.append(\"image\",t.image),`nostrconnect://${t.clientPubkey}?${e.toString()}`}var Tt=class{constructor(t,e){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=e,this.pool=e.pool||new $s,this.secretKey=t,this.isOpen=!1,this.idPrefix=Math.random().toString(36).substring(7),this.serial=0,this.listeners={},this.waitingForAuth={}}static fromBunker(t,e,n={}){if(e.relays.length===0)throw new Error(\"no relays specified for this bunker\");let r=new Tt(t,n);return r.conversationKey=gn(t,e.pubkey),r.bp=e,r.setupSubscription(),r}static async fromURI(t,e,n={},r=3e5){let o=new URL(e),s=o.searchParams.get(\"secret\");if(!s)throw new Error(\"nostrconnect:// URI has no secret\");let i=new Tt(t,n),c=Fr(t);return new Promise((a,f)=>{let h=!1,l=i.pool.subscribe(o.searchParams.getAll(\"relay\"),{kinds:[wn],\"#p\":[c],limit:0},{onevent:async x=>{try{let d=gn(t,x.pubkey),y=Xr(x.content,d);JSON.parse(y).result===s&&(l.close(),i.bp={pubkey:x.pubkey,relays:o.searchParams.getAll(\"relay\"),secret:s},i.conversationKey=gn(t,x.pubkey),i.setupSubscription(),h=!0,n.skipSwitchRelays||await Promise.race([new Promise(E=>setTimeout(E,1e3)),i.switchRelays()]),a(i))}catch(d){console.warn(\"failed to process potential connection event\",d)}},onclose:()=>{h||f(new Error(\"subscription closed before connection was established.\"))},maxWait:typeof r==\"number\"?r:void 0,abort:typeof r!=\"number\"?r:void 0})})}setupSubscription(){let t=this.listeners,e=this.waitingForAuth,n=this.conversationKey;this.subCloser=this.pool.subscribe(this.bp.relays,{kinds:[wn],authors:[this.bp.pubkey],\"#p\":[Fr(this.secretKey)],limit:0},{onevent:async r=>{let o=JSON.parse(Xr(r.content,n)),{id:s,result:i,error:c}=o;if(i===\"auth_url\"&&e[s]){delete e[s],this.params.onauth?this.params.onauth(c):console.warn(`nostr-tools/nip46: remote signer ${this.bp.pubkey} tried to send an \"auth_url\"='${c}' but there was no onauth() callback configured.`);return}let a=t[s];a&&(c?a.reject(c):i&&a.resolve(i),delete t[s])},onclose:()=>{this.subCloser=void 0}}),this.isOpen=!0}async switchRelays(){try{let t=await this.sendRequest(\"switch_relays\",[]),e=JSON.parse(t);if(!e||JSON.stringify(e.sort())===JSON.stringify(this.bp.relays))return!1;this.bp.relays=e;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(t,e){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=Bs(JSON.stringify({id:o,method:t,params:e}),this.conversationKey),i=ys({kind:wn,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 t=await this.sendRequest(\"ping\",[]);if(t!==\"pong\")throw new Error(`result is not pong: ${t}`)}async connect(t){let e=[this.bp.pubkey,this.bp.secret||\"\"];t&&(e.push(\"\"),e.push(JSON.stringify(t))),await this.sendRequest(\"connect\",e)}async logout(){let t=await this.sendRequest(\"logout\",[]);if(t!==\"ack\")throw new Error(`result is not ack: ${t}`);await this.close()}async getPublicKey(){return this.cachedPubKey||(this.cachedPubKey=await this.sendRequest(\"get_public_key\",[])),this.cachedPubKey}async signEvent(t){let e=await this.sendRequest(\"sign_event\",[JSON.stringify(t)]),n=JSON.parse(e);if(Jr(n))return n;throw new Error(`event returned from bunker is improperly signed: ${JSON.stringify(n)}`)}async nip04Encrypt(t,e){return await this.sendRequest(\"nip04_encrypt\",[t,e])}async nip04Decrypt(t,e){return await this.sendRequest(\"nip04_decrypt\",[t,e])}async nip44Encrypt(t,e){return await this.sendRequest(\"nip44_encrypt\",[t,e])}async nip44Decrypt(t,e){return await this.sendRequest(\"nip44_decrypt\",[t,e])}};var Yc=new TextDecoder(\"utf-8\"),Fc=new TextEncoder;var Ks=5e3;function co(t){return Ws(\"npub\",ne(t))}function js(t,e){let n=xn.toWords(e);return xn.encode(t,n,Ks)}function Ws(t,e){return js(t,e)}function ao(t){return t instanceof Uint8Array||ArrayBuffer.isView(t)&&t.constructor.name===\"Uint8Array\"&&\"BYTES_PER_ELEMENT\"in t&&t.BYTES_PER_ELEMENT===1}var Sn=t=>t?`\"${t}\" `:\"\";function Gs(t,e=\"\"){if(typeof t!=\"number\")throw new TypeError(Sn(e)+\"expected number, got \"+typeof t);if(!Number.isSafeInteger(t)||t<0)throw new RangeError(Sn(e)+\"expected integer >= 0, got \"+t);return t}function Zs(t,e,n=\"\"){if(ao(t)&&(e===void 0||t.length===e))return t;e!==void 0&&Gs(e,\"length\");let r=ao(t),o=e!==void 0?` of length ${e}`:\"\",s=r?`length=${t.length}`:`type=${typeof t}`,i=Sn(n)+\"expected Uint8Array\"+o+\", got \"+s;throw r?new RangeError(i):new TypeError(i)}var uo=typeof Uint8Array.from([]).toHex==\"function\"&&typeof Uint8Array.fromHex==\"function\",zs=Array.from({length:256},(t,e)=>e.toString(16).padStart(2,\"0\"));function lo(t){if(Zs(t),uo)return t.toHex();let e=\"\";for(let n=0;n<t.length;n++)e+=zs[t[n]];return e}function fo(t){return t>=48&&t<=57?t-48:t>=65&&t<=70?t-55:t>=97&&t<=102?t-87:void 0}function ho(t){if(typeof t!=\"string\")throw new TypeError(\"hex string expected, got \"+typeof t);if(uo)try{return Uint8Array.fromHex(t)}catch(o){throw o instanceof SyntaxError?new RangeError(o.message):o}let e=t.length,n=e/2;if(e%2)throw new RangeError(\"hex string expected, got unpadded hex of length \"+e);let r=new Uint8Array(n);for(let o=0,s=0;o<n;o++,s+=2){let i=fo(t.charCodeAt(s)),c=fo(t.charCodeAt(s+1));if(i===void 0||c===void 0){let a=t[s]+t[s+1];throw new RangeError('hex string expected, got non-hex character \"'+a+'\" at index '+s)}r[o]=i*16+c}return r}window.NostrSigner={generateSecretKey:Br,getPublicKey:Ar,finalizeEvent:Sr,BunkerSigner:Tt,parseBunkerInput:so,createNostrConnectURI:io,npubEncode:co,bytesToHex:lo,hexToBytes:ho};})();\n";2+export const SIGNER_HASH = "f0b3555edcae";3+export const SIGNER_JS = "\"use strict\";(()=>{var po=Object.defineProperty;var xo=(t,e,n)=>e in t?po(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var u=(t,e,n)=>xo(t,typeof e!=\"symbol\"?e+\"\":e,n);function je(t){return t instanceof Uint8Array||ArrayBuffer.isView(t)&&t.constructor.name===\"Uint8Array\"}function fe(t,e=\"\"){if(!Number.isSafeInteger(t)||t<0){let n=e&&`\"${e}\" `;throw new Error(`${n}expected integer >= 0, got ${t}`)}}function N(t,e,n=\"\"){let r=je(t),o=t?.length,s=e!==void 0;if(!r||s&&o!==e){let i=n&&`\"${n}\" `,c=s?` of length ${e}`:\"\",a=r?`length=${o}`:`type=${typeof t}`;throw new Error(i+\"expected Uint8Array\"+c+\", got \"+a)}return t}function Qe(t){if(typeof t!=\"function\"||typeof t.create!=\"function\")throw new Error(\"Hash must wrapped by utils.createHasher\");fe(t.outputLen),fe(t.blockLen)}function Ie(t,e=!0){if(t.destroyed)throw new Error(\"Hash instance has been destroyed\");if(e&&t.finished)throw new Error(\"Hash#digest() has already been called\")}function Rn(t,e){N(t,void 0,\"digestInto() output\");let n=e.outputLen;if(t.length<n)throw new Error('\"digestInto() output\" expected to be of length >='+n)}function Se(...t){for(let e=0;e<t.length;e++)t[e].fill(0)}function et(t){return new DataView(t.buffer,t.byteOffset,t.byteLength)}function se(t,e){return t<<32-e|t>>>e}var On=typeof Uint8Array.from([]).toHex==\"function\"&&typeof Uint8Array.fromHex==\"function\",yo=Array.from({length:256},(t,e)=>e.toString(16).padStart(2,\"0\"));function Le(t){if(N(t),On)return t.toHex();let e=\"\";for(let n=0;n<t.length;n++)e+=yo[t[n]];return e}var le={_0:48,_9:57,A:65,F:70,a:97,f:102};function Hn(t){if(t>=le._0&&t<=le._9)return t-le._0;if(t>=le.A&&t<=le.F)return t-(le.A-10);if(t>=le.a&&t<=le.f)return t-(le.a-10)}function Te(t){if(typeof t!=\"string\")throw new Error(\"hex string expected, got \"+typeof t);if(On)return Uint8Array.fromHex(t);let e=t.length,n=e/2;if(e%2)throw new Error(\"hex string expected, got unpadded hex of length \"+e);let r=new Uint8Array(n);for(let o=0,s=0;o<n;o++,s+=2){let i=Hn(t.charCodeAt(s)),c=Hn(t.charCodeAt(s+1));if(i===void 0||c===void 0){let a=t[s]+t[s+1];throw new Error('hex string expected, got non-hex character \"'+a+'\" at index '+s)}r[o]=i*16+c}return r}function te(...t){let e=0;for(let r=0;r<t.length;r++){let o=t[r];N(o),e+=o.length}let n=new Uint8Array(e);for(let r=0,o=0;r<t.length;r++){let s=t[r];n.set(s,o),o+=s.length}return n}function Un(t,e={}){let n=(o,s)=>t(s).update(o).digest(),r=t(void 0);return n.outputLen=r.outputLen,n.blockLen=r.blockLen,n.create=o=>t(o),Object.assign(n,e),Object.freeze(n)}function He(t=32){let e=typeof globalThis==\"object\"?globalThis.crypto:null;if(typeof e?.getRandomValues!=\"function\")throw new Error(\"crypto.getRandomValues must be defined\");return e.getRandomValues(new Uint8Array(t))}var _n=t=>({oid:Uint8Array.from([6,9,96,134,72,1,101,3,4,2,t])});function In(t,e,n){return t&e^~t&n}function kn(t,e,n){return t&e^t&n^e&n}var tt=class{constructor(e,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=e,this.outputLen=n,this.padOffset=r,this.isLE=o,this.buffer=new Uint8Array(e),this.view=et(this.buffer)}update(e){Ie(this),N(e);let{view:n,buffer:r,blockLen:o}=this,s=e.length;for(let i=0;i<s;){let c=Math.min(o-this.pos,s-i);if(c===o){let a=et(e);for(;o<=s-i;i+=o)this.process(a,i);continue}r.set(e.subarray(i,i+c),this.pos),this.pos+=c,i+=c,this.pos===o&&(this.process(n,0),this.pos=0)}return this.length+=e.length,this.roundClean(),this}digestInto(e){Ie(this),Rn(e,this),this.finished=!0;let{buffer:n,view:r,blockLen:o,isLE:s}=this,{pos:i}=this;n[i++]=128,Se(this.buffer.subarray(i)),this.padOffset>o-i&&(this.process(r,0),i=0);for(let l=i;l<o;l++)n[l]=0;r.setBigUint64(o-8,BigInt(this.length*8),s),this.process(r,0);let c=et(e),a=this.outputLen;if(a%4)throw new Error(\"_sha2: outputLen must be aligned to 32bit\");let f=a/4,h=this.get();if(f>h.length)throw new Error(\"_sha2: outputLen bigger than state\");for(let l=0;l<f;l++)c.setUint32(4*l,h[l],s)}digest(){let{buffer:e,outputLen:n}=this;this.digestInto(e);let r=e.slice(0,n);return this.destroy(),r}_cloneInto(e){e||(e=new this.constructor),e.set(...this.get());let{blockLen:n,buffer:r,length:o,finished:s,destroyed:i,pos:c}=this;return e.destroyed=i,e.finished=s,e.length=o,e.pos=c,o%n&&e.buffer.set(r),e}clone(){return this._cloneInto()}},he=Uint32Array.from([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]);var go=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]),me=new Uint32Array(64),Rt=class extends tt{constructor(e){super(64,e,8,!1)}get(){let{A:e,B:n,C:r,D:o,E:s,F:i,G:c,H:a}=this;return[e,n,r,o,s,i,c,a]}set(e,n,r,o,s,i,c,a){this.A=e|0,this.B=n|0,this.C=r|0,this.D=o|0,this.E=s|0,this.F=i|0,this.G=c|0,this.H=a|0}process(e,n){for(let l=0;l<16;l++,n+=4)me[l]=e.getUint32(n,!1);for(let l=16;l<64;l++){let x=me[l-15],d=me[l-2],y=se(x,7)^se(x,18)^x>>>3,m=se(d,17)^se(d,19)^d>>>10;me[l]=m+me[l-7]+y+me[l-16]|0}let{A:r,B:o,C:s,D:i,E:c,F:a,G:f,H:h}=this;for(let l=0;l<64;l++){let x=se(c,6)^se(c,11)^se(c,25),d=h+x+In(c,a,f)+go[l]+me[l]|0,m=(se(r,2)^se(r,13)^se(r,22))+kn(r,o,s)|0;h=f,f=a,a=c,c=i+d|0,i=s,s=o,o=r,r=d+m|0}r=r+this.A|0,o=o+this.B|0,s=s+this.C|0,i=i+this.D|0,c=c+this.E|0,a=a+this.F|0,f=f+this.G|0,h=h+this.H|0,this.set(r,o,s,i,c,a,f,h)}roundClean(){Se(me)}destroy(){this.set(0,0,0,0,0,0,0,0),Se(this.buffer)}},Ot=class extends Rt{constructor(){super(32);u(this,\"A\",he[0]|0);u(this,\"B\",he[1]|0);u(this,\"C\",he[2]|0);u(this,\"D\",he[3]|0);u(this,\"E\",he[4]|0);u(this,\"F\",he[5]|0);u(this,\"G\",he[6]|0);u(this,\"H\",he[7]|0)}};var nt=Un(()=>new Ot,_n(1));var _t=BigInt(0),Ut=BigInt(1);function We(t,e=\"\"){if(typeof t!=\"boolean\"){let n=e&&`\"${e}\" `;throw new Error(n+\"expected boolean, got type=\"+typeof t)}return t}function Cn(t){if(typeof t==\"bigint\"){if(!rt(t))throw new Error(\"positive bigint expected, got \"+t)}else fe(t);return t}function Ge(t){let e=Cn(t).toString(16);return e.length&1?\"0\"+e:e}function Nn(t){if(typeof t!=\"string\")throw new Error(\"hex string expected, got \"+typeof t);return t===\"\"?_t:BigInt(\"0x\"+t)}function Ee(t){return Nn(Le(t))}function It(t){return Nn(Le(wo(N(t)).reverse()))}function ot(t,e){fe(e),t=Cn(t);let n=Te(t.toString(16).padStart(e*2,\"0\"));if(n.length!==e)throw new Error(\"number too large\");return n}function kt(t,e){return ot(t,e).reverse()}function wo(t){return Uint8Array.from(t)}function Pn(t){return Uint8Array.from(t,(e,n)=>{let r=e.charCodeAt(0);if(e.length!==1||r>127)throw new Error(`string contains non-ASCII character \"${t[n]}\" with code ${r} at position ${n}`);return r})}var rt=t=>typeof t==\"bigint\"&&_t<=t;function mo(t,e,n){return rt(t)&&rt(e)&&rt(n)&&e<=t&&t<n}function qn(t,e,n,r){if(!mo(e,n,r))throw new Error(\"expected valid \"+t+\": \"+n+\" <= n < \"+r+\", got \"+e)}function Ct(t){let e;for(e=0;t>_t;t>>=Ut,e+=1);return e}var Ze=t=>(Ut<<BigInt(t))-Ut;function Dn(t,e,n){if(fe(t,\"hashLen\"),fe(e,\"qByteLen\"),typeof n!=\"function\")throw new Error(\"hmacFn must be a function\");let r=E=>new Uint8Array(E),o=Uint8Array.of(),s=Uint8Array.of(0),i=Uint8Array.of(1),c=1e3,a=r(t),f=r(t),h=0,l=()=>{a.fill(1),f.fill(0),h=0},x=(...E)=>n(f,te(a,...E)),d=(E=o)=>{f=x(s,E),a=x(),E.length!==0&&(f=x(i,E),a=x())},y=()=>{if(h++>=c)throw new Error(\"drbg: tried max amount of iterations\");let E=0,S=[];for(;E<e;){a=x();let U=a.slice();S.push(U),E+=a.length}return te(...S)};return(E,S)=>{l(),d(E);let U;for(;!(U=S(y()));)d();return l(),U}}function ze(t,e={},n={}){if(!t||typeof t!=\"object\")throw new Error(\"expected valid options object\");function r(s,i,c){let a=t[s];if(c&&a===void 0)return;let f=typeof a;if(f!==i||a===null)throw new Error(`param \"${s}\" is invalid: expected ${i}, got ${f}`)}let o=(s,i)=>Object.entries(s).forEach(([c,a])=>r(c,a,i));o(e,!1),o(n,!0)}function Nt(t){let e=new WeakMap;return(n,...r)=>{let o=e.get(n);if(o!==void 0)return o;let s=t(n,...r);return e.set(n,s),s}}var ee=BigInt(0),J=BigInt(1),Re=BigInt(2),Mn=BigInt(3),Kn=BigInt(4),jn=BigInt(5),Eo=BigInt(7),Wn=BigInt(8),vo=BigInt(9),Gn=BigInt(16);function ie(t,e){let n=t%e;return n>=ee?n:e+n}function re(t,e,n){let r=t;for(;e-- >ee;)r*=r,r%=n;return r}function $n(t,e){if(t===ee)throw new Error(\"invert: expected non-zero number\");if(e<=ee)throw new Error(\"invert: expected positive modulus, got \"+e);let n=ie(t,e),r=e,o=ee,s=J,i=J,c=ee;for(;n!==ee;){let f=r/n,h=r%n,l=o-i*f,x=s-c*f;r=n,n=h,o=i,s=c,i=l,c=x}if(r!==J)throw new Error(\"invert: does not exist\");return ie(o,e)}function qt(t,e,n){if(!t.eql(t.sqr(e),n))throw new Error(\"Cannot find square root\")}function Zn(t,e){let n=(t.ORDER+J)/Kn,r=t.pow(e,n);return qt(t,r,e),r}function Bo(t,e){let n=(t.ORDER-jn)/Wn,r=t.mul(e,Re),o=t.pow(r,n),s=t.mul(e,o),i=t.mul(t.mul(s,Re),o),c=t.mul(s,t.sub(i,t.ONE));return qt(t,c,e),c}function Ao(t){let e=ke(t),n=zn(t),r=n(e,e.neg(e.ONE)),o=n(e,r),s=n(e,e.neg(r)),i=(t+Eo)/Gn;return(c,a)=>{let f=c.pow(a,i),h=c.mul(f,r),l=c.mul(f,o),x=c.mul(f,s),d=c.eql(c.sqr(h),a),y=c.eql(c.sqr(l),a);f=c.cmov(f,h,d),h=c.cmov(x,l,y);let m=c.eql(c.sqr(h),a),E=c.cmov(f,h,m);return qt(c,E,a),E}}function zn(t){if(t<Mn)throw new Error(\"sqrt is not defined for small field\");let e=t-J,n=0;for(;e%Re===ee;)e/=Re,n++;let r=Re,o=ke(t);for(;Vn(o,r)===1;)if(r++>1e3)throw new Error(\"Cannot find square root: probably non-prime P\");if(n===1)return Zn;let s=o.pow(r,e),i=(e+J)/Re;return function(a,f){if(a.is0(f))return f;if(Vn(a,f)!==1)throw new Error(\"Cannot find square root\");let h=n,l=a.mul(a.ONE,s),x=a.pow(f,e),d=a.pow(f,i);for(;!a.eql(x,a.ONE);){if(a.is0(x))return a.ZERO;let y=1,m=a.sqr(x);for(;!a.eql(m,a.ONE);)if(y++,m=a.sqr(m),y===h)throw new Error(\"Cannot find square root\");let E=J<<BigInt(h-y-1),S=a.pow(l,E);h=y,l=a.sqr(S),x=a.mul(x,l),d=a.mul(d,S)}return d}}function So(t){return t%Kn===Mn?Zn:t%Wn===jn?Bo:t%Gn===vo?Ao(t):zn(t)}var Lo=[\"create\",\"isValid\",\"is0\",\"neg\",\"inv\",\"sqrt\",\"sqr\",\"eql\",\"add\",\"sub\",\"mul\",\"pow\",\"div\",\"addN\",\"subN\",\"mulN\",\"sqrN\"];function Dt(t){let e={ORDER:\"bigint\",BYTES:\"number\",BITS:\"number\"},n=Lo.reduce((r,o)=>(r[o]=\"function\",r),e);return ze(t,n),t}function To(t,e,n){if(n<ee)throw new Error(\"invalid exponent, negatives unsupported\");if(n===ee)return t.ONE;if(n===J)return e;let r=t.ONE,o=e;for(;n>ee;)n&J&&(r=t.mul(r,o)),o=t.sqr(o),n>>=J;return r}function st(t,e,n=!1){let r=new Array(e.length).fill(n?t.ZERO:void 0),o=e.reduce((i,c,a)=>t.is0(c)?i:(r[a]=i,t.mul(i,c)),t.ONE),s=t.inv(o);return e.reduceRight((i,c,a)=>t.is0(c)?i:(r[a]=t.mul(i,r[a]),t.mul(i,c)),s),r}function Vn(t,e){let n=(t.ORDER-J)/Re,r=t.pow(e,n),o=t.eql(r,t.ONE),s=t.eql(r,t.ZERO),i=t.eql(r,t.neg(t.ONE));if(!o&&!s&&!i)throw new Error(\"invalid Legendre symbol result\");return o?1:s?0:-1}function Ho(t,e){e!==void 0&&fe(e);let n=e!==void 0?e:t.toString(2).length,r=Math.ceil(n/8);return{nBitLength:n,nByteLength:r}}var Pt=class{constructor(e,n={}){u(this,\"ORDER\");u(this,\"BITS\");u(this,\"BYTES\");u(this,\"isLE\");u(this,\"ZERO\",ee);u(this,\"ONE\",J);u(this,\"_lengths\");u(this,\"_sqrt\");u(this,\"_mod\");if(e<=ee)throw new Error(\"invalid field: expected ORDER > 0, got \"+e);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}=Ho(e,r);if(s>2048)throw new Error(\"invalid field: expected ORDER of <= 2048 bytes\");this.ORDER=e,this.BITS=o,this.BYTES=s,this._sqrt=void 0,Object.preventExtensions(this)}create(e){return ie(e,this.ORDER)}isValid(e){if(typeof e!=\"bigint\")throw new Error(\"invalid field element: expected bigint, got \"+typeof e);return ee<=e&&e<this.ORDER}is0(e){return e===ee}isValidNot0(e){return!this.is0(e)&&this.isValid(e)}isOdd(e){return(e&J)===J}neg(e){return ie(-e,this.ORDER)}eql(e,n){return e===n}sqr(e){return ie(e*e,this.ORDER)}add(e,n){return ie(e+n,this.ORDER)}sub(e,n){return ie(e-n,this.ORDER)}mul(e,n){return ie(e*n,this.ORDER)}pow(e,n){return To(this,e,n)}div(e,n){return ie(e*$n(n,this.ORDER),this.ORDER)}sqrN(e){return e*e}addN(e,n){return e+n}subN(e,n){return e-n}mulN(e,n){return e*n}inv(e){return $n(e,this.ORDER)}sqrt(e){return this._sqrt||(this._sqrt=So(this.ORDER)),this._sqrt(this,e)}toBytes(e){return this.isLE?kt(e,this.BYTES):ot(e,this.BYTES)}fromBytes(e,n=!1){N(e);let{_lengths:r,BYTES:o,isLE:s,ORDER:i,_mod:c}=this;if(r){if(!r.includes(e.length)||e.length>o)throw new Error(\"Field.fromBytes: expected \"+r+\" bytes, got \"+e.length);let f=new Uint8Array(o);f.set(e,s?0:f.length-e.length),e=f}if(e.length!==o)throw new Error(\"Field.fromBytes: expected \"+o+\" bytes, got \"+e.length);let a=s?It(e):Ee(e);if(c&&(a=ie(a,i)),!n&&!this.isValid(a))throw new Error(\"invalid field element: outside of range 0..ORDER\");return a}invertBatch(e){return st(this,e)}cmov(e,n,r){return r?n:e}};function ke(t,e={}){return new Pt(t,e)}function Yn(t){if(typeof t!=\"bigint\")throw new Error(\"field order must be bigint\");let e=t.toString(2).length;return Math.ceil(e/8)}function $t(t){let e=Yn(t);return e+Math.ceil(e/2)}function it(t,e,n=!1){N(t);let r=t.length,o=Yn(e),s=$t(e);if(r<16||r<s||r>1024)throw new Error(\"expected \"+s+\"-1024 bytes of input, got \"+r);let i=n?It(t):Ee(t),c=ie(i,e-J)+J;return n?kt(c,o):ot(c,o)}var Ce=BigInt(0),Oe=BigInt(1);function Ye(t,e){let n=e.negate();return t?n:e}function jt(t,e){let n=st(t.Fp,e.map(r=>r.Z));return e.map((r,o)=>t.fromAffine(r.toAffine(n[o])))}function Qn(t,e){if(!Number.isSafeInteger(t)||t<=0||t>e)throw new Error(\"invalid window size, expected [1..\"+e+\"], got W=\"+t)}function Vt(t,e){Qn(t,e);let n=Math.ceil(e/t)+1,r=2**(t-1),o=2**t,s=Ze(t),i=BigInt(t);return{windows:n,windowSize:r,mask:s,maxNumber:o,shiftBy:i}}function Fn(t,e,n){let{windowSize:r,mask:o,maxNumber:s,shiftBy:i}=n,c=Number(t&o),a=t>>i;c>r&&(c-=s,a+=Oe);let f=e*r,h=f+Math.abs(c)-1,l=c===0,x=c<0,d=e%2!==0;return{nextN:a,offset:h,isZero:l,isNeg:x,isNegF:d,offsetF:f}}var Mt=new WeakMap,er=new WeakMap;function Kt(t){return er.get(t)||1}function Xn(t){if(t!==Ce)throw new Error(\"invalid wNAF\")}var ct=class{constructor(e,n){u(this,\"BASE\");u(this,\"ZERO\");u(this,\"Fn\");u(this,\"bits\");this.BASE=e.BASE,this.ZERO=e.ZERO,this.Fn=e.Fn,this.bits=n}_unsafeLadder(e,n,r=this.ZERO){let o=e;for(;n>Ce;)n&Oe&&(r=r.add(o)),o=o.double(),n>>=Oe;return r}precomputeWindow(e,n){let{windows:r,windowSize:o}=Vt(n,this.bits),s=[],i=e,c=i;for(let a=0;a<r;a++){c=i,s.push(c);for(let f=1;f<o;f++)c=c.add(i),s.push(c);i=c.double()}return s}wNAF(e,n,r){if(!this.Fn.isValid(r))throw new Error(\"invalid scalar\");let o=this.ZERO,s=this.BASE,i=Vt(e,this.bits);for(let c=0;c<i.windows;c++){let{nextN:a,offset:f,isZero:h,isNeg:l,isNegF:x,offsetF:d}=Fn(r,c,i);r=a,h?s=s.add(Ye(x,n[d])):o=o.add(Ye(l,n[f]))}return Xn(r),{p:o,f:s}}wNAFUnsafe(e,n,r,o=this.ZERO){let s=Vt(e,this.bits);for(let i=0;i<s.windows&&r!==Ce;i++){let{nextN:c,offset:a,isZero:f,isNeg:h}=Fn(r,i,s);if(r=c,!f){let l=n[a];o=o.add(h?l.negate():l)}}return Xn(r),o}getPrecomputes(e,n,r){let o=Mt.get(n);return o||(o=this.precomputeWindow(n,e),e!==1&&(typeof r==\"function\"&&(o=r(o)),Mt.set(n,o))),o}cached(e,n,r){let o=Kt(e);return this.wNAF(o,this.getPrecomputes(o,e,r),n)}unsafe(e,n,r,o){let s=Kt(e);return s===1?this._unsafeLadder(e,n,o):this.wNAFUnsafe(s,this.getPrecomputes(s,e,r),n,o)}createCache(e,n){Qn(n,this.bits),er.set(e,n),Mt.delete(e)}hasCache(e){return Kt(e)!==1}};function tr(t,e,n,r){let o=e,s=t.ZERO,i=t.ZERO;for(;n>Ce||r>Ce;)n&Oe&&(s=s.add(o)),r&Oe&&(i=i.add(o)),o=o.double(),n>>=Oe,r>>=Oe;return{p1:s,p2:i}}function Jn(t,e,n){if(e){if(e.ORDER!==t)throw new Error(\"Field.ORDER must match order: Fp == p, Fn == n\");return Dt(e),e}else return ke(t,{isLE:n})}function nr(t,e,n={},r){if(r===void 0&&(r=t===\"edwards\"),!e||typeof e!=\"object\")throw new Error(`expected valid ${t} CURVE object`);for(let a of[\"p\",\"n\",\"h\"]){let f=e[a];if(!(typeof f==\"bigint\"&&f>Ce))throw new Error(`CURVE.${a} must be positive bigint`)}let o=Jn(e.p,n.Fp,r),s=Jn(e.n,n.Fn,r),c=[\"Gx\",\"Gy\",\"a\",t===\"weierstrass\"?\"b\":\"d\"];for(let a of c)if(!o.isValid(e[a]))throw new Error(`CURVE.${a} must be valid field element of CURVE.Fp`);return e=Object.freeze(Object.assign({},e)),{CURVE:e,Fp:o,Fn:s}}function at(t,e){return function(r){let o=t(r);return{secretKey:o,publicKey:e(o)}}}var ft=class{constructor(e,n){u(this,\"oHash\");u(this,\"iHash\");u(this,\"blockLen\");u(this,\"outputLen\");u(this,\"finished\",!1);u(this,\"destroyed\",!1);if(Qe(e),N(n,void 0,\"key\"),this.iHash=e.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?e.create().update(n).digest():n);for(let s=0;s<o.length;s++)o[s]^=54;this.iHash.update(o),this.oHash=e.create();for(let s=0;s<o.length;s++)o[s]^=106;this.oHash.update(o),Se(o)}update(e){return Ie(this),this.iHash.update(e),this}digestInto(e){Ie(this),N(e,this.outputLen,\"output\"),this.finished=!0,this.iHash.digestInto(e),this.oHash.update(e),this.oHash.digestInto(e),this.destroy()}digest(){let e=new Uint8Array(this.oHash.outputLen);return this.digestInto(e),e}_cloneInto(e){e||(e=Object.create(Object.getPrototypeOf(this),{}));let{oHash:n,iHash:r,finished:o,destroyed:s,blockLen:i,outputLen:c}=this;return e=e,e.finished=o,e.destroyed=s,e.blockLen=i,e.outputLen=c,e.oHash=n._cloneInto(e.oHash),e.iHash=r._cloneInto(e.iHash),e}clone(){return this._cloneInto()}destroy(){this.destroyed=!0,this.oHash.destroy(),this.iHash.destroy()}},Wt=(t,e,n)=>new ft(t,e).update(n).digest();Wt.create=(t,e)=>new ft(t,e);var rr=(t,e)=>(t+(t>=0?e:-e)/or)/e;function Ro(t,e,n){let[[r,o],[s,i]]=e,c=rr(i*t,n),a=rr(-o*t,n),f=t-c*r-a*s,h=-c*o-a*i,l=f<de,x=h<de;l&&(f=-f),x&&(h=-h);let d=Ze(Math.ceil(Ct(n)/2))+Ne;if(f<de||f>=d||h<de||h>=d)throw new Error(\"splitScalar (endomorphism): failed, k=\"+t);return{k1neg:l,k1:f,k2neg:x,k2:h}}function Zt(t){if(![\"compact\",\"recovered\",\"der\"].includes(t))throw new Error('Signature format must be \"compact\", \"recovered\", or \"der\"');return t}function Gt(t,e){let n={};for(let r of Object.keys(e))n[r]=t[r]===void 0?e[r]:t[r];return We(n.lowS,\"lowS\"),We(n.prehash,\"prehash\"),n.format!==void 0&&Zt(n.format),n}var zt=class extends Error{constructor(e=\"\"){super(e)}},ve={Err:zt,_tlv:{encode:(t,e)=>{let{Err:n}=ve;if(t<0||t>256)throw new n(\"tlv.encode: wrong tag\");if(e.length&1)throw new n(\"tlv.encode: unpadded data\");let r=e.length/2,o=Ge(r);if(o.length/2&128)throw new n(\"tlv.encode: long form length too big\");let s=r>127?Ge(o.length/2|128):\"\";return Ge(t)+s+o+e},decode(t,e){let{Err:n}=ve,r=0;if(t<0||t>256)throw new n(\"tlv.encode: wrong tag\");if(e.length<2||e[r++]!==t)throw new n(\"tlv.decode: wrong tlv\");let o=e[r++],s=!!(o&128),i=0;if(!s)i=o;else{let a=o&127;if(!a)throw new n(\"tlv.decode(long): indefinite length not supported\");if(a>4)throw new n(\"tlv.decode(long): byte length is too big\");let f=e.subarray(r,r+a);if(f.length!==a)throw new n(\"tlv.decode: length bytes not complete\");if(f[0]===0)throw new n(\"tlv.decode(long): zero leftmost byte\");for(let h of f)i=i<<8|h;if(r+=a,i<128)throw new n(\"tlv.decode(long): not minimal encoding\")}let c=e.subarray(r,r+i);if(c.length!==i)throw new n(\"tlv.decode: wrong value length\");return{v:c,l:e.subarray(r+i)}}},_int:{encode(t){let{Err:e}=ve;if(t<de)throw new e(\"integer: negative integers are not allowed\");let n=Ge(t);if(Number.parseInt(n[0],16)&8&&(n=\"00\"+n),n.length&1)throw new e(\"unexpected DER parsing assertion: unpadded hex\");return n},decode(t){let{Err:e}=ve;if(t[0]&128)throw new e(\"invalid signature integer: negative\");if(t[0]===0&&!(t[1]&128))throw new e(\"invalid signature integer: unnecessary leading zero\");return Ee(t)}},toSig(t){let{Err:e,_int:n,_tlv:r}=ve,o=N(t,void 0,\"signature\"),{v:s,l:i}=r.decode(48,o);if(i.length)throw new e(\"invalid signature: left bytes after parsing\");let{v:c,l:a}=r.decode(2,s),{v:f,l:h}=r.decode(2,a);if(h.length)throw new e(\"invalid signature: left bytes after parsing\");return{r:n.decode(c),s:n.decode(f)}},hexFromSig(t){let{_tlv:e,_int:n}=ve,r=e.encode(2,n.encode(t.r)),o=e.encode(2,n.encode(t.s)),s=r+o;return e.encode(48,s)}},de=BigInt(0),Ne=BigInt(1),or=BigInt(2),ut=BigInt(3),Oo=BigInt(4);function sr(t,e={}){let n=nr(\"weierstrass\",t,e),{Fp:r,Fn:o}=n,s=n.CURVE,{h:i,n:c}=s;ze(e,{},{allowInfinityPoint:\"boolean\",clearCofactor:\"function\",isTorsionFree:\"function\",fromBytes:\"function\",toBytes:\"function\",endo:\"object\"});let{endo:a}=e;if(a&&(!r.is0(s.a)||typeof a.beta!=\"bigint\"||!Array.isArray(a.basises)))throw new Error('invalid endo: expected \"beta\": bigint and \"basises\": array');let f=cr(r,o);function h(){if(!r.isOdd)throw new Error(\"compression is not supported: Field does not have .isOdd()\")}function l(R,p,b){let{x:w,y:v}=p.toAffine(),A=r.toBytes(w);if(We(b,\"isCompressed\"),b){h();let g=!r.isOdd(v);return te(ir(g),A)}else return te(Uint8Array.of(4),A,r.toBytes(v))}function x(R){N(R,void 0,\"Point\");let{publicKey:p,publicKeyUncompressed:b}=f,w=R.length,v=R[0],A=R.subarray(1);if(w===p&&(v===2||v===3)){let g=r.fromBytes(A);if(!r.isValid(g))throw new Error(\"bad point: is not on curve, wrong x\");let L=m(g),B;try{B=r.sqrt(L)}catch(M){let D=M instanceof Error?\": \"+M.message:\"\";throw new Error(\"bad point: is not on curve, sqrt error\"+D)}h();let T=r.isOdd(B);return(v&1)===1!==T&&(B=r.neg(B)),{x:g,y:B}}else if(w===b&&v===4){let g=r.BYTES,L=r.fromBytes(A.subarray(0,g)),B=r.fromBytes(A.subarray(g,g*2));if(!E(L,B))throw new Error(\"bad point: is not on curve\");return{x:L,y:B}}else throw new Error(`bad point: got length ${w}, expected compressed=${p} or uncompressed=${b}`)}let d=e.toBytes||l,y=e.fromBytes||x;function m(R){let p=r.sqr(R),b=r.mul(p,R);return r.add(r.add(b,r.mul(R,s.a)),s.b)}function E(R,p){let b=r.sqr(p),w=m(R);return r.eql(b,w)}if(!E(s.Gx,s.Gy))throw new Error(\"bad curve params: generator point\");let S=r.mul(r.pow(s.a,ut),Oo),U=r.mul(r.sqr(s.b),BigInt(27));if(r.is0(r.add(S,U)))throw new Error(\"bad curve params: a or b\");function C(R,p,b=!1){if(!r.isValid(p)||b&&r.is0(p))throw new Error(`bad point coordinate ${R}`);return p}function P(R){if(!(R instanceof k))throw new Error(\"Weierstrass Point expected\")}function F(R){if(!a||!a.basises)throw new Error(\"no endo\");return Ro(R,a.basises,o.ORDER)}let V=Nt((R,p)=>{let{X:b,Y:w,Z:v}=R;if(r.eql(v,r.ONE))return{x:b,y:w};let A=R.is0();p==null&&(p=A?r.ONE:r.inv(v));let g=r.mul(b,p),L=r.mul(w,p),B=r.mul(v,p);if(A)return{x:r.ZERO,y:r.ZERO};if(!r.eql(B,r.ONE))throw new Error(\"invZ was invalid\");return{x:g,y:L}}),Z=Nt(R=>{if(R.is0()){if(e.allowInfinityPoint&&!r.is0(R.Y))return;throw new Error(\"bad point: ZERO\")}let{x:p,y:b}=R.toAffine();if(!r.isValid(p)||!r.isValid(b))throw new Error(\"bad point: x or y not field elements\");if(!E(p,b))throw new Error(\"bad point: equation left != right\");if(!R.isTorsionFree())throw new Error(\"bad point: not in prime-order subgroup\");return!0});function j(R,p,b,w,v){return b=new k(r.mul(b.X,R),b.Y,b.Z),p=Ye(w,p),b=Ye(v,b),p.add(b)}let O=class O{constructor(p,b,w){u(this,\"X\");u(this,\"Y\");u(this,\"Z\");this.X=C(\"x\",p),this.Y=C(\"y\",b,!0),this.Z=C(\"z\",w),Object.freeze(this)}static CURVE(){return s}static fromAffine(p){let{x:b,y:w}=p||{};if(!p||!r.isValid(b)||!r.isValid(w))throw new Error(\"invalid affine point\");if(p instanceof O)throw new Error(\"projective point not allowed\");return r.is0(b)&&r.is0(w)?O.ZERO:new O(b,w,r.ONE)}static fromBytes(p){let b=O.fromAffine(y(N(p,void 0,\"point\")));return b.assertValidity(),b}static fromHex(p){return O.fromBytes(Te(p))}get x(){return this.toAffine().x}get y(){return this.toAffine().y}precompute(p=8,b=!0){return q.createCache(this,p),b||this.multiply(ut),this}assertValidity(){Z(this)}hasEvenY(){let{y:p}=this.toAffine();if(!r.isOdd)throw new Error(\"Field doesn't support isOdd\");return!r.isOdd(p)}equals(p){P(p);let{X:b,Y:w,Z:v}=this,{X:A,Y:g,Z:L}=p,B=r.eql(r.mul(b,L),r.mul(A,v)),T=r.eql(r.mul(w,L),r.mul(g,v));return B&&T}negate(){return new O(this.X,r.neg(this.Y),this.Z)}double(){let{a:p,b}=s,w=r.mul(b,ut),{X:v,Y:A,Z:g}=this,L=r.ZERO,B=r.ZERO,T=r.ZERO,_=r.mul(v,v),M=r.mul(A,A),D=r.mul(g,g),I=r.mul(v,A);return I=r.add(I,I),T=r.mul(v,g),T=r.add(T,T),L=r.mul(p,T),B=r.mul(w,D),B=r.add(L,B),L=r.sub(M,B),B=r.add(M,B),B=r.mul(L,B),L=r.mul(I,L),T=r.mul(w,T),D=r.mul(p,D),I=r.sub(_,D),I=r.mul(p,I),I=r.add(I,T),T=r.add(_,_),_=r.add(T,_),_=r.add(_,D),_=r.mul(_,I),B=r.add(B,_),D=r.mul(A,g),D=r.add(D,D),_=r.mul(D,I),L=r.sub(L,_),T=r.mul(D,M),T=r.add(T,T),T=r.add(T,T),new O(L,B,T)}add(p){P(p);let{X:b,Y:w,Z:v}=this,{X:A,Y:g,Z:L}=p,B=r.ZERO,T=r.ZERO,_=r.ZERO,M=s.a,D=r.mul(s.b,ut),I=r.mul(b,A),K=r.mul(w,g),W=r.mul(v,L),z=r.add(b,w),G=r.add(A,g);z=r.mul(z,G),G=r.add(I,K),z=r.sub(z,G),G=r.add(b,v);let Q=r.add(A,L);return G=r.mul(G,Q),Q=r.add(I,W),G=r.sub(G,Q),Q=r.add(w,v),B=r.add(g,L),Q=r.mul(Q,B),B=r.add(K,W),Q=r.sub(Q,B),_=r.mul(M,G),B=r.mul(D,W),_=r.add(B,_),B=r.sub(K,_),_=r.add(K,_),T=r.mul(B,_),K=r.add(I,I),K=r.add(K,I),W=r.mul(M,W),G=r.mul(D,G),K=r.add(K,W),W=r.sub(I,W),W=r.mul(M,W),G=r.add(G,W),I=r.mul(K,G),T=r.add(T,I),I=r.mul(Q,G),B=r.mul(z,B),B=r.sub(B,I),I=r.mul(z,K),_=r.mul(Q,_),_=r.add(_,I),new O(B,T,_)}subtract(p){return this.add(p.negate())}is0(){return this.equals(O.ZERO)}multiply(p){let{endo:b}=e;if(!o.isValidNot0(p))throw new Error(\"invalid scalar: out of range\");let w,v,A=g=>q.cached(this,g,L=>jt(O,L));if(b){let{k1neg:g,k1:L,k2neg:B,k2:T}=F(p),{p:_,f:M}=A(L),{p:D,f:I}=A(T);v=M.add(I),w=j(b.beta,_,D,g,B)}else{let{p:g,f:L}=A(p);w=g,v=L}return jt(O,[w,v])[0]}multiplyUnsafe(p){let{endo:b}=e,w=this;if(!o.isValid(p))throw new Error(\"invalid scalar: out of range\");if(p===de||w.is0())return O.ZERO;if(p===Ne)return w;if(q.hasCache(this))return this.multiply(p);if(b){let{k1neg:v,k1:A,k2neg:g,k2:L}=F(p),{p1:B,p2:T}=tr(O,w,A,L);return j(b.beta,B,T,v,g)}else return q.unsafe(w,p)}toAffine(p){return V(this,p)}isTorsionFree(){let{isTorsionFree:p}=e;return i===Ne?!0:p?p(O,this):q.unsafe(this,c).is0()}clearCofactor(){let{clearCofactor:p}=e;return i===Ne?this:p?p(O,this):this.multiplyUnsafe(i)}isSmallOrder(){return this.multiplyUnsafe(i).is0()}toBytes(p=!0){return We(p,\"isCompressed\"),this.assertValidity(),d(O,this,p)}toHex(p=!0){return Le(this.toBytes(p))}toString(){return`<Point ${this.is0()?\"ZERO\":this.toHex()}>`}};u(O,\"BASE\",new O(s.Gx,s.Gy,r.ONE)),u(O,\"ZERO\",new O(r.ZERO,r.ONE,r.ZERO)),u(O,\"Fp\",r),u(O,\"Fn\",o);let k=O,$=o.BITS,q=new ct(k,e.endo?Math.ceil($/2):$);return k.BASE.precompute(8),k}function ir(t){return Uint8Array.of(t?2:3)}function cr(t,e){return{secretKey:e.BYTES,publicKey:1+t.BYTES,publicKeyUncompressed:1+2*t.BYTES,publicKeyHasPrefix:!0,signature:2*e.BYTES}}function Uo(t,e={}){let{Fn:n}=t,r=e.randomBytes||He,o=Object.assign(cr(t.Fp,n),{seed:$t(n.ORDER)});function s(d){try{let y=n.fromBytes(d);return n.isValidNot0(y)}catch{return!1}}function i(d,y){let{publicKey:m,publicKeyUncompressed:E}=o;try{let S=d.length;return y===!0&&S!==m||y===!1&&S!==E?!1:!!t.fromBytes(d)}catch{return!1}}function c(d=r(o.seed)){return it(N(d,o.seed,\"seed\"),n.ORDER)}function a(d,y=!0){return t.BASE.multiply(n.fromBytes(d)).toBytes(y)}function f(d){let{secretKey:y,publicKey:m,publicKeyUncompressed:E}=o;if(!je(d)||\"_lengths\"in n&&n._lengths||y===m)return;let S=N(d,void 0,\"key\").length;return S===m||S===E}function h(d,y,m=!0){if(f(d)===!0)throw new Error(\"first arg must be private key\");if(f(y)===!1)throw new Error(\"second arg must be public key\");let E=n.fromBytes(d);return t.fromBytes(y).multiply(E).toBytes(m)}let l={isValidSecretKey:s,isValidPublicKey:i,randomSecretKey:c},x=at(c,a);return Object.freeze({getPublicKey:a,getSharedSecret:h,keygen:x,Point:t,utils:l,lengths:o})}function ar(t,e,n={}){Qe(e),ze(n,{},{hmac:\"function\",lowS:\"boolean\",randomBytes:\"function\",bits2int:\"function\",bits2int_modN:\"function\"}),n=Object.assign({},n);let r=n.randomBytes||He,o=n.hmac||((p,b)=>Wt(e,p,b)),{Fp:s,Fn:i}=t,{ORDER:c,BITS:a}=i,{keygen:f,getPublicKey:h,getSharedSecret:l,utils:x,lengths:d}=Uo(t,n),y={prehash:!0,lowS:typeof n.lowS==\"boolean\"?n.lowS:!0,format:\"compact\",extraEntropy:!1},m=c*or<s.ORDER;function E(p){let b=c>>Ne;return p>b}function S(p,b){if(!i.isValidNot0(b))throw new Error(`invalid signature ${p}: out of range 1..Point.Fn.ORDER`);return b}function U(){if(m)throw new Error('\"recovered\" sig type is not supported for cofactor >2 curves')}function C(p,b){Zt(b);let w=d.signature,v=b===\"compact\"?w:b===\"recovered\"?w+1:void 0;return N(p,v)}class P{constructor(b,w,v){u(this,\"r\");u(this,\"s\");u(this,\"recovery\");if(this.r=S(\"r\",b),this.s=S(\"s\",w),v!=null){if(U(),![0,1,2,3].includes(v))throw new Error(\"invalid recovery id\");this.recovery=v}Object.freeze(this)}static fromBytes(b,w=y.format){C(b,w);let v;if(w===\"der\"){let{r:B,s:T}=ve.toSig(N(b));return new P(B,T)}w===\"recovered\"&&(v=b[0],w=\"compact\",b=b.subarray(1));let A=d.signature/2,g=b.subarray(0,A),L=b.subarray(A,A*2);return new P(i.fromBytes(g),i.fromBytes(L),v)}static fromHex(b,w){return this.fromBytes(Te(b),w)}assertRecovery(){let{recovery:b}=this;if(b==null)throw new Error(\"invalid recovery id: must be present\");return b}addRecoveryBit(b){return new P(this.r,this.s,b)}recoverPublicKey(b){let{r:w,s:v}=this,A=this.assertRecovery(),g=A===2||A===3?w+c:w;if(!s.isValid(g))throw new Error(\"invalid recovery id: sig.r+curve.n != R.x\");let L=s.toBytes(g),B=t.fromBytes(te(ir((A&1)===0),L)),T=i.inv(g),_=V(N(b,void 0,\"msgHash\")),M=i.create(-_*T),D=i.create(v*T),I=t.BASE.multiplyUnsafe(M).add(B.multiplyUnsafe(D));if(I.is0())throw new Error(\"invalid recovery: point at infinify\");return I.assertValidity(),I}hasHighS(){return E(this.s)}toBytes(b=y.format){if(Zt(b),b===\"der\")return Te(ve.hexFromSig(this));let{r:w,s:v}=this,A=i.toBytes(w),g=i.toBytes(v);return b===\"recovered\"?(U(),te(Uint8Array.of(this.assertRecovery()),A,g)):te(A,g)}toHex(b){return Le(this.toBytes(b))}}let F=n.bits2int||function(b){if(b.length>8192)throw new Error(\"input is too large\");let w=Ee(b),v=b.length*8-a;return v>0?w>>BigInt(v):w},V=n.bits2int_modN||function(b){return i.create(F(b))},Z=Ze(a);function j(p){return qn(\"num < 2^\"+a,p,de,Z),i.toBytes(p)}function k(p,b){return N(p,void 0,\"message\"),b?N(e(p),void 0,\"prehashed message\"):p}function $(p,b,w){let{lowS:v,prehash:A,extraEntropy:g}=Gt(w,y);p=k(p,A);let L=V(p),B=i.fromBytes(b);if(!i.isValidNot0(B))throw new Error(\"invalid private key\");let T=[j(B),j(L)];if(g!=null&&g!==!1){let I=g===!0?r(d.secretKey):g;T.push(N(I,void 0,\"extraEntropy\"))}let _=te(...T),M=L;function D(I){let K=F(I);if(!i.isValidNot0(K))return;let W=i.inv(K),z=t.BASE.multiply(K).toAffine(),G=i.create(z.x);if(G===de)return;let Q=i.create(W*i.create(M+G*B));if(Q===de)return;let Ln=(z.x===G?0:2)|Number(z.y&Ne),Tn=Q;return v&&E(Q)&&(Tn=i.neg(Q),Ln^=1),new P(G,Tn,m?void 0:Ln)}return{seed:_,k2sig:D}}function q(p,b,w={}){let{seed:v,k2sig:A}=$(p,b,w);return Dn(e.outputLen,i.BYTES,o)(v,A).toBytes(w.format)}function O(p,b,w,v={}){let{lowS:A,prehash:g,format:L}=Gt(v,y);if(w=N(w,void 0,\"publicKey\"),b=k(b,g),!je(p)){let B=p instanceof P?\", use sig.toBytes()\":\"\";throw new Error(\"verify expects Uint8Array signature\"+B)}C(p,L);try{let B=P.fromBytes(p,L),T=t.fromBytes(w);if(A&&B.hasHighS())return!1;let{r:_,s:M}=B,D=V(b),I=i.inv(M),K=i.create(D*I),W=i.create(_*I),z=t.BASE.multiplyUnsafe(K).add(T.multiplyUnsafe(W));return z.is0()?!1:i.create(z.x)===_}catch{return!1}}function R(p,b,w={}){let{prehash:v}=Gt(w,y);return b=k(b,v),P.fromBytes(p,\"recovered\").recoverPublicKey(b).toBytes()}return Object.freeze({keygen:f,getPublicKey:h,getSharedSecret:l,utils:x,lengths:d,Point:t,sign:q,verify:O,recoverPublicKey:R,Signature:P,hash:e})}var dt={p:BigInt(\"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f\"),n:BigInt(\"0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141\"),h:BigInt(1),a:BigInt(0),b:BigInt(7),Gx:BigInt(\"0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"),Gy:BigInt(\"0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8\")},_o={beta:BigInt(\"0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee\"),basises:[[BigInt(\"0x3086d221a7d46bcde86c90e49284eb15\"),-BigInt(\"0xe4437ed6010e88286f547fa90abfe4c3\")],[BigInt(\"0x114ca50f7a8e2f3f657c1108d9d44cfd8\"),BigInt(\"0x3086d221a7d46bcde86c90e49284eb15\")]]},Io=BigInt(0),Yt=BigInt(2);function ko(t){let e=dt.p,n=BigInt(3),r=BigInt(6),o=BigInt(11),s=BigInt(22),i=BigInt(23),c=BigInt(44),a=BigInt(88),f=t*t*t%e,h=f*f*t%e,l=re(h,n,e)*h%e,x=re(l,n,e)*h%e,d=re(x,Yt,e)*f%e,y=re(d,o,e)*d%e,m=re(y,s,e)*y%e,E=re(m,c,e)*m%e,S=re(E,a,e)*E%e,U=re(S,c,e)*m%e,C=re(U,n,e)*h%e,P=re(C,i,e)*y%e,F=re(P,r,e)*f%e,V=re(F,Yt,e);if(!lt.eql(lt.sqr(V),t))throw new Error(\"Cannot find square root\");return V}var lt=ke(dt.p,{sqrt:ko}),Ue=sr(dt,{Fp:lt,endo:_o}),lr=ar(Ue,nt),fr={};function ht(t,...e){let n=fr[t];if(n===void 0){let r=nt(Pn(t));n=te(r,r),fr[t]=n}return nt(te(n,...e))}var Xt=t=>t.toBytes(!0).slice(1),Jt=t=>t%Yt===Io;function Ft(t){let{Fn:e,BASE:n}=Ue,r=e.fromBytes(t),o=n.multiply(r);return{scalar:Jt(o.y)?r:e.neg(r),bytes:Xt(o)}}function hr(t){let e=lt;if(!e.isValidNot0(t))throw new Error(\"invalid x: Fail if x \\u2265 p\");let n=e.create(t*t),r=e.create(n*t+BigInt(7)),o=e.sqrt(r);Jt(o)||(o=e.neg(o));let s=Ue.fromAffine({x:t,y:o});return s.assertValidity(),s}var Fe=Ee;function dr(...t){return Ue.Fn.create(Fe(ht(\"BIP0340/challenge\",...t)))}function ur(t){return Ft(t).bytes}function Co(t,e,n=He(32)){let{Fn:r}=Ue,o=N(t,void 0,\"message\"),{bytes:s,scalar:i}=Ft(e),c=N(n,32,\"auxRand\"),a=r.toBytes(i^Fe(ht(\"BIP0340/aux\",c))),f=ht(\"BIP0340/nonce\",a,s,o),{bytes:h,scalar:l}=Ft(f),x=dr(h,s,o),d=new Uint8Array(64);if(d.set(h,0),d.set(r.toBytes(r.create(l+x*i)),32),!br(d,o,s))throw new Error(\"sign: Invalid signature produced\");return d}function br(t,e,n){let{Fp:r,Fn:o,BASE:s}=Ue,i=N(t,64,\"signature\"),c=N(e,void 0,\"message\"),a=N(n,32,\"publicKey\");try{let f=hr(Fe(a)),h=Fe(i.subarray(0,32));if(!r.isValidNot0(h))return!1;let l=Fe(i.subarray(32,64));if(!o.isValidNot0(l))return!1;let x=dr(o.toBytes(h),Xt(f),c),d=s.multiplyUnsafe(l).add(f.multiplyUnsafe(o.neg(x))),{x:y,y:m}=d.toAffine();return!(d.is0()||!Jt(m)||y!==h)}catch{return!1}}var oe=(()=>{let n=(r=He(48))=>it(r,dt.n);return{keygen:at(n,ur),getPublicKey:ur,sign:Co,verify:br,Point:Ue,utils:{randomSecretKey:n,taggedHash:ht,lift_x:hr,pointToBytes:Xt},lengths:{secretKey:32,publicKey:32,publicKeyHasPrefix:!1,signature:64,seed:48}}})();function No(t){return t instanceof Uint8Array||ArrayBuffer.isView(t)&&t.constructor.name===\"Uint8Array\"}function bt(t,e=\"\"){if(!Number.isSafeInteger(t)||t<0){let n=e&&`\"${e}\" `;throw new Error(`${n}expected integer >= 0, got ${t}`)}}function ue(t,e,n=\"\"){let r=No(t),o=t?.length,s=e!==void 0;if(!r||s&&o!==e){let i=n&&`\"${n}\" `,c=s?` of length ${e}`:\"\",a=r?`length=${o}`:`type=${typeof t}`;throw new Error(i+\"expected Uint8Array\"+c+\", got \"+a)}return t}function Xe(t){if(typeof t!=\"function\"||typeof t.create!=\"function\")throw new Error(\"Hash must wrapped by utils.createHasher\");bt(t.outputLen),bt(t.blockLen)}function Pe(t,e=!0){if(t.destroyed)throw new Error(\"Hash instance has been destroyed\");if(e&&t.finished)throw new Error(\"Hash#digest() has already been called\")}function xr(t,e){ue(t,void 0,\"digestInto() output\");let n=e.outputLen;if(t.length<n)throw new Error('\"digestInto() output\" expected to be of length >='+n)}function pe(...t){for(let e=0;e<t.length;e++)t[e].fill(0)}function pt(t){return new DataView(t.buffer,t.byteOffset,t.byteLength)}function ce(t,e){return t<<32-e|t>>>e}var yr=typeof Uint8Array.from([]).toHex==\"function\"&&typeof Uint8Array.fromHex==\"function\",Po=Array.from({length:256},(t,e)=>e.toString(16).padStart(2,\"0\"));function ae(t){if(ue(t),yr)return t.toHex();let e=\"\";for(let n=0;n<t.length;n++)e+=Po[t[n]];return e}var be={_0:48,_9:57,A:65,F:70,a:97,f:102};function pr(t){if(t>=be._0&&t<=be._9)return t-be._0;if(t>=be.A&&t<=be.F)return t-(be.A-10);if(t>=be.a&&t<=be.f)return t-(be.a-10)}function ne(t){if(typeof t!=\"string\")throw new Error(\"hex string expected, got \"+typeof t);if(yr)return Uint8Array.fromHex(t);let e=t.length,n=e/2;if(e%2)throw new Error(\"hex string expected, got unpadded hex of length \"+e);let r=new Uint8Array(n);for(let o=0,s=0;o<n;o++,s+=2){let i=pr(t.charCodeAt(s)),c=pr(t.charCodeAt(s+1));if(i===void 0||c===void 0){let a=t[s]+t[s+1];throw new Error('hex string expected, got non-hex character \"'+a+'\" at index '+s)}r[o]=i*16+c}return r}function qe(...t){let e=0;for(let r=0;r<t.length;r++){let o=t[r];ue(o),e+=o.length}let n=new Uint8Array(e);for(let r=0,o=0;r<t.length;r++){let s=t[r];n.set(s,o),o+=s.length}return n}function gr(t,e={}){let n=(o,s)=>t(s).update(o).digest(),r=t(void 0);return n.outputLen=r.outputLen,n.blockLen=r.blockLen,n.create=o=>t(o),Object.assign(n,e),Object.freeze(n)}function wr(t=32){let e=typeof globalThis==\"object\"?globalThis.crypto:null;if(typeof e?.getRandomValues!=\"function\")throw new Error(\"crypto.getRandomValues must be defined\");return e.getRandomValues(new Uint8Array(t))}var mr=t=>({oid:Uint8Array.from([6,9,96,134,72,1,101,3,4,2,t])});function Er(t,e,n){return t&e^~t&n}function vr(t,e,n){return t&e^t&n^e&n}var xt=class{constructor(e,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=e,this.outputLen=n,this.padOffset=r,this.isLE=o,this.buffer=new Uint8Array(e),this.view=pt(this.buffer)}update(e){Pe(this),ue(e);let{view:n,buffer:r,blockLen:o}=this,s=e.length;for(let i=0;i<s;){let c=Math.min(o-this.pos,s-i);if(c===o){let a=pt(e);for(;o<=s-i;i+=o)this.process(a,i);continue}r.set(e.subarray(i,i+c),this.pos),this.pos+=c,i+=c,this.pos===o&&(this.process(n,0),this.pos=0)}return this.length+=e.length,this.roundClean(),this}digestInto(e){Pe(this),xr(e,this),this.finished=!0;let{buffer:n,view:r,blockLen:o,isLE:s}=this,{pos:i}=this;n[i++]=128,pe(this.buffer.subarray(i)),this.padOffset>o-i&&(this.process(r,0),i=0);for(let l=i;l<o;l++)n[l]=0;r.setBigUint64(o-8,BigInt(this.length*8),s),this.process(r,0);let c=pt(e),a=this.outputLen;if(a%4)throw new Error(\"_sha2: outputLen must be aligned to 32bit\");let f=a/4,h=this.get();if(f>h.length)throw new Error(\"_sha2: outputLen bigger than state\");for(let l=0;l<f;l++)c.setUint32(4*l,h[l],s)}digest(){let{buffer:e,outputLen:n}=this;this.digestInto(e);let r=e.slice(0,n);return this.destroy(),r}_cloneInto(e){e||(e=new this.constructor),e.set(...this.get());let{blockLen:n,buffer:r,length:o,finished:s,destroyed:i,pos:c}=this;return e.destroyed=i,e.finished=s,e.length=o,e.pos=c,o%n&&e.buffer.set(r),e}clone(){return this._cloneInto()}},xe=Uint32Array.from([1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225]);var qo=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]),Be=new Uint32Array(64),Qt=class extends xt{constructor(e){super(64,e,8,!1)}get(){let{A:e,B:n,C:r,D:o,E:s,F:i,G:c,H:a}=this;return[e,n,r,o,s,i,c,a]}set(e,n,r,o,s,i,c,a){this.A=e|0,this.B=n|0,this.C=r|0,this.D=o|0,this.E=s|0,this.F=i|0,this.G=c|0,this.H=a|0}process(e,n){for(let l=0;l<16;l++,n+=4)Be[l]=e.getUint32(n,!1);for(let l=16;l<64;l++){let x=Be[l-15],d=Be[l-2],y=ce(x,7)^ce(x,18)^x>>>3,m=ce(d,17)^ce(d,19)^d>>>10;Be[l]=m+Be[l-7]+y+Be[l-16]|0}let{A:r,B:o,C:s,D:i,E:c,F:a,G:f,H:h}=this;for(let l=0;l<64;l++){let x=ce(c,6)^ce(c,11)^ce(c,25),d=h+x+Er(c,a,f)+qo[l]+Be[l]|0,m=(ce(r,2)^ce(r,13)^ce(r,22))+vr(r,o,s)|0;h=f,f=a,a=c,c=i+d|0,i=s,s=o,o=r,r=d+m|0}r=r+this.A|0,o=o+this.B|0,s=s+this.C|0,i=i+this.D|0,c=c+this.E|0,a=a+this.F|0,f=f+this.G|0,h=h+this.H|0,this.set(r,o,s,i,c,a,f,h)}roundClean(){pe(Be)}destroy(){this.set(0,0,0,0,0,0,0,0),pe(this.buffer)}},en=class extends Qt{constructor(){super(32);u(this,\"A\",xe[0]|0);u(this,\"B\",xe[1]|0);u(this,\"C\",xe[2]|0);u(this,\"D\",xe[3]|0);u(this,\"E\",xe[4]|0);u(this,\"F\",xe[5]|0);u(this,\"G\",xe[6]|0);u(this,\"H\",xe[7]|0)}};var Ae=gr(()=>new en,mr(1));var Yi=new TextDecoder(\"utf-8\"),Do=new TextEncoder;function $o(t){if(t.length!==64)return!1;for(let e=0;e<64;e++){let n=t.charCodeAt(e);if(isNaN(n)||n<48||n>102||n>57&&n<97)return!1}return!0}var De=Symbol(\"verified\"),Vo=t=>t instanceof Object;function Mo(t){if(!Vo(t)||typeof t.kind!=\"number\"||typeof t.content!=\"string\"||typeof t.created_at!=\"number\"||typeof t.pubkey!=\"string\"||!$o(t.pubkey)||!Array.isArray(t.tags))return!1;for(let e=0;e<t.tags.length;e++){let n=t.tags[e];if(!Array.isArray(n))return!1;for(let r=0;r<n.length;r++)if(typeof n[r]!=\"string\")return!1}return!0}var Ko=class{generateSecretKey(){return oe.utils.randomSecretKey()}getPublicKey(t){return ae(oe.getPublicKey(t))}finalizeEvent(t,e){let n=t;return n.pubkey=ae(oe.getPublicKey(e)),n.id=tn(n),n.sig=ae(oe.sign(ne(tn(n)),e)),n[De]=!0,n}verifyEvent(t){if(typeof t[De]==\"boolean\")return t[De];try{let e=tn(t);if(e!==t.id)return t[De]=!1,!1;let n=oe.verify(ne(t.sig),ne(e),ne(t.pubkey));return t[De]=n,n}catch{return t[De]=!1,!1}}};function jo(t){if(!Mo(t))throw new Error(\"can't serialize event with wrong or missing properties\");return JSON.stringify([0,t.pubkey,t.created_at,t.kind,t.tags,t.content])}function tn(t){let e=Ae(Do.encode(jo(t)));return ae(e)}var yt=new Ko,Br=yt.generateSecretKey,Ar=yt.getPublicKey,Sr=yt.finalizeEvent,Lr=yt.verifyEvent;function Wo(t){return t instanceof Uint8Array||ArrayBuffer.isView(t)&&t.constructor.name===\"Uint8Array\"}function gt(t){if(typeof t!=\"boolean\")throw new Error(`boolean expected, not ${t}`)}function wt(t){if(!Number.isSafeInteger(t)||t<0)throw new Error(\"positive integer expected, got \"+t)}function Y(t,e,n=\"\"){let r=Wo(t),o=t?.length,s=e!==void 0;if(!r||s&&o!==e){let i=n&&`\"${n}\" `,c=s?` of length ${e}`:\"\",a=r?`length=${o}`:`type=${typeof t}`;throw new Error(i+\"expected Uint8Array\"+c+\", got \"+a)}return t}function nn(t,e=!0){if(t.destroyed)throw new Error(\"Hash instance has been destroyed\");if(e&&t.finished)throw new Error(\"Hash#digest() has already been called\")}function Tr(t,e){Y(t,void 0,\"output\");let n=e.outputLen;if(t.length<n)throw new Error(\"digestInto() expects output buffer of length at least \"+n)}function ye(t){return new Uint32Array(t.buffer,t.byteOffset,Math.floor(t.byteLength/4))}function ge(...t){for(let e=0;e<t.length;e++)t[e].fill(0)}function Go(t){return new DataView(t.buffer,t.byteOffset,t.byteLength)}var Zo=new Uint8Array(new Uint32Array([287454020]).buffer)[0]===68;function Hr(t,e){if(e==null||typeof e!=\"object\")throw new Error(\"options must be defined\");return Object.assign(t,e)}function mt(t,e){if(t.length!==e.length)return!1;let n=0;for(let r=0;r<t.length;r++)n|=t[r]^e[r];return n===0}var rn=(t,e)=>{function n(r,...o){if(Y(r,void 0,\"key\"),!Zo)throw new Error(\"Non little-endian hardware is not yet supported\");if(t.nonceLength!==void 0){let h=o[0];Y(h,t.varSizeNonce?void 0:t.nonceLength,\"nonce\")}let s=t.tagLength;s&&o[1]!==void 0&&Y(o[1],void 0,\"AAD\");let i=e(r,...o),c=(h,l)=>{if(l!==void 0){if(h!==2)throw new Error(\"cipher output not supported\");Y(l,void 0,\"output\")}},a=!1;return{encrypt(h,l){if(a)throw new Error(\"cannot encrypt() twice with same key + nonce\");return a=!0,Y(h),c(i.encrypt.length,l),i.encrypt(h,l)},decrypt(h,l){if(Y(h),s&&h.length<s)throw new Error('\"ciphertext\" expected length bigger than tagLength='+s);return c(i.decrypt.length,l),i.decrypt(h,l)}}}return Object.assign(n,t),n};function on(t,e,n=!0){if(e===void 0)return new Uint8Array(t);if(e.length!==t)throw new Error('\"output\" expected Uint8Array of length '+t+\", got: \"+e.length);if(n&&!zo(e))throw new Error(\"invalid output, must be aligned\");return e}function Rr(t,e,n){gt(n);let r=new Uint8Array(16),o=Go(r);return o.setBigUint64(0,BigInt(e),n),o.setBigUint64(8,BigInt(t),n),r}function zo(t){return t.byteOffset%4===0}function $e(t){return Uint8Array.from(t)}var Ur=t=>Uint8Array.from(t.split(\"\"),e=>e.charCodeAt(0)),Yo=Ur(\"expand 16-byte k\"),Fo=Ur(\"expand 32-byte k\"),Xo=ye(Yo),Jo=ye(Fo);function H(t,e){return t<<e|t>>>32-e}function sn(t){return t.byteOffset%4===0}var Et=64,Qo=16,_r=2**32-1,Or=Uint32Array.of();function es(t,e,n,r,o,s,i,c){let a=o.length,f=new Uint8Array(Et),h=ye(f),l=sn(o)&&sn(s),x=l?ye(o):Or,d=l?ye(s):Or;for(let y=0;y<a;i++){if(t(e,n,r,h,i,c),i>=_r)throw new Error(\"arx: counter overflow\");let m=Math.min(Et,a-y);if(l&&m===Et){let E=y/4;if(y%4!==0)throw new Error(\"arx: invalid block position\");for(let S=0,U;S<Qo;S++)U=E+S,d[U]=x[U]^h[S];y+=Et;continue}for(let E=0,S;E<m;E++)S=y+E,s[S]=o[S]^f[E];y+=m}}function cn(t,e){let{allowShortKeys:n,extendNonceFn:r,counterLength:o,counterRight:s,rounds:i}=Hr({allowShortKeys:!1,counterLength:8,counterRight:!1,rounds:20},e);if(typeof t!=\"function\")throw new Error(\"core must be a function\");return wt(o),wt(i),gt(s),gt(n),(c,a,f,h,l=0)=>{Y(c,void 0,\"key\"),Y(a,void 0,\"nonce\"),Y(f,void 0,\"data\");let x=f.length;if(h===void 0&&(h=new Uint8Array(x)),Y(h,void 0,\"output\"),wt(l),l<0||l>=_r)throw new Error(\"arx: counter overflow\");if(h.length<x)throw new Error(`arx: output (${h.length}) is shorter than data (${x})`);let d=[],y=c.length,m,E;if(y===32)d.push(m=$e(c)),E=Jo;else if(y===16&&n)m=new Uint8Array(32),m.set(c),m.set(c,16),E=Xo,d.push(m);else throw Y(c,32,\"arx key\"),new Error(\"invalid key size\");sn(a)||d.push(a=$e(a));let S=ye(m);if(r){if(a.length!==24)throw new Error(\"arx: extended nonce must be 24 bytes\");r(E,S,ye(a.subarray(0,16)),S),a=a.subarray(16)}let U=16-o;if(U!==a.length)throw new Error(`arx: nonce must be ${U} or 16 bytes`);if(U!==12){let P=new Uint8Array(12);P.set(a,s?0:12-a.length),a=P,d.push(a)}let C=ye(a);return es(t,E,S,C,f,h,l,i),ge(...d),h}}function X(t,e){return t[e++]&255|(t[e++]&255)<<8}var an=class{constructor(e){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);e=$e(Y(e,32,\"key\"));let n=X(e,0),r=X(e,2),o=X(e,4),s=X(e,6),i=X(e,8),c=X(e,10),a=X(e,12),f=X(e,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|c<<2)&8191,this.r[7]=(c>>>11|a<<5)&8065,this.r[8]=(a>>>8|f<<8)&8191,this.r[9]=f>>>5&127;for(let h=0;h<8;h++)this.pad[h]=X(e,16+2*h)}process(e,n,r=!1){let o=r?0:2048,{h:s,r:i}=this,c=i[0],a=i[1],f=i[2],h=i[3],l=i[4],x=i[5],d=i[6],y=i[7],m=i[8],E=i[9],S=X(e,n+0),U=X(e,n+2),C=X(e,n+4),P=X(e,n+6),F=X(e,n+8),V=X(e,n+10),Z=X(e,n+12),j=X(e,n+14),k=s[0]+(S&8191),$=s[1]+((S>>>13|U<<3)&8191),q=s[2]+((U>>>10|C<<6)&8191),O=s[3]+((C>>>7|P<<9)&8191),R=s[4]+((P>>>4|F<<12)&8191),p=s[5]+(F>>>1&8191),b=s[6]+((F>>>14|V<<2)&8191),w=s[7]+((V>>>11|Z<<5)&8191),v=s[8]+((Z>>>8|j<<8)&8191),A=s[9]+(j>>>5|o),g=0,L=g+k*c+$*(5*E)+q*(5*m)+O*(5*y)+R*(5*d);g=L>>>13,L&=8191,L+=p*(5*x)+b*(5*l)+w*(5*h)+v*(5*f)+A*(5*a),g+=L>>>13,L&=8191;let B=g+k*a+$*c+q*(5*E)+O*(5*m)+R*(5*y);g=B>>>13,B&=8191,B+=p*(5*d)+b*(5*x)+w*(5*l)+v*(5*h)+A*(5*f),g+=B>>>13,B&=8191;let T=g+k*f+$*a+q*c+O*(5*E)+R*(5*m);g=T>>>13,T&=8191,T+=p*(5*y)+b*(5*d)+w*(5*x)+v*(5*l)+A*(5*h),g+=T>>>13,T&=8191;let _=g+k*h+$*f+q*a+O*c+R*(5*E);g=_>>>13,_&=8191,_+=p*(5*m)+b*(5*y)+w*(5*d)+v*(5*x)+A*(5*l),g+=_>>>13,_&=8191;let M=g+k*l+$*h+q*f+O*a+R*c;g=M>>>13,M&=8191,M+=p*(5*E)+b*(5*m)+w*(5*y)+v*(5*d)+A*(5*x),g+=M>>>13,M&=8191;let D=g+k*x+$*l+q*h+O*f+R*a;g=D>>>13,D&=8191,D+=p*c+b*(5*E)+w*(5*m)+v*(5*y)+A*(5*d),g+=D>>>13,D&=8191;let I=g+k*d+$*x+q*l+O*h+R*f;g=I>>>13,I&=8191,I+=p*a+b*c+w*(5*E)+v*(5*m)+A*(5*y),g+=I>>>13,I&=8191;let K=g+k*y+$*d+q*x+O*l+R*h;g=K>>>13,K&=8191,K+=p*f+b*a+w*c+v*(5*E)+A*(5*m),g+=K>>>13,K&=8191;let W=g+k*m+$*y+q*d+O*x+R*l;g=W>>>13,W&=8191,W+=p*h+b*f+w*a+v*c+A*(5*E),g+=W>>>13,W&=8191;let z=g+k*E+$*m+q*y+O*d+R*x;g=z>>>13,z&=8191,z+=p*l+b*h+w*f+v*a+A*c,g+=z>>>13,z&=8191,g=(g<<2)+g|0,g=g+L|0,L=g&8191,g=g>>>13,B+=g,s[0]=L,s[1]=B,s[2]=T,s[3]=_,s[4]=M,s[5]=D,s[6]=I,s[7]=K,s[8]=W,s[9]=z}finalize(){let{h:e,pad:n}=this,r=new Uint16Array(10),o=e[1]>>>13;e[1]&=8191;for(let c=2;c<10;c++)e[c]+=o,o=e[c]>>>13,e[c]&=8191;e[0]+=o*5,o=e[0]>>>13,e[0]&=8191,e[1]+=o,o=e[1]>>>13,e[1]&=8191,e[2]+=o,r[0]=e[0]+5,o=r[0]>>>13,r[0]&=8191;for(let c=1;c<10;c++)r[c]=e[c]+o,o=r[c]>>>13,r[c]&=8191;r[9]-=8192;let s=(o^1)-1;for(let c=0;c<10;c++)r[c]&=s;s=~s;for(let c=0;c<10;c++)e[c]=e[c]&s|r[c];e[0]=(e[0]|e[1]<<13)&65535,e[1]=(e[1]>>>3|e[2]<<10)&65535,e[2]=(e[2]>>>6|e[3]<<7)&65535,e[3]=(e[3]>>>9|e[4]<<4)&65535,e[4]=(e[4]>>>12|e[5]<<1|e[6]<<14)&65535,e[5]=(e[6]>>>2|e[7]<<11)&65535,e[6]=(e[7]>>>5|e[8]<<8)&65535,e[7]=(e[8]>>>8|e[9]<<5)&65535;let i=e[0]+n[0];e[0]=i&65535;for(let c=1;c<8;c++)i=(e[c]+n[c]|0)+(i>>>16)|0,e[c]=i&65535;ge(r)}update(e){nn(this),Y(e),e=$e(e);let{buffer:n,blockLen:r}=this,o=e.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(e,s);continue}n.set(e.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(){ge(this.h,this.r,this.buffer,this.pad)}digestInto(e){nn(this),Tr(e,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++)e[s++]=r[i]>>>0,e[s++]=r[i]>>>8;return e}digest(){let{buffer:e,outputLen:n}=this;this.digestInto(e);let r=e.slice(0,n);return this.destroy(),r}};function ts(t){let e=(r,o)=>t(o).update(r).digest(),n=t(new Uint8Array(32));return e.outputLen=n.outputLen,e.blockLen=n.blockLen,e.create=r=>t(r),e}var Ir=ts(t=>new an(t));function Nr(t,e,n,r,o,s=20){let i=t[0],c=t[1],a=t[2],f=t[3],h=e[0],l=e[1],x=e[2],d=e[3],y=e[4],m=e[5],E=e[6],S=e[7],U=o,C=n[0],P=n[1],F=n[2],V=i,Z=c,j=a,k=f,$=h,q=l,O=x,R=d,p=y,b=m,w=E,v=S,A=U,g=C,L=P,B=F;for(let _=0;_<s;_+=2)V=V+$|0,A=H(A^V,16),p=p+A|0,$=H($^p,12),V=V+$|0,A=H(A^V,8),p=p+A|0,$=H($^p,7),Z=Z+q|0,g=H(g^Z,16),b=b+g|0,q=H(q^b,12),Z=Z+q|0,g=H(g^Z,8),b=b+g|0,q=H(q^b,7),j=j+O|0,L=H(L^j,16),w=w+L|0,O=H(O^w,12),j=j+O|0,L=H(L^j,8),w=w+L|0,O=H(O^w,7),k=k+R|0,B=H(B^k,16),v=v+B|0,R=H(R^v,12),k=k+R|0,B=H(B^k,8),v=v+B|0,R=H(R^v,7),V=V+q|0,B=H(B^V,16),w=w+B|0,q=H(q^w,12),V=V+q|0,B=H(B^V,8),w=w+B|0,q=H(q^w,7),Z=Z+O|0,A=H(A^Z,16),v=v+A|0,O=H(O^v,12),Z=Z+O|0,A=H(A^Z,8),v=v+A|0,O=H(O^v,7),j=j+R|0,g=H(g^j,16),p=p+g|0,R=H(R^p,12),j=j+R|0,g=H(g^j,8),p=p+g|0,R=H(R^p,7),k=k+$|0,L=H(L^k,16),b=b+L|0,$=H($^b,12),k=k+$|0,L=H(L^k,8),b=b+L|0,$=H($^b,7);let T=0;r[T++]=i+V|0,r[T++]=c+Z|0,r[T++]=a+j|0,r[T++]=f+k|0,r[T++]=h+$|0,r[T++]=l+q|0,r[T++]=x+O|0,r[T++]=d+R|0,r[T++]=y+p|0,r[T++]=m+b|0,r[T++]=E+w|0,r[T++]=S+v|0,r[T++]=U+A|0,r[T++]=C+g|0,r[T++]=P+L|0,r[T++]=F+B|0}function ns(t,e,n,r){let o=t[0],s=t[1],i=t[2],c=t[3],a=e[0],f=e[1],h=e[2],l=e[3],x=e[4],d=e[5],y=e[6],m=e[7],E=n[0],S=n[1],U=n[2],C=n[3];for(let F=0;F<20;F+=2)o=o+a|0,E=H(E^o,16),x=x+E|0,a=H(a^x,12),o=o+a|0,E=H(E^o,8),x=x+E|0,a=H(a^x,7),s=s+f|0,S=H(S^s,16),d=d+S|0,f=H(f^d,12),s=s+f|0,S=H(S^s,8),d=d+S|0,f=H(f^d,7),i=i+h|0,U=H(U^i,16),y=y+U|0,h=H(h^y,12),i=i+h|0,U=H(U^i,8),y=y+U|0,h=H(h^y,7),c=c+l|0,C=H(C^c,16),m=m+C|0,l=H(l^m,12),c=c+l|0,C=H(C^c,8),m=m+C|0,l=H(l^m,7),o=o+f|0,C=H(C^o,16),y=y+C|0,f=H(f^y,12),o=o+f|0,C=H(C^o,8),y=y+C|0,f=H(f^y,7),s=s+h|0,E=H(E^s,16),m=m+E|0,h=H(h^m,12),s=s+h|0,E=H(E^s,8),m=m+E|0,h=H(h^m,7),i=i+l|0,S=H(S^i,16),x=x+S|0,l=H(l^x,12),i=i+l|0,S=H(S^i,8),x=x+S|0,l=H(l^x,7),c=c+a|0,U=H(U^c,16),d=d+U|0,a=H(a^d,12),c=c+a|0,U=H(U^c,8),d=d+U|0,a=H(a^d,7);let P=0;r[P++]=o,r[P++]=s,r[P++]=i,r[P++]=c,r[P++]=E,r[P++]=S,r[P++]=U,r[P++]=C}var vt=cn(Nr,{counterRight:!1,counterLength:4,allowShortKeys:!1}),rs=cn(Nr,{counterRight:!1,counterLength:8,extendNonceFn:ns,allowShortKeys:!1});var os=new Uint8Array(16),kr=(t,e)=>{t.update(e);let n=e.length%16;n&&t.update(os.subarray(n))},ss=new Uint8Array(32);function Cr(t,e,n,r,o){o!==void 0&&Y(o,void 0,\"AAD\");let s=t(e,n,ss),i=Rr(r.length,o?o.length:0,!0),c=Ir.create(s);o&&kr(c,o),kr(c,r),c.update(i);let a=c.digest();return ge(s,i),a}var Pr=t=>(e,n,r)=>({encrypt(s,i){let c=s.length;i=on(c+16,i,!1),i.set(s);let a=i.subarray(0,-16);t(e,n,a,a,1);let f=Cr(t,e,n,a,r);return i.set(f,c),ge(f),i},decrypt(s,i){i=on(s.length-16,i,!1);let c=s.subarray(0,-16),a=s.subarray(-16),f=Cr(t,e,n,c,r);if(!mt(a,f))throw new Error(\"invalid tag\");return i.set(s.subarray(0,-16)),t(e,n,i,i,1),ge(f),i}}),pc=rn({blockSize:64,nonceLength:12,tagLength:16},Pr(vt)),xc=rn({blockSize:64,nonceLength:24,tagLength:16},Pr(rs));var Bt=class{constructor(e,n){u(this,\"oHash\");u(this,\"iHash\");u(this,\"blockLen\");u(this,\"outputLen\");u(this,\"finished\",!1);u(this,\"destroyed\",!1);if(Xe(e),ue(n,void 0,\"key\"),this.iHash=e.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?e.create().update(n).digest():n);for(let s=0;s<o.length;s++)o[s]^=54;this.iHash.update(o),this.oHash=e.create();for(let s=0;s<o.length;s++)o[s]^=106;this.oHash.update(o),pe(o)}update(e){return Pe(this),this.iHash.update(e),this}digestInto(e){Pe(this),ue(e,this.outputLen,\"output\"),this.finished=!0,this.iHash.digestInto(e),this.oHash.update(e),this.oHash.digestInto(e),this.destroy()}digest(){let e=new Uint8Array(this.oHash.outputLen);return this.digestInto(e),e}_cloneInto(e){e||(e=Object.create(Object.getPrototypeOf(this),{}));let{oHash:n,iHash:r,finished:o,destroyed:s,blockLen:i,outputLen:c}=this;return e=e,e.finished=o,e.destroyed=s,e.blockLen=i,e.outputLen=c,e.oHash=n._cloneInto(e.oHash),e.iHash=r._cloneInto(e.iHash),e}clone(){return this._cloneInto()}destroy(){this.destroyed=!0,this.oHash.destroy(),this.iHash.destroy()}},Ve=(t,e,n)=>new Bt(t,e).update(n).digest();Ve.create=(t,e)=>new Bt(t,e);function Dr(t,e,n){return Xe(t),n===void 0&&(n=new Uint8Array(t.outputLen)),Ve(t,n,e)}var fn=Uint8Array.of(0),qr=Uint8Array.of();function $r(t,e,n,r=32){Xe(t),bt(r,\"length\");let o=t.outputLen;if(r>255*o)throw new Error(\"Length must be <= 255*HashLen\");let s=Math.ceil(r/o);n===void 0?n=qr:ue(n,void 0,\"info\");let i=new Uint8Array(s*o),c=Ve.create(t,e),a=c._cloneInto(),f=new Uint8Array(c.outputLen);for(let h=0;h<s;h++)fn[0]=h+1,a.update(h===0?qr:f).update(n).update(fn).digestInto(f),i.set(f,o*h),c._cloneInto(a);return c.destroy(),a.destroy(),pe(f,fn),i.slice(0,r)}function dn(t){return t instanceof Uint8Array||ArrayBuffer.isView(t)&&t.constructor.name===\"Uint8Array\"}function is(t){if(!dn(t))throw new Error(\"Uint8Array expected\")}function jr(t,e){return Array.isArray(e)?e.length===0?!0:t?e.every(n=>typeof n==\"string\"):e.every(n=>Number.isSafeInteger(n)):!1}function cs(t){if(typeof t!=\"function\")throw new Error(\"function expected\");return!0}function _e(t,e){if(typeof e!=\"string\")throw new Error(`${t}: string expected`);return!0}function bn(t){if(!Number.isSafeInteger(t))throw new Error(`invalid integer: ${t}`)}function un(t){if(!Array.isArray(t))throw new Error(\"array expected\")}function St(t,e){if(!jr(!0,e))throw new Error(`${t}: array of strings expected`)}function Wr(t,e){if(!jr(!1,e))throw new Error(`${t}: array of numbers expected`)}function Gr(...t){let e=s=>s,n=(s,i)=>c=>s(i(c)),r=t.map(s=>s.encode).reduceRight(n,e),o=t.map(s=>s.decode).reduce(n,e);return{encode:r,decode:o}}function Zr(t){let e=typeof t==\"string\"?t.split(\"\"):t,n=e.length;St(\"alphabet\",e);let r=new Map(e.map((o,s)=>[o,s]));return{encode:o=>(un(o),o.map(s=>{if(!Number.isSafeInteger(s)||s<0||s>=n)throw new Error(`alphabet.encode: digit index outside alphabet \"${s}\". Allowed: ${t}`);return e[s]})),decode:o=>(un(o),o.map(s=>{_e(\"alphabet.decode\",s);let i=r.get(s);if(i===void 0)throw new Error(`Unknown letter: \"${s}\". Allowed: ${t}`);return i}))}}function zr(t=\"\"){return _e(\"join\",t),{encode:e=>(St(\"join.decode\",e),e.join(t)),decode:e=>(_e(\"join.decode\",e),e.split(t))}}function as(t,e=\"=\"){return bn(t),_e(\"padding\",e),{encode(n){for(St(\"padding.encode\",n);n.length*t%8;)n.push(e);return n},decode(n){St(\"padding.decode\",n);let r=n.length;if(r*t%8)throw new Error(\"padding: invalid, string should have whole number of bytes\");for(;r>0&&n[r-1]===e;r--)if((r-1)*t%8===0)throw new Error(\"padding: invalid, string has too much padding\");return n.slice(0,r)}}}var Yr=(t,e)=>e===0?t:Yr(e,t%e),Lt=(t,e)=>t+(e-Yr(t,e)),At=(()=>{let t=[];for(let e=0;e<40;e++)t.push(2**e);return t})();function ln(t,e,n,r){if(un(t),e<=0||e>32)throw new Error(`convertRadix2: wrong from=${e}`);if(n<=0||n>32)throw new Error(`convertRadix2: wrong to=${n}`);if(Lt(e,n)>32)throw new Error(`convertRadix2: carry overflow from=${e} to=${n} carryBits=${Lt(e,n)}`);let o=0,s=0,i=At[e],c=At[n]-1,a=[];for(let f of t){if(bn(f),f>=i)throw new Error(`convertRadix2: invalid data word=${f} from=${e}`);if(o=o<<e|f,s+e>32)throw new Error(`convertRadix2: carry overflow pos=${s} from=${e}`);for(s+=e;s>=n;s-=n)a.push((o>>s-n&c)>>>0);let h=At[s];if(h===void 0)throw new Error(\"invalid carry\");o&=h-1}if(o=o<<n-s&c,!r&&s>=e)throw new Error(\"Excess padding\");if(!r&&o>0)throw new Error(`Non-zero padding: ${o}`);return r&&s>0&&a.push(o>>>0),a}function Fr(t,e=!1){if(bn(t),t<=0||t>32)throw new Error(\"radix2: bits should be in (0..32]\");if(Lt(8,t)>32||Lt(t,8)>32)throw new Error(\"radix2: carry overflow\");return{encode:n=>{if(!dn(n))throw new Error(\"radix2.encode input should be Uint8Array\");return ln(Array.from(n),8,t,!e)},decode:n=>(Wr(\"radix2.decode\",n),Uint8Array.from(ln(n,t,8,e)))}}function Vr(t){return cs(t),function(...e){try{return t.apply(null,e)}catch{}}}var fs=typeof Uint8Array.from([]).toBase64==\"function\"&&typeof Uint8Array.fromBase64==\"function\",us=(t,e)=>{_e(\"base64\",t);let n=e?/^[A-Za-z0-9=_-]+$/:/^[A-Za-z0-9=+/]+$/,r=e?\"base64url\":\"base64\";if(t.length>0&&!n.test(t))throw new Error(\"invalid base64\");return Uint8Array.fromBase64(t,{alphabet:r,lastChunkHandling:\"strict\"})},pn=fs?{encode(t){return is(t),t.toBase64()},decode(t){return us(t,!1)}}:Gr(Fr(6),Zr(\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"),as(6),zr(\"\"));var hn=Gr(Zr(\"qpzry9x8gf2tvdw0s3jn54khce6mua7l\"),zr(\"\")),Mr=[996825010,642813549,513874426,1027748829,705979059];function Je(t){let e=t>>25,n=(t&33554431)<<5;for(let r=0;r<Mr.length;r++)(e>>r&1)===1&&(n^=Mr[r]);return n}function Kr(t,e,n=1){let r=t.length,o=1;for(let s=0;s<r;s++){let i=t.charCodeAt(s);if(i<33||i>126)throw new Error(`Invalid prefix (${t})`);o=Je(o)^i>>5}o=Je(o);for(let s=0;s<r;s++)o=Je(o)^t.charCodeAt(s)&31;for(let s of e)o=Je(o)^s;for(let s=0;s<6;s++)o=Je(o);return o^=n,hn.encode(ln([o%At[30]],30,5,!1))}function ls(t){let e=t===\"bech32\"?1:734539939,n=Fr(5),r=n.decode,o=n.encode,s=Vr(r);function i(l,x,d=90){_e(\"bech32.encode prefix\",l),dn(x)&&(x=Array.from(x)),Wr(\"bech32.encode\",x);let y=l.length;if(y===0)throw new TypeError(`Invalid prefix length ${y}`);let m=y+7+x.length;if(d!==!1&&m>d)throw new TypeError(`Length ${m} exceeds limit ${d}`);let E=l.toLowerCase(),S=Kr(E,x,e);return`${E}1${hn.encode(x)}${S}`}function c(l,x=90){_e(\"bech32.decode input\",l);let d=l.length;if(d<8||x!==!1&&d>x)throw new TypeError(`invalid string length: ${d} (${l}). Expected (8..${x})`);let y=l.toLowerCase();if(l!==y&&l!==l.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 E=y.slice(0,m),S=y.slice(m+1);if(S.length<6)throw new Error(\"Data must be at least 6 characters long\");let U=hn.decode(S).slice(0,-6),C=Kr(E,U,e);if(!S.endsWith(C))throw new Error(`Invalid checksum in ${l}: expected \"${C}\"`);return{prefix:E,words:U}}let a=Vr(c);function f(l){let{prefix:x,words:d}=c(l,!1);return{prefix:x,words:d,bytes:r(d)}}function h(l,x){return i(l,o(x))}return{encode:i,decode:c,encodeFromBytes:h,decodeToBytes:f,decodeUnsafe:a,fromWords:r,fromWordsUnsafe:s,toWords:o}}var xn=ls(\"bech32\");var hs=new TextDecoder(\"utf-8\"),En=new TextEncoder;function Ke(t){try{t.indexOf(\"://\")===-1&&(t=\"wss://\"+t);let e=new URL(t);return e.protocol===\"http:\"?e.protocol=\"ws:\":e.protocol===\"https:\"&&(e.protocol=\"wss:\"),e.pathname=e.pathname.replace(/\\/+/g,\"/\"),e.pathname.endsWith(\"/\")&&(e.pathname=e.pathname.slice(0,-1)),(e.port===\"80\"&&e.protocol===\"ws:\"||e.port===\"443\"&&e.protocol===\"wss:\")&&(e.port=\"\"),e.searchParams.sort(),e.hash=\"\",e.toString()}catch{throw new Error(`Invalid URL: ${t}`)}}function ds(t){if(t.length!==64)return!1;for(let e=0;e<64;e++){let n=t.charCodeAt(e);if(isNaN(n)||n<48||n>102||n>57&&n<97)return!1}return!0}var Me=Symbol(\"verified\"),bs=t=>t instanceof Object;function ps(t){if(!bs(t)||typeof t.kind!=\"number\"||typeof t.content!=\"string\"||typeof t.created_at!=\"number\"||typeof t.pubkey!=\"string\"||!ds(t.pubkey)||!Array.isArray(t.tags))return!1;for(let e=0;e<t.tags.length;e++){let n=t.tags[e];if(!Array.isArray(n))return!1;for(let r=0;r<n.length;r++)if(typeof n[r]!=\"string\")return!1}return!0}var xs=class{generateSecretKey(){return oe.utils.randomSecretKey()}getPublicKey(t){return ae(oe.getPublicKey(t))}finalizeEvent(t,e){let n=t;return n.pubkey=ae(oe.getPublicKey(e)),n.id=yn(n),n.sig=ae(oe.sign(ne(yn(n)),e)),n[Me]=!0,n}verifyEvent(t){if(typeof t[Me]==\"boolean\")return t[Me];try{let e=yn(t);if(e!==t.id)return t[Me]=!1,!1;let n=oe.verify(ne(t.sig),ne(e),ne(t.pubkey));return t[Me]=n,n}catch{return t[Me]=!1,!1}}};function ys(t){if(!ps(t))throw new Error(\"can't serialize event with wrong or missing properties\");return JSON.stringify([0,t.pubkey,t.created_at,t.kind,t.tags,t.content])}function yn(t){let e=Ae(En.encode(ys(t)));return ae(e)}var Ht=new xs,Hc=Ht.generateSecretKey,Xr=Ht.getPublicKey,gs=Ht.finalizeEvent,Qr=Ht.verifyEvent,vn=1,Bn=4294967295,An=65536;function gn(t,e){let n=lr.getSharedSecret(t,ne(\"02\"+e)).subarray(1,33);return Dr(Ae,n,En.encode(\"nip44-v2\"))}function eo(t,e){let n=$r(Ae,t,e,76);return{chacha_key:n.subarray(0,32),chacha_nonce:n.subarray(32,44),hmac_key:n.subarray(44,76)}}function to(t){if(!Number.isSafeInteger(t)||t<1)throw new Error(\"expected positive integer\");if(t<=32)return 32;let e=2**(Math.floor(Math.log2(t-1))+1),n=e<=256?32:e/8;return n*(Math.floor((t-1)/n)+1)}function ws(t){if(!Number.isSafeInteger(t)||t<vn||t>65535)throw new Error(\"invalid plaintext size: must be between 1 and 65535 bytes\");let e=new Uint8Array(2);return new DataView(e.buffer).setUint16(0,t,!1),e}function ms(t){if(!Number.isSafeInteger(t)||t<An||t>Bn)throw new Error(\"invalid plaintext size: must be between 65536 and 4294967295 bytes\");let e=new Uint8Array(4);return new DataView(e.buffer).setUint32(0,t,!1),e}function Es(t){let e=En.encode(t),n=e.length;if(n<vn||n>Bn)throw new Error(\"invalid plaintext size: must be between 1 and 4294967295 bytes\");let r=n>=An?qe(new Uint8Array([0,0]),ms(n)):ws(n),o=new Uint8Array(to(n)-n);return qe(r,e,o)}function vs(t){let e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=e.getUint16(0),r,o;if(n===0){if(r=e.getUint32(2),r<An)throw new Error(\"invalid padding\");o=6}else r=n,o=2;let s=t.subarray(o,o+r);if(r<vn||r>Bn||s.length!==r||t.length!==o+to(r))throw new Error(\"invalid padding\");return hs.decode(s)}function no(t,e,n){if(n.length!==32)throw new Error(\"AAD associated data must be 32 bytes\");let r=qe(n,e);return Ve(Ae,t,r)}function Bs(t){if(typeof t!=\"string\")throw new Error(\"payload must be a valid string\");let e=t.length;if(e<132)throw new Error(\"invalid payload length: \"+e);if(t[0]===\"#\")throw new Error(\"unknown encryption version\");let n;try{n=pn.decode(t)}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 As(t,e,n=wr(32)){let{chacha_key:r,chacha_nonce:o,hmac_key:s}=eo(e,n),i=Es(t),c=vt(r,o,i),a=no(s,c,n);return pn.encode(qe(new Uint8Array([2]),n,c,a))}function Jr(t,e){let{nonce:n,ciphertext:r,mac:o}=Bs(t),{chacha_key:s,chacha_nonce:i,hmac_key:c}=eo(e,n),a=no(c,r,n);if(!mt(a,o))throw new Error(\"invalid MAC\");let f=vt(s,i,r);return vs(f)}var Ss=/^(?:([\\w.+-]+)@)?([\\w_-]+(\\.[\\w_-]+)+)$/,Ls;try{Ls=fetch}catch{}var Ts=22242,wn=24133;function Hs(t,e){if(t.ids&&t.ids.indexOf(e.id)===-1||t.kinds&&t.kinds.indexOf(e.kind)===-1||t.authors&&t.authors.indexOf(e.pubkey)===-1)return!1;for(let n in t)if(n[0]===\"#\"){let r=n.slice(1),o=t[`#${r}`];if(o&&!e.tags.find(([s,i])=>s===n.slice(1)&&o.indexOf(i)!==-1))return!1}return!(t.since&&e.created_at<t.since||t.until&&e.created_at>t.until)}function Rs(t,e){for(let n=0;n<t.length;n++)if(Hs(t[n],e))return!0;return!1}function Os(t,e){let n=e.length+3,r=t.indexOf(`\"${e}\":`)+n,o=t.slice(r).indexOf('\"')+r+1;return t.slice(o,o+64)}function Us(t){let e=t.slice(0,22).indexOf('\"EVENT\"');if(e===-1)return null;let n=t.slice(e+7+1).indexOf('\"');if(n===-1)return null;let r=e+7+1+n,o=t.slice(r+1,80).indexOf('\"');if(o===-1)return null;let s=r+1+o;return t.slice(r+1,s)}function _s(t,e){return{kind:Ts,created_at:Math.floor(Date.now()/1e3),tags:[[\"relay\",t],[\"challenge\",e]],content:\"\"}}var mn=class extends Error{constructor(t,e){super(`Tried to send message '${t} on a closed connection to ${e}.`),this.name=\"SendingOnClosedConnection\"}},ro=class{constructor(t,e){u(this,\"url\");u(this,\"_connected\",!1);u(this,\"onclose\",null);u(this,\"onnotice\",t=>console.debug(`NOTICE from ${this.url}: ${t}`));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=Ke(t),this.verifyEvent=e.verifyEvent,this._WebSocket=e.websocketImplementation||WebSocket,this.enablePing=e.enablePing,this.enableReconnect=e.enableReconnect||!1,e.idleTimeout&&(this.idleTimeout=e.idleTimeout)}static async connect(t,e){let n=new ro(t,e);return await n.connect(e),n}closeAllSubscriptions(t){for(let[e,n]of this.openSubs)n.close(t);this.openSubs.clear();for(let[e,n]of this.openEventPublishes)n.reject(new Error(t));this.openEventPublishes.clear();for(let[e,n]of this.openCountRequests)n.reject(new Error(t));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 t=this.resubscribeBackoff[Math.min(this.reconnectAttempts,this.resubscribeBackoff.length-1)];this.reconnectAttempts++,this.reconnectTimeoutHandle=setTimeout(async()=>{try{await this.connect()}catch{}},t)}handleHardClose(t){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(t))}async connect(t){let e;return this.connectionPromise?this.connectionPromise:(this.challenge=void 0,this.authPromise=void 0,this.skipReconnection=!1,this.connectionPromise=new Promise((n,r)=>{t?.timeout&&(e=setTimeout(()=>{r(\"connection timed out\"),this.connectionPromise=void 0,this.reconnectAttempts===0&&(this.skipReconnection=!0),this.handleHardClose(\"relay connection timed out\")},t.timeout)),t?.abort&&(t.abort.onabort=r);try{this.ws=new this._WebSocket(this.url)}catch(o){clearTimeout(e),r(o);return}this.ws.onopen=()=>{this.reconnectTimeoutHandle&&(clearTimeout(this.reconnectTimeoutHandle),this.reconnectTimeoutHandle=void 0),clearTimeout(e),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(e),r(\"connection failed\"),this.connectionPromise=void 0,this.reconnectAttempts===0&&(this.skipReconnection=!0),this.handleHardClose(\"relay connection failed\")},this.ws.onclose=o=>{clearTimeout(e),r(o.message||\"websocket closed\"),this.handleHardClose(\"relay connection closed\")},this.ws.onmessage=this._onmessage.bind(this)}),this.connectionPromise)}waitForPingPong(){return new Promise(t=>{this.ws.once(\"pong\",()=>t(!0)),this.ws.ping()})}waitForDummyReq(){return new Promise((t,e)=>{if(!this.connectionPromise)return e(new Error(`no connection to ${this.url}, can't ping`));try{let n=this.subscribe([{ids:[\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"],limit:0}],{label:\"<forced-ping>\",oneose:()=>{t(!0),n.close()},onclose(){t(!0)},eoseTimeout:this.pingTimeout+1e3})}catch(n){e(n)}})}async pingpong(){this.ws?.readyState===1&&(await Promise.any([this.ws&&this.ws.ping&&this.ws.once?this.waitForPingPong():this.waitForDummyReq(),new Promise(e=>setTimeout(()=>e(!1),this.pingTimeout))])||this.ws?.readyState===this._WebSocket.OPEN&&this.ws?.close())}async send(t){if(!this.connectionPromise)throw new mn(t,this.url);this.connectionPromise.then(()=>{this.ws?.send(t)})}async auth(t){let e=this.challenge;if(!e)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 t(_s(this.url,e)),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(t){this.idleSince=void 0,this.clearIdleTimeout(),this.ongoingOperations++;let e=new Promise((n,r)=>{let o=setTimeout(()=>{let s=this.openEventPublishes.get(t.id);s&&(s.reject(new Error(\"publish timed out\")),this.openEventPublishes.delete(t.id))},this.publishTimeout);this.openEventPublishes.set(t.id,{resolve:n,reject:r,timeout:o})});try{await this.send('[\"EVENT\",'+JSON.stringify(t)+\"]\")}catch(n){let r=this.openEventPublishes.get(t.id);r&&(r.reject(n),this.openEventPublishes.delete(t.id))}return this.ongoingOperations--,this.ongoingOperations===0&&(this.idleSince=Date.now(),this.scheduleIdleClose()),e}async count(t,e){return(await this.countWithHLL(t,e)).count}async countWithHLL(t,e){this.serial++;let n=e?.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(t).substring(1))}catch(o){let s=this.openCountRequests.get(n);s&&(s.reject(o),this.openCountRequests.delete(n))}return r}subscribe(t,e){e.label!==\"<forced-ping>\"&&(this.idleSince=void 0,this.clearIdleTimeout(),this.ongoingOperations++);let n=this.prepareSubscription(t,e);return n.fire(),e.abort&&(e.abort.onabort=()=>n.close(String(e.abort.reason||\"<aborted>\"))),n}prepareSubscription(t,e){this.serial++;let n=e.id||(e.label?e.label+\":\":\"sub:\")+this.serial,r=new Is(this,n,t,e);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(t){let e=t.data;if(!e)return;let n=Us(e);if(n){let r=this.openSubs.get(n);if(!r)return;let o=Os(e,\"id\"),s=r.alreadyHaveEvent?.(o);if(r.receivedEvent?.(this,o),s)return}try{let r=JSON.parse(e);switch(r[0]){case\"EVENT\":{let o=this.openSubs.get(r[1]),s=r[2];Rs(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],c=this.openEventPublishes.get(o);c&&(clearTimeout(c.timeout),s?c.resolve(i):c.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 mn))throw o});return}default:{this.openSubs.get(r[1])?.oncustom?.(r);return}}}catch(r){try{let[o,s,i]=JSON.parse(e);console.warn(`[nostr] relay ${this.url} error processing message:`,r,i)}catch{console.warn(`[nostr] relay ${this.url} error processing message:`,r)}return}}},Is=class{constructor(t,e,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=t,this.filters=n,this.id=e,this.alreadyHaveEvent=r.alreadyHaveEvent,this.receivedEvent=r.receivedEvent,this.eoseTimeout=r.eoseTimeout||t.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(t=\"closed by caller\"){if(!this.closed&&this.relay.connected){try{this.relay.send('[\"CLOSE\",'+JSON.stringify(this.id)+\"]\")}catch(e){if(!(e instanceof mn))throw e}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?.(t)}},we=256,ks=we*2,Pc=new TextEncoder;function Cs(t,e){switch(e){case\"reactions\":return{\"#e\":[t],kinds:[7]};case\"reposts\":return{\"#e\":[t],kinds:[6]};case\"quotes\":return{\"#q\":[t],kinds:[1,1111]};case\"replies\":return{\"#e\":[t],kinds:[1]};case\"comments\":return{\"#E\":[t],kinds:[1111]};case\"followers\":return{\"#p\":[t],kinds:[3]}}}function Ns(){return new Uint8Array(we)}function Ps(t){if(t.length!==ks||!/^[0-9a-f]+$/.test(t))return;let e=new Uint8Array(we);for(let n=0;n<we;n++)e[n]=parseInt(t.slice(n*2,n*2+2),16);return e}function qs(t){if(t.length!==we)throw new Error(`invalid number of registers ${t.length}`);let e=\"\";for(let n=0;n<we;n++)e+=t[n].toString(16).padStart(2,\"0\");return e}function Ds(t,e){if(t.length===0&&(t=Ns()),t.length!==we)throw new Error(`invalid number of registers ${t.length}`);if(e.length!==we)throw new Error(`invalid number of registers ${e.length}`);for(let n=0;n<we;n++)e[n]>t[n]&&(t[n]=e[n]);return t}var $s=class{constructor(t){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=t.verifyEvent,this._WebSocket=t.websocketImplementation,this.enablePing=t.enablePing,this.enableReconnect=t.enableReconnect||!1,t.idleTimeout&&(this.idleTimeout=t.idleTimeout),this.automaticallyAuth=t.automaticallyAuth,this.onRelayConnectionFailure=t.onRelayConnectionFailure,this.onRelayConnectionSuccess=t.onRelayConnectionSuccess,this.allowConnectingToRelay=t.allowConnectingToRelay,this.maxWaitForConnection=t.maxWaitForConnection||3e3}async ensureRelay(t,e){t=Ke(t);let n=this.relays.get(t);if(n||(n=new ro(t,{verifyEvent:this.verifyEvent,websocketImplementation:this._WebSocket,enablePing:this.enablePing,enableReconnect:this.enableReconnect,idleTimeout:this.idleTimeout}),n.onclose=()=>{this.relays.delete(t)},this.relays.set(t,n)),this.automaticallyAuth){let r=this.automaticallyAuth(t);r&&(n.onauth=r)}try{await n.connect({timeout:e?.connectionTimeout,abort:e?.abort})}catch(r){throw this.relays.delete(t),r}return n}close(t){t.map(Ke).forEach(e=>{this.relays.get(e)?.close(),this.relays.delete(e)})}subscribe(t,e,n){let r=[],o=[];for(let s=0;s<t.length;s++){let i=Ke(t[s]);r.find(c=>c.url===i)||o.indexOf(i)===-1&&(o.push(i),r.push({url:i,filter:e}))}return this.subscribeMap(r,n)}subscribeMany(t,e,n){return this.subscribe(t,e,n)}subscribeMap(t,e){let n=new Map;for(let x of t){let{url:d,filter:y}=x;n.has(d)||n.set(d,[]),n.get(d).push(y)}let r=Array.from(n.entries()).map(([x,d])=>({url:x,filters:d}));this.trackRelays&&(e.receivedEvent=(x,d)=>{let y=this.seenOn.get(d);y||(y=new Set,this.seenOn.set(d,y)),y.add(x)});let o=new Set,s=[],i=[],c=x=>{i[x]||(i[x]=!0,i.filter(d=>d).length===r.length&&(e.oneose?.(),c=()=>{}))},a=[],f=(x,d,y)=>{a[x]||(c(x),a[x]={url:d,reason:y},a.filter(m=>m).length===r.length&&(e.onclose?.(a),f=()=>{}))},h=x=>{if(e.alreadyHaveEvent?.(x))return!0;let d=o.has(x);return o.add(x),d},l=Promise.all(r.map(async({url:x,filters:d},y)=>{if(this.allowConnectingToRelay?.(x,[\"read\",d])===!1){f(y,x,\"connection skipped by allowConnectingToRelay\");return}let m;try{m=await this.ensureRelay(x,{connectionTimeout:this.maxWaitForConnection<(e.maxWait||0)?Math.max(e.maxWait*.8,e.maxWait-1e3):this.maxWaitForConnection,abort:e.abort})}catch(S){this.onRelayConnectionFailure?.(x),f(y,x,S?.message||String(S));return}this.onRelayConnectionSuccess?.(x);let E=m.subscribe(d,{...e,oneose:()=>c(y),onclose:S=>{S.startsWith(\"auth-required: \")&&e.onauth?m.auth(e.onauth).then(()=>{m.subscribe(d,{...e,oneose:()=>c(y),onclose:U=>{f(y,x,U)},alreadyHaveEvent:h,eoseTimeout:e.maxWait,abort:e.abort})}).catch(U=>{f(y,x,`auth was required and attempted, but failed with: ${U}`)}):f(y,x,S)},alreadyHaveEvent:h,eoseTimeout:e.maxWait,abort:e.abort});s.push(E)}));return{async close(x){await l,s.forEach(d=>{d.close(x)})}}}subscribeEose(t,e,n){let r;return r=this.subscribe(t,e,{...n,oneose(){let o=\"closed automatically on eose\";r?r.close(o):n.onclose?.(t.map(s=>({url:s,reason:o})))}}),r}subscribeManyEose(t,e,n){return this.subscribeEose(t,e,n)}async querySync(t,e,n){return new Promise(async r=>{let o=[];this.subscribeEose(t,e,{...n,onevent(s){o.push(s)},onclose(s){r(o)}})})}async get(t,e,n){e.limit=1;let r=await this.querySync(t,e,n);return r.sort((o,s)=>s.created_at-o.created_at),r[0]||null}async countMany(t,e,n,r){let o=Cs(e,n),s=[];for(let f=0;f<t.length;f++){let h=Ke(t[f]);s.indexOf(h)===-1&&s.push(h)}let i=await Promise.all(s.map(async f=>{if(this.allowConnectingToRelay?.(f,[\"read\",[o]])===!1)return null;let h;try{h=await this.ensureRelay(f,{connectionTimeout:this.maxWaitForConnection<(r?.maxWait||0)?Math.max(r.maxWait*.8,r.maxWait-1e3):this.maxWaitForConnection,abort:r?.abort})}catch{return this.onRelayConnectionFailure?.(f),null}return this.onRelayConnectionSuccess?.(f),h.countWithHLL([o],{id:r?.id}).catch(()=>null)})),c=0,a;for(let f of i){if(!f||(f.count>c&&(c=f.count),!f.hll||f.hll.length!==512))continue;let h=Ps(f.hll);h&&(a=Ds(a||new Uint8Array(0),h))}return a?{count:c,hll:qs(a)}:{count:c}}publish(t,e,n){return t.map(Ke).map(async(r,o,s)=>{if(s.indexOf(r)!==o)return Promise.reject(\"duplicate url\");if(this.allowConnectingToRelay?.(r,[\"write\",e])===!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(c){return this.onRelayConnectionFailure?.(r),Promise.reject(\"connection failure: \"+String(c))}return i.publish(e).catch(async c=>{if(c instanceof Error&&c.message.startsWith(\"auth-required: \")&&n?.onauth)return await i.auth(n.onauth),i.publish(e);throw c}).then(c=>{if(this.trackRelays){let a=this.seenOn.get(e.id);a||(a=new Set,this.seenOn.set(e.id,a)),a.add(i)}return c})})}listConnectionStatus(){let t=new Map;return this.relays.forEach((e,n)=>t.set(n,e.connected)),t}destroy(){this.relays.forEach(t=>t.close()),this.relays=new Map}pruneIdleRelays(t=1e4){let e=[];for(let[n,r]of this.relays)r.idleSince&&Date.now()-r.idleSince>=t&&(this.relays.delete(n),e.push(n),r.close());return e}},oo;try{oo=WebSocket}catch{}var Vs=class extends $s{constructor(t){super({verifyEvent:Qr,websocketImplementation:oo,maxWaitForConnection:3e3,...t})}},so;try{so=fetch}catch{}var Ms=/^bunker:\\/\\/([0-9a-f]{64})\\??([?\\/\\w:.=&%-]*)$/;async function io(t){let e=t.match(Ms);if(e)try{let n=e[1],r=new URLSearchParams(e[2]);return{pubkey:n,relays:r.getAll(\"relay\"),secret:r.get(\"secret\")}}catch{}return Ks(t)}async function Ks(t){let e=t.match(Ss);if(!e)return null;let[n,r=\"_\",o]=e;try{let s=`https://${o}/.well-known/nostr.json?name=${r}`,i=await(await so(s,{redirect:\"error\"})).json(),c=i.names[r],a=i.nip46[c]||[];return{pubkey:c,relays:a,secret:null}}catch{return null}}function co(t){let e=new URLSearchParams;return t.relays.forEach(n=>{e.append(\"relay\",n)}),e.append(\"secret\",t.secret),t.perms&&t.perms.length>0&&e.append(\"perms\",t.perms.join(\",\")),t.name&&e.append(\"name\",t.name),t.url&&e.append(\"url\",t.url),t.image&&e.append(\"image\",t.image),`nostrconnect://${t.clientPubkey}?${e.toString()}`}var Tt=class{constructor(t,e){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=e,this.pool=e.pool||new Vs,this.secretKey=t,this.isOpen=!1,this.idPrefix=Math.random().toString(36).substring(7),this.serial=0,this.listeners={},this.waitingForAuth={}}static fromBunker(t,e,n={}){if(e.relays.length===0)throw new Error(\"no relays specified for this bunker\");let r=new Tt(t,n);return r.conversationKey=gn(t,e.pubkey),r.bp=e,r.setupSubscription(),r}static async fromURI(t,e,n={},r=3e5){let o=new URL(e),s=o.searchParams.get(\"secret\");if(!s)throw new Error(\"nostrconnect:// URI has no secret\");let i=new Tt(t,n),c=Xr(t);return new Promise((a,f)=>{let h=!1,l=i.pool.subscribe(o.searchParams.getAll(\"relay\"),{kinds:[wn],\"#p\":[c],limit:0},{onevent:async x=>{try{let d=gn(t,x.pubkey),y=Jr(x.content,d);JSON.parse(y).result===s&&(l.close(),i.bp={pubkey:x.pubkey,relays:o.searchParams.getAll(\"relay\"),secret:s},i.conversationKey=gn(t,x.pubkey),i.setupSubscription(),h=!0,n.skipSwitchRelays||await Promise.race([new Promise(E=>setTimeout(E,1e3)),i.switchRelays()]),a(i))}catch(d){console.warn(\"failed to process potential connection event\",d)}},onclose:()=>{h||f(new Error(\"subscription closed before connection was established.\"))},maxWait:typeof r==\"number\"?r:void 0,abort:typeof r!=\"number\"?r:void 0})})}setupSubscription(){let t=this.listeners,e=this.waitingForAuth,n=this.conversationKey;this.subCloser=this.pool.subscribe(this.bp.relays,{kinds:[wn],authors:[this.bp.pubkey],\"#p\":[Xr(this.secretKey)],limit:0},{onevent:async r=>{let o=JSON.parse(Jr(r.content,n)),{id:s,result:i,error:c}=o;if(i===\"auth_url\"&&e[s]){delete e[s],this.params.onauth?this.params.onauth(c):console.warn(`nostr-tools/nip46: remote signer ${this.bp.pubkey} tried to send an \"auth_url\"='${c}' but there was no onauth() callback configured.`);return}let a=t[s];a&&(c?a.reject(c):i&&a.resolve(i),delete t[s])},onclose:()=>{this.subCloser=void 0}}),this.isOpen=!0}async switchRelays(){try{let t=await this.sendRequest(\"switch_relays\",[]),e=JSON.parse(t);if(!e||JSON.stringify(e.sort())===JSON.stringify(this.bp.relays))return!1;this.bp.relays=e;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(t,e){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=As(JSON.stringify({id:o,method:t,params:e}),this.conversationKey),i=gs({kind:wn,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 t=await this.sendRequest(\"ping\",[]);if(t!==\"pong\")throw new Error(`result is not pong: ${t}`)}async connect(t){let e=[this.bp.pubkey,this.bp.secret||\"\"];t&&(e.push(\"\"),e.push(JSON.stringify(t))),await this.sendRequest(\"connect\",e)}async logout(){let t=await this.sendRequest(\"logout\",[]);if(t!==\"ack\")throw new Error(`result is not ack: ${t}`);await this.close()}async getPublicKey(){return this.cachedPubKey||(this.cachedPubKey=await this.sendRequest(\"get_public_key\",[])),this.cachedPubKey}async signEvent(t){let e=await this.sendRequest(\"sign_event\",[JSON.stringify(t)]),n=JSON.parse(e);if(Qr(n))return n;throw new Error(`event returned from bunker is improperly signed: ${JSON.stringify(n)}`)}async nip04Encrypt(t,e){return await this.sendRequest(\"nip04_encrypt\",[t,e])}async nip04Decrypt(t,e){return await this.sendRequest(\"nip04_decrypt\",[t,e])}async nip44Encrypt(t,e){return await this.sendRequest(\"nip44_encrypt\",[t,e])}async nip44Decrypt(t,e){return await this.sendRequest(\"nip44_decrypt\",[t,e])}};var Yc=new TextDecoder(\"utf-8\"),Fc=new TextEncoder;var js=5e3;function ao(t){return Gs(\"npub\",ne(t))}function Ws(t,e){let n=xn.toWords(e);return xn.encode(t,n,js)}function Gs(t,e){return Ws(t,e)}function fo(t){return t instanceof Uint8Array||ArrayBuffer.isView(t)&&t.constructor.name===\"Uint8Array\"&&\"BYTES_PER_ELEMENT\"in t&&t.BYTES_PER_ELEMENT===1}var Sn=t=>t?`\"${t}\" `:\"\";function Zs(t,e=\"\"){if(typeof t!=\"number\")throw new TypeError(Sn(e)+\"expected number, got \"+typeof t);if(!Number.isSafeInteger(t)||t<0)throw new RangeError(Sn(e)+\"expected integer >= 0, got \"+t);return t}function zs(t,e,n=\"\"){if(fo(t)&&(e===void 0||t.length===e))return t;e!==void 0&&Zs(e,\"length\");let r=fo(t),o=e!==void 0?` of length ${e}`:\"\",s=r?`length=${t.length}`:`type=${typeof t}`,i=Sn(n)+\"expected Uint8Array\"+o+\", got \"+s;throw r?new RangeError(i):new TypeError(i)}var lo=typeof Uint8Array.from([]).toHex==\"function\"&&typeof Uint8Array.fromHex==\"function\",Ys=Array.from({length:256},(t,e)=>e.toString(16).padStart(2,\"0\"));function ho(t){if(zs(t),lo)return t.toHex();let e=\"\";for(let n=0;n<t.length;n++)e+=Ys[t[n]];return e}function uo(t){return t>=48&&t<=57?t-48:t>=65&&t<=70?t-55:t>=97&&t<=102?t-87:void 0}function bo(t){if(typeof t!=\"string\")throw new TypeError(\"hex string expected, got \"+typeof t);if(lo)try{return Uint8Array.fromHex(t)}catch(o){throw o instanceof SyntaxError?new RangeError(o.message):o}let e=t.length,n=e/2;if(e%2)throw new RangeError(\"hex string expected, got unpadded hex of length \"+e);let r=new Uint8Array(n);for(let o=0,s=0;o<n;o++,s+=2){let i=uo(t.charCodeAt(s)),c=uo(t.charCodeAt(s+1));if(i===void 0||c===void 0){let a=t[s]+t[s+1];throw new RangeError('hex string expected, got non-hex character \"'+a+'\" at index '+s)}r[o]=i*16+c}return r}window.NostrSigner={generateSecretKey:Br,getPublicKey:Ar,finalizeEvent:Sr,verifyEvent:Lr,BunkerSigner:Tt,parseBunkerInput:io,createNostrConnectURI:co,npubEncode:ao,bytesToHex:ho,hexToBytes:bo};})();\n";