nostr-tools 1.7.5 → 1.8.0

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 CHANGED
@@ -170,6 +170,26 @@ let relaysForEvent = pool.seenOn(
170
170
  // relaysForEvent will be an array of URLs from relays a given event was seen on
171
171
  ```
172
172
 
173
+ ### Parsing references (mentions) from a content using NIP-10 and NIP-27
174
+
175
+ ```js
176
+ import {parseReferences} from 'nostr-tools'
177
+
178
+ let references = parseReferences(event)
179
+ let simpleAugmentedContent = event.content
180
+ for (let i = 0; i < references.length; i++) {
181
+ let {text, profile, event, address} = references[i]
182
+ let augmentedReference = profile
183
+ ? `<strong>@${profilesCache[profile.pubkey].name}</strong>`
184
+ : event
185
+ ? `<em>${eventsCache[event.id].content.slice(0, 5)}</em>`
186
+ : address
187
+ ? `<a href="${text}">[link]</a>`
188
+ : text
189
+ simpleAugmentedContent.replaceAll(text, augmentedReference)
190
+ }
191
+ ```
192
+
173
193
  ### Querying profile data from a NIP-05 address
174
194
 
175
195
  ```js
package/lib/esm/nostr.mjs CHANGED
@@ -569,6 +569,7 @@ var SimplePool = class {
569
569
  let set = this._seenOn[id] || /* @__PURE__ */ new Set();
570
570
  set.add(url);
571
571
  this._seenOn[id] = set;
572
+ _knownIds.add(id);
572
573
  return _knownIds.has(id);
573
574
  };
574
575
  let subs = [];
@@ -593,7 +594,6 @@ var SimplePool = class {
593
594
  return;
594
595
  let s = r.sub(filters, modifiedOpts);
595
596
  s.on("event", (event) => {
596
- _knownIds.add(event.id);
597
597
  for (let cb of eventListeners.values())
598
598
  cb(event);
599
599
  });
@@ -688,139 +688,6 @@ var SimplePool = class {
688
688
  }
689
689
  };
690
690
 
691
- // nip04.ts
692
- var nip04_exports = {};
693
- __export(nip04_exports, {
694
- decrypt: () => decrypt,
695
- encrypt: () => encrypt
696
- });
697
- import { randomBytes } from "@noble/hashes/utils";
698
- import * as secp256k13 from "@noble/secp256k1";
699
- import { base64 } from "@scure/base";
700
- async function encrypt(privkey, pubkey, text) {
701
- const key = secp256k13.getSharedSecret(privkey, "02" + pubkey);
702
- const normalizedKey = getNormalizedX(key);
703
- let iv = Uint8Array.from(randomBytes(16));
704
- let plaintext = utf8Encoder.encode(text);
705
- let cryptoKey = await crypto.subtle.importKey(
706
- "raw",
707
- normalizedKey,
708
- { name: "AES-CBC" },
709
- false,
710
- ["encrypt"]
711
- );
712
- let ciphertext = await crypto.subtle.encrypt(
713
- { name: "AES-CBC", iv },
714
- cryptoKey,
715
- plaintext
716
- );
717
- let ctb64 = base64.encode(new Uint8Array(ciphertext));
718
- let ivb64 = base64.encode(new Uint8Array(iv.buffer));
719
- return `${ctb64}?iv=${ivb64}`;
720
- }
721
- async function decrypt(privkey, pubkey, data) {
722
- let [ctb64, ivb64] = data.split("?iv=");
723
- let key = secp256k13.getSharedSecret(privkey, "02" + pubkey);
724
- let normalizedKey = getNormalizedX(key);
725
- let cryptoKey = await crypto.subtle.importKey(
726
- "raw",
727
- normalizedKey,
728
- { name: "AES-CBC" },
729
- false,
730
- ["decrypt"]
731
- );
732
- let ciphertext = base64.decode(ctb64);
733
- let iv = base64.decode(ivb64);
734
- let plaintext = await crypto.subtle.decrypt(
735
- { name: "AES-CBC", iv },
736
- cryptoKey,
737
- ciphertext
738
- );
739
- let text = utf8Decoder.decode(plaintext);
740
- return text;
741
- }
742
- function getNormalizedX(key) {
743
- return key.slice(1, 33);
744
- }
745
-
746
- // nip05.ts
747
- var nip05_exports = {};
748
- __export(nip05_exports, {
749
- queryProfile: () => queryProfile,
750
- searchDomain: () => searchDomain,
751
- useFetchImplementation: () => useFetchImplementation
752
- });
753
- var _fetch;
754
- try {
755
- _fetch = fetch;
756
- } catch {
757
- }
758
- function useFetchImplementation(fetchImplementation) {
759
- _fetch = fetchImplementation;
760
- }
761
- async function searchDomain(domain, query = "") {
762
- try {
763
- let res = await (await _fetch(`https://${domain}/.well-known/nostr.json?name=${query}`)).json();
764
- return res.names;
765
- } catch (_) {
766
- return {};
767
- }
768
- }
769
- async function queryProfile(fullname) {
770
- let [name, domain] = fullname.split("@");
771
- if (!domain) {
772
- domain = name;
773
- name = "_";
774
- }
775
- if (!name.match(/^[A-Za-z0-9-_]+$/))
776
- return null;
777
- if (!domain.includes("."))
778
- return null;
779
- let res;
780
- try {
781
- res = await (await _fetch(`https://${domain}/.well-known/nostr.json?name=${name}`)).json();
782
- } catch (err) {
783
- return null;
784
- }
785
- if (!res?.names?.[name])
786
- return null;
787
- let pubkey = res.names[name];
788
- let relays = res.relays?.[pubkey] || [];
789
- return {
790
- pubkey,
791
- relays
792
- };
793
- }
794
-
795
- // nip06.ts
796
- var nip06_exports = {};
797
- __export(nip06_exports, {
798
- generateSeedWords: () => generateSeedWords,
799
- privateKeyFromSeedWords: () => privateKeyFromSeedWords,
800
- validateWords: () => validateWords
801
- });
802
- import * as secp256k14 from "@noble/secp256k1";
803
- import { wordlist } from "@scure/bip39/wordlists/english.js";
804
- import {
805
- generateMnemonic,
806
- mnemonicToSeedSync,
807
- validateMnemonic
808
- } from "@scure/bip39";
809
- import { HDKey } from "@scure/bip32";
810
- function privateKeyFromSeedWords(mnemonic, passphrase) {
811
- let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase));
812
- let privateKey = root.derive(`m/44'/1237'/0'/0/0`).privateKey;
813
- if (!privateKey)
814
- throw new Error("could not derive private key");
815
- return secp256k14.utils.bytesToHex(privateKey);
816
- }
817
- function generateSeedWords() {
818
- return generateMnemonic(wordlist);
819
- }
820
- function validateWords(words) {
821
- return validateMnemonic(words, wordlist);
822
- }
823
-
824
691
  // nip19.ts
825
692
  var nip19_exports = {};
826
693
  __export(nip19_exports, {
@@ -832,7 +699,7 @@ __export(nip19_exports, {
832
699
  npubEncode: () => npubEncode,
833
700
  nsecEncode: () => nsecEncode
834
701
  });
835
- import * as secp256k15 from "@noble/secp256k1";
702
+ import * as secp256k13 from "@noble/secp256k1";
836
703
  import { bech32 } from "@scure/base";
837
704
  var Bech32MaxSize = 5e3;
838
705
  function decode(nip19) {
@@ -848,7 +715,7 @@ function decode(nip19) {
848
715
  return {
849
716
  type: "nprofile",
850
717
  data: {
851
- pubkey: secp256k15.utils.bytesToHex(tlv[0][0]),
718
+ pubkey: secp256k13.utils.bytesToHex(tlv[0][0]),
852
719
  relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
853
720
  }
854
721
  };
@@ -859,11 +726,14 @@ function decode(nip19) {
859
726
  throw new Error("missing TLV 0 for nevent");
860
727
  if (tlv[0][0].length !== 32)
861
728
  throw new Error("TLV 0 should be 32 bytes");
729
+ if (tlv[2] && tlv[2][0].length !== 32)
730
+ throw new Error("TLV 2 should be 32 bytes");
862
731
  return {
863
732
  type: "nevent",
864
733
  data: {
865
- id: secp256k15.utils.bytesToHex(tlv[0][0]),
866
- relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
734
+ id: secp256k13.utils.bytesToHex(tlv[0][0]),
735
+ relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : [],
736
+ author: tlv[2]?.[0] ? secp256k13.utils.bytesToHex(tlv[2][0]) : void 0
867
737
  }
868
738
  };
869
739
  }
@@ -883,8 +753,8 @@ function decode(nip19) {
883
753
  type: "naddr",
884
754
  data: {
885
755
  identifier: utf8Decoder.decode(tlv[0][0]),
886
- pubkey: secp256k15.utils.bytesToHex(tlv[2][0]),
887
- kind: parseInt(secp256k15.utils.bytesToHex(tlv[3][0]), 16),
756
+ pubkey: secp256k13.utils.bytesToHex(tlv[2][0]),
757
+ kind: parseInt(secp256k13.utils.bytesToHex(tlv[3][0]), 16),
888
758
  relays: tlv[1] ? tlv[1].map((d) => utf8Decoder.decode(d)) : []
889
759
  }
890
760
  };
@@ -892,7 +762,7 @@ function decode(nip19) {
892
762
  case "nsec":
893
763
  case "npub":
894
764
  case "note":
895
- return { type: prefix, data: secp256k15.utils.bytesToHex(data) };
765
+ return { type: prefix, data: secp256k13.utils.bytesToHex(data) };
896
766
  default:
897
767
  throw new Error(`unknown prefix ${prefix}`);
898
768
  }
@@ -922,13 +792,13 @@ function noteEncode(hex) {
922
792
  return encodeBytes("note", hex);
923
793
  }
924
794
  function encodeBytes(prefix, hex) {
925
- let data = secp256k15.utils.hexToBytes(hex);
795
+ let data = secp256k13.utils.hexToBytes(hex);
926
796
  let words = bech32.toWords(data);
927
797
  return bech32.encode(prefix, words, Bech32MaxSize);
928
798
  }
929
799
  function nprofileEncode(profile) {
930
800
  let data = encodeTLV({
931
- 0: [secp256k15.utils.hexToBytes(profile.pubkey)],
801
+ 0: [secp256k13.utils.hexToBytes(profile.pubkey)],
932
802
  1: (profile.relays || []).map((url) => utf8Encoder.encode(url))
933
803
  });
934
804
  let words = bech32.toWords(data);
@@ -936,8 +806,9 @@ function nprofileEncode(profile) {
936
806
  }
937
807
  function neventEncode(event) {
938
808
  let data = encodeTLV({
939
- 0: [secp256k15.utils.hexToBytes(event.id)],
940
- 1: (event.relays || []).map((url) => utf8Encoder.encode(url))
809
+ 0: [secp256k13.utils.hexToBytes(event.id)],
810
+ 1: (event.relays || []).map((url) => utf8Encoder.encode(url)),
811
+ 2: event.author ? [secp256k13.utils.hexToBytes(event.author)] : []
941
812
  });
942
813
  let words = bech32.toWords(data);
943
814
  return bech32.encode("nevent", words, Bech32MaxSize);
@@ -948,7 +819,7 @@ function naddrEncode(addr) {
948
819
  let data = encodeTLV({
949
820
  0: [utf8Encoder.encode(addr.identifier)],
950
821
  1: (addr.relays || []).map((url) => utf8Encoder.encode(url)),
951
- 2: [secp256k15.utils.hexToBytes(addr.pubkey)],
822
+ 2: [secp256k13.utils.hexToBytes(addr.pubkey)],
952
823
  3: [new Uint8Array(kind)]
953
824
  });
954
825
  let words = bech32.toWords(data);
@@ -965,7 +836,229 @@ function encodeTLV(tlv) {
965
836
  entries.push(entry);
966
837
  });
967
838
  });
968
- return secp256k15.utils.concatBytes(...entries);
839
+ return secp256k13.utils.concatBytes(...entries);
840
+ }
841
+
842
+ // references.ts
843
+ var mentionRegex = /\bnostr:((note|npub|naddr|nevent|nprofile)1\w+)\b|#\[(\d+)\]/g;
844
+ function parseReferences(evt) {
845
+ let references = [];
846
+ for (let ref of evt.content.matchAll(mentionRegex)) {
847
+ if (ref[2]) {
848
+ try {
849
+ let { type, data } = decode(ref[1]);
850
+ switch (type) {
851
+ case "npub": {
852
+ references.push({
853
+ text: ref[0],
854
+ profile: { pubkey: data, relays: [] }
855
+ });
856
+ break;
857
+ }
858
+ case "nprofile": {
859
+ references.push({
860
+ text: ref[0],
861
+ profile: data
862
+ });
863
+ break;
864
+ }
865
+ case "note": {
866
+ references.push({
867
+ text: ref[0],
868
+ event: { id: data, relays: [] }
869
+ });
870
+ break;
871
+ }
872
+ case "nevent": {
873
+ references.push({
874
+ text: ref[0],
875
+ event: data
876
+ });
877
+ break;
878
+ }
879
+ case "naddr": {
880
+ references.push({
881
+ text: ref[0],
882
+ address: data
883
+ });
884
+ break;
885
+ }
886
+ }
887
+ } catch (err) {
888
+ }
889
+ } else if (ref[3]) {
890
+ let idx = parseInt(ref[3], 10);
891
+ let tag = evt.tags[idx];
892
+ if (!tag)
893
+ continue;
894
+ switch (tag[0]) {
895
+ case "p": {
896
+ references.push({
897
+ text: ref[0],
898
+ profile: { pubkey: tag[1], relays: tag[2] ? [tag[2]] : [] }
899
+ });
900
+ break;
901
+ }
902
+ case "e": {
903
+ references.push({
904
+ text: ref[0],
905
+ event: { id: tag[1], relays: tag[2] ? [tag[2]] : [] }
906
+ });
907
+ break;
908
+ }
909
+ case "a": {
910
+ try {
911
+ let [kind, pubkey, identifier] = ref[1].split(":");
912
+ references.push({
913
+ text: ref[0],
914
+ address: {
915
+ identifier,
916
+ pubkey,
917
+ kind: parseInt(kind, 10),
918
+ relays: tag[2] ? [tag[2]] : []
919
+ }
920
+ });
921
+ } catch (err) {
922
+ }
923
+ break;
924
+ }
925
+ }
926
+ }
927
+ }
928
+ return references;
929
+ }
930
+
931
+ // nip04.ts
932
+ var nip04_exports = {};
933
+ __export(nip04_exports, {
934
+ decrypt: () => decrypt,
935
+ encrypt: () => encrypt
936
+ });
937
+ import { randomBytes } from "@noble/hashes/utils";
938
+ import * as secp256k14 from "@noble/secp256k1";
939
+ import { base64 } from "@scure/base";
940
+ async function encrypt(privkey, pubkey, text) {
941
+ const key = secp256k14.getSharedSecret(privkey, "02" + pubkey);
942
+ const normalizedKey = getNormalizedX(key);
943
+ let iv = Uint8Array.from(randomBytes(16));
944
+ let plaintext = utf8Encoder.encode(text);
945
+ let cryptoKey = await crypto.subtle.importKey(
946
+ "raw",
947
+ normalizedKey,
948
+ { name: "AES-CBC" },
949
+ false,
950
+ ["encrypt"]
951
+ );
952
+ let ciphertext = await crypto.subtle.encrypt(
953
+ { name: "AES-CBC", iv },
954
+ cryptoKey,
955
+ plaintext
956
+ );
957
+ let ctb64 = base64.encode(new Uint8Array(ciphertext));
958
+ let ivb64 = base64.encode(new Uint8Array(iv.buffer));
959
+ return `${ctb64}?iv=${ivb64}`;
960
+ }
961
+ async function decrypt(privkey, pubkey, data) {
962
+ let [ctb64, ivb64] = data.split("?iv=");
963
+ let key = secp256k14.getSharedSecret(privkey, "02" + pubkey);
964
+ let normalizedKey = getNormalizedX(key);
965
+ let cryptoKey = await crypto.subtle.importKey(
966
+ "raw",
967
+ normalizedKey,
968
+ { name: "AES-CBC" },
969
+ false,
970
+ ["decrypt"]
971
+ );
972
+ let ciphertext = base64.decode(ctb64);
973
+ let iv = base64.decode(ivb64);
974
+ let plaintext = await crypto.subtle.decrypt(
975
+ { name: "AES-CBC", iv },
976
+ cryptoKey,
977
+ ciphertext
978
+ );
979
+ let text = utf8Decoder.decode(plaintext);
980
+ return text;
981
+ }
982
+ function getNormalizedX(key) {
983
+ return key.slice(1, 33);
984
+ }
985
+
986
+ // nip05.ts
987
+ var nip05_exports = {};
988
+ __export(nip05_exports, {
989
+ queryProfile: () => queryProfile,
990
+ searchDomain: () => searchDomain,
991
+ useFetchImplementation: () => useFetchImplementation
992
+ });
993
+ var _fetch;
994
+ try {
995
+ _fetch = fetch;
996
+ } catch {
997
+ }
998
+ function useFetchImplementation(fetchImplementation) {
999
+ _fetch = fetchImplementation;
1000
+ }
1001
+ async function searchDomain(domain, query = "") {
1002
+ try {
1003
+ let res = await (await _fetch(`https://${domain}/.well-known/nostr.json?name=${query}`)).json();
1004
+ return res.names;
1005
+ } catch (_) {
1006
+ return {};
1007
+ }
1008
+ }
1009
+ async function queryProfile(fullname) {
1010
+ let [name, domain] = fullname.split("@");
1011
+ if (!domain) {
1012
+ domain = name;
1013
+ name = "_";
1014
+ }
1015
+ if (!name.match(/^[A-Za-z0-9-_]+$/))
1016
+ return null;
1017
+ if (!domain.includes("."))
1018
+ return null;
1019
+ let res;
1020
+ try {
1021
+ res = await (await _fetch(`https://${domain}/.well-known/nostr.json?name=${name}`)).json();
1022
+ } catch (err) {
1023
+ return null;
1024
+ }
1025
+ if (!res?.names?.[name])
1026
+ return null;
1027
+ let pubkey = res.names[name];
1028
+ let relays = res.relays?.[pubkey] || [];
1029
+ return {
1030
+ pubkey,
1031
+ relays
1032
+ };
1033
+ }
1034
+
1035
+ // nip06.ts
1036
+ var nip06_exports = {};
1037
+ __export(nip06_exports, {
1038
+ generateSeedWords: () => generateSeedWords,
1039
+ privateKeyFromSeedWords: () => privateKeyFromSeedWords,
1040
+ validateWords: () => validateWords
1041
+ });
1042
+ import * as secp256k15 from "@noble/secp256k1";
1043
+ import { wordlist } from "@scure/bip39/wordlists/english.js";
1044
+ import {
1045
+ generateMnemonic,
1046
+ mnemonicToSeedSync,
1047
+ validateMnemonic
1048
+ } from "@scure/bip39";
1049
+ import { HDKey } from "@scure/bip32";
1050
+ function privateKeyFromSeedWords(mnemonic, passphrase) {
1051
+ let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase));
1052
+ let privateKey = root.derive(`m/44'/1237'/0'/0/0`).privateKey;
1053
+ if (!privateKey)
1054
+ throw new Error("could not derive private key");
1055
+ return secp256k15.utils.bytesToHex(privateKey);
1056
+ }
1057
+ function generateSeedWords() {
1058
+ return generateMnemonic(wordlist);
1059
+ }
1060
+ function validateWords(words) {
1061
+ return validateMnemonic(words, wordlist);
969
1062
  }
970
1063
 
971
1064
  // nip26.ts
@@ -1191,6 +1284,7 @@ export {
1191
1284
  nip26_exports as nip26,
1192
1285
  nip39_exports as nip39,
1193
1286
  nip57_exports as nip57,
1287
+ parseReferences,
1194
1288
  relayInit,
1195
1289
  serializeEvent,
1196
1290
  signEvent,