create daemon

This commit is contained in:
Your Name
2025-09-29 07:21:46 -04:00
parent 955090b079
commit 69514943a9
1341 changed files with 181418 additions and 0 deletions

24
thrower_daemon/node_modules/nostr-tools/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>

436
thrower_daemon/node_modules/nostr-tools/README.md generated vendored Normal file
View File

@@ -0,0 +1,436 @@
# ![](https://img.shields.io/github/actions/workflow/status/nbd-wtf/nostr-tools/test.yml) [![JSR](https://jsr.io/badges/@nostr/tools)](https://jsr.io/@nostr/tools) nostr-tools
Tools for developing [Nostr](https://github.com/fiatjaf/nostr) clients.
Only depends on _@scure_ and _@noble_ packages.
This package is only providing lower-level functionality. If you want higher-level features, take a look at [@nostr/gadgets](https://jsr.io/@nostr/gadgets) which is based on this library and expands upon it and has other goodies (it's only available on jsr).
## Installation
```bash
# npm
npm install --save nostr-tools
# jsr
npx jsr add @nostr/tools
```
If using TypeScript, this package requires TypeScript >= 5.0.
## Documentation
https://jsr.io/@nostr/tools/doc
## Usage
### Generating a private key and a public key
```js
import { generateSecretKey, getPublicKey } from 'nostr-tools/pure'
let sk = generateSecretKey() // `sk` is a Uint8Array
let pk = getPublicKey(sk) // `pk` is a hex string
```
To get the secret key in hex format, use
```js
import { bytesToHex, hexToBytes } from '@noble/hashes/utils' // already an installed dependency
let skHex = bytesToHex(sk)
let backToBytes = hexToBytes(skHex)
```
### Creating, signing and verifying events
```js
import { finalizeEvent, verifyEvent } from 'nostr-tools/pure'
let event = finalizeEvent({
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags: [],
content: 'hello',
}, sk)
let isGood = verifyEvent(event)
```
### Interacting with one or multiple relays
Doesn't matter what you do, you always should be using a `SimplePool`:
```js
import { finalizeEvent, generateSecretKey, getPublicKey } from 'nostr-tools/pure'
import { SimplePool } from 'nostr-tools/pool'
const pool = new SimplePool()
const relays = ['wss://relay.example.com', 'wss://relay.example2.com']
// let's query for one event that exists
const event = pool.get(
relays,
{
ids: ['d7dd5eb3ab747e16f8d0212d53032ea2a7cadef53837e5a6c66d42849fcb9027'],
},
)
if (event) {
console.log('it exists indeed on this relay:', event)
}
// let's query for more than one event that exists
const events = pool.querySync(
relays,
{
kinds: [1],
limit: 10
},
)
if (events) {
console.log('it exists indeed on this relay:', events)
}
// let's publish a new event while simultaneously monitoring the relay for it
let sk = generateSecretKey()
let pk = getPublicKey(sk)
pool.subscribe(
['wss://a.com', 'wss://b.com', 'wss://c.com'],
{
kinds: [1],
authors: [pk],
},
{
onevent(event) {
console.log('got event:', event)
}
}
)
let eventTemplate = {
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags: [],
content: 'hello world',
}
// this assigns the pubkey, calculates the event id and signs the event in a single step
const signedEvent = finalizeEvent(eventTemplate, sk)
await Promise.any(pool.publish(['wss://a.com', 'wss://b.com'], signedEvent))
relay.close()
```
To use this on Node.js you first must install `ws` and call something like this:
```js
import { useWebSocketImplementation } from 'nostr-tools/pool'
// or import { useWebSocketImplementation } from 'nostr-tools/relay' if you're using the Relay directly
import WebSocket from 'ws'
useWebSocketImplementation(WebSocket)
```
You can enable regular pings of connected relays with the `enablePing` option. This will set up a heartbeat that closes the websocket if it doesn't receive a response in time. Some platforms don't report websocket disconnections due to network issues, and enabling this can increase reliability.
```js
import { SimplePool } from 'nostr-tools/pool'
const pool = new SimplePool({ enablePing: true })
```
### Parsing references (mentions) from a content based on NIP-27
```js
import * as nip27 from '@nostr/tools/nip27'
for (let block of nip27.parse(evt.content)) {
switch (block.type) {
case 'text':
console.log(block.text)
break
case 'reference': {
if ('id' in block.pointer) {
console.log("it's a nevent1 uri", block.pointer)
} else if ('identifier' in block.pointer) {
console.log("it's a naddr1 uri", block.pointer)
} else {
console.log("it's an npub1 or nprofile1 uri", block.pointer)
}
break
}
case 'url': {
console.log("it's a normal url:", block.url)
break
}
case 'image':
case 'video':
case 'audio':
console.log("it's a media url:", block.url)
break
case 'relay':
console.log("it's a websocket url, probably a relay address:", block.url)
break
default:
break
}
}
```
### Connecting to a bunker using NIP-46
`BunkerSigner` allows your application to request signatures and other actions from a remote NIP-46 signer, often called a "bunker". There are two primary ways to establish a connection, depending on whether the client or the bunker initiates the connection.
A local secret key is required for the client to communicate securely with the bunker. This key should generally be persisted for the user's session.
```js
import { generateSecretKey } from '@nostr/tools/pure'
const localSecretKey = generateSecretKey()
```
### Method 1: Using a Bunker URI (`bunker://`)
This is the bunker-initiated flow. Your client receives a `bunker://` string or a NIP-05 identifier from the user. You use `BunkerSigner.fromBunker()` to create an instance, which returns immediately. For the **initial connection** with a new bunker, you must explicitly call `await bunker.connect()` to establish the connection and receive authorization.
```js
import { BunkerSigner, parseBunkerInput } from '@nostr/tools/nip46'
import { SimplePool } from '@nostr/tools/pool'
// parse a bunker URI
const bunkerPointer = await parseBunkerInput('bunker://abcd...?relay=wss://relay.example.com')
if (!bunkerPointer) {
throw new Error('Invalid bunker input')
}
// create the bunker instance
const pool = new SimplePool()
const bunker = BunkerSigner.fromBunker(localSecretKey, bunkerPointer, { pool })
await bunker.connect()
// and use it
const pubkey = await bunker.getPublicKey()
const event = await bunker.signEvent({
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags: [],
content: 'Hello from bunker!'
})
// cleanup
await signer.close()
pool.close([])
```
> **Note on Reconnecting:** Once a connection has been successfully established and the `BunkerPointer` is stored, you do **not** need to call `await bunker.connect()` on subsequent sessions.
### Method 2: Using a Client-generated URI (`nostrconnect://`)
This is the client-initiated flow, which generally provides a better user experience for first-time connections (e.g., via QR code). Your client generates a `nostrconnect://` URI and waits for the bunker to connect to it.
`BunkerSigner.fromURI()` is an **asynchronous** method. It returns a `Promise` that resolves only after the bunker has successfully connected. Therefore, the returned signer instance is already fully connected and ready to use, so you **do not** need to call `.connect()` on it.
```js
import { getPublicKey } from '@nostr/tools/pure'
import { BunkerSigner, createNostrConnectURI } from '@nostr/tools/nip46'
import { SimplePool } from '@nostr/tools/pool'
const clientPubkey = getPublicKey(localSecretKey)
// generate a connection URI for the bunker to scan
const connectionUri = createNostrConnectURI({
clientPubkey,
relays: ['wss://relay.damus.io', 'wss://relay.primal.net'],
secret: 'a-random-secret-string', // A secret to verify the bunker's response
name: 'My Awesome App'
})
// wait for the bunker to connect
const pool = new SimplePool()
const signer = await BunkerSigner.fromURI(localSecretKey, connectionUri, { pool })
// and use it
const pubkey = await signer.getPublicKey()
const event = await signer.signEvent({
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags: [],
content: 'Hello from a client-initiated connection!'
})
// cleanup
await signer.close()
pool.close([])
```
> **Note on Persistence:** This method is ideal for the initial sign-in. To allow users to stay logged in across sessions, you should store the connection details and use `Method 1` for subsequent reconnections.
### Parsing thread from any note based on NIP-10
```js
import * as nip10 from '@nostr/tools/nip10'
// event is a nostr event with tags
const refs = nip10.parse(event)
// get the root event of the thread
if (refs.root) {
console.log('root event:', refs.root.id)
console.log('root event relay hints:', refs.root.relays)
console.log('root event author:', refs.root.author)
}
// get the immediate parent being replied to
if (refs.reply) {
console.log('reply to:', refs.reply.id)
console.log('reply relay hints:', refs.reply.relays)
console.log('reply author:', refs.reply.author)
}
// get any mentioned events
for (let mention of refs.mentions) {
console.log('mentioned event:', mention.id)
console.log('mention relay hints:', mention.relays)
console.log('mention author:', mention.author)
}
// get any quoted events
for (let quote of refs.quotes) {
console.log('quoted event:', quote.id)
console.log('quote relay hints:', quote.relays)
}
// get any referenced profiles
for (let profile of refs.profiles) {
console.log('referenced profile:', profile.pubkey)
console.log('profile relay hints:', profile.relays)
}
```
### Querying profile data from a NIP-05 address
```js
import { queryProfile } from 'nostr-tools/nip05'
let profile = await queryProfile('jb55.com')
console.log(profile.pubkey)
// prints: 32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245
console.log(profile.relays)
// prints: [wss://relay.damus.io]
```
To use this on Node.js < v18, you first must install `node-fetch@2` and call something like this:
```js
import { useFetchImplementation } from 'nostr-tools/nip05'
useFetchImplementation(require('node-fetch'))
```
### Including NIP-07 types
```js
import type { WindowNostr } from 'nostr-tools/nip07'
declare global {
interface Window {
nostr?: WindowNostr;
}
}
```
### Encoding and decoding NIP-19 codes
```js
import { generateSecretKey, getPublicKey } from 'nostr-tools/pure'
import * as nip19 from 'nostr-tools/nip19'
let sk = generateSecretKey()
let nsec = nip19.nsecEncode(sk)
let { type, data } = nip19.decode(nsec)
assert(type === 'nsec')
assert(data === sk)
let pk = getPublicKey(generateSecretKey())
let npub = nip19.npubEncode(pk)
let { type, data } = nip19.decode(npub)
assert(type === 'npub')
assert(data === pk)
let pk = getPublicKey(generateSecretKey())
let relays = ['wss://relay.nostr.example.mydomain.example.com', 'wss://nostr.banana.com']
let nprofile = nip19.nprofileEncode({ pubkey: pk, relays })
let { type, data } = nip19.decode(nprofile)
assert(type === 'nprofile')
assert(data.pubkey === pk)
assert(data.relays.length === 2)
```
### Using it with `nostr-wasm`
[`nostr-wasm`](https://github.com/fiatjaf/nostr-wasm) is a thin wrapper over [libsecp256k1](https://github.com/bitcoin-core/secp256k1) compiled to WASM just for hashing, signing and verifying Nostr events.
```js
import { setNostrWasm, generateSecretKey, finalizeEvent, verifyEvent } from 'nostr-tools/wasm'
import { initNostrWasm } from 'nostr-wasm'
// make sure this promise resolves before your app starts calling finalizeEvent or verifyEvent
initNostrWasm().then(setNostrWasm)
// or use 'nostr-wasm/gzipped' or even 'nostr-wasm/headless',
// see https://www.npmjs.com/package/nostr-wasm for options
```
If you're going to use `Relay` and `SimplePool` you must also import `nostr-tools/abstract-relay` and/or `nostr-tools/abstract-pool` instead of the defaults and then instantiate them by passing the `verifyEvent`:
```js
import { setNostrWasm, verifyEvent } from 'nostr-tools/wasm'
import { AbstractRelay } from 'nostr-tools/abstract-relay'
import { AbstractSimplePool } from 'nostr-tools/abstract-pool'
import { initNostrWasm } from 'nostr-wasm'
initNostrWasm().then(setNostrWasm)
const relay = AbstractRelay.connect('wss://relayable.org', { verifyEvent })
const pool = new AbstractSimplePool({ verifyEvent })
```
This may be faster than the pure-JS [noble libraries](https://paulmillr.com/noble/) used by default and in `nostr-tools/pure`. Benchmarks:
```
benchmark time (avg) (min … max) p75 p99 p995
------------------------------------------------- -----------------------------
• relay read message and verify event (many events)
------------------------------------------------- -----------------------------
wasm 34.94 ms/iter (34.61 ms … 35.73 ms) 35.07 ms 35.73 ms 35.73 ms
pure js 239.7 ms/iter (235.41 ms … 243.69 ms) 240.51 ms 243.69 ms 243.69 ms
trusted 402.71 µs/iter (344.57 µs … 2.98 ms) 407.39 µs 745.62 µs 812.59 µs
summary for relay read message and verify event
wasm
86.77x slower than trusted
6.86x faster than pure js
```
### Using from the browser (if you don't want to use a bundler)
```html
<script src="https://unpkg.com/nostr-tools/lib/nostr.bundle.js"></script>
<script>
window.NostrTools.generateSecretKey('...') // and so on
</script>
```
## Plumbing
To develop `nostr-tools`, install [`just`](https://just.systems/) and run `just -l` to see commands available.
## License
This is free and unencumbered software released into the public domain. By submitting patches to this project, you agree to dedicate any and all copyright interest in this software to the public domain.
## Contributing to this repository
Use NIP-34 to send your patches to:
```
naddr1qq9kummnw3ez6ar0dak8xqg5waehxw309aex2mrp0yhxummnw3ezucn8qyt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueqpzemhxue69uhhyetvv9ujuurjd9kkzmpwdejhgq3q80cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsxpqqqpmejdv00jq
```

View File

@@ -0,0 +1,796 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// abstract-pool.ts
var abstract_pool_exports = {};
__export(abstract_pool_exports, {
AbstractSimplePool: () => AbstractSimplePool
});
module.exports = __toCommonJS(abstract_pool_exports);
// core.ts
var verifiedSymbol = Symbol("verified");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
function normalizeURL(url) {
try {
if (url.indexOf("://") === -1)
url = "wss://" + url;
let p = new URL(url);
p.pathname = p.pathname.replace(/\/+/g, "/");
if (p.pathname.endsWith("/"))
p.pathname = p.pathname.slice(0, -1);
if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:")
p.port = "";
p.searchParams.sort();
p.hash = "";
return p.toString();
} catch (e) {
throw new Error(`Invalid URL: ${url}`);
}
}
var QueueNode = class {
value;
next = null;
prev = null;
constructor(message) {
this.value = message;
}
};
var Queue = class {
first;
last;
constructor() {
this.first = null;
this.last = null;
}
enqueue(value) {
const newNode = new QueueNode(value);
if (!this.last) {
this.first = newNode;
this.last = newNode;
} else if (this.last === this.first) {
this.last = newNode;
this.last.prev = this.first;
this.first.next = newNode;
} else {
newNode.prev = this.last;
this.last.next = newNode;
this.last = newNode;
}
return true;
}
dequeue() {
if (!this.first)
return null;
if (this.first === this.last) {
const target2 = this.first;
this.first = null;
this.last = null;
return target2.value;
}
const target = this.first;
this.first = target.next;
if (this.first) {
this.first.prev = null;
}
return target.value;
}
};
// kinds.ts
var ClientAuth = 22242;
// filter.ts
function matchFilter(filter, event) {
if (filter.ids && filter.ids.indexOf(event.id) === -1) {
return false;
}
if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {
return false;
}
if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {
return false;
}
for (let f in filter) {
if (f[0] === "#") {
let tagName = f.slice(1);
let values = filter[`#${tagName}`];
if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values.indexOf(v) !== -1))
return false;
}
}
if (filter.since && event.created_at < filter.since)
return false;
if (filter.until && event.created_at > filter.until)
return false;
return true;
}
function matchFilters(filters, event) {
for (let i = 0; i < filters.length; i++) {
if (matchFilter(filters[i], event)) {
return true;
}
}
return false;
}
// fakejson.ts
function getHex64(json, field) {
let len = field.length + 3;
let idx = json.indexOf(`"${field}":`) + len;
let s = json.slice(idx).indexOf(`"`) + idx + 1;
return json.slice(s, s + 64);
}
function getSubscriptionId(json) {
let idx = json.slice(0, 22).indexOf(`"EVENT"`);
if (idx === -1)
return null;
let pstart = json.slice(idx + 7 + 1).indexOf(`"`);
if (pstart === -1)
return null;
let start = idx + 7 + 1 + pstart;
let pend = json.slice(start + 1, 80).indexOf(`"`);
if (pend === -1)
return null;
let end = start + 1 + pend;
return json.slice(start + 1, end);
}
// nip42.ts
function makeAuthEvent(relayURL, challenge) {
return {
kind: ClientAuth,
created_at: Math.floor(Date.now() / 1e3),
tags: [
["relay", relayURL],
["challenge", challenge]
],
content: ""
};
}
// helpers.ts
async function yieldThread() {
return new Promise((resolve) => {
const ch = new MessageChannel();
const handler = () => {
ch.port1.removeEventListener("message", handler);
resolve();
};
ch.port1.addEventListener("message", handler);
ch.port2.postMessage(0);
ch.port1.start();
});
}
var alwaysTrue = (t) => {
t[verifiedSymbol] = true;
return true;
};
// abstract-relay.ts
var SendingOnClosedConnection = class extends Error {
constructor(message, relay) {
super(`Tried to send message '${message} on a closed connection to ${relay}.`);
this.name = "SendingOnClosedConnection";
}
};
var AbstractRelay = class {
url;
_connected = false;
onclose = null;
onnotice = (msg) => console.debug(`NOTICE from ${this.url}: ${msg}`);
baseEoseTimeout = 4400;
connectionTimeout = 4400;
publishTimeout = 4400;
pingFrequency = 2e4;
pingTimeout = 2e4;
openSubs = /* @__PURE__ */ new Map();
enablePing;
connectionTimeoutHandle;
connectionPromise;
openCountRequests = /* @__PURE__ */ new Map();
openEventPublishes = /* @__PURE__ */ new Map();
ws;
incomingMessageQueue = new Queue();
queueRunning = false;
challenge;
authPromise;
serial = 0;
verifyEvent;
_WebSocket;
constructor(url, opts) {
this.url = normalizeURL(url);
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation || WebSocket;
this.enablePing = opts.enablePing;
}
static async connect(url, opts) {
const relay = new AbstractRelay(url, opts);
await relay.connect();
return relay;
}
closeAllSubscriptions(reason) {
for (let [_, sub] of this.openSubs) {
sub.close(reason);
}
this.openSubs.clear();
for (let [_, ep] of this.openEventPublishes) {
ep.reject(new Error(reason));
}
this.openEventPublishes.clear();
for (let [_, cr] of this.openCountRequests) {
cr.reject(new Error(reason));
}
this.openCountRequests.clear();
}
get connected() {
return this._connected;
}
async connect() {
if (this.connectionPromise)
return this.connectionPromise;
this.challenge = void 0;
this.authPromise = void 0;
this.connectionPromise = new Promise((resolve, reject) => {
this.connectionTimeoutHandle = setTimeout(() => {
reject("connection timed out");
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection timed out");
}, this.connectionTimeout);
try {
this.ws = new this._WebSocket(this.url);
} catch (err) {
clearTimeout(this.connectionTimeoutHandle);
reject(err);
return;
}
this.ws.onopen = () => {
clearTimeout(this.connectionTimeoutHandle);
this._connected = true;
if (this.enablePing) {
this.pingpong();
}
resolve();
};
this.ws.onerror = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket error");
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
};
this.ws.onclose = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket closed");
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
};
this.ws.onmessage = this._onmessage.bind(this);
});
return this.connectionPromise;
}
async waitForPingPong() {
return new Promise((res, err) => {
;
this.ws && this.ws.on && this.ws.on("pong", () => res(true)) || err("ws can't listen for pong");
this.ws && this.ws.ping && this.ws.ping();
});
}
async waitForDummyReq() {
return new Promise((resolve, _) => {
const sub = this.subscribe([{ ids: ["a".repeat(64)] }], {
oneose: () => {
sub.close();
resolve(true);
},
eoseTimeout: this.pingTimeout + 1e3
});
});
}
async pingpong() {
if (this.ws?.readyState === 1) {
const result = await Promise.any([
this.ws && this.ws.ping && this.ws.on ? this.waitForPingPong() : this.waitForDummyReq(),
new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
]);
if (result) {
setTimeout(() => this.pingpong(), this.pingFrequency);
} else {
this.closeAllSubscriptions("pingpong timed out");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
}
}
async runQueue() {
this.queueRunning = true;
while (true) {
if (false === this.handleNext()) {
break;
}
await yieldThread();
}
this.queueRunning = false;
}
handleNext() {
const json = this.incomingMessageQueue.dequeue();
if (!json) {
return false;
}
const subid = getSubscriptionId(json);
if (subid) {
const so = this.openSubs.get(subid);
if (!so) {
return;
}
const id = getHex64(json, "id");
const alreadyHave = so.alreadyHaveEvent?.(id);
so.receivedEvent?.(this, id);
if (alreadyHave) {
return;
}
}
try {
let data = JSON.parse(json);
switch (data[0]) {
case "EVENT": {
const so = this.openSubs.get(data[1]);
const event = data[2];
if (this.verifyEvent(event) && matchFilters(so.filters, event)) {
so.onevent(event);
}
return;
}
case "COUNT": {
const id = data[1];
const payload = data[2];
const cr = this.openCountRequests.get(id);
if (cr) {
cr.resolve(payload.count);
this.openCountRequests.delete(id);
}
return;
}
case "EOSE": {
const so = this.openSubs.get(data[1]);
if (!so)
return;
so.receivedEose();
return;
}
case "OK": {
const id = data[1];
const ok = data[2];
const reason = data[3];
const ep = this.openEventPublishes.get(id);
if (ep) {
clearTimeout(ep.timeout);
if (ok)
ep.resolve(reason);
else
ep.reject(new Error(reason));
this.openEventPublishes.delete(id);
}
return;
}
case "CLOSED": {
const id = data[1];
const so = this.openSubs.get(id);
if (!so)
return;
so.closed = true;
so.close(data[2]);
return;
}
case "NOTICE":
this.onnotice(data[1]);
return;
case "AUTH": {
this.challenge = data[1];
return;
}
}
} catch (err) {
return;
}
}
async send(message) {
if (!this.connectionPromise)
throw new SendingOnClosedConnection(message, this.url);
this.connectionPromise.then(() => {
this.ws?.send(message);
});
}
async auth(signAuthEvent) {
const challenge = this.challenge;
if (!challenge)
throw new Error("can't perform auth, no challenge was received");
if (this.authPromise)
return this.authPromise;
this.authPromise = new Promise(async (resolve, reject) => {
try {
let evt = await signAuthEvent(makeAuthEvent(this.url, challenge));
let timeout = setTimeout(() => {
let ep = this.openEventPublishes.get(evt.id);
if (ep) {
ep.reject(new Error("auth timed out"));
this.openEventPublishes.delete(evt.id);
}
}, this.publishTimeout);
this.openEventPublishes.set(evt.id, { resolve, reject, timeout });
this.send('["AUTH",' + JSON.stringify(evt) + "]");
} catch (err) {
console.warn("subscribe auth function failed:", err);
}
});
return this.authPromise;
}
async publish(event) {
const ret = new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
const ep = this.openEventPublishes.get(event.id);
if (ep) {
ep.reject(new Error("publish timed out"));
this.openEventPublishes.delete(event.id);
}
}, this.publishTimeout);
this.openEventPublishes.set(event.id, { resolve, reject, timeout });
});
this.send('["EVENT",' + JSON.stringify(event) + "]");
return ret;
}
async count(filters, params) {
this.serial++;
const id = params?.id || "count:" + this.serial;
const ret = new Promise((resolve, reject) => {
this.openCountRequests.set(id, { resolve, reject });
});
this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1));
return ret;
}
subscribe(filters, params) {
const subscription = this.prepareSubscription(filters, params);
subscription.fire();
return subscription;
}
prepareSubscription(filters, params) {
this.serial++;
const id = params.id || (params.label ? params.label + ":" : "sub:") + this.serial;
const subscription = new Subscription(this, id, filters, params);
this.openSubs.set(id, subscription);
return subscription;
}
close() {
this.closeAllSubscriptions("relay connection closed by us");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
_onmessage(ev) {
this.incomingMessageQueue.enqueue(ev.data);
if (!this.queueRunning) {
this.runQueue();
}
}
};
var Subscription = class {
relay;
id;
closed = false;
eosed = false;
filters;
alreadyHaveEvent;
receivedEvent;
onevent;
oneose;
onclose;
eoseTimeout;
eoseTimeoutHandle;
constructor(relay, id, filters, params) {
this.relay = relay;
this.filters = filters;
this.id = id;
this.alreadyHaveEvent = params.alreadyHaveEvent;
this.receivedEvent = params.receivedEvent;
this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout;
this.oneose = params.oneose;
this.onclose = params.onclose;
this.onevent = params.onevent || ((event) => {
console.warn(
`onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,
event
);
});
}
fire() {
this.relay.send('["REQ","' + this.id + '",' + JSON.stringify(this.filters).substring(1));
this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout);
}
receivedEose() {
if (this.eosed)
return;
clearTimeout(this.eoseTimeoutHandle);
this.eosed = true;
this.oneose?.();
}
close(reason = "closed by caller") {
if (!this.closed && this.relay.connected) {
try {
this.relay.send('["CLOSE",' + JSON.stringify(this.id) + "]");
} catch (err) {
if (err instanceof SendingOnClosedConnection) {
} else {
throw err;
}
}
this.closed = true;
}
this.relay.openSubs.delete(this.id);
this.onclose?.(reason);
}
};
// abstract-pool.ts
var AbstractSimplePool = class {
relays = /* @__PURE__ */ new Map();
seenOn = /* @__PURE__ */ new Map();
trackRelays = false;
verifyEvent;
enablePing;
trustedRelayURLs = /* @__PURE__ */ new Set();
_WebSocket;
constructor(opts) {
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation;
this.enablePing = opts.enablePing;
}
async ensureRelay(url, params) {
url = normalizeURL(url);
let relay = this.relays.get(url);
if (!relay) {
relay = new AbstractRelay(url, {
verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,
websocketImplementation: this._WebSocket,
enablePing: this.enablePing
});
relay.onclose = () => {
this.relays.delete(url);
};
if (params?.connectionTimeout)
relay.connectionTimeout = params.connectionTimeout;
this.relays.set(url, relay);
}
await relay.connect();
return relay;
}
close(relays) {
relays.map(normalizeURL).forEach((url) => {
this.relays.get(url)?.close();
this.relays.delete(url);
});
}
subscribe(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const request = [];
for (let i = 0; i < relays.length; i++) {
const url = normalizeURL(relays[i]);
if (!request.find((r) => r.url === url)) {
request.push({ url, filter });
}
}
return this.subscribeMap(request, params);
}
subscribeMany(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const request = [];
const uniqUrls = [];
for (let i = 0; i < relays.length; i++) {
const url = normalizeURL(relays[i]);
if (uniqUrls.indexOf(url) === -1) {
uniqUrls.push(url);
request.push({ url, filter });
}
}
return this.subscribeMap(request, params);
}
subscribeMap(requests, params) {
params.onauth = params.onauth || params.doauth;
const grouped = /* @__PURE__ */ new Map();
for (const req of requests) {
const { url, filter } = req;
if (!grouped.has(url))
grouped.set(url, []);
grouped.get(url).push(filter);
}
const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }));
if (this.trackRelays) {
params.receivedEvent = (relay, id) => {
let set = this.seenOn.get(id);
if (!set) {
set = /* @__PURE__ */ new Set();
this.seenOn.set(id, set);
}
set.add(relay);
};
}
const _knownIds = /* @__PURE__ */ new Set();
const subs = [];
const eosesReceived = [];
let handleEose = (i) => {
if (eosesReceived[i])
return;
eosesReceived[i] = true;
if (eosesReceived.filter((a) => a).length === requests.length) {
params.oneose?.();
handleEose = () => {
};
}
};
const closesReceived = [];
let handleClose = (i, reason) => {
if (closesReceived[i])
return;
handleEose(i);
closesReceived[i] = reason;
if (closesReceived.filter((a) => a).length === requests.length) {
params.onclose?.(closesReceived);
handleClose = () => {
};
}
};
const localAlreadyHaveEventHandler = (id) => {
if (params.alreadyHaveEvent?.(id)) {
return true;
}
const have = _knownIds.has(id);
_knownIds.add(id);
return have;
};
const allOpened = Promise.all(
groupedRequests.map(async ({ url, filters }, i) => {
let relay;
try {
relay = await this.ensureRelay(url, {
connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1e3) : void 0
});
} catch (err) {
handleClose(i, err?.message || String(err));
return;
}
let subscription = relay.subscribe(filters, {
...params,
oneose: () => handleEose(i),
onclose: (reason) => {
if (reason.startsWith("auth-required: ") && params.onauth) {
relay.auth(params.onauth).then(() => {
relay.subscribe(filters, {
...params,
oneose: () => handleEose(i),
onclose: (reason2) => {
handleClose(i, reason2);
},
alreadyHaveEvent: localAlreadyHaveEventHandler,
eoseTimeout: params.maxWait
});
}).catch((err) => {
handleClose(i, `auth was required and attempted, but failed with: ${err}`);
});
} else {
handleClose(i, reason);
}
},
alreadyHaveEvent: localAlreadyHaveEventHandler,
eoseTimeout: params.maxWait
});
subs.push(subscription);
})
);
return {
async close(reason) {
await allOpened;
subs.forEach((sub) => {
sub.close(reason);
});
}
};
}
subscribeEose(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const subcloser = this.subscribe(relays, filter, {
...params,
oneose() {
subcloser.close("closed automatically on eose");
}
});
return subcloser;
}
subscribeManyEose(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const subcloser = this.subscribeMany(relays, filter, {
...params,
oneose() {
subcloser.close("closed automatically on eose");
}
});
return subcloser;
}
async querySync(relays, filter, params) {
return new Promise(async (resolve) => {
const events = [];
this.subscribeEose(relays, filter, {
...params,
onevent(event) {
events.push(event);
},
onclose(_) {
resolve(events);
}
});
});
}
async get(relays, filter, params) {
filter.limit = 1;
const events = await this.querySync(relays, filter, params);
events.sort((a, b) => b.created_at - a.created_at);
return events[0] || null;
}
publish(relays, event, options) {
return relays.map(normalizeURL).map(async (url, i, arr) => {
if (arr.indexOf(url) !== i) {
return Promise.reject("duplicate url");
}
let r = await this.ensureRelay(url);
return r.publish(event).catch(async (err) => {
if (err instanceof Error && err.message.startsWith("auth-required: ") && options?.onauth) {
await r.auth(options.onauth);
return r.publish(event);
}
throw err;
}).then((reason) => {
if (this.trackRelays) {
let set = this.seenOn.get(event.id);
if (!set) {
set = /* @__PURE__ */ new Set();
this.seenOn.set(event.id, set);
}
set.add(r);
}
return reason;
});
});
}
listConnectionStatus() {
const map = /* @__PURE__ */ new Map();
this.relays.forEach((relay, url) => map.set(url, relay.connected));
return map;
}
destroy() {
this.relays.forEach((conn) => conn.close());
this.relays = /* @__PURE__ */ new Map();
}
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,551 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// abstract-relay.ts
var abstract_relay_exports = {};
__export(abstract_relay_exports, {
AbstractRelay: () => AbstractRelay,
SendingOnClosedConnection: () => SendingOnClosedConnection,
Subscription: () => Subscription
});
module.exports = __toCommonJS(abstract_relay_exports);
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
function normalizeURL(url) {
try {
if (url.indexOf("://") === -1)
url = "wss://" + url;
let p = new URL(url);
p.pathname = p.pathname.replace(/\/+/g, "/");
if (p.pathname.endsWith("/"))
p.pathname = p.pathname.slice(0, -1);
if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:")
p.port = "";
p.searchParams.sort();
p.hash = "";
return p.toString();
} catch (e) {
throw new Error(`Invalid URL: ${url}`);
}
}
var QueueNode = class {
value;
next = null;
prev = null;
constructor(message) {
this.value = message;
}
};
var Queue = class {
first;
last;
constructor() {
this.first = null;
this.last = null;
}
enqueue(value) {
const newNode = new QueueNode(value);
if (!this.last) {
this.first = newNode;
this.last = newNode;
} else if (this.last === this.first) {
this.last = newNode;
this.last.prev = this.first;
this.first.next = newNode;
} else {
newNode.prev = this.last;
this.last.next = newNode;
this.last = newNode;
}
return true;
}
dequeue() {
if (!this.first)
return null;
if (this.first === this.last) {
const target2 = this.first;
this.first = null;
this.last = null;
return target2.value;
}
const target = this.first;
this.first = target.next;
if (this.first) {
this.first.prev = null;
}
return target.value;
}
};
// kinds.ts
var ClientAuth = 22242;
// filter.ts
function matchFilter(filter, event) {
if (filter.ids && filter.ids.indexOf(event.id) === -1) {
return false;
}
if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {
return false;
}
if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {
return false;
}
for (let f in filter) {
if (f[0] === "#") {
let tagName = f.slice(1);
let values = filter[`#${tagName}`];
if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values.indexOf(v) !== -1))
return false;
}
}
if (filter.since && event.created_at < filter.since)
return false;
if (filter.until && event.created_at > filter.until)
return false;
return true;
}
function matchFilters(filters, event) {
for (let i = 0; i < filters.length; i++) {
if (matchFilter(filters[i], event)) {
return true;
}
}
return false;
}
// fakejson.ts
function getHex64(json, field) {
let len = field.length + 3;
let idx = json.indexOf(`"${field}":`) + len;
let s = json.slice(idx).indexOf(`"`) + idx + 1;
return json.slice(s, s + 64);
}
function getSubscriptionId(json) {
let idx = json.slice(0, 22).indexOf(`"EVENT"`);
if (idx === -1)
return null;
let pstart = json.slice(idx + 7 + 1).indexOf(`"`);
if (pstart === -1)
return null;
let start = idx + 7 + 1 + pstart;
let pend = json.slice(start + 1, 80).indexOf(`"`);
if (pend === -1)
return null;
let end = start + 1 + pend;
return json.slice(start + 1, end);
}
// nip42.ts
function makeAuthEvent(relayURL, challenge) {
return {
kind: ClientAuth,
created_at: Math.floor(Date.now() / 1e3),
tags: [
["relay", relayURL],
["challenge", challenge]
],
content: ""
};
}
// helpers.ts
async function yieldThread() {
return new Promise((resolve) => {
const ch = new MessageChannel();
const handler = () => {
ch.port1.removeEventListener("message", handler);
resolve();
};
ch.port1.addEventListener("message", handler);
ch.port2.postMessage(0);
ch.port1.start();
});
}
// abstract-relay.ts
var SendingOnClosedConnection = class extends Error {
constructor(message, relay) {
super(`Tried to send message '${message} on a closed connection to ${relay}.`);
this.name = "SendingOnClosedConnection";
}
};
var AbstractRelay = class {
url;
_connected = false;
onclose = null;
onnotice = (msg) => console.debug(`NOTICE from ${this.url}: ${msg}`);
baseEoseTimeout = 4400;
connectionTimeout = 4400;
publishTimeout = 4400;
pingFrequency = 2e4;
pingTimeout = 2e4;
openSubs = /* @__PURE__ */ new Map();
enablePing;
connectionTimeoutHandle;
connectionPromise;
openCountRequests = /* @__PURE__ */ new Map();
openEventPublishes = /* @__PURE__ */ new Map();
ws;
incomingMessageQueue = new Queue();
queueRunning = false;
challenge;
authPromise;
serial = 0;
verifyEvent;
_WebSocket;
constructor(url, opts) {
this.url = normalizeURL(url);
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation || WebSocket;
this.enablePing = opts.enablePing;
}
static async connect(url, opts) {
const relay = new AbstractRelay(url, opts);
await relay.connect();
return relay;
}
closeAllSubscriptions(reason) {
for (let [_, sub] of this.openSubs) {
sub.close(reason);
}
this.openSubs.clear();
for (let [_, ep] of this.openEventPublishes) {
ep.reject(new Error(reason));
}
this.openEventPublishes.clear();
for (let [_, cr] of this.openCountRequests) {
cr.reject(new Error(reason));
}
this.openCountRequests.clear();
}
get connected() {
return this._connected;
}
async connect() {
if (this.connectionPromise)
return this.connectionPromise;
this.challenge = void 0;
this.authPromise = void 0;
this.connectionPromise = new Promise((resolve, reject) => {
this.connectionTimeoutHandle = setTimeout(() => {
reject("connection timed out");
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection timed out");
}, this.connectionTimeout);
try {
this.ws = new this._WebSocket(this.url);
} catch (err) {
clearTimeout(this.connectionTimeoutHandle);
reject(err);
return;
}
this.ws.onopen = () => {
clearTimeout(this.connectionTimeoutHandle);
this._connected = true;
if (this.enablePing) {
this.pingpong();
}
resolve();
};
this.ws.onerror = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket error");
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
};
this.ws.onclose = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket closed");
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
};
this.ws.onmessage = this._onmessage.bind(this);
});
return this.connectionPromise;
}
async waitForPingPong() {
return new Promise((res, err) => {
;
this.ws && this.ws.on && this.ws.on("pong", () => res(true)) || err("ws can't listen for pong");
this.ws && this.ws.ping && this.ws.ping();
});
}
async waitForDummyReq() {
return new Promise((resolve, _) => {
const sub = this.subscribe([{ ids: ["a".repeat(64)] }], {
oneose: () => {
sub.close();
resolve(true);
},
eoseTimeout: this.pingTimeout + 1e3
});
});
}
async pingpong() {
if (this.ws?.readyState === 1) {
const result = await Promise.any([
this.ws && this.ws.ping && this.ws.on ? this.waitForPingPong() : this.waitForDummyReq(),
new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
]);
if (result) {
setTimeout(() => this.pingpong(), this.pingFrequency);
} else {
this.closeAllSubscriptions("pingpong timed out");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
}
}
async runQueue() {
this.queueRunning = true;
while (true) {
if (false === this.handleNext()) {
break;
}
await yieldThread();
}
this.queueRunning = false;
}
handleNext() {
const json = this.incomingMessageQueue.dequeue();
if (!json) {
return false;
}
const subid = getSubscriptionId(json);
if (subid) {
const so = this.openSubs.get(subid);
if (!so) {
return;
}
const id = getHex64(json, "id");
const alreadyHave = so.alreadyHaveEvent?.(id);
so.receivedEvent?.(this, id);
if (alreadyHave) {
return;
}
}
try {
let data = JSON.parse(json);
switch (data[0]) {
case "EVENT": {
const so = this.openSubs.get(data[1]);
const event = data[2];
if (this.verifyEvent(event) && matchFilters(so.filters, event)) {
so.onevent(event);
}
return;
}
case "COUNT": {
const id = data[1];
const payload = data[2];
const cr = this.openCountRequests.get(id);
if (cr) {
cr.resolve(payload.count);
this.openCountRequests.delete(id);
}
return;
}
case "EOSE": {
const so = this.openSubs.get(data[1]);
if (!so)
return;
so.receivedEose();
return;
}
case "OK": {
const id = data[1];
const ok = data[2];
const reason = data[3];
const ep = this.openEventPublishes.get(id);
if (ep) {
clearTimeout(ep.timeout);
if (ok)
ep.resolve(reason);
else
ep.reject(new Error(reason));
this.openEventPublishes.delete(id);
}
return;
}
case "CLOSED": {
const id = data[1];
const so = this.openSubs.get(id);
if (!so)
return;
so.closed = true;
so.close(data[2]);
return;
}
case "NOTICE":
this.onnotice(data[1]);
return;
case "AUTH": {
this.challenge = data[1];
return;
}
}
} catch (err) {
return;
}
}
async send(message) {
if (!this.connectionPromise)
throw new SendingOnClosedConnection(message, this.url);
this.connectionPromise.then(() => {
this.ws?.send(message);
});
}
async auth(signAuthEvent) {
const challenge = this.challenge;
if (!challenge)
throw new Error("can't perform auth, no challenge was received");
if (this.authPromise)
return this.authPromise;
this.authPromise = new Promise(async (resolve, reject) => {
try {
let evt = await signAuthEvent(makeAuthEvent(this.url, challenge));
let timeout = setTimeout(() => {
let ep = this.openEventPublishes.get(evt.id);
if (ep) {
ep.reject(new Error("auth timed out"));
this.openEventPublishes.delete(evt.id);
}
}, this.publishTimeout);
this.openEventPublishes.set(evt.id, { resolve, reject, timeout });
this.send('["AUTH",' + JSON.stringify(evt) + "]");
} catch (err) {
console.warn("subscribe auth function failed:", err);
}
});
return this.authPromise;
}
async publish(event) {
const ret = new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
const ep = this.openEventPublishes.get(event.id);
if (ep) {
ep.reject(new Error("publish timed out"));
this.openEventPublishes.delete(event.id);
}
}, this.publishTimeout);
this.openEventPublishes.set(event.id, { resolve, reject, timeout });
});
this.send('["EVENT",' + JSON.stringify(event) + "]");
return ret;
}
async count(filters, params) {
this.serial++;
const id = params?.id || "count:" + this.serial;
const ret = new Promise((resolve, reject) => {
this.openCountRequests.set(id, { resolve, reject });
});
this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1));
return ret;
}
subscribe(filters, params) {
const subscription = this.prepareSubscription(filters, params);
subscription.fire();
return subscription;
}
prepareSubscription(filters, params) {
this.serial++;
const id = params.id || (params.label ? params.label + ":" : "sub:") + this.serial;
const subscription = new Subscription(this, id, filters, params);
this.openSubs.set(id, subscription);
return subscription;
}
close() {
this.closeAllSubscriptions("relay connection closed by us");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
_onmessage(ev) {
this.incomingMessageQueue.enqueue(ev.data);
if (!this.queueRunning) {
this.runQueue();
}
}
};
var Subscription = class {
relay;
id;
closed = false;
eosed = false;
filters;
alreadyHaveEvent;
receivedEvent;
onevent;
oneose;
onclose;
eoseTimeout;
eoseTimeoutHandle;
constructor(relay, id, filters, params) {
this.relay = relay;
this.filters = filters;
this.id = id;
this.alreadyHaveEvent = params.alreadyHaveEvent;
this.receivedEvent = params.receivedEvent;
this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout;
this.oneose = params.oneose;
this.onclose = params.onclose;
this.onevent = params.onevent || ((event) => {
console.warn(
`onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,
event
);
});
}
fire() {
this.relay.send('["REQ","' + this.id + '",' + JSON.stringify(this.filters).substring(1));
this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout);
}
receivedEose() {
if (this.eosed)
return;
clearTimeout(this.eoseTimeoutHandle);
this.eosed = true;
this.oneose?.();
}
close(reason = "closed by caller") {
if (!this.closed && this.relay.connected) {
try {
this.relay.send('["CLOSE",' + JSON.stringify(this.id) + "]");
} catch (err) {
if (err instanceof SendingOnClosedConnection) {
} else {
throw err;
}
}
this.closed = true;
}
this.relay.openSubs.delete(this.id);
this.onclose?.(reason);
}
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,66 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// fakejson.ts
var fakejson_exports = {};
__export(fakejson_exports, {
getHex64: () => getHex64,
getInt: () => getInt,
getSubscriptionId: () => getSubscriptionId,
matchEventId: () => matchEventId,
matchEventKind: () => matchEventKind,
matchEventPubkey: () => matchEventPubkey
});
module.exports = __toCommonJS(fakejson_exports);
function getHex64(json, field) {
let len = field.length + 3;
let idx = json.indexOf(`"${field}":`) + len;
let s = json.slice(idx).indexOf(`"`) + idx + 1;
return json.slice(s, s + 64);
}
function getInt(json, field) {
let len = field.length;
let idx = json.indexOf(`"${field}":`) + len + 3;
let sliced = json.slice(idx);
let end = Math.min(sliced.indexOf(","), sliced.indexOf("}"));
return parseInt(sliced.slice(0, end), 10);
}
function getSubscriptionId(json) {
let idx = json.slice(0, 22).indexOf(`"EVENT"`);
if (idx === -1)
return null;
let pstart = json.slice(idx + 7 + 1).indexOf(`"`);
if (pstart === -1)
return null;
let start = idx + 7 + 1 + pstart;
let pend = json.slice(start + 1, 80).indexOf(`"`);
if (pend === -1)
return null;
let end = start + 1 + pend;
return json.slice(start + 1, end);
}
function matchEventId(json, id) {
return id === getHex64(json, "id");
}
function matchEventPubkey(json, pubkey) {
return pubkey === getHex64(json, "pubkey");
}
function matchEventKind(json, kind) {
return kind === getInt(json, "kind");
}

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../fakejson.ts"],
"sourcesContent": ["export function getHex64(json: string, field: string): string {\n let len = field.length + 3\n let idx = json.indexOf(`\"${field}\":`) + len\n let s = json.slice(idx).indexOf(`\"`) + idx + 1\n return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n let len = field.length\n let idx = json.indexOf(`\"${field}\":`) + len + 3\n let sliced = json.slice(idx)\n let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n if (idx === -1) return null\n\n let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n if (pstart === -1) return null\n let start = idx + 7 + 1 + pstart\n\n let pend = json.slice(start + 1, 80).indexOf(`\"`)\n if (pend === -1) return null\n let end = start + 1 + pend\n\n return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n return kind === getInt(json, 'kind')\n}\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,SAAS,SAAS,MAAc,OAAuB;AAC5D,MAAI,MAAM,MAAM,SAAS;AACzB,MAAI,MAAM,KAAK,QAAQ,IAAI,SAAS,IAAI;AACxC,MAAI,IAAI,KAAK,MAAM,GAAG,EAAE,QAAQ,GAAG,IAAI,MAAM;AAC7C,SAAO,KAAK,MAAM,GAAG,IAAI,EAAE;AAC7B;AAEO,SAAS,OAAO,MAAc,OAAuB;AAC1D,MAAI,MAAM,MAAM;AAChB,MAAI,MAAM,KAAK,QAAQ,IAAI,SAAS,IAAI,MAAM;AAC9C,MAAI,SAAS,KAAK,MAAM,GAAG;AAC3B,MAAI,MAAM,KAAK,IAAI,OAAO,QAAQ,GAAG,GAAG,OAAO,QAAQ,GAAG,CAAC;AAC3D,SAAO,SAAS,OAAO,MAAM,GAAG,GAAG,GAAG,EAAE;AAC1C;AAEO,SAAS,kBAAkB,MAA6B;AAC7D,MAAI,MAAM,KAAK,MAAM,GAAG,EAAE,EAAE,QAAQ,SAAS;AAC7C,MAAI,QAAQ;AAAI,WAAO;AAEvB,MAAI,SAAS,KAAK,MAAM,MAAM,IAAI,CAAC,EAAE,QAAQ,GAAG;AAChD,MAAI,WAAW;AAAI,WAAO;AAC1B,MAAI,QAAQ,MAAM,IAAI,IAAI;AAE1B,MAAI,OAAO,KAAK,MAAM,QAAQ,GAAG,EAAE,EAAE,QAAQ,GAAG;AAChD,MAAI,SAAS;AAAI,WAAO;AACxB,MAAI,MAAM,QAAQ,IAAI;AAEtB,SAAO,KAAK,MAAM,QAAQ,GAAG,GAAG;AAClC;AAEO,SAAS,aAAa,MAAc,IAAqB;AAC9D,SAAO,OAAO,SAAS,MAAM,IAAI;AACnC;AAEO,SAAS,iBAAiB,MAAc,QAAyB;AACtE,SAAO,WAAW,SAAS,MAAM,QAAQ;AAC3C;AAEO,SAAS,eAAe,MAAc,MAAuB;AAClE,SAAO,SAAS,OAAO,MAAM,MAAM;AACrC;",
"names": []
}

View File

@@ -0,0 +1,111 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// filter.ts
var filter_exports = {};
__export(filter_exports, {
getFilterLimit: () => getFilterLimit,
matchFilter: () => matchFilter,
matchFilters: () => matchFilters,
mergeFilters: () => mergeFilters
});
module.exports = __toCommonJS(filter_exports);
// kinds.ts
function isReplaceableKind(kind) {
return [0, 3].includes(kind) || 1e4 <= kind && kind < 2e4;
}
function isAddressableKind(kind) {
return 3e4 <= kind && kind < 4e4;
}
// filter.ts
function matchFilter(filter, event) {
if (filter.ids && filter.ids.indexOf(event.id) === -1) {
return false;
}
if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {
return false;
}
if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {
return false;
}
for (let f in filter) {
if (f[0] === "#") {
let tagName = f.slice(1);
let values = filter[`#${tagName}`];
if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values.indexOf(v) !== -1))
return false;
}
}
if (filter.since && event.created_at < filter.since)
return false;
if (filter.until && event.created_at > filter.until)
return false;
return true;
}
function matchFilters(filters, event) {
for (let i = 0; i < filters.length; i++) {
if (matchFilter(filters[i], event)) {
return true;
}
}
return false;
}
function mergeFilters(...filters) {
let result = {};
for (let i = 0; i < filters.length; i++) {
let filter = filters[i];
Object.entries(filter).forEach(([property, values]) => {
if (property === "kinds" || property === "ids" || property === "authors" || property[0] === "#") {
result[property] = result[property] || [];
for (let v = 0; v < values.length; v++) {
let value = values[v];
if (!result[property].includes(value))
result[property].push(value);
}
}
});
if (filter.limit && (!result.limit || filter.limit > result.limit))
result.limit = filter.limit;
if (filter.until && (!result.until || filter.until > result.until))
result.until = filter.until;
if (filter.since && (!result.since || filter.since < result.since))
result.since = filter.since;
}
return result;
}
function getFilterLimit(filter) {
if (filter.ids && !filter.ids.length)
return 0;
if (filter.kinds && !filter.kinds.length)
return 0;
if (filter.authors && !filter.authors.length)
return 0;
for (const [key, value] of Object.entries(filter)) {
if (key[0] === "#" && Array.isArray(value) && !value.length)
return 0;
}
return Math.min(
Math.max(0, filter.limit ?? Infinity),
filter.ids?.length ?? Infinity,
filter.authors?.length && filter.kinds?.every((kind) => isReplaceableKind(kind)) ? filter.authors.length * filter.kinds.length : Infinity,
filter.authors?.length && filter.kinds?.every((kind) => isAddressableKind(kind)) && filter["#d"]?.length ? filter.authors.length * filter.kinds.length * filter["#d"].length : Infinity
);
}

File diff suppressed because one or more lines are too long

2783
thrower_daemon/node_modules/nostr-tools/lib/cjs/index.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,243 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// kinds.ts
var kinds_exports = {};
__export(kinds_exports, {
Application: () => Application,
BadgeAward: () => BadgeAward,
BadgeDefinition: () => BadgeDefinition,
BlockedRelaysList: () => BlockedRelaysList,
BookmarkList: () => BookmarkList,
Bookmarksets: () => Bookmarksets,
Calendar: () => Calendar,
CalendarEventRSVP: () => CalendarEventRSVP,
ChannelCreation: () => ChannelCreation,
ChannelHideMessage: () => ChannelHideMessage,
ChannelMessage: () => ChannelMessage,
ChannelMetadata: () => ChannelMetadata,
ChannelMuteUser: () => ChannelMuteUser,
ClassifiedListing: () => ClassifiedListing,
ClientAuth: () => ClientAuth,
CommunitiesList: () => CommunitiesList,
CommunityDefinition: () => CommunityDefinition,
CommunityPostApproval: () => CommunityPostApproval,
Contacts: () => Contacts,
CreateOrUpdateProduct: () => CreateOrUpdateProduct,
CreateOrUpdateStall: () => CreateOrUpdateStall,
Curationsets: () => Curationsets,
Date: () => Date,
DirectMessageRelaysList: () => DirectMessageRelaysList,
DraftClassifiedListing: () => DraftClassifiedListing,
DraftLong: () => DraftLong,
Emojisets: () => Emojisets,
EncryptedDirectMessage: () => EncryptedDirectMessage,
EventDeletion: () => EventDeletion,
FileMetadata: () => FileMetadata,
FileServerPreference: () => FileServerPreference,
Followsets: () => Followsets,
GenericRepost: () => GenericRepost,
Genericlists: () => Genericlists,
GiftWrap: () => GiftWrap,
HTTPAuth: () => HTTPAuth,
Handlerinformation: () => Handlerinformation,
Handlerrecommendation: () => Handlerrecommendation,
Highlights: () => Highlights,
InterestsList: () => InterestsList,
Interestsets: () => Interestsets,
JobFeedback: () => JobFeedback,
JobRequest: () => JobRequest,
JobResult: () => JobResult,
Label: () => Label,
LightningPubRPC: () => LightningPubRPC,
LiveChatMessage: () => LiveChatMessage,
LiveEvent: () => LiveEvent,
LongFormArticle: () => LongFormArticle,
Metadata: () => Metadata,
Mutelist: () => Mutelist,
NWCWalletInfo: () => NWCWalletInfo,
NWCWalletRequest: () => NWCWalletRequest,
NWCWalletResponse: () => NWCWalletResponse,
NostrConnect: () => NostrConnect,
OpenTimestamps: () => OpenTimestamps,
Pinlist: () => Pinlist,
PrivateDirectMessage: () => PrivateDirectMessage,
ProblemTracker: () => ProblemTracker,
ProfileBadges: () => ProfileBadges,
PublicChatsList: () => PublicChatsList,
Reaction: () => Reaction,
RecommendRelay: () => RecommendRelay,
RelayList: () => RelayList,
Relaysets: () => Relaysets,
Report: () => Report,
Reporting: () => Reporting,
Repost: () => Repost,
Seal: () => Seal,
SearchRelaysList: () => SearchRelaysList,
ShortTextNote: () => ShortTextNote,
Time: () => Time,
UserEmojiList: () => UserEmojiList,
UserStatuses: () => UserStatuses,
Zap: () => Zap,
ZapGoal: () => ZapGoal,
ZapRequest: () => ZapRequest,
classifyKind: () => classifyKind,
isAddressableKind: () => isAddressableKind,
isEphemeralKind: () => isEphemeralKind,
isKind: () => isKind,
isRegularKind: () => isRegularKind,
isReplaceableKind: () => isReplaceableKind
});
module.exports = __toCommonJS(kinds_exports);
// core.ts
var verifiedSymbol = Symbol("verified");
var isRecord = (obj) => obj instanceof Object;
function validateEvent(event) {
if (!isRecord(event))
return false;
if (typeof event.kind !== "number")
return false;
if (typeof event.content !== "string")
return false;
if (typeof event.created_at !== "number")
return false;
if (typeof event.pubkey !== "string")
return false;
if (!event.pubkey.match(/^[a-f0-9]{64}$/))
return false;
if (!Array.isArray(event.tags))
return false;
for (let i = 0; i < event.tags.length; i++) {
let tag = event.tags[i];
if (!Array.isArray(tag))
return false;
for (let j = 0; j < tag.length; j++) {
if (typeof tag[j] !== "string")
return false;
}
}
return true;
}
// kinds.ts
function isRegularKind(kind) {
return 1e3 <= kind && kind < 1e4 || [1, 2, 4, 5, 6, 7, 8, 16, 40, 41, 42, 43, 44].includes(kind);
}
function isReplaceableKind(kind) {
return [0, 3].includes(kind) || 1e4 <= kind && kind < 2e4;
}
function isEphemeralKind(kind) {
return 2e4 <= kind && kind < 3e4;
}
function isAddressableKind(kind) {
return 3e4 <= kind && kind < 4e4;
}
function classifyKind(kind) {
if (isRegularKind(kind))
return "regular";
if (isReplaceableKind(kind))
return "replaceable";
if (isEphemeralKind(kind))
return "ephemeral";
if (isAddressableKind(kind))
return "parameterized";
return "unknown";
}
function isKind(event, kind) {
const kindAsArray = kind instanceof Array ? kind : [kind];
return validateEvent(event) && kindAsArray.includes(event.kind) || false;
}
var Metadata = 0;
var ShortTextNote = 1;
var RecommendRelay = 2;
var Contacts = 3;
var EncryptedDirectMessage = 4;
var EventDeletion = 5;
var Repost = 6;
var Reaction = 7;
var BadgeAward = 8;
var Seal = 13;
var PrivateDirectMessage = 14;
var GenericRepost = 16;
var ChannelCreation = 40;
var ChannelMetadata = 41;
var ChannelMessage = 42;
var ChannelHideMessage = 43;
var ChannelMuteUser = 44;
var OpenTimestamps = 1040;
var GiftWrap = 1059;
var FileMetadata = 1063;
var LiveChatMessage = 1311;
var ProblemTracker = 1971;
var Report = 1984;
var Reporting = 1984;
var Label = 1985;
var CommunityPostApproval = 4550;
var JobRequest = 5999;
var JobResult = 6999;
var JobFeedback = 7e3;
var ZapGoal = 9041;
var ZapRequest = 9734;
var Zap = 9735;
var Highlights = 9802;
var Mutelist = 1e4;
var Pinlist = 10001;
var RelayList = 10002;
var BookmarkList = 10003;
var CommunitiesList = 10004;
var PublicChatsList = 10005;
var BlockedRelaysList = 10006;
var SearchRelaysList = 10007;
var InterestsList = 10015;
var UserEmojiList = 10030;
var DirectMessageRelaysList = 10050;
var FileServerPreference = 10096;
var NWCWalletInfo = 13194;
var LightningPubRPC = 21e3;
var ClientAuth = 22242;
var NWCWalletRequest = 23194;
var NWCWalletResponse = 23195;
var NostrConnect = 24133;
var HTTPAuth = 27235;
var Followsets = 3e4;
var Genericlists = 30001;
var Relaysets = 30002;
var Bookmarksets = 30003;
var Curationsets = 30004;
var ProfileBadges = 30008;
var BadgeDefinition = 30009;
var Interestsets = 30015;
var CreateOrUpdateStall = 30017;
var CreateOrUpdateProduct = 30018;
var LongFormArticle = 30023;
var DraftLong = 30024;
var Emojisets = 30030;
var Application = 30078;
var LiveEvent = 30311;
var UserStatuses = 30315;
var ClassifiedListing = 30402;
var DraftClassifiedListing = 30403;
var Date = 31922;
var Time = 31923;
var Calendar = 31924;
var CalendarEventRSVP = 31925;
var Handlerrecommendation = 31989;
var Handlerinformation = 31990;
var CommunityDefinition = 34550;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,61 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip04.ts
var nip04_exports = {};
__export(nip04_exports, {
decrypt: () => decrypt,
encrypt: () => encrypt
});
module.exports = __toCommonJS(nip04_exports);
var import_utils2 = require("@noble/hashes/utils");
var import_secp256k1 = require("@noble/curves/secp256k1");
var import_aes = require("@noble/ciphers/aes");
var import_base = require("@scure/base");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// nip04.ts
function encrypt(secretKey, pubkey, text) {
const privkey = secretKey instanceof Uint8Array ? (0, import_utils2.bytesToHex)(secretKey) : secretKey;
const key = import_secp256k1.secp256k1.getSharedSecret(privkey, "02" + pubkey);
const normalizedKey = getNormalizedX(key);
let iv = Uint8Array.from((0, import_utils2.randomBytes)(16));
let plaintext = utf8Encoder.encode(text);
let ciphertext = (0, import_aes.cbc)(normalizedKey, iv).encrypt(plaintext);
let ctb64 = import_base.base64.encode(new Uint8Array(ciphertext));
let ivb64 = import_base.base64.encode(new Uint8Array(iv.buffer));
return `${ctb64}?iv=${ivb64}`;
}
function decrypt(secretKey, pubkey, data) {
const privkey = secretKey instanceof Uint8Array ? (0, import_utils2.bytesToHex)(secretKey) : secretKey;
let [ctb64, ivb64] = data.split("?iv=");
let key = import_secp256k1.secp256k1.getSharedSecret(privkey, "02" + pubkey);
let normalizedKey = getNormalizedX(key);
let iv = import_base.base64.decode(ivb64);
let ciphertext = import_base.base64.decode(ctb64);
let plaintext = (0, import_aes.cbc)(normalizedKey, iv).decrypt(ciphertext);
return utf8Decoder.decode(plaintext);
}
function getNormalizedX(key) {
return key.slice(1, 33);
}

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../nip04.ts", "../../utils.ts"],
"sourcesContent": ["import { bytesToHex, randomBytes } from '@noble/hashes/utils'\nimport { secp256k1 } from '@noble/curves/secp256k1'\nimport { cbc } from '@noble/ciphers/aes'\nimport { base64 } from '@scure/base'\n\nimport { utf8Decoder, utf8Encoder } from './utils.ts'\n\nexport function encrypt(secretKey: string | Uint8Array, pubkey: string, text: string): string {\n const privkey: string = secretKey instanceof Uint8Array ? bytesToHex(secretKey) : secretKey\n const key = secp256k1.getSharedSecret(privkey, '02' + pubkey)\n const normalizedKey = getNormalizedX(key)\n\n let iv = Uint8Array.from(randomBytes(16))\n let plaintext = utf8Encoder.encode(text)\n\n let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext)\n\n let ctb64 = base64.encode(new Uint8Array(ciphertext))\n let ivb64 = base64.encode(new Uint8Array(iv.buffer))\n\n return `${ctb64}?iv=${ivb64}`\n}\n\nexport function decrypt(secretKey: string | Uint8Array, pubkey: string, data: string): string {\n const privkey: string = secretKey instanceof Uint8Array ? bytesToHex(secretKey) : secretKey\n let [ctb64, ivb64] = data.split('?iv=')\n let key = secp256k1.getSharedSecret(privkey, '02' + pubkey)\n let normalizedKey = getNormalizedX(key)\n\n let iv = base64.decode(ivb64)\n let ciphertext = base64.decode(ctb64)\n\n let plaintext = cbc(normalizedKey, iv).decrypt(ciphertext)\n\n return utf8Decoder.decode(plaintext)\n}\n\nfunction getNormalizedX(key: Uint8Array): Uint8Array {\n return key.slice(1, 33)\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport class QueueNode<V> {\n public value: V\n public next: QueueNode<V> | null = null\n public prev: QueueNode<V> | null = null\n\n constructor(message: V) {\n this.value = message\n }\n}\n\nexport class Queue<V> {\n public first: QueueNode<V> | null\n public last: QueueNode<V> | null\n\n constructor() {\n this.first = null\n this.last = null\n }\n\n enqueue(value: V): boolean {\n const newNode = new QueueNode(value)\n if (!this.last) {\n // list is empty\n this.first = newNode\n this.last = newNode\n } else if (this.last === this.first) {\n // list has a single element\n this.last = newNode\n this.last.prev = this.first\n this.first.next = newNode\n } else {\n // list has elements, add as last\n newNode.prev = this.last\n this.last.next = newNode\n this.last = newNode\n }\n return true\n }\n\n dequeue(): V | null {\n if (!this.first) return null\n\n if (this.first === this.last) {\n const target = this.first\n this.first = null\n this.last = null\n return target.value\n }\n\n const target = this.first\n this.first = target.next\n if (this.first) {\n this.first.prev = null // fix: clean up prev pointer\n }\n\n return target.value\n }\n}\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,gBAAwC;AACxC,uBAA0B;AAC1B,iBAAoB;AACpB,kBAAuB;;;ACEvB,mBAAuC;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;ADIjD,SAAS,QAAQ,WAAgC,QAAgB,MAAsB;AAC5F,QAAM,UAAkB,qBAAqB,iBAAa,0BAAW,SAAS,IAAI;AAClF,QAAM,MAAM,2BAAU,gBAAgB,SAAS,OAAO,MAAM;AAC5D,QAAM,gBAAgB,eAAe,GAAG;AAExC,MAAI,KAAK,WAAW,SAAK,2BAAY,EAAE,CAAC;AACxC,MAAI,YAAY,YAAY,OAAO,IAAI;AAEvC,MAAI,iBAAa,gBAAI,eAAe,EAAE,EAAE,QAAQ,SAAS;AAEzD,MAAI,QAAQ,mBAAO,OAAO,IAAI,WAAW,UAAU,CAAC;AACpD,MAAI,QAAQ,mBAAO,OAAO,IAAI,WAAW,GAAG,MAAM,CAAC;AAEnD,SAAO,GAAG,YAAY;AACxB;AAEO,SAAS,QAAQ,WAAgC,QAAgB,MAAsB;AAC5F,QAAM,UAAkB,qBAAqB,iBAAa,0BAAW,SAAS,IAAI;AAClF,MAAI,CAAC,OAAO,KAAK,IAAI,KAAK,MAAM,MAAM;AACtC,MAAI,MAAM,2BAAU,gBAAgB,SAAS,OAAO,MAAM;AAC1D,MAAI,gBAAgB,eAAe,GAAG;AAEtC,MAAI,KAAK,mBAAO,OAAO,KAAK;AAC5B,MAAI,aAAa,mBAAO,OAAO,KAAK;AAEpC,MAAI,gBAAY,gBAAI,eAAe,EAAE,EAAE,QAAQ,UAAU;AAEzD,SAAO,YAAY,OAAO,SAAS;AACrC;AAEA,SAAS,eAAe,KAA6B;AACnD,SAAO,IAAI,MAAM,GAAG,EAAE;AACxB;",
"names": ["import_utils"]
}

View File

@@ -0,0 +1,76 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip05.ts
var nip05_exports = {};
__export(nip05_exports, {
NIP05_REGEX: () => NIP05_REGEX,
isNip05: () => isNip05,
isValid: () => isValid,
queryProfile: () => queryProfile,
searchDomain: () => searchDomain,
useFetchImplementation: () => useFetchImplementation
});
module.exports = __toCommonJS(nip05_exports);
var NIP05_REGEX = /^(?:([\w.+-]+)@)?([\w_-]+(\.[\w_-]+)+)$/;
var isNip05 = (value) => NIP05_REGEX.test(value || "");
var _fetch;
try {
_fetch = fetch;
} catch (_) {
null;
}
function useFetchImplementation(fetchImplementation) {
_fetch = fetchImplementation;
}
async function searchDomain(domain, query = "") {
try {
const url = `https://${domain}/.well-known/nostr.json?name=${query}`;
const res = await _fetch(url, { redirect: "manual" });
if (res.status !== 200) {
throw Error("Wrong response code");
}
const json = await res.json();
return json.names;
} catch (_) {
return {};
}
}
async function queryProfile(fullname) {
const match = fullname.match(NIP05_REGEX);
if (!match)
return null;
const [, name = "_", domain] = match;
try {
const url = `https://${domain}/.well-known/nostr.json?name=${name}`;
const res = await _fetch(url, { redirect: "manual" });
if (res.status !== 200) {
throw Error("Wrong response code");
}
const json = await res.json();
const pubkey = json.names[name];
return pubkey ? { pubkey, relays: json.relays?.[pubkey] } : null;
} catch (_e) {
return null;
}
}
async function isValid(pubkey, nip05) {
const res = await queryProfile(nip05);
return res ? res.pubkey === pubkey : false;
}

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../nip05.ts"],
"sourcesContent": ["import { ProfilePointer } from './nip19.ts'\n\nexport type Nip05 = `${string}@${string}`\n\n/**\n * NIP-05 regex. The localpart is optional, and should be assumed to be `_` otherwise.\n *\n * - 0: full match\n * - 1: name (optional)\n * - 2: domain\n */\nexport const NIP05_REGEX = /^(?:([\\w.+-]+)@)?([\\w_-]+(\\.[\\w_-]+)+)$/\nexport const isNip05 = (value?: string | null): value is Nip05 => NIP05_REGEX.test(value || '')\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet _fetch: any\n\ntry {\n _fetch = fetch\n} catch (_) {\n null\n}\n\nexport function useFetchImplementation(fetchImplementation: unknown) {\n _fetch = fetchImplementation\n}\n\nexport async function searchDomain(domain: string, query = ''): Promise<{ [name: string]: string }> {\n try {\n const url = `https://${domain}/.well-known/nostr.json?name=${query}`\n const res = await _fetch(url, { redirect: 'manual' })\n if (res.status !== 200) {\n throw Error('Wrong response code')\n }\n const json = await res.json()\n return json.names\n } catch (_) {\n return {}\n }\n}\n\nexport async function queryProfile(fullname: string): Promise<ProfilePointer | null> {\n const match = fullname.match(NIP05_REGEX)\n if (!match) return null\n\n const [, name = '_', domain] = match\n\n try {\n const url = `https://${domain}/.well-known/nostr.json?name=${name}`\n const res = await _fetch(url, { redirect: 'manual' })\n if (res.status !== 200) {\n throw Error('Wrong response code')\n }\n const json = await res.json()\n\n const pubkey = json.names[name]\n return pubkey ? { pubkey, relays: json.relays?.[pubkey] } : null\n } catch (_e) {\n return null\n }\n}\n\nexport async function isValid(pubkey: string, nip05: Nip05): Promise<boolean> {\n const res = await queryProfile(nip05)\n return res ? res.pubkey === pubkey : false\n}\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWO,IAAM,cAAc;AACpB,IAAM,UAAU,CAAC,UAA0C,YAAY,KAAK,SAAS,EAAE;AAG9F,IAAI;AAEJ,IAAI;AACF,WAAS;AACX,SAAS,GAAP;AACA;AACF;AAEO,SAAS,uBAAuB,qBAA8B;AACnE,WAAS;AACX;AAEA,eAAsB,aAAa,QAAgB,QAAQ,IAAyC;AAClG,MAAI;AACF,UAAM,MAAM,WAAW,sCAAsC;AAC7D,UAAM,MAAM,MAAM,OAAO,KAAK,EAAE,UAAU,SAAS,CAAC;AACpD,QAAI,IAAI,WAAW,KAAK;AACtB,YAAM,MAAM,qBAAqB;AAAA,IACnC;AACA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,WAAO,KAAK;AAAA,EACd,SAAS,GAAP;AACA,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAsB,aAAa,UAAkD;AACnF,QAAM,QAAQ,SAAS,MAAM,WAAW;AACxC,MAAI,CAAC;AAAO,WAAO;AAEnB,QAAM,CAAC,EAAE,OAAO,KAAK,MAAM,IAAI;AAE/B,MAAI;AACF,UAAM,MAAM,WAAW,sCAAsC;AAC7D,UAAM,MAAM,MAAM,OAAO,KAAK,EAAE,UAAU,SAAS,CAAC;AACpD,QAAI,IAAI,WAAW,KAAK;AACtB,YAAM,MAAM,qBAAqB;AAAA,IACnC;AACA,UAAM,OAAO,MAAM,IAAI,KAAK;AAE5B,UAAM,SAAS,KAAK,MAAM;AAC1B,WAAO,SAAS,EAAE,QAAQ,QAAQ,KAAK,SAAS,QAAQ,IAAI;AAAA,EAC9D,SAAS,IAAP;AACA,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,QAAQ,QAAgB,OAAgC;AAC5E,QAAM,MAAM,MAAM,aAAa,KAAK;AACpC,SAAO,MAAM,IAAI,WAAW,SAAS;AACvC;",
"names": []
}

View File

@@ -0,0 +1,82 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip06.ts
var nip06_exports = {};
__export(nip06_exports, {
accountFromExtendedKey: () => accountFromExtendedKey,
accountFromSeedWords: () => accountFromSeedWords,
extendedKeysFromSeedWords: () => extendedKeysFromSeedWords,
generateSeedWords: () => generateSeedWords,
privateKeyFromSeedWords: () => privateKeyFromSeedWords,
validateWords: () => validateWords
});
module.exports = __toCommonJS(nip06_exports);
var import_utils = require("@noble/hashes/utils");
var import_english = require("@scure/bip39/wordlists/english");
var import_bip39 = require("@scure/bip39");
var import_bip32 = require("@scure/bip32");
var DERIVATION_PATH = `m/44'/1237'`;
function privateKeyFromSeedWords(mnemonic, passphrase, accountIndex = 0) {
let root = import_bip32.HDKey.fromMasterSeed((0, import_bip39.mnemonicToSeedSync)(mnemonic, passphrase));
let privateKey = root.derive(`${DERIVATION_PATH}/${accountIndex}'/0/0`).privateKey;
if (!privateKey)
throw new Error("could not derive private key");
return privateKey;
}
function accountFromSeedWords(mnemonic, passphrase, accountIndex = 0) {
const root = import_bip32.HDKey.fromMasterSeed((0, import_bip39.mnemonicToSeedSync)(mnemonic, passphrase));
const seed = root.derive(`${DERIVATION_PATH}/${accountIndex}'/0/0`);
const publicKey = (0, import_utils.bytesToHex)(seed.publicKey.slice(1));
const privateKey = seed.privateKey;
if (!privateKey || !publicKey) {
throw new Error("could not derive key pair");
}
return { privateKey, publicKey };
}
function extendedKeysFromSeedWords(mnemonic, passphrase, extendedAccountIndex = 0) {
let root = import_bip32.HDKey.fromMasterSeed((0, import_bip39.mnemonicToSeedSync)(mnemonic, passphrase));
let seed = root.derive(`${DERIVATION_PATH}/${extendedAccountIndex}'`);
let privateExtendedKey = seed.privateExtendedKey;
let publicExtendedKey = seed.publicExtendedKey;
if (!privateExtendedKey && !publicExtendedKey)
throw new Error("could not derive extended key pair");
return { privateExtendedKey, publicExtendedKey };
}
function accountFromExtendedKey(base58key, accountIndex = 0) {
let extendedKey = import_bip32.HDKey.fromExtendedKey(base58key);
let version = base58key.slice(0, 4);
let child = extendedKey.deriveChild(0).deriveChild(accountIndex);
let publicKey = (0, import_utils.bytesToHex)(child.publicKey.slice(1));
if (!publicKey)
throw new Error("could not derive public key");
if (version === "xprv") {
let privateKey = child.privateKey;
if (!privateKey)
throw new Error("could not derive private key");
return { privateKey, publicKey };
}
return { publicKey };
}
function generateSeedWords() {
return (0, import_bip39.generateMnemonic)(import_english.wordlist);
}
function validateWords(words) {
return (0, import_bip39.validateMnemonic)(words, import_english.wordlist);
}

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../nip06.ts"],
"sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils'\nimport { wordlist } from '@scure/bip39/wordlists/english'\nimport { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39'\nimport { HDKey } from '@scure/bip32'\n\nconst DERIVATION_PATH = `m/44'/1237'`\n\nexport function privateKeyFromSeedWords(mnemonic: string, passphrase?: string, accountIndex = 0): Uint8Array {\n let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n let privateKey = root.derive(`${DERIVATION_PATH}/${accountIndex}'/0/0`).privateKey\n if (!privateKey) throw new Error('could not derive private key')\n return privateKey\n}\n\nexport function accountFromSeedWords(\n mnemonic: string,\n passphrase?: string,\n accountIndex = 0,\n): {\n privateKey: Uint8Array\n publicKey: string\n} {\n const root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n const seed = root.derive(`${DERIVATION_PATH}/${accountIndex}'/0/0`)\n const publicKey = bytesToHex(seed.publicKey!.slice(1))\n const privateKey = seed.privateKey\n if (!privateKey || !publicKey) {\n throw new Error('could not derive key pair')\n }\n return { privateKey, publicKey }\n}\n\nexport function extendedKeysFromSeedWords(\n mnemonic: string,\n passphrase?: string,\n extendedAccountIndex = 0,\n): {\n privateExtendedKey: string\n publicExtendedKey: string\n} {\n let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n let seed = root.derive(`${DERIVATION_PATH}/${extendedAccountIndex}'`)\n let privateExtendedKey = seed.privateExtendedKey\n let publicExtendedKey = seed.publicExtendedKey\n if (!privateExtendedKey && !publicExtendedKey) throw new Error('could not derive extended key pair')\n return { privateExtendedKey, publicExtendedKey }\n}\n\nexport function accountFromExtendedKey(\n base58key: string,\n accountIndex = 0,\n): {\n privateKey?: Uint8Array\n publicKey: string\n} {\n let extendedKey = HDKey.fromExtendedKey(base58key)\n let version = base58key.slice(0, 4)\n let child = extendedKey.deriveChild(0).deriveChild(accountIndex)\n let publicKey = bytesToHex(child.publicKey!.slice(1))\n if (!publicKey) throw new Error('could not derive public key')\n if (version === 'xprv') {\n let privateKey = child.privateKey!\n if (!privateKey) throw new Error('could not derive private key')\n return { privateKey, publicKey }\n }\n return { publicKey }\n}\n\nexport function generateSeedWords(): string {\n return generateMnemonic(wordlist)\n}\n\nexport function validateWords(words: string): boolean {\n return validateMnemonic(words, wordlist)\n}\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA2B;AAC3B,qBAAyB;AACzB,mBAAuE;AACvE,mBAAsB;AAEtB,IAAM,kBAAkB;AAEjB,SAAS,wBAAwB,UAAkB,YAAqB,eAAe,GAAe;AAC3G,MAAI,OAAO,mBAAM,mBAAe,iCAAmB,UAAU,UAAU,CAAC;AACxE,MAAI,aAAa,KAAK,OAAO,GAAG,mBAAmB,mBAAmB,EAAE;AACxE,MAAI,CAAC;AAAY,UAAM,IAAI,MAAM,8BAA8B;AAC/D,SAAO;AACT;AAEO,SAAS,qBACd,UACA,YACA,eAAe,GAIf;AACA,QAAM,OAAO,mBAAM,mBAAe,iCAAmB,UAAU,UAAU,CAAC;AAC1E,QAAM,OAAO,KAAK,OAAO,GAAG,mBAAmB,mBAAmB;AAClE,QAAM,gBAAY,yBAAW,KAAK,UAAW,MAAM,CAAC,CAAC;AACrD,QAAM,aAAa,KAAK;AACxB,MAAI,CAAC,cAAc,CAAC,WAAW;AAC7B,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC7C;AACA,SAAO,EAAE,YAAY,UAAU;AACjC;AAEO,SAAS,0BACd,UACA,YACA,uBAAuB,GAIvB;AACA,MAAI,OAAO,mBAAM,mBAAe,iCAAmB,UAAU,UAAU,CAAC;AACxE,MAAI,OAAO,KAAK,OAAO,GAAG,mBAAmB,uBAAuB;AACpE,MAAI,qBAAqB,KAAK;AAC9B,MAAI,oBAAoB,KAAK;AAC7B,MAAI,CAAC,sBAAsB,CAAC;AAAmB,UAAM,IAAI,MAAM,oCAAoC;AACnG,SAAO,EAAE,oBAAoB,kBAAkB;AACjD;AAEO,SAAS,uBACd,WACA,eAAe,GAIf;AACA,MAAI,cAAc,mBAAM,gBAAgB,SAAS;AACjD,MAAI,UAAU,UAAU,MAAM,GAAG,CAAC;AAClC,MAAI,QAAQ,YAAY,YAAY,CAAC,EAAE,YAAY,YAAY;AAC/D,MAAI,gBAAY,yBAAW,MAAM,UAAW,MAAM,CAAC,CAAC;AACpD,MAAI,CAAC;AAAW,UAAM,IAAI,MAAM,6BAA6B;AAC7D,MAAI,YAAY,QAAQ;AACtB,QAAI,aAAa,MAAM;AACvB,QAAI,CAAC;AAAY,YAAM,IAAI,MAAM,8BAA8B;AAC/D,WAAO,EAAE,YAAY,UAAU;AAAA,EACjC;AACA,SAAO,EAAE,UAAU;AACrB;AAEO,SAAS,oBAA4B;AAC1C,aAAO,+BAAiB,uBAAQ;AAClC;AAEO,SAAS,cAAc,OAAwB;AACpD,aAAO,+BAAiB,OAAO,uBAAQ;AACzC;",
"names": []
}

View File

@@ -0,0 +1,18 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip07.ts
var nip07_exports = {};
module.exports = __toCommonJS(nip07_exports);

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../nip07.ts"],
"sourcesContent": ["import { EventTemplate, VerifiedEvent } from './core.ts'\n\nexport interface WindowNostr {\n getPublicKey(): Promise<string>\n signEvent(event: EventTemplate): Promise<VerifiedEvent>\n nip04?: {\n encrypt(pubkey: string, plaintext: string): Promise<string>\n decrypt(pubkey: string, ciphertext: string): Promise<string>\n }\n nip44?: {\n encrypt(pubkey: string, plaintext: string): Promise<string>\n decrypt(pubkey: string, ciphertext: string): Promise<string>\n }\n}\n"],
"mappings": ";;;;;;;;;;;;;;;;AAAA;AAAA;",
"names": []
}

View File

@@ -0,0 +1,124 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip10.ts
var nip10_exports = {};
__export(nip10_exports, {
parse: () => parse
});
module.exports = __toCommonJS(nip10_exports);
function parse(event) {
const result = {
reply: void 0,
root: void 0,
mentions: [],
profiles: [],
quotes: []
};
let maybeParent;
let maybeRoot;
for (let i = event.tags.length - 1; i >= 0; i--) {
const tag = event.tags[i];
if (tag[0] === "e" && tag[1]) {
const [_, eTagEventId, eTagRelayUrl, eTagMarker, eTagAuthor] = tag;
const eventPointer = {
id: eTagEventId,
relays: eTagRelayUrl ? [eTagRelayUrl] : [],
author: eTagAuthor
};
if (eTagMarker === "root") {
result.root = eventPointer;
continue;
}
if (eTagMarker === "reply") {
result.reply = eventPointer;
continue;
}
if (eTagMarker === "mention") {
result.mentions.push(eventPointer);
continue;
}
if (!maybeParent) {
maybeParent = eventPointer;
} else {
maybeRoot = eventPointer;
}
result.mentions.push(eventPointer);
continue;
}
if (tag[0] === "q" && tag[1]) {
const [_, eTagEventId, eTagRelayUrl] = tag;
result.quotes.push({
id: eTagEventId,
relays: eTagRelayUrl ? [eTagRelayUrl] : []
});
}
if (tag[0] === "p" && tag[1]) {
result.profiles.push({
pubkey: tag[1],
relays: tag[2] ? [tag[2]] : []
});
continue;
}
}
if (!result.root) {
result.root = maybeRoot || maybeParent || result.reply;
}
if (!result.reply) {
result.reply = maybeParent || result.root;
}
;
[result.reply, result.root].forEach((ref) => {
if (!ref)
return;
let idx = result.mentions.indexOf(ref);
if (idx !== -1) {
result.mentions.splice(idx, 1);
}
if (ref.author) {
let author = result.profiles.find((p) => p.pubkey === ref.author);
if (author && author.relays) {
if (!ref.relays) {
ref.relays = [];
}
author.relays.forEach((url) => {
if (ref.relays?.indexOf(url) === -1)
ref.relays.push(url);
});
author.relays = ref.relays;
}
}
});
result.mentions.forEach((ref) => {
if (ref.author) {
let author = result.profiles.find((p) => p.pubkey === ref.author);
if (author && author.relays) {
if (!ref.relays) {
ref.relays = [];
}
author.relays.forEach((url) => {
if (ref.relays.indexOf(url) === -1)
ref.relays.push(url);
});
author.relays = ref.relays;
}
}
});
return result;
}

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../nip10.ts"],
"sourcesContent": ["import type { Event } from './core.ts'\nimport type { EventPointer, ProfilePointer } from './nip19.ts'\n\nexport function parse(event: Pick<Event, 'tags'>): {\n /**\n * Pointer to the root of the thread.\n */\n root: EventPointer | undefined\n\n /**\n * Pointer to a \"parent\" event that parsed event replies to (responded to).\n */\n reply: EventPointer | undefined\n\n /**\n * Pointers to events that may or may not be in the reply chain.\n */\n mentions: EventPointer[]\n\n /**\n * Pointers to events that were directly quoted.\n */\n quotes: EventPointer[]\n\n /**\n * List of pubkeys that are involved in the thread in no particular order.\n */\n profiles: ProfilePointer[]\n} {\n const result: ReturnType<typeof parse> = {\n reply: undefined,\n root: undefined,\n mentions: [],\n profiles: [],\n quotes: [],\n }\n\n let maybeParent: EventPointer | undefined\n let maybeRoot: EventPointer | undefined\n\n for (let i = event.tags.length - 1; i >= 0; i--) {\n const tag = event.tags[i]\n\n if (tag[0] === 'e' && tag[1]) {\n const [_, eTagEventId, eTagRelayUrl, eTagMarker, eTagAuthor] = tag as [\n string,\n string,\n undefined | string,\n undefined | string,\n undefined | string,\n ]\n\n const eventPointer: EventPointer = {\n id: eTagEventId,\n relays: eTagRelayUrl ? [eTagRelayUrl] : [],\n author: eTagAuthor,\n }\n\n if (eTagMarker === 'root') {\n result.root = eventPointer\n continue\n }\n\n if (eTagMarker === 'reply') {\n result.reply = eventPointer\n continue\n }\n\n if (eTagMarker === 'mention') {\n result.mentions.push(eventPointer)\n continue\n }\n\n if (!maybeParent) {\n maybeParent = eventPointer\n } else {\n maybeRoot = eventPointer\n }\n\n result.mentions.push(eventPointer)\n continue\n }\n\n if (tag[0] === 'q' && tag[1]) {\n const [_, eTagEventId, eTagRelayUrl] = tag as [string, string, undefined | string]\n result.quotes.push({\n id: eTagEventId,\n relays: eTagRelayUrl ? [eTagRelayUrl] : [],\n })\n }\n\n if (tag[0] === 'p' && tag[1]) {\n result.profiles.push({\n pubkey: tag[1],\n relays: tag[2] ? [tag[2]] : [],\n })\n continue\n }\n }\n\n // get legacy (positional) markers, set reply to root and vice-versa if one of them is missing\n if (!result.root) {\n result.root = maybeRoot || maybeParent || result.reply\n }\n if (!result.reply) {\n result.reply = maybeParent || result.root\n }\n\n // remove root and reply from mentions, inherit relay hints from authors if any\n ;[result.reply, result.root].forEach(ref => {\n if (!ref) return\n\n let idx = result.mentions.indexOf(ref)\n if (idx !== -1) {\n result.mentions.splice(idx, 1)\n }\n if (ref.author) {\n let author = result.profiles.find(p => p.pubkey === ref.author)\n if (author && author.relays) {\n if (!ref.relays) {\n ref.relays = []\n }\n author.relays.forEach(url => {\n if (ref.relays!?.indexOf(url) === -1) ref.relays!.push(url)\n })\n author.relays = ref.relays\n }\n }\n })\n\n result.mentions.forEach(ref => {\n if (ref!.author) {\n let author = result.profiles.find(p => p.pubkey === ref.author)\n if (author && author.relays) {\n if (!ref.relays) {\n ref.relays = []\n }\n author.relays.forEach(url => {\n if (ref.relays!.indexOf(url) === -1) ref.relays!.push(url)\n })\n author.relays = ref.relays\n }\n }\n })\n\n return result\n}\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGO,SAAS,MAAM,OAyBpB;AACA,QAAM,SAAmC;AAAA,IACvC,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU,CAAC;AAAA,IACX,UAAU,CAAC;AAAA,IACX,QAAQ,CAAC;AAAA,EACX;AAEA,MAAI;AACJ,MAAI;AAEJ,WAAS,IAAI,MAAM,KAAK,SAAS,GAAG,KAAK,GAAG,KAAK;AAC/C,UAAM,MAAM,MAAM,KAAK;AAEvB,QAAI,IAAI,OAAO,OAAO,IAAI,IAAI;AAC5B,YAAM,CAAC,GAAG,aAAa,cAAc,YAAY,UAAU,IAAI;AAQ/D,YAAM,eAA6B;AAAA,QACjC,IAAI;AAAA,QACJ,QAAQ,eAAe,CAAC,YAAY,IAAI,CAAC;AAAA,QACzC,QAAQ;AAAA,MACV;AAEA,UAAI,eAAe,QAAQ;AACzB,eAAO,OAAO;AACd;AAAA,MACF;AAEA,UAAI,eAAe,SAAS;AAC1B,eAAO,QAAQ;AACf;AAAA,MACF;AAEA,UAAI,eAAe,WAAW;AAC5B,eAAO,SAAS,KAAK,YAAY;AACjC;AAAA,MACF;AAEA,UAAI,CAAC,aAAa;AAChB,sBAAc;AAAA,MAChB,OAAO;AACL,oBAAY;AAAA,MACd;AAEA,aAAO,SAAS,KAAK,YAAY;AACjC;AAAA,IACF;AAEA,QAAI,IAAI,OAAO,OAAO,IAAI,IAAI;AAC5B,YAAM,CAAC,GAAG,aAAa,YAAY,IAAI;AACvC,aAAO,OAAO,KAAK;AAAA,QACjB,IAAI;AAAA,QACJ,QAAQ,eAAe,CAAC,YAAY,IAAI,CAAC;AAAA,MAC3C,CAAC;AAAA,IACH;AAEA,QAAI,IAAI,OAAO,OAAO,IAAI,IAAI;AAC5B,aAAO,SAAS,KAAK;AAAA,QACnB,QAAQ,IAAI;AAAA,QACZ,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC;AAAA,MAC/B,CAAC;AACD;AAAA,IACF;AAAA,EACF;AAGA,MAAI,CAAC,OAAO,MAAM;AAChB,WAAO,OAAO,aAAa,eAAe,OAAO;AAAA,EACnD;AACA,MAAI,CAAC,OAAO,OAAO;AACjB,WAAO,QAAQ,eAAe,OAAO;AAAA,EACvC;AAGA;AAAC,GAAC,OAAO,OAAO,OAAO,IAAI,EAAE,QAAQ,SAAO;AAC1C,QAAI,CAAC;AAAK;AAEV,QAAI,MAAM,OAAO,SAAS,QAAQ,GAAG;AACrC,QAAI,QAAQ,IAAI;AACd,aAAO,SAAS,OAAO,KAAK,CAAC;AAAA,IAC/B;AACA,QAAI,IAAI,QAAQ;AACd,UAAI,SAAS,OAAO,SAAS,KAAK,OAAK,EAAE,WAAW,IAAI,MAAM;AAC9D,UAAI,UAAU,OAAO,QAAQ;AAC3B,YAAI,CAAC,IAAI,QAAQ;AACf,cAAI,SAAS,CAAC;AAAA,QAChB;AACA,eAAO,OAAO,QAAQ,SAAO;AAC3B,cAAI,IAAI,QAAS,QAAQ,GAAG,MAAM;AAAI,gBAAI,OAAQ,KAAK,GAAG;AAAA,QAC5D,CAAC;AACD,eAAO,SAAS,IAAI;AAAA,MACtB;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,SAAS,QAAQ,SAAO;AAC7B,QAAI,IAAK,QAAQ;AACf,UAAI,SAAS,OAAO,SAAS,KAAK,OAAK,EAAE,WAAW,IAAI,MAAM;AAC9D,UAAI,UAAU,OAAO,QAAQ;AAC3B,YAAI,CAAC,IAAI,QAAQ;AACf,cAAI,SAAS,CAAC;AAAA,QAChB;AACA,eAAO,OAAO,QAAQ,SAAO;AAC3B,cAAI,IAAI,OAAQ,QAAQ,GAAG,MAAM;AAAI,gBAAI,OAAQ,KAAK,GAAG;AAAA,QAC3D,CAAC;AACD,eAAO,SAAS,IAAI;AAAA,MACtB;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO;AACT;",
"names": []
}

View File

@@ -0,0 +1,39 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip11.ts
var nip11_exports = {};
__export(nip11_exports, {
fetchRelayInformation: () => fetchRelayInformation,
useFetchImplementation: () => useFetchImplementation
});
module.exports = __toCommonJS(nip11_exports);
var _fetch;
try {
_fetch = fetch;
} catch {
}
function useFetchImplementation(fetchImplementation) {
_fetch = fetchImplementation;
}
async function fetchRelayInformation(url) {
return await (await fetch(url.replace("ws://", "http://").replace("wss://", "https://"), {
headers: { Accept: "application/nostr+json" }
})).json();
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,73 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip13.ts
var nip13_exports = {};
__export(nip13_exports, {
fastEventHash: () => fastEventHash,
getPow: () => getPow,
minePow: () => minePow
});
module.exports = __toCommonJS(nip13_exports);
var import_utils2 = require("@noble/hashes/utils");
var import_sha256 = require("@noble/hashes/sha256");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// nip13.ts
function getPow(hex) {
let count = 0;
for (let i = 0; i < 64; i += 8) {
const nibble = parseInt(hex.substring(i, i + 8), 16);
if (nibble === 0) {
count += 32;
} else {
count += Math.clz32(nibble);
break;
}
}
return count;
}
function minePow(unsigned, difficulty) {
let count = 0;
const event = unsigned;
const tag = ["nonce", count.toString(), difficulty.toString()];
event.tags.push(tag);
while (true) {
const now = Math.floor(new Date().getTime() / 1e3);
if (now !== event.created_at) {
count = 0;
event.created_at = now;
}
tag[1] = (++count).toString();
event.id = fastEventHash(event);
if (getPow(event.id) >= difficulty) {
break;
}
}
return event;
}
function fastEventHash(evt) {
return (0, import_utils2.bytesToHex)(
(0, import_sha256.sha256)(utf8Encoder.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])))
);
}

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../nip13.ts", "../../utils.ts"],
"sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils'\nimport { type UnsignedEvent, type Event } from './pure.ts'\nimport { sha256 } from '@noble/hashes/sha256'\n\nimport { utf8Encoder } from './utils.ts'\n\n/** Get POW difficulty from a Nostr hex ID. */\nexport function getPow(hex: string): number {\n let count = 0\n\n for (let i = 0; i < 64; i += 8) {\n const nibble = parseInt(hex.substring(i, i + 8), 16)\n if (nibble === 0) {\n count += 32\n } else {\n count += Math.clz32(nibble)\n break\n }\n }\n\n return count\n}\n\n/**\n * Mine an event with the desired POW. This function mutates the event.\n * Note that this operation is synchronous and should be run in a worker context to avoid blocking the main thread.\n */\nexport function minePow(unsigned: UnsignedEvent, difficulty: number): Omit<Event, 'sig'> {\n let count = 0\n\n const event = unsigned as Omit<Event, 'sig'>\n const tag = ['nonce', count.toString(), difficulty.toString()]\n\n event.tags.push(tag)\n\n while (true) {\n const now = Math.floor(new Date().getTime() / 1000)\n\n if (now !== event.created_at) {\n count = 0\n event.created_at = now\n }\n\n tag[1] = (++count).toString()\n\n event.id = fastEventHash(event)\n\n if (getPow(event.id) >= difficulty) {\n break\n }\n }\n\n return event\n}\n\nexport function fastEventHash(evt: UnsignedEvent): string {\n return bytesToHex(\n sha256(utf8Encoder.encode(JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]))),\n )\n}\n", "import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport class QueueNode<V> {\n public value: V\n public next: QueueNode<V> | null = null\n public prev: QueueNode<V> | null = null\n\n constructor(message: V) {\n this.value = message\n }\n}\n\nexport class Queue<V> {\n public first: QueueNode<V> | null\n public last: QueueNode<V> | null\n\n constructor() {\n this.first = null\n this.last = null\n }\n\n enqueue(value: V): boolean {\n const newNode = new QueueNode(value)\n if (!this.last) {\n // list is empty\n this.first = newNode\n this.last = newNode\n } else if (this.last === this.first) {\n // list has a single element\n this.last = newNode\n this.last.prev = this.first\n this.first.next = newNode\n } else {\n // list has elements, add as last\n newNode.prev = this.last\n this.last.next = newNode\n this.last = newNode\n }\n return true\n }\n\n dequeue(): V | null {\n if (!this.first) return null\n\n if (this.first === this.last) {\n const target = this.first\n this.first = null\n this.last = null\n return target.value\n }\n\n const target = this.first\n this.first = target.next\n if (this.first) {\n this.first.prev = null // fix: clean up prev pointer\n }\n\n return target.value\n }\n}\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,gBAA2B;AAE3B,oBAAuB;;;ACGvB,mBAAuC;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;ADIjD,SAAS,OAAO,KAAqB;AAC1C,MAAI,QAAQ;AAEZ,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG;AAC9B,UAAM,SAAS,SAAS,IAAI,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE;AACnD,QAAI,WAAW,GAAG;AAChB,eAAS;AAAA,IACX,OAAO;AACL,eAAS,KAAK,MAAM,MAAM;AAC1B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAMO,SAAS,QAAQ,UAAyB,YAAwC;AACvF,MAAI,QAAQ;AAEZ,QAAM,QAAQ;AACd,QAAM,MAAM,CAAC,SAAS,MAAM,SAAS,GAAG,WAAW,SAAS,CAAC;AAE7D,QAAM,KAAK,KAAK,GAAG;AAEnB,SAAO,MAAM;AACX,UAAM,MAAM,KAAK,MAAM,IAAI,KAAK,EAAE,QAAQ,IAAI,GAAI;AAElD,QAAI,QAAQ,MAAM,YAAY;AAC5B,cAAQ;AACR,YAAM,aAAa;AAAA,IACrB;AAEA,QAAI,MAAM,EAAE,OAAO,SAAS;AAE5B,UAAM,KAAK,cAAc,KAAK;AAE9B,QAAI,OAAO,MAAM,EAAE,KAAK,YAAY;AAClC;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,cAAc,KAA4B;AACxD,aAAO;AAAA,QACL,sBAAO,YAAY,OAAO,KAAK,UAAU,CAAC,GAAG,IAAI,QAAQ,IAAI,YAAY,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC;AAAA,EAC7G;AACF;",
"names": ["import_utils"]
}

View File

@@ -0,0 +1,321 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip17.ts
var nip17_exports = {};
__export(nip17_exports, {
unwrapEvent: () => unwrapEvent2,
unwrapManyEvents: () => unwrapManyEvents2,
wrapEvent: () => wrapEvent2,
wrapManyEvents: () => wrapManyEvents
});
module.exports = __toCommonJS(nip17_exports);
// pure.ts
var import_secp256k1 = require("@noble/curves/secp256k1");
var import_utils2 = require("@noble/hashes/utils");
// core.ts
var verifiedSymbol = Symbol("verified");
var isRecord = (obj) => obj instanceof Object;
function validateEvent(event) {
if (!isRecord(event))
return false;
if (typeof event.kind !== "number")
return false;
if (typeof event.content !== "string")
return false;
if (typeof event.created_at !== "number")
return false;
if (typeof event.pubkey !== "string")
return false;
if (!event.pubkey.match(/^[a-f0-9]{64}$/))
return false;
if (!Array.isArray(event.tags))
return false;
for (let i2 = 0; i2 < event.tags.length; i2++) {
let tag = event.tags[i2];
if (!Array.isArray(tag))
return false;
for (let j = 0; j < tag.length; j++) {
if (typeof tag[j] !== "string")
return false;
}
}
return true;
}
// pure.ts
var import_sha256 = require("@noble/hashes/sha256");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// pure.ts
var JS = class {
generateSecretKey() {
return import_secp256k1.schnorr.utils.randomPrivateKey();
}
getPublicKey(secretKey) {
return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
}
finalizeEvent(t, secretKey) {
const event = t;
event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
event.id = getEventHash(event);
event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
event[verifiedSymbol] = true;
return event;
}
verifyEvent(event) {
if (typeof event[verifiedSymbol] === "boolean")
return event[verifiedSymbol];
const hash = getEventHash(event);
if (hash !== event.id) {
event[verifiedSymbol] = false;
return false;
}
try {
const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
event[verifiedSymbol] = valid;
return valid;
} catch (err) {
event[verifiedSymbol] = false;
return false;
}
}
};
function serializeEvent(evt) {
if (!validateEvent(evt))
throw new Error("can't serialize event with wrong or missing properties");
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
}
function getEventHash(event) {
let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
return (0, import_utils2.bytesToHex)(eventHash);
}
var i = new JS();
var generateSecretKey = i.generateSecretKey;
var getPublicKey = i.getPublicKey;
var finalizeEvent = i.finalizeEvent;
var verifyEvent = i.verifyEvent;
// kinds.ts
var Seal = 13;
var PrivateDirectMessage = 14;
var GiftWrap = 1059;
// nip44.ts
var import_chacha = require("@noble/ciphers/chacha");
var import_utils4 = require("@noble/ciphers/utils");
var import_secp256k12 = require("@noble/curves/secp256k1");
var import_hkdf = require("@noble/hashes/hkdf");
var import_hmac = require("@noble/hashes/hmac");
var import_sha2562 = require("@noble/hashes/sha256");
var import_utils5 = require("@noble/hashes/utils");
var import_base = require("@scure/base");
var minPlaintextSize = 1;
var maxPlaintextSize = 65535;
function getConversationKey(privkeyA, pubkeyB) {
const sharedX = import_secp256k12.secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
return (0, import_hkdf.extract)(import_sha2562.sha256, sharedX, "nip44-v2");
}
function getMessageKeys(conversationKey, nonce) {
const keys = (0, import_hkdf.expand)(import_sha2562.sha256, conversationKey, nonce, 76);
return {
chacha_key: keys.subarray(0, 32),
chacha_nonce: keys.subarray(32, 44),
hmac_key: keys.subarray(44, 76)
};
}
function calcPaddedLen(len) {
if (!Number.isSafeInteger(len) || len < 1)
throw new Error("expected positive integer");
if (len <= 32)
return 32;
const nextPower = 1 << Math.floor(Math.log2(len - 1)) + 1;
const chunk = nextPower <= 256 ? 32 : nextPower / 8;
return chunk * (Math.floor((len - 1) / chunk) + 1);
}
function writeU16BE(num) {
if (!Number.isSafeInteger(num) || num < minPlaintextSize || num > maxPlaintextSize)
throw new Error("invalid plaintext size: must be between 1 and 65535 bytes");
const arr = new Uint8Array(2);
new DataView(arr.buffer).setUint16(0, num, false);
return arr;
}
function pad(plaintext) {
const unpadded = utf8Encoder.encode(plaintext);
const unpaddedLen = unpadded.length;
const prefix = writeU16BE(unpaddedLen);
const suffix = new Uint8Array(calcPaddedLen(unpaddedLen) - unpaddedLen);
return (0, import_utils5.concatBytes)(prefix, unpadded, suffix);
}
function unpad(padded) {
const unpaddedLen = new DataView(padded.buffer).getUint16(0);
const unpadded = padded.subarray(2, 2 + unpaddedLen);
if (unpaddedLen < minPlaintextSize || unpaddedLen > maxPlaintextSize || unpadded.length !== unpaddedLen || padded.length !== 2 + calcPaddedLen(unpaddedLen))
throw new Error("invalid padding");
return utf8Decoder.decode(unpadded);
}
function hmacAad(key, message, aad) {
if (aad.length !== 32)
throw new Error("AAD associated data must be 32 bytes");
const combined = (0, import_utils5.concatBytes)(aad, message);
return (0, import_hmac.hmac)(import_sha2562.sha256, key, combined);
}
function decodePayload(payload) {
if (typeof payload !== "string")
throw new Error("payload must be a valid string");
const plen = payload.length;
if (plen < 132 || plen > 87472)
throw new Error("invalid payload length: " + plen);
if (payload[0] === "#")
throw new Error("unknown encryption version");
let data;
try {
data = import_base.base64.decode(payload);
} catch (error) {
throw new Error("invalid base64: " + error.message);
}
const dlen = data.length;
if (dlen < 99 || dlen > 65603)
throw new Error("invalid data length: " + dlen);
const vers = data[0];
if (vers !== 2)
throw new Error("unknown encryption version " + vers);
return {
nonce: data.subarray(1, 33),
ciphertext: data.subarray(33, -32),
mac: data.subarray(-32)
};
}
function encrypt(plaintext, conversationKey, nonce = (0, import_utils5.randomBytes)(32)) {
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);
const padded = pad(plaintext);
const ciphertext = (0, import_chacha.chacha20)(chacha_key, chacha_nonce, padded);
const mac = hmacAad(hmac_key, ciphertext, nonce);
return import_base.base64.encode((0, import_utils5.concatBytes)(new Uint8Array([2]), nonce, ciphertext, mac));
}
function decrypt(payload, conversationKey) {
const { nonce, ciphertext, mac } = decodePayload(payload);
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);
const calculatedMac = hmacAad(hmac_key, ciphertext, nonce);
if (!(0, import_utils4.equalBytes)(calculatedMac, mac))
throw new Error("invalid MAC");
const padded = (0, import_chacha.chacha20)(chacha_key, chacha_nonce, ciphertext);
return unpad(padded);
}
// nip59.ts
var TWO_DAYS = 2 * 24 * 60 * 60;
var now = () => Math.round(Date.now() / 1e3);
var randomNow = () => Math.round(now() - Math.random() * TWO_DAYS);
var nip44ConversationKey = (privateKey, publicKey) => getConversationKey(privateKey, publicKey);
var nip44Encrypt = (data, privateKey, publicKey) => encrypt(JSON.stringify(data), nip44ConversationKey(privateKey, publicKey));
var nip44Decrypt = (data, privateKey) => JSON.parse(decrypt(data.content, nip44ConversationKey(privateKey, data.pubkey)));
function createRumor(event, privateKey) {
const rumor = {
created_at: now(),
content: "",
tags: [],
...event,
pubkey: getPublicKey(privateKey)
};
rumor.id = getEventHash(rumor);
return rumor;
}
function createSeal(rumor, privateKey, recipientPublicKey) {
return finalizeEvent(
{
kind: Seal,
content: nip44Encrypt(rumor, privateKey, recipientPublicKey),
created_at: randomNow(),
tags: []
},
privateKey
);
}
function createWrap(seal, recipientPublicKey) {
const randomKey = generateSecretKey();
return finalizeEvent(
{
kind: GiftWrap,
content: nip44Encrypt(seal, randomKey, recipientPublicKey),
created_at: randomNow(),
tags: [["p", recipientPublicKey]]
},
randomKey
);
}
function wrapEvent(event, senderPrivateKey, recipientPublicKey) {
const rumor = createRumor(event, senderPrivateKey);
const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey);
return createWrap(seal, recipientPublicKey);
}
function unwrapEvent(wrap, recipientPrivateKey) {
const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey);
return nip44Decrypt(unwrappedSeal, recipientPrivateKey);
}
function unwrapManyEvents(wrappedEvents, recipientPrivateKey) {
let unwrappedEvents = [];
wrappedEvents.forEach((e) => {
unwrappedEvents.push(unwrapEvent(e, recipientPrivateKey));
});
unwrappedEvents.sort((a, b) => a.created_at - b.created_at);
return unwrappedEvents;
}
// nip17.ts
function createEvent(recipients, message, conversationTitle, replyTo) {
const baseEvent = {
created_at: Math.ceil(Date.now() / 1e3),
kind: PrivateDirectMessage,
tags: [],
content: message
};
const recipientsArray = Array.isArray(recipients) ? recipients : [recipients];
recipientsArray.forEach(({ publicKey, relayUrl }) => {
baseEvent.tags.push(relayUrl ? ["p", publicKey, relayUrl] : ["p", publicKey]);
});
if (replyTo) {
baseEvent.tags.push(["e", replyTo.eventId, replyTo.relayUrl || "", "reply"]);
}
if (conversationTitle) {
baseEvent.tags.push(["subject", conversationTitle]);
}
return baseEvent;
}
function wrapEvent2(senderPrivateKey, recipient, message, conversationTitle, replyTo) {
const event = createEvent(recipient, message, conversationTitle, replyTo);
return wrapEvent(event, senderPrivateKey, recipient.publicKey);
}
function wrapManyEvents(senderPrivateKey, recipients, message, conversationTitle, replyTo) {
if (!recipients || recipients.length === 0) {
throw new Error("At least one recipient is required.");
}
const senderPublicKey = getPublicKey(senderPrivateKey);
return [{ publicKey: senderPublicKey }, ...recipients].map(
(recipient) => wrapEvent2(senderPrivateKey, recipient, message, conversationTitle, replyTo)
);
}
var unwrapEvent2 = unwrapEvent;
var unwrapManyEvents2 = unwrapManyEvents;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,188 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip18.ts
var nip18_exports = {};
__export(nip18_exports, {
finishRepostEvent: () => finishRepostEvent,
getRepostedEvent: () => getRepostedEvent,
getRepostedEventPointer: () => getRepostedEventPointer
});
module.exports = __toCommonJS(nip18_exports);
// pure.ts
var import_secp256k1 = require("@noble/curves/secp256k1");
var import_utils2 = require("@noble/hashes/utils");
// core.ts
var verifiedSymbol = Symbol("verified");
var isRecord = (obj) => obj instanceof Object;
function validateEvent(event) {
if (!isRecord(event))
return false;
if (typeof event.kind !== "number")
return false;
if (typeof event.content !== "string")
return false;
if (typeof event.created_at !== "number")
return false;
if (typeof event.pubkey !== "string")
return false;
if (!event.pubkey.match(/^[a-f0-9]{64}$/))
return false;
if (!Array.isArray(event.tags))
return false;
for (let i2 = 0; i2 < event.tags.length; i2++) {
let tag = event.tags[i2];
if (!Array.isArray(tag))
return false;
for (let j = 0; j < tag.length; j++) {
if (typeof tag[j] !== "string")
return false;
}
}
return true;
}
// pure.ts
var import_sha256 = require("@noble/hashes/sha256");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// pure.ts
var JS = class {
generateSecretKey() {
return import_secp256k1.schnorr.utils.randomPrivateKey();
}
getPublicKey(secretKey) {
return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
}
finalizeEvent(t, secretKey) {
const event = t;
event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
event.id = getEventHash(event);
event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
event[verifiedSymbol] = true;
return event;
}
verifyEvent(event) {
if (typeof event[verifiedSymbol] === "boolean")
return event[verifiedSymbol];
const hash = getEventHash(event);
if (hash !== event.id) {
event[verifiedSymbol] = false;
return false;
}
try {
const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
event[verifiedSymbol] = valid;
return valid;
} catch (err) {
event[verifiedSymbol] = false;
return false;
}
}
};
function serializeEvent(evt) {
if (!validateEvent(evt))
throw new Error("can't serialize event with wrong or missing properties");
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
}
function getEventHash(event) {
let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
return (0, import_utils2.bytesToHex)(eventHash);
}
var i = new JS();
var generateSecretKey = i.generateSecretKey;
var getPublicKey = i.getPublicKey;
var finalizeEvent = i.finalizeEvent;
var verifyEvent = i.verifyEvent;
// kinds.ts
var ShortTextNote = 1;
var Repost = 6;
var GenericRepost = 16;
// nip18.ts
function finishRepostEvent(t, reposted, relayUrl, privateKey) {
let kind;
const tags = [...t.tags ?? [], ["e", reposted.id, relayUrl], ["p", reposted.pubkey]];
if (reposted.kind === ShortTextNote) {
kind = Repost;
} else {
kind = GenericRepost;
tags.push(["k", String(reposted.kind)]);
}
return finalizeEvent(
{
kind,
tags,
content: t.content === "" || reposted.tags?.find((tag) => tag[0] === "-") ? "" : JSON.stringify(reposted),
created_at: t.created_at
},
privateKey
);
}
function getRepostedEventPointer(event) {
if (![Repost, GenericRepost].includes(event.kind)) {
return void 0;
}
let lastETag;
let lastPTag;
for (let i2 = event.tags.length - 1; i2 >= 0 && (lastETag === void 0 || lastPTag === void 0); i2--) {
const tag = event.tags[i2];
if (tag.length >= 2) {
if (tag[0] === "e" && lastETag === void 0) {
lastETag = tag;
} else if (tag[0] === "p" && lastPTag === void 0) {
lastPTag = tag;
}
}
}
if (lastETag === void 0) {
return void 0;
}
return {
id: lastETag[1],
relays: [lastETag[2], lastPTag?.[2]].filter((x) => typeof x === "string"),
author: lastPTag?.[1]
};
}
function getRepostedEvent(event, { skipVerification } = {}) {
const pointer = getRepostedEventPointer(event);
if (pointer === void 0 || event.content === "") {
return void 0;
}
let repostedEvent;
try {
repostedEvent = JSON.parse(event.content);
} catch (error) {
return void 0;
}
if (repostedEvent.id !== pointer.id) {
return void 0;
}
if (!skipVerification && !verifyEvent(repostedEvent)) {
return void 0;
}
return repostedEvent;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,217 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip19.ts
var nip19_exports = {};
__export(nip19_exports, {
BECH32_REGEX: () => BECH32_REGEX,
Bech32MaxSize: () => Bech32MaxSize,
NostrTypeGuard: () => NostrTypeGuard,
decode: () => decode,
decodeNostrURI: () => decodeNostrURI,
encodeBytes: () => encodeBytes,
naddrEncode: () => naddrEncode,
neventEncode: () => neventEncode,
noteEncode: () => noteEncode,
nprofileEncode: () => nprofileEncode,
npubEncode: () => npubEncode,
nsecEncode: () => nsecEncode
});
module.exports = __toCommonJS(nip19_exports);
var import_utils2 = require("@noble/hashes/utils");
var import_base = require("@scure/base");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// nip19.ts
var NostrTypeGuard = {
isNProfile: (value) => /^nprofile1[a-z\d]+$/.test(value || ""),
isNEvent: (value) => /^nevent1[a-z\d]+$/.test(value || ""),
isNAddr: (value) => /^naddr1[a-z\d]+$/.test(value || ""),
isNSec: (value) => /^nsec1[a-z\d]{58}$/.test(value || ""),
isNPub: (value) => /^npub1[a-z\d]{58}$/.test(value || ""),
isNote: (value) => /^note1[a-z\d]+$/.test(value || ""),
isNcryptsec: (value) => /^ncryptsec1[a-z\d]+$/.test(value || "")
};
var Bech32MaxSize = 5e3;
var BECH32_REGEX = /[\x21-\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/;
function integerToUint8Array(number) {
const uint8Array = new Uint8Array(4);
uint8Array[0] = number >> 24 & 255;
uint8Array[1] = number >> 16 & 255;
uint8Array[2] = number >> 8 & 255;
uint8Array[3] = number & 255;
return uint8Array;
}
function decodeNostrURI(nip19code) {
try {
if (nip19code.startsWith("nostr:"))
nip19code = nip19code.substring(6);
return decode(nip19code);
} catch (_err) {
return { type: "invalid", data: null };
}
}
function decode(code) {
let { prefix, words } = import_base.bech32.decode(code, Bech32MaxSize);
let data = new Uint8Array(import_base.bech32.fromWords(words));
switch (prefix) {
case "nprofile": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for nprofile");
if (tlv[0][0].length !== 32)
throw new Error("TLV 0 should be 32 bytes");
return {
type: "nprofile",
data: {
pubkey: (0, import_utils2.bytesToHex)(tlv[0][0]),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
}
};
}
case "nevent": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for nevent");
if (tlv[0][0].length !== 32)
throw new Error("TLV 0 should be 32 bytes");
if (tlv[2] && tlv[2][0].length !== 32)
throw new Error("TLV 2 should be 32 bytes");
if (tlv[3] && tlv[3][0].length !== 4)
throw new Error("TLV 3 should be 4 bytes");
return {
type: "nevent",
data: {
id: (0, import_utils2.bytesToHex)(tlv[0][0]),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
author: tlv[2]?.[0] ? (0, import_utils2.bytesToHex)(tlv[2][0]) : void 0,
kind: tlv[3]?.[0] ? parseInt((0, import_utils2.bytesToHex)(tlv[3][0]), 16) : void 0
}
};
}
case "naddr": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for naddr");
if (!tlv[2]?.[0])
throw new Error("missing TLV 2 for naddr");
if (tlv[2][0].length !== 32)
throw new Error("TLV 2 should be 32 bytes");
if (!tlv[3]?.[0])
throw new Error("missing TLV 3 for naddr");
if (tlv[3][0].length !== 4)
throw new Error("TLV 3 should be 4 bytes");
return {
type: "naddr",
data: {
identifier: utf8Decoder.decode(tlv[0][0]),
pubkey: (0, import_utils2.bytesToHex)(tlv[2][0]),
kind: parseInt((0, import_utils2.bytesToHex)(tlv[3][0]), 16),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
}
};
}
case "nsec":
return { type: prefix, data };
case "npub":
case "note":
return { type: prefix, data: (0, import_utils2.bytesToHex)(data) };
default:
throw new Error(`unknown prefix ${prefix}`);
}
}
function parseTLV(data) {
let result = {};
let rest = data;
while (rest.length > 0) {
let t = rest[0];
let l = rest[1];
let v = rest.slice(2, 2 + l);
rest = rest.slice(2 + l);
if (v.length < l)
throw new Error(`not enough data to read on TLV ${t}`);
result[t] = result[t] || [];
result[t].push(v);
}
return result;
}
function nsecEncode(key) {
return encodeBytes("nsec", key);
}
function npubEncode(hex) {
return encodeBytes("npub", (0, import_utils2.hexToBytes)(hex));
}
function noteEncode(hex) {
return encodeBytes("note", (0, import_utils2.hexToBytes)(hex));
}
function encodeBech32(prefix, data) {
let words = import_base.bech32.toWords(data);
return import_base.bech32.encode(prefix, words, Bech32MaxSize);
}
function encodeBytes(prefix, bytes) {
return encodeBech32(prefix, bytes);
}
function nprofileEncode(profile) {
let data = encodeTLV({
0: [(0, import_utils2.hexToBytes)(profile.pubkey)],
1: (profile.relays || []).map((url) => utf8Encoder.encode(url))
});
return encodeBech32("nprofile", data);
}
function neventEncode(event) {
let kindArray;
if (event.kind !== void 0) {
kindArray = integerToUint8Array(event.kind);
}
let data = encodeTLV({
0: [(0, import_utils2.hexToBytes)(event.id)],
1: (event.relays || []).map((url) => utf8Encoder.encode(url)),
2: event.author ? [(0, import_utils2.hexToBytes)(event.author)] : [],
3: kindArray ? [new Uint8Array(kindArray)] : []
});
return encodeBech32("nevent", data);
}
function naddrEncode(addr) {
let kind = new ArrayBuffer(4);
new DataView(kind).setUint32(0, addr.kind, false);
let data = encodeTLV({
0: [utf8Encoder.encode(addr.identifier)],
1: (addr.relays || []).map((url) => utf8Encoder.encode(url)),
2: [(0, import_utils2.hexToBytes)(addr.pubkey)],
3: [new Uint8Array(kind)]
});
return encodeBech32("naddr", data);
}
function encodeTLV(tlv) {
let entries = [];
Object.entries(tlv).reverse().forEach(([t, vs]) => {
vs.forEach((v) => {
let entry = new Uint8Array(v.length + 2);
entry.set([parseInt(t)], 0);
entry.set([v.length], 1);
entry.set(v, 2);
entries.push(entry);
});
});
return (0, import_utils2.concatBytes)(...entries);
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,140 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip21.ts
var nip21_exports = {};
__export(nip21_exports, {
NOSTR_URI_REGEX: () => NOSTR_URI_REGEX,
parse: () => parse,
test: () => test
});
module.exports = __toCommonJS(nip21_exports);
// nip19.ts
var import_utils2 = require("@noble/hashes/utils");
var import_base = require("@scure/base");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// nip19.ts
var Bech32MaxSize = 5e3;
var BECH32_REGEX = /[\x21-\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/;
function decode(code) {
let { prefix, words } = import_base.bech32.decode(code, Bech32MaxSize);
let data = new Uint8Array(import_base.bech32.fromWords(words));
switch (prefix) {
case "nprofile": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for nprofile");
if (tlv[0][0].length !== 32)
throw new Error("TLV 0 should be 32 bytes");
return {
type: "nprofile",
data: {
pubkey: (0, import_utils2.bytesToHex)(tlv[0][0]),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
}
};
}
case "nevent": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for nevent");
if (tlv[0][0].length !== 32)
throw new Error("TLV 0 should be 32 bytes");
if (tlv[2] && tlv[2][0].length !== 32)
throw new Error("TLV 2 should be 32 bytes");
if (tlv[3] && tlv[3][0].length !== 4)
throw new Error("TLV 3 should be 4 bytes");
return {
type: "nevent",
data: {
id: (0, import_utils2.bytesToHex)(tlv[0][0]),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
author: tlv[2]?.[0] ? (0, import_utils2.bytesToHex)(tlv[2][0]) : void 0,
kind: tlv[3]?.[0] ? parseInt((0, import_utils2.bytesToHex)(tlv[3][0]), 16) : void 0
}
};
}
case "naddr": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for naddr");
if (!tlv[2]?.[0])
throw new Error("missing TLV 2 for naddr");
if (tlv[2][0].length !== 32)
throw new Error("TLV 2 should be 32 bytes");
if (!tlv[3]?.[0])
throw new Error("missing TLV 3 for naddr");
if (tlv[3][0].length !== 4)
throw new Error("TLV 3 should be 4 bytes");
return {
type: "naddr",
data: {
identifier: utf8Decoder.decode(tlv[0][0]),
pubkey: (0, import_utils2.bytesToHex)(tlv[2][0]),
kind: parseInt((0, import_utils2.bytesToHex)(tlv[3][0]), 16),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
}
};
}
case "nsec":
return { type: prefix, data };
case "npub":
case "note":
return { type: prefix, data: (0, import_utils2.bytesToHex)(data) };
default:
throw new Error(`unknown prefix ${prefix}`);
}
}
function parseTLV(data) {
let result = {};
let rest = data;
while (rest.length > 0) {
let t = rest[0];
let l = rest[1];
let v = rest.slice(2, 2 + l);
rest = rest.slice(2 + l);
if (v.length < l)
throw new Error(`not enough data to read on TLV ${t}`);
result[t] = result[t] || [];
result[t].push(v);
}
return result;
}
// nip21.ts
var NOSTR_URI_REGEX = new RegExp(`nostr:(${BECH32_REGEX.source})`);
function test(value) {
return typeof value === "string" && new RegExp(`^${NOSTR_URI_REGEX.source}$`).test(value);
}
function parse(uri) {
const match = uri.match(new RegExp(`^${NOSTR_URI_REGEX.source}$`));
if (!match)
throw new Error(`Invalid Nostr URI: ${uri}`);
return {
uri: match[0],
value: match[1],
decoded: decode(match[1])
};
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,159 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip25.ts
var nip25_exports = {};
__export(nip25_exports, {
finishReactionEvent: () => finishReactionEvent,
getReactedEventPointer: () => getReactedEventPointer
});
module.exports = __toCommonJS(nip25_exports);
// pure.ts
var import_secp256k1 = require("@noble/curves/secp256k1");
var import_utils2 = require("@noble/hashes/utils");
// core.ts
var verifiedSymbol = Symbol("verified");
var isRecord = (obj) => obj instanceof Object;
function validateEvent(event) {
if (!isRecord(event))
return false;
if (typeof event.kind !== "number")
return false;
if (typeof event.content !== "string")
return false;
if (typeof event.created_at !== "number")
return false;
if (typeof event.pubkey !== "string")
return false;
if (!event.pubkey.match(/^[a-f0-9]{64}$/))
return false;
if (!Array.isArray(event.tags))
return false;
for (let i2 = 0; i2 < event.tags.length; i2++) {
let tag = event.tags[i2];
if (!Array.isArray(tag))
return false;
for (let j = 0; j < tag.length; j++) {
if (typeof tag[j] !== "string")
return false;
}
}
return true;
}
// pure.ts
var import_sha256 = require("@noble/hashes/sha256");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// pure.ts
var JS = class {
generateSecretKey() {
return import_secp256k1.schnorr.utils.randomPrivateKey();
}
getPublicKey(secretKey) {
return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
}
finalizeEvent(t, secretKey) {
const event = t;
event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
event.id = getEventHash(event);
event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
event[verifiedSymbol] = true;
return event;
}
verifyEvent(event) {
if (typeof event[verifiedSymbol] === "boolean")
return event[verifiedSymbol];
const hash = getEventHash(event);
if (hash !== event.id) {
event[verifiedSymbol] = false;
return false;
}
try {
const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
event[verifiedSymbol] = valid;
return valid;
} catch (err) {
event[verifiedSymbol] = false;
return false;
}
}
};
function serializeEvent(evt) {
if (!validateEvent(evt))
throw new Error("can't serialize event with wrong or missing properties");
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
}
function getEventHash(event) {
let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
return (0, import_utils2.bytesToHex)(eventHash);
}
var i = new JS();
var generateSecretKey = i.generateSecretKey;
var getPublicKey = i.getPublicKey;
var finalizeEvent = i.finalizeEvent;
var verifyEvent = i.verifyEvent;
// kinds.ts
var Reaction = 7;
// nip25.ts
function finishReactionEvent(t, reacted, privateKey) {
const inheritedTags = reacted.tags.filter((tag) => tag.length >= 2 && (tag[0] === "e" || tag[0] === "p"));
return finalizeEvent(
{
...t,
kind: Reaction,
tags: [...t.tags ?? [], ...inheritedTags, ["e", reacted.id], ["p", reacted.pubkey]],
content: t.content ?? "+"
},
privateKey
);
}
function getReactedEventPointer(event) {
if (event.kind !== Reaction) {
return void 0;
}
let lastETag;
let lastPTag;
for (let i2 = event.tags.length - 1; i2 >= 0 && (lastETag === void 0 || lastPTag === void 0); i2--) {
const tag = event.tags[i2];
if (tag.length >= 2) {
if (tag[0] === "e" && lastETag === void 0) {
lastETag = tag;
} else if (tag[0] === "p" && lastPTag === void 0) {
lastPTag = tag;
}
}
}
if (lastETag === void 0 || lastPTag === void 0) {
return void 0;
}
return {
id: lastETag[1],
relays: [lastETag[2], lastPTag[2]].filter((x) => x !== void 0),
author: lastPTag[1]
};
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,229 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip27.ts
var nip27_exports = {};
__export(nip27_exports, {
parse: () => parse
});
module.exports = __toCommonJS(nip27_exports);
// nip19.ts
var import_utils2 = require("@noble/hashes/utils");
var import_base = require("@scure/base");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// nip19.ts
var Bech32MaxSize = 5e3;
function decode(code) {
let { prefix, words } = import_base.bech32.decode(code, Bech32MaxSize);
let data = new Uint8Array(import_base.bech32.fromWords(words));
switch (prefix) {
case "nprofile": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for nprofile");
if (tlv[0][0].length !== 32)
throw new Error("TLV 0 should be 32 bytes");
return {
type: "nprofile",
data: {
pubkey: (0, import_utils2.bytesToHex)(tlv[0][0]),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
}
};
}
case "nevent": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for nevent");
if (tlv[0][0].length !== 32)
throw new Error("TLV 0 should be 32 bytes");
if (tlv[2] && tlv[2][0].length !== 32)
throw new Error("TLV 2 should be 32 bytes");
if (tlv[3] && tlv[3][0].length !== 4)
throw new Error("TLV 3 should be 4 bytes");
return {
type: "nevent",
data: {
id: (0, import_utils2.bytesToHex)(tlv[0][0]),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
author: tlv[2]?.[0] ? (0, import_utils2.bytesToHex)(tlv[2][0]) : void 0,
kind: tlv[3]?.[0] ? parseInt((0, import_utils2.bytesToHex)(tlv[3][0]), 16) : void 0
}
};
}
case "naddr": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for naddr");
if (!tlv[2]?.[0])
throw new Error("missing TLV 2 for naddr");
if (tlv[2][0].length !== 32)
throw new Error("TLV 2 should be 32 bytes");
if (!tlv[3]?.[0])
throw new Error("missing TLV 3 for naddr");
if (tlv[3][0].length !== 4)
throw new Error("TLV 3 should be 4 bytes");
return {
type: "naddr",
data: {
identifier: utf8Decoder.decode(tlv[0][0]),
pubkey: (0, import_utils2.bytesToHex)(tlv[2][0]),
kind: parseInt((0, import_utils2.bytesToHex)(tlv[3][0]), 16),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
}
};
}
case "nsec":
return { type: prefix, data };
case "npub":
case "note":
return { type: prefix, data: (0, import_utils2.bytesToHex)(data) };
default:
throw new Error(`unknown prefix ${prefix}`);
}
}
function parseTLV(data) {
let result = {};
let rest = data;
while (rest.length > 0) {
let t = rest[0];
let l = rest[1];
let v = rest.slice(2, 2 + l);
rest = rest.slice(2 + l);
if (v.length < l)
throw new Error(`not enough data to read on TLV ${t}`);
result[t] = result[t] || [];
result[t].push(v);
}
return result;
}
// nip27.ts
var noCharacter = /\W/m;
var noURLCharacter = /\W |\W$|$|,| /m;
function* parse(content) {
const max = content.length;
let prevIndex = 0;
let index = 0;
while (index < max) {
let u = content.indexOf(":", index);
if (u === -1) {
break;
}
if (content.substring(u - 5, u) === "nostr") {
const m = content.substring(u + 60).match(noCharacter);
const end = m ? u + 60 + m.index : max;
try {
let pointer;
let { data, type } = decode(content.substring(u + 1, end));
switch (type) {
case "npub":
pointer = { pubkey: data };
break;
case "nsec":
case "note":
index = end + 1;
continue;
default:
pointer = data;
}
if (prevIndex !== u - 5) {
yield { type: "text", text: content.substring(prevIndex, u - 5) };
}
yield { type: "reference", pointer };
index = end;
prevIndex = index;
continue;
} catch (_err) {
index = u + 1;
continue;
}
} else if (content.substring(u - 5, u) === "https" || content.substring(u - 4, u) === "http") {
const m = content.substring(u + 4).match(noURLCharacter);
const end = m ? u + 4 + m.index : max;
const prefixLen = content[u - 1] === "s" ? 5 : 4;
try {
let url = new URL(content.substring(u - prefixLen, end));
if (url.hostname.indexOf(".") === -1) {
throw new Error("invalid url");
}
if (prevIndex !== u - prefixLen) {
yield { type: "text", text: content.substring(prevIndex, u - prefixLen) };
}
if (/\.(png|jpe?g|gif|webp)$/i.test(url.pathname)) {
yield { type: "image", url: url.toString() };
index = end;
prevIndex = index;
continue;
}
if (/\.(mp4|avi|webm|mkv)$/i.test(url.pathname)) {
yield { type: "video", url: url.toString() };
index = end;
prevIndex = index;
continue;
}
if (/\.(mp3|aac|ogg|opus)$/i.test(url.pathname)) {
yield { type: "audio", url: url.toString() };
index = end;
prevIndex = index;
continue;
}
yield { type: "url", url: url.toString() };
index = end;
prevIndex = index;
continue;
} catch (_err) {
index = end + 1;
continue;
}
} else if (content.substring(u - 3, u) === "wss" || content.substring(u - 2, u) === "ws") {
const m = content.substring(u + 4).match(noURLCharacter);
const end = m ? u + 4 + m.index : max;
const prefixLen = content[u - 1] === "s" ? 3 : 2;
try {
let url = new URL(content.substring(u - prefixLen, end));
if (url.hostname.indexOf(".") === -1) {
throw new Error("invalid ws url");
}
if (prevIndex !== u - prefixLen) {
yield { type: "text", text: content.substring(prevIndex, u - prefixLen) };
}
yield { type: "relay", url: url.toString() };
index = end;
prevIndex = index;
continue;
} catch (_err) {
index = end + 1;
continue;
}
} else {
index = u + 1;
continue;
}
}
if (prevIndex !== max) {
yield { type: "text", text: content.substring(prevIndex) };
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,220 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip28.ts
var nip28_exports = {};
__export(nip28_exports, {
channelCreateEvent: () => channelCreateEvent,
channelHideMessageEvent: () => channelHideMessageEvent,
channelMessageEvent: () => channelMessageEvent,
channelMetadataEvent: () => channelMetadataEvent,
channelMuteUserEvent: () => channelMuteUserEvent
});
module.exports = __toCommonJS(nip28_exports);
// pure.ts
var import_secp256k1 = require("@noble/curves/secp256k1");
var import_utils2 = require("@noble/hashes/utils");
// core.ts
var verifiedSymbol = Symbol("verified");
var isRecord = (obj) => obj instanceof Object;
function validateEvent(event) {
if (!isRecord(event))
return false;
if (typeof event.kind !== "number")
return false;
if (typeof event.content !== "string")
return false;
if (typeof event.created_at !== "number")
return false;
if (typeof event.pubkey !== "string")
return false;
if (!event.pubkey.match(/^[a-f0-9]{64}$/))
return false;
if (!Array.isArray(event.tags))
return false;
for (let i2 = 0; i2 < event.tags.length; i2++) {
let tag = event.tags[i2];
if (!Array.isArray(tag))
return false;
for (let j = 0; j < tag.length; j++) {
if (typeof tag[j] !== "string")
return false;
}
}
return true;
}
// pure.ts
var import_sha256 = require("@noble/hashes/sha256");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// pure.ts
var JS = class {
generateSecretKey() {
return import_secp256k1.schnorr.utils.randomPrivateKey();
}
getPublicKey(secretKey) {
return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
}
finalizeEvent(t, secretKey) {
const event = t;
event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
event.id = getEventHash(event);
event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
event[verifiedSymbol] = true;
return event;
}
verifyEvent(event) {
if (typeof event[verifiedSymbol] === "boolean")
return event[verifiedSymbol];
const hash = getEventHash(event);
if (hash !== event.id) {
event[verifiedSymbol] = false;
return false;
}
try {
const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
event[verifiedSymbol] = valid;
return valid;
} catch (err) {
event[verifiedSymbol] = false;
return false;
}
}
};
function serializeEvent(evt) {
if (!validateEvent(evt))
throw new Error("can't serialize event with wrong or missing properties");
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
}
function getEventHash(event) {
let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
return (0, import_utils2.bytesToHex)(eventHash);
}
var i = new JS();
var generateSecretKey = i.generateSecretKey;
var getPublicKey = i.getPublicKey;
var finalizeEvent = i.finalizeEvent;
var verifyEvent = i.verifyEvent;
// kinds.ts
var ChannelCreation = 40;
var ChannelMetadata = 41;
var ChannelMessage = 42;
var ChannelHideMessage = 43;
var ChannelMuteUser = 44;
// nip28.ts
var channelCreateEvent = (t, privateKey) => {
let content;
if (typeof t.content === "object") {
content = JSON.stringify(t.content);
} else if (typeof t.content === "string") {
content = t.content;
} else {
return void 0;
}
return finalizeEvent(
{
kind: ChannelCreation,
tags: [...t.tags ?? []],
content,
created_at: t.created_at
},
privateKey
);
};
var channelMetadataEvent = (t, privateKey) => {
let content;
if (typeof t.content === "object") {
content = JSON.stringify(t.content);
} else if (typeof t.content === "string") {
content = t.content;
} else {
return void 0;
}
return finalizeEvent(
{
kind: ChannelMetadata,
tags: [["e", t.channel_create_event_id], ...t.tags ?? []],
content,
created_at: t.created_at
},
privateKey
);
};
var channelMessageEvent = (t, privateKey) => {
const tags = [["e", t.channel_create_event_id, t.relay_url, "root"]];
if (t.reply_to_channel_message_event_id) {
tags.push(["e", t.reply_to_channel_message_event_id, t.relay_url, "reply"]);
}
return finalizeEvent(
{
kind: ChannelMessage,
tags: [...tags, ...t.tags ?? []],
content: t.content,
created_at: t.created_at
},
privateKey
);
};
var channelHideMessageEvent = (t, privateKey) => {
let content;
if (typeof t.content === "object") {
content = JSON.stringify(t.content);
} else if (typeof t.content === "string") {
content = t.content;
} else {
return void 0;
}
return finalizeEvent(
{
kind: ChannelHideMessage,
tags: [["e", t.channel_message_event_id], ...t.tags ?? []],
content,
created_at: t.created_at
},
privateKey
);
};
var channelMuteUserEvent = (t, privateKey) => {
let content;
if (typeof t.content === "object") {
content = JSON.stringify(t.content);
} else if (typeof t.content === "string") {
content = t.content;
} else {
return void 0;
}
return finalizeEvent(
{
kind: ChannelMuteUser,
tags: [["p", t.pubkey_to_mute], ...t.tags ?? []],
content,
created_at: t.created_at
},
privateKey
);
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,495 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip29.ts
var nip29_exports = {};
__export(nip29_exports, {
GroupAdminPermission: () => GroupAdminPermission,
encodeGroupReference: () => encodeGroupReference,
fetchGroupAdminsEvent: () => fetchGroupAdminsEvent,
fetchGroupMembersEvent: () => fetchGroupMembersEvent,
fetchGroupMetadataEvent: () => fetchGroupMetadataEvent,
fetchRelayInformationByGroupReference: () => fetchRelayInformationByGroupReference,
generateGroupAdminsEventTemplate: () => generateGroupAdminsEventTemplate,
generateGroupMembersEventTemplate: () => generateGroupMembersEventTemplate,
generateGroupMetadataEventTemplate: () => generateGroupMetadataEventTemplate,
getNormalizedRelayURLByGroupReference: () => getNormalizedRelayURLByGroupReference,
loadGroup: () => loadGroup,
loadGroupFromCode: () => loadGroupFromCode,
parseGroupAdminsEvent: () => parseGroupAdminsEvent,
parseGroupCode: () => parseGroupCode,
parseGroupMembersEvent: () => parseGroupMembersEvent,
parseGroupMetadataEvent: () => parseGroupMetadataEvent,
subscribeRelayGroupsMetadataEvents: () => subscribeRelayGroupsMetadataEvents,
validateGroupAdminsEvent: () => validateGroupAdminsEvent,
validateGroupMembersEvent: () => validateGroupMembersEvent,
validateGroupMetadataEvent: () => validateGroupMetadataEvent
});
module.exports = __toCommonJS(nip29_exports);
// nip11.ts
var _fetch;
try {
_fetch = fetch;
} catch {
}
async function fetchRelayInformation(url) {
return await (await fetch(url.replace("ws://", "http://").replace("wss://", "https://"), {
headers: { Accept: "application/nostr+json" }
})).json();
}
// nip19.ts
var import_utils2 = require("@noble/hashes/utils");
var import_base = require("@scure/base");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
function normalizeURL(url) {
try {
if (url.indexOf("://") === -1)
url = "wss://" + url;
let p = new URL(url);
p.pathname = p.pathname.replace(/\/+/g, "/");
if (p.pathname.endsWith("/"))
p.pathname = p.pathname.slice(0, -1);
if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:")
p.port = "";
p.searchParams.sort();
p.hash = "";
return p.toString();
} catch (e) {
throw new Error(`Invalid URL: ${url}`);
}
}
// nip19.ts
var NostrTypeGuard = {
isNProfile: (value) => /^nprofile1[a-z\d]+$/.test(value || ""),
isNEvent: (value) => /^nevent1[a-z\d]+$/.test(value || ""),
isNAddr: (value) => /^naddr1[a-z\d]+$/.test(value || ""),
isNSec: (value) => /^nsec1[a-z\d]{58}$/.test(value || ""),
isNPub: (value) => /^npub1[a-z\d]{58}$/.test(value || ""),
isNote: (value) => /^note1[a-z\d]+$/.test(value || ""),
isNcryptsec: (value) => /^ncryptsec1[a-z\d]+$/.test(value || "")
};
var Bech32MaxSize = 5e3;
function decode(code) {
let { prefix, words } = import_base.bech32.decode(code, Bech32MaxSize);
let data = new Uint8Array(import_base.bech32.fromWords(words));
switch (prefix) {
case "nprofile": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for nprofile");
if (tlv[0][0].length !== 32)
throw new Error("TLV 0 should be 32 bytes");
return {
type: "nprofile",
data: {
pubkey: (0, import_utils2.bytesToHex)(tlv[0][0]),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
}
};
}
case "nevent": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for nevent");
if (tlv[0][0].length !== 32)
throw new Error("TLV 0 should be 32 bytes");
if (tlv[2] && tlv[2][0].length !== 32)
throw new Error("TLV 2 should be 32 bytes");
if (tlv[3] && tlv[3][0].length !== 4)
throw new Error("TLV 3 should be 4 bytes");
return {
type: "nevent",
data: {
id: (0, import_utils2.bytesToHex)(tlv[0][0]),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
author: tlv[2]?.[0] ? (0, import_utils2.bytesToHex)(tlv[2][0]) : void 0,
kind: tlv[3]?.[0] ? parseInt((0, import_utils2.bytesToHex)(tlv[3][0]), 16) : void 0
}
};
}
case "naddr": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for naddr");
if (!tlv[2]?.[0])
throw new Error("missing TLV 2 for naddr");
if (tlv[2][0].length !== 32)
throw new Error("TLV 2 should be 32 bytes");
if (!tlv[3]?.[0])
throw new Error("missing TLV 3 for naddr");
if (tlv[3][0].length !== 4)
throw new Error("TLV 3 should be 4 bytes");
return {
type: "naddr",
data: {
identifier: utf8Decoder.decode(tlv[0][0]),
pubkey: (0, import_utils2.bytesToHex)(tlv[2][0]),
kind: parseInt((0, import_utils2.bytesToHex)(tlv[3][0]), 16),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
}
};
}
case "nsec":
return { type: prefix, data };
case "npub":
case "note":
return { type: prefix, data: (0, import_utils2.bytesToHex)(data) };
default:
throw new Error(`unknown prefix ${prefix}`);
}
}
function parseTLV(data) {
let result = {};
let rest = data;
while (rest.length > 0) {
let t = rest[0];
let l = rest[1];
let v = rest.slice(2, 2 + l);
rest = rest.slice(2 + l);
if (v.length < l)
throw new Error(`not enough data to read on TLV ${t}`);
result[t] = result[t] || [];
result[t].push(v);
}
return result;
}
// nip29.ts
var GroupAdminPermission = /* @__PURE__ */ ((GroupAdminPermission2) => {
GroupAdminPermission2["AddUser"] = "add-user";
GroupAdminPermission2["EditMetadata"] = "edit-metadata";
GroupAdminPermission2["DeleteEvent"] = "delete-event";
GroupAdminPermission2["RemoveUser"] = "remove-user";
GroupAdminPermission2["AddPermission"] = "add-permission";
GroupAdminPermission2["RemovePermission"] = "remove-permission";
GroupAdminPermission2["EditGroupStatus"] = "edit-group-status";
GroupAdminPermission2["PutUser"] = "put-user";
GroupAdminPermission2["CreateGroup"] = "create-group";
GroupAdminPermission2["DeleteGroup"] = "delete-group";
GroupAdminPermission2["CreateInvite"] = "create-invite";
return GroupAdminPermission2;
})(GroupAdminPermission || {});
function generateGroupMetadataEventTemplate(group) {
const tags = [["d", group.metadata.id]];
group.metadata.name && tags.push(["name", group.metadata.name]);
group.metadata.picture && tags.push(["picture", group.metadata.picture]);
group.metadata.about && tags.push(["about", group.metadata.about]);
group.metadata.isPublic && tags.push(["public"]);
group.metadata.isOpen && tags.push(["open"]);
return {
content: "",
created_at: Math.floor(Date.now() / 1e3),
kind: 39e3,
tags
};
}
function validateGroupMetadataEvent(event) {
if (event.kind !== 39e3)
return false;
if (!event.pubkey)
return false;
const requiredTags = ["d"];
for (const tag of requiredTags) {
if (!event.tags.find(([t]) => t == tag))
return false;
}
return true;
}
function generateGroupAdminsEventTemplate(group, admins) {
const tags = [["d", group.metadata.id]];
for (const admin of admins) {
tags.push(["p", admin.pubkey, admin.label || "", ...admin.permissions]);
}
return {
content: "",
created_at: Math.floor(Date.now() / 1e3),
kind: 39001,
tags
};
}
function validateGroupAdminsEvent(event) {
if (event.kind !== 39001)
return false;
const requiredTags = ["d"];
for (const tag of requiredTags) {
if (!event.tags.find(([t]) => t == tag))
return false;
}
for (const [tag, _value, _label, ...permissions] of event.tags) {
if (tag !== "p")
continue;
for (let i = 0; i < permissions.length; i += 1) {
if (typeof permissions[i] !== "string")
return false;
if (!Object.values(GroupAdminPermission).includes(permissions[i]))
return false;
}
}
return true;
}
function generateGroupMembersEventTemplate(group, members) {
const tags = [["d", group.metadata.id]];
for (const member of members) {
tags.push(["p", member.pubkey, member.label || ""]);
}
return {
content: "",
created_at: Math.floor(Date.now() / 1e3),
kind: 39002,
tags
};
}
function validateGroupMembersEvent(event) {
if (event.kind !== 39002)
return false;
const requiredTags = ["d"];
for (const tag of requiredTags) {
if (!event.tags.find(([t]) => t == tag))
return false;
}
return true;
}
function getNormalizedRelayURLByGroupReference(groupReference) {
return normalizeURL(groupReference.host);
}
async function fetchRelayInformationByGroupReference(groupReference) {
const normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
return fetchRelayInformation(normalizedRelayURL);
}
async function fetchGroupMetadataEvent({
pool,
groupReference,
relayInformation,
normalizedRelayURL
}) {
if (!normalizedRelayURL) {
normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
}
if (!relayInformation) {
relayInformation = await fetchRelayInformation(normalizedRelayURL);
}
const groupMetadataEvent = await pool.get([normalizedRelayURL], {
kinds: [39e3],
authors: [relayInformation.pubkey],
"#d": [groupReference.id]
});
if (!groupMetadataEvent)
throw new Error(`group '${groupReference.id}' not found on ${normalizedRelayURL}`);
return groupMetadataEvent;
}
function parseGroupMetadataEvent(event) {
if (!validateGroupMetadataEvent(event))
throw new Error("invalid group metadata event");
const metadata = {
id: "",
pubkey: event.pubkey
};
for (const [tag, value] of event.tags) {
switch (tag) {
case "d":
metadata.id = value;
break;
case "name":
metadata.name = value;
break;
case "picture":
metadata.picture = value;
break;
case "about":
metadata.about = value;
break;
case "public":
metadata.isPublic = true;
break;
case "open":
metadata.isOpen = true;
break;
}
}
return metadata;
}
async function fetchGroupAdminsEvent({
pool,
groupReference,
relayInformation,
normalizedRelayURL
}) {
if (!normalizedRelayURL) {
normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
}
if (!relayInformation) {
relayInformation = await fetchRelayInformation(normalizedRelayURL);
}
const groupAdminsEvent = await pool.get([normalizedRelayURL], {
kinds: [39001],
authors: [relayInformation.pubkey],
"#d": [groupReference.id]
});
if (!groupAdminsEvent)
throw new Error(`admins for group '${groupReference.id}' not found on ${normalizedRelayURL}`);
return groupAdminsEvent;
}
function parseGroupAdminsEvent(event) {
if (!validateGroupAdminsEvent(event))
throw new Error("invalid group admins event");
const admins = [];
for (const [tag, value, label, ...permissions] of event.tags) {
if (tag !== "p")
continue;
admins.push({
pubkey: value,
label,
permissions
});
}
return admins;
}
async function fetchGroupMembersEvent({
pool,
groupReference,
relayInformation,
normalizedRelayURL
}) {
if (!normalizedRelayURL) {
normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
}
if (!relayInformation) {
relayInformation = await fetchRelayInformation(normalizedRelayURL);
}
const groupMembersEvent = await pool.get([normalizedRelayURL], {
kinds: [39002],
authors: [relayInformation.pubkey],
"#d": [groupReference.id]
});
if (!groupMembersEvent)
throw new Error(`members for group '${groupReference.id}' not found on ${normalizedRelayURL}`);
return groupMembersEvent;
}
function parseGroupMembersEvent(event) {
if (!validateGroupMembersEvent(event))
throw new Error("invalid group members event");
const members = [];
for (const [tag, value, label] of event.tags) {
if (tag !== "p")
continue;
members.push({
pubkey: value,
label
});
}
return members;
}
async function loadGroup({
pool,
groupReference,
normalizedRelayURL,
relayInformation
}) {
if (!normalizedRelayURL) {
normalizedRelayURL = getNormalizedRelayURLByGroupReference(groupReference);
}
if (!relayInformation) {
relayInformation = await fetchRelayInformation(normalizedRelayURL);
}
const metadataEvent = await fetchGroupMetadataEvent({ pool, groupReference, normalizedRelayURL, relayInformation });
const metadata = parseGroupMetadataEvent(metadataEvent);
const adminsEvent = await fetchGroupAdminsEvent({ pool, groupReference, normalizedRelayURL, relayInformation });
const admins = parseGroupAdminsEvent(adminsEvent);
const membersEvent = await fetchGroupMembersEvent({ pool, groupReference, normalizedRelayURL, relayInformation });
const members = parseGroupMembersEvent(membersEvent);
const group = {
relay: normalizedRelayURL,
metadata,
admins,
members,
reference: groupReference
};
return group;
}
async function loadGroupFromCode(pool, code) {
const groupReference = parseGroupCode(code);
if (!groupReference)
throw new Error("invalid group code");
return loadGroup({ pool, groupReference });
}
function parseGroupCode(code) {
if (NostrTypeGuard.isNAddr(code)) {
try {
let { data } = decode(code);
let { relays, identifier } = data;
if (!relays || relays.length === 0)
return null;
let host = relays[0];
if (host.startsWith("wss://")) {
host = host.slice(6);
}
return { host, id: identifier };
} catch (err) {
return null;
}
} else if (code.split("'").length === 2) {
let spl = code.split("'");
return { host: spl[0], id: spl[1] };
}
return null;
}
function encodeGroupReference(gr) {
const { host, id } = gr;
const normalizedHost = host.replace(/^(https?:\/\/|wss?:\/\/)/, "");
return `${normalizedHost}'${id}`;
}
function subscribeRelayGroupsMetadataEvents({
pool,
relayURL,
onError,
onEvent,
onConnect
}) {
let sub;
const normalizedRelayURL = normalizeURL(relayURL);
fetchRelayInformation(normalizedRelayURL).then(async (info) => {
const abstractedRelay = await pool.ensureRelay(normalizedRelayURL);
onConnect?.();
sub = abstractedRelay.prepareSubscription(
[
{
kinds: [39e3],
limit: 50,
authors: [info.pubkey]
}
],
{
onevent(event) {
onEvent(event);
}
}
);
}).catch((err) => {
sub.close();
onError(err);
});
return () => sub.close();
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,53 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip30.ts
var nip30_exports = {};
__export(nip30_exports, {
EMOJI_SHORTCODE_REGEX: () => EMOJI_SHORTCODE_REGEX,
matchAll: () => matchAll,
regex: () => regex,
replaceAll: () => replaceAll
});
module.exports = __toCommonJS(nip30_exports);
var EMOJI_SHORTCODE_REGEX = /:(\w+):/;
var regex = () => new RegExp(`\\B${EMOJI_SHORTCODE_REGEX.source}\\B`, "g");
function* matchAll(content) {
const matches = content.matchAll(regex());
for (const match of matches) {
try {
const [shortcode, name] = match;
yield {
shortcode,
name,
start: match.index,
end: match.index + shortcode.length
};
} catch (_e) {
}
}
}
function replaceAll(content, replacer) {
return content.replaceAll(regex(), (shortcode, name) => {
return replacer({
shortcode,
name
});
});
}

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../nip30.ts"],
"sourcesContent": ["/** Regex for a single emoji shortcode. */\nexport const EMOJI_SHORTCODE_REGEX = /:(\\w+):/\n\n/** Regex to find emoji shortcodes in content. */\nexport const regex = (): RegExp => new RegExp(`\\\\B${EMOJI_SHORTCODE_REGEX.source}\\\\B`, 'g')\n\n/** Represents a Nostr custom emoji. */\nexport interface CustomEmoji {\n /** The matched emoji name with colons. */\n shortcode: `:${string}:`\n /** The matched emoji name without colons. */\n name: string\n}\n\n/** Match result for a custom emoji in text content. */\nexport interface CustomEmojiMatch extends CustomEmoji {\n /** Index where the emoji begins in the text content. */\n start: number\n /** Index where the emoji ends in the text content. */\n end: number\n}\n\n/** Find all custom emoji shortcodes. */\nexport function* matchAll(content: string): Iterable<CustomEmojiMatch> {\n const matches = content.matchAll(regex())\n\n for (const match of matches) {\n try {\n const [shortcode, name] = match\n\n yield {\n shortcode: shortcode as `:${string}:`,\n name,\n start: match.index!,\n end: match.index! + shortcode.length,\n }\n } catch (_e) {\n // do nothing\n }\n }\n}\n\n/** Replace all emoji shortcodes in the content. */\nexport function replaceAll(content: string, replacer: (match: CustomEmoji) => string): string {\n return content.replaceAll(regex(), (shortcode, name) => {\n return replacer({\n shortcode: shortcode as `:${string}:`,\n name,\n })\n })\n}\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACO,IAAM,wBAAwB;AAG9B,IAAM,QAAQ,MAAc,IAAI,OAAO,MAAM,sBAAsB,aAAa,GAAG;AAmBnF,UAAU,SAAS,SAA6C;AACrE,QAAM,UAAU,QAAQ,SAAS,MAAM,CAAC;AAExC,aAAW,SAAS,SAAS;AAC3B,QAAI;AACF,YAAM,CAAC,WAAW,IAAI,IAAI;AAE1B,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,OAAO,MAAM;AAAA,QACb,KAAK,MAAM,QAAS,UAAU;AAAA,MAChC;AAAA,IACF,SAAS,IAAP;AAAA,IAEF;AAAA,EACF;AACF;AAGO,SAAS,WAAW,SAAiB,UAAkD;AAC5F,SAAO,QAAQ,WAAW,MAAM,GAAG,CAAC,WAAW,SAAS;AACtD,WAAO,SAAS;AAAA,MACd;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;",
"names": []
}

View File

@@ -0,0 +1,42 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip39.ts
var nip39_exports = {};
__export(nip39_exports, {
useFetchImplementation: () => useFetchImplementation,
validateGithub: () => validateGithub
});
module.exports = __toCommonJS(nip39_exports);
var _fetch;
try {
_fetch = fetch;
} catch {
}
function useFetchImplementation(fetchImplementation) {
_fetch = fetchImplementation;
}
async function validateGithub(pubkey, username, proof) {
try {
let res = await (await _fetch(`https://gist.github.com/${username}/${proof}/raw`)).text();
return res === `Verifying that I control the following Nostr public key: ${pubkey}`;
} catch (_) {
return false;
}
}

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../nip39.ts"],
"sourcesContent": ["var _fetch: any\n\ntry {\n _fetch = fetch\n} catch {}\n\nexport function useFetchImplementation(fetchImplementation: any) {\n _fetch = fetchImplementation\n}\n\nexport async function validateGithub(pubkey: string, username: string, proof: string): Promise<boolean> {\n try {\n let res = await (await _fetch(`https://gist.github.com/${username}/${proof}/raw`)).text()\n return res === `Verifying that I control the following Nostr public key: ${pubkey}`\n } catch (_) {\n return false\n }\n}\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAI;AAEJ,IAAI;AACF,WAAS;AACX,QAAE;AAAO;AAEF,SAAS,uBAAuB,qBAA0B;AAC/D,WAAS;AACX;AAEA,eAAsB,eAAe,QAAgB,UAAkB,OAAiC;AACtG,MAAI;AACF,QAAI,MAAM,OAAO,MAAM,OAAO,2BAA2B,YAAY,WAAW,GAAG,KAAK;AACxF,WAAO,QAAQ,4DAA4D;AAAA,EAC7E,SAAS,GAAP;AACA,WAAO;AAAA,EACT;AACF;",
"names": []
}

View File

@@ -0,0 +1,63 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip40.ts
var nip40_exports = {};
__export(nip40_exports, {
getExpiration: () => getExpiration,
isEventExpired: () => isEventExpired,
onExpire: () => onExpire,
waitForExpire: () => waitForExpire
});
module.exports = __toCommonJS(nip40_exports);
function getExpiration(event) {
const tag = event.tags.find(([name]) => name === "expiration");
if (tag) {
return new Date(parseInt(tag[1]) * 1e3);
}
}
function isEventExpired(event) {
const expiration = getExpiration(event);
if (expiration) {
return Date.now() > expiration.getTime();
} else {
return false;
}
}
async function waitForExpire(event) {
const expiration = getExpiration(event);
if (expiration) {
const diff = expiration.getTime() - Date.now();
if (diff > 0) {
await sleep(diff);
return event;
} else {
return event;
}
} else {
throw new Error("Event has no expiration");
}
}
function onExpire(event, callback) {
waitForExpire(event).then(callback).catch(() => {
});
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../nip40.ts"],
"sourcesContent": ["import { Event } from './core.ts'\n\n/** Get the expiration of the event as a `Date` object, if any. */\nfunction getExpiration(event: Event): Date | undefined {\n const tag = event.tags.find(([name]) => name === 'expiration')\n if (tag) {\n return new Date(parseInt(tag[1]) * 1000)\n }\n}\n\n/** Check if the event has expired. */\nfunction isEventExpired(event: Event): boolean {\n const expiration = getExpiration(event)\n if (expiration) {\n return Date.now() > expiration.getTime()\n } else {\n return false\n }\n}\n\n/** Returns a promise that resolves when the event expires. */\nasync function waitForExpire(event: Event): Promise<Event> {\n const expiration = getExpiration(event)\n if (expiration) {\n const diff = expiration.getTime() - Date.now()\n if (diff > 0) {\n await sleep(diff)\n return event\n } else {\n return event\n }\n } else {\n throw new Error('Event has no expiration')\n }\n}\n\n/** Calls the callback when the event expires. */\nfunction onExpire(event: Event, callback: (event: Event) => void): void {\n waitForExpire(event)\n .then(callback)\n .catch(() => {})\n}\n\n/** Resolves when the given number of milliseconds have elapsed. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nexport { getExpiration, isEventExpired, waitForExpire, onExpire }\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,SAAS,cAAc,OAAgC;AACrD,QAAM,MAAM,MAAM,KAAK,KAAK,CAAC,CAAC,IAAI,MAAM,SAAS,YAAY;AAC7D,MAAI,KAAK;AACP,WAAO,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,GAAI;AAAA,EACzC;AACF;AAGA,SAAS,eAAe,OAAuB;AAC7C,QAAM,aAAa,cAAc,KAAK;AACtC,MAAI,YAAY;AACd,WAAO,KAAK,IAAI,IAAI,WAAW,QAAQ;AAAA,EACzC,OAAO;AACL,WAAO;AAAA,EACT;AACF;AAGA,eAAe,cAAc,OAA8B;AACzD,QAAM,aAAa,cAAc,KAAK;AACtC,MAAI,YAAY;AACd,UAAM,OAAO,WAAW,QAAQ,IAAI,KAAK,IAAI;AAC7C,QAAI,OAAO,GAAG;AACZ,YAAM,MAAM,IAAI;AAChB,aAAO;AAAA,IACT,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF,OAAO;AACL,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AACF;AAGA,SAAS,SAAS,OAAc,UAAwC;AACtE,gBAAc,KAAK,EAChB,KAAK,QAAQ,EACb,MAAM,MAAM;AAAA,EAAC,CAAC;AACnB;AAGA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AACvD;",
"names": []
}

View File

@@ -0,0 +1,41 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip42.ts
var nip42_exports = {};
__export(nip42_exports, {
makeAuthEvent: () => makeAuthEvent
});
module.exports = __toCommonJS(nip42_exports);
// kinds.ts
var ClientAuth = 22242;
// nip42.ts
function makeAuthEvent(relayURL, challenge) {
return {
kind: ClientAuth,
created_at: Math.floor(Date.now() / 1e3),
tags: [
["relay", relayURL],
["challenge", challenge]
],
content: ""
};
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,143 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip44.ts
var nip44_exports = {};
__export(nip44_exports, {
decrypt: () => decrypt,
encrypt: () => encrypt,
getConversationKey: () => getConversationKey,
v2: () => v2
});
module.exports = __toCommonJS(nip44_exports);
var import_chacha = require("@noble/ciphers/chacha");
var import_utils2 = require("@noble/ciphers/utils");
var import_secp256k1 = require("@noble/curves/secp256k1");
var import_hkdf = require("@noble/hashes/hkdf");
var import_hmac = require("@noble/hashes/hmac");
var import_sha256 = require("@noble/hashes/sha256");
var import_utils3 = require("@noble/hashes/utils");
var import_base = require("@scure/base");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// nip44.ts
var minPlaintextSize = 1;
var maxPlaintextSize = 65535;
function getConversationKey(privkeyA, pubkeyB) {
const sharedX = import_secp256k1.secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
return (0, import_hkdf.extract)(import_sha256.sha256, sharedX, "nip44-v2");
}
function getMessageKeys(conversationKey, nonce) {
const keys = (0, import_hkdf.expand)(import_sha256.sha256, conversationKey, nonce, 76);
return {
chacha_key: keys.subarray(0, 32),
chacha_nonce: keys.subarray(32, 44),
hmac_key: keys.subarray(44, 76)
};
}
function calcPaddedLen(len) {
if (!Number.isSafeInteger(len) || len < 1)
throw new Error("expected positive integer");
if (len <= 32)
return 32;
const nextPower = 1 << Math.floor(Math.log2(len - 1)) + 1;
const chunk = nextPower <= 256 ? 32 : nextPower / 8;
return chunk * (Math.floor((len - 1) / chunk) + 1);
}
function writeU16BE(num) {
if (!Number.isSafeInteger(num) || num < minPlaintextSize || num > maxPlaintextSize)
throw new Error("invalid plaintext size: must be between 1 and 65535 bytes");
const arr = new Uint8Array(2);
new DataView(arr.buffer).setUint16(0, num, false);
return arr;
}
function pad(plaintext) {
const unpadded = utf8Encoder.encode(plaintext);
const unpaddedLen = unpadded.length;
const prefix = writeU16BE(unpaddedLen);
const suffix = new Uint8Array(calcPaddedLen(unpaddedLen) - unpaddedLen);
return (0, import_utils3.concatBytes)(prefix, unpadded, suffix);
}
function unpad(padded) {
const unpaddedLen = new DataView(padded.buffer).getUint16(0);
const unpadded = padded.subarray(2, 2 + unpaddedLen);
if (unpaddedLen < minPlaintextSize || unpaddedLen > maxPlaintextSize || unpadded.length !== unpaddedLen || padded.length !== 2 + calcPaddedLen(unpaddedLen))
throw new Error("invalid padding");
return utf8Decoder.decode(unpadded);
}
function hmacAad(key, message, aad) {
if (aad.length !== 32)
throw new Error("AAD associated data must be 32 bytes");
const combined = (0, import_utils3.concatBytes)(aad, message);
return (0, import_hmac.hmac)(import_sha256.sha256, key, combined);
}
function decodePayload(payload) {
if (typeof payload !== "string")
throw new Error("payload must be a valid string");
const plen = payload.length;
if (plen < 132 || plen > 87472)
throw new Error("invalid payload length: " + plen);
if (payload[0] === "#")
throw new Error("unknown encryption version");
let data;
try {
data = import_base.base64.decode(payload);
} catch (error) {
throw new Error("invalid base64: " + error.message);
}
const dlen = data.length;
if (dlen < 99 || dlen > 65603)
throw new Error("invalid data length: " + dlen);
const vers = data[0];
if (vers !== 2)
throw new Error("unknown encryption version " + vers);
return {
nonce: data.subarray(1, 33),
ciphertext: data.subarray(33, -32),
mac: data.subarray(-32)
};
}
function encrypt(plaintext, conversationKey, nonce = (0, import_utils3.randomBytes)(32)) {
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);
const padded = pad(plaintext);
const ciphertext = (0, import_chacha.chacha20)(chacha_key, chacha_nonce, padded);
const mac = hmacAad(hmac_key, ciphertext, nonce);
return import_base.base64.encode((0, import_utils3.concatBytes)(new Uint8Array([2]), nonce, ciphertext, mac));
}
function decrypt(payload, conversationKey) {
const { nonce, ciphertext, mac } = decodePayload(payload);
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);
const calculatedMac = hmacAad(hmac_key, ciphertext, nonce);
if (!(0, import_utils2.equalBytes)(calculatedMac, mac))
throw new Error("invalid MAC");
const padded = (0, import_chacha.chacha20)(chacha_key, chacha_nonce, ciphertext);
return unpad(padded);
}
var v2 = {
utils: {
getConversationKey,
calcPaddedLen
},
encrypt,
decrypt
};

File diff suppressed because one or more lines are too long

1360
thrower_daemon/node_modules/nostr-tools/lib/cjs/nip46.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,168 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip47.ts
var nip47_exports = {};
__export(nip47_exports, {
makeNwcRequestEvent: () => makeNwcRequestEvent,
parseConnectionString: () => parseConnectionString
});
module.exports = __toCommonJS(nip47_exports);
// pure.ts
var import_secp256k1 = require("@noble/curves/secp256k1");
var import_utils2 = require("@noble/hashes/utils");
// core.ts
var verifiedSymbol = Symbol("verified");
var isRecord = (obj) => obj instanceof Object;
function validateEvent(event) {
if (!isRecord(event))
return false;
if (typeof event.kind !== "number")
return false;
if (typeof event.content !== "string")
return false;
if (typeof event.created_at !== "number")
return false;
if (typeof event.pubkey !== "string")
return false;
if (!event.pubkey.match(/^[a-f0-9]{64}$/))
return false;
if (!Array.isArray(event.tags))
return false;
for (let i2 = 0; i2 < event.tags.length; i2++) {
let tag = event.tags[i2];
if (!Array.isArray(tag))
return false;
for (let j = 0; j < tag.length; j++) {
if (typeof tag[j] !== "string")
return false;
}
}
return true;
}
// pure.ts
var import_sha256 = require("@noble/hashes/sha256");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// pure.ts
var JS = class {
generateSecretKey() {
return import_secp256k1.schnorr.utils.randomPrivateKey();
}
getPublicKey(secretKey) {
return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
}
finalizeEvent(t, secretKey) {
const event = t;
event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
event.id = getEventHash(event);
event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
event[verifiedSymbol] = true;
return event;
}
verifyEvent(event) {
if (typeof event[verifiedSymbol] === "boolean")
return event[verifiedSymbol];
const hash = getEventHash(event);
if (hash !== event.id) {
event[verifiedSymbol] = false;
return false;
}
try {
const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
event[verifiedSymbol] = valid;
return valid;
} catch (err) {
event[verifiedSymbol] = false;
return false;
}
}
};
function serializeEvent(evt) {
if (!validateEvent(evt))
throw new Error("can't serialize event with wrong or missing properties");
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
}
function getEventHash(event) {
let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
return (0, import_utils2.bytesToHex)(eventHash);
}
var i = new JS();
var generateSecretKey = i.generateSecretKey;
var getPublicKey = i.getPublicKey;
var finalizeEvent = i.finalizeEvent;
var verifyEvent = i.verifyEvent;
// kinds.ts
var NWCWalletRequest = 23194;
// nip04.ts
var import_utils4 = require("@noble/hashes/utils");
var import_secp256k12 = require("@noble/curves/secp256k1");
var import_aes = require("@noble/ciphers/aes");
var import_base = require("@scure/base");
function encrypt(secretKey, pubkey, text) {
const privkey = secretKey instanceof Uint8Array ? (0, import_utils4.bytesToHex)(secretKey) : secretKey;
const key = import_secp256k12.secp256k1.getSharedSecret(privkey, "02" + pubkey);
const normalizedKey = getNormalizedX(key);
let iv = Uint8Array.from((0, import_utils4.randomBytes)(16));
let plaintext = utf8Encoder.encode(text);
let ciphertext = (0, import_aes.cbc)(normalizedKey, iv).encrypt(plaintext);
let ctb64 = import_base.base64.encode(new Uint8Array(ciphertext));
let ivb64 = import_base.base64.encode(new Uint8Array(iv.buffer));
return `${ctb64}?iv=${ivb64}`;
}
function getNormalizedX(key) {
return key.slice(1, 33);
}
// nip47.ts
function parseConnectionString(connectionString) {
const { host, pathname, searchParams } = new URL(connectionString);
const pubkey = pathname || host;
const relay = searchParams.get("relay");
const secret = searchParams.get("secret");
if (!pubkey || !relay || !secret) {
throw new Error("invalid connection string");
}
return { pubkey, relay, secret };
}
async function makeNwcRequestEvent(pubkey, secretKey, invoice) {
const content = {
method: "pay_invoice",
params: {
invoice
}
};
const encryptedContent = encrypt(secretKey, pubkey, JSON.stringify(content));
const eventTemplate = {
kind: NWCWalletRequest,
created_at: Math.round(Date.now() / 1e3),
content: encryptedContent,
tags: [["p", pubkey]]
};
return finalizeEvent(eventTemplate, secretKey);
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,77 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip49.ts
var nip49_exports = {};
__export(nip49_exports, {
decrypt: () => decrypt,
encrypt: () => encrypt
});
module.exports = __toCommonJS(nip49_exports);
var import_scrypt = require("@noble/hashes/scrypt");
var import_chacha = require("@noble/ciphers/chacha");
var import_utils2 = require("@noble/hashes/utils");
// nip19.ts
var import_utils = require("@noble/hashes/utils");
var import_base = require("@scure/base");
var Bech32MaxSize = 5e3;
function encodeBech32(prefix, data) {
let words = import_base.bech32.toWords(data);
return import_base.bech32.encode(prefix, words, Bech32MaxSize);
}
function encodeBytes(prefix, bytes) {
return encodeBech32(prefix, bytes);
}
// nip49.ts
var import_base2 = require("@scure/base");
function encrypt(sec, password, logn = 16, ksb = 2) {
let salt = (0, import_utils2.randomBytes)(16);
let n = 2 ** logn;
let key = (0, import_scrypt.scrypt)(password.normalize("NFKC"), salt, { N: n, r: 8, p: 1, dkLen: 32 });
let nonce = (0, import_utils2.randomBytes)(24);
let aad = Uint8Array.from([ksb]);
let xc2p1 = (0, import_chacha.xchacha20poly1305)(key, nonce, aad);
let ciphertext = xc2p1.encrypt(sec);
let b = (0, import_utils2.concatBytes)(Uint8Array.from([2]), Uint8Array.from([logn]), salt, nonce, aad, ciphertext);
return encodeBytes("ncryptsec", b);
}
function decrypt(ncryptsec, password) {
let { prefix, words } = import_base2.bech32.decode(ncryptsec, Bech32MaxSize);
if (prefix !== "ncryptsec") {
throw new Error(`invalid prefix ${prefix}, expected 'ncryptsec'`);
}
let b = new Uint8Array(import_base2.bech32.fromWords(words));
let version = b[0];
if (version !== 2) {
throw new Error(`invalid version ${version}, expected 0x02`);
}
let logn = b[1];
let n = 2 ** logn;
let salt = b.slice(2, 2 + 16);
let nonce = b.slice(2 + 16, 2 + 16 + 24);
let ksb = b[2 + 16 + 24];
let aad = Uint8Array.from([ksb]);
let ciphertext = b.slice(2 + 16 + 24 + 1);
let key = (0, import_scrypt.scrypt)(password.normalize("NFKC"), salt, { N: n, r: 8, p: 1, dkLen: 32 });
let xc2p1 = (0, import_chacha.xchacha20poly1305)(key, nonce, aad);
let sec = xc2p1.decrypt(ciphertext);
return sec;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,35 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip54.ts
var nip54_exports = {};
__export(nip54_exports, {
normalizeIdentifier: () => normalizeIdentifier
});
module.exports = __toCommonJS(nip54_exports);
function normalizeIdentifier(name) {
name = name.trim().toLowerCase();
name = name.normalize("NFKC");
return Array.from(name).map((char) => {
if (/\p{Letter}/u.test(char) || /\p{Number}/u.test(char)) {
return char;
}
return "-";
}).join("");
}

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../nip54.ts"],
"sourcesContent": ["export function normalizeIdentifier(name: string): string {\n // Trim and lowercase\n name = name.trim().toLowerCase()\n\n // Normalize Unicode to NFKC form\n name = name.normalize('NFKC')\n\n // Convert to array of characters and map each one\n return Array.from(name)\n .map(char => {\n // Check if character is letter or number using Unicode ranges\n if (/\\p{Letter}/u.test(char) || /\\p{Number}/u.test(char)) {\n return char\n }\n\n return '-'\n })\n .join('')\n}\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,SAAS,oBAAoB,MAAsB;AAExD,SAAO,KAAK,KAAK,EAAE,YAAY;AAG/B,SAAO,KAAK,UAAU,MAAM;AAG5B,SAAO,MAAM,KAAK,IAAI,EACnB,IAAI,UAAQ;AAEX,QAAI,cAAc,KAAK,IAAI,KAAK,cAAc,KAAK,IAAI,GAAG;AACxD,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,CAAC,EACA,KAAK,EAAE;AACZ;",
"names": []
}

View File

@@ -0,0 +1,103 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip55.ts
var nip55_exports = {};
__export(nip55_exports, {
decryptNip04Uri: () => decryptNip04Uri,
decryptNip44Uri: () => decryptNip44Uri,
decryptZapEventUri: () => decryptZapEventUri,
encryptNip04Uri: () => encryptNip04Uri,
encryptNip44Uri: () => encryptNip44Uri,
getPublicKeyUri: () => getPublicKeyUri,
signEventUri: () => signEventUri
});
module.exports = __toCommonJS(nip55_exports);
function encodeParams(params) {
return new URLSearchParams(params).toString();
}
function filterUndefined(obj) {
return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== void 0));
}
function buildUri({
base,
type,
callbackUrl,
returnType = "signature",
compressionType = "none",
...params
}) {
const baseParams = {
type,
compressionType,
returnType,
callbackUrl,
id: params.id,
current_user: params.currentUser,
permissions: params.permissions && params.permissions.length > 0 ? encodeURIComponent(JSON.stringify(params.permissions)) : void 0,
pubKey: params.pubKey,
plainText: params.plainText,
encryptedText: params.encryptedText,
appName: params.appName
};
const filteredParams = filterUndefined(baseParams);
return `${base}?${encodeParams(filteredParams)}`;
}
function buildDefaultUri(type, params) {
return buildUri({
base: "nostrsigner:",
type,
...params
});
}
function getPublicKeyUri({ permissions = [], ...params }) {
return buildDefaultUri("get_public_key", { permissions, ...params });
}
function signEventUri({ eventJson, ...params }) {
return buildUri({
base: `nostrsigner:${encodeURIComponent(JSON.stringify(eventJson))}`,
type: "sign_event",
...params
});
}
function encryptUri(type, params) {
return buildDefaultUri(type, { ...params, plainText: params.content });
}
function decryptUri(type, params) {
return buildDefaultUri(type, { ...params, encryptedText: params.content });
}
function encryptNip04Uri(params) {
return encryptUri("nip04_encrypt", params);
}
function decryptNip04Uri(params) {
return decryptUri("nip04_decrypt", params);
}
function encryptNip44Uri(params) {
return encryptUri("nip44_encrypt", params);
}
function decryptNip44Uri(params) {
return decryptUri("nip44_decrypt", params);
}
function decryptZapEventUri({ eventJson, ...params }) {
return buildUri({
base: `nostrsigner:${encodeURIComponent(JSON.stringify(eventJson))}`,
type: "decrypt_zap_event",
...params
});
}

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../nip55.ts"],
"sourcesContent": ["type BaseParams = {\n callbackUrl?: string\n returnType?: 'signature' | 'event'\n compressionType?: 'none' | 'gzip'\n}\n\ntype PermissionsParams = BaseParams & {\n permissions?: { type: string; kind?: number }[]\n}\n\ntype EventUriParams = BaseParams & {\n eventJson: Record<string, unknown>\n id?: string\n currentUser?: string\n}\n\ntype EncryptDecryptParams = BaseParams & {\n pubKey: string\n content: string\n id?: string\n currentUser?: string\n}\n\ntype UriParams = BaseParams & {\n base: string\n type: string\n id?: string\n currentUser?: string\n permissions?: { type: string; kind?: number }[]\n pubKey?: string\n plainText?: string\n encryptedText?: string\n appName?: string\n}\n\nfunction encodeParams(params: Record<string, unknown>): string {\n return new URLSearchParams(params as Record<string, string>).toString()\n}\n\nfunction filterUndefined<T extends Record<string, unknown>>(obj: T): T {\n return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined)) as T\n}\n\nfunction buildUri({\n base,\n type,\n callbackUrl,\n returnType = 'signature',\n compressionType = 'none',\n ...params\n}: UriParams): string {\n const baseParams = {\n type,\n compressionType,\n returnType,\n callbackUrl,\n id: params.id,\n current_user: params.currentUser,\n permissions:\n params.permissions && params.permissions.length > 0\n ? encodeURIComponent(JSON.stringify(params.permissions))\n : undefined,\n pubKey: params.pubKey,\n plainText: params.plainText,\n encryptedText: params.encryptedText,\n appName: params.appName,\n }\n\n const filteredParams = filterUndefined(baseParams)\n return `${base}?${encodeParams(filteredParams)}`\n}\n\nfunction buildDefaultUri(type: string, params: Partial<UriParams>): string {\n return buildUri({\n base: 'nostrsigner:',\n type,\n ...params,\n })\n}\n\nexport function getPublicKeyUri({ permissions = [], ...params }: PermissionsParams): string {\n return buildDefaultUri('get_public_key', { permissions, ...params })\n}\n\nexport function signEventUri({ eventJson, ...params }: EventUriParams): string {\n return buildUri({\n base: `nostrsigner:${encodeURIComponent(JSON.stringify(eventJson))}`,\n type: 'sign_event',\n ...params,\n })\n}\n\nfunction encryptUri(type: 'nip44_encrypt' | 'nip04_encrypt', params: EncryptDecryptParams): string {\n return buildDefaultUri(type, { ...params, plainText: params.content })\n}\n\nfunction decryptUri(type: 'nip44_decrypt' | 'nip04_decrypt', params: EncryptDecryptParams): string {\n return buildDefaultUri(type, { ...params, encryptedText: params.content })\n}\n\nexport function encryptNip04Uri(params: EncryptDecryptParams): string {\n return encryptUri('nip04_encrypt', params)\n}\n\nexport function decryptNip04Uri(params: EncryptDecryptParams): string {\n return decryptUri('nip04_decrypt', params)\n}\n\nexport function encryptNip44Uri(params: EncryptDecryptParams): string {\n return encryptUri('nip44_encrypt', params)\n}\n\nexport function decryptNip44Uri(params: EncryptDecryptParams): string {\n return decryptUri('nip44_decrypt', params)\n}\n\nexport function decryptZapEventUri({ eventJson, ...params }: EventUriParams): string {\n return buildUri({\n base: `nostrsigner:${encodeURIComponent(JSON.stringify(eventJson))}`,\n type: 'decrypt_zap_event',\n ...params,\n })\n}\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmCA,SAAS,aAAa,QAAyC;AAC7D,SAAO,IAAI,gBAAgB,MAAgC,EAAE,SAAS;AACxE;AAEA,SAAS,gBAAmD,KAAW;AACrE,SAAO,OAAO,YAAY,OAAO,QAAQ,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,UAAU,MAAS,CAAC;AAC1F;AAEA,SAAS,SAAS;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,kBAAkB;AAAA,KACf;AACL,GAAsB;AACpB,QAAM,aAAa;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,IAAI,OAAO;AAAA,IACX,cAAc,OAAO;AAAA,IACrB,aACE,OAAO,eAAe,OAAO,YAAY,SAAS,IAC9C,mBAAmB,KAAK,UAAU,OAAO,WAAW,CAAC,IACrD;AAAA,IACN,QAAQ,OAAO;AAAA,IACf,WAAW,OAAO;AAAA,IAClB,eAAe,OAAO;AAAA,IACtB,SAAS,OAAO;AAAA,EAClB;AAEA,QAAM,iBAAiB,gBAAgB,UAAU;AACjD,SAAO,GAAG,QAAQ,aAAa,cAAc;AAC/C;AAEA,SAAS,gBAAgB,MAAc,QAAoC;AACzE,SAAO,SAAS;AAAA,IACd,MAAM;AAAA,IACN;AAAA,IACA,GAAG;AAAA,EACL,CAAC;AACH;AAEO,SAAS,gBAAgB,EAAE,cAAc,CAAC,MAAM,OAAO,GAA8B;AAC1F,SAAO,gBAAgB,kBAAkB,EAAE,aAAa,GAAG,OAAO,CAAC;AACrE;AAEO,SAAS,aAAa,EAAE,cAAc,OAAO,GAA2B;AAC7E,SAAO,SAAS;AAAA,IACd,MAAM,eAAe,mBAAmB,KAAK,UAAU,SAAS,CAAC;AAAA,IACjE,MAAM;AAAA,IACN,GAAG;AAAA,EACL,CAAC;AACH;AAEA,SAAS,WAAW,MAAyC,QAAsC;AACjG,SAAO,gBAAgB,MAAM,EAAE,GAAG,QAAQ,WAAW,OAAO,QAAQ,CAAC;AACvE;AAEA,SAAS,WAAW,MAAyC,QAAsC;AACjG,SAAO,gBAAgB,MAAM,EAAE,GAAG,QAAQ,eAAe,OAAO,QAAQ,CAAC;AAC3E;AAEO,SAAS,gBAAgB,QAAsC;AACpE,SAAO,WAAW,iBAAiB,MAAM;AAC3C;AAEO,SAAS,gBAAgB,QAAsC;AACpE,SAAO,WAAW,iBAAiB,MAAM;AAC3C;AAEO,SAAS,gBAAgB,QAAsC;AACpE,SAAO,WAAW,iBAAiB,MAAM;AAC3C;AAEO,SAAS,gBAAgB,QAAsC;AACpE,SAAO,WAAW,iBAAiB,MAAM;AAC3C;AAEO,SAAS,mBAAmB,EAAE,cAAc,OAAO,GAA2B;AACnF,SAAO,SAAS;AAAA,IACd,MAAM,eAAe,mBAAmB,KAAK,UAAU,SAAS,CAAC;AAAA,IACjE,MAAM;AAAA,IACN,GAAG;AAAA,EACL,CAAC;AACH;",
"names": []
}

View File

@@ -0,0 +1,274 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip57.ts
var nip57_exports = {};
__export(nip57_exports, {
getSatoshisAmountFromBolt11: () => getSatoshisAmountFromBolt11,
getZapEndpoint: () => getZapEndpoint,
makeZapReceipt: () => makeZapReceipt,
makeZapRequest: () => makeZapRequest,
useFetchImplementation: () => useFetchImplementation,
validateZapRequest: () => validateZapRequest
});
module.exports = __toCommonJS(nip57_exports);
var import_base = require("@scure/base");
// pure.ts
var import_secp256k1 = require("@noble/curves/secp256k1");
var import_utils2 = require("@noble/hashes/utils");
// core.ts
var verifiedSymbol = Symbol("verified");
var isRecord = (obj) => obj instanceof Object;
function validateEvent(event) {
if (!isRecord(event))
return false;
if (typeof event.kind !== "number")
return false;
if (typeof event.content !== "string")
return false;
if (typeof event.created_at !== "number")
return false;
if (typeof event.pubkey !== "string")
return false;
if (!event.pubkey.match(/^[a-f0-9]{64}$/))
return false;
if (!Array.isArray(event.tags))
return false;
for (let i2 = 0; i2 < event.tags.length; i2++) {
let tag = event.tags[i2];
if (!Array.isArray(tag))
return false;
for (let j = 0; j < tag.length; j++) {
if (typeof tag[j] !== "string")
return false;
}
}
return true;
}
// pure.ts
var import_sha256 = require("@noble/hashes/sha256");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// pure.ts
var JS = class {
generateSecretKey() {
return import_secp256k1.schnorr.utils.randomPrivateKey();
}
getPublicKey(secretKey) {
return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
}
finalizeEvent(t, secretKey) {
const event = t;
event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
event.id = getEventHash(event);
event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
event[verifiedSymbol] = true;
return event;
}
verifyEvent(event) {
if (typeof event[verifiedSymbol] === "boolean")
return event[verifiedSymbol];
const hash = getEventHash(event);
if (hash !== event.id) {
event[verifiedSymbol] = false;
return false;
}
try {
const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
event[verifiedSymbol] = valid;
return valid;
} catch (err) {
event[verifiedSymbol] = false;
return false;
}
}
};
function serializeEvent(evt) {
if (!validateEvent(evt))
throw new Error("can't serialize event with wrong or missing properties");
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
}
function getEventHash(event) {
let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
return (0, import_utils2.bytesToHex)(eventHash);
}
var i = new JS();
var generateSecretKey = i.generateSecretKey;
var getPublicKey = i.getPublicKey;
var finalizeEvent = i.finalizeEvent;
var verifyEvent = i.verifyEvent;
// kinds.ts
function isReplaceableKind(kind) {
return [0, 3].includes(kind) || 1e4 <= kind && kind < 2e4;
}
function isAddressableKind(kind) {
return 3e4 <= kind && kind < 4e4;
}
// nip57.ts
var _fetch;
try {
_fetch = fetch;
} catch {
}
function useFetchImplementation(fetchImplementation) {
_fetch = fetchImplementation;
}
async function getZapEndpoint(metadata) {
try {
let lnurl = "";
let { lud06, lud16 } = JSON.parse(metadata.content);
if (lud06) {
let { words } = import_base.bech32.decode(lud06, 1e3);
let data = import_base.bech32.fromWords(words);
lnurl = utf8Decoder.decode(data);
} else if (lud16) {
let [name, domain] = lud16.split("@");
lnurl = new URL(`/.well-known/lnurlp/${name}`, `https://${domain}`).toString();
} else {
return null;
}
let res = await _fetch(lnurl);
let body = await res.json();
if (body.allowsNostr && body.nostrPubkey) {
return body.callback;
}
} catch (err) {
}
return null;
}
function makeZapRequest(params) {
let zr = {
kind: 9734,
created_at: Math.round(Date.now() / 1e3),
content: params.comment || "",
tags: [
["p", "pubkey" in params ? params.pubkey : params.event.pubkey],
["amount", params.amount.toString()],
["relays", ...params.relays]
]
};
if ("event" in params) {
zr.tags.push(["e", params.event.id]);
if (isReplaceableKind(params.event.kind)) {
const a = ["a", `${params.event.kind}:${params.event.pubkey}:`];
zr.tags.push(a);
} else if (isAddressableKind(params.event.kind)) {
let d = params.event.tags.find(([t, v]) => t === "d" && v);
if (!d)
throw new Error("d tag not found or is empty");
const a = ["a", `${params.event.kind}:${params.event.pubkey}:${d[1]}`];
zr.tags.push(a);
}
zr.tags.push(["k", params.event.kind.toString()]);
}
return zr;
}
function validateZapRequest(zapRequestString) {
let zapRequest;
try {
zapRequest = JSON.parse(zapRequestString);
} catch (err) {
return "Invalid zap request JSON.";
}
if (!validateEvent(zapRequest))
return "Zap request is not a valid Nostr event.";
if (!verifyEvent(zapRequest))
return "Invalid signature on zap request.";
let p = zapRequest.tags.find(([t, v]) => t === "p" && v);
if (!p)
return "Zap request doesn't have a 'p' tag.";
if (!p[1].match(/^[a-f0-9]{64}$/))
return "Zap request 'p' tag is not valid hex.";
let e = zapRequest.tags.find(([t, v]) => t === "e" && v);
if (e && !e[1].match(/^[a-f0-9]{64}$/))
return "Zap request 'e' tag is not valid hex.";
let relays = zapRequest.tags.find(([t, v]) => t === "relays" && v);
if (!relays)
return "Zap request doesn't have a 'relays' tag.";
return null;
}
function makeZapReceipt({
zapRequest,
preimage,
bolt11,
paidAt
}) {
let zr = JSON.parse(zapRequest);
let tagsFromZapRequest = zr.tags.filter(([t]) => t === "e" || t === "p" || t === "a");
let zap = {
kind: 9735,
created_at: Math.round(paidAt.getTime() / 1e3),
content: "",
tags: [...tagsFromZapRequest, ["P", zr.pubkey], ["bolt11", bolt11], ["description", zapRequest]]
};
if (preimage) {
zap.tags.push(["preimage", preimage]);
}
return zap;
}
function getSatoshisAmountFromBolt11(bolt11) {
if (bolt11.length < 50) {
return 0;
}
bolt11 = bolt11.substring(0, 50);
const idx = bolt11.lastIndexOf("1");
if (idx === -1) {
return 0;
}
const hrp = bolt11.substring(0, idx);
if (!hrp.startsWith("lnbc")) {
return 0;
}
const amount = hrp.substring(4);
if (amount.length < 1) {
return 0;
}
const char = amount[amount.length - 1];
const digit = char.charCodeAt(0) - "0".charCodeAt(0);
const isDigit = digit >= 0 && digit <= 9;
let cutPoint = amount.length - 1;
if (isDigit) {
cutPoint++;
}
if (cutPoint < 1) {
return 0;
}
const num = parseInt(amount.substring(0, cutPoint));
switch (char) {
case "m":
return num * 1e5;
case "u":
return num * 100;
case "n":
return num / 10;
case "p":
return num / 1e4;
default:
return num * 1e8;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,117 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip58.ts
var nip58_exports = {};
__export(nip58_exports, {
generateBadgeAwardEventTemplate: () => generateBadgeAwardEventTemplate,
generateBadgeDefinitionEventTemplate: () => generateBadgeDefinitionEventTemplate,
generateProfileBadgesEventTemplate: () => generateProfileBadgesEventTemplate,
validateBadgeAwardEvent: () => validateBadgeAwardEvent,
validateBadgeDefinitionEvent: () => validateBadgeDefinitionEvent,
validateProfileBadgesEvent: () => validateProfileBadgesEvent
});
module.exports = __toCommonJS(nip58_exports);
// kinds.ts
var BadgeAward = 8;
var ProfileBadges = 30008;
var BadgeDefinition = 30009;
// nip58.ts
function generateBadgeDefinitionEventTemplate({
d,
description,
image,
name,
thumbs
}) {
const tags = [["d", d]];
name && tags.push(["name", name]);
description && tags.push(["description", description]);
image && tags.push(["image", ...image]);
if (thumbs) {
for (const thumb of thumbs) {
tags.push(["thumb", ...thumb]);
}
}
const eventTemplate = {
content: "",
created_at: Math.floor(Date.now() / 1e3),
kind: BadgeDefinition,
tags
};
return eventTemplate;
}
function validateBadgeDefinitionEvent(event) {
if (event.kind !== BadgeDefinition)
return false;
const requiredTags = ["d"];
for (const tag of requiredTags) {
if (!event.tags.find(([t]) => t == tag))
return false;
}
return true;
}
function generateBadgeAwardEventTemplate({ a, p }) {
const tags = [["a", a]];
for (const _p of p) {
tags.push(["p", ..._p]);
}
const eventTemplate = {
content: "",
created_at: Math.floor(Date.now() / 1e3),
kind: BadgeAward,
tags
};
return eventTemplate;
}
function validateBadgeAwardEvent(event) {
if (event.kind !== BadgeAward)
return false;
const requiredTags = ["a", "p"];
for (const tag of requiredTags) {
if (!event.tags.find(([t]) => t == tag))
return false;
}
return true;
}
function generateProfileBadgesEventTemplate({ badges }) {
const tags = [["d", "profile_badges"]];
for (const badge of badges) {
tags.push(["a", badge.a], ["e", ...badge.e]);
}
const eventTemplate = {
content: "",
created_at: Math.floor(Date.now() / 1e3),
kind: ProfileBadges,
tags
};
return eventTemplate;
}
function validateProfileBadgesEvent(event) {
if (event.kind !== ProfileBadges)
return false;
const requiredTags = ["d"];
for (const tag of requiredTags) {
if (!event.tags.find(([t]) => t == tag))
return false;
}
return true;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,298 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip59.ts
var nip59_exports = {};
__export(nip59_exports, {
createRumor: () => createRumor,
createSeal: () => createSeal,
createWrap: () => createWrap,
unwrapEvent: () => unwrapEvent,
unwrapManyEvents: () => unwrapManyEvents,
wrapEvent: () => wrapEvent,
wrapManyEvents: () => wrapManyEvents
});
module.exports = __toCommonJS(nip59_exports);
// nip44.ts
var import_chacha = require("@noble/ciphers/chacha");
var import_utils2 = require("@noble/ciphers/utils");
var import_secp256k1 = require("@noble/curves/secp256k1");
var import_hkdf = require("@noble/hashes/hkdf");
var import_hmac = require("@noble/hashes/hmac");
var import_sha256 = require("@noble/hashes/sha256");
var import_utils3 = require("@noble/hashes/utils");
var import_base = require("@scure/base");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// nip44.ts
var minPlaintextSize = 1;
var maxPlaintextSize = 65535;
function getConversationKey(privkeyA, pubkeyB) {
const sharedX = import_secp256k1.secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
return (0, import_hkdf.extract)(import_sha256.sha256, sharedX, "nip44-v2");
}
function getMessageKeys(conversationKey, nonce) {
const keys = (0, import_hkdf.expand)(import_sha256.sha256, conversationKey, nonce, 76);
return {
chacha_key: keys.subarray(0, 32),
chacha_nonce: keys.subarray(32, 44),
hmac_key: keys.subarray(44, 76)
};
}
function calcPaddedLen(len) {
if (!Number.isSafeInteger(len) || len < 1)
throw new Error("expected positive integer");
if (len <= 32)
return 32;
const nextPower = 1 << Math.floor(Math.log2(len - 1)) + 1;
const chunk = nextPower <= 256 ? 32 : nextPower / 8;
return chunk * (Math.floor((len - 1) / chunk) + 1);
}
function writeU16BE(num) {
if (!Number.isSafeInteger(num) || num < minPlaintextSize || num > maxPlaintextSize)
throw new Error("invalid plaintext size: must be between 1 and 65535 bytes");
const arr = new Uint8Array(2);
new DataView(arr.buffer).setUint16(0, num, false);
return arr;
}
function pad(plaintext) {
const unpadded = utf8Encoder.encode(plaintext);
const unpaddedLen = unpadded.length;
const prefix = writeU16BE(unpaddedLen);
const suffix = new Uint8Array(calcPaddedLen(unpaddedLen) - unpaddedLen);
return (0, import_utils3.concatBytes)(prefix, unpadded, suffix);
}
function unpad(padded) {
const unpaddedLen = new DataView(padded.buffer).getUint16(0);
const unpadded = padded.subarray(2, 2 + unpaddedLen);
if (unpaddedLen < minPlaintextSize || unpaddedLen > maxPlaintextSize || unpadded.length !== unpaddedLen || padded.length !== 2 + calcPaddedLen(unpaddedLen))
throw new Error("invalid padding");
return utf8Decoder.decode(unpadded);
}
function hmacAad(key, message, aad) {
if (aad.length !== 32)
throw new Error("AAD associated data must be 32 bytes");
const combined = (0, import_utils3.concatBytes)(aad, message);
return (0, import_hmac.hmac)(import_sha256.sha256, key, combined);
}
function decodePayload(payload) {
if (typeof payload !== "string")
throw new Error("payload must be a valid string");
const plen = payload.length;
if (plen < 132 || plen > 87472)
throw new Error("invalid payload length: " + plen);
if (payload[0] === "#")
throw new Error("unknown encryption version");
let data;
try {
data = import_base.base64.decode(payload);
} catch (error) {
throw new Error("invalid base64: " + error.message);
}
const dlen = data.length;
if (dlen < 99 || dlen > 65603)
throw new Error("invalid data length: " + dlen);
const vers = data[0];
if (vers !== 2)
throw new Error("unknown encryption version " + vers);
return {
nonce: data.subarray(1, 33),
ciphertext: data.subarray(33, -32),
mac: data.subarray(-32)
};
}
function encrypt(plaintext, conversationKey, nonce = (0, import_utils3.randomBytes)(32)) {
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);
const padded = pad(plaintext);
const ciphertext = (0, import_chacha.chacha20)(chacha_key, chacha_nonce, padded);
const mac = hmacAad(hmac_key, ciphertext, nonce);
return import_base.base64.encode((0, import_utils3.concatBytes)(new Uint8Array([2]), nonce, ciphertext, mac));
}
function decrypt(payload, conversationKey) {
const { nonce, ciphertext, mac } = decodePayload(payload);
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);
const calculatedMac = hmacAad(hmac_key, ciphertext, nonce);
if (!(0, import_utils2.equalBytes)(calculatedMac, mac))
throw new Error("invalid MAC");
const padded = (0, import_chacha.chacha20)(chacha_key, chacha_nonce, ciphertext);
return unpad(padded);
}
// pure.ts
var import_secp256k12 = require("@noble/curves/secp256k1");
var import_utils5 = require("@noble/hashes/utils");
// core.ts
var verifiedSymbol = Symbol("verified");
var isRecord = (obj) => obj instanceof Object;
function validateEvent(event) {
if (!isRecord(event))
return false;
if (typeof event.kind !== "number")
return false;
if (typeof event.content !== "string")
return false;
if (typeof event.created_at !== "number")
return false;
if (typeof event.pubkey !== "string")
return false;
if (!event.pubkey.match(/^[a-f0-9]{64}$/))
return false;
if (!Array.isArray(event.tags))
return false;
for (let i2 = 0; i2 < event.tags.length; i2++) {
let tag = event.tags[i2];
if (!Array.isArray(tag))
return false;
for (let j = 0; j < tag.length; j++) {
if (typeof tag[j] !== "string")
return false;
}
}
return true;
}
// pure.ts
var import_sha2562 = require("@noble/hashes/sha256");
var JS = class {
generateSecretKey() {
return import_secp256k12.schnorr.utils.randomPrivateKey();
}
getPublicKey(secretKey) {
return (0, import_utils5.bytesToHex)(import_secp256k12.schnorr.getPublicKey(secretKey));
}
finalizeEvent(t, secretKey) {
const event = t;
event.pubkey = (0, import_utils5.bytesToHex)(import_secp256k12.schnorr.getPublicKey(secretKey));
event.id = getEventHash(event);
event.sig = (0, import_utils5.bytesToHex)(import_secp256k12.schnorr.sign(getEventHash(event), secretKey));
event[verifiedSymbol] = true;
return event;
}
verifyEvent(event) {
if (typeof event[verifiedSymbol] === "boolean")
return event[verifiedSymbol];
const hash = getEventHash(event);
if (hash !== event.id) {
event[verifiedSymbol] = false;
return false;
}
try {
const valid = import_secp256k12.schnorr.verify(event.sig, hash, event.pubkey);
event[verifiedSymbol] = valid;
return valid;
} catch (err) {
event[verifiedSymbol] = false;
return false;
}
}
};
function serializeEvent(evt) {
if (!validateEvent(evt))
throw new Error("can't serialize event with wrong or missing properties");
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
}
function getEventHash(event) {
let eventHash = (0, import_sha2562.sha256)(utf8Encoder.encode(serializeEvent(event)));
return (0, import_utils5.bytesToHex)(eventHash);
}
var i = new JS();
var generateSecretKey = i.generateSecretKey;
var getPublicKey = i.getPublicKey;
var finalizeEvent = i.finalizeEvent;
var verifyEvent = i.verifyEvent;
// kinds.ts
var Seal = 13;
var GiftWrap = 1059;
// nip59.ts
var TWO_DAYS = 2 * 24 * 60 * 60;
var now = () => Math.round(Date.now() / 1e3);
var randomNow = () => Math.round(now() - Math.random() * TWO_DAYS);
var nip44ConversationKey = (privateKey, publicKey) => getConversationKey(privateKey, publicKey);
var nip44Encrypt = (data, privateKey, publicKey) => encrypt(JSON.stringify(data), nip44ConversationKey(privateKey, publicKey));
var nip44Decrypt = (data, privateKey) => JSON.parse(decrypt(data.content, nip44ConversationKey(privateKey, data.pubkey)));
function createRumor(event, privateKey) {
const rumor = {
created_at: now(),
content: "",
tags: [],
...event,
pubkey: getPublicKey(privateKey)
};
rumor.id = getEventHash(rumor);
return rumor;
}
function createSeal(rumor, privateKey, recipientPublicKey) {
return finalizeEvent(
{
kind: Seal,
content: nip44Encrypt(rumor, privateKey, recipientPublicKey),
created_at: randomNow(),
tags: []
},
privateKey
);
}
function createWrap(seal, recipientPublicKey) {
const randomKey = generateSecretKey();
return finalizeEvent(
{
kind: GiftWrap,
content: nip44Encrypt(seal, randomKey, recipientPublicKey),
created_at: randomNow(),
tags: [["p", recipientPublicKey]]
},
randomKey
);
}
function wrapEvent(event, senderPrivateKey, recipientPublicKey) {
const rumor = createRumor(event, senderPrivateKey);
const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey);
return createWrap(seal, recipientPublicKey);
}
function wrapManyEvents(event, senderPrivateKey, recipientsPublicKeys) {
if (!recipientsPublicKeys || recipientsPublicKeys.length === 0) {
throw new Error("At least one recipient is required.");
}
const senderPublicKey = getPublicKey(senderPrivateKey);
const wrappeds = [wrapEvent(event, senderPrivateKey, senderPublicKey)];
recipientsPublicKeys.forEach((recipientPublicKey) => {
wrappeds.push(wrapEvent(event, senderPrivateKey, recipientPublicKey));
});
return wrappeds;
}
function unwrapEvent(wrap, recipientPrivateKey) {
const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey);
return nip44Decrypt(unwrappedSeal, recipientPrivateKey);
}
function unwrapManyEvents(wrappedEvents, recipientPrivateKey) {
let unwrappedEvents = [];
wrappedEvents.forEach((e) => {
unwrappedEvents.push(unwrapEvent(e, recipientPrivateKey));
});
unwrappedEvents.sort((a, b) => a.created_at - b.created_at);
return unwrappedEvents;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,70 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip75.ts
var nip75_exports = {};
__export(nip75_exports, {
generateGoalEventTemplate: () => generateGoalEventTemplate,
validateZapGoalEvent: () => validateZapGoalEvent
});
module.exports = __toCommonJS(nip75_exports);
// kinds.ts
var ZapGoal = 9041;
// nip75.ts
function generateGoalEventTemplate({
amount,
content,
relays,
a,
closedAt,
image,
r,
summary,
zapTags
}) {
const tags = [
["amount", amount],
["relays", ...relays]
];
closedAt && tags.push(["closed_at", closedAt.toString()]);
image && tags.push(["image", image]);
summary && tags.push(["summary", summary]);
r && tags.push(["r", r]);
a && tags.push(["a", a]);
zapTags && tags.push(...zapTags);
const eventTemplate = {
created_at: Math.floor(Date.now() / 1e3),
kind: ZapGoal,
content,
tags
};
return eventTemplate;
}
function validateZapGoalEvent(event) {
if (event.kind !== ZapGoal)
return false;
const requiredTags = ["amount", "relays"];
for (const tag of requiredTags) {
if (!event.tags.find(([t]) => t == tag))
return false;
}
return true;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,142 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip94.ts
var nip94_exports = {};
__export(nip94_exports, {
generateEventTemplate: () => generateEventTemplate,
parseEvent: () => parseEvent,
validateEvent: () => validateEvent
});
module.exports = __toCommonJS(nip94_exports);
// kinds.ts
var FileMetadata = 1063;
// nip94.ts
function generateEventTemplate(fileMetadata) {
const eventTemplate = {
content: fileMetadata.content,
created_at: Math.floor(Date.now() / 1e3),
kind: FileMetadata,
tags: [
["url", fileMetadata.url],
["m", fileMetadata.m],
["x", fileMetadata.x],
["ox", fileMetadata.ox]
]
};
if (fileMetadata.size)
eventTemplate.tags.push(["size", fileMetadata.size]);
if (fileMetadata.dim)
eventTemplate.tags.push(["dim", fileMetadata.dim]);
if (fileMetadata.i)
eventTemplate.tags.push(["i", fileMetadata.i]);
if (fileMetadata.blurhash)
eventTemplate.tags.push(["blurhash", fileMetadata.blurhash]);
if (fileMetadata.thumb)
eventTemplate.tags.push(["thumb", fileMetadata.thumb]);
if (fileMetadata.image)
eventTemplate.tags.push(["image", fileMetadata.image]);
if (fileMetadata.summary)
eventTemplate.tags.push(["summary", fileMetadata.summary]);
if (fileMetadata.alt)
eventTemplate.tags.push(["alt", fileMetadata.alt]);
if (fileMetadata.fallback)
fileMetadata.fallback.forEach((url) => eventTemplate.tags.push(["fallback", url]));
return eventTemplate;
}
function validateEvent(event) {
if (event.kind !== FileMetadata)
return false;
if (!event.content)
return false;
const requiredTags = ["url", "m", "x", "ox"];
for (const tag of requiredTags) {
if (!event.tags.find(([t]) => t == tag))
return false;
}
const sizeTag = event.tags.find(([t]) => t == "size");
if (sizeTag && isNaN(Number(sizeTag[1])))
return false;
const dimTag = event.tags.find(([t]) => t == "dim");
if (dimTag && !dimTag[1].match(/^\d+x\d+$/))
return false;
return true;
}
function parseEvent(event) {
if (!validateEvent(event)) {
throw new Error("Invalid event");
}
const fileMetadata = {
content: event.content,
url: "",
m: "",
x: "",
ox: ""
};
for (const [tag, value] of event.tags) {
switch (tag) {
case "url":
fileMetadata.url = value;
break;
case "m":
fileMetadata.m = value;
break;
case "x":
fileMetadata.x = value;
break;
case "ox":
fileMetadata.ox = value;
break;
case "size":
fileMetadata.size = value;
break;
case "dim":
fileMetadata.dim = value;
break;
case "magnet":
fileMetadata.magnet = value;
break;
case "i":
fileMetadata.i = value;
break;
case "blurhash":
fileMetadata.blurhash = value;
break;
case "thumb":
fileMetadata.thumb = value;
break;
case "image":
fileMetadata.image = value;
break;
case "summary":
fileMetadata.summary = value;
break;
case "alt":
fileMetadata.alt = value;
break;
case "fallback":
fileMetadata.fallback ??= [];
fileMetadata.fallback.push(value);
break;
}
}
return fileMetadata;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,230 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip98.ts
var nip98_exports = {};
__export(nip98_exports, {
getToken: () => getToken,
hashPayload: () => hashPayload,
unpackEventFromToken: () => unpackEventFromToken,
validateEvent: () => validateEvent2,
validateEventKind: () => validateEventKind,
validateEventMethodTag: () => validateEventMethodTag,
validateEventPayloadTag: () => validateEventPayloadTag,
validateEventTimestamp: () => validateEventTimestamp,
validateEventUrlTag: () => validateEventUrlTag,
validateToken: () => validateToken
});
module.exports = __toCommonJS(nip98_exports);
var import_sha2562 = require("@noble/hashes/sha256");
var import_utils4 = require("@noble/hashes/utils");
var import_base = require("@scure/base");
// pure.ts
var import_secp256k1 = require("@noble/curves/secp256k1");
var import_utils2 = require("@noble/hashes/utils");
// core.ts
var verifiedSymbol = Symbol("verified");
var isRecord = (obj) => obj instanceof Object;
function validateEvent(event) {
if (!isRecord(event))
return false;
if (typeof event.kind !== "number")
return false;
if (typeof event.content !== "string")
return false;
if (typeof event.created_at !== "number")
return false;
if (typeof event.pubkey !== "string")
return false;
if (!event.pubkey.match(/^[a-f0-9]{64}$/))
return false;
if (!Array.isArray(event.tags))
return false;
for (let i2 = 0; i2 < event.tags.length; i2++) {
let tag = event.tags[i2];
if (!Array.isArray(tag))
return false;
for (let j = 0; j < tag.length; j++) {
if (typeof tag[j] !== "string")
return false;
}
}
return true;
}
// pure.ts
var import_sha256 = require("@noble/hashes/sha256");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// pure.ts
var JS = class {
generateSecretKey() {
return import_secp256k1.schnorr.utils.randomPrivateKey();
}
getPublicKey(secretKey) {
return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
}
finalizeEvent(t, secretKey) {
const event = t;
event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
event.id = getEventHash(event);
event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
event[verifiedSymbol] = true;
return event;
}
verifyEvent(event) {
if (typeof event[verifiedSymbol] === "boolean")
return event[verifiedSymbol];
const hash = getEventHash(event);
if (hash !== event.id) {
event[verifiedSymbol] = false;
return false;
}
try {
const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
event[verifiedSymbol] = valid;
return valid;
} catch (err) {
event[verifiedSymbol] = false;
return false;
}
}
};
function serializeEvent(evt) {
if (!validateEvent(evt))
throw new Error("can't serialize event with wrong or missing properties");
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
}
function getEventHash(event) {
let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
return (0, import_utils2.bytesToHex)(eventHash);
}
var i = new JS();
var generateSecretKey = i.generateSecretKey;
var getPublicKey = i.getPublicKey;
var finalizeEvent = i.finalizeEvent;
var verifyEvent = i.verifyEvent;
// kinds.ts
var HTTPAuth = 27235;
// nip98.ts
var _authorizationScheme = "Nostr ";
async function getToken(loginUrl, httpMethod, sign, includeAuthorizationScheme = false, payload) {
const event = {
kind: HTTPAuth,
tags: [
["u", loginUrl],
["method", httpMethod]
],
created_at: Math.round(new Date().getTime() / 1e3),
content: ""
};
if (payload) {
event.tags.push(["payload", hashPayload(payload)]);
}
const signedEvent = await sign(event);
const authorizationScheme = includeAuthorizationScheme ? _authorizationScheme : "";
return authorizationScheme + import_base.base64.encode(utf8Encoder.encode(JSON.stringify(signedEvent)));
}
async function validateToken(token, url, method) {
const event = await unpackEventFromToken(token).catch((error) => {
throw error;
});
const valid = await validateEvent2(event, url, method).catch((error) => {
throw error;
});
return valid;
}
async function unpackEventFromToken(token) {
if (!token) {
throw new Error("Missing token");
}
token = token.replace(_authorizationScheme, "");
const eventB64 = utf8Decoder.decode(import_base.base64.decode(token));
if (!eventB64 || eventB64.length === 0 || !eventB64.startsWith("{")) {
throw new Error("Invalid token");
}
const event = JSON.parse(eventB64);
return event;
}
function validateEventTimestamp(event) {
if (!event.created_at) {
return false;
}
return Math.round(new Date().getTime() / 1e3) - event.created_at < 60;
}
function validateEventKind(event) {
return event.kind === HTTPAuth;
}
function validateEventUrlTag(event, url) {
const urlTag = event.tags.find((t) => t[0] === "u");
if (!urlTag) {
return false;
}
return urlTag.length > 0 && urlTag[1] === url;
}
function validateEventMethodTag(event, method) {
const methodTag = event.tags.find((t) => t[0] === "method");
if (!methodTag) {
return false;
}
return methodTag.length > 0 && methodTag[1].toLowerCase() === method.toLowerCase();
}
function hashPayload(payload) {
const hash = (0, import_sha2562.sha256)(utf8Encoder.encode(JSON.stringify(payload)));
return (0, import_utils4.bytesToHex)(hash);
}
function validateEventPayloadTag(event, payload) {
const payloadTag = event.tags.find((t) => t[0] === "payload");
if (!payloadTag) {
return false;
}
const payloadHash = hashPayload(payload);
return payloadTag.length > 0 && payloadTag[1] === payloadHash;
}
async function validateEvent2(event, url, method, body) {
if (!verifyEvent(event)) {
throw new Error("Invalid nostr event, signature invalid");
}
if (!validateEventKind(event)) {
throw new Error("Invalid nostr event, kind invalid");
}
if (!validateEventTimestamp(event)) {
throw new Error("Invalid nostr event, created_at timestamp invalid");
}
if (!validateEventUrlTag(event, url)) {
throw new Error("Invalid nostr event, url tag invalid");
}
if (!validateEventMethodTag(event, method)) {
throw new Error("Invalid nostr event, method tag invalid");
}
if (Boolean(body) && typeof body === "object" && Object.keys(body).length > 0) {
if (!validateEventPayloadTag(event, body)) {
throw new Error("Invalid nostr event, payload tag does not match request body hash");
}
}
return true;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,153 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nip99.ts
var nip99_exports = {};
__export(nip99_exports, {
generateEventTemplate: () => generateEventTemplate,
parseEvent: () => parseEvent,
validateEvent: () => validateEvent
});
module.exports = __toCommonJS(nip99_exports);
// kinds.ts
var ClassifiedListing = 30402;
var DraftClassifiedListing = 30403;
// nip99.ts
function validateEvent(event) {
if (![ClassifiedListing, DraftClassifiedListing].includes(event.kind))
return false;
const requiredTags = ["d", "title", "summary", "location", "published_at", "price"];
const requiredTagCount = requiredTags.length;
const tagCounts = {};
if (event.tags.length < requiredTagCount)
return false;
for (const tag of event.tags) {
if (tag.length < 2)
return false;
const [tagName, ...tagValues] = tag;
if (tagName == "published_at") {
const timestamp = parseInt(tagValues[0]);
if (isNaN(timestamp))
return false;
} else if (tagName == "price") {
if (tagValues.length < 2)
return false;
const price = parseInt(tagValues[0]);
if (isNaN(price) || tagValues[1].length != 3)
return false;
} else if ((tagName == "e" || tagName == "a") && tag.length != 3) {
return false;
}
if (requiredTags.includes(tagName)) {
tagCounts[tagName] = (tagCounts[tagName] || 0) + 1;
}
}
return Object.values(tagCounts).every((count) => count == 1) && Object.keys(tagCounts).length == requiredTagCount;
}
function parseEvent(event) {
if (!validateEvent(event)) {
throw new Error("Invalid event");
}
const listing = {
isDraft: event.kind === DraftClassifiedListing,
title: "",
summary: "",
content: event.content,
publishedAt: "",
location: "",
price: {
amount: "",
currency: ""
},
images: [],
hashtags: [],
additionalTags: {}
};
for (let i = 0; i < event.tags.length; i++) {
const tag = event.tags[i];
const [tagName, ...tagValues] = tag;
if (tagName == "title") {
listing.title = tagValues[0];
} else if (tagName == "summary") {
listing.summary = tagValues[0];
} else if (tagName == "published_at") {
listing.publishedAt = tagValues[0];
} else if (tagName == "location") {
listing.location = tagValues[0];
} else if (tagName == "price") {
listing.price.amount = tagValues[0];
listing.price.currency = tagValues[1];
if (tagValues.length == 3) {
listing.price.frequency = tagValues[2];
}
} else if (tagName == "image") {
listing.images.push({
url: tagValues[0],
dimensions: tagValues?.[1] ?? void 0
});
} else if (tagName == "t") {
listing.hashtags.push(tagValues[0]);
} else if (tagName == "e" || tagName == "a") {
listing.additionalTags[tagName] = [...tagValues];
}
}
return listing;
}
function generateEventTemplate(listing) {
const priceTag = ["price", listing.price.amount, listing.price.currency];
if (listing.price.frequency)
priceTag.push(listing.price.frequency);
const tags = [
["d", listing.title.trim().toLowerCase().replace(/ /g, "-")],
["title", listing.title],
["published_at", listing.publishedAt],
["summary", listing.summary],
["location", listing.location],
priceTag
];
for (let i = 0; i < listing.images.length; i++) {
const image = listing.images[i];
const imageTag = ["image", image.url];
if (image.dimensions)
imageTag.push(image.dimensions);
tags.push(imageTag);
}
for (let i = 0; i < listing.hashtags.length; i++) {
const t = listing.hashtags[i];
tags.push(["t", t]);
}
for (const [key, value] of Object.entries(listing.additionalTags)) {
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
const val = value[i];
tags.push([key, val]);
}
} else {
tags.push([key, value]);
}
}
return {
kind: listing.isDraft ? DraftClassifiedListing : ClassifiedListing,
content: listing.content,
tags,
created_at: Math.floor(Date.now() / 1e3)
};
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,184 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// nipb7.ts
var nipb7_exports = {};
__export(nipb7_exports, {
BlossomClient: () => BlossomClient
});
module.exports = __toCommonJS(nipb7_exports);
var import_sha256 = require("@noble/hashes/sha256");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// nipb7.ts
var BlossomClient = class {
mediaserver;
signer;
constructor(mediaserver, signer) {
if (!mediaserver.startsWith("http")) {
mediaserver = "https://" + mediaserver;
}
this.mediaserver = mediaserver.replace(/\/$/, "") + "/";
this.signer = signer;
}
async httpCall(method, url, contentType, addAuthorization, body, result) {
const headers = {};
if (contentType) {
headers["Content-Type"] = contentType;
}
if (addAuthorization) {
const auth = await addAuthorization();
if (auth) {
headers["Authorization"] = auth;
}
}
const response = await fetch(this.mediaserver + url, {
method,
headers,
body
});
if (response.status >= 300) {
const reason = response.headers.get("X-Reason") || response.statusText;
throw new Error(`${url} returned an error (${response.status}): ${reason}`);
}
if (result !== null && response.headers.get("content-type")?.includes("application/json")) {
return await response.json();
}
return response;
}
async authorizationHeader(modify) {
const now = Math.floor(Date.now() / 1e3);
const event = {
created_at: now,
kind: 24242,
content: "blossom stuff",
tags: [["expiration", String(now + 60)]]
};
if (modify) {
modify(event);
}
try {
const signedEvent = await this.signer.signEvent(event);
const eventJson = JSON.stringify(signedEvent);
return "Nostr " + btoa(eventJson);
} catch (error) {
return "";
}
}
isValid32ByteHex(hash) {
return /^[a-f0-9]{64}$/i.test(hash);
}
async check(hash) {
if (!this.isValid32ByteHex(hash)) {
throw new Error(`${hash} is not a valid 32-byte hex string`);
}
try {
await this.httpCall("HEAD", hash);
} catch (error) {
throw new Error(`failed to check for ${hash}: ${error}`);
}
}
async uploadBlob(file, contentType) {
const hash = (0, import_utils.bytesToHex)((0, import_sha256.sha256)(new Uint8Array(await file.arrayBuffer())));
const actualContentType = contentType || file.type || "application/octet-stream";
const bd = await this.httpCall(
"PUT",
"upload",
actualContentType,
() => this.authorizationHeader((evt) => {
evt.tags.push(["t", "upload"]);
evt.tags.push(["x", hash]);
}),
file,
{}
);
return bd;
}
async uploadFile(file) {
return this.uploadBlob(file, file.type);
}
async download(hash) {
if (!this.isValid32ByteHex(hash)) {
throw new Error(`${hash} is not a valid 32-byte hex string`);
}
const authHeader = await this.authorizationHeader((evt) => {
evt.tags.push(["t", "get"]);
evt.tags.push(["x", hash]);
});
const response = await fetch(this.mediaserver + hash, {
method: "GET",
headers: {
Authorization: authHeader
}
});
if (response.status >= 300) {
throw new Error(`${hash} is not present in ${this.mediaserver}: ${response.status}`);
}
return await response.arrayBuffer();
}
async downloadAsBlob(hash) {
const arrayBuffer = await this.download(hash);
return new Blob([arrayBuffer]);
}
async list() {
const pubkey = await this.signer.getPublicKey();
if (!this.isValid32ByteHex(pubkey)) {
throw new Error(`pubkey ${pubkey} is not valid`);
}
try {
const bds = await this.httpCall(
"GET",
`list/${pubkey}`,
void 0,
() => this.authorizationHeader((evt) => {
evt.tags.push(["t", "list"]);
}),
void 0,
[]
);
return bds;
} catch (error) {
throw new Error(`failed to list blobs: ${error}`);
}
}
async delete(hash) {
if (!this.isValid32ByteHex(hash)) {
throw new Error(`${hash} is not a valid 32-byte hex string`);
}
try {
await this.httpCall(
"DELETE",
hash,
void 0,
() => this.authorizationHeader((evt) => {
evt.tags.push(["t", "delete"]);
evt.tags.push(["x", hash]);
}),
void 0,
null
);
} catch (error) {
throw new Error(`failed to delete ${hash}: ${error}`);
}
}
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"type":"commonjs"}

896
thrower_daemon/node_modules/nostr-tools/lib/cjs/pool.js generated vendored Normal file
View File

@@ -0,0 +1,896 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// pool.ts
var pool_exports = {};
__export(pool_exports, {
AbstractSimplePool: () => AbstractSimplePool,
SimplePool: () => SimplePool,
useWebSocketImplementation: () => useWebSocketImplementation
});
module.exports = __toCommonJS(pool_exports);
// pure.ts
var import_secp256k1 = require("@noble/curves/secp256k1");
var import_utils2 = require("@noble/hashes/utils");
// core.ts
var verifiedSymbol = Symbol("verified");
var isRecord = (obj) => obj instanceof Object;
function validateEvent(event) {
if (!isRecord(event))
return false;
if (typeof event.kind !== "number")
return false;
if (typeof event.content !== "string")
return false;
if (typeof event.created_at !== "number")
return false;
if (typeof event.pubkey !== "string")
return false;
if (!event.pubkey.match(/^[a-f0-9]{64}$/))
return false;
if (!Array.isArray(event.tags))
return false;
for (let i2 = 0; i2 < event.tags.length; i2++) {
let tag = event.tags[i2];
if (!Array.isArray(tag))
return false;
for (let j = 0; j < tag.length; j++) {
if (typeof tag[j] !== "string")
return false;
}
}
return true;
}
// pure.ts
var import_sha256 = require("@noble/hashes/sha256");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
function normalizeURL(url) {
try {
if (url.indexOf("://") === -1)
url = "wss://" + url;
let p = new URL(url);
p.pathname = p.pathname.replace(/\/+/g, "/");
if (p.pathname.endsWith("/"))
p.pathname = p.pathname.slice(0, -1);
if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:")
p.port = "";
p.searchParams.sort();
p.hash = "";
return p.toString();
} catch (e) {
throw new Error(`Invalid URL: ${url}`);
}
}
var QueueNode = class {
value;
next = null;
prev = null;
constructor(message) {
this.value = message;
}
};
var Queue = class {
first;
last;
constructor() {
this.first = null;
this.last = null;
}
enqueue(value) {
const newNode = new QueueNode(value);
if (!this.last) {
this.first = newNode;
this.last = newNode;
} else if (this.last === this.first) {
this.last = newNode;
this.last.prev = this.first;
this.first.next = newNode;
} else {
newNode.prev = this.last;
this.last.next = newNode;
this.last = newNode;
}
return true;
}
dequeue() {
if (!this.first)
return null;
if (this.first === this.last) {
const target2 = this.first;
this.first = null;
this.last = null;
return target2.value;
}
const target = this.first;
this.first = target.next;
if (this.first) {
this.first.prev = null;
}
return target.value;
}
};
// pure.ts
var JS = class {
generateSecretKey() {
return import_secp256k1.schnorr.utils.randomPrivateKey();
}
getPublicKey(secretKey) {
return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
}
finalizeEvent(t, secretKey) {
const event = t;
event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
event.id = getEventHash(event);
event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
event[verifiedSymbol] = true;
return event;
}
verifyEvent(event) {
if (typeof event[verifiedSymbol] === "boolean")
return event[verifiedSymbol];
const hash = getEventHash(event);
if (hash !== event.id) {
event[verifiedSymbol] = false;
return false;
}
try {
const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
event[verifiedSymbol] = valid;
return valid;
} catch (err) {
event[verifiedSymbol] = false;
return false;
}
}
};
function serializeEvent(evt) {
if (!validateEvent(evt))
throw new Error("can't serialize event with wrong or missing properties");
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
}
function getEventHash(event) {
let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
return (0, import_utils2.bytesToHex)(eventHash);
}
var i = new JS();
var generateSecretKey = i.generateSecretKey;
var getPublicKey = i.getPublicKey;
var finalizeEvent = i.finalizeEvent;
var verifyEvent = i.verifyEvent;
// kinds.ts
var ClientAuth = 22242;
// filter.ts
function matchFilter(filter, event) {
if (filter.ids && filter.ids.indexOf(event.id) === -1) {
return false;
}
if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {
return false;
}
if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {
return false;
}
for (let f in filter) {
if (f[0] === "#") {
let tagName = f.slice(1);
let values = filter[`#${tagName}`];
if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values.indexOf(v) !== -1))
return false;
}
}
if (filter.since && event.created_at < filter.since)
return false;
if (filter.until && event.created_at > filter.until)
return false;
return true;
}
function matchFilters(filters, event) {
for (let i2 = 0; i2 < filters.length; i2++) {
if (matchFilter(filters[i2], event)) {
return true;
}
}
return false;
}
// fakejson.ts
function getHex64(json, field) {
let len = field.length + 3;
let idx = json.indexOf(`"${field}":`) + len;
let s = json.slice(idx).indexOf(`"`) + idx + 1;
return json.slice(s, s + 64);
}
function getSubscriptionId(json) {
let idx = json.slice(0, 22).indexOf(`"EVENT"`);
if (idx === -1)
return null;
let pstart = json.slice(idx + 7 + 1).indexOf(`"`);
if (pstart === -1)
return null;
let start = idx + 7 + 1 + pstart;
let pend = json.slice(start + 1, 80).indexOf(`"`);
if (pend === -1)
return null;
let end = start + 1 + pend;
return json.slice(start + 1, end);
}
// nip42.ts
function makeAuthEvent(relayURL, challenge) {
return {
kind: ClientAuth,
created_at: Math.floor(Date.now() / 1e3),
tags: [
["relay", relayURL],
["challenge", challenge]
],
content: ""
};
}
// helpers.ts
async function yieldThread() {
return new Promise((resolve) => {
const ch = new MessageChannel();
const handler = () => {
ch.port1.removeEventListener("message", handler);
resolve();
};
ch.port1.addEventListener("message", handler);
ch.port2.postMessage(0);
ch.port1.start();
});
}
var alwaysTrue = (t) => {
t[verifiedSymbol] = true;
return true;
};
// abstract-relay.ts
var SendingOnClosedConnection = class extends Error {
constructor(message, relay) {
super(`Tried to send message '${message} on a closed connection to ${relay}.`);
this.name = "SendingOnClosedConnection";
}
};
var AbstractRelay = class {
url;
_connected = false;
onclose = null;
onnotice = (msg) => console.debug(`NOTICE from ${this.url}: ${msg}`);
baseEoseTimeout = 4400;
connectionTimeout = 4400;
publishTimeout = 4400;
pingFrequency = 2e4;
pingTimeout = 2e4;
openSubs = /* @__PURE__ */ new Map();
enablePing;
connectionTimeoutHandle;
connectionPromise;
openCountRequests = /* @__PURE__ */ new Map();
openEventPublishes = /* @__PURE__ */ new Map();
ws;
incomingMessageQueue = new Queue();
queueRunning = false;
challenge;
authPromise;
serial = 0;
verifyEvent;
_WebSocket;
constructor(url, opts) {
this.url = normalizeURL(url);
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation || WebSocket;
this.enablePing = opts.enablePing;
}
static async connect(url, opts) {
const relay = new AbstractRelay(url, opts);
await relay.connect();
return relay;
}
closeAllSubscriptions(reason) {
for (let [_, sub] of this.openSubs) {
sub.close(reason);
}
this.openSubs.clear();
for (let [_, ep] of this.openEventPublishes) {
ep.reject(new Error(reason));
}
this.openEventPublishes.clear();
for (let [_, cr] of this.openCountRequests) {
cr.reject(new Error(reason));
}
this.openCountRequests.clear();
}
get connected() {
return this._connected;
}
async connect() {
if (this.connectionPromise)
return this.connectionPromise;
this.challenge = void 0;
this.authPromise = void 0;
this.connectionPromise = new Promise((resolve, reject) => {
this.connectionTimeoutHandle = setTimeout(() => {
reject("connection timed out");
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection timed out");
}, this.connectionTimeout);
try {
this.ws = new this._WebSocket(this.url);
} catch (err) {
clearTimeout(this.connectionTimeoutHandle);
reject(err);
return;
}
this.ws.onopen = () => {
clearTimeout(this.connectionTimeoutHandle);
this._connected = true;
if (this.enablePing) {
this.pingpong();
}
resolve();
};
this.ws.onerror = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket error");
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
};
this.ws.onclose = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket closed");
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
};
this.ws.onmessage = this._onmessage.bind(this);
});
return this.connectionPromise;
}
async waitForPingPong() {
return new Promise((res, err) => {
;
this.ws && this.ws.on && this.ws.on("pong", () => res(true)) || err("ws can't listen for pong");
this.ws && this.ws.ping && this.ws.ping();
});
}
async waitForDummyReq() {
return new Promise((resolve, _) => {
const sub = this.subscribe([{ ids: ["a".repeat(64)] }], {
oneose: () => {
sub.close();
resolve(true);
},
eoseTimeout: this.pingTimeout + 1e3
});
});
}
async pingpong() {
if (this.ws?.readyState === 1) {
const result = await Promise.any([
this.ws && this.ws.ping && this.ws.on ? this.waitForPingPong() : this.waitForDummyReq(),
new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
]);
if (result) {
setTimeout(() => this.pingpong(), this.pingFrequency);
} else {
this.closeAllSubscriptions("pingpong timed out");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
}
}
async runQueue() {
this.queueRunning = true;
while (true) {
if (false === this.handleNext()) {
break;
}
await yieldThread();
}
this.queueRunning = false;
}
handleNext() {
const json = this.incomingMessageQueue.dequeue();
if (!json) {
return false;
}
const subid = getSubscriptionId(json);
if (subid) {
const so = this.openSubs.get(subid);
if (!so) {
return;
}
const id = getHex64(json, "id");
const alreadyHave = so.alreadyHaveEvent?.(id);
so.receivedEvent?.(this, id);
if (alreadyHave) {
return;
}
}
try {
let data = JSON.parse(json);
switch (data[0]) {
case "EVENT": {
const so = this.openSubs.get(data[1]);
const event = data[2];
if (this.verifyEvent(event) && matchFilters(so.filters, event)) {
so.onevent(event);
}
return;
}
case "COUNT": {
const id = data[1];
const payload = data[2];
const cr = this.openCountRequests.get(id);
if (cr) {
cr.resolve(payload.count);
this.openCountRequests.delete(id);
}
return;
}
case "EOSE": {
const so = this.openSubs.get(data[1]);
if (!so)
return;
so.receivedEose();
return;
}
case "OK": {
const id = data[1];
const ok = data[2];
const reason = data[3];
const ep = this.openEventPublishes.get(id);
if (ep) {
clearTimeout(ep.timeout);
if (ok)
ep.resolve(reason);
else
ep.reject(new Error(reason));
this.openEventPublishes.delete(id);
}
return;
}
case "CLOSED": {
const id = data[1];
const so = this.openSubs.get(id);
if (!so)
return;
so.closed = true;
so.close(data[2]);
return;
}
case "NOTICE":
this.onnotice(data[1]);
return;
case "AUTH": {
this.challenge = data[1];
return;
}
}
} catch (err) {
return;
}
}
async send(message) {
if (!this.connectionPromise)
throw new SendingOnClosedConnection(message, this.url);
this.connectionPromise.then(() => {
this.ws?.send(message);
});
}
async auth(signAuthEvent) {
const challenge = this.challenge;
if (!challenge)
throw new Error("can't perform auth, no challenge was received");
if (this.authPromise)
return this.authPromise;
this.authPromise = new Promise(async (resolve, reject) => {
try {
let evt = await signAuthEvent(makeAuthEvent(this.url, challenge));
let timeout = setTimeout(() => {
let ep = this.openEventPublishes.get(evt.id);
if (ep) {
ep.reject(new Error("auth timed out"));
this.openEventPublishes.delete(evt.id);
}
}, this.publishTimeout);
this.openEventPublishes.set(evt.id, { resolve, reject, timeout });
this.send('["AUTH",' + JSON.stringify(evt) + "]");
} catch (err) {
console.warn("subscribe auth function failed:", err);
}
});
return this.authPromise;
}
async publish(event) {
const ret = new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
const ep = this.openEventPublishes.get(event.id);
if (ep) {
ep.reject(new Error("publish timed out"));
this.openEventPublishes.delete(event.id);
}
}, this.publishTimeout);
this.openEventPublishes.set(event.id, { resolve, reject, timeout });
});
this.send('["EVENT",' + JSON.stringify(event) + "]");
return ret;
}
async count(filters, params) {
this.serial++;
const id = params?.id || "count:" + this.serial;
const ret = new Promise((resolve, reject) => {
this.openCountRequests.set(id, { resolve, reject });
});
this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1));
return ret;
}
subscribe(filters, params) {
const subscription = this.prepareSubscription(filters, params);
subscription.fire();
return subscription;
}
prepareSubscription(filters, params) {
this.serial++;
const id = params.id || (params.label ? params.label + ":" : "sub:") + this.serial;
const subscription = new Subscription(this, id, filters, params);
this.openSubs.set(id, subscription);
return subscription;
}
close() {
this.closeAllSubscriptions("relay connection closed by us");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
_onmessage(ev) {
this.incomingMessageQueue.enqueue(ev.data);
if (!this.queueRunning) {
this.runQueue();
}
}
};
var Subscription = class {
relay;
id;
closed = false;
eosed = false;
filters;
alreadyHaveEvent;
receivedEvent;
onevent;
oneose;
onclose;
eoseTimeout;
eoseTimeoutHandle;
constructor(relay, id, filters, params) {
this.relay = relay;
this.filters = filters;
this.id = id;
this.alreadyHaveEvent = params.alreadyHaveEvent;
this.receivedEvent = params.receivedEvent;
this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout;
this.oneose = params.oneose;
this.onclose = params.onclose;
this.onevent = params.onevent || ((event) => {
console.warn(
`onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,
event
);
});
}
fire() {
this.relay.send('["REQ","' + this.id + '",' + JSON.stringify(this.filters).substring(1));
this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout);
}
receivedEose() {
if (this.eosed)
return;
clearTimeout(this.eoseTimeoutHandle);
this.eosed = true;
this.oneose?.();
}
close(reason = "closed by caller") {
if (!this.closed && this.relay.connected) {
try {
this.relay.send('["CLOSE",' + JSON.stringify(this.id) + "]");
} catch (err) {
if (err instanceof SendingOnClosedConnection) {
} else {
throw err;
}
}
this.closed = true;
}
this.relay.openSubs.delete(this.id);
this.onclose?.(reason);
}
};
// abstract-pool.ts
var AbstractSimplePool = class {
relays = /* @__PURE__ */ new Map();
seenOn = /* @__PURE__ */ new Map();
trackRelays = false;
verifyEvent;
enablePing;
trustedRelayURLs = /* @__PURE__ */ new Set();
_WebSocket;
constructor(opts) {
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation;
this.enablePing = opts.enablePing;
}
async ensureRelay(url, params) {
url = normalizeURL(url);
let relay = this.relays.get(url);
if (!relay) {
relay = new AbstractRelay(url, {
verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,
websocketImplementation: this._WebSocket,
enablePing: this.enablePing
});
relay.onclose = () => {
this.relays.delete(url);
};
if (params?.connectionTimeout)
relay.connectionTimeout = params.connectionTimeout;
this.relays.set(url, relay);
}
await relay.connect();
return relay;
}
close(relays) {
relays.map(normalizeURL).forEach((url) => {
this.relays.get(url)?.close();
this.relays.delete(url);
});
}
subscribe(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const request = [];
for (let i2 = 0; i2 < relays.length; i2++) {
const url = normalizeURL(relays[i2]);
if (!request.find((r) => r.url === url)) {
request.push({ url, filter });
}
}
return this.subscribeMap(request, params);
}
subscribeMany(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const request = [];
const uniqUrls = [];
for (let i2 = 0; i2 < relays.length; i2++) {
const url = normalizeURL(relays[i2]);
if (uniqUrls.indexOf(url) === -1) {
uniqUrls.push(url);
request.push({ url, filter });
}
}
return this.subscribeMap(request, params);
}
subscribeMap(requests, params) {
params.onauth = params.onauth || params.doauth;
const grouped = /* @__PURE__ */ new Map();
for (const req of requests) {
const { url, filter } = req;
if (!grouped.has(url))
grouped.set(url, []);
grouped.get(url).push(filter);
}
const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }));
if (this.trackRelays) {
params.receivedEvent = (relay, id) => {
let set = this.seenOn.get(id);
if (!set) {
set = /* @__PURE__ */ new Set();
this.seenOn.set(id, set);
}
set.add(relay);
};
}
const _knownIds = /* @__PURE__ */ new Set();
const subs = [];
const eosesReceived = [];
let handleEose = (i2) => {
if (eosesReceived[i2])
return;
eosesReceived[i2] = true;
if (eosesReceived.filter((a) => a).length === requests.length) {
params.oneose?.();
handleEose = () => {
};
}
};
const closesReceived = [];
let handleClose = (i2, reason) => {
if (closesReceived[i2])
return;
handleEose(i2);
closesReceived[i2] = reason;
if (closesReceived.filter((a) => a).length === requests.length) {
params.onclose?.(closesReceived);
handleClose = () => {
};
}
};
const localAlreadyHaveEventHandler = (id) => {
if (params.alreadyHaveEvent?.(id)) {
return true;
}
const have = _knownIds.has(id);
_knownIds.add(id);
return have;
};
const allOpened = Promise.all(
groupedRequests.map(async ({ url, filters }, i2) => {
let relay;
try {
relay = await this.ensureRelay(url, {
connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1e3) : void 0
});
} catch (err) {
handleClose(i2, err?.message || String(err));
return;
}
let subscription = relay.subscribe(filters, {
...params,
oneose: () => handleEose(i2),
onclose: (reason) => {
if (reason.startsWith("auth-required: ") && params.onauth) {
relay.auth(params.onauth).then(() => {
relay.subscribe(filters, {
...params,
oneose: () => handleEose(i2),
onclose: (reason2) => {
handleClose(i2, reason2);
},
alreadyHaveEvent: localAlreadyHaveEventHandler,
eoseTimeout: params.maxWait
});
}).catch((err) => {
handleClose(i2, `auth was required and attempted, but failed with: ${err}`);
});
} else {
handleClose(i2, reason);
}
},
alreadyHaveEvent: localAlreadyHaveEventHandler,
eoseTimeout: params.maxWait
});
subs.push(subscription);
})
);
return {
async close(reason) {
await allOpened;
subs.forEach((sub) => {
sub.close(reason);
});
}
};
}
subscribeEose(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const subcloser = this.subscribe(relays, filter, {
...params,
oneose() {
subcloser.close("closed automatically on eose");
}
});
return subcloser;
}
subscribeManyEose(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const subcloser = this.subscribeMany(relays, filter, {
...params,
oneose() {
subcloser.close("closed automatically on eose");
}
});
return subcloser;
}
async querySync(relays, filter, params) {
return new Promise(async (resolve) => {
const events = [];
this.subscribeEose(relays, filter, {
...params,
onevent(event) {
events.push(event);
},
onclose(_) {
resolve(events);
}
});
});
}
async get(relays, filter, params) {
filter.limit = 1;
const events = await this.querySync(relays, filter, params);
events.sort((a, b) => b.created_at - a.created_at);
return events[0] || null;
}
publish(relays, event, options) {
return relays.map(normalizeURL).map(async (url, i2, arr) => {
if (arr.indexOf(url) !== i2) {
return Promise.reject("duplicate url");
}
let r = await this.ensureRelay(url);
return r.publish(event).catch(async (err) => {
if (err instanceof Error && err.message.startsWith("auth-required: ") && options?.onauth) {
await r.auth(options.onauth);
return r.publish(event);
}
throw err;
}).then((reason) => {
if (this.trackRelays) {
let set = this.seenOn.get(event.id);
if (!set) {
set = /* @__PURE__ */ new Set();
this.seenOn.set(event.id, set);
}
set.add(r);
}
return reason;
});
});
}
listConnectionStatus() {
const map = /* @__PURE__ */ new Map();
this.relays.forEach((relay, url) => map.set(url, relay.connected));
return map;
}
destroy() {
this.relays.forEach((conn) => conn.close());
this.relays = /* @__PURE__ */ new Map();
}
};
// pool.ts
var _WebSocket;
try {
_WebSocket = WebSocket;
} catch {
}
function useWebSocketImplementation(websocketImplementation) {
_WebSocket = websocketImplementation;
}
var SimplePool = class extends AbstractSimplePool {
constructor(options) {
super({ verifyEvent, websocketImplementation: _WebSocket, ...options });
}
};

File diff suppressed because one or more lines are too long

130
thrower_daemon/node_modules/nostr-tools/lib/cjs/pure.js generated vendored Normal file
View File

@@ -0,0 +1,130 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// pure.ts
var pure_exports = {};
__export(pure_exports, {
finalizeEvent: () => finalizeEvent,
generateSecretKey: () => generateSecretKey,
getEventHash: () => getEventHash,
getPublicKey: () => getPublicKey,
serializeEvent: () => serializeEvent,
sortEvents: () => sortEvents,
validateEvent: () => validateEvent,
verifiedSymbol: () => verifiedSymbol,
verifyEvent: () => verifyEvent
});
module.exports = __toCommonJS(pure_exports);
var import_secp256k1 = require("@noble/curves/secp256k1");
var import_utils2 = require("@noble/hashes/utils");
// core.ts
var verifiedSymbol = Symbol("verified");
var isRecord = (obj) => obj instanceof Object;
function validateEvent(event) {
if (!isRecord(event))
return false;
if (typeof event.kind !== "number")
return false;
if (typeof event.content !== "string")
return false;
if (typeof event.created_at !== "number")
return false;
if (typeof event.pubkey !== "string")
return false;
if (!event.pubkey.match(/^[a-f0-9]{64}$/))
return false;
if (!Array.isArray(event.tags))
return false;
for (let i2 = 0; i2 < event.tags.length; i2++) {
let tag = event.tags[i2];
if (!Array.isArray(tag))
return false;
for (let j = 0; j < tag.length; j++) {
if (typeof tag[j] !== "string")
return false;
}
}
return true;
}
function sortEvents(events) {
return events.sort((a, b) => {
if (a.created_at !== b.created_at) {
return b.created_at - a.created_at;
}
return a.id.localeCompare(b.id);
});
}
// pure.ts
var import_sha256 = require("@noble/hashes/sha256");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// pure.ts
var JS = class {
generateSecretKey() {
return import_secp256k1.schnorr.utils.randomPrivateKey();
}
getPublicKey(secretKey) {
return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
}
finalizeEvent(t, secretKey) {
const event = t;
event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
event.id = getEventHash(event);
event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
event[verifiedSymbol] = true;
return event;
}
verifyEvent(event) {
if (typeof event[verifiedSymbol] === "boolean")
return event[verifiedSymbol];
const hash = getEventHash(event);
if (hash !== event.id) {
event[verifiedSymbol] = false;
return false;
}
try {
const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
event[verifiedSymbol] = valid;
return valid;
} catch (err) {
event[verifiedSymbol] = false;
return false;
}
}
};
function serializeEvent(evt) {
if (!validateEvent(evt))
throw new Error("can't serialize event with wrong or missing properties");
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
}
function getEventHash(event) {
let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
return (0, import_utils2.bytesToHex)(eventHash);
}
var i = new JS();
var generateSecretKey = i.generateSecretKey;
var getPublicKey = i.getPublicKey;
var finalizeEvent = i.finalizeEvent;
var verifyEvent = i.verifyEvent;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,210 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// references.ts
var references_exports = {};
__export(references_exports, {
parseReferences: () => parseReferences
});
module.exports = __toCommonJS(references_exports);
// nip19.ts
var import_utils2 = require("@noble/hashes/utils");
var import_base = require("@scure/base");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// nip19.ts
var Bech32MaxSize = 5e3;
function decode(code) {
let { prefix, words } = import_base.bech32.decode(code, Bech32MaxSize);
let data = new Uint8Array(import_base.bech32.fromWords(words));
switch (prefix) {
case "nprofile": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for nprofile");
if (tlv[0][0].length !== 32)
throw new Error("TLV 0 should be 32 bytes");
return {
type: "nprofile",
data: {
pubkey: (0, import_utils2.bytesToHex)(tlv[0][0]),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
}
};
}
case "nevent": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for nevent");
if (tlv[0][0].length !== 32)
throw new Error("TLV 0 should be 32 bytes");
if (tlv[2] && tlv[2][0].length !== 32)
throw new Error("TLV 2 should be 32 bytes");
if (tlv[3] && tlv[3][0].length !== 4)
throw new Error("TLV 3 should be 4 bytes");
return {
type: "nevent",
data: {
id: (0, import_utils2.bytesToHex)(tlv[0][0]),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
author: tlv[2]?.[0] ? (0, import_utils2.bytesToHex)(tlv[2][0]) : void 0,
kind: tlv[3]?.[0] ? parseInt((0, import_utils2.bytesToHex)(tlv[3][0]), 16) : void 0
}
};
}
case "naddr": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for naddr");
if (!tlv[2]?.[0])
throw new Error("missing TLV 2 for naddr");
if (tlv[2][0].length !== 32)
throw new Error("TLV 2 should be 32 bytes");
if (!tlv[3]?.[0])
throw new Error("missing TLV 3 for naddr");
if (tlv[3][0].length !== 4)
throw new Error("TLV 3 should be 4 bytes");
return {
type: "naddr",
data: {
identifier: utf8Decoder.decode(tlv[0][0]),
pubkey: (0, import_utils2.bytesToHex)(tlv[2][0]),
kind: parseInt((0, import_utils2.bytesToHex)(tlv[3][0]), 16),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
}
};
}
case "nsec":
return { type: prefix, data };
case "npub":
case "note":
return { type: prefix, data: (0, import_utils2.bytesToHex)(data) };
default:
throw new Error(`unknown prefix ${prefix}`);
}
}
function parseTLV(data) {
let result = {};
let rest = data;
while (rest.length > 0) {
let t = rest[0];
let l = rest[1];
let v = rest.slice(2, 2 + l);
rest = rest.slice(2 + l);
if (v.length < l)
throw new Error(`not enough data to read on TLV ${t}`);
result[t] = result[t] || [];
result[t].push(v);
}
return result;
}
// references.ts
var mentionRegex = /\bnostr:((note|npub|naddr|nevent|nprofile)1\w+)\b|#\[(\d+)\]/g;
function parseReferences(evt) {
let references = [];
for (let ref of evt.content.matchAll(mentionRegex)) {
if (ref[2]) {
try {
let { type, data } = decode(ref[1]);
switch (type) {
case "npub": {
references.push({
text: ref[0],
profile: { pubkey: data, relays: [] }
});
break;
}
case "nprofile": {
references.push({
text: ref[0],
profile: data
});
break;
}
case "note": {
references.push({
text: ref[0],
event: { id: data, relays: [] }
});
break;
}
case "nevent": {
references.push({
text: ref[0],
event: data
});
break;
}
case "naddr": {
references.push({
text: ref[0],
address: data
});
break;
}
}
} catch (err) {
}
} else if (ref[3]) {
let idx = parseInt(ref[3], 10);
let tag = evt.tags[idx];
if (!tag)
continue;
switch (tag[0]) {
case "p": {
references.push({
text: ref[0],
profile: { pubkey: tag[1], relays: tag[2] ? [tag[2]] : [] }
});
break;
}
case "e": {
references.push({
text: ref[0],
event: { id: tag[1], relays: tag[2] ? [tag[2]] : [] }
});
break;
}
case "a": {
try {
let [kind, pubkey, identifier] = tag[1].split(":");
references.push({
text: ref[0],
address: {
identifier,
pubkey,
kind: parseInt(kind, 10),
relays: tag[2] ? [tag[2]] : []
}
});
} catch (err) {
}
break;
}
}
}
}
return references;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,659 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// relay.ts
var relay_exports = {};
__export(relay_exports, {
AbstractRelay: () => AbstractRelay,
Relay: () => Relay,
SendingOnClosedConnection: () => SendingOnClosedConnection,
Subscription: () => Subscription,
useWebSocketImplementation: () => useWebSocketImplementation
});
module.exports = __toCommonJS(relay_exports);
// pure.ts
var import_secp256k1 = require("@noble/curves/secp256k1");
var import_utils2 = require("@noble/hashes/utils");
// core.ts
var verifiedSymbol = Symbol("verified");
var isRecord = (obj) => obj instanceof Object;
function validateEvent(event) {
if (!isRecord(event))
return false;
if (typeof event.kind !== "number")
return false;
if (typeof event.content !== "string")
return false;
if (typeof event.created_at !== "number")
return false;
if (typeof event.pubkey !== "string")
return false;
if (!event.pubkey.match(/^[a-f0-9]{64}$/))
return false;
if (!Array.isArray(event.tags))
return false;
for (let i2 = 0; i2 < event.tags.length; i2++) {
let tag = event.tags[i2];
if (!Array.isArray(tag))
return false;
for (let j = 0; j < tag.length; j++) {
if (typeof tag[j] !== "string")
return false;
}
}
return true;
}
// pure.ts
var import_sha256 = require("@noble/hashes/sha256");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
function normalizeURL(url) {
try {
if (url.indexOf("://") === -1)
url = "wss://" + url;
let p = new URL(url);
p.pathname = p.pathname.replace(/\/+/g, "/");
if (p.pathname.endsWith("/"))
p.pathname = p.pathname.slice(0, -1);
if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:")
p.port = "";
p.searchParams.sort();
p.hash = "";
return p.toString();
} catch (e) {
throw new Error(`Invalid URL: ${url}`);
}
}
var QueueNode = class {
value;
next = null;
prev = null;
constructor(message) {
this.value = message;
}
};
var Queue = class {
first;
last;
constructor() {
this.first = null;
this.last = null;
}
enqueue(value) {
const newNode = new QueueNode(value);
if (!this.last) {
this.first = newNode;
this.last = newNode;
} else if (this.last === this.first) {
this.last = newNode;
this.last.prev = this.first;
this.first.next = newNode;
} else {
newNode.prev = this.last;
this.last.next = newNode;
this.last = newNode;
}
return true;
}
dequeue() {
if (!this.first)
return null;
if (this.first === this.last) {
const target2 = this.first;
this.first = null;
this.last = null;
return target2.value;
}
const target = this.first;
this.first = target.next;
if (this.first) {
this.first.prev = null;
}
return target.value;
}
};
// pure.ts
var JS = class {
generateSecretKey() {
return import_secp256k1.schnorr.utils.randomPrivateKey();
}
getPublicKey(secretKey) {
return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
}
finalizeEvent(t, secretKey) {
const event = t;
event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
event.id = getEventHash(event);
event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
event[verifiedSymbol] = true;
return event;
}
verifyEvent(event) {
if (typeof event[verifiedSymbol] === "boolean")
return event[verifiedSymbol];
const hash = getEventHash(event);
if (hash !== event.id) {
event[verifiedSymbol] = false;
return false;
}
try {
const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
event[verifiedSymbol] = valid;
return valid;
} catch (err) {
event[verifiedSymbol] = false;
return false;
}
}
};
function serializeEvent(evt) {
if (!validateEvent(evt))
throw new Error("can't serialize event with wrong or missing properties");
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
}
function getEventHash(event) {
let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
return (0, import_utils2.bytesToHex)(eventHash);
}
var i = new JS();
var generateSecretKey = i.generateSecretKey;
var getPublicKey = i.getPublicKey;
var finalizeEvent = i.finalizeEvent;
var verifyEvent = i.verifyEvent;
// kinds.ts
var ClientAuth = 22242;
// filter.ts
function matchFilter(filter, event) {
if (filter.ids && filter.ids.indexOf(event.id) === -1) {
return false;
}
if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {
return false;
}
if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {
return false;
}
for (let f in filter) {
if (f[0] === "#") {
let tagName = f.slice(1);
let values = filter[`#${tagName}`];
if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values.indexOf(v) !== -1))
return false;
}
}
if (filter.since && event.created_at < filter.since)
return false;
if (filter.until && event.created_at > filter.until)
return false;
return true;
}
function matchFilters(filters, event) {
for (let i2 = 0; i2 < filters.length; i2++) {
if (matchFilter(filters[i2], event)) {
return true;
}
}
return false;
}
// fakejson.ts
function getHex64(json, field) {
let len = field.length + 3;
let idx = json.indexOf(`"${field}":`) + len;
let s = json.slice(idx).indexOf(`"`) + idx + 1;
return json.slice(s, s + 64);
}
function getSubscriptionId(json) {
let idx = json.slice(0, 22).indexOf(`"EVENT"`);
if (idx === -1)
return null;
let pstart = json.slice(idx + 7 + 1).indexOf(`"`);
if (pstart === -1)
return null;
let start = idx + 7 + 1 + pstart;
let pend = json.slice(start + 1, 80).indexOf(`"`);
if (pend === -1)
return null;
let end = start + 1 + pend;
return json.slice(start + 1, end);
}
// nip42.ts
function makeAuthEvent(relayURL, challenge) {
return {
kind: ClientAuth,
created_at: Math.floor(Date.now() / 1e3),
tags: [
["relay", relayURL],
["challenge", challenge]
],
content: ""
};
}
// helpers.ts
async function yieldThread() {
return new Promise((resolve) => {
const ch = new MessageChannel();
const handler = () => {
ch.port1.removeEventListener("message", handler);
resolve();
};
ch.port1.addEventListener("message", handler);
ch.port2.postMessage(0);
ch.port1.start();
});
}
// abstract-relay.ts
var SendingOnClosedConnection = class extends Error {
constructor(message, relay) {
super(`Tried to send message '${message} on a closed connection to ${relay}.`);
this.name = "SendingOnClosedConnection";
}
};
var AbstractRelay = class {
url;
_connected = false;
onclose = null;
onnotice = (msg) => console.debug(`NOTICE from ${this.url}: ${msg}`);
baseEoseTimeout = 4400;
connectionTimeout = 4400;
publishTimeout = 4400;
pingFrequency = 2e4;
pingTimeout = 2e4;
openSubs = /* @__PURE__ */ new Map();
enablePing;
connectionTimeoutHandle;
connectionPromise;
openCountRequests = /* @__PURE__ */ new Map();
openEventPublishes = /* @__PURE__ */ new Map();
ws;
incomingMessageQueue = new Queue();
queueRunning = false;
challenge;
authPromise;
serial = 0;
verifyEvent;
_WebSocket;
constructor(url, opts) {
this.url = normalizeURL(url);
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation || WebSocket;
this.enablePing = opts.enablePing;
}
static async connect(url, opts) {
const relay = new AbstractRelay(url, opts);
await relay.connect();
return relay;
}
closeAllSubscriptions(reason) {
for (let [_, sub] of this.openSubs) {
sub.close(reason);
}
this.openSubs.clear();
for (let [_, ep] of this.openEventPublishes) {
ep.reject(new Error(reason));
}
this.openEventPublishes.clear();
for (let [_, cr] of this.openCountRequests) {
cr.reject(new Error(reason));
}
this.openCountRequests.clear();
}
get connected() {
return this._connected;
}
async connect() {
if (this.connectionPromise)
return this.connectionPromise;
this.challenge = void 0;
this.authPromise = void 0;
this.connectionPromise = new Promise((resolve, reject) => {
this.connectionTimeoutHandle = setTimeout(() => {
reject("connection timed out");
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection timed out");
}, this.connectionTimeout);
try {
this.ws = new this._WebSocket(this.url);
} catch (err) {
clearTimeout(this.connectionTimeoutHandle);
reject(err);
return;
}
this.ws.onopen = () => {
clearTimeout(this.connectionTimeoutHandle);
this._connected = true;
if (this.enablePing) {
this.pingpong();
}
resolve();
};
this.ws.onerror = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket error");
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
};
this.ws.onclose = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket closed");
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
};
this.ws.onmessage = this._onmessage.bind(this);
});
return this.connectionPromise;
}
async waitForPingPong() {
return new Promise((res, err) => {
;
this.ws && this.ws.on && this.ws.on("pong", () => res(true)) || err("ws can't listen for pong");
this.ws && this.ws.ping && this.ws.ping();
});
}
async waitForDummyReq() {
return new Promise((resolve, _) => {
const sub = this.subscribe([{ ids: ["a".repeat(64)] }], {
oneose: () => {
sub.close();
resolve(true);
},
eoseTimeout: this.pingTimeout + 1e3
});
});
}
async pingpong() {
if (this.ws?.readyState === 1) {
const result = await Promise.any([
this.ws && this.ws.ping && this.ws.on ? this.waitForPingPong() : this.waitForDummyReq(),
new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
]);
if (result) {
setTimeout(() => this.pingpong(), this.pingFrequency);
} else {
this.closeAllSubscriptions("pingpong timed out");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
}
}
async runQueue() {
this.queueRunning = true;
while (true) {
if (false === this.handleNext()) {
break;
}
await yieldThread();
}
this.queueRunning = false;
}
handleNext() {
const json = this.incomingMessageQueue.dequeue();
if (!json) {
return false;
}
const subid = getSubscriptionId(json);
if (subid) {
const so = this.openSubs.get(subid);
if (!so) {
return;
}
const id = getHex64(json, "id");
const alreadyHave = so.alreadyHaveEvent?.(id);
so.receivedEvent?.(this, id);
if (alreadyHave) {
return;
}
}
try {
let data = JSON.parse(json);
switch (data[0]) {
case "EVENT": {
const so = this.openSubs.get(data[1]);
const event = data[2];
if (this.verifyEvent(event) && matchFilters(so.filters, event)) {
so.onevent(event);
}
return;
}
case "COUNT": {
const id = data[1];
const payload = data[2];
const cr = this.openCountRequests.get(id);
if (cr) {
cr.resolve(payload.count);
this.openCountRequests.delete(id);
}
return;
}
case "EOSE": {
const so = this.openSubs.get(data[1]);
if (!so)
return;
so.receivedEose();
return;
}
case "OK": {
const id = data[1];
const ok = data[2];
const reason = data[3];
const ep = this.openEventPublishes.get(id);
if (ep) {
clearTimeout(ep.timeout);
if (ok)
ep.resolve(reason);
else
ep.reject(new Error(reason));
this.openEventPublishes.delete(id);
}
return;
}
case "CLOSED": {
const id = data[1];
const so = this.openSubs.get(id);
if (!so)
return;
so.closed = true;
so.close(data[2]);
return;
}
case "NOTICE":
this.onnotice(data[1]);
return;
case "AUTH": {
this.challenge = data[1];
return;
}
}
} catch (err) {
return;
}
}
async send(message) {
if (!this.connectionPromise)
throw new SendingOnClosedConnection(message, this.url);
this.connectionPromise.then(() => {
this.ws?.send(message);
});
}
async auth(signAuthEvent) {
const challenge = this.challenge;
if (!challenge)
throw new Error("can't perform auth, no challenge was received");
if (this.authPromise)
return this.authPromise;
this.authPromise = new Promise(async (resolve, reject) => {
try {
let evt = await signAuthEvent(makeAuthEvent(this.url, challenge));
let timeout = setTimeout(() => {
let ep = this.openEventPublishes.get(evt.id);
if (ep) {
ep.reject(new Error("auth timed out"));
this.openEventPublishes.delete(evt.id);
}
}, this.publishTimeout);
this.openEventPublishes.set(evt.id, { resolve, reject, timeout });
this.send('["AUTH",' + JSON.stringify(evt) + "]");
} catch (err) {
console.warn("subscribe auth function failed:", err);
}
});
return this.authPromise;
}
async publish(event) {
const ret = new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
const ep = this.openEventPublishes.get(event.id);
if (ep) {
ep.reject(new Error("publish timed out"));
this.openEventPublishes.delete(event.id);
}
}, this.publishTimeout);
this.openEventPublishes.set(event.id, { resolve, reject, timeout });
});
this.send('["EVENT",' + JSON.stringify(event) + "]");
return ret;
}
async count(filters, params) {
this.serial++;
const id = params?.id || "count:" + this.serial;
const ret = new Promise((resolve, reject) => {
this.openCountRequests.set(id, { resolve, reject });
});
this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1));
return ret;
}
subscribe(filters, params) {
const subscription = this.prepareSubscription(filters, params);
subscription.fire();
return subscription;
}
prepareSubscription(filters, params) {
this.serial++;
const id = params.id || (params.label ? params.label + ":" : "sub:") + this.serial;
const subscription = new Subscription(this, id, filters, params);
this.openSubs.set(id, subscription);
return subscription;
}
close() {
this.closeAllSubscriptions("relay connection closed by us");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
_onmessage(ev) {
this.incomingMessageQueue.enqueue(ev.data);
if (!this.queueRunning) {
this.runQueue();
}
}
};
var Subscription = class {
relay;
id;
closed = false;
eosed = false;
filters;
alreadyHaveEvent;
receivedEvent;
onevent;
oneose;
onclose;
eoseTimeout;
eoseTimeoutHandle;
constructor(relay, id, filters, params) {
this.relay = relay;
this.filters = filters;
this.id = id;
this.alreadyHaveEvent = params.alreadyHaveEvent;
this.receivedEvent = params.receivedEvent;
this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout;
this.oneose = params.oneose;
this.onclose = params.onclose;
this.onevent = params.onevent || ((event) => {
console.warn(
`onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,
event
);
});
}
fire() {
this.relay.send('["REQ","' + this.id + '",' + JSON.stringify(this.filters).substring(1));
this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout);
}
receivedEose() {
if (this.eosed)
return;
clearTimeout(this.eoseTimeoutHandle);
this.eosed = true;
this.oneose?.();
}
close(reason = "closed by caller") {
if (!this.closed && this.relay.connected) {
try {
this.relay.send('["CLOSE",' + JSON.stringify(this.id) + "]");
} catch (err) {
if (err instanceof SendingOnClosedConnection) {
} else {
throw err;
}
}
this.closed = true;
}
this.relay.openSubs.delete(this.id);
this.onclose?.(reason);
}
};
// relay.ts
var _WebSocket;
try {
_WebSocket = WebSocket;
} catch {
}
function useWebSocketImplementation(websocketImplementation) {
_WebSocket = websocketImplementation;
}
var Relay = class extends AbstractRelay {
constructor(url) {
super(url, { verifyEvent, websocketImplementation: _WebSocket });
}
static async connect(url) {
const relay = new Relay(url);
await relay.connect();
return relay;
}
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,130 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// signer.ts
var signer_exports = {};
__export(signer_exports, {
PlainKeySigner: () => PlainKeySigner
});
module.exports = __toCommonJS(signer_exports);
// pure.ts
var import_secp256k1 = require("@noble/curves/secp256k1");
var import_utils2 = require("@noble/hashes/utils");
// core.ts
var verifiedSymbol = Symbol("verified");
var isRecord = (obj) => obj instanceof Object;
function validateEvent(event) {
if (!isRecord(event))
return false;
if (typeof event.kind !== "number")
return false;
if (typeof event.content !== "string")
return false;
if (typeof event.created_at !== "number")
return false;
if (typeof event.pubkey !== "string")
return false;
if (!event.pubkey.match(/^[a-f0-9]{64}$/))
return false;
if (!Array.isArray(event.tags))
return false;
for (let i2 = 0; i2 < event.tags.length; i2++) {
let tag = event.tags[i2];
if (!Array.isArray(tag))
return false;
for (let j = 0; j < tag.length; j++) {
if (typeof tag[j] !== "string")
return false;
}
}
return true;
}
// pure.ts
var import_sha256 = require("@noble/hashes/sha256");
// utils.ts
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
// pure.ts
var JS = class {
generateSecretKey() {
return import_secp256k1.schnorr.utils.randomPrivateKey();
}
getPublicKey(secretKey) {
return (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
}
finalizeEvent(t, secretKey) {
const event = t;
event.pubkey = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.getPublicKey(secretKey));
event.id = getEventHash(event);
event.sig = (0, import_utils2.bytesToHex)(import_secp256k1.schnorr.sign(getEventHash(event), secretKey));
event[verifiedSymbol] = true;
return event;
}
verifyEvent(event) {
if (typeof event[verifiedSymbol] === "boolean")
return event[verifiedSymbol];
const hash = getEventHash(event);
if (hash !== event.id) {
event[verifiedSymbol] = false;
return false;
}
try {
const valid = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
event[verifiedSymbol] = valid;
return valid;
} catch (err) {
event[verifiedSymbol] = false;
return false;
}
}
};
function serializeEvent(evt) {
if (!validateEvent(evt))
throw new Error("can't serialize event with wrong or missing properties");
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
}
function getEventHash(event) {
let eventHash = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
return (0, import_utils2.bytesToHex)(eventHash);
}
var i = new JS();
var generateSecretKey = i.generateSecretKey;
var getPublicKey = i.getPublicKey;
var finalizeEvent = i.finalizeEvent;
var verifyEvent = i.verifyEvent;
// signer.ts
var PlainKeySigner = class {
secretKey;
constructor(secretKey) {
this.secretKey = secretKey;
}
async getPublicKey() {
return getPublicKey(this.secretKey);
}
async signEvent(event) {
return finalizeEvent(event, this.secretKey);
}
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,145 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// utils.ts
var utils_exports = {};
__export(utils_exports, {
Queue: () => Queue,
QueueNode: () => QueueNode,
binarySearch: () => binarySearch,
bytesToHex: () => import_utils.bytesToHex,
hexToBytes: () => import_utils.hexToBytes,
insertEventIntoAscendingList: () => insertEventIntoAscendingList,
insertEventIntoDescendingList: () => insertEventIntoDescendingList,
normalizeURL: () => normalizeURL,
utf8Decoder: () => utf8Decoder,
utf8Encoder: () => utf8Encoder
});
module.exports = __toCommonJS(utils_exports);
var import_utils = require("@noble/hashes/utils");
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
function normalizeURL(url) {
try {
if (url.indexOf("://") === -1)
url = "wss://" + url;
let p = new URL(url);
p.pathname = p.pathname.replace(/\/+/g, "/");
if (p.pathname.endsWith("/"))
p.pathname = p.pathname.slice(0, -1);
if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:")
p.port = "";
p.searchParams.sort();
p.hash = "";
return p.toString();
} catch (e) {
throw new Error(`Invalid URL: ${url}`);
}
}
function insertEventIntoDescendingList(sortedArray, event) {
const [idx, found] = binarySearch(sortedArray, (b) => {
if (event.id === b.id)
return 0;
if (event.created_at === b.created_at)
return -1;
return b.created_at - event.created_at;
});
if (!found) {
sortedArray.splice(idx, 0, event);
}
return sortedArray;
}
function insertEventIntoAscendingList(sortedArray, event) {
const [idx, found] = binarySearch(sortedArray, (b) => {
if (event.id === b.id)
return 0;
if (event.created_at === b.created_at)
return -1;
return event.created_at - b.created_at;
});
if (!found) {
sortedArray.splice(idx, 0, event);
}
return sortedArray;
}
function binarySearch(arr, compare) {
let start = 0;
let end = arr.length - 1;
while (start <= end) {
const mid = Math.floor((start + end) / 2);
const cmp = compare(arr[mid]);
if (cmp === 0) {
return [mid, true];
}
if (cmp < 0) {
end = mid - 1;
} else {
start = mid + 1;
}
}
return [start, false];
}
var QueueNode = class {
value;
next = null;
prev = null;
constructor(message) {
this.value = message;
}
};
var Queue = class {
first;
last;
constructor() {
this.first = null;
this.last = null;
}
enqueue(value) {
const newNode = new QueueNode(value);
if (!this.last) {
this.first = newNode;
this.last = newNode;
} else if (this.last === this.first) {
this.last = newNode;
this.last.prev = this.first;
this.first.next = newNode;
} else {
newNode.prev = this.last;
this.last.next = newNode;
this.last = newNode;
}
return true;
}
dequeue() {
if (!this.first)
return null;
if (this.first === this.last) {
const target2 = this.first;
this.first = null;
this.last = null;
return target2.value;
}
const target = this.first;
this.first = target.next;
if (this.first) {
this.first.prev = null;
}
return target.value;
}
};

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../utils.ts"],
"sourcesContent": ["import type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event[], event: Event): Event[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport class QueueNode<V> {\n public value: V\n public next: QueueNode<V> | null = null\n public prev: QueueNode<V> | null = null\n\n constructor(message: V) {\n this.value = message\n }\n}\n\nexport class Queue<V> {\n public first: QueueNode<V> | null\n public last: QueueNode<V> | null\n\n constructor() {\n this.first = null\n this.last = null\n }\n\n enqueue(value: V): boolean {\n const newNode = new QueueNode(value)\n if (!this.last) {\n // list is empty\n this.first = newNode\n this.last = newNode\n } else if (this.last === this.first) {\n // list has a single element\n this.last = newNode\n this.last.prev = this.first\n this.first.next = newNode\n } else {\n // list has elements, add as last\n newNode.prev = this.last\n this.last.next = newNode\n this.last = newNode\n }\n return true\n }\n\n dequeue(): V | null {\n if (!this.first) return null\n\n if (this.first === this.last) {\n const target = this.first\n this.first = null\n this.last = null\n return target.value\n }\n\n const target = this.first\n this.first = target.next\n if (this.first) {\n this.first.prev = null // fix: clean up prev pointer\n }\n\n return target.value\n }\n}\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,mBAAuC;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;AAIjD,SAAS,aAAa,KAAqB;AAChD,MAAI;AACF,QAAI,IAAI,QAAQ,KAAK,MAAM;AAAI,YAAM,WAAW;AAChD,QAAI,IAAI,IAAI,IAAI,GAAG;AACnB,MAAE,WAAW,EAAE,SAAS,QAAQ,QAAQ,GAAG;AAC3C,QAAI,EAAE,SAAS,SAAS,GAAG;AAAG,QAAE,WAAW,EAAE,SAAS,MAAM,GAAG,EAAE;AACjE,QAAK,EAAE,SAAS,QAAQ,EAAE,aAAa,SAAW,EAAE,SAAS,SAAS,EAAE,aAAa;AAAS,QAAE,OAAO;AACvG,MAAE,aAAa,KAAK;AACpB,MAAE,OAAO;AACT,WAAO,EAAE,SAAS;AAAA,EACpB,SAAS,GAAP;AACA,UAAM,IAAI,MAAM,gBAAgB,KAAK;AAAA,EACvC;AACF;AAEO,SAAS,8BAA8B,aAAsB,OAAuB;AACzF,QAAM,CAAC,KAAK,KAAK,IAAI,aAAa,aAAa,OAAK;AAClD,QAAI,MAAM,OAAO,EAAE;AAAI,aAAO;AAC9B,QAAI,MAAM,eAAe,EAAE;AAAY,aAAO;AAC9C,WAAO,EAAE,aAAa,MAAM;AAAA,EAC9B,CAAC;AACD,MAAI,CAAC,OAAO;AACV,gBAAY,OAAO,KAAK,GAAG,KAAK;AAAA,EAClC;AACA,SAAO;AACT;AAEO,SAAS,6BAA6B,aAAsB,OAAuB;AACxF,QAAM,CAAC,KAAK,KAAK,IAAI,aAAa,aAAa,OAAK;AAClD,QAAI,MAAM,OAAO,EAAE;AAAI,aAAO;AAC9B,QAAI,MAAM,eAAe,EAAE;AAAY,aAAO;AAC9C,WAAO,MAAM,aAAa,EAAE;AAAA,EAC9B,CAAC;AACD,MAAI,CAAC,OAAO;AACV,gBAAY,OAAO,KAAK,GAAG,KAAK;AAAA,EAClC;AACA,SAAO;AACT;AAEO,SAAS,aAAgB,KAAU,SAA8C;AACtF,MAAI,QAAQ;AACZ,MAAI,MAAM,IAAI,SAAS;AAEvB,SAAO,SAAS,KAAK;AACnB,UAAM,MAAM,KAAK,OAAO,QAAQ,OAAO,CAAC;AACxC,UAAM,MAAM,QAAQ,IAAI,IAAI;AAE5B,QAAI,QAAQ,GAAG;AACb,aAAO,CAAC,KAAK,IAAI;AAAA,IACnB;AAEA,QAAI,MAAM,GAAG;AACX,YAAM,MAAM;AAAA,IACd,OAAO;AACL,cAAQ,MAAM;AAAA,IAChB;AAAA,EACF;AAEA,SAAO,CAAC,OAAO,KAAK;AACtB;AAEO,IAAM,YAAN,MAAmB;AAAA,EACjB;AAAA,EACA,OAA4B;AAAA,EAC5B,OAA4B;AAAA,EAEnC,YAAY,SAAY;AACtB,SAAK,QAAQ;AAAA,EACf;AACF;AAEO,IAAM,QAAN,MAAe;AAAA,EACb;AAAA,EACA;AAAA,EAEP,cAAc;AACZ,SAAK,QAAQ;AACb,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,QAAQ,OAAmB;AACzB,UAAM,UAAU,IAAI,UAAU,KAAK;AACnC,QAAI,CAAC,KAAK,MAAM;AAEd,WAAK,QAAQ;AACb,WAAK,OAAO;AAAA,IACd,WAAW,KAAK,SAAS,KAAK,OAAO;AAEnC,WAAK,OAAO;AACZ,WAAK,KAAK,OAAO,KAAK;AACtB,WAAK,MAAM,OAAO;AAAA,IACpB,OAAO;AAEL,cAAQ,OAAO,KAAK;AACpB,WAAK,KAAK,OAAO;AACjB,WAAK,OAAO;AAAA,IACd;AACA,WAAO;AAAA,EACT;AAAA,EAEA,UAAoB;AAClB,QAAI,CAAC,KAAK;AAAO,aAAO;AAExB,QAAI,KAAK,UAAU,KAAK,MAAM;AAC5B,YAAMA,UAAS,KAAK;AACpB,WAAK,QAAQ;AACb,WAAK,OAAO;AACZ,aAAOA,QAAO;AAAA,IAChB;AAEA,UAAM,SAAS,KAAK;AACpB,SAAK,QAAQ,OAAO;AACpB,QAAI,KAAK,OAAO;AACd,WAAK,MAAM,OAAO;AAAA,IACpB;AAEA,WAAO,OAAO;AAAA,EAChB;AACF;",
"names": ["target"]
}

103
thrower_daemon/node_modules/nostr-tools/lib/cjs/wasm.js generated vendored Normal file
View File

@@ -0,0 +1,103 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// wasm.ts
var wasm_exports = {};
__export(wasm_exports, {
finalizeEvent: () => finalizeEvent,
generateSecretKey: () => generateSecretKey,
getPublicKey: () => getPublicKey,
setNostrWasm: () => setNostrWasm,
sortEvents: () => sortEvents,
validateEvent: () => validateEvent,
verifiedSymbol: () => verifiedSymbol,
verifyEvent: () => verifyEvent
});
module.exports = __toCommonJS(wasm_exports);
var import_utils = require("@noble/hashes/utils");
// core.ts
var verifiedSymbol = Symbol("verified");
var isRecord = (obj) => obj instanceof Object;
function validateEvent(event) {
if (!isRecord(event))
return false;
if (typeof event.kind !== "number")
return false;
if (typeof event.content !== "string")
return false;
if (typeof event.created_at !== "number")
return false;
if (typeof event.pubkey !== "string")
return false;
if (!event.pubkey.match(/^[a-f0-9]{64}$/))
return false;
if (!Array.isArray(event.tags))
return false;
for (let i2 = 0; i2 < event.tags.length; i2++) {
let tag = event.tags[i2];
if (!Array.isArray(tag))
return false;
for (let j = 0; j < tag.length; j++) {
if (typeof tag[j] !== "string")
return false;
}
}
return true;
}
function sortEvents(events) {
return events.sort((a, b) => {
if (a.created_at !== b.created_at) {
return b.created_at - a.created_at;
}
return a.id.localeCompare(b.id);
});
}
// wasm.ts
var nw;
function setNostrWasm(x) {
nw = x;
}
var Wasm = class {
generateSecretKey() {
return nw.generateSecretKey();
}
getPublicKey(secretKey) {
return (0, import_utils.bytesToHex)(nw.getPublicKey(secretKey));
}
finalizeEvent(t, secretKey) {
nw.finalizeEvent(t, secretKey);
return t;
}
verifyEvent(event) {
try {
nw.verifyEvent(event);
event[verifiedSymbol] = true;
return true;
} catch (err) {
return false;
}
}
};
var i = new Wasm();
var generateSecretKey = i.generateSecretKey;
var getPublicKey = i.getPublicKey;
var finalizeEvent = i.finalizeEvent;
var verifyEvent = i.verifyEvent;

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": ["../../wasm.ts", "../../core.ts"],
"sourcesContent": ["import { bytesToHex } from '@noble/hashes/utils'\nimport { Nostr as NostrWasm } from 'nostr-wasm'\nimport { EventTemplate, Event, Nostr, VerifiedEvent, verifiedSymbol } from './core.ts'\n\nlet nw: NostrWasm\n\nexport function setNostrWasm(x: NostrWasm) {\n nw = x\n}\n\nclass Wasm implements Nostr {\n generateSecretKey(): Uint8Array {\n return nw.generateSecretKey()\n }\n getPublicKey(secretKey: Uint8Array): string {\n return bytesToHex(nw.getPublicKey(secretKey))\n }\n finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n nw.finalizeEvent(t as any, secretKey)\n return t as VerifiedEvent\n }\n verifyEvent(event: Event): event is VerifiedEvent {\n try {\n nw.verifyEvent(event)\n event[verifiedSymbol] = true\n return true\n } catch (err) {\n return false\n }\n }\n}\n\nconst i: Wasm = new Wasm()\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n generateSecretKey(): Uint8Array\n getPublicKey(secretKey: Uint8Array): string\n finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport interface Event {\n kind: number\n tags: string[][]\n content: string\n created_at: number\n pubkey: string\n id: string\n sig: string\n [verifiedSymbol]?: boolean\n}\n\nexport type NostrEvent = Event\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n if (!isRecord(event)) return false\n if (typeof event.kind !== 'number') return false\n if (typeof event.content !== 'string') return false\n if (typeof event.created_at !== 'number') return false\n if (typeof event.pubkey !== 'string') return false\n if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n if (!Array.isArray(event.tags)) return false\n for (let i = 0; i < event.tags.length; i++) {\n let tag = event.tags[i]\n if (!Array.isArray(tag)) return false\n for (let j = 0; j < tag.length; j++) {\n if (typeof tag[j] !== 'string') return false\n }\n }\n\n return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n return events.sort((a: NostrEvent, b: NostrEvent): number => {\n if (a.created_at !== b.created_at) {\n return b.created_at - a.created_at\n }\n return a.id.localeCompare(b.id)\n })\n}\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA2B;;;ACQpB,IAAM,iBAAiB,OAAO,UAAU;AAsB/C,IAAM,WAAW,CAAC,QAAiD,eAAe;AAE3E,SAAS,cAAiB,OAAsC;AACrE,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,MAAI,OAAO,MAAM,SAAS;AAAU,WAAO;AAC3C,MAAI,OAAO,MAAM,YAAY;AAAU,WAAO;AAC9C,MAAI,OAAO,MAAM,eAAe;AAAU,WAAO;AACjD,MAAI,OAAO,MAAM,WAAW;AAAU,WAAO;AAC7C,MAAI,CAAC,MAAM,OAAO,MAAM,gBAAgB;AAAG,WAAO;AAElD,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI;AAAG,WAAO;AACvC,WAASA,KAAI,GAAGA,KAAI,MAAM,KAAK,QAAQA,MAAK;AAC1C,QAAI,MAAM,MAAM,KAAKA;AACrB,QAAI,CAAC,MAAM,QAAQ,GAAG;AAAG,aAAO;AAChC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,OAAO,IAAI,OAAO;AAAU,eAAO;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;AAOO,SAAS,WAAW,QAA0B;AACnD,SAAO,OAAO,KAAK,CAAC,GAAe,MAA0B;AAC3D,QAAI,EAAE,eAAe,EAAE,YAAY;AACjC,aAAO,EAAE,aAAa,EAAE;AAAA,IAC1B;AACA,WAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAAA,EAChC,CAAC;AACH;;;AD5DA,IAAI;AAEG,SAAS,aAAa,GAAc;AACzC,OAAK;AACP;AAEA,IAAM,OAAN,MAA4B;AAAA,EAC1B,oBAAgC;AAC9B,WAAO,GAAG,kBAAkB;AAAA,EAC9B;AAAA,EACA,aAAa,WAA+B;AAC1C,eAAO,yBAAW,GAAG,aAAa,SAAS,CAAC;AAAA,EAC9C;AAAA,EACA,cAAc,GAAkB,WAAsC;AACpE,OAAG,cAAc,GAAU,SAAS;AACpC,WAAO;AAAA,EACT;AAAA,EACA,YAAY,OAAsC;AAChD,QAAI;AACF,SAAG,YAAY,KAAK;AACpB,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT,SAAS,KAAP;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,IAAM,IAAU,IAAI,KAAK;AAClB,IAAM,oBAAoB,EAAE;AAC5B,IAAM,eAAe,EAAE;AACvB,IAAM,gBAAgB,EAAE;AACxB,IAAM,cAAc,EAAE;",
"names": ["i"]
}

View File

@@ -0,0 +1,773 @@
// core.ts
var verifiedSymbol = Symbol("verified");
// utils.ts
import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
function normalizeURL(url) {
try {
if (url.indexOf("://") === -1)
url = "wss://" + url;
let p = new URL(url);
p.pathname = p.pathname.replace(/\/+/g, "/");
if (p.pathname.endsWith("/"))
p.pathname = p.pathname.slice(0, -1);
if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:")
p.port = "";
p.searchParams.sort();
p.hash = "";
return p.toString();
} catch (e) {
throw new Error(`Invalid URL: ${url}`);
}
}
var QueueNode = class {
value;
next = null;
prev = null;
constructor(message) {
this.value = message;
}
};
var Queue = class {
first;
last;
constructor() {
this.first = null;
this.last = null;
}
enqueue(value) {
const newNode = new QueueNode(value);
if (!this.last) {
this.first = newNode;
this.last = newNode;
} else if (this.last === this.first) {
this.last = newNode;
this.last.prev = this.first;
this.first.next = newNode;
} else {
newNode.prev = this.last;
this.last.next = newNode;
this.last = newNode;
}
return true;
}
dequeue() {
if (!this.first)
return null;
if (this.first === this.last) {
const target2 = this.first;
this.first = null;
this.last = null;
return target2.value;
}
const target = this.first;
this.first = target.next;
if (this.first) {
this.first.prev = null;
}
return target.value;
}
};
// kinds.ts
var ClientAuth = 22242;
// filter.ts
function matchFilter(filter, event) {
if (filter.ids && filter.ids.indexOf(event.id) === -1) {
return false;
}
if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {
return false;
}
if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {
return false;
}
for (let f in filter) {
if (f[0] === "#") {
let tagName = f.slice(1);
let values = filter[`#${tagName}`];
if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values.indexOf(v) !== -1))
return false;
}
}
if (filter.since && event.created_at < filter.since)
return false;
if (filter.until && event.created_at > filter.until)
return false;
return true;
}
function matchFilters(filters, event) {
for (let i = 0; i < filters.length; i++) {
if (matchFilter(filters[i], event)) {
return true;
}
}
return false;
}
// fakejson.ts
function getHex64(json, field) {
let len = field.length + 3;
let idx = json.indexOf(`"${field}":`) + len;
let s = json.slice(idx).indexOf(`"`) + idx + 1;
return json.slice(s, s + 64);
}
function getSubscriptionId(json) {
let idx = json.slice(0, 22).indexOf(`"EVENT"`);
if (idx === -1)
return null;
let pstart = json.slice(idx + 7 + 1).indexOf(`"`);
if (pstart === -1)
return null;
let start = idx + 7 + 1 + pstart;
let pend = json.slice(start + 1, 80).indexOf(`"`);
if (pend === -1)
return null;
let end = start + 1 + pend;
return json.slice(start + 1, end);
}
// nip42.ts
function makeAuthEvent(relayURL, challenge) {
return {
kind: ClientAuth,
created_at: Math.floor(Date.now() / 1e3),
tags: [
["relay", relayURL],
["challenge", challenge]
],
content: ""
};
}
// helpers.ts
async function yieldThread() {
return new Promise((resolve) => {
const ch = new MessageChannel();
const handler = () => {
ch.port1.removeEventListener("message", handler);
resolve();
};
ch.port1.addEventListener("message", handler);
ch.port2.postMessage(0);
ch.port1.start();
});
}
var alwaysTrue = (t) => {
t[verifiedSymbol] = true;
return true;
};
// abstract-relay.ts
var SendingOnClosedConnection = class extends Error {
constructor(message, relay) {
super(`Tried to send message '${message} on a closed connection to ${relay}.`);
this.name = "SendingOnClosedConnection";
}
};
var AbstractRelay = class {
url;
_connected = false;
onclose = null;
onnotice = (msg) => console.debug(`NOTICE from ${this.url}: ${msg}`);
baseEoseTimeout = 4400;
connectionTimeout = 4400;
publishTimeout = 4400;
pingFrequency = 2e4;
pingTimeout = 2e4;
openSubs = /* @__PURE__ */ new Map();
enablePing;
connectionTimeoutHandle;
connectionPromise;
openCountRequests = /* @__PURE__ */ new Map();
openEventPublishes = /* @__PURE__ */ new Map();
ws;
incomingMessageQueue = new Queue();
queueRunning = false;
challenge;
authPromise;
serial = 0;
verifyEvent;
_WebSocket;
constructor(url, opts) {
this.url = normalizeURL(url);
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation || WebSocket;
this.enablePing = opts.enablePing;
}
static async connect(url, opts) {
const relay = new AbstractRelay(url, opts);
await relay.connect();
return relay;
}
closeAllSubscriptions(reason) {
for (let [_, sub] of this.openSubs) {
sub.close(reason);
}
this.openSubs.clear();
for (let [_, ep] of this.openEventPublishes) {
ep.reject(new Error(reason));
}
this.openEventPublishes.clear();
for (let [_, cr] of this.openCountRequests) {
cr.reject(new Error(reason));
}
this.openCountRequests.clear();
}
get connected() {
return this._connected;
}
async connect() {
if (this.connectionPromise)
return this.connectionPromise;
this.challenge = void 0;
this.authPromise = void 0;
this.connectionPromise = new Promise((resolve, reject) => {
this.connectionTimeoutHandle = setTimeout(() => {
reject("connection timed out");
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection timed out");
}, this.connectionTimeout);
try {
this.ws = new this._WebSocket(this.url);
} catch (err) {
clearTimeout(this.connectionTimeoutHandle);
reject(err);
return;
}
this.ws.onopen = () => {
clearTimeout(this.connectionTimeoutHandle);
this._connected = true;
if (this.enablePing) {
this.pingpong();
}
resolve();
};
this.ws.onerror = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket error");
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
};
this.ws.onclose = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket closed");
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
};
this.ws.onmessage = this._onmessage.bind(this);
});
return this.connectionPromise;
}
async waitForPingPong() {
return new Promise((res, err) => {
;
this.ws && this.ws.on && this.ws.on("pong", () => res(true)) || err("ws can't listen for pong");
this.ws && this.ws.ping && this.ws.ping();
});
}
async waitForDummyReq() {
return new Promise((resolve, _) => {
const sub = this.subscribe([{ ids: ["a".repeat(64)] }], {
oneose: () => {
sub.close();
resolve(true);
},
eoseTimeout: this.pingTimeout + 1e3
});
});
}
async pingpong() {
if (this.ws?.readyState === 1) {
const result = await Promise.any([
this.ws && this.ws.ping && this.ws.on ? this.waitForPingPong() : this.waitForDummyReq(),
new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
]);
if (result) {
setTimeout(() => this.pingpong(), this.pingFrequency);
} else {
this.closeAllSubscriptions("pingpong timed out");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
}
}
async runQueue() {
this.queueRunning = true;
while (true) {
if (false === this.handleNext()) {
break;
}
await yieldThread();
}
this.queueRunning = false;
}
handleNext() {
const json = this.incomingMessageQueue.dequeue();
if (!json) {
return false;
}
const subid = getSubscriptionId(json);
if (subid) {
const so = this.openSubs.get(subid);
if (!so) {
return;
}
const id = getHex64(json, "id");
const alreadyHave = so.alreadyHaveEvent?.(id);
so.receivedEvent?.(this, id);
if (alreadyHave) {
return;
}
}
try {
let data = JSON.parse(json);
switch (data[0]) {
case "EVENT": {
const so = this.openSubs.get(data[1]);
const event = data[2];
if (this.verifyEvent(event) && matchFilters(so.filters, event)) {
so.onevent(event);
}
return;
}
case "COUNT": {
const id = data[1];
const payload = data[2];
const cr = this.openCountRequests.get(id);
if (cr) {
cr.resolve(payload.count);
this.openCountRequests.delete(id);
}
return;
}
case "EOSE": {
const so = this.openSubs.get(data[1]);
if (!so)
return;
so.receivedEose();
return;
}
case "OK": {
const id = data[1];
const ok = data[2];
const reason = data[3];
const ep = this.openEventPublishes.get(id);
if (ep) {
clearTimeout(ep.timeout);
if (ok)
ep.resolve(reason);
else
ep.reject(new Error(reason));
this.openEventPublishes.delete(id);
}
return;
}
case "CLOSED": {
const id = data[1];
const so = this.openSubs.get(id);
if (!so)
return;
so.closed = true;
so.close(data[2]);
return;
}
case "NOTICE":
this.onnotice(data[1]);
return;
case "AUTH": {
this.challenge = data[1];
return;
}
}
} catch (err) {
return;
}
}
async send(message) {
if (!this.connectionPromise)
throw new SendingOnClosedConnection(message, this.url);
this.connectionPromise.then(() => {
this.ws?.send(message);
});
}
async auth(signAuthEvent) {
const challenge = this.challenge;
if (!challenge)
throw new Error("can't perform auth, no challenge was received");
if (this.authPromise)
return this.authPromise;
this.authPromise = new Promise(async (resolve, reject) => {
try {
let evt = await signAuthEvent(makeAuthEvent(this.url, challenge));
let timeout = setTimeout(() => {
let ep = this.openEventPublishes.get(evt.id);
if (ep) {
ep.reject(new Error("auth timed out"));
this.openEventPublishes.delete(evt.id);
}
}, this.publishTimeout);
this.openEventPublishes.set(evt.id, { resolve, reject, timeout });
this.send('["AUTH",' + JSON.stringify(evt) + "]");
} catch (err) {
console.warn("subscribe auth function failed:", err);
}
});
return this.authPromise;
}
async publish(event) {
const ret = new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
const ep = this.openEventPublishes.get(event.id);
if (ep) {
ep.reject(new Error("publish timed out"));
this.openEventPublishes.delete(event.id);
}
}, this.publishTimeout);
this.openEventPublishes.set(event.id, { resolve, reject, timeout });
});
this.send('["EVENT",' + JSON.stringify(event) + "]");
return ret;
}
async count(filters, params) {
this.serial++;
const id = params?.id || "count:" + this.serial;
const ret = new Promise((resolve, reject) => {
this.openCountRequests.set(id, { resolve, reject });
});
this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1));
return ret;
}
subscribe(filters, params) {
const subscription = this.prepareSubscription(filters, params);
subscription.fire();
return subscription;
}
prepareSubscription(filters, params) {
this.serial++;
const id = params.id || (params.label ? params.label + ":" : "sub:") + this.serial;
const subscription = new Subscription(this, id, filters, params);
this.openSubs.set(id, subscription);
return subscription;
}
close() {
this.closeAllSubscriptions("relay connection closed by us");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
_onmessage(ev) {
this.incomingMessageQueue.enqueue(ev.data);
if (!this.queueRunning) {
this.runQueue();
}
}
};
var Subscription = class {
relay;
id;
closed = false;
eosed = false;
filters;
alreadyHaveEvent;
receivedEvent;
onevent;
oneose;
onclose;
eoseTimeout;
eoseTimeoutHandle;
constructor(relay, id, filters, params) {
this.relay = relay;
this.filters = filters;
this.id = id;
this.alreadyHaveEvent = params.alreadyHaveEvent;
this.receivedEvent = params.receivedEvent;
this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout;
this.oneose = params.oneose;
this.onclose = params.onclose;
this.onevent = params.onevent || ((event) => {
console.warn(
`onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,
event
);
});
}
fire() {
this.relay.send('["REQ","' + this.id + '",' + JSON.stringify(this.filters).substring(1));
this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout);
}
receivedEose() {
if (this.eosed)
return;
clearTimeout(this.eoseTimeoutHandle);
this.eosed = true;
this.oneose?.();
}
close(reason = "closed by caller") {
if (!this.closed && this.relay.connected) {
try {
this.relay.send('["CLOSE",' + JSON.stringify(this.id) + "]");
} catch (err) {
if (err instanceof SendingOnClosedConnection) {
} else {
throw err;
}
}
this.closed = true;
}
this.relay.openSubs.delete(this.id);
this.onclose?.(reason);
}
};
// abstract-pool.ts
var AbstractSimplePool = class {
relays = /* @__PURE__ */ new Map();
seenOn = /* @__PURE__ */ new Map();
trackRelays = false;
verifyEvent;
enablePing;
trustedRelayURLs = /* @__PURE__ */ new Set();
_WebSocket;
constructor(opts) {
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation;
this.enablePing = opts.enablePing;
}
async ensureRelay(url, params) {
url = normalizeURL(url);
let relay = this.relays.get(url);
if (!relay) {
relay = new AbstractRelay(url, {
verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,
websocketImplementation: this._WebSocket,
enablePing: this.enablePing
});
relay.onclose = () => {
this.relays.delete(url);
};
if (params?.connectionTimeout)
relay.connectionTimeout = params.connectionTimeout;
this.relays.set(url, relay);
}
await relay.connect();
return relay;
}
close(relays) {
relays.map(normalizeURL).forEach((url) => {
this.relays.get(url)?.close();
this.relays.delete(url);
});
}
subscribe(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const request = [];
for (let i = 0; i < relays.length; i++) {
const url = normalizeURL(relays[i]);
if (!request.find((r) => r.url === url)) {
request.push({ url, filter });
}
}
return this.subscribeMap(request, params);
}
subscribeMany(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const request = [];
const uniqUrls = [];
for (let i = 0; i < relays.length; i++) {
const url = normalizeURL(relays[i]);
if (uniqUrls.indexOf(url) === -1) {
uniqUrls.push(url);
request.push({ url, filter });
}
}
return this.subscribeMap(request, params);
}
subscribeMap(requests, params) {
params.onauth = params.onauth || params.doauth;
const grouped = /* @__PURE__ */ new Map();
for (const req of requests) {
const { url, filter } = req;
if (!grouped.has(url))
grouped.set(url, []);
grouped.get(url).push(filter);
}
const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }));
if (this.trackRelays) {
params.receivedEvent = (relay, id) => {
let set = this.seenOn.get(id);
if (!set) {
set = /* @__PURE__ */ new Set();
this.seenOn.set(id, set);
}
set.add(relay);
};
}
const _knownIds = /* @__PURE__ */ new Set();
const subs = [];
const eosesReceived = [];
let handleEose = (i) => {
if (eosesReceived[i])
return;
eosesReceived[i] = true;
if (eosesReceived.filter((a) => a).length === requests.length) {
params.oneose?.();
handleEose = () => {
};
}
};
const closesReceived = [];
let handleClose = (i, reason) => {
if (closesReceived[i])
return;
handleEose(i);
closesReceived[i] = reason;
if (closesReceived.filter((a) => a).length === requests.length) {
params.onclose?.(closesReceived);
handleClose = () => {
};
}
};
const localAlreadyHaveEventHandler = (id) => {
if (params.alreadyHaveEvent?.(id)) {
return true;
}
const have = _knownIds.has(id);
_knownIds.add(id);
return have;
};
const allOpened = Promise.all(
groupedRequests.map(async ({ url, filters }, i) => {
let relay;
try {
relay = await this.ensureRelay(url, {
connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1e3) : void 0
});
} catch (err) {
handleClose(i, err?.message || String(err));
return;
}
let subscription = relay.subscribe(filters, {
...params,
oneose: () => handleEose(i),
onclose: (reason) => {
if (reason.startsWith("auth-required: ") && params.onauth) {
relay.auth(params.onauth).then(() => {
relay.subscribe(filters, {
...params,
oneose: () => handleEose(i),
onclose: (reason2) => {
handleClose(i, reason2);
},
alreadyHaveEvent: localAlreadyHaveEventHandler,
eoseTimeout: params.maxWait
});
}).catch((err) => {
handleClose(i, `auth was required and attempted, but failed with: ${err}`);
});
} else {
handleClose(i, reason);
}
},
alreadyHaveEvent: localAlreadyHaveEventHandler,
eoseTimeout: params.maxWait
});
subs.push(subscription);
})
);
return {
async close(reason) {
await allOpened;
subs.forEach((sub) => {
sub.close(reason);
});
}
};
}
subscribeEose(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const subcloser = this.subscribe(relays, filter, {
...params,
oneose() {
subcloser.close("closed automatically on eose");
}
});
return subcloser;
}
subscribeManyEose(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const subcloser = this.subscribeMany(relays, filter, {
...params,
oneose() {
subcloser.close("closed automatically on eose");
}
});
return subcloser;
}
async querySync(relays, filter, params) {
return new Promise(async (resolve) => {
const events = [];
this.subscribeEose(relays, filter, {
...params,
onevent(event) {
events.push(event);
},
onclose(_) {
resolve(events);
}
});
});
}
async get(relays, filter, params) {
filter.limit = 1;
const events = await this.querySync(relays, filter, params);
events.sort((a, b) => b.created_at - a.created_at);
return events[0] || null;
}
publish(relays, event, options) {
return relays.map(normalizeURL).map(async (url, i, arr) => {
if (arr.indexOf(url) !== i) {
return Promise.reject("duplicate url");
}
let r = await this.ensureRelay(url);
return r.publish(event).catch(async (err) => {
if (err instanceof Error && err.message.startsWith("auth-required: ") && options?.onauth) {
await r.auth(options.onauth);
return r.publish(event);
}
throw err;
}).then((reason) => {
if (this.trackRelays) {
let set = this.seenOn.get(event.id);
if (!set) {
set = /* @__PURE__ */ new Set();
this.seenOn.set(event.id, set);
}
set.add(r);
}
return reason;
});
});
}
listConnectionStatus() {
const map = /* @__PURE__ */ new Map();
this.relays.forEach((relay, url) => map.set(url, relay.connected));
return map;
}
destroy() {
this.relays.forEach((conn) => conn.close());
this.relays = /* @__PURE__ */ new Map();
}
};
export {
AbstractSimplePool
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,528 @@
// utils.ts
import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
function normalizeURL(url) {
try {
if (url.indexOf("://") === -1)
url = "wss://" + url;
let p = new URL(url);
p.pathname = p.pathname.replace(/\/+/g, "/");
if (p.pathname.endsWith("/"))
p.pathname = p.pathname.slice(0, -1);
if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:")
p.port = "";
p.searchParams.sort();
p.hash = "";
return p.toString();
} catch (e) {
throw new Error(`Invalid URL: ${url}`);
}
}
var QueueNode = class {
value;
next = null;
prev = null;
constructor(message) {
this.value = message;
}
};
var Queue = class {
first;
last;
constructor() {
this.first = null;
this.last = null;
}
enqueue(value) {
const newNode = new QueueNode(value);
if (!this.last) {
this.first = newNode;
this.last = newNode;
} else if (this.last === this.first) {
this.last = newNode;
this.last.prev = this.first;
this.first.next = newNode;
} else {
newNode.prev = this.last;
this.last.next = newNode;
this.last = newNode;
}
return true;
}
dequeue() {
if (!this.first)
return null;
if (this.first === this.last) {
const target2 = this.first;
this.first = null;
this.last = null;
return target2.value;
}
const target = this.first;
this.first = target.next;
if (this.first) {
this.first.prev = null;
}
return target.value;
}
};
// kinds.ts
var ClientAuth = 22242;
// filter.ts
function matchFilter(filter, event) {
if (filter.ids && filter.ids.indexOf(event.id) === -1) {
return false;
}
if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {
return false;
}
if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {
return false;
}
for (let f in filter) {
if (f[0] === "#") {
let tagName = f.slice(1);
let values = filter[`#${tagName}`];
if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values.indexOf(v) !== -1))
return false;
}
}
if (filter.since && event.created_at < filter.since)
return false;
if (filter.until && event.created_at > filter.until)
return false;
return true;
}
function matchFilters(filters, event) {
for (let i = 0; i < filters.length; i++) {
if (matchFilter(filters[i], event)) {
return true;
}
}
return false;
}
// fakejson.ts
function getHex64(json, field) {
let len = field.length + 3;
let idx = json.indexOf(`"${field}":`) + len;
let s = json.slice(idx).indexOf(`"`) + idx + 1;
return json.slice(s, s + 64);
}
function getSubscriptionId(json) {
let idx = json.slice(0, 22).indexOf(`"EVENT"`);
if (idx === -1)
return null;
let pstart = json.slice(idx + 7 + 1).indexOf(`"`);
if (pstart === -1)
return null;
let start = idx + 7 + 1 + pstart;
let pend = json.slice(start + 1, 80).indexOf(`"`);
if (pend === -1)
return null;
let end = start + 1 + pend;
return json.slice(start + 1, end);
}
// nip42.ts
function makeAuthEvent(relayURL, challenge) {
return {
kind: ClientAuth,
created_at: Math.floor(Date.now() / 1e3),
tags: [
["relay", relayURL],
["challenge", challenge]
],
content: ""
};
}
// helpers.ts
async function yieldThread() {
return new Promise((resolve) => {
const ch = new MessageChannel();
const handler = () => {
ch.port1.removeEventListener("message", handler);
resolve();
};
ch.port1.addEventListener("message", handler);
ch.port2.postMessage(0);
ch.port1.start();
});
}
// abstract-relay.ts
var SendingOnClosedConnection = class extends Error {
constructor(message, relay) {
super(`Tried to send message '${message} on a closed connection to ${relay}.`);
this.name = "SendingOnClosedConnection";
}
};
var AbstractRelay = class {
url;
_connected = false;
onclose = null;
onnotice = (msg) => console.debug(`NOTICE from ${this.url}: ${msg}`);
baseEoseTimeout = 4400;
connectionTimeout = 4400;
publishTimeout = 4400;
pingFrequency = 2e4;
pingTimeout = 2e4;
openSubs = /* @__PURE__ */ new Map();
enablePing;
connectionTimeoutHandle;
connectionPromise;
openCountRequests = /* @__PURE__ */ new Map();
openEventPublishes = /* @__PURE__ */ new Map();
ws;
incomingMessageQueue = new Queue();
queueRunning = false;
challenge;
authPromise;
serial = 0;
verifyEvent;
_WebSocket;
constructor(url, opts) {
this.url = normalizeURL(url);
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation || WebSocket;
this.enablePing = opts.enablePing;
}
static async connect(url, opts) {
const relay = new AbstractRelay(url, opts);
await relay.connect();
return relay;
}
closeAllSubscriptions(reason) {
for (let [_, sub] of this.openSubs) {
sub.close(reason);
}
this.openSubs.clear();
for (let [_, ep] of this.openEventPublishes) {
ep.reject(new Error(reason));
}
this.openEventPublishes.clear();
for (let [_, cr] of this.openCountRequests) {
cr.reject(new Error(reason));
}
this.openCountRequests.clear();
}
get connected() {
return this._connected;
}
async connect() {
if (this.connectionPromise)
return this.connectionPromise;
this.challenge = void 0;
this.authPromise = void 0;
this.connectionPromise = new Promise((resolve, reject) => {
this.connectionTimeoutHandle = setTimeout(() => {
reject("connection timed out");
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection timed out");
}, this.connectionTimeout);
try {
this.ws = new this._WebSocket(this.url);
} catch (err) {
clearTimeout(this.connectionTimeoutHandle);
reject(err);
return;
}
this.ws.onopen = () => {
clearTimeout(this.connectionTimeoutHandle);
this._connected = true;
if (this.enablePing) {
this.pingpong();
}
resolve();
};
this.ws.onerror = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket error");
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
};
this.ws.onclose = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket closed");
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
};
this.ws.onmessage = this._onmessage.bind(this);
});
return this.connectionPromise;
}
async waitForPingPong() {
return new Promise((res, err) => {
;
this.ws && this.ws.on && this.ws.on("pong", () => res(true)) || err("ws can't listen for pong");
this.ws && this.ws.ping && this.ws.ping();
});
}
async waitForDummyReq() {
return new Promise((resolve, _) => {
const sub = this.subscribe([{ ids: ["a".repeat(64)] }], {
oneose: () => {
sub.close();
resolve(true);
},
eoseTimeout: this.pingTimeout + 1e3
});
});
}
async pingpong() {
if (this.ws?.readyState === 1) {
const result = await Promise.any([
this.ws && this.ws.ping && this.ws.on ? this.waitForPingPong() : this.waitForDummyReq(),
new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
]);
if (result) {
setTimeout(() => this.pingpong(), this.pingFrequency);
} else {
this.closeAllSubscriptions("pingpong timed out");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
}
}
async runQueue() {
this.queueRunning = true;
while (true) {
if (false === this.handleNext()) {
break;
}
await yieldThread();
}
this.queueRunning = false;
}
handleNext() {
const json = this.incomingMessageQueue.dequeue();
if (!json) {
return false;
}
const subid = getSubscriptionId(json);
if (subid) {
const so = this.openSubs.get(subid);
if (!so) {
return;
}
const id = getHex64(json, "id");
const alreadyHave = so.alreadyHaveEvent?.(id);
so.receivedEvent?.(this, id);
if (alreadyHave) {
return;
}
}
try {
let data = JSON.parse(json);
switch (data[0]) {
case "EVENT": {
const so = this.openSubs.get(data[1]);
const event = data[2];
if (this.verifyEvent(event) && matchFilters(so.filters, event)) {
so.onevent(event);
}
return;
}
case "COUNT": {
const id = data[1];
const payload = data[2];
const cr = this.openCountRequests.get(id);
if (cr) {
cr.resolve(payload.count);
this.openCountRequests.delete(id);
}
return;
}
case "EOSE": {
const so = this.openSubs.get(data[1]);
if (!so)
return;
so.receivedEose();
return;
}
case "OK": {
const id = data[1];
const ok = data[2];
const reason = data[3];
const ep = this.openEventPublishes.get(id);
if (ep) {
clearTimeout(ep.timeout);
if (ok)
ep.resolve(reason);
else
ep.reject(new Error(reason));
this.openEventPublishes.delete(id);
}
return;
}
case "CLOSED": {
const id = data[1];
const so = this.openSubs.get(id);
if (!so)
return;
so.closed = true;
so.close(data[2]);
return;
}
case "NOTICE":
this.onnotice(data[1]);
return;
case "AUTH": {
this.challenge = data[1];
return;
}
}
} catch (err) {
return;
}
}
async send(message) {
if (!this.connectionPromise)
throw new SendingOnClosedConnection(message, this.url);
this.connectionPromise.then(() => {
this.ws?.send(message);
});
}
async auth(signAuthEvent) {
const challenge = this.challenge;
if (!challenge)
throw new Error("can't perform auth, no challenge was received");
if (this.authPromise)
return this.authPromise;
this.authPromise = new Promise(async (resolve, reject) => {
try {
let evt = await signAuthEvent(makeAuthEvent(this.url, challenge));
let timeout = setTimeout(() => {
let ep = this.openEventPublishes.get(evt.id);
if (ep) {
ep.reject(new Error("auth timed out"));
this.openEventPublishes.delete(evt.id);
}
}, this.publishTimeout);
this.openEventPublishes.set(evt.id, { resolve, reject, timeout });
this.send('["AUTH",' + JSON.stringify(evt) + "]");
} catch (err) {
console.warn("subscribe auth function failed:", err);
}
});
return this.authPromise;
}
async publish(event) {
const ret = new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
const ep = this.openEventPublishes.get(event.id);
if (ep) {
ep.reject(new Error("publish timed out"));
this.openEventPublishes.delete(event.id);
}
}, this.publishTimeout);
this.openEventPublishes.set(event.id, { resolve, reject, timeout });
});
this.send('["EVENT",' + JSON.stringify(event) + "]");
return ret;
}
async count(filters, params) {
this.serial++;
const id = params?.id || "count:" + this.serial;
const ret = new Promise((resolve, reject) => {
this.openCountRequests.set(id, { resolve, reject });
});
this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1));
return ret;
}
subscribe(filters, params) {
const subscription = this.prepareSubscription(filters, params);
subscription.fire();
return subscription;
}
prepareSubscription(filters, params) {
this.serial++;
const id = params.id || (params.label ? params.label + ":" : "sub:") + this.serial;
const subscription = new Subscription(this, id, filters, params);
this.openSubs.set(id, subscription);
return subscription;
}
close() {
this.closeAllSubscriptions("relay connection closed by us");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
_onmessage(ev) {
this.incomingMessageQueue.enqueue(ev.data);
if (!this.queueRunning) {
this.runQueue();
}
}
};
var Subscription = class {
relay;
id;
closed = false;
eosed = false;
filters;
alreadyHaveEvent;
receivedEvent;
onevent;
oneose;
onclose;
eoseTimeout;
eoseTimeoutHandle;
constructor(relay, id, filters, params) {
this.relay = relay;
this.filters = filters;
this.id = id;
this.alreadyHaveEvent = params.alreadyHaveEvent;
this.receivedEvent = params.receivedEvent;
this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout;
this.oneose = params.oneose;
this.onclose = params.onclose;
this.onevent = params.onevent || ((event) => {
console.warn(
`onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,
event
);
});
}
fire() {
this.relay.send('["REQ","' + this.id + '",' + JSON.stringify(this.filters).substring(1));
this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout);
}
receivedEose() {
if (this.eosed)
return;
clearTimeout(this.eoseTimeoutHandle);
this.eosed = true;
this.oneose?.();
}
close(reason = "closed by caller") {
if (!this.closed && this.relay.connected) {
try {
this.relay.send('["CLOSE",' + JSON.stringify(this.id) + "]");
} catch (err) {
if (err instanceof SendingOnClosedConnection) {
} else {
throw err;
}
}
this.closed = true;
}
this.relay.openSubs.delete(this.id);
this.onclose?.(reason);
}
};
export {
AbstractRelay,
SendingOnClosedConnection,
Subscription
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,45 @@
// fakejson.ts
function getHex64(json, field) {
let len = field.length + 3;
let idx = json.indexOf(`"${field}":`) + len;
let s = json.slice(idx).indexOf(`"`) + idx + 1;
return json.slice(s, s + 64);
}
function getInt(json, field) {
let len = field.length;
let idx = json.indexOf(`"${field}":`) + len + 3;
let sliced = json.slice(idx);
let end = Math.min(sliced.indexOf(","), sliced.indexOf("}"));
return parseInt(sliced.slice(0, end), 10);
}
function getSubscriptionId(json) {
let idx = json.slice(0, 22).indexOf(`"EVENT"`);
if (idx === -1)
return null;
let pstart = json.slice(idx + 7 + 1).indexOf(`"`);
if (pstart === -1)
return null;
let start = idx + 7 + 1 + pstart;
let pend = json.slice(start + 1, 80).indexOf(`"`);
if (pend === -1)
return null;
let end = start + 1 + pend;
return json.slice(start + 1, end);
}
function matchEventId(json, id) {
return id === getHex64(json, "id");
}
function matchEventPubkey(json, pubkey) {
return pubkey === getHex64(json, "pubkey");
}
function matchEventKind(json, kind) {
return kind === getInt(json, "kind");
}
export {
getHex64,
getInt,
getSubscriptionId,
matchEventId,
matchEventKind,
matchEventPubkey
};

Some files were not shown because too many files have changed in this diff Show More