nostr-tools 2.3.0 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/lib/cjs/abstract-pool.js +9 -6
- package/lib/cjs/abstract-pool.js.map +2 -2
- package/lib/cjs/abstract-relay.js +9 -6
- package/lib/cjs/abstract-relay.js.map +2 -2
- package/lib/cjs/filter.js.map +2 -2
- package/lib/cjs/index.js +60 -53
- package/lib/cjs/index.js.map +3 -3
- package/lib/cjs/kinds.js.map +2 -2
- package/lib/cjs/nip04.js.map +2 -2
- package/lib/cjs/nip11.js.map +2 -2
- package/lib/cjs/nip13.js.map +2 -2
- package/lib/cjs/nip18.js.map +2 -2
- package/lib/cjs/nip19.js.map +2 -2
- package/lib/cjs/nip21.js.map +2 -2
- package/lib/cjs/nip25.js.map +2 -2
- package/lib/cjs/nip27.js.map +2 -2
- package/lib/cjs/nip28.js.map +2 -2
- package/lib/cjs/nip29.js.map +2 -2
- package/lib/cjs/nip30.js.map +2 -2
- package/lib/cjs/nip42.js.map +1 -1
- package/lib/cjs/nip44.js +51 -47
- package/lib/cjs/nip44.js.map +2 -2
- package/lib/cjs/nip46.js +20 -9
- package/lib/cjs/nip46.js.map +3 -3
- package/lib/cjs/nip47.js.map +2 -2
- package/lib/cjs/nip57.js.map +2 -2
- package/lib/cjs/nip75.js.map +1 -1
- package/lib/cjs/nip94.js.map +1 -1
- package/lib/cjs/nip96.js +3 -5
- package/lib/cjs/nip96.js.map +2 -2
- package/lib/cjs/nip98.js.map +2 -2
- package/lib/cjs/nip99.js.map +1 -1
- package/lib/cjs/pool.js +9 -6
- package/lib/cjs/pool.js.map +2 -2
- package/lib/cjs/pure.js.map +2 -2
- package/lib/cjs/references.js.map +2 -2
- package/lib/cjs/relay.js +9 -6
- package/lib/cjs/relay.js.map +2 -2
- package/lib/cjs/utils.js.map +2 -2
- package/lib/esm/abstract-pool.js +9 -6
- package/lib/esm/abstract-pool.js.map +2 -2
- package/lib/esm/abstract-relay.js +9 -6
- package/lib/esm/abstract-relay.js.map +2 -2
- package/lib/esm/filter.js.map +2 -2
- package/lib/esm/index.js +60 -53
- package/lib/esm/index.js.map +3 -3
- package/lib/esm/kinds.js.map +2 -2
- package/lib/esm/nip04.js.map +2 -2
- package/lib/esm/nip11.js.map +2 -2
- package/lib/esm/nip13.js.map +2 -2
- package/lib/esm/nip18.js.map +2 -2
- package/lib/esm/nip19.js.map +2 -2
- package/lib/esm/nip21.js.map +2 -2
- package/lib/esm/nip25.js.map +2 -2
- package/lib/esm/nip27.js.map +2 -2
- package/lib/esm/nip28.js.map +2 -2
- package/lib/esm/nip29.js.map +2 -2
- package/lib/esm/nip30.js.map +2 -2
- package/lib/esm/nip42.js.map +1 -1
- package/lib/esm/nip44.js +53 -47
- package/lib/esm/nip44.js.map +2 -2
- package/lib/esm/nip46.js +20 -9
- package/lib/esm/nip46.js.map +3 -3
- package/lib/esm/nip47.js.map +2 -2
- package/lib/esm/nip57.js.map +2 -2
- package/lib/esm/nip75.js.map +1 -1
- package/lib/esm/nip94.js.map +1 -1
- package/lib/esm/nip96.js +5 -5
- package/lib/esm/nip96.js.map +3 -3
- package/lib/esm/nip98.js.map +2 -2
- package/lib/esm/nip99.js.map +1 -1
- package/lib/esm/pool.js +9 -6
- package/lib/esm/pool.js.map +2 -2
- package/lib/esm/pure.js.map +2 -2
- package/lib/esm/references.js.map +2 -2
- package/lib/esm/relay.js +9 -6
- package/lib/esm/relay.js.map +2 -2
- package/lib/esm/utils.js.map +2 -2
- package/lib/nostr.bundle.js +60 -53
- package/lib/nostr.bundle.js.map +3 -3
- package/lib/types/abstract-relay.d.ts +1 -0
- package/lib/types/nip44.d.ts +27 -53
- package/lib/types/nip46.d.ts +2 -1
- package/lib/types/nip47.d.ts +6 -3
- package/lib/types/nip96.d.ts +2 -0
- package/lib/types/test-helpers.d.ts +0 -1
- package/lib/types/utils.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../kinds.ts", "../../filter.ts", "../../fakejson.ts", "../../utils.ts", "../../nip42.ts", "../../helpers.ts", "../../abstract-relay.ts"],
|
|
4
|
-
"sourcesContent": ["/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number) {\n return (1000 <= kind && kind < 10000) || [1, 2, 4, 5, 6, 7, 8, 16, 40, 41, 42, 43, 44].includes(kind)\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) {\n return [0, 3].includes(kind) || (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) {\n return 20000 <= kind && kind < 30000\n}\n\n/** Events are **parameterized replaceable**, 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 isParameterizedReplaceableKind(kind: number) {\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 (isParameterizedReplaceableKind(kind)) return 'parameterized'\n return 'unknown'\n}\n\nexport const Metadata = 0\nexport const ShortTextNote = 1\nexport const RecommendRelay = 2\nexport const Contacts = 3\nexport const EncryptedDirectMessage = 4\nexport const EncryptedDirectMessages = 4\nexport const EventDeletion = 5\nexport const Repost = 6\nexport const Reaction = 7\nexport const BadgeAward = 8\nexport const GenericRepost = 16\nexport const ChannelCreation = 40\nexport const ChannelMetadata = 41\nexport const ChannelMessage = 42\nexport const ChannelHideMessage = 43\nexport const ChannelMuteUser = 44\nexport const OpenTimestamps = 1040\nexport const FileMetadata = 1063\nexport const LiveChatMessage = 1311\nexport const ProblemTracker = 1971\nexport const Report = 1984\nexport const Reporting = 1984\nexport const Label = 1985\nexport const CommunityPostApproval = 4550\nexport const JobRequest = 5999\nexport const JobResult = 6999\nexport const JobFeedback = 7000\nexport const ZapGoal = 9041\nexport const ZapRequest = 9734\nexport const Zap = 9735\nexport const Highlights = 9802\nexport const Mutelist = 10000\nexport const Pinlist = 10001\nexport const RelayList = 10002\nexport const BookmarkList = 10003\nexport const CommunitiesList = 10004\nexport const PublicChatsList = 10005\nexport const BlockedRelaysList = 10006\nexport const SearchRelaysList = 10007\nexport const InterestsList = 10015\nexport const UserEmojiList = 10030\nexport const FileServerPreference = 10096\nexport const NWCWalletInfo = 13194\nexport const LightningPubRPC = 21000\nexport const ClientAuth = 22242\nexport const NWCWalletRequest = 23194\nexport const NWCWalletResponse = 23195\nexport const NostrConnect = 24133\nexport const HTTPAuth = 27235\nexport const Followsets = 30000\nexport const Genericlists = 30001\nexport const Relaysets = 30002\nexport const Bookmarksets = 30003\nexport const Curationsets = 30004\nexport const ProfileBadges = 30008\nexport const BadgeDefinition = 30009\nexport const Interestsets = 30015\nexport const CreateOrUpdateStall = 30017\nexport const CreateOrUpdateProduct = 30018\nexport const LongFormArticle = 30023\nexport const DraftLong = 30024\nexport const Emojisets = 30030\nexport const Application = 30078\nexport const LiveEvent = 30311\nexport const UserStatuses = 30315\nexport const ClassifiedListing = 30402\nexport const DraftClassifiedListing = 30403\nexport const Date = 31922\nexport const Time = 31923\nexport const Calendar = 31924\nexport const CalendarEventRSVP = 31925\nexport const Handlerrecommendation = 31989\nexport const Handlerinformation = 31990\nexport const CommunityDefinition = 34550\n", "import { Event } from './core.ts'\nimport { 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 if (!filter.ids.some(prefix => event.id.startsWith(prefix))) {\n return false\n }\n }\n if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) return false\n if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n if (!filter.authors.some(prefix => event.pubkey.startsWith(prefix))) {\n return false\n }\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)) return true\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/** Calculate the intrinsic limit of a filter. This function may return `Infinity`. */\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 return Math.min(\n Math.max(0, filter.limit ?? Infinity),\n filter.ids?.length ?? Infinity,\n filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n ? filter.authors.length * filter.kinds.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 type { Event } from './core.ts'\n\nexport const utf8Decoder = new TextDecoder('utf-8')\nexport const utf8Encoder = new TextEncoder()\n\nexport function normalizeURL(url: string): string {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\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}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event) {\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: Event[], event: Event) {\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 class QueueNode<V> {\n public value: V\n public next: QueueNode<V> | null = null\n public prev: QueueNode<V> | null = null\n\n constructor(message: V) {\n this.value = message\n }\n}\n\nexport class Queue<V> {\n public first: QueueNode<V> | null\n public last: QueueNode<V> | null\n\n constructor() {\n this.first = null\n this.last = null\n }\n\n enqueue(value: V): boolean {\n const newNode = new QueueNode(value)\n if (!this.last) {\n // list is empty\n this.first = newNode\n this.last = newNode\n } else if (this.last === this.first) {\n // list has a single element\n this.last = newNode\n this.last.prev = this.first\n this.first.next = newNode\n } else {\n // list has elements, add as last\n newNode.prev = this.last\n this.last.next = newNode\n this.last = newNode\n }\n return true\n }\n\n dequeue(): V | null {\n if (!this.first) return null\n\n if (this.first === this.last) {\n const target = this.first\n this.first = null\n this.last = null\n return target.value\n }\n\n const target = this.first\n this.first = target.next\n\n return target.value\n }\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", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n return new Promise<void>(resolve => {\n const ch = new MessageChannel()\n const handler = () => {\n // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n ch.port1.removeEventListener('message', handler)\n resolve()\n }\n // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n ch.port1.addEventListener('message', handler)\n ch.port2.postMessage(0)\n ch.port1.start()\n })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n t[verifiedSymbol] = true\n return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.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 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\n public baseEoseTimeout: number = 4400\n public connectionTimeout: number = 4400\n public openSubs = new Map<string, Subscription>()\n private connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n private connectionPromise: Promise<void> | undefined\n private openCountRequests = new Map<string, CountResolver>()\n private openEventPublishes = new Map<string, EventPublishResolver>()\n private ws: WebSocket | undefined\n private incomingMessageQueue = new Queue<string>()\n private queueRunning = false\n private challenge: string | undefined\n private serial: number = 0\n private verifyEvent: Nostr['verifyEvent']\n\n constructor(url: string, opts: { verifyEvent: Nostr['verifyEvent'] }) {\n this.url = normalizeURL(url)\n this.verifyEvent = opts.verifyEvent\n }\n\n static async connect(url: string, opts: { verifyEvent: Nostr['verifyEvent'] }) {\n const relay = new AbstractRelay(url, opts)\n await relay.connect()\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 public async connect(): Promise<void> {\n if (this.connectionPromise) return this.connectionPromise\n\n this.challenge = undefined\n this.connectionPromise = new Promise((resolve, reject) => {\n this.connectionTimeoutHandle = setTimeout(() => {\n reject('connection timed out')\n this.connectionPromise = undefined\n this.onclose?.()\n this.closeAllSubscriptions('relay connection timed out')\n }, this.connectionTimeout)\n\n try {\n this.ws = new _WebSocket(this.url)\n } catch (err) {\n reject(err)\n return\n }\n\n this.ws.onopen = () => {\n clearTimeout(this.connectionTimeoutHandle)\n this._connected = true\n resolve()\n }\n\n this.ws.onerror = ev => {\n reject((ev as any).message)\n if (this._connected) {\n this.onclose?.()\n this.closeAllSubscriptions('relay connection errored')\n this._connected = false\n }\n }\n\n this.ws.onclose = async () => {\n this.connectionPromise = undefined\n this.onclose?.()\n this.closeAllSubscriptions('relay connection closed')\n this._connected = false\n }\n\n this.ws.onmessage = this._onmessage.bind(this)\n })\n\n return this.connectionPromise\n }\n\n private async runQueue() {\n this.queueRunning = true\n while (true) {\n if (false === this.handleNext()) {\n break\n }\n await yieldThread()\n }\n this.queueRunning = false\n }\n\n private handleNext(): undefined | false {\n const json = this.incomingMessageQueue.dequeue()\n if (!json) {\n return false\n }\n\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 Event\n if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n so.onevent(event)\n }\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 (ok) ep.resolve(reason)\n else ep.reject(new Error(reason))\n this.openEventPublishes.delete(id)\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 case 'AUTH': {\n this.challenge = data[1] as string\n return\n }\n }\n } catch (err) {\n return\n }\n }\n\n public async send(message: string) {\n if (!this.connectionPromise) throw new Error('sending on closed connection')\n\n this.connectionPromise.then(() => {\n this.ws?.send(message)\n })\n }\n\n public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>) {\n if (!this.challenge) throw new Error(\"can't perform auth, no challenge was received\")\n const evt = await signAuthEvent(makeAuthEvent(this.url, this.challenge))\n const ret = new Promise<string>((resolve, reject) => {\n this.openEventPublishes.set(evt.id, { resolve, reject })\n })\n this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n return ret\n }\n\n public async publish(event: Event): Promise<string> {\n const ret = new Promise<string>((resolve, reject) => {\n this.openEventPublishes.set(event.id, { resolve, reject })\n })\n this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\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) + ']')\n return ret\n }\n\n public subscribe(filters: Filter[], params: Partial<SubscriptionParams>): Subscription {\n const subscription = this.prepareSubscription(filters, params)\n subscription.fire()\n return subscription\n }\n\n public prepareSubscription(filters: Filter[], params: Partial<SubscriptionParams> & { id?: string }): Subscription {\n this.serial++\n const id = params.id || 'sub:' + this.serial\n const subscription = new Subscription(this, id, filters, params)\n this.openSubs.set(id, subscription)\n return subscription\n }\n\n public close() {\n this.closeAllSubscriptions('relay connection closed by us')\n this._connected = false\n this.ws?.close()\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>) {\n this.incomingMessageQueue.enqueue(ev.data as string)\n if (!this.queueRunning) {\n this.runQueue()\n }\n }\n}\n\nexport class Subscription {\n public readonly relay: AbstractRelay\n public readonly id: string\n\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 public eoseTimeout: number\n private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\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) {\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 this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n this.closed = true\n }\n this.relay.openSubs.delete(this.id)\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}\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}\n"],
|
|
5
|
-
"mappings": ";AA4EO,IAAM,aAAa;;;AC9DnB,SAAS,YAAY,QAAgB,OAAuB;AACjE,MAAI,OAAO,OAAO,OAAO,IAAI,QAAQ,MAAM,EAAE,MAAM,IAAI;AACrD,QAAI,CAAC,OAAO,IAAI,KAAK,YAAU,MAAM,GAAG,WAAW,MAAM,CAAC,GAAG;AAC3D,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,OAAO,SAAS,OAAO,MAAM,QAAQ,MAAM,IAAI,MAAM;AAAI,WAAO;AACpE,MAAI,OAAO,WAAW,OAAO,QAAQ,QAAQ,MAAM,MAAM,MAAM,IAAI;AACjE,QAAI,CAAC,OAAO,QAAQ,KAAK,YAAU,MAAM,OAAO,WAAW,MAAM,CAAC,GAAG;AACnE,aAAO;AAAA,IACT;AAAA,EACF;AAEA,WAAS,KAAK,QAAQ;AACpB,QAAI,EAAE,OAAO,KAAK;AAChB,UAAI,UAAU,EAAE,MAAM,CAAC;AACvB,UAAI,SAAS,OAAO,IAAI;AACxB,UAAI,UAAU,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,MAAM,EAAE,MAAM,CAAC,KAAK,OAAQ,QAAQ,CAAC,MAAM,EAAE;AAAG,eAAO;AAAA,IACpG;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,MAAM,aAAa,OAAO;AAAO,WAAO;AAC5D,MAAI,OAAO,SAAS,MAAM,aAAa,OAAO;AAAO,WAAO;AAE5D,SAAO;AACT;AAEO,SAAS,aAAa,SAAmB,OAAuB;AACrE,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,QAAI,YAAY,QAAQ,IAAI,KAAK;AAAG,aAAO;AAAA,EAC7C;AACA,SAAO;AACT;;;AC9CO,SAAS,SAAS,MAAc,OAAuB;AAC5D,MAAI,MAAM,MAAM,SAAS;AACzB,MAAI,MAAM,KAAK,QAAQ,IAAI,SAAS,IAAI;AACxC,MAAI,IAAI,KAAK,MAAM,GAAG,EAAE,QAAQ,GAAG,IAAI,MAAM;AAC7C,SAAO,KAAK,MAAM,GAAG,IAAI,EAAE;AAC7B;AAUO,SAAS,kBAAkB,MAA6B;AAC7D,MAAI,MAAM,KAAK,MAAM,GAAG,EAAE,EAAE,QAAQ,SAAS;AAC7C,MAAI,QAAQ;AAAI,WAAO;AAEvB,MAAI,SAAS,KAAK,MAAM,MAAM,IAAI,CAAC,EAAE,QAAQ,GAAG;AAChD,MAAI,WAAW;AAAI,WAAO;AAC1B,MAAI,QAAQ,MAAM,IAAI,IAAI;AAE1B,MAAI,OAAO,KAAK,MAAM,QAAQ,GAAG,EAAE,EAAE,QAAQ,GAAG;AAChD,MAAI,SAAS;AAAI,WAAO;AACxB,MAAI,MAAM,QAAQ,IAAI;AAEtB,SAAO,KAAK,MAAM,QAAQ,GAAG,GAAG;AAClC;;;AC1BO,IAAM,
|
|
4
|
+
"sourcesContent": ["/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n return (1000 <= kind && kind < 10000) || [1, 2, 4, 5, 6, 7, 8, 16, 40, 41, 42, 43, 44].includes(kind)\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 [0, 3].includes(kind) || (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 **parameterized replaceable**, 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 isParameterizedReplaceableKind(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 (isParameterizedReplaceableKind(kind)) return 'parameterized'\n return 'unknown'\n}\n\nexport const Metadata = 0\nexport const ShortTextNote = 1\nexport const RecommendRelay = 2\nexport const Contacts = 3\nexport const EncryptedDirectMessage = 4\nexport const EncryptedDirectMessages = 4\nexport const EventDeletion = 5\nexport const Repost = 6\nexport const Reaction = 7\nexport const BadgeAward = 8\nexport const GenericRepost = 16\nexport const ChannelCreation = 40\nexport const ChannelMetadata = 41\nexport const ChannelMessage = 42\nexport const ChannelHideMessage = 43\nexport const ChannelMuteUser = 44\nexport const OpenTimestamps = 1040\nexport const FileMetadata = 1063\nexport const LiveChatMessage = 1311\nexport const ProblemTracker = 1971\nexport const Report = 1984\nexport const Reporting = 1984\nexport const Label = 1985\nexport const CommunityPostApproval = 4550\nexport const JobRequest = 5999\nexport const JobResult = 6999\nexport const JobFeedback = 7000\nexport const ZapGoal = 9041\nexport const ZapRequest = 9734\nexport const Zap = 9735\nexport const Highlights = 9802\nexport const Mutelist = 10000\nexport const Pinlist = 10001\nexport const RelayList = 10002\nexport const BookmarkList = 10003\nexport const CommunitiesList = 10004\nexport const PublicChatsList = 10005\nexport const BlockedRelaysList = 10006\nexport const SearchRelaysList = 10007\nexport const InterestsList = 10015\nexport const UserEmojiList = 10030\nexport const FileServerPreference = 10096\nexport const NWCWalletInfo = 13194\nexport const LightningPubRPC = 21000\nexport const ClientAuth = 22242\nexport const NWCWalletRequest = 23194\nexport const NWCWalletResponse = 23195\nexport const NostrConnect = 24133\nexport const HTTPAuth = 27235\nexport const Followsets = 30000\nexport const Genericlists = 30001\nexport const Relaysets = 30002\nexport const Bookmarksets = 30003\nexport const Curationsets = 30004\nexport const ProfileBadges = 30008\nexport const BadgeDefinition = 30009\nexport const Interestsets = 30015\nexport const CreateOrUpdateStall = 30017\nexport const CreateOrUpdateProduct = 30018\nexport const LongFormArticle = 30023\nexport const DraftLong = 30024\nexport const Emojisets = 30030\nexport const Application = 30078\nexport const LiveEvent = 30311\nexport const UserStatuses = 30315\nexport const ClassifiedListing = 30402\nexport const DraftClassifiedListing = 30403\nexport const Date = 31922\nexport const Time = 31923\nexport const Calendar = 31924\nexport const CalendarEventRSVP = 31925\nexport const Handlerrecommendation = 31989\nexport const Handlerinformation = 31990\nexport const CommunityDefinition = 34550\n", "import { Event } from './core.ts'\nimport { 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 if (!filter.ids.some(prefix => event.id.startsWith(prefix))) {\n return false\n }\n }\n if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) return false\n if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n if (!filter.authors.some(prefix => event.pubkey.startsWith(prefix))) {\n return false\n }\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)) return true\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/** Calculate the intrinsic limit of a filter. This function may return `Infinity`. */\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 return Math.min(\n Math.max(0, filter.limit ?? Infinity),\n filter.ids?.length ?? Infinity,\n filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n ? filter.authors.length * filter.kinds.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 type { Event } from './core.ts'\n\nexport const utf8Decoder: TextDecoder = new TextDecoder('utf-8')\nexport const utf8Encoder: TextEncoder = new TextEncoder()\n\nexport function normalizeURL(url: string): string {\n if (url.indexOf('://') === -1) url = 'wss://' + url\n let p = new URL(url)\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}\n\nexport function insertEventIntoDescendingList(sortedArray: Event[], event: Event): Event[] {\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: Event[], event: Event): Event[] {\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 class QueueNode<V> {\n public value: V\n public next: QueueNode<V> | null = null\n public prev: QueueNode<V> | null = null\n\n constructor(message: V) {\n this.value = message\n }\n}\n\nexport class Queue<V> {\n public first: QueueNode<V> | null\n public last: QueueNode<V> | null\n\n constructor() {\n this.first = null\n this.last = null\n }\n\n enqueue(value: V): boolean {\n const newNode = new QueueNode(value)\n if (!this.last) {\n // list is empty\n this.first = newNode\n this.last = newNode\n } else if (this.last === this.first) {\n // list has a single element\n this.last = newNode\n this.last.prev = this.first\n this.first.next = newNode\n } else {\n // list has elements, add as last\n newNode.prev = this.last\n this.last.next = newNode\n this.last = newNode\n }\n return true\n }\n\n dequeue(): V | null {\n if (!this.first) return null\n\n if (this.first === this.last) {\n const target = this.first\n this.first = null\n this.last = null\n return target.value\n }\n\n const target = this.first\n this.first = target.next\n\n return target.value\n }\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", "import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'\n\nexport async function yieldThread() {\n return new Promise<void>(resolve => {\n const ch = new MessageChannel()\n const handler = () => {\n // @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)\n ch.port1.removeEventListener('message', handler)\n resolve()\n }\n // @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)\n ch.port1.addEventListener('message', handler)\n ch.port2.postMessage(0)\n ch.port1.start()\n })\n}\n\nexport const alwaysTrue: Nostr['verifyEvent'] = (t: Event): t is VerifiedEvent => {\n t[verifiedSymbol] = true\n return true\n}\n", "/* global WebSocket */\n\nimport type { Event, EventTemplate, VerifiedEvent, Nostr } from './core.ts'\nimport { matchFilters, type Filter } from './filter.ts'\nimport { getHex64, getSubscriptionId } from './fakejson.ts'\nimport { Queue, normalizeURL } from './utils.ts'\nimport { makeAuthEvent } from './nip42.ts'\nimport { yieldThread } from './helpers.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 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\n public baseEoseTimeout: number = 4400\n public connectionTimeout: number = 4400\n public openSubs: Map<string, Subscription> = new Map()\n private connectionTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n private connectionPromise: Promise<void> | undefined\n private openCountRequests = new Map<string, CountResolver>()\n private openEventPublishes = new Map<string, EventPublishResolver>()\n private ws: WebSocket | undefined\n private incomingMessageQueue = new Queue<string>()\n private queueRunning = false\n private challenge: string | undefined\n private serial: number = 0\n private verifyEvent: Nostr['verifyEvent']\n\n constructor(url: string, opts: { verifyEvent: Nostr['verifyEvent'] }) {\n this.url = normalizeURL(url)\n this.verifyEvent = opts.verifyEvent\n }\n\n static async connect(url: string, opts: { verifyEvent: Nostr['verifyEvent'] }): Promise<AbstractRelay> {\n const relay = new AbstractRelay(url, opts)\n await relay.connect()\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 public async connect(): Promise<void> {\n if (this.connectionPromise) return this.connectionPromise\n\n this.challenge = undefined\n this.connectionPromise = new Promise((resolve, reject) => {\n this.connectionTimeoutHandle = setTimeout(() => {\n reject('connection timed out')\n this.connectionPromise = undefined\n this.onclose?.()\n this.closeAllSubscriptions('relay connection timed out')\n }, this.connectionTimeout)\n\n try {\n this.ws = new _WebSocket(this.url)\n } catch (err) {\n reject(err)\n return\n }\n\n this.ws.onopen = () => {\n clearTimeout(this.connectionTimeoutHandle)\n this._connected = true\n resolve()\n }\n\n this.ws.onerror = ev => {\n reject((ev as any).message)\n if (this._connected) {\n this._connected = false\n this.connectionPromise = undefined\n this.onclose?.()\n this.closeAllSubscriptions('relay connection errored')\n }\n }\n\n this.ws.onclose = async () => {\n if (this._connected) {\n this._connected = false\n this.connectionPromise = undefined\n this.onclose?.()\n this.closeAllSubscriptions('relay connection closed')\n }\n }\n\n this.ws.onmessage = this._onmessage.bind(this)\n })\n\n return this.connectionPromise\n }\n\n private async runQueue() {\n this.queueRunning = true\n while (true) {\n if (false === this.handleNext()) {\n break\n }\n await yieldThread()\n }\n this.queueRunning = false\n }\n\n private handleNext(): undefined | false {\n const json = this.incomingMessageQueue.dequeue()\n if (!json) {\n return false\n }\n\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 Event\n if (this.verifyEvent(event) && matchFilters(so.filters, event)) {\n so.onevent(event)\n }\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 (ok) ep.resolve(reason)\n else ep.reject(new Error(reason))\n this.openEventPublishes.delete(id)\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 case 'AUTH': {\n this.challenge = data[1] as string\n return\n }\n }\n } catch (err) {\n return\n }\n }\n\n public async send(message: string) {\n if (!this.connectionPromise) throw new Error('sending on closed connection')\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 if (!this.challenge) throw new Error(\"can't perform auth, no challenge was received\")\n const evt = await signAuthEvent(makeAuthEvent(this.url, this.challenge))\n const ret = new Promise<string>((resolve, reject) => {\n this.openEventPublishes.set(evt.id, { resolve, reject })\n })\n this.send('[\"AUTH\",' + JSON.stringify(evt) + ']')\n return ret\n }\n\n public async publish(event: Event): Promise<string> {\n const ret = new Promise<string>((resolve, reject) => {\n this.openEventPublishes.set(event.id, { resolve, reject })\n })\n this.send('[\"EVENT\",' + JSON.stringify(event) + ']')\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) + ']')\n return ret\n }\n\n public subscribe(filters: Filter[], params: Partial<SubscriptionParams>): Subscription {\n const subscription = this.prepareSubscription(filters, params)\n subscription.fire()\n return subscription\n }\n\n public prepareSubscription(filters: Filter[], params: Partial<SubscriptionParams> & { id?: string }): Subscription {\n this.serial++\n const id = params.id || 'sub:' + this.serial\n const subscription = new Subscription(this, id, filters, params)\n this.openSubs.set(id, subscription)\n return subscription\n }\n\n public close() {\n this.closeAllSubscriptions('relay connection closed by us')\n this._connected = false\n this.ws?.close()\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>) {\n this.incomingMessageQueue.enqueue(ev.data as string)\n if (!this.queueRunning) {\n this.runQueue()\n }\n }\n}\n\nexport class Subscription {\n public readonly relay: AbstractRelay\n public readonly id: string\n\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 public eoseTimeout: number\n private eoseTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n constructor(relay: AbstractRelay, id: string, filters: Filter[], params: SubscriptionParams) {\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 this.relay.send('[\"CLOSE\",' + JSON.stringify(this.id) + ']')\n this.closed = true\n }\n this.relay.openSubs.delete(this.id)\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}\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}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["target"]
|
|
7
7
|
}
|
package/lib/esm/filter.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../kinds.ts", "../../filter.ts"],
|
|
4
|
-
"sourcesContent": ["/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number) {\n return (1000 <= kind && kind < 10000) || [1, 2, 4, 5, 6, 7, 8, 16, 40, 41, 42, 43, 44].includes(kind)\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) {\n return [0, 3].includes(kind) || (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) {\n return 20000 <= kind && kind < 30000\n}\n\n/** Events are **parameterized replaceable**, 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 isParameterizedReplaceableKind(kind: number) {\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 (isParameterizedReplaceableKind(kind)) return 'parameterized'\n return 'unknown'\n}\n\nexport const Metadata = 0\nexport const ShortTextNote = 1\nexport const RecommendRelay = 2\nexport const Contacts = 3\nexport const EncryptedDirectMessage = 4\nexport const EncryptedDirectMessages = 4\nexport const EventDeletion = 5\nexport const Repost = 6\nexport const Reaction = 7\nexport const BadgeAward = 8\nexport const GenericRepost = 16\nexport const ChannelCreation = 40\nexport const ChannelMetadata = 41\nexport const ChannelMessage = 42\nexport const ChannelHideMessage = 43\nexport const ChannelMuteUser = 44\nexport const OpenTimestamps = 1040\nexport const FileMetadata = 1063\nexport const LiveChatMessage = 1311\nexport const ProblemTracker = 1971\nexport const Report = 1984\nexport const Reporting = 1984\nexport const Label = 1985\nexport const CommunityPostApproval = 4550\nexport const JobRequest = 5999\nexport const JobResult = 6999\nexport const JobFeedback = 7000\nexport const ZapGoal = 9041\nexport const ZapRequest = 9734\nexport const Zap = 9735\nexport const Highlights = 9802\nexport const Mutelist = 10000\nexport const Pinlist = 10001\nexport const RelayList = 10002\nexport const BookmarkList = 10003\nexport const CommunitiesList = 10004\nexport const PublicChatsList = 10005\nexport const BlockedRelaysList = 10006\nexport const SearchRelaysList = 10007\nexport const InterestsList = 10015\nexport const UserEmojiList = 10030\nexport const FileServerPreference = 10096\nexport const NWCWalletInfo = 13194\nexport const LightningPubRPC = 21000\nexport const ClientAuth = 22242\nexport const NWCWalletRequest = 23194\nexport const NWCWalletResponse = 23195\nexport const NostrConnect = 24133\nexport const HTTPAuth = 27235\nexport const Followsets = 30000\nexport const Genericlists = 30001\nexport const Relaysets = 30002\nexport const Bookmarksets = 30003\nexport const Curationsets = 30004\nexport const ProfileBadges = 30008\nexport const BadgeDefinition = 30009\nexport const Interestsets = 30015\nexport const CreateOrUpdateStall = 30017\nexport const CreateOrUpdateProduct = 30018\nexport const LongFormArticle = 30023\nexport const DraftLong = 30024\nexport const Emojisets = 30030\nexport const Application = 30078\nexport const LiveEvent = 30311\nexport const UserStatuses = 30315\nexport const ClassifiedListing = 30402\nexport const DraftClassifiedListing = 30403\nexport const Date = 31922\nexport const Time = 31923\nexport const Calendar = 31924\nexport const CalendarEventRSVP = 31925\nexport const Handlerrecommendation = 31989\nexport const Handlerinformation = 31990\nexport const CommunityDefinition = 34550\n", "import { Event } from './core.ts'\nimport { 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 if (!filter.ids.some(prefix => event.id.startsWith(prefix))) {\n return false\n }\n }\n if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) return false\n if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n if (!filter.authors.some(prefix => event.pubkey.startsWith(prefix))) {\n return false\n }\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)) return true\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/** Calculate the intrinsic limit of a filter. This function may return `Infinity`. */\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 return Math.min(\n Math.max(0, filter.limit ?? Infinity),\n filter.ids?.length ?? Infinity,\n filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n ? filter.authors.length * filter.kinds.length\n : Infinity,\n )\n}\n"],
|
|
5
|
-
"mappings": ";AAMO,SAAS,kBAAkB,
|
|
4
|
+
"sourcesContent": ["/** Events are **regular**, which means they're all expected to be stored by relays. */\nexport function isRegularKind(kind: number): boolean {\n return (1000 <= kind && kind < 10000) || [1, 2, 4, 5, 6, 7, 8, 16, 40, 41, 42, 43, 44].includes(kind)\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 [0, 3].includes(kind) || (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 **parameterized replaceable**, 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 isParameterizedReplaceableKind(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 (isParameterizedReplaceableKind(kind)) return 'parameterized'\n return 'unknown'\n}\n\nexport const Metadata = 0\nexport const ShortTextNote = 1\nexport const RecommendRelay = 2\nexport const Contacts = 3\nexport const EncryptedDirectMessage = 4\nexport const EncryptedDirectMessages = 4\nexport const EventDeletion = 5\nexport const Repost = 6\nexport const Reaction = 7\nexport const BadgeAward = 8\nexport const GenericRepost = 16\nexport const ChannelCreation = 40\nexport const ChannelMetadata = 41\nexport const ChannelMessage = 42\nexport const ChannelHideMessage = 43\nexport const ChannelMuteUser = 44\nexport const OpenTimestamps = 1040\nexport const FileMetadata = 1063\nexport const LiveChatMessage = 1311\nexport const ProblemTracker = 1971\nexport const Report = 1984\nexport const Reporting = 1984\nexport const Label = 1985\nexport const CommunityPostApproval = 4550\nexport const JobRequest = 5999\nexport const JobResult = 6999\nexport const JobFeedback = 7000\nexport const ZapGoal = 9041\nexport const ZapRequest = 9734\nexport const Zap = 9735\nexport const Highlights = 9802\nexport const Mutelist = 10000\nexport const Pinlist = 10001\nexport const RelayList = 10002\nexport const BookmarkList = 10003\nexport const CommunitiesList = 10004\nexport const PublicChatsList = 10005\nexport const BlockedRelaysList = 10006\nexport const SearchRelaysList = 10007\nexport const InterestsList = 10015\nexport const UserEmojiList = 10030\nexport const FileServerPreference = 10096\nexport const NWCWalletInfo = 13194\nexport const LightningPubRPC = 21000\nexport const ClientAuth = 22242\nexport const NWCWalletRequest = 23194\nexport const NWCWalletResponse = 23195\nexport const NostrConnect = 24133\nexport const HTTPAuth = 27235\nexport const Followsets = 30000\nexport const Genericlists = 30001\nexport const Relaysets = 30002\nexport const Bookmarksets = 30003\nexport const Curationsets = 30004\nexport const ProfileBadges = 30008\nexport const BadgeDefinition = 30009\nexport const Interestsets = 30015\nexport const CreateOrUpdateStall = 30017\nexport const CreateOrUpdateProduct = 30018\nexport const LongFormArticle = 30023\nexport const DraftLong = 30024\nexport const Emojisets = 30030\nexport const Application = 30078\nexport const LiveEvent = 30311\nexport const UserStatuses = 30315\nexport const ClassifiedListing = 30402\nexport const DraftClassifiedListing = 30403\nexport const Date = 31922\nexport const Time = 31923\nexport const Calendar = 31924\nexport const CalendarEventRSVP = 31925\nexport const Handlerrecommendation = 31989\nexport const Handlerinformation = 31990\nexport const CommunityDefinition = 34550\n", "import { Event } from './core.ts'\nimport { 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 if (!filter.ids.some(prefix => event.id.startsWith(prefix))) {\n return false\n }\n }\n if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) return false\n if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {\n if (!filter.authors.some(prefix => event.pubkey.startsWith(prefix))) {\n return false\n }\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)) return true\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/** Calculate the intrinsic limit of a filter. This function may return `Infinity`. */\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 return Math.min(\n Math.max(0, filter.limit ?? Infinity),\n filter.ids?.length ?? Infinity,\n filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))\n ? filter.authors.length * filter.kinds.length\n : Infinity,\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAMO,SAAS,kBAAkB,MAAuB;AACvD,SAAO,CAAC,GAAG,CAAC,EAAE,SAAS,IAAI,KAAM,OAAS,QAAQ,OAAO;AAC3D;;;ACMO,SAAS,YAAY,QAAgB,OAAuB;AACjE,MAAI,OAAO,OAAO,OAAO,IAAI,QAAQ,MAAM,EAAE,MAAM,IAAI;AACrD,QAAI,CAAC,OAAO,IAAI,KAAK,YAAU,MAAM,GAAG,WAAW,MAAM,CAAC,GAAG;AAC3D,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,OAAO,SAAS,OAAO,MAAM,QAAQ,MAAM,IAAI,MAAM;AAAI,WAAO;AACpE,MAAI,OAAO,WAAW,OAAO,QAAQ,QAAQ,MAAM,MAAM,MAAM,IAAI;AACjE,QAAI,CAAC,OAAO,QAAQ,KAAK,YAAU,MAAM,OAAO,WAAW,MAAM,CAAC,GAAG;AACnE,aAAO;AAAA,IACT;AAAA,EACF;AAEA,WAAS,KAAK,QAAQ;AACpB,QAAI,EAAE,OAAO,KAAK;AAChB,UAAI,UAAU,EAAE,MAAM,CAAC;AACvB,UAAI,SAAS,OAAO,IAAI;AACxB,UAAI,UAAU,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,MAAM,EAAE,MAAM,CAAC,KAAK,OAAQ,QAAQ,CAAC,MAAM,EAAE;AAAG,eAAO;AAAA,IACpG;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,MAAM,aAAa,OAAO;AAAO,WAAO;AAC5D,MAAI,OAAO,SAAS,MAAM,aAAa,OAAO;AAAO,WAAO;AAE5D,SAAO;AACT;AAEO,SAAS,aAAa,SAAmB,OAAuB;AACrE,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,QAAI,YAAY,QAAQ,IAAI,KAAK;AAAG,aAAO;AAAA,EAC7C;AACA,SAAO;AACT;AAEO,SAAS,gBAAgB,SAA2B;AACzD,MAAI,SAAiB,CAAC;AACtB,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,QAAI,SAAS,QAAQ;AACrB,WAAO,QAAQ,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU,MAAM,MAAM;AACrD,UAAI,aAAa,WAAW,aAAa,SAAS,aAAa,aAAa,SAAS,OAAO,KAAK;AAE/F,eAAO,YAAY,OAAO,aAAa,CAAC;AAExC,iBAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AAEtC,cAAI,QAAQ,OAAO;AAEnB,cAAI,CAAC,OAAO,UAAU,SAAS,KAAK;AAAG,mBAAO,UAAU,KAAK,KAAK;AAAA,QACpE;AAAA,MACF;AAAA,IACF,CAAC;AAED,QAAI,OAAO,UAAU,CAAC,OAAO,SAAS,OAAO,QAAQ,OAAO;AAAQ,aAAO,QAAQ,OAAO;AAC1F,QAAI,OAAO,UAAU,CAAC,OAAO,SAAS,OAAO,QAAQ,OAAO;AAAQ,aAAO,QAAQ,OAAO;AAC1F,QAAI,OAAO,UAAU,CAAC,OAAO,SAAS,OAAO,QAAQ,OAAO;AAAQ,aAAO,QAAQ,OAAO;AAAA,EAC5F;AAEA,SAAO;AACT;AAGO,SAAS,eAAe,QAAwB;AACrD,MAAI,OAAO,OAAO,CAAC,OAAO,IAAI;AAAQ,WAAO;AAC7C,MAAI,OAAO,SAAS,CAAC,OAAO,MAAM;AAAQ,WAAO;AACjD,MAAI,OAAO,WAAW,CAAC,OAAO,QAAQ;AAAQ,WAAO;AAErD,SAAO,KAAK;AAAA,IACV,KAAK,IAAI,GAAG,OAAO,SAAS,QAAQ;AAAA,IACpC,OAAO,KAAK,UAAU;AAAA,IACtB,OAAO,SAAS,UAAU,OAAO,OAAO,MAAM,UAAQ,kBAAkB,IAAI,CAAC,IACzE,OAAO,QAAQ,SAAS,OAAO,MAAM,SACrC;AAAA,EACN;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/lib/esm/index.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
2
3
|
var __export = (target, all) => {
|
|
3
4
|
for (var name in all)
|
|
4
5
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
6
|
};
|
|
7
|
+
var __publicField = (obj, key, value) => {
|
|
8
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
9
|
+
return value;
|
|
10
|
+
};
|
|
6
11
|
|
|
7
12
|
// pure.ts
|
|
8
13
|
import { schnorr } from "@noble/curves/secp256k1";
|
|
@@ -619,16 +624,19 @@ var AbstractRelay = class {
|
|
|
619
624
|
this.ws.onerror = (ev) => {
|
|
620
625
|
reject(ev.message);
|
|
621
626
|
if (this._connected) {
|
|
627
|
+
this._connected = false;
|
|
628
|
+
this.connectionPromise = void 0;
|
|
622
629
|
this.onclose?.();
|
|
623
630
|
this.closeAllSubscriptions("relay connection errored");
|
|
624
|
-
this._connected = false;
|
|
625
631
|
}
|
|
626
632
|
};
|
|
627
633
|
this.ws.onclose = async () => {
|
|
628
|
-
this.
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
634
|
+
if (this._connected) {
|
|
635
|
+
this._connected = false;
|
|
636
|
+
this.connectionPromise = void 0;
|
|
637
|
+
this.onclose?.();
|
|
638
|
+
this.closeAllSubscriptions("relay connection closed");
|
|
639
|
+
}
|
|
632
640
|
};
|
|
633
641
|
this.ws.onmessage = this._onmessage.bind(this);
|
|
634
642
|
});
|
|
@@ -821,7 +829,7 @@ var Subscription = class {
|
|
|
821
829
|
this.oneose?.();
|
|
822
830
|
}
|
|
823
831
|
close(reason = "closed by caller") {
|
|
824
|
-
if (!this.closed) {
|
|
832
|
+
if (!this.closed && this.relay.connected) {
|
|
825
833
|
this.relay.send('["CLOSE",' + JSON.stringify(this.id) + "]");
|
|
826
834
|
this.closed = true;
|
|
827
835
|
}
|
|
@@ -1806,26 +1814,23 @@ import { sha256 as sha2562 } from "@noble/hashes/sha256";
|
|
|
1806
1814
|
import { concatBytes as concatBytes2, randomBytes as randomBytes2, utf8ToBytes } from "@noble/hashes/utils";
|
|
1807
1815
|
import { base64 as base642 } from "@scure/base";
|
|
1808
1816
|
var decoder = new TextDecoder();
|
|
1809
|
-
var
|
|
1810
|
-
|
|
1811
|
-
maxPlaintextSize: 65535,
|
|
1812
|
-
utf8Encode: utf8ToBytes,
|
|
1813
|
-
utf8Decode(bytes) {
|
|
1817
|
+
var _u = class {
|
|
1818
|
+
static utf8Decode(bytes) {
|
|
1814
1819
|
return decoder.decode(bytes);
|
|
1815
|
-
}
|
|
1816
|
-
getConversationKey(privkeyA, pubkeyB) {
|
|
1820
|
+
}
|
|
1821
|
+
static getConversationKey(privkeyA, pubkeyB) {
|
|
1817
1822
|
const sharedX = secp256k12.getSharedSecret(privkeyA, "02" + pubkeyB).subarray(1, 33);
|
|
1818
1823
|
return hkdf_extract(sha2562, sharedX, "nip44-v2");
|
|
1819
|
-
}
|
|
1820
|
-
getMessageKeys(conversationKey, nonce) {
|
|
1824
|
+
}
|
|
1825
|
+
static getMessageKeys(conversationKey, nonce) {
|
|
1821
1826
|
const keys = hkdf_expand(sha2562, conversationKey, nonce, 76);
|
|
1822
1827
|
return {
|
|
1823
1828
|
chacha_key: keys.subarray(0, 32),
|
|
1824
1829
|
chacha_nonce: keys.subarray(32, 44),
|
|
1825
1830
|
hmac_key: keys.subarray(44, 76)
|
|
1826
1831
|
};
|
|
1827
|
-
}
|
|
1828
|
-
calcPaddedLen(len) {
|
|
1832
|
+
}
|
|
1833
|
+
static calcPaddedLen(len) {
|
|
1829
1834
|
if (!Number.isSafeInteger(len) || len < 1)
|
|
1830
1835
|
throw new Error("expected positive integer");
|
|
1831
1836
|
if (len <= 32)
|
|
@@ -1833,35 +1838,35 @@ var u = {
|
|
|
1833
1838
|
const nextPower = 1 << Math.floor(Math.log2(len - 1)) + 1;
|
|
1834
1839
|
const chunk = nextPower <= 256 ? 32 : nextPower / 8;
|
|
1835
1840
|
return chunk * (Math.floor((len - 1) / chunk) + 1);
|
|
1836
|
-
}
|
|
1837
|
-
writeU16BE(num) {
|
|
1838
|
-
if (!Number.isSafeInteger(num) || num <
|
|
1841
|
+
}
|
|
1842
|
+
static writeU16BE(num) {
|
|
1843
|
+
if (!Number.isSafeInteger(num) || num < _u.minPlaintextSize || num > _u.maxPlaintextSize)
|
|
1839
1844
|
throw new Error("invalid plaintext size: must be between 1 and 65535 bytes");
|
|
1840
1845
|
const arr = new Uint8Array(2);
|
|
1841
1846
|
new DataView(arr.buffer).setUint16(0, num, false);
|
|
1842
1847
|
return arr;
|
|
1843
|
-
}
|
|
1844
|
-
pad(plaintext) {
|
|
1845
|
-
const unpadded =
|
|
1848
|
+
}
|
|
1849
|
+
static pad(plaintext) {
|
|
1850
|
+
const unpadded = _u.utf8Encode(plaintext);
|
|
1846
1851
|
const unpaddedLen = unpadded.length;
|
|
1847
|
-
const prefix =
|
|
1848
|
-
const suffix = new Uint8Array(
|
|
1852
|
+
const prefix = _u.writeU16BE(unpaddedLen);
|
|
1853
|
+
const suffix = new Uint8Array(_u.calcPaddedLen(unpaddedLen) - unpaddedLen);
|
|
1849
1854
|
return concatBytes2(prefix, unpadded, suffix);
|
|
1850
|
-
}
|
|
1851
|
-
unpad(padded) {
|
|
1855
|
+
}
|
|
1856
|
+
static unpad(padded) {
|
|
1852
1857
|
const unpaddedLen = new DataView(padded.buffer).getUint16(0);
|
|
1853
1858
|
const unpadded = padded.subarray(2, 2 + unpaddedLen);
|
|
1854
|
-
if (unpaddedLen <
|
|
1859
|
+
if (unpaddedLen < _u.minPlaintextSize || unpaddedLen > _u.maxPlaintextSize || unpadded.length !== unpaddedLen || padded.length !== 2 + _u.calcPaddedLen(unpaddedLen))
|
|
1855
1860
|
throw new Error("invalid padding");
|
|
1856
|
-
return
|
|
1857
|
-
}
|
|
1858
|
-
hmacAad(key, message, aad) {
|
|
1861
|
+
return _u.utf8Decode(unpadded);
|
|
1862
|
+
}
|
|
1863
|
+
static hmacAad(key, message, aad) {
|
|
1859
1864
|
if (aad.length !== 32)
|
|
1860
1865
|
throw new Error("AAD associated data must be 32 bytes");
|
|
1861
1866
|
const combined = concatBytes2(aad, message);
|
|
1862
1867
|
return hmac(sha2562, key, combined);
|
|
1863
|
-
}
|
|
1864
|
-
decodePayload(payload) {
|
|
1868
|
+
}
|
|
1869
|
+
static decodePayload(payload) {
|
|
1865
1870
|
if (typeof payload !== "string")
|
|
1866
1871
|
throw new Error("payload must be a valid string");
|
|
1867
1872
|
const plen = payload.length;
|
|
@@ -1888,27 +1893,29 @@ var u = {
|
|
|
1888
1893
|
};
|
|
1889
1894
|
}
|
|
1890
1895
|
};
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
}
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1896
|
+
var u = _u;
|
|
1897
|
+
__publicField(u, "minPlaintextSize", 1);
|
|
1898
|
+
__publicField(u, "maxPlaintextSize", 65535);
|
|
1899
|
+
__publicField(u, "utf8Encode", utf8ToBytes);
|
|
1900
|
+
var v2 = class {
|
|
1901
|
+
static encrypt(plaintext, conversationKey, nonce = randomBytes2(32)) {
|
|
1902
|
+
const { chacha_key, chacha_nonce, hmac_key } = u.getMessageKeys(conversationKey, nonce);
|
|
1903
|
+
const padded = u.pad(plaintext);
|
|
1904
|
+
const ciphertext = chacha20(chacha_key, chacha_nonce, padded);
|
|
1905
|
+
const mac = u.hmacAad(hmac_key, ciphertext, nonce);
|
|
1906
|
+
return base642.encode(concatBytes2(new Uint8Array([2]), nonce, ciphertext, mac));
|
|
1907
|
+
}
|
|
1908
|
+
static decrypt(payload, conversationKey) {
|
|
1909
|
+
const { nonce, ciphertext, mac } = u.decodePayload(payload);
|
|
1910
|
+
const { chacha_key, chacha_nonce, hmac_key } = u.getMessageKeys(conversationKey, nonce);
|
|
1911
|
+
const calculatedMac = u.hmacAad(hmac_key, ciphertext, nonce);
|
|
1912
|
+
if (!equalBytes(calculatedMac, mac))
|
|
1913
|
+
throw new Error("invalid MAC");
|
|
1914
|
+
const padded = chacha20(chacha_key, chacha_nonce, ciphertext);
|
|
1915
|
+
return u.unpad(padded);
|
|
1916
|
+
}
|
|
1911
1917
|
};
|
|
1918
|
+
__publicField(v2, "utils", u);
|
|
1912
1919
|
var nip44_default = { v2 };
|
|
1913
1920
|
|
|
1914
1921
|
// nip47.ts
|