@qntx/nostr 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +25 -0
- package/dist/builder-BphJa-Y-.d.mts +67 -0
- package/dist/builder-q0VsvnYE.mjs +158 -0
- package/dist/builder-q0VsvnYE.mjs.map +1 -0
- package/dist/client-Bd8B1mYm.mjs +835 -0
- package/dist/client-Bd8B1mYm.mjs.map +1 -0
- package/dist/client.d.mts +2 -0
- package/dist/client.mjs +2 -0
- package/dist/core.d.mts +8 -0
- package/dist/core.mjs +9 -0
- package/dist/error-B49rbDzT.d.mts +7 -0
- package/dist/error-CwBxle6N.mjs +13 -0
- package/dist/error-CwBxle6N.mjs.map +1 -0
- package/dist/error-DZEs5OuZ.mjs +24 -0
- package/dist/error-DZEs5OuZ.mjs.map +1 -0
- package/dist/error-WEFS-R-4.d.mts +20 -0
- package/dist/event-B5D2jxt_.d.mts +108 -0
- package/dist/event-CzoQEFMF.mjs +188 -0
- package/dist/event-CzoQEFMF.mjs.map +1 -0
- package/dist/filter-BxwjEkZp.mjs +113 -0
- package/dist/filter-BxwjEkZp.mjs.map +1 -0
- package/dist/filter-DUEKP3q1.d.mts +40 -0
- package/dist/gossip-3xkRRCm_.mjs +220 -0
- package/dist/gossip-3xkRRCm_.mjs.map +1 -0
- package/dist/gossip-C5RVB1UB.d.mts +71 -0
- package/dist/gossip.d.mts +3 -0
- package/dist/gossip.mjs +2 -0
- package/dist/http-6vevbBlu.d.mts +22 -0
- package/dist/http-D2rhokZo.mjs +24 -0
- package/dist/http-D2rhokZo.mjs.map +1 -0
- package/dist/index-BYMnhRIC.d.mts +161 -0
- package/dist/index-C0C4e-Gn.d.mts +55 -0
- package/dist/index-CaxrMD1A.d.mts +1 -0
- package/dist/index-DaRyjo7O.d.mts +77 -0
- package/dist/index-DgOugrpF.d.mts +275 -0
- package/dist/index-EwL6fHLo.d.mts +267 -0
- package/dist/index-k2YhCgsh.d.mts +57 -0
- package/dist/index.d.mts +22 -0
- package/dist/index.mjs +23 -0
- package/dist/key-BiGxGLhg.d.mts +37 -0
- package/dist/key-Dn-aX4x4.mjs +130 -0
- package/dist/key-Dn-aX4x4.mjs.map +1 -0
- package/dist/kind-BLl4cLOc.mjs +59 -0
- package/dist/kind-BLl4cLOc.mjs.map +1 -0
- package/dist/loaders-BNQszDAn.mjs +767 -0
- package/dist/loaders-BNQszDAn.mjs.map +1 -0
- package/dist/loaders.d.mts +3 -0
- package/dist/loaders.mjs +2 -0
- package/dist/memory-BCmhZPek.mjs +461 -0
- package/dist/memory-BCmhZPek.mjs.map +1 -0
- package/dist/message-C_Uj-Y-c.mjs +179 -0
- package/dist/message-C_Uj-Y-c.mjs.map +1 -0
- package/dist/message-DJC-Jz6T.d.mts +32 -0
- package/dist/nip17-BWq-Kfq5.d.mts +38 -0
- package/dist/nip19-D7905oIv.d.mts +63 -0
- package/dist/nip46-C_DoxiS_.d.mts +51 -0
- package/dist/nip59-DljW_sYh.d.mts +76 -0
- package/dist/nip65-Di5hZRuV.d.mts +21 -0
- package/dist/nip77-ByxlgoQO.d.mts +55 -0
- package/dist/nips/blossom.d.mts +93 -0
- package/dist/nips/blossom.mjs +325 -0
- package/dist/nips/blossom.mjs.map +1 -0
- package/dist/nips/nip04.d.mts +8 -0
- package/dist/nips/nip04.mjs +41 -0
- package/dist/nips/nip04.mjs.map +1 -0
- package/dist/nips/nip05.d.mts +89 -0
- package/dist/nips/nip05.mjs +147 -0
- package/dist/nips/nip05.mjs.map +1 -0
- package/dist/nips/nip10.d.mts +43 -0
- package/dist/nips/nip10.mjs +194 -0
- package/dist/nips/nip10.mjs.map +1 -0
- package/dist/nips/nip11.d.mts +43 -0
- package/dist/nips/nip11.mjs +99 -0
- package/dist/nips/nip11.mjs.map +1 -0
- package/dist/nips/nip13.d.mts +23 -0
- package/dist/nips/nip13.mjs +88 -0
- package/dist/nips/nip13.mjs.map +1 -0
- package/dist/nips/nip17.d.mts +2 -0
- package/dist/nips/nip17.mjs +138 -0
- package/dist/nips/nip17.mjs.map +1 -0
- package/dist/nips/nip19.d.mts +2 -0
- package/dist/nips/nip19.mjs +174 -0
- package/dist/nips/nip19.mjs.map +1 -0
- package/dist/nips/nip21.d.mts +17 -0
- package/dist/nips/nip21.mjs +29 -0
- package/dist/nips/nip21.mjs.map +1 -0
- package/dist/nips/nip27.d.mts +42 -0
- package/dist/nips/nip27.mjs +175 -0
- package/dist/nips/nip27.mjs.map +1 -0
- package/dist/nips/nip42.d.mts +12 -0
- package/dist/nips/nip42.mjs +22 -0
- package/dist/nips/nip42.mjs.map +1 -0
- package/dist/nips/nip44.d.mts +12 -0
- package/dist/nips/nip44.mjs +125 -0
- package/dist/nips/nip44.mjs.map +1 -0
- package/dist/nips/nip46.d.mts +2 -0
- package/dist/nips/nip46.mjs +132 -0
- package/dist/nips/nip46.mjs.map +1 -0
- package/dist/nips/nip49.d.mts +12 -0
- package/dist/nips/nip49.mjs +80 -0
- package/dist/nips/nip49.mjs.map +1 -0
- package/dist/nips/nip51.d.mts +88 -0
- package/dist/nips/nip51.mjs +246 -0
- package/dist/nips/nip51.mjs.map +1 -0
- package/dist/nips/nip57.d.mts +43 -0
- package/dist/nips/nip57.mjs +230 -0
- package/dist/nips/nip57.mjs.map +1 -0
- package/dist/nips/nip59.d.mts +2 -0
- package/dist/nips/nip59.mjs +171 -0
- package/dist/nips/nip59.mjs.map +1 -0
- package/dist/nips/nip65.d.mts +2 -0
- package/dist/nips/nip65.mjs +76 -0
- package/dist/nips/nip65.mjs.map +1 -0
- package/dist/nips/nip77.d.mts +2 -0
- package/dist/nips/nip77.mjs +421 -0
- package/dist/nips/nip77.mjs.map +1 -0
- package/dist/nips/nip96.d.mts +31 -0
- package/dist/nips/nip96.mjs +95 -0
- package/dist/nips/nip96.mjs.map +1 -0
- package/dist/nips/nip98.d.mts +28 -0
- package/dist/nips/nip98.mjs +91 -0
- package/dist/nips/nip98.mjs.map +1 -0
- package/dist/nostr_crypto_wasm_bg-hi5aXi5P.mjs +4 -0
- package/dist/nostr_crypto_wasm_bg.wasm +0 -0
- package/dist/pool-C_j1OmBB.mjs +1881 -0
- package/dist/pool-C_j1OmBB.mjs.map +1 -0
- package/dist/pool-DhRi7l-b.d.mts +297 -0
- package/dist/relay.d.mts +4 -0
- package/dist/relay.mjs +2 -0
- package/dist/signer-CZ6K6FUc.mjs +473 -0
- package/dist/signer-CZ6K6FUc.mjs.map +1 -0
- package/dist/signer.d.mts +3 -0
- package/dist/signer.mjs +2 -0
- package/dist/storage-DOy0iLRE.mjs +870 -0
- package/dist/storage-DOy0iLRE.mjs.map +1 -0
- package/dist/storage.d.mts +4 -0
- package/dist/storage.mjs +4 -0
- package/dist/types-BzyADqnw.d.mts +19 -0
- package/dist/types-Dn24TPFs.d.mts +43 -0
- package/dist/util-6tfLQ9JF.mjs +73 -0
- package/dist/util-6tfLQ9JF.mjs.map +1 -0
- package/dist/wasm.d.mts +19 -0
- package/dist/wasm.mjs +237 -0
- package/dist/wasm.mjs.map +1 -0
- package/package.json +204 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { l as utf8Encoder, r as bytesToHex } from "../util-6tfLQ9JF.mjs";
|
|
2
|
+
import { a as NostrError } from "../error-DZEs5OuZ.mjs";
|
|
3
|
+
import { c as serializeEvent } from "../event-CzoQEFMF.mjs";
|
|
4
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
5
|
+
//#region src/nips/nip13.ts
|
|
6
|
+
/**
|
|
7
|
+
* NIP-13: Proof of Work.
|
|
8
|
+
* Does not import signer, relay, or client. Does not sign.
|
|
9
|
+
*
|
|
10
|
+
* @see https://github.com/nostr-protocol/nips/blob/master/13.md
|
|
11
|
+
*/
|
|
12
|
+
var Nip13Error = class extends NostrError {
|
|
13
|
+
constructor(message, options) {
|
|
14
|
+
super(message, options);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
/** Leading zero bits of a hex event id or raw sha256 bytes. */
|
|
18
|
+
function getPow(idOrHash) {
|
|
19
|
+
if (typeof idOrHash === "string") {
|
|
20
|
+
let count = 0;
|
|
21
|
+
for (let i = 0; i < idOrHash.length; i += 8) {
|
|
22
|
+
const chunk = Number.parseInt(idOrHash.substring(i, i + 8), 16);
|
|
23
|
+
if (chunk === 0) count += 32;
|
|
24
|
+
else {
|
|
25
|
+
count += Math.clz32(chunk);
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return count;
|
|
30
|
+
}
|
|
31
|
+
let count = 0;
|
|
32
|
+
for (let i = 0; i < idOrHash.length; i++) {
|
|
33
|
+
const byte = idOrHash[i];
|
|
34
|
+
if (byte === 0) count += 8;
|
|
35
|
+
else {
|
|
36
|
+
count += Math.clz32(byte) - 24;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return count;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Returns a new unsigned event with nonce tag and computed id.
|
|
44
|
+
* Does not mutate input. Yields every `yieldEvery` hashes.
|
|
45
|
+
*/
|
|
46
|
+
async function minePow(unsigned, difficulty, opts) {
|
|
47
|
+
const signal = opts?.signal;
|
|
48
|
+
const yieldEvery = Math.max(1, opts?.yieldEvery ?? 1e3);
|
|
49
|
+
const nonce = [
|
|
50
|
+
"nonce",
|
|
51
|
+
"0",
|
|
52
|
+
String(difficulty)
|
|
53
|
+
];
|
|
54
|
+
const mined = {
|
|
55
|
+
kind: unsigned.kind,
|
|
56
|
+
tags: [...unsigned.tags, nonce],
|
|
57
|
+
content: unsigned.content,
|
|
58
|
+
created_at: unsigned.created_at,
|
|
59
|
+
pubkey: unsigned.pubkey
|
|
60
|
+
};
|
|
61
|
+
let count = 0;
|
|
62
|
+
let iterations = 0;
|
|
63
|
+
while (true) {
|
|
64
|
+
if (signal?.aborted) throw new Nip13Error("mining aborted", { cause: signal.reason });
|
|
65
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
66
|
+
if (now !== mined.created_at) {
|
|
67
|
+
count = 0;
|
|
68
|
+
mined.created_at = now;
|
|
69
|
+
}
|
|
70
|
+
nonce[1] = String(++count);
|
|
71
|
+
const hash = sha256(utf8Encoder.encode(serializeEvent(mined)));
|
|
72
|
+
if (getPow(hash) >= difficulty) return {
|
|
73
|
+
...mined,
|
|
74
|
+
id: bytesToHex(hash)
|
|
75
|
+
};
|
|
76
|
+
iterations++;
|
|
77
|
+
if (iterations >= yieldEvery) {
|
|
78
|
+
iterations = 0;
|
|
79
|
+
await new Promise((resolve) => {
|
|
80
|
+
setTimeout(resolve, 0);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { Nip13Error, getPow, minePow };
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=nip13.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nip13.mjs","names":[],"sources":["../../src/nips/nip13.ts"],"sourcesContent":["/**\n * NIP-13: Proof of Work.\n * Does not import signer, relay, or client. Does not sign.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/13.md\n */\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { NostrError } from \"../core/error.ts\";\nimport { serializeEvent, type UnsignedEvent } from \"../core/event.ts\";\nimport { bytesToHex, utf8Encoder } from \"../core/util.ts\";\n\nexport class Nip13Error extends NostrError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n }\n}\n\nexport type MinePowOptions = {\n /** Yield to the event loop after this many hashes. Default 1000. */\n yieldEvery?: number;\n signal?: AbortSignal;\n};\n\n/** Leading zero bits of a hex event id or raw sha256 bytes. */\nexport function getPow(idOrHash: string | Uint8Array): number {\n if (typeof idOrHash === \"string\") {\n let count = 0;\n for (let i = 0; i < idOrHash.length; i += 8) {\n const chunk = Number.parseInt(idOrHash.substring(i, i + 8), 16);\n if (chunk === 0) {\n count += 32;\n } else {\n count += Math.clz32(chunk);\n break;\n }\n }\n return count;\n }\n\n let count = 0;\n for (let i = 0; i < idOrHash.length; i++) {\n const byte = idOrHash[i]!;\n if (byte === 0) {\n count += 8;\n } else {\n count += Math.clz32(byte) - 24;\n break;\n }\n }\n return count;\n}\n\n/**\n * Returns a new unsigned event with nonce tag and computed id.\n * Does not mutate input. Yields every `yieldEvery` hashes.\n */\nexport async function minePow(\n unsigned: UnsignedEvent,\n difficulty: number,\n opts?: MinePowOptions,\n): Promise<UnsignedEvent & { id: string }> {\n const signal = opts?.signal;\n const yieldEvery = Math.max(1, opts?.yieldEvery ?? 1000);\n\n const nonce: [string, string, string] = [\"nonce\", \"0\", String(difficulty)];\n const mined = {\n kind: unsigned.kind,\n tags: [...unsigned.tags, nonce],\n content: unsigned.content,\n created_at: unsigned.created_at,\n pubkey: unsigned.pubkey,\n };\n\n let count = 0;\n let iterations = 0;\n\n while (true) {\n if (signal?.aborted) {\n throw new Nip13Error(\"mining aborted\", { cause: signal.reason });\n }\n\n const now = Math.floor(Date.now() / 1000);\n if (now !== mined.created_at) {\n count = 0;\n mined.created_at = now;\n }\n\n nonce[1] = String(++count);\n const hash = sha256(utf8Encoder.encode(serializeEvent(mined)));\n if (getPow(hash) >= difficulty) {\n return { ...mined, id: bytesToHex(hash) };\n }\n\n iterations++;\n if (iterations >= yieldEvery) {\n iterations = 0;\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 0);\n });\n }\n }\n}\n"],"mappings":";;;;;;;;;;;AAWA,IAAa,aAAb,cAAgC,WAAW;CACzC,YAAY,SAAiB,SAAwB;EACnD,MAAM,SAAS,OAAO;CACxB;AACF;;AASA,SAAgB,OAAO,UAAuC;CAC5D,IAAI,OAAO,aAAa,UAAU;EAChC,IAAI,QAAQ;EACZ,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GAAG;GAC3C,MAAM,QAAQ,OAAO,SAAS,SAAS,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE;GAC9D,IAAI,UAAU,GACZ,SAAS;QACJ;IACL,SAAS,KAAK,MAAM,KAAK;IACzB;GACF;EACF;EACA,OAAO;CACT;CAEA,IAAI,QAAQ;CACZ,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,OAAO,SAAS;EACtB,IAAI,SAAS,GACX,SAAS;OACJ;GACL,SAAS,KAAK,MAAM,IAAI,IAAI;GAC5B;EACF;CACF;CACA,OAAO;AACT;;;;;AAMA,eAAsB,QACpB,UACA,YACA,MACyC;CACzC,MAAM,SAAS,MAAM;CACrB,MAAM,aAAa,KAAK,IAAI,GAAG,MAAM,cAAc,GAAI;CAEvD,MAAM,QAAkC;EAAC;EAAS;EAAK,OAAO,UAAU;CAAC;CACzE,MAAM,QAAQ;EACZ,MAAM,SAAS;EACf,MAAM,CAAC,GAAG,SAAS,MAAM,KAAK;EAC9B,SAAS,SAAS;EAClB,YAAY,SAAS;EACrB,QAAQ,SAAS;CACnB;CAEA,IAAI,QAAQ;CACZ,IAAI,aAAa;CAEjB,OAAO,MAAM;EACX,IAAI,QAAQ,SACV,MAAM,IAAI,WAAW,kBAAkB,EAAE,OAAO,OAAO,OAAO,CAAC;EAGjE,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;EACxC,IAAI,QAAQ,MAAM,YAAY;GAC5B,QAAQ;GACR,MAAM,aAAa;EACrB;EAEA,MAAM,KAAK,OAAO,EAAE,KAAK;EACzB,MAAM,OAAO,OAAO,YAAY,OAAO,eAAe,KAAK,CAAC,CAAC;EAC7D,IAAI,OAAO,IAAI,KAAK,YAClB,OAAO;GAAE,GAAG;GAAO,IAAI,WAAW,IAAI;EAAE;EAG1C;EACA,IAAI,cAAc,YAAY;GAC5B,aAAa;GACb,MAAM,IAAI,SAAe,YAAY;IACnC,WAAW,SAAS,CAAC;GACvB,CAAC;EACH;CACF;AACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as buildChatMessageRumor, c as normalizeRecipients, d as wrapDirectMessage, i as ReplyTo, l as parseDmRelayList, n as Nip17Error, o as dmRelayListEventBuilder, r as Recipient, s as dmRelayListToTags, t as ChatMessageOptions, u as requireDmRelays } from "../nip17-BWq-Kfq5.mjs";
|
|
2
|
+
export { ChatMessageOptions, Nip17Error, Recipient, ReplyTo, buildChatMessageRumor, dmRelayListEventBuilder, dmRelayListToTags, normalizeRecipients, parseDmRelayList, requireDmRelays, wrapDirectMessage };
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { s as normalizeURL, t as assertHex32 } from "../util-6tfLQ9JF.mjs";
|
|
2
|
+
import { a as NostrError, n as EventValidationError } from "../error-DZEs5OuZ.mjs";
|
|
3
|
+
import { t as Kind } from "../kind-BLl4cLOc.mjs";
|
|
4
|
+
import { m as Tag } from "../event-CzoQEFMF.mjs";
|
|
5
|
+
import { t as EventBuilder } from "../builder-q0VsvnYE.mjs";
|
|
6
|
+
import { createGiftWrap, createRumor, createSeal } from "./nip59.mjs";
|
|
7
|
+
//#region src/nips/nip17.ts
|
|
8
|
+
var Nip17Error = class extends NostrError {
|
|
9
|
+
constructor(message, options) {
|
|
10
|
+
super(message, options);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
/** Parse kind:10050 DM relay list (`["relay", url]` tags). */
|
|
14
|
+
function parseDmRelayList(event) {
|
|
15
|
+
if (event.kind !== Kind.DirectMessageRelaysList) throw new EventValidationError(`expected kind ${Kind.DirectMessageRelaysList}, got ${event.kind}`);
|
|
16
|
+
const out = [];
|
|
17
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18
|
+
for (const tag of event.tags) {
|
|
19
|
+
if (tag[0] !== "relay" || !tag[1]) continue;
|
|
20
|
+
let url;
|
|
21
|
+
try {
|
|
22
|
+
url = normalizeURL(tag[1]);
|
|
23
|
+
} catch {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (seen.has(url)) continue;
|
|
27
|
+
seen.add(url);
|
|
28
|
+
out.push(url);
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
/** Encode DM relay URLs as NIP-17 `relay` tags. */
|
|
33
|
+
function dmRelayListToTags(relays) {
|
|
34
|
+
const tags = [];
|
|
35
|
+
const seen = /* @__PURE__ */ new Set();
|
|
36
|
+
for (const raw of relays) {
|
|
37
|
+
let url;
|
|
38
|
+
try {
|
|
39
|
+
url = normalizeURL(raw);
|
|
40
|
+
} catch {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (seen.has(url)) continue;
|
|
44
|
+
seen.add(url);
|
|
45
|
+
tags.push(["relay", url]);
|
|
46
|
+
}
|
|
47
|
+
return tags;
|
|
48
|
+
}
|
|
49
|
+
/** Build an unsigned kind:10050 EventBuilder. */
|
|
50
|
+
function dmRelayListEventBuilder(relays) {
|
|
51
|
+
return new EventBuilder(Kind.DirectMessageRelaysList, "").tags(dmRelayListToTags(relays));
|
|
52
|
+
}
|
|
53
|
+
function asRecipientList(input) {
|
|
54
|
+
if (typeof input === "string") return [input];
|
|
55
|
+
if (Array.isArray(input)) return input;
|
|
56
|
+
return [input];
|
|
57
|
+
}
|
|
58
|
+
/** Accept a hex pubkey, a Recipient, or a readonly array of either. Dedup by pubkey. */
|
|
59
|
+
function normalizeRecipients(input) {
|
|
60
|
+
const out = [];
|
|
61
|
+
const seen = /* @__PURE__ */ new Set();
|
|
62
|
+
for (const item of asRecipientList(input)) {
|
|
63
|
+
const rec = typeof item === "string" ? { pubkey: item } : item;
|
|
64
|
+
const pubkey = assertHex32(rec.pubkey, "public key");
|
|
65
|
+
if (seen.has(pubkey)) continue;
|
|
66
|
+
seen.add(pubkey);
|
|
67
|
+
out.push({
|
|
68
|
+
pubkey,
|
|
69
|
+
relayHint: rec.relayHint
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
function buildChatMessageRumor(senderPubkey, recipients, content, opts) {
|
|
75
|
+
if (recipients.length === 0) throw new Nip17Error("recipients must not be empty");
|
|
76
|
+
const tags = [];
|
|
77
|
+
for (const recipient of recipients) {
|
|
78
|
+
const pk = assertHex32(recipient.pubkey, "public key");
|
|
79
|
+
tags.push(Tag.p(pk, recipient.relayHint));
|
|
80
|
+
}
|
|
81
|
+
if (opts?.replyTo) tags.push(Tag.e(assertHex32(opts.replyTo.id, "event id"), opts.replyTo.relayHint ?? ""));
|
|
82
|
+
if (opts?.subject !== void 0) tags.push(["subject", opts.subject]);
|
|
83
|
+
return createRumor(senderPubkey, {
|
|
84
|
+
kind: Kind.PrivateDirectMessage,
|
|
85
|
+
content,
|
|
86
|
+
tags,
|
|
87
|
+
created_at: opts?.created_at
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function wrapTargets(sender, recipients) {
|
|
91
|
+
const senderPk = sender.toLowerCase();
|
|
92
|
+
const out = [{
|
|
93
|
+
pubkey: senderPk,
|
|
94
|
+
relayHint: recipients.find((r) => r.pubkey.toLowerCase() === senderPk)?.relayHint
|
|
95
|
+
}];
|
|
96
|
+
const seen = /* @__PURE__ */ new Set([senderPk]);
|
|
97
|
+
for (const recipient of recipients) {
|
|
98
|
+
const pk = recipient.pubkey.toLowerCase();
|
|
99
|
+
if (seen.has(pk)) continue;
|
|
100
|
+
seen.add(pk);
|
|
101
|
+
out.push({
|
|
102
|
+
pubkey: pk,
|
|
103
|
+
relayHint: recipient.relayHint
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
async function wrapDirectMessage(crypto, recipients, rumor, opts) {
|
|
109
|
+
if (recipients.length === 0) throw new Nip17Error("recipients must not be empty");
|
|
110
|
+
const targets = wrapTargets(rumor.pubkey, recipients);
|
|
111
|
+
const out = [];
|
|
112
|
+
const timeOpts = opts ? {
|
|
113
|
+
now: opts.now,
|
|
114
|
+
randomInt: opts.randomInt,
|
|
115
|
+
timestamps: opts.timestamps,
|
|
116
|
+
randomize: opts.randomize
|
|
117
|
+
} : void 0;
|
|
118
|
+
for (const target of targets) {
|
|
119
|
+
const seal = await createSeal(crypto, target.pubkey, rumor, timeOpts);
|
|
120
|
+
const wrap = createGiftWrap(seal, target.pubkey, {
|
|
121
|
+
...timeOpts,
|
|
122
|
+
relayHint: target.relayHint
|
|
123
|
+
});
|
|
124
|
+
out.push({
|
|
125
|
+
recipient: target.pubkey,
|
|
126
|
+
wrap
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
function requireDmRelays(pubkey, relays) {
|
|
132
|
+
if (relays.length === 0) throw new Nip17Error(`pubkey ${pubkey} is not ready to receive DMs (no kind 10050)`);
|
|
133
|
+
return [...relays];
|
|
134
|
+
}
|
|
135
|
+
//#endregion
|
|
136
|
+
export { Nip17Error, buildChatMessageRumor, dmRelayListEventBuilder, dmRelayListToTags, normalizeRecipients, parseDmRelayList, requireDmRelays, wrapDirectMessage };
|
|
137
|
+
|
|
138
|
+
//# sourceMappingURL=nip17.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nip17.mjs","names":["TagBuilder"],"sources":["../../src/nips/nip17.ts"],"sourcesContent":["/**\n * NIP-17: Private Direct Messages.\n * Kind 10050 advertises where gift-wraps should be delivered.\n * Kind 14 rumor construction and per-recipient wrap live here.\n * Envelope primitives live in nip59.ts.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/17.md\n */\nimport type { Event } from \"../core/event.ts\";\nimport { EventValidationError, NostrError } from \"../core/error.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport type { Tag } from \"../core/tag.ts\";\nimport { Tag as TagBuilder } from \"../core/tag.ts\";\nimport { EventBuilder } from \"../core/builder.ts\";\nimport { assertHex32, normalizeURL } from \"../core/util.ts\";\nimport {\n createGiftWrap,\n createRumor,\n createSeal,\n type Nip59Crypto,\n type Rumor,\n type WrapOptions,\n} from \"./nip59.ts\";\n\nexport class Nip17Error extends NostrError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n }\n}\n\nexport type Recipient = {\n readonly pubkey: string;\n readonly relayHint?: string;\n};\n\nexport type ReplyTo = {\n readonly id: string;\n readonly relayHint?: string;\n};\n\nexport type ChatMessageOptions = {\n readonly created_at?: number;\n readonly subject?: string;\n readonly replyTo?: ReplyTo;\n};\n\n/** Parse kind:10050 DM relay list (`[\"relay\", url]` tags). */\nexport function parseDmRelayList(event: Pick<Event, \"kind\" | \"tags\">): string[] {\n if (event.kind !== Kind.DirectMessageRelaysList) {\n throw new EventValidationError(\n `expected kind ${Kind.DirectMessageRelaysList}, got ${event.kind}`,\n );\n }\n const out: string[] = [];\n const seen = new Set<string>();\n for (const tag of event.tags) {\n if (tag[0] !== \"relay\" || !tag[1]) continue;\n let url: string;\n try {\n url = normalizeURL(tag[1]);\n } catch {\n continue;\n }\n if (seen.has(url)) continue;\n seen.add(url);\n out.push(url);\n }\n return out;\n}\n\n/** Encode DM relay URLs as NIP-17 `relay` tags. */\nexport function dmRelayListToTags(relays: readonly string[]): Tag[] {\n const tags: Tag[] = [];\n const seen = new Set<string>();\n for (const raw of relays) {\n let url: string;\n try {\n url = normalizeURL(raw);\n } catch {\n continue;\n }\n if (seen.has(url)) continue;\n seen.add(url);\n tags.push([\"relay\", url]);\n }\n return tags;\n}\n\n/** Build an unsigned kind:10050 EventBuilder. */\nexport function dmRelayListEventBuilder(relays: readonly string[]): EventBuilder {\n return new EventBuilder(Kind.DirectMessageRelaysList, \"\").tags(dmRelayListToTags(relays));\n}\n\nfunction asRecipientList(\n input: string | Recipient | readonly (string | Recipient)[],\n): readonly (string | Recipient)[] {\n if (typeof input === \"string\") return [input];\n if (Array.isArray(input)) return input as readonly (string | Recipient)[];\n return [input as Recipient];\n}\n\n/** Accept a hex pubkey, a Recipient, or a readonly array of either. Dedup by pubkey. */\nexport function normalizeRecipients(\n input: string | Recipient | readonly (string | Recipient)[],\n): Recipient[] {\n const out: Recipient[] = [];\n const seen = new Set<string>();\n for (const item of asRecipientList(input)) {\n const rec: Recipient = typeof item === \"string\" ? { pubkey: item } : item;\n const pubkey = assertHex32(rec.pubkey, \"public key\");\n if (seen.has(pubkey)) continue;\n seen.add(pubkey);\n out.push({ pubkey, relayHint: rec.relayHint });\n }\n return out;\n}\n\nexport function buildChatMessageRumor(\n senderPubkey: string,\n recipients: readonly Recipient[],\n content: string,\n opts?: ChatMessageOptions,\n): Rumor {\n if (recipients.length === 0) {\n throw new Nip17Error(\"recipients must not be empty\");\n }\n const tags: Tag[] = [];\n for (const recipient of recipients) {\n const pk = assertHex32(recipient.pubkey, \"public key\");\n tags.push(TagBuilder.p(pk, recipient.relayHint));\n }\n if (opts?.replyTo) {\n // Kind 14 reply e-tag is unmarked: [\"e\", <id>, <relay-url>]\n tags.push(TagBuilder.e(assertHex32(opts.replyTo.id, \"event id\"), opts.replyTo.relayHint ?? \"\"));\n }\n if (opts?.subject !== undefined) {\n tags.push([\"subject\", opts.subject]);\n }\n return createRumor(senderPubkey, {\n kind: Kind.PrivateDirectMessage,\n content,\n tags,\n created_at: opts?.created_at,\n });\n}\n\nfunction wrapTargets(sender: string, recipients: readonly Recipient[]): Recipient[] {\n const senderPk = sender.toLowerCase();\n const self = recipients.find((r) => r.pubkey.toLowerCase() === senderPk);\n const out: Recipient[] = [{ pubkey: senderPk, relayHint: self?.relayHint }];\n const seen = new Set<string>([senderPk]);\n for (const recipient of recipients) {\n const pk = recipient.pubkey.toLowerCase();\n if (seen.has(pk)) continue;\n seen.add(pk);\n out.push({ pubkey: pk, relayHint: recipient.relayHint });\n }\n return out;\n}\n\nexport async function wrapDirectMessage(\n crypto: Nip59Crypto,\n recipients: readonly Recipient[],\n rumor: Rumor,\n opts?: Pick<WrapOptions, \"now\" | \"randomInt\" | \"timestamps\" | \"randomize\">,\n): Promise<ReadonlyArray<{ recipient: string; wrap: Event }>> {\n if (recipients.length === 0) {\n throw new Nip17Error(\"recipients must not be empty\");\n }\n const targets = wrapTargets(rumor.pubkey, recipients);\n const out: Array<{ recipient: string; wrap: Event }> = [];\n const timeOpts = opts\n ? {\n now: opts.now,\n randomInt: opts.randomInt,\n timestamps: opts.timestamps,\n randomize: opts.randomize,\n }\n : undefined;\n for (const target of targets) {\n const seal = await createSeal(crypto, target.pubkey, rumor, timeOpts);\n const wrap = createGiftWrap(seal, target.pubkey, {\n ...timeOpts,\n relayHint: target.relayHint,\n });\n out.push({ recipient: target.pubkey, wrap });\n }\n return out;\n}\n\nexport function requireDmRelays(pubkey: string, relays: readonly string[]): string[] {\n if (relays.length === 0) {\n throw new Nip17Error(`pubkey ${pubkey} is not ready to receive DMs (no kind 10050)`);\n }\n return [...relays];\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { _ as neventEncode, a as NAddr, b as npubEncode, c as NPub, d as Note, f as ProfilePointer, g as naddrEncode, h as encodeBytes, i as EventPointer, l as NSec, m as decodeNostrURI, n as Bech32MaxSize, o as NEvent, p as decode, r as DecodedResult, s as NProfile, t as AddressPointer, u as Nip19Error, v as noteEncode, x as nsecEncode, y as nprofileEncode } from "../nip19-D7905oIv.mjs";
|
|
2
|
+
export { AddressPointer, Bech32MaxSize, DecodedResult, EventPointer, NAddr, NEvent, NProfile, NPub, NSec, Nip19Error, Note, ProfilePointer, decode, decodeNostrURI, encodeBytes, naddrEncode, neventEncode, noteEncode, nprofileEncode, npubEncode, nsecEncode };
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { c as utf8Decoder, i as hexToBytes, l as utf8Encoder, r as bytesToHex } from "../util-6tfLQ9JF.mjs";
|
|
2
|
+
import { a as NostrError } from "../error-DZEs5OuZ.mjs";
|
|
3
|
+
import { bech32 } from "@scure/base";
|
|
4
|
+
//#region src/nips/nip19.ts
|
|
5
|
+
const Bech32MaxSize = 5e3;
|
|
6
|
+
var Nip19Error = class extends NostrError {
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
function integerToUint8Array(number) {
|
|
12
|
+
const out = /* @__PURE__ */ new Uint8Array(4);
|
|
13
|
+
out[0] = number >> 24 & 255;
|
|
14
|
+
out[1] = number >> 16 & 255;
|
|
15
|
+
out[2] = number >> 8 & 255;
|
|
16
|
+
out[3] = number & 255;
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
function parseTLV(data) {
|
|
20
|
+
const result = {};
|
|
21
|
+
let rest = data;
|
|
22
|
+
while (rest.length > 0) {
|
|
23
|
+
if (rest.length < 2) throw new Nip19Error("not enough data to read TLV");
|
|
24
|
+
const t = rest[0];
|
|
25
|
+
const l = rest[1];
|
|
26
|
+
const v = rest.slice(2, 2 + l);
|
|
27
|
+
rest = rest.slice(2 + l);
|
|
28
|
+
if (v.length < l) throw new Nip19Error(`not enough data to read on TLV ${t}`);
|
|
29
|
+
result[t] = result[t] || [];
|
|
30
|
+
result[t].push(v);
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
function encodeTLV(tlv) {
|
|
35
|
+
const entries = [];
|
|
36
|
+
for (const [t, vs] of Object.entries(tlv).reverse()) for (const v of vs) {
|
|
37
|
+
const entry = new Uint8Array(v.length + 2);
|
|
38
|
+
entry[0] = Number.parseInt(t, 10);
|
|
39
|
+
entry[1] = v.length;
|
|
40
|
+
entry.set(v, 2);
|
|
41
|
+
entries.push(entry);
|
|
42
|
+
}
|
|
43
|
+
let total = 0;
|
|
44
|
+
for (const e of entries) total += e.length;
|
|
45
|
+
const out = new Uint8Array(total);
|
|
46
|
+
let offset = 0;
|
|
47
|
+
for (const e of entries) {
|
|
48
|
+
out.set(e, offset);
|
|
49
|
+
offset += e.length;
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
function encodeBech32(prefix, data) {
|
|
54
|
+
const words = bech32.toWords(data);
|
|
55
|
+
return bech32.encode(prefix, words, Bech32MaxSize);
|
|
56
|
+
}
|
|
57
|
+
function encodeBytes(prefix, bytes) {
|
|
58
|
+
return encodeBech32(prefix, bytes);
|
|
59
|
+
}
|
|
60
|
+
function nsecEncode(key) {
|
|
61
|
+
return encodeBytes("nsec", key);
|
|
62
|
+
}
|
|
63
|
+
function npubEncode(hex) {
|
|
64
|
+
return encodeBytes("npub", hexToBytes(hex));
|
|
65
|
+
}
|
|
66
|
+
function noteEncode(hex) {
|
|
67
|
+
return encodeBytes("note", hexToBytes(hex));
|
|
68
|
+
}
|
|
69
|
+
function nprofileEncode(profile) {
|
|
70
|
+
return encodeBech32("nprofile", encodeTLV({
|
|
71
|
+
0: [hexToBytes(profile.pubkey)],
|
|
72
|
+
1: (profile.relays || []).map((url) => utf8Encoder.encode(url))
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
function neventEncode(event) {
|
|
76
|
+
const kindArray = event.kind !== void 0 ? integerToUint8Array(event.kind) : void 0;
|
|
77
|
+
return encodeBech32("nevent", encodeTLV({
|
|
78
|
+
0: [hexToBytes(event.id)],
|
|
79
|
+
1: (event.relays || []).map((url) => utf8Encoder.encode(url)),
|
|
80
|
+
2: event.author ? [hexToBytes(event.author)] : [],
|
|
81
|
+
3: kindArray ? [kindArray] : []
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
function naddrEncode(addr) {
|
|
85
|
+
const kind = integerToUint8Array(addr.kind);
|
|
86
|
+
return encodeBech32("naddr", encodeTLV({
|
|
87
|
+
0: [utf8Encoder.encode(addr.identifier)],
|
|
88
|
+
1: (addr.relays || []).map((url) => utf8Encoder.encode(url)),
|
|
89
|
+
2: [hexToBytes(addr.pubkey)],
|
|
90
|
+
3: [kind]
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
function decode(code) {
|
|
94
|
+
let { prefix, words } = bech32.decode(code, Bech32MaxSize);
|
|
95
|
+
const data = new Uint8Array(bech32.fromWords(words));
|
|
96
|
+
switch (prefix) {
|
|
97
|
+
case "nprofile": {
|
|
98
|
+
const tlv = parseTLV(data);
|
|
99
|
+
if (!tlv[0]?.[0]) throw new Nip19Error("missing TLV 0 for nprofile");
|
|
100
|
+
if (tlv[0][0].length !== 32) throw new Nip19Error("TLV 0 should be 32 bytes");
|
|
101
|
+
return {
|
|
102
|
+
type: "nprofile",
|
|
103
|
+
data: {
|
|
104
|
+
pubkey: bytesToHex(tlv[0][0]),
|
|
105
|
+
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
case "nevent": {
|
|
110
|
+
const tlv = parseTLV(data);
|
|
111
|
+
if (!tlv[0]?.[0]) throw new Nip19Error("missing TLV 0 for nevent");
|
|
112
|
+
if (tlv[0][0].length !== 32) throw new Nip19Error("TLV 0 should be 32 bytes");
|
|
113
|
+
if (tlv[2]?.[0] && tlv[2][0].length !== 32) throw new Nip19Error("TLV 2 should be 32 bytes");
|
|
114
|
+
if (tlv[3]?.[0] && tlv[3][0].length !== 4) throw new Nip19Error("TLV 3 should be 4 bytes");
|
|
115
|
+
return {
|
|
116
|
+
type: "nevent",
|
|
117
|
+
data: {
|
|
118
|
+
id: bytesToHex(tlv[0][0]),
|
|
119
|
+
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
|
|
120
|
+
author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : void 0,
|
|
121
|
+
kind: tlv[3]?.[0] ? Number.parseInt(bytesToHex(tlv[3][0]), 16) : void 0
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
case "naddr": {
|
|
126
|
+
const tlv = parseTLV(data);
|
|
127
|
+
if (!tlv[0]?.[0]) throw new Nip19Error("missing TLV 0 for naddr");
|
|
128
|
+
if (!tlv[2]?.[0]) throw new Nip19Error("missing TLV 2 for naddr");
|
|
129
|
+
if (tlv[2][0].length !== 32) throw new Nip19Error("TLV 2 should be 32 bytes");
|
|
130
|
+
if (!tlv[3]?.[0]) throw new Nip19Error("missing TLV 3 for naddr");
|
|
131
|
+
if (tlv[3][0].length !== 4) throw new Nip19Error("TLV 3 should be 4 bytes");
|
|
132
|
+
return {
|
|
133
|
+
type: "naddr",
|
|
134
|
+
data: {
|
|
135
|
+
identifier: utf8Decoder.decode(tlv[0][0]),
|
|
136
|
+
pubkey: bytesToHex(tlv[2][0]),
|
|
137
|
+
kind: Number.parseInt(bytesToHex(tlv[3][0]), 16),
|
|
138
|
+
relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
case "nsec":
|
|
143
|
+
if (data.length !== 32) throw new Nip19Error("nsec must be 32 bytes");
|
|
144
|
+
return {
|
|
145
|
+
type: "nsec",
|
|
146
|
+
data
|
|
147
|
+
};
|
|
148
|
+
case "npub":
|
|
149
|
+
case "note":
|
|
150
|
+
if (data.length !== 32) throw new Nip19Error(`${prefix} must be 32 bytes`);
|
|
151
|
+
return {
|
|
152
|
+
type: prefix,
|
|
153
|
+
data: bytesToHex(data)
|
|
154
|
+
};
|
|
155
|
+
default: throw new Nip19Error(`unknown prefix ${prefix}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/** Decode `nostr:` URI or bare bech32; returns invalid sentinel instead of throwing. */
|
|
159
|
+
function decodeNostrURI(nip19code) {
|
|
160
|
+
try {
|
|
161
|
+
let code = nip19code;
|
|
162
|
+
if (code.startsWith("nostr:")) code = code.slice(6);
|
|
163
|
+
return decode(code);
|
|
164
|
+
} catch {
|
|
165
|
+
return {
|
|
166
|
+
type: "invalid",
|
|
167
|
+
data: null
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
//#endregion
|
|
172
|
+
export { Bech32MaxSize, Nip19Error, decode, decodeNostrURI, encodeBytes, naddrEncode, neventEncode, noteEncode, nprofileEncode, npubEncode, nsecEncode };
|
|
173
|
+
|
|
174
|
+
//# sourceMappingURL=nip19.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nip19.mjs","names":[],"sources":["../../src/nips/nip19.ts"],"sourcesContent":["import { bech32 } from \"@scure/base\";\nimport { NostrError } from \"../core/error.ts\";\nimport { bytesToHex, hexToBytes, utf8Decoder, utf8Encoder } from \"../core/util.ts\";\n\nexport type NProfile = `nprofile1${string}`;\nexport type NEvent = `nevent1${string}`;\nexport type NAddr = `naddr1${string}`;\nexport type NSec = `nsec1${string}`;\nexport type NPub = `npub1${string}`;\nexport type Note = `note1${string}`;\n\nexport const Bech32MaxSize = 5000;\n\nexport type ProfilePointer = {\n pubkey: string;\n relays?: string[];\n};\n\nexport type EventPointer = {\n id: string;\n relays?: string[];\n author?: string;\n kind?: number;\n};\n\nexport type AddressPointer = {\n identifier: string;\n pubkey: string;\n kind: number;\n relays?: string[];\n};\n\nexport type DecodedResult =\n | { type: \"nprofile\"; data: ProfilePointer }\n | { type: \"nevent\"; data: EventPointer }\n | { type: \"naddr\"; data: AddressPointer }\n | { type: \"nsec\"; data: Uint8Array }\n | { type: \"npub\"; data: string }\n | { type: \"note\"; data: string };\n\nexport class Nip19Error extends NostrError {\n constructor(message: string) {\n super(message);\n }\n}\n\ntype TLV = { [t: number]: Uint8Array[] };\n\nfunction integerToUint8Array(number: number): Uint8Array {\n const out = new Uint8Array(4);\n out[0] = (number >> 24) & 0xff;\n out[1] = (number >> 16) & 0xff;\n out[2] = (number >> 8) & 0xff;\n out[3] = number & 0xff;\n return out;\n}\n\nfunction parseTLV(data: Uint8Array): TLV {\n const result: TLV = {};\n let rest = data;\n while (rest.length > 0) {\n if (rest.length < 2) throw new Nip19Error(\"not enough data to read TLV\");\n const t = rest[0]!;\n const l = rest[1]!;\n const v = rest.slice(2, 2 + l);\n rest = rest.slice(2 + l);\n if (v.length < l) throw new Nip19Error(`not enough data to read on TLV ${t}`);\n result[t] = result[t] || [];\n result[t]!.push(v);\n }\n return result;\n}\n\nfunction encodeTLV(tlv: TLV): Uint8Array {\n const entries: Uint8Array[] = [];\n for (const [t, vs] of Object.entries(tlv).reverse()) {\n for (const v of vs) {\n const entry = new Uint8Array(v.length + 2);\n entry[0] = Number.parseInt(t, 10);\n entry[1] = v.length;\n entry.set(v, 2);\n entries.push(entry);\n }\n }\n let total = 0;\n for (const e of entries) total += e.length;\n const out = new Uint8Array(total);\n let offset = 0;\n for (const e of entries) {\n out.set(e, offset);\n offset += e.length;\n }\n return out;\n}\n\nfunction encodeBech32<Prefix extends string>(\n prefix: Prefix,\n data: Uint8Array,\n): `${Prefix}1${string}` {\n const words = bech32.toWords(data);\n return bech32.encode(prefix, words, Bech32MaxSize) as `${Prefix}1${string}`;\n}\n\nexport function encodeBytes<Prefix extends string>(\n prefix: Prefix,\n bytes: Uint8Array,\n): `${Prefix}1${string}` {\n return encodeBech32(prefix, bytes);\n}\n\nexport function nsecEncode(key: Uint8Array): NSec {\n return encodeBytes(\"nsec\", key);\n}\n\nexport function npubEncode(hex: string): NPub {\n return encodeBytes(\"npub\", hexToBytes(hex));\n}\n\nexport function noteEncode(hex: string): Note {\n return encodeBytes(\"note\", hexToBytes(hex));\n}\n\nexport function nprofileEncode(profile: ProfilePointer): NProfile {\n const data = encodeTLV({\n 0: [hexToBytes(profile.pubkey)],\n 1: (profile.relays || []).map((url) => utf8Encoder.encode(url)),\n });\n return encodeBech32(\"nprofile\", data);\n}\n\nexport function neventEncode(event: EventPointer): NEvent {\n const kindArray = event.kind !== undefined ? integerToUint8Array(event.kind) : undefined;\n const data = encodeTLV({\n 0: [hexToBytes(event.id)],\n 1: (event.relays || []).map((url) => utf8Encoder.encode(url)),\n 2: event.author ? [hexToBytes(event.author)] : [],\n 3: kindArray ? [kindArray] : [],\n });\n return encodeBech32(\"nevent\", data);\n}\n\nexport function naddrEncode(addr: AddressPointer): NAddr {\n const kind = integerToUint8Array(addr.kind);\n const data = encodeTLV({\n 0: [utf8Encoder.encode(addr.identifier)],\n 1: (addr.relays || []).map((url) => utf8Encoder.encode(url)),\n 2: [hexToBytes(addr.pubkey)],\n 3: [kind],\n });\n return encodeBech32(\"naddr\", data);\n}\n\nexport function decode(code: string): DecodedResult {\n let { prefix, words } = bech32.decode(code as `${string}1${string}`, Bech32MaxSize);\n const data = new Uint8Array(bech32.fromWords(words));\n\n switch (prefix) {\n case \"nprofile\": {\n const tlv = parseTLV(data);\n if (!tlv[0]?.[0]) throw new Nip19Error(\"missing TLV 0 for nprofile\");\n if (tlv[0][0].length !== 32) throw new Nip19Error(\"TLV 0 should be 32 bytes\");\n return {\n type: \"nprofile\",\n data: {\n pubkey: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],\n },\n };\n }\n case \"nevent\": {\n const tlv = parseTLV(data);\n if (!tlv[0]?.[0]) throw new Nip19Error(\"missing TLV 0 for nevent\");\n if (tlv[0][0].length !== 32) throw new Nip19Error(\"TLV 0 should be 32 bytes\");\n if (tlv[2]?.[0] && tlv[2][0].length !== 32) throw new Nip19Error(\"TLV 2 should be 32 bytes\");\n if (tlv[3]?.[0] && tlv[3][0].length !== 4) throw new Nip19Error(\"TLV 3 should be 4 bytes\");\n return {\n type: \"nevent\",\n data: {\n id: bytesToHex(tlv[0][0]),\n relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],\n author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined,\n kind: tlv[3]?.[0] ? Number.parseInt(bytesToHex(tlv[3][0]), 16) : undefined,\n },\n };\n }\n case \"naddr\": {\n const tlv = parseTLV(data);\n if (!tlv[0]?.[0]) throw new Nip19Error(\"missing TLV 0 for naddr\");\n if (!tlv[2]?.[0]) throw new Nip19Error(\"missing TLV 2 for naddr\");\n if (tlv[2][0].length !== 32) throw new Nip19Error(\"TLV 2 should be 32 bytes\");\n if (!tlv[3]?.[0]) throw new Nip19Error(\"missing TLV 3 for naddr\");\n if (tlv[3][0].length !== 4) throw new Nip19Error(\"TLV 3 should be 4 bytes\");\n return {\n type: \"naddr\",\n data: {\n identifier: utf8Decoder.decode(tlv[0][0]),\n pubkey: bytesToHex(tlv[2][0]),\n kind: Number.parseInt(bytesToHex(tlv[3][0]), 16),\n relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],\n },\n };\n }\n case \"nsec\":\n if (data.length !== 32) throw new Nip19Error(\"nsec must be 32 bytes\");\n return { type: \"nsec\", data };\n case \"npub\":\n case \"note\":\n if (data.length !== 32) throw new Nip19Error(`${prefix} must be 32 bytes`);\n return { type: prefix, data: bytesToHex(data) };\n default:\n throw new Nip19Error(`unknown prefix ${prefix}`);\n }\n}\n\n/** Decode `nostr:` URI or bare bech32; returns invalid sentinel instead of throwing. */\nexport function decodeNostrURI(nip19code: string): DecodedResult | { type: \"invalid\"; data: null } {\n try {\n let code = nip19code;\n if (code.startsWith(\"nostr:\")) code = code.slice(6);\n return decode(code);\n } catch {\n return { type: \"invalid\", data: null };\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { r as DecodedResult } from "../nip19-D7905oIv.mjs";
|
|
2
|
+
//#region src/nips/nip21.d.ts
|
|
3
|
+
/** Matches `nostr:<bech32>` (not anchored). */
|
|
4
|
+
declare const NOSTR_URI_REGEX: RegExp;
|
|
5
|
+
type NostrURI = {
|
|
6
|
+
/** Full URI including `nostr:` */
|
|
7
|
+
uri: `nostr:${string}`;
|
|
8
|
+
/** Bech32 entity without prefix */
|
|
9
|
+
value: string;
|
|
10
|
+
decoded: DecodedResult;
|
|
11
|
+
};
|
|
12
|
+
declare function isNostrURI(value: unknown): value is `nostr:${string}`;
|
|
13
|
+
/** Parse and decode a full `nostr:…` URI. NIP-21 excludes `nsec`. */
|
|
14
|
+
declare function parseNostrURI(uri: string): NostrURI;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { NOSTR_URI_REGEX, NostrURI, isNostrURI, parseNostrURI };
|
|
17
|
+
//# sourceMappingURL=nip21.d.mts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { a as NostrError } from "../error-DZEs5OuZ.mjs";
|
|
2
|
+
import { decode } from "./nip19.mjs";
|
|
3
|
+
//#region src/nips/nip21.ts
|
|
4
|
+
/**
|
|
5
|
+
* NIP-21: `nostr:` URI scheme
|
|
6
|
+
* @see https://github.com/nostr-protocol/nips/blob/master/21.md
|
|
7
|
+
*/
|
|
8
|
+
/** Matches `nostr:<bech32>` (not anchored). */
|
|
9
|
+
const NOSTR_URI_REGEX = /nostr:([a-z0-9]+1[02-9ac-hj-np-z]+)/i;
|
|
10
|
+
function isNostrURI(value) {
|
|
11
|
+
if (typeof value !== "string") return false;
|
|
12
|
+
if (!/^nostr:[a-z0-9]+1[02-9ac-hj-np-z]+$/i.test(value)) return false;
|
|
13
|
+
return !value.toLowerCase().startsWith("nostr:nsec1");
|
|
14
|
+
}
|
|
15
|
+
/** Parse and decode a full `nostr:…` URI. NIP-21 excludes `nsec`. */
|
|
16
|
+
function parseNostrURI(uri) {
|
|
17
|
+
const match = uri.match(/^nostr:([a-z0-9]+1[02-9ac-hj-np-z]+)$/i);
|
|
18
|
+
if (!match?.[1]) throw new NostrError(`invalid Nostr URI: ${uri}`);
|
|
19
|
+
if (match[1].toLowerCase().startsWith("nsec1")) throw new NostrError("NIP-21 identifiers exclude nsec");
|
|
20
|
+
return {
|
|
21
|
+
uri: `nostr:${match[1]}`,
|
|
22
|
+
value: match[1],
|
|
23
|
+
decoded: decode(match[1])
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { NOSTR_URI_REGEX, isNostrURI, parseNostrURI };
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=nip21.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nip21.mjs","names":[],"sources":["../../src/nips/nip21.ts"],"sourcesContent":["/**\n * NIP-21: `nostr:` URI scheme\n * @see https://github.com/nostr-protocol/nips/blob/master/21.md\n */\nimport { NostrError } from \"../core/error.ts\";\nimport { decode, type DecodedResult } from \"./nip19.ts\";\n\n/** Matches `nostr:<bech32>` (not anchored). */\nexport const NOSTR_URI_REGEX = /nostr:([a-z0-9]+1[02-9ac-hj-np-z]+)/i;\n\nexport type NostrURI = {\n /** Full URI including `nostr:` */\n uri: `nostr:${string}`;\n /** Bech32 entity without prefix */\n value: string;\n decoded: DecodedResult;\n};\n\nexport function isNostrURI(value: unknown): value is `nostr:${string}` {\n if (typeof value !== \"string\") return false;\n if (!/^nostr:[a-z0-9]+1[02-9ac-hj-np-z]+$/i.test(value)) return false;\n return !value.toLowerCase().startsWith(\"nostr:nsec1\");\n}\n\n/** Parse and decode a full `nostr:…` URI. NIP-21 excludes `nsec`. */\nexport function parseNostrURI(uri: string): NostrURI {\n const match = uri.match(/^nostr:([a-z0-9]+1[02-9ac-hj-np-z]+)$/i);\n if (!match?.[1]) throw new NostrError(`invalid Nostr URI: ${uri}`);\n if (match[1].toLowerCase().startsWith(\"nsec1\")) {\n throw new NostrError(\"NIP-21 identifiers exclude nsec\");\n }\n return {\n uri: `nostr:${match[1]}` as `nostr:${string}`,\n value: match[1],\n decoded: decode(match[1]),\n };\n}\n"],"mappings":";;;;;;;;AAQA,MAAa,kBAAkB;AAU/B,SAAgB,WAAW,OAA4C;CACrE,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI,CAAC,uCAAuC,KAAK,KAAK,GAAG,OAAO;CAChE,OAAO,CAAC,MAAM,YAAY,CAAC,CAAC,WAAW,aAAa;AACtD;;AAGA,SAAgB,cAAc,KAAuB;CACnD,MAAM,QAAQ,IAAI,MAAM,wCAAwC;CAChE,IAAI,CAAC,QAAQ,IAAI,MAAM,IAAI,WAAW,sBAAsB,KAAK;CACjE,IAAI,MAAM,EAAE,CAAC,YAAY,CAAC,CAAC,WAAW,OAAO,GAC3C,MAAM,IAAI,WAAW,iCAAiC;CAExD,OAAO;EACL,KAAK,SAAS,MAAM;EACpB,OAAO,MAAM;EACb,SAAS,OAAO,MAAM,EAAE;CAC1B;AACF"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { t as Event } from "../event-B5D2jxt_.mjs";
|
|
2
|
+
import { f as ProfilePointer, i as EventPointer, t as AddressPointer } from "../nip19-D7905oIv.mjs";
|
|
3
|
+
//#region src/nips/nip27.d.ts
|
|
4
|
+
type ContentBlock = {
|
|
5
|
+
type: "text";
|
|
6
|
+
text: string;
|
|
7
|
+
} | {
|
|
8
|
+
type: "reference";
|
|
9
|
+
pointer: ProfilePointer | AddressPointer | EventPointer;
|
|
10
|
+
} | {
|
|
11
|
+
type: "url";
|
|
12
|
+
url: string;
|
|
13
|
+
} | {
|
|
14
|
+
type: "relay";
|
|
15
|
+
url: string;
|
|
16
|
+
} | {
|
|
17
|
+
type: "image";
|
|
18
|
+
url: string;
|
|
19
|
+
} | {
|
|
20
|
+
type: "video";
|
|
21
|
+
url: string;
|
|
22
|
+
} | {
|
|
23
|
+
type: "audio";
|
|
24
|
+
url: string;
|
|
25
|
+
} | {
|
|
26
|
+
type: "emoji";
|
|
27
|
+
shortcode: string;
|
|
28
|
+
url: string;
|
|
29
|
+
} | {
|
|
30
|
+
type: "hashtag";
|
|
31
|
+
value: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Parse note content (or a full event, to pick up emoji tags) into blocks.
|
|
35
|
+
* Generator — consume with `for…of` or `[...parseContent(s)]`.
|
|
36
|
+
*/
|
|
37
|
+
declare function parseContent(content: string | Pick<Event, "content" | "tags">): Iterable<ContentBlock>;
|
|
38
|
+
/** Collect all content blocks into an array. */
|
|
39
|
+
declare function parseContentBlocks(content: string | Pick<Event, "content" | "tags">): ContentBlock[];
|
|
40
|
+
//#endregion
|
|
41
|
+
export { ContentBlock, parseContent, parseContentBlocks };
|
|
42
|
+
//# sourceMappingURL=nip27.d.mts.map
|