nostr-tools 2.23.1 → 2.23.3

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.
Files changed (69) hide show
  1. package/lib/cjs/abstract-pool.js +10 -2
  2. package/lib/cjs/abstract-pool.js.map +2 -2
  3. package/lib/cjs/abstract-relay.js +10 -2
  4. package/lib/cjs/abstract-relay.js.map +2 -2
  5. package/lib/cjs/index.js +15 -7
  6. package/lib/cjs/index.js.map +2 -2
  7. package/lib/cjs/nip17.js +5 -5
  8. package/lib/cjs/nip17.js.map +2 -2
  9. package/lib/cjs/nip18.js +5 -5
  10. package/lib/cjs/nip18.js.map +2 -2
  11. package/lib/cjs/nip25.js +5 -5
  12. package/lib/cjs/nip25.js.map +2 -2
  13. package/lib/cjs/nip28.js +5 -5
  14. package/lib/cjs/nip28.js.map +2 -2
  15. package/lib/cjs/nip46.js +15 -7
  16. package/lib/cjs/nip46.js.map +2 -2
  17. package/lib/cjs/nip47.js +5 -5
  18. package/lib/cjs/nip47.js.map +2 -2
  19. package/lib/cjs/nip57.js +5 -5
  20. package/lib/cjs/nip57.js.map +2 -2
  21. package/lib/cjs/nip59.js +5 -5
  22. package/lib/cjs/nip59.js.map +2 -2
  23. package/lib/cjs/nip98.js +5 -5
  24. package/lib/cjs/nip98.js.map +2 -2
  25. package/lib/cjs/pool.js +15 -7
  26. package/lib/cjs/pool.js.map +2 -2
  27. package/lib/cjs/pure.js +5 -5
  28. package/lib/cjs/pure.js.map +2 -2
  29. package/lib/cjs/relay.js +15 -7
  30. package/lib/cjs/relay.js.map +2 -2
  31. package/lib/cjs/signer.js +5 -5
  32. package/lib/cjs/signer.js.map +2 -2
  33. package/lib/esm/abstract-pool.js +10 -2
  34. package/lib/esm/abstract-pool.js.map +2 -2
  35. package/lib/esm/abstract-relay.js +10 -2
  36. package/lib/esm/abstract-relay.js.map +2 -2
  37. package/lib/esm/index.js +15 -7
  38. package/lib/esm/index.js.map +2 -2
  39. package/lib/esm/nip17.js +5 -5
  40. package/lib/esm/nip17.js.map +2 -2
  41. package/lib/esm/nip18.js +5 -5
  42. package/lib/esm/nip18.js.map +2 -2
  43. package/lib/esm/nip25.js +5 -5
  44. package/lib/esm/nip25.js.map +2 -2
  45. package/lib/esm/nip28.js +5 -5
  46. package/lib/esm/nip28.js.map +2 -2
  47. package/lib/esm/nip46.js +15 -7
  48. package/lib/esm/nip46.js.map +2 -2
  49. package/lib/esm/nip47.js +5 -5
  50. package/lib/esm/nip47.js.map +2 -2
  51. package/lib/esm/nip57.js +5 -5
  52. package/lib/esm/nip57.js.map +2 -2
  53. package/lib/esm/nip59.js +5 -5
  54. package/lib/esm/nip59.js.map +2 -2
  55. package/lib/esm/nip98.js +5 -5
  56. package/lib/esm/nip98.js.map +2 -2
  57. package/lib/esm/pool.js +15 -7
  58. package/lib/esm/pool.js.map +2 -2
  59. package/lib/esm/pure.js +5 -5
  60. package/lib/esm/pure.js.map +2 -2
  61. package/lib/esm/relay.js +15 -7
  62. package/lib/esm/relay.js.map +2 -2
  63. package/lib/esm/signer.js +5 -5
  64. package/lib/esm/signer.js.map +2 -2
  65. package/lib/nostr.bundle.js +23 -15
  66. package/lib/nostr.bundle.js.map +3 -3
  67. package/lib/types/abstract-pool.d.ts +2 -2
  68. package/lib/types/abstract-relay.d.ts +2 -0
  69. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../pure.ts", "../../core.ts", "../../utils.ts"],
4
- "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n generateSecretKey(): Uint8Array {\n return schnorr.utils.randomSecretKey()\n }\n getPublicKey(secretKey: Uint8Array): string {\n return bytesToHex(schnorr.getPublicKey(secretKey))\n }\n finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n const event = t as VerifiedEvent\n event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n event.id = getEventHash(event)\n event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n event[verifiedSymbol] = true\n return event\n }\n verifyEvent(event: Event): event is VerifiedEvent {\n if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n const hash = getEventHash(event)\n if (hash !== event.id) {\n event[verifiedSymbol] = false\n return false\n }\n\n try {\n const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n event[verifiedSymbol] = valid\n return valid\n } catch (err) {\n event[verifiedSymbol] = false\n return false\n }\n }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n generateSecretKey(): Uint8Array\n getPublicKey(secretKey: Uint8Array): string\n finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n kind: number\n tags: string[][]\n content: string\n created_at: number\n pubkey: string\n id: string\n sig: string\n [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n if (!isRecord(event)) return false\n if (typeof event.kind !== 'number') return false\n if (typeof event.content !== 'string') return false\n if (typeof event.created_at !== 'number') return false\n if (typeof event.pubkey !== 'string') return false\n if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n if (!Array.isArray(event.tags)) return false\n for (let i = 0; i < event.tags.length; i++) {\n let tag = event.tags[i]\n if (!Array.isArray(tag)) return false\n for (let j = 0; j < tag.length; j++) {\n if (typeof tag[j] !== 'string') return false\n }\n }\n\n return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n return events.sort((a: NostrEvent, b: NostrEvent): number => {\n if (a.created_at !== b.created_at) {\n return b.created_at - a.created_at\n }\n return a.id.localeCompare(b.id)\n })\n}\n", "import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n generateSecretKey(): Uint8Array {\n return schnorr.utils.randomSecretKey()\n }\n getPublicKey(secretKey: Uint8Array): string {\n return bytesToHex(schnorr.getPublicKey(secretKey))\n }\n finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n const event = t as VerifiedEvent\n event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n event.id = getEventHash(event)\n event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n event[verifiedSymbol] = true\n return event\n }\n verifyEvent(event: Event): event is VerifiedEvent {\n if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n try {\n const hash = getEventHash(event)\n if (hash !== event.id) {\n event[verifiedSymbol] = false\n return false\n }\n\n const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n event[verifiedSymbol] = valid\n return valid\n } catch (err) {\n event[verifiedSymbol] = false\n return false\n }\n }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n generateSecretKey(): Uint8Array\n getPublicKey(secretKey: Uint8Array): string\n finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n kind: number\n tags: string[][]\n content: string\n created_at: number\n pubkey: string\n id: string\n sig: string\n [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n if (!isRecord(event)) return false\n if (typeof event.kind !== 'number') return false\n if (typeof event.content !== 'string') return false\n if (typeof event.created_at !== 'number') return false\n if (typeof event.pubkey !== 'string') return false\n if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n if (!Array.isArray(event.tags)) return false\n for (let i = 0; i < event.tags.length; i++) {\n let tag = event.tags[i]\n if (!Array.isArray(tag)) return false\n for (let j = 0; j < tag.length; j++) {\n if (typeof tag[j] !== 'string') return false\n }\n }\n\n return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n return events.sort((a: NostrEvent, b: NostrEvent): number => {\n if (a.created_at !== b.created_at) {\n return b.created_at - a.created_at\n }\n return a.id.localeCompare(b.id)\n })\n}\n", "import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["import_utils", "i"]
7
7
  }
package/lib/cjs/relay.js CHANGED
@@ -110,12 +110,12 @@ var JS = class {
110
110
  verifyEvent(event) {
111
111
  if (typeof event[verifiedSymbol] === "boolean")
112
112
  return event[verifiedSymbol];
113
- const hash = getEventHash(event);
114
- if (hash !== event.id) {
115
- event[verifiedSymbol] = false;
116
- return false;
117
- }
118
113
  try {
114
+ const hash = getEventHash(event);
115
+ if (hash !== event.id) {
116
+ event[verifiedSymbol] = false;
117
+ return false;
118
+ }
119
119
  const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
120
120
  event[verifiedSymbol] = valid;
121
121
  return valid;
@@ -537,6 +537,8 @@ var AbstractRelay = class {
537
537
  const event = data[2];
538
538
  if (this.verifyEvent(event) && matchFilters(so.filters, event)) {
539
539
  so.onevent(event);
540
+ } else {
541
+ so.oninvalidevent?.(event);
540
542
  }
541
543
  if (!so.lastEmitted || so.lastEmitted < event.created_at)
542
544
  so.lastEmitted = event.created_at;
@@ -601,8 +603,12 @@ var AbstractRelay = class {
601
603
  }
602
604
  }
603
605
  } catch (err) {
604
- const [_, __, event] = JSON.parse(json);
605
- window.printer.maybe(event.pubkey, ":: caught err", event, this.url, err);
606
+ try {
607
+ const [_, __, event] = JSON.parse(json);
608
+ console.warn(`[nostr] relay ${this.url} error processing message:`, err, event);
609
+ } catch (_) {
610
+ console.warn(`[nostr] relay ${this.url} error processing message:`, err);
611
+ }
606
612
  return;
607
613
  }
608
614
  }
@@ -617,6 +623,7 @@ var Subscription = class {
617
623
  alreadyHaveEvent;
618
624
  receivedEvent;
619
625
  onevent;
626
+ oninvalidevent;
620
627
  oneose;
621
628
  onclose;
622
629
  oncustom;
@@ -633,6 +640,7 @@ var Subscription = class {
633
640
  this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout;
634
641
  this.oneose = params.oneose;
635
642
  this.onclose = params.onclose;
643
+ this.oninvalidevent = params.oninvalidevent;
636
644
  this.onevent = params.onevent || ((event) => {
637
645
  console.warn(
638
646
  `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../relay.ts", "../../pure.ts", "../../core.ts", "../../utils.ts", "../../kinds.ts", "../../filter.ts", "../../fakejson.ts", "../../nip42.ts", "../../abstract-relay.ts"],
4
- "sourcesContent": ["/* global WebSocket */\n\nimport { verifyEvent } from './pure.ts'\nimport { AbstractRelay, type AbstractRelayConstructorOptions } from './abstract-relay.ts'\n\nvar _WebSocket: typeof WebSocket\n\ntry {\n _WebSocket = WebSocket\n} catch {}\n\nexport function useWebSocketImplementation(websocketImplementation: any) {\n _WebSocket = websocketImplementation\n}\n\nexport class Relay extends AbstractRelay {\n constructor(url: string, options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect'>) {\n super(url, { verifyEvent, websocketImplementation: _WebSocket, ...options })\n }\n\n static async connect(\n url: string,\n options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect'>,\n ): Promise<Relay> {\n const relay = new Relay(url, options)\n await relay.connect()\n return relay\n }\n}\n\nexport type RelayRecord = Record<string, { read: boolean; write: boolean }>\n\nexport * from './abstract-relay.ts'\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n generateSecretKey(): Uint8Array {\n return schnorr.utils.randomSecretKey()\n }\n getPublicKey(secretKey: Uint8Array): string {\n return bytesToHex(schnorr.getPublicKey(secretKey))\n }\n finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n const event = t as VerifiedEvent\n event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n event.id = getEventHash(event)\n event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n event[verifiedSymbol] = true\n return event\n }\n verifyEvent(event: Event): event is VerifiedEvent {\n if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n const hash = getEventHash(event)\n if (hash !== event.id) {\n event[verifiedSymbol] = false\n return false\n }\n\n try {\n const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n event[verifiedSymbol] = valid\n return valid\n } catch (err) {\n event[verifiedSymbol] = false\n return false\n }\n }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n generateSecretKey(): Uint8Array\n getPublicKey(secretKey: Uint8Array): string\n finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n kind: number\n tags: string[][]\n content: string\n created_at: number\n pubkey: string\n id: string\n sig: string\n [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n if (!isRecord(event)) return false\n if (typeof event.kind !== 'number') return false\n if (typeof event.content !== 'string') return false\n if (typeof event.created_at !== 'number') return false\n if (typeof event.pubkey !== 'string') return false\n if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n if (!Array.isArray(event.tags)) return false\n for (let i = 0; i < event.tags.length; i++) {\n let tag = event.tags[i]\n if (!Array.isArray(tag)) return false\n for (let j = 0; j < tag.length; j++) {\n if (typeof tag[j] !== 'string') return false\n }\n }\n\n return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n return events.sort((a: NostrEvent, b: NostrEvent): number => {\n if (a.created_at !== b.created_at) {\n return b.created_at - a.created_at\n }\n return a.id.localeCompare(b.id)\n })\n}\n", "import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n if (isRegularKind(kind)) return 'regular'\n if (isReplaceableKind(kind)) return 'replaceable'\n if (isEphemeralKind(kind)) return 'ephemeral'\n if (isAddressableKind(kind)) return 'parameterized'\n return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n ids?: string[]\n kinds?: number[]\n authors?: string[]\n since?: number\n until?: number\n limit?: number\n search?: string\n [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n return false\n }\n if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n return false\n }\n if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n return false\n }\n\n for (let f in filter) {\n if (f[0] === '#') {\n let tagName = f.slice(1)\n let values = filter[`#${tagName}`]\n if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n }\n }\n\n if (filter.since && event.created_at < filter.since) return false\n if (filter.until && event.created_at > filter.until) return false\n\n return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n for (let i = 0; i < filters.length; i++) {\n if (matchFilter(filters[i], event)) {\n return true\n }\n }\n return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n let result: Filter = {}\n for (let i = 0; i < filters.length; i++) {\n let filter = filters[i]\n Object.entries(filter).forEach(([property, values]) => {\n if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n // @ts-ignore\n result[property] = result[property] || []\n // @ts-ignore\n for (let v = 0; v < values.length; v++) {\n // @ts-ignore\n let value = values[v]\n // @ts-ignore\n if (!result[property].includes(value)) result[property].push(value)\n }\n }\n })\n\n if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n }\n\n return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n if (filter.ids && !filter.ids.length) return 0\n if (filter.kinds && !filter.kinds.length) return 0\n if (filter.authors && !filter.authors.length) return 0\n\n for (const [key, value] of Object.entries(filter)) {\n if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n }\n\n return Math.min(\n // The `limit` property creates an artificial limit.\n Math.max(0, filter.limit ?? Infinity),\n\n // There can only be one event per `id`.\n filter.ids?.length ?? Infinity,\n\n // Replaceable events are limited by the number of authors and kinds.\n filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n ? filter.authors.length * filter.kinds.length\n : Infinity,\n\n // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n ? filter.authors.length * filter.kinds.length * filter['#d'].length\n : Infinity,\n )\n}\n", "export function getHex64(json: string, field: string): string {\n let len = field.length + 3\n let idx = json.indexOf(`\"${field}\":`) + len\n let s = json.slice(idx).indexOf(`\"`) + idx + 1\n return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n let len = field.length\n let idx = json.indexOf(`\"${field}\":`) + len + 3\n let sliced = json.slice(idx)\n let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n if (idx === -1) return null\n\n let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n if (pstart === -1) return null\n let start = idx + 7 + 1 + pstart\n\n let pend = json.slice(start + 1, 80).indexOf(`\"`)\n if (pend === -1) return null\n let end = start + 1 + pend\n\n return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n return {\n kind: ClientAuth,\n created_at: Math.floor(Date.now() / 1000),\n tags: [\n ['relay', relayURL],\n ['challenge', challenge],\n ],\n content: '',\n }\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\n\ntype RelayWebSocket = WebSocket & {\n ping?(): void\n on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n verifyEvent: Nostr['verifyEvent']\n websocketImplementation?: typeof WebSocket\n enablePing?: boolean\n enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n constructor(message: string, relay: string) {\n super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n this.name = 'SendingOnClosedConnection'\n }\n}\n\nexport class AbstractRelay {\n public readonly url: string\n private _connected: boolean = false\n\n public onclose: (() => void) | null = null\n public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n public baseEoseTimeout: number = 4400\n public publishTimeout: number = 4400\n public pingFrequency: number = 29000\n public pingTimeout: number = 20000\n public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n public openSubs: Map<string, Subscription> = new Map()\n public enablePing: boolean | undefined\n public enableReconnect: boolean\n public idleSince: number | undefined = Date.now() // when undefined that means it isn't idle\n public ongoingOperations: number = 0 // used to compute idleness\n private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n private reconnectAttempts: number = 0\n private skipReconnection: boolean = false\n\n private connectionPromise: Promise<void> | undefined\n private openCountRequests = new Map<string, CountResolver>()\n private openEventPublishes = new Map<string, EventPublishResolver>()\n private ws: RelayWebSocket | undefined\n private challenge: string | undefined\n private authPromise: Promise<string> | undefined\n private serial: number = 0\n private verifyEvent: Nostr['verifyEvent']\n\n private _WebSocket: typeof WebSocket\n\n constructor(url: string, opts: AbstractRelayConstructorOptions) {\n this.url = normalizeURL(url)\n this.verifyEvent = opts.verifyEvent\n this._WebSocket = opts.websocketImplementation || WebSocket\n this.enablePing = opts.enablePing\n this.enableReconnect = opts.enableReconnect || false\n }\n\n static async connect(\n url: string,\n opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n ): Promise<AbstractRelay> {\n const relay = new AbstractRelay(url, opts)\n await relay.connect(opts)\n return relay\n }\n\n private closeAllSubscriptions(reason: string) {\n for (let [_, sub] of this.openSubs) {\n sub.close(reason)\n }\n this.openSubs.clear()\n\n for (let [_, ep] of this.openEventPublishes) {\n ep.reject(new Error(reason))\n }\n this.openEventPublishes.clear()\n\n for (let [_, cr] of this.openCountRequests) {\n cr.reject(new Error(reason))\n }\n this.openCountRequests.clear()\n }\n\n public get connected(): boolean {\n return this._connected\n }\n\n private async reconnect(): Promise<void> {\n const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n this.reconnectAttempts++\n\n this.reconnectTimeoutHandle = setTimeout(async () => {\n try {\n await this.connect()\n } catch (err) {\n // this will be called again through onclose/onerror\n }\n }, backoff)\n }\n\n private handleHardClose(reason: string) {\n if (this.pingIntervalHandle) {\n clearInterval(this.pingIntervalHandle)\n this.pingIntervalHandle = undefined\n }\n\n this._connected = false\n this.connectionPromise = undefined\n this.idleSince = undefined\n\n if (this.enableReconnect && !this.skipReconnection) {\n this.reconnect()\n } else {\n this.onclose?.()\n this.closeAllSubscriptions(reason)\n }\n }\n\n public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n if (this.connectionPromise) return this.connectionPromise\n\n this.challenge = undefined\n this.authPromise = undefined\n this.skipReconnection = false\n this.connectionPromise = new Promise((resolve, reject) => {\n if (opts?.timeout) {\n connectionTimeoutHandle = setTimeout(() => {\n reject('connection timed out')\n this.connectionPromise = undefined\n this.skipReconnection = true\n this.onclose?.()\n this.handleHardClose('relay connection timed out')\n }, opts.timeout)\n }\n\n if (opts?.abort) {\n opts.abort.onabort = reject\n }\n\n try {\n this.ws = new this._WebSocket(this.url)\n } catch (err) {\n clearTimeout(connectionTimeoutHandle)\n reject(err)\n return\n }\n\n this.ws.onopen = () => {\n if (this.reconnectTimeoutHandle) {\n clearTimeout(this.reconnectTimeoutHandle)\n this.reconnectTimeoutHandle = undefined\n }\n clearTimeout(connectionTimeoutHandle)\n this._connected = true\n\n const isReconnection = this.reconnectAttempts > 0\n this.reconnectAttempts = 0\n\n // resubscribe to all open subscriptions\n for (const sub of this.openSubs.values()) {\n sub.eosed = false\n if (isReconnection) {\n for (let f = 0; f < sub.filters.length; f++) {\n if (sub.lastEmitted) {\n sub.filters[f].since = sub.lastEmitted + 1\n }\n }\n }\n sub.fire()\n }\n\n if (this.enablePing) {\n this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n }\n resolve()\n }\n\n this.ws.onerror = () => {\n clearTimeout(connectionTimeoutHandle)\n reject('connection failed')\n this.connectionPromise = undefined\n this.skipReconnection = true\n this.onclose?.()\n this.handleHardClose('relay connection failed')\n }\n\n this.ws.onclose = ev => {\n clearTimeout(connectionTimeoutHandle)\n reject((ev as any).message || 'websocket closed')\n this.handleHardClose('relay connection closed')\n }\n\n this.ws.onmessage = this._onmessage.bind(this)\n })\n\n return this.connectionPromise\n }\n\n private waitForPingPong() {\n return new Promise(resolve => {\n // listen for pong\n ;(this.ws as any).once('pong', () => resolve(true))\n // send a ping\n this.ws!.ping!()\n })\n }\n\n private waitForDummyReq() {\n return new Promise((resolve, reject) => {\n if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n // make a dummy request with expected empty eose reply\n // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n try {\n const sub = this.subscribe(\n [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n {\n label: '<forced-ping>',\n oneose: () => {\n resolve(true)\n sub.close()\n },\n onclose() {\n // if we get a CLOSED it's because the relay is alive\n resolve(true)\n },\n eoseTimeout: this.pingTimeout + 1000,\n },\n )\n } catch (err) {\n reject(err)\n }\n })\n }\n\n // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n private async pingpong() {\n // if the websocket is connected\n if (this.ws?.readyState === 1) {\n // wait for either a ping-pong reply or a timeout\n const result = await Promise.any([\n // browsers don't have ping so use a dummy req\n this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n ])\n\n if (!result) {\n // pingpong closing socket\n if (this.ws?.readyState === this._WebSocket.OPEN) {\n this.ws?.close()\n }\n }\n }\n }\n\n public async send(message: string) {\n if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n this.connectionPromise.then(() => {\n this.ws?.send(message)\n })\n }\n\n public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n const challenge = this.challenge\n if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n if (this.authPromise) return this.authPromise\n\n this.authPromise = new Promise<string>(async (resolve, reject) => {\n try {\n let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n let timeout = setTimeout(() => {\n let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n if (ep) {\n ep.reject(new Error('auth timed out'))\n this.openEventPublishes.delete(evt.id)\n }\n }, this.publishTimeout)\n this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n } catch (err) {\n console.warn('subscribe auth function failed:', err)\n }\n })\n return this.authPromise\n }\n\n public async publish(event: Event): Promise<string> {\n this.idleSince = undefined\n this.ongoingOperations++\n\n const ret = new Promise<string>((resolve, reject) => {\n const timeout = setTimeout(() => {\n const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n if (ep) {\n ep.reject(new Error('publish timed out'))\n this.openEventPublishes.delete(event.id)\n }\n }, this.publishTimeout)\n this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n })\n this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n\n // compute idleness state\n this.ongoingOperations--\n if (this.ongoingOperations === 0) this.idleSince = Date.now()\n\n return ret\n }\n\n public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n this.serial++\n const id = params?.id || 'count:' + this.serial\n const ret = new Promise<number>((resolve, reject) => {\n this.openCountRequests.set(id, { resolve, reject })\n })\n this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n return ret\n }\n\n public subscribe(\n filters: Filter[],\n params: Partial<SubscriptionParams> & { label?: string; id?: string },\n ): Subscription {\n if (params.label !== '<forced-ping>') {\n this.idleSince = undefined\n this.ongoingOperations++\n }\n\n const sub = this.prepareSubscription(filters, params)\n sub.fire()\n\n if (params.abort) {\n params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n }\n\n return sub\n }\n\n public prepareSubscription(\n filters: Filter[],\n params: Partial<SubscriptionParams> & { label?: string; id?: string },\n ): Subscription {\n this.serial++\n const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n const sub = new Subscription(this, id, filters, params)\n this.openSubs.set(id, sub)\n return sub\n }\n\n public close() {\n this.skipReconnection = true\n if (this.reconnectTimeoutHandle) {\n clearTimeout(this.reconnectTimeoutHandle)\n this.reconnectTimeoutHandle = undefined\n }\n if (this.pingIntervalHandle) {\n clearInterval(this.pingIntervalHandle)\n this.pingIntervalHandle = undefined\n }\n this.closeAllSubscriptions('relay connection closed by us')\n this._connected = false\n this.idleSince = undefined\n this.onclose?.()\n if (this.ws?.readyState === this._WebSocket.OPEN) {\n this.ws?.close()\n }\n }\n\n // this is the function assigned to this.ws.onmessage\n // it's exposed for testing and debugging purposes\n public _onmessage(ev: MessageEvent<any>): void {\n const json = ev.data\n if (!json) {\n return\n }\n\n // shortcut EVENT sub\n const subid = getSubscriptionId(json)\n if (subid) {\n const so = this.openSubs.get(subid as string)\n if (!so) {\n // this is an EVENT message, but for a subscription we don't have, so just stop here\n return\n }\n\n // this will be called only when this message is a EVENT message for a subscription we have\n // we do this before parsing the JSON to not have to do that for duplicate events\n // since JSON parsing is slow\n const id = getHex64(json, 'id')\n const alreadyHave = so.alreadyHaveEvent?.(id)\n\n // notify any interested client that the relay has this event\n // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n so.receivedEvent?.(this, id)\n\n if (alreadyHave) {\n // if we had already seen this event we can just stop here\n return\n }\n }\n\n try {\n let data = JSON.parse(json)\n // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n // will naturally be caught by the encompassing try..catch block\n\n switch (data[0]) {\n case 'EVENT': {\n const so = this.openSubs.get(data[1] as string) as Subscription\n const event = data[2] as NostrEvent\n if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n so.onevent(event)\n }\n if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n return\n }\n case 'COUNT': {\n const id: string = data[1]\n const payload = data[2] as { count: number }\n const cr = this.openCountRequests.get(id) as CountResolver\n if (cr) {\n cr.resolve(payload.count)\n this.openCountRequests.delete(id)\n }\n return\n }\n case 'EOSE': {\n const so = this.openSubs.get(data[1] as string)\n if (!so) return\n so.receivedEose()\n return\n }\n case 'OK': {\n const id: string = data[1]\n const ok: boolean = data[2]\n const reason: string = data[3]\n const ep = this.openEventPublishes.get(id) as EventPublishResolver\n if (ep) {\n clearTimeout(ep.timeout)\n if (ok) ep.resolve(reason)\n else ep.reject(new Error(reason))\n this.openEventPublishes.delete(id)\n }\n return\n }\n case 'CLOSED': {\n const id: string = data[1]\n const so = this.openSubs.get(id)\n if (!so) return\n so.closed = true\n so.close(data[2] as string)\n return\n }\n case 'NOTICE': {\n this.onnotice(data[1] as string)\n return\n }\n case 'AUTH': {\n this.challenge = data[1] as string\n if (this.onauth) {\n this.auth(this.onauth)\n }\n return\n }\n default: {\n const so = this.openSubs.get(data[1])\n so?.oncustom?.(data)\n return\n }\n }\n } catch (err) {\n const [_, __, event] = JSON.parse(json)\n ;(window as any).printer.maybe(event.pubkey, ':: caught err', event, this.url, err)\n return\n }\n }\n}\n\nexport class Subscription {\n public readonly relay: AbstractRelay\n public readonly id: string\n\n public lastEmitted: number | undefined\n public closed: boolean = false\n public eosed: boolean = false\n public filters: Filter[]\n public alreadyHaveEvent: ((id: string) => boolean) | undefined\n public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n public onevent: (evt: Event) => void\n public oneose: (() => void) | undefined\n public onclose: ((reason: string) => void) | undefined\n\n // will get any messages that have this subscription id as their second item and are not default standard\n public oncustom: ((msg: string[]) => void) | undefined\n\n public eoseTimeout: number\n private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n this.relay = relay\n this.filters = filters\n this.id = id\n this.alreadyHaveEvent = params.alreadyHaveEvent\n this.receivedEvent = params.receivedEvent\n this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n this.oneose = params.oneose\n this.onclose = params.onclose\n this.onevent =\n params.onevent ||\n (event => {\n console.warn(\n `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n event,\n )\n })\n }\n\n public fire() {\n this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n // only now we start counting the eoseTimeout\n this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n }\n\n public receivedEose() {\n if (this.eosed) return\n clearTimeout(this.eoseTimeoutHandle)\n this.eosed = true\n this.oneose?.()\n }\n\n public close(reason: string = 'closed by caller') {\n if (!this.closed && this.relay.connected) {\n // if the connection was closed by the user calling .close() we will send a CLOSE message\n // otherwise this._open will be already set to false so we will skip this\n try {\n this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n } catch (err) {\n if (err instanceof SendingOnClosedConnection) {\n /* doesn't matter, it's ok */\n } else {\n throw err\n }\n }\n this.closed = true\n }\n this.relay.openSubs.delete(this.id)\n\n // compute idleness state\n this.relay.ongoingOperations--\n if (this.relay.ongoingOperations === 0) this.relay.idleSince = Date.now()\n\n this.onclose?.(reason)\n }\n}\n\nexport type SubscriptionParams = {\n onevent?: (evt: Event) => void\n oneose?: () => void\n onclose?: (reason: string) => void\n alreadyHaveEvent?: (id: string) => boolean\n receivedEvent?: (relay: AbstractRelay, id: string) => void\n eoseTimeout?: number\n abort?: AbortSignal\n}\n\nexport type CountResolver = {\n resolve: (count: number) => void\n reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n resolve: (reason: string) => void\n reject: (err: Error) => void\n timeout: ReturnType<typeof setTimeout>\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/* global WebSocket */\n\nimport { verifyEvent } from './pure.ts'\nimport { AbstractRelay, type AbstractRelayConstructorOptions } from './abstract-relay.ts'\n\nvar _WebSocket: typeof WebSocket\n\ntry {\n _WebSocket = WebSocket\n} catch {}\n\nexport function useWebSocketImplementation(websocketImplementation: any) {\n _WebSocket = websocketImplementation\n}\n\nexport class Relay extends AbstractRelay {\n constructor(url: string, options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect'>) {\n super(url, { verifyEvent, websocketImplementation: _WebSocket, ...options })\n }\n\n static async connect(\n url: string,\n options?: Pick<AbstractRelayConstructorOptions, 'enablePing' | 'enableReconnect'>,\n ): Promise<Relay> {\n const relay = new Relay(url, options)\n await relay.connect()\n return relay\n }\n}\n\nexport type RelayRecord = Record<string, { read: boolean; write: boolean }>\n\nexport * from './abstract-relay.ts'\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n generateSecretKey(): Uint8Array {\n return schnorr.utils.randomSecretKey()\n }\n getPublicKey(secretKey: Uint8Array): string {\n return bytesToHex(schnorr.getPublicKey(secretKey))\n }\n finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n const event = t as VerifiedEvent\n event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n event.id = getEventHash(event)\n event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n event[verifiedSymbol] = true\n return event\n }\n verifyEvent(event: Event): event is VerifiedEvent {\n if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n try {\n const hash = getEventHash(event)\n if (hash !== event.id) {\n event[verifiedSymbol] = false\n return false\n }\n\n const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n event[verifiedSymbol] = valid\n return valid\n } catch (err) {\n event[verifiedSymbol] = false\n return false\n }\n }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n generateSecretKey(): Uint8Array\n getPublicKey(secretKey: Uint8Array): string\n finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n kind: number\n tags: string[][]\n content: string\n created_at: number\n pubkey: string\n id: string\n sig: string\n [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n if (!isRecord(event)) return false\n if (typeof event.kind !== 'number') return false\n if (typeof event.content !== 'string') return false\n if (typeof event.created_at !== 'number') return false\n if (typeof event.pubkey !== 'string') return false\n if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n if (!Array.isArray(event.tags)) return false\n for (let i = 0; i < event.tags.length; i++) {\n let tag = event.tags[i]\n if (!Array.isArray(tag)) return false\n for (let j = 0; j < tag.length; j++) {\n if (typeof tag[j] !== 'string') return false\n }\n }\n\n return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n return events.sort((a: NostrEvent, b: NostrEvent): number => {\n if (a.created_at !== b.created_at) {\n return b.created_at - a.created_at\n }\n return a.id.localeCompare(b.id)\n })\n}\n", "import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n", "import { NostrEvent, validateEvent } from './pure.ts'\n\n/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n return kind < 10000 && kind !== 0 && kind !== 3\n}\n\n/** Events are **replaceable**, which means that, for each combination of `pubkey` and `kind`, only the latest event is expected to (SHOULD) be stored by relays, older versions are expected to be discarded. */\nexport function isReplaceableKind(kind: number): boolean {\n return kind === 0 || kind === 3 || (10000 <= kind && kind < 20000)\n}\n\n/** Events are **ephemeral**, which means they are not expected to be stored by relays. */\nexport function isEphemeralKind(kind: number): boolean {\n return 20000 <= kind && kind < 30000\n}\n\n/** Events are **addressable**, which means that, for each combination of `pubkey`, `kind` and the `d` tag, only the latest event is expected to be stored by relays, older versions are expected to be discarded. */\nexport function isAddressableKind(kind: number): boolean {\n return 30000 <= kind && kind < 40000\n}\n\n/** Classification of the event kind. */\nexport type KindClassification = 'regular' | 'replaceable' | 'ephemeral' | 'parameterized' | 'unknown'\n\n/** Determine the classification of this kind of event if known, or `unknown`. */\nexport function classifyKind(kind: number): KindClassification {\n if (isRegularKind(kind)) return 'regular'\n if (isReplaceableKind(kind)) return 'replaceable'\n if (isEphemeralKind(kind)) return 'ephemeral'\n if (isAddressableKind(kind)) return 'parameterized'\n return 'unknown'\n}\n\nexport function isKind<T extends number>(event: unknown, kind: T | Array<T>): event is NostrEvent & { kind: T } {\n const kindAsArray: number[] = kind instanceof Array ? kind : [kind]\n return (validateEvent(event) && kindAsArray.includes(event.kind)) || false\n}\n\nexport const Metadata = 0\nexport type Metadata = typeof Metadata\nexport const ShortTextNote = 1\nexport type ShortTextNote = typeof ShortTextNote\nexport const RecommendRelay = 2\nexport type RecommendRelay = typeof RecommendRelay\nexport const Contacts = 3\nexport type Contacts = typeof Contacts\nexport const EncryptedDirectMessage = 4\nexport type EncryptedDirectMessage = typeof EncryptedDirectMessage\nexport const EventDeletion = 5\nexport type EventDeletion = typeof EventDeletion\nexport const Repost = 6\nexport type Repost = typeof Repost\nexport const Reaction = 7\nexport type Reaction = typeof Reaction\nexport const BadgeAward = 8\nexport type BadgeAward = typeof BadgeAward\nexport const ChatMessage = 9\nexport type ChatMessage = typeof ChatMessage\nexport const ForumThread = 11\nexport type ForumThread = typeof ForumThread\nexport const Seal = 13\nexport type Seal = typeof Seal\nexport const PrivateDirectMessage = 14\nexport type PrivateDirectMessage = typeof PrivateDirectMessage\nexport const FileMessage = 15\nexport type FileMessage = typeof FileMessage\nexport const GenericRepost = 16\nexport type GenericRepost = typeof GenericRepost\nexport const Photo = 20\nexport type Photo = typeof Photo\nexport const NormalVideo = 21\nexport type NormalVideo = typeof NormalVideo\nexport const ShortVideo = 22\nexport type ShortVideo = typeof ShortVideo\nexport const ChannelCreation = 40\nexport type ChannelCreation = typeof ChannelCreation\nexport const ChannelMetadata = 41\nexport type ChannelMetadata = typeof ChannelMetadata\nexport const ChannelMessage = 42\nexport type ChannelMessage = typeof ChannelMessage\nexport const ChannelHideMessage = 43\nexport type ChannelHideMessage = typeof ChannelHideMessage\nexport const ChannelMuteUser = 44\nexport type ChannelMuteUser = typeof ChannelMuteUser\nexport const OpenTimestamps = 1040\nexport type OpenTimestamps = typeof OpenTimestamps\nexport const GiftWrap = 1059\nexport type GiftWrap = typeof GiftWrap\nexport const Poll = 1068\nexport type Poll = typeof Poll\nexport const FileMetadata = 1063\nexport type FileMetadata = typeof FileMetadata\nexport const Comment = 1111\nexport type Comment = typeof Comment\nexport const LiveChatMessage = 1311\nexport type LiveChatMessage = typeof LiveChatMessage\nexport const Voice = 1222\nexport type Voice = typeof Voice\nexport const VoiceComment = 1244\nexport type VoiceComment = typeof VoiceComment\nexport const ProblemTracker = 1971\nexport type ProblemTracker = typeof ProblemTracker\nexport const Report = 1984\nexport type Report = typeof Report\nexport const Reporting = 1984\nexport type Reporting = typeof Reporting\nexport const Label = 1985\nexport type Label = typeof Label\nexport const CommunityPostApproval = 4550\nexport type CommunityPostApproval = typeof CommunityPostApproval\nexport const JobRequest = 5999\nexport type JobRequest = typeof JobRequest\nexport const JobResult = 6999\nexport type JobResult = typeof JobResult\nexport const JobFeedback = 7000\nexport type JobFeedback = typeof JobFeedback\nexport const ZapGoal = 9041\nexport type ZapGoal = typeof ZapGoal\nexport const ZapRequest = 9734\nexport type ZapRequest = typeof ZapRequest\nexport const Zap = 9735\nexport type Zap = typeof Zap\nexport const Highlights = 9802\nexport type Highlights = typeof Highlights\nexport const PollResponse = 1018\nexport type PollResponse = typeof PollResponse\nexport const Mutelist = 10000\nexport type Mutelist = typeof Mutelist\nexport const Pinlist = 10001\nexport type Pinlist = typeof Pinlist\nexport const RelayList = 10002\nexport type RelayList = typeof RelayList\nexport const BookmarkList = 10003\nexport type BookmarkList = typeof BookmarkList\nexport const CommunitiesList = 10004\nexport type CommunitiesList = typeof CommunitiesList\nexport const PublicChatsList = 10005\nexport type PublicChatsList = typeof PublicChatsList\nexport const BlockedRelaysList = 10006\nexport type BlockedRelaysList = typeof BlockedRelaysList\nexport const SearchRelaysList = 10007\nexport type SearchRelaysList = typeof SearchRelaysList\nexport const FavoriteRelays = 10012\nexport type FavoriteRelays = typeof FavoriteRelays\nexport const InterestsList = 10015\nexport type InterestsList = typeof InterestsList\nexport const UserEmojiList = 10030\nexport type UserEmojiList = typeof UserEmojiList\nexport const DirectMessageRelaysList = 10050\nexport type DirectMessageRelaysList = typeof DirectMessageRelaysList\nexport const FileServerPreference = 10096\nexport type FileServerPreference = typeof FileServerPreference\nexport const BlossomServerList = 10063\nexport type BlossomServerList = typeof BlossomServerList\nexport const NWCWalletInfo = 13194\nexport type NWCWalletInfo = typeof NWCWalletInfo\nexport const LightningPubRPC = 21000\nexport type LightningPubRPC = typeof LightningPubRPC\nexport const ClientAuth = 22242\nexport type ClientAuth = typeof ClientAuth\nexport const NWCWalletRequest = 23194\nexport type NWCWalletRequest = typeof NWCWalletRequest\nexport const NWCWalletResponse = 23195\nexport type NWCWalletResponse = typeof NWCWalletResponse\nexport const NostrConnect = 24133\nexport type NostrConnect = typeof NostrConnect\nexport const HTTPAuth = 27235\nexport type HTTPAuth = typeof HTTPAuth\nexport const Followsets = 30000\nexport type Followsets = typeof Followsets\nexport const Genericlists = 30001\nexport type Genericlists = typeof Genericlists\nexport const Relaysets = 30002\nexport type Relaysets = typeof Relaysets\nexport const Bookmarksets = 30003\nexport type Bookmarksets = typeof Bookmarksets\nexport const Curationsets = 30004\nexport type Curationsets = typeof Curationsets\nexport const ProfileBadges = 30008\nexport type ProfileBadges = typeof ProfileBadges\nexport const BadgeDefinition = 30009\nexport type BadgeDefinition = typeof BadgeDefinition\nexport const Interestsets = 30015\nexport type Interestsets = typeof Interestsets\nexport const CreateOrUpdateStall = 30017\nexport type CreateOrUpdateStall = typeof CreateOrUpdateStall\nexport const CreateOrUpdateProduct = 30018\nexport type CreateOrUpdateProduct = typeof CreateOrUpdateProduct\nexport const LongFormArticle = 30023\nexport type LongFormArticle = typeof LongFormArticle\nexport const DraftLong = 30024\nexport type DraftLong = typeof DraftLong\nexport const Emojisets = 30030\nexport type Emojisets = typeof Emojisets\nexport const Application = 30078\nexport type Application = typeof Application\nexport const LiveEvent = 30311\nexport type LiveEvent = typeof LiveEvent\nexport const UserStatuses = 30315\nexport type UserStatuses = typeof UserStatuses\nexport const ClassifiedListing = 30402\nexport type ClassifiedListing = typeof ClassifiedListing\nexport const DraftClassifiedListing = 30403\nexport type DraftClassifiedListing = typeof DraftClassifiedListing\nexport const Date = 31922\nexport type Date = typeof Date\nexport const Time = 31923\nexport type Time = typeof Time\nexport const Calendar = 31924\nexport type Calendar = typeof Calendar\nexport const CalendarEventRSVP = 31925\nexport type CalendarEventRSVP = typeof CalendarEventRSVP\nexport const RelayReview = 31987\nexport type RelayReview = typeof RelayReview\nexport const Handlerrecommendation = 31989\nexport type Handlerrecommendation = typeof Handlerrecommendation\nexport const Handlerinformation = 31990\nexport type Handlerinformation = typeof Handlerinformation\nexport const CommunityDefinition = 34550\nexport type CommunityDefinition = typeof CommunityDefinition\nexport const GroupMetadata = 39000\nexport type GroupMetadata = typeof GroupMetadata\n", "import { Event } from './core.ts'\nimport { isAddressableKind, isReplaceableKind } from './kinds.ts'\n\nexport type Filter = {\n ids?: string[]\n kinds?: number[]\n authors?: string[]\n since?: number\n until?: number\n limit?: number\n search?: string\n [key: `#${string}`]: string[] | undefined\n}\n\nexport function matchFilter(filter: Filter, event: Event): boolean {\n if (filter.ids && filter.ids.indexOf(event.id) === -1) {\n return false\n }\n if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {\n return false\n }\n if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n return false\n }\n\n for (let f in filter) {\n if (f[0] === '#') {\n let tagName = f.slice(1)\n let values = filter[`#${tagName}`]\n if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false\n }\n }\n\n if (filter.since && event.created_at < filter.since) return false\n if (filter.until && event.created_at > filter.until) return false\n\n return true\n}\n\nexport function matchFilters(filters: Filter[], event: Event): boolean {\n for (let i = 0; i < filters.length; i++) {\n if (matchFilter(filters[i], event)) {\n return true\n }\n }\n return false\n}\n\nexport function mergeFilters(...filters: Filter[]): Filter {\n let result: Filter = {}\n for (let i = 0; i < filters.length; i++) {\n let filter = filters[i]\n Object.entries(filter).forEach(([property, values]) => {\n if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') {\n // @ts-ignore\n result[property] = result[property] || []\n // @ts-ignore\n for (let v = 0; v < values.length; v++) {\n // @ts-ignore\n let value = values[v]\n // @ts-ignore\n if (!result[property].includes(value)) result[property].push(value)\n }\n }\n })\n\n if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit\n if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until\n if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since\n }\n\n return result\n}\n\n/**\n * Calculate the intrinsic limit of a filter.\n * This function returns a positive integer, or `Infinity` if there is no intrinsic limit.\n */\nexport function getFilterLimit(filter: Filter): number {\n if (filter.ids && !filter.ids.length) return 0\n if (filter.kinds && !filter.kinds.length) return 0\n if (filter.authors && !filter.authors.length) return 0\n\n for (const [key, value] of Object.entries(filter)) {\n if (key[0] === '#' && Array.isArray(value) && !value.length) return 0\n }\n\n return Math.min(\n // The `limit` property creates an artificial limit.\n Math.max(0, filter.limit ?? Infinity),\n\n // There can only be one event per `id`.\n filter.ids?.length ?? Infinity,\n\n // Replaceable events are limited by the number of authors and kinds.\n filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n ? filter.authors.length * filter.kinds.length\n : Infinity,\n\n // Parameterized replaceable events are limited by the number of authors, kinds, and \"d\" tags.\n filter.authors?.length && filter.kinds?.every(kind => isAddressableKind(kind)) && filter['#d']?.length\n ? filter.authors.length * filter.kinds.length * filter['#d'].length\n : Infinity,\n )\n}\n", "export function getHex64(json: string, field: string): string {\n let len = field.length + 3\n let idx = json.indexOf(`\"${field}\":`) + len\n let s = json.slice(idx).indexOf(`\"`) + idx + 1\n return json.slice(s, s + 64)\n}\n\nexport function getInt(json: string, field: string): number {\n let len = field.length\n let idx = json.indexOf(`\"${field}\":`) + len + 3\n let sliced = json.slice(idx)\n let end = Math.min(sliced.indexOf(','), sliced.indexOf('}'))\n return parseInt(sliced.slice(0, end), 10)\n}\n\nexport function getSubscriptionId(json: string): string | null {\n let idx = json.slice(0, 22).indexOf(`\"EVENT\"`)\n if (idx === -1) return null\n\n let pstart = json.slice(idx + 7 + 1).indexOf(`\"`)\n if (pstart === -1) return null\n let start = idx + 7 + 1 + pstart\n\n let pend = json.slice(start + 1, 80).indexOf(`\"`)\n if (pend === -1) return null\n let end = start + 1 + pend\n\n return json.slice(start + 1, end)\n}\n\nexport function matchEventId(json: string, id: string): boolean {\n return id === getHex64(json, 'id')\n}\n\nexport function matchEventPubkey(json: string, pubkey: string): boolean {\n return pubkey === getHex64(json, 'pubkey')\n}\n\nexport function matchEventKind(json: string, kind: number): boolean {\n return kind === getInt(json, 'kind')\n}\n", "import { EventTemplate } from './core.ts'\nimport { ClientAuth } from './kinds.ts'\n\n/**\n * creates an EventTemplate for an AUTH event to be signed.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n return {\n kind: ClientAuth,\n created_at: Math.floor(Date.now() / 1000),\n tags: [\n ['relay', relayURL],\n ['challenge', challenge],\n ],\n content: '',\n }\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr, NostrEvent } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\n\ntype RelayWebSocket = WebSocket & {\n ping?(): void\n on?(event: 'pong', listener: () => void): any\n}\n\nexport type AbstractRelayConstructorOptions = {\n verifyEvent: Nostr['verifyEvent']\n websocketImplementation?: typeof WebSocket\n enablePing?: boolean\n enableReconnect?: boolean\n}\n\nexport class SendingOnClosedConnection extends Error {\n constructor(message: string, relay: string) {\n super(`Tried to send message '${message} on a closed connection to ${relay}.`)\n this.name = 'SendingOnClosedConnection'\n }\n}\n\nexport class AbstractRelay {\n public readonly url: string\n private _connected: boolean = false\n\n public onclose: (() => void) | null = null\n public onnotice: (msg: string) => void = msg => console.debug(`NOTICE from ${this.url}: ${msg}`)\n public onauth: undefined | ((evt: EventTemplate) => Promise<VerifiedEvent>)\n\n public baseEoseTimeout: number = 4400\n public publishTimeout: number = 4400\n public pingFrequency: number = 29000\n public pingTimeout: number = 20000\n public resubscribeBackoff: number[] = [10000, 10000, 10000, 20000, 20000, 30000, 60000]\n public openSubs: Map<string, Subscription> = new Map()\n public enablePing: boolean | undefined\n public enableReconnect: boolean\n public idleSince: number | undefined = Date.now() // when undefined that means it isn't idle\n public ongoingOperations: number = 0 // used to compute idleness\n private reconnectTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n private pingIntervalHandle: ReturnType<typeof setInterval> | undefined\n private reconnectAttempts: number = 0\n private skipReconnection: boolean = false\n\n private connectionPromise: Promise<void> | undefined\n private openCountRequests = new Map<string, CountResolver>()\n private openEventPublishes = new Map<string, EventPublishResolver>()\n private ws: RelayWebSocket | undefined\n private challenge: string | undefined\n private authPromise: Promise<string> | undefined\n private serial: number = 0\n private verifyEvent: Nostr['verifyEvent']\n\n private _WebSocket: typeof WebSocket\n\n constructor(url: string, opts: AbstractRelayConstructorOptions) {\n this.url = normalizeURL(url)\n this.verifyEvent = opts.verifyEvent\n this._WebSocket = opts.websocketImplementation || WebSocket\n this.enablePing = opts.enablePing\n this.enableReconnect = opts.enableReconnect || false\n }\n\n static async connect(\n url: string,\n opts: AbstractRelayConstructorOptions & Parameters<AbstractRelay['connect']>[0],\n ): Promise<AbstractRelay> {\n const relay = new AbstractRelay(url, opts)\n await relay.connect(opts)\n return relay\n }\n\n private closeAllSubscriptions(reason: string) {\n for (let [_, sub] of this.openSubs) {\n sub.close(reason)\n }\n this.openSubs.clear()\n\n for (let [_, ep] of this.openEventPublishes) {\n ep.reject(new Error(reason))\n }\n this.openEventPublishes.clear()\n\n for (let [_, cr] of this.openCountRequests) {\n cr.reject(new Error(reason))\n }\n this.openCountRequests.clear()\n }\n\n public get connected(): boolean {\n return this._connected\n }\n\n private async reconnect(): Promise<void> {\n const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)]\n this.reconnectAttempts++\n\n this.reconnectTimeoutHandle = setTimeout(async () => {\n try {\n await this.connect()\n } catch (err) {\n // this will be called again through onclose/onerror\n }\n }, backoff)\n }\n\n private handleHardClose(reason: string) {\n if (this.pingIntervalHandle) {\n clearInterval(this.pingIntervalHandle)\n this.pingIntervalHandle = undefined\n }\n\n this._connected = false\n this.connectionPromise = undefined\n this.idleSince = undefined\n\n if (this.enableReconnect && !this.skipReconnection) {\n this.reconnect()\n } else {\n this.onclose?.()\n this.closeAllSubscriptions(reason)\n }\n }\n\n public async connect(opts?: { timeout?: number; abort?: AbortSignal }): Promise<void> {\n let connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n if (this.connectionPromise) return this.connectionPromise\n\n this.challenge = undefined\n this.authPromise = undefined\n this.skipReconnection = false\n this.connectionPromise = new Promise((resolve, reject) => {\n if (opts?.timeout) {\n connectionTimeoutHandle = setTimeout(() => {\n reject('connection timed out')\n this.connectionPromise = undefined\n this.skipReconnection = true\n this.onclose?.()\n this.handleHardClose('relay connection timed out')\n }, opts.timeout)\n }\n\n if (opts?.abort) {\n opts.abort.onabort = reject\n }\n\n try {\n this.ws = new this._WebSocket(this.url)\n } catch (err) {\n clearTimeout(connectionTimeoutHandle)\n reject(err)\n return\n }\n\n this.ws.onopen = () => {\n if (this.reconnectTimeoutHandle) {\n clearTimeout(this.reconnectTimeoutHandle)\n this.reconnectTimeoutHandle = undefined\n }\n clearTimeout(connectionTimeoutHandle)\n this._connected = true\n\n const isReconnection = this.reconnectAttempts > 0\n this.reconnectAttempts = 0\n\n // resubscribe to all open subscriptions\n for (const sub of this.openSubs.values()) {\n sub.eosed = false\n if (isReconnection) {\n for (let f = 0; f < sub.filters.length; f++) {\n if (sub.lastEmitted) {\n sub.filters[f].since = sub.lastEmitted + 1\n }\n }\n }\n sub.fire()\n }\n\n if (this.enablePing) {\n this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency)\n }\n resolve()\n }\n\n this.ws.onerror = () => {\n clearTimeout(connectionTimeoutHandle)\n reject('connection failed')\n this.connectionPromise = undefined\n this.skipReconnection = true\n this.onclose?.()\n this.handleHardClose('relay connection failed')\n }\n\n this.ws.onclose = ev => {\n clearTimeout(connectionTimeoutHandle)\n reject((ev as any).message || 'websocket closed')\n this.handleHardClose('relay connection closed')\n }\n\n this.ws.onmessage = this._onmessage.bind(this)\n })\n\n return this.connectionPromise\n }\n\n private waitForPingPong() {\n return new Promise(resolve => {\n // listen for pong\n ;(this.ws as any).once('pong', () => resolve(true))\n // send a ping\n this.ws!.ping!()\n })\n }\n\n private waitForDummyReq() {\n return new Promise((resolve, reject) => {\n if (!this.connectionPromise) return reject(new Error(`no connection to ${this.url}, can't ping`))\n\n // make a dummy request with expected empty eose reply\n // [\"REQ\", \"_\", {\"ids\":[\"aaaa...aaaa\"], \"limit\": 0}]\n try {\n const sub = this.subscribe(\n [{ ids: ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], limit: 0 }],\n {\n label: '<forced-ping>',\n oneose: () => {\n resolve(true)\n sub.close()\n },\n onclose() {\n // if we get a CLOSED it's because the relay is alive\n resolve(true)\n },\n eoseTimeout: this.pingTimeout + 1000,\n },\n )\n } catch (err) {\n reject(err)\n }\n })\n }\n\n // nodejs requires this magic here to ensure connections are closed when internet goes off and stuff\n // in browsers it's done automatically. see https://github.com/nbd-wtf/nostr-tools/issues/491\n private async pingpong() {\n // if the websocket is connected\n if (this.ws?.readyState === 1) {\n // wait for either a ping-pong reply or a timeout\n const result = await Promise.any([\n // browsers don't have ping so use a dummy req\n this.ws && this.ws.ping && (this.ws as any).once ? this.waitForPingPong() : this.waitForDummyReq(),\n new Promise(res => setTimeout(() => res(false), this.pingTimeout)),\n ])\n\n if (!result) {\n // pingpong closing socket\n if (this.ws?.readyState === this._WebSocket.OPEN) {\n this.ws?.close()\n }\n }\n }\n }\n\n public async send(message: string) {\n if (!this.connectionPromise) throw new SendingOnClosedConnection(message, this.url)\n\n this.connectionPromise.then(() => {\n this.ws?.send(message)\n })\n }\n\n public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>): Promise<string> {\n const challenge = this.challenge\n if (!challenge) throw new Error(\"can't perform auth, no challenge was received\")\n if (this.authPromise) return this.authPromise\n\n this.authPromise = new Promise<string>(async (resolve, reject) => {\n try {\n let evt = await signAuthEvent(makeAuthEvent(this.url, challenge))\n let timeout = setTimeout(() => {\n let ep = this.openEventPublishes.get(evt.id) as EventPublishResolver\n if (ep) {\n ep.reject(new Error('auth timed out'))\n this.openEventPublishes.delete(evt.id)\n }\n }, this.publishTimeout)\n this.openEventPublishes.set(evt.id, { resolve, reject, timeout })\n this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n } catch (err) {\n console.warn('subscribe auth function failed:', err)\n }\n })\n return this.authPromise\n }\n\n public async publish(event: Event): Promise<string> {\n this.idleSince = undefined\n this.ongoingOperations++\n\n const ret = new Promise<string>((resolve, reject) => {\n const timeout = setTimeout(() => {\n const ep = this.openEventPublishes.get(event.id) as EventPublishResolver\n if (ep) {\n ep.reject(new Error('publish timed out'))\n this.openEventPublishes.delete(event.id)\n }\n }, this.publishTimeout)\n this.openEventPublishes.set(event.id, { resolve, reject, timeout })\n })\n this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\n\n // compute idleness state\n this.ongoingOperations--\n if (this.ongoingOperations === 0) this.idleSince = Date.now()\n\n return ret\n }\n\n public async count(filters: Filter[], params: { id?: string | null }): Promise<number> {\n this.serial++\n const id = params?.id || 'count:' + this.serial\n const ret = new Promise<number>((resolve, reject) => {\n this.openCountRequests.set(id, { resolve, reject })\n })\n this.send('[\"COUNT\",\"' + id + '\",' + JSON.stringify(filters).substring(1))\n return ret\n }\n\n public subscribe(\n filters: Filter[],\n params: Partial<SubscriptionParams> & { label?: string; id?: string },\n ): Subscription {\n if (params.label !== '<forced-ping>') {\n this.idleSince = undefined\n this.ongoingOperations++\n }\n\n const sub = this.prepareSubscription(filters, params)\n sub.fire()\n\n if (params.abort) {\n params.abort.onabort = () => sub.close(String(params.abort!.reason || '<aborted>'))\n }\n\n return sub\n }\n\n public prepareSubscription(\n filters: Filter[],\n params: Partial<SubscriptionParams> & { label?: string; id?: string },\n ): Subscription {\n this.serial++\n const id = params.id || (params.label ? params.label + ':' : 'sub:') + this.serial\n const sub = new Subscription(this, id, filters, params)\n this.openSubs.set(id, sub)\n return sub\n }\n\n public close() {\n this.skipReconnection = true\n if (this.reconnectTimeoutHandle) {\n clearTimeout(this.reconnectTimeoutHandle)\n this.reconnectTimeoutHandle = undefined\n }\n if (this.pingIntervalHandle) {\n clearInterval(this.pingIntervalHandle)\n this.pingIntervalHandle = undefined\n }\n this.closeAllSubscriptions('relay connection closed by us')\n this._connected = false\n this.idleSince = undefined\n this.onclose?.()\n if (this.ws?.readyState === this._WebSocket.OPEN) {\n this.ws?.close()\n }\n }\n\n // this is the function assigned to this.ws.onmessage\n // it's exposed for testing and debugging purposes\n public _onmessage(ev: MessageEvent<any>): void {\n const json = ev.data\n if (!json) {\n return\n }\n\n // shortcut EVENT sub\n const subid = getSubscriptionId(json)\n if (subid) {\n const so = this.openSubs.get(subid as string)\n if (!so) {\n // this is an EVENT message, but for a subscription we don't have, so just stop here\n return\n }\n\n // this will be called only when this message is a EVENT message for a subscription we have\n // we do this before parsing the JSON to not have to do that for duplicate events\n // since JSON parsing is slow\n const id = getHex64(json, 'id')\n const alreadyHave = so.alreadyHaveEvent?.(id)\n\n // notify any interested client that the relay has this event\n // (do this after alreadyHaveEvent() because the client may rely on this to answer that)\n so.receivedEvent?.(this, id)\n\n if (alreadyHave) {\n // if we had already seen this event we can just stop here\n return\n }\n }\n\n try {\n let data = JSON.parse(json)\n // we won't do any checks against the data since all failures (i.e. invalid messages from relays)\n // will naturally be caught by the encompassing try..catch block\n\n switch (data[0]) {\n case 'EVENT': {\n const so = this.openSubs.get(data[1] as string) as Subscription\n const event = data[2] as NostrEvent\n if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n so.onevent(event)\n } else {\n so.oninvalidevent?.(event)\n }\n if (!so.lastEmitted || so.lastEmitted < event.created_at) so.lastEmitted = event.created_at\n return\n }\n case 'COUNT': {\n const id: string = data[1]\n const payload = data[2] as { count: number }\n const cr = this.openCountRequests.get(id) as CountResolver\n if (cr) {\n cr.resolve(payload.count)\n this.openCountRequests.delete(id)\n }\n return\n }\n case 'EOSE': {\n const so = this.openSubs.get(data[1] as string)\n if (!so) return\n so.receivedEose()\n return\n }\n case 'OK': {\n const id: string = data[1]\n const ok: boolean = data[2]\n const reason: string = data[3]\n const ep = this.openEventPublishes.get(id) as EventPublishResolver\n if (ep) {\n clearTimeout(ep.timeout)\n if (ok) ep.resolve(reason)\n else ep.reject(new Error(reason))\n this.openEventPublishes.delete(id)\n }\n return\n }\n case 'CLOSED': {\n const id: string = data[1]\n const so = this.openSubs.get(id)\n if (!so) return\n so.closed = true\n so.close(data[2] as string)\n return\n }\n case 'NOTICE': {\n this.onnotice(data[1] as string)\n return\n }\n case 'AUTH': {\n this.challenge = data[1] as string\n if (this.onauth) {\n this.auth(this.onauth)\n }\n return\n }\n default: {\n const so = this.openSubs.get(data[1])\n so?.oncustom?.(data)\n return\n }\n }\n } catch (err) {\n try {\n const [_, __, event] = JSON.parse(json)\n console.warn(`[nostr] relay ${this.url} error processing message:`, err, event)\n } catch (_) {\n console.warn(`[nostr] relay ${this.url} error processing message:`, err)\n }\n return\n }\n }\n}\n\nexport class Subscription {\n public readonly relay: AbstractRelay\n public readonly id: string\n\n public lastEmitted: number | undefined\n public closed: boolean = false\n public eosed: boolean = false\n public filters: Filter[]\n public alreadyHaveEvent: ((id: string) => boolean) | undefined\n public receivedEvent: ((relay: AbstractRelay, id: string) => void) | undefined\n\n public onevent: (evt: Event) => void\n public oninvalidevent: ((evt: unknown) => void) | undefined\n public oneose: (() => void) | undefined\n public onclose: ((reason: string) => void) | undefined\n\n // will get any messages that have this subscription id as their second item and are not default standard\n public oncustom: ((msg: string[]) => void) | undefined\n\n public eoseTimeout: number\n private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\n if (filters.length === 0) throw new Error(\"subscription can't be created with zero filters\")\n\n this.relay = relay\n this.filters = filters\n this.id = id\n this.alreadyHaveEvent = params.alreadyHaveEvent\n this.receivedEvent = params.receivedEvent\n this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout\n\n this.oneose = params.oneose\n this.onclose = params.onclose\n this.oninvalidevent = params.oninvalidevent\n this.onevent =\n params.onevent ||\n (event => {\n console.warn(\n `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,\n event,\n )\n })\n }\n\n public fire() {\n this.relay.send('[\"REQ\",\"' + this.id + '\",' + JSON.stringify(this.filters).substring(1))\n\n // only now we start counting the eoseTimeout\n this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout)\n }\n\n public receivedEose() {\n if (this.eosed) return\n clearTimeout(this.eoseTimeoutHandle)\n this.eosed = true\n this.oneose?.()\n }\n\n public close(reason: string = 'closed by caller') {\n if (!this.closed && this.relay.connected) {\n // if the connection was closed by the user calling .close() we will send a CLOSE message\n // otherwise this._open will be already set to false so we will skip this\n try {\n this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n } catch (err) {\n if (err instanceof SendingOnClosedConnection) {\n /* doesn't matter, it's ok */\n } else {\n throw err\n }\n }\n this.closed = true\n }\n this.relay.openSubs.delete(this.id)\n\n // compute idleness state\n this.relay.ongoingOperations--\n if (this.relay.ongoingOperations === 0) this.relay.idleSince = Date.now()\n\n this.onclose?.(reason)\n }\n}\n\nexport type SubscriptionParams = {\n onevent?: (evt: Event) => void\n oninvalidevent?: (evt: unknown) => void\n oneose?: () => void\n onclose?: (reason: string) => void\n alreadyHaveEvent?: (id: string) => boolean\n receivedEvent?: (relay: AbstractRelay, id: string) => void\n eoseTimeout?: number\n abort?: AbortSignal\n}\n\nexport type CountResolver = {\n resolve: (count: number) => void\n reject: (err: Error) => void\n}\n\nexport type EventPublishResolver = {\n resolve: (reason: string) => void\n reject: (err: Error) => void\n timeout: ReturnType<typeof setTimeout>\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["import_utils", "i", "i"]
7
7
  }
package/lib/cjs/signer.js CHANGED
@@ -85,12 +85,12 @@ var JS = class {
85
85
  verifyEvent(event) {
86
86
  if (typeof event[verifiedSymbol] === "boolean")
87
87
  return event[verifiedSymbol];
88
- const hash = getEventHash(event);
89
- if (hash !== event.id) {
90
- event[verifiedSymbol] = false;
91
- return false;
92
- }
93
88
  try {
89
+ const hash = getEventHash(event);
90
+ if (hash !== event.id) {
91
+ event[verifiedSymbol] = false;
92
+ return false;
93
+ }
94
94
  const valid = import_secp256k1.schnorr.verify((0, import_utils2.hexToBytes)(event.sig), (0, import_utils2.hexToBytes)(hash), (0, import_utils2.hexToBytes)(event.pubkey));
95
95
  event[verifiedSymbol] = valid;
96
96
  return valid;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../signer.ts", "../../pure.ts", "../../core.ts", "../../utils.ts"],
4
- "sourcesContent": ["import { EventTemplate, VerifiedEvent } from './core.ts'\nimport { finalizeEvent, getPublicKey } from './pure.ts'\n\nexport interface Signer {\n getPublicKey(): Promise<string>\n signEvent(event: EventTemplate): Promise<VerifiedEvent>\n}\n\nexport class PlainKeySigner implements Signer {\n private secretKey: Uint8Array\n\n constructor(secretKey: Uint8Array) {\n this.secretKey = secretKey\n }\n\n async getPublicKey(): Promise<string> {\n return getPublicKey(this.secretKey)\n }\n\n async signEvent(event: EventTemplate): Promise<VerifiedEvent> {\n return finalizeEvent(event, this.secretKey)\n }\n}\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n generateSecretKey(): Uint8Array {\n return schnorr.utils.randomSecretKey()\n }\n getPublicKey(secretKey: Uint8Array): string {\n return bytesToHex(schnorr.getPublicKey(secretKey))\n }\n finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n const event = t as VerifiedEvent\n event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n event.id = getEventHash(event)\n event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n event[verifiedSymbol] = true\n return event\n }\n verifyEvent(event: Event): event is VerifiedEvent {\n if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n const hash = getEventHash(event)\n if (hash !== event.id) {\n event[verifiedSymbol] = false\n return false\n }\n\n try {\n const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n event[verifiedSymbol] = valid\n return valid\n } catch (err) {\n event[verifiedSymbol] = false\n return false\n }\n }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n generateSecretKey(): Uint8Array\n getPublicKey(secretKey: Uint8Array): string\n finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n kind: number\n tags: string[][]\n content: string\n created_at: number\n pubkey: string\n id: string\n sig: string\n [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n if (!isRecord(event)) return false\n if (typeof event.kind !== 'number') return false\n if (typeof event.content !== 'string') return false\n if (typeof event.created_at !== 'number') return false\n if (typeof event.pubkey !== 'string') return false\n if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n if (!Array.isArray(event.tags)) return false\n for (let i = 0; i < event.tags.length; i++) {\n let tag = event.tags[i]\n if (!Array.isArray(tag)) return false\n for (let j = 0; j < tag.length; j++) {\n if (typeof tag[j] !== 'string') return false\n }\n }\n\n return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n return events.sort((a: NostrEvent, b: NostrEvent): number => {\n if (a.created_at !== b.created_at) {\n return b.created_at - a.created_at\n }\n return a.id.localeCompare(b.id)\n })\n}\n", "import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { EventTemplate, VerifiedEvent } from './core.ts'\nimport { finalizeEvent, getPublicKey } from './pure.ts'\n\nexport interface Signer {\n getPublicKey(): Promise<string>\n signEvent(event: EventTemplate): Promise<VerifiedEvent>\n}\n\nexport class PlainKeySigner implements Signer {\n private secretKey: Uint8Array\n\n constructor(secretKey: Uint8Array) {\n this.secretKey = secretKey\n }\n\n async getPublicKey(): Promise<string> {\n return getPublicKey(this.secretKey)\n }\n\n async signEvent(event: EventTemplate): Promise<VerifiedEvent> {\n return finalizeEvent(event, this.secretKey)\n }\n}\n", "import { schnorr } from '@noble/curves/secp256k1.js'\nimport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\nimport { Nostr, Event, EventTemplate, UnsignedEvent, VerifiedEvent, verifiedSymbol, validateEvent } from './core.ts'\nimport { sha256 } from '@noble/hashes/sha2.js'\n\nimport { utf8Encoder } from './utils.ts'\n\nclass JS implements Nostr {\n generateSecretKey(): Uint8Array {\n return schnorr.utils.randomSecretKey()\n }\n getPublicKey(secretKey: Uint8Array): string {\n return bytesToHex(schnorr.getPublicKey(secretKey))\n }\n finalizeEvent(t: EventTemplate, secretKey: Uint8Array): VerifiedEvent {\n const event = t as VerifiedEvent\n event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey))\n event.id = getEventHash(event)\n event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey))\n event[verifiedSymbol] = true\n return event\n }\n verifyEvent(event: Event): event is VerifiedEvent {\n if (typeof event[verifiedSymbol] === 'boolean') return event[verifiedSymbol]\n\n try {\n const hash = getEventHash(event)\n if (hash !== event.id) {\n event[verifiedSymbol] = false\n return false\n }\n\n const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey))\n event[verifiedSymbol] = valid\n return valid\n } catch (err) {\n event[verifiedSymbol] = false\n return false\n }\n }\n}\n\nexport function serializeEvent(evt: UnsignedEvent): string {\n if (!validateEvent(evt)) throw new Error(\"can't serialize event with wrong or missing properties\")\n return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content])\n}\n\nexport function getEventHash(event: UnsignedEvent): string {\n let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))\n return bytesToHex(eventHash)\n}\n\nconst i: JS = new JS()\n\nexport const generateSecretKey = i.generateSecretKey\nexport const getPublicKey = i.getPublicKey\nexport const finalizeEvent = i.finalizeEvent\nexport const verifyEvent = i.verifyEvent\nexport * from './core.ts'\n", "export interface Nostr {\n generateSecretKey(): Uint8Array\n getPublicKey(secretKey: Uint8Array): string\n finalizeEvent(event: EventTemplate, secretKey: Uint8Array): VerifiedEvent\n verifyEvent(event: Event): event is VerifiedEvent\n}\n\n/** Designates a verified event signature. */\nexport const verifiedSymbol = Symbol('verified')\n\nexport type NostrEvent = {\n kind: number\n tags: string[][]\n content: string\n created_at: number\n pubkey: string\n id: string\n sig: string\n [verifiedSymbol]?: boolean\n}\n\nexport type Event = NostrEvent\nexport type EventTemplate = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at'>\nexport type UnsignedEvent = Pick<Event, 'kind' | 'tags' | 'content' | 'created_at' | 'pubkey'>\n\n/** An event whose signature has been verified. */\nexport interface VerifiedEvent extends Event {\n [verifiedSymbol]: true\n}\n\nconst isRecord = (obj: unknown): obj is Record<string, unknown> => obj instanceof Object\n\nexport function validateEvent<T>(event: T): event is T & UnsignedEvent {\n if (!isRecord(event)) return false\n if (typeof event.kind !== 'number') return false\n if (typeof event.content !== 'string') return false\n if (typeof event.created_at !== 'number') return false\n if (typeof event.pubkey !== 'string') return false\n if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false\n\n if (!Array.isArray(event.tags)) return false\n for (let i = 0; i < event.tags.length; i++) {\n let tag = event.tags[i]\n if (!Array.isArray(tag)) return false\n for (let j = 0; j < tag.length; j++) {\n if (typeof tag[j] !== 'string') return false\n }\n }\n\n return true\n}\n\n/**\n * Sort events in reverse-chronological order by the `created_at` timestamp,\n * and then by the event `id` (lexicographically) in case of ties.\n * This mutates the array.\n */\nexport function sortEvents(events: Event[]): Event[] {\n return events.sort((a: NostrEvent, b: NostrEvent): number => {\n if (a.created_at !== b.created_at) {\n return b.created_at - a.created_at\n }\n return a.id.localeCompare(b.id)\n })\n}\n", "import type { NostrEvent } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport { bytesToHex, hexToBytes } from '@noble/hashes/utils.js'\n\nexport function normalizeURL(url: string): string {\n try {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\n if (p.protocol === 'http:') p.protocol = 'ws:'\n else if (p.protocol === 'https:') p.protocol = 'wss:'\n p.pathname = p.pathname.replace(/\\/+/g, '/')\n if (p.pathname.endsWith('/')) p.pathname = p.pathname.slice(0, -1)\n if ((p.port === '80' && p.protocol === 'ws:') || (p.port === '443' && p.protocol === 'wss:')) p.port = ''\n p.searchParams.sort()\n p.hash = ''\n return p.toString()\n } catch (e) {\n throw new Error(`Invalid URL: ${url}`)\n }\n}\n\nexport function insertEventIntoDescendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return b.created_at - event.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function insertEventIntoAscendingList(sortedArray: NostrEvent[], event: NostrEvent): NostrEvent[] {\n const [idx, found] = binarySearch(sortedArray, b => {\n if (event.id === b.id) return 0\n if (event.created_at === b.created_at) return -1\n return event.created_at - b.created_at\n })\n if (!found) {\n sortedArray.splice(idx, 0, event)\n }\n return sortedArray\n}\n\nexport function binarySearch<T>(arr: T[], compare: (b: T) => number): [number, boolean] {\n let start = 0\n let end = arr.length - 1\n\n while (start <= end) {\n const mid = Math.floor((start + end) / 2)\n const cmp = compare(arr[mid])\n\n if (cmp === 0) {\n return [mid, true]\n }\n\n if (cmp < 0) {\n end = mid - 1\n } else {\n start = mid + 1\n }\n }\n\n return [start, false]\n}\n\nexport function mergeReverseSortedLists(list1: NostrEvent[], list2: NostrEvent[]): NostrEvent[] {\n const result: NostrEvent[] = new Array(list1.length + list2.length)\n result.length = 0\n let i1 = 0\n let i2 = 0\n let sameTimestampIds: string[] = []\n\n while (i1 < list1.length && i2 < list2.length) {\n let next: NostrEvent\n if (list1[i1]?.created_at > list2[i2]?.created_at) {\n next = list1[i1]\n i1++\n } else {\n next = list2[i2]\n i2++\n }\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i1 < list1.length) {\n const next = list1[i1]\n i1++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n while (i2 < list2.length) {\n const next = list2[i2]\n i2++\n\n if (result.length > 0 && result[result.length - 1].created_at === next.created_at) {\n if (sameTimestampIds.includes(next.id)) continue\n } else {\n sameTimestampIds.length = 0\n }\n result.push(next)\n sameTimestampIds.push(next.id)\n }\n\n return result\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,uBAAwB;AACxB,IAAAA,gBAAuC;;;ACOhC,IAAM,iBAAiB,OAAO,UAAU;AAsB/C,IAAM,WAAW,CAAC,QAAiD,eAAe;AAE3E,SAAS,cAAiB,OAAsC;AACrE,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,MAAI,OAAO,MAAM,SAAS;AAAU,WAAO;AAC3C,MAAI,OAAO,MAAM,YAAY;AAAU,WAAO;AAC9C,MAAI,OAAO,MAAM,eAAe;AAAU,WAAO;AACjD,MAAI,OAAO,MAAM,WAAW;AAAU,WAAO;AAC7C,MAAI,CAAC,MAAM,OAAO,MAAM,gBAAgB;AAAG,WAAO;AAElD,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI;AAAG,WAAO;AACvC,WAASC,KAAI,GAAGA,KAAI,MAAM,KAAK,QAAQA,MAAK;AAC1C,QAAI,MAAM,MAAM,KAAKA;AACrB,QAAI,CAAC,MAAM,QAAQ,GAAG;AAAG,aAAO;AAChC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,OAAO,IAAI,OAAO;AAAU,eAAO;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;;;AD/CA,kBAAuB;;;AEEvB,mBAAuC;AAHhC,IAAM,cAA2B,IAAI,YAAY,OAAO;AACxD,IAAM,cAA2B,IAAI,YAAY;;;AFIxD,IAAM,KAAN,MAA0B;AAAA,EACxB,oBAAgC;AAC9B,WAAO,yBAAQ,MAAM,gBAAgB;AAAA,EACvC;AAAA,EACA,aAAa,WAA+B;AAC1C,eAAO,0BAAW,yBAAQ,aAAa,SAAS,CAAC;AAAA,EACnD;AAAA,EACA,cAAc,GAAkB,WAAsC;AACpE,UAAM,QAAQ;AACd,UAAM,aAAS,0BAAW,yBAAQ,aAAa,SAAS,CAAC;AACzD,UAAM,KAAK,aAAa,KAAK;AAC7B,UAAM,UAAM,0BAAW,yBAAQ,SAAK,0BAAW,aAAa,KAAK,CAAC,GAAG,SAAS,CAAC;AAC/E,UAAM,kBAAkB;AACxB,WAAO;AAAA,EACT;AAAA,EACA,YAAY,OAAsC;AAChD,QAAI,OAAO,MAAM,oBAAoB;AAAW,aAAO,MAAM;AAE7D,QAAI;AACF,YAAM,OAAO,aAAa,KAAK;AAC/B,UAAI,SAAS,MAAM,IAAI;AACrB,cAAM,kBAAkB;AACxB,eAAO;AAAA,MACT;AAEA,YAAM,QAAQ,yBAAQ,WAAO,0BAAW,MAAM,GAAG,OAAG,0BAAW,IAAI,OAAG,0BAAW,MAAM,MAAM,CAAC;AAC9F,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT,SAAS,KAAP;AACA,YAAM,kBAAkB;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,eAAe,KAA4B;AACzD,MAAI,CAAC,cAAc,GAAG;AAAG,UAAM,IAAI,MAAM,wDAAwD;AACjG,SAAO,KAAK,UAAU,CAAC,GAAG,IAAI,QAAQ,IAAI,YAAY,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,CAAC;AACxF;AAEO,SAAS,aAAa,OAA8B;AACzD,MAAI,gBAAY,oBAAO,YAAY,OAAO,eAAe,KAAK,CAAC,CAAC;AAChE,aAAO,0BAAW,SAAS;AAC7B;AAEA,IAAM,IAAQ,IAAI,GAAG;AAEd,IAAM,oBAAoB,EAAE;AAC5B,IAAM,eAAe,EAAE;AACvB,IAAM,gBAAgB,EAAE;AACxB,IAAM,cAAc,EAAE;;;ADjDtB,IAAM,iBAAN,MAAuC;AAAA,EACpC;AAAA,EAER,YAAY,WAAuB;AACjC,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,eAAgC;AACpC,WAAO,aAAa,KAAK,SAAS;AAAA,EACpC;AAAA,EAEA,MAAM,UAAU,OAA8C;AAC5D,WAAO,cAAc,OAAO,KAAK,SAAS;AAAA,EAC5C;AACF;",
6
6
  "names": ["import_utils", "i"]
7
7
  }
@@ -424,6 +424,8 @@ var AbstractRelay = class {
424
424
  const event = data[2];
425
425
  if (this.verifyEvent(event) && matchFilters(so.filters, event)) {
426
426
  so.onevent(event);
427
+ } else {
428
+ so.oninvalidevent?.(event);
427
429
  }
428
430
  if (!so.lastEmitted || so.lastEmitted < event.created_at)
429
431
  so.lastEmitted = event.created_at;
@@ -488,8 +490,12 @@ var AbstractRelay = class {
488
490
  }
489
491
  }
490
492
  } catch (err) {
491
- const [_, __, event] = JSON.parse(json);
492
- window.printer.maybe(event.pubkey, ":: caught err", event, this.url, err);
493
+ try {
494
+ const [_, __, event] = JSON.parse(json);
495
+ console.warn(`[nostr] relay ${this.url} error processing message:`, err, event);
496
+ } catch (_) {
497
+ console.warn(`[nostr] relay ${this.url} error processing message:`, err);
498
+ }
493
499
  return;
494
500
  }
495
501
  }
@@ -504,6 +510,7 @@ var Subscription = class {
504
510
  alreadyHaveEvent;
505
511
  receivedEvent;
506
512
  onevent;
513
+ oninvalidevent;
507
514
  oneose;
508
515
  onclose;
509
516
  oncustom;
@@ -520,6 +527,7 @@ var Subscription = class {
520
527
  this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout;
521
528
  this.oneose = params.oneose;
522
529
  this.onclose = params.onclose;
530
+ this.oninvalidevent = params.oninvalidevent;
523
531
  this.onevent = params.onevent || ((event) => {
524
532
  console.warn(
525
533
  `onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,