nostr-tools 2.1.4 → 2.1.7
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 +4 -0
- package/lib/cjs/abstract-pool.js +2 -0
- package/lib/cjs/abstract-pool.js.map +2 -2
- package/lib/cjs/abstract-relay.js +2 -0
- package/lib/cjs/abstract-relay.js.map +2 -2
- package/lib/cjs/filter.js.map +1 -1
- package/lib/cjs/index.js +21 -25
- package/lib/cjs/index.js.map +2 -2
- package/lib/cjs/kinds.js +4 -0
- package/lib/cjs/kinds.js.map +2 -2
- package/lib/cjs/nip04.js.map +1 -1
- package/lib/cjs/nip05.js +13 -25
- package/lib/cjs/nip05.js.map +2 -2
- package/lib/cjs/nip13.js.map +1 -1
- package/lib/cjs/nip18.js.map +1 -1
- package/lib/cjs/nip19.js +2 -0
- package/lib/cjs/nip19.js.map +2 -2
- package/lib/cjs/nip21.js.map +2 -2
- package/lib/cjs/nip25.js.map +1 -1
- package/lib/cjs/nip27.js.map +2 -2
- package/lib/cjs/nip28.js.map +1 -1
- package/lib/cjs/nip29.js +224 -10
- package/lib/cjs/nip29.js.map +3 -3
- package/lib/cjs/nip42.js.map +2 -2
- package/lib/cjs/nip46.js +906 -3
- package/lib/cjs/nip46.js.map +4 -4
- package/lib/cjs/nip47.js.map +2 -2
- package/lib/cjs/nip49.js +77 -0
- package/lib/cjs/nip49.js.map +7 -0
- package/lib/cjs/nip57.js.map +1 -1
- package/lib/cjs/nip94.js +136 -0
- package/lib/cjs/nip94.js.map +7 -0
- package/lib/cjs/nip96.js +252 -0
- package/lib/cjs/nip96.js.map +7 -0
- package/lib/cjs/nip98.js.map +2 -2
- package/lib/cjs/nip99.js.map +2 -2
- package/lib/cjs/pool.js +2 -0
- package/lib/cjs/pool.js.map +2 -2
- package/lib/cjs/pure.js.map +1 -1
- package/lib/cjs/references.js.map +2 -2
- package/lib/cjs/relay.js +2 -0
- package/lib/cjs/relay.js.map +2 -2
- package/lib/cjs/utils.js +2 -0
- package/lib/cjs/utils.js.map +2 -2
- package/lib/esm/abstract-pool.js +2 -0
- package/lib/esm/abstract-pool.js.map +2 -2
- package/lib/esm/abstract-relay.js +2 -0
- package/lib/esm/abstract-relay.js.map +2 -2
- package/lib/esm/filter.js.map +1 -1
- package/lib/esm/index.js +21 -25
- package/lib/esm/index.js.map +2 -2
- package/lib/esm/kinds.js +4 -0
- package/lib/esm/kinds.js.map +2 -2
- package/lib/esm/nip04.js.map +1 -1
- package/lib/esm/nip05.js +13 -25
- package/lib/esm/nip05.js.map +2 -2
- package/lib/esm/nip13.js.map +1 -1
- package/lib/esm/nip18.js.map +1 -1
- package/lib/esm/nip19.js +2 -0
- package/lib/esm/nip19.js.map +2 -2
- package/lib/esm/nip21.js.map +2 -2
- package/lib/esm/nip25.js.map +1 -1
- package/lib/esm/nip27.js.map +2 -2
- package/lib/esm/nip28.js.map +1 -1
- package/lib/esm/nip29.js +222 -10
- package/lib/esm/nip29.js.map +3 -3
- package/lib/esm/nip42.js.map +2 -2
- package/lib/esm/nip46.js +904 -3
- package/lib/esm/nip46.js.map +4 -4
- package/lib/esm/nip47.js.map +2 -2
- package/lib/esm/nip49.js +56 -0
- package/lib/esm/nip49.js.map +7 -0
- package/lib/esm/nip57.js.map +1 -1
- package/lib/esm/nip94.js +113 -0
- package/lib/esm/nip94.js.map +7 -0
- package/lib/esm/nip96.js +229 -0
- package/lib/esm/nip96.js.map +7 -0
- package/lib/esm/nip98.js.map +2 -2
- package/lib/esm/nip99.js.map +2 -2
- package/lib/esm/pool.js +2 -0
- package/lib/esm/pool.js.map +2 -2
- package/lib/esm/pure.js.map +1 -1
- package/lib/esm/references.js.map +2 -2
- package/lib/esm/relay.js +2 -0
- package/lib/esm/relay.js.map +2 -2
- package/lib/esm/utils.js +2 -0
- package/lib/esm/utils.js.map +2 -2
- package/lib/nostr.bundle.js +21 -25
- package/lib/nostr.bundle.js.map +2 -2
- package/lib/types/abstract-relay.d.ts +0 -1
- package/lib/types/kinds.d.ts +2 -0
- package/lib/types/nip05.d.ts +1 -9
- package/lib/types/nip19.d.ts +2 -0
- package/lib/types/nip29.d.ts +18 -3
- package/lib/types/nip46.d.ts +83 -2
- package/lib/types/nip49.d.ts +2 -0
- package/lib/types/nip49.test.d.ts +1 -0
- package/lib/types/nip94.d.ts +83 -0
- package/lib/types/nip94.test.d.ts +1 -0
- package/lib/types/nip96.d.ts +254 -0
- package/lib/types/nip96.test.d.ts +1 -0
- package/lib/types/pure.d.ts +1 -1
- package/lib/types/test-helpers.d.ts +8 -4
- package/lib/types/utils.d.ts +0 -1
- package/package.json +38 -8
package/README.md
CHANGED
|
@@ -274,3 +274,7 @@ To develop `nostr-tools`, install [`just`](https://just.systems/) and run `just
|
|
|
274
274
|
## License
|
|
275
275
|
|
|
276
276
|
This is free and unencumbered software released into the public domain. By submitting patches to this project, you agree to dedicate any and all copyright interest in this software to the public domain.
|
|
277
|
+
|
|
278
|
+
## Contributing to this repository
|
|
279
|
+
|
|
280
|
+
Use NIP-34 to send your patches to `naddr1qq9kummnw3ez6ar0dak8xqg5waehxw309aex2mrp0yhxummnw3ezucn8qyt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueqpzemhxue69uhhyetvv9ujuurjd9kkzmpwdejhgq3q80cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsxpqqqpmejdv00jq`.
|
package/lib/cjs/abstract-pool.js
CHANGED
|
@@ -89,6 +89,8 @@ function getSubscriptionId(json) {
|
|
|
89
89
|
var utf8Decoder = new TextDecoder("utf-8");
|
|
90
90
|
var utf8Encoder = new TextEncoder();
|
|
91
91
|
function normalizeURL(url) {
|
|
92
|
+
if (url.indexOf("://") === -1)
|
|
93
|
+
url = "wss://" + url;
|
|
92
94
|
let p = new URL(url);
|
|
93
95
|
p.pathname = p.pathname.replace(/\/+/g, "/");
|
|
94
96
|
if (p.pathname.endsWith("/"))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../abstract-pool.ts", "../../kinds.ts", "../../filter.ts", "../../fakejson.ts", "../../utils.ts", "../../nip42.ts", "../../core.ts", "../../helpers.ts", "../../abstract-relay.ts"],
|
|
4
|
-
"sourcesContent": ["import { AbstractRelay as AbstractRelay, SubscriptionParams, Subscription } from './abstract-relay.ts'\nimport { normalizeURL } from './utils.ts'\n\nimport type { Event, Nostr } from './core.ts'\nimport { type Filter } from './filter.ts'\nimport { alwaysTrue } from './helpers.ts'\n\nexport type SubCloser = { close: () => void }\n\nexport type SubscribeManyParams = Omit<SubscriptionParams, 'onclose' | 'id'> & {\n maxWait?: number\n onclose?: (reasons: string[]) => void\n id?: string\n}\n\nexport class AbstractSimplePool {\n private relays = new Map<string, AbstractRelay>()\n public seenOn = new Map<string, Set<AbstractRelay>>()\n public trackRelays: boolean = false\n\n public verifyEvent: Nostr['verifyEvent']\n public trustedRelayURLs = new Set<string>()\n\n constructor(opts: { verifyEvent: Nostr['verifyEvent'] }) {\n this.verifyEvent = opts.verifyEvent\n }\n\n async ensureRelay(url: string, params?: { connectionTimeout?: number }): Promise<AbstractRelay> {\n url = normalizeURL(url)\n\n let relay = this.relays.get(url)\n if (!relay) {\n relay = new AbstractRelay(url, {\n verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,\n })\n if (params?.connectionTimeout) relay.connectionTimeout = params.connectionTimeout\n this.relays.set(url, relay)\n }\n await relay.connect()\n\n return relay\n }\n\n close(relays: string[]) {\n relays.map(normalizeURL).forEach(url => {\n this.relays.get(url)?.close()\n })\n }\n\n subscribeMany(relays: string[], filters: Filter[], params: SubscribeManyParams): SubCloser {\n if (this.trackRelays) {\n params.receivedEvent = (relay: AbstractRelay, id: string) => {\n let set = this.seenOn.get(id)\n if (!set) {\n set = new Set()\n this.seenOn.set(id, set)\n }\n set.add(relay)\n }\n }\n\n const _knownIds = new Set<string>()\n const subs: Subscription[] = []\n\n // batch all EOSEs into a single\n const eosesReceived: boolean[] = []\n let handleEose = (i: number) => {\n eosesReceived[i] = true\n if (eosesReceived.filter(a => a).length === relays.length) {\n params.oneose?.()\n handleEose = () => {}\n }\n }\n // batch all closes into a single\n const closesReceived: string[] = []\n let handleClose = (i: number, reason: string) => {\n handleEose(i)\n closesReceived[i] = reason\n if (closesReceived.filter(a => a).length === relays.length) {\n params.onclose?.(closesReceived)\n handleClose = () => {}\n }\n }\n\n const localAlreadyHaveEventHandler = (id: string) => {\n if (params.alreadyHaveEvent?.(id)) {\n return true\n }\n const have = _knownIds.has(id)\n _knownIds.add(id)\n return have\n }\n\n // open a subscription in all given relays\n const allOpened = Promise.all(\n relays.map(normalizeURL).map(async (url, i, arr) => {\n if (arr.indexOf(url) !== i) {\n // duplicate\n handleClose(i, 'duplicate url')\n return\n }\n\n let relay: AbstractRelay\n try {\n relay = await this.ensureRelay(url, {\n connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1000) : undefined,\n })\n } catch (err) {\n handleClose(i, (err as any)?.message || String(err))\n return\n }\n\n let subscription = relay.subscribe(filters, {\n ...params,\n oneose: () => handleEose(i),\n onclose: reason => handleClose(i, reason),\n alreadyHaveEvent: localAlreadyHaveEventHandler,\n eoseTimeout: params.maxWait,\n })\n\n subs.push(subscription)\n }),\n )\n\n return {\n async close() {\n await allOpened\n subs.forEach(sub => {\n sub.close()\n })\n },\n }\n }\n\n subscribeManyEose(\n relays: string[],\n filters: Filter[],\n params: Pick<SubscribeManyParams, 'id' | 'onevent' | 'onclose' | 'maxWait'>,\n ): SubCloser {\n const subcloser = this.subscribeMany(relays, filters, {\n ...params,\n oneose() {\n subcloser.close()\n },\n })\n return subcloser\n }\n\n async querySync(\n relays: string[],\n filter: Filter,\n params?: Pick<SubscribeManyParams, 'id' | 'maxWait'>,\n ): Promise<Event[]> {\n return new Promise(async resolve => {\n const events: Event[] = []\n this.subscribeManyEose(relays, [filter], {\n ...params,\n onevent(event: Event) {\n events.push(event)\n },\n onclose(_: string[]) {\n resolve(events)\n },\n })\n })\n }\n\n async get(\n relays: string[],\n filter: Filter,\n params?: Pick<SubscribeManyParams, 'id' | 'maxWait'>,\n ): Promise<Event | null> {\n filter.limit = 1\n const events = await this.querySync(relays, filter, params)\n events.sort((a, b) => b.created_at - a.created_at)\n return events[0] || null\n }\n\n publish(relays: string[], event: Event): Promise<string>[] {\n return relays.map(normalizeURL).map(async (url, i, arr) => {\n if (arr.indexOf(url) !== i) {\n // duplicate\n return Promise.reject('duplicate url')\n }\n\n let r = await this.ensureRelay(url)\n return r.publish(event)\n })\n }\n}\n", "/** 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 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 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", "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 interface Event {\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 NostrEvent = Event\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] === 'object') return false\n }\n }\n\n return true\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\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": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC2EO,IAAM,aAAa;;;AC7DnB,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,cAAc,IAAI,YAAY,OAAO;AAC3C,IAAM,cAAc,IAAI,YAAY;AAEpC,SAAS,aAAa,KAAqB;AAChD,MAAI,IAAI,IAAI,IAAI,GAAG;AACnB,IAAE,WAAW,EAAE,SAAS,QAAQ,QAAQ,GAAG;AAC3C,MAAI,EAAE,SAAS,SAAS,GAAG;AAAG,MAAE,WAAW,EAAE,SAAS,MAAM,GAAG,EAAE;AACjE,MAAK,EAAE,SAAS,QAAQ,EAAE,aAAa,SAAW,EAAE,SAAS,SAAS,EAAE,aAAa;AAAS,MAAE,OAAO;AACvG,IAAE,aAAa,KAAK;AACpB,IAAE,OAAO;AACT,SAAO,EAAE,SAAS;AACpB;AAgDO,IAAM,YAAN,MAAmB;AAAA,EACjB;AAAA,EACA,OAA4B;AAAA,EAC5B,OAA4B;AAAA,EAEnC,YAAY,SAAY;AACtB,SAAK,QAAQ;AAAA,EACf;AACF;AAEO,IAAM,QAAN,MAAe;AAAA,EACb;AAAA,EACA;AAAA,EAEP,cAAc;AACZ,SAAK,QAAQ;AACb,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,QAAQ,OAAmB;AACzB,UAAM,UAAU,IAAI,UAAU,KAAK;AACnC,QAAI,CAAC,KAAK,MAAM;AAEd,WAAK,QAAQ;AACb,WAAK,OAAO;AAAA,IACd,WAAW,KAAK,SAAS,KAAK,OAAO;AAEnC,WAAK,OAAO;AACZ,WAAK,KAAK,OAAO,KAAK;AACtB,WAAK,MAAM,OAAO;AAAA,IACpB,OAAO;AAEL,cAAQ,OAAO,KAAK;AACpB,WAAK,KAAK,OAAO;AACjB,WAAK,OAAO;AAAA,IACd;AACA,WAAO;AAAA,EACT;AAAA,EAEA,UAAoB;AAClB,QAAI,CAAC,KAAK;AAAO,aAAO;AAExB,QAAI,KAAK,UAAU,KAAK,MAAM;AAC5B,YAAMA,UAAS,KAAK;AACpB,WAAK,QAAQ;AACb,WAAK,OAAO;AACZ,aAAOA,QAAO;AAAA,IAChB;AAEA,UAAM,SAAS,KAAK;AACpB,SAAK,QAAQ,OAAO;AAEpB,WAAO,OAAO;AAAA,EAChB;AACF;;;AC7GO,SAAS,cAAc,UAAkB,WAAkC;AAChF,SAAO;AAAA,IACL,MAAM;AAAA,IACN,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,IACxC,MAAM;AAAA,MACJ,CAAC,SAAS,QAAQ;AAAA,MAClB,CAAC,aAAa,SAAS;AAAA,IACzB;AAAA,IACA,SAAS;AAAA,EACX;AACF;;;ACRO,IAAM,iBAAiB,OAAO,UAAU;;;ACN/C,eAAsB,cAAc;AAClC,SAAO,IAAI,QAAc,aAAW;AAClC,UAAM,KAAK,IAAI,eAAe;AAC9B,UAAM,UAAU,MAAM;AAEpB,SAAG,MAAM,oBAAoB,WAAW,OAAO;AAC/C,cAAQ;AAAA,IACV;AAEA,OAAG,MAAM,iBAAiB,WAAW,OAAO;AAC5C,OAAG,MAAM,YAAY,CAAC;AACtB,OAAG,MAAM,MAAM;AAAA,EACjB,CAAC;AACH;AAEO,IAAM,aAAmC,CAAC,MAAiC;AAChF,IAAE,kBAAkB;AACpB,SAAO;AACT;;;ACXO,IAAM,gBAAN,MAAoB;AAAA,EACT;AAAA,EACR,aAAsB;AAAA,EAEvB,UAA+B;AAAA,EAC/B,WAAkC,SAAO,QAAQ,MAAM,eAAe,KAAK,QAAQ,KAAK;AAAA,EAExF,kBAA0B;AAAA,EAC1B,oBAA4B;AAAA,EAC5B,WAAW,oBAAI,IAA0B;AAAA,EACxC;AAAA,EAEA;AAAA,EACA,oBAAoB,oBAAI,IAA2B;AAAA,EACnD,qBAAqB,oBAAI,IAAkC;AAAA,EAC3D;AAAA,EACA,uBAAuB,IAAI,MAAc;AAAA,EACzC,eAAe;AAAA,EACf;AAAA,EACA,SAAiB;AAAA,EACjB;AAAA,EAER,YAAY,KAAa,MAA6C;AACpE,SAAK,MAAM,aAAa,GAAG;AAC3B,SAAK,cAAc,KAAK;AAAA,EAC1B;AAAA,EAEA,aAAa,QAAQ,KAAa,MAA6C;AAC7E,UAAM,QAAQ,IAAI,cAAc,KAAK,IAAI;AACzC,UAAM,MAAM,QAAQ;AACpB,WAAO;AAAA,EACT;AAAA,EAEQ,sBAAsB,QAAgB;AAC5C,aAAS,CAAC,GAAG,GAAG,KAAK,KAAK,UAAU;AAClC,UAAI,MAAM,MAAM;AAAA,IAClB;AACA,SAAK,SAAS,MAAM;AAEpB,aAAS,CAAC,GAAG,EAAE,KAAK,KAAK,oBAAoB;AAC3C,SAAG,OAAO,IAAI,MAAM,MAAM,CAAC;AAAA,IAC7B;AACA,SAAK,mBAAmB,MAAM;AAE9B,aAAS,CAAC,GAAG,EAAE,KAAK,KAAK,mBAAmB;AAC1C,SAAG,OAAO,IAAI,MAAM,MAAM,CAAC;AAAA,IAC7B;AACA,SAAK,kBAAkB,MAAM;AAAA,EAC/B;AAAA,EAEA,IAAW,YAAqB;AAC9B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAa,UAAyB;AACpC,QAAI,KAAK;AAAmB,aAAO,KAAK;AAExC,SAAK,YAAY;AACjB,SAAK,oBAAoB,IAAI,QAAQ,CAAC,SAAS,WAAW;AACxD,WAAK,0BAA0B,WAAW,MAAM;AAC9C,eAAO,sBAAsB;AAC7B,aAAK,oBAAoB;AACzB,aAAK,UAAU;AACf,aAAK,sBAAsB,4BAA4B;AAAA,MACzD,GAAG,KAAK,iBAAiB;AAEzB,UAAI;AACF,aAAK,KAAK,IAAI,UAAU,KAAK,GAAG;AAAA,MAClC,SAAS,KAAP;AACA,eAAO,GAAG;AACV;AAAA,MACF;AAEA,WAAK,GAAG,SAAS,MAAM;AACrB,qBAAa,KAAK,uBAAuB;AACzC,aAAK,aAAa;AAClB,gBAAQ;AAAA,MACV;AAEA,WAAK,GAAG,UAAU,QAAM;AACtB,eAAQ,GAAW,OAAO;AAC1B,YAAI,KAAK,YAAY;AACnB,eAAK,UAAU;AACf,eAAK,sBAAsB,0BAA0B;AACrD,eAAK,aAAa;AAAA,QACpB;AAAA,MACF;AAEA,WAAK,GAAG,UAAU,YAAY;AAC5B,aAAK,oBAAoB;AACzB,aAAK,UAAU;AACf,aAAK,sBAAsB,yBAAyB;AACpD,aAAK,aAAa;AAAA,MACpB;AAEA,WAAK,GAAG,YAAY,KAAK,WAAW,KAAK,IAAI;AAAA,IAC/C,CAAC;AAED,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,WAAW;AACvB,SAAK,eAAe;AACpB,WAAO,MAAM;AACX,UAAI,UAAU,KAAK,WAAW,GAAG;AAC/B;AAAA,MACF;AACA,YAAM,YAAY;AAAA,IACpB;AACA,SAAK,eAAe;AAAA,EACtB;AAAA,EAEQ,aAAgC;AACtC,UAAM,OAAO,KAAK,qBAAqB,QAAQ;AAC/C,QAAI,CAAC,MAAM;AACT,aAAO;AAAA,IACT;AAEA,UAAM,QAAQ,kBAAkB,IAAI;AACpC,QAAI,OAAO;AACT,YAAM,KAAK,KAAK,SAAS,IAAI,KAAe;AAC5C,UAAI,CAAC,IAAI;AAEP;AAAA,MACF;AAKA,YAAM,KAAK,SAAS,MAAM,IAAI;AAC9B,YAAM,cAAc,GAAG,mBAAmB,EAAE;AAI5C,SAAG,gBAAgB,MAAM,EAAE;AAE3B,UAAI,aAAa;AAEf;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACF,UAAI,OAAO,KAAK,MAAM,IAAI;AAI1B,cAAQ,KAAK,IAAI;AAAA,QACf,KAAK,SAAS;AACZ,gBAAM,KAAK,KAAK,SAAS,IAAI,KAAK,EAAY;AAC9C,gBAAM,QAAQ,KAAK;AACnB,cAAI,KAAK,YAAY,KAAK,KAAK,aAAa,GAAG,SAAS,KAAK,GAAG;AAC9D,eAAG,QAAQ,KAAK;AAAA,UAClB;AACA;AAAA,QACF;AAAA,QACA,KAAK,SAAS;AACZ,gBAAM,KAAa,KAAK;AACxB,gBAAM,UAAU,KAAK;AACrB,gBAAM,KAAK,KAAK,kBAAkB,IAAI,EAAE;AACxC,cAAI,IAAI;AACN,eAAG,QAAQ,QAAQ,KAAK;AACxB,iBAAK,kBAAkB,OAAO,EAAE;AAAA,UAClC;AACA;AAAA,QACF;AAAA,QACA,KAAK,QAAQ;AACX,gBAAM,KAAK,KAAK,SAAS,IAAI,KAAK,EAAY;AAC9C,cAAI,CAAC;AAAI;AACT,aAAG,aAAa;AAChB;AAAA,QACF;AAAA,QACA,KAAK,MAAM;AACT,gBAAM,KAAa,KAAK;AACxB,gBAAM,KAAc,KAAK;AACzB,gBAAM,SAAiB,KAAK;AAC5B,gBAAM,KAAK,KAAK,mBAAmB,IAAI,EAAE;AACzC,cAAI;AAAI,eAAG,QAAQ,MAAM;AAAA;AACpB,eAAG,OAAO,IAAI,MAAM,MAAM,CAAC;AAChC,eAAK,mBAAmB,OAAO,EAAE;AACjC;AAAA,QACF;AAAA,QACA,KAAK,UAAU;AACb,gBAAM,KAAa,KAAK;AACxB,gBAAM,KAAK,KAAK,SAAS,IAAI,EAAE;AAC/B,cAAI,CAAC;AAAI;AACT,aAAG,SAAS;AACZ,aAAG,MAAM,KAAK,EAAY;AAC1B;AAAA,QACF;AAAA,QACA,KAAK;AACH,eAAK,SAAS,KAAK,EAAY;AAC/B;AAAA,QACF,KAAK,QAAQ;AACX,eAAK,YAAY,KAAK;AACtB;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,KAAP;AACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAa,KAAK,SAAiB;AACjC,QAAI,CAAC,KAAK;AAAmB,YAAM,IAAI,MAAM,8BAA8B;AAE3E,SAAK,kBAAkB,KAAK,MAAM;AAChC,WAAK,IAAI,KAAK,OAAO;AAAA,IACvB,CAAC;AAAA,EACH;AAAA,EAEA,MAAa,KAAK,eAA+D;AAC/E,QAAI,CAAC,KAAK;AAAW,YAAM,IAAI,MAAM,+CAA+C;AACpF,UAAM,MAAM,MAAM,cAAc,cAAc,KAAK,KAAK,KAAK,SAAS,CAAC;AACvE,UAAM,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AACnD,WAAK,mBAAmB,IAAI,IAAI,IAAI,EAAE,SAAS,OAAO,CAAC;AAAA,IACzD,CAAC;AACD,SAAK,KAAK,aAAa,KAAK,UAAU,GAAG,IAAI,GAAG;AAChD,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,QAAQ,OAA+B;AAClD,UAAM,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AACnD,WAAK,mBAAmB,IAAI,MAAM,IAAI,EAAE,SAAS,OAAO,CAAC;AAAA,IAC3D,CAAC;AACD,SAAK,KAAK,cAAc,KAAK,UAAU,KAAK,IAAI,GAAG;AACnD,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,MAAM,SAAmB,QAAiD;AACrF,SAAK;AACL,UAAM,KAAK,QAAQ,MAAM,WAAW,KAAK;AACzC,UAAM,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AACnD,WAAK,kBAAkB,IAAI,IAAI,EAAE,SAAS,OAAO,CAAC;AAAA,IACpD,CAAC;AACD,SAAK,KAAK,eAAe,KAAK,OAAO,KAAK,UAAU,OAAO,IAAI,GAAG;AAClE,WAAO;AAAA,EACT;AAAA,EAEO,UAAU,SAAmB,QAAmD;AACrF,UAAM,eAAe,KAAK,oBAAoB,SAAS,MAAM;AAC7D,iBAAa,KAAK;AAClB,WAAO;AAAA,EACT;AAAA,EAEO,oBAAoB,SAAmB,QAAqE;AACjH,SAAK;AACL,UAAM,KAAK,OAAO,MAAM,SAAS,KAAK;AACtC,UAAM,eAAe,IAAI,aAAa,MAAM,IAAI,SAAS,MAAM;AAC/D,SAAK,SAAS,IAAI,IAAI,YAAY;AAClC,WAAO;AAAA,EACT;AAAA,EAEO,QAAQ;AACb,SAAK,sBAAsB,+BAA+B;AAC1D,SAAK,aAAa;AAClB,SAAK,IAAI,MAAM;AAAA,EACjB;AAAA,EAIO,WAAW,IAAuB;AACvC,SAAK,qBAAqB,QAAQ,GAAG,IAAc;AACnD,QAAI,CAAC,KAAK,cAAc;AACtB,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AACF;AAEO,IAAM,eAAN,MAAmB;AAAA,EACR;AAAA,EACA;AAAA,EAET,SAAkB;AAAA,EAClB,QAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACC;AAAA,EAER,YAAY,OAAsB,IAAY,SAAmB,QAA4B;AAC3F,SAAK,QAAQ;AACb,SAAK,UAAU;AACf,SAAK,KAAK;AACV,SAAK,mBAAmB,OAAO;AAC/B,SAAK,gBAAgB,OAAO;AAC5B,SAAK,cAAc,OAAO,eAAe,MAAM;AAE/C,SAAK,SAAS,OAAO;AACrB,SAAK,UAAU,OAAO;AACtB,SAAK,UACH,OAAO,YACN,WAAS;AACR,cAAQ;AAAA,QACN,oDAAoD,KAAK,gBAAgB,KAAK,MAAM;AAAA,QACpF;AAAA,MACF;AAAA,IACF;AAAA,EACJ;AAAA,EAEO,OAAO;AACZ,SAAK,MAAM,KAAK,aAAa,KAAK,KAAK,OAAO,KAAK,UAAU,KAAK,OAAO,EAAE,UAAU,CAAC,CAAC;AAGvF,SAAK,oBAAoB,WAAW,KAAK,aAAa,KAAK,IAAI,GAAG,KAAK,WAAW;AAAA,EACpF;AAAA,EAEO,eAAe;AACpB,QAAI,KAAK;AAAO;AAChB,iBAAa,KAAK,iBAAiB;AACnC,SAAK,QAAQ;AACb,SAAK,SAAS;AAAA,EAChB;AAAA,EAEO,MAAM,SAAiB,oBAAoB;AAChD,QAAI,CAAC,KAAK,QAAQ;AAGhB,WAAK,MAAM,KAAK,cAAc,KAAK,UAAU,KAAK,EAAE,IAAI,GAAG;AAC3D,WAAK,SAAS;AAAA,IAChB;AACA,SAAK,MAAM,SAAS,OAAO,KAAK,EAAE;AAClC,SAAK,UAAU,MAAM;AAAA,EACvB;AACF;;;ARpUO,IAAM,qBAAN,MAAyB;AAAA,EACtB,SAAS,oBAAI,IAA2B;AAAA,EACzC,SAAS,oBAAI,IAAgC;AAAA,EAC7C,cAAuB;AAAA,EAEvB;AAAA,EACA,mBAAmB,oBAAI,IAAY;AAAA,EAE1C,YAAY,MAA6C;AACvD,SAAK,cAAc,KAAK;AAAA,EAC1B;AAAA,EAEA,MAAM,YAAY,KAAa,QAAiE;AAC9F,UAAM,aAAa,GAAG;AAEtB,QAAI,QAAQ,KAAK,OAAO,IAAI,GAAG;AAC/B,QAAI,CAAC,OAAO;AACV,cAAQ,IAAI,cAAc,KAAK;AAAA,QAC7B,aAAa,KAAK,iBAAiB,IAAI,GAAG,IAAI,aAAa,KAAK;AAAA,MAClE,CAAC;AACD,UAAI,QAAQ;AAAmB,cAAM,oBAAoB,OAAO;AAChE,WAAK,OAAO,IAAI,KAAK,KAAK;AAAA,IAC5B;AACA,UAAM,MAAM,QAAQ;AAEpB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAkB;AACtB,WAAO,IAAI,YAAY,EAAE,QAAQ,SAAO;AACtC,WAAK,OAAO,IAAI,GAAG,GAAG,MAAM;AAAA,IAC9B,CAAC;AAAA,EACH;AAAA,EAEA,cAAc,QAAkB,SAAmB,QAAwC;AACzF,QAAI,KAAK,aAAa;AACpB,aAAO,gBAAgB,CAAC,OAAsB,OAAe;AAC3D,YAAI,MAAM,KAAK,OAAO,IAAI,EAAE;AAC5B,YAAI,CAAC,KAAK;AACR,gBAAM,oBAAI,IAAI;AACd,eAAK,OAAO,IAAI,IAAI,GAAG;AAAA,QACzB;AACA,YAAI,IAAI,KAAK;AAAA,MACf;AAAA,IACF;AAEA,UAAM,YAAY,oBAAI,IAAY;AAClC,UAAM,OAAuB,CAAC;AAG9B,UAAM,gBAA2B,CAAC;AAClC,QAAI,aAAa,CAAC,MAAc;AAC9B,oBAAc,KAAK;AACnB,UAAI,cAAc,OAAO,OAAK,CAAC,EAAE,WAAW,OAAO,QAAQ;AACzD,eAAO,SAAS;AAChB,qBAAa,MAAM;AAAA,QAAC;AAAA,MACtB;AAAA,IACF;AAEA,UAAM,iBAA2B,CAAC;AAClC,QAAI,cAAc,CAAC,GAAW,WAAmB;AAC/C,iBAAW,CAAC;AACZ,qBAAe,KAAK;AACpB,UAAI,eAAe,OAAO,OAAK,CAAC,EAAE,WAAW,OAAO,QAAQ;AAC1D,eAAO,UAAU,cAAc;AAC/B,sBAAc,MAAM;AAAA,QAAC;AAAA,MACvB;AAAA,IACF;AAEA,UAAM,+BAA+B,CAAC,OAAe;AACnD,UAAI,OAAO,mBAAmB,EAAE,GAAG;AACjC,eAAO;AAAA,MACT;AACA,YAAM,OAAO,UAAU,IAAI,EAAE;AAC7B,gBAAU,IAAI,EAAE;AAChB,aAAO;AAAA,IACT;AAGA,UAAM,YAAY,QAAQ;AAAA,MACxB,OAAO,IAAI,YAAY,EAAE,IAAI,OAAO,KAAK,GAAG,QAAQ;AAClD,YAAI,IAAI,QAAQ,GAAG,MAAM,GAAG;AAE1B,sBAAY,GAAG,eAAe;AAC9B;AAAA,QACF;AAEA,YAAI;AACJ,YAAI;AACF,kBAAQ,MAAM,KAAK,YAAY,KAAK;AAAA,YAClC,mBAAmB,OAAO,UAAU,KAAK,IAAI,OAAO,UAAU,KAAK,OAAO,UAAU,GAAI,IAAI;AAAA,UAC9F,CAAC;AAAA,QACH,SAAS,KAAP;AACA,sBAAY,GAAI,KAAa,WAAW,OAAO,GAAG,CAAC;AACnD;AAAA,QACF;AAEA,YAAI,eAAe,MAAM,UAAU,SAAS;AAAA,UAC1C,GAAG;AAAA,UACH,QAAQ,MAAM,WAAW,CAAC;AAAA,UAC1B,SAAS,YAAU,YAAY,GAAG,MAAM;AAAA,UACxC,kBAAkB;AAAA,UAClB,aAAa,OAAO;AAAA,QACtB,CAAC;AAED,aAAK,KAAK,YAAY;AAAA,MACxB,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,MACL,MAAM,QAAQ;AACZ,cAAM;AACN,aAAK,QAAQ,SAAO;AAClB,cAAI,MAAM;AAAA,QACZ,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA,EAEA,kBACE,QACA,SACA,QACW;AACX,UAAM,YAAY,KAAK,cAAc,QAAQ,SAAS;AAAA,MACpD,GAAG;AAAA,MACH,SAAS;AACP,kBAAU,MAAM;AAAA,MAClB;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,UACJ,QACA,QACA,QACkB;AAClB,WAAO,IAAI,QAAQ,OAAM,YAAW;AAClC,YAAM,SAAkB,CAAC;AACzB,WAAK,kBAAkB,QAAQ,CAAC,MAAM,GAAG;AAAA,QACvC,GAAG;AAAA,QACH,QAAQ,OAAc;AACpB,iBAAO,KAAK,KAAK;AAAA,QACnB;AAAA,QACA,QAAQ,GAAa;AACnB,kBAAQ,MAAM;AAAA,QAChB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,IACJ,QACA,QACA,QACuB;AACvB,WAAO,QAAQ;AACf,UAAM,SAAS,MAAM,KAAK,UAAU,QAAQ,QAAQ,MAAM;AAC1D,WAAO,KAAK,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,UAAU;AACjD,WAAO,OAAO,MAAM;AAAA,EACtB;AAAA,EAEA,QAAQ,QAAkB,OAAiC;AACzD,WAAO,OAAO,IAAI,YAAY,EAAE,IAAI,OAAO,KAAK,GAAG,QAAQ;AACzD,UAAI,IAAI,QAAQ,GAAG,MAAM,GAAG;AAE1B,eAAO,QAAQ,OAAO,eAAe;AAAA,MACvC;AAEA,UAAI,IAAI,MAAM,KAAK,YAAY,GAAG;AAClC,aAAO,EAAE,QAAQ,KAAK;AAAA,IACxB,CAAC;AAAA,EACH;AACF;",
|
|
4
|
+
"sourcesContent": ["import { AbstractRelay as AbstractRelay, SubscriptionParams, Subscription } from './abstract-relay.ts'\nimport { normalizeURL } from './utils.ts'\n\nimport type { Event, Nostr } from './core.ts'\nimport { type Filter } from './filter.ts'\nimport { alwaysTrue } from './helpers.ts'\n\nexport type SubCloser = { close: () => void }\n\nexport type SubscribeManyParams = Omit<SubscriptionParams, 'onclose' | 'id'> & {\n maxWait?: number\n onclose?: (reasons: string[]) => void\n id?: string\n}\n\nexport class AbstractSimplePool {\n private relays = new Map<string, AbstractRelay>()\n public seenOn = new Map<string, Set<AbstractRelay>>()\n public trackRelays: boolean = false\n\n public verifyEvent: Nostr['verifyEvent']\n public trustedRelayURLs = new Set<string>()\n\n constructor(opts: { verifyEvent: Nostr['verifyEvent'] }) {\n this.verifyEvent = opts.verifyEvent\n }\n\n async ensureRelay(url: string, params?: { connectionTimeout?: number }): Promise<AbstractRelay> {\n url = normalizeURL(url)\n\n let relay = this.relays.get(url)\n if (!relay) {\n relay = new AbstractRelay(url, {\n verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,\n })\n if (params?.connectionTimeout) relay.connectionTimeout = params.connectionTimeout\n this.relays.set(url, relay)\n }\n await relay.connect()\n\n return relay\n }\n\n close(relays: string[]) {\n relays.map(normalizeURL).forEach(url => {\n this.relays.get(url)?.close()\n })\n }\n\n subscribeMany(relays: string[], filters: Filter[], params: SubscribeManyParams): SubCloser {\n if (this.trackRelays) {\n params.receivedEvent = (relay: AbstractRelay, id: string) => {\n let set = this.seenOn.get(id)\n if (!set) {\n set = new Set()\n this.seenOn.set(id, set)\n }\n set.add(relay)\n }\n }\n\n const _knownIds = new Set<string>()\n const subs: Subscription[] = []\n\n // batch all EOSEs into a single\n const eosesReceived: boolean[] = []\n let handleEose = (i: number) => {\n eosesReceived[i] = true\n if (eosesReceived.filter(a => a).length === relays.length) {\n params.oneose?.()\n handleEose = () => {}\n }\n }\n // batch all closes into a single\n const closesReceived: string[] = []\n let handleClose = (i: number, reason: string) => {\n handleEose(i)\n closesReceived[i] = reason\n if (closesReceived.filter(a => a).length === relays.length) {\n params.onclose?.(closesReceived)\n handleClose = () => {}\n }\n }\n\n const localAlreadyHaveEventHandler = (id: string) => {\n if (params.alreadyHaveEvent?.(id)) {\n return true\n }\n const have = _knownIds.has(id)\n _knownIds.add(id)\n return have\n }\n\n // open a subscription in all given relays\n const allOpened = Promise.all(\n relays.map(normalizeURL).map(async (url, i, arr) => {\n if (arr.indexOf(url) !== i) {\n // duplicate\n handleClose(i, 'duplicate url')\n return\n }\n\n let relay: AbstractRelay\n try {\n relay = await this.ensureRelay(url, {\n connectionTimeout: params.maxWait ? Math.max(params.maxWait * 0.8, params.maxWait - 1000) : undefined,\n })\n } catch (err) {\n handleClose(i, (err as any)?.message || String(err))\n return\n }\n\n let subscription = relay.subscribe(filters, {\n ...params,\n oneose: () => handleEose(i),\n onclose: reason => handleClose(i, reason),\n alreadyHaveEvent: localAlreadyHaveEventHandler,\n eoseTimeout: params.maxWait,\n })\n\n subs.push(subscription)\n }),\n )\n\n return {\n async close() {\n await allOpened\n subs.forEach(sub => {\n sub.close()\n })\n },\n }\n }\n\n subscribeManyEose(\n relays: string[],\n filters: Filter[],\n params: Pick<SubscribeManyParams, 'id' | 'onevent' | 'onclose' | 'maxWait'>,\n ): SubCloser {\n const subcloser = this.subscribeMany(relays, filters, {\n ...params,\n oneose() {\n subcloser.close()\n },\n })\n return subcloser\n }\n\n async querySync(\n relays: string[],\n filter: Filter,\n params?: Pick<SubscribeManyParams, 'id' | 'maxWait'>,\n ): Promise<Event[]> {\n return new Promise(async resolve => {\n const events: Event[] = []\n this.subscribeManyEose(relays, [filter], {\n ...params,\n onevent(event: Event) {\n events.push(event)\n },\n onclose(_: string[]) {\n resolve(events)\n },\n })\n })\n }\n\n async get(\n relays: string[],\n filter: Filter,\n params?: Pick<SubscribeManyParams, 'id' | 'maxWait'>,\n ): Promise<Event | null> {\n filter.limit = 1\n const events = await this.querySync(relays, filter, params)\n events.sort((a, b) => b.created_at - a.created_at)\n return events[0] || null\n }\n\n publish(relays: string[], event: Event): Promise<string>[] {\n return relays.map(normalizeURL).map(async (url, i, arr) => {\n if (arr.indexOf(url) !== i) {\n // duplicate\n return Promise.reject('duplicate url')\n }\n\n let r = await this.ensureRelay(url)\n return r.publish(event)\n })\n }\n}\n", "/** 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 NostrConnectAdmin = 24134\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", "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 interface Event {\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 NostrEvent = Event\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] === 'object') return false\n }\n }\n\n return true\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\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": "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",
|
|
6
6
|
"names": ["target"]
|
|
7
7
|
}
|
|
@@ -90,6 +90,8 @@ function getSubscriptionId(json) {
|
|
|
90
90
|
var utf8Decoder = new TextDecoder("utf-8");
|
|
91
91
|
var utf8Encoder = new TextEncoder();
|
|
92
92
|
function normalizeURL(url) {
|
|
93
|
+
if (url.indexOf("://") === -1)
|
|
94
|
+
url = "wss://" + url;
|
|
93
95
|
let p = new URL(url);
|
|
94
96
|
p.pathname = p.pathname.replace(/\/+/g, "/");
|
|
95
97
|
if (p.pathname.endsWith("/"))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../abstract-relay.ts", "../../kinds.ts", "../../filter.ts", "../../fakejson.ts", "../../utils.ts", "../../nip42.ts", "../../helpers.ts"],
|
|
4
|
-
"sourcesContent": ["/* 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\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", "/** 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 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 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"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;
|
|
4
|
+
"sourcesContent": ["/* 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\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", "/** 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 NostrConnectAdmin = 24134\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"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["target"]
|
|
7
7
|
}
|
package/lib/cjs/filter.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../filter.ts", "../../kinds.ts"],
|
|
4
|
-
"sourcesContent": ["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", "/** 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 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"],
|
|
4
|
+
"sourcesContent": ["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", "/** 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 NostrConnectAdmin = 24134\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"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/lib/cjs/index.js
CHANGED
|
@@ -115,6 +115,8 @@ __export(utils_exports, {
|
|
|
115
115
|
var utf8Decoder = new TextDecoder("utf-8");
|
|
116
116
|
var utf8Encoder = new TextEncoder();
|
|
117
117
|
function normalizeURL(url) {
|
|
118
|
+
if (url.indexOf("://") === -1)
|
|
119
|
+
url = "wss://" + url;
|
|
118
120
|
let p = new URL(url);
|
|
119
121
|
p.pathname = p.pathname.replace(/\/+/g, "/");
|
|
120
122
|
if (p.pathname.endsWith("/"))
|
|
@@ -296,6 +298,7 @@ __export(kinds_exports, {
|
|
|
296
298
|
EncryptedDirectMessages: () => EncryptedDirectMessages,
|
|
297
299
|
EventDeletion: () => EventDeletion,
|
|
298
300
|
FileMetadata: () => FileMetadata,
|
|
301
|
+
FileServerPreference: () => FileServerPreference,
|
|
299
302
|
Followsets: () => Followsets,
|
|
300
303
|
GenericRepost: () => GenericRepost,
|
|
301
304
|
Genericlists: () => Genericlists,
|
|
@@ -319,6 +322,7 @@ __export(kinds_exports, {
|
|
|
319
322
|
NWCWalletRequest: () => NWCWalletRequest,
|
|
320
323
|
NWCWalletResponse: () => NWCWalletResponse,
|
|
321
324
|
NostrConnect: () => NostrConnect,
|
|
325
|
+
NostrConnectAdmin: () => NostrConnectAdmin,
|
|
322
326
|
OpenTimestamps: () => OpenTimestamps,
|
|
323
327
|
Pinlist: () => Pinlist,
|
|
324
328
|
ProblemTracker: () => ProblemTracker,
|
|
@@ -409,12 +413,14 @@ var BlockedRelaysList = 10006;
|
|
|
409
413
|
var SearchRelaysList = 10007;
|
|
410
414
|
var InterestsList = 10015;
|
|
411
415
|
var UserEmojiList = 10030;
|
|
416
|
+
var FileServerPreference = 10096;
|
|
412
417
|
var NWCWalletInfo = 13194;
|
|
413
418
|
var LightningPubRPC = 21e3;
|
|
414
419
|
var ClientAuth = 22242;
|
|
415
420
|
var NWCWalletRequest = 23194;
|
|
416
421
|
var NWCWalletResponse = 23195;
|
|
417
422
|
var NostrConnect = 24133;
|
|
423
|
+
var NostrConnectAdmin = 24134;
|
|
418
424
|
var HTTPAuth = 27235;
|
|
419
425
|
var Followsets = 3e4;
|
|
420
426
|
var Genericlists = 30001;
|
|
@@ -1046,7 +1052,9 @@ var SimplePool = class extends AbstractSimplePool {
|
|
|
1046
1052
|
var nip19_exports = {};
|
|
1047
1053
|
__export(nip19_exports, {
|
|
1048
1054
|
BECH32_REGEX: () => BECH32_REGEX,
|
|
1055
|
+
Bech32MaxSize: () => Bech32MaxSize,
|
|
1049
1056
|
decode: () => decode,
|
|
1057
|
+
encodeBytes: () => encodeBytes,
|
|
1050
1058
|
naddrEncode: () => naddrEncode,
|
|
1051
1059
|
neventEncode: () => neventEncode,
|
|
1052
1060
|
noteEncode: () => noteEncode,
|
|
@@ -1360,11 +1368,12 @@ function getNormalizedX(key) {
|
|
|
1360
1368
|
var nip05_exports = {};
|
|
1361
1369
|
__export(nip05_exports, {
|
|
1362
1370
|
NIP05_REGEX: () => NIP05_REGEX,
|
|
1371
|
+
isValid: () => isValid,
|
|
1363
1372
|
queryProfile: () => queryProfile,
|
|
1364
1373
|
searchDomain: () => searchDomain,
|
|
1365
1374
|
useFetchImplementation: () => useFetchImplementation
|
|
1366
1375
|
});
|
|
1367
|
-
var NIP05_REGEX = /^(?:([\w.+-]+)@)?([\
|
|
1376
|
+
var NIP05_REGEX = /^(?:([\w.+-]+)@)?([\w_-]+(\.[\w_-]+)+)$/;
|
|
1368
1377
|
var _fetch;
|
|
1369
1378
|
try {
|
|
1370
1379
|
_fetch = fetch;
|
|
@@ -1375,8 +1384,10 @@ function useFetchImplementation(fetchImplementation) {
|
|
|
1375
1384
|
}
|
|
1376
1385
|
async function searchDomain(domain, query = "") {
|
|
1377
1386
|
try {
|
|
1378
|
-
|
|
1379
|
-
|
|
1387
|
+
const url = `https://${domain}/.well-known/nostr.json?name=${query}`;
|
|
1388
|
+
const res = await _fetch(url, { redirect: "error" });
|
|
1389
|
+
const json = await res.json();
|
|
1390
|
+
return json.names;
|
|
1380
1391
|
} catch (_) {
|
|
1381
1392
|
return {};
|
|
1382
1393
|
}
|
|
@@ -1387,32 +1398,17 @@ async function queryProfile(fullname) {
|
|
|
1387
1398
|
return null;
|
|
1388
1399
|
const [_, name = "_", domain] = match;
|
|
1389
1400
|
try {
|
|
1390
|
-
const
|
|
1391
|
-
const
|
|
1392
|
-
|
|
1393
|
-
return pubkey ? { pubkey, relays: relays?.[pubkey] } : null;
|
|
1401
|
+
const url = `https://${domain}/.well-known/nostr.json?name=${name}`;
|
|
1402
|
+
const res = await (await _fetch(url, { redirect: "error" })).json();
|
|
1403
|
+
let pubkey = res.names[name];
|
|
1404
|
+
return pubkey ? { pubkey, relays: res.relays?.[pubkey] } : null;
|
|
1394
1405
|
} catch (_e) {
|
|
1395
1406
|
return null;
|
|
1396
1407
|
}
|
|
1397
1408
|
}
|
|
1398
|
-
function
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
};
|
|
1402
|
-
for (const [name, pubkey] of Object.entries(json.names)) {
|
|
1403
|
-
if (typeof name === "string" && typeof pubkey === "string") {
|
|
1404
|
-
result.names[name] = pubkey;
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
if (json.relays) {
|
|
1408
|
-
result.relays = {};
|
|
1409
|
-
for (const [pubkey, relays] of Object.entries(json.relays)) {
|
|
1410
|
-
if (typeof pubkey === "string" && Array.isArray(relays)) {
|
|
1411
|
-
result.relays[pubkey] = relays.filter((relay) => typeof relay === "string");
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
return result;
|
|
1409
|
+
async function isValid(pubkey, nip05) {
|
|
1410
|
+
let res = await queryProfile(nip05);
|
|
1411
|
+
return res ? res.pubkey === pubkey : false;
|
|
1416
1412
|
}
|
|
1417
1413
|
|
|
1418
1414
|
// nip10.ts
|