Files
rss-nostr-lambda/node_modules/nostr-tools/lib/esm/index.js
T

2110 lines
58 KiB
JavaScript

var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// keys.ts
import { schnorr } from "@noble/curves/secp256k1";
import { bytesToHex } from "@noble/hashes/utils";
function generatePrivateKey() {
return bytesToHex(schnorr.utils.randomPrivateKey());
}
function getPublicKey(privateKey) {
return bytesToHex(schnorr.getPublicKey(privateKey));
}
// event.ts
import { schnorr as schnorr2 } from "@noble/curves/secp256k1";
import { sha256 } from "@noble/hashes/sha256";
import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
// utils.ts
var utils_exports = {};
__export(utils_exports, {
MessageNode: () => MessageNode,
MessageQueue: () => MessageQueue,
insertEventIntoAscendingList: () => insertEventIntoAscendingList,
insertEventIntoDescendingList: () => insertEventIntoDescendingList,
normalizeURL: () => normalizeURL,
utf8Decoder: () => utf8Decoder,
utf8Encoder: () => utf8Encoder
});
var utf8Decoder = new TextDecoder("utf-8");
var utf8Encoder = new TextEncoder();
function normalizeURL(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();
}
function insertEventIntoDescendingList(sortedArray, event) {
let start = 0;
let end = sortedArray.length - 1;
let midPoint;
let position = start;
if (end < 0) {
position = 0;
} else if (event.created_at < sortedArray[end].created_at) {
position = end + 1;
} else if (event.created_at >= sortedArray[start].created_at) {
position = start;
} else
while (true) {
if (end <= start + 1) {
position = end;
break;
}
midPoint = Math.floor(start + (end - start) / 2);
if (sortedArray[midPoint].created_at > event.created_at) {
start = midPoint;
} else if (sortedArray[midPoint].created_at < event.created_at) {
end = midPoint;
} else {
position = midPoint;
break;
}
}
if (sortedArray[position]?.id !== event.id) {
return [...sortedArray.slice(0, position), event, ...sortedArray.slice(position)];
}
return sortedArray;
}
function insertEventIntoAscendingList(sortedArray, event) {
let start = 0;
let end = sortedArray.length - 1;
let midPoint;
let position = start;
if (end < 0) {
position = 0;
} else if (event.created_at > sortedArray[end].created_at) {
position = end + 1;
} else if (event.created_at <= sortedArray[start].created_at) {
position = start;
} else
while (true) {
if (end <= start + 1) {
position = end;
break;
}
midPoint = Math.floor(start + (end - start) / 2);
if (sortedArray[midPoint].created_at < event.created_at) {
start = midPoint;
} else if (sortedArray[midPoint].created_at > event.created_at) {
end = midPoint;
} else {
position = midPoint;
break;
}
}
if (sortedArray[position]?.id !== event.id) {
return [...sortedArray.slice(0, position), event, ...sortedArray.slice(position)];
}
return sortedArray;
}
var MessageNode = class {
_value;
_next;
get value() {
return this._value;
}
set value(message) {
this._value = message;
}
get next() {
return this._next;
}
set next(node) {
this._next = node;
}
constructor(message) {
this._value = message;
this._next = null;
}
};
var MessageQueue = class {
_first;
_last;
get first() {
return this._first;
}
set first(messageNode) {
this._first = messageNode;
}
get last() {
return this._last;
}
set last(messageNode) {
this._last = messageNode;
}
_size;
get size() {
return this._size;
}
set size(v) {
this._size = v;
}
constructor() {
this._first = null;
this._last = null;
this._size = 0;
}
enqueue(message) {
const newNode = new MessageNode(message);
if (this._size === 0 || !this._last) {
this._first = newNode;
this._last = newNode;
} else {
this._last.next = newNode;
this._last = newNode;
}
this._size++;
return true;
}
dequeue() {
if (this._size === 0 || !this._first)
return null;
let prev = this._first;
this._first = prev.next;
prev.next = null;
this._size--;
return prev.value;
}
};
// event.ts
var verifiedSymbol = Symbol("verified");
var Kind = /* @__PURE__ */ ((Kind3) => {
Kind3[Kind3["Metadata"] = 0] = "Metadata";
Kind3[Kind3["Text"] = 1] = "Text";
Kind3[Kind3["RecommendRelay"] = 2] = "RecommendRelay";
Kind3[Kind3["Contacts"] = 3] = "Contacts";
Kind3[Kind3["EncryptedDirectMessage"] = 4] = "EncryptedDirectMessage";
Kind3[Kind3["EventDeletion"] = 5] = "EventDeletion";
Kind3[Kind3["Repost"] = 6] = "Repost";
Kind3[Kind3["Reaction"] = 7] = "Reaction";
Kind3[Kind3["BadgeAward"] = 8] = "BadgeAward";
Kind3[Kind3["ChannelCreation"] = 40] = "ChannelCreation";
Kind3[Kind3["ChannelMetadata"] = 41] = "ChannelMetadata";
Kind3[Kind3["ChannelMessage"] = 42] = "ChannelMessage";
Kind3[Kind3["ChannelHideMessage"] = 43] = "ChannelHideMessage";
Kind3[Kind3["ChannelMuteUser"] = 44] = "ChannelMuteUser";
Kind3[Kind3["Blank"] = 255] = "Blank";
Kind3[Kind3["Report"] = 1984] = "Report";
Kind3[Kind3["ZapRequest"] = 9734] = "ZapRequest";
Kind3[Kind3["Zap"] = 9735] = "Zap";
Kind3[Kind3["RelayList"] = 10002] = "RelayList";
Kind3[Kind3["ClientAuth"] = 22242] = "ClientAuth";
Kind3[Kind3["NwcRequest"] = 23194] = "NwcRequest";
Kind3[Kind3["HttpAuth"] = 27235] = "HttpAuth";
Kind3[Kind3["ProfileBadge"] = 30008] = "ProfileBadge";
Kind3[Kind3["BadgeDefinition"] = 30009] = "BadgeDefinition";
Kind3[Kind3["Article"] = 30023] = "Article";
Kind3[Kind3["FileMetadata"] = 1063] = "FileMetadata";
return Kind3;
})(Kind || {});
function getBlankEvent(kind = 255 /* Blank */) {
return {
kind,
content: "",
tags: [],
created_at: 0
};
}
function finishEvent(t, privateKey) {
const event = t;
event.pubkey = getPublicKey(privateKey);
event.id = getEventHash(event);
event.sig = getSignature(event, privateKey);
event[verifiedSymbol] = true;
return event;
}
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 = sha256(utf8Encoder.encode(serializeEvent(event)));
return bytesToHex2(eventHash);
}
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] === "object")
return false;
}
}
return true;
}
function verifySignature(event) {
if (typeof event[verifiedSymbol] === "boolean")
return event[verifiedSymbol];
const hash = getEventHash(event);
if (hash !== event.id) {
return event[verifiedSymbol] = false;
}
try {
return event[verifiedSymbol] = schnorr2.verify(event.sig, hash, event.pubkey);
} catch (err) {
return event[verifiedSymbol] = false;
}
}
function signEvent(event, key) {
console.warn(
"nostr-tools: `signEvent` is deprecated and will be removed or changed in the future. Please use `getSignature` instead."
);
return getSignature(event, key);
}
function getSignature(event, key) {
return bytesToHex2(schnorr2.sign(getEventHash(event), key));
}
// filter.ts
function matchFilter(filter, event) {
if (filter.ids && filter.ids.indexOf(event.id) === -1) {
if (!filter.ids.some((prefix) => event.id.startsWith(prefix))) {
return false;
}
}
if (filter.kinds && filter.kinds.indexOf(event.kind) === -1)
return false;
if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {
if (!filter.authors.some((prefix) => event.pubkey.startsWith(prefix))) {
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;
}
// fakejson.ts
var fakejson_exports = {};
__export(fakejson_exports, {
getHex64: () => getHex64,
getInt: () => getInt,
getSubscriptionId: () => getSubscriptionId,
matchEventId: () => matchEventId,
matchEventKind: () => matchEventKind,
matchEventPubkey: () => matchEventPubkey
});
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");
}
// relay.ts
var newListeners = () => ({
connect: [],
disconnect: [],
error: [],
notice: [],
auth: []
});
function relayInit(url, options = {}) {
let { listTimeout = 3e3, getTimeout = 3e3, countTimeout = 3e3 } = options;
var ws;
var openSubs = {};
var listeners = newListeners();
var subListeners = {};
var pubListeners = {};
var connectionPromise;
async function connectRelay() {
if (connectionPromise)
return connectionPromise;
connectionPromise = new Promise((resolve, reject) => {
try {
ws = new WebSocket(url);
} catch (err) {
reject(err);
}
ws.onopen = () => {
listeners.connect.forEach((cb) => cb());
resolve();
};
ws.onerror = () => {
connectionPromise = void 0;
listeners.error.forEach((cb) => cb());
reject();
};
ws.onclose = async () => {
connectionPromise = void 0;
listeners.disconnect.forEach((cb) => cb());
};
let incomingMessageQueue = new MessageQueue();
let handleNextInterval;
ws.onmessage = (e) => {
incomingMessageQueue.enqueue(e.data);
if (!handleNextInterval) {
handleNextInterval = setInterval(handleNext, 0);
}
};
function handleNext() {
if (incomingMessageQueue.size === 0) {
clearInterval(handleNextInterval);
handleNextInterval = null;
return;
}
var json = incomingMessageQueue.dequeue();
if (!json)
return;
let subid = getSubscriptionId(json);
if (subid) {
let so = openSubs[subid];
if (so && so.alreadyHaveEvent && so.alreadyHaveEvent(getHex64(json, "id"), url)) {
return;
}
}
try {
let data = JSON.parse(json);
switch (data[0]) {
case "EVENT": {
let id2 = data[1];
let event = data[2];
if (validateEvent(event) && openSubs[id2] && (openSubs[id2].skipVerification || verifySignature(event)) && matchFilters(openSubs[id2].filters, event)) {
openSubs[id2];
(subListeners[id2]?.event || []).forEach((cb) => cb(event));
}
return;
}
case "COUNT":
let id = data[1];
let payload = data[2];
if (openSubs[id]) {
;
(subListeners[id]?.count || []).forEach((cb) => cb(payload));
}
return;
case "EOSE": {
let id2 = data[1];
if (id2 in subListeners) {
subListeners[id2].eose.forEach((cb) => cb());
subListeners[id2].eose = [];
}
return;
}
case "OK": {
let id2 = data[1];
let ok = data[2];
let reason = data[3] || "";
if (id2 in pubListeners) {
let { resolve: resolve2, reject: reject2 } = pubListeners[id2];
if (ok)
resolve2(null);
else
reject2(new Error(reason));
}
return;
}
case "NOTICE":
let notice = data[1];
listeners.notice.forEach((cb) => cb(notice));
return;
case "AUTH": {
let challenge = data[1];
listeners.auth?.forEach((cb) => cb(challenge));
return;
}
}
} catch (err) {
return;
}
}
});
return connectionPromise;
}
function connected() {
return ws?.readyState === 1;
}
async function connect() {
if (connected())
return;
await connectRelay();
}
async function trySend(params) {
let msg = JSON.stringify(params);
if (!connected()) {
await new Promise((resolve) => setTimeout(resolve, 1e3));
if (!connected()) {
return;
}
}
try {
ws.send(msg);
} catch (err) {
console.log(err);
}
}
const sub = (filters, {
verb = "REQ",
skipVerification = false,
alreadyHaveEvent = null,
id = Math.random().toString().slice(2)
} = {}) => {
let subid = id;
openSubs[subid] = {
id: subid,
filters,
skipVerification,
alreadyHaveEvent
};
trySend([verb, subid, ...filters]);
let subscription = {
sub: (newFilters, newOpts = {}) => sub(newFilters || filters, {
skipVerification: newOpts.skipVerification || skipVerification,
alreadyHaveEvent: newOpts.alreadyHaveEvent || alreadyHaveEvent,
id: subid
}),
unsub: () => {
delete openSubs[subid];
delete subListeners[subid];
trySend(["CLOSE", subid]);
},
on: (type, cb) => {
subListeners[subid] = subListeners[subid] || {
event: [],
count: [],
eose: []
};
subListeners[subid][type].push(cb);
},
off: (type, cb) => {
let listeners2 = subListeners[subid];
let idx = listeners2[type].indexOf(cb);
if (idx >= 0)
listeners2[type].splice(idx, 1);
},
get events() {
return eventsGenerator(subscription);
}
};
return subscription;
};
function _publishEvent(event, type) {
return new Promise((resolve, reject) => {
if (!event.id) {
reject(new Error(`event ${event} has no id`));
return;
}
let id = event.id;
trySend([type, event]);
pubListeners[id] = { resolve, reject };
});
}
return {
url,
sub,
on: (type, cb) => {
listeners[type].push(cb);
if (type === "connect" && ws?.readyState === 1) {
;
cb();
}
},
off: (type, cb) => {
let index = listeners[type].indexOf(cb);
if (index !== -1)
listeners[type].splice(index, 1);
},
list: (filters, opts) => new Promise((resolve) => {
let s = sub(filters, opts);
let events = [];
let timeout = setTimeout(() => {
s.unsub();
resolve(events);
}, listTimeout);
s.on("eose", () => {
s.unsub();
clearTimeout(timeout);
resolve(events);
});
s.on("event", (event) => {
events.push(event);
});
}),
get: (filter, opts) => new Promise((resolve) => {
let s = sub([filter], opts);
let timeout = setTimeout(() => {
s.unsub();
resolve(null);
}, getTimeout);
s.on("event", (event) => {
s.unsub();
clearTimeout(timeout);
resolve(event);
});
}),
count: (filters) => new Promise((resolve) => {
let s = sub(filters, { ...sub, verb: "COUNT" });
let timeout = setTimeout(() => {
s.unsub();
resolve(null);
}, countTimeout);
s.on("count", (event) => {
s.unsub();
clearTimeout(timeout);
resolve(event);
});
}),
async publish(event) {
await _publishEvent(event, "EVENT");
},
async auth(event) {
await _publishEvent(event, "AUTH");
},
connect,
close() {
listeners = newListeners();
subListeners = {};
pubListeners = {};
if (ws?.readyState === WebSocket.OPEN) {
ws.close();
}
},
get status() {
return ws?.readyState ?? 3;
}
};
}
async function* eventsGenerator(sub) {
let nextResolve;
const eventQueue = [];
const pushToQueue = (event) => {
if (nextResolve) {
nextResolve(event);
nextResolve = void 0;
} else {
eventQueue.push(event);
}
};
sub.on("event", pushToQueue);
try {
while (true) {
if (eventQueue.length > 0) {
yield eventQueue.shift();
} else {
const event = await new Promise((resolve) => {
nextResolve = resolve;
});
yield event;
}
}
} finally {
sub.off("event", pushToQueue);
}
}
// pool.ts
var SimplePool = class {
_conn;
_seenOn = {};
batchedByKey = {};
eoseSubTimeout;
getTimeout;
seenOnEnabled = true;
batchInterval = 100;
constructor(options = {}) {
this._conn = {};
this.eoseSubTimeout = options.eoseSubTimeout || 3400;
this.getTimeout = options.getTimeout || 3400;
this.seenOnEnabled = options.seenOnEnabled !== false;
this.batchInterval = options.batchInterval || 100;
}
close(relays) {
relays.forEach((url) => {
let relay = this._conn[normalizeURL(url)];
if (relay)
relay.close();
});
}
async ensureRelay(url) {
const nm = normalizeURL(url);
if (!this._conn[nm]) {
this._conn[nm] = relayInit(nm, {
getTimeout: this.getTimeout * 0.9,
listTimeout: this.getTimeout * 0.9
});
}
const relay = this._conn[nm];
await relay.connect();
return relay;
}
sub(relays, filters, opts) {
let _knownIds = /* @__PURE__ */ new Set();
let modifiedOpts = { ...opts || {} };
modifiedOpts.alreadyHaveEvent = (id, url) => {
if (opts?.alreadyHaveEvent?.(id, url)) {
return true;
}
if (this.seenOnEnabled) {
let set = this._seenOn[id] || /* @__PURE__ */ new Set();
set.add(url);
this._seenOn[id] = set;
}
return _knownIds.has(id);
};
let subs = [];
let eventListeners = /* @__PURE__ */ new Set();
let eoseListeners = /* @__PURE__ */ new Set();
let eosesMissing = relays.length;
let eoseSent = false;
let eoseTimeout = setTimeout(
() => {
eoseSent = true;
for (let cb of eoseListeners.values())
cb();
},
opts?.eoseSubTimeout || this.eoseSubTimeout
);
relays.filter((r, i, a) => a.indexOf(r) === i).forEach(async (relay) => {
let r;
try {
r = await this.ensureRelay(relay);
} catch (err) {
handleEose();
return;
}
if (!r)
return;
let s = r.sub(filters, modifiedOpts);
s.on("event", (event) => {
_knownIds.add(event.id);
for (let cb of eventListeners.values())
cb(event);
});
s.on("eose", () => {
if (eoseSent)
return;
handleEose();
});
subs.push(s);
function handleEose() {
eosesMissing--;
if (eosesMissing === 0) {
clearTimeout(eoseTimeout);
for (let cb of eoseListeners.values())
cb();
}
}
});
let greaterSub = {
sub(filters2, opts2) {
subs.forEach((sub) => sub.sub(filters2, opts2));
return greaterSub;
},
unsub() {
subs.forEach((sub) => sub.unsub());
},
on(type, cb) {
if (type === "event") {
eventListeners.add(cb);
} else if (type === "eose") {
eoseListeners.add(cb);
}
},
off(type, cb) {
if (type === "event") {
eventListeners.delete(cb);
} else if (type === "eose")
eoseListeners.delete(cb);
},
get events() {
return eventsGenerator(greaterSub);
}
};
return greaterSub;
}
get(relays, filter, opts) {
return new Promise((resolve) => {
let sub = this.sub(relays, [filter], opts);
let timeout = setTimeout(() => {
sub.unsub();
resolve(null);
}, this.getTimeout);
sub.on("event", (event) => {
resolve(event);
clearTimeout(timeout);
sub.unsub();
});
});
}
list(relays, filters, opts) {
return new Promise((resolve) => {
let events = [];
let sub = this.sub(relays, filters, opts);
sub.on("event", (event) => {
events.push(event);
});
sub.on("eose", () => {
sub.unsub();
resolve(events);
});
});
}
batchedList(batchKey, relays, filters) {
return new Promise((resolve) => {
if (!this.batchedByKey[batchKey]) {
this.batchedByKey[batchKey] = [
{
filters,
relays,
resolve,
events: []
}
];
setTimeout(() => {
Object.keys(this.batchedByKey).forEach(async (batchKey2) => {
const batchedRequests = this.batchedByKey[batchKey2];
const filters2 = [];
const relays2 = [];
batchedRequests.forEach((br) => {
filters2.push(...br.filters);
relays2.push(...br.relays);
});
const sub = this.sub(relays2, [mergeFilters(...filters2)]);
sub.on("event", (event) => {
batchedRequests.forEach((br) => matchFilters(br.filters, event) && br.events.push(event));
});
sub.on("eose", () => {
sub.unsub();
batchedRequests.forEach((br) => br.resolve(br.events));
});
delete this.batchedByKey[batchKey2];
});
}, this.batchInterval);
} else {
this.batchedByKey[batchKey].push({
filters,
relays,
resolve,
events: []
});
}
});
}
publish(relays, event) {
return relays.map(async (relay) => {
let r = await this.ensureRelay(relay);
return r.publish(event);
});
}
seenOn(id) {
return Array.from(this._seenOn[id]?.values?.() || []);
}
};
// nip19.ts
var nip19_exports = {};
__export(nip19_exports, {
BECH32_REGEX: () => BECH32_REGEX,
decode: () => decode,
naddrEncode: () => naddrEncode,
neventEncode: () => neventEncode,
noteEncode: () => noteEncode,
nprofileEncode: () => nprofileEncode,
npubEncode: () => npubEncode,
nrelayEncode: () => nrelayEncode,
nsecEncode: () => nsecEncode
});
import { bytesToHex as bytesToHex3, concatBytes, hexToBytes } from "@noble/hashes/utils";
import { bech32 } from "@scure/base";
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 decode(nip19) {
let { prefix, words } = bech32.decode(nip19, Bech32MaxSize);
let data = new Uint8Array(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: bytesToHex3(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: bytesToHex3(tlv[0][0]),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
author: tlv[2]?.[0] ? bytesToHex3(tlv[2][0]) : void 0,
kind: tlv[3]?.[0] ? parseInt(bytesToHex3(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: bytesToHex3(tlv[2][0]),
kind: parseInt(bytesToHex3(tlv[3][0]), 16),
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
}
};
}
case "nrelay": {
let tlv = parseTLV(data);
if (!tlv[0]?.[0])
throw new Error("missing TLV 0 for nrelay");
return {
type: "nrelay",
data: utf8Decoder.decode(tlv[0][0])
};
}
case "nsec":
case "npub":
case "note":
return { type: prefix, data: bytesToHex3(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];
if (!l)
throw new Error(`malformed TLV ${t}`);
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(hex) {
return encodeBytes("nsec", hex);
}
function npubEncode(hex) {
return encodeBytes("npub", hex);
}
function noteEncode(hex) {
return encodeBytes("note", hex);
}
function encodeBech32(prefix, data) {
let words = bech32.toWords(data);
return bech32.encode(prefix, words, Bech32MaxSize);
}
function encodeBytes(prefix, hex) {
let data = hexToBytes(hex);
return encodeBech32(prefix, data);
}
function nprofileEncode(profile) {
let data = encodeTLV({
0: [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: [hexToBytes(event.id)],
1: (event.relays || []).map((url) => utf8Encoder.encode(url)),
2: event.author ? [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: [hexToBytes(addr.pubkey)],
3: [new Uint8Array(kind)]
});
return encodeBech32("naddr", data);
}
function nrelayEncode(url) {
let data = encodeTLV({
0: [utf8Encoder.encode(url)]
});
return encodeBech32("nrelay", data);
}
function encodeTLV(tlv) {
let entries = [];
Object.entries(tlv).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 concatBytes(...entries);
}
// 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;
}
// nip04.ts
var nip04_exports = {};
__export(nip04_exports, {
decrypt: () => decrypt,
encrypt: () => encrypt
});
import { randomBytes } from "@noble/hashes/utils";
import { secp256k1 } from "@noble/curves/secp256k1";
import { base64 } from "@scure/base";
if (typeof crypto !== "undefined" && !crypto.subtle && crypto.webcrypto) {
crypto.subtle = crypto.webcrypto.subtle;
}
async function encrypt(privkey, pubkey, text) {
const key = secp256k1.getSharedSecret(privkey, "02" + pubkey);
const normalizedKey = getNormalizedX(key);
let iv = Uint8Array.from(randomBytes(16));
let plaintext = utf8Encoder.encode(text);
let cryptoKey = await crypto.subtle.importKey("raw", normalizedKey, { name: "AES-CBC" }, false, ["encrypt"]);
let ciphertext = await crypto.subtle.encrypt({ name: "AES-CBC", iv }, cryptoKey, plaintext);
let ctb64 = base64.encode(new Uint8Array(ciphertext));
let ivb64 = base64.encode(new Uint8Array(iv.buffer));
return `${ctb64}?iv=${ivb64}`;
}
async function decrypt(privkey, pubkey, data) {
let [ctb64, ivb64] = data.split("?iv=");
let key = secp256k1.getSharedSecret(privkey, "02" + pubkey);
let normalizedKey = getNormalizedX(key);
let cryptoKey = await crypto.subtle.importKey("raw", normalizedKey, { name: "AES-CBC" }, false, ["decrypt"]);
let ciphertext = base64.decode(ctb64);
let iv = base64.decode(ivb64);
let plaintext = await crypto.subtle.decrypt({ name: "AES-CBC", iv }, cryptoKey, ciphertext);
let text = utf8Decoder.decode(plaintext);
return text;
}
function getNormalizedX(key) {
return key.slice(1, 33);
}
// nip05.ts
var nip05_exports = {};
__export(nip05_exports, {
NIP05_REGEX: () => NIP05_REGEX,
queryProfile: () => queryProfile,
searchDomain: () => searchDomain,
useFetchImplementation: () => useFetchImplementation
});
var NIP05_REGEX = /^(?:([\w.+-]+)@)?([\w.-]+)$/;
var _fetch;
try {
_fetch = fetch;
} catch {
}
function useFetchImplementation(fetchImplementation) {
_fetch = fetchImplementation;
}
async function searchDomain(domain, query = "") {
try {
let res = await (await _fetch(`https://${domain}/.well-known/nostr.json?name=${query}`)).json();
return res.names;
} catch (_) {
return {};
}
}
async function queryProfile(fullname) {
const match = fullname.match(NIP05_REGEX);
if (!match)
return null;
const [_, name = "_", domain] = match;
try {
const res = await _fetch(`https://${domain}/.well-known/nostr.json?name=${name}`);
const { names, relays } = parseNIP05Result(await res.json());
const pubkey = names[name];
return pubkey ? { pubkey, relays: relays?.[pubkey] } : null;
} catch (_e) {
return null;
}
}
function parseNIP05Result(json) {
const result = {
names: {}
};
for (const [name, pubkey] of Object.entries(json.names)) {
if (typeof name === "string" && typeof pubkey === "string") {
result.names[name] = pubkey;
}
}
if (json.relays) {
result.relays = {};
for (const [pubkey, relays] of Object.entries(json.relays)) {
if (typeof pubkey === "string" && Array.isArray(relays)) {
result.relays[pubkey] = relays.filter((relay) => typeof relay === "string");
}
}
}
return result;
}
// nip06.ts
var nip06_exports = {};
__export(nip06_exports, {
generateSeedWords: () => generateSeedWords,
privateKeyFromSeedWords: () => privateKeyFromSeedWords,
validateWords: () => validateWords
});
import { bytesToHex as bytesToHex4 } from "@noble/hashes/utils";
import { wordlist } from "@scure/bip39/wordlists/english";
import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from "@scure/bip39";
import { HDKey } from "@scure/bip32";
function privateKeyFromSeedWords(mnemonic, passphrase) {
let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase));
let privateKey = root.derive(`m/44'/1237'/0'/0/0`).privateKey;
if (!privateKey)
throw new Error("could not derive private key");
return bytesToHex4(privateKey);
}
function generateSeedWords() {
return generateMnemonic(wordlist);
}
function validateWords(words) {
return validateMnemonic(words, wordlist);
}
// nip10.ts
var nip10_exports = {};
__export(nip10_exports, {
parse: () => parse
});
function parse(event) {
const result = {
reply: void 0,
root: void 0,
mentions: [],
profiles: []
};
const eTags = [];
for (const tag of event.tags) {
if (tag[0] === "e" && tag[1]) {
eTags.push(tag);
}
if (tag[0] === "p" && tag[1]) {
result.profiles.push({
pubkey: tag[1],
relays: tag[2] ? [tag[2]] : []
});
}
}
for (let eTagIndex = 0; eTagIndex < eTags.length; eTagIndex++) {
const eTag = eTags[eTagIndex];
const [_, eTagEventId, eTagRelayUrl, eTagMarker] = eTag;
const eventPointer = {
id: eTagEventId,
relays: eTagRelayUrl ? [eTagRelayUrl] : []
};
const isFirstETag = eTagIndex === 0;
const isLastETag = eTagIndex === eTags.length - 1;
if (eTagMarker === "root") {
result.root = eventPointer;
continue;
}
if (eTagMarker === "reply") {
result.reply = eventPointer;
continue;
}
if (eTagMarker === "mention") {
result.mentions.push(eventPointer);
continue;
}
if (isFirstETag) {
result.root = eventPointer;
continue;
}
if (isLastETag) {
result.reply = eventPointer;
continue;
}
result.mentions.push(eventPointer);
}
return result;
}
// nip13.ts
var nip13_exports = {};
__export(nip13_exports, {
getPow: () => getPow,
minePow: () => minePow
});
function getPow(hex) {
let count = 0;
for (let i = 0; i < hex.length; i++) {
const nibble = parseInt(hex[i], 16);
if (nibble === 0) {
count += 4;
} else {
count += Math.clz32(nibble) - 28;
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 = getEventHash(event);
if (getPow(event.id) >= difficulty) {
break;
}
}
return event;
}
// nip18.ts
var nip18_exports = {};
__export(nip18_exports, {
finishRepostEvent: () => finishRepostEvent,
getRepostedEvent: () => getRepostedEvent,
getRepostedEventPointer: () => getRepostedEventPointer
});
function finishRepostEvent(t, reposted, relayUrl, privateKey) {
return finishEvent(
{
kind: 6 /* Repost */,
tags: [...t.tags ?? [], ["e", reposted.id, relayUrl], ["p", reposted.pubkey]],
content: t.content === "" ? "" : JSON.stringify(reposted),
created_at: t.created_at
},
privateKey
);
}
function getRepostedEventPointer(event) {
if (event.kind !== 6 /* Repost */) {
return void 0;
}
let lastETag;
let lastPTag;
for (let i = event.tags.length - 1; i >= 0 && (lastETag === void 0 || lastPTag === void 0); i--) {
const tag = event.tags[i];
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 && !verifySignature(repostedEvent)) {
return void 0;
}
return repostedEvent;
}
// nip21.ts
var nip21_exports = {};
__export(nip21_exports, {
NOSTR_URI_REGEX: () => NOSTR_URI_REGEX,
parse: () => parse2,
test: () => test
});
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 parse2(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])
};
}
// nip25.ts
var nip25_exports = {};
__export(nip25_exports, {
finishReactionEvent: () => finishReactionEvent,
getReactedEventPointer: () => getReactedEventPointer
});
function finishReactionEvent(t, reacted, privateKey) {
const inheritedTags = reacted.tags.filter((tag) => tag.length >= 2 && (tag[0] === "e" || tag[0] === "p"));
return finishEvent(
{
...t,
kind: 7 /* Reaction */,
tags: [...t.tags ?? [], ...inheritedTags, ["e", reacted.id], ["p", reacted.pubkey]],
content: t.content ?? "+"
},
privateKey
);
}
function getReactedEventPointer(event) {
if (event.kind !== 7 /* Reaction */) {
return void 0;
}
let lastETag;
let lastPTag;
for (let i = event.tags.length - 1; i >= 0 && (lastETag === void 0 || lastPTag === void 0); i--) {
const tag = event.tags[i];
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]
};
}
// nip26.ts
var nip26_exports = {};
__export(nip26_exports, {
createDelegation: () => createDelegation,
getDelegator: () => getDelegator
});
import { schnorr as schnorr3 } from "@noble/curves/secp256k1";
import { bytesToHex as bytesToHex5 } from "@noble/hashes/utils";
import { sha256 as sha2562 } from "@noble/hashes/sha256";
function createDelegation(privateKey, parameters) {
let conditions = [];
if ((parameters.kind || -1) >= 0)
conditions.push(`kind=${parameters.kind}`);
if (parameters.until)
conditions.push(`created_at<${parameters.until}`);
if (parameters.since)
conditions.push(`created_at>${parameters.since}`);
let cond = conditions.join("&");
if (cond === "")
throw new Error("refusing to create a delegation without any conditions");
let sighash = sha2562(utf8Encoder.encode(`nostr:delegation:${parameters.pubkey}:${cond}`));
let sig = bytesToHex5(schnorr3.sign(sighash, privateKey));
return {
from: getPublicKey(privateKey),
to: parameters.pubkey,
cond,
sig
};
}
function getDelegator(event) {
let tag = event.tags.find((tag2) => tag2[0] === "delegation" && tag2.length >= 4);
if (!tag)
return null;
let pubkey = tag[1];
let cond = tag[2];
let sig = tag[3];
let conditions = cond.split("&");
for (let i = 0; i < conditions.length; i++) {
let [key, operator, value] = conditions[i].split(/\b/);
if (key === "kind" && operator === "=" && event.kind === parseInt(value))
continue;
else if (key === "created_at" && operator === "<" && event.created_at < parseInt(value))
continue;
else if (key === "created_at" && operator === ">" && event.created_at > parseInt(value))
continue;
else
return null;
}
let sighash = sha2562(utf8Encoder.encode(`nostr:delegation:${event.pubkey}:${cond}`));
if (!schnorr3.verify(sig, sighash, pubkey))
return null;
return pubkey;
}
// nip27.ts
var nip27_exports = {};
__export(nip27_exports, {
matchAll: () => matchAll,
regex: () => regex,
replaceAll: () => replaceAll
});
var regex = () => new RegExp(`\\b${NOSTR_URI_REGEX.source}\\b`, "g");
function* matchAll(content) {
const matches = content.matchAll(regex());
for (const match of matches) {
try {
const [uri, value] = match;
yield {
uri,
value,
decoded: decode(value),
start: match.index,
end: match.index + uri.length
};
} catch (_e) {
}
}
}
function replaceAll(content, replacer) {
return content.replaceAll(regex(), (uri, value) => {
return replacer({
uri,
value,
decoded: decode(value)
});
});
}
// nip28.ts
var nip28_exports = {};
__export(nip28_exports, {
channelCreateEvent: () => channelCreateEvent,
channelHideMessageEvent: () => channelHideMessageEvent,
channelMessageEvent: () => channelMessageEvent,
channelMetadataEvent: () => channelMetadataEvent,
channelMuteUserEvent: () => channelMuteUserEvent
});
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 finishEvent(
{
kind: 40 /* 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 finishEvent(
{
kind: 41 /* 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 finishEvent(
{
kind: 42 /* 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 finishEvent(
{
kind: 43 /* 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 finishEvent(
{
kind: 44 /* ChannelMuteUser */,
tags: [["p", t.pubkey_to_mute], ...t.tags ?? []],
content,
created_at: t.created_at
},
privateKey
);
};
// nip39.ts
var nip39_exports = {};
__export(nip39_exports, {
useFetchImplementation: () => useFetchImplementation2,
validateGithub: () => validateGithub
});
var _fetch2;
try {
_fetch2 = fetch;
} catch {
}
function useFetchImplementation2(fetchImplementation) {
_fetch2 = fetchImplementation;
}
async function validateGithub(pubkey, username, proof) {
try {
let res = await (await _fetch2(`https://gist.github.com/${username}/${proof}/raw`)).text();
return res === `Verifying that I control the following Nostr public key: ${pubkey}`;
} catch (_) {
return false;
}
}
// nip42.ts
var nip42_exports = {};
__export(nip42_exports, {
authenticate: () => authenticate
});
var authenticate = async ({
challenge,
relay,
sign
}) => {
const e = {
kind: 22242 /* ClientAuth */,
created_at: Math.floor(Date.now() / 1e3),
tags: [
["relay", relay.url],
["challenge", challenge]
],
content: ""
};
return relay.auth(await sign(e));
};
// nip44.ts
var nip44_exports = {};
__export(nip44_exports, {
decrypt: () => decrypt2,
encrypt: () => encrypt2,
utils: () => utils
});
import { chacha20 } from "@noble/ciphers/chacha";
import { ensureBytes, equalBytes } from "@noble/ciphers/utils";
import { secp256k1 as secp256k12 } from "@noble/curves/secp256k1";
import { hkdf } from "@noble/hashes/hkdf";
import { hmac } from "@noble/hashes/hmac";
import { sha256 as sha2563 } from "@noble/hashes/sha256";
import { concatBytes as concatBytes2, randomBytes as randomBytes2 } from "@noble/hashes/utils";
import { base64 as base642 } from "@scure/base";
var utils = {
v2: {
maxPlaintextSize: 65536 - 128,
minCiphertextSize: 100,
maxCiphertextSize: 102400,
getConversationKey(privkeyA, pubkeyB) {
const key = secp256k12.getSharedSecret(privkeyA, "02" + pubkeyB);
return key.subarray(1, 33);
},
getMessageKeys(conversationKey, salt) {
const keys = hkdf(sha2563, conversationKey, salt, "nip44-v2", 76);
return {
encryption: keys.subarray(0, 32),
nonce: keys.subarray(32, 44),
auth: keys.subarray(44, 76)
};
},
calcPadding(len) {
if (!Number.isSafeInteger(len) || len < 0)
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);
},
pad(unpadded) {
const unpaddedB = utf8Encoder.encode(unpadded);
const len = unpaddedB.length;
if (len < 1 || len >= utils.v2.maxPlaintextSize)
throw new Error("invalid plaintext length: must be between 1b and 64KB");
const paddedLen = utils.v2.calcPadding(len);
const zeros = new Uint8Array(paddedLen - len);
const lenBuf = new Uint8Array(2);
new DataView(lenBuf.buffer).setUint16(0, len);
return concatBytes2(lenBuf, unpaddedB, zeros);
},
unpad(padded) {
const unpaddedLen = new DataView(padded.buffer).getUint16(0);
const unpadded = padded.subarray(2, 2 + unpaddedLen);
if (unpaddedLen === 0 || unpadded.length !== unpaddedLen || padded.length !== 2 + utils.v2.calcPadding(unpaddedLen))
throw new Error("invalid padding");
return utf8Decoder.decode(unpadded);
}
}
};
function encrypt2(key, plaintext, options = {}) {
const version = options.version ?? 2;
if (version !== 2)
throw new Error("unknown encryption version " + version);
const salt = options.salt ?? randomBytes2(32);
ensureBytes(salt, 32);
const keys = utils.v2.getMessageKeys(key, salt);
const padded = utils.v2.pad(plaintext);
const ciphertext = chacha20(keys.encryption, keys.nonce, padded);
const mac = hmac(sha2563, keys.auth, ciphertext);
return base642.encode(concatBytes2(new Uint8Array([version]), salt, ciphertext, mac));
}
function decrypt2(key, ciphertext) {
const u = utils.v2;
ensureBytes(key, 32);
const clen = ciphertext.length;
if (clen < u.minCiphertextSize || clen >= u.maxCiphertextSize)
throw new Error("invalid ciphertext length: " + clen);
if (ciphertext[0] === "#")
throw new Error("unknown encryption version");
let data;
try {
data = base642.decode(ciphertext);
} catch (error) {
throw new Error("invalid base64: " + error.message);
}
const vers = data.subarray(0, 1)[0];
if (vers !== 2)
throw new Error("unknown encryption version " + vers);
const salt = data.subarray(1, 33);
const ciphertext_ = data.subarray(33, -32);
const mac = data.subarray(-32);
const keys = u.getMessageKeys(key, salt);
const calculatedMac = hmac(sha2563, keys.auth, ciphertext_);
if (!equalBytes(calculatedMac, mac))
throw new Error("invalid MAC");
const padded = chacha20(keys.encryption, keys.nonce, ciphertext_);
return u.unpad(padded);
}
// nip47.ts
var nip47_exports = {};
__export(nip47_exports, {
makeNwcRequestEvent: () => makeNwcRequestEvent,
parseConnectionString: () => parseConnectionString
});
function parseConnectionString(connectionString) {
const { pathname, searchParams } = new URL(connectionString);
const pubkey = pathname;
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,
secret,
invoice
}) {
const content = {
method: "pay_invoice",
params: {
invoice
}
};
const encryptedContent = await encrypt(secret, pubkey, JSON.stringify(content));
const eventTemplate = {
kind: 23194 /* NwcRequest */,
created_at: Math.round(Date.now() / 1e3),
content: encryptedContent,
tags: [["p", pubkey]]
};
return finishEvent(eventTemplate, secret);
}
// nip57.ts
var nip57_exports = {};
__export(nip57_exports, {
getZapEndpoint: () => getZapEndpoint,
makeZapReceipt: () => makeZapReceipt,
makeZapRequest: () => makeZapRequest,
useFetchImplementation: () => useFetchImplementation3,
validateZapRequest: () => validateZapRequest
});
import { bech32 as bech322 } from "@scure/base";
var _fetch3;
try {
_fetch3 = fetch;
} catch {
}
function useFetchImplementation3(fetchImplementation) {
_fetch3 = fetchImplementation;
}
async function getZapEndpoint(metadata) {
try {
let lnurl = "";
let { lud06, lud16 } = JSON.parse(metadata.content);
if (lud06) {
let { words } = bech322.decode(lud06, 1e3);
let data = bech322.fromWords(words);
lnurl = utf8Decoder.decode(data);
} else if (lud16) {
let [name, domain] = lud16.split("@");
lnurl = `https://${domain}/.well-known/lnurlp/${name}`;
} else {
return null;
}
let res = await _fetch3(lnurl);
let body = await res.json();
if (body.allowsNostr && body.nostrPubkey) {
return body.callback;
}
} catch (err) {
}
return null;
}
function makeZapRequest({
profile,
event,
amount,
relays,
comment = ""
}) {
if (!amount)
throw new Error("amount not given");
if (!profile)
throw new Error("profile not given");
let zr = {
kind: 9734,
created_at: Math.round(Date.now() / 1e3),
content: comment,
tags: [
["p", profile],
["amount", amount.toString()],
["relays", ...relays]
]
};
if (event) {
zr.tags.push(["e", event]);
}
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 (!verifySignature(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, ["bolt11", bolt11], ["description", zapRequest]]
};
if (preimage) {
zap.tags.push(["preimage", preimage]);
}
return zap;
}
// nip98.ts
var nip98_exports = {};
__export(nip98_exports, {
getToken: () => getToken,
unpackEventFromToken: () => unpackEventFromToken,
validateEvent: () => validateEvent2,
validateToken: () => validateToken
});
import { base64 as base643 } from "@scure/base";
var _authorizationScheme = "Nostr ";
async function getToken(loginUrl, httpMethod, sign, includeAuthorizationScheme = false) {
if (!loginUrl || !httpMethod)
throw new Error("Missing loginUrl or httpMethod");
const event = getBlankEvent(27235 /* HttpAuth */);
event.tags = [
["u", loginUrl],
["method", httpMethod]
];
event.created_at = Math.round(new Date().getTime() / 1e3);
const signedEvent = await sign(event);
const authorizationScheme = includeAuthorizationScheme ? _authorizationScheme : "";
return authorizationScheme + base643.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(base643.decode(token));
if (!eventB64 || eventB64.length === 0 || !eventB64.startsWith("{")) {
throw new Error("Invalid token");
}
const event = JSON.parse(eventB64);
return event;
}
async function validateEvent2(event, url, method) {
if (!event) {
throw new Error("Invalid nostr event");
}
if (!verifySignature(event)) {
throw new Error("Invalid nostr event, signature invalid");
}
if (event.kind !== 27235 /* HttpAuth */) {
throw new Error("Invalid nostr event, kind invalid");
}
if (!event.created_at) {
throw new Error("Invalid nostr event, created_at invalid");
}
if (Math.round(new Date().getTime() / 1e3) - event.created_at > 60) {
throw new Error("Invalid nostr event, expired");
}
const urlTag = event.tags.find((t) => t[0] === "u");
if (urlTag?.length !== 1 && urlTag?.[1] !== url) {
throw new Error("Invalid nostr event, url tag invalid");
}
const methodTag = event.tags.find((t) => t[0] === "method");
if (methodTag?.length !== 1 && methodTag?.[1].toLowerCase() !== method.toLowerCase()) {
throw new Error("Invalid nostr event, method tag invalid");
}
return true;
}
export {
Kind,
SimplePool,
eventsGenerator,
finishEvent,
fakejson_exports as fj,
generatePrivateKey,
getBlankEvent,
getEventHash,
getPublicKey,
getSignature,
matchFilter,
matchFilters,
mergeFilters,
nip04_exports as nip04,
nip05_exports as nip05,
nip06_exports as nip06,
nip10_exports as nip10,
nip13_exports as nip13,
nip18_exports as nip18,
nip19_exports as nip19,
nip21_exports as nip21,
nip25_exports as nip25,
nip26_exports as nip26,
nip27_exports as nip27,
nip28_exports as nip28,
nip39_exports as nip39,
nip42_exports as nip42,
nip44_exports as nip44,
nip47_exports as nip47,
nip57_exports as nip57,
nip98_exports as nip98,
parseReferences,
relayInit,
serializeEvent,
signEvent,
utils_exports as utils,
validateEvent,
verifiedSymbol,
verifySignature
};