include node_modules so release .zip is deployable
This commit is contained in:
+154
@@ -0,0 +1,154 @@
|
||||
"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);
|
||||
|
||||
// event.ts
|
||||
var event_exports = {};
|
||||
__export(event_exports, {
|
||||
Kind: () => Kind,
|
||||
finishEvent: () => finishEvent,
|
||||
getBlankEvent: () => getBlankEvent,
|
||||
getEventHash: () => getEventHash,
|
||||
getSignature: () => getSignature,
|
||||
serializeEvent: () => serializeEvent,
|
||||
signEvent: () => signEvent,
|
||||
validateEvent: () => validateEvent,
|
||||
verifiedSymbol: () => verifiedSymbol,
|
||||
verifySignature: () => verifySignature
|
||||
});
|
||||
module.exports = __toCommonJS(event_exports);
|
||||
var import_secp256k12 = require("@noble/curves/secp256k1");
|
||||
var import_sha256 = require("@noble/hashes/sha256");
|
||||
var import_utils2 = require("@noble/hashes/utils");
|
||||
|
||||
// keys.ts
|
||||
var import_secp256k1 = require("@noble/curves/secp256k1");
|
||||
var import_utils = require("@noble/hashes/utils");
|
||||
function getPublicKey(privateKey) {
|
||||
return (0, import_utils.bytesToHex)(import_secp256k1.schnorr.getPublicKey(privateKey));
|
||||
}
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// event.ts
|
||||
var verifiedSymbol = Symbol("verified");
|
||||
var Kind = /* @__PURE__ */ ((Kind2) => {
|
||||
Kind2[Kind2["Metadata"] = 0] = "Metadata";
|
||||
Kind2[Kind2["Text"] = 1] = "Text";
|
||||
Kind2[Kind2["RecommendRelay"] = 2] = "RecommendRelay";
|
||||
Kind2[Kind2["Contacts"] = 3] = "Contacts";
|
||||
Kind2[Kind2["EncryptedDirectMessage"] = 4] = "EncryptedDirectMessage";
|
||||
Kind2[Kind2["EventDeletion"] = 5] = "EventDeletion";
|
||||
Kind2[Kind2["Repost"] = 6] = "Repost";
|
||||
Kind2[Kind2["Reaction"] = 7] = "Reaction";
|
||||
Kind2[Kind2["BadgeAward"] = 8] = "BadgeAward";
|
||||
Kind2[Kind2["ChannelCreation"] = 40] = "ChannelCreation";
|
||||
Kind2[Kind2["ChannelMetadata"] = 41] = "ChannelMetadata";
|
||||
Kind2[Kind2["ChannelMessage"] = 42] = "ChannelMessage";
|
||||
Kind2[Kind2["ChannelHideMessage"] = 43] = "ChannelHideMessage";
|
||||
Kind2[Kind2["ChannelMuteUser"] = 44] = "ChannelMuteUser";
|
||||
Kind2[Kind2["Blank"] = 255] = "Blank";
|
||||
Kind2[Kind2["Report"] = 1984] = "Report";
|
||||
Kind2[Kind2["ZapRequest"] = 9734] = "ZapRequest";
|
||||
Kind2[Kind2["Zap"] = 9735] = "Zap";
|
||||
Kind2[Kind2["RelayList"] = 10002] = "RelayList";
|
||||
Kind2[Kind2["ClientAuth"] = 22242] = "ClientAuth";
|
||||
Kind2[Kind2["NwcRequest"] = 23194] = "NwcRequest";
|
||||
Kind2[Kind2["HttpAuth"] = 27235] = "HttpAuth";
|
||||
Kind2[Kind2["ProfileBadge"] = 30008] = "ProfileBadge";
|
||||
Kind2[Kind2["BadgeDefinition"] = 30009] = "BadgeDefinition";
|
||||
Kind2[Kind2["Article"] = 30023] = "Article";
|
||||
Kind2[Kind2["FileMetadata"] = 1063] = "FileMetadata";
|
||||
return Kind2;
|
||||
})(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 = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
|
||||
return (0, import_utils2.bytesToHex)(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] = import_secp256k12.schnorr.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 (0, import_utils2.bytesToHex)(import_secp256k12.schnorr.sign(getEventHash(event), key));
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+66
@@ -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");
|
||||
}
|
||||
+7
@@ -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": []
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
"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, {
|
||||
matchFilter: () => matchFilter,
|
||||
matchFilters: () => matchFilters,
|
||||
mergeFilters: () => mergeFilters
|
||||
});
|
||||
module.exports = __toCommonJS(filter_exports);
|
||||
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;
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../filter.ts"],
|
||||
"sourcesContent": ["import { Event } from './event.ts'\n\nexport type Filter<K extends number = number> = {\n ids?: string[]\n kinds?: K[]\n authors?: string[]\n since?: number\n until?: number\n limit?: number\n search?: string\n [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter<number>, event: Event<number>): boolean {\n if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n if (!filter.ids.some(prefix => event.id.startsWith(prefix))) {\n return false\n }\n }\n if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) return false\n if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n if (!filter.authors.some(prefix => event.pubkey.startsWith(prefix))) {\n return false\n }\n }\n\n for (let f in filter) {\n if (f[0] === '#') {\n let tagName = f.slice(1)\n let values = filter[`#${tagName}`]\n if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n }\n }\n\n if (filter.since && event.created_at < filter.since) return false\n if (filter.until && event.created_at > filter.until) return false\n\n return true\n}\n\nexport function matchFilters(filters: Filter<number>[], event: Event<number>): boolean {\n for (let i = 0; i < filters.length; i++) {\n if (matchFilter(filters[i], event)) return true\n }\n return false\n}\n\nexport function mergeFilters(...filters: Filter<number>[]): Filter<number> {\n let result: Filter<number> = {}\n for (let i = 0; i < filters.length; i++) {\n let filter = filters[i]\n Object.entries(filter).forEach(([property, values]) => {\n if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n // @ts-ignore\n result[property] = result[property] || []\n // @ts-ignore\n for (let v = 0; v < values.length; v++) {\n // @ts-ignore\n let value = values[v]\n // @ts-ignore\n if (!result[property].includes(value)) result[property].push(value)\n }\n }\n })\n\n if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n }\n\n return result\n}\n"],
|
||||
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaO,SAAS,YAAY,QAAwB,OAA+B;AACjF,MAAI,OAAO,OAAO,OAAO,IAAI,QAAQ,MAAM,EAAE,MAAM,IAAI;AACrD,QAAI,CAAC,OAAO,IAAI,KAAK,YAAU,MAAM,GAAG,WAAW,MAAM,CAAC,GAAG;AAC3D,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,OAAO,SAAS,OAAO,MAAM,QAAQ,MAAM,IAAI,MAAM;AAAI,WAAO;AACpE,MAAI,OAAO,WAAW,OAAO,QAAQ,QAAQ,MAAM,MAAM,MAAM,IAAI;AACjE,QAAI,CAAC,OAAO,QAAQ,KAAK,YAAU,MAAM,OAAO,WAAW,MAAM,CAAC,GAAG;AACnE,aAAO;AAAA,IACT;AAAA,EACF;AAEA,WAAS,KAAK,QAAQ;AACpB,QAAI,EAAE,OAAO,KAAK;AAChB,UAAI,UAAU,EAAE,MAAM,CAAC;AACvB,UAAI,SAAS,OAAO,IAAI;AACxB,UAAI,UAAU,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,MAAM,EAAE,MAAM,CAAC,KAAK,OAAQ,QAAQ,CAAC,MAAM,EAAE;AAAG,eAAO;AAAA,IACpG;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,MAAM,aAAa,OAAO;AAAO,WAAO;AAC5D,MAAI,OAAO,SAAS,MAAM,aAAa,OAAO;AAAO,WAAO;AAE5D,SAAO;AACT;AAEO,SAAS,aAAa,SAA2B,OAA+B;AACrF,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,QAAI,YAAY,QAAQ,IAAI,KAAK;AAAG,aAAO;AAAA,EAC7C;AACA,SAAO;AACT;AAEO,SAAS,gBAAgB,SAA2C;AACzE,MAAI,SAAyB,CAAC;AAC9B,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,QAAI,SAAS,QAAQ;AACrB,WAAO,QAAQ,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU,MAAM,MAAM;AACrD,UAAI,aAAa,WAAW,aAAa,SAAS,aAAa,aAAa,SAAS,OAAO,KAAK;AAE/F,eAAO,YAAY,OAAO,aAAa,CAAC;AAExC,iBAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AAEtC,cAAI,QAAQ,OAAO;AAEnB,cAAI,CAAC,OAAO,UAAU,SAAS,KAAK;AAAG,mBAAO,UAAU,KAAK,KAAK;AAAA,QACpE;AAAA,MACF;AAAA,IACF,CAAC;AAED,QAAI,OAAO,UAAU,CAAC,OAAO,SAAS,OAAO,QAAQ,OAAO;AAAQ,aAAO,QAAQ,OAAO;AAC1F,QAAI,OAAO,UAAU,CAAC,OAAO,SAAS,OAAO,QAAQ,OAAO;AAAQ,aAAO,QAAQ,OAAO;AAC1F,QAAI,OAAO,UAAU,CAAC,OAAO,SAAS,OAAO,QAAQ,OAAO;AAAQ,aAAO,QAAQ,OAAO;AAAA,EAC5F;AAEA,SAAO;AACT;",
|
||||
"names": []
|
||||
}
|
||||
+2126
File diff suppressed because it is too large
Load Diff
+7
File diff suppressed because one or more lines are too long
+34
@@ -0,0 +1,34 @@
|
||||
"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);
|
||||
|
||||
// keys.ts
|
||||
var keys_exports = {};
|
||||
__export(keys_exports, {
|
||||
generatePrivateKey: () => generatePrivateKey,
|
||||
getPublicKey: () => getPublicKey
|
||||
});
|
||||
module.exports = __toCommonJS(keys_exports);
|
||||
var import_secp256k1 = require("@noble/curves/secp256k1");
|
||||
var import_utils = require("@noble/hashes/utils");
|
||||
function generatePrivateKey() {
|
||||
return (0, import_utils.bytesToHex)(import_secp256k1.schnorr.utils.randomPrivateKey());
|
||||
}
|
||||
function getPublicKey(privateKey) {
|
||||
return (0, import_utils.bytesToHex)(import_secp256k1.schnorr.getPublicKey(privateKey));
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../keys.ts"],
|
||||
"sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\n\nexport function generatePrivateKey(): string {\n return bytesToHex(schnorr.utils.randomPrivateKey())\n}\n\nexport function getPublicKey(privateKey: string): string {\n return bytesToHex(schnorr.getPublicKey(privateKey))\n}\n"],
|
||||
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAwB;AACxB,mBAA2B;AAEpB,SAAS,qBAA6B;AAC3C,aAAO,yBAAW,yBAAQ,MAAM,iBAAiB,CAAC;AACpD;AAEO,SAAS,aAAa,YAA4B;AACvD,aAAO,yBAAW,yBAAQ,aAAa,UAAU,CAAC;AACpD;",
|
||||
"names": []
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
"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, {
|
||||
classifyKind: () => classifyKind,
|
||||
isEphemeralKind: () => isEphemeralKind,
|
||||
isParameterizedReplaceableKind: () => isParameterizedReplaceableKind,
|
||||
isRegularKind: () => isRegularKind,
|
||||
isReplaceableKind: () => isReplaceableKind
|
||||
});
|
||||
module.exports = __toCommonJS(kinds_exports);
|
||||
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 1e4 <= kind && kind < 2e4 || [0, 3].includes(kind);
|
||||
}
|
||||
function isEphemeralKind(kind) {
|
||||
return 2e4 <= kind && kind < 3e4;
|
||||
}
|
||||
function isParameterizedReplaceableKind(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 (isParameterizedReplaceableKind(kind))
|
||||
return "parameterized";
|
||||
return "unknown";
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../kinds.ts"],
|
||||
"sourcesContent": ["/** Events are **regular**, which means they're all expected to be stored by relays. */\nfunction isRegularKind(kind: number) {\n return (1000 <= kind && kind < 10000) || [1, 2, 4, 5, 6, 7, 8, 16, 40, 41, 42, 43, 44].includes(kind)\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nfunction isReplaceableKind(kind: number) {\n return (10000 <= kind && kind < 20000) || [0, 3].includes(kind)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nfunction isEphemeralKind(kind: number) {\n return 20000 <= kind && kind < 30000\n}\n\n/** Events are **parameterized replaceable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nfunction isParameterizedReplaceableKind(kind: number) {\n return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\ntype KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nfunction classifyKind(kind: number): KindClassification {\n if (isRegularKind(kind)) return 'regular'\n if (isReplaceableKind(kind)) return 'replaceable'\n if (isEphemeralKind(kind)) return 'ephemeral'\n if (isParameterizedReplaceableKind(kind)) return 'parameterized'\n return 'unknown'\n}\n\nexport {\n classifyKind,\n isEphemeralKind,\n isParameterizedReplaceableKind,\n isRegularKind,\n isReplaceableKind,\n type KindClassification,\n}\n"],
|
||||
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,SAAS,cAAc,MAAc;AACnC,SAAQ,OAAQ,QAAQ,OAAO,OAAU,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,EAAE,SAAS,IAAI;AACtG;AAGA,SAAS,kBAAkB,MAAc;AACvC,SAAQ,OAAS,QAAQ,OAAO,OAAU,CAAC,GAAG,CAAC,EAAE,SAAS,IAAI;AAChE;AAGA,SAAS,gBAAgB,MAAc;AACrC,SAAO,OAAS,QAAQ,OAAO;AACjC;AAGA,SAAS,+BAA+B,MAAc;AACpD,SAAO,OAAS,QAAQ,OAAO;AACjC;AAMA,SAAS,aAAa,MAAkC;AACtD,MAAI,cAAc,IAAI;AAAG,WAAO;AAChC,MAAI,kBAAkB,IAAI;AAAG,WAAO;AACpC,MAAI,gBAAgB,IAAI;AAAG,WAAO;AAClC,MAAI,+BAA+B,IAAI;AAAG,WAAO;AACjD,SAAO;AACT;",
|
||||
"names": []
|
||||
}
|
||||
+63
@@ -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);
|
||||
|
||||
// nip04.ts
|
||||
var nip04_exports = {};
|
||||
__export(nip04_exports, {
|
||||
decrypt: () => decrypt,
|
||||
encrypt: () => encrypt
|
||||
});
|
||||
module.exports = __toCommonJS(nip04_exports);
|
||||
var import_utils = require("@noble/hashes/utils");
|
||||
var import_secp256k1 = require("@noble/curves/secp256k1");
|
||||
var import_base = require("@scure/base");
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// nip04.ts
|
||||
if (typeof crypto !== "undefined" && !crypto.subtle && crypto.webcrypto) {
|
||||
crypto.subtle = crypto.webcrypto.subtle;
|
||||
}
|
||||
async function encrypt(privkey, pubkey, text) {
|
||||
const key = import_secp256k1.secp256k1.getSharedSecret(privkey, "02" + pubkey);
|
||||
const normalizedKey = getNormalizedX(key);
|
||||
let iv = Uint8Array.from((0, import_utils.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 = import_base.base64.encode(new Uint8Array(ciphertext));
|
||||
let ivb64 = import_base.base64.encode(new Uint8Array(iv.buffer));
|
||||
return `${ctb64}?iv=${ivb64}`;
|
||||
}
|
||||
async function decrypt(privkey, pubkey, data) {
|
||||
let [ctb64, ivb64] = data.split("?iv=");
|
||||
let key = import_secp256k1.secp256k1.getSharedSecret(privkey, "02" + pubkey);
|
||||
let normalizedKey = getNormalizedX(key);
|
||||
let cryptoKey = await crypto.subtle.importKey("raw", normalizedKey, { name: "AES-CBC" }, false, ["decrypt"]);
|
||||
let ciphertext = import_base.base64.decode(ctb64);
|
||||
let iv = import_base.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);
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+78
@@ -0,0 +1,78 @@
|
||||
"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,
|
||||
queryProfile: () => queryProfile,
|
||||
searchDomain: () => searchDomain,
|
||||
useFetchImplementation: () => useFetchImplementation
|
||||
});
|
||||
module.exports = __toCommonJS(nip05_exports);
|
||||
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;
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../nip05.ts"],
|
||||
"sourcesContent": ["import { ProfilePointer } from './nip19.ts'\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.-]+)$/\n\nvar _fetch: any\n\ntry {\n _fetch = fetch\n} catch {}\n\nexport function useFetchImplementation(fetchImplementation: any) {\n _fetch = fetchImplementation\n}\n\nexport async function searchDomain(domain: string, query = ''): Promise<{ [name: string]: string }> {\n try {\n let res = await (await _fetch(`https://${domain}/.well-known/nostr.json?name=${query}`)).json()\n\n return res.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 res = await _fetch(`https://${domain}/.well-known/nostr.json?name=${name}`)\n const { names, relays } = parseNIP05Result(await res.json())\n\n const pubkey = names[name]\n return pubkey ? { pubkey, relays: relays?.[pubkey] } : null\n } catch (_e) {\n return null\n }\n}\n\n/** nostr.json result. */\nexport interface NIP05Result {\n names: {\n [name: string]: string\n }\n relays?: {\n [pubkey: string]: string[]\n }\n}\n\n/** Parse the nostr.json and throw if it's not valid. */\nfunction parseNIP05Result(json: any): NIP05Result {\n const result: NIP05Result = {\n names: {},\n }\n\n for (const [name, pubkey] of Object.entries(json.names)) {\n if (typeof name === 'string' && typeof pubkey === 'string') {\n result.names[name] = pubkey\n }\n }\n\n if (json.relays) {\n result.relays = {}\n for (const [pubkey, relays] of Object.entries(json.relays)) {\n if (typeof pubkey === 'string' && Array.isArray(relays)) {\n result.relays[pubkey] = relays.filter((relay: unknown) => typeof relay === 'string')\n }\n }\n }\n\n return result\n}\n"],
|
||||
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASO,IAAM,cAAc;AAE3B,IAAI;AAEJ,IAAI;AACF,WAAS;AACX,QAAE;AAAO;AAEF,SAAS,uBAAuB,qBAA0B;AAC/D,WAAS;AACX;AAEA,eAAsB,aAAa,QAAgB,QAAQ,IAAyC;AAClG,MAAI;AACF,QAAI,MAAM,OAAO,MAAM,OAAO,WAAW,sCAAsC,OAAO,GAAG,KAAK;AAE9F,WAAO,IAAI;AAAA,EACb,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,GAAG,OAAO,KAAK,MAAM,IAAI;AAEhC,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,WAAW,sCAAsC,MAAM;AAChF,UAAM,EAAE,OAAO,OAAO,IAAI,iBAAiB,MAAM,IAAI,KAAK,CAAC;AAE3D,UAAM,SAAS,MAAM;AACrB,WAAO,SAAS,EAAE,QAAQ,QAAQ,SAAS,QAAQ,IAAI;AAAA,EACzD,SAAS,IAAP;AACA,WAAO;AAAA,EACT;AACF;AAaA,SAAS,iBAAiB,MAAwB;AAChD,QAAM,SAAsB;AAAA,IAC1B,OAAO,CAAC;AAAA,EACV;AAEA,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,KAAK,KAAK,GAAG;AACvD,QAAI,OAAO,SAAS,YAAY,OAAO,WAAW,UAAU;AAC1D,aAAO,MAAM,QAAQ;AAAA,IACvB;AAAA,EACF;AAEA,MAAI,KAAK,QAAQ;AACf,WAAO,SAAS,CAAC;AACjB,eAAW,CAAC,QAAQ,MAAM,KAAK,OAAO,QAAQ,KAAK,MAAM,GAAG;AAC1D,UAAI,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AACvD,eAAO,OAAO,UAAU,OAAO,OAAO,CAAC,UAAmB,OAAO,UAAU,QAAQ;AAAA,MACrF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;",
|
||||
"names": []
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
"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, {
|
||||
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");
|
||||
function privateKeyFromSeedWords(mnemonic, passphrase) {
|
||||
let root = import_bip32.HDKey.fromMasterSeed((0, import_bip39.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 (0, import_utils.bytesToHex)(privateKey);
|
||||
}
|
||||
function generateSeedWords() {
|
||||
return (0, import_bip39.generateMnemonic)(import_english.wordlist);
|
||||
}
|
||||
function validateWords(words) {
|
||||
return (0, import_bip39.validateMnemonic)(words, import_english.wordlist);
|
||||
}
|
||||
+7
@@ -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\nexport function privateKeyFromSeedWords(mnemonic: string, passphrase?: string): string {\n let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n let privateKey = root.derive(`m/44'/1237'/0'/0/0`).privateKey\n if (!privateKey) throw new Error('could not derive private key')\n return bytesToHex(privateKey)\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,mBAA2B;AAC3B,qBAAyB;AACzB,mBAAuE;AACvE,mBAAsB;AAEf,SAAS,wBAAwB,UAAkB,YAA6B;AACrF,MAAI,OAAO,mBAAM,mBAAe,iCAAmB,UAAU,UAAU,CAAC;AACxE,MAAI,aAAa,KAAK,OAAO,oBAAoB,EAAE;AACnD,MAAI,CAAC;AAAY,UAAM,IAAI,MAAM,8BAA8B;AAC/D,aAAO,yBAAW,UAAU;AAC9B;AAEO,SAAS,oBAA4B;AAC1C,aAAO,+BAAiB,uBAAQ;AAClC;AAEO,SAAS,cAAc,OAAwB;AACpD,aAAO,+BAAiB,OAAO,uBAAQ;AACzC;",
|
||||
"names": []
|
||||
}
|
||||
+77
@@ -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);
|
||||
|
||||
// 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: []
|
||||
};
|
||||
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;
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../nip10.ts"],
|
||||
"sourcesContent": ["import type { Event } from './event.ts'\nimport type { EventPointer, ProfilePointer } from './nip19.ts'\n\nexport type NIP10Result = {\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 which may or may not be in the reply chain.\n */\n mentions: EventPointer[]\n\n /**\n * List of pubkeys that are involved in the thread in no particular order.\n */\n profiles: ProfilePointer[]\n}\n\nexport function parse(event: Pick<Event, 'tags'>): NIP10Result {\n const result: NIP10Result = {\n reply: undefined,\n root: undefined,\n mentions: [],\n profiles: [],\n }\n\n const eTags: string[][] = []\n\n for (const tag of event.tags) {\n if (tag[0] === 'e' && tag[1]) {\n eTags.push(tag)\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 }\n }\n\n for (let eTagIndex = 0; eTagIndex < eTags.length; eTagIndex++) {\n const eTag = eTags[eTagIndex]\n\n const [_, eTagEventId, eTagRelayUrl, eTagMarker] = eTag as [string, string, undefined | string, undefined | string]\n\n const eventPointer: EventPointer = {\n id: eTagEventId,\n relays: eTagRelayUrl ? [eTagRelayUrl] : [],\n }\n\n const isFirstETag = eTagIndex === 0\n const isLastETag = eTagIndex === eTags.length - 1\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 (isFirstETag) {\n result.root = eventPointer\n continue\n }\n\n if (isLastETag) {\n result.reply = eventPointer\n continue\n }\n\n result.mentions.push(eventPointer)\n }\n\n return result\n}\n"],
|
||||
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAyBO,SAAS,MAAM,OAAyC;AAC7D,QAAM,SAAsB;AAAA,IAC1B,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU,CAAC;AAAA,IACX,UAAU,CAAC;AAAA,EACb;AAEA,QAAM,QAAoB,CAAC;AAE3B,aAAW,OAAO,MAAM,MAAM;AAC5B,QAAI,IAAI,OAAO,OAAO,IAAI,IAAI;AAC5B,YAAM,KAAK,GAAG;AAAA,IAChB;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;AAAA,IACH;AAAA,EACF;AAEA,WAAS,YAAY,GAAG,YAAY,MAAM,QAAQ,aAAa;AAC7D,UAAM,OAAO,MAAM;AAEnB,UAAM,CAAC,GAAG,aAAa,cAAc,UAAU,IAAI;AAEnD,UAAM,eAA6B;AAAA,MACjC,IAAI;AAAA,MACJ,QAAQ,eAAe,CAAC,YAAY,IAAI,CAAC;AAAA,IAC3C;AAEA,UAAM,cAAc,cAAc;AAClC,UAAM,aAAa,cAAc,MAAM,SAAS;AAEhD,QAAI,eAAe,QAAQ;AACzB,aAAO,OAAO;AACd;AAAA,IACF;AAEA,QAAI,eAAe,SAAS;AAC1B,aAAO,QAAQ;AACf;AAAA,IACF;AAEA,QAAI,eAAe,WAAW;AAC5B,aAAO,SAAS,KAAK,YAAY;AACjC;AAAA,IACF;AAEA,QAAI,aAAa;AACf,aAAO,OAAO;AACd;AAAA,IACF;AAEA,QAAI,YAAY;AACd,aAAO,QAAQ;AACf;AAAA,IACF;AAEA,WAAO,SAAS,KAAK,YAAY;AAAA,EACnC;AAEA,SAAO;AACT;",
|
||||
"names": []
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
"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, {
|
||||
getPow: () => getPow,
|
||||
minePow: () => minePow
|
||||
});
|
||||
module.exports = __toCommonJS(nip13_exports);
|
||||
|
||||
// event.ts
|
||||
var import_secp256k1 = require("@noble/curves/secp256k1");
|
||||
var import_sha256 = require("@noble/hashes/sha256");
|
||||
var import_utils = require("@noble/hashes/utils");
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// event.ts
|
||||
var verifiedSymbol = Symbol("verified");
|
||||
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_utils.bytesToHex)(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;
|
||||
}
|
||||
|
||||
// nip13.ts
|
||||
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;
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+163
@@ -0,0 +1,163 @@
|
||||
"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);
|
||||
|
||||
// event.ts
|
||||
var import_secp256k12 = require("@noble/curves/secp256k1");
|
||||
var import_sha256 = require("@noble/hashes/sha256");
|
||||
var import_utils2 = require("@noble/hashes/utils");
|
||||
|
||||
// keys.ts
|
||||
var import_secp256k1 = require("@noble/curves/secp256k1");
|
||||
var import_utils = require("@noble/hashes/utils");
|
||||
function getPublicKey(privateKey) {
|
||||
return (0, import_utils.bytesToHex)(import_secp256k1.schnorr.getPublicKey(privateKey));
|
||||
}
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// event.ts
|
||||
var verifiedSymbol = Symbol("verified");
|
||||
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 = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
|
||||
return (0, import_utils2.bytesToHex)(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] = import_secp256k12.schnorr.verify(event.sig, hash, event.pubkey);
|
||||
} catch (err) {
|
||||
return event[verifiedSymbol] = false;
|
||||
}
|
||||
}
|
||||
function getSignature(event, key) {
|
||||
return (0, import_utils2.bytesToHex)(import_secp256k12.schnorr.sign(getEventHash(event), key));
|
||||
}
|
||||
|
||||
// nip18.ts
|
||||
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;
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+212
@@ -0,0 +1,212 @@
|
||||
"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,
|
||||
decode: () => decode,
|
||||
naddrEncode: () => naddrEncode,
|
||||
neventEncode: () => neventEncode,
|
||||
noteEncode: () => noteEncode,
|
||||
nprofileEncode: () => nprofileEncode,
|
||||
npubEncode: () => npubEncode,
|
||||
nrelayEncode: () => nrelayEncode,
|
||||
nsecEncode: () => nsecEncode
|
||||
});
|
||||
module.exports = __toCommonJS(nip19_exports);
|
||||
var import_utils = require("@noble/hashes/utils");
|
||||
var import_base = require("@scure/base");
|
||||
|
||||
// utils.ts
|
||||
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 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 } = import_base.bech32.decode(nip19, 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_utils.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_utils.bytesToHex)(tlv[0][0]),
|
||||
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
|
||||
author: tlv[2]?.[0] ? (0, import_utils.bytesToHex)(tlv[2][0]) : void 0,
|
||||
kind: tlv[3]?.[0] ? parseInt((0, import_utils.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_utils.bytesToHex)(tlv[2][0]),
|
||||
kind: parseInt((0, import_utils.bytesToHex)(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: (0, import_utils.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];
|
||||
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 = import_base.bech32.toWords(data);
|
||||
return import_base.bech32.encode(prefix, words, Bech32MaxSize);
|
||||
}
|
||||
function encodeBytes(prefix, hex) {
|
||||
let data = (0, import_utils.hexToBytes)(hex);
|
||||
return encodeBech32(prefix, data);
|
||||
}
|
||||
function nprofileEncode(profile) {
|
||||
let data = encodeTLV({
|
||||
0: [(0, import_utils.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_utils.hexToBytes)(event.id)],
|
||||
1: (event.relays || []).map((url) => utf8Encoder.encode(url)),
|
||||
2: event.author ? [(0, import_utils.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_utils.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 (0, import_utils.concatBytes)(...entries);
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+149
@@ -0,0 +1,149 @@
|
||||
"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_utils = require("@noble/hashes/utils");
|
||||
var import_base = require("@scure/base");
|
||||
|
||||
// utils.ts
|
||||
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(nip19) {
|
||||
let { prefix, words } = import_base.bech32.decode(nip19, 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_utils.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_utils.bytesToHex)(tlv[0][0]),
|
||||
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
|
||||
author: tlv[2]?.[0] ? (0, import_utils.bytesToHex)(tlv[2][0]) : void 0,
|
||||
kind: tlv[3]?.[0] ? parseInt((0, import_utils.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_utils.bytesToHex)(tlv[2][0]),
|
||||
kind: parseInt((0, import_utils.bytesToHex)(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: (0, import_utils.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];
|
||||
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;
|
||||
}
|
||||
|
||||
// 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])
|
||||
};
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+131
@@ -0,0 +1,131 @@
|
||||
"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);
|
||||
|
||||
// event.ts
|
||||
var import_secp256k12 = require("@noble/curves/secp256k1");
|
||||
var import_sha256 = require("@noble/hashes/sha256");
|
||||
var import_utils2 = require("@noble/hashes/utils");
|
||||
|
||||
// keys.ts
|
||||
var import_secp256k1 = require("@noble/curves/secp256k1");
|
||||
var import_utils = require("@noble/hashes/utils");
|
||||
function getPublicKey(privateKey) {
|
||||
return (0, import_utils.bytesToHex)(import_secp256k1.schnorr.getPublicKey(privateKey));
|
||||
}
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// event.ts
|
||||
var verifiedSymbol = Symbol("verified");
|
||||
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 = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
|
||||
return (0, import_utils2.bytesToHex)(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 getSignature(event, key) {
|
||||
return (0, import_utils2.bytesToHex)(import_secp256k12.schnorr.sign(getEventHash(event), key));
|
||||
}
|
||||
|
||||
// nip25.ts
|
||||
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]
|
||||
};
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+86
@@ -0,0 +1,86 @@
|
||||
"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);
|
||||
|
||||
// nip26.ts
|
||||
var nip26_exports = {};
|
||||
__export(nip26_exports, {
|
||||
createDelegation: () => createDelegation,
|
||||
getDelegator: () => getDelegator
|
||||
});
|
||||
module.exports = __toCommonJS(nip26_exports);
|
||||
var import_secp256k12 = require("@noble/curves/secp256k1");
|
||||
var import_utils2 = require("@noble/hashes/utils");
|
||||
var import_sha256 = require("@noble/hashes/sha256");
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// keys.ts
|
||||
var import_secp256k1 = require("@noble/curves/secp256k1");
|
||||
var import_utils = require("@noble/hashes/utils");
|
||||
function getPublicKey(privateKey) {
|
||||
return (0, import_utils.bytesToHex)(import_secp256k1.schnorr.getPublicKey(privateKey));
|
||||
}
|
||||
|
||||
// nip26.ts
|
||||
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 = (0, import_sha256.sha256)(utf8Encoder.encode(`nostr:delegation:${parameters.pubkey}:${cond}`));
|
||||
let sig = (0, import_utils2.bytesToHex)(import_secp256k12.schnorr.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 = (0, import_sha256.sha256)(utf8Encoder.encode(`nostr:delegation:${event.pubkey}:${cond}`));
|
||||
if (!import_secp256k12.schnorr.verify(sig, sighash, pubkey))
|
||||
return null;
|
||||
return pubkey;
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+164
@@ -0,0 +1,164 @@
|
||||
"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, {
|
||||
matchAll: () => matchAll,
|
||||
regex: () => regex,
|
||||
replaceAll: () => replaceAll
|
||||
});
|
||||
module.exports = __toCommonJS(nip27_exports);
|
||||
|
||||
// nip19.ts
|
||||
var import_utils = require("@noble/hashes/utils");
|
||||
var import_base = require("@scure/base");
|
||||
|
||||
// utils.ts
|
||||
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(nip19) {
|
||||
let { prefix, words } = import_base.bech32.decode(nip19, 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_utils.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_utils.bytesToHex)(tlv[0][0]),
|
||||
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
|
||||
author: tlv[2]?.[0] ? (0, import_utils.bytesToHex)(tlv[2][0]) : void 0,
|
||||
kind: tlv[3]?.[0] ? parseInt((0, import_utils.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_utils.bytesToHex)(tlv[2][0]),
|
||||
kind: parseInt((0, import_utils.bytesToHex)(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: (0, import_utils.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];
|
||||
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;
|
||||
}
|
||||
|
||||
// nip21.ts
|
||||
var NOSTR_URI_REGEX = new RegExp(`nostr:(${BECH32_REGEX.source})`);
|
||||
|
||||
// nip27.ts
|
||||
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)
|
||||
});
|
||||
});
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+188
@@ -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);
|
||||
|
||||
// nip28.ts
|
||||
var nip28_exports = {};
|
||||
__export(nip28_exports, {
|
||||
channelCreateEvent: () => channelCreateEvent,
|
||||
channelHideMessageEvent: () => channelHideMessageEvent,
|
||||
channelMessageEvent: () => channelMessageEvent,
|
||||
channelMetadataEvent: () => channelMetadataEvent,
|
||||
channelMuteUserEvent: () => channelMuteUserEvent
|
||||
});
|
||||
module.exports = __toCommonJS(nip28_exports);
|
||||
|
||||
// event.ts
|
||||
var import_secp256k12 = require("@noble/curves/secp256k1");
|
||||
var import_sha256 = require("@noble/hashes/sha256");
|
||||
var import_utils2 = require("@noble/hashes/utils");
|
||||
|
||||
// keys.ts
|
||||
var import_secp256k1 = require("@noble/curves/secp256k1");
|
||||
var import_utils = require("@noble/hashes/utils");
|
||||
function getPublicKey(privateKey) {
|
||||
return (0, import_utils.bytesToHex)(import_secp256k1.schnorr.getPublicKey(privateKey));
|
||||
}
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// event.ts
|
||||
var verifiedSymbol = Symbol("verified");
|
||||
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 = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
|
||||
return (0, import_utils2.bytesToHex)(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 getSignature(event, key) {
|
||||
return (0, import_utils2.bytesToHex)(import_secp256k12.schnorr.sign(getEventHash(event), key));
|
||||
}
|
||||
|
||||
// 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 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
|
||||
);
|
||||
};
|
||||
+7
File diff suppressed because one or more lines are too long
+42
@@ -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;
|
||||
}
|
||||
}
|
||||
+7
@@ -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": []
|
||||
}
|
||||
+41
@@ -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, {
|
||||
authenticate: () => authenticate
|
||||
});
|
||||
module.exports = __toCommonJS(nip42_exports);
|
||||
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));
|
||||
};
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../nip42.ts"],
|
||||
"sourcesContent": ["import { Kind, type EventTemplate, type Event } from './event.ts'\nimport { Relay } from './relay.ts'\n\n/**\n * Authenticate via NIP-42 flow.\n *\n * @example\n * const sign = window.nostr.signEvent\n * relay.on('auth', challenge =>\n * authenticate({ relay, sign, challenge })\n * )\n */\nexport const authenticate = async ({\n challenge,\n relay,\n sign,\n}: {\n challenge: string\n relay: Relay\n sign: <K extends number = number>(e: EventTemplate<K>) => Promise<Event<K>> | Event<K>\n}): Promise<void> => {\n const e: EventTemplate = {\n kind: Kind.ClientAuth,\n created_at: Math.floor(Date.now() / 1000),\n tags: [\n ['relay', relay.url],\n ['challenge', challenge],\n ],\n content: '',\n }\n return relay.auth(await sign(e))\n}\n"],
|
||||
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAYO,IAAM,eAAe,OAAO;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AACF,MAIqB;AACnB,QAAM,IAAmB;AAAA,IACvB;AAAA,IACA,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,IACxC,MAAM;AAAA,MACJ,CAAC,SAAS,MAAM,GAAG;AAAA,MACnB,CAAC,aAAa,SAAS;AAAA,IACzB;AAAA,IACA,SAAS;AAAA,EACX;AACA,SAAO,MAAM,KAAK,MAAM,KAAK,CAAC,CAAC;AACjC;",
|
||||
"names": []
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
"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,
|
||||
utils: () => utils
|
||||
});
|
||||
module.exports = __toCommonJS(nip44_exports);
|
||||
var import_chacha = require("@noble/ciphers/chacha");
|
||||
var import_utils = 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_utils2 = require("@noble/hashes/utils");
|
||||
var import_base = require("@scure/base");
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// nip44.ts
|
||||
var utils = {
|
||||
v2: {
|
||||
maxPlaintextSize: 65536 - 128,
|
||||
minCiphertextSize: 100,
|
||||
maxCiphertextSize: 102400,
|
||||
getConversationKey(privkeyA, pubkeyB) {
|
||||
const key = import_secp256k1.secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB);
|
||||
return key.subarray(1, 33);
|
||||
},
|
||||
getMessageKeys(conversationKey, salt) {
|
||||
const keys = (0, import_hkdf.hkdf)(import_sha256.sha256, 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 (0, import_utils2.concatBytes)(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 encrypt(key, plaintext, options = {}) {
|
||||
const version = options.version ?? 2;
|
||||
if (version !== 2)
|
||||
throw new Error("unknown encryption version " + version);
|
||||
const salt = options.salt ?? (0, import_utils2.randomBytes)(32);
|
||||
(0, import_utils.ensureBytes)(salt, 32);
|
||||
const keys = utils.v2.getMessageKeys(key, salt);
|
||||
const padded = utils.v2.pad(plaintext);
|
||||
const ciphertext = (0, import_chacha.chacha20)(keys.encryption, keys.nonce, padded);
|
||||
const mac = (0, import_hmac.hmac)(import_sha256.sha256, keys.auth, ciphertext);
|
||||
return import_base.base64.encode((0, import_utils2.concatBytes)(new Uint8Array([version]), salt, ciphertext, mac));
|
||||
}
|
||||
function decrypt(key, ciphertext) {
|
||||
const u = utils.v2;
|
||||
(0, import_utils.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 = import_base.base64.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 = (0, import_hmac.hmac)(import_sha256.sha256, keys.auth, ciphertext_);
|
||||
if (!(0, import_utils.equalBytes)(calculatedMac, mac))
|
||||
throw new Error("invalid MAC");
|
||||
const padded = (0, import_chacha.chacha20)(keys.encryption, keys.nonce, ciphertext_);
|
||||
return u.unpad(padded);
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+146
@@ -0,0 +1,146 @@
|
||||
"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);
|
||||
|
||||
// event.ts
|
||||
var import_secp256k12 = require("@noble/curves/secp256k1");
|
||||
var import_sha256 = require("@noble/hashes/sha256");
|
||||
var import_utils2 = require("@noble/hashes/utils");
|
||||
|
||||
// keys.ts
|
||||
var import_secp256k1 = require("@noble/curves/secp256k1");
|
||||
var import_utils = require("@noble/hashes/utils");
|
||||
function getPublicKey(privateKey) {
|
||||
return (0, import_utils.bytesToHex)(import_secp256k1.schnorr.getPublicKey(privateKey));
|
||||
}
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// event.ts
|
||||
var verifiedSymbol = Symbol("verified");
|
||||
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 = (0, import_sha256.sha256)(utf8Encoder.encode(serializeEvent(event)));
|
||||
return (0, import_utils2.bytesToHex)(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 getSignature(event, key) {
|
||||
return (0, import_utils2.bytesToHex)(import_secp256k12.schnorr.sign(getEventHash(event), key));
|
||||
}
|
||||
|
||||
// nip04.ts
|
||||
var import_utils4 = require("@noble/hashes/utils");
|
||||
var import_secp256k13 = require("@noble/curves/secp256k1");
|
||||
var import_base = require("@scure/base");
|
||||
if (typeof crypto !== "undefined" && !crypto.subtle && crypto.webcrypto) {
|
||||
crypto.subtle = crypto.webcrypto.subtle;
|
||||
}
|
||||
async function encrypt(privkey, pubkey, text) {
|
||||
const key = import_secp256k13.secp256k1.getSharedSecret(privkey, "02" + pubkey);
|
||||
const normalizedKey = getNormalizedX(key);
|
||||
let iv = Uint8Array.from((0, import_utils4.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 = 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 { 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);
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+193
@@ -0,0 +1,193 @@
|
||||
"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, {
|
||||
getZapEndpoint: () => getZapEndpoint,
|
||||
makeZapReceipt: () => makeZapReceipt,
|
||||
makeZapRequest: () => makeZapRequest,
|
||||
useFetchImplementation: () => useFetchImplementation,
|
||||
validateZapRequest: () => validateZapRequest
|
||||
});
|
||||
module.exports = __toCommonJS(nip57_exports);
|
||||
var import_base = require("@scure/base");
|
||||
|
||||
// event.ts
|
||||
var import_secp256k1 = require("@noble/curves/secp256k1");
|
||||
var import_sha256 = require("@noble/hashes/sha256");
|
||||
var import_utils = require("@noble/hashes/utils");
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// event.ts
|
||||
var verifiedSymbol = Symbol("verified");
|
||||
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_utils.bytesToHex)(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] = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
|
||||
} catch (err) {
|
||||
return event[verifiedSymbol] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = `https://${domain}/.well-known/lnurlp/${name}`;
|
||||
} 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({
|
||||
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;
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+161
@@ -0,0 +1,161 @@
|
||||
"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,
|
||||
unpackEventFromToken: () => unpackEventFromToken,
|
||||
validateEvent: () => validateEvent2,
|
||||
validateToken: () => validateToken
|
||||
});
|
||||
module.exports = __toCommonJS(nip98_exports);
|
||||
var import_base = require("@scure/base");
|
||||
|
||||
// event.ts
|
||||
var import_secp256k1 = require("@noble/curves/secp256k1");
|
||||
var import_sha256 = require("@noble/hashes/sha256");
|
||||
var import_utils = require("@noble/hashes/utils");
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// event.ts
|
||||
var verifiedSymbol = Symbol("verified");
|
||||
function getBlankEvent(kind = 255 /* Blank */) {
|
||||
return {
|
||||
kind,
|
||||
content: "",
|
||||
tags: [],
|
||||
created_at: 0
|
||||
};
|
||||
}
|
||||
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_utils.bytesToHex)(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] = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
|
||||
} catch (err) {
|
||||
return event[verifiedSymbol] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// nip98.ts
|
||||
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 + 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+747
@@ -0,0 +1,747 @@
|
||||
"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, {
|
||||
SimplePool: () => SimplePool
|
||||
});
|
||||
module.exports = __toCommonJS(pool_exports);
|
||||
|
||||
// event.ts
|
||||
var import_secp256k1 = require("@noble/curves/secp256k1");
|
||||
var import_sha256 = require("@noble/hashes/sha256");
|
||||
var import_utils = require("@noble/hashes/utils");
|
||||
|
||||
// utils.ts
|
||||
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();
|
||||
}
|
||||
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");
|
||||
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_utils.bytesToHex)(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] = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
|
||||
} catch (err) {
|
||||
return event[verifiedSymbol] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
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);
|
||||
}
|
||||
|
||||
// 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?.() || []);
|
||||
}
|
||||
};
|
||||
+7
File diff suppressed because one or more lines are too long
+219
@@ -0,0 +1,219 @@
|
||||
"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_utils = require("@noble/hashes/utils");
|
||||
var import_base = require("@scure/base");
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// nip19.ts
|
||||
var Bech32MaxSize = 5e3;
|
||||
function decode(nip19) {
|
||||
let { prefix, words } = import_base.bech32.decode(nip19, 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_utils.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_utils.bytesToHex)(tlv[0][0]),
|
||||
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
|
||||
author: tlv[2]?.[0] ? (0, import_utils.bytesToHex)(tlv[2][0]) : void 0,
|
||||
kind: tlv[3]?.[0] ? parseInt((0, import_utils.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_utils.bytesToHex)(tlv[2][0]),
|
||||
kind: parseInt((0, import_utils.bytesToHex)(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: (0, import_utils.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];
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+515
@@ -0,0 +1,515 @@
|
||||
"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, {
|
||||
eventsGenerator: () => eventsGenerator,
|
||||
relayInit: () => relayInit
|
||||
});
|
||||
module.exports = __toCommonJS(relay_exports);
|
||||
|
||||
// event.ts
|
||||
var import_secp256k1 = require("@noble/curves/secp256k1");
|
||||
var import_sha256 = require("@noble/hashes/sha256");
|
||||
var import_utils = require("@noble/hashes/utils");
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
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");
|
||||
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_utils.bytesToHex)(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] = import_secp256k1.schnorr.verify(event.sig, hash, event.pubkey);
|
||||
} catch (err) {
|
||||
return event[verifiedSymbol] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
+7
File diff suppressed because one or more lines are too long
+37
@@ -0,0 +1,37 @@
|
||||
"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);
|
||||
|
||||
// test-helpers.ts
|
||||
var test_helpers_exports = {};
|
||||
__export(test_helpers_exports, {
|
||||
buildEvent: () => buildEvent
|
||||
});
|
||||
module.exports = __toCommonJS(test_helpers_exports);
|
||||
function buildEvent(params) {
|
||||
return {
|
||||
id: "",
|
||||
kind: 1,
|
||||
pubkey: "",
|
||||
created_at: 0,
|
||||
content: "",
|
||||
tags: [],
|
||||
sig: "",
|
||||
...params
|
||||
};
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../test-helpers.ts"],
|
||||
"sourcesContent": ["import type { Event } from './event.ts'\n\ntype EventParams<K extends number> = Partial<Event<K>>\n\n/** Build an event for testing purposes. */\nexport function buildEvent<K extends number = 1>(params: EventParams<K>): Event<K> {\n return {\n id: '',\n kind: 1 as K,\n pubkey: '',\n created_at: 0,\n content: '',\n tags: [],\n sig: '',\n ...params,\n }\n}\n"],
|
||||
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAKO,SAAS,WAAiC,QAAkC;AACjF,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,MAAM,CAAC;AAAA,IACP,KAAK;AAAA,IACL,GAAG;AAAA,EACL;AACF;",
|
||||
"names": []
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
"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, {
|
||||
MessageNode: () => MessageNode,
|
||||
MessageQueue: () => MessageQueue,
|
||||
insertEventIntoAscendingList: () => insertEventIntoAscendingList,
|
||||
insertEventIntoDescendingList: () => insertEventIntoDescendingList,
|
||||
normalizeURL: () => normalizeURL,
|
||||
utf8Decoder: () => utf8Decoder,
|
||||
utf8Encoder: () => utf8Encoder
|
||||
});
|
||||
module.exports = __toCommonJS(utils_exports);
|
||||
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;
|
||||
}
|
||||
};
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../utils.ts"],
|
||||
"sourcesContent": ["import type { Event } from './event.ts'\n\nexport const utf8Decoder = new TextDecoder('utf-8')\nexport const utf8Encoder = new TextEncoder()\n\nexport function normalizeURL(url: string): string {\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}\n\n//\n// fast insert-into-sorted-array functions adapted from https://github.com/terrymorse58/fast-sorted-array\n//\nexport function insertEventIntoDescendingList(sortedArray: Event<number>[], event: Event<number>) {\n let start = 0\n let end = sortedArray.length - 1\n let midPoint\n let position = start\n\n if (end < 0) {\n position = 0\n } else if (event.created_at < sortedArray[end].created_at) {\n position = end + 1\n } else if (event.created_at >= sortedArray[start].created_at) {\n position = start\n } else\n while (true) {\n if (end <= start + 1) {\n position = end\n break\n }\n midPoint = Math.floor(start + (end - start) / 2)\n if (sortedArray[midPoint].created_at > event.created_at) {\n start = midPoint\n } else if (sortedArray[midPoint].created_at < event.created_at) {\n end = midPoint\n } else {\n // aMidPoint === num\n position = midPoint\n break\n }\n }\n\n // insert when num is NOT already in (no duplicates)\n if (sortedArray[position]?.id !== event.id) {\n return [...sortedArray.slice(0, position), event, ...sortedArray.slice(position)]\n }\n\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: Event<number>[], event: Event<number>) {\n let start = 0\n let end = sortedArray.length - 1\n let midPoint\n let position = start\n\n if (end < 0) {\n position = 0\n } else if (event.created_at > sortedArray[end].created_at) {\n position = end + 1\n } else if (event.created_at <= sortedArray[start].created_at) {\n position = start\n } else\n while (true) {\n if (end <= start + 1) {\n position = end\n break\n }\n midPoint = Math.floor(start + (end - start) / 2)\n if (sortedArray[midPoint].created_at < event.created_at) {\n start = midPoint\n } else if (sortedArray[midPoint].created_at > event.created_at) {\n end = midPoint\n } else {\n // aMidPoint === num\n position = midPoint\n break\n }\n }\n\n // insert when num is NOT already in (no duplicates)\n if (sortedArray[position]?.id !== event.id) {\n return [...sortedArray.slice(0, position), event, ...sortedArray.slice(position)]\n }\n\n return sortedArray\n}\n\nexport class MessageNode {\n private _value: string\n private _next: MessageNode | null\n\n public get value(): string {\n return this._value\n }\n public set value(message: string) {\n this._value = message\n }\n public get next(): MessageNode | null {\n return this._next\n }\n public set next(node: MessageNode | null) {\n this._next = node\n }\n\n constructor(message: string) {\n this._value = message\n this._next = null\n }\n}\n\nexport class MessageQueue {\n private _first: MessageNode | null\n private _last: MessageNode | null\n\n public get first(): MessageNode | null {\n return this._first\n }\n public set first(messageNode: MessageNode | null) {\n this._first = messageNode\n }\n public get last(): MessageNode | null {\n return this._last\n }\n public set last(messageNode: MessageNode | null) {\n this._last = messageNode\n }\n private _size: number\n public get size(): number {\n return this._size\n }\n public set size(v: number) {\n this._size = v\n }\n\n constructor() {\n this._first = null\n this._last = null\n this._size = 0\n }\n enqueue(message: string): boolean {\n const newNode = new MessageNode(message)\n if (this._size === 0 || !this._last) {\n this._first = newNode\n this._last = newNode\n } else {\n this._last.next = newNode\n this._last = newNode\n }\n this._size++\n return true\n }\n dequeue(): string | null {\n if (this._size === 0 || !this._first) return null\n\n let prev = this._first\n this._first = prev.next\n prev.next = null\n\n this._size--\n return prev.value\n }\n}\n"],
|
||||
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEO,IAAM,cAAc,IAAI,YAAY,OAAO;AAC3C,IAAM,cAAc,IAAI,YAAY;AAEpC,SAAS,aAAa,KAAqB;AAChD,MAAI,IAAI,IAAI,IAAI,GAAG;AACnB,IAAE,WAAW,EAAE,SAAS,QAAQ,QAAQ,GAAG;AAC3C,MAAI,EAAE,SAAS,SAAS,GAAG;AAAG,MAAE,WAAW,EAAE,SAAS,MAAM,GAAG,EAAE;AACjE,MAAK,EAAE,SAAS,QAAQ,EAAE,aAAa,SAAW,EAAE,SAAS,SAAS,EAAE,aAAa;AAAS,MAAE,OAAO;AACvG,IAAE,aAAa,KAAK;AACpB,IAAE,OAAO;AACT,SAAO,EAAE,SAAS;AACpB;AAKO,SAAS,8BAA8B,aAA8B,OAAsB;AAChG,MAAI,QAAQ;AACZ,MAAI,MAAM,YAAY,SAAS;AAC/B,MAAI;AACJ,MAAI,WAAW;AAEf,MAAI,MAAM,GAAG;AACX,eAAW;AAAA,EACb,WAAW,MAAM,aAAa,YAAY,KAAK,YAAY;AACzD,eAAW,MAAM;AAAA,EACnB,WAAW,MAAM,cAAc,YAAY,OAAO,YAAY;AAC5D,eAAW;AAAA,EACb;AACE,WAAO,MAAM;AACX,UAAI,OAAO,QAAQ,GAAG;AACpB,mBAAW;AACX;AAAA,MACF;AACA,iBAAW,KAAK,MAAM,SAAS,MAAM,SAAS,CAAC;AAC/C,UAAI,YAAY,UAAU,aAAa,MAAM,YAAY;AACvD,gBAAQ;AAAA,MACV,WAAW,YAAY,UAAU,aAAa,MAAM,YAAY;AAC9D,cAAM;AAAA,MACR,OAAO;AAEL,mBAAW;AACX;AAAA,MACF;AAAA,IACF;AAGF,MAAI,YAAY,WAAW,OAAO,MAAM,IAAI;AAC1C,WAAO,CAAC,GAAG,YAAY,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,YAAY,MAAM,QAAQ,CAAC;AAAA,EAClF;AAEA,SAAO;AACT;AAEO,SAAS,6BAA6B,aAA8B,OAAsB;AAC/F,MAAI,QAAQ;AACZ,MAAI,MAAM,YAAY,SAAS;AAC/B,MAAI;AACJ,MAAI,WAAW;AAEf,MAAI,MAAM,GAAG;AACX,eAAW;AAAA,EACb,WAAW,MAAM,aAAa,YAAY,KAAK,YAAY;AACzD,eAAW,MAAM;AAAA,EACnB,WAAW,MAAM,cAAc,YAAY,OAAO,YAAY;AAC5D,eAAW;AAAA,EACb;AACE,WAAO,MAAM;AACX,UAAI,OAAO,QAAQ,GAAG;AACpB,mBAAW;AACX;AAAA,MACF;AACA,iBAAW,KAAK,MAAM,SAAS,MAAM,SAAS,CAAC;AAC/C,UAAI,YAAY,UAAU,aAAa,MAAM,YAAY;AACvD,gBAAQ;AAAA,MACV,WAAW,YAAY,UAAU,aAAa,MAAM,YAAY;AAC9D,cAAM;AAAA,MACR,OAAO;AAEL,mBAAW;AACX;AAAA,MACF;AAAA,IACF;AAGF,MAAI,YAAY,WAAW,OAAO,MAAM,IAAI;AAC1C,WAAO,CAAC,GAAG,YAAY,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,YAAY,MAAM,QAAQ,CAAC;AAAA,EAClF;AAEA,SAAO;AACT;AAEO,IAAM,cAAN,MAAkB;AAAA,EACf;AAAA,EACA;AAAA,EAER,IAAW,QAAgB;AACzB,WAAO,KAAK;AAAA,EACd;AAAA,EACA,IAAW,MAAM,SAAiB;AAChC,SAAK,SAAS;AAAA,EAChB;AAAA,EACA,IAAW,OAA2B;AACpC,WAAO,KAAK;AAAA,EACd;AAAA,EACA,IAAW,KAAK,MAA0B;AACxC,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,YAAY,SAAiB;AAC3B,SAAK,SAAS;AACd,SAAK,QAAQ;AAAA,EACf;AACF;AAEO,IAAM,eAAN,MAAmB;AAAA,EAChB;AAAA,EACA;AAAA,EAER,IAAW,QAA4B;AACrC,WAAO,KAAK;AAAA,EACd;AAAA,EACA,IAAW,MAAM,aAAiC;AAChD,SAAK,SAAS;AAAA,EAChB;AAAA,EACA,IAAW,OAA2B;AACpC,WAAO,KAAK;AAAA,EACd;AAAA,EACA,IAAW,KAAK,aAAiC;AAC/C,SAAK,QAAQ;AAAA,EACf;AAAA,EACQ;AAAA,EACR,IAAW,OAAe;AACxB,WAAO,KAAK;AAAA,EACd;AAAA,EACA,IAAW,KAAK,GAAW;AACzB,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,cAAc;AACZ,SAAK,SAAS;AACd,SAAK,QAAQ;AACb,SAAK,QAAQ;AAAA,EACf;AAAA,EACA,QAAQ,SAA0B;AAChC,UAAM,UAAU,IAAI,YAAY,OAAO;AACvC,QAAI,KAAK,UAAU,KAAK,CAAC,KAAK,OAAO;AACnC,WAAK,SAAS;AACd,WAAK,QAAQ;AAAA,IACf,OAAO;AACL,WAAK,MAAM,OAAO;AAClB,WAAK,QAAQ;AAAA,IACf;AACA,SAAK;AACL,WAAO;AAAA,EACT;AAAA,EACA,UAAyB;AACvB,QAAI,KAAK,UAAU,KAAK,CAAC,KAAK;AAAQ,aAAO;AAE7C,QAAI,OAAO,KAAK;AAChB,SAAK,SAAS,KAAK;AACnB,SAAK,OAAO;AAEZ,SAAK;AACL,WAAO,KAAK;AAAA,EACd;AACF;",
|
||||
"names": []
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
// event.ts
|
||||
import { schnorr as schnorr2 } from "@noble/curves/secp256k1";
|
||||
import { sha256 } from "@noble/hashes/sha256";
|
||||
import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
|
||||
|
||||
// keys.ts
|
||||
import { schnorr } from "@noble/curves/secp256k1";
|
||||
import { bytesToHex } from "@noble/hashes/utils";
|
||||
function getPublicKey(privateKey) {
|
||||
return bytesToHex(schnorr.getPublicKey(privateKey));
|
||||
}
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// event.ts
|
||||
var verifiedSymbol = Symbol("verified");
|
||||
var Kind = /* @__PURE__ */ ((Kind2) => {
|
||||
Kind2[Kind2["Metadata"] = 0] = "Metadata";
|
||||
Kind2[Kind2["Text"] = 1] = "Text";
|
||||
Kind2[Kind2["RecommendRelay"] = 2] = "RecommendRelay";
|
||||
Kind2[Kind2["Contacts"] = 3] = "Contacts";
|
||||
Kind2[Kind2["EncryptedDirectMessage"] = 4] = "EncryptedDirectMessage";
|
||||
Kind2[Kind2["EventDeletion"] = 5] = "EventDeletion";
|
||||
Kind2[Kind2["Repost"] = 6] = "Repost";
|
||||
Kind2[Kind2["Reaction"] = 7] = "Reaction";
|
||||
Kind2[Kind2["BadgeAward"] = 8] = "BadgeAward";
|
||||
Kind2[Kind2["ChannelCreation"] = 40] = "ChannelCreation";
|
||||
Kind2[Kind2["ChannelMetadata"] = 41] = "ChannelMetadata";
|
||||
Kind2[Kind2["ChannelMessage"] = 42] = "ChannelMessage";
|
||||
Kind2[Kind2["ChannelHideMessage"] = 43] = "ChannelHideMessage";
|
||||
Kind2[Kind2["ChannelMuteUser"] = 44] = "ChannelMuteUser";
|
||||
Kind2[Kind2["Blank"] = 255] = "Blank";
|
||||
Kind2[Kind2["Report"] = 1984] = "Report";
|
||||
Kind2[Kind2["ZapRequest"] = 9734] = "ZapRequest";
|
||||
Kind2[Kind2["Zap"] = 9735] = "Zap";
|
||||
Kind2[Kind2["RelayList"] = 10002] = "RelayList";
|
||||
Kind2[Kind2["ClientAuth"] = 22242] = "ClientAuth";
|
||||
Kind2[Kind2["NwcRequest"] = 23194] = "NwcRequest";
|
||||
Kind2[Kind2["HttpAuth"] = 27235] = "HttpAuth";
|
||||
Kind2[Kind2["ProfileBadge"] = 30008] = "ProfileBadge";
|
||||
Kind2[Kind2["BadgeDefinition"] = 30009] = "BadgeDefinition";
|
||||
Kind2[Kind2["Article"] = 30023] = "Article";
|
||||
Kind2[Kind2["FileMetadata"] = 1063] = "FileMetadata";
|
||||
return Kind2;
|
||||
})(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));
|
||||
}
|
||||
export {
|
||||
Kind,
|
||||
finishEvent,
|
||||
getBlankEvent,
|
||||
getEventHash,
|
||||
getSignature,
|
||||
serializeEvent,
|
||||
signEvent,
|
||||
validateEvent,
|
||||
verifiedSymbol,
|
||||
verifySignature
|
||||
};
|
||||
+7
File diff suppressed because one or more lines are too long
+45
@@ -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
|
||||
};
|
||||
+7
@@ -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": []
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
// 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;
|
||||
}
|
||||
export {
|
||||
matchFilter,
|
||||
matchFilters,
|
||||
mergeFilters
|
||||
};
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../filter.ts"],
|
||||
"sourcesContent": ["import { Event } from './event.ts'\n\nexport type Filter<K extends number = number> = {\n ids?: string[]\n kinds?: K[]\n authors?: string[]\n since?: number\n until?: number\n limit?: number\n search?: string\n [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter<number>, event: Event<number>): boolean {\n if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n if (!filter.ids.some(prefix => event.id.startsWith(prefix))) {\n return false\n }\n }\n if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) return false\n if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n if (!filter.authors.some(prefix => event.pubkey.startsWith(prefix))) {\n return false\n }\n }\n\n for (let f in filter) {\n if (f[0] === '#') {\n let tagName = f.slice(1)\n let values = filter[`#${tagName}`]\n if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n }\n }\n\n if (filter.since && event.created_at < filter.since) return false\n if (filter.until && event.created_at > filter.until) return false\n\n return true\n}\n\nexport function matchFilters(filters: Filter<number>[], event: Event<number>): boolean {\n for (let i = 0; i < filters.length; i++) {\n if (matchFilter(filters[i], event)) return true\n }\n return false\n}\n\nexport function mergeFilters(...filters: Filter<number>[]): Filter<number> {\n let result: Filter<number> = {}\n for (let i = 0; i < filters.length; i++) {\n let filter = filters[i]\n Object.entries(filter).forEach(([property, values]) => {\n if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n // @ts-ignore\n result[property] = result[property] || []\n // @ts-ignore\n for (let v = 0; v < values.length; v++) {\n // @ts-ignore\n let value = values[v]\n // @ts-ignore\n if (!result[property].includes(value)) result[property].push(value)\n }\n }\n })\n\n if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n }\n\n return result\n}\n"],
|
||||
"mappings": ";AAaO,SAAS,YAAY,QAAwB,OAA+B;AACjF,MAAI,OAAO,OAAO,OAAO,IAAI,QAAQ,MAAM,EAAE,MAAM,IAAI;AACrD,QAAI,CAAC,OAAO,IAAI,KAAK,YAAU,MAAM,GAAG,WAAW,MAAM,CAAC,GAAG;AAC3D,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,OAAO,SAAS,OAAO,MAAM,QAAQ,MAAM,IAAI,MAAM;AAAI,WAAO;AACpE,MAAI,OAAO,WAAW,OAAO,QAAQ,QAAQ,MAAM,MAAM,MAAM,IAAI;AACjE,QAAI,CAAC,OAAO,QAAQ,KAAK,YAAU,MAAM,OAAO,WAAW,MAAM,CAAC,GAAG;AACnE,aAAO;AAAA,IACT;AAAA,EACF;AAEA,WAAS,KAAK,QAAQ;AACpB,QAAI,EAAE,OAAO,KAAK;AAChB,UAAI,UAAU,EAAE,MAAM,CAAC;AACvB,UAAI,SAAS,OAAO,IAAI;AACxB,UAAI,UAAU,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,MAAM,EAAE,MAAM,CAAC,KAAK,OAAQ,QAAQ,CAAC,MAAM,EAAE;AAAG,eAAO;AAAA,IACpG;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,MAAM,aAAa,OAAO;AAAO,WAAO;AAC5D,MAAI,OAAO,SAAS,MAAM,aAAa,OAAO;AAAO,WAAO;AAE5D,SAAO;AACT;AAEO,SAAS,aAAa,SAA2B,OAA+B;AACrF,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,QAAI,YAAY,QAAQ,IAAI,KAAK;AAAG,aAAO;AAAA,EAC7C;AACA,SAAO;AACT;AAEO,SAAS,gBAAgB,SAA2C;AACzE,MAAI,SAAyB,CAAC;AAC9B,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,QAAI,SAAS,QAAQ;AACrB,WAAO,QAAQ,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU,MAAM,MAAM;AACrD,UAAI,aAAa,WAAW,aAAa,SAAS,aAAa,aAAa,SAAS,OAAO,KAAK;AAE/F,eAAO,YAAY,OAAO,aAAa,CAAC;AAExC,iBAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AAEtC,cAAI,QAAQ,OAAO;AAEnB,cAAI,CAAC,OAAO,UAAU,SAAS,KAAK;AAAG,mBAAO,UAAU,KAAK,KAAK;AAAA,QACpE;AAAA,MACF;AAAA,IACF,CAAC;AAED,QAAI,OAAO,UAAU,CAAC,OAAO,SAAS,OAAO,QAAQ,OAAO;AAAQ,aAAO,QAAQ,OAAO;AAC1F,QAAI,OAAO,UAAU,CAAC,OAAO,SAAS,OAAO,QAAQ,OAAO;AAAQ,aAAO,QAAQ,OAAO;AAC1F,QAAI,OAAO,UAAU,CAAC,OAAO,SAAS,OAAO,QAAQ,OAAO;AAAQ,aAAO,QAAQ,OAAO;AAAA,EAC5F;AAEA,SAAO;AACT;",
|
||||
"names": []
|
||||
}
|
||||
+2109
File diff suppressed because it is too large
Load Diff
+7
File diff suppressed because one or more lines are too long
+13
@@ -0,0 +1,13 @@
|
||||
// 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));
|
||||
}
|
||||
export {
|
||||
generatePrivateKey,
|
||||
getPublicKey
|
||||
};
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../keys.ts"],
|
||||
"sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1'\nimport { bytesToHex } from '@noble/hashes/utils'\n\nexport function generatePrivateKey(): string {\n return bytesToHex(schnorr.utils.randomPrivateKey())\n}\n\nexport function getPublicKey(privateKey: string): string {\n return bytesToHex(schnorr.getPublicKey(privateKey))\n}\n"],
|
||||
"mappings": ";AAAA,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAEpB,SAAS,qBAA6B;AAC3C,SAAO,WAAW,QAAQ,MAAM,iBAAiB,CAAC;AACpD;AAEO,SAAS,aAAa,YAA4B;AACvD,SAAO,WAAW,QAAQ,aAAa,UAAU,CAAC;AACpD;",
|
||||
"names": []
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// 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 1e4 <= kind && kind < 2e4 || [0, 3].includes(kind);
|
||||
}
|
||||
function isEphemeralKind(kind) {
|
||||
return 2e4 <= kind && kind < 3e4;
|
||||
}
|
||||
function isParameterizedReplaceableKind(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 (isParameterizedReplaceableKind(kind))
|
||||
return "parameterized";
|
||||
return "unknown";
|
||||
}
|
||||
export {
|
||||
classifyKind,
|
||||
isEphemeralKind,
|
||||
isParameterizedReplaceableKind,
|
||||
isRegularKind,
|
||||
isReplaceableKind
|
||||
};
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../kinds.ts"],
|
||||
"sourcesContent": ["/** Events are **regular**, which means they're all expected to be stored by relays. */\nfunction isRegularKind(kind: number) {\n return (1000 <= kind && kind < 10000) || [1, 2, 4, 5, 6, 7, 8, 16, 40, 41, 42, 43, 44].includes(kind)\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nfunction isReplaceableKind(kind: number) {\n return (10000 <= kind && kind < 20000) || [0, 3].includes(kind)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nfunction isEphemeralKind(kind: number) {\n return 20000 <= kind && kind < 30000\n}\n\n/** Events are **parameterized replaceable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nfunction isParameterizedReplaceableKind(kind: number) {\n return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\ntype KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nfunction classifyKind(kind: number): KindClassification {\n if (isRegularKind(kind)) return 'regular'\n if (isReplaceableKind(kind)) return 'replaceable'\n if (isEphemeralKind(kind)) return 'ephemeral'\n if (isParameterizedReplaceableKind(kind)) return 'parameterized'\n return 'unknown'\n}\n\nexport {\n classifyKind,\n isEphemeralKind,\n isParameterizedReplaceableKind,\n isRegularKind,\n isReplaceableKind,\n type KindClassification,\n}\n"],
|
||||
"mappings": ";AACA,SAAS,cAAc,MAAc;AACnC,SAAQ,OAAQ,QAAQ,OAAO,OAAU,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,EAAE,SAAS,IAAI;AACtG;AAGA,SAAS,kBAAkB,MAAc;AACvC,SAAQ,OAAS,QAAQ,OAAO,OAAU,CAAC,GAAG,CAAC,EAAE,SAAS,IAAI;AAChE;AAGA,SAAS,gBAAgB,MAAc;AACrC,SAAO,OAAS,QAAQ,OAAO;AACjC;AAGA,SAAS,+BAA+B,MAAc;AACpD,SAAO,OAAS,QAAQ,OAAO;AACjC;AAMA,SAAS,aAAa,MAAkC;AACtD,MAAI,cAAc,IAAI;AAAG,WAAO;AAChC,MAAI,kBAAkB,IAAI;AAAG,WAAO;AACpC,MAAI,gBAAgB,IAAI;AAAG,WAAO;AAClC,MAAI,+BAA+B,IAAI;AAAG,WAAO;AACjD,SAAO;AACT;",
|
||||
"names": []
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// nip04.ts
|
||||
import { randomBytes } from "@noble/hashes/utils";
|
||||
import { secp256k1 } from "@noble/curves/secp256k1";
|
||||
import { base64 } from "@scure/base";
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// nip04.ts
|
||||
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);
|
||||
}
|
||||
export {
|
||||
decrypt,
|
||||
encrypt
|
||||
};
|
||||
+7
File diff suppressed because one or more lines are too long
+57
@@ -0,0 +1,57 @@
|
||||
// nip05.ts
|
||||
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;
|
||||
}
|
||||
export {
|
||||
NIP05_REGEX,
|
||||
queryProfile,
|
||||
searchDomain,
|
||||
useFetchImplementation
|
||||
};
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../nip05.ts"],
|
||||
"sourcesContent": ["import { ProfilePointer } from './nip19.ts'\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.-]+)$/\n\nvar _fetch: any\n\ntry {\n _fetch = fetch\n} catch {}\n\nexport function useFetchImplementation(fetchImplementation: any) {\n _fetch = fetchImplementation\n}\n\nexport async function searchDomain(domain: string, query = ''): Promise<{ [name: string]: string }> {\n try {\n let res = await (await _fetch(`https://${domain}/.well-known/nostr.json?name=${query}`)).json()\n\n return res.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 res = await _fetch(`https://${domain}/.well-known/nostr.json?name=${name}`)\n const { names, relays } = parseNIP05Result(await res.json())\n\n const pubkey = names[name]\n return pubkey ? { pubkey, relays: relays?.[pubkey] } : null\n } catch (_e) {\n return null\n }\n}\n\n/** nostr.json result. */\nexport interface NIP05Result {\n names: {\n [name: string]: string\n }\n relays?: {\n [pubkey: string]: string[]\n }\n}\n\n/** Parse the nostr.json and throw if it's not valid. */\nfunction parseNIP05Result(json: any): NIP05Result {\n const result: NIP05Result = {\n names: {},\n }\n\n for (const [name, pubkey] of Object.entries(json.names)) {\n if (typeof name === 'string' && typeof pubkey === 'string') {\n result.names[name] = pubkey\n }\n }\n\n if (json.relays) {\n result.relays = {}\n for (const [pubkey, relays] of Object.entries(json.relays)) {\n if (typeof pubkey === 'string' && Array.isArray(relays)) {\n result.relays[pubkey] = relays.filter((relay: unknown) => typeof relay === 'string')\n }\n }\n }\n\n return result\n}\n"],
|
||||
"mappings": ";AASO,IAAM,cAAc;AAE3B,IAAI;AAEJ,IAAI;AACF,WAAS;AACX,QAAE;AAAO;AAEF,SAAS,uBAAuB,qBAA0B;AAC/D,WAAS;AACX;AAEA,eAAsB,aAAa,QAAgB,QAAQ,IAAyC;AAClG,MAAI;AACF,QAAI,MAAM,OAAO,MAAM,OAAO,WAAW,sCAAsC,OAAO,GAAG,KAAK;AAE9F,WAAO,IAAI;AAAA,EACb,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,GAAG,OAAO,KAAK,MAAM,IAAI;AAEhC,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,WAAW,sCAAsC,MAAM;AAChF,UAAM,EAAE,OAAO,OAAO,IAAI,iBAAiB,MAAM,IAAI,KAAK,CAAC;AAE3D,UAAM,SAAS,MAAM;AACrB,WAAO,SAAS,EAAE,QAAQ,QAAQ,SAAS,QAAQ,IAAI;AAAA,EACzD,SAAS,IAAP;AACA,WAAO;AAAA,EACT;AACF;AAaA,SAAS,iBAAiB,MAAwB;AAChD,QAAM,SAAsB;AAAA,IAC1B,OAAO,CAAC;AAAA,EACV;AAEA,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,KAAK,KAAK,GAAG;AACvD,QAAI,OAAO,SAAS,YAAY,OAAO,WAAW,UAAU;AAC1D,aAAO,MAAM,QAAQ;AAAA,IACvB;AAAA,EACF;AAEA,MAAI,KAAK,QAAQ;AACf,WAAO,SAAS,CAAC;AACjB,eAAW,CAAC,QAAQ,MAAM,KAAK,OAAO,QAAQ,KAAK,MAAM,GAAG;AAC1D,UAAI,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AACvD,eAAO,OAAO,UAAU,OAAO,OAAO,CAAC,UAAmB,OAAO,UAAU,QAAQ;AAAA,MACrF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;",
|
||||
"names": []
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// nip06.ts
|
||||
import { bytesToHex } 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 bytesToHex(privateKey);
|
||||
}
|
||||
function generateSeedWords() {
|
||||
return generateMnemonic(wordlist);
|
||||
}
|
||||
function validateWords(words) {
|
||||
return validateMnemonic(words, wordlist);
|
||||
}
|
||||
export {
|
||||
generateSeedWords,
|
||||
privateKeyFromSeedWords,
|
||||
validateWords
|
||||
};
|
||||
+7
@@ -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\nexport function privateKeyFromSeedWords(mnemonic: string, passphrase?: string): string {\n let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))\n let privateKey = root.derive(`m/44'/1237'/0'/0/0`).privateKey\n if (!privateKey) throw new Error('could not derive private key')\n return bytesToHex(privateKey)\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,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB;AACzB,SAAS,kBAAkB,oBAAoB,wBAAwB;AACvE,SAAS,aAAa;AAEf,SAAS,wBAAwB,UAAkB,YAA6B;AACrF,MAAI,OAAO,MAAM,eAAe,mBAAmB,UAAU,UAAU,CAAC;AACxE,MAAI,aAAa,KAAK,OAAO,oBAAoB,EAAE;AACnD,MAAI,CAAC;AAAY,UAAM,IAAI,MAAM,8BAA8B;AAC/D,SAAO,WAAW,UAAU;AAC9B;AAEO,SAAS,oBAA4B;AAC1C,SAAO,iBAAiB,QAAQ;AAClC;AAEO,SAAS,cAAc,OAAwB;AACpD,SAAO,iBAAiB,OAAO,QAAQ;AACzC;",
|
||||
"names": []
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
// nip10.ts
|
||||
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;
|
||||
}
|
||||
export {
|
||||
parse
|
||||
};
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../nip10.ts"],
|
||||
"sourcesContent": ["import type { Event } from './event.ts'\nimport type { EventPointer, ProfilePointer } from './nip19.ts'\n\nexport type NIP10Result = {\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 which may or may not be in the reply chain.\n */\n mentions: EventPointer[]\n\n /**\n * List of pubkeys that are involved in the thread in no particular order.\n */\n profiles: ProfilePointer[]\n}\n\nexport function parse(event: Pick<Event, 'tags'>): NIP10Result {\n const result: NIP10Result = {\n reply: undefined,\n root: undefined,\n mentions: [],\n profiles: [],\n }\n\n const eTags: string[][] = []\n\n for (const tag of event.tags) {\n if (tag[0] === 'e' && tag[1]) {\n eTags.push(tag)\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 }\n }\n\n for (let eTagIndex = 0; eTagIndex < eTags.length; eTagIndex++) {\n const eTag = eTags[eTagIndex]\n\n const [_, eTagEventId, eTagRelayUrl, eTagMarker] = eTag as [string, string, undefined | string, undefined | string]\n\n const eventPointer: EventPointer = {\n id: eTagEventId,\n relays: eTagRelayUrl ? [eTagRelayUrl] : [],\n }\n\n const isFirstETag = eTagIndex === 0\n const isLastETag = eTagIndex === eTags.length - 1\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 (isFirstETag) {\n result.root = eventPointer\n continue\n }\n\n if (isLastETag) {\n result.reply = eventPointer\n continue\n }\n\n result.mentions.push(eventPointer)\n }\n\n return result\n}\n"],
|
||||
"mappings": ";AAyBO,SAAS,MAAM,OAAyC;AAC7D,QAAM,SAAsB;AAAA,IAC1B,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU,CAAC;AAAA,IACX,UAAU,CAAC;AAAA,EACb;AAEA,QAAM,QAAoB,CAAC;AAE3B,aAAW,OAAO,MAAM,MAAM;AAC5B,QAAI,IAAI,OAAO,OAAO,IAAI,IAAI;AAC5B,YAAM,KAAK,GAAG;AAAA,IAChB;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;AAAA,IACH;AAAA,EACF;AAEA,WAAS,YAAY,GAAG,YAAY,MAAM,QAAQ,aAAa;AAC7D,UAAM,OAAO,MAAM;AAEnB,UAAM,CAAC,GAAG,aAAa,cAAc,UAAU,IAAI;AAEnD,UAAM,eAA6B;AAAA,MACjC,IAAI;AAAA,MACJ,QAAQ,eAAe,CAAC,YAAY,IAAI,CAAC;AAAA,IAC3C;AAEA,UAAM,cAAc,cAAc;AAClC,UAAM,aAAa,cAAc,MAAM,SAAS;AAEhD,QAAI,eAAe,QAAQ;AACzB,aAAO,OAAO;AACd;AAAA,IACF;AAEA,QAAI,eAAe,SAAS;AAC1B,aAAO,QAAQ;AACf;AAAA,IACF;AAEA,QAAI,eAAe,WAAW;AAC5B,aAAO,SAAS,KAAK,YAAY;AACjC;AAAA,IACF;AAEA,QAAI,aAAa;AACf,aAAO,OAAO;AACd;AAAA,IACF;AAEA,QAAI,YAAY;AACd,aAAO,QAAQ;AACf;AAAA,IACF;AAEA,WAAO,SAAS,KAAK,YAAY;AAAA,EACnC;AAEA,SAAO;AACT;",
|
||||
"names": []
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
// event.ts
|
||||
import { schnorr } from "@noble/curves/secp256k1";
|
||||
import { sha256 } from "@noble/hashes/sha256";
|
||||
import { bytesToHex } from "@noble/hashes/utils";
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// event.ts
|
||||
var verifiedSymbol = Symbol("verified");
|
||||
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 bytesToHex(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;
|
||||
}
|
||||
|
||||
// nip13.ts
|
||||
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;
|
||||
}
|
||||
export {
|
||||
getPow,
|
||||
minePow
|
||||
};
|
||||
+7
File diff suppressed because one or more lines are too long
+140
@@ -0,0 +1,140 @@
|
||||
// event.ts
|
||||
import { schnorr as schnorr2 } from "@noble/curves/secp256k1";
|
||||
import { sha256 } from "@noble/hashes/sha256";
|
||||
import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
|
||||
|
||||
// keys.ts
|
||||
import { schnorr } from "@noble/curves/secp256k1";
|
||||
import { bytesToHex } from "@noble/hashes/utils";
|
||||
function getPublicKey(privateKey) {
|
||||
return bytesToHex(schnorr.getPublicKey(privateKey));
|
||||
}
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// event.ts
|
||||
var verifiedSymbol = Symbol("verified");
|
||||
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 getSignature(event, key) {
|
||||
return bytesToHex2(schnorr2.sign(getEventHash(event), key));
|
||||
}
|
||||
|
||||
// nip18.ts
|
||||
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;
|
||||
}
|
||||
export {
|
||||
finishRepostEvent,
|
||||
getRepostedEvent,
|
||||
getRepostedEventPointer
|
||||
};
|
||||
+7
File diff suppressed because one or more lines are too long
+191
@@ -0,0 +1,191 @@
|
||||
// nip19.ts
|
||||
import { bytesToHex, concatBytes, hexToBytes } from "@noble/hashes/utils";
|
||||
import { bech32 } from "@scure/base";
|
||||
|
||||
// utils.ts
|
||||
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 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: 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: bytesToHex(tlv[0][0]),
|
||||
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
|
||||
author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : void 0,
|
||||
kind: tlv[3]?.[0] ? parseInt(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: bytesToHex(tlv[2][0]),
|
||||
kind: parseInt(bytesToHex(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: 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];
|
||||
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);
|
||||
}
|
||||
export {
|
||||
BECH32_REGEX,
|
||||
decode,
|
||||
naddrEncode,
|
||||
neventEncode,
|
||||
noteEncode,
|
||||
nprofileEncode,
|
||||
npubEncode,
|
||||
nrelayEncode,
|
||||
nsecEncode
|
||||
};
|
||||
+7
File diff suppressed because one or more lines are too long
+126
@@ -0,0 +1,126 @@
|
||||
// nip19.ts
|
||||
import { bytesToHex, concatBytes, hexToBytes } from "@noble/hashes/utils";
|
||||
import { bech32 } from "@scure/base";
|
||||
|
||||
// utils.ts
|
||||
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(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: 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: bytesToHex(tlv[0][0]),
|
||||
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
|
||||
author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : void 0,
|
||||
kind: tlv[3]?.[0] ? parseInt(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: bytesToHex(tlv[2][0]),
|
||||
kind: parseInt(bytesToHex(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: 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];
|
||||
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;
|
||||
}
|
||||
|
||||
// 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])
|
||||
};
|
||||
}
|
||||
export {
|
||||
NOSTR_URI_REGEX,
|
||||
parse,
|
||||
test
|
||||
};
|
||||
+7
File diff suppressed because one or more lines are too long
+108
@@ -0,0 +1,108 @@
|
||||
// event.ts
|
||||
import { schnorr as schnorr2 } from "@noble/curves/secp256k1";
|
||||
import { sha256 } from "@noble/hashes/sha256";
|
||||
import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
|
||||
|
||||
// keys.ts
|
||||
import { schnorr } from "@noble/curves/secp256k1";
|
||||
import { bytesToHex } from "@noble/hashes/utils";
|
||||
function getPublicKey(privateKey) {
|
||||
return bytesToHex(schnorr.getPublicKey(privateKey));
|
||||
}
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// event.ts
|
||||
var verifiedSymbol = Symbol("verified");
|
||||
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 getSignature(event, key) {
|
||||
return bytesToHex2(schnorr2.sign(getEventHash(event), key));
|
||||
}
|
||||
|
||||
// nip25.ts
|
||||
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]
|
||||
};
|
||||
}
|
||||
export {
|
||||
finishReactionEvent,
|
||||
getReactedEventPointer
|
||||
};
|
||||
+7
File diff suppressed because one or more lines are too long
+65
@@ -0,0 +1,65 @@
|
||||
// nip26.ts
|
||||
import { schnorr as schnorr2 } from "@noble/curves/secp256k1";
|
||||
import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
|
||||
import { sha256 } from "@noble/hashes/sha256";
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// keys.ts
|
||||
import { schnorr } from "@noble/curves/secp256k1";
|
||||
import { bytesToHex } from "@noble/hashes/utils";
|
||||
function getPublicKey(privateKey) {
|
||||
return bytesToHex(schnorr.getPublicKey(privateKey));
|
||||
}
|
||||
|
||||
// nip26.ts
|
||||
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 = sha256(utf8Encoder.encode(`nostr:delegation:${parameters.pubkey}:${cond}`));
|
||||
let sig = bytesToHex2(schnorr2.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 = sha256(utf8Encoder.encode(`nostr:delegation:${event.pubkey}:${cond}`));
|
||||
if (!schnorr2.verify(sig, sighash, pubkey))
|
||||
return null;
|
||||
return pubkey;
|
||||
}
|
||||
export {
|
||||
createDelegation,
|
||||
getDelegator
|
||||
};
|
||||
+7
File diff suppressed because one or more lines are too long
+141
@@ -0,0 +1,141 @@
|
||||
// nip19.ts
|
||||
import { bytesToHex, concatBytes, hexToBytes } from "@noble/hashes/utils";
|
||||
import { bech32 } from "@scure/base";
|
||||
|
||||
// utils.ts
|
||||
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(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: 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: bytesToHex(tlv[0][0]),
|
||||
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
|
||||
author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : void 0,
|
||||
kind: tlv[3]?.[0] ? parseInt(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: bytesToHex(tlv[2][0]),
|
||||
kind: parseInt(bytesToHex(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: 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];
|
||||
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;
|
||||
}
|
||||
|
||||
// nip21.ts
|
||||
var NOSTR_URI_REGEX = new RegExp(`nostr:(${BECH32_REGEX.source})`);
|
||||
|
||||
// nip27.ts
|
||||
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)
|
||||
});
|
||||
});
|
||||
}
|
||||
export {
|
||||
matchAll,
|
||||
regex,
|
||||
replaceAll
|
||||
};
|
||||
+7
File diff suppressed because one or more lines are too long
+165
@@ -0,0 +1,165 @@
|
||||
// event.ts
|
||||
import { schnorr as schnorr2 } from "@noble/curves/secp256k1";
|
||||
import { sha256 } from "@noble/hashes/sha256";
|
||||
import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
|
||||
|
||||
// keys.ts
|
||||
import { schnorr } from "@noble/curves/secp256k1";
|
||||
import { bytesToHex } from "@noble/hashes/utils";
|
||||
function getPublicKey(privateKey) {
|
||||
return bytesToHex(schnorr.getPublicKey(privateKey));
|
||||
}
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// event.ts
|
||||
var verifiedSymbol = Symbol("verified");
|
||||
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 getSignature(event, key) {
|
||||
return bytesToHex2(schnorr2.sign(getEventHash(event), key));
|
||||
}
|
||||
|
||||
// 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 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
|
||||
);
|
||||
};
|
||||
export {
|
||||
channelCreateEvent,
|
||||
channelHideMessageEvent,
|
||||
channelMessageEvent,
|
||||
channelMetadataEvent,
|
||||
channelMuteUserEvent
|
||||
};
|
||||
+7
File diff suppressed because one or more lines are too long
+21
@@ -0,0 +1,21 @@
|
||||
// nip39.ts
|
||||
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;
|
||||
}
|
||||
}
|
||||
export {
|
||||
useFetchImplementation,
|
||||
validateGithub
|
||||
};
|
||||
+7
@@ -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,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": []
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// nip42.ts
|
||||
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));
|
||||
};
|
||||
export {
|
||||
authenticate
|
||||
};
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../nip42.ts"],
|
||||
"sourcesContent": ["import { Kind, type EventTemplate, type Event } from './event.ts'\nimport { Relay } from './relay.ts'\n\n/**\n * Authenticate via NIP-42 flow.\n *\n * @example\n * const sign = window.nostr.signEvent\n * relay.on('auth', challenge =>\n * authenticate({ relay, sign, challenge })\n * )\n */\nexport const authenticate = async ({\n challenge,\n relay,\n sign,\n}: {\n challenge: string\n relay: Relay\n sign: <K extends number = number>(e: EventTemplate<K>) => Promise<Event<K>> | Event<K>\n}): Promise<void> => {\n const e: EventTemplate = {\n kind: Kind.ClientAuth,\n created_at: Math.floor(Date.now() / 1000),\n tags: [\n ['relay', relay.url],\n ['challenge', challenge],\n ],\n content: '',\n }\n return relay.auth(await sign(e))\n}\n"],
|
||||
"mappings": ";AAYO,IAAM,eAAe,OAAO;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AACF,MAIqB;AACnB,QAAM,IAAmB;AAAA,IACvB;AAAA,IACA,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,IACxC,MAAM;AAAA,MACJ,CAAC,SAAS,MAAM,GAAG;AAAA,MACnB,CAAC,aAAa,SAAS;AAAA,IACzB;AAAA,IACA,SAAS;AAAA,EACX;AACA,SAAO,MAAM,KAAK,MAAM,KAAK,CAAC,CAAC;AACjC;",
|
||||
"names": []
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
// nip44.ts
|
||||
import { chacha20 } from "@noble/ciphers/chacha";
|
||||
import { ensureBytes, equalBytes } from "@noble/ciphers/utils";
|
||||
import { secp256k1 } from "@noble/curves/secp256k1";
|
||||
import { hkdf } from "@noble/hashes/hkdf";
|
||||
import { hmac } from "@noble/hashes/hmac";
|
||||
import { sha256 } from "@noble/hashes/sha256";
|
||||
import { concatBytes, randomBytes } from "@noble/hashes/utils";
|
||||
import { base64 } from "@scure/base";
|
||||
|
||||
// utils.ts
|
||||
var utf8Decoder = new TextDecoder("utf-8");
|
||||
var utf8Encoder = new TextEncoder();
|
||||
|
||||
// nip44.ts
|
||||
var utils = {
|
||||
v2: {
|
||||
maxPlaintextSize: 65536 - 128,
|
||||
minCiphertextSize: 100,
|
||||
maxCiphertextSize: 102400,
|
||||
getConversationKey(privkeyA, pubkeyB) {
|
||||
const key = secp256k1.getSharedSecret(privkeyA, "02" + pubkeyB);
|
||||
return key.subarray(1, 33);
|
||||
},
|
||||
getMessageKeys(conversationKey, salt) {
|
||||
const keys = hkdf(sha256, 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 concatBytes(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 encrypt(key, plaintext, options = {}) {
|
||||
const version = options.version ?? 2;
|
||||
if (version !== 2)
|
||||
throw new Error("unknown encryption version " + version);
|
||||
const salt = options.salt ?? randomBytes(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(sha256, keys.auth, ciphertext);
|
||||
return base64.encode(concatBytes(new Uint8Array([version]), salt, ciphertext, mac));
|
||||
}
|
||||
function decrypt(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 = base64.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(sha256, keys.auth, ciphertext_);
|
||||
if (!equalBytes(calculatedMac, mac))
|
||||
throw new Error("invalid MAC");
|
||||
const padded = chacha20(keys.encryption, keys.nonce, ciphertext_);
|
||||
return u.unpad(padded);
|
||||
}
|
||||
export {
|
||||
decrypt,
|
||||
encrypt,
|
||||
utils
|
||||
};
|
||||
+7
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user