@qntx/nostr 0.14.0 → 0.15.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/dist/{builder-CGgM_QHT.mjs → builder-70CC2EBq.mjs} +4 -4
- package/dist/{builder-CGgM_QHT.mjs.map → builder-70CC2EBq.mjs.map} +1 -1
- package/dist/{builder-C72nJITG.d.mts → builder-Dk00TdNf.d.mts} +2 -2
- package/dist/{client-COkEihj_.mjs → client-BXmvZ7vU.mjs} +45 -30
- package/dist/client-BXmvZ7vU.mjs.map +1 -0
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs +1 -2
- package/dist/core.d.mts +8 -7
- package/dist/core.mjs +8 -8
- package/dist/{error-Dyb89ahb.mjs → error-CoP2myne.mjs} +2 -2
- package/dist/{error-Dyb89ahb.mjs.map → error-CoP2myne.mjs.map} +1 -1
- package/dist/{error-BPqAU5RB.d.mts → error-DE1UCZ2J.d.mts} +6 -2
- package/dist/{error-BjSUsx6M.d.mts → error-IHMcLy8-.d.mts} +2 -2
- package/dist/{error-QyECUDIp.mjs → error-VDn7Nv-6.mjs} +2 -2
- package/dist/{error-QyECUDIp.mjs.map → error-VDn7Nv-6.mjs.map} +1 -1
- package/dist/{event-DvM1f03b.mjs → event-DHtcm0jM.mjs} +17 -6
- package/dist/event-DHtcm0jM.mjs.map +1 -0
- package/dist/{filter-3jOh4uLN.mjs → filter-BbShkmc4.mjs} +34 -28
- package/dist/filter-BbShkmc4.mjs.map +1 -0
- package/dist/{filter-oYG2zjJ3.d.mts → filter-w1R-mU63.d.mts} +8 -6
- package/dist/{gossip-BvCItMVZ.d.mts → gossip-C-7WhJ7v.d.mts} +3 -3
- package/dist/{gossip-CRyhK6E6.mjs → gossip-CejMm_QM.mjs} +13 -11
- package/dist/gossip-CejMm_QM.mjs.map +1 -0
- package/dist/gossip.d.mts +1 -1
- package/dist/gossip.mjs +1 -2
- package/dist/{index-B2JlP2Jf.d.mts → index-11znTLM0.d.mts} +4 -4
- package/dist/{index-DzHmhHqD.d.mts → index-B7nMjbyt.d.mts} +3 -3
- package/dist/{index-muydfuYD.d.mts → index-BuNI8JH_.d.mts} +13 -13
- package/dist/{index-Dpj_MUxb.d.mts → index-C5QomO2D.d.mts} +10 -3
- package/dist/{index-DCobP2Jj.d.mts → index-DDZjqV48.d.mts} +10 -10
- package/dist/{index-CcgmG3F6.d.mts → index-DYk24u9o.d.mts} +12 -7
- package/dist/index.d.mts +21 -20
- package/dist/index.mjs +19 -19
- package/dist/{key-D_oEutQ6.mjs → key-Bt0ow7mI.mjs} +33 -11
- package/dist/key-Bt0ow7mI.mjs.map +1 -0
- package/dist/{key-DojW2QZl.d.mts → key-E0AJKMRB.d.mts} +19 -11
- package/dist/{loaders-BRh2riV9.mjs → loaders-COGGrZY4.mjs} +7 -7
- package/dist/loaders-COGGrZY4.mjs.map +1 -0
- package/dist/loaders.d.mts +2 -2
- package/dist/loaders.mjs +1 -2
- package/dist/{memory-D_GdEQYG.mjs → memory-Dq4dg-U4.mjs} +2 -2
- package/dist/{memory-D_GdEQYG.mjs.map → memory-Dq4dg-U4.mjs.map} +1 -1
- package/dist/{memory-index-3GSrPIWj.mjs → memory-index-DhlmbqWx.mjs} +19 -18
- package/dist/memory-index-DhlmbqWx.mjs.map +1 -0
- package/dist/{message-BMfreCQc.mjs → message-D6k16gtg.mjs} +20 -20
- package/dist/message-D6k16gtg.mjs.map +1 -0
- package/dist/{verifier-DhAbqVJF.d.mts → message-u_7KOq6Q.d.mts} +3 -22
- package/dist/{nip17-BZvFQLcs.d.mts → nip17--YwFSuph.d.mts} +4 -4
- package/dist/{nip19-D4RKn6GC.d.mts → nip19-qtyc5sCl.d.mts} +2 -2
- package/dist/{nip46-i8L8_nIm.d.mts → nip46-C_ZFQprM.d.mts} +2 -2
- package/dist/{nip51-C_kTYcud.d.mts → nip51-yi9w8FsM.d.mts} +2 -2
- package/dist/{nip59-DfJ6Moeu.d.mts → nip59-B6Ey8L9z.d.mts} +12 -3
- package/dist/{nip65-CPNE0SV0.d.mts → nip65-XrBjV9WM.d.mts} +2 -2
- package/dist/{nip77-DhpoL7d8.d.mts → nip77-BAXijIt_.d.mts} +2 -2
- package/dist/nips/blossom.d.mts +3 -2
- package/dist/nips/blossom.mjs +11 -11
- package/dist/nips/blossom.mjs.map +1 -1
- package/dist/nips/nip04.d.mts +3 -2
- package/dist/nips/nip04.mjs +10 -12
- package/dist/nips/nip04.mjs.map +1 -1
- package/dist/nips/nip05.d.mts +2 -2
- package/dist/nips/nip05.mjs +9 -8
- package/dist/nips/nip05.mjs.map +1 -1
- package/dist/nips/nip10.d.mts +3 -2
- package/dist/nips/nip10.mjs +19 -19
- package/dist/nips/nip10.mjs.map +1 -1
- package/dist/nips/nip11.d.mts +2 -1
- package/dist/nips/nip11.mjs +6 -4
- package/dist/nips/nip11.mjs.map +1 -1
- package/dist/nips/nip13.d.mts +1 -1
- package/dist/nips/nip13.mjs +2 -2
- package/dist/nips/nip13.mjs.map +1 -1
- package/dist/nips/nip17.d.mts +1 -1
- package/dist/nips/nip17.mjs +6 -6
- package/dist/nips/nip17.mjs.map +1 -1
- package/dist/nips/nip19.d.mts +1 -1
- package/dist/nips/nip19.mjs +1 -1
- package/dist/nips/nip21.d.mts +2 -2
- package/dist/nips/nip21.mjs +3 -3
- package/dist/nips/nip21.mjs.map +1 -1
- package/dist/nips/nip27.d.mts +3 -3
- package/dist/nips/nip27.mjs +308 -85
- package/dist/nips/nip27.mjs.map +1 -1
- package/dist/nips/nip42.mjs +1 -1
- package/dist/nips/nip44.d.mts +2 -1
- package/dist/nips/nip44.mjs +4 -4
- package/dist/nips/nip44.mjs.map +1 -1
- package/dist/nips/nip46.d.mts +1 -1
- package/dist/nips/nip46.mjs +9 -8
- package/dist/nips/nip46.mjs.map +1 -1
- package/dist/nips/nip49.d.mts +1 -1
- package/dist/nips/nip49.mjs +1 -1
- package/dist/nips/nip51.d.mts +1 -1
- package/dist/nips/nip51.mjs +14 -14
- package/dist/nips/nip51.mjs.map +1 -1
- package/dist/nips/nip57.mjs +10 -10
- package/dist/nips/nip57.mjs.map +1 -1
- package/dist/nips/nip59.d.mts +1 -1
- package/dist/nips/nip59.mjs +14 -11
- package/dist/nips/nip59.mjs.map +1 -1
- package/dist/nips/nip65.d.mts +1 -1
- package/dist/nips/nip65.mjs +2 -2
- package/dist/nips/nip77.d.mts +1 -1
- package/dist/nips/nip77.mjs +2 -2
- package/dist/nips/nip77.mjs.map +1 -1
- package/dist/nips/nip98.d.mts +1 -1
- package/dist/nips/nip98.mjs +5 -5
- package/dist/nips/nip98.mjs.map +1 -1
- package/dist/{pool-MehgIIzm.d.mts → pool-BMfTQZjM.d.mts} +5 -4
- package/dist/{pool-lMT_mvzn.mjs → pool-D13eVCqc.mjs} +5 -5
- package/dist/{pool-lMT_mvzn.mjs.map → pool-D13eVCqc.mjs.map} +1 -1
- package/dist/{put-BEM5j8xK.mjs → put-DuymazkT.mjs} +5 -5
- package/dist/put-DuymazkT.mjs.map +1 -0
- package/dist/{reactive-B7lPFgTS.d.mts → reactive-BkzOsSwU.d.mts} +3 -3
- package/dist/{reactive-DFHY-f00.mjs → reactive-C0-rTqN9.mjs} +10 -9
- package/dist/reactive-C0-rTqN9.mjs.map +1 -0
- package/dist/{relay-DIR77xQ2.mjs → relay-CbgToreY.mjs} +6 -6
- package/dist/{relay-DIR77xQ2.mjs.map → relay-CbgToreY.mjs.map} +1 -1
- package/dist/relay.d.mts +3 -3
- package/dist/relay.mjs +2 -2
- package/dist/{signer-Bha9gQy8.mjs → signer-DQzjm3pt.mjs} +28 -14
- package/dist/signer-DQzjm3pt.mjs.map +1 -0
- package/dist/signer.d.mts +2 -2
- package/dist/signer.mjs +2 -2
- package/dist/{storage-n2BE1bdY.mjs → storage-DvDAuxzC.mjs} +26 -41
- package/dist/storage-DvDAuxzC.mjs.map +1 -0
- package/dist/storage.d.mts +3 -3
- package/dist/storage.mjs +4 -4
- package/dist/store.d.mts +1 -1
- package/dist/store.mjs +1 -2
- package/dist/testing.d.mts +2 -2
- package/dist/testing.mjs +7 -7
- package/dist/{types-DiVVyf1e.d.mts → types-COupAqtN.d.mts} +2 -2
- package/dist/{types-BJ4wZ7JJ.d.mts → types-DF8yT6EG.d.mts} +7 -1
- package/dist/{util-DfsyJ9nz.mjs → util-BcRBO6o-.mjs} +18 -3
- package/dist/util-BcRBO6o-.mjs.map +1 -0
- package/dist/{verifier-DEHUy4AD.mjs → verifier-DBSPHzGg.mjs} +6 -15
- package/dist/verifier-DBSPHzGg.mjs.map +1 -0
- package/dist/verifier-Or2nDvHb.d.mts +16 -0
- package/package.json +3 -3
- package/dist/client-COkEihj_.mjs.map +0 -1
- package/dist/event-DvM1f03b.mjs.map +0 -1
- package/dist/filter-3jOh4uLN.mjs.map +0 -1
- package/dist/gossip-CRyhK6E6.mjs.map +0 -1
- package/dist/key-D_oEutQ6.mjs.map +0 -1
- package/dist/loaders-BRh2riV9.mjs.map +0 -1
- package/dist/memory-index-3GSrPIWj.mjs.map +0 -1
- package/dist/message-BMfreCQc.mjs.map +0 -1
- package/dist/put-BEM5j8xK.mjs.map +0 -1
- package/dist/reactive-DFHY-f00.mjs.map +0 -1
- package/dist/signer-Bha9gQy8.mjs.map +0 -1
- package/dist/storage-n2BE1bdY.mjs.map +0 -1
- package/dist/util-DfsyJ9nz.mjs.map +0 -1
- package/dist/verifier-DEHUy4AD.mjs.map +0 -1
package/dist/nips/nip27.mjs
CHANGED
|
@@ -1,27 +1,211 @@
|
|
|
1
|
+
import { d as lowerAscii } from "../util-BcRBO6o-.mjs";
|
|
1
2
|
import { decode } from "./nip19.mjs";
|
|
2
3
|
//#region src/nips/nip27.ts
|
|
3
|
-
const
|
|
4
|
+
const WORD_CHAR = /[\p{L}\p{N}]/u;
|
|
5
|
+
const TAG_CHAR = /[\p{L}\p{M}\p{N}_]/u;
|
|
6
|
+
const WHITE_SPACE = /\p{White_Space}/u;
|
|
7
|
+
/** ASCII lowercase of one code point (`A`-`Z` → `a`-`z`; everything else unchanged). */
|
|
8
|
+
function foldAscii(c) {
|
|
9
|
+
return c >= "A" && c <= "Z" ? c.toLowerCase() : c;
|
|
10
|
+
}
|
|
11
|
+
/** `0`, `2`–`9`, `a c-h j-n p-z`, both ASCII cases — the bech32 data charset. */
|
|
12
|
+
function isBech32Char(c) {
|
|
13
|
+
const l = foldAscii(c);
|
|
14
|
+
return l >= "0" && l <= "9" && l !== "1" || l >= "a" && l <= "z" && l !== "b" && l !== "i" && l !== "o";
|
|
15
|
+
}
|
|
16
|
+
/** `[A-Za-z0-9_-]` — the emoji shortcode charset. */
|
|
17
|
+
function isEmojiChar(c) {
|
|
18
|
+
return c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c >= "0" && c <= "9" || c === "_" || c === "-";
|
|
19
|
+
}
|
|
20
|
+
function isTagChar(c) {
|
|
21
|
+
return TAG_CHAR.test(c);
|
|
22
|
+
}
|
|
23
|
+
function isWordChar(c) {
|
|
24
|
+
return WORD_CHAR.test(c);
|
|
25
|
+
}
|
|
26
|
+
/** URL-run stops: Unicode White_Space plus `<>`, `"`, CJK punctuation and full-width forms. */
|
|
27
|
+
function isUrlStop(c) {
|
|
28
|
+
if (WHITE_SPACE.test(c) || c === "<" || c === ">" || c === "\"") return true;
|
|
29
|
+
const cp = c.codePointAt(0);
|
|
30
|
+
return cp !== void 0 && (cp >= 12288 && cp <= 12351 || cp >= 65280 && cp <= 65519);
|
|
31
|
+
}
|
|
4
32
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
33
|
+
* ASCII-insensitive literal match at the start of `rest` — the keyword form (`nostr:`,
|
|
34
|
+
* `lightning:`, `http`…). Returns the matched length in UTF-16 units, or `undefined`. Literals are
|
|
35
|
+
* ASCII, so the length equals the literal's length.
|
|
8
36
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
37
|
+
function litAt(rest, lit) {
|
|
38
|
+
if (rest.length < lit.length) return;
|
|
39
|
+
for (let i = 0; i < lit.length; i++) if (foldAscii(rest[i] ?? "") !== lit[i]) return;
|
|
40
|
+
return lit.length;
|
|
41
|
+
}
|
|
42
|
+
/** UTF-16 length of the maximal bech32-charset run at the start of `rest` (may be 0). */
|
|
43
|
+
function bech32Run(rest) {
|
|
44
|
+
let n = 0;
|
|
45
|
+
for (const c of rest) {
|
|
46
|
+
if (!isBech32Char(c)) break;
|
|
47
|
+
n += c.length;
|
|
48
|
+
}
|
|
49
|
+
return n;
|
|
50
|
+
}
|
|
51
|
+
/** `1` then a non-empty bech32-charset run — the entity/invoice separator and data. */
|
|
52
|
+
function sepThenData(rest, at) {
|
|
53
|
+
if (rest[at] !== "1") return;
|
|
54
|
+
const run = bech32Run(rest.slice(at + 1));
|
|
55
|
+
return run > 0 ? at + 1 + run : void 0;
|
|
56
|
+
}
|
|
57
|
+
const NIP19_PREFIXES = [
|
|
58
|
+
"npub",
|
|
59
|
+
"nprofile",
|
|
60
|
+
"note",
|
|
61
|
+
"nevent",
|
|
62
|
+
"naddr",
|
|
63
|
+
"nsec"
|
|
64
|
+
];
|
|
65
|
+
const BARE_PREFIXES = [
|
|
66
|
+
"npub",
|
|
67
|
+
"nprofile",
|
|
68
|
+
"note",
|
|
69
|
+
"nevent",
|
|
70
|
+
"naddr"
|
|
71
|
+
];
|
|
72
|
+
/** `nostr:` + NIP-19 prefix + `1` + charset. Returns `[codeStart, end]` — code start skips `nostr:`. */
|
|
73
|
+
function nostrAt(rest) {
|
|
74
|
+
const codeStart = litAt(rest, "nostr:");
|
|
75
|
+
if (codeStart === void 0) return;
|
|
76
|
+
let at = codeStart;
|
|
77
|
+
let prefixLen;
|
|
78
|
+
for (const p of NIP19_PREFIXES) {
|
|
79
|
+
prefixLen = litAt(rest.slice(at), p);
|
|
80
|
+
if (prefixLen !== void 0) break;
|
|
81
|
+
}
|
|
82
|
+
if (prefixLen === void 0) return;
|
|
83
|
+
at += prefixLen;
|
|
84
|
+
const end = sepThenData(rest, at);
|
|
85
|
+
return end === void 0 ? void 0 : [codeStart, end];
|
|
86
|
+
}
|
|
87
|
+
/** `http(s)://` / `ws(s)://` + URL run. Returns the raw match end. */
|
|
88
|
+
function urlAt(rest, ws) {
|
|
89
|
+
let at = litAt(rest, ws ? "ws" : "http");
|
|
90
|
+
if (at === void 0) return;
|
|
91
|
+
const s = rest[at];
|
|
92
|
+
if (s !== void 0 && foldAscii(s) === "s") at += 1;
|
|
93
|
+
const sep = litAt(rest.slice(at), "://");
|
|
94
|
+
if (sep === void 0) return;
|
|
95
|
+
at += sep;
|
|
96
|
+
let run = 0;
|
|
97
|
+
for (const c of rest.slice(at)) {
|
|
98
|
+
if (isUrlStop(c)) break;
|
|
99
|
+
run += c.length;
|
|
100
|
+
}
|
|
101
|
+
return run > 0 ? at + run : void 0;
|
|
102
|
+
}
|
|
103
|
+
const INVOICE_CURRENCIES = [
|
|
104
|
+
"bcrt",
|
|
105
|
+
"tbs",
|
|
106
|
+
"bc",
|
|
107
|
+
"tb"
|
|
108
|
+
];
|
|
109
|
+
/**
|
|
110
|
+
* `(lightning:)?ln(bcrt|tbs|bc|tb)` + a maximal `BECH32 ∪ {1}` run, then split at the run's last
|
|
111
|
+
* `1`: the amount before it is empty or `\d+` plus at most one of `munp` (ASCII either case), and
|
|
112
|
+
* the data after it is non-empty. Returns `[prefixLen, end]`.
|
|
113
|
+
*/
|
|
114
|
+
function invoiceAt(rest) {
|
|
115
|
+
const prefix = litAt(rest, "lightning:") ?? 0;
|
|
116
|
+
const ln = litAt(rest.slice(prefix), "ln");
|
|
117
|
+
if (ln === void 0) return;
|
|
118
|
+
let at = prefix + ln;
|
|
119
|
+
let currency;
|
|
120
|
+
for (const cur of INVOICE_CURRENCIES) {
|
|
121
|
+
currency = litAt(rest.slice(at), cur);
|
|
122
|
+
if (currency !== void 0) break;
|
|
123
|
+
}
|
|
124
|
+
if (currency === void 0) return;
|
|
125
|
+
at += currency;
|
|
126
|
+
let runEnd = at;
|
|
127
|
+
let lastSep = -1;
|
|
128
|
+
for (const c of rest.slice(at)) {
|
|
129
|
+
if (c === "1") {
|
|
130
|
+
lastSep = runEnd;
|
|
131
|
+
runEnd += 1;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (!isBech32Char(c)) break;
|
|
135
|
+
runEnd += c.length;
|
|
136
|
+
}
|
|
137
|
+
if (lastSep === -1) return;
|
|
138
|
+
if (rest.slice(lastSep + 1, runEnd).length === 0) return;
|
|
139
|
+
const amount = rest.slice(at, lastSep);
|
|
140
|
+
if (amount.length > 0) {
|
|
141
|
+
let i = 0;
|
|
142
|
+
for (const c of amount) {
|
|
143
|
+
if (c < "0" || c > "9") break;
|
|
144
|
+
i += c.length;
|
|
145
|
+
}
|
|
146
|
+
const tail = amount.slice(i);
|
|
147
|
+
if (i === 0 || tail.length > 1 || tail.length === 1 && !"munpMUNP".includes(tail)) return;
|
|
148
|
+
}
|
|
149
|
+
return [prefix, runEnd];
|
|
150
|
+
}
|
|
151
|
+
/** `#` + a maximal `TAG` run — returns the run end (the 1–42 length cap is a validation). */
|
|
152
|
+
function hashtagAt(rest) {
|
|
153
|
+
if (!rest.startsWith("#")) return;
|
|
154
|
+
let end = 1;
|
|
155
|
+
let count = 0;
|
|
156
|
+
for (const c of rest.slice(1)) {
|
|
157
|
+
if (!isTagChar(c)) break;
|
|
158
|
+
count += 1;
|
|
159
|
+
end += c.length;
|
|
160
|
+
}
|
|
161
|
+
return count === 0 ? void 0 : [end, count];
|
|
162
|
+
}
|
|
163
|
+
/** `:[A-Za-z0-9_-]+:` — a shortcode candidate (membership checked at validation). */
|
|
164
|
+
function emojiAt(rest) {
|
|
165
|
+
if (!rest.startsWith(":")) return;
|
|
166
|
+
let run = 0;
|
|
167
|
+
for (const c of rest.slice(1)) {
|
|
168
|
+
if (!isEmojiChar(c)) break;
|
|
169
|
+
run += c.length;
|
|
170
|
+
}
|
|
171
|
+
if (run === 0) return;
|
|
172
|
+
return rest[1 + run] === ":" ? 1 + run + 1 : void 0;
|
|
173
|
+
}
|
|
174
|
+
/** `npub|nprofile|note|nevent|naddr` + `1` + charset — the legacy bare rule. */
|
|
175
|
+
function bareAt(rest) {
|
|
176
|
+
for (const p of BARE_PREFIXES) {
|
|
177
|
+
const len = litAt(rest, p);
|
|
178
|
+
if (len !== void 0) return sepThenData(rest, len);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/** First shape match at `pos`, in rule order. Returns the token and the raw match end. */
|
|
182
|
+
function candidateAt(rest, legacy) {
|
|
183
|
+
const nostr = nostrAt(rest);
|
|
184
|
+
if (nostr !== void 0) return [{
|
|
185
|
+
kind: "nostr",
|
|
186
|
+
codeStart: nostr[0]
|
|
187
|
+
}, nostr[1]];
|
|
188
|
+
const url = urlAt(rest, false);
|
|
189
|
+
if (url !== void 0) return [{ kind: "url" }, url];
|
|
190
|
+
const relay = urlAt(rest, true);
|
|
191
|
+
if (relay !== void 0) return [{ kind: "relay" }, relay];
|
|
192
|
+
const invoice = invoiceAt(rest);
|
|
193
|
+
if (invoice !== void 0) return [{
|
|
194
|
+
kind: "invoice",
|
|
195
|
+
prefixLen: invoice[0]
|
|
196
|
+
}, invoice[1]];
|
|
197
|
+
const hashtag = hashtagAt(rest);
|
|
198
|
+
if (hashtag !== void 0) return [{
|
|
199
|
+
kind: "hashtag",
|
|
200
|
+
count: hashtag[1]
|
|
201
|
+
}, hashtag[0]];
|
|
202
|
+
const emoji = emojiAt(rest);
|
|
203
|
+
if (emoji !== void 0) return [{ kind: "emoji" }, emoji];
|
|
204
|
+
if (legacy) {
|
|
205
|
+
const bare = bareAt(rest);
|
|
206
|
+
if (bare !== void 0) return [{ kind: "bare" }, bare];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
25
209
|
const TRAILING_URL_CHARS = /* @__PURE__ */ new Set([
|
|
26
210
|
".",
|
|
27
211
|
",",
|
|
@@ -31,35 +215,6 @@ const TRAILING_URL_CHARS = /* @__PURE__ */ new Set([
|
|
|
31
215
|
"?",
|
|
32
216
|
"'"
|
|
33
217
|
]);
|
|
34
|
-
const IMAGE_EXT = /* @__PURE__ */ new Set([
|
|
35
|
-
"jpg",
|
|
36
|
-
"jpeg",
|
|
37
|
-
"png",
|
|
38
|
-
"gif",
|
|
39
|
-
"webp",
|
|
40
|
-
"avif",
|
|
41
|
-
"svg"
|
|
42
|
-
]);
|
|
43
|
-
const VIDEO_EXT = /* @__PURE__ */ new Set([
|
|
44
|
-
"mp4",
|
|
45
|
-
"webm",
|
|
46
|
-
"mov",
|
|
47
|
-
"m4v",
|
|
48
|
-
"m3u8"
|
|
49
|
-
]);
|
|
50
|
-
const AUDIO_EXT = /* @__PURE__ */ new Set([
|
|
51
|
-
"mp3",
|
|
52
|
-
"m4a",
|
|
53
|
-
"ogg",
|
|
54
|
-
"oga",
|
|
55
|
-
"opus",
|
|
56
|
-
"wav",
|
|
57
|
-
"flac",
|
|
58
|
-
"aac"
|
|
59
|
-
]);
|
|
60
|
-
function isChar(cp, re) {
|
|
61
|
-
return cp !== void 0 && re.test(String.fromCodePoint(cp));
|
|
62
|
-
}
|
|
63
218
|
/** Drop trailing sentence punctuation and closers that have no opener inside the URL. */
|
|
64
219
|
function trimUrlTail(raw) {
|
|
65
220
|
let url = raw;
|
|
@@ -89,6 +244,32 @@ function mimeMedia(mime) {
|
|
|
89
244
|
const base = slash === -1 ? mime : mime.slice(0, slash);
|
|
90
245
|
return base === "image" || base === "video" || base === "audio" ? base : void 0;
|
|
91
246
|
}
|
|
247
|
+
const IMAGE_EXT = /* @__PURE__ */ new Set([
|
|
248
|
+
"jpg",
|
|
249
|
+
"jpeg",
|
|
250
|
+
"png",
|
|
251
|
+
"gif",
|
|
252
|
+
"webp",
|
|
253
|
+
"avif",
|
|
254
|
+
"svg"
|
|
255
|
+
]);
|
|
256
|
+
const VIDEO_EXT = /* @__PURE__ */ new Set([
|
|
257
|
+
"mp4",
|
|
258
|
+
"webm",
|
|
259
|
+
"mov",
|
|
260
|
+
"m4v",
|
|
261
|
+
"m3u8"
|
|
262
|
+
]);
|
|
263
|
+
const AUDIO_EXT = /* @__PURE__ */ new Set([
|
|
264
|
+
"mp3",
|
|
265
|
+
"m4a",
|
|
266
|
+
"ogg",
|
|
267
|
+
"oga",
|
|
268
|
+
"opus",
|
|
269
|
+
"wav",
|
|
270
|
+
"flac",
|
|
271
|
+
"aac"
|
|
272
|
+
]);
|
|
92
273
|
function extMedia(pathname) {
|
|
93
274
|
const dot = pathname.lastIndexOf(".");
|
|
94
275
|
if (dot === -1) return;
|
|
@@ -146,10 +327,21 @@ function referenceFor(code, bare) {
|
|
|
146
327
|
bare
|
|
147
328
|
};
|
|
148
329
|
}
|
|
330
|
+
function scalarAt(text, utf16) {
|
|
331
|
+
const cp = text.codePointAt(utf16);
|
|
332
|
+
return cp === void 0 ? void 0 : String.fromCodePoint(cp);
|
|
333
|
+
}
|
|
334
|
+
/** The scalar value immediately before `pos` (astral-safe). */
|
|
335
|
+
function scalarBefore(text, pos) {
|
|
336
|
+
if (pos <= 0) return;
|
|
337
|
+
const pair = pos >= 2 ? text.codePointAt(pos - 2) : void 0;
|
|
338
|
+
if (pair !== void 0 && pair > 65535) return String.fromCodePoint(pair);
|
|
339
|
+
return scalarAt(text, pos - 1);
|
|
340
|
+
}
|
|
149
341
|
/**
|
|
150
342
|
* Parse note content (or a full event, to pick up `emoji` tags) into ordered blocks. One
|
|
151
|
-
* left-to-right scan
|
|
152
|
-
*
|
|
343
|
+
* left-to-right scan over Unicode scalar values; adjacent text merges and no empty text blocks are
|
|
344
|
+
* emitted.
|
|
153
345
|
*/
|
|
154
346
|
function parseContent(content, opts) {
|
|
155
347
|
const emojis = /* @__PURE__ */ new Map();
|
|
@@ -159,9 +351,8 @@ function parseContent(content, opts) {
|
|
|
159
351
|
for (const tag of content.tags) if (tag[0] === "emoji" && tag[1] !== void 0 && tag[1] !== "" && tag[2] !== void 0 && tag[2] !== "") emojis.set(tag[1], tag[2]);
|
|
160
352
|
text = content.content;
|
|
161
353
|
}
|
|
162
|
-
const scan = opts?.legacyBech32 === true ? SCAN_LEGACY : SCAN;
|
|
163
|
-
scan.lastIndex = 0;
|
|
164
354
|
const blocks = [];
|
|
355
|
+
const legacy = opts?.legacyBech32 === true;
|
|
165
356
|
let textStart = 0;
|
|
166
357
|
const flushText = (end) => {
|
|
167
358
|
if (end > textStart) blocks.push({
|
|
@@ -169,42 +360,74 @@ function parseContent(content, opts) {
|
|
|
169
360
|
text: text.slice(textStart, end)
|
|
170
361
|
});
|
|
171
362
|
};
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
363
|
+
let pos = 0;
|
|
364
|
+
while (pos < text.length) {
|
|
365
|
+
const rest = text.slice(pos);
|
|
366
|
+
const candidate = candidateAt(rest, legacy);
|
|
367
|
+
if (candidate === void 0) {
|
|
368
|
+
const c = scalarAt(text, pos);
|
|
369
|
+
pos += c === void 0 ? 1 : c.length;
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
const [token, endRel] = candidate;
|
|
373
|
+
const end = pos + endRel;
|
|
374
|
+
const prevScalar = scalarBefore(text, pos);
|
|
375
|
+
const next = scalarAt(text, end);
|
|
376
|
+
let outcome;
|
|
377
|
+
switch (token.kind) {
|
|
378
|
+
case "nostr": {
|
|
379
|
+
const block = referenceFor(rest.slice(token.codeStart, endRel), false);
|
|
380
|
+
outcome = block === void 0 ? void 0 : [block, end];
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
case "url": {
|
|
384
|
+
const block = urlBlock(rest.slice(0, endRel), false, opts?.imeta);
|
|
385
|
+
outcome = block === void 0 ? void 0 : [block, pos + block.url.length];
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
case "relay": {
|
|
389
|
+
const block = urlBlock(rest.slice(0, endRel), true, void 0);
|
|
390
|
+
outcome = block === void 0 ? void 0 : [block, pos + block.url.length];
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
case "invoice":
|
|
394
|
+
outcome = prevScalar !== void 0 && isWordChar(prevScalar) || next !== void 0 && isWordChar(next) ? void 0 : [{
|
|
395
|
+
type: "invoice",
|
|
396
|
+
bolt11: lowerAscii(rest.slice(token.prefixLen, endRel))
|
|
397
|
+
}, end];
|
|
398
|
+
break;
|
|
399
|
+
case "hashtag":
|
|
400
|
+
outcome = token.count > 42 || prevScalar !== void 0 && isTagChar(prevScalar) ? void 0 : [{
|
|
401
|
+
type: "hashtag",
|
|
402
|
+
value: rest.slice(1, endRel)
|
|
403
|
+
}, end];
|
|
404
|
+
break;
|
|
405
|
+
case "emoji": {
|
|
406
|
+
const shortcode = rest.slice(1, endRel - 1);
|
|
407
|
+
const url = emojis.get(shortcode);
|
|
408
|
+
outcome = url === void 0 ? void 0 : [{
|
|
409
|
+
type: "emoji",
|
|
410
|
+
shortcode,
|
|
411
|
+
url
|
|
412
|
+
}, end];
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
case "bare": if (prevScalar !== void 0 && isWordChar(prevScalar) || next !== void 0 && isWordChar(next)) outcome = void 0;
|
|
416
|
+
else {
|
|
417
|
+
const block = referenceFor(rest.slice(0, endRel), true);
|
|
418
|
+
outcome = block === void 0 ? void 0 : [block, end];
|
|
419
|
+
}
|
|
200
420
|
}
|
|
201
|
-
if (
|
|
202
|
-
|
|
421
|
+
if (outcome === void 0) {
|
|
422
|
+
const c = scalarAt(text, pos);
|
|
423
|
+
pos += c === void 0 ? 1 : c.length;
|
|
203
424
|
continue;
|
|
204
425
|
}
|
|
205
|
-
|
|
426
|
+
const [block, nextTextStart] = outcome;
|
|
427
|
+
flushText(pos);
|
|
206
428
|
blocks.push(block);
|
|
207
|
-
textStart =
|
|
429
|
+
textStart = nextTextStart;
|
|
430
|
+
pos = end;
|
|
208
431
|
}
|
|
209
432
|
flushText(text.length);
|
|
210
433
|
return blocks;
|
package/dist/nips/nip27.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nip27.mjs","names":[],"sources":["../../src/nips/nip27.ts"],"sourcesContent":["/**\n * NIP-27: Text Note References. Tokenizes note content into text, `nostr:` references, URLs, media,\n * relays, hashtags, custom emoji, and Lightning invoices.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/27.md\n */\nimport type { Event } from \"../core/event.ts\";\nimport { decode } from \"./nip19.ts\";\nimport type { AddressPointer, EventPointer, ProfilePointer } from \"./nip19.ts\";\n\nexport type ContentBlock =\n | { readonly type: \"text\"; readonly text: string }\n | {\n readonly type: \"reference\";\n readonly pointer: ProfilePointer | EventPointer | AddressPointer;\n /**\n * `true` when the reference had no `nostr:` prefix (see\n * {@link ParseContentOptions.legacyBech32}).\n */\n readonly bare: boolean;\n }\n | { readonly type: \"url\"; readonly url: string }\n | { readonly type: \"image\" | \"video\" | \"audio\"; readonly url: string }\n | { readonly type: \"relay\"; readonly url: string }\n | { readonly type: \"hashtag\"; readonly value: string }\n | { readonly type: \"emoji\"; readonly shortcode: string; readonly url: string }\n | { readonly type: \"invoice\"; readonly bolt11: string };\n\nexport type ParseContentOptions = {\n /**\n * Recognize bare `npub`, `nprofile`, `note`, `nevent`, `naddr` without the `nostr:` prefix.\n * Default `false`.\n */\n legacyBech32?: boolean | undefined;\n /**\n * URL → MIME type (the `m` field of NIP-92 `imeta` tags); takes precedence over extension\n * classification.\n */\n imeta?: ReadonlyMap<string, string> | undefined;\n};\n\n// Bech32 data charset ([02-9ac-hj-np-z], both ASCII cases) and the NIP-19 entity prefixes.\nconst BECH32_CHARS = \"02-9AC-HJ-NP-Zac-hj-np-z\";\n\n/**\n * Pair every letter of an ASCII literal into a case class (`[Nn]…`) so keywords match\n * case-insensitively for ASCII only. The regex `i` flag cannot be used here: under `u` it also\n * folds U+017F (ſ)→s and U+212A (K)→k, which would let `noſtr:` or `lnbc1K…` slip through.\n */\nconst asciiCi = (word: string): string =>\n word.replaceAll(/[a-zA-Z]/g, (c) => `[${c.toLowerCase()}${c.toUpperCase()}]`);\n\nconst NIP19_PREFIX = asciiCi(\"(?:npub|nprofile|note|nevent|naddr|nsec)1\");\nconst NIP19_BARE_PREFIX = asciiCi(\"(?:npub|nprofile|note|nevent|naddr)1\");\n\n// A URL run ends at whitespace, angle brackets, double quotes, CJK punctuation\n// (U+3000–U+303F), or full-width forms (U+FF00–U+FFEF).\nconst URL_RUN = `[^\\\\s<>\"\\\\u3000-\\\\u303f\\\\uff00-\\\\uffef]+`;\n\n// Rule order matters: at every position the first matching alternative wins.\nconst SCAN_BODY = [\n // 1. nostr: URIs carry a NIP-19 entity.\n `(?<nostr>${asciiCi(\"nostr:\")}${NIP19_PREFIX}[${BECH32_CHARS}]+)`,\n // 2./3. http(s) and ws(s) URLs share the same boundary rules.\n `(?<url>${asciiCi(\"https\")}?://${URL_RUN})`,\n `(?<relay>${asciiCi(\"wss\")}?://${URL_RUN})`,\n // 4. BOLT11 invoices, with an optional lightning: URI prefix. The HRP may carry an amount\n // (`lnbc10u1…` = ln + currency + amount + multiplier + the `1` separator).\n `(?<invoice>(?:${asciiCi(\"lightning:\")})?${asciiCi(\"ln\")}(?:${asciiCi(\"bcrt\")}|${asciiCi(\"tbs\")}|${asciiCi(\"bc\")}|${asciiCi(\"tb\")})(?:\\\\d+[munpMUNP]?)?1[${BECH32_CHARS}]+)`,\n // 5. Hashtags: Unicode letters/marks/numbers and underscore, at most 42 code points.\n `(?<hashtag>#[\\\\p{L}\\\\p{M}\\\\p{N}_]{1,42})`,\n // 6. Custom emoji shortcodes (validated against the event's emoji tags). NIP-30 allows\n // alphanumerics, hyphens, and underscores.\n `(?<emoji>:[A-Za-z0-9_-]+:)`,\n].join(\"|\");\n\nconst SCAN = new RegExp(SCAN_BODY, \"gu\");\n// 7. Bare NIP-19 entities — legacy rendering support, opt-in only.\nconst SCAN_LEGACY = new RegExp(\n `${SCAN_BODY}|(?<bare>${NIP19_BARE_PREFIX}[${BECH32_CHARS}]+)`,\n \"gu\",\n);\n\n// The character before `#` must be absent or a non-tag character.\nconst TAG_CHAR = /[\\p{L}\\p{M}\\p{N}_]/u;\n// Invoice and bare-bech32 tokens must not be glued to a letter or digit.\nconst WORD_CHAR = /[\\p{L}\\p{N}]/u;\n\nconst TRAILING_URL_CHARS = new Set([\".\", \",\", \";\", \":\", \"!\", \"?\", \"'\"]);\n\nconst IMAGE_EXT = new Set([\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"avif\", \"svg\"]);\nconst VIDEO_EXT = new Set([\"mp4\", \"webm\", \"mov\", \"m4v\", \"m3u8\"]);\nconst AUDIO_EXT = new Set([\"mp3\", \"m4a\", \"ogg\", \"oga\", \"opus\", \"wav\", \"flac\", \"aac\"]);\n\nfunction isChar(cp: number | undefined, re: RegExp): boolean {\n return cp !== undefined && re.test(String.fromCodePoint(cp));\n}\n\n/** Drop trailing sentence punctuation and closers that have no opener inside the URL. */\nfunction trimUrlTail(raw: string): string {\n let url = raw;\n for (;;) {\n const last = url.at(-1);\n if (last !== undefined && TRAILING_URL_CHARS.has(last)) {\n url = url.slice(0, -1);\n continue;\n }\n if (last === \")\" || last === \"]\") {\n const open = last === \")\" ? \"(\" : \"[\";\n let opens = 0;\n let closes = 0;\n for (const c of url) {\n if (c === open) {\n opens++;\n } else if (c === last) {\n closes++;\n }\n }\n if (closes > opens) {\n url = url.slice(0, -1);\n continue;\n }\n }\n return url;\n }\n}\n\ntype MediaType = \"image\" | \"video\" | \"audio\";\n\nfunction mimeMedia(mime: string | undefined): MediaType | undefined {\n if (mime === undefined) {\n return undefined;\n }\n const slash = mime.indexOf(\"/\");\n const base = slash === -1 ? mime : mime.slice(0, slash);\n return base === \"image\" || base === \"video\" || base === \"audio\" ? base : undefined;\n}\n\nfunction extMedia(pathname: string): MediaType | undefined {\n const dot = pathname.lastIndexOf(\".\");\n if (dot === -1) {\n return undefined;\n }\n const ext = pathname.slice(dot + 1).toLowerCase();\n if (IMAGE_EXT.has(ext)) {\n return \"image\";\n }\n if (VIDEO_EXT.has(ext)) {\n return \"video\";\n }\n return AUDIO_EXT.has(ext) ? \"audio\" : undefined;\n}\n\nfunction urlBlock(\n raw: string,\n relay: boolean,\n imeta: ReadonlyMap<string, string> | undefined,\n): ContentBlock | undefined {\n const url = trimUrlTail(raw);\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return undefined;\n }\n if (!parsed.hostname.includes(\".\")) {\n return undefined;\n }\n if (relay) {\n return { type: \"relay\", url };\n }\n const media = mimeMedia(imeta?.get(url)) ?? extMedia(parsed.pathname);\n return media === undefined ? { type: \"url\", url } : { type: media, url };\n}\n\nfunction referenceFor(code: string, bare: boolean): ContentBlock | undefined {\n let pointer: ProfilePointer | EventPointer | AddressPointer;\n try {\n const decoded = decode(code);\n switch (decoded.type) {\n case \"npub\":\n pointer = { pubkey: decoded.data };\n break;\n case \"note\":\n pointer = { id: decoded.data };\n break;\n case \"nprofile\":\n case \"nevent\":\n case \"naddr\":\n pointer = decoded.data;\n break;\n case \"nsec\":\n return undefined;\n }\n } catch {\n return undefined;\n }\n return { type: \"reference\", pointer, bare };\n}\n\n/**\n * Parse note content (or a full event, to pick up `emoji` tags) into ordered blocks. One\n * left-to-right scan; at every position the rules fire in the alternation order above. Adjacent\n * text merges and no empty text blocks are emitted.\n */\nexport function parseContent(\n content: string | Pick<Event, \"content\" | \"tags\">,\n opts?: ParseContentOptions,\n): ContentBlock[] {\n const emojis = new Map<string, string>();\n let text: string;\n if (typeof content === \"string\") {\n text = content;\n } else {\n for (const tag of content.tags) {\n if (\n tag[0] === \"emoji\" &&\n tag[1] !== undefined &&\n tag[1] !== \"\" &&\n tag[2] !== undefined &&\n tag[2] !== \"\"\n ) {\n emojis.set(tag[1], tag[2]);\n }\n }\n text = content.content;\n }\n\n const scan = opts?.legacyBech32 === true ? SCAN_LEGACY : SCAN;\n scan.lastIndex = 0;\n\n const blocks: ContentBlock[] = [];\n let textStart = 0;\n const flushText = (end: number): void => {\n if (end > textStart) {\n blocks.push({ type: \"text\", text: text.slice(textStart, end) });\n }\n };\n\n for (let m = scan.exec(text); m !== null; m = scan.exec(text)) {\n const start = m.index;\n const [token] = m;\n const end = start + token.length;\n const g = m.groups;\n let block: ContentBlock | undefined;\n\n if (g?.[\"nostr\"] !== undefined) {\n block = referenceFor(token.slice(\"nostr:\".length), false);\n } else if (g?.[\"url\"] !== undefined) {\n block = urlBlock(token, false, opts?.imeta);\n } else if (g?.[\"relay\"] !== undefined) {\n block = urlBlock(token, true, undefined);\n } else if (g?.[\"invoice\"] !== undefined) {\n const prev = start === 0 ? undefined : text.codePointAt(start - 1);\n if (!isChar(prev, WORD_CHAR) && !isChar(text.codePointAt(end), WORD_CHAR)) {\n const invoice = token.toLowerCase().startsWith(\"lightning:\")\n ? token.slice(\"lightning:\".length)\n : token;\n block = { type: \"invoice\", bolt11: invoice.toLowerCase() };\n }\n } else if (g?.[\"hashtag\"] !== undefined) {\n const prev = start === 0 ? undefined : text.codePointAt(start - 1);\n // A tag char right after the match means the {1,42} cap truncated a longer\n // run; the whole run is text, not a hashtag plus remainder.\n if (!isChar(prev, TAG_CHAR) && !isChar(text.codePointAt(end), TAG_CHAR)) {\n block = { type: \"hashtag\", value: token.slice(1) };\n }\n } else if (g?.[\"emoji\"] !== undefined) {\n const url = emojis.get(token.slice(1, -1));\n if (url !== undefined) {\n block = { type: \"emoji\", shortcode: token.slice(1, -1), url };\n }\n } else if (g?.[\"bare\"] !== undefined) {\n const prev = start === 0 ? undefined : text.codePointAt(start - 1);\n if (!isChar(prev, WORD_CHAR) && !isChar(text.codePointAt(end), WORD_CHAR)) {\n block = referenceFor(token, true);\n }\n }\n\n if (block === undefined) {\n // The candidate is plain text; resume scanning one code unit later so any token inside\n // its span can still match.\n scan.lastIndex = start + 1;\n continue;\n }\n flushText(start);\n blocks.push(block);\n // URL-family blocks may end before the regex run once trailing punctuation is trimmed.\n textStart =\n block.type === \"url\" ||\n block.type === \"relay\" ||\n block.type === \"image\" ||\n block.type === \"video\" ||\n block.type === \"audio\"\n ? start + block.url.length\n : end;\n }\n\n flushText(text.length);\n return blocks;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nip27.mjs","names":[],"sources":["../../src/nips/nip27.ts"],"sourcesContent":["/**\n * NIP-27: Text Note References. Tokenizes note content into text, `nostr:` references, URLs, media,\n * relays, hashtags, custom emoji, and Lightning invoices.\n *\n * The tokenizer is a hand-written scanner — one left-to-right walk over Unicode scalar values. At\n * position `i` the first rule whose shape matches decides; a candidate that fails validation makes\n * `i` text and the scan resumes at the next scalar (no later rule is tried at `i`). Keywords\n * compare ASCII case-insensitively; there is no Unicode case folding and no regex backtracking.\n * `crates/nk-core/src/nips/nip27.rs` implements the same rules line for line.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/27.md\n */\nimport type { Event } from \"../core/event.ts\";\nimport { lowerAscii } from \"../core/util.ts\";\nimport { decode } from \"./nip19.ts\";\nimport type { AddressPointer, EventPointer, ProfilePointer } from \"./nip19.ts\";\n\nexport type ContentBlock =\n | { readonly type: \"text\"; readonly text: string }\n | {\n readonly type: \"reference\";\n readonly pointer: ProfilePointer | EventPointer | AddressPointer;\n /**\n * `true` when the reference had no `nostr:` prefix (see\n * {@link ParseContentOptions.legacyBech32}).\n */\n readonly bare: boolean;\n }\n | { readonly type: \"url\"; readonly url: string }\n | { readonly type: \"image\" | \"video\" | \"audio\"; readonly url: string }\n | { readonly type: \"relay\"; readonly url: string }\n | { readonly type: \"hashtag\"; readonly value: string }\n | { readonly type: \"emoji\"; readonly shortcode: string; readonly url: string }\n | { readonly type: \"invoice\"; readonly bolt11: string };\n\nexport type ParseContentOptions = {\n /**\n * Recognize bare `npub`, `nprofile`, `note`, `nevent`, `naddr` without the `nostr:` prefix.\n * Default `false`.\n */\n legacyBech32?: boolean | undefined;\n /**\n * URL → MIME type (the `m` field of NIP-92 `imeta` tags); takes precedence over extension\n * classification.\n */\n imeta?: ReadonlyMap<string, string> | undefined;\n};\n\n// Per-scalar character classes — each test takes exactly one code point.\nconst WORD_CHAR = /[\\p{L}\\p{N}]/u;\nconst TAG_CHAR = /[\\p{L}\\p{M}\\p{N}_]/u;\nconst WHITE_SPACE = /\\p{White_Space}/u;\n\n/** ASCII lowercase of one code point (`A`-`Z` → `a`-`z`; everything else unchanged). */\nfunction foldAscii(c: string): string {\n return c >= \"A\" && c <= \"Z\" ? c.toLowerCase() : c;\n}\n\n/** `0`, `2`–`9`, `a c-h j-n p-z`, both ASCII cases — the bech32 data charset. */\nfunction isBech32Char(c: string): boolean {\n const l = foldAscii(c);\n return (\n (l >= \"0\" && l <= \"9\" && l !== \"1\") ||\n (l >= \"a\" && l <= \"z\" && l !== \"b\" && l !== \"i\" && l !== \"o\")\n );\n}\n\n/** `[A-Za-z0-9_-]` — the emoji shortcode charset. */\nfunction isEmojiChar(c: string): boolean {\n return (\n (c >= \"a\" && c <= \"z\") ||\n (c >= \"A\" && c <= \"Z\") ||\n (c >= \"0\" && c <= \"9\") ||\n c === \"_\" ||\n c === \"-\"\n );\n}\n\nfunction isTagChar(c: string): boolean {\n return TAG_CHAR.test(c);\n}\n\nfunction isWordChar(c: string): boolean {\n return WORD_CHAR.test(c);\n}\n\n/** URL-run stops: Unicode White_Space plus `<>`, `\"`, CJK punctuation and full-width forms. */\nfunction isUrlStop(c: string): boolean {\n if (WHITE_SPACE.test(c) || c === \"<\" || c === \">\" || c === '\"') {\n return true;\n }\n const cp = c.codePointAt(0);\n return cp !== undefined && ((cp >= 0x3000 && cp <= 0x303f) || (cp >= 0xff00 && cp <= 0xffef));\n}\n\n/**\n * ASCII-insensitive literal match at the start of `rest` — the keyword form (`nostr:`,\n * `lightning:`, `http`…). Returns the matched length in UTF-16 units, or `undefined`. Literals are\n * ASCII, so the length equals the literal's length.\n */\nfunction litAt(rest: string, lit: string): number | undefined {\n if (rest.length < lit.length) {\n return undefined;\n }\n for (let i = 0; i < lit.length; i++) {\n if (foldAscii(rest[i] ?? \"\") !== lit[i]) {\n return undefined;\n }\n }\n return lit.length;\n}\n\n/** UTF-16 length of the maximal bech32-charset run at the start of `rest` (may be 0). */\nfunction bech32Run(rest: string): number {\n let n = 0;\n for (const c of rest) {\n if (!isBech32Char(c)) {\n break;\n }\n n += c.length;\n }\n return n;\n}\n\n/** `1` then a non-empty bech32-charset run — the entity/invoice separator and data. */\nfunction sepThenData(rest: string, at: number): number | undefined {\n if (rest[at] !== \"1\") {\n return undefined;\n }\n const run = bech32Run(rest.slice(at + 1));\n return run > 0 ? at + 1 + run : undefined;\n}\n\nconst NIP19_PREFIXES = [\"npub\", \"nprofile\", \"note\", \"nevent\", \"naddr\", \"nsec\"] as const;\nconst BARE_PREFIXES = [\"npub\", \"nprofile\", \"note\", \"nevent\", \"naddr\"] as const;\n\n/** `nostr:` + NIP-19 prefix + `1` + charset. Returns `[codeStart, end]` — code start skips `nostr:`. */\nfunction nostrAt(rest: string): [number, number] | undefined {\n const codeStart = litAt(rest, \"nostr:\");\n if (codeStart === undefined) {\n return undefined;\n }\n let at = codeStart;\n let prefixLen: number | undefined;\n for (const p of NIP19_PREFIXES) {\n prefixLen = litAt(rest.slice(at), p);\n if (prefixLen !== undefined) {\n break;\n }\n }\n if (prefixLen === undefined) {\n return undefined;\n }\n at += prefixLen;\n const end = sepThenData(rest, at);\n return end === undefined ? undefined : [codeStart, end];\n}\n\n/** `http(s)://` / `ws(s)://` + URL run. Returns the raw match end. */\nfunction urlAt(rest: string, ws: boolean): number | undefined {\n const base = ws ? \"ws\" : \"http\";\n let at = litAt(rest, base);\n if (at === undefined) {\n return undefined;\n }\n // `s?` — optional second s, ASCII-only.\n const s = rest[at];\n if (s !== undefined && foldAscii(s) === \"s\") {\n at += 1;\n }\n const sep = litAt(rest.slice(at), \"://\");\n if (sep === undefined) {\n return undefined;\n }\n at += sep;\n let run = 0;\n for (const c of rest.slice(at)) {\n if (isUrlStop(c)) {\n break;\n }\n run += c.length;\n }\n return run > 0 ? at + run : undefined;\n}\n\nconst INVOICE_CURRENCIES = [\"bcrt\", \"tbs\", \"bc\", \"tb\"] as const;\n\n/**\n * `(lightning:)?ln(bcrt|tbs|bc|tb)` + a maximal `BECH32 ∪ {1}` run, then split at the run's last\n * `1`: the amount before it is empty or `\\d+` plus at most one of `munp` (ASCII either case), and\n * the data after it is non-empty. Returns `[prefixLen, end]`.\n */\nfunction invoiceAt(rest: string): [number, number] | undefined {\n const prefix = litAt(rest, \"lightning:\") ?? 0;\n const ln = litAt(rest.slice(prefix), \"ln\");\n if (ln === undefined) {\n return undefined;\n }\n let at = prefix + ln;\n let currency: number | undefined;\n for (const cur of INVOICE_CURRENCIES) {\n currency = litAt(rest.slice(at), cur);\n if (currency !== undefined) {\n break;\n }\n }\n if (currency === undefined) {\n return undefined;\n }\n at += currency;\n // The maximal run may contain `1`s — the LAST `1` is the bech32 separator.\n let runEnd = at;\n let lastSep = -1;\n for (const c of rest.slice(at)) {\n if (c === \"1\") {\n lastSep = runEnd;\n runEnd += 1;\n continue;\n }\n if (!isBech32Char(c)) {\n break;\n }\n runEnd += c.length;\n }\n if (lastSep === -1) {\n return undefined;\n }\n const data = rest.slice(lastSep + 1, runEnd);\n if (data.length === 0) {\n return undefined;\n }\n const amount = rest.slice(at, lastSep);\n if (amount.length > 0) {\n let i = 0;\n for (const c of amount) {\n if (c < \"0\" || c > \"9\") {\n break;\n }\n i += c.length;\n }\n const tail = amount.slice(i);\n if (i === 0 || tail.length > 1 || (tail.length === 1 && !\"munpMUNP\".includes(tail))) {\n return undefined;\n }\n }\n return [prefix, runEnd];\n}\n\n/** `#` + a maximal `TAG` run — returns the run end (the 1–42 length cap is a validation). */\nfunction hashtagAt(rest: string): [number, number] | undefined {\n if (!rest.startsWith(\"#\")) {\n return undefined;\n }\n let end = 1;\n let count = 0;\n for (const c of rest.slice(1)) {\n if (!isTagChar(c)) {\n break;\n }\n count += 1;\n end += c.length;\n }\n return count === 0 ? undefined : [end, count];\n}\n\n/** `:[A-Za-z0-9_-]+:` — a shortcode candidate (membership checked at validation). */\nfunction emojiAt(rest: string): number | undefined {\n if (!rest.startsWith(\":\")) {\n return undefined;\n }\n let run = 0;\n for (const c of rest.slice(1)) {\n if (!isEmojiChar(c)) {\n break;\n }\n run += c.length;\n }\n if (run === 0) {\n return undefined;\n }\n return rest[1 + run] === \":\" ? 1 + run + 1 : undefined;\n}\n\n/** `npub|nprofile|note|nevent|naddr` + `1` + charset — the legacy bare rule. */\nfunction bareAt(rest: string): number | undefined {\n for (const p of BARE_PREFIXES) {\n const len = litAt(rest, p);\n if (len !== undefined) {\n return sepThenData(rest, len);\n }\n }\n return undefined;\n}\n\ntype Token =\n | { kind: \"nostr\"; codeStart: number }\n | { kind: \"hashtag\"; count: number }\n | { kind: \"url\" | \"relay\" | \"emoji\" | \"bare\" }\n | { kind: \"invoice\"; prefixLen: number };\n\n/** First shape match at `pos`, in rule order. Returns the token and the raw match end. */\nfunction candidateAt(rest: string, legacy: boolean): [Token, number] | undefined {\n const nostr = nostrAt(rest);\n if (nostr !== undefined) {\n return [{ kind: \"nostr\", codeStart: nostr[0] }, nostr[1]];\n }\n const url = urlAt(rest, false);\n if (url !== undefined) {\n return [{ kind: \"url\" }, url];\n }\n const relay = urlAt(rest, true);\n if (relay !== undefined) {\n return [{ kind: \"relay\" }, relay];\n }\n const invoice = invoiceAt(rest);\n if (invoice !== undefined) {\n return [{ kind: \"invoice\", prefixLen: invoice[0] }, invoice[1]];\n }\n const hashtag = hashtagAt(rest);\n if (hashtag !== undefined) {\n return [{ kind: \"hashtag\", count: hashtag[1] }, hashtag[0]];\n }\n const emoji = emojiAt(rest);\n if (emoji !== undefined) {\n return [{ kind: \"emoji\" }, emoji];\n }\n if (legacy) {\n const bare = bareAt(rest);\n if (bare !== undefined) {\n return [{ kind: \"bare\" }, bare];\n }\n }\n return undefined;\n}\n\nconst TRAILING_URL_CHARS = new Set([\".\", \",\", \";\", \":\", \"!\", \"?\", \"'\"]);\n\n/** Drop trailing sentence punctuation and closers that have no opener inside the URL. */\nfunction trimUrlTail(raw: string): string {\n let url = raw;\n for (;;) {\n const last = url.at(-1);\n if (last !== undefined && TRAILING_URL_CHARS.has(last)) {\n url = url.slice(0, -1);\n continue;\n }\n if (last === \")\" || last === \"]\") {\n const open = last === \")\" ? \"(\" : \"[\";\n let opens = 0;\n let closes = 0;\n for (const c of url) {\n if (c === open) {\n opens++;\n } else if (c === last) {\n closes++;\n }\n }\n if (closes > opens) {\n url = url.slice(0, -1);\n continue;\n }\n }\n return url;\n }\n}\n\ntype MediaType = \"image\" | \"video\" | \"audio\";\n\nfunction mimeMedia(mime: string | undefined): MediaType | undefined {\n if (mime === undefined) {\n return undefined;\n }\n const slash = mime.indexOf(\"/\");\n const base = slash === -1 ? mime : mime.slice(0, slash);\n return base === \"image\" || base === \"video\" || base === \"audio\" ? base : undefined;\n}\n\nconst IMAGE_EXT = new Set([\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"avif\", \"svg\"]);\nconst VIDEO_EXT = new Set([\"mp4\", \"webm\", \"mov\", \"m4v\", \"m3u8\"]);\nconst AUDIO_EXT = new Set([\"mp3\", \"m4a\", \"ogg\", \"oga\", \"opus\", \"wav\", \"flac\", \"aac\"]);\n\nfunction extMedia(pathname: string): MediaType | undefined {\n const dot = pathname.lastIndexOf(\".\");\n if (dot === -1) {\n return undefined;\n }\n const ext = pathname.slice(dot + 1).toLowerCase();\n if (IMAGE_EXT.has(ext)) {\n return \"image\";\n }\n if (VIDEO_EXT.has(ext)) {\n return \"video\";\n }\n return AUDIO_EXT.has(ext) ? \"audio\" : undefined;\n}\n\ntype UrlBlock =\n | { readonly type: \"url\" | \"relay\"; readonly url: string }\n | { readonly type: \"image\" | \"video\" | \"audio\"; readonly url: string };\n\nfunction urlBlock(\n raw: string,\n relay: boolean,\n imeta: ReadonlyMap<string, string> | undefined,\n): UrlBlock | undefined {\n const url = trimUrlTail(raw);\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return undefined;\n }\n if (!parsed.hostname.includes(\".\")) {\n return undefined;\n }\n if (relay) {\n return { type: \"relay\", url };\n }\n const media = mimeMedia(imeta?.get(url)) ?? extMedia(parsed.pathname);\n return media === undefined ? { type: \"url\", url } : { type: media, url };\n}\n\nfunction referenceFor(code: string, bare: boolean): ContentBlock | undefined {\n let pointer: ProfilePointer | EventPointer | AddressPointer;\n try {\n const decoded = decode(code);\n switch (decoded.type) {\n case \"npub\":\n pointer = { pubkey: decoded.data };\n break;\n case \"note\":\n pointer = { id: decoded.data };\n break;\n case \"nprofile\":\n case \"nevent\":\n case \"naddr\":\n pointer = decoded.data;\n break;\n case \"nsec\":\n return undefined;\n }\n } catch {\n return undefined;\n }\n return { type: \"reference\", pointer, bare };\n}\n\nfunction scalarAt(text: string, utf16: number): string | undefined {\n const cp = text.codePointAt(utf16);\n return cp === undefined ? undefined : String.fromCodePoint(cp);\n}\n\n/** The scalar value immediately before `pos` (astral-safe). */\nfunction scalarBefore(text: string, pos: number): string | undefined {\n if (pos <= 0) {\n return undefined;\n }\n // An astral scalar starting at `pos - 2` is a surrogate pair ending exactly at `pos`.\n const pair = pos >= 2 ? text.codePointAt(pos - 2) : undefined;\n if (pair !== undefined && pair > 0xffff) {\n return String.fromCodePoint(pair);\n }\n return scalarAt(text, pos - 1);\n}\n\n/**\n * Parse note content (or a full event, to pick up `emoji` tags) into ordered blocks. One\n * left-to-right scan over Unicode scalar values; adjacent text merges and no empty text blocks are\n * emitted.\n */\nexport function parseContent(\n content: string | Pick<Event, \"content\" | \"tags\">,\n opts?: ParseContentOptions,\n): ContentBlock[] {\n const emojis = new Map<string, string>();\n let text: string;\n if (typeof content === \"string\") {\n text = content;\n } else {\n for (const tag of content.tags) {\n if (\n tag[0] === \"emoji\" &&\n tag[1] !== undefined &&\n tag[1] !== \"\" &&\n tag[2] !== undefined &&\n tag[2] !== \"\"\n ) {\n emojis.set(tag[1], tag[2]);\n }\n }\n text = content.content;\n }\n\n const blocks: ContentBlock[] = [];\n const legacy = opts?.legacyBech32 === true;\n let textStart = 0;\n const flushText = (end: number): void => {\n if (end > textStart) {\n blocks.push({ type: \"text\", text: text.slice(textStart, end) });\n }\n };\n\n let pos = 0;\n while (pos < text.length) {\n const rest = text.slice(pos);\n const candidate = candidateAt(rest, legacy);\n if (candidate === undefined) {\n const c = scalarAt(text, pos);\n pos += c === undefined ? 1 : c.length;\n continue;\n }\n const [token, endRel] = candidate;\n const end = pos + endRel;\n const prevScalar = scalarBefore(text, pos);\n const next = scalarAt(text, end);\n\n let outcome: [ContentBlock, number] | undefined;\n switch (token.kind) {\n case \"nostr\": {\n const block = referenceFor(rest.slice(token.codeStart, endRel), false);\n outcome = block === undefined ? undefined : [block, end];\n break;\n }\n case \"url\": {\n const block = urlBlock(rest.slice(0, endRel), false, opts?.imeta);\n outcome = block === undefined ? undefined : [block, pos + block.url.length];\n break;\n }\n case \"relay\": {\n const block = urlBlock(rest.slice(0, endRel), true, undefined);\n outcome = block === undefined ? undefined : [block, pos + block.url.length];\n break;\n }\n case \"invoice\": {\n const glued =\n (prevScalar !== undefined && isWordChar(prevScalar)) ||\n (next !== undefined && isWordChar(next));\n outcome = glued\n ? undefined\n : [{ type: \"invoice\", bolt11: lowerAscii(rest.slice(token.prefixLen, endRel)) }, end];\n break;\n }\n case \"hashtag\": {\n const invalid = token.count > 42 || (prevScalar !== undefined && isTagChar(prevScalar));\n outcome = invalid ? undefined : [{ type: \"hashtag\", value: rest.slice(1, endRel) }, end];\n break;\n }\n case \"emoji\": {\n const shortcode = rest.slice(1, endRel - 1);\n const url = emojis.get(shortcode);\n outcome = url === undefined ? undefined : [{ type: \"emoji\", shortcode, url }, end];\n break;\n }\n case \"bare\":\n if (\n (prevScalar !== undefined && isWordChar(prevScalar)) ||\n (next !== undefined && isWordChar(next))\n ) {\n outcome = undefined;\n } else {\n const block = referenceFor(rest.slice(0, endRel), true);\n outcome = block === undefined ? undefined : [block, end];\n }\n break;\n }\n\n if (outcome === undefined) {\n // Shape matched but validation failed — `pos` is text, resume at the next scalar.\n const c = scalarAt(text, pos);\n pos += c === undefined ? 1 : c.length;\n continue;\n }\n const [block, nextTextStart] = outcome;\n flushText(pos);\n blocks.push(block);\n textStart = nextTextStart;\n pos = end;\n }\n\n flushText(text.length);\n return blocks;\n}\n"],"mappings":"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"}
|
package/dist/nips/nip42.mjs
CHANGED
package/dist/nips/nip44.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { n as NostrBackend } from "../key-E0AJKMRB.mjs";
|
|
1
2
|
//#region src/nips/nip44.d.ts
|
|
2
3
|
/**
|
|
3
4
|
* Default decode-side payload cap: the base64 length of a payload carrying exactly 1 MiB (0x100000
|
|
@@ -7,7 +8,7 @@
|
|
|
7
8
|
* large local payloads.
|
|
8
9
|
*/
|
|
9
10
|
export declare const DEFAULT_MAX_PAYLOAD_CHARS: number;
|
|
10
|
-
export declare function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array;
|
|
11
|
+
export declare function getConversationKey(privkeyA: Uint8Array, pubkeyB: string, backend?: NostrBackend): Uint8Array;
|
|
11
12
|
/**
|
|
12
13
|
* NIP-44 conversation key from the raw 32-byte ECDH x-coordinate instead of a secret key, for key
|
|
13
14
|
* holders that only expose ECDH ({@link NostrKeyOperations}).
|
package/dist/nips/nip44.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { _ as
|
|
1
|
+
import { _ as utf8Decoder, d as lowerAscii, n as assertHex32, r as assertSecretKeyBytes, s as hexToBytes$1, v as utf8Encoder, y as CryptoError } from "../util-BcRBO6o-.mjs";
|
|
2
|
+
import { a as nobleBackend } from "../key-Bt0ow7mI.mjs";
|
|
2
3
|
import { concatBytes, randomBytes } from "@noble/hashes/utils.js";
|
|
3
4
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
4
|
-
import { secp256k1 } from "@noble/curves/secp256k1.js";
|
|
5
5
|
import { base64 } from "@scure/base";
|
|
6
6
|
import { chacha20 } from "@noble/ciphers/chacha.js";
|
|
7
7
|
import { equalBytes } from "@noble/ciphers/utils.js";
|
|
@@ -22,10 +22,10 @@ const DEFAULT_MAX_PAYLOAD_CHARS = Math.ceil((71 + calcPaddedLen(1048576)) / 3) *
|
|
|
22
22
|
function assert32(bytes, label) {
|
|
23
23
|
if (bytes.length !== 32) throw new CryptoError(`${label} must be 32 bytes`);
|
|
24
24
|
}
|
|
25
|
-
function getConversationKey(privkeyA, pubkeyB) {
|
|
25
|
+
function getConversationKey(privkeyA, pubkeyB, backend = nobleBackend) {
|
|
26
26
|
assertSecretKeyBytes(privkeyA);
|
|
27
27
|
assertHex32(pubkeyB, "public key");
|
|
28
|
-
return getConversationKeyFromSharedSecret(
|
|
28
|
+
return getConversationKeyFromSharedSecret(backend.ecdhX(privkeyA, hexToBytes$1(lowerAscii(pubkeyB))));
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
31
|
* NIP-44 conversation key from the raw 32-byte ECDH x-coordinate instead of a secret key, for key
|