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

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
};

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": ";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,88 @@
// 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
);
}
export {
getFilterLimit,
matchFilter,
matchFilters,
mergeFilters
};

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