@qntx/nostr 0.1.0 → 0.2.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/README.md +16 -0
- package/dist/abort-Ba8tDrOI.mjs +38 -0
- package/dist/abort-Ba8tDrOI.mjs.map +1 -0
- package/dist/{builder-BphJa-Y-.d.mts → builder-BWn55Paf.d.mts} +20 -14
- package/dist/{builder-q0VsvnYE.mjs → builder-CyrGKRVP.mjs} +32 -16
- package/dist/builder-CyrGKRVP.mjs.map +1 -0
- package/dist/{client-Bd8B1mYm.mjs → client-5DhgYV7P.mjs} +372 -195
- package/dist/client-5DhgYV7P.mjs.map +1 -0
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs +1 -1
- package/dist/core.d.mts +8 -8
- package/dist/core.mjs +8 -9
- package/dist/error-BW7re1UN.d.mts +9 -0
- package/dist/error-B__OMBRY.mjs +14 -0
- package/dist/error-B__OMBRY.mjs.map +1 -0
- package/dist/{error-WEFS-R-4.d.mts → error-CR8Urc84.d.mts} +20 -8
- package/dist/{error-CwBxle6N.mjs → error-DrEIM7FO.mjs} +5 -3
- package/dist/error-DrEIM7FO.mjs.map +1 -0
- package/dist/{event-CzoQEFMF.mjs → event-D8Nfoh4K.mjs} +40 -32
- package/dist/event-D8Nfoh4K.mjs.map +1 -0
- package/dist/{event-B5D2jxt_.d.mts → event-fU3Y01PR.d.mts} +23 -20
- package/dist/{filter-BxwjEkZp.mjs → filter-BQl3GlHt.mjs} +42 -35
- package/dist/filter-BQl3GlHt.mjs.map +1 -0
- package/dist/filter-C0IMByE3.d.mts +37 -0
- package/dist/{gossip-C5RVB1UB.d.mts → gossip-BRQwIM_q.d.mts} +25 -23
- package/dist/{gossip-3xkRRCm_.mjs → gossip-oloDMWEj.mjs} +52 -34
- package/dist/gossip-oloDMWEj.mjs.map +1 -0
- package/dist/gossip.d.mts +1 -1
- package/dist/gossip.mjs +1 -1
- package/dist/{http-6vevbBlu.d.mts → http-C4HwIu4B.d.mts} +7 -7
- package/dist/{http-D2rhokZo.mjs → http-CuazNRIP.mjs} +10 -6
- package/dist/http-CuazNRIP.mjs.map +1 -0
- package/dist/index-B-CGNDPS.d.mts +207 -0
- package/dist/index-BIhSADwK.d.mts +339 -0
- package/dist/{index-BYMnhRIC.d.mts → index-Ba7bJ7DD.d.mts} +68 -50
- package/dist/{index-DaRyjo7O.d.mts → index-Boe8FshM.d.mts} +13 -7
- package/dist/index-D-TK_KJG.d.mts +84 -0
- package/dist/index-D1bqFHKB.d.mts +188 -0
- package/dist/index.d.mts +22 -20
- package/dist/index.mjs +19 -18
- package/dist/{key-Dn-aX4x4.mjs → key-Bbtys6o7.mjs} +22 -14
- package/dist/key-Bbtys6o7.mjs.map +1 -0
- package/dist/{key-BiGxGLhg.d.mts → key-BlsYhN1V.d.mts} +9 -6
- package/dist/{kind-BLl4cLOc.mjs → kind-Cxwhhkx8.mjs} +2 -1
- package/dist/kind-Cxwhhkx8.mjs.map +1 -0
- package/dist/{loaders-BNQszDAn.mjs → loaders-o6YXB_Ri.mjs} +329 -305
- package/dist/loaders-o6YXB_Ri.mjs.map +1 -0
- package/dist/loaders.d.mts +3 -3
- package/dist/loaders.mjs +2 -2
- package/dist/memory-C2EVx0_O.mjs +46 -0
- package/dist/memory-C2EVx0_O.mjs.map +1 -0
- package/dist/{memory-BCmhZPek.mjs → memory-index-Do1mz0WF.mjs} +212 -82
- package/dist/memory-index-Do1mz0WF.mjs.map +1 -0
- package/dist/{message-C_Uj-Y-c.mjs → message-DcY6Fi9o.mjs} +38 -24
- package/dist/message-DcY6Fi9o.mjs.map +1 -0
- package/dist/{message-DJC-Jz6T.d.mts → message-iGPIqnn0.d.mts} +15 -8
- package/dist/{nip17-BWq-Kfq5.d.mts → nip17-DNEkz8rM.d.mts} +18 -17
- package/dist/{nip19-D7905oIv.d.mts → nip19-BevCJZsp.d.mts} +23 -3
- package/dist/{nip46-C_DoxiS_.d.mts → nip46-DDV4dC56.d.mts} +13 -12
- package/dist/{nip59-DljW_sYh.d.mts → nip59-zQgdLcEr.d.mts} +28 -25
- package/dist/{nip65-Di5hZRuV.d.mts → nip65-DBfh2smt.d.mts} +8 -3
- package/dist/{nip77-ByxlgoQO.d.mts → nip77-DH4RhS5r.d.mts} +9 -8
- package/dist/nips/blossom.d.mts +37 -37
- package/dist/nips/blossom.mjs +67 -52
- package/dist/nips/blossom.mjs.map +1 -1
- package/dist/nips/nip04.d.mts +2 -3
- package/dist/nips/nip04.mjs +6 -10
- package/dist/nips/nip04.mjs.map +1 -1
- package/dist/nips/nip05.d.mts +37 -43
- package/dist/nips/nip05.mjs +57 -57
- package/dist/nips/nip05.mjs.map +1 -1
- package/dist/nips/nip10.d.mts +18 -19
- package/dist/nips/nip10.mjs +76 -61
- package/dist/nips/nip10.mjs.map +1 -1
- package/dist/nips/nip11.d.mts +9 -8
- package/dist/nips/nip11.mjs +14 -11
- package/dist/nips/nip11.mjs.map +1 -1
- package/dist/nips/nip13.d.mts +9 -9
- package/dist/nips/nip13.mjs +15 -17
- package/dist/nips/nip13.mjs.map +1 -1
- package/dist/nips/nip17.d.mts +1 -1
- package/dist/nips/nip17.mjs +17 -12
- package/dist/nips/nip17.mjs.map +1 -1
- package/dist/nips/nip19.d.mts +1 -1
- package/dist/nips/nip19.mjs +52 -29
- package/dist/nips/nip19.mjs.map +1 -1
- package/dist/nips/nip21.d.mts +5 -6
- package/dist/nips/nip21.mjs +8 -7
- package/dist/nips/nip21.mjs.map +1 -1
- package/dist/nips/nip27.d.mts +7 -8
- package/dist/nips/nip27.mjs +26 -25
- package/dist/nips/nip27.mjs.map +1 -1
- package/dist/nips/nip42.d.mts +5 -6
- package/dist/nips/nip42.mjs +3 -3
- package/dist/nips/nip42.mjs.map +1 -1
- package/dist/nips/nip44.d.mts +24 -7
- package/dist/nips/nip44.mjs +25 -15
- package/dist/nips/nip44.mjs.map +1 -1
- package/dist/nips/nip46.d.mts +1 -1
- package/dist/nips/nip46.mjs +52 -49
- package/dist/nips/nip46.mjs.map +1 -1
- package/dist/nips/nip49.d.mts +8 -7
- package/dist/nips/nip49.mjs +16 -12
- package/dist/nips/nip49.mjs.map +1 -1
- package/dist/nips/nip51.d.mts +25 -27
- package/dist/nips/nip51.mjs +47 -36
- package/dist/nips/nip51.mjs.map +1 -1
- package/dist/nips/nip57.d.mts +12 -13
- package/dist/nips/nip57.mjs +35 -38
- package/dist/nips/nip57.mjs.map +1 -1
- package/dist/nips/nip59.d.mts +1 -1
- package/dist/nips/nip59.mjs +14 -18
- package/dist/nips/nip59.mjs.map +1 -1
- package/dist/nips/nip65.d.mts +1 -1
- package/dist/nips/nip65.mjs +11 -8
- package/dist/nips/nip65.mjs.map +1 -1
- package/dist/nips/nip77.d.mts +1 -1
- package/dist/nips/nip77.mjs +56 -34
- package/dist/nips/nip77.mjs.map +1 -1
- package/dist/nips/nip96.d.mts +21 -12
- package/dist/nips/nip96.mjs +59 -36
- package/dist/nips/nip96.mjs.map +1 -1
- package/dist/nips/nip98.d.mts +12 -12
- package/dist/nips/nip98.mjs +18 -19
- package/dist/nips/nip98.mjs.map +1 -1
- package/dist/pool-B7p_reAm.d.mts +333 -0
- package/dist/{pool-C_j1OmBB.mjs → pool-C2xOcsun.mjs} +792 -568
- package/dist/pool-C2xOcsun.mjs.map +1 -0
- package/dist/reactive-DDlThDSZ.mjs +324 -0
- package/dist/reactive-DDlThDSZ.mjs.map +1 -0
- package/dist/reactive-DFpqtIf_.d.mts +66 -0
- package/dist/relay.d.mts +5 -4
- package/dist/relay.mjs +2 -2
- package/dist/report-CHld4uWj.mjs +32 -0
- package/dist/report-CHld4uWj.mjs.map +1 -0
- package/dist/{signer-CZ6K6FUc.mjs → signer-CKR9EHce.mjs} +107 -72
- package/dist/signer-CKR9EHce.mjs.map +1 -0
- package/dist/signer.d.mts +3 -3
- package/dist/signer.mjs +3 -2
- package/dist/storage-xXxLGCVb.mjs +1376 -0
- package/dist/storage-xXxLGCVb.mjs.map +1 -0
- package/dist/storage.d.mts +4 -4
- package/dist/storage.mjs +5 -4
- package/dist/store.d.mts +2 -0
- package/dist/store.mjs +2 -0
- package/dist/testing.d.mts +135 -0
- package/dist/testing.mjs +963 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/types-2pCUprRU.d.mts +47 -0
- package/dist/types-CRQEk0Oi.d.mts +19 -0
- package/dist/util-LfQirgDk.mjs +113 -0
- package/dist/util-LfQirgDk.mjs.map +1 -0
- package/dist/wasm.d.mts +4 -4
- package/dist/wasm.mjs +54 -55
- package/dist/wasm.mjs.map +1 -1
- package/dist/websocket-CmeqvAkp.d.mts +27 -0
- package/package.json +93 -39
- package/dist/builder-q0VsvnYE.mjs.map +0 -1
- package/dist/client-Bd8B1mYm.mjs.map +0 -1
- package/dist/error-B49rbDzT.d.mts +0 -7
- package/dist/error-CwBxle6N.mjs.map +0 -1
- package/dist/error-DZEs5OuZ.mjs +0 -24
- package/dist/error-DZEs5OuZ.mjs.map +0 -1
- package/dist/event-CzoQEFMF.mjs.map +0 -1
- package/dist/filter-BxwjEkZp.mjs.map +0 -1
- package/dist/filter-DUEKP3q1.d.mts +0 -40
- package/dist/gossip-3xkRRCm_.mjs.map +0 -1
- package/dist/http-D2rhokZo.mjs.map +0 -1
- package/dist/index-C0C4e-Gn.d.mts +0 -55
- package/dist/index-DgOugrpF.d.mts +0 -275
- package/dist/index-EwL6fHLo.d.mts +0 -267
- package/dist/index-k2YhCgsh.d.mts +0 -57
- package/dist/key-Dn-aX4x4.mjs.map +0 -1
- package/dist/kind-BLl4cLOc.mjs.map +0 -1
- package/dist/loaders-BNQszDAn.mjs.map +0 -1
- package/dist/memory-BCmhZPek.mjs.map +0 -1
- package/dist/message-C_Uj-Y-c.mjs.map +0 -1
- package/dist/pool-C_j1OmBB.mjs.map +0 -1
- package/dist/pool-DhRi7l-b.d.mts +0 -297
- package/dist/signer-CZ6K6FUc.mjs.map +0 -1
- package/dist/storage-DOy0iLRE.mjs +0 -870
- package/dist/storage-DOy0iLRE.mjs.map +0 -1
- package/dist/types-BzyADqnw.d.mts +0 -19
- package/dist/types-Dn24TPFs.d.mts +0 -43
- package/dist/util-6tfLQ9JF.mjs +0 -73
- package/dist/util-6tfLQ9JF.mjs.map +0 -1
package/dist/nips/nip10.mjs
CHANGED
|
@@ -1,85 +1,94 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { t as EventBuilder } from "../builder-q0VsvnYE.mjs";
|
|
1
|
+
import { o as isHex32, p as EventValidationError } from "../util-LfQirgDk.mjs";
|
|
2
|
+
import { t as Kind } from "../kind-Cxwhhkx8.mjs";
|
|
3
|
+
import { _ as formatEventAddress, b as parseEventAddress, h as Tag } from "../event-D8Nfoh4K.mjs";
|
|
4
|
+
import { t as EventBuilder } from "../builder-CyrGKRVP.mjs";
|
|
6
5
|
//#region src/nips/nip10.ts
|
|
7
6
|
/**
|
|
8
7
|
* NIP-10: Text Notes and Threads
|
|
8
|
+
*
|
|
9
9
|
* @see https://github.com/nostr-protocol/nips/blob/master/10.md
|
|
10
10
|
*/
|
|
11
|
+
function eTagAuthor(tag) {
|
|
12
|
+
for (const author of [tag[4], tag[3]]) if (author !== void 0 && isHex32(author.toLowerCase())) return author.toLowerCase();
|
|
13
|
+
}
|
|
11
14
|
function eventPointerFromETag(tag) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
id:
|
|
16
|
-
relays: tag[2] ? [tag[2]] : []
|
|
17
|
-
author
|
|
15
|
+
const id = tag.at(1);
|
|
16
|
+
if (tag[0] !== "e" || id === void 0 || !isHex32(id.toLowerCase())) return;
|
|
17
|
+
const pointer = {
|
|
18
|
+
id: id.toLowerCase(),
|
|
19
|
+
relays: tag[2] !== void 0 && tag[2] !== "" ? [tag[2]] : []
|
|
18
20
|
};
|
|
21
|
+
const author = eTagAuthor(tag);
|
|
22
|
+
if (author !== void 0) pointer.author = author;
|
|
23
|
+
return pointer;
|
|
19
24
|
}
|
|
20
25
|
function quoteFromQTag(tag) {
|
|
21
|
-
|
|
22
|
-
if (
|
|
26
|
+
const value = tag.at(1);
|
|
27
|
+
if (tag[0] !== "q" || value === void 0 || value === "") return;
|
|
28
|
+
const relays = tag[2] !== void 0 && tag[2] !== "" ? [tag[2]] : [];
|
|
29
|
+
if (isHex32(value.toLowerCase())) {
|
|
23
30
|
const pointer = {
|
|
24
|
-
id:
|
|
25
|
-
relays
|
|
31
|
+
id: value.toLowerCase(),
|
|
32
|
+
relays
|
|
26
33
|
};
|
|
27
|
-
|
|
34
|
+
const author = tag.at(3);
|
|
35
|
+
if (author !== void 0 && isHex32(author.toLowerCase())) pointer.author = author.toLowerCase();
|
|
28
36
|
return pointer;
|
|
29
37
|
}
|
|
30
|
-
const addr = parseEventAddress(
|
|
31
|
-
if (!addr) return
|
|
38
|
+
const addr = parseEventAddress(value);
|
|
39
|
+
if (!addr) return;
|
|
32
40
|
return {
|
|
33
41
|
identifier: addr.identifier,
|
|
34
42
|
pubkey: addr.pubkey,
|
|
35
43
|
kind: addr.kind,
|
|
36
|
-
relays
|
|
44
|
+
relays
|
|
37
45
|
};
|
|
38
46
|
}
|
|
39
47
|
function quoteToTag(quote) {
|
|
40
48
|
if (typeof quote === "string") {
|
|
41
|
-
if (isHex32(quote)) return { tag: ["q", quote.toLowerCase()] };
|
|
49
|
+
if (isHex32(quote.toLowerCase())) return { tag: ["q", quote.toLowerCase()] };
|
|
42
50
|
const addr = parseEventAddress(quote);
|
|
43
|
-
if (!addr) return
|
|
51
|
+
if (!addr) return;
|
|
44
52
|
return { tag: ["q", formatEventAddress(addr.kind, addr.pubkey, addr.identifier)] };
|
|
45
53
|
}
|
|
46
54
|
if ("id" in quote) {
|
|
47
55
|
const id = quote.id.toLowerCase();
|
|
48
56
|
const relay = quote.relays?.[0];
|
|
49
|
-
const author = quote.author && isHex32(quote.author) ? quote.author.toLowerCase() : void 0;
|
|
57
|
+
const author = quote.author !== void 0 && isHex32(quote.author.toLowerCase()) ? quote.author.toLowerCase() : void 0;
|
|
58
|
+
let tag;
|
|
59
|
+
if (author === void 0) tag = relay !== void 0 && relay !== "" ? [
|
|
60
|
+
"q",
|
|
61
|
+
id,
|
|
62
|
+
relay
|
|
63
|
+
] : ["q", id];
|
|
64
|
+
else tag = [
|
|
65
|
+
"q",
|
|
66
|
+
id,
|
|
67
|
+
relay ?? "",
|
|
68
|
+
author
|
|
69
|
+
];
|
|
50
70
|
return {
|
|
51
|
-
tag
|
|
52
|
-
"q",
|
|
53
|
-
id,
|
|
54
|
-
relay ?? "",
|
|
55
|
-
author
|
|
56
|
-
] : relay ? [
|
|
57
|
-
"q",
|
|
58
|
-
id,
|
|
59
|
-
relay
|
|
60
|
-
] : ["q", id],
|
|
71
|
+
tag,
|
|
61
72
|
author,
|
|
62
|
-
relay
|
|
73
|
+
relay
|
|
63
74
|
};
|
|
64
75
|
}
|
|
65
76
|
const coord = formatEventAddress(quote.kind, quote.pubkey, quote.identifier);
|
|
66
77
|
const relay = quote.relays?.[0];
|
|
67
78
|
return {
|
|
68
|
-
tag: relay ? [
|
|
79
|
+
tag: relay !== void 0 && relay !== "" ? [
|
|
69
80
|
"q",
|
|
70
81
|
coord,
|
|
71
82
|
relay
|
|
72
83
|
] : ["q", coord],
|
|
73
84
|
author: quote.pubkey,
|
|
74
|
-
relay
|
|
85
|
+
relay
|
|
75
86
|
};
|
|
76
87
|
}
|
|
77
88
|
function assertKind1Parent(parent) {
|
|
78
89
|
if (parent.kind !== Kind.TextNote) throw new EventValidationError("NIP-10 replyTo is for kind 1");
|
|
79
90
|
}
|
|
80
|
-
/**
|
|
81
|
-
* Parse NIP-10 thread markers and legacy positional e-tags from an event.
|
|
82
|
-
*/
|
|
91
|
+
/** Parse NIP-10 thread markers and legacy positional e-tags from an event. */
|
|
83
92
|
function parseThreadTags(event) {
|
|
84
93
|
const result = {
|
|
85
94
|
root: void 0,
|
|
@@ -92,9 +101,12 @@ function parseThreadTags(event) {
|
|
|
92
101
|
let maybeRoot;
|
|
93
102
|
for (let i = event.tags.length - 1; i >= 0; i--) {
|
|
94
103
|
const tag = event.tags[i];
|
|
95
|
-
if (tag
|
|
104
|
+
if (tag === void 0) continue;
|
|
105
|
+
const eValue = tag.at(1);
|
|
106
|
+
if (tag[0] === "e" && eValue !== void 0 && isHex32(eValue.toLowerCase())) {
|
|
96
107
|
const pointer = eventPointerFromETag(tag);
|
|
97
|
-
|
|
108
|
+
if (pointer === void 0) continue;
|
|
109
|
+
const marker = tag.at(3);
|
|
98
110
|
if (marker === "root") {
|
|
99
111
|
result.root = pointer;
|
|
100
112
|
continue;
|
|
@@ -103,12 +115,12 @@ function parseThreadTags(event) {
|
|
|
103
115
|
result.reply = pointer;
|
|
104
116
|
continue;
|
|
105
117
|
}
|
|
106
|
-
if (marker && !isHex32(marker)) {
|
|
118
|
+
if (marker !== void 0 && marker !== "" && !isHex32(marker.toLowerCase())) {
|
|
107
119
|
result.mentions.push(pointer);
|
|
108
120
|
continue;
|
|
109
121
|
}
|
|
110
|
-
if (
|
|
111
|
-
else
|
|
122
|
+
if (maybeParent) maybeRoot = pointer;
|
|
123
|
+
else maybeParent = pointer;
|
|
112
124
|
result.mentions.push(pointer);
|
|
113
125
|
continue;
|
|
114
126
|
}
|
|
@@ -117,13 +129,14 @@ function parseThreadTags(event) {
|
|
|
117
129
|
if (quote) result.quotes.push(quote);
|
|
118
130
|
continue;
|
|
119
131
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
132
|
+
const pValue = tag.at(1);
|
|
133
|
+
if (tag[0] === "p" && pValue !== void 0 && isHex32(pValue.toLowerCase())) result.profiles.push({
|
|
134
|
+
pubkey: pValue.toLowerCase(),
|
|
135
|
+
relays: tag[2] !== void 0 && tag[2] !== "" ? [tag[2]] : []
|
|
123
136
|
});
|
|
124
137
|
}
|
|
125
|
-
|
|
126
|
-
|
|
138
|
+
result.root ??= maybeRoot ?? maybeParent ?? result.reply;
|
|
139
|
+
result.reply ??= maybeParent ?? result.root;
|
|
127
140
|
const drop = new Set([result.root?.id, result.reply?.id].filter((id) => Boolean(id)));
|
|
128
141
|
result.mentions = result.mentions.filter((m) => !drop.has(m.id));
|
|
129
142
|
for (const ref of [
|
|
@@ -131,18 +144,20 @@ function parseThreadTags(event) {
|
|
|
131
144
|
result.root,
|
|
132
145
|
...result.mentions
|
|
133
146
|
]) {
|
|
134
|
-
if (
|
|
135
|
-
const
|
|
136
|
-
if (
|
|
147
|
+
if (ref === void 0) continue;
|
|
148
|
+
const refAuthor = ref.author;
|
|
149
|
+
if (refAuthor === void 0 || refAuthor === "") continue;
|
|
150
|
+
const profileRelays = result.profiles.find((p) => p.pubkey === refAuthor)?.relays;
|
|
151
|
+
if (profileRelays === void 0 || profileRelays.length === 0) continue;
|
|
137
152
|
const relays = [...ref.relays ?? []];
|
|
138
|
-
for (const url of
|
|
153
|
+
for (const url of profileRelays) if (!relays.includes(url)) relays.push(url);
|
|
139
154
|
ref.relays = relays;
|
|
140
155
|
}
|
|
141
156
|
return result;
|
|
142
157
|
}
|
|
143
158
|
/**
|
|
144
|
-
* Build NIP-10 e/p tags for a reply to `parent`.
|
|
145
|
-
*
|
|
159
|
+
* Build NIP-10 e/p tags for a reply to `parent`. Uses marked tags (`root` / `reply`) per preferred
|
|
160
|
+
* modern style.
|
|
146
161
|
*/
|
|
147
162
|
function buildReplyTags(opts) {
|
|
148
163
|
assertKind1Parent(opts.parent);
|
|
@@ -155,31 +170,31 @@ function buildReplyTags(opts) {
|
|
|
155
170
|
const parentIsRoot = root.id.toLowerCase() === opts.parent.id.toLowerCase();
|
|
156
171
|
const tags = [];
|
|
157
172
|
const rootRelay = root.relays?.[0] ?? opts.relayHint ?? "";
|
|
158
|
-
tags.push(Tag.e(root.id, rootRelay, "root", root.author
|
|
173
|
+
tags.push(Tag.e(root.id, rootRelay, "root", root.author));
|
|
159
174
|
if (!parentIsRoot) tags.push(Tag.e(opts.parent.id, opts.relayHint ?? "", "reply", opts.parent.pubkey));
|
|
160
175
|
const pSeen = /* @__PURE__ */ new Set();
|
|
161
176
|
const addP = (pk, relay) => {
|
|
162
177
|
const key = pk.toLowerCase();
|
|
163
178
|
if (pSeen.has(key)) return;
|
|
164
179
|
pSeen.add(key);
|
|
165
|
-
tags.push(Tag.p(pk, relay
|
|
180
|
+
tags.push(Tag.p(pk, relay ?? void 0));
|
|
166
181
|
};
|
|
167
|
-
if (root.author) addP(root.author, root.relays?.[0]);
|
|
182
|
+
if (root.author !== void 0 && root.author !== "") addP(root.author, root.relays?.[0]);
|
|
168
183
|
addP(opts.parent.pubkey, opts.relayHint);
|
|
169
184
|
for (const p of thread.profiles) addP(p.pubkey, p.relays?.[0]);
|
|
170
185
|
const qTags = [];
|
|
171
186
|
for (const quote of opts.quotes ?? []) {
|
|
172
187
|
const built = quoteToTag(quote);
|
|
173
|
-
if (
|
|
174
|
-
if (built.author) addP(built.author, built.relay);
|
|
188
|
+
if (built === void 0) continue;
|
|
189
|
+
if (built.author !== void 0 && built.author !== "") addP(built.author, built.relay);
|
|
175
190
|
qTags.push(built.tag);
|
|
176
191
|
}
|
|
177
192
|
tags.push(...qTags);
|
|
178
193
|
return tags;
|
|
179
194
|
}
|
|
180
195
|
/**
|
|
181
|
-
* Build an {@link EventBuilder} reply with NIP-10 tags.
|
|
182
|
-
*
|
|
196
|
+
* Build an {@link EventBuilder} reply with NIP-10 tags. Lives here (not on EventBuilder) so core
|
|
197
|
+
* does not depend on nips.
|
|
183
198
|
*/
|
|
184
199
|
function replyTo(parent, content, opts) {
|
|
185
200
|
return EventBuilder.textNote(content).tags(buildReplyTags({
|
package/dist/nips/nip10.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nip10.mjs","names":[],"sources":["../../src/nips/nip10.ts"],"sourcesContent":["/**\n * NIP-10: Text Notes and Threads\n * @see https://github.com/nostr-protocol/nips/blob/master/10.md\n */\nimport { EventBuilder } from \"../core/builder.ts\";\nimport { EventValidationError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { formatEventAddress, parseEventAddress, Tag } from \"../core/tag.ts\";\nimport { isHex32 } from \"../core/util.ts\";\nimport type { AddressPointer, EventPointer, ProfilePointer } from \"./nip19.ts\";\n\nexport type ThreadReferences = {\n /** Pointer to the root of the thread. */\n root: EventPointer | undefined;\n /** Pointer to the parent event this note replies to. */\n reply: EventPointer | undefined;\n /** Other e-tagged events (not root/reply). */\n mentions: EventPointer[];\n /** Quoted events (`q` tags): event ids or addresses. Discriminate with `\"id\" in q`. */\n quotes: Array<EventPointer | AddressPointer>;\n /** p-tagged profiles involved in the thread. */\n profiles: ProfilePointer[];\n};\n\ntype ReplyParent = Pick<Event, \"id\" | \"pubkey\" | \"tags\" | \"kind\">;\ntype QuoteInput = string | EventPointer | AddressPointer;\n\nfunction eventPointerFromETag(tag: readonly string[]): EventPointer | undefined {\n if (tag[0] !== \"e\" || !tag[1] || !isHex32(tag[1])) return undefined;\n // NIP-10 5-tuple pubkey is index 4; NIP-01 4-tuple pubkey is index 3.\n const author =\n tag[4] && isHex32(tag[4])\n ? tag[4].toLowerCase()\n : tag[3] && isHex32(tag[3])\n ? tag[3].toLowerCase()\n : undefined;\n return {\n id: tag[1].toLowerCase(),\n relays: tag[2] ? [tag[2]] : [],\n author,\n };\n}\n\nfunction quoteFromQTag(tag: readonly string[]): EventPointer | AddressPointer | undefined {\n if (tag[0] !== \"q\" || !tag[1]) return undefined;\n if (isHex32(tag[1])) {\n const pointer: EventPointer = {\n id: tag[1].toLowerCase(),\n relays: tag[2] ? [tag[2]] : [],\n };\n if (tag[3] && isHex32(tag[3])) pointer.author = tag[3].toLowerCase();\n return pointer;\n }\n const addr = parseEventAddress(tag[1]);\n if (!addr) return undefined;\n // Address q tags do not use the event-id pubkey slot (index 3).\n return {\n identifier: addr.identifier,\n pubkey: addr.pubkey,\n kind: addr.kind,\n relays: tag[2] ? [tag[2]] : [],\n };\n}\n\nfunction quoteToTag(quote: QuoteInput): { tag: Tag; author?: string; relay?: string } | undefined {\n if (typeof quote === \"string\") {\n if (isHex32(quote)) return { tag: [\"q\", quote.toLowerCase()] };\n const addr = parseEventAddress(quote);\n if (!addr) return undefined;\n return { tag: [\"q\", formatEventAddress(addr.kind, addr.pubkey, addr.identifier)] };\n }\n if (\"id\" in quote) {\n const id = quote.id.toLowerCase();\n const relay = quote.relays?.[0];\n const author = quote.author && isHex32(quote.author) ? quote.author.toLowerCase() : undefined;\n const tag: Tag =\n author !== undefined ? [\"q\", id, relay ?? \"\", author] : relay ? [\"q\", id, relay] : [\"q\", id];\n return { tag, author, relay: relay || undefined };\n }\n const coord = formatEventAddress(quote.kind, quote.pubkey, quote.identifier);\n const relay = quote.relays?.[0];\n const tag: Tag = relay ? [\"q\", coord, relay] : [\"q\", coord];\n return { tag, author: quote.pubkey, relay: relay || undefined };\n}\n\nfunction assertKind1Parent(parent: ReplyParent): void {\n // NIP-10 is kind 1 only; comments are NIP-22.\n if (parent.kind !== Kind.TextNote) {\n throw new EventValidationError(\"NIP-10 replyTo is for kind 1\");\n }\n}\n\n/**\n * Parse NIP-10 thread markers and legacy positional e-tags from an event.\n */\nexport function parseThreadTags(event: Pick<Event, \"tags\">): ThreadReferences {\n const result: ThreadReferences = {\n root: undefined,\n reply: undefined,\n mentions: [],\n quotes: [],\n profiles: [],\n };\n\n let maybeParent: EventPointer | undefined;\n let maybeRoot: EventPointer | undefined;\n\n for (let i = event.tags.length - 1; i >= 0; i--) {\n const tag = event.tags[i]!;\n\n if (tag[0] === \"e\" && tag[1] && isHex32(tag[1])) {\n const pointer = eventPointerFromETag(tag)!;\n const marker = tag[3];\n\n if (marker === \"root\") {\n result.root = pointer;\n continue;\n }\n if (marker === \"reply\") {\n result.reply = pointer;\n continue;\n }\n // Preferred markers are root/reply only. A hex32 at index 3 is NIP-01 pubkey, not a marker.\n if (marker && !isHex32(marker)) {\n result.mentions.push(pointer);\n continue;\n }\n\n // Legacy positional: last unmarked is parent, second-to-last is root.\n if (!maybeParent) maybeParent = pointer;\n else maybeRoot = pointer;\n result.mentions.push(pointer);\n continue;\n }\n\n if (tag[0] === \"q\") {\n const quote = quoteFromQTag(tag);\n if (quote) result.quotes.push(quote);\n continue;\n }\n\n if (tag[0] === \"p\" && tag[1] && isHex32(tag[1])) {\n result.profiles.push({\n pubkey: tag[1].toLowerCase(),\n relays: tag[2] ? [tag[2]] : [],\n });\n }\n }\n\n if (!result.root) {\n result.root = maybeRoot ?? maybeParent ?? result.reply;\n }\n if (!result.reply) {\n result.reply = maybeParent ?? result.root;\n }\n\n // Drop root/reply from mentions (by id).\n const drop = new Set(\n [result.root?.id, result.reply?.id].filter((id): id is string => Boolean(id)),\n );\n result.mentions = result.mentions.filter((m) => !drop.has(m.id));\n\n // Inherit relay hints from matching p-tags.\n for (const ref of [result.reply, result.root, ...result.mentions]) {\n if (!ref?.author) continue;\n const author = result.profiles.find((p) => p.pubkey === ref.author);\n if (!author?.relays?.length) continue;\n const relays = [...(ref.relays ?? [])];\n for (const url of author.relays) {\n if (!relays.includes(url)) relays.push(url);\n }\n ref.relays = relays;\n }\n\n return result;\n}\n\nexport type ReplyTagsOptions = {\n /** Parent event being replied to. */\n parent: ReplyParent;\n /** Optional relay hint for the parent e-tag. */\n relayHint?: string;\n /** Quoted events (`q` tags): hex ids, `kind:pubkey:d` coords, or pointers. */\n quotes?: QuoteInput[];\n};\n\n/**\n * Build NIP-10 e/p tags for a reply to `parent`.\n * Uses marked tags (`root` / `reply`) per preferred modern style.\n */\nexport function buildReplyTags(opts: ReplyTagsOptions): Tag[] {\n assertKind1Parent(opts.parent);\n const thread = parseThreadTags(opts.parent);\n const root = thread.root ?? { id: opts.parent.id, relays: [], author: opts.parent.pubkey };\n const parentIsRoot = root.id.toLowerCase() === opts.parent.id.toLowerCase();\n\n const tags: Tag[] = [];\n const rootRelay = root.relays?.[0] ?? opts.relayHint ?? \"\";\n tags.push(Tag.e(root.id, rootRelay, \"root\", root.author ?? opts.parent.pubkey));\n\n if (!parentIsRoot) {\n tags.push(Tag.e(opts.parent.id, opts.relayHint ?? \"\", \"reply\", opts.parent.pubkey));\n }\n\n // Ensure root + parent authors are p-tagged.\n const pSeen = new Set<string>();\n const addP = (pk: string, relay?: string) => {\n const key = pk.toLowerCase();\n if (pSeen.has(key)) return;\n pSeen.add(key);\n tags.push(Tag.p(pk, relay || undefined));\n };\n if (root.author) addP(root.author, root.relays?.[0]);\n addP(opts.parent.pubkey, opts.relayHint);\n for (const p of thread.profiles) addP(p.pubkey, p.relays?.[0]);\n\n const qTags: Tag[] = [];\n for (const quote of opts.quotes ?? []) {\n const built = quoteToTag(quote);\n if (!built) continue;\n if (built.author) addP(built.author, built.relay);\n qTags.push(built.tag);\n }\n tags.push(...qTags);\n\n return tags;\n}\n\n/**\n * Build an {@link EventBuilder} reply with NIP-10 tags.\n * Lives here (not on EventBuilder) so core does not depend on nips.\n */\nexport function replyTo(\n parent: ReplyParent,\n content: string,\n opts?: Pick<ReplyTagsOptions, \"relayHint\" | \"quotes\">,\n): EventBuilder {\n return EventBuilder.textNote(content).tags(\n buildReplyTags({\n parent,\n relayHint: opts?.relayHint,\n quotes: opts?.quotes,\n }),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nip10.mjs","names":[],"sources":["../../src/nips/nip10.ts"],"sourcesContent":["/**\n * NIP-10: Text Notes and Threads\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/10.md\n */\nimport { EventBuilder } from \"../core/builder.ts\";\nimport { EventValidationError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport { formatEventAddress, parseEventAddress, Tag } from \"../core/tag.ts\";\nimport { isHex32 } from \"../core/util.ts\";\nimport type { AddressPointer, EventPointer, ProfilePointer } from \"./nip19.ts\";\n\n/** Parsed NIP-10 thread references from an event's `e`/`q`/`p` tags. */\nexport type ThreadReferences = {\n /** Pointer to the root of the thread. */\n root: EventPointer | undefined;\n /** Pointer to the parent event this note replies to. */\n reply: EventPointer | undefined;\n /** Other e-tagged events (not root/reply). */\n mentions: EventPointer[];\n /** Quoted events (`q` tags): event ids or addresses. Discriminate with `\"id\" in q`. */\n quotes: Array<EventPointer | AddressPointer>;\n /** P-tagged profiles involved in the thread. */\n profiles: ProfilePointer[];\n};\n\ntype ReplyParent = Pick<Event, \"id\" | \"pubkey\" | \"tags\" | \"kind\">;\ntype QuoteInput = string | EventPointer | AddressPointer;\n\nfunction eTagAuthor(tag: ReadonlyArray<string>): string | undefined {\n // NIP-10 5-tuple pubkey is index 4; NIP-01 4-tuple pubkey is index 3.\n for (const author of [tag[4], tag[3]]) {\n if (author !== undefined && isHex32(author.toLowerCase())) {\n return author.toLowerCase();\n }\n }\n return undefined;\n}\n\nfunction eventPointerFromETag(tag: ReadonlyArray<string>): EventPointer | undefined {\n const id = tag.at(1);\n if (tag[0] !== \"e\" || id === undefined || !isHex32(id.toLowerCase())) {\n return undefined;\n }\n const pointer: EventPointer = {\n id: id.toLowerCase(),\n relays: tag[2] !== undefined && tag[2] !== \"\" ? [tag[2]] : [],\n };\n const author = eTagAuthor(tag);\n if (author !== undefined) {\n pointer.author = author;\n }\n return pointer;\n}\n\nfunction quoteFromQTag(tag: ReadonlyArray<string>): EventPointer | AddressPointer | undefined {\n const value = tag.at(1);\n if (tag[0] !== \"q\" || value === undefined || value === \"\") {\n return undefined;\n }\n const relays = tag[2] !== undefined && tag[2] !== \"\" ? [tag[2]] : [];\n if (isHex32(value.toLowerCase())) {\n const pointer: EventPointer = { id: value.toLowerCase(), relays };\n const author = tag.at(3);\n if (author !== undefined && isHex32(author.toLowerCase())) {\n pointer.author = author.toLowerCase();\n }\n return pointer;\n }\n const addr = parseEventAddress(value);\n if (!addr) {\n return undefined;\n }\n // Address q tags do not use the event-id pubkey slot (index 3).\n return {\n identifier: addr.identifier,\n pubkey: addr.pubkey,\n kind: addr.kind,\n relays,\n };\n}\n\ntype BuiltQuote = { tag: Tag; author?: string | undefined; relay?: string | undefined };\n\nfunction quoteToTag(quote: QuoteInput): BuiltQuote | undefined {\n if (typeof quote === \"string\") {\n if (isHex32(quote.toLowerCase())) {\n return { tag: [\"q\", quote.toLowerCase()] };\n }\n const addr = parseEventAddress(quote);\n if (!addr) {\n return undefined;\n }\n return { tag: [\"q\", formatEventAddress(addr.kind, addr.pubkey, addr.identifier)] };\n }\n if (\"id\" in quote) {\n const id = quote.id.toLowerCase();\n const relay = quote.relays?.[0];\n const author =\n quote.author !== undefined && isHex32(quote.author.toLowerCase())\n ? quote.author.toLowerCase()\n : undefined;\n let tag: Tag;\n if (author === undefined) {\n tag = relay !== undefined && relay !== \"\" ? [\"q\", id, relay] : [\"q\", id];\n } else {\n tag = [\"q\", id, relay ?? \"\", author];\n }\n return { tag, author, relay };\n }\n const coord = formatEventAddress(quote.kind, quote.pubkey, quote.identifier);\n const relay = quote.relays?.[0];\n const tag: Tag = relay !== undefined && relay !== \"\" ? [\"q\", coord, relay] : [\"q\", coord];\n return { tag, author: quote.pubkey, relay };\n}\n\nfunction assertKind1Parent(parent: ReplyParent): void {\n // NIP-10 is kind 1 only; comments are NIP-22.\n if (parent.kind !== Kind.TextNote) {\n throw new EventValidationError(\"NIP-10 replyTo is for kind 1\");\n }\n}\n\n/** Parse NIP-10 thread markers and legacy positional e-tags from an event. */\nexport function parseThreadTags(event: Pick<Event, \"tags\">): ThreadReferences {\n const result: ThreadReferences = {\n root: undefined,\n reply: undefined,\n mentions: [],\n quotes: [],\n profiles: [],\n };\n\n let maybeParent: EventPointer | undefined;\n let maybeRoot: EventPointer | undefined;\n\n for (let i = event.tags.length - 1; i >= 0; i--) {\n const tag = event.tags[i];\n if (tag === undefined) {\n continue;\n }\n\n const eValue = tag.at(1);\n if (tag[0] === \"e\" && eValue !== undefined && isHex32(eValue.toLowerCase())) {\n const pointer = eventPointerFromETag(tag);\n if (pointer === undefined) {\n continue;\n }\n const marker = tag.at(3);\n\n if (marker === \"root\") {\n result.root = pointer;\n continue;\n }\n if (marker === \"reply\") {\n result.reply = pointer;\n continue;\n }\n // Preferred markers are root/reply only. A hex32 at index 3 is NIP-01 pubkey, not a marker.\n if (marker !== undefined && marker !== \"\" && !isHex32(marker.toLowerCase())) {\n result.mentions.push(pointer);\n continue;\n }\n\n // Legacy positional: last unmarked is parent, second-to-last is root.\n if (maybeParent) {\n maybeRoot = pointer;\n } else {\n maybeParent = pointer;\n }\n result.mentions.push(pointer);\n continue;\n }\n\n if (tag[0] === \"q\") {\n const quote = quoteFromQTag(tag);\n if (quote) {\n result.quotes.push(quote);\n }\n continue;\n }\n\n const pValue = tag.at(1);\n if (tag[0] === \"p\" && pValue !== undefined && isHex32(pValue.toLowerCase())) {\n result.profiles.push({\n pubkey: pValue.toLowerCase(),\n relays: tag[2] !== undefined && tag[2] !== \"\" ? [tag[2]] : [],\n });\n }\n }\n\n result.root ??= maybeRoot ?? maybeParent ?? result.reply;\n result.reply ??= maybeParent ?? result.root;\n\n // Drop root/reply from mentions (by id).\n const drop = new Set(\n [result.root?.id, result.reply?.id].filter((id): id is string => Boolean(id)),\n );\n result.mentions = result.mentions.filter((m) => !drop.has(m.id));\n\n // Inherit relay hints from matching p-tags.\n for (const ref of [result.reply, result.root, ...result.mentions]) {\n if (ref === undefined) {\n continue;\n }\n const refAuthor = ref.author;\n if (refAuthor === undefined || refAuthor === \"\") {\n continue;\n }\n const profile = result.profiles.find((p) => p.pubkey === refAuthor);\n const profileRelays = profile?.relays;\n if (profileRelays === undefined || profileRelays.length === 0) {\n continue;\n }\n const relays = [...(ref.relays ?? [])];\n for (const url of profileRelays) {\n if (!relays.includes(url)) {\n relays.push(url);\n }\n }\n ref.relays = relays;\n }\n\n return result;\n}\n\n/** Options for {@link buildReplyTags}. */\nexport type ReplyTagsOptions = {\n /** Parent event being replied to. */\n parent: ReplyParent;\n /** Optional relay hint for the parent e-tag. */\n relayHint?: string | undefined;\n /** Quoted events (`q` tags): hex ids, `kind:pubkey:d` coords, or pointers. */\n quotes?: QuoteInput[] | undefined;\n};\n\n/**\n * Build NIP-10 e/p tags for a reply to `parent`. Uses marked tags (`root` / `reply`) per preferred\n * modern style.\n */\nexport function buildReplyTags(opts: ReplyTagsOptions): Tag[] {\n assertKind1Parent(opts.parent);\n const thread = parseThreadTags(opts.parent);\n const root = thread.root ?? { id: opts.parent.id, relays: [], author: opts.parent.pubkey };\n const parentIsRoot = root.id.toLowerCase() === opts.parent.id.toLowerCase();\n\n const tags: Tag[] = [];\n const rootRelay = root.relays?.[0] ?? opts.relayHint ?? \"\";\n tags.push(Tag.e(root.id, rootRelay, \"root\", root.author));\n\n if (!parentIsRoot) {\n tags.push(Tag.e(opts.parent.id, opts.relayHint ?? \"\", \"reply\", opts.parent.pubkey));\n }\n\n // Ensure root + parent authors are p-tagged.\n const pSeen = new Set<string>();\n const addP = (pk: string, relay?: string) => {\n const key = pk.toLowerCase();\n if (pSeen.has(key)) {\n return;\n }\n pSeen.add(key);\n tags.push(Tag.p(pk, relay ?? undefined));\n };\n if (root.author !== undefined && root.author !== \"\") {\n addP(root.author, root.relays?.[0]);\n }\n addP(opts.parent.pubkey, opts.relayHint);\n for (const p of thread.profiles) {\n addP(p.pubkey, p.relays?.[0]);\n }\n\n const qTags: Tag[] = [];\n for (const quote of opts.quotes ?? []) {\n const built = quoteToTag(quote);\n if (built === undefined) {\n continue;\n }\n if (built.author !== undefined && built.author !== \"\") {\n addP(built.author, built.relay);\n }\n qTags.push(built.tag);\n }\n tags.push(...qTags);\n\n return tags;\n}\n\n/**\n * Build an {@link EventBuilder} reply with NIP-10 tags. Lives here (not on EventBuilder) so core\n * does not depend on nips.\n */\nexport function replyTo(\n parent: ReplyParent,\n content: string,\n opts?: Pick<ReplyTagsOptions, \"relayHint\" | \"quotes\">,\n): EventBuilder {\n return EventBuilder.textNote(content).tags(\n buildReplyTags({\n parent,\n relayHint: opts?.relayHint,\n quotes: opts?.quotes,\n }),\n );\n}\n"],"mappings":";;;;;;;;;;AA8BA,SAAS,WAAW,KAAgD;CAElE,KAAK,MAAM,UAAU,CAAC,IAAI,IAAI,IAAI,EAAE,GAClC,IAAI,WAAW,KAAA,KAAa,QAAQ,OAAO,YAAY,CAAC,GACtD,OAAO,OAAO,YAAY;AAIhC;AAEA,SAAS,qBAAqB,KAAsD;CAClF,MAAM,KAAK,IAAI,GAAG,CAAC;CACnB,IAAI,IAAI,OAAO,OAAO,OAAO,KAAA,KAAa,CAAC,QAAQ,GAAG,YAAY,CAAC,GACjE;CAEF,MAAM,UAAwB;EAC5B,IAAI,GAAG,YAAY;EACnB,QAAQ,IAAI,OAAO,KAAA,KAAa,IAAI,OAAO,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC;CAC9D;CACA,MAAM,SAAS,WAAW,GAAG;CAC7B,IAAI,WAAW,KAAA,GACb,QAAQ,SAAS;CAEnB,OAAO;AACT;AAEA,SAAS,cAAc,KAAuE;CAC5F,MAAM,QAAQ,IAAI,GAAG,CAAC;CACtB,IAAI,IAAI,OAAO,OAAO,UAAU,KAAA,KAAa,UAAU,IACrD;CAEF,MAAM,SAAS,IAAI,OAAO,KAAA,KAAa,IAAI,OAAO,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC;CACnE,IAAI,QAAQ,MAAM,YAAY,CAAC,GAAG;EAChC,MAAM,UAAwB;GAAE,IAAI,MAAM,YAAY;GAAG;EAAO;EAChE,MAAM,SAAS,IAAI,GAAG,CAAC;EACvB,IAAI,WAAW,KAAA,KAAa,QAAQ,OAAO,YAAY,CAAC,GACtD,QAAQ,SAAS,OAAO,YAAY;EAEtC,OAAO;CACT;CACA,MAAM,OAAO,kBAAkB,KAAK;CACpC,IAAI,CAAC,MACH;CAGF,OAAO;EACL,YAAY,KAAK;EACjB,QAAQ,KAAK;EACb,MAAM,KAAK;EACX;CACF;AACF;AAIA,SAAS,WAAW,OAA2C;CAC7D,IAAI,OAAO,UAAU,UAAU;EAC7B,IAAI,QAAQ,MAAM,YAAY,CAAC,GAC7B,OAAO,EAAE,KAAK,CAAC,KAAK,MAAM,YAAY,CAAC,EAAE;EAE3C,MAAM,OAAO,kBAAkB,KAAK;EACpC,IAAI,CAAC,MACH;EAEF,OAAO,EAAE,KAAK,CAAC,KAAK,mBAAmB,KAAK,MAAM,KAAK,QAAQ,KAAK,UAAU,CAAC,EAAE;CACnF;CACA,IAAI,QAAQ,OAAO;EACjB,MAAM,KAAK,MAAM,GAAG,YAAY;EAChC,MAAM,QAAQ,MAAM,SAAS;EAC7B,MAAM,SACJ,MAAM,WAAW,KAAA,KAAa,QAAQ,MAAM,OAAO,YAAY,CAAC,IAC5D,MAAM,OAAO,YAAY,IACzB,KAAA;EACN,IAAI;EACJ,IAAI,WAAW,KAAA,GACb,MAAM,UAAU,KAAA,KAAa,UAAU,KAAK;GAAC;GAAK;GAAI;EAAK,IAAI,CAAC,KAAK,EAAE;OAEvE,MAAM;GAAC;GAAK;GAAI,SAAS;GAAI;EAAM;EAErC,OAAO;GAAE;GAAK;GAAQ;EAAM;CAC9B;CACA,MAAM,QAAQ,mBAAmB,MAAM,MAAM,MAAM,QAAQ,MAAM,UAAU;CAC3E,MAAM,QAAQ,MAAM,SAAS;CAE7B,OAAO;EAAE,KADQ,UAAU,KAAA,KAAa,UAAU,KAAK;GAAC;GAAK;GAAO;EAAK,IAAI,CAAC,KAAK,KAAK;EAC1E,QAAQ,MAAM;EAAQ;CAAM;AAC5C;AAEA,SAAS,kBAAkB,QAA2B;CAEpD,IAAI,OAAO,SAAS,KAAK,UACvB,MAAM,IAAI,qBAAqB,8BAA8B;AAEjE;;AAGA,SAAgB,gBAAgB,OAA8C;CAC5E,MAAM,SAA2B;EAC/B,MAAM,KAAA;EACN,OAAO,KAAA;EACP,UAAU,CAAC;EACX,QAAQ,CAAC;EACT,UAAU,CAAC;CACb;CAEA,IAAI;CACJ,IAAI;CAEJ,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,GAAG,KAAK,GAAG,KAAK;EAC/C,MAAM,MAAM,MAAM,KAAK;EACvB,IAAI,QAAQ,KAAA,GACV;EAGF,MAAM,SAAS,IAAI,GAAG,CAAC;EACvB,IAAI,IAAI,OAAO,OAAO,WAAW,KAAA,KAAa,QAAQ,OAAO,YAAY,CAAC,GAAG;GAC3E,MAAM,UAAU,qBAAqB,GAAG;GACxC,IAAI,YAAY,KAAA,GACd;GAEF,MAAM,SAAS,IAAI,GAAG,CAAC;GAEvB,IAAI,WAAW,QAAQ;IACrB,OAAO,OAAO;IACd;GACF;GACA,IAAI,WAAW,SAAS;IACtB,OAAO,QAAQ;IACf;GACF;GAEA,IAAI,WAAW,KAAA,KAAa,WAAW,MAAM,CAAC,QAAQ,OAAO,YAAY,CAAC,GAAG;IAC3E,OAAO,SAAS,KAAK,OAAO;IAC5B;GACF;GAGA,IAAI,aACF,YAAY;QAEZ,cAAc;GAEhB,OAAO,SAAS,KAAK,OAAO;GAC5B;EACF;EAEA,IAAI,IAAI,OAAO,KAAK;GAClB,MAAM,QAAQ,cAAc,GAAG;GAC/B,IAAI,OACF,OAAO,OAAO,KAAK,KAAK;GAE1B;EACF;EAEA,MAAM,SAAS,IAAI,GAAG,CAAC;EACvB,IAAI,IAAI,OAAO,OAAO,WAAW,KAAA,KAAa,QAAQ,OAAO,YAAY,CAAC,GACxE,OAAO,SAAS,KAAK;GACnB,QAAQ,OAAO,YAAY;GAC3B,QAAQ,IAAI,OAAO,KAAA,KAAa,IAAI,OAAO,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC;EAC9D,CAAC;CAEL;CAEA,OAAO,SAAS,aAAa,eAAe,OAAO;CACnD,OAAO,UAAU,eAAe,OAAO;CAGvC,MAAM,OAAO,IAAI,IACf,CAAC,OAAO,MAAM,IAAI,OAAO,OAAO,EAAE,CAAC,CAAC,QAAQ,OAAqB,QAAQ,EAAE,CAAC,CAC9E;CACA,OAAO,WAAW,OAAO,SAAS,QAAQ,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;CAG/D,KAAK,MAAM,OAAO;EAAC,OAAO;EAAO,OAAO;EAAM,GAAG,OAAO;CAAQ,GAAG;EACjE,IAAI,QAAQ,KAAA,GACV;EAEF,MAAM,YAAY,IAAI;EACtB,IAAI,cAAc,KAAA,KAAa,cAAc,IAC3C;EAGF,MAAM,gBADU,OAAO,SAAS,MAAM,MAAM,EAAE,WAAW,SAC7B,CAAC,EAAE;EAC/B,IAAI,kBAAkB,KAAA,KAAa,cAAc,WAAW,GAC1D;EAEF,MAAM,SAAS,CAAC,GAAI,IAAI,UAAU,CAAC,CAAE;EACrC,KAAK,MAAM,OAAO,eAChB,IAAI,CAAC,OAAO,SAAS,GAAG,GACtB,OAAO,KAAK,GAAG;EAGnB,IAAI,SAAS;CACf;CAEA,OAAO;AACT;;;;;AAgBA,SAAgB,eAAe,MAA+B;CAC5D,kBAAkB,KAAK,MAAM;CAC7B,MAAM,SAAS,gBAAgB,KAAK,MAAM;CAC1C,MAAM,OAAO,OAAO,QAAQ;EAAE,IAAI,KAAK,OAAO;EAAI,QAAQ,CAAC;EAAG,QAAQ,KAAK,OAAO;CAAO;CACzF,MAAM,eAAe,KAAK,GAAG,YAAY,MAAM,KAAK,OAAO,GAAG,YAAY;CAE1E,MAAM,OAAc,CAAC;CACrB,MAAM,YAAY,KAAK,SAAS,MAAM,KAAK,aAAa;CACxD,KAAK,KAAK,IAAI,EAAE,KAAK,IAAI,WAAW,QAAQ,KAAK,MAAM,CAAC;CAExD,IAAI,CAAC,cACH,KAAK,KAAK,IAAI,EAAE,KAAK,OAAO,IAAI,KAAK,aAAa,IAAI,SAAS,KAAK,OAAO,MAAM,CAAC;CAIpF,MAAM,wBAAQ,IAAI,IAAY;CAC9B,MAAM,QAAQ,IAAY,UAAmB;EAC3C,MAAM,MAAM,GAAG,YAAY;EAC3B,IAAI,MAAM,IAAI,GAAG,GACf;EAEF,MAAM,IAAI,GAAG;EACb,KAAK,KAAK,IAAI,EAAE,IAAI,SAAS,KAAA,CAAS,CAAC;CACzC;CACA,IAAI,KAAK,WAAW,KAAA,KAAa,KAAK,WAAW,IAC/C,KAAK,KAAK,QAAQ,KAAK,SAAS,EAAE;CAEpC,KAAK,KAAK,OAAO,QAAQ,KAAK,SAAS;CACvC,KAAK,MAAM,KAAK,OAAO,UACrB,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE;CAG9B,MAAM,QAAe,CAAC;CACtB,KAAK,MAAM,SAAS,KAAK,UAAU,CAAC,GAAG;EACrC,MAAM,QAAQ,WAAW,KAAK;EAC9B,IAAI,UAAU,KAAA,GACZ;EAEF,IAAI,MAAM,WAAW,KAAA,KAAa,MAAM,WAAW,IACjD,KAAK,MAAM,QAAQ,MAAM,KAAK;EAEhC,MAAM,KAAK,MAAM,GAAG;CACtB;CACA,KAAK,KAAK,GAAG,KAAK;CAElB,OAAO;AACT;;;;;AAMA,SAAgB,QACd,QACA,SACA,MACc;CACd,OAAO,aAAa,SAAS,OAAO,CAAC,CAAC,KACpC,eAAe;EACb;EACA,WAAW,MAAM;EACjB,QAAQ,MAAM;CAChB,CAAC,CACH;AACF"}
|
package/dist/nips/nip11.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { a as NostrError } from "../error-
|
|
2
|
-
import { t as ManualFetch } from "../http-
|
|
1
|
+
import { a as NostrError } from "../error-CR8Urc84.mjs";
|
|
2
|
+
import { t as ManualFetch } from "../http-C4HwIu4B.mjs";
|
|
3
3
|
//#region src/nips/nip11.d.ts
|
|
4
|
-
type Nip11Fetch = ManualFetch;
|
|
5
|
-
type RelayInformation = {
|
|
4
|
+
export type Nip11Fetch = ManualFetch;
|
|
5
|
+
export type RelayInformation = {
|
|
6
6
|
name?: string;
|
|
7
7
|
description?: string;
|
|
8
8
|
banner?: string;
|
|
@@ -31,13 +31,14 @@ type RelayInformation = {
|
|
|
31
31
|
payments_url?: string;
|
|
32
32
|
terms_of_service?: string;
|
|
33
33
|
};
|
|
34
|
-
declare class Nip11Error extends NostrError {
|
|
34
|
+
export declare class Nip11Error extends NostrError {
|
|
35
|
+
name: string;
|
|
36
|
+
}
|
|
35
37
|
/** Convert a relay websocket URL to the HTTP URL that serves the NIP-11 document. */
|
|
36
|
-
declare function relayInfoHttpUrl(wsUrl: string): string;
|
|
37
|
-
declare function fetchRelayInformation(wsUrl: string, opts?: {
|
|
38
|
+
export declare function relayInfoHttpUrl(wsUrl: string): string;
|
|
39
|
+
export declare function fetchRelayInformation(wsUrl: string, opts?: {
|
|
38
40
|
fetch?: Nip11Fetch;
|
|
39
41
|
signal?: AbortSignal;
|
|
40
42
|
}): Promise<RelayInformation>;
|
|
41
43
|
//#endregion
|
|
42
|
-
export { Nip11Error, Nip11Fetch, RelayInformation, fetchRelayInformation, relayInfoHttpUrl };
|
|
43
44
|
//# sourceMappingURL=nip11.d.mts.map
|
package/dist/nips/nip11.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as requireGlobalFetch, t as fetchManual } from "../http-
|
|
1
|
+
import { c as isRecord, g as NostrError } from "../util-LfQirgDk.mjs";
|
|
2
|
+
import { n as requireGlobalFetch, t as fetchManual } from "../http-CuazNRIP.mjs";
|
|
3
3
|
//#region src/nips/nip11.ts
|
|
4
4
|
/**
|
|
5
5
|
* NIP-11: Relay Information Document
|
|
6
|
+
*
|
|
6
7
|
* @see https://github.com/nostr-protocol/nips/blob/master/11.md
|
|
7
8
|
*/
|
|
8
9
|
const ACCEPT = "application/nostr+json";
|
|
@@ -36,14 +37,16 @@ const LIMITATION_BOOLEANS = [
|
|
|
36
37
|
"payment_required",
|
|
37
38
|
"restricted_writes"
|
|
38
39
|
];
|
|
39
|
-
var Nip11Error = class extends NostrError {
|
|
40
|
+
var Nip11Error = class extends NostrError {
|
|
41
|
+
name = "Nip11Error";
|
|
42
|
+
};
|
|
40
43
|
/** Convert a relay websocket URL to the HTTP URL that serves the NIP-11 document. */
|
|
41
44
|
function relayInfoHttpUrl(wsUrl) {
|
|
42
45
|
let url;
|
|
43
46
|
try {
|
|
44
47
|
url = new URL(wsUrl);
|
|
45
|
-
} catch (
|
|
46
|
-
throw new Nip11Error(`invalid relay URL: ${wsUrl}`, { cause:
|
|
48
|
+
} catch (error) {
|
|
49
|
+
throw new Nip11Error(`invalid relay URL: ${wsUrl}`, { cause: error instanceof Error ? error : void 0 });
|
|
47
50
|
}
|
|
48
51
|
if (url.protocol === "wss:") url.protocol = "https:";
|
|
49
52
|
else if (url.protocol === "ws:") url.protocol = "http:";
|
|
@@ -54,16 +57,16 @@ function isNonNegativeInteger(value) {
|
|
|
54
57
|
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
55
58
|
}
|
|
56
59
|
function parseRelayInformation(json) {
|
|
57
|
-
if (
|
|
60
|
+
if (!isRecord(json)) throw new Nip11Error("relay information document must be a JSON object");
|
|
58
61
|
const raw = json;
|
|
59
62
|
const info = {};
|
|
60
63
|
for (const key of STRING_FIELDS) {
|
|
61
64
|
const value = raw[key];
|
|
62
65
|
if (typeof value === "string") info[key] = value;
|
|
63
66
|
}
|
|
64
|
-
if (Array.isArray(raw
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
if (Array.isArray(raw["supported_nips"])) info.supported_nips = raw["supported_nips"].filter(isNonNegativeInteger);
|
|
68
|
+
const rawLim = raw["limitation"];
|
|
69
|
+
if (isRecord(rawLim)) {
|
|
67
70
|
const limitation = {};
|
|
68
71
|
for (const key of LIMITATION_NUMBERS) {
|
|
69
72
|
const value = rawLim[key];
|
|
@@ -88,8 +91,8 @@ async function fetchRelayInformation(wsUrl, opts) {
|
|
|
88
91
|
let json;
|
|
89
92
|
try {
|
|
90
93
|
json = await res.json();
|
|
91
|
-
} catch (
|
|
92
|
-
throw new Nip11Error("relay information document is not valid JSON", { cause:
|
|
94
|
+
} catch (error) {
|
|
95
|
+
throw new Nip11Error("relay information document is not valid JSON", { cause: error instanceof Error ? error : void 0 });
|
|
93
96
|
}
|
|
94
97
|
return parseRelayInformation(json);
|
|
95
98
|
}
|
package/dist/nips/nip11.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nip11.mjs","names":[],"sources":["../../src/nips/nip11.ts"],"sourcesContent":["/**\n * NIP-11: Relay Information Document\n * @see https://github.com/nostr-protocol/nips/blob/master/11.md\n */\nimport { NostrError } from \"../core/error.ts\";\nimport { fetchManual, requireGlobalFetch
|
|
1
|
+
{"version":3,"file":"nip11.mjs","names":[],"sources":["../../src/nips/nip11.ts"],"sourcesContent":["/**\n * NIP-11: Relay Information Document\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/11.md\n */\nimport { NostrError } from \"../core/error.ts\";\nimport { isRecord } from \"../core/util.ts\";\nimport { fetchManual, requireGlobalFetch } from \"./http.ts\";\nimport type { ManualFetch } from \"./http.ts\";\n\nconst ACCEPT = \"application/nostr+json\";\n\nconst STRING_FIELDS = [\n \"name\",\n \"description\",\n \"banner\",\n \"icon\",\n \"pubkey\",\n \"self\",\n \"contact\",\n \"software\",\n \"version\",\n \"payments_url\",\n \"terms_of_service\",\n] as const;\n\nconst LIMITATION_NUMBERS = [\n \"max_message_length\",\n \"max_subscriptions\",\n \"max_limit\",\n \"max_subid_length\",\n \"max_event_tags\",\n \"max_content_length\",\n \"min_pow_difficulty\",\n \"created_at_lower_limit\",\n \"created_at_upper_limit\",\n \"default_limit\",\n] as const;\n\nconst LIMITATION_BOOLEANS = [\"auth_required\", \"payment_required\", \"restricted_writes\"] as const;\n\nexport type Nip11Fetch = ManualFetch;\n\nexport type RelayInformation = {\n name?: string;\n description?: string;\n banner?: string;\n icon?: string;\n pubkey?: string;\n self?: string;\n contact?: string;\n supported_nips?: number[];\n software?: string;\n version?: string;\n limitation?: {\n max_message_length?: number;\n max_subscriptions?: number;\n max_limit?: number;\n max_subid_length?: number;\n max_event_tags?: number;\n max_content_length?: number;\n default_limit?: number;\n auth_required?: boolean;\n payment_required?: boolean;\n restricted_writes?: boolean;\n min_pow_difficulty?: number;\n created_at_lower_limit?: number;\n created_at_upper_limit?: number;\n };\n payments_url?: string;\n terms_of_service?: string;\n};\n\nexport class Nip11Error extends NostrError {\n override name = \"Nip11Error\";\n}\n\n/** Convert a relay websocket URL to the HTTP URL that serves the NIP-11 document. */\nexport function relayInfoHttpUrl(wsUrl: string): string {\n let url: URL;\n try {\n url = new URL(wsUrl);\n } catch (error) {\n throw new Nip11Error(`invalid relay URL: ${wsUrl}`, {\n cause: error instanceof Error ? error : undefined,\n });\n }\n\n if (url.protocol === \"wss:\") {\n url.protocol = \"https:\";\n } else if (url.protocol === \"ws:\") {\n url.protocol = \"http:\";\n } else if (url.protocol !== \"https:\" && url.protocol !== \"http:\") {\n throw new Nip11Error(`unsupported relay URL scheme: ${url.protocol}`);\n }\n\n return url.toString();\n}\n\nfunction isNonNegativeInteger(value: unknown): value is number {\n return typeof value === \"number\" && Number.isInteger(value) && value >= 0;\n}\n\nfunction parseRelayInformation(json: unknown): RelayInformation {\n if (!isRecord(json)) {\n throw new Nip11Error(\"relay information document must be a JSON object\");\n }\n const raw = json;\n const info: RelayInformation = {};\n\n for (const key of STRING_FIELDS) {\n const value = raw[key];\n if (typeof value === \"string\") {\n info[key] = value;\n }\n }\n\n if (Array.isArray(raw[\"supported_nips\"])) {\n info.supported_nips = raw[\"supported_nips\"].filter(isNonNegativeInteger);\n }\n\n const rawLim = raw[\"limitation\"];\n if (isRecord(rawLim)) {\n const limitation: NonNullable<RelayInformation[\"limitation\"]> = {};\n for (const key of LIMITATION_NUMBERS) {\n const value = rawLim[key];\n if (isNonNegativeInteger(value)) {\n limitation[key] = value;\n }\n }\n for (const key of LIMITATION_BOOLEANS) {\n const value = rawLim[key];\n if (typeof value === \"boolean\") {\n limitation[key] = value;\n }\n }\n if (Object.keys(limitation).length > 0) {\n info.limitation = limitation;\n }\n }\n\n return info;\n}\n\nexport async function fetchRelayInformation(\n wsUrl: string,\n opts?: { fetch?: Nip11Fetch; signal?: AbortSignal },\n): Promise<RelayInformation> {\n const httpUrl = relayInfoHttpUrl(wsUrl);\n const fetchImpl =\n opts?.fetch ??\n requireGlobalFetch(() => new Nip11Error(\"no fetch implementation available; pass opts.fetch\"));\n\n const res = await fetchManual(\n fetchImpl,\n httpUrl,\n { headers: { Accept: ACCEPT }, signal: opts?.signal },\n (cause) =>\n new Nip11Error(`relay information request failed: ${httpUrl}`, {\n cause: cause instanceof Error ? cause : undefined,\n }),\n );\n\n if (!res.ok) {\n throw new Nip11Error(`relay information request failed: HTTP ${res.status}`);\n }\n\n let json: unknown;\n try {\n json = await res.json();\n } catch (error) {\n throw new Nip11Error(\"relay information document is not valid JSON\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n\n return parseRelayInformation(json);\n}\n"],"mappings":";;;;;;;;AAUA,MAAM,SAAS;AAEf,MAAM,gBAAgB;CACpB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,qBAAqB;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,sBAAsB;CAAC;CAAiB;CAAoB;AAAmB;AAkCrF,IAAa,aAAb,cAAgC,WAAW;CACzC,OAAgB;AAClB;;AAGA,SAAgB,iBAAiB,OAAuB;CACtD,IAAI;CACJ,IAAI;EACF,MAAM,IAAI,IAAI,KAAK;CACrB,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,sBAAsB,SAAS,EAClD,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;CAEA,IAAI,IAAI,aAAa,QACnB,IAAI,WAAW;MACV,IAAI,IAAI,aAAa,OAC1B,IAAI,WAAW;MACV,IAAI,IAAI,aAAa,YAAY,IAAI,aAAa,SACvD,MAAM,IAAI,WAAW,iCAAiC,IAAI,UAAU;CAGtE,OAAO,IAAI,SAAS;AACtB;AAEA,SAAS,qBAAqB,OAAiC;CAC7D,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,KAAK,KAAK,SAAS;AAC1E;AAEA,SAAS,sBAAsB,MAAiC;CAC9D,IAAI,CAAC,SAAS,IAAI,GAChB,MAAM,IAAI,WAAW,kDAAkD;CAEzE,MAAM,MAAM;CACZ,MAAM,OAAyB,CAAC;CAEhC,KAAK,MAAM,OAAO,eAAe;EAC/B,MAAM,QAAQ,IAAI;EAClB,IAAI,OAAO,UAAU,UACnB,KAAK,OAAO;CAEhB;CAEA,IAAI,MAAM,QAAQ,IAAI,iBAAiB,GACrC,KAAK,iBAAiB,IAAI,iBAAiB,CAAC,OAAO,oBAAoB;CAGzE,MAAM,SAAS,IAAI;CACnB,IAAI,SAAS,MAAM,GAAG;EACpB,MAAM,aAA0D,CAAC;EACjE,KAAK,MAAM,OAAO,oBAAoB;GACpC,MAAM,QAAQ,OAAO;GACrB,IAAI,qBAAqB,KAAK,GAC5B,WAAW,OAAO;EAEtB;EACA,KAAK,MAAM,OAAO,qBAAqB;GACrC,MAAM,QAAQ,OAAO;GACrB,IAAI,OAAO,UAAU,WACnB,WAAW,OAAO;EAEtB;EACA,IAAI,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,GACnC,KAAK,aAAa;CAEtB;CAEA,OAAO;AACT;AAEA,eAAsB,sBACpB,OACA,MAC2B;CAC3B,MAAM,UAAU,iBAAiB,KAAK;CACtC,MAAM,YACJ,MAAM,SACN,yBAAyB,IAAI,WAAW,oDAAoD,CAAC;CAE/F,MAAM,MAAM,MAAM,YAChB,WACA,SACA;EAAE,SAAS,EAAE,QAAQ,OAAO;EAAG,QAAQ,MAAM;CAAO,IACnD,UACC,IAAI,WAAW,qCAAqC,WAAW,EAC7D,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC,CACL;CAEA,IAAI,CAAC,IAAI,IACP,MAAM,IAAI,WAAW,0CAA0C,IAAI,QAAQ;CAG7E,IAAI;CACJ,IAAI;EACF,OAAO,MAAM,IAAI,KAAK;CACxB,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,gDAAgD,EACnE,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;CAEA,OAAO,sBAAsB,IAAI;AACnC"}
|
package/dist/nips/nip13.d.mts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { r as UnsignedEvent } from "../event-
|
|
2
|
-
import { a as NostrError } from "../error-
|
|
1
|
+
import { r as UnsignedEvent } from "../event-fU3Y01PR.mjs";
|
|
2
|
+
import { a as NostrError } from "../error-CR8Urc84.mjs";
|
|
3
3
|
//#region src/nips/nip13.d.ts
|
|
4
|
-
declare class Nip13Error extends NostrError {
|
|
4
|
+
export declare class Nip13Error extends NostrError {
|
|
5
|
+
name: string;
|
|
5
6
|
constructor(message: string, options?: ErrorOptions);
|
|
6
7
|
}
|
|
7
|
-
type MinePowOptions = {
|
|
8
|
+
export type MinePowOptions = {
|
|
8
9
|
/** Yield to the event loop after this many hashes. Default 1000. */
|
|
9
10
|
yieldEvery?: number;
|
|
10
11
|
signal?: AbortSignal;
|
|
11
12
|
};
|
|
12
13
|
/** Leading zero bits of a hex event id or raw sha256 bytes. */
|
|
13
|
-
declare function getPow(idOrHash: string | Uint8Array): number;
|
|
14
|
+
export declare function getPow(idOrHash: string | Uint8Array): number;
|
|
14
15
|
/**
|
|
15
|
-
* Returns a new unsigned event with nonce tag and computed id.
|
|
16
|
-
*
|
|
16
|
+
* Returns a new unsigned event with nonce tag and computed id. Does not mutate input. Yields every
|
|
17
|
+
* `yieldEvery` hashes.
|
|
17
18
|
*/
|
|
18
|
-
declare function minePow(unsigned: UnsignedEvent, difficulty: number, opts?: MinePowOptions): Promise<UnsignedEvent & {
|
|
19
|
+
export declare function minePow(unsigned: UnsignedEvent, difficulty: number, opts?: MinePowOptions): Promise<UnsignedEvent & {
|
|
19
20
|
id: string;
|
|
20
21
|
}>;
|
|
21
22
|
//#endregion
|
|
22
|
-
export { MinePowOptions, Nip13Error, getPow, minePow };
|
|
23
23
|
//# sourceMappingURL=nip13.d.mts.map
|
package/dist/nips/nip13.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { d as utf8Encoder, g as NostrError, i as bytesToHex, o as isHex32 } from "../util-LfQirgDk.mjs";
|
|
2
|
+
import { l as serializeEvent } from "../event-D8Nfoh4K.mjs";
|
|
3
|
+
import { t as abortReason } from "../abort-Ba8tDrOI.mjs";
|
|
4
4
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
5
5
|
//#region src/nips/nip13.ts
|
|
6
6
|
/**
|
|
7
|
-
* NIP-13: Proof of Work.
|
|
8
|
-
* Does not import signer, relay, or client. Does not sign.
|
|
7
|
+
* NIP-13: Proof of Work. Does not import signer, relay, or client. Does not sign.
|
|
9
8
|
*
|
|
10
9
|
* @see https://github.com/nostr-protocol/nips/blob/master/13.md
|
|
11
10
|
*/
|
|
12
11
|
var Nip13Error = class extends NostrError {
|
|
12
|
+
name = "Nip13Error";
|
|
13
13
|
constructor(message, options) {
|
|
14
14
|
super(message, options);
|
|
15
15
|
}
|
|
@@ -17,9 +17,10 @@ var Nip13Error = class extends NostrError {
|
|
|
17
17
|
/** Leading zero bits of a hex event id or raw sha256 bytes. */
|
|
18
18
|
function getPow(idOrHash) {
|
|
19
19
|
if (typeof idOrHash === "string") {
|
|
20
|
+
if (!isHex32(idOrHash.toLowerCase())) throw new Nip13Error(`expected 64-char hex event id, got ${idOrHash.length} chars`);
|
|
20
21
|
let count = 0;
|
|
21
22
|
for (let i = 0; i < idOrHash.length; i += 8) {
|
|
22
|
-
const chunk = Number.parseInt(idOrHash.
|
|
23
|
+
const chunk = Number.parseInt(idOrHash.slice(i, i + 8), 16);
|
|
23
24
|
if (chunk === 0) count += 32;
|
|
24
25
|
else {
|
|
25
26
|
count += Math.clz32(chunk);
|
|
@@ -29,19 +30,16 @@ function getPow(idOrHash) {
|
|
|
29
30
|
return count;
|
|
30
31
|
}
|
|
31
32
|
let count = 0;
|
|
32
|
-
for (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
count += Math.clz32(byte) - 24;
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
33
|
+
for (const byte of idOrHash) if (byte === 0) count += 8;
|
|
34
|
+
else {
|
|
35
|
+
count += Math.clz32(byte) - 24;
|
|
36
|
+
break;
|
|
39
37
|
}
|
|
40
38
|
return count;
|
|
41
39
|
}
|
|
42
40
|
/**
|
|
43
|
-
* Returns a new unsigned event with nonce tag and computed id.
|
|
44
|
-
*
|
|
41
|
+
* Returns a new unsigned event with nonce tag and computed id. Does not mutate input. Yields every
|
|
42
|
+
* `yieldEvery` hashes.
|
|
45
43
|
*/
|
|
46
44
|
async function minePow(unsigned, difficulty, opts) {
|
|
47
45
|
const signal = opts?.signal;
|
|
@@ -61,7 +59,7 @@ async function minePow(unsigned, difficulty, opts) {
|
|
|
61
59
|
let count = 0;
|
|
62
60
|
let iterations = 0;
|
|
63
61
|
while (true) {
|
|
64
|
-
if (signal?.aborted) throw
|
|
62
|
+
if (signal?.aborted === true) throw abortReason(signal);
|
|
65
63
|
const now = Math.floor(Date.now() / 1e3);
|
|
66
64
|
if (now !== mined.created_at) {
|
|
67
65
|
count = 0;
|
|
@@ -77,7 +75,7 @@ async function minePow(unsigned, difficulty, opts) {
|
|
|
77
75
|
if (iterations >= yieldEvery) {
|
|
78
76
|
iterations = 0;
|
|
79
77
|
await new Promise((resolve) => {
|
|
80
|
-
setTimeout(resolve
|
|
78
|
+
setTimeout(resolve);
|
|
81
79
|
});
|
|
82
80
|
}
|
|
83
81
|
}
|
package/dist/nips/nip13.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nip13.mjs","names":[],"sources":["../../src/nips/nip13.ts"],"sourcesContent":["/**\n * NIP-13: Proof of Work
|
|
1
|
+
{"version":3,"file":"nip13.mjs","names":[],"sources":["../../src/nips/nip13.ts"],"sourcesContent":["/**\n * NIP-13: Proof of Work. 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\";\n\nimport { abortReason } from \"../core/abort.ts\";\nimport { NostrError } from \"../core/error.ts\";\nimport { serializeEvent } from \"../core/event.ts\";\nimport type { UnsignedEvent } from \"../core/event.ts\";\nimport { bytesToHex, isHex32, utf8Encoder } from \"../core/util.ts\";\n\nexport class Nip13Error extends NostrError {\n override name = \"Nip13Error\";\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 if (!isHex32(idOrHash.toLowerCase())) {\n throw new Nip13Error(`expected 64-char hex event id, got ${idOrHash.length} chars`);\n }\n let count = 0;\n for (let i = 0; i < idOrHash.length; i += 8) {\n const chunk = Number.parseInt(idOrHash.slice(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 (const byte of idOrHash) {\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. Does not mutate input. Yields every\n * `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 === true) {\n throw abortReason(signal);\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 // oxlint-disable-next-line no-await-in-loop -- the yield is part of each mining round\n await new Promise<void>((resolve) => {\n setTimeout(resolve);\n });\n }\n }\n}\n"],"mappings":";;;;;;;;;;AAaA,IAAa,aAAb,cAAgC,WAAW;CACzC,OAAgB;CAChB,YAAY,SAAiB,SAAwB;EACnD,MAAM,SAAS,OAAO;CACxB;AACF;;AASA,SAAgB,OAAO,UAAuC;CAC5D,IAAI,OAAO,aAAa,UAAU;EAChC,IAAI,CAAC,QAAQ,SAAS,YAAY,CAAC,GACjC,MAAM,IAAI,WAAW,sCAAsC,SAAS,OAAO,OAAO;EAEpF,IAAI,QAAQ;EACZ,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GAAG;GAC3C,MAAM,QAAQ,OAAO,SAAS,SAAS,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE;GAC1D,IAAI,UAAU,GACZ,SAAS;QACJ;IACL,SAAS,KAAK,MAAM,KAAK;IACzB;GACF;EACF;EACA,OAAO;CACT;CAEA,IAAI,QAAQ;CACZ,KAAK,MAAM,QAAQ,UACjB,IAAI,SAAS,GACX,SAAS;MACJ;EACL,SAAS,KAAK,MAAM,IAAI,IAAI;EAC5B;CACF;CAEF,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,YAAY,MACtB,MAAM,YAAY,MAAM;EAG1B,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;GAEb,MAAM,IAAI,SAAe,YAAY;IACnC,WAAW,OAAO;GACpB,CAAC;EACH;CACF;AACF"}
|
package/dist/nips/nip17.d.mts
CHANGED
|
@@ -1,2 +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-
|
|
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-DNEkz8rM.mjs";
|
|
2
2
|
export { ChatMessageOptions, Nip17Error, Recipient, ReplyTo, buildChatMessageRumor, dmRelayListEventBuilder, dmRelayListToTags, normalizeRecipients, parseDmRelayList, requireDmRelays, wrapDirectMessage };
|