From 185e089ffdc81f8d2d67ae913a0a6a298344f552 Mon Sep 17 00:00:00 2001 From: Kieran Date: Mon, 8 May 2023 17:55:46 +0100 Subject: [PATCH 01/24] feat: gossip model --- packages/app/src/Cache/FeedCache.ts | 31 +++++ packages/app/src/Cache/UserCache.ts | 47 ++------ packages/app/src/Cache/UserRelayCache.ts | 18 +++ packages/app/src/Cache/index.ts | 2 + packages/app/src/Feed/LoginFeed.ts | 14 ++- packages/app/src/Feed/RelaysFeedFollows.tsx | 99 ++++++++-------- .../app/src/Hooks/useRelaysForFollows.tsx | 65 ----------- packages/app/src/State/Relays/index.ts | 83 ------------- packages/app/src/System/GossipModel.ts | 109 ++++++++++++++++++ packages/app/src/System/Query.ts | 1 - packages/app/src/System/index.ts | 56 +++++++-- 11 files changed, 275 insertions(+), 250 deletions(-) create mode 100644 packages/app/src/Cache/UserRelayCache.ts delete mode 100644 packages/app/src/Hooks/useRelaysForFollows.tsx delete mode 100644 packages/app/src/State/Relays/index.ts create mode 100644 packages/app/src/System/GossipModel.ts diff --git a/packages/app/src/Cache/FeedCache.ts b/packages/app/src/Cache/FeedCache.ts index 6300ebeb..10feed3a 100644 --- a/packages/app/src/Cache/FeedCache.ts +++ b/packages/app/src/Cache/FeedCache.ts @@ -118,6 +118,37 @@ export default abstract class FeedCache { this.notifyChange(obj.map(a => this.key(a))); } + /** + * Try to update an entry where created values exists + * @param m Profile metadata + * @returns + */ + async update(m: TCachedWithCreated) { + const k = this.key(m); + const existing = this.getFromCache(k) as TCachedWithCreated; + const updateType = (() => { + if (!existing) { + return "new"; + } + if (existing.created < m.created) { + return "updated"; + } + if (existing && existing.loaded < m.loaded) { + return "refresh"; + } + return "no_change"; + })(); + console.debug(`Updating ${k} ${updateType}`, m); + if (updateType !== "no_change") { + const updated = { + ...existing, + ...m, + }; + await this.set(updated); + } + return updateType; + } + /** * Loads a list of rows from disk cache * @param keys List of ids to load diff --git a/packages/app/src/Cache/UserCache.ts b/packages/app/src/Cache/UserCache.ts index ab6feade..e2b8b94a 100644 --- a/packages/app/src/Cache/UserCache.ts +++ b/packages/app/src/Cache/UserCache.ts @@ -53,35 +53,15 @@ class UserProfileCache extends FeedCache { * @param m Profile metadata * @returns */ - async update(m: MetadataCache) { - const existing = this.getFromCache(m.pubkey); - const updateType = (() => { - if (!existing) { - return "new_profile"; - } - if (existing.created < m.created) { - return "updated_profile"; - } - if (existing && existing.loaded < m.loaded) { - return "refresh_profile"; - } - return "no_change"; - })(); - console.debug(`Updating ${m.pubkey} ${updateType}`, m); - if (updateType !== "no_change") { - const writeProfile = { - ...existing, - ...m, - }; - await this.#setItem(writeProfile); - if (updateType !== "refresh_profile") { - const lnurl = m.lud16 ?? m.lud06; - if (lnurl) { - this.#zapperQueue.push({ - pubkey: m.pubkey, - lnurl, - }); - } + override async update(m: MetadataCache) { + const updateType = await super.update(m); + if (updateType !== "refresh") { + const lnurl = m.lud16 ?? m.lud06; + if (lnurl) { + this.#zapperQueue.push({ + pubkey: m.pubkey, + lnurl, + }); } if (m.nip05) { this.#nip5Queue.push({ @@ -97,15 +77,6 @@ class UserProfileCache extends FeedCache { return []; } - async #setItem(m: MetadataCache) { - this.cache.set(m.pubkey, m); - if (db.ready) { - await db.users.put(m); - this.onTable.add(m.pubkey); - } - this.notifyChange([m.pubkey]); - } - async #processZapperQueue() { await this.#batchQueue( this.#zapperQueue, diff --git a/packages/app/src/Cache/UserRelayCache.ts b/packages/app/src/Cache/UserRelayCache.ts new file mode 100644 index 00000000..2f0a6ede --- /dev/null +++ b/packages/app/src/Cache/UserRelayCache.ts @@ -0,0 +1,18 @@ +import { db, UsersRelays } from "Db"; +import FeedCache from "./FeedCache"; + +class UsersRelaysCache extends FeedCache { + constructor() { + super("UserRelays", db.userRelays); + } + + key(of: UsersRelays): string { + return of.pubkey; + } + + takeSnapshot(): Array { + return [...this.cache.values()]; + } +} + +export const UserRelays = new UsersRelaysCache(); diff --git a/packages/app/src/Cache/index.ts b/packages/app/src/Cache/index.ts index b8ac6bc4..cf1bd971 100644 --- a/packages/app/src/Cache/index.ts +++ b/packages/app/src/Cache/index.ts @@ -3,6 +3,7 @@ import { hexToBech32, unixNowMs } from "Util"; import { DmCache } from "./DMCache"; import { InteractionCache } from "./EventInteractionCache"; import { UserCache } from "./UserCache"; +import { UserRelays } from "./UserRelayCache"; export interface MetadataCache extends UserMetadata { /** @@ -55,6 +56,7 @@ export async function preload() { await UserCache.preload(); await DmCache.preload(); await InteractionCache.preload(); + await UserRelays.preload(); } export { UserCache, DmCache }; diff --git a/packages/app/src/Feed/LoginFeed.ts b/packages/app/src/Feed/LoginFeed.ts index 84066370..8f0be3c7 100644 --- a/packages/app/src/Feed/LoginFeed.ts +++ b/packages/app/src/Feed/LoginFeed.ts @@ -13,13 +13,15 @@ import useLogin from "Hooks/useLogin"; import { addSubscription, setBlocked, setBookmarked, setFollows, setMuted, setPinned, setRelays, setTags } from "Login"; import { SnortPubKey } from "Const"; import { SubscriptionEvent } from "Subscription"; +import useRelaysFeedFollows from "./RelaysFeedFollows"; +import { UserRelays } from "Cache/UserRelayCache"; /** * Managed loading data for the current logged in user */ export default function useLoginFeed() { const login = useLogin(); - const { publicKey: pubKey, readNotifications } = login; + const { publicKey: pubKey, readNotifications, follows } = login; const { isMuted } = useModeration(); const publisher = useEventPublisher(); @@ -171,8 +173,12 @@ export default function useLoginFeed() { } }, [listsFeed]); - /*const fRelays = useRelaysFeedFollows(follows); useEffect(() => { - FollowsRelays.bulkSet(fRelays).catch(console.error); - }, [dispatch, fRelays]);*/ + UserRelays.buffer(follows.item).catch(console.error); + }, [follows.item]); + + const fRelays = useRelaysFeedFollows(follows.item); + useEffect(() => { + UserRelays.bulkSet(fRelays).catch(console.error); + }, [fRelays]); } diff --git a/packages/app/src/Feed/RelaysFeedFollows.tsx b/packages/app/src/Feed/RelaysFeedFollows.tsx index 839fbd48..cf5ea5c5 100644 --- a/packages/app/src/Feed/RelaysFeedFollows.tsx +++ b/packages/app/src/Feed/RelaysFeedFollows.tsx @@ -5,69 +5,72 @@ import { sanitizeRelayUrl } from "Util"; import { PubkeyReplaceableNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; -type UserRelayMap = Record>; +interface RelayList { + pubkey: string; + created: number; + relays: FullRelaySettings[]; +} -export default function useRelaysFeedFollows(pubkeys: HexKey[]): UserRelayMap { +export default function useRelaysFeedFollows(pubkeys: HexKey[]): Array { const sub = useMemo(() => { const b = new RequestBuilder(`relays:follows`); b.withFilter().authors(pubkeys).kinds([EventKind.Relays, EventKind.ContactList]); return b; }, [pubkeys]); - function mapFromRelays(notes: Array): UserRelayMap { - return Object.fromEntries( - notes.map(ev => { - return [ - ev.pubkey, - ev.tags - .map(a => { - return { - url: sanitizeRelayUrl(a[1]), - settings: { - read: a[2] === "read" || a[2] === undefined, - write: a[2] === "write" || a[2] === undefined, - }, - } as FullRelaySettings; - }) - .filter(a => a.url !== undefined), - ]; - }) - ); + function mapFromRelays(notes: Array): Array { + return notes.map(ev => { + return { + pubkey: ev.pubkey, + created: ev.created_at, + relays: ev.tags + .map(a => { + return { + url: sanitizeRelayUrl(a[1]), + settings: { + read: a[2] === "read" || a[2] === undefined, + write: a[2] === "write" || a[2] === undefined, + }, + } as FullRelaySettings; + }) + .filter(a => a.url !== undefined), + }; + }); } - function mapFromContactList(notes: Array): UserRelayMap { - return Object.fromEntries( - notes.map(ev => { - if (ev.content !== "" && ev.content !== "{}" && ev.content.startsWith("{") && ev.content.endsWith("}")) { - try { - const relays: Record = JSON.parse(ev.content); - return [ - ev.pubkey, - Object.entries(relays) - .map(([k, v]) => { - return { - url: sanitizeRelayUrl(k), - settings: v, - } as FullRelaySettings; - }) - .filter(a => a.url !== undefined), - ]; - } catch { - // ignored - } + function mapFromContactList(notes: Array): Array { + return notes.map(ev => { + if (ev.content !== "" && ev.content !== "{}" && ev.content.startsWith("{") && ev.content.endsWith("}")) { + try { + const relays: Record = JSON.parse(ev.content); + return { + pubkey: ev.pubkey, + created: ev.created_at, + relays: Object.entries(relays) + .map(([k, v]) => { + return { + url: sanitizeRelayUrl(k), + settings: v, + } as FullRelaySettings; + }) + .filter(a => a.url !== undefined), + }; + } catch { + // ignored } - return [ev.pubkey, []]; - }) - ); + } + return { + pubkey: ev.pubkey, + created: 0, + relays: [], + }; + }); } const relays = useRequestBuilder(PubkeyReplaceableNoteStore, sub); const notesRelays = relays.data?.filter(a => a.kind === EventKind.Relays) ?? []; const notesContactLists = relays.data?.filter(a => a.kind === EventKind.ContactList) ?? []; return useMemo(() => { - return { - ...mapFromContactList(notesContactLists), - ...mapFromRelays(notesRelays), - } as UserRelayMap; + return [...mapFromContactList(notesContactLists), ...mapFromRelays(notesRelays)]; }, [relays]); } diff --git a/packages/app/src/Hooks/useRelaysForFollows.tsx b/packages/app/src/Hooks/useRelaysForFollows.tsx deleted file mode 100644 index bb14fce7..00000000 --- a/packages/app/src/Hooks/useRelaysForFollows.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { HexKey } from "@snort/nostr"; -import { useMemo } from "react"; -import { FollowsRelays } from "State/Relays"; -import { unwrap } from "Util"; - -export type RelayPicker = ReturnType; - -/** - * Number of relays to pick per pubkey - */ -const PickNRelays = 2; - -export default function useRelaysForFollows(keys: Array) { - return useMemo(() => { - if (keys.length === 0) { - return {}; - } - - const allRelays = keys.map(a => { - return { - key: a, - relays: FollowsRelays.snapshot.get(a), - }; - }); - - const missing = allRelays.filter(a => a.relays === undefined); - const hasRelays = allRelays.filter(a => a.relays !== undefined); - const relayUserMap = hasRelays.reduce((acc, v) => { - for (const r of unwrap(v.relays)) { - if (!acc.has(r.url)) { - acc.set(r.url, new Set([v.key])); - } else { - unwrap(acc.get(r.url)).add(v.key); - } - } - return acc; - }, new Map>()); - const topRelays = [...relayUserMap.entries()].sort(([, v], [, v1]) => v1.size - v.size); - - // - count keys per relay - // - pick n top relays - // - map keys per relay (for subscription filter) - - const userPickedRelays = keys.map(k => { - // pick top 3 relays for this key - const relaysForKey = topRelays - .filter(([, v]) => v.has(k)) - .slice(0, PickNRelays) - .map(([k]) => k); - return { k, relaysForKey }; - }); - - const pickedRelays = new Set(userPickedRelays.map(a => a.relaysForKey).flat()); - - const picked = Object.fromEntries( - [...pickedRelays].map(a => { - const keysOnPickedRelay = new Set(userPickedRelays.filter(b => b.relaysForKey.includes(a)).map(b => b.k)); - return [a, [...keysOnPickedRelay]]; - }) - ); - picked[""] = missing.map(a => a.key); - console.debug(picked); - return picked; - }, [keys]); -} diff --git a/packages/app/src/State/Relays/index.ts b/packages/app/src/State/Relays/index.ts deleted file mode 100644 index 6918255f..00000000 --- a/packages/app/src/State/Relays/index.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { FullRelaySettings, HexKey } from "@snort/nostr"; -import { db } from "Db"; -import { unixNowMs, unwrap } from "Util"; - -export class UserRelays { - #store: Map>; - - #snapshot: Readonly>>; - - constructor() { - this.#store = new Map(); - this.#snapshot = Object.freeze(new Map()); - } - - get snapshot() { - return this.#snapshot; - } - - async get(key: HexKey) { - if (!this.#store.has(key) && db.ready) { - const cached = await db.userRelays.get(key); - if (cached) { - this.#store.set(key, cached.relays); - return cached.relays; - } - } - return this.#store.get(key); - } - - async bulkGet(keys: Array) { - const missing = keys.filter(a => !this.#store.has(a)); - if (missing.length > 0 && db.ready) { - const cached = await db.userRelays.bulkGet(missing); - cached.forEach(a => { - if (a) { - this.#store.set(a.pubkey, a.relays); - } - }); - } - return new Map(keys.map(a => [a, this.#store.get(a) ?? []])); - } - - async set(key: HexKey, relays: Array) { - this.#store.set(key, relays); - if (db.ready) { - await db.userRelays.put({ - pubkey: key, - relays, - }); - } - this._update(); - } - - async bulkSet(obj: Record>) { - if (db.ready) { - await db.userRelays.bulkPut( - Object.entries(obj).map(([k, v]) => { - return { - pubkey: k, - relays: v, - }; - }) - ); - } - Object.entries(obj).forEach(([k, v]) => this.#store.set(k, v)); - this._update(); - } - - async preload() { - const start = unixNowMs(); - const keys = await db.userRelays.toCollection().keys(); - const fullCache = await db.userRelays.bulkGet(keys); - this.#store = new Map(fullCache.filter(a => a !== undefined).map(a => [unwrap(a).pubkey, a?.relays ?? []])); - this._update(); - console.debug(`Preloaded ${this.#store.size} users relays in ${(unixNowMs() - start).toLocaleString()} ms`); - } - - private _update() { - this.#snapshot = Object.freeze(new Map(this.#store)); - } -} - -export const FollowsRelays = new UserRelays(); diff --git a/packages/app/src/System/GossipModel.ts b/packages/app/src/System/GossipModel.ts new file mode 100644 index 00000000..ed589501 --- /dev/null +++ b/packages/app/src/System/GossipModel.ts @@ -0,0 +1,109 @@ +import { RawReqFilter } from "@snort/nostr"; +import { UserRelays } from "Cache/UserRelayCache"; +import { unwrap } from "Util"; + +const PickNRelays = 2; + +export interface RelayTaggedFilter { + relay: string; + filter: RawReqFilter; +} + +export interface RelayTaggedFilters { + relay: string; + filters: Array; +} + +export function splitAllByWriteRelays(filters: Array) { + const allSplit = filters.map(splitByWriteRelays).reduce((acc, v) => { + for (const vn of v) { + const existing = acc.get(vn.relay); + if (existing) { + existing.push(vn.filter); + } else { + acc.set(vn.relay, [vn.filter]); + } + } + return acc; + }, new Map>()); + + return [...allSplit.entries()].map(([k, v]) => { + return { + relay: k, + filters: v, + } as RelayTaggedFilters; + }); +} + +/** + * Split filters by authors + * @param filter + * @returns + */ +export function splitByWriteRelays(filter: RawReqFilter): Array { + if ((filter.authors?.length ?? 0) === 0) + return [ + { + relay: "", + filter, + }, + ]; + + const allRelays = unwrap(filter.authors).map(a => { + return { + key: a, + relays: UserRelays.getFromCache(a)?.relays, + }; + }); + + const missing = allRelays.filter(a => a.relays === undefined); + const hasRelays = allRelays.filter(a => a.relays !== undefined); + const relayUserMap = hasRelays.reduce((acc, v) => { + for (const r of unwrap(v.relays)) { + if (!acc.has(r.url)) { + acc.set(r.url, new Set([v.key])); + } else { + unwrap(acc.get(r.url)).add(v.key); + } + } + return acc; + }, new Map>()); + + // selection algo will just pick relays with the most users + const topRelays = [...relayUserMap.entries()].sort(([, v], [, v1]) => v1.size - v.size); + + // - count keys per relay + // - pick n top relays + // - map keys per relay (for subscription filter) + + const userPickedRelays = unwrap(filter.authors).map(k => { + // pick top 3 relays for this key + const relaysForKey = topRelays + .filter(([, v]) => v.has(k)) + .slice(0, PickNRelays) + .map(([k]) => k); + return { k, relaysForKey }; + }); + + const pickedRelays = new Set(userPickedRelays.map(a => a.relaysForKey).flat()); + + const picked = [...pickedRelays].map(a => { + const keysOnPickedRelay = new Set(userPickedRelays.filter(b => b.relaysForKey.includes(a)).map(b => b.k)); + return { + relay: a, + filter: { + ...filter, + authors: [...keysOnPickedRelay], + }, + } as RelayTaggedFilter; + }); + picked.push({ + relay: "", + filter: { + ...filter, + authors: missing.map(a => a.key), + }, + }); + console.debug("GOSSIP", picked); + return picked; +} diff --git a/packages/app/src/System/Query.ts b/packages/app/src/System/Query.ts index 9b9f7a3e..4cf26b6c 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/app/src/System/Query.ts @@ -151,7 +151,6 @@ export class Query { } cleanup() { - console.debug("Cleanup", this.id); this.#stopCheckTraces(); } diff --git a/packages/app/src/System/index.ts b/packages/app/src/System/index.ts index 42a43aef..ffeba073 100644 --- a/packages/app/src/System/index.ts +++ b/packages/app/src/System/index.ts @@ -12,6 +12,7 @@ import { } from "./NoteCollection"; import { diffFilters } from "./RequestSplitter"; import { Query } from "./Query"; +import { splitAllByWriteRelays } from "./GossipModel"; export { NoteStore, @@ -89,7 +90,7 @@ export class NostrSystem { try { const addr = unwrap(sanitizeRelayUrl(address)); if (!this.Sockets.has(addr)) { - const c = new Connection(addr, options, this.HandleAuth); + const c = new Connection(addr, options, this.HandleAuth?.bind(this)); this.Sockets.set(addr, c); c.OnEvent = (s, e) => this.OnEvent(s, e); c.OnEose = s => this.OnEndOfStoredEvents(c, s); @@ -146,7 +147,7 @@ export class NostrSystem { try { const addr = unwrap(sanitizeRelayUrl(address)); if (!this.Sockets.has(addr)) { - const c = new Connection(addr, { read: true, write: false }, this.HandleAuth, true); + const c = new Connection(addr, { read: true, write: false }, this.HandleAuth?.bind(this), true); this.Sockets.set(addr, c); c.OnEvent = (s, e) => this.OnEvent(s, e); c.OnEose = s => this.OnEndOfStoredEvents(c, s); @@ -212,11 +213,15 @@ export class NostrSystem { this.#changed(); return unwrap(q.feed) as Readonly; } else { - const subQ = new Query(`${q.id}-${q.subQueries.length + 1}`, filters, q.feed); - q.subQueries.push(subQ); + const splitFilters = splitAllByWriteRelays(filters); + for (const sf of splitFilters) { + const subQ = new Query(`${q.id}-${q.subQueries.length + 1}`, sf.filters, q.feed); + subQ.relays = sf.relay ? [sf.relay] : []; + q.subQueries.push(subQ); + this.SendQuery(subQ); + } q.filters = filters; q.feed.loading = true; - this.SendQuery(subQ); this.#changed(); return q.feed as Readonly; } @@ -227,7 +232,9 @@ export class NostrSystem { AddQuery(type: { new (): T }, rb: RequestBuilder): T { const store = new type(); - const q = new Query(rb.id, rb.build(), store); + + const filters = rb.build(); + const q = new Query(rb.id, filters, store); if (rb.options?.leaveOpen) { q.leaveOpen = rb.options.leaveOpen; } @@ -236,7 +243,17 @@ export class NostrSystem { } this.Queries.set(rb.id, q); - this.SendQuery(q); + const splitFilters = splitAllByWriteRelays(filters); + if (splitFilters.length > 1) { + for (const sf of splitFilters) { + const subQ = new Query(`${q.id}-${q.subQueries.length + 1}`, sf.filters, q.feed); + subQ.relays = sf.relay ? [sf.relay] : []; + q.subQueries.push(subQ); + this.SendQuery(subQ); + } + } else { + this.SendQuery(q); + } this.#changed(); return store; } @@ -248,9 +265,27 @@ export class NostrSystem { } } - SendQuery(q: Query) { - for (const [, s] of this.Sockets) { - q.sendToRelay(s); + async SendQuery(q: Query) { + if (q.relays.length > 0) { + for (const r of q.relays) { + const s = this.Sockets.get(r); + if (s) { + q.sendToRelay(s); + } else { + const nc = await this.ConnectEphemeralRelay(r); + if (nc) { + q.sendToRelay(nc); + } else { + console.warn("Failed to connect to new relay for:", r, q); + } + } + } + } else { + for (const [, s] of this.Sockets) { + if (!s.Ephemeral) { + q.sendToRelay(s); + } + } } } @@ -304,7 +339,6 @@ export class NostrSystem { if (v.closingAt && v.closingAt < now) { v.sendClose(); this.Queries.delete(k); - console.debug("Removed:", k); changed = true; } } From 126a55f3c4f8de1be8c8a9d91290af34c00a8c0e Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 18 May 2023 16:51:21 +0100 Subject: [PATCH 02/24] fixes --- packages/app/src/Cache/UserCache.ts | 4 +- packages/app/src/Pages/ZapPool.tsx | 2 +- packages/app/src/System/GossipModel.ts | 16 ++++---- packages/app/src/System/Query.ts | 19 ++++++--- packages/app/src/Wallet/index.ts | 54 +++++++++---------------- packages/nostr/src/legacy/Connection.ts | 33 +++++++++------ 6 files changed, 64 insertions(+), 64 deletions(-) diff --git a/packages/app/src/Cache/UserCache.ts b/packages/app/src/Cache/UserCache.ts index e2b8b94a..5dc2a3d4 100644 --- a/packages/app/src/Cache/UserCache.ts +++ b/packages/app/src/Cache/UserCache.ts @@ -85,7 +85,7 @@ class UserProfileCache extends FeedCache { await svc.load(); const p = this.getFromCache(i.pubkey); if (p) { - this.#setItem({ + await this.set({ ...p, zapService: svc.zapperPubkey, }); @@ -105,7 +105,7 @@ class UserProfileCache extends FeedCache { const nip5pk = await fetchNip05Pubkey(name, domain); const p = this.getFromCache(i.pubkey); if (p) { - this.#setItem({ + await this.set({ ...p, isNostrAddressValid: i.pubkey === nip5pk, }); diff --git a/packages/app/src/Pages/ZapPool.tsx b/packages/app/src/Pages/ZapPool.tsx index 95a889d1..27935a8a 100644 --- a/packages/app/src/Pages/ZapPool.tsx +++ b/packages/app/src/Pages/ZapPool.tsx @@ -80,7 +80,7 @@ export default function ZapPoolPage() { const relayConnections = useMemo(() => { return [...System.Sockets.values()] .map(a => { - if (a.Info?.pubkey) { + if (a.Info?.pubkey && !a.Ephemeral) { return { address: a.Address, pubkey: a.Info.pubkey, diff --git a/packages/app/src/System/GossipModel.ts b/packages/app/src/System/GossipModel.ts index ed589501..3b2ae236 100644 --- a/packages/app/src/System/GossipModel.ts +++ b/packages/app/src/System/GossipModel.ts @@ -97,13 +97,15 @@ export function splitByWriteRelays(filter: RawReqFilter): Array a.key), - }, - }); + if (missing.length > 0) { + picked.push({ + relay: "", + filter: { + ...filter, + authors: missing.map(a => a.key), + }, + }); + } console.debug("GOSSIP", picked); return picked; } diff --git a/packages/app/src/System/Query.ts b/packages/app/src/System/Query.ts index 4cf26b6c..a0a2313d 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/app/src/System/Query.ts @@ -1,6 +1,6 @@ import { v4 as uuid } from "uuid"; import { Connection, RawReqFilter, Nips } from "@snort/nostr"; -import { unixNowMs } from "Util"; +import { unixNowMs, unwrap } from "Util"; import { NoteStore } from "./NoteCollection"; /** * Tracing for relay query status @@ -32,6 +32,9 @@ class QueryTrace { gotEose() { this.eose = unixNowMs(); + if (this.responseTime > 5_000) { + console.debug(`Slow query ${this.subId} on ${this.relay} took ${this.responseTime.toLocaleString()}ms`); + } } forceEose() { @@ -66,6 +69,13 @@ class QueryTrace { return (this.eose === undefined ? unixNowMs() : this.eose) - this.start; } + /** + * Total time spent waiting for relay to respond + */ + get responseTime() { + return this.finished ? unwrap(this.eose) - unwrap(this.sent) : 0; + } + /** * If tracing is finished, we got EOSE or timeout */ @@ -193,16 +203,13 @@ export class Query { qt?.gotEose(); if (sub === this.id) { console.debug(`[EOSE][${sub}] ${conn.Address}`); - this.#feed.loading = this.progress < 1; - if (!this.leaveOpen && !this.#feed.loading) { - this.sendClose(); + if (!this.leaveOpen) { + qt?.sendClose(); } } else { const subQ = this.subQueries.find(a => a.id === sub); if (subQ) { subQ.eose(sub, conn); - } else { - throw new Error("No query found"); } } } diff --git a/packages/app/src/Wallet/index.ts b/packages/app/src/Wallet/index.ts index 19f826c7..b00c67d9 100644 --- a/packages/app/src/Wallet/index.ts +++ b/packages/app/src/Wallet/index.ts @@ -1,5 +1,6 @@ -import { useSyncExternalStore } from "react"; +import { useEffect, useSyncExternalStore } from "react"; +import ExternalStore from "ExternalStore"; import { decodeInvoice, unwrap } from "Util"; import LNDHubWallet from "./LNDHub"; import { NostrConnectWallet } from "./NostrWalletConnect"; @@ -124,35 +125,19 @@ export interface WalletStoreSnapshot { wallet?: LNWallet; } -type WalletStateHook = (state: WalletStoreSnapshot) => void; - -export class WalletStore { +export class WalletStore extends ExternalStore { #configs: Array; #instance: Map; - #hooks: Array; - #snapshot: Readonly; - constructor() { + super(); this.#configs = []; this.#instance = new Map(); - this.#hooks = []; - this.#snapshot = Object.freeze({ - configs: [], - }); this.load(false); setupWebLNWalletConfig(this); this.snapshotState(); } - hook(fn: WalletStateHook) { - this.#hooks.push(fn); - return () => { - const idx = this.#hooks.findIndex(a => a === fn); - this.#hooks = this.#hooks.splice(idx, 1); - }; - } - list() { return Object.freeze([...this.#configs]); } @@ -171,9 +156,9 @@ export class WalletStore { const w = this.#activateWallet(activeConfig); if (w) { if ("then" in w) { - w.then(wx => { + w.then(async wx => { this.#instance.set(activeConfig.id, wx); - this.snapshotState(); + this.notifyChange(); }); return undefined; } @@ -209,7 +194,7 @@ export class WalletStore { save() { const json = JSON.stringify(this.#configs); window.localStorage.setItem("wallet-config", json); - this.snapshotState(); + this.notifyChange(); } load(snapshot = true) { @@ -218,7 +203,7 @@ export class WalletStore { this.#configs = JSON.parse(cfg); } if (snapshot) { - this.snapshotState(); + this.notifyChange(); } } @@ -226,21 +211,12 @@ export class WalletStore { this.#instance.forEach(w => w.close()); } - getSnapshot() { - return this.#snapshot; - } - - snapshotState() { - const newState = { + takeSnapshot(): WalletStoreSnapshot { + return { configs: [...this.#configs], config: this.#configs.find(a => a.active), wallet: this.get(), } as WalletStoreSnapshot; - this.#snapshot = Object.freeze(newState); - for (const hook of this.#hooks) { - console.debug(this.#snapshot); - hook(this.#snapshot); - } } #activateWallet(cfg: WalletConfig): LNWallet | Promise | undefined { @@ -270,8 +246,14 @@ window.document.addEventListener("close", () => { }); export function useWallet() { - return useSyncExternalStore( + const wallet = useSyncExternalStore( h => Wallets.hook(h), - () => Wallets.getSnapshot() + () => Wallets.snapshot() ); + useEffect(() => { + if (wallet.wallet?.isReady() === false && wallet.wallet.canAutoLogin()) { + wallet.wallet.login().catch(console.error); + } + }, [wallet]); + return wallet; } diff --git a/packages/nostr/src/legacy/Connection.ts b/packages/nostr/src/legacy/Connection.ts index 8a1b5723..452f2ac8 100644 --- a/packages/nostr/src/legacy/Connection.ts +++ b/packages/nostr/src/legacy/Connection.ts @@ -96,10 +96,6 @@ export class Connection { this.AwaitingAuth = new Map(); this.Auth = auth; this.Ephemeral = ephemeral; - - if (this.Ephemeral) { - this.ResetEphemeralTimeout(); - } } ResetEphemeralTimeout() { @@ -109,7 +105,7 @@ export class Connection { if (this.Ephemeral) { this.EphemeralTimeout = setTimeout(() => { this.Close(); - }, 10_000); + }, 30_000); } } @@ -139,6 +135,13 @@ export class Connection { console.warn("Could not load relay information", e); } + if (this.Socket) { + this.Id = uuid(); + this.Socket.onopen = null; + this.Socket.onmessage = null; + this.Socket.onerror = null; + this.Socket.onclose = null; + } this.IsClosed = false; this.Socket = new WebSocket(this.Address); this.Socket.onopen = () => this.OnOpen(); @@ -161,17 +164,15 @@ export class Connection { this.ConnectTimeout = DefaultConnectTimeout; console.log(`[${this.Address}] Open!`); this.Down = false; + if (this.Ephemeral) { + this.ResetEphemeralTimeout(); + } this.OnConnected?.(); + this.#sendPendingRaw(); } OnClose(e: CloseEvent) { if (!this.IsClosed) { - this.OnDisconnect?.([...this.ActiveRequests], this.PendingRequests.map(a => a.cmd[1])) - this.#ResetQueues(); - - // reset connection Id on disconnect, for query-tracking - this.Id = uuid(); - this.ConnectTimeout = this.ConnectTimeout * 2; console.log( `[${this.Address}] Closed (${e.reason}), trying again in ${( @@ -188,6 +189,11 @@ export class Connection { console.log(`[${this.Address}] Closed!`); this.ReconnectTimer = null; } + + this.OnDisconnect?.([...this.ActiveRequests], this.PendingRequests.map(a => a.cmd[1])) + this.#ResetQueues(); + // reset connection Id on disconnect, for query-tracking + this.Id = uuid(); this.#UpdateState(); } @@ -387,6 +393,9 @@ export class Connection { const authPending = !this.Authed && (this.AwaitingAuth.size > 0 || this.Info?.limitation?.auth_required === true); if (this.Socket?.readyState !== WebSocket.OPEN || authPending) { this.PendingRaw.push(obj); + if (this.Socket?.readyState === WebSocket.CLOSED && this.Ephemeral && this.IsClosed) { + this.Connect() + } return false; } @@ -405,7 +414,7 @@ export class Connection { #sendOnWire(obj: unknown) { if (this.Socket?.readyState !== WebSocket.OPEN) { - throw new Error("Socket is not open"); + throw new Error(`Socket is not open, state is ${this.Socket?.readyState}`); } const json = JSON.stringify(obj); this.Socket.send(json); From c36544f9a3d0bfdc44dc29b16baeebb0162eda54 Mon Sep 17 00:00:00 2001 From: Kieran Date: Fri, 19 May 2023 15:15:13 +0100 Subject: [PATCH 03/24] tests --- package.json | 3 +- packages/app/jest.config.js | 7 + packages/app/package.json | 7 +- packages/app/src/Const.ts | 2 +- .../app/src/System/NoteCollection.test.ts | 1 + packages/app/src/System/Query.test.ts | 1 + .../app/src/System/RequestBuilder.test.ts | 1 + .../app/src/System/RequestSplitter.test.ts | 1 + packages/app/src/Util.test.ts | 31 +- packages/app/src/Util.ts | 4 +- packages/app/webpack.config.js | 2 +- packages/nostr/test/setup.ts | 2 +- yarn.lock | 1238 ++++++++++++++++- 13 files changed, 1221 insertions(+), 79 deletions(-) create mode 100644 packages/app/jest.config.js diff --git a/package.json b/package.json index 8e2e17d5..c5407974 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ ], "scripts": { "build": "yarn workspace @snort/nostr build && yarn workspace @snort/app build", - "start": "yarn workspace @snort/nostr build && yarn workspace @snort/app start" + "start": "yarn workspace @snort/nostr build && yarn workspace @snort/app start", + "test": "yarn workspace @snort/app test" }, "devDependencies": { "@tauri-apps/cli": "^1.2.3", diff --git a/packages/app/jest.config.js b/packages/app/jest.config.js new file mode 100644 index 00000000..4f0c217a --- /dev/null +++ b/packages/app/jest.config.js @@ -0,0 +1,7 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + bail: true, + preset: "ts-jest", + testEnvironment: "jsdom", + roots: ["./src/"], +}; diff --git a/packages/app/package.json b/packages/app/package.json index 6b487ed1..a28acdba 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -48,7 +48,7 @@ "scripts": { "start": "webpack serve", "build": "webpack --node-env=production", - "test": "", + "test": "jest", "intl-extract": "formatjs extract 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file src/lang.json --flatten true", "intl-compile": "formatjs compile src/lang.json --out-file src/translations/en.json", "format": "prettier --write .", @@ -80,7 +80,7 @@ "@babel/preset-react": "^7.18.6", "@formatjs/cli": "^6.0.1", "@formatjs/ts-transformer": "^3.13.1", - "@types/jest": "^29.2.5", + "@types/jest": "^29.5.1", "@types/node": "^18.11.18", "@types/react": "^18.0.26", "@types/react-dom": "^18.0.10", @@ -99,9 +99,12 @@ "eslint-webpack-plugin": "^4.0.1", "html-webpack-plugin": "^5.5.1", "husky": ">=6", + "jest": "^29.5.0", + "jest-environment-jsdom": "^29.5.0", "lint-staged": ">=10", "mini-css-extract-plugin": "^2.7.5", "prettier": "2.8.3", + "ts-jest": "^29.1.0", "ts-loader": "^9.4.2", "typescript": "^5.0.4", "webpack": "^5.82.1", diff --git a/packages/app/src/Const.ts b/packages/app/src/Const.ts index 7dd90867..5c287e5b 100644 --- a/packages/app/src/Const.ts +++ b/packages/app/src/Const.ts @@ -33,7 +33,7 @@ export const DefaultConnectTimeout = 2000; /** * How long profile cache should be considered valid for */ -export const ProfileCacheExpire = 1_000 * 60 * 30; +export const ProfileCacheExpire = 1_000 * 60 * 60 * 6; /** * Default bootstrap relays diff --git a/packages/app/src/System/NoteCollection.test.ts b/packages/app/src/System/NoteCollection.test.ts index 7a992bf4..8df3559a 100644 --- a/packages/app/src/System/NoteCollection.test.ts +++ b/packages/app/src/System/NoteCollection.test.ts @@ -1,4 +1,5 @@ import { TaggedRawEvent } from "@snort/nostr"; +import { describe, expect } from "@jest/globals"; import { FlatNoteStore, ReplaceableNoteStore } from "./NoteCollection"; describe("NoteStore", () => { diff --git a/packages/app/src/System/Query.test.ts b/packages/app/src/System/Query.test.ts index 3fece031..2d6bf824 100644 --- a/packages/app/src/System/Query.test.ts +++ b/packages/app/src/System/Query.test.ts @@ -1,4 +1,5 @@ import { Connection } from "@snort/nostr"; +import { describe, expect } from "@jest/globals"; import { Query } from "./Query"; import { getRandomValues } from "crypto"; import { FlatNoteStore } from "./NoteCollection"; diff --git a/packages/app/src/System/RequestBuilder.test.ts b/packages/app/src/System/RequestBuilder.test.ts index ec062046..29527438 100644 --- a/packages/app/src/System/RequestBuilder.test.ts +++ b/packages/app/src/System/RequestBuilder.test.ts @@ -1,4 +1,5 @@ import { RequestBuilder } from "./RequestBuilder"; +import { describe, expect } from "@jest/globals"; describe("RequestBuilder", () => { describe("basic", () => { diff --git a/packages/app/src/System/RequestSplitter.test.ts b/packages/app/src/System/RequestSplitter.test.ts index 0dd214b2..49c2d0cb 100644 --- a/packages/app/src/System/RequestSplitter.test.ts +++ b/packages/app/src/System/RequestSplitter.test.ts @@ -1,4 +1,5 @@ import { RawReqFilter } from "@snort/nostr"; +import { describe, expect } from "@jest/globals"; import { diffFilters } from "./RequestSplitter"; describe("RequestSplitter", () => { diff --git a/packages/app/src/Util.test.ts b/packages/app/src/Util.test.ts index dfae60e0..ef537689 100644 --- a/packages/app/src/Util.test.ts +++ b/packages/app/src/Util.test.ts @@ -1,4 +1,5 @@ import { splitByUrl, magnetURIDecode, getRelayName, validateNostrLink } from "./Util"; +import { describe, expect } from "@jest/globals"; describe("splitByUrl", () => { it("should split a string by URLs", () => { @@ -12,8 +13,8 @@ describe("splitByUrl", () => { " but I made a ", "https://example.com", "! simple example (", - "https://example.com", - ") of how ", + "https://example.com)", + " of how ", "https://example.com/yo-yo", " ", "https://example.example.com", @@ -92,22 +93,18 @@ describe("getRelayName", () => { }); describe("validateNostrLink", () => { - it("should return true for valid nostr links", () => { - [ - "nostr:npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg", - "web+nostr:npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg", - "nostr:note15449edq4qa5wzgqvh8td0q0dp6hwtes4pknsrm7eygeenhlj99xsq94wu9", - "nostr:nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p", - "nostr:nevent1qqs226juks2sw68pyqxtn4khs8ksath9uc2smfcpalvjyvuemlezjngrd87dq", - "nostr:naddr1qqzkjurnw4ksz9thwden5te0wfjkccte9ehx7um5wghx7un8qgs2d90kkcq3nk2jry62dyf50k0h36rhpdtd594my40w9pkal876jxgrqsqqqa28pccpzu", - ].forEach(link => { - expect(validateNostrLink(link)).toBe(true); - }); + test.each([ + "nostr:npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg", + "web+nostr:npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg", + "nostr:note15449edq4qa5wzgqvh8td0q0dp6hwtes4pknsrm7eygeenhlj99xsq94wu9", + "nostr:nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p", + "nostr:nevent1qqs226juks2sw68pyqxtn4khs8ksath9uc2smfcpalvjyvuemlezjngrd87dq", + "nostr:naddr1qqzkjurnw4ksz9thwden5te0wfjkccte9ehx7um5wghx7un8qgs2d90kkcq3nk2jry62dyf50k0h36rhpdtd594my40w9pkal876jxgrqsqqqa28pccpzu", + ])("should return true for valid nostr links", la => { + expect(validateNostrLink(la)).toBe(true); }); - it("should return false for invalid nostr links", () => { - ["nostr:npub", "web+nostr:npub", "nostr:nevent1xxx"].forEach(link => { - expect(validateNostrLink(link)).toBe(false); - }); + test.each(["nostr:npub", "web+nostr:npub", "nostr:nevent1xxx"])("should return false for invalid nostr links", lb => { + expect(validateNostrLink(lb)).toBe(false); }); }); diff --git a/packages/app/src/Util.ts b/packages/app/src/Util.ts index 9b06e5cb..fb0acc55 100644 --- a/packages/app/src/Util.ts +++ b/packages/app/src/Util.ts @@ -65,7 +65,7 @@ export function bech32ToHex(str: string) { const nKey = bech32.decode(str, 1_000); const buff = bech32.fromWords(nKey.words); return utils.bytesToHex(Uint8Array.from(buff)); - } catch { + } catch (e) { return str; } } @@ -517,11 +517,9 @@ export interface NostrLink { export function validateNostrLink(link: string): boolean { try { const parsedLink = parseNostrLink(link); - if (!parsedLink) { return false; } - if (parsedLink.type === NostrPrefix.PublicKey || parsedLink.type === NostrPrefix.Note) { return parsedLink.id.length === 64; } diff --git a/packages/app/webpack.config.js b/packages/app/webpack.config.js index 55d2f77d..5ac1695c 100644 --- a/packages/app/webpack.config.js +++ b/packages/app/webpack.config.js @@ -56,7 +56,7 @@ const config = { module: { rules: [ { - test: /\.(ts|tsx)$/i, + test: /\.tsx?$/i, use: [ "babel-loader", { diff --git a/packages/nostr/test/setup.ts b/packages/nostr/test/setup.ts index 25638acf..0d096bbc 100644 --- a/packages/nostr/test/setup.ts +++ b/packages/nostr/test/setup.ts @@ -30,7 +30,7 @@ export interface Setup { } export async function setup( - done: jest.DoneCallback, + done: (e?: unknown) => void, test: (setup: Setup) => void | Promise ) { try { diff --git a/yarn.lock b/yarn.lock index 1b09ee9f..f69433cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -36,7 +36,7 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.7.tgz#61caffb60776e49a57ba61a88f02bedd8714f6bc" integrity sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA== -"@babel/core@^7.10.4": +"@babel/core@^7.10.4", "@babel/core@^7.11.6", "@babel/core@^7.12.3": version "7.21.8" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.8.tgz#2a8c7f0f53d60100ba4c32470ba0281c92aa9aa4" integrity sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ== @@ -88,7 +88,7 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/generator@^7.21.5": +"@babel/generator@^7.21.5", "@babel/generator@^7.7.2": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.5.tgz#c0c0e5449504c7b7de8236d99338c3e2a340745f" integrity sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w== @@ -367,7 +367,7 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.21.5", "@babel/parser@^7.21.8": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.21.5", "@babel/parser@^7.21.8": version "7.21.8" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.8.tgz#642af7d0333eab9c0ad70b14ac5e76dbde7bfdf8" integrity sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA== @@ -529,7 +529,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.12.13": +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": version "7.12.13" resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== @@ -564,7 +571,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.19.0" -"@babel/plugin-syntax-import-meta@^7.10.4": +"@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== @@ -578,14 +585,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@7", "@babel/plugin-syntax-jsx@^7.21.4": +"@babel/plugin-syntax-jsx@7", "@babel/plugin-syntax-jsx@^7.21.4", "@babel/plugin-syntax-jsx@^7.7.2": version "7.21.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz" integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== dependencies: "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== @@ -599,7 +606,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.10.4": +"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== @@ -634,13 +641,20 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-top-level-await@^7.14.5": +"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.14.5" resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz#2751948e9b7c6d771a8efa59340c15d4a2891ff8" + integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-transform-arrow-functions@^7.20.7": version "7.20.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz" @@ -1176,7 +1190,7 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.18.10", "@babel/template@^7.20.7": +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": version "7.20.7" resolved "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz" integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== @@ -1185,7 +1199,7 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@7", "@babel/traverse@^7.21.5": +"@babel/traverse@7", "@babel/traverse@^7.21.5", "@babel/traverse@^7.7.2": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.5.tgz#ad22361d352a5154b498299d523cf72998a4b133" integrity sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw== @@ -1217,7 +1231,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.11", "@babel/types@^7.21.5", "@babel/types@^7.3.0": +"@babel/types@^7.0.0", "@babel/types@^7.12.11", "@babel/types@^7.21.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.21.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.5.tgz#18dfbd47c39d3904d5db3d3dc2cc80bedb60e5b6" integrity sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q== @@ -1235,6 +1249,11 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + "@cashu/cashu-ts@^0.6.1": version "0.6.1" resolved "https://registry.npmjs.org/@cashu/cashu-ts/-/cashu-ts-0.6.1.tgz" @@ -1446,6 +1465,78 @@ resolved "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz" integrity sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ== +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.5.0.tgz#593a6c5c0d3f75689835f1b3b4688c4f8544cb57" + integrity sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ== + dependencies: + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + slash "^3.0.0" + +"@jest/core@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.5.0.tgz#76674b96904484e8214614d17261cc491e5f1f03" + integrity sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ== + dependencies: + "@jest/console" "^29.5.0" + "@jest/reporters" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.5.0" + jest-config "^29.5.0" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-resolve-dependencies "^29.5.0" + jest-runner "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" + jest-watcher "^29.5.0" + micromatch "^4.0.4" + pretty-format "^29.5.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65" + integrity sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ== + dependencies: + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + jest-mock "^29.5.0" + "@jest/expect-utils@^29.5.0": version "29.5.0" resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz" @@ -1453,6 +1544,66 @@ dependencies: jest-get-type "^29.4.3" +"@jest/expect@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.5.0.tgz#80952f5316b23c483fbca4363ce822af79c38fba" + integrity sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g== + dependencies: + expect "^29.5.0" + jest-snapshot "^29.5.0" + +"@jest/fake-timers@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.5.0.tgz#d4d09ec3286b3d90c60bdcd66ed28d35f1b4dc2c" + integrity sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg== + dependencies: + "@jest/types" "^29.5.0" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" + jest-util "^29.5.0" + +"@jest/globals@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.5.0.tgz#6166c0bfc374c58268677539d0c181f9c1833298" + integrity sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/types" "^29.5.0" + jest-mock "^29.5.0" + +"@jest/reporters@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.5.0.tgz#985dfd91290cd78ddae4914ba7921bcbabe8ac9b" + integrity sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@jridgewell/trace-mapping" "^0.3.15" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + jest-worker "^29.5.0" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + "@jest/schemas@^29.4.3": version "29.4.3" resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz" @@ -1460,6 +1611,56 @@ dependencies: "@sinclair/typebox" "^0.25.16" +"@jest/source-map@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.3.tgz#ff8d05cbfff875d4a791ab679b4333df47951d20" + integrity sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.15" + callsites "^3.0.0" + graceful-fs "^4.2.9" + +"@jest/test-result@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.5.0.tgz#7c856a6ca84f45cc36926a4e9c6b57f1973f1408" + integrity sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ== + dependencies: + "@jest/console" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz#34d7d82d3081abd523dbddc038a3ddcb9f6d3cc4" + integrity sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ== + dependencies: + "@jest/test-result" "^29.5.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.5.0" + slash "^3.0.0" + +"@jest/transform@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.5.0.tgz#cf9c872d0965f0cbd32f1458aa44a2b1988b00f9" + integrity sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.5.0" + "@jridgewell/trace-mapping" "^0.3.15" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.5.0" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + "@jest/types@^29.5.0": version "29.5.0" resolved "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz" @@ -1530,6 +1731,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz" @@ -1939,6 +2148,20 @@ resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz" integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== +"@sinonjs/commons@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" + integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.2.0.tgz#b3e322a34c5f26e3184e7f6115695f299c1b1194" + integrity sha512-OPwQlEdg40HAj5KNF8WW6q2KG4Z+cBCZb3m4ninfTZKaBmbIJodviQsDBoYMPHkOyJJMHnOJo5j2+LKDOhOACg== + dependencies: + "@sinonjs/commons" "^3.0.0" + "@surma/rollup-plugin-off-main-thread@^2.2.3": version "2.2.3" resolved "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz" @@ -2052,7 +2275,7 @@ resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz" integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== -"@types/babel__core@*", "@types/babel__core@^7.1.7": +"@types/babel__core@*", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.7": version "7.20.0" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== @@ -2085,7 +2308,7 @@ "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.1.7": +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6", "@types/babel__traverse@^7.1.7": version "7.18.5" resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.5.tgz#c107216842905afafd3b6e774f6f935da6f5db80" integrity sha512-enCvTL8m/EHS/zIvJno9nE+ndYPh1/oNFzRYRmtUqJICG2VnCSBzMLW5VN2KCQU91f23tsNKR8v7VJJQMatl7Q== @@ -2206,6 +2429,13 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/graceful-fs@^4.1.3": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" + integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== + dependencies: + "@types/node" "*" + "@types/hoist-non-react-statics@^3.3.1": version "3.3.1" resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz" @@ -2226,7 +2456,7 @@ dependencies: "@types/node" "*" -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz" integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== @@ -2245,14 +2475,23 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^29.2.5": - version "29.5.0" - resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz" - integrity sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg== +"@types/jest@^29.5.1": + version "29.5.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.1.tgz#83c818aa9a87da27d6da85d3378e5a34d2f31a47" + integrity sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ== dependencies: expect "^29.0.0" pretty-format "^29.0.0" +"@types/jsdom@^20.0.0": + version "20.0.1" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808" + integrity sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ== + dependencies: + "@types/node" "*" + "@types/tough-cookie" "*" + parse5 "^7.0.0" + "@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" @@ -2331,6 +2570,11 @@ resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.3.0.tgz#75db5e75a713c5a83d5b76780c3da84a82806003" integrity sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g== +"@types/prettier@^2.1.5": + version "2.7.2" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" + integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== + "@types/prop-types@*": version "15.7.5" resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz" @@ -2418,6 +2662,11 @@ resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/tough-cookie@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" + integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== + "@types/trusted-types@^2.0.2": version "2.0.3" resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz" @@ -2883,6 +3132,11 @@ resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== +abab@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + abbrev@1: version "1.1.1" resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" @@ -2896,6 +3150,14 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" +acorn-globals@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" + integrity sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q== + dependencies: + acorn "^8.1.0" + acorn-walk "^8.0.2" + acorn-import-assertions@^1.7.6: version "1.8.0" resolved "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz" @@ -2906,12 +3168,12 @@ acorn-jsx@^5.3.2: resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^8.0.0, acorn-walk@^8.1.1: +acorn-walk@^8.0.0, acorn-walk@^8.0.2, acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^8.0.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0: +acorn@^8.0.4, acorn@^8.1.0, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.1: version "8.8.2" resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== @@ -3030,7 +3292,7 @@ ansi-styles@^6.0.0: resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== -anymatch@~3.1.2: +anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.3" resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== @@ -3158,6 +3420,19 @@ axios@^1.2.1: form-data "^4.0.0" proxy-from-env "^1.1.0" +babel-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" + integrity sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q== + dependencies: + "@jest/transform" "^29.5.0" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.5.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + babel-loader@^9.1.2: version "9.1.2" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-9.1.2.tgz#a16a080de52d08854ee14570469905a5fc00d39c" @@ -3183,6 +3458,27 @@ babel-plugin-formatjs@^10.5.1: "@types/babel__traverse" "^7.1.7" tslib "^2.4.0" +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" + integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + babel-plugin-polyfill-corejs2@^0.3.3: version "0.3.3" resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz" @@ -3207,6 +3503,32 @@ babel-plugin-polyfill-regenerator@^0.4.1: dependencies: "@babel/helper-define-polyfill-provider" "^0.3.3" +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +babel-preset-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" + integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg== + dependencies: + babel-plugin-jest-hoist "^29.5.0" + babel-preset-current-node-syntax "^1.0.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" @@ -3348,6 +3670,20 @@ browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.21.3, browserslist@^4 node-releases "^2.0.8" update-browserslist-db "^1.0.10" +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + buffer-from@^1.0.0, buffer-from@^1.1.0: version "1.1.2" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" @@ -3465,7 +3801,12 @@ camel-case@^4.1.2: pascal-case "^3.1.2" tslib "^2.0.3" -camelcase@^6.0.0: +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.0.0, camelcase@^6.2.0: version "6.3.0" resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -3525,6 +3866,11 @@ chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1: ansi-styles "^4.1.0" supports-color "^7.1.0" +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + chardet@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" @@ -3565,6 +3911,11 @@ ci-info@^3.2.0: resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz" integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== +cjs-module-lexer@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" + integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== + clean-css@^5.2.2: version "5.3.2" resolved "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz" @@ -3626,6 +3977,15 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + clone-buffer@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz" @@ -3671,6 +4031,16 @@ cmd-shim@^5.0.0: dependencies: mkdirp-infer-owner "^2.0.0" +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" @@ -3814,11 +4184,16 @@ content-type@~1.0.4: resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -convert-source-map@^1.7.0: +convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" @@ -4011,6 +4386,23 @@ csso@^5.0.5: dependencies: css-tree "~2.2.0" +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + csstype@^3.0.2: version "3.1.2" resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz" @@ -4033,6 +4425,15 @@ dargs@^7.0.0: resolved "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== +data-urls@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" + integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== + dependencies: + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + dateformat@^4.5.0: version "4.6.3" resolved "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz" @@ -4062,6 +4463,16 @@ decamelize@^4.0.0: resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== +decimal.js@^10.4.2: + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== + +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== + deep-eql@^4.1.2: version "4.1.3" resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz" @@ -4074,7 +4485,7 @@ deep-extend@^0.6.0: resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-is@^0.1.3: +deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== @@ -4141,6 +4552,11 @@ destroy@1.2.0: resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + detect-node@^2.0.4: version "2.1.0" resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz" @@ -4250,6 +4666,13 @@ domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== + dependencies: + webidl-conversions "^7.0.0" + domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: version "4.3.1" resolved "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz" @@ -4317,6 +4740,11 @@ electron-to-chromium@^1.4.284: resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.351.tgz" integrity sha512-W35n4jAsyj6OZGxeWe+gA6+2Md4jDO19fzfsRKEt3DBwIdlVTT8O9Uv8ojgUAoQeXASdgG9zMU+8n8Xg/W6dRQ== +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + emoji-regex@10.2.1, emoji-regex@^10.2.1: version "10.2.1" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.2.1.tgz#a41c330d957191efd3d9dfe6e1e8e1e9ab048b3f" @@ -4370,7 +4798,7 @@ entities@^2.0.0: resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -entities@^4.2.0: +entities@^4.2.0, entities@^4.4.0: version "4.5.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== @@ -4495,6 +4923,18 @@ escape-string-regexp@^2.0.0: resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + eslint-plugin-formatjs@^4.10.1: version "4.10.1" resolved "https://registry.yarnpkg.com/eslint-plugin-formatjs/-/eslint-plugin-formatjs-4.10.1.tgz#c67184ac54188dcad84d6541e6b5467248ab6550" @@ -4613,7 +5053,7 @@ espree@^9.5.2: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" -esprima@^4.0.0: +esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -4697,7 +5137,12 @@ execa@^7.0.0: signal-exit "^3.0.7" strip-final-newline "^3.0.0" -expect@*, expect@^29.0.0: +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== + +expect@*, expect@^29.0.0, expect@^29.5.0: version "29.5.0" resolved "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz" integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== @@ -4770,12 +5215,12 @@ fast-glob@^3.2.11, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== @@ -4799,6 +5244,13 @@ faye-websocket@^0.11.3: dependencies: websocket-driver ">=0.5.1" +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== + dependencies: + bser "2.1.1" + figures@^3.0.0: version "3.2.0" resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" @@ -4956,7 +5408,7 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@~2.3.2: +fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -5039,6 +5491,11 @@ get-own-enumerable-property-symbols@^3.0.0: resolved "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz" integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" @@ -5262,11 +5719,23 @@ hpack.js@^2.1.6: readable-stream "^2.0.1" wbuf "^1.1.0" +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== + dependencies: + whatwg-encoding "^2.0.0" + html-entities@^2.3.2: version "2.3.3" resolved "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz" integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + html-minifier-terser@^6.0.2: version "6.1.0" resolved "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz" @@ -5375,7 +5844,7 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" -https-proxy-agent@^5.0.0: +https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -5412,7 +5881,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.6.2: +iconv-lite@0.6.3, iconv-lite@^0.6.2: version "0.6.3" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -5640,6 +6109,11 @@ is-fullwidth-code-point@^4.0.0: resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz" integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" @@ -5711,6 +6185,11 @@ is-plain-object@^5.0.0: resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + is-regex@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" @@ -5827,6 +6306,48 @@ isomorphic-ws@^5.0.0: resolved "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz" integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.1.3: + version "3.1.5" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" + integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + jake@^10.8.5: version "10.8.5" resolved "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz" @@ -5837,6 +6358,86 @@ jake@^10.8.5: filelist "^1.0.1" minimatch "^3.0.4" +jest-changed-files@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" + integrity sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag== + dependencies: + execa "^5.0.0" + p-limit "^3.1.0" + +jest-circus@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.5.0.tgz#b5926989449e75bff0d59944bae083c9d7fb7317" + integrity sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + is-generator-fn "^2.0.0" + jest-each "^29.5.0" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + p-limit "^3.1.0" + pretty-format "^29.5.0" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-cli@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.5.0.tgz#b34c20a6d35968f3ee47a7437ff8e53e086b4a67" + integrity sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw== + dependencies: + "@jest/core" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" + prompts "^2.0.1" + yargs "^17.3.1" + +jest-config@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.5.0.tgz#3cc972faec8c8aaea9ae158c694541b79f3748da" + integrity sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.5.0" + "@jest/types" "^29.5.0" + babel-jest "^29.5.0" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.5.0" + jest-environment-node "^29.5.0" + jest-get-type "^29.4.3" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-runner "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.5.0" + slash "^3.0.0" + strip-json-comments "^3.1.1" + jest-diff@^29.5.0: version "29.5.0" resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz" @@ -5847,11 +6448,82 @@ jest-diff@^29.5.0: jest-get-type "^29.4.3" pretty-format "^29.5.0" +jest-docblock@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8" + integrity sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg== + dependencies: + detect-newline "^3.0.0" + +jest-each@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.5.0.tgz#fc6e7014f83eac68e22b7195598de8554c2e5c06" + integrity sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA== + dependencies: + "@jest/types" "^29.5.0" + chalk "^4.0.0" + jest-get-type "^29.4.3" + jest-util "^29.5.0" + pretty-format "^29.5.0" + +jest-environment-jsdom@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.5.0.tgz#cfe86ebaf1453f3297b5ff3470fbe94739c960cb" + integrity sha512-/KG8yEK4aN8ak56yFVdqFDzKNHgF4BAymCx2LbPNPsUshUlfAl0eX402Xm1pt+eoG9SLZEUVifqXtX8SK74KCw== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/jsdom" "^20.0.0" + "@types/node" "*" + jest-mock "^29.5.0" + jest-util "^29.5.0" + jsdom "^20.0.0" + +jest-environment-node@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.5.0.tgz#f17219d0f0cc0e68e0727c58b792c040e332c967" + integrity sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + jest-mock "^29.5.0" + jest-util "^29.5.0" + jest-get-type@^29.4.3: version "29.4.3" resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz" integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== +jest-haste-map@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.5.0.tgz#69bd67dc9012d6e2723f20a945099e972b2e94de" + integrity sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA== + dependencies: + "@jest/types" "^29.5.0" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.4.3" + jest-util "^29.5.0" + jest-worker "^29.5.0" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + +jest-leak-detector@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz#cf4bdea9615c72bac4a3a7ba7e7930f9c0610c8c" + integrity sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow== + dependencies: + jest-get-type "^29.4.3" + pretty-format "^29.5.0" + jest-matcher-utils@^29.5.0: version "29.5.0" resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz" @@ -5877,7 +6549,133 @@ jest-message-util@^29.5.0: slash "^3.0.0" stack-utils "^2.0.3" -jest-util@^29.5.0: +jest-mock@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed" + integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw== + dependencies: + "@jest/types" "^29.5.0" + "@types/node" "*" + jest-util "^29.5.0" + +jest-pnp-resolver@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== + +jest-regex-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" + integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== + +jest-resolve-dependencies@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz#f0ea29955996f49788bf70996052aa98e7befee4" + integrity sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg== + dependencies: + jest-regex-util "^29.4.3" + jest-snapshot "^29.5.0" + +jest-resolve@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.5.0.tgz#b053cc95ad1d5f6327f0ac8aae9f98795475ecdc" + integrity sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.5.0" + jest-pnp-resolver "^1.2.2" + jest-util "^29.5.0" + jest-validate "^29.5.0" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + +jest-runner@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.5.0.tgz#6a57c282eb0ef749778d444c1d758c6a7693b6f8" + integrity sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ== + dependencies: + "@jest/console" "^29.5.0" + "@jest/environment" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.4.3" + jest-environment-node "^29.5.0" + jest-haste-map "^29.5.0" + jest-leak-detector "^29.5.0" + jest-message-util "^29.5.0" + jest-resolve "^29.5.0" + jest-runtime "^29.5.0" + jest-util "^29.5.0" + jest-watcher "^29.5.0" + jest-worker "^29.5.0" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.5.0.tgz#c83f943ee0c1da7eb91fa181b0811ebd59b03420" + integrity sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw== + dependencies: + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/globals" "^29.5.0" + "@jest/source-map" "^29.4.3" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" + jest-regex-util "^29.4.3" + jest-resolve "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-snapshot@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.5.0.tgz#c9c1ce0331e5b63cd444e2f95a55a73b84b1e8ce" + integrity sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/babel__traverse" "^7.0.6" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.5.0" + graceful-fs "^4.2.9" + jest-diff "^29.5.0" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + natural-compare "^1.4.0" + pretty-format "^29.5.0" + semver "^7.3.5" + +jest-util@^29.0.0, jest-util@^29.5.0: version "29.5.0" resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz" integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== @@ -5889,6 +6687,32 @@ jest-util@^29.5.0: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-validate@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.5.0.tgz#8e5a8f36178d40e47138dc00866a5f3bd9916ffc" + integrity sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ== + dependencies: + "@jest/types" "^29.5.0" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.4.3" + leven "^3.1.0" + pretty-format "^29.5.0" + +jest-watcher@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.5.0.tgz#cf7f0f949828ba65ddbbb45c743a382a4d911363" + integrity sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA== + dependencies: + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.5.0" + string-length "^4.0.1" + jest-worker@^26.2.1: version "26.6.2" resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz" @@ -5917,6 +6741,16 @@ jest-worker@^29.4.3, jest-worker@^29.5.0: merge-stream "^2.0.0" supports-color "^8.0.0" +jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.5.0.tgz#f75157622f5ce7ad53028f2f8888ab53e1f1f24e" + integrity sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ== + dependencies: + "@jest/core" "^29.5.0" + "@jest/types" "^29.5.0" + import-local "^3.0.2" + jest-cli "^29.5.0" + js-sdsl@^4.1.4: version "4.4.0" resolved "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz" @@ -5934,7 +6768,7 @@ js-yaml@4.1.0, js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -js-yaml@^3.13.0: +js-yaml@^3.13.0, js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -5942,6 +6776,38 @@ js-yaml@^3.13.0: argparse "^1.0.7" esprima "^4.0.0" +jsdom@^20.0.0: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" + integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ== + dependencies: + abab "^2.0.6" + acorn "^8.8.1" + acorn-globals "^7.0.0" + cssom "^0.5.0" + cssstyle "^2.3.0" + data-urls "^3.0.2" + decimal.js "^10.4.2" + domexception "^4.0.0" + escodegen "^2.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.2" + parse5 "^7.1.1" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.2" + w3c-xmlserializer "^4.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + ws "^8.11.0" + xml-name-validator "^4.0.0" + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" @@ -5996,7 +6862,7 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" -json5@^2.2.0, json5@^2.2.2: +json5@^2.2.0, json5@^2.2.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -6040,6 +6906,11 @@ kind-of@^6.0.2: resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + launch-editor@^2.6.0: version "2.6.0" resolved "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz" @@ -6061,6 +6932,14 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + light-bolt11-decoder@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/light-bolt11-decoder/-/light-bolt11-decoder-2.1.0.tgz" @@ -6152,7 +7031,7 @@ lodash.flow@^3.5.0: resolved "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz" integrity sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw== -lodash.memoize@^4.1.2: +lodash.memoize@4.x, lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== @@ -6249,14 +7128,14 @@ magic-string@^0.30.0: dependencies: "@jridgewell/sourcemap-codec" "^1.4.13" -make-dir@^3.0.2: +make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" -make-error@^1.1.1: +make-error@1.x, make-error@^1.1.1: version "1.3.6" resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -6305,6 +7184,13 @@ make-fetch-happen@^9.1.0: socks-proxy-agent "^6.0.0" ssri "^8.0.0" +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + match-sorter@4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/match-sorter/-/match-sorter-4.0.0.tgz" @@ -6561,7 +7447,7 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: mocha@^10.2.0: version "10.2.0" - resolved "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== dependencies: ansi-colors "4.1.1" @@ -6701,6 +7587,11 @@ node-gyp@^8.2.0: tar "^6.1.2" which "^2.0.2" +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + node-releases@^2.0.8: version "2.0.10" resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz" @@ -6834,6 +7725,11 @@ nth-check@^2.0.1: dependencies: boolbase "^1.0.0" +nwsapi@^2.2.2: + version "2.2.4" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.4.tgz#fd59d5e904e8e1f03c25a7d5a15cfa16c714a1e5" + integrity sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g== + object-assign@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" @@ -6911,6 +7807,18 @@ opener@^1.5.2: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + optionator@^0.9.1: version "0.9.1" resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" @@ -6955,7 +7863,7 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -7068,7 +7976,7 @@ parse-conflict-json@^2.0.1: just-diff "^5.0.1" just-diff-apply "^5.2.0" -parse-json@^5.0.0: +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -7078,6 +7986,13 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse5@^7.0.0, parse5@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" @@ -7156,6 +8071,11 @@ pify@^4.0.1: resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +pirates@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" + integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" @@ -7441,6 +8361,11 @@ prelude-ls@^1.2.1: resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + prettier@2.8.3: version "2.8.3" resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz" @@ -7506,6 +8431,14 @@ promise-retry@^2.0.1: err-code "^2.0.2" retry "^0.12.0" +prompts@^2.0.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + prop-types@^15.7.2: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" @@ -7528,11 +8461,21 @@ proxy-from-env@^1.1.0: resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -punycode@^2.1.0: +psl@^1.1.33: + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + +punycode@^2.1.0, punycode@^2.1.1: version "2.3.0" resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +pure-rand@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306" + integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ== + qr-code-styling@^1.6.0-rc.1: version "1.6.0-rc.1" resolved "https://registry.npmjs.org/qr-code-styling/-/qr-code-styling-1.6.0-rc.1.tgz" @@ -7552,6 +8495,11 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" @@ -7897,6 +8845,11 @@ resolve-from@^5.0.0: resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== + resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0: version "1.22.1" resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz" @@ -8001,6 +8954,13 @@ safe-regex-test@^1.0.0: resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" + scheduler@^0.23.0: version "0.23.0" resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz" @@ -8063,18 +9023,18 @@ selfsigned@^2.1.1: resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: +semver@7.x, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.5.1" resolved "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz" integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw== dependencies: lru-cache "^6.0.0" +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + semver@^7.3.7, semver@^7.3.8: version "7.3.8" resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz" @@ -8216,6 +9176,11 @@ sirv@^1.0.7: mrmime "^1.0.0" totalist "^1.0.0" +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + sjcl@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/sjcl/-/sjcl-1.0.8.tgz#f2ec8d7dc1f0f21b069b8914a41a8f236b0e252a" @@ -8314,6 +9279,14 @@ source-map-js@^1.0.1, source-map-js@^1.0.2: resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@^0.5.6, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" @@ -8434,6 +9407,14 @@ string-argv@^0.3.1: resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz" integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" @@ -8557,6 +9538,11 @@ strip-bom@^3.0.0: resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + strip-comments@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz" @@ -8623,6 +9609,11 @@ svgo@^3.0.2: csso "^5.0.5" picocolors "^1.0.0" +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: version "2.2.1" resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" @@ -8697,6 +9688,15 @@ terser@^5.16.8: commander "^2.20.0" source-map-support "~0.5.20" +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" @@ -8729,6 +9729,11 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" @@ -8751,6 +9756,16 @@ totalist@^1.0.0: resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== +tough-cookie@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" + integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + tr46@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz" @@ -8758,6 +9773,13 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" +tr46@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" + integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== + dependencies: + punycode "^2.1.1" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" @@ -8768,6 +9790,20 @@ treeverse@^1.0.4: resolved "https://registry.npmjs.org/treeverse/-/treeverse-1.0.4.tgz" integrity sha512-whw60l7r+8ZU8Tu/Uc2yxtc4ZTZbR/PF3u1IPNKGQ6p8EICLb3Z2lAgoqw9bqYd8IkgnsaOcLzYHFckjqNsf0g== +ts-jest@^29.1.0: + version "29.1.0" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.0.tgz#4a9db4104a49b76d2b368ea775b6c9535c603891" + integrity sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "4.x" + make-error "1.x" + semver "7.x" + yargs-parser "^21.0.1" + ts-loader@^9.4.2: version "9.4.2" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.4.2.tgz#80a45eee92dd5170b900b3d00abcfa14949aeb78" @@ -8854,7 +9890,14 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@^4.0.0, type-detect@^4.0.5: +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5: version "4.0.8" resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== @@ -8998,6 +10041,11 @@ universal-user-agent@^6.0.0: resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + universalify@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" @@ -9033,6 +10081,14 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + use-composed-ref@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz" @@ -9090,6 +10146,15 @@ v8-compile-cache-lib@^3.0.1: resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== +v8-to-istanbul@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" + integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" @@ -9133,11 +10198,25 @@ vinyl@^2.0.1: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" +w3c-xmlserializer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" + integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== + dependencies: + xml-name-validator "^4.0.0" + walk-up-path@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/walk-up-path/-/walk-up-path-1.0.0.tgz" integrity sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg== +walker@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + watchpack@^2.4.0: version "2.4.0" resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz" @@ -9170,6 +10249,11 @@ webidl-conversions@^4.0.2: resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + webpack-bundle-analyzer@^4.8.0: version "4.8.0" resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.8.0.tgz#951b8aaf491f665d2ae325d8b84da229157b1d04" @@ -9347,6 +10431,26 @@ websocket-extensions@>=0.1.1: resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" + integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" @@ -9414,7 +10518,7 @@ wildcard@^2.0.0: resolved "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz" integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== -word-wrap@^1.2.3: +word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== @@ -9616,7 +10720,7 @@ wrappy@1: resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -write-file-atomic@^4.0.0: +write-file-atomic@^4.0.0, write-file-atomic@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== @@ -9629,11 +10733,21 @@ ws@^7.3.1: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== -ws@^8.12.1, ws@^8.13.0: +ws@^8.11.0, ws@^8.12.1, ws@^8.13.0: version "8.13.0" resolved "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz" integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" @@ -9659,6 +10773,11 @@ yargs-parser@20.2.4, yargs-parser@^20.2.2: resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== +yargs-parser@^21.0.1, yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yargs-unparser@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz" @@ -9682,6 +10801,19 @@ yargs@16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yargs@^17.3.1: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yeoman-environment@^3.9.1: version "3.16.2" resolved "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-3.16.2.tgz" From 8f7a9a1327c38c815af1d3da9af0ad8ed2b412c6 Mon Sep 17 00:00:00 2001 From: Kieran Date: Wed, 24 May 2023 11:12:23 +0100 Subject: [PATCH 04/24] fix tests --- packages/app/jest.config.js | 4 +- .../app/src/Cache/EventInteractionCache.ts | 2 +- packages/app/src/Cache/FeedCache.ts | 2 +- packages/app/src/Cache/index.ts | 2 +- packages/app/src/Element/AvatarEditor.tsx | 2 +- packages/app/src/Element/BadgeList.tsx | 2 +- packages/app/src/Element/DM.tsx | 2 +- packages/app/src/Element/DmWindow.tsx | 2 +- packages/app/src/Element/FollowButton.tsx | 2 +- packages/app/src/Element/HyperText.tsx | 2 +- packages/app/src/Element/Invoice.tsx | 2 +- packages/app/src/Element/MagnetLink.tsx | 2 +- packages/app/src/Element/Mention.tsx | 2 +- packages/app/src/Element/Nip5Service.tsx | 4 +- packages/app/src/Element/NostrFileHeader.tsx | 2 +- packages/app/src/Element/NostrLink.tsx | 2 +- packages/app/src/Element/Note.tsx | 2 +- packages/app/src/Element/NoteCreator.tsx | 2 +- packages/app/src/Element/NoteFooter.tsx | 2 +- packages/app/src/Element/NoteQuote.tsx | 2 +- packages/app/src/Element/NoteReaction.tsx | 2 +- packages/app/src/Element/NoteToSelf.tsx | 2 +- packages/app/src/Element/Poll.tsx | 2 +- packages/app/src/Element/ProfileImage.tsx | 2 +- packages/app/src/Element/ProxyImg.tsx | 2 +- packages/app/src/Element/PubkeyList.tsx | 2 +- packages/app/src/Element/Relay.tsx | 2 +- packages/app/src/Element/SendSats.tsx | 2 +- packages/app/src/Element/SubDebug.tsx | 2 +- .../app/src/Element/SuggestedProfiles.tsx | 2 +- packages/app/src/Element/Text.tsx | 2 +- packages/app/src/Element/Textarea.tsx | 2 +- packages/app/src/Element/Thread.tsx | 2 +- packages/app/src/Element/Timeline.tsx | 2 +- packages/app/src/Element/Username.tsx | 2 +- packages/app/src/Element/WriteDm.tsx | 2 +- packages/app/src/Element/Zap.tsx | 4 +- packages/app/src/Feed/BadgesFeed.ts | 2 +- packages/app/src/Feed/EventFeed.ts | 2 +- packages/app/src/Feed/LoginFeed.ts | 2 +- packages/app/src/Feed/MuteList.ts | 2 +- packages/app/src/Feed/RelaysFeedFollows.tsx | 2 +- packages/app/src/Feed/ThreadFeed.ts | 2 +- packages/app/src/Feed/TimelineFeed.ts | 2 +- packages/app/src/Hooks/useImgProxy.ts | 2 +- .../app/src/Hooks/useInteractionCache.tsx | 2 +- packages/app/src/Hooks/useLoginHandler.tsx | 2 +- packages/app/src/Hooks/useModeration.tsx | 2 +- packages/app/src/Hooks/useRequestBuilder.tsx | 2 +- packages/app/src/LNURL.ts | 2 +- packages/app/src/Login/Functions.ts | 2 +- packages/app/src/Login/MultiAccountStore.ts | 2 +- packages/app/src/Nip05/Verifier.ts | 2 +- packages/app/src/Notifications.ts | 2 +- packages/app/src/Pages/ChatPage.tsx | 2 +- packages/app/src/Pages/DonatePage.tsx | 2 +- packages/app/src/Pages/Layout.tsx | 2 +- packages/app/src/Pages/LoginPage.tsx | 2 +- packages/app/src/Pages/MessagesPage.tsx | 2 +- packages/app/src/Pages/NostrLinkHandler.tsx | 2 +- packages/app/src/Pages/Notifications.tsx | 2 +- packages/app/src/Pages/ProfilePage.tsx | 4 +- packages/app/src/Pages/Root.tsx | 2 +- packages/app/src/Pages/SearchPage.tsx | 2 +- packages/app/src/Pages/WalletPage.tsx | 2 +- packages/app/src/Pages/ZapPool.tsx | 2 +- packages/app/src/Pages/new/ImportFollows.tsx | 2 +- packages/app/src/Pages/new/NewUserFlow.tsx | 2 +- packages/app/src/Pages/settings/Index.tsx | 2 +- packages/app/src/Pages/settings/Keys.tsx | 2 +- .../app/src/Pages/settings/Preferences.tsx | 2 +- packages/app/src/Pages/settings/Profile.tsx | 2 +- packages/app/src/Pages/settings/RelayInfo.tsx | 2 +- packages/app/src/Pages/settings/Relays.tsx | 2 +- .../app/src/Pages/settings/wallet/Cashu.tsx | 2 +- .../app/src/Pages/settings/wallet/LNC.tsx | 2 +- .../app/src/Pages/settings/wallet/LNDHub.tsx | 2 +- .../app/src/Pages/settings/wallet/NWC.tsx | 2 +- .../Utils.test.ts} | 55 ++++++++++++++----- .../app/src/{Util.ts => SnortUtils/index.ts} | 18 +++++- packages/app/src/Subscription/index.ts | 2 +- packages/app/src/System/EventBuilder.ts | 2 +- packages/app/src/System/EventExt.ts | 2 +- packages/app/src/System/EventPublisher.ts | 2 +- packages/app/src/System/GossipModel.ts | 2 +- packages/app/src/System/NoteCollection.ts | 2 +- packages/app/src/System/ProfileCache.ts | 2 +- packages/app/src/System/Query.ts | 2 +- packages/app/src/System/RequestBuilder.ts | 2 +- packages/app/src/System/index.ts | 2 +- packages/app/src/Toaster.tsx | 2 +- packages/app/src/Upload/VoidCat.ts | 2 +- packages/app/src/Upload/index.ts | 2 +- packages/app/src/Wallet/LNCWallet.ts | 2 +- packages/app/src/Wallet/index.ts | 4 +- packages/app/src/ZapPoolController.ts | 2 +- packages/app/src/index.tsx | 2 +- .../app/src/{setupTests.js => setupTests.ts} | 1 - 98 files changed, 158 insertions(+), 116 deletions(-) rename packages/app/src/{Util.test.ts => SnortUtils/Utils.test.ts} (69%) rename packages/app/src/{Util.ts => SnortUtils/index.ts} (96%) rename packages/app/src/{setupTests.js => setupTests.ts} (59%) diff --git a/packages/app/jest.config.js b/packages/app/jest.config.js index 4f0c217a..928edeee 100644 --- a/packages/app/jest.config.js +++ b/packages/app/jest.config.js @@ -3,5 +3,7 @@ module.exports = { bail: true, preset: "ts-jest", testEnvironment: "jsdom", - roots: ["./src/"], + roots: ["src"], + moduleDirectories: ["src", "node_modules"], + setupFiles: ["./src/setupTests.ts"], }; diff --git a/packages/app/src/Cache/EventInteractionCache.ts b/packages/app/src/Cache/EventInteractionCache.ts index 2ea3ac80..1a727c06 100644 --- a/packages/app/src/Cache/EventInteractionCache.ts +++ b/packages/app/src/Cache/EventInteractionCache.ts @@ -1,6 +1,6 @@ import { db, EventInteraction } from "Db"; import { LoginStore } from "Login"; -import { sha256 } from "Util"; +import { sha256 } from "SnortUtils"; import FeedCache from "./FeedCache"; class EventInteractionCache extends FeedCache { diff --git a/packages/app/src/Cache/FeedCache.ts b/packages/app/src/Cache/FeedCache.ts index 10feed3a..4a24f541 100644 --- a/packages/app/src/Cache/FeedCache.ts +++ b/packages/app/src/Cache/FeedCache.ts @@ -1,6 +1,6 @@ import { db } from "Db"; import { Table } from "dexie"; -import { unixNowMs, unwrap } from "Util"; +import { unixNowMs, unwrap } from "SnortUtils"; type HookFn = () => void; diff --git a/packages/app/src/Cache/index.ts b/packages/app/src/Cache/index.ts index cf1bd971..f147ecfa 100644 --- a/packages/app/src/Cache/index.ts +++ b/packages/app/src/Cache/index.ts @@ -1,5 +1,5 @@ import { HexKey, RawEvent, UserMetadata } from "@snort/nostr"; -import { hexToBech32, unixNowMs } from "Util"; +import { hexToBech32, unixNowMs } from "SnortUtils"; import { DmCache } from "./DMCache"; import { InteractionCache } from "./EventInteractionCache"; import { UserCache } from "./UserCache"; diff --git a/packages/app/src/Element/AvatarEditor.tsx b/packages/app/src/Element/AvatarEditor.tsx index 690fc189..b7b5776a 100644 --- a/packages/app/src/Element/AvatarEditor.tsx +++ b/packages/app/src/Element/AvatarEditor.tsx @@ -1,7 +1,7 @@ import Icon from "Icons/Icon"; import { useState } from "react"; import useFileUpload from "Upload"; -import { openFile, unwrap } from "Util"; +import { openFile, unwrap } from "SnortUtils"; interface AvatarEditorProps { picture?: string; diff --git a/packages/app/src/Element/BadgeList.tsx b/packages/app/src/Element/BadgeList.tsx index 9eec2784..83b494f1 100644 --- a/packages/app/src/Element/BadgeList.tsx +++ b/packages/app/src/Element/BadgeList.tsx @@ -9,7 +9,7 @@ import { ProxyImg } from "Element/ProxyImg"; import Icon from "Icons/Icon"; import Modal from "Element/Modal"; import Username from "Element/Username"; -import { findTag } from "Util"; +import { findTag } from "SnortUtils"; export default function BadgeList({ badges }: { badges: TaggedRawEvent[] }) { const [showModal, setShowModal] = useState(false); diff --git a/packages/app/src/Element/DM.tsx b/packages/app/src/Element/DM.tsx index dfc8911b..b893075d 100644 --- a/packages/app/src/Element/DM.tsx +++ b/packages/app/src/Element/DM.tsx @@ -8,7 +8,7 @@ import useEventPublisher from "Feed/EventPublisher"; import NoteTime from "Element/NoteTime"; import Text from "Element/Text"; import { setLastReadDm } from "Pages/MessagesPage"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; import useLogin from "Hooks/useLogin"; import messages from "./messages"; diff --git a/packages/app/src/Element/DmWindow.tsx b/packages/app/src/Element/DmWindow.tsx index 79244a4a..2347e571 100644 --- a/packages/app/src/Element/DmWindow.tsx +++ b/packages/app/src/Element/DmWindow.tsx @@ -9,7 +9,7 @@ import NoteToSelf from "Element/NoteToSelf"; import { useDmCache } from "Hooks/useDmsCache"; import useLogin from "Hooks/useLogin"; import WriteDm from "Element/WriteDm"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; export default function DmWindow({ id }: { id: string }) { const pubKey = useLogin().publicKey; diff --git a/packages/app/src/Element/FollowButton.tsx b/packages/app/src/Element/FollowButton.tsx index 044f20e4..eb20789c 100644 --- a/packages/app/src/Element/FollowButton.tsx +++ b/packages/app/src/Element/FollowButton.tsx @@ -3,7 +3,7 @@ import { FormattedMessage } from "react-intl"; import { HexKey } from "@snort/nostr"; import useEventPublisher from "Feed/EventPublisher"; -import { parseId } from "Util"; +import { parseId } from "SnortUtils"; import useLogin from "Hooks/useLogin"; import AsyncButton from "Element/AsyncButton"; diff --git a/packages/app/src/Element/HyperText.tsx b/packages/app/src/Element/HyperText.tsx index f7439a7c..83de733f 100644 --- a/packages/app/src/Element/HyperText.tsx +++ b/packages/app/src/Element/HyperText.tsx @@ -13,7 +13,7 @@ import { NostrNestsRegex, WavlakeRegex, } from "Const"; -import { magnetURIDecode } from "Util"; +import { magnetURIDecode } from "SnortUtils"; import SoundCloudEmbed from "Element/SoundCloudEmded"; import MixCloudEmbed from "Element/MixCloudEmbed"; import SpotifyEmbed from "Element/SpotifyEmbed"; diff --git a/packages/app/src/Element/Invoice.tsx b/packages/app/src/Element/Invoice.tsx index 2d6d1251..f8704c17 100644 --- a/packages/app/src/Element/Invoice.tsx +++ b/packages/app/src/Element/Invoice.tsx @@ -6,7 +6,7 @@ import { useMemo } from "react"; import SendSats from "Element/SendSats"; import Icon from "Icons/Icon"; import { useWallet } from "Wallet"; -import { decodeInvoice } from "Util"; +import { decodeInvoice } from "SnortUtils"; import messages from "./messages"; diff --git a/packages/app/src/Element/MagnetLink.tsx b/packages/app/src/Element/MagnetLink.tsx index 169a45cf..4dca7d5e 100644 --- a/packages/app/src/Element/MagnetLink.tsx +++ b/packages/app/src/Element/MagnetLink.tsx @@ -1,6 +1,6 @@ import { FormattedMessage } from "react-intl"; -import { Magnet } from "Util"; +import { Magnet } from "SnortUtils"; interface MagnetLinkProps { magnet: Magnet; diff --git a/packages/app/src/Element/Mention.tsx b/packages/app/src/Element/Mention.tsx index add8c869..63783f01 100644 --- a/packages/app/src/Element/Mention.tsx +++ b/packages/app/src/Element/Mention.tsx @@ -3,7 +3,7 @@ import { Link } from "react-router-dom"; import { HexKey } from "@snort/nostr"; import { useUserProfile } from "Hooks/useUserProfile"; -import { profileLink } from "Util"; +import { profileLink } from "SnortUtils"; import { getDisplayName } from "Element/ProfileImage"; export default function Mention({ pubkey, relays }: { pubkey: HexKey; relays?: Array | string }) { diff --git a/packages/app/src/Element/Nip5Service.tsx b/packages/app/src/Element/Nip5Service.tsx index 8feb5428..7bc93d3e 100644 --- a/packages/app/src/Element/Nip5Service.tsx +++ b/packages/app/src/Element/Nip5Service.tsx @@ -3,7 +3,7 @@ import { useIntl, FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; import { UserMetadata } from "@snort/nostr"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; import { formatShort } from "Number"; import { ServiceProvider, @@ -19,7 +19,7 @@ import SendSats from "Element/SendSats"; import Copy from "Element/Copy"; import { useUserProfile } from "Hooks/useUserProfile"; import useEventPublisher from "Feed/EventPublisher"; -import { debounce } from "Util"; +import { debounce } from "SnortUtils"; import useLogin from "Hooks/useLogin"; import SnortServiceProvider from "Nip05/SnortServiceProvider"; import { mapEventToProfile, UserCache } from "Cache"; diff --git a/packages/app/src/Element/NostrFileHeader.tsx b/packages/app/src/Element/NostrFileHeader.tsx index 91bb015b..277d71da 100644 --- a/packages/app/src/Element/NostrFileHeader.tsx +++ b/packages/app/src/Element/NostrFileHeader.tsx @@ -1,7 +1,7 @@ import { FormattedMessage } from "react-intl"; import { RawEvent } from "@snort/nostr"; -import { findTag, NostrLink } from "Util"; +import { findTag, NostrLink } from "SnortUtils"; import useEventFeed from "Feed/EventFeed"; import PageSpinner from "Element/PageSpinner"; import Reveal from "Element/Reveal"; diff --git a/packages/app/src/Element/NostrLink.tsx b/packages/app/src/Element/NostrLink.tsx index c4f2a077..d87abcec 100644 --- a/packages/app/src/Element/NostrLink.tsx +++ b/packages/app/src/Element/NostrLink.tsx @@ -2,7 +2,7 @@ import { Link } from "react-router-dom"; import { NostrPrefix } from "@snort/nostr"; import Mention from "Element/Mention"; -import { parseNostrLink } from "Util"; +import { parseNostrLink } from "SnortUtils"; import NoteQuote from "Element/NoteQuote"; export default function NostrLink({ link, depth }: { link: string; depth?: number }) { diff --git a/packages/app/src/Element/Note.tsx b/packages/app/src/Element/Note.tsx index 273de2ea..03f4f9d0 100644 --- a/packages/app/src/Element/Note.tsx +++ b/packages/app/src/Element/Note.tsx @@ -19,7 +19,7 @@ import { normalizeReaction, Reaction, profileLink, -} from "Util"; +} from "SnortUtils"; import NoteFooter, { Translation } from "Element/NoteFooter"; import NoteTime from "Element/NoteTime"; import Reveal from "Element/Reveal"; diff --git a/packages/app/src/Element/NoteCreator.tsx b/packages/app/src/Element/NoteCreator.tsx index b1e5f59d..df08f85f 100644 --- a/packages/app/src/Element/NoteCreator.tsx +++ b/packages/app/src/Element/NoteCreator.tsx @@ -5,7 +5,7 @@ import { encodeTLV, EventKind, NostrPrefix, TaggedRawEvent } from "@snort/nostr" import Icon from "Icons/Icon"; import useEventPublisher from "Feed/EventPublisher"; -import { openFile } from "Util"; +import { openFile } from "SnortUtils"; import Textarea from "Element/Textarea"; import Modal from "Element/Modal"; import ProfileImage from "Element/ProfileImage"; diff --git a/packages/app/src/Element/NoteFooter.tsx b/packages/app/src/Element/NoteFooter.tsx index 219738bb..6347f37d 100644 --- a/packages/app/src/Element/NoteFooter.tsx +++ b/packages/app/src/Element/NoteFooter.tsx @@ -10,7 +10,7 @@ import Spinner from "Icons/Spinner"; import { formatShort } from "Number"; import useEventPublisher from "Feed/EventPublisher"; -import { delay, normalizeReaction, unwrap } from "Util"; +import { delay, normalizeReaction, unwrap } from "SnortUtils"; import { NoteCreator } from "Element/NoteCreator"; import { ReBroadcaster } from "Element/ReBroadcaster"; import Reactions from "Element/Reactions"; diff --git a/packages/app/src/Element/NoteQuote.tsx b/packages/app/src/Element/NoteQuote.tsx index 75cdc3b9..31cca7ba 100644 --- a/packages/app/src/Element/NoteQuote.tsx +++ b/packages/app/src/Element/NoteQuote.tsx @@ -1,5 +1,5 @@ import useEventFeed from "Feed/EventFeed"; -import { NostrLink } from "Util"; +import { NostrLink } from "SnortUtils"; import Note from "Element/Note"; import PageSpinner from "Element/PageSpinner"; diff --git a/packages/app/src/Element/NoteReaction.tsx b/packages/app/src/Element/NoteReaction.tsx index a9a7132d..aee50f87 100644 --- a/packages/app/src/Element/NoteReaction.tsx +++ b/packages/app/src/Element/NoteReaction.tsx @@ -5,7 +5,7 @@ import { EventKind, RawEvent, TaggedRawEvent, NostrPrefix } from "@snort/nostr"; import Note from "Element/Note"; import ProfileImage from "Element/ProfileImage"; -import { eventLink, hexToBech32 } from "Util"; +import { eventLink, hexToBech32 } from "SnortUtils"; import NoteTime from "Element/NoteTime"; import useModeration from "Hooks/useModeration"; import { EventExt } from "System/EventExt"; diff --git a/packages/app/src/Element/NoteToSelf.tsx b/packages/app/src/Element/NoteToSelf.tsx index 348372dd..b134762a 100644 --- a/packages/app/src/Element/NoteToSelf.tsx +++ b/packages/app/src/Element/NoteToSelf.tsx @@ -1,7 +1,7 @@ import "./NoteToSelf.css"; import { Link, useNavigate } from "react-router-dom"; import { FormattedMessage } from "react-intl"; -import { profileLink } from "Util"; +import { profileLink } from "SnortUtils"; import messages from "./messages"; import Icon from "Icons/Icon"; diff --git a/packages/app/src/Element/Poll.tsx b/packages/app/src/Element/Poll.tsx index 0316c3f6..cb44e29b 100644 --- a/packages/app/src/Element/Poll.tsx +++ b/packages/app/src/Element/Poll.tsx @@ -8,7 +8,7 @@ import useEventPublisher from "Feed/EventPublisher"; import { useWallet } from "Wallet"; import { useUserProfile } from "Hooks/useUserProfile"; import { LNURL } from "LNURL"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; import { formatShort } from "Number"; import Spinner from "Icons/Spinner"; import SendSats from "Element/SendSats"; diff --git a/packages/app/src/Element/ProfileImage.tsx b/packages/app/src/Element/ProfileImage.tsx index ec3e017c..184e5e0a 100644 --- a/packages/app/src/Element/ProfileImage.tsx +++ b/packages/app/src/Element/ProfileImage.tsx @@ -4,7 +4,7 @@ import React, { useMemo } from "react"; import { HexKey, NostrPrefix } from "@snort/nostr"; import { useUserProfile } from "Hooks/useUserProfile"; -import { hexToBech32, profileLink } from "Util"; +import { hexToBech32, profileLink } from "SnortUtils"; import Avatar from "Element/Avatar"; import Nip05 from "Element/Nip05"; import { MetadataCache } from "Cache"; diff --git a/packages/app/src/Element/ProxyImg.tsx b/packages/app/src/Element/ProxyImg.tsx index 05c27e5f..93782da3 100644 --- a/packages/app/src/Element/ProxyImg.tsx +++ b/packages/app/src/Element/ProxyImg.tsx @@ -1,7 +1,7 @@ import useImgProxy from "Hooks/useImgProxy"; import { useEffect, useState } from "react"; import { FormattedMessage } from "react-intl"; -import { getUrlHostname } from "Util"; +import { getUrlHostname } from "SnortUtils"; interface ProxyImgProps extends React.DetailedHTMLProps, HTMLImageElement> { size?: number; diff --git a/packages/app/src/Element/PubkeyList.tsx b/packages/app/src/Element/PubkeyList.tsx index df415ab2..5ef6fa39 100644 --- a/packages/app/src/Element/PubkeyList.tsx +++ b/packages/app/src/Element/PubkeyList.tsx @@ -1,5 +1,5 @@ import { RawEvent } from "@snort/nostr"; -import { dedupe } from "Util"; +import { dedupe } from "SnortUtils"; import FollowListBase from "./FollowListBase"; export default function PubkeyList({ ev, className }: { ev: RawEvent; className?: string }) { diff --git a/packages/app/src/Element/Relay.tsx b/packages/app/src/Element/Relay.tsx index 5d9e6afc..6c4d898f 100644 --- a/packages/app/src/Element/Relay.tsx +++ b/packages/app/src/Element/Relay.tsx @@ -6,7 +6,7 @@ import { RelaySettings } from "@snort/nostr"; import useRelayState from "Feed/RelayState"; import { System } from "System"; -import { getRelayName, unixNowMs, unwrap } from "Util"; +import { getRelayName, unixNowMs, unwrap } from "SnortUtils"; import useLogin from "Hooks/useLogin"; import { setRelays } from "Login"; import Icon from "Icons/Icon"; diff --git a/packages/app/src/Element/SendSats.tsx b/packages/app/src/Element/SendSats.tsx index 60ac0949..cbb3dde2 100644 --- a/packages/app/src/Element/SendSats.tsx +++ b/packages/app/src/Element/SendSats.tsx @@ -11,7 +11,7 @@ import Modal from "Element/Modal"; import QrCode from "Element/QrCode"; import Copy from "Element/Copy"; import { LNURL, LNURLError, LNURLErrorCode, LNURLInvoice, LNURLSuccessAction } from "LNURL"; -import { chunks, debounce } from "Util"; +import { chunks, debounce } from "SnortUtils"; import { useWallet } from "Wallet"; import useLogin from "Hooks/useLogin"; import { generateRandomKey } from "Login"; diff --git a/packages/app/src/Element/SubDebug.tsx b/packages/app/src/Element/SubDebug.tsx index 0f1de9ce..db948ca6 100644 --- a/packages/app/src/Element/SubDebug.tsx +++ b/packages/app/src/Element/SubDebug.tsx @@ -4,7 +4,7 @@ import { useState } from "react"; import useRelayState from "Feed/RelayState"; import Tabs, { Tab } from "Element/Tabs"; import { System } from "System"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; import useSystemState from "Hooks/useSystemState"; import { RawReqFilter } from "@snort/nostr"; import { useCopy } from "useCopy"; diff --git a/packages/app/src/Element/SuggestedProfiles.tsx b/packages/app/src/Element/SuggestedProfiles.tsx index 319befe7..5449260d 100644 --- a/packages/app/src/Element/SuggestedProfiles.tsx +++ b/packages/app/src/Element/SuggestedProfiles.tsx @@ -7,7 +7,7 @@ import PageSpinner from "Element/PageSpinner"; import NostrBandApi from "External/NostrBand"; import SemisolDevApi from "External/SemisolDev"; import useLogin from "Hooks/useLogin"; -import { hexToBech32 } from "Util"; +import { hexToBech32 } from "SnortUtils"; enum Provider { NostrBand = 1, diff --git a/packages/app/src/Element/Text.tsx b/packages/app/src/Element/Text.tsx index 68017aad..22ae3cf7 100644 --- a/packages/app/src/Element/Text.tsx +++ b/packages/app/src/Element/Text.tsx @@ -4,7 +4,7 @@ import { Link, useLocation } from "react-router-dom"; import { HexKey, NostrPrefix } from "@snort/nostr"; import { MentionRegex, InvoiceRegex, HashtagRegex, CashuRegex } from "Const"; -import { eventLink, hexToBech32, splitByUrl, validateNostrLink } from "Util"; +import { eventLink, hexToBech32, splitByUrl, validateNostrLink } from "SnortUtils"; import Invoice from "Element/Invoice"; import Hashtag from "Element/Hashtag"; import Mention from "Element/Mention"; diff --git a/packages/app/src/Element/Textarea.tsx b/packages/app/src/Element/Textarea.tsx index fff173bb..ed22224a 100644 --- a/packages/app/src/Element/Textarea.tsx +++ b/packages/app/src/Element/Textarea.tsx @@ -8,7 +8,7 @@ import { NostrPrefix } from "@snort/nostr"; import Avatar from "Element/Avatar"; import Nip05 from "Element/Nip05"; -import { hexToBech32 } from "Util"; +import { hexToBech32 } from "SnortUtils"; import { MetadataCache } from "Cache"; import { UserCache } from "Cache/UserCache"; diff --git a/packages/app/src/Element/Thread.tsx b/packages/app/src/Element/Thread.tsx index 8e650e31..8013d027 100644 --- a/packages/app/src/Element/Thread.tsx +++ b/packages/app/src/Element/Thread.tsx @@ -5,7 +5,7 @@ import { useNavigate, useLocation, Link, useParams } from "react-router-dom"; import { TaggedRawEvent, u256, EventKind, NostrPrefix } from "@snort/nostr"; import { EventExt, Thread as ThreadInfo } from "System/EventExt"; -import { eventLink, unwrap, getReactions, parseNostrLink, getAllReactions, findTag } from "Util"; +import { eventLink, unwrap, getReactions, parseNostrLink, getAllReactions, findTag } from "SnortUtils"; import BackButton from "Element/BackButton"; import Note from "Element/Note"; import NoteGhost from "Element/NoteGhost"; diff --git a/packages/app/src/Element/Timeline.tsx b/packages/app/src/Element/Timeline.tsx index 8fb9644f..039fed06 100644 --- a/packages/app/src/Element/Timeline.tsx +++ b/packages/app/src/Element/Timeline.tsx @@ -5,7 +5,7 @@ import { useInView } from "react-intersection-observer"; import { TaggedRawEvent, EventKind, u256 } from "@snort/nostr"; import Icon from "Icons/Icon"; -import { dedupeByPubkey, findTag, tagFilterOfTextRepost } from "Util"; +import { dedupeByPubkey, findTag, tagFilterOfTextRepost } from "SnortUtils"; import ProfileImage from "Element/ProfileImage"; import useTimelineFeed, { TimelineFeed, TimelineSubject } from "Feed/TimelineFeed"; import LoadMore from "Element/LoadMore"; diff --git a/packages/app/src/Element/Username.tsx b/packages/app/src/Element/Username.tsx index 989890f9..76b5078f 100644 --- a/packages/app/src/Element/Username.tsx +++ b/packages/app/src/Element/Username.tsx @@ -4,7 +4,7 @@ import { useNavigate, Link } from "react-router-dom"; import { HexKey } from "@snort/nostr"; import { useUserProfile } from "Hooks/useUserProfile"; -import { profileLink } from "Util"; +import { profileLink } from "SnortUtils"; export default function Username({ pubkey, onLinkVisit }: { pubkey: HexKey; onLinkVisit(): void }) { const user = useUserProfile(pubkey); diff --git a/packages/app/src/Element/WriteDm.tsx b/packages/app/src/Element/WriteDm.tsx index eb9a25ea..37228edb 100644 --- a/packages/app/src/Element/WriteDm.tsx +++ b/packages/app/src/Element/WriteDm.tsx @@ -4,7 +4,7 @@ import Icon from "Icons/Icon"; import Spinner from "Icons/Spinner"; import { useState } from "react"; import useFileUpload from "Upload"; -import { openFile } from "Util"; +import { openFile } from "SnortUtils"; import Textarea from "./Textarea"; export default function WriteDm({ chatPubKey }: { chatPubKey: string }) { diff --git a/packages/app/src/Element/Zap.tsx b/packages/app/src/Element/Zap.tsx index 106e7727..4916ccaf 100644 --- a/packages/app/src/Element/Zap.tsx +++ b/packages/app/src/Element/Zap.tsx @@ -3,11 +3,11 @@ import { useMemo } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { HexKey, TaggedRawEvent } from "@snort/nostr"; -import { decodeInvoice, InvoiceDetails, sha256, unwrap } from "Util"; +import { decodeInvoice, InvoiceDetails, sha256, unwrap } from "SnortUtils"; import { formatShort } from "Number"; import Text from "Element/Text"; import ProfileImage from "Element/ProfileImage"; -import { findTag } from "Util"; +import { findTag } from "SnortUtils"; import { UserCache } from "Cache/UserCache"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Feed/BadgesFeed.ts b/packages/app/src/Feed/BadgesFeed.ts index a9e55c7a..194150ac 100644 --- a/packages/app/src/Feed/BadgesFeed.ts +++ b/packages/app/src/Feed/BadgesFeed.ts @@ -1,7 +1,7 @@ import { useMemo } from "react"; import { EventKind, HexKey, Lists } from "@snort/nostr"; -import { unwrap, findTag, chunks } from "Util"; +import { unwrap, findTag, chunks } from "SnortUtils"; import { RequestBuilder } from "System"; import { FlatNoteStore, ReplaceableNoteStore } from "System/NoteCollection"; import useRequestBuilder from "Hooks/useRequestBuilder"; diff --git a/packages/app/src/Feed/EventFeed.ts b/packages/app/src/Feed/EventFeed.ts index 359dbee8..fca62014 100644 --- a/packages/app/src/Feed/EventFeed.ts +++ b/packages/app/src/Feed/EventFeed.ts @@ -3,7 +3,7 @@ import { NostrPrefix } from "@snort/nostr"; import useRequestBuilder from "Hooks/useRequestBuilder"; import { RequestBuilder, ReplaceableNoteStore } from "System"; -import { NostrLink, unwrap } from "Util"; +import { NostrLink, unwrap } from "SnortUtils"; export default function useEventFeed(link: NostrLink) { const sub = useMemo(() => { diff --git a/packages/app/src/Feed/LoginFeed.ts b/packages/app/src/Feed/LoginFeed.ts index 8f0be3c7..fab34810 100644 --- a/packages/app/src/Feed/LoginFeed.ts +++ b/packages/app/src/Feed/LoginFeed.ts @@ -1,7 +1,7 @@ import { useEffect, useMemo } from "react"; import { TaggedRawEvent, Lists, EventKind } from "@snort/nostr"; -import { bech32ToHex, getNewest, getNewestEventTagsByKey, unwrap } from "Util"; +import { bech32ToHex, getNewest, getNewestEventTagsByKey, unwrap } from "SnortUtils"; import { makeNotification, sendNotification } from "Notifications"; import useEventPublisher from "Feed/EventPublisher"; import { getMutedKeys } from "Feed/MuteList"; diff --git a/packages/app/src/Feed/MuteList.ts b/packages/app/src/Feed/MuteList.ts index 4d551448..d7ac989e 100644 --- a/packages/app/src/Feed/MuteList.ts +++ b/packages/app/src/Feed/MuteList.ts @@ -1,7 +1,7 @@ import { useMemo } from "react"; import { HexKey, TaggedRawEvent, Lists, EventKind } from "@snort/nostr"; -import { getNewest } from "Util"; +import { getNewest } from "SnortUtils"; import { ParameterizedReplaceableNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Feed/RelaysFeedFollows.tsx b/packages/app/src/Feed/RelaysFeedFollows.tsx index cf5ea5c5..90bfb0f3 100644 --- a/packages/app/src/Feed/RelaysFeedFollows.tsx +++ b/packages/app/src/Feed/RelaysFeedFollows.tsx @@ -1,7 +1,7 @@ import { useMemo } from "react"; import { HexKey, FullRelaySettings, TaggedRawEvent, RelaySettings, EventKind } from "@snort/nostr"; -import { sanitizeRelayUrl } from "Util"; +import { sanitizeRelayUrl } from "SnortUtils"; import { PubkeyReplaceableNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; diff --git a/packages/app/src/Feed/ThreadFeed.ts b/packages/app/src/Feed/ThreadFeed.ts index 84df5db7..9d36cbe3 100644 --- a/packages/app/src/Feed/ThreadFeed.ts +++ b/packages/app/src/Feed/ThreadFeed.ts @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState } from "react"; import { u256, EventKind } from "@snort/nostr"; -import { appendDedupe, NostrLink } from "Util"; +import { appendDedupe, NostrLink } from "SnortUtils"; import { FlatNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Feed/TimelineFeed.ts b/packages/app/src/Feed/TimelineFeed.ts index da814655..f4edf359 100644 --- a/packages/app/src/Feed/TimelineFeed.ts +++ b/packages/app/src/Feed/TimelineFeed.ts @@ -1,7 +1,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { EventKind, u256 } from "@snort/nostr"; -import { unixNow, unwrap, tagFilterOfTextRepost } from "Util"; +import { unixNow, unwrap, tagFilterOfTextRepost } from "SnortUtils"; import { FlatNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; import useTimelineWindow from "Hooks/useTimelineWindow"; diff --git a/packages/app/src/Hooks/useImgProxy.ts b/packages/app/src/Hooks/useImgProxy.ts index 3cf5d4bf..de44acf6 100644 --- a/packages/app/src/Hooks/useImgProxy.ts +++ b/packages/app/src/Hooks/useImgProxy.ts @@ -1,6 +1,6 @@ import * as utils from "@noble/curves/abstract/utils"; import * as base64 from "@protobufjs/base64"; -import { hmacSha256, unwrap } from "Util"; +import { hmacSha256, unwrap } from "SnortUtils"; import useLogin from "Hooks/useLogin"; export interface ImgProxySettings { diff --git a/packages/app/src/Hooks/useInteractionCache.tsx b/packages/app/src/Hooks/useInteractionCache.tsx index d2aa74fe..999331e2 100644 --- a/packages/app/src/Hooks/useInteractionCache.tsx +++ b/packages/app/src/Hooks/useInteractionCache.tsx @@ -3,7 +3,7 @@ import { HexKey, u256 } from "@snort/nostr"; import { InteractionCache } from "Cache/EventInteractionCache"; import { EventInteraction } from "Db"; -import { sha256, unwrap } from "Util"; +import { sha256, unwrap } from "SnortUtils"; export function useInteractionCache(pubkey?: HexKey, event?: u256) { const id = event && pubkey ? sha256(event + pubkey) : undefined; diff --git a/packages/app/src/Hooks/useLoginHandler.tsx b/packages/app/src/Hooks/useLoginHandler.tsx index 94865c5c..aa7a3b8f 100644 --- a/packages/app/src/Hooks/useLoginHandler.tsx +++ b/packages/app/src/Hooks/useLoginHandler.tsx @@ -4,7 +4,7 @@ import { EmailRegex, MnemonicRegex } from "Const"; import { LoginStore } from "Login"; import { generateBip39Entropy, entropyToPrivateKey } from "nip6"; import { getNip05PubKey } from "Pages/LoginPage"; -import { bech32ToHex } from "Util"; +import { bech32ToHex } from "SnortUtils"; export default function useLoginHandler() { const { formatMessage } = useIntl(); diff --git a/packages/app/src/Hooks/useModeration.tsx b/packages/app/src/Hooks/useModeration.tsx index ce20ad48..f4532240 100644 --- a/packages/app/src/Hooks/useModeration.tsx +++ b/packages/app/src/Hooks/useModeration.tsx @@ -2,7 +2,7 @@ import { HexKey } from "@snort/nostr"; import useEventPublisher from "Feed/EventPublisher"; import useLogin from "Hooks/useLogin"; import { setBlocked, setMuted } from "Login"; -import { appendDedupe } from "Util"; +import { appendDedupe } from "SnortUtils"; export default function useModeration() { const login = useLogin(); diff --git a/packages/app/src/Hooks/useRequestBuilder.tsx b/packages/app/src/Hooks/useRequestBuilder.tsx index a1bba65a..cf8ae450 100644 --- a/packages/app/src/Hooks/useRequestBuilder.tsx +++ b/packages/app/src/Hooks/useRequestBuilder.tsx @@ -1,7 +1,7 @@ import { useSyncExternalStore } from "react"; import { RequestBuilder, System } from "System"; import { EmptySnapshot, NoteStore, StoreSnapshot } from "System/NoteCollection"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; const useRequestBuilder = >( type: { new (): TStore }, diff --git a/packages/app/src/LNURL.ts b/packages/app/src/LNURL.ts index 2f80f692..1c3c3e01 100644 --- a/packages/app/src/LNURL.ts +++ b/packages/app/src/LNURL.ts @@ -1,6 +1,6 @@ import { HexKey, RawEvent } from "@snort/nostr"; import { EmailRegex } from "Const"; -import { bech32ToText, unwrap } from "Util"; +import { bech32ToText, unwrap } from "SnortUtils"; const PayServiceTag = "payRequest"; diff --git a/packages/app/src/Login/Functions.ts b/packages/app/src/Login/Functions.ts index 9be5e0a7..112b3fbd 100644 --- a/packages/app/src/Login/Functions.ts +++ b/packages/app/src/Login/Functions.ts @@ -5,7 +5,7 @@ import * as utils from "@noble/curves/abstract/utils"; import { DefaultRelays, SnortPubKey } from "Const"; import { LoginStore, UserPreferences, LoginSession } from "Login"; import { generateBip39Entropy, entropyToPrivateKey } from "nip6"; -import { bech32ToHex, dedupeById, randomSample, sanitizeRelayUrl, unixNowMs, unwrap } from "Util"; +import { bech32ToHex, dedupeById, randomSample, sanitizeRelayUrl, unixNowMs, unwrap } from "SnortUtils"; import { SubscriptionEvent } from "Subscription"; import { EventPublisher } from "System/EventPublisher"; diff --git a/packages/app/src/Login/MultiAccountStore.ts b/packages/app/src/Login/MultiAccountStore.ts index 916833a4..a7c5d5be 100644 --- a/packages/app/src/Login/MultiAccountStore.ts +++ b/packages/app/src/Login/MultiAccountStore.ts @@ -6,7 +6,7 @@ import { HexKey, RelaySettings } from "@snort/nostr"; import { DefaultRelays } from "Const"; import ExternalStore from "ExternalStore"; import { LoginSession } from "Login"; -import { deepClone, sanitizeRelayUrl, unwrap } from "Util"; +import { deepClone, sanitizeRelayUrl, unwrap } from "SnortUtils"; import { DefaultPreferences, UserPreferences } from "./Preferences"; const AccountStoreKey = "sessions"; diff --git a/packages/app/src/Nip05/Verifier.ts b/packages/app/src/Nip05/Verifier.ts index 5d02204f..51b23920 100644 --- a/packages/app/src/Nip05/Verifier.ts +++ b/packages/app/src/Nip05/Verifier.ts @@ -1,5 +1,5 @@ import DnsOverHttpResolver from "dns-over-http-resolver"; -import { bech32ToHex } from "Util"; +import { bech32ToHex } from "SnortUtils"; const resolver = new DnsOverHttpResolver(); interface NostrJson { diff --git a/packages/app/src/Notifications.ts b/packages/app/src/Notifications.ts index 5dc28164..01a6b746 100644 --- a/packages/app/src/Notifications.ts +++ b/packages/app/src/Notifications.ts @@ -5,7 +5,7 @@ import { EventKind } from "@snort/nostr"; import { MetadataCache } from "Cache"; import { getDisplayName } from "Element/ProfileImage"; import { MentionRegex } from "Const"; -import { tagFilterOfTextRepost, unwrap } from "Util"; +import { tagFilterOfTextRepost, unwrap } from "SnortUtils"; import { UserCache } from "Cache/UserCache"; import { LoginSession } from "Login"; diff --git a/packages/app/src/Pages/ChatPage.tsx b/packages/app/src/Pages/ChatPage.tsx index 9c15b003..d1f3e691 100644 --- a/packages/app/src/Pages/ChatPage.tsx +++ b/packages/app/src/Pages/ChatPage.tsx @@ -1,6 +1,6 @@ import DmWindow from "Element/DmWindow"; import { useParams } from "react-router-dom"; -import { bech32ToHex } from "Util"; +import { bech32ToHex } from "SnortUtils"; import "./ChatPage.css"; diff --git a/packages/app/src/Pages/DonatePage.tsx b/packages/app/src/Pages/DonatePage.tsx index 57be4a64..f5334df3 100644 --- a/packages/app/src/Pages/DonatePage.tsx +++ b/packages/app/src/Pages/DonatePage.tsx @@ -5,7 +5,7 @@ import { HexKey } from "@snort/nostr"; import { ApiHost, KieranPubKey, SnortPubKey } from "Const"; import ProfilePreview from "Element/ProfilePreview"; import ZapButton from "Element/ZapButton"; -import { bech32ToHex } from "Util"; +import { bech32ToHex } from "SnortUtils"; import SnortApi, { RevenueSplit, RevenueToday } from "SnortApi"; const Developers = [ diff --git a/packages/app/src/Pages/Layout.tsx b/packages/app/src/Pages/Layout.tsx index 415299a8..f57d9c51 100644 --- a/packages/app/src/Pages/Layout.tsx +++ b/packages/app/src/Pages/Layout.tsx @@ -23,7 +23,7 @@ import { mapPlanName } from "./subscribe"; import useLogin from "Hooks/useLogin"; import Avatar from "Element/Avatar"; import { useUserProfile } from "Hooks/useUserProfile"; -import { profileLink } from "Util"; +import { profileLink } from "SnortUtils"; import { getCurrentSubscription } from "Subscription"; import Toaster from "Toaster"; diff --git a/packages/app/src/Pages/LoginPage.tsx b/packages/app/src/Pages/LoginPage.tsx index 89165021..3f64c537 100644 --- a/packages/app/src/Pages/LoginPage.tsx +++ b/packages/app/src/Pages/LoginPage.tsx @@ -5,7 +5,7 @@ import { useNavigate } from "react-router-dom"; import { useIntl, FormattedMessage } from "react-intl"; import { HexKey } from "@snort/nostr"; -import { bech32ToHex, unwrap } from "Util"; +import { bech32ToHex, unwrap } from "SnortUtils"; import ZapButton from "Element/ZapButton"; import useImgProxy from "Hooks/useImgProxy"; import Icon from "Icons/Icon"; diff --git a/packages/app/src/Pages/MessagesPage.tsx b/packages/app/src/Pages/MessagesPage.tsx index 30fed4e7..15ca8d5f 100644 --- a/packages/app/src/Pages/MessagesPage.tsx +++ b/packages/app/src/Pages/MessagesPage.tsx @@ -5,7 +5,7 @@ import { HexKey, RawEvent, NostrPrefix } from "@snort/nostr"; import UnreadCount from "Element/UnreadCount"; import ProfileImage, { getDisplayName } from "Element/ProfileImage"; -import { dedupe, hexToBech32, unwrap } from "Util"; +import { dedupe, hexToBech32, unwrap } from "SnortUtils"; import NoteToSelf from "Element/NoteToSelf"; import useModeration from "Hooks/useModeration"; import { useDmCache } from "Hooks/useDmsCache"; diff --git a/packages/app/src/Pages/NostrLinkHandler.tsx b/packages/app/src/Pages/NostrLinkHandler.tsx index 195f0867..7c977ab6 100644 --- a/packages/app/src/Pages/NostrLinkHandler.tsx +++ b/packages/app/src/Pages/NostrLinkHandler.tsx @@ -4,7 +4,7 @@ import { FormattedMessage } from "react-intl"; import { useNavigate, useParams } from "react-router-dom"; import Spinner from "Icons/Spinner"; -import { parseNostrLink, profileLink } from "Util"; +import { parseNostrLink, profileLink } from "SnortUtils"; import { getNip05PubKey } from "Pages/LoginPage"; import { System } from "System"; diff --git a/packages/app/src/Pages/Notifications.tsx b/packages/app/src/Pages/Notifications.tsx index 9cf9907c..c5a83077 100644 --- a/packages/app/src/Pages/Notifications.tsx +++ b/packages/app/src/Pages/Notifications.tsx @@ -4,7 +4,7 @@ import Timeline from "Element/Timeline"; import { TaskList } from "Tasks/TaskList"; import useLogin from "Hooks/useLogin"; import { markNotificationsRead } from "Login"; -import { unixNow } from "Util"; +import { unixNow } from "SnortUtils"; export default function NotificationsPage() { const login = useLogin(); diff --git a/packages/app/src/Pages/ProfilePage.tsx b/packages/app/src/Pages/ProfilePage.tsx index 1db6305c..c58fc6b7 100644 --- a/packages/app/src/Pages/ProfilePage.tsx +++ b/packages/app/src/Pages/ProfilePage.tsx @@ -4,7 +4,7 @@ import { useIntl, FormattedMessage } from "react-intl"; import { useNavigate, useParams } from "react-router-dom"; import { encodeTLV, EventKind, HexKey, NostrPrefix } from "@snort/nostr"; -import { parseNostrLink, getReactions, unwrap } from "Util"; +import { parseNostrLink, getReactions, unwrap } from "SnortUtils"; import { formatShort } from "Number"; import Note from "Element/Note"; import Bookmarks from "Element/Bookmarks"; @@ -23,7 +23,7 @@ import useModeration from "Hooks/useModeration"; import useZapsFeed from "Feed/ZapsFeed"; import { default as ZapElement } from "Element/Zap"; import FollowButton from "Element/FollowButton"; -import { parseId, hexToBech32 } from "Util"; +import { parseId, hexToBech32 } from "SnortUtils"; import Avatar from "Element/Avatar"; import Timeline from "Element/Timeline"; import Text from "Element/Text"; diff --git a/packages/app/src/Pages/Root.tsx b/packages/app/src/Pages/Root.tsx index 0ac7cc2e..2ca863b8 100644 --- a/packages/app/src/Pages/Root.tsx +++ b/packages/app/src/Pages/Root.tsx @@ -7,7 +7,7 @@ import Tabs, { Tab } from "Element/Tabs"; import Timeline from "Element/Timeline"; import { System } from "System"; import { TimelineSubject } from "Feed/TimelineFeed"; -import { debounce, getRelayName, sha256, unixNow, unwrap } from "Util"; +import { debounce, getRelayName, sha256, unixNow, unwrap } from "SnortUtils"; import useLogin from "Hooks/useLogin"; import Discover from "Pages/Discover"; diff --git a/packages/app/src/Pages/SearchPage.tsx b/packages/app/src/Pages/SearchPage.tsx index 09ef2e2b..7e18cd66 100644 --- a/packages/app/src/Pages/SearchPage.tsx +++ b/packages/app/src/Pages/SearchPage.tsx @@ -3,7 +3,7 @@ import { useParams } from "react-router-dom"; import Timeline from "Element/Timeline"; import { Tab, TabElement } from "Element/Tabs"; import { useEffect, useState } from "react"; -import { debounce } from "Util"; +import { debounce } from "SnortUtils"; import { router } from "index"; import { SearchRelays } from "Const"; import { System } from "System"; diff --git a/packages/app/src/Pages/WalletPage.tsx b/packages/app/src/Pages/WalletPage.tsx index e0861694..e6ca2f92 100644 --- a/packages/app/src/Pages/WalletPage.tsx +++ b/packages/app/src/Pages/WalletPage.tsx @@ -7,7 +7,7 @@ import { FormattedMessage, FormattedNumber, useIntl } from "react-intl"; import NoteTime from "Element/NoteTime"; import { WalletInvoice, Sats, WalletInfo, WalletInvoiceState, useWallet, LNWallet, Wallets } from "Wallet"; import AsyncButton from "Element/AsyncButton"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; import { WebLNWallet } from "Wallet/WebLN"; import Icon from "Icons/Icon"; diff --git a/packages/app/src/Pages/ZapPool.tsx b/packages/app/src/Pages/ZapPool.tsx index 27935a8a..ca753c0d 100644 --- a/packages/app/src/Pages/ZapPool.tsx +++ b/packages/app/src/Pages/ZapPool.tsx @@ -8,7 +8,7 @@ import ProfilePreview from "Element/ProfilePreview"; import useLogin from "Hooks/useLogin"; import { System } from "System"; import { UploaderServices } from "Upload"; -import { bech32ToHex, getRelayName, unwrap } from "Util"; +import { bech32ToHex, getRelayName, unwrap } from "SnortUtils"; import { ZapPoolController, ZapPoolRecipient, ZapPoolRecipientType } from "ZapPoolController"; import { useUserProfile } from "Hooks/useUserProfile"; import AsyncButton from "Element/AsyncButton"; diff --git a/packages/app/src/Pages/new/ImportFollows.tsx b/packages/app/src/Pages/new/ImportFollows.tsx index f00fb0b2..3a1654c5 100644 --- a/packages/app/src/Pages/new/ImportFollows.tsx +++ b/packages/app/src/Pages/new/ImportFollows.tsx @@ -6,7 +6,7 @@ import { ApiHost } from "Const"; import Logo from "Element/Logo"; import AsyncButton from "Element/AsyncButton"; import FollowListBase from "Element/FollowListBase"; -import { bech32ToHex } from "Util"; +import { bech32ToHex } from "SnortUtils"; import SnortApi from "SnortApi"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Pages/new/NewUserFlow.tsx b/packages/app/src/Pages/new/NewUserFlow.tsx index 528f9da5..07ad76c7 100644 --- a/packages/app/src/Pages/new/NewUserFlow.tsx +++ b/packages/app/src/Pages/new/NewUserFlow.tsx @@ -4,7 +4,7 @@ import { useNavigate } from "react-router-dom"; import Logo from "Element/Logo"; import { CollapsedSection } from "Element/Collapsed"; import Copy from "Element/Copy"; -import { hexToBech32 } from "Util"; +import { hexToBech32 } from "SnortUtils"; import { hexToMnemonic } from "nip6"; import useLogin from "Hooks/useLogin"; import { PROFILE } from "."; diff --git a/packages/app/src/Pages/settings/Index.tsx b/packages/app/src/Pages/settings/Index.tsx index 7c929d41..202a96c3 100644 --- a/packages/app/src/Pages/settings/Index.tsx +++ b/packages/app/src/Pages/settings/Index.tsx @@ -4,7 +4,7 @@ import { useNavigate } from "react-router-dom"; import Icon from "Icons/Icon"; import { LoginStore, logout } from "Login"; import useLogin from "Hooks/useLogin"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; import { getCurrentSubscription } from "Subscription"; import { CollapsedSection } from "Element/Collapsed"; diff --git a/packages/app/src/Pages/settings/Keys.tsx b/packages/app/src/Pages/settings/Keys.tsx index e5220cf1..6882fdde 100644 --- a/packages/app/src/Pages/settings/Keys.tsx +++ b/packages/app/src/Pages/settings/Keys.tsx @@ -5,7 +5,7 @@ import { encodeTLV, NostrPrefix } from "@snort/nostr"; import Copy from "Element/Copy"; import useLogin from "Hooks/useLogin"; import { hexToMnemonic } from "nip6"; -import { hexToBech32 } from "Util"; +import { hexToBech32 } from "SnortUtils"; export default function ExportKeys() { const { publicKey, privateKey, generatedEntropy } = useLogin(); diff --git a/packages/app/src/Pages/settings/Preferences.tsx b/packages/app/src/Pages/settings/Preferences.tsx index 51be7be8..03b337ea 100644 --- a/packages/app/src/Pages/settings/Preferences.tsx +++ b/packages/app/src/Pages/settings/Preferences.tsx @@ -5,7 +5,7 @@ import { useEffect, useState } from "react"; import useLogin from "Hooks/useLogin"; import { DefaultPreferences, updatePreferences, UserPreferences } from "Login"; import { DefaultImgProxy } from "Const"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; import messages from "./messages"; diff --git a/packages/app/src/Pages/settings/Profile.tsx b/packages/app/src/Pages/settings/Profile.tsx index 87eaa9b8..5ce684e4 100644 --- a/packages/app/src/Pages/settings/Profile.tsx +++ b/packages/app/src/Pages/settings/Profile.tsx @@ -6,7 +6,7 @@ import { useNavigate } from "react-router-dom"; import useEventPublisher from "Feed/EventPublisher"; import { useUserProfile } from "Hooks/useUserProfile"; -import { openFile } from "Util"; +import { openFile } from "SnortUtils"; import useFileUpload from "Upload"; import AsyncButton from "Element/AsyncButton"; import { mapEventToProfile, UserCache } from "Cache"; diff --git a/packages/app/src/Pages/settings/RelayInfo.tsx b/packages/app/src/Pages/settings/RelayInfo.tsx index ac51b073..a02cca5d 100644 --- a/packages/app/src/Pages/settings/RelayInfo.tsx +++ b/packages/app/src/Pages/settings/RelayInfo.tsx @@ -2,7 +2,7 @@ import { FormattedMessage } from "react-intl"; import ProfilePreview from "Element/ProfilePreview"; import useRelayState from "Feed/RelayState"; import { useNavigate, useParams } from "react-router-dom"; -import { parseId, unwrap } from "Util"; +import { parseId, unwrap } from "SnortUtils"; import { System } from "System"; import { removeRelay } from "Login"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Pages/settings/Relays.tsx b/packages/app/src/Pages/settings/Relays.tsx index 7cb7572e..f2f42435 100644 --- a/packages/app/src/Pages/settings/Relays.tsx +++ b/packages/app/src/Pages/settings/Relays.tsx @@ -1,7 +1,7 @@ import { useMemo, useState } from "react"; import { FormattedMessage } from "react-intl"; -import { randomSample, unixNowMs } from "Util"; +import { randomSample, unixNowMs } from "SnortUtils"; import Relay from "Element/Relay"; import useEventPublisher from "Feed/EventPublisher"; import { System } from "System"; diff --git a/packages/app/src/Pages/settings/wallet/Cashu.tsx b/packages/app/src/Pages/settings/wallet/Cashu.tsx index 02e4b728..3c41d72c 100644 --- a/packages/app/src/Pages/settings/wallet/Cashu.tsx +++ b/packages/app/src/Pages/settings/wallet/Cashu.tsx @@ -3,7 +3,7 @@ import { FormattedMessage, useIntl } from "react-intl"; import { v4 as uuid } from "uuid"; import AsyncButton from "Element/AsyncButton"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; import { WalletConfig, WalletKind, Wallets } from "Wallet"; import { useNavigate } from "react-router-dom"; diff --git a/packages/app/src/Pages/settings/wallet/LNC.tsx b/packages/app/src/Pages/settings/wallet/LNC.tsx index 85763c84..206d02a5 100644 --- a/packages/app/src/Pages/settings/wallet/LNC.tsx +++ b/packages/app/src/Pages/settings/wallet/LNC.tsx @@ -5,7 +5,7 @@ import { v4 as uuid } from "uuid"; import AsyncButton from "Element/AsyncButton"; import { LNWallet, WalletInfo, WalletKind, Wallets } from "Wallet"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; const ConnectLNC = () => { const { formatMessage } = useIntl(); diff --git a/packages/app/src/Pages/settings/wallet/LNDHub.tsx b/packages/app/src/Pages/settings/wallet/LNDHub.tsx index 869b1271..85ddc0fe 100644 --- a/packages/app/src/Pages/settings/wallet/LNDHub.tsx +++ b/packages/app/src/Pages/settings/wallet/LNDHub.tsx @@ -3,7 +3,7 @@ import { FormattedMessage, useIntl } from "react-intl"; import { v4 as uuid } from "uuid"; import AsyncButton from "Element/AsyncButton"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; import LNDHubWallet from "Wallet/LNDHub"; import { WalletConfig, WalletKind, Wallets } from "Wallet"; import { useNavigate } from "react-router-dom"; diff --git a/packages/app/src/Pages/settings/wallet/NWC.tsx b/packages/app/src/Pages/settings/wallet/NWC.tsx index 84116fda..bd5e1feb 100644 --- a/packages/app/src/Pages/settings/wallet/NWC.tsx +++ b/packages/app/src/Pages/settings/wallet/NWC.tsx @@ -3,7 +3,7 @@ import { FormattedMessage, useIntl } from "react-intl"; import { v4 as uuid } from "uuid"; import AsyncButton from "Element/AsyncButton"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; import { WalletConfig, WalletKind, Wallets } from "Wallet"; import { Link, useNavigate } from "react-router-dom"; import { NostrConnectWallet } from "Wallet/NostrWalletConnect"; diff --git a/packages/app/src/Util.test.ts b/packages/app/src/SnortUtils/Utils.test.ts similarity index 69% rename from packages/app/src/Util.test.ts rename to packages/app/src/SnortUtils/Utils.test.ts index ef537689..83f10ff1 100644 --- a/packages/app/src/Util.test.ts +++ b/packages/app/src/SnortUtils/Utils.test.ts @@ -1,4 +1,6 @@ -import { splitByUrl, magnetURIDecode, getRelayName, validateNostrLink } from "./Util"; +import { NostrPrefix } from "@snort/nostr"; +import { parseNostrLink, tryParseNostrLink } from "."; +import { splitByUrl, magnetURIDecode, getRelayName } from "."; import { describe, expect } from "@jest/globals"; describe("splitByUrl", () => { @@ -92,19 +94,46 @@ describe("getRelayName", () => { }); }); -describe("validateNostrLink", () => { - test.each([ - "nostr:npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg", - "web+nostr:npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg", - "nostr:note15449edq4qa5wzgqvh8td0q0dp6hwtes4pknsrm7eygeenhlj99xsq94wu9", - "nostr:nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p", - "nostr:nevent1qqs226juks2sw68pyqxtn4khs8ksath9uc2smfcpalvjyvuemlezjngrd87dq", - "nostr:naddr1qqzkjurnw4ksz9thwden5te0wfjkccte9ehx7um5wghx7un8qgs2d90kkcq3nk2jry62dyf50k0h36rhpdtd594my40w9pkal876jxgrqsqqqa28pccpzu", - ])("should return true for valid nostr links", la => { - expect(validateNostrLink(la)).toBe(true); +describe("tryParseNostrLink", () => { + it("is a valid nostr link", () => { + expect(parseNostrLink("nostr:npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg")).toMatchObject({ + id: "7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e", + type: NostrPrefix.PublicKey, + }); + expect(parseNostrLink("web+nostr:npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg")).toMatchObject({ + id: "7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e", + type: NostrPrefix.PublicKey, + }); + expect(parseNostrLink("nostr:note15449edq4qa5wzgqvh8td0q0dp6hwtes4pknsrm7eygeenhlj99xsq94wu9")).toMatchObject({ + id: "a56a5cb4150768e1200cb9d6d781ed0eaee5e6150da701efd9223399dff2294d", + type: NostrPrefix.Note, + }); + expect( + parseNostrLink( + "nostr:nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p" + ) + ).toMatchObject({ + id: "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", + type: NostrPrefix.Profile, + relays: ["wss://r.x.com", "wss://djbas.sadkb.com"], + }); + expect(parseNostrLink("nostr:nevent1qqs226juks2sw68pyqxtn4khs8ksath9uc2smfcpalvjyvuemlezjngrd87dq")).toMatchObject({ + id: "a56a5cb4150768e1200cb9d6d781ed0eaee5e6150da701efd9223399dff2294d", + type: NostrPrefix.Event, + }); + expect( + parseNostrLink( + "nostr:naddr1qqzkjurnw4ksz9thwden5te0wfjkccte9ehx7um5wghx7un8qgs2d90kkcq3nk2jry62dyf50k0h36rhpdtd594my40w9pkal876jxgrqsqqqa28pccpzu" + ) + ).toMatchObject({ + id: "ipsum", + type: NostrPrefix.Address, + relays: ["wss://relay.nostr.org"], + author: "a695f6b60119d9521934a691347d9f78e8770b56da16bb255ee286ddf9fda919", + kind: 30023, + }); }); - test.each(["nostr:npub", "web+nostr:npub", "nostr:nevent1xxx"])("should return false for invalid nostr links", lb => { - expect(validateNostrLink(lb)).toBe(false); + expect(tryParseNostrLink(lb)).toBeUndefined(); }); }); diff --git a/packages/app/src/Util.ts b/packages/app/src/SnortUtils/index.ts similarity index 96% rename from packages/app/src/Util.ts rename to packages/app/src/SnortUtils/index.ts index fb0acc55..7147666f 100644 --- a/packages/app/src/Util.ts +++ b/packages/app/src/SnortUtils/index.ts @@ -18,6 +18,7 @@ import { RawEvent, } from "@snort/nostr"; import { MetadataCache } from "Cache"; +import NostrLink from "Element/NostrLink"; export const sha256 = (str: string | Uint8Array): u256 => { return utils.bytesToHex(hash(str)); @@ -530,7 +531,15 @@ export function validateNostrLink(link: string): boolean { } } -export function parseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLink | undefined { +export function tryParseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLink | undefined { + try { + return parseNostrLink(link, prefixHint); + } catch { + return undefined; + } +} + +export function parseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLink { const entity = link.startsWith("web+nostr:") || link.startsWith("nostr:") ? link.split(":")[1] : link; const isPrefix = (prefix: NostrPrefix) => { @@ -539,6 +548,7 @@ export function parseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLin if (isPrefix(NostrPrefix.PublicKey)) { const id = bech32ToHex(entity); + if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); return { type: NostrPrefix.PublicKey, id: id, @@ -546,6 +556,7 @@ export function parseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLin }; } else if (isPrefix(NostrPrefix.Note)) { const id = bech32ToHex(entity); + if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); return { type: NostrPrefix.Note, id: id, @@ -563,6 +574,7 @@ export function parseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLin return entity; // return original }; if (isPrefix(NostrPrefix.Profile)) { + if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); return { type: NostrPrefix.Profile, id, @@ -572,6 +584,7 @@ export function parseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLin encode, }; } else if (isPrefix(NostrPrefix.Event)) { + if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); return { type: NostrPrefix.Event, id, @@ -596,9 +609,8 @@ export function parseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLin id: link, encode: () => hexToBech32(prefixHint, link), }; - } else { - throw new Error("Invalid nostr link"); } + throw new Error("Invalid nostr link"); } export function sanitizeRelayUrl(url: string) { diff --git a/packages/app/src/Subscription/index.ts b/packages/app/src/Subscription/index.ts index e1faf8aa..6486f2b6 100644 --- a/packages/app/src/Subscription/index.ts +++ b/packages/app/src/Subscription/index.ts @@ -1,4 +1,4 @@ -import { unixNow } from "Util"; +import { unixNow } from "SnortUtils"; export enum SubscriptionType { Supporter = 0, diff --git a/packages/app/src/System/EventBuilder.ts b/packages/app/src/System/EventBuilder.ts index b2168432..0c353d77 100644 --- a/packages/app/src/System/EventBuilder.ts +++ b/packages/app/src/System/EventBuilder.ts @@ -1,6 +1,6 @@ import { EventKind, HexKey, NostrPrefix, RawEvent } from "@snort/nostr"; import { HashtagRegex } from "Const"; -import { getPublicKey, parseNostrLink, unixNow } from "Util"; +import { getPublicKey, parseNostrLink, unixNow } from "SnortUtils"; import { EventExt } from "./EventExt"; export class EventBuilder { diff --git a/packages/app/src/System/EventExt.ts b/packages/app/src/System/EventExt.ts index 934ff32b..305e34db 100644 --- a/packages/app/src/System/EventExt.ts +++ b/packages/app/src/System/EventExt.ts @@ -2,7 +2,7 @@ import * as secp from "@noble/curves/secp256k1"; import * as utils from "@noble/curves/abstract/utils"; import { EventKind, HexKey, RawEvent, Tag } from "@snort/nostr"; import base64 from "@protobufjs/base64"; -import { sha256, unixNow } from "Util"; +import { sha256, unixNow } from "SnortUtils"; export interface Thread { root?: Tag; diff --git a/packages/app/src/System/EventPublisher.ts b/packages/app/src/System/EventPublisher.ts index ac58686b..68e556dc 100644 --- a/packages/app/src/System/EventPublisher.ts +++ b/packages/app/src/System/EventPublisher.ts @@ -14,7 +14,7 @@ import { import { DefaultRelays } from "Const"; import { System } from "System"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; import { EventBuilder } from "./EventBuilder"; import { EventExt } from "./EventExt"; import { barrierQueue, processWorkQueue, WorkQueueItem } from "WorkQueue"; diff --git a/packages/app/src/System/GossipModel.ts b/packages/app/src/System/GossipModel.ts index 3b2ae236..596473c8 100644 --- a/packages/app/src/System/GossipModel.ts +++ b/packages/app/src/System/GossipModel.ts @@ -1,6 +1,6 @@ import { RawReqFilter } from "@snort/nostr"; import { UserRelays } from "Cache/UserRelayCache"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; const PickNRelays = 2; diff --git a/packages/app/src/System/NoteCollection.ts b/packages/app/src/System/NoteCollection.ts index 3363869b..cbae06a2 100644 --- a/packages/app/src/System/NoteCollection.ts +++ b/packages/app/src/System/NoteCollection.ts @@ -1,5 +1,5 @@ import { TaggedRawEvent, u256 } from "@snort/nostr"; -import { findTag } from "Util"; +import { findTag } from "SnortUtils"; export interface StoreSnapshot { data: TSnapshot | undefined; diff --git a/packages/app/src/System/ProfileCache.ts b/packages/app/src/System/ProfileCache.ts index fe96a8e4..12bc12b0 100644 --- a/packages/app/src/System/ProfileCache.ts +++ b/packages/app/src/System/ProfileCache.ts @@ -3,7 +3,7 @@ import { ProfileCacheExpire } from "Const"; import { mapEventToProfile, MetadataCache } from "Cache"; import { UserCache } from "Cache/UserCache"; import { PubkeyReplaceableNoteStore, RequestBuilder, System } from "System"; -import { unixNowMs } from "Util"; +import { unixNowMs } from "SnortUtils"; class ProfileLoaderService { /** diff --git a/packages/app/src/System/Query.ts b/packages/app/src/System/Query.ts index a0a2313d..36178492 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/app/src/System/Query.ts @@ -1,6 +1,6 @@ import { v4 as uuid } from "uuid"; import { Connection, RawReqFilter, Nips } from "@snort/nostr"; -import { unixNowMs, unwrap } from "Util"; +import { unixNowMs, unwrap } from "SnortUtils"; import { NoteStore } from "./NoteCollection"; /** * Tracing for relay query status diff --git a/packages/app/src/System/RequestBuilder.ts b/packages/app/src/System/RequestBuilder.ts index 63a66f2d..5d3deb5d 100644 --- a/packages/app/src/System/RequestBuilder.ts +++ b/packages/app/src/System/RequestBuilder.ts @@ -1,5 +1,5 @@ import { RawReqFilter, u256, HexKey, EventKind } from "@snort/nostr"; -import { appendDedupe } from "Util"; +import { appendDedupe } from "SnortUtils"; /** * Which strategy is used when building REQ filters diff --git a/packages/app/src/System/index.ts b/packages/app/src/System/index.ts index ffeba073..02112fb1 100644 --- a/packages/app/src/System/index.ts +++ b/packages/app/src/System/index.ts @@ -1,6 +1,6 @@ import { AuthHandler, TaggedRawEvent, RelaySettings, Connection, RawReqFilter, RawEvent } from "@snort/nostr"; -import { sanitizeRelayUrl, unixNowMs, unwrap } from "Util"; +import { sanitizeRelayUrl, unixNowMs, unwrap } from "SnortUtils"; import { RequestBuilder } from "./RequestBuilder"; import { EventBuilder } from "./EventBuilder"; import { diff --git a/packages/app/src/Toaster.tsx b/packages/app/src/Toaster.tsx index 29a41379..e00f2ac6 100644 --- a/packages/app/src/Toaster.tsx +++ b/packages/app/src/Toaster.tsx @@ -2,7 +2,7 @@ import { ReactNode, useSyncExternalStore } from "react"; import { v4 as uuid } from "uuid"; import ExternalStore from "ExternalStore"; import Icon from "Icons/Icon"; -import { unixNow } from "Util"; +import { unixNow } from "SnortUtils"; import "./Toaster.css"; diff --git a/packages/app/src/Upload/VoidCat.ts b/packages/app/src/Upload/VoidCat.ts index 9dc7dfd6..60a46952 100644 --- a/packages/app/src/Upload/VoidCat.ts +++ b/packages/app/src/Upload/VoidCat.ts @@ -4,7 +4,7 @@ import { VoidApi } from "@void-cat/api"; import { FileExtensionRegex, VoidCatHost } from "Const"; import { EventPublisher } from "System/EventPublisher"; import { UploadResult } from "Upload"; -import { magnetURIDecode } from "Util"; +import { magnetURIDecode } from "SnortUtils"; /** * Upload file to void.cat diff --git a/packages/app/src/Upload/index.ts b/packages/app/src/Upload/index.ts index 9f286009..623f0ad4 100644 --- a/packages/app/src/Upload/index.ts +++ b/packages/app/src/Upload/index.ts @@ -5,7 +5,7 @@ import NostrBuild from "Upload/NostrBuild"; import VoidCat from "Upload/VoidCat"; import NostrImg from "Upload/NostrImg"; import { KieranPubKey } from "Const"; -import { bech32ToHex } from "Util"; +import { bech32ToHex } from "SnortUtils"; export interface UploadResult { url?: string; diff --git a/packages/app/src/Wallet/LNCWallet.ts b/packages/app/src/Wallet/LNCWallet.ts index fe8d8e37..fa861c44 100644 --- a/packages/app/src/Wallet/LNCWallet.ts +++ b/packages/app/src/Wallet/LNCWallet.ts @@ -1,5 +1,5 @@ import LNC from "@lightninglabs/lnc-web"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; import { InvoiceRequest, LNWallet, diff --git a/packages/app/src/Wallet/index.ts b/packages/app/src/Wallet/index.ts index b00c67d9..aa93597d 100644 --- a/packages/app/src/Wallet/index.ts +++ b/packages/app/src/Wallet/index.ts @@ -1,7 +1,7 @@ import { useEffect, useSyncExternalStore } from "react"; import ExternalStore from "ExternalStore"; -import { decodeInvoice, unwrap } from "Util"; +import { decodeInvoice, unwrap } from "SnortUtils"; import LNDHubWallet from "./LNDHub"; import { NostrConnectWallet } from "./NostrWalletConnect"; import { setupWebLNWalletConfig, WebLNWallet } from "./WebLN"; @@ -135,7 +135,7 @@ export class WalletStore extends ExternalStore { this.#instance = new Map(); this.load(false); setupWebLNWalletConfig(this); - this.snapshotState(); + this.notifyChange(); } list() { diff --git a/packages/app/src/ZapPoolController.ts b/packages/app/src/ZapPoolController.ts index 052d0872..41148b7d 100644 --- a/packages/app/src/ZapPoolController.ts +++ b/packages/app/src/ZapPoolController.ts @@ -3,7 +3,7 @@ import { getDisplayName } from "Element/ProfileImage"; import ExternalStore from "ExternalStore"; import { LNURL } from "LNURL"; import { Toastore } from "Toaster"; -import { unixNow } from "Util"; +import { unixNow } from "SnortUtils"; import { LNWallet, WalletInvoiceState, Wallets } from "Wallet"; export enum ZapPoolRecipientType { diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index e3b5e458..82fa17a3 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -9,7 +9,7 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom"; import * as serviceWorkerRegistration from "serviceWorkerRegistration"; import { IntlProvider } from "IntlProvider"; -import { unwrap } from "Util"; +import { unwrap } from "SnortUtils"; import Store from "State/Store"; import Layout from "Pages/Layout"; import LoginPage from "Pages/LoginPage"; diff --git a/packages/app/src/setupTests.js b/packages/app/src/setupTests.ts similarity index 59% rename from packages/app/src/setupTests.js rename to packages/app/src/setupTests.ts index 0b5fd4d5..4930f245 100644 --- a/packages/app/src/setupTests.js +++ b/packages/app/src/setupTests.ts @@ -1,4 +1,3 @@ -// @ts-expect-error - we have a folder called util so TS gets confused import { TextEncoder, TextDecoder } from "util"; Object.assign(global, { TextDecoder, TextEncoder }); From 7b151e1b17b22da823b1aad7e3bd78a3e043f2d9 Mon Sep 17 00:00:00 2001 From: Kieran Date: Wed, 24 May 2023 17:17:17 +0100 Subject: [PATCH 05/24] sub-query via query trace --- packages/app/src/Element/Timeline.tsx | 2 +- packages/app/src/Hooks/useSystemState.tsx | 2 +- packages/app/src/Pages/Debug.tsx | 9 ++ packages/app/src/System/Query.test.ts | 21 ++-- packages/app/src/System/Query.ts | 122 ++++++++++++++-------- packages/app/src/System/index.ts | 94 +++++++++-------- packages/app/src/index.tsx | 5 + 7 files changed, 151 insertions(+), 104 deletions(-) create mode 100644 packages/app/src/Pages/Debug.tsx diff --git a/packages/app/src/Element/Timeline.tsx b/packages/app/src/Element/Timeline.tsx index 039fed06..af28e335 100644 --- a/packages/app/src/Element/Timeline.tsx +++ b/packages/app/src/Element/Timeline.tsx @@ -143,7 +143,7 @@ const Timeline = (props: TimelineProps) => { )} {mainFeed.map(eventElement)} {(props.loadMore === undefined || props.loadMore === true) && ( - + feed.loadMore()} shouldLoadMore={!feed.loading}> diff --git a/packages/app/src/Hooks/useSystemState.tsx b/packages/app/src/Hooks/useSystemState.tsx index c128a242..a9aa8b94 100644 --- a/packages/app/src/Hooks/useSystemState.tsx +++ b/packages/app/src/Hooks/useSystemState.tsx @@ -4,6 +4,6 @@ import { System, SystemSnapshot } from "System"; export default function useSystemState() { return useSyncExternalStore( cb => System.hook(cb), - () => System.getSnapshot() + () => System.snapshot() ); } diff --git a/packages/app/src/Pages/Debug.tsx b/packages/app/src/Pages/Debug.tsx new file mode 100644 index 00000000..37c650d6 --- /dev/null +++ b/packages/app/src/Pages/Debug.tsx @@ -0,0 +1,9 @@ +import SubDebug from "Element/SubDebug"; + +export default function DebugPage() { + return ( + <> + + + ); +} diff --git a/packages/app/src/System/Query.test.ts b/packages/app/src/System/Query.test.ts index 2d6bf824..3c8d6735 100644 --- a/packages/app/src/System/Query.test.ts +++ b/packages/app/src/System/Query.test.ts @@ -1,6 +1,6 @@ import { Connection } from "@snort/nostr"; import { describe, expect } from "@jest/globals"; -import { Query } from "./Query"; +import { Query, QueryBase } from "./Query"; import { getRandomValues } from "crypto"; import { FlatNoteStore } from "./NoteCollection"; @@ -44,24 +44,21 @@ describe("query", () => { q.eose(q.id, c3); expect(q.progress).toBe(1); - const qs = new Query( - "test-1", - [ + const qs = { + id: "test-1", + filters: [ { kinds: [1], authors: ["test-sub"], }, ], - new FlatNoteStore() - ); - q.subQueries.push(qs); - qs.sendToRelay(c1); + } as QueryBase; + q.sendSubQueryToRelay(c1, qs); - expect(q.progress).toBe(0.5); + expect(q.progress).toBe(3 / 4); q.eose(qs.id, c1); expect(q.progress).toBe(1); - qs.sendToRelay(c2); - // 1 + 0.5 (1/2 sent sub query) - expect(q.progress).toBe(1.5 / 2); + q.sendSubQueryToRelay(c2, qs); + expect(q.progress).toBe(4 / 5); }); }); diff --git a/packages/app/src/System/Query.ts b/packages/app/src/System/Query.ts index 36178492..01b45c5d 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/app/src/System/Query.ts @@ -16,18 +16,21 @@ class QueryTrace { close?: number; #wasForceClosed = false; readonly #fnClose: (id: string) => void; + readonly #fnProgress: () => void; - constructor(sub: string, relay: string, connId: string, fnClose: (id: string) => void) { + constructor(sub: string, relay: string, connId: string, fnClose: (id: string) => void, fnProgress: () => void) { this.id = uuid(); this.subId = sub; this.relay = relay; this.connId = connId; this.start = unixNowMs(); this.#fnClose = fnClose; + this.#fnProgress = fnProgress; } sentToRelay() { this.sent = unixNowMs(); + this.#fnProgress(); } gotEose() { @@ -35,23 +38,28 @@ class QueryTrace { if (this.responseTime > 5_000) { console.debug(`Slow query ${this.subId} on ${this.relay} took ${this.responseTime.toLocaleString()}ms`); } + this.#fnProgress(); + console.debug(`[EOSE][${this.subId}] ${this.relay}`); } forceEose() { this.eose = unixNowMs(); this.#wasForceClosed = true; + this.#fnProgress(); + console.debug(`[F-EOSE][${this.subId}] ${this.relay}`); } sendClose() { this.close = unixNowMs(); this.#fnClose(this.subId); + this.#fnProgress(); } log() { console.debug( - `QT:${this.id}, ${this.relay}, ${this.subId}, finished=${ + `QT:${this.id}, ${this.subId}, finished=${ this.finished - }, queued=${this.queued.toLocaleString()}ms, runtime=${this.runtime?.toLocaleString()}ms` + }, queued=${this.queued.toLocaleString()}ms, runtime=${this.runtime?.toLocaleString()}ms, ${this.relay}` ); } @@ -59,7 +67,7 @@ class QueryTrace { * Time spent in queue */ get queued() { - return (this.sent === undefined ? unixNowMs() : this.sent) - this.start; + return (this.sent === undefined ? unixNowMs() : this.#wasForceClosed ? unwrap(this.eose) : this.sent) - this.start; } /** @@ -84,10 +92,7 @@ class QueryTrace { } } -/** - * Active or queued query on the system - */ -export class Query { +export interface QueryBase { /** * Uniquie ID of this query */ @@ -99,9 +104,18 @@ export class Query { filters: Array; /** - * Sub-Queries which are connected to this subscription + * List of relays to send this query to */ - subQueries: Array = []; + relays?: Array; +} + +/** + * Active or queued query on the system + */ +export class Query implements QueryBase { + id: string; + filters: Array; + relays?: Array; /** * Which relays this query has already been executed on @@ -113,11 +127,6 @@ export class Query { */ leaveOpen = false; - /** - * List of relays to send this query to - */ - relays: Array = []; - /** * Time when this query can be removed */ @@ -133,6 +142,8 @@ export class Query { */ #feed: NoteStore; + subQueryCounter = 0; + constructor(id: string, filters: Array, feed: NoteStore) { this.id = id; this.filters = filters; @@ -165,35 +176,36 @@ export class Query { } sendToRelay(c: Connection) { - if (this.relays.length > 0 && !this.relays.includes(c.Address)) { + if (!this.#canSendQuery(c, this)) { return; } - if (this.relays.length === 0 && c.Ephemeral) { - console.debug("Cant send non-specific REQ to ephemeral connection"); + this.#sendQueryInternal(c, this); + } + + sendSubQueryToRelay(c: Connection, subq: QueryBase) { + if (!this.#canSendQuery(c, subq)) { return; } - if (this.filters.some(a => a.search) && !c.SupportsNip(Nips.Search)) { - console.debug("Cant send REQ to non-search relay", c.Address); - return; - } - const qt = new QueryTrace(this.id, c.Address, c.Id, x => c.CloseReq(x)); - this.#tracing.push(qt); - c.QueueReq(["REQ", this.id, ...this.filters], () => qt.sentToRelay()); + this.#sendQueryInternal(c, subq); } connectionLost(c: Connection, active: Array, pending: Array) { - const allQueriesLost = [...active, ...pending].filter(a => this.id === a || this.subQueries.some(b => b.id === a)); + const allQueriesLost = [...active, ...pending].filter(a => this.id === a || this.#tracing.some(b => b.subId === a)); if (allQueriesLost.length > 0) { console.debug("Lost", allQueriesLost, c.Address, c.Id); } + for (const qLost of allQueriesLost) { + const qt = this.#tracing.find(a => a.subId === qLost && a.connId == c.Id); + qt?.forceEose(); + } } sendClose() { for (const qt of this.#tracing) { qt.sendClose(); } - for (const sq of this.subQueries) { - sq.sendClose(); + for (const qt of this.#tracing) { + qt.sendClose(); } this.cleanup(); } @@ -202,15 +214,9 @@ export class Query { const qt = this.#tracing.find(a => a.subId === sub && a.connId === conn.Id); qt?.gotEose(); if (sub === this.id) { - console.debug(`[EOSE][${sub}] ${conn.Address}`); if (!this.leaveOpen) { qt?.sendClose(); } - } else { - const subQ = this.subQueries.find(a => a.id === sub); - if (subQ) { - subQ.eose(sub, conn); - } } } @@ -218,19 +224,19 @@ export class Query { * Get the progress to EOSE, can be used to determine when we should load more content */ get progress() { - let thisProgress = this.#tracing.reduce((acc, v) => (acc += v.finished ? 1 : 0), 0) / this.#tracing.length; + const thisProgress = this.#tracing.reduce((acc, v) => (acc += v.finished ? 1 : 0), 0) / this.#tracing.length; if (isNaN(thisProgress)) { - thisProgress = 0; - } - if (this.subQueries.length === 0) { - return thisProgress; + return 0; } + return thisProgress; + } - let totalProgress = thisProgress; - for (const sq of this.subQueries) { - totalProgress += sq.progress; + #onProgress() { + const isFinished = this.progress === 1; + if (this.feed.loading !== isFinished) { + console.debug(`[QT] ${this.id}, loading=${this.feed.loading}, progress=${this.progress}`); + this.feed.loading = isFinished; } - return totalProgress / (this.subQueries.length + 1); } #stopCheckTraces() { @@ -243,11 +249,37 @@ export class Query { this.#stopCheckTraces(); this.#checkTrace = setInterval(() => { for (const v of this.#tracing) { - //v.log(); if (v.runtime > 5_000 && !v.finished) { v.forceEose(); } } - }, 2_000); + }, 500); + } + + #canSendQuery(c: Connection, q: QueryBase) { + if (q.relays && !q.relays.includes(c.Address)) { + return false; + } + if ((q.relays?.length ?? 0) === 0 && c.Ephemeral) { + console.debug("Cant send non-specific REQ to ephemeral connection"); + return false; + } + if (q.filters.some(a => a.search) && !c.SupportsNip(Nips.Search)) { + console.debug("Cant send REQ to non-search relay", c.Address); + return false; + } + return true; + } + + #sendQueryInternal(c: Connection, q: QueryBase) { + const qt = new QueryTrace( + q.id, + c.Address, + c.Id, + x => c.CloseReq(x), + () => this.#onProgress() + ); + this.#tracing.push(qt); + c.QueueReq(["REQ", q.id, ...q.filters], () => qt.sentToRelay()); } } diff --git a/packages/app/src/System/index.ts b/packages/app/src/System/index.ts index 02112fb1..13304c26 100644 --- a/packages/app/src/System/index.ts +++ b/packages/app/src/System/index.ts @@ -11,8 +11,9 @@ import { ReplaceableNoteStore, } from "./NoteCollection"; import { diffFilters } from "./RequestSplitter"; -import { Query } from "./Query"; +import { Query, QueryBase } from "./Query"; import { splitAllByWriteRelays } from "./GossipModel"; +import ExternalStore from "ExternalStore"; export { NoteStore, @@ -40,7 +41,7 @@ export type HookSystemSnapshot = () => void; /** * Manages nostr content retrieval system */ -export class NostrSystem { +export class NostrSystem extends ExternalStore { /** * All currently connected websockets */ @@ -56,33 +57,12 @@ export class NostrSystem { */ HandleAuth?: AuthHandler; - /** - * State change hooks - */ - #stateHooks: Array = []; - - /** - * Current snapshot of the system - */ - #snapshot: Readonly = { queries: [] }; - constructor() { + super(); this.Sockets = new Map(); this.#cleanup(); } - hook(cb: HookSystemSnapshot): HookSystemSnapshotRelease { - this.#stateHooks.push(cb); - return () => { - const idx = this.#stateHooks.findIndex(a => a === cb); - this.#stateHooks.splice(idx, 1); - }; - } - - getSnapshot(): Readonly { - return this.#snapshot; - } - /** * Connect to a NOSTR relay if not already connected */ @@ -210,19 +190,20 @@ export class NostrSystem { const diff = diffFilters(q.filters, filters); if (!diff.changed && !req.options?.skipDiff) { - this.#changed(); + this.notifyChange(); return unwrap(q.feed) as Readonly; } else { const splitFilters = splitAllByWriteRelays(filters); for (const sf of splitFilters) { - const subQ = new Query(`${q.id}-${q.subQueries.length + 1}`, sf.filters, q.feed); - subQ.relays = sf.relay ? [sf.relay] : []; - q.subQueries.push(subQ); - this.SendQuery(subQ); + const subQ = { + id: `${q.id}-${q.subQueryCounter++}`, + filters: sf.filters, + relays: sf.relay ? [sf.relay] : [], + } as QueryBase; + this.SendSubQuery(q, subQ); } q.filters = filters; - q.feed.loading = true; - this.#changed(); + this.notifyChange(); return q.feed as Readonly; } } else { @@ -246,15 +227,17 @@ export class NostrSystem { const splitFilters = splitAllByWriteRelays(filters); if (splitFilters.length > 1) { for (const sf of splitFilters) { - const subQ = new Query(`${q.id}-${q.subQueries.length + 1}`, sf.filters, q.feed); - subQ.relays = sf.relay ? [sf.relay] : []; - q.subQueries.push(subQ); - this.SendQuery(subQ); + const subQ = { + id: `${q.id}-${q.subQueryCounter++}`, + filters: sf.filters, + relays: sf.relay ? [sf.relay] : [], + } as QueryBase; + this.SendSubQuery(q, subQ); } } else { this.SendQuery(q); } - this.#changed(); + this.notifyChange(); return store; } @@ -266,7 +249,7 @@ export class NostrSystem { } async SendQuery(q: Query) { - if (q.relays.length > 0) { + if (q.relays && q.relays.length > 0) { for (const r of q.relays) { const s = this.Sockets.get(r); if (s) { @@ -289,6 +272,30 @@ export class NostrSystem { } } + async SendSubQuery(q: Query, subQ: QueryBase) { + if (subQ.relays && subQ.relays.length > 0) { + for (const r of subQ.relays) { + const s = this.Sockets.get(r); + if (s) { + q.sendSubQueryToRelay(s, subQ); + } else { + const nc = await this.ConnectEphemeralRelay(r); + if (nc) { + q.sendSubQueryToRelay(nc, subQ); + } else { + console.warn("Failed to connect to new relay for:", r, subQ); + } + } + } + } else { + for (const [, s] of this.Sockets) { + if (!s.Ephemeral) { + q.sendSubQueryToRelay(s, subQ); + } + } + } + } + /** * Send events to writable relays */ @@ -316,20 +323,17 @@ export class NostrSystem { }); } - #changed() { - this.#snapshot = Object.freeze({ + takeSnapshot(): SystemSnapshot { + return { queries: [...this.Queries.values()].map(a => { return { id: a.id, filters: a.filters, closing: a.closing, - subFilters: a.subQueries.map(a => a.filters).flat(), + subFilters: [], }; }), - }); - for (const h of this.#stateHooks) { - h(); - } + }; } #cleanup() { @@ -343,7 +347,7 @@ export class NostrSystem { } } if (changed) { - this.#changed(); + this.notifyChange(); } setTimeout(() => this.#cleanup(), 1_000); } diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index 82fa17a3..863e2bf6 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -31,6 +31,7 @@ import NostrLinkHandler from "Pages/NostrLinkHandler"; import Thread from "Element/Thread"; import { SubscribeRoutes } from "Pages/subscribe"; import ZapPoolPage from "Pages/ZapPool"; +import DebugPage from "Pages/Debug"; // @ts-ignore window.__webpack_nonce__ = "ZmlhdGphZiBzYWlkIHNub3J0LnNvY2lhbCBpcyBwcmV0dHkgZ29vZCwgd2UgbWFkZSBpdCE="; @@ -99,6 +100,10 @@ export const router = createBrowserRouter([ ...NewUserRoutes, ...WalletRoutes, ...SubscribeRoutes, + { + path: "/debug", + element: , + }, { path: "/*", element: , From 2ccee7bd7c4899130883de3150ad46131b1347a2 Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 25 May 2023 00:03:54 +0100 Subject: [PATCH 06/24] Use debug logger package Fix profile loading loop for expired profile same result --- packages/app/package.json | 2 + packages/app/src/Cache/FeedCache.ts | 25 +++++++------ packages/app/src/Pages/Layout.tsx | 2 - packages/app/src/System/GossipModel.ts | 3 +- packages/app/src/System/NoteCollection.ts | 4 +- packages/app/src/System/ProfileCache.ts | 37 +++++++++++++------ packages/app/src/System/Query.ts | 36 ++++++------------ packages/app/src/System/index.ts | 16 ++++---- packages/app/src/Wallet/LNCWallet.ts | 10 +++-- packages/app/src/Wallet/LNDHub.ts | 1 - packages/app/src/Wallet/NostrWalletConnect.ts | 3 +- packages/nostr/src/legacy/Connection.ts | 6 +-- yarn.lock | 14 ++++++- 13 files changed, 89 insertions(+), 70 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index a28acdba..1d68eff0 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -17,6 +17,7 @@ "@void-cat/api": "^1.0.4", "base32-decode": "^1.0.0", "bech32": "^2.0.0", + "debug": "^4.3.4", "dexie": "^3.2.2", "dexie-react-hooks": "^1.1.1", "dns-over-http-resolver": "^2.1.1", @@ -80,6 +81,7 @@ "@babel/preset-react": "^7.18.6", "@formatjs/cli": "^6.0.1", "@formatjs/ts-transformer": "^3.13.1", + "@types/debug": "^4.1.8", "@types/jest": "^29.5.1", "@types/node": "^18.11.18", "@types/react": "^18.0.26", diff --git a/packages/app/src/Cache/FeedCache.ts b/packages/app/src/Cache/FeedCache.ts index 4a24f541..ccd91667 100644 --- a/packages/app/src/Cache/FeedCache.ts +++ b/packages/app/src/Cache/FeedCache.ts @@ -1,4 +1,5 @@ import { db } from "Db"; +import debug from "debug"; import { Table } from "dexie"; import { unixNowMs, unwrap } from "SnortUtils"; @@ -24,13 +25,14 @@ export default abstract class FeedCache { this.#name = name; this.#table = table; setInterval(() => { - console.debug( - `[${this.#name}] ${this.cache.size} loaded, ${this.onTable.size} on-disk, ${this.#hooks.length} hooks, ${( - (this.#hits / (this.#hits + this.#miss)) * - 100 - ).toFixed(1)} % hit` + debug(this.#name)( + "%d loaded, %d on-disk, %d hooks, %d% hit", + this.cache.size, + this.onTable.size, + this.#hooks.length, + ((this.#hits / (this.#hits + this.#miss)) * 100).toFixed(1) ); - }, 5_000); + }, 30_000); } async preload() { @@ -138,7 +140,7 @@ export default abstract class FeedCache { } return "no_change"; })(); - console.debug(`Updating ${k} ${updateType}`, m); + debug(this.#name)("Updating %s %s %o", k, updateType, m); if (updateType !== "no_change") { const updated = { ...existing, @@ -168,10 +170,11 @@ export default abstract class FeedCache { this.cache.set(this.key(a), a); }); this.notifyChange(fromCacheFiltered.map(a => this.key(a))); - console.debug( - `[${this.#name}] Loaded ${fromCacheFiltered.length}/${keys.length} in ${( - unixNowMs() - start - ).toLocaleString()} ms` + debug(this.#name)( + `Loaded %d/%d in %d ms`, + fromCacheFiltered.length, + keys.length, + (unixNowMs() - start).toLocaleString() ); return mapped.filter(a => !a.has).map(a => a.key); } diff --git a/packages/app/src/Pages/Layout.tsx b/packages/app/src/Pages/Layout.tsx index f57d9c51..9c538dd2 100644 --- a/packages/app/src/Pages/Layout.tsx +++ b/packages/app/src/Pages/Layout.tsx @@ -16,7 +16,6 @@ import useModeration from "Hooks/useModeration"; import { NoteCreator } from "Element/NoteCreator"; import { db } from "Db"; import useEventPublisher from "Feed/EventPublisher"; -import SubDebug from "Element/SubDebug"; import { preload } from "Cache"; import { useDmCache } from "Hooks/useDmsCache"; import { mapPlanName } from "./subscribe"; @@ -170,7 +169,6 @@ export default function Layout() { )} - {window.localStorage.getItem("debug") && } ); diff --git a/packages/app/src/System/GossipModel.ts b/packages/app/src/System/GossipModel.ts index 596473c8..06c359f4 100644 --- a/packages/app/src/System/GossipModel.ts +++ b/packages/app/src/System/GossipModel.ts @@ -1,6 +1,7 @@ import { RawReqFilter } from "@snort/nostr"; import { UserRelays } from "Cache/UserRelayCache"; import { unwrap } from "SnortUtils"; +import debug from "debug"; const PickNRelays = 2; @@ -106,6 +107,6 @@ export function splitByWriteRelays(filter: RawReqFilter): Array i } onEvent(cb: OnEventCallback): OnEventCallbackRelease { - const existing = this.#eventHooks.findIndex(a => a === cb); - if (existing === -1) { + const existing = this.#eventHooks.find(a => a === cb); + if (!existing) { this.#eventHooks.push(cb); return () => { const idx = this.#eventHooks.findIndex(a => a === cb); diff --git a/packages/app/src/System/ProfileCache.ts b/packages/app/src/System/ProfileCache.ts index 12bc12b0..00a6fb55 100644 --- a/packages/app/src/System/ProfileCache.ts +++ b/packages/app/src/System/ProfileCache.ts @@ -4,6 +4,7 @@ import { mapEventToProfile, MetadataCache } from "Cache"; import { UserCache } from "Cache/UserCache"; import { PubkeyReplaceableNoteStore, RequestBuilder, System } from "System"; import { unixNowMs } from "SnortUtils"; +import debug from "debug"; class ProfileLoaderService { /** @@ -11,6 +12,8 @@ class ProfileLoaderService { */ WantsMetadata: Set = new Set(); + readonly #log = debug("ProfileCache"); + constructor() { this.#FetchMetadata(); } @@ -39,12 +42,10 @@ class ProfileLoaderService { } } - async onProfileEvent(ev: Readonly>) { - for (const e of ev) { - const profile = mapEventToProfile(e); - if (profile) { - await UserCache.update(profile); - } + async onProfileEvent(e: Readonly) { + const profile = mapEventToProfile(e); + if (profile) { + await UserCache.update(profile); } } @@ -57,18 +58,25 @@ class ProfileLoaderService { .filter(a => (UserCache.getFromCache(a)?.loaded ?? 0) < expire); const missing = new Set([...missingFromCache, ...expired]); if (missing.size > 0) { - console.debug(`[UserCache] Wants profiles: ${missingFromCache.length} missing, ${expired.length} expired`); + this.#log("Wants profiles: %d missing, %d expired", missingFromCache.length, expired.length); - const sub = new RequestBuilder(`profiles`); + const sub = new RequestBuilder("profiles"); sub + .withOptions({ + skipDiff: true, + }) .withFilter() .kinds([EventKind.SetMetadata]) .authors([...missing]); + const newProfiles = new Set(); const q = System.Query(PubkeyReplaceableNoteStore, sub); // never release this callback, it will stop firing anyway after eose const releaseOnEvent = q.onEvent(async e => { - await this.onProfileEvent(e); + for (const pe of e) { + newProfiles.add(pe.id); + await this.onProfileEvent(pe); + } }); const results = await new Promise>>(resolve => { let timeout: ReturnType | undefined = undefined; @@ -76,21 +84,21 @@ class ProfileLoaderService { if (!q.loading) { clearTimeout(timeout); resolve(q.getSnapshotData() ?? []); - console.debug("Profiles finished: ", sub.id); + this.#log("Profiles finished: %s", sub.id); release(); } }); timeout = setTimeout(() => { release(); resolve(q.getSnapshotData() ?? []); - console.debug("Profiles timeout: ", sub.id); + this.#log("Profiles timeout: %s", sub.id); }, 5_000); }); releaseOnEvent(); const couldNotFetch = [...missing].filter(a => !results.some(b => b.pubkey === a)); if (couldNotFetch.length > 0) { - console.debug("No profiles: ", couldNotFetch); + this.#log("No profiles: %o", couldNotFetch); const empty = couldNotFetch.map(a => UserCache.update({ pubkey: a, @@ -100,6 +108,11 @@ class ProfileLoaderService { ); await Promise.all(empty); } + + // When we fetch an expired profile and its the same as what we already have + // onEvent is not fired and the loaded timestamp never gets updated + const expiredSame = results.filter(a => !newProfiles.has(a.id) && expired.includes(a.pubkey)); + await Promise.all(expiredSame.map(v => this.onProfileEvent(v))); } setTimeout(() => this.#FetchMetadata(), 500); diff --git a/packages/app/src/System/Query.ts b/packages/app/src/System/Query.ts index 01b45c5d..d877f3ab 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/app/src/System/Query.ts @@ -1,7 +1,9 @@ import { v4 as uuid } from "uuid"; +import debug from "debug"; import { Connection, RawReqFilter, Nips } from "@snort/nostr"; import { unixNowMs, unwrap } from "SnortUtils"; import { NoteStore } from "./NoteCollection"; + /** * Tracing for relay query status */ @@ -17,6 +19,7 @@ class QueryTrace { #wasForceClosed = false; readonly #fnClose: (id: string) => void; readonly #fnProgress: () => void; + readonly #log = debug("QueryTrace"); constructor(sub: string, relay: string, connId: string, fnClose: (id: string) => void, fnProgress: () => void) { this.id = uuid(); @@ -35,18 +38,15 @@ class QueryTrace { gotEose() { this.eose = unixNowMs(); - if (this.responseTime > 5_000) { - console.debug(`Slow query ${this.subId} on ${this.relay} took ${this.responseTime.toLocaleString()}ms`); - } this.#fnProgress(); - console.debug(`[EOSE][${this.subId}] ${this.relay}`); + //this.#log("[EOSE] %s %s", this.subId, this.relay); } forceEose() { this.eose = unixNowMs(); this.#wasForceClosed = true; this.#fnProgress(); - console.debug(`[F-EOSE][${this.subId}] ${this.relay}`); + //this.#log("[F-EOSE] %s %s", this.subId, this.relay); } sendClose() { @@ -55,14 +55,6 @@ class QueryTrace { this.#fnProgress(); } - log() { - console.debug( - `QT:${this.id}, ${this.subId}, finished=${ - this.finished - }, queued=${this.queued.toLocaleString()}ms, runtime=${this.runtime?.toLocaleString()}ms, ${this.relay}` - ); - } - /** * Time spent in queue */ @@ -143,6 +135,7 @@ export class Query implements QueryBase { #feed: NoteStore; subQueryCounter = 0; + #log = debug("Query"); constructor(id: string, filters: Array, feed: NoteStore) { this.id = id; @@ -189,15 +182,8 @@ export class Query implements QueryBase { this.#sendQueryInternal(c, subq); } - connectionLost(c: Connection, active: Array, pending: Array) { - const allQueriesLost = [...active, ...pending].filter(a => this.id === a || this.#tracing.some(b => b.subId === a)); - if (allQueriesLost.length > 0) { - console.debug("Lost", allQueriesLost, c.Address, c.Id); - } - for (const qLost of allQueriesLost) { - const qt = this.#tracing.find(a => a.subId === qLost && a.connId == c.Id); - qt?.forceEose(); - } + connectionLost(id: string) { + this.#tracing.filter(a => a.connId == id).forEach(a => a.forceEose()); } sendClose() { @@ -234,7 +220,7 @@ export class Query implements QueryBase { #onProgress() { const isFinished = this.progress === 1; if (this.feed.loading !== isFinished) { - console.debug(`[QT] ${this.id}, loading=${this.feed.loading}, progress=${this.progress}`); + this.#log("%s loading=%s, progress=%d", this.id, this.feed.loading, this.progress); this.feed.loading = isFinished; } } @@ -261,11 +247,11 @@ export class Query implements QueryBase { return false; } if ((q.relays?.length ?? 0) === 0 && c.Ephemeral) { - console.debug("Cant send non-specific REQ to ephemeral connection"); + this.#log("Cant send non-specific REQ to ephemeral connection"); return false; } if (q.filters.some(a => a.search) && !c.SupportsNip(Nips.Search)) { - console.debug("Cant send REQ to non-search relay", c.Address); + this.#log("Cant send REQ to non-search relay", c.Address); return false; } return true; diff --git a/packages/app/src/System/index.ts b/packages/app/src/System/index.ts index 13304c26..5c8a1e49 100644 --- a/packages/app/src/System/index.ts +++ b/packages/app/src/System/index.ts @@ -74,7 +74,7 @@ export class NostrSystem extends ExternalStore { this.Sockets.set(addr, c); c.OnEvent = (s, e) => this.OnEvent(s, e); c.OnEose = s => this.OnEndOfStoredEvents(c, s); - c.OnDisconnect = (a, p) => this.OnRelayDisconnect(c, a, p); + c.OnDisconnect = id => this.OnRelayDisconnect(id); c.OnConnected = () => { for (const [, q] of this.Queries) { q.sendToRelay(c); @@ -90,9 +90,9 @@ export class NostrSystem extends ExternalStore { } } - OnRelayDisconnect(c: Connection, active: Array, pending: Array) { + OnRelayDisconnect(id: string) { for (const [, q] of this.Queries) { - q.connectionLost(c, active, pending); + q.connectionLost(id); } } @@ -131,10 +131,12 @@ export class NostrSystem extends ExternalStore { this.Sockets.set(addr, c); c.OnEvent = (s, e) => this.OnEvent(s, e); c.OnEose = s => this.OnEndOfStoredEvents(c, s); - c.OnDisconnect = (a, p) => this.OnRelayDisconnect(c, a, p); + c.OnDisconnect = id => this.OnRelayDisconnect(id); c.OnConnected = () => { for (const [, q] of this.Queries) { - q.sendToRelay(c); + if (q.progress !== 1) { + q.sendToRelay(c); + } } }; await c.Connect(); @@ -198,7 +200,7 @@ export class NostrSystem extends ExternalStore { const subQ = { id: `${q.id}-${q.subQueryCounter++}`, filters: sf.filters, - relays: sf.relay ? [sf.relay] : [], + relays: sf.relay ? [sf.relay] : undefined, } as QueryBase; this.SendSubQuery(q, subQ); } @@ -230,7 +232,7 @@ export class NostrSystem extends ExternalStore { const subQ = { id: `${q.id}-${q.subQueryCounter++}`, filters: sf.filters, - relays: sf.relay ? [sf.relay] : [], + relays: sf.relay ? [sf.relay] : undefined, } as QueryBase; this.SendSubQuery(q, subQ); } diff --git a/packages/app/src/Wallet/LNCWallet.ts b/packages/app/src/Wallet/LNCWallet.ts index fa861c44..342dfec8 100644 --- a/packages/app/src/Wallet/LNCWallet.ts +++ b/packages/app/src/Wallet/LNCWallet.ts @@ -11,6 +11,7 @@ import { WalletInvoice, WalletInvoiceState, } from "Wallet"; +import debug from "debug"; enum Payment_PaymentStatus { UNKNOWN = "UNKNOWN", @@ -22,6 +23,7 @@ enum Payment_PaymentStatus { export class LNCWallet implements LNWallet { #lnc: LNC; + readonly #log = debug("LNC"); private constructor(pairingPhrase?: string, password?: string) { this.#lnc = new LNC({ @@ -90,7 +92,7 @@ export class LNCWallet implements LNWallet { async getBalance(): Promise { const rsp = await this.#lnc.lnd.lightning.channelBalance(); - console.debug(rsp); + this.#log(rsp); return parseInt(rsp.localBalance?.sat ?? "0"); } @@ -112,7 +114,7 @@ export class LNCWallet implements LNWallet { feeLimitSat: "100", }, msg => { - console.debug(msg); + this.#log(msg); if (msg.status === Payment_PaymentStatus.SUCCEEDED) { resolve({ preimage: msg.paymentPreimage, @@ -122,7 +124,7 @@ export class LNCWallet implements LNWallet { } }, err => { - console.debug(err); + this.#log(err); reject(err); } ); @@ -135,7 +137,7 @@ export class LNCWallet implements LNWallet { reversed: true, }); - console.debug(invoices); + this.#log(invoices); return invoices.payments.map(a => { const parsedInvoice = prToWalletInvoice(a.paymentRequest); if (!parsedInvoice) { diff --git a/packages/app/src/Wallet/LNDHub.ts b/packages/app/src/Wallet/LNDHub.ts index 83cb6519..738dfeb9 100644 --- a/packages/app/src/Wallet/LNDHub.ts +++ b/packages/app/src/Wallet/LNDHub.ts @@ -26,7 +26,6 @@ export default class LNDHubWallet implements LNWallet { if (url.startsWith("lndhub://")) { const regex = /^lndhub:\/\/([\S-]+):([\S-]+)@(.*)$/i; const parsedUrl = url.match(regex); - console.debug(parsedUrl); if (!parsedUrl || parsedUrl.length !== 4) { throw new Error("Invalid LNDHUB config"); } diff --git a/packages/app/src/Wallet/NostrWalletConnect.ts b/packages/app/src/Wallet/NostrWalletConnect.ts index 1fd6d70d..5979b957 100644 --- a/packages/app/src/Wallet/NostrWalletConnect.ts +++ b/packages/app/src/Wallet/NostrWalletConnect.ts @@ -2,6 +2,7 @@ import { Connection, EventKind, RawEvent } from "@snort/nostr"; import { EventBuilder } from "System"; import { EventExt } from "System/EventExt"; import { LNWallet, WalletError, WalletErrorCode, WalletInfo, WalletInvoice, WalletInvoiceState } from "Wallet"; +import debug from "debug"; interface WalletConnectConfig { relayUrl: string; @@ -184,7 +185,7 @@ export class NostrConnectWallet implements LNWallet { this.#commandQueue.set(evCommand.id, { resolve: async (o: string) => { const reply = JSON.parse(await EventExt.decryptData(o, this.#config.secret, this.#config.walletPubkey)); - console.debug("NWC", reply); + debug("NWC")("%o", reply); resolve(reply); }, reject, diff --git a/packages/nostr/src/legacy/Connection.ts b/packages/nostr/src/legacy/Connection.ts index 452f2ac8..36ed4c4d 100644 --- a/packages/nostr/src/legacy/Connection.ts +++ b/packages/nostr/src/legacy/Connection.ts @@ -47,7 +47,7 @@ export class Connection { cmd: ReqCommand, cb: () => void }> = []; - ActiveRequests: Set = new Set(); + ActiveRequests = new Set(); Settings: RelaySettings; Info?: RelayInfo; @@ -63,7 +63,7 @@ export class Connection { OnConnected?: () => void; OnEvent?: (sub: string, e: TaggedRawEvent) => void; OnEose?: (sub: string) => void; - OnDisconnect?: (active: Array, pending: Array) => void; + OnDisconnect?: (id: string) => void; Auth?: AuthHandler; AwaitingAuth: Map; Authed = false; @@ -190,7 +190,7 @@ export class Connection { this.ReconnectTimer = null; } - this.OnDisconnect?.([...this.ActiveRequests], this.PendingRequests.map(a => a.cmd[1])) + this.OnDisconnect?.(this.Id); this.#ResetQueues(); // reset connection Id on disconnect, for query-tracking this.Id = uuid(); diff --git a/yarn.lock b/yarn.lock index f69433cc..100b9383 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2357,6 +2357,13 @@ dependencies: "@types/node" "*" +"@types/debug@^4.1.8": + version "4.1.8" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.8.tgz#cef723a5d0a90990313faec2d1e22aee5eecb317" + integrity sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ== + dependencies: + "@types/ms" "*" + "@types/eslint-scope@^3.7.3": version "3.7.4" resolved "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz" @@ -2529,6 +2536,11 @@ resolved "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz" integrity sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q== +"@types/ms@*": + version "0.7.31" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" + integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== + "@types/node@*", "@types/node@^18.11.18": version "18.15.11" resolved "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz" @@ -4448,7 +4460,7 @@ debug@2.6.9: debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" From 988416f3531b0ca75d9be8526adb76769d7c9abb Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 25 May 2023 10:31:47 +0100 Subject: [PATCH 07/24] fix: cache preload feat: Fetch only newest relay list updates --- packages/app/src/Cache/DMCache.ts | 4 +-- .../app/src/Cache/EventInteractionCache.ts | 4 +-- packages/app/src/Cache/FeedCache.ts | 20 +++++++-------- packages/app/src/Cache/UserCache.ts | 8 ++++++ packages/app/src/Cache/UserRelayCache.ts | 13 ++++++++++ packages/app/src/Cache/index.ts | 13 ++++++---- packages/app/src/Db/index.ts | 1 + packages/app/src/Feed/LoginFeed.ts | 2 ++ packages/app/src/Feed/RelaysFeedFollows.tsx | 14 +++++++---- packages/app/src/Pages/Layout.tsx | 25 ------------------- packages/app/src/System/Query.ts | 2 +- packages/app/src/System/index.ts | 2 +- packages/app/src/index.tsx | 22 ++++++++++++++++ 13 files changed, 79 insertions(+), 51 deletions(-) diff --git a/packages/app/src/Cache/DMCache.ts b/packages/app/src/Cache/DMCache.ts index a147559f..2ea1820d 100644 --- a/packages/app/src/Cache/DMCache.ts +++ b/packages/app/src/Cache/DMCache.ts @@ -11,8 +11,8 @@ class DMCache extends FeedCache { return of.id; } - override async preload(): Promise { - await super.preload(); + override async preload(follows?: Array): Promise { + await super.preload(follows); // load all dms to memory await this.buffer([...this.onTable]); } diff --git a/packages/app/src/Cache/EventInteractionCache.ts b/packages/app/src/Cache/EventInteractionCache.ts index 1a727c06..08893705 100644 --- a/packages/app/src/Cache/EventInteractionCache.ts +++ b/packages/app/src/Cache/EventInteractionCache.ts @@ -12,8 +12,8 @@ class EventInteractionCache extends FeedCache { return sha256(of.event + of.by); } - override async preload(): Promise { - await super.preload(); + override async preload(follows?: Array): Promise { + await super.preload(follows); const data = window.localStorage.getItem("zap-cache"); if (data) { diff --git a/packages/app/src/Cache/FeedCache.ts b/packages/app/src/Cache/FeedCache.ts index ccd91667..8d7966ed 100644 --- a/packages/app/src/Cache/FeedCache.ts +++ b/packages/app/src/Cache/FeedCache.ts @@ -12,18 +12,18 @@ interface HookFilter { export default abstract class FeedCache { #name: string; - #table: Table; #hooks: Array = []; #snapshot: Readonly> = []; #changed = true; #hits = 0; #miss = 0; + protected table: Table; protected onTable: Set = new Set(); protected cache: Map = new Map(); constructor(name: string, table: Table) { this.#name = name; - this.#table = table; + this.table = table; setInterval(() => { debug(this.#name)( "%d loaded, %d on-disk, %d hooks, %d% hit", @@ -35,9 +35,9 @@ export default abstract class FeedCache { }, 30_000); } - async preload() { + async preload(follows?: Array) { if (db.ready) { - const keys = await this.#table.toCollection().primaryKeys(); + const keys = await this.table.toCollection().primaryKeys(); this.onTable = new Set(keys.map(a => a as string)); } } @@ -75,7 +75,7 @@ export default abstract class FeedCache { async get(key?: string) { if (key && !this.cache.has(key) && db.ready) { - const cached = await this.#table.get(key); + const cached = await this.table.get(key); if (cached) { this.cache.set(this.key(cached), cached); this.notifyChange([key]); @@ -88,7 +88,7 @@ export default abstract class FeedCache { async bulkGet(keys: Array) { const missing = keys.filter(a => !this.cache.has(a)); if (missing.length > 0 && db.ready) { - const cached = await this.#table.bulkGet(missing); + const cached = await this.table.bulkGet(missing); cached.forEach(a => { if (a) { this.cache.set(this.key(a), a); @@ -105,7 +105,7 @@ export default abstract class FeedCache { const k = this.key(obj); this.cache.set(k, obj); if (db.ready) { - await this.#table.put(obj); + await this.table.put(obj); this.onTable.add(k); } this.notifyChange([k]); @@ -113,7 +113,7 @@ export default abstract class FeedCache { async bulkSet(obj: Array) { if (db.ready) { - await this.#table.bulkPut(obj); + await this.table.bulkPut(obj); obj.forEach(a => this.onTable.add(this.key(a))); } obj.forEach(v => this.cache.set(this.key(v), v)); @@ -164,7 +164,7 @@ export default abstract class FeedCache { key: a, })); const start = unixNowMs(); - const fromCache = await this.#table.bulkGet(mapped.filter(a => a.has).map(a => a.key)); + const fromCache = await this.table.bulkGet(mapped.filter(a => a.has).map(a => a.key)); const fromCacheFiltered = fromCache.filter(a => a !== undefined).map(a => unwrap(a)); fromCacheFiltered.forEach(a => { this.cache.set(this.key(a), a); @@ -184,7 +184,7 @@ export default abstract class FeedCache { } async clear() { - await this.#table.clear(); + await this.table.clear(); this.cache.clear(); this.onTable.clear(); } diff --git a/packages/app/src/Cache/UserCache.ts b/packages/app/src/Cache/UserCache.ts index 5dc2a3d4..06c99f73 100644 --- a/packages/app/src/Cache/UserCache.ts +++ b/packages/app/src/Cache/UserCache.ts @@ -18,6 +18,14 @@ class UserProfileCache extends FeedCache { return of.pubkey; } + override async preload(follows?: Array): Promise { + await super.preload(follows); + // load follows profiles + if (follows) { + await this.buffer(follows); + } + } + async search(q: string): Promise> { if (db.ready) { // on-disk cache will always have more data diff --git a/packages/app/src/Cache/UserRelayCache.ts b/packages/app/src/Cache/UserRelayCache.ts index 2f0a6ede..532d8a69 100644 --- a/packages/app/src/Cache/UserRelayCache.ts +++ b/packages/app/src/Cache/UserRelayCache.ts @@ -10,6 +10,19 @@ class UsersRelaysCache extends FeedCache { return of.pubkey; } + override async preload(follows?: Array): Promise { + await super.preload(follows); + if (follows) { + await this.buffer(follows); + } + } + + newest(): number { + let ret = 0; + this.cache.forEach(v => (ret = v.created_at > ret ? v.created_at : ret)); + return ret; + } + takeSnapshot(): Array { return [...this.cache.values()]; } diff --git a/packages/app/src/Cache/index.ts b/packages/app/src/Cache/index.ts index f147ecfa..8942fede 100644 --- a/packages/app/src/Cache/index.ts +++ b/packages/app/src/Cache/index.ts @@ -52,11 +52,14 @@ export function mapEventToProfile(ev: RawEvent) { } } -export async function preload() { - await UserCache.preload(); - await DmCache.preload(); - await InteractionCache.preload(); - await UserRelays.preload(); +export async function preload(follows?: Array) { + const preloads = [ + UserCache.preload(follows), + DmCache.preload(follows), + InteractionCache.preload(follows), + UserRelays.preload(follows), + ]; + await Promise.all(preloads); } export { UserCache, DmCache }; diff --git a/packages/app/src/Db/index.ts b/packages/app/src/Db/index.ts index 4ed7ddaf..df0fe111 100644 --- a/packages/app/src/Db/index.ts +++ b/packages/app/src/Db/index.ts @@ -21,6 +21,7 @@ export interface RelayMetrics { export interface UsersRelays { pubkey: HexKey; + created_at: number; relays: FullRelaySettings[]; } diff --git a/packages/app/src/Feed/LoginFeed.ts b/packages/app/src/Feed/LoginFeed.ts index fab34810..9f329067 100644 --- a/packages/app/src/Feed/LoginFeed.ts +++ b/packages/app/src/Feed/LoginFeed.ts @@ -1,5 +1,6 @@ import { useEffect, useMemo } from "react"; import { TaggedRawEvent, Lists, EventKind } from "@snort/nostr"; +import debug from "debug"; import { bech32ToHex, getNewest, getNewestEventTagsByKey, unwrap } from "SnortUtils"; import { makeNotification, sendNotification } from "Notifications"; @@ -41,6 +42,7 @@ export default function useLoginFeed() { .limit(1); const dmSince = DmCache.newest(); + debug("LoginFeed")("Loading dms since %s", new Date(dmSince * 1000).toISOString()); b.withFilter().authors([pubKey]).kinds([EventKind.DirectMessage]).since(dmSince); b.withFilter().kinds([EventKind.DirectMessage]).tag("p", [pubKey]).since(dmSince); return b; diff --git a/packages/app/src/Feed/RelaysFeedFollows.tsx b/packages/app/src/Feed/RelaysFeedFollows.tsx index 90bfb0f3..0f67815f 100644 --- a/packages/app/src/Feed/RelaysFeedFollows.tsx +++ b/packages/app/src/Feed/RelaysFeedFollows.tsx @@ -1,20 +1,24 @@ import { useMemo } from "react"; import { HexKey, FullRelaySettings, TaggedRawEvent, RelaySettings, EventKind } from "@snort/nostr"; +import debug from "debug"; import { sanitizeRelayUrl } from "SnortUtils"; import { PubkeyReplaceableNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; +import { UserRelays } from "Cache/UserRelayCache"; interface RelayList { pubkey: string; - created: number; + created_at: number; relays: FullRelaySettings[]; } export default function useRelaysFeedFollows(pubkeys: HexKey[]): Array { const sub = useMemo(() => { const b = new RequestBuilder(`relays:follows`); - b.withFilter().authors(pubkeys).kinds([EventKind.Relays, EventKind.ContactList]); + const since = UserRelays.newest(); + debug("LoginFeed")("Loading relay lists since %s", new Date(since * 1000).toISOString()); + b.withFilter().authors(pubkeys).kinds([EventKind.Relays, EventKind.ContactList]).since(since); return b; }, [pubkeys]); @@ -22,7 +26,7 @@ export default function useRelaysFeedFollows(pubkeys: HexKey[]): Array { return { pubkey: ev.pubkey, - created: ev.created_at, + created_at: ev.created_at, relays: ev.tags .map(a => { return { @@ -45,7 +49,7 @@ export default function useRelaysFeedFollows(pubkeys: HexKey[]): Array = JSON.parse(ev.content); return { pubkey: ev.pubkey, - created: ev.created_at, + created_at: ev.created_at, relays: Object.entries(relays) .map(([k, v]) => { return { @@ -61,7 +65,7 @@ export default function useRelaysFeedFollows(pubkeys: HexKey[]): Array { - // check DB support then init - db.isAvailable().then(async a => { - db.ready = a; - if (a) { - await preload(); - } - console.debug(`Using db: ${a ? "IndexedDB" : "In-Memory"}`); - - try { - if ("registerProtocolHandler" in window.navigator) { - window.navigator.registerProtocolHandler( - "web+nostr", - `${window.location.protocol}//${window.location.host}/%s` - ); - console.info("Registered protocol handler for 'web+nostr'"); - } - } catch (e) { - console.error("Failed to register protocol handler", e); - } - }); - }, []); - return (
{!shouldHideHeader && ( diff --git a/packages/app/src/System/Query.ts b/packages/app/src/System/Query.ts index d877f3ab..cb06c125 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/app/src/System/Query.ts @@ -247,7 +247,7 @@ export class Query implements QueryBase { return false; } if ((q.relays?.length ?? 0) === 0 && c.Ephemeral) { - this.#log("Cant send non-specific REQ to ephemeral connection"); + this.#log("Cant send non-specific REQ to ephemeral connection %o", q.relays); return false; } if (q.filters.some(a => a.search) && !c.SupportsNip(Nips.Search)) { diff --git a/packages/app/src/System/index.ts b/packages/app/src/System/index.ts index 5c8a1e49..89146f19 100644 --- a/packages/app/src/System/index.ts +++ b/packages/app/src/System/index.ts @@ -221,7 +221,7 @@ export class NostrSystem extends ExternalStore { if (rb.options?.leaveOpen) { q.leaveOpen = rb.options.leaveOpen; } - if (rb.options?.relays) { + if (rb.options?.relays && (rb.options?.relays?.length ?? 0) > 0) { q.relays = rb.options.relays; } diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index 863e2bf6..0d012cf1 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -32,6 +32,9 @@ import Thread from "Element/Thread"; import { SubscribeRoutes } from "Pages/subscribe"; import ZapPoolPage from "Pages/ZapPool"; import DebugPage from "Pages/Debug"; +import { db } from "Db"; +import { preload } from "Cache"; +import { LoginStore } from "Login"; // @ts-ignore window.__webpack_nonce__ = "ZmlhdGphZiBzYWlkIHNub3J0LnNvY2lhbCBpcyBwcmV0dHkgZ29vZCwgd2UgbWFkZSBpdCE="; @@ -42,6 +45,25 @@ export const router = createBrowserRouter([ { element: , errorElement: , + loader: async () => { + db.ready = await db.isAvailable(); + if (db.ready) { + await preload(LoginStore.takeSnapshot().follows.item); + } + + try { + if ("registerProtocolHandler" in window.navigator) { + window.navigator.registerProtocolHandler( + "web+nostr", + `${window.location.protocol}//${window.location.host}/%s` + ); + console.info("Registered protocol handler for 'web+nostr'"); + } + } catch (e) { + console.error("Failed to register protocol handler", e); + } + return null; + }, children: [ ...RootRoutes, { From f684658183370faa1f72e49b2dbc39d8d607551f Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 25 May 2023 11:05:06 +0100 Subject: [PATCH 08/24] fix: Service worker precache delete fix: pubkey in profile metadata breaks cache refresh fix: non-specific queries not sent to any relays sometimes fix: sub-query trace not sending "CLOSE" --- packages/app/src/Cache/index.ts | 2 +- packages/app/src/System/Query.ts | 21 ++++++++++++----- packages/app/src/System/index.ts | 38 ++++++------------------------ packages/app/src/service-worker.ts | 2 -- 4 files changed, 23 insertions(+), 40 deletions(-) diff --git a/packages/app/src/Cache/index.ts b/packages/app/src/Cache/index.ts index 8942fede..24b8e991 100644 --- a/packages/app/src/Cache/index.ts +++ b/packages/app/src/Cache/index.ts @@ -41,10 +41,10 @@ export function mapEventToProfile(ev: RawEvent) { try { const data: UserMetadata = JSON.parse(ev.content); return { + ...data, pubkey: ev.pubkey, npub: hexToBech32("npub", ev.pubkey), created: ev.created_at, - ...data, loaded: unixNowMs(), } as MetadataCache; } catch (e) { diff --git a/packages/app/src/System/Query.ts b/packages/app/src/System/Query.ts index cb06c125..9622e959 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/app/src/System/Query.ts @@ -13,6 +13,7 @@ class QueryTrace { readonly relay: string; readonly connId: string; readonly start: number; + readonly leaveOpen: boolean; sent?: number; eose?: number; close?: number; @@ -21,11 +22,19 @@ class QueryTrace { readonly #fnProgress: () => void; readonly #log = debug("QueryTrace"); - constructor(sub: string, relay: string, connId: string, fnClose: (id: string) => void, fnProgress: () => void) { + constructor( + sub: string, + relay: string, + connId: string, + leaveOpen: boolean, + fnClose: (id: string) => void, + fnProgress: () => void + ) { this.id = uuid(); this.subId = sub; this.relay = relay; this.connId = connId; + this.leaveOpen = leaveOpen; this.start = unixNowMs(); this.#fnClose = fnClose; this.#fnProgress = fnProgress; @@ -39,6 +48,9 @@ class QueryTrace { gotEose() { this.eose = unixNowMs(); this.#fnProgress(); + if (!this.leaveOpen) { + this.sendClose(); + } //this.#log("[EOSE] %s %s", this.subId, this.relay); } @@ -46,6 +58,7 @@ class QueryTrace { this.eose = unixNowMs(); this.#wasForceClosed = true; this.#fnProgress(); + this.sendClose(); //this.#log("[F-EOSE] %s %s", this.subId, this.relay); } @@ -199,11 +212,6 @@ export class Query implements QueryBase { eose(sub: string, conn: Readonly) { const qt = this.#tracing.find(a => a.subId === sub && a.connId === conn.Id); qt?.gotEose(); - if (sub === this.id) { - if (!this.leaveOpen) { - qt?.sendClose(); - } - } } /** @@ -262,6 +270,7 @@ export class Query implements QueryBase { q.id, c.Address, c.Id, + this.leaveOpen, x => c.CloseReq(x), () => this.#onProgress() ); diff --git a/packages/app/src/System/index.ts b/packages/app/src/System/index.ts index 89146f19..9a061a84 100644 --- a/packages/app/src/System/index.ts +++ b/packages/app/src/System/index.ts @@ -202,7 +202,7 @@ export class NostrSystem extends ExternalStore { filters: sf.filters, relays: sf.relay ? [sf.relay] : undefined, } as QueryBase; - this.SendSubQuery(q, subQ); + this.SendQuery(q, subQ, (q, s, c) => q.sendSubQueryToRelay(c, s)); } q.filters = filters; this.notifyChange(); @@ -234,10 +234,10 @@ export class NostrSystem extends ExternalStore { filters: sf.filters, relays: sf.relay ? [sf.relay] : undefined, } as QueryBase; - this.SendSubQuery(q, subQ); + this.SendQuery(q, subQ, (q, s, c) => q.sendSubQueryToRelay(c, s)); } } else { - this.SendQuery(q); + this.SendQuery(q, q, (q, s, c) => q.sendToRelay(c)); } this.notifyChange(); return store; @@ -250,16 +250,16 @@ export class NostrSystem extends ExternalStore { } } - async SendQuery(q: Query) { + async SendQuery(q: Query, qSend: QueryBase, qSender: (q: Query, qSend: QueryBase, c: Connection) => void) { if (q.relays && q.relays.length > 0) { for (const r of q.relays) { const s = this.Sockets.get(r); if (s) { - q.sendToRelay(s); + qSender(q, qSend, s); } else { const nc = await this.ConnectEphemeralRelay(r); if (nc) { - q.sendToRelay(nc); + qSender(q, qSend, nc); } else { console.warn("Failed to connect to new relay for:", r, q); } @@ -268,31 +268,7 @@ export class NostrSystem extends ExternalStore { } else { for (const [, s] of this.Sockets) { if (!s.Ephemeral) { - q.sendToRelay(s); - } - } - } - } - - async SendSubQuery(q: Query, subQ: QueryBase) { - if (subQ.relays && subQ.relays.length > 0) { - for (const r of subQ.relays) { - const s = this.Sockets.get(r); - if (s) { - q.sendSubQueryToRelay(s, subQ); - } else { - const nc = await this.ConnectEphemeralRelay(r); - if (nc) { - q.sendSubQueryToRelay(nc, subQ); - } else { - console.warn("Failed to connect to new relay for:", r, subQ); - } - } - } - } else { - for (const [, s] of this.Sockets) { - if (!s.Ephemeral) { - q.sendSubQueryToRelay(s, subQ); + qSender(q, qSend, s); } } } diff --git a/packages/app/src/service-worker.ts b/packages/app/src/service-worker.ts index 1e03c491..26cf6bf3 100644 --- a/packages/app/src/service-worker.ts +++ b/packages/app/src/service-worker.ts @@ -4,12 +4,10 @@ declare var self: ServiceWorkerGlobalScope; import { clientsClaim } from "workbox-core"; import { ExpirationPlugin } from "workbox-expiration"; -import { precacheAndRoute } from "workbox-precaching"; import { registerRoute } from "workbox-routing"; import { StaleWhileRevalidate, CacheFirst } from "workbox-strategies"; clientsClaim(); -precacheAndRoute(self.__WB_MANIFEST); const staticTypes = ["image", "video", "audio"]; registerRoute( From 9bfb6ede0a2ec81eaac91cbcf4d70223f1a985c1 Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 25 May 2023 11:12:08 +0100 Subject: [PATCH 09/24] fix: eslint errors --- packages/app/src/Cache/DMCache.ts | 4 ++-- packages/app/src/Cache/EventInteractionCache.ts | 4 ++-- packages/app/src/Cache/FeedCache.ts | 2 +- packages/app/src/Cache/UserCache.ts | 2 +- packages/app/src/Cache/UserRelayCache.ts | 2 +- packages/app/src/Cache/index.ts | 4 ++-- packages/app/src/Element/Relay.tsx | 1 - packages/app/src/Wallet/Cashu.ts | 3 +-- packages/app/src/ZapPoolController.ts | 5 ++--- packages/app/src/index.tsx | 2 +- packages/app/src/service-worker.ts | 2 +- 11 files changed, 14 insertions(+), 17 deletions(-) diff --git a/packages/app/src/Cache/DMCache.ts b/packages/app/src/Cache/DMCache.ts index 2ea1820d..a147559f 100644 --- a/packages/app/src/Cache/DMCache.ts +++ b/packages/app/src/Cache/DMCache.ts @@ -11,8 +11,8 @@ class DMCache extends FeedCache { return of.id; } - override async preload(follows?: Array): Promise { - await super.preload(follows); + override async preload(): Promise { + await super.preload(); // load all dms to memory await this.buffer([...this.onTable]); } diff --git a/packages/app/src/Cache/EventInteractionCache.ts b/packages/app/src/Cache/EventInteractionCache.ts index 08893705..1a727c06 100644 --- a/packages/app/src/Cache/EventInteractionCache.ts +++ b/packages/app/src/Cache/EventInteractionCache.ts @@ -12,8 +12,8 @@ class EventInteractionCache extends FeedCache { return sha256(of.event + of.by); } - override async preload(follows?: Array): Promise { - await super.preload(follows); + override async preload(): Promise { + await super.preload(); const data = window.localStorage.getItem("zap-cache"); if (data) { diff --git a/packages/app/src/Cache/FeedCache.ts b/packages/app/src/Cache/FeedCache.ts index 8d7966ed..1e62fb2d 100644 --- a/packages/app/src/Cache/FeedCache.ts +++ b/packages/app/src/Cache/FeedCache.ts @@ -35,7 +35,7 @@ export default abstract class FeedCache { }, 30_000); } - async preload(follows?: Array) { + async preload() { if (db.ready) { const keys = await this.table.toCollection().primaryKeys(); this.onTable = new Set(keys.map(a => a as string)); diff --git a/packages/app/src/Cache/UserCache.ts b/packages/app/src/Cache/UserCache.ts index 06c99f73..731a7422 100644 --- a/packages/app/src/Cache/UserCache.ts +++ b/packages/app/src/Cache/UserCache.ts @@ -19,7 +19,7 @@ class UserProfileCache extends FeedCache { } override async preload(follows?: Array): Promise { - await super.preload(follows); + await super.preload(); // load follows profiles if (follows) { await this.buffer(follows); diff --git a/packages/app/src/Cache/UserRelayCache.ts b/packages/app/src/Cache/UserRelayCache.ts index 532d8a69..37e38036 100644 --- a/packages/app/src/Cache/UserRelayCache.ts +++ b/packages/app/src/Cache/UserRelayCache.ts @@ -11,7 +11,7 @@ class UsersRelaysCache extends FeedCache { } override async preload(follows?: Array): Promise { - await super.preload(follows); + await super.preload(); if (follows) { await this.buffer(follows); } diff --git a/packages/app/src/Cache/index.ts b/packages/app/src/Cache/index.ts index 24b8e991..f5f13a68 100644 --- a/packages/app/src/Cache/index.ts +++ b/packages/app/src/Cache/index.ts @@ -55,8 +55,8 @@ export function mapEventToProfile(ev: RawEvent) { export async function preload(follows?: Array) { const preloads = [ UserCache.preload(follows), - DmCache.preload(follows), - InteractionCache.preload(follows), + DmCache.preload(), + InteractionCache.preload(), UserRelays.preload(follows), ]; await Promise.all(preloads); diff --git a/packages/app/src/Element/Relay.tsx b/packages/app/src/Element/Relay.tsx index 6c4d898f..fcc2b912 100644 --- a/packages/app/src/Element/Relay.tsx +++ b/packages/app/src/Element/Relay.tsx @@ -35,7 +35,6 @@ export default function Relay(props: RelayProps) { ); } - const latency = Math.floor(state?.avgLatency ?? 0); return ( <>
diff --git a/packages/app/src/Wallet/Cashu.ts b/packages/app/src/Wallet/Cashu.ts index 7bbf87e0..541e65ba 100644 --- a/packages/app/src/Wallet/Cashu.ts +++ b/packages/app/src/Wallet/Cashu.ts @@ -1,5 +1,4 @@ -import { InvoiceRequest, LNWallet, Sats, WalletError, WalletErrorCode, WalletInfo, WalletInvoice } from "Wallet"; - +import { LNWallet, Sats, WalletError, WalletErrorCode, WalletInfo, WalletInvoice } from "Wallet"; import { CashuMint, CashuWallet as TheCashuWallet, Proof } from "@cashu/cashu-ts"; export class CashuWallet implements LNWallet { diff --git a/packages/app/src/ZapPoolController.ts b/packages/app/src/ZapPoolController.ts index 41148b7d..3db499f2 100644 --- a/packages/app/src/ZapPoolController.ts +++ b/packages/app/src/ZapPoolController.ts @@ -21,13 +21,12 @@ export interface ZapPoolRecipient { } class ZapPool extends ExternalStore> { - #store: Map; + #store = new Map(); #isPayoutInProgress = false; - #lastPayout: number = 0; + #lastPayout = 0; constructor() { super(); - this.#store = new Map(); this.#load(); setTimeout(() => this.#autoPayout().catch(console.error), 5_000); } diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index 0d012cf1..c3e3313c 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -36,7 +36,7 @@ import { db } from "Db"; import { preload } from "Cache"; import { LoginStore } from "Login"; -// @ts-ignore +// @ts-expect-error Setting webpack nonce window.__webpack_nonce__ = "ZmlhdGphZiBzYWlkIHNub3J0LnNvY2lhbCBpcyBwcmV0dHkgZ29vZCwgd2UgbWFkZSBpdCE="; serviceWorkerRegistration.register(); diff --git a/packages/app/src/service-worker.ts b/packages/app/src/service-worker.ts index 26cf6bf3..497749be 100644 --- a/packages/app/src/service-worker.ts +++ b/packages/app/src/service-worker.ts @@ -1,6 +1,6 @@ /// import {} from "."; -declare var self: ServiceWorkerGlobalScope; +declare const self: ServiceWorkerGlobalScope; import { clientsClaim } from "workbox-core"; import { ExpirationPlugin } from "workbox-expiration"; From ca92b365e0dbbcb8746301114bcd371e1fc0183f Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 25 May 2023 11:51:59 +0100 Subject: [PATCH 10/24] fix: connect login relays before layout fix: save relays bug --- packages/app/src/Pages/settings/Relays.tsx | 2 +- packages/app/src/System/Query.ts | 2 +- packages/app/src/System/index.ts | 8 ++++++-- packages/app/src/index.tsx | 7 ++++++- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/app/src/Pages/settings/Relays.tsx b/packages/app/src/Pages/settings/Relays.tsx index f2f42435..0cf5b6b2 100644 --- a/packages/app/src/Pages/settings/Relays.tsx +++ b/packages/app/src/Pages/settings/Relays.tsx @@ -27,7 +27,7 @@ const RelaySettingsPage = () => { try { const onlineRelays = await fetch("https://api.nostr.watch/v1/online").then(r => r.json()); const relayList = await publisher.relayList(login.relays.item); - const rs = Object.keys(relays).concat(randomSample(onlineRelays, 20)); + const rs = Object.keys(relays.item).concat(randomSample(onlineRelays, 20)); publisher.broadcastAll(relayList, rs); } catch (error) { console.error(error); diff --git a/packages/app/src/System/Query.ts b/packages/app/src/System/Query.ts index 9622e959..16e0b8eb 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/app/src/System/Query.ts @@ -255,7 +255,7 @@ export class Query implements QueryBase { return false; } if ((q.relays?.length ?? 0) === 0 && c.Ephemeral) { - this.#log("Cant send non-specific REQ to ephemeral connection %o", q.relays); + this.#log("Cant send non-specific REQ to ephemeral connection %O %O %O", q, q.relays, c); return false; } if (q.filters.some(a => a.search) && !c.SupportsNip(Nips.Search)) { diff --git a/packages/app/src/System/index.ts b/packages/app/src/System/index.ts index 9a061a84..ed3ba4e8 100644 --- a/packages/app/src/System/index.ts +++ b/packages/app/src/System/index.ts @@ -1,4 +1,5 @@ import { AuthHandler, TaggedRawEvent, RelaySettings, Connection, RawReqFilter, RawEvent } from "@snort/nostr"; +import debug from "debug"; import { sanitizeRelayUrl, unixNowMs, unwrap } from "SnortUtils"; import { RequestBuilder } from "./RequestBuilder"; @@ -57,6 +58,8 @@ export class NostrSystem extends ExternalStore { */ HandleAuth?: AuthHandler; + #log = debug("System"); + constructor() { super(); this.Sockets = new Map(); @@ -251,8 +254,9 @@ export class NostrSystem extends ExternalStore { } async SendQuery(q: Query, qSend: QueryBase, qSender: (q: Query, qSend: QueryBase, c: Connection) => void) { - if (q.relays && q.relays.length > 0) { - for (const r of q.relays) { + if (qSend.relays && qSend.relays.length > 0) { + for (const r of qSend.relays) { + this.#log("Sending query to %s %O", r, qSend); const s = this.Sockets.get(r); if (s) { qSender(q, qSend, s); diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index c3e3313c..9ab1b151 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -35,6 +35,7 @@ import DebugPage from "Pages/Debug"; import { db } from "Db"; import { preload } from "Cache"; import { LoginStore } from "Login"; +import { System } from "System"; // @ts-expect-error Setting webpack nonce window.__webpack_nonce__ = "ZmlhdGphZiBzYWlkIHNub3J0LnNvY2lhbCBpcyBwcmV0dHkgZ29vZCwgd2UgbWFkZSBpdCE="; @@ -46,11 +47,15 @@ export const router = createBrowserRouter([ element: , errorElement: , loader: async () => { + const login = LoginStore.takeSnapshot(); db.ready = await db.isAvailable(); if (db.ready) { - await preload(LoginStore.takeSnapshot().follows.item); + await preload(login.follows.item); } + for (const [k, v] of Object.entries(login.relays.item)) { + await System.ConnectToRelay(k, v); + } try { if ("registerProtocolHandler" in window.navigator) { window.navigator.registerProtocolHandler( From 9a33466c7cc59e5478d51aaedcb921454d055ea6 Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 25 May 2023 19:52:03 +0100 Subject: [PATCH 11/24] refactor request builder to handle relay hints --- packages/app/src/Feed/RelaysFeedFollows.tsx | 1 + packages/app/src/Feed/ThreadFeed.ts | 41 +++++-- packages/app/src/System/GossipModel.ts | 35 +++--- packages/app/src/System/NoteCollection.ts | 7 +- packages/app/src/System/Query.ts | 48 +++----- .../app/src/System/RequestBuilder.test.ts | 23 ++-- packages/app/src/System/RequestBuilder.ts | 108 ++++++++++++++++-- packages/app/src/System/RequestMerger.ts | 5 + packages/app/src/System/RequestSplitter.ts | 3 + packages/app/src/System/index.ts | 84 +++++++------- packages/nostr/src/legacy/Connection.ts | 5 +- 11 files changed, 242 insertions(+), 118 deletions(-) create mode 100644 packages/app/src/System/RequestMerger.ts diff --git a/packages/app/src/Feed/RelaysFeedFollows.tsx b/packages/app/src/Feed/RelaysFeedFollows.tsx index 0f67815f..42566875 100644 --- a/packages/app/src/Feed/RelaysFeedFollows.tsx +++ b/packages/app/src/Feed/RelaysFeedFollows.tsx @@ -42,6 +42,7 @@ export default function useRelaysFeedFollows(pubkeys: HexKey[]): Array): Array { return notes.map(ev => { if (ev.content !== "" && ev.content !== "{}" && ev.content.startsWith("{") && ev.content.endsWith("}")) { diff --git a/packages/app/src/Feed/ThreadFeed.ts b/packages/app/src/Feed/ThreadFeed.ts index 9d36cbe3..f7a41f7a 100644 --- a/packages/app/src/Feed/ThreadFeed.ts +++ b/packages/app/src/Feed/ThreadFeed.ts @@ -6,10 +6,18 @@ import { FlatNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; import useLogin from "Hooks/useLogin"; +interface RelayTaggedEventId { + id: u256; + relay?: string; +} export default function useThreadFeed(link: NostrLink) { - const [trackingEvents, setTrackingEvent] = useState([link.id]); + const linkTagged = { + id: link.id, + relay: link.relays?.[0], + }; + const [trackingEvents, setTrackingEvent] = useState>([linkTagged]); const [trackingATags, setTrackingATags] = useState([]); - const [allEvents, setAllEvents] = useState([link.id]); + const [allEvents, setAllEvents] = useState>([linkTagged]); const pref = useLogin().preferences; const sub = useMemo(() => { @@ -17,7 +25,10 @@ export default function useThreadFeed(link: NostrLink) { sub.withOptions({ leaveOpen: true, }); - sub.withFilter().ids(trackingEvents); + const fTracking = sub.withFilter(); + for (const te of trackingEvents) { + fTracking.id(te.id, te.relay); + } sub .withFilter() .kinds( @@ -25,7 +36,10 @@ export default function useThreadFeed(link: NostrLink) { ? [EventKind.Reaction, EventKind.TextNote, EventKind.Repost, EventKind.ZapReceipt] : [EventKind.TextNote, EventKind.ZapReceipt, EventKind.Repost] ) - .tag("e", allEvents); + .tag( + "e", + allEvents.map(a => a.id) + ); if (trackingATags.length > 0) { const parsed = trackingATags.map(a => a.split(":")); @@ -45,16 +59,27 @@ export default function useThreadFeed(link: NostrLink) { useEffect(() => { setTrackingATags([]); - setTrackingEvent([link.id]); - setAllEvents([link.id]); + setTrackingEvent([linkTagged]); + setAllEvents([linkTagged]); }, [link.id]); useEffect(() => { if (store.data) { const mainNotes = store.data?.filter(a => a.kind === EventKind.TextNote || a.kind === EventKind.Polls) ?? []; - const eTags = mainNotes.map(a => a.tags.filter(b => b[0] === "e").map(b => b[1])).flat(); - const eTagsMissing = eTags.filter(a => !mainNotes.some(b => b.id === a)); + const eTags = mainNotes + .map(a => + a.tags + .filter(b => b[0] === "e") + .map(b => { + return { + id: b[1], + relay: b[2], + }; + }) + ) + .flat(); + const eTagsMissing = eTags.filter(a => !mainNotes.some(b => b.id === a.id)); setTrackingEvent(s => appendDedupe(s, eTagsMissing)); setAllEvents(s => appendDedupe(s, eTags)); diff --git a/packages/app/src/System/GossipModel.ts b/packages/app/src/System/GossipModel.ts index 06c359f4..914f57a6 100644 --- a/packages/app/src/System/GossipModel.ts +++ b/packages/app/src/System/GossipModel.ts @@ -1,5 +1,4 @@ -import { RawReqFilter } from "@snort/nostr"; -import { UserRelays } from "Cache/UserRelayCache"; +import { FullRelaySettings, RawReqFilter } from "@snort/nostr"; import { unwrap } from "SnortUtils"; import debug from "debug"; @@ -15,18 +14,24 @@ export interface RelayTaggedFilters { filters: Array; } -export function splitAllByWriteRelays(filters: Array) { - const allSplit = filters.map(splitByWriteRelays).reduce((acc, v) => { - for (const vn of v) { - const existing = acc.get(vn.relay); - if (existing) { - existing.push(vn.filter); - } else { - acc.set(vn.relay, [vn.filter]); +export interface RelayCache { + get(pubkey?: string): Array | undefined; +} + +export function splitAllByWriteRelays(cache: RelayCache, filters: Array) { + const allSplit = filters + .map(a => splitByWriteRelays(cache, a)) + .reduce((acc, v) => { + for (const vn of v) { + const existing = acc.get(vn.relay); + if (existing) { + existing.push(vn.filter); + } else { + acc.set(vn.relay, [vn.filter]); + } } - } - return acc; - }, new Map>()); + return acc; + }, new Map>()); return [...allSplit.entries()].map(([k, v]) => { return { @@ -41,7 +46,7 @@ export function splitAllByWriteRelays(filters: Array) { * @param filter * @returns */ -export function splitByWriteRelays(filter: RawReqFilter): Array { +export function splitByWriteRelays(cache: RelayCache, filter: RawReqFilter): Array { if ((filter.authors?.length ?? 0) === 0) return [ { @@ -53,7 +58,7 @@ export function splitByWriteRelays(filter: RawReqFilter): Array { return { key: a, - relays: UserRelays.getFromCache(a)?.relays, + relays: cache.get(a)?.filter(a => a.settings.write), }; }); diff --git a/packages/app/src/System/NoteCollection.ts b/packages/app/src/System/NoteCollection.ts index 3044d32b..da7efa64 100644 --- a/packages/app/src/System/NoteCollection.ts +++ b/packages/app/src/System/NoteCollection.ts @@ -1,5 +1,5 @@ import { TaggedRawEvent, u256 } from "@snort/nostr"; -import { findTag } from "SnortUtils"; +import { appendDedupe, findTag } from "SnortUtils"; export interface StoreSnapshot { data: TSnapshot | undefined; @@ -142,6 +142,11 @@ export class FlatNoteStore extends HookedNoteStore b.id === a.id); + if (existing) { + existing.relays = appendDedupe(existing.relays, a.relays); + } } }); diff --git a/packages/app/src/System/Query.ts b/packages/app/src/System/Query.ts index 16e0b8eb..8edc1bf6 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/app/src/System/Query.ts @@ -13,28 +13,18 @@ class QueryTrace { readonly relay: string; readonly connId: string; readonly start: number; - readonly leaveOpen: boolean; sent?: number; eose?: number; close?: number; #wasForceClosed = false; readonly #fnClose: (id: string) => void; readonly #fnProgress: () => void; - readonly #log = debug("QueryTrace"); - constructor( - sub: string, - relay: string, - connId: string, - leaveOpen: boolean, - fnClose: (id: string) => void, - fnProgress: () => void - ) { + constructor(sub: string, relay: string, connId: string, fnClose: (id: string) => void, fnProgress: () => void) { this.id = uuid(); this.subId = sub; this.relay = relay; this.connId = connId; - this.leaveOpen = leaveOpen; this.start = unixNowMs(); this.#fnClose = fnClose; this.#fnProgress = fnProgress; @@ -48,10 +38,6 @@ class QueryTrace { gotEose() { this.eose = unixNowMs(); this.#fnProgress(); - if (!this.leaveOpen) { - this.sendClose(); - } - //this.#log("[EOSE] %s %s", this.subId, this.relay); } forceEose() { @@ -59,7 +45,6 @@ class QueryTrace { this.#wasForceClosed = true; this.#fnProgress(); this.sendClose(); - //this.#log("[F-EOSE] %s %s", this.subId, this.relay); } sendClose() { @@ -117,10 +102,16 @@ export interface QueryBase { /** * Active or queued query on the system */ -export class Query implements QueryBase { +export class Query { + /** + * Uniquie ID of this query + */ id: string; - filters: Array; - relays?: Array; + + /** + * A merged set of all filters send to relays for this query + */ + filters: Array = []; /** * Which relays this query has already been executed on @@ -150,9 +141,8 @@ export class Query implements QueryBase { subQueryCounter = 0; #log = debug("Query"); - constructor(id: string, filters: Array, feed: NoteStore) { + constructor(id: string, feed: NoteStore) { this.id = id; - this.filters = filters; this.#feed = feed; this.#checkTraces(); } @@ -181,14 +171,7 @@ export class Query implements QueryBase { this.#stopCheckTraces(); } - sendToRelay(c: Connection) { - if (!this.#canSendQuery(c, this)) { - return; - } - this.#sendQueryInternal(c, this); - } - - sendSubQueryToRelay(c: Connection, subq: QueryBase) { + sendToRelay(c: Connection, subq: QueryBase) { if (!this.#canSendQuery(c, subq)) { return; } @@ -200,9 +183,6 @@ export class Query implements QueryBase { } sendClose() { - for (const qt of this.#tracing) { - qt.sendClose(); - } for (const qt of this.#tracing) { qt.sendClose(); } @@ -212,6 +192,9 @@ export class Query implements QueryBase { eose(sub: string, conn: Readonly) { const qt = this.#tracing.find(a => a.subId === sub && a.connId === conn.Id); qt?.gotEose(); + if (!this.leaveOpen) { + qt?.sendClose(); + } } /** @@ -270,7 +253,6 @@ export class Query implements QueryBase { q.id, c.Address, c.Id, - this.leaveOpen, x => c.CloseReq(x), () => this.#onProgress() ); diff --git a/packages/app/src/System/RequestBuilder.test.ts b/packages/app/src/System/RequestBuilder.test.ts index 29527438..fe1a84c4 100644 --- a/packages/app/src/System/RequestBuilder.test.ts +++ b/packages/app/src/System/RequestBuilder.test.ts @@ -1,22 +1,29 @@ +import { RelayCache } from "./GossipModel"; import { RequestBuilder } from "./RequestBuilder"; import { describe, expect } from "@jest/globals"; describe("RequestBuilder", () => { + const relayCache = { + get: () => { + return undefined; + }, + } as RelayCache; + describe("basic", () => { test("empty filter", () => { const b = new RequestBuilder("test"); b.withFilter(); - expect(b.build()).toEqual([{}]); + expect(b.build(relayCache)).toEqual([{}]); }); test("only kind", () => { const b = new RequestBuilder("test"); b.withFilter().kinds([0]); - expect(b.build()).toEqual([{ kinds: [0] }]); + expect(b.build(relayCache)).toEqual([{ kinds: [0] }]); }); test("empty authors", () => { const b = new RequestBuilder("test"); b.withFilter().authors([]); - expect(b.build()).toEqual([{ authors: [] }]); + expect(b.build(relayCache)).toEqual([{ authors: [] }]); }); test("authors/kinds/ids", () => { const authors = ["a1", "a2"]; @@ -24,7 +31,7 @@ describe("RequestBuilder", () => { const ids = ["id1", "id2", "id3"]; const b = new RequestBuilder("test"); b.withFilter().authors(authors).kinds(kinds).ids(ids); - expect(b.build()).toEqual([{ ids, authors, kinds }]); + expect(b.build(relayCache)).toEqual([{ ids, authors, kinds }]); }); test("authors and kinds, duplicates removed", () => { const authors = ["a1", "a2"]; @@ -32,12 +39,12 @@ describe("RequestBuilder", () => { const ids = ["id1", "id2", "id3"]; const b = new RequestBuilder("test"); b.withFilter().ids(ids).authors(authors).kinds(kinds).ids(ids).authors(authors).kinds(kinds); - expect(b.build()).toEqual([{ ids, authors, kinds }]); + expect(b.build(relayCache)).toEqual([{ ids, authors, kinds }]); }); test("search", () => { const b = new RequestBuilder("test"); b.withFilter().kinds([1]).search("test-search"); - expect(b.build()).toEqual([{ kinds: [1], search: "test-search" }]); + expect(b.build(relayCache)).toEqual([{ kinds: [1], search: "test-search" }]); }); test("timeline", () => { const authors = ["a1", "a2"]; @@ -46,7 +53,7 @@ describe("RequestBuilder", () => { const since = 5; const b = new RequestBuilder("test"); b.withFilter().kinds(kinds).authors(authors).since(since).until(until); - expect(b.build()).toEqual([{ kinds, authors, until, since }]); + expect(b.build(relayCache)).toEqual([{ kinds, authors, until, since }]); }); test("multi-filter timeline", () => { const authors = ["a1", "a2"]; @@ -56,7 +63,7 @@ describe("RequestBuilder", () => { const b = new RequestBuilder("test"); b.withFilter().kinds(kinds).authors(authors).since(since).until(until); b.withFilter().kinds(kinds).authors(authors).since(since).until(until); - expect(b.build()).toEqual([ + expect(b.build(relayCache)).toEqual([ { kinds, authors, until, since }, { kinds, authors, until, since }, ]); diff --git a/packages/app/src/System/RequestBuilder.ts b/packages/app/src/System/RequestBuilder.ts index 5d3deb5d..16c7f653 100644 --- a/packages/app/src/System/RequestBuilder.ts +++ b/packages/app/src/System/RequestBuilder.ts @@ -1,10 +1,14 @@ import { RawReqFilter, u256, HexKey, EventKind } from "@snort/nostr"; -import { appendDedupe } from "SnortUtils"; +import { appendDedupe, dedupe } from "SnortUtils"; +import { QueryBase } from "./Query"; +import { diffFilters } from "./RequestSplitter"; +import { RelayCache, splitByWriteRelays } from "./GossipModel"; +import { mergeSimilar } from "./RequestMerger"; /** * Which strategy is used when building REQ filters */ -export enum NostrRequestStrategy { +export enum RequestStrategy { /** * Use the users default relays to fetch events, * this is the fallback option when there is no better way to query a given filter set @@ -26,10 +30,9 @@ export enum NostrRequestStrategy { * A built REQ filter ready for sending to System */ export interface BuiltRawReqFilter { - id: string; - filter: Array; - relays: Array; - strategy: NostrRequestStrategy; + filter: RawReqFilter; + relay: string; + strategy: RequestStrategy; } export interface RequestBuilderOptions { @@ -76,8 +79,55 @@ export class RequestBuilder { return this; } - build(): Array { - return this.#builders.map(a => a.filter); + buildRaw(): Array { + return this.#builders.map(f => f.filter); + } + + build(relays: RelayCache): Array { + const expanded = this.#builders.map(a => a.build(relays)).flat(); + return this.#mergeSimilar(expanded); + } + + /** + * Detects a change in request from a previous set of filters + * @param q All previous filters merged + * @returns + */ + buildDiff(relays: RelayCache, q: QueryBase): Array { + const next = this.buildRaw(); + const diff = diffFilters(q.filters, next); + if (diff.changed) { + } + return []; + } + + /** + * Merge a set of expanded filters into the smallest number of subscriptions by merging similar requests + * @param expanded + * @returns + */ + #mergeSimilar(expanded: Array) { + const relayMerged = expanded.reduce((acc, v) => { + const existing = acc.get(v.relay); + if (existing) { + existing.push(v); + } else { + acc.set(v.relay, [v]); + } + return acc; + }, new Map>()); + + const filtersSquashed = [...relayMerged.values()].flatMap(a => { + return mergeSimilar(a.map(b => b.filter)).map(b => { + return { + filter: b, + relay: a[0].relay, + strategy: a[0].strategy, + } as BuiltRawReqFilter; + }); + }); + + return filtersSquashed; } } @@ -86,7 +136,7 @@ export class RequestBuilder { */ export class RequestFilterBuilder { #filter: RawReqFilter = {}; - #relayHints: Map> = new Map(); + #relayHints = new Map>(); get filter() { return { ...this.#filter }; @@ -149,4 +199,44 @@ export class RequestFilterBuilder { this.#filter.search = keyword; return this; } + + /** + * Build/expand this filter into a set of relay specific queries + */ + build(relays: RelayCache): Array { + // when querying for specific event ids with relay hints + // take the first approach which is to split the filter by relay + if (this.#filter.ids && this.#relayHints.size > 0) { + const relays = dedupe([...this.#relayHints.values()].flat()); + return relays.map(r => { + return { + filter: { + ...this.#filter, + ids: [...this.#relayHints.entries()].filter(([, v]) => v.includes(r)).map(([k]) => k), + }, + relay: r, + strategy: RequestStrategy.RelayHintedEventIds, + }; + }); + } + + // If any authors are set use the gossip model to fetch data for each author + if (this.#filter.authors) { + const split = splitByWriteRelays(relays, this.#filter); + return split.map(a => { + return { + ...a, + strategy: RequestStrategy.AuthorsRelays, + }; + }); + } + + return [ + { + filter: this.filter, + relay: "*", + strategy: RequestStrategy.DefaultRelays, + }, + ]; + } } diff --git a/packages/app/src/System/RequestMerger.ts b/packages/app/src/System/RequestMerger.ts new file mode 100644 index 00000000..3e93e753 --- /dev/null +++ b/packages/app/src/System/RequestMerger.ts @@ -0,0 +1,5 @@ +import { RawReqFilter } from "@snort/nostr"; + +export function mergeSimilar(filters: Array): Array { + return filters; +} diff --git a/packages/app/src/System/RequestSplitter.ts b/packages/app/src/System/RequestSplitter.ts index 23794565..7f422cee 100644 --- a/packages/app/src/System/RequestSplitter.ts +++ b/packages/app/src/System/RequestSplitter.ts @@ -15,6 +15,9 @@ export function diffFilters(a: Array, b: Array) { for (const [k, v] of Object.entries(bN)) { if (Array.isArray(v)) { const prevArray = prev[k] as Array; + if (!prevArray) { + throw new Error(`Tried to add new filter prop ${k} which isnt supported!`); + } const thisArray = v as Array; const added = thisArray.filter(a => !prevArray.includes(a)); // support adding new values to array, removing values is ignored since we only care about getting new values diff --git a/packages/app/src/System/index.ts b/packages/app/src/System/index.ts index ed3ba4e8..f80b2133 100644 --- a/packages/app/src/System/index.ts +++ b/packages/app/src/System/index.ts @@ -15,6 +15,7 @@ import { diffFilters } from "./RequestSplitter"; import { Query, QueryBase } from "./Query"; import { splitAllByWriteRelays } from "./GossipModel"; import ExternalStore from "ExternalStore"; +import { UserRelays } from "Cache/UserRelayCache"; export { NoteStore, @@ -59,6 +60,9 @@ export class NostrSystem extends ExternalStore { HandleAuth?: AuthHandler; #log = debug("System"); + #relayCache = { + get: (pk?: string) => UserRelays.getFromCache(pk)?.relays, + }; constructor() { super(); @@ -188,17 +192,42 @@ export class NostrSystem extends ExternalStore { if (!req) return new type(); - if (this.Queries.has(req.id)) { - const filters = req.build(); - const q = unwrap(this.Queries.get(req.id)); - q.unCancel(); + const existing = this.Queries.get(req.id); + if (existing) { + const filters = req.buildDiff(this.#relayCache, existing); + existing.unCancel(); - const diff = diffFilters(q.filters, filters); - if (!diff.changed && !req.options?.skipDiff) { + if (filters.length === 0 && !req.options?.skipDiff) { this.notifyChange(); - return unwrap(q.feed) as Readonly; + return existing.feed as Readonly; } else { - const splitFilters = splitAllByWriteRelays(filters); + for (const subQ of filters) { + this.SendQuery( + existing, + { + id: `${existing.id}-${existing.subQueryCounter++}`, + filters: [subQ.filter], + relays: [], + }, + (q, s, c) => q.sendSubQueryToRelay(c, s) + ); + } + q.filters = filters; + this.notifyChange(); + return q.feed as Readonly; + } + } else { + const store = new type(); + + const filters = req.build(this.#relayCache); + const q = new Query(req.id, store); + if (req.options?.leaveOpen) { + q.leaveOpen = req.options.leaveOpen; + } + + this.Queries.set(rb.id, q); + const splitFilters = splitAllByWriteRelays(filters); + if (splitFilters.length > 1) { for (const sf of splitFilters) { const subQ = { id: `${q.id}-${q.subQueryCounter++}`, @@ -207,45 +236,14 @@ export class NostrSystem extends ExternalStore { } as QueryBase; this.SendQuery(q, subQ, (q, s, c) => q.sendSubQueryToRelay(c, s)); } - q.filters = filters; - this.notifyChange(); - return q.feed as Readonly; + } else { + this.SendQuery(q, q, (q, s, c) => q.sendToRelay(c)); } - } else { - return this.AddQuery(type, req); + this.notifyChange(); + return store; } } - AddQuery(type: { new (): T }, rb: RequestBuilder): T { - const store = new type(); - - const filters = rb.build(); - const q = new Query(rb.id, filters, store); - if (rb.options?.leaveOpen) { - q.leaveOpen = rb.options.leaveOpen; - } - if (rb.options?.relays && (rb.options?.relays?.length ?? 0) > 0) { - q.relays = rb.options.relays; - } - - this.Queries.set(rb.id, q); - const splitFilters = splitAllByWriteRelays(filters); - if (splitFilters.length > 1) { - for (const sf of splitFilters) { - const subQ = { - id: `${q.id}-${q.subQueryCounter++}`, - filters: sf.filters, - relays: sf.relay ? [sf.relay] : undefined, - } as QueryBase; - this.SendQuery(q, subQ, (q, s, c) => q.sendSubQueryToRelay(c, s)); - } - } else { - this.SendQuery(q, q, (q, s, c) => q.sendToRelay(c)); - } - this.notifyChange(); - return store; - } - CancelQuery(sub: string) { const q = this.Queries.get(sub); if (q) { diff --git a/packages/nostr/src/legacy/Connection.ts b/packages/nostr/src/legacy/Connection.ts index 36ed4c4d..c9eff0c9 100644 --- a/packages/nostr/src/legacy/Connection.ts +++ b/packages/nostr/src/legacy/Connection.ts @@ -211,7 +211,10 @@ export class Connection { break; } case "EVENT": { - this.OnEvent?.(msg[1], msg[2]); + this.OnEvent?.(msg[1], { + ...msg[2], + relays: [this.Address] + }); this.Stats.EventsReceived++; this.#UpdateState(); break; From baec8d69041da70afc5a35e8910bf983b6bfa684 Mon Sep 17 00:00:00 2001 From: Kieran Date: Mon, 29 May 2023 22:25:40 +0100 Subject: [PATCH 12/24] tmp --- packages/app/src/System/Query.test.ts | 25 ++-- packages/app/src/System/Query.ts | 35 ++--- .../app/src/System/RequestBuilder.test.ts | 124 +++++++++++++++--- packages/app/src/System/RequestBuilder.ts | 31 +++-- packages/app/src/System/RequestMerger.test.ts | 44 +++++++ packages/app/src/System/RequestMerger.ts | 27 +++- packages/app/src/System/RequestSplitter.ts | 16 +-- packages/app/src/System/index.ts | 37 +++--- 8 files changed, 255 insertions(+), 84 deletions(-) create mode 100644 packages/app/src/System/RequestMerger.test.ts diff --git a/packages/app/src/System/Query.test.ts b/packages/app/src/System/Query.test.ts index 3c8d6735..cc4dbb1b 100644 --- a/packages/app/src/System/Query.test.ts +++ b/packages/app/src/System/Query.test.ts @@ -9,16 +9,7 @@ window.crypto.getRandomValues = getRandomValues as any; describe("query", () => { test("progress", () => { - const q = new Query( - "test", - [ - { - kinds: [1], - authors: ["test"], - }, - ], - new FlatNoteStore() - ); + const q = new Query("test", new FlatNoteStore()); const opt = { read: true, write: true, @@ -30,7 +21,15 @@ describe("query", () => { const c3 = new Connection("wss://three.com", opt); c3.Down = false; - q.sendToRelay(c1); + q.sendToRelay(c1, { + id: "test", + filters: [ + { + kinds: [1], + authors: ["test"], + }, + ], + }); q.sendToRelay(c2); q.sendToRelay(c3); @@ -53,12 +52,12 @@ describe("query", () => { }, ], } as QueryBase; - q.sendSubQueryToRelay(c1, qs); + q.sendToRelay(c1, qs); expect(q.progress).toBe(3 / 4); q.eose(qs.id, c1); expect(q.progress).toBe(1); - q.sendSubQueryToRelay(c2, qs); + q.sendToRelay(c2, qs); expect(q.progress).toBe(4 / 5); }); }); diff --git a/packages/app/src/System/Query.ts b/packages/app/src/System/Query.ts index 8edc1bf6..b3090dd9 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/app/src/System/Query.ts @@ -3,15 +3,13 @@ import debug from "debug"; import { Connection, RawReqFilter, Nips } from "@snort/nostr"; import { unixNowMs, unwrap } from "SnortUtils"; import { NoteStore } from "./NoteCollection"; +import { mergeSimilar } from "./RequestMerger"; /** * Tracing for relay query status */ class QueryTrace { readonly id: string; - readonly subId: string; - readonly relay: string; - readonly connId: string; readonly start: number; sent?: number; eose?: number; @@ -20,11 +18,15 @@ class QueryTrace { readonly #fnClose: (id: string) => void; readonly #fnProgress: () => void; - constructor(sub: string, relay: string, connId: string, fnClose: (id: string) => void, fnProgress: () => void) { + constructor( + readonly subId: string, + readonly relay: string, + readonly filters: Array, + readonly connId: string, + fnClose: (id: string) => void, + fnProgress: () => void + ) { this.id = uuid(); - this.subId = sub; - this.relay = relay; - this.connId = connId; this.start = unixNowMs(); this.#fnClose = fnClose; this.#fnProgress = fnProgress; @@ -102,17 +104,12 @@ export interface QueryBase { /** * Active or queued query on the system */ -export class Query { +export class Query implements QueryBase { /** * Uniquie ID of this query */ id: string; - /** - * A merged set of all filters send to relays for this query - */ - filters: Array = []; - /** * Which relays this query has already been executed on */ @@ -159,6 +156,11 @@ export class Query { return this.#feed; } + get filters() { + const filters = this.#tracing.flatMap(a => a.filters); + return mergeSimilar(filters); + } + cancel() { this.#cancelTimeout = unixNowMs() + 5_000; } @@ -171,11 +173,11 @@ export class Query { this.#stopCheckTraces(); } - sendToRelay(c: Connection, subq: QueryBase) { - if (!this.#canSendQuery(c, subq)) { + sendToRelay(c: Connection, subq?: QueryBase) { + if (!this.#canSendQuery(c, subq ?? this)) { return; } - this.#sendQueryInternal(c, subq); + this.#sendQueryInternal(c, subq ?? this); } connectionLost(id: string) { @@ -252,6 +254,7 @@ export class Query { const qt = new QueryTrace( q.id, c.Address, + q.filters, c.Id, x => c.CloseReq(x), () => this.#onProgress() diff --git a/packages/app/src/System/RequestBuilder.test.ts b/packages/app/src/System/RequestBuilder.test.ts index fe1a84c4..079d36c5 100644 --- a/packages/app/src/System/RequestBuilder.test.ts +++ b/packages/app/src/System/RequestBuilder.test.ts @@ -1,29 +1,39 @@ import { RelayCache } from "./GossipModel"; -import { RequestBuilder } from "./RequestBuilder"; +import { RequestBuilder, RequestStrategy } from "./RequestBuilder"; import { describe, expect } from "@jest/globals"; -describe("RequestBuilder", () => { - const relayCache = { - get: () => { - return undefined; - }, - } as RelayCache; +const DummyCache = { + get: (pk?: string) => { + if (!pk) return undefined; + return [ + { + url: `wss://${pk}.com/`, + settings: { + read: true, + write: true, + }, + }, + ]; + }, +} as RelayCache; + +describe("RequestBuilder", () => { describe("basic", () => { test("empty filter", () => { const b = new RequestBuilder("test"); b.withFilter(); - expect(b.build(relayCache)).toEqual([{}]); + expect(b.buildRaw()).toEqual([{}]); }); test("only kind", () => { const b = new RequestBuilder("test"); b.withFilter().kinds([0]); - expect(b.build(relayCache)).toEqual([{ kinds: [0] }]); + expect(b.buildRaw()).toMatchObject([{ kinds: [0] }]); }); test("empty authors", () => { const b = new RequestBuilder("test"); b.withFilter().authors([]); - expect(b.build(relayCache)).toEqual([{ authors: [] }]); + expect(b.buildRaw()).toMatchObject([{ authors: [] }]); }); test("authors/kinds/ids", () => { const authors = ["a1", "a2"]; @@ -31,7 +41,7 @@ describe("RequestBuilder", () => { const ids = ["id1", "id2", "id3"]; const b = new RequestBuilder("test"); b.withFilter().authors(authors).kinds(kinds).ids(ids); - expect(b.build(relayCache)).toEqual([{ ids, authors, kinds }]); + expect(b.buildRaw()).toMatchObject([{ ids, authors, kinds }]); }); test("authors and kinds, duplicates removed", () => { const authors = ["a1", "a2"]; @@ -39,12 +49,12 @@ describe("RequestBuilder", () => { const ids = ["id1", "id2", "id3"]; const b = new RequestBuilder("test"); b.withFilter().ids(ids).authors(authors).kinds(kinds).ids(ids).authors(authors).kinds(kinds); - expect(b.build(relayCache)).toEqual([{ ids, authors, kinds }]); + expect(b.buildRaw()).toMatchObject([{ ids, authors, kinds }]); }); test("search", () => { const b = new RequestBuilder("test"); b.withFilter().kinds([1]).search("test-search"); - expect(b.build(relayCache)).toEqual([{ kinds: [1], search: "test-search" }]); + expect(b.buildRaw()).toMatchObject([{ kinds: [1], search: "test-search" }]); }); test("timeline", () => { const authors = ["a1", "a2"]; @@ -53,7 +63,7 @@ describe("RequestBuilder", () => { const since = 5; const b = new RequestBuilder("test"); b.withFilter().kinds(kinds).authors(authors).since(since).until(until); - expect(b.build(relayCache)).toEqual([{ kinds, authors, until, since }]); + expect(b.buildRaw()).toMatchObject([{ kinds, authors, until, since }]); }); test("multi-filter timeline", () => { const authors = ["a1", "a2"]; @@ -63,10 +73,94 @@ describe("RequestBuilder", () => { const b = new RequestBuilder("test"); b.withFilter().kinds(kinds).authors(authors).since(since).until(until); b.withFilter().kinds(kinds).authors(authors).since(since).until(until); - expect(b.build(relayCache)).toEqual([ + expect(b.buildRaw()).toMatchObject([ { kinds, authors, until, since }, { kinds, authors, until, since }, ]); }); }); + + describe("diff basic", () => { + const rb = new RequestBuilder("test"); + const f0 = rb.withFilter(); + + const a = rb.buildRaw(); + f0.authors(["a"]); + expect(a).toEqual([{}]); + + const b = rb.buildDiff(DummyCache, { + filters: a, + id: "test", + }); + expect(b).toMatchObject([ + { + filters: [{ authors: ["a"] }], + }, + ]); + }); + + describe("build gossip simply", () => { + const rb = new RequestBuilder("test"); + rb.withFilter().authors(["a", "b"]).kinds([0]); + + const a = rb.build(DummyCache); + expect(a).toEqual([ + { + strategy: RequestStrategy.AuthorsRelays, + relay: "wss://a.com/", + filters: [ + { + kinds: [0], + authors: ["a"], + }, + ], + }, + { + strategy: RequestStrategy.AuthorsRelays, + relay: "wss://b.com/", + filters: [ + { + kinds: [0], + authors: ["b"], + }, + ], + }, + ]); + }); + + describe("build gossip merged similar filters", () => { + const rb = new RequestBuilder("test"); + rb.withFilter().authors(["a", "b"]).kinds([0]); + rb.withFilter().authors(["a", "b"]).kinds([10002]); + rb.withFilter().authors(["a"]).limit(10).kinds([4]); + + const a = rb.build(DummyCache); + expect(a).toEqual([ + { + strategy: RequestStrategy.AuthorsRelays, + relay: "wss://a.com/", + filters: [ + { + kinds: [0, 10002], + authors: ["a"], + }, + { + kinds: [4], + authors: ["a"], + limit: 10, + }, + ], + }, + { + strategy: RequestStrategy.AuthorsRelays, + relay: "wss://b.com/", + filters: [ + { + kinds: [0, 10002], + authors: ["b"], + }, + ], + }, + ]); + }); }); diff --git a/packages/app/src/System/RequestBuilder.ts b/packages/app/src/System/RequestBuilder.ts index 16c7f653..74e923a1 100644 --- a/packages/app/src/System/RequestBuilder.ts +++ b/packages/app/src/System/RequestBuilder.ts @@ -2,7 +2,7 @@ import { RawReqFilter, u256, HexKey, EventKind } from "@snort/nostr"; import { appendDedupe, dedupe } from "SnortUtils"; import { QueryBase } from "./Query"; import { diffFilters } from "./RequestSplitter"; -import { RelayCache, splitByWriteRelays } from "./GossipModel"; +import { RelayCache, splitAllByWriteRelays, splitByWriteRelays } from "./GossipModel"; import { mergeSimilar } from "./RequestMerger"; /** @@ -30,7 +30,7 @@ export enum RequestStrategy { * A built REQ filter ready for sending to System */ export interface BuiltRawReqFilter { - filter: RawReqFilter; + filters: Array; relay: string; strategy: RequestStrategy; } @@ -97,6 +97,14 @@ export class RequestBuilder { const next = this.buildRaw(); const diff = diffFilters(q.filters, next); if (diff.changed) { + console.debug("DIFF", q.filters, next, diff); + return splitAllByWriteRelays(relays, diff.filters).map(a => { + return { + strategy: RequestStrategy.AuthorsRelays, + filters: a.filters, + relay: a.relay, + }; + }); } return []; } @@ -118,9 +126,9 @@ export class RequestBuilder { }, new Map>()); const filtersSquashed = [...relayMerged.values()].flatMap(a => { - return mergeSimilar(a.map(b => b.filter)).map(b => { + return mergeSimilar(a.flatMap(b => b.filters)).map(b => { return { - filter: b, + filters: [b], relay: a[0].relay, strategy: a[0].strategy, } as BuiltRawReqFilter; @@ -210,10 +218,12 @@ export class RequestFilterBuilder { const relays = dedupe([...this.#relayHints.values()].flat()); return relays.map(r => { return { - filter: { - ...this.#filter, - ids: [...this.#relayHints.entries()].filter(([, v]) => v.includes(r)).map(([k]) => k), - }, + filters: [ + { + ...this.#filter, + ids: [...this.#relayHints.entries()].filter(([, v]) => v.includes(r)).map(([k]) => k), + }, + ], relay: r, strategy: RequestStrategy.RelayHintedEventIds, }; @@ -225,7 +235,8 @@ export class RequestFilterBuilder { const split = splitByWriteRelays(relays, this.#filter); return split.map(a => { return { - ...a, + filters: [a.filter], + relay: a.relay, strategy: RequestStrategy.AuthorsRelays, }; }); @@ -233,7 +244,7 @@ export class RequestFilterBuilder { return [ { - filter: this.filter, + filters: [this.filter], relay: "*", strategy: RequestStrategy.DefaultRelays, }, diff --git a/packages/app/src/System/RequestMerger.test.ts b/packages/app/src/System/RequestMerger.test.ts new file mode 100644 index 00000000..a3b308a6 --- /dev/null +++ b/packages/app/src/System/RequestMerger.test.ts @@ -0,0 +1,44 @@ +import { RawReqFilter } from "@snort/nostr"; +import { mergeSimilar } from "./RequestMerger"; + +describe("RequestMerger", () => { + it("should simple merge authors", () => { + const a = { + authors: ["a"], + } as RawReqFilter; + const b = { + authors: ["b"], + } as RawReqFilter; + + const merged = mergeSimilar([a, b]); + expect(merged).toMatchObject([ + { + authors: ["a", "b"], + }, + ]); + }); + + it("should append non-mergable filters", () => { + const a = { + authors: ["a"], + } as RawReqFilter; + const b = { + authors: ["b"], + } as RawReqFilter; + const c = { + limit: 5, + authors: ["a"], + }; + + const merged = mergeSimilar([a, b, c]); + expect(merged).toMatchObject([ + { + authors: ["a", "b"], + }, + { + limit: 5, + authors: ["a"], + }, + ]); + }); +}); diff --git a/packages/app/src/System/RequestMerger.ts b/packages/app/src/System/RequestMerger.ts index 3e93e753..de12b992 100644 --- a/packages/app/src/System/RequestMerger.ts +++ b/packages/app/src/System/RequestMerger.ts @@ -1,5 +1,30 @@ import { RawReqFilter } from "@snort/nostr"; export function mergeSimilar(filters: Array): Array { - return filters; + const hasCriticalKeySet = (a: RawReqFilter) => { + return a.limit !== undefined || a.since !== undefined || a.until !== undefined; + }; + const canEasilyMerge = filters.filter(a => !hasCriticalKeySet(a)); + const cannotMerge = filters.filter(a => hasCriticalKeySet(a)); + return [...(canEasilyMerge.length > 0 ? [simpleMerge(canEasilyMerge)] : []), ...cannotMerge]; +} + +function simpleMerge(filters: Array) { + const result: any = {}; + + filters.forEach(filter => { + Object.entries(filter).forEach(([key, value]) => { + if (Array.isArray(value)) { + if (result[key] === undefined) { + result[key] = [...value]; + } else { + result[key] = [...new Set([...result[key], ...value])]; + } + } else { + throw new Error("Cannot simple merge with non-array filter properties"); + } + }); + }); + + return result as RawReqFilter; } diff --git a/packages/app/src/System/RequestSplitter.ts b/packages/app/src/System/RequestSplitter.ts index 7f422cee..69fb17fe 100644 --- a/packages/app/src/System/RequestSplitter.ts +++ b/packages/app/src/System/RequestSplitter.ts @@ -1,5 +1,8 @@ import { RawReqFilter } from "@snort/nostr"; +// Critical keys changing means the entire filter has changed +export const CriticalKeys = ["since", "until", "limit"]; + export function diffFilters(a: Array, b: Array) { const result: Array = []; let anyChanged = false; @@ -9,25 +12,20 @@ export function diffFilters(a: Array, b: Array) { result.push(bN); anyChanged = true; } else { - // Critical keys changing means the entire filter has changed - const criticalKeys = ["since", "until", "limit"]; let anyCriticalKeyChanged = false; for (const [k, v] of Object.entries(bN)) { if (Array.isArray(v)) { - const prevArray = prev[k] as Array; - if (!prevArray) { - throw new Error(`Tried to add new filter prop ${k} which isnt supported!`); - } + const prevArray = prev[k] as Array | undefined; const thisArray = v as Array; - const added = thisArray.filter(a => !prevArray.includes(a)); + const added = thisArray.filter(a => !prevArray?.includes(a)); // support adding new values to array, removing values is ignored since we only care about getting new values - result[i] = { ...result[i], [k]: added.length === 0 ? prevArray : added }; + result[i] = { ...result[i], [k]: added.length === 0 ? prevArray ?? thisArray : added }; if (added.length > 0) { anyChanged = true; } } else if (prev[k] !== v) { result[i] = { ...result[i], [k]: v }; - if (criticalKeys.includes(k)) { + if (CriticalKeys.includes(k)) { anyCriticalKeyChanged = anyChanged = true; break; } diff --git a/packages/app/src/System/index.ts b/packages/app/src/System/index.ts index f80b2133..239321c5 100644 --- a/packages/app/src/System/index.ts +++ b/packages/app/src/System/index.ts @@ -84,7 +84,7 @@ export class NostrSystem extends ExternalStore { c.OnDisconnect = id => this.OnRelayDisconnect(id); c.OnConnected = () => { for (const [, q] of this.Queries) { - q.sendToRelay(c); + q.sendToRelay(c, q); } }; await c.Connect(); @@ -142,7 +142,7 @@ export class NostrSystem extends ExternalStore { c.OnConnected = () => { for (const [, q] of this.Queries) { if (q.progress !== 1) { - q.sendToRelay(c); + q.sendToRelay(c, q); } } }; @@ -206,15 +206,14 @@ export class NostrSystem extends ExternalStore { existing, { id: `${existing.id}-${existing.subQueryCounter++}`, - filters: [subQ.filter], - relays: [], + filters: subQ.filters, + relays: [subQ.relay], }, - (q, s, c) => q.sendSubQueryToRelay(c, s) + (q, s, c) => q.sendToRelay(c, s) ); } - q.filters = filters; this.notifyChange(); - return q.feed as Readonly; + return existing.feed as Readonly; } } else { const store = new type(); @@ -225,22 +224,20 @@ export class NostrSystem extends ExternalStore { q.leaveOpen = req.options.leaveOpen; } - this.Queries.set(rb.id, q); - const splitFilters = splitAllByWriteRelays(filters); - if (splitFilters.length > 1) { - for (const sf of splitFilters) { - const subQ = { + this.Queries.set(req.id, q); + for (const subQ of filters) { + this.SendQuery( + q, + { id: `${q.id}-${q.subQueryCounter++}`, - filters: sf.filters, - relays: sf.relay ? [sf.relay] : undefined, - } as QueryBase; - this.SendQuery(q, subQ, (q, s, c) => q.sendSubQueryToRelay(c, s)); - } - } else { - this.SendQuery(q, q, (q, s, c) => q.sendToRelay(c)); + filters: subQ.filters, + relays: [subQ.relay], + }, + (q, s, c) => q.sendToRelay(c, s) + ); } this.notifyChange(); - return store; + return q.feed as Readonly; } } From f86053c14fde190e72ca39a0318dfd7e35fbc268 Mon Sep 17 00:00:00 2001 From: Kieran Date: Tue, 30 May 2023 14:48:38 +0100 Subject: [PATCH 13/24] move @snort/nostr code into System --- packages/app/package.json | 1 - packages/app/src/Cache/DMCache.ts | 2 +- packages/app/src/Cache/index.ts | 2 +- packages/app/src/Const.ts | 4 +- packages/app/src/Db/index.ts | 2 +- packages/app/src/Element/Avatar.tsx | 2 +- packages/app/src/Element/BadgeList.tsx | 2 +- packages/app/src/Element/BlockButton.tsx | 2 +- packages/app/src/Element/Bookmarks.tsx | 2 +- packages/app/src/Element/DM.tsx | 2 +- packages/app/src/Element/DmWindow.tsx | 2 +- packages/app/src/Element/FollowButton.tsx | 2 +- packages/app/src/Element/FollowListBase.tsx | 2 +- packages/app/src/Element/Mention.tsx | 2 +- packages/app/src/Element/MuteButton.tsx | 2 +- packages/app/src/Element/MutedList.tsx | 2 +- packages/app/src/Element/Nip05.tsx | 2 +- packages/app/src/Element/Nip5Service.tsx | 2 +- packages/app/src/Element/NostrFileHeader.tsx | 2 +- packages/app/src/Element/NostrLink.tsx | 2 +- packages/app/src/Element/Note.tsx | 2 +- packages/app/src/Element/NoteCreator.tsx | 2 +- packages/app/src/Element/NoteFooter.tsx | 2 +- packages/app/src/Element/NoteReaction.tsx | 2 +- packages/app/src/Element/Poll.tsx | 2 +- packages/app/src/Element/ProfileImage.tsx | 2 +- packages/app/src/Element/ProfilePreview.tsx | 2 +- packages/app/src/Element/PubkeyList.tsx | 2 +- packages/app/src/Element/Reactions.tsx | 2 +- packages/app/src/Element/Relay.tsx | 4 +- packages/app/src/Element/RelaysMetadata.tsx | 2 +- packages/app/src/Element/SendSats.tsx | 5 +- packages/app/src/Element/SubDebug.tsx | 4 +- .../app/src/Element/SuggestedProfiles.tsx | 2 +- packages/app/src/Element/Text.tsx | 2 +- packages/app/src/Element/Textarea.tsx | 2 +- packages/app/src/Element/Thread.tsx | 2 +- packages/app/src/Element/Timeline.tsx | 2 +- packages/app/src/Element/TrendingPosts.tsx | 2 +- packages/app/src/Element/TrendingUsers.tsx | 2 +- packages/app/src/Element/Username.tsx | 2 +- packages/app/src/Element/WriteDm.tsx | 2 +- packages/app/src/Element/Zap.tsx | 2 +- packages/app/src/Element/ZapButton.tsx | 2 +- packages/app/src/Element/ZapstrEmbed.tsx | 2 +- packages/app/src/External/NostrBand.ts | 2 +- packages/app/src/Feed/BadgesFeed.ts | 2 +- packages/app/src/Feed/BookmarkFeed.tsx | 2 +- packages/app/src/Feed/EventFeed.ts | 2 +- packages/app/src/Feed/EventPublisher.ts | 3 +- packages/app/src/Feed/FollowersFeed.ts | 2 +- packages/app/src/Feed/FollowsFeed.ts | 2 +- packages/app/src/Feed/LoginFeed.ts | 2 +- packages/app/src/Feed/MuteList.ts | 2 +- packages/app/src/Feed/PinnedFeed.tsx | 2 +- packages/app/src/Feed/RelayState.ts | 4 +- packages/app/src/Feed/RelaysFeed.tsx | 2 +- packages/app/src/Feed/RelaysFeedFollows.tsx | 2 +- packages/app/src/Feed/ThreadFeed.ts | 2 +- packages/app/src/Feed/TimelineFeed.ts | 2 +- packages/app/src/Feed/ZapsFeed.ts | 2 +- .../app/src/Hooks/useInteractionCache.tsx | 2 +- packages/app/src/Hooks/useModeration.tsx | 2 +- .../app/src/Hooks/useNotelistSubscription.ts | 2 +- packages/app/src/Hooks/useRequestBuilder.tsx | 3 +- packages/app/src/Hooks/useSystemState.tsx | 3 +- packages/app/src/Hooks/useUserProfile.ts | 4 +- packages/app/src/LNURL.ts | 2 +- packages/app/src/Login/Functions.ts | 5 +- packages/app/src/Login/LoginSession.ts | 2 +- packages/app/src/Login/MultiAccountStore.ts | 2 +- .../app/src/Nip05/SnortServiceProvider.ts | 2 +- packages/app/src/Notifications.ts | 3 +- packages/app/src/Pages/DonatePage.tsx | 2 +- packages/app/src/Pages/Layout.tsx | 2 +- packages/app/src/Pages/LoginPage.tsx | 2 +- packages/app/src/Pages/MessagesPage.tsx | 2 +- packages/app/src/Pages/NostrLinkHandler.tsx | 4 +- packages/app/src/Pages/ProfilePage.tsx | 2 +- packages/app/src/Pages/Root.tsx | 2 +- packages/app/src/Pages/SearchPage.tsx | 3 +- packages/app/src/Pages/ZapPool.tsx | 2 +- packages/app/src/Pages/settings/Keys.tsx | 2 +- packages/app/src/Pages/settings/RelayInfo.tsx | 2 +- packages/app/src/Pages/settings/Relays.tsx | 2 +- packages/app/src/SnortApi.ts | 2 +- packages/app/src/SnortUtils/Utils.test.ts | 2 +- packages/app/src/SnortUtils/index.ts | 2 +- packages/app/src/State/NoteCreator.ts | 2 +- packages/app/src/State/ReBroadcast.ts | 2 +- packages/app/src/System/Connection.ts | 448 ++++++++++++++++++ packages/app/src/System/ConnectionStats.ts | 34 ++ packages/app/src/System/Const.ts | 4 + packages/app/src/System/EventBuilder.ts | 2 +- packages/app/src/System/EventExt.ts | 2 +- packages/app/src/System/EventKind.ts | 29 ++ packages/app/src/System/EventPublisher.ts | 34 +- packages/app/src/System/GossipModel.ts | 2 +- packages/app/src/System/Links.ts | 88 ++++ packages/app/src/System/Nips.ts | 3 + packages/app/src/System/Nostr.ts | 84 ++++ .../app/src/System/NoteCollection.test.ts | 2 +- packages/app/src/System/NoteCollection.ts | 2 +- packages/app/src/System/ProfileCache.ts | 15 +- packages/app/src/System/Query.test.ts | 2 +- packages/app/src/System/Query.ts | 2 +- packages/app/src/System/RelayInfo.ts | 16 + packages/app/src/System/RequestBuilder.ts | 4 +- packages/app/src/System/RequestMerger.test.ts | 16 +- packages/app/src/System/RequestMerger.ts | 30 +- .../app/src/System/RequestSplitter.test.ts | 2 +- packages/app/src/System/RequestSplitter.ts | 4 +- packages/app/src/System/Tag.ts | 88 ++++ packages/app/src/System/Util.ts | 34 ++ packages/app/src/System/index.ts | 63 ++- packages/app/src/Upload/VoidCat.ts | 2 +- packages/app/src/Upload/index.ts | 2 +- packages/app/src/Wallet/NostrWalletConnect.ts | 2 +- packages/app/src/index.tsx | 16 +- 119 files changed, 1071 insertions(+), 163 deletions(-) create mode 100644 packages/app/src/System/Connection.ts create mode 100644 packages/app/src/System/ConnectionStats.ts create mode 100644 packages/app/src/System/Const.ts create mode 100644 packages/app/src/System/EventKind.ts create mode 100644 packages/app/src/System/Links.ts create mode 100644 packages/app/src/System/Nips.ts create mode 100644 packages/app/src/System/Nostr.ts create mode 100644 packages/app/src/System/RelayInfo.ts create mode 100644 packages/app/src/System/Tag.ts create mode 100644 packages/app/src/System/Util.ts diff --git a/packages/app/package.json b/packages/app/package.json index 1d68eff0..d14e6a8d 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -12,7 +12,6 @@ "@reduxjs/toolkit": "^1.9.1", "@scure/bip32": "^1.3.0", "@scure/bip39": "^1.1.1", - "@snort/nostr": "^1.0.0", "@szhsin/react-menu": "^3.3.1", "@void-cat/api": "^1.0.4", "base32-decode": "^1.0.0", diff --git a/packages/app/src/Cache/DMCache.ts b/packages/app/src/Cache/DMCache.ts index a147559f..cacfd379 100644 --- a/packages/app/src/Cache/DMCache.ts +++ b/packages/app/src/Cache/DMCache.ts @@ -1,4 +1,4 @@ -import { RawEvent } from "@snort/nostr"; +import { RawEvent } from "System"; import { db } from "Db"; import FeedCache from "./FeedCache"; diff --git a/packages/app/src/Cache/index.ts b/packages/app/src/Cache/index.ts index f5f13a68..02941f3d 100644 --- a/packages/app/src/Cache/index.ts +++ b/packages/app/src/Cache/index.ts @@ -1,4 +1,4 @@ -import { HexKey, RawEvent, UserMetadata } from "@snort/nostr"; +import { HexKey, RawEvent, UserMetadata } from "System"; import { hexToBech32, unixNowMs } from "SnortUtils"; import { DmCache } from "./DMCache"; import { InteractionCache } from "./EventInteractionCache"; diff --git a/packages/app/src/Const.ts b/packages/app/src/Const.ts index 5c287e5b..e16d2262 100644 --- a/packages/app/src/Const.ts +++ b/packages/app/src/Const.ts @@ -1,4 +1,6 @@ -import { RelaySettings } from "@snort/nostr"; +import { UserRelays } from "Cache/UserRelayCache"; +import { NostrSystem, RelaySettings } from "System"; +import { ProfileLoaderService } from "System/ProfileCache"; /** * Add-on api for snort features diff --git a/packages/app/src/Db/index.ts b/packages/app/src/Db/index.ts index df0fe111..25f0e788 100644 --- a/packages/app/src/Db/index.ts +++ b/packages/app/src/Db/index.ts @@ -1,5 +1,5 @@ import Dexie, { Table } from "dexie"; -import { FullRelaySettings, HexKey, RawEvent, u256 } from "@snort/nostr"; +import { FullRelaySettings, HexKey, RawEvent, u256 } from "System"; import { MetadataCache } from "Cache"; export const NAME = "snortDB"; diff --git a/packages/app/src/Element/Avatar.tsx b/packages/app/src/Element/Avatar.tsx index 04a0fa0d..576ef169 100644 --- a/packages/app/src/Element/Avatar.tsx +++ b/packages/app/src/Element/Avatar.tsx @@ -2,7 +2,7 @@ import "./Avatar.css"; import Nostrich from "nostrich.webp"; import { CSSProperties, useEffect, useState } from "react"; -import type { UserMetadata } from "@snort/nostr"; +import type { UserMetadata } from "System"; import useImgProxy from "Hooks/useImgProxy"; diff --git a/packages/app/src/Element/BadgeList.tsx b/packages/app/src/Element/BadgeList.tsx index 83b494f1..e007b685 100644 --- a/packages/app/src/Element/BadgeList.tsx +++ b/packages/app/src/Element/BadgeList.tsx @@ -3,7 +3,7 @@ import "./BadgeList.css"; import { useState } from "react"; import { FormattedMessage } from "react-intl"; -import { TaggedRawEvent } from "@snort/nostr"; +import { TaggedRawEvent } from "System"; import { ProxyImg } from "Element/ProxyImg"; import Icon from "Icons/Icon"; diff --git a/packages/app/src/Element/BlockButton.tsx b/packages/app/src/Element/BlockButton.tsx index e908192d..10c4859f 100644 --- a/packages/app/src/Element/BlockButton.tsx +++ b/packages/app/src/Element/BlockButton.tsx @@ -1,5 +1,5 @@ import { FormattedMessage } from "react-intl"; -import { HexKey } from "@snort/nostr"; +import { HexKey } from "System"; import useModeration from "Hooks/useModeration"; import messages from "./messages"; diff --git a/packages/app/src/Element/Bookmarks.tsx b/packages/app/src/Element/Bookmarks.tsx index dda75eec..04680f64 100644 --- a/packages/app/src/Element/Bookmarks.tsx +++ b/packages/app/src/Element/Bookmarks.tsx @@ -1,6 +1,6 @@ import { useState, useMemo, ChangeEvent } from "react"; import { FormattedMessage } from "react-intl"; -import { HexKey, TaggedRawEvent } from "@snort/nostr"; +import { HexKey, TaggedRawEvent } from "System"; import Note from "Element/Note"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Element/DM.tsx b/packages/app/src/Element/DM.tsx index b893075d..de058fee 100644 --- a/packages/app/src/Element/DM.tsx +++ b/packages/app/src/Element/DM.tsx @@ -2,7 +2,7 @@ import "./DM.css"; import { useEffect, useState } from "react"; import { useIntl } from "react-intl"; import { useInView } from "react-intersection-observer"; -import { TaggedRawEvent } from "@snort/nostr"; +import { TaggedRawEvent } from "System"; import useEventPublisher from "Feed/EventPublisher"; import NoteTime from "Element/NoteTime"; diff --git a/packages/app/src/Element/DmWindow.tsx b/packages/app/src/Element/DmWindow.tsx index 2347e571..827984b7 100644 --- a/packages/app/src/Element/DmWindow.tsx +++ b/packages/app/src/Element/DmWindow.tsx @@ -1,6 +1,6 @@ import "./DmWindow.css"; import { useEffect, useMemo, useRef } from "react"; -import { TaggedRawEvent } from "@snort/nostr"; +import { TaggedRawEvent } from "System"; import ProfileImage from "Element/ProfileImage"; import DM from "Element/DM"; diff --git a/packages/app/src/Element/FollowButton.tsx b/packages/app/src/Element/FollowButton.tsx index eb20789c..e284bc83 100644 --- a/packages/app/src/Element/FollowButton.tsx +++ b/packages/app/src/Element/FollowButton.tsx @@ -1,6 +1,6 @@ import "./FollowButton.css"; import { FormattedMessage } from "react-intl"; -import { HexKey } from "@snort/nostr"; +import { HexKey } from "System"; import useEventPublisher from "Feed/EventPublisher"; import { parseId } from "SnortUtils"; diff --git a/packages/app/src/Element/FollowListBase.tsx b/packages/app/src/Element/FollowListBase.tsx index 122f6c4d..e98ef131 100644 --- a/packages/app/src/Element/FollowListBase.tsx +++ b/packages/app/src/Element/FollowListBase.tsx @@ -1,6 +1,6 @@ import { ReactNode } from "react"; import { FormattedMessage } from "react-intl"; -import { HexKey } from "@snort/nostr"; +import { HexKey } from "System"; import useEventPublisher from "Feed/EventPublisher"; import ProfilePreview from "Element/ProfilePreview"; diff --git a/packages/app/src/Element/Mention.tsx b/packages/app/src/Element/Mention.tsx index 63783f01..b7a8af60 100644 --- a/packages/app/src/Element/Mention.tsx +++ b/packages/app/src/Element/Mention.tsx @@ -1,6 +1,6 @@ import { useMemo } from "react"; import { Link } from "react-router-dom"; -import { HexKey } from "@snort/nostr"; +import { HexKey } from "System"; import { useUserProfile } from "Hooks/useUserProfile"; import { profileLink } from "SnortUtils"; diff --git a/packages/app/src/Element/MuteButton.tsx b/packages/app/src/Element/MuteButton.tsx index 7a8d05eb..7f8d492f 100644 --- a/packages/app/src/Element/MuteButton.tsx +++ b/packages/app/src/Element/MuteButton.tsx @@ -1,5 +1,5 @@ import { FormattedMessage } from "react-intl"; -import { HexKey } from "@snort/nostr"; +import { HexKey } from "System"; import useModeration from "Hooks/useModeration"; import messages from "./messages"; diff --git a/packages/app/src/Element/MutedList.tsx b/packages/app/src/Element/MutedList.tsx index 4616182a..6fa9044c 100644 --- a/packages/app/src/Element/MutedList.tsx +++ b/packages/app/src/Element/MutedList.tsx @@ -1,5 +1,5 @@ import { FormattedMessage } from "react-intl"; -import { HexKey } from "@snort/nostr"; +import { HexKey } from "System"; import MuteButton from "Element/MuteButton"; import ProfilePreview from "Element/ProfilePreview"; import useModeration from "Hooks/useModeration"; diff --git a/packages/app/src/Element/Nip05.tsx b/packages/app/src/Element/Nip05.tsx index 4d1fb286..e3e14170 100644 --- a/packages/app/src/Element/Nip05.tsx +++ b/packages/app/src/Element/Nip05.tsx @@ -1,5 +1,5 @@ import "./Nip05.css"; -import { HexKey } from "@snort/nostr"; +import { HexKey } from "System"; import Icon from "Icons/Icon"; import { useUserProfile } from "Hooks/useUserProfile"; diff --git a/packages/app/src/Element/Nip5Service.tsx b/packages/app/src/Element/Nip5Service.tsx index 7bc93d3e..122c249b 100644 --- a/packages/app/src/Element/Nip5Service.tsx +++ b/packages/app/src/Element/Nip5Service.tsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState, ChangeEvent } from "react"; import { useIntl, FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; -import { UserMetadata } from "@snort/nostr"; +import { UserMetadata } from "System"; import { unwrap } from "SnortUtils"; import { formatShort } from "Number"; diff --git a/packages/app/src/Element/NostrFileHeader.tsx b/packages/app/src/Element/NostrFileHeader.tsx index 277d71da..49531b0c 100644 --- a/packages/app/src/Element/NostrFileHeader.tsx +++ b/packages/app/src/Element/NostrFileHeader.tsx @@ -1,5 +1,5 @@ import { FormattedMessage } from "react-intl"; -import { RawEvent } from "@snort/nostr"; +import { RawEvent } from "System"; import { findTag, NostrLink } from "SnortUtils"; import useEventFeed from "Feed/EventFeed"; diff --git a/packages/app/src/Element/NostrLink.tsx b/packages/app/src/Element/NostrLink.tsx index d87abcec..64efbde8 100644 --- a/packages/app/src/Element/NostrLink.tsx +++ b/packages/app/src/Element/NostrLink.tsx @@ -1,5 +1,5 @@ import { Link } from "react-router-dom"; -import { NostrPrefix } from "@snort/nostr"; +import { NostrPrefix } from "System"; import Mention from "Element/Mention"; import { parseNostrLink } from "SnortUtils"; diff --git a/packages/app/src/Element/Note.tsx b/packages/app/src/Element/Note.tsx index 03f4f9d0..5a582f6a 100644 --- a/packages/app/src/Element/Note.tsx +++ b/packages/app/src/Element/Note.tsx @@ -3,7 +3,7 @@ import React, { useMemo, useState, useLayoutEffect, ReactNode } from "react"; import { useNavigate, Link } from "react-router-dom"; import { useInView } from "react-intersection-observer"; import { useIntl, FormattedMessage } from "react-intl"; -import { TaggedRawEvent, HexKey, EventKind, NostrPrefix, Lists } from "@snort/nostr"; +import { TaggedRawEvent, HexKey, EventKind, NostrPrefix, Lists } from "System"; import useEventPublisher from "Feed/EventPublisher"; import Icon from "Icons/Icon"; diff --git a/packages/app/src/Element/NoteCreator.tsx b/packages/app/src/Element/NoteCreator.tsx index df08f85f..6a4a2a4a 100644 --- a/packages/app/src/Element/NoteCreator.tsx +++ b/packages/app/src/Element/NoteCreator.tsx @@ -1,7 +1,7 @@ import "./NoteCreator.css"; import { FormattedMessage, useIntl } from "react-intl"; import { useDispatch, useSelector } from "react-redux"; -import { encodeTLV, EventKind, NostrPrefix, TaggedRawEvent } from "@snort/nostr"; +import { encodeTLV, EventKind, NostrPrefix, TaggedRawEvent } from "System"; import Icon from "Icons/Icon"; import useEventPublisher from "Feed/EventPublisher"; diff --git a/packages/app/src/Element/NoteFooter.tsx b/packages/app/src/Element/NoteFooter.tsx index 6347f37d..25fef378 100644 --- a/packages/app/src/Element/NoteFooter.tsx +++ b/packages/app/src/Element/NoteFooter.tsx @@ -3,7 +3,7 @@ import { useSelector, useDispatch } from "react-redux"; import { useIntl, FormattedMessage } from "react-intl"; import { Menu, MenuItem } from "@szhsin/react-menu"; import { useLongPress } from "use-long-press"; -import { TaggedRawEvent, HexKey, u256, encodeTLV, NostrPrefix, Lists } from "@snort/nostr"; +import { TaggedRawEvent, HexKey, u256, encodeTLV, NostrPrefix, Lists } from "System"; import Icon from "Icons/Icon"; import Spinner from "Icons/Spinner"; diff --git a/packages/app/src/Element/NoteReaction.tsx b/packages/app/src/Element/NoteReaction.tsx index aee50f87..236b3772 100644 --- a/packages/app/src/Element/NoteReaction.tsx +++ b/packages/app/src/Element/NoteReaction.tsx @@ -1,7 +1,7 @@ import "./NoteReaction.css"; import { Link } from "react-router-dom"; import { useMemo } from "react"; -import { EventKind, RawEvent, TaggedRawEvent, NostrPrefix } from "@snort/nostr"; +import { EventKind, RawEvent, TaggedRawEvent, NostrPrefix } from "System"; import Note from "Element/Note"; import ProfileImage from "Element/ProfileImage"; diff --git a/packages/app/src/Element/Poll.tsx b/packages/app/src/Element/Poll.tsx index cb44e29b..bb29730e 100644 --- a/packages/app/src/Element/Poll.tsx +++ b/packages/app/src/Element/Poll.tsx @@ -1,4 +1,4 @@ -import { TaggedRawEvent } from "@snort/nostr"; +import { TaggedRawEvent } from "System"; import { useState } from "react"; import { FormattedMessage, FormattedNumber, useIntl } from "react-intl"; diff --git a/packages/app/src/Element/ProfileImage.tsx b/packages/app/src/Element/ProfileImage.tsx index 184e5e0a..96c5e15a 100644 --- a/packages/app/src/Element/ProfileImage.tsx +++ b/packages/app/src/Element/ProfileImage.tsx @@ -1,7 +1,7 @@ import "./ProfileImage.css"; import React, { useMemo } from "react"; -import { HexKey, NostrPrefix } from "@snort/nostr"; +import { HexKey, NostrPrefix } from "System"; import { useUserProfile } from "Hooks/useUserProfile"; import { hexToBech32, profileLink } from "SnortUtils"; diff --git a/packages/app/src/Element/ProfilePreview.tsx b/packages/app/src/Element/ProfilePreview.tsx index 0d927bb2..15f02c05 100644 --- a/packages/app/src/Element/ProfilePreview.tsx +++ b/packages/app/src/Element/ProfilePreview.tsx @@ -4,7 +4,7 @@ import { ReactNode } from "react"; import ProfileImage from "Element/ProfileImage"; import FollowButton from "Element/FollowButton"; import { useUserProfile } from "Hooks/useUserProfile"; -import { HexKey } from "@snort/nostr"; +import { HexKey } from "System"; import { useInView } from "react-intersection-observer"; export interface ProfilePreviewProps { diff --git a/packages/app/src/Element/PubkeyList.tsx b/packages/app/src/Element/PubkeyList.tsx index 5ef6fa39..17a3b161 100644 --- a/packages/app/src/Element/PubkeyList.tsx +++ b/packages/app/src/Element/PubkeyList.tsx @@ -1,4 +1,4 @@ -import { RawEvent } from "@snort/nostr"; +import { RawEvent } from "System"; import { dedupe } from "SnortUtils"; import FollowListBase from "./FollowListBase"; diff --git a/packages/app/src/Element/Reactions.tsx b/packages/app/src/Element/Reactions.tsx index ab8340f7..2dcfbe30 100644 --- a/packages/app/src/Element/Reactions.tsx +++ b/packages/app/src/Element/Reactions.tsx @@ -2,7 +2,7 @@ import "./Reactions.css"; import { useState, useMemo, useEffect } from "react"; import { useIntl, FormattedMessage } from "react-intl"; -import { TaggedRawEvent } from "@snort/nostr"; +import { TaggedRawEvent } from "System"; import { formatShort } from "Number"; import Icon from "Icons/Icon"; diff --git a/packages/app/src/Element/Relay.tsx b/packages/app/src/Element/Relay.tsx index fcc2b912..e4779802 100644 --- a/packages/app/src/Element/Relay.tsx +++ b/packages/app/src/Element/Relay.tsx @@ -2,10 +2,10 @@ import "./Relay.css"; import { useMemo } from "react"; import { FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; -import { RelaySettings } from "@snort/nostr"; +import { RelaySettings } from "System"; import useRelayState from "Feed/RelayState"; -import { System } from "System"; +import { System } from "index"; import { getRelayName, unixNowMs, unwrap } from "SnortUtils"; import useLogin from "Hooks/useLogin"; import { setRelays } from "Login"; diff --git a/packages/app/src/Element/RelaysMetadata.tsx b/packages/app/src/Element/RelaysMetadata.tsx index 4152c677..8973a428 100644 --- a/packages/app/src/Element/RelaysMetadata.tsx +++ b/packages/app/src/Element/RelaysMetadata.tsx @@ -2,7 +2,7 @@ import "./RelaysMetadata.css"; import Nostrich from "nostrich.webp"; import { useState } from "react"; -import { FullRelaySettings } from "@snort/nostr"; +import { FullRelaySettings } from "System"; import Icon from "Icons/Icon"; const RelayFavicon = ({ url }: { url: string }) => { diff --git a/packages/app/src/Element/SendSats.tsx b/packages/app/src/Element/SendSats.tsx index cbb3dde2..93d8e240 100644 --- a/packages/app/src/Element/SendSats.tsx +++ b/packages/app/src/Element/SendSats.tsx @@ -1,8 +1,9 @@ import "./SendSats.css"; import React, { useEffect, useMemo, useState } from "react"; import { useIntl, FormattedMessage } from "react-intl"; -import { HexKey, RawEvent } from "@snort/nostr"; +import { HexKey, RawEvent } from "System"; +import { System } from "index"; import { formatShort } from "Number"; import Icon from "Icons/Icon"; import useEventPublisher from "Feed/EventPublisher"; @@ -133,7 +134,7 @@ export default function SendSats(props: SendSatsProps) { const randomKey = generateRandomKey(); console.debug("Generated new key for zap: ", randomKey); - const publisher = new EventPublisher(randomKey.publicKey, randomKey.privateKey); + const publisher = new EventPublisher(System, randomKey.publicKey, randomKey.privateKey); zap = await publisher.zap(amount * 1000, author, relays, note, comment, eb => eb.tag(["anon", ""])); } else { zap = await publisher.zap(amount * 1000, author, relays, note, comment); diff --git a/packages/app/src/Element/SubDebug.tsx b/packages/app/src/Element/SubDebug.tsx index db948ca6..66982fba 100644 --- a/packages/app/src/Element/SubDebug.tsx +++ b/packages/app/src/Element/SubDebug.tsx @@ -3,11 +3,11 @@ import { useState } from "react"; import useRelayState from "Feed/RelayState"; import Tabs, { Tab } from "Element/Tabs"; -import { System } from "System"; import { unwrap } from "SnortUtils"; import useSystemState from "Hooks/useSystemState"; -import { RawReqFilter } from "@snort/nostr"; +import { RawReqFilter } from "System"; import { useCopy } from "useCopy"; +import { System } from "index"; function RelayInfo({ id }: { id: string }) { const state = useRelayState(id); diff --git a/packages/app/src/Element/SuggestedProfiles.tsx b/packages/app/src/Element/SuggestedProfiles.tsx index 5449260d..5cb06808 100644 --- a/packages/app/src/Element/SuggestedProfiles.tsx +++ b/packages/app/src/Element/SuggestedProfiles.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { HexKey, NostrPrefix } from "@snort/nostr"; +import { HexKey, NostrPrefix } from "System"; import { FormattedMessage } from "react-intl"; import FollowListBase from "Element/FollowListBase"; diff --git a/packages/app/src/Element/Text.tsx b/packages/app/src/Element/Text.tsx index 22ae3cf7..89ede775 100644 --- a/packages/app/src/Element/Text.tsx +++ b/packages/app/src/Element/Text.tsx @@ -1,7 +1,7 @@ import "./Text.css"; import { useMemo } from "react"; import { Link, useLocation } from "react-router-dom"; -import { HexKey, NostrPrefix } from "@snort/nostr"; +import { HexKey, NostrPrefix } from "System"; import { MentionRegex, InvoiceRegex, HashtagRegex, CashuRegex } from "Const"; import { eventLink, hexToBech32, splitByUrl, validateNostrLink } from "SnortUtils"; diff --git a/packages/app/src/Element/Textarea.tsx b/packages/app/src/Element/Textarea.tsx index ed22224a..c92c497e 100644 --- a/packages/app/src/Element/Textarea.tsx +++ b/packages/app/src/Element/Textarea.tsx @@ -4,7 +4,7 @@ import "./Textarea.css"; import { useIntl } from "react-intl"; import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete"; import TextareaAutosize from "react-textarea-autosize"; -import { NostrPrefix } from "@snort/nostr"; +import { NostrPrefix } from "System"; import Avatar from "Element/Avatar"; import Nip05 from "Element/Nip05"; diff --git a/packages/app/src/Element/Thread.tsx b/packages/app/src/Element/Thread.tsx index 8013d027..2d252382 100644 --- a/packages/app/src/Element/Thread.tsx +++ b/packages/app/src/Element/Thread.tsx @@ -2,7 +2,7 @@ import "./Thread.css"; import { useMemo, useState, ReactNode } from "react"; import { useIntl } from "react-intl"; import { useNavigate, useLocation, Link, useParams } from "react-router-dom"; -import { TaggedRawEvent, u256, EventKind, NostrPrefix } from "@snort/nostr"; +import { TaggedRawEvent, u256, EventKind, NostrPrefix } from "System"; import { EventExt, Thread as ThreadInfo } from "System/EventExt"; import { eventLink, unwrap, getReactions, parseNostrLink, getAllReactions, findTag } from "SnortUtils"; diff --git a/packages/app/src/Element/Timeline.tsx b/packages/app/src/Element/Timeline.tsx index af28e335..1159b1b9 100644 --- a/packages/app/src/Element/Timeline.tsx +++ b/packages/app/src/Element/Timeline.tsx @@ -2,7 +2,7 @@ import "./Timeline.css"; import { FormattedMessage } from "react-intl"; import { useCallback, useMemo } from "react"; import { useInView } from "react-intersection-observer"; -import { TaggedRawEvent, EventKind, u256 } from "@snort/nostr"; +import { TaggedRawEvent, EventKind, u256 } from "System"; import Icon from "Icons/Icon"; import { dedupeByPubkey, findTag, tagFilterOfTextRepost } from "SnortUtils"; diff --git a/packages/app/src/Element/TrendingPosts.tsx b/packages/app/src/Element/TrendingPosts.tsx index b2434498..98ceee3d 100644 --- a/packages/app/src/Element/TrendingPosts.tsx +++ b/packages/app/src/Element/TrendingPosts.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { RawEvent, TaggedRawEvent } from "@snort/nostr"; +import { RawEvent, TaggedRawEvent } from "System"; import { FormattedMessage } from "react-intl"; import PageSpinner from "Element/PageSpinner"; diff --git a/packages/app/src/Element/TrendingUsers.tsx b/packages/app/src/Element/TrendingUsers.tsx index 477cca1f..9460b8ab 100644 --- a/packages/app/src/Element/TrendingUsers.tsx +++ b/packages/app/src/Element/TrendingUsers.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { HexKey } from "@snort/nostr"; +import { HexKey } from "System"; import { FormattedMessage } from "react-intl"; import FollowListBase from "Element/FollowListBase"; diff --git a/packages/app/src/Element/Username.tsx b/packages/app/src/Element/Username.tsx index 76b5078f..47a12ca7 100644 --- a/packages/app/src/Element/Username.tsx +++ b/packages/app/src/Element/Username.tsx @@ -1,7 +1,7 @@ import { MouseEvent } from "react"; import { useNavigate, Link } from "react-router-dom"; -import { HexKey } from "@snort/nostr"; +import { HexKey } from "System"; import { useUserProfile } from "Hooks/useUserProfile"; import { profileLink } from "SnortUtils"; diff --git a/packages/app/src/Element/WriteDm.tsx b/packages/app/src/Element/WriteDm.tsx index 37228edb..4a594f2e 100644 --- a/packages/app/src/Element/WriteDm.tsx +++ b/packages/app/src/Element/WriteDm.tsx @@ -1,4 +1,4 @@ -import { encodeTLV, NostrPrefix, RawEvent } from "@snort/nostr"; +import { encodeTLV, NostrPrefix, RawEvent } from "System"; import useEventPublisher from "Feed/EventPublisher"; import Icon from "Icons/Icon"; import Spinner from "Icons/Spinner"; diff --git a/packages/app/src/Element/Zap.tsx b/packages/app/src/Element/Zap.tsx index 4916ccaf..6a84212b 100644 --- a/packages/app/src/Element/Zap.tsx +++ b/packages/app/src/Element/Zap.tsx @@ -1,7 +1,7 @@ import "./Zap.css"; import { useMemo } from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import { HexKey, TaggedRawEvent } from "@snort/nostr"; +import { HexKey, TaggedRawEvent } from "System"; import { decodeInvoice, InvoiceDetails, sha256, unwrap } from "SnortUtils"; import { formatShort } from "Number"; diff --git a/packages/app/src/Element/ZapButton.tsx b/packages/app/src/Element/ZapButton.tsx index 4d7cdd1a..ef460d7d 100644 --- a/packages/app/src/Element/ZapButton.tsx +++ b/packages/app/src/Element/ZapButton.tsx @@ -1,6 +1,6 @@ import "./ZapButton.css"; import { useState } from "react"; -import { HexKey } from "@snort/nostr"; +import { HexKey } from "System"; import { useUserProfile } from "Hooks/useUserProfile"; import SendSats from "Element/SendSats"; diff --git a/packages/app/src/Element/ZapstrEmbed.tsx b/packages/app/src/Element/ZapstrEmbed.tsx index b8cb0e0c..84068e39 100644 --- a/packages/app/src/Element/ZapstrEmbed.tsx +++ b/packages/app/src/Element/ZapstrEmbed.tsx @@ -1,6 +1,6 @@ import "./ZapstrEmbed.css"; import { Link } from "react-router-dom"; -import { encodeTLV, NostrPrefix, RawEvent } from "@snort/nostr"; +import { encodeTLV, NostrPrefix, RawEvent } from "System"; import { ProxyImg } from "Element/ProxyImg"; import ProfileImage from "Element/ProfileImage"; diff --git a/packages/app/src/External/NostrBand.ts b/packages/app/src/External/NostrBand.ts index 930edeb0..eeb00c9a 100644 --- a/packages/app/src/External/NostrBand.ts +++ b/packages/app/src/External/NostrBand.ts @@ -1,4 +1,4 @@ -import { RawEvent } from "@snort/nostr"; +import { RawEvent } from "System"; export interface TrendingUser { pubkey: string; diff --git a/packages/app/src/Feed/BadgesFeed.ts b/packages/app/src/Feed/BadgesFeed.ts index 194150ac..baf0bacf 100644 --- a/packages/app/src/Feed/BadgesFeed.ts +++ b/packages/app/src/Feed/BadgesFeed.ts @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { EventKind, HexKey, Lists } from "@snort/nostr"; +import { EventKind, HexKey, Lists } from "System"; import { unwrap, findTag, chunks } from "SnortUtils"; import { RequestBuilder } from "System"; diff --git a/packages/app/src/Feed/BookmarkFeed.tsx b/packages/app/src/Feed/BookmarkFeed.tsx index 3412a75a..647cd5b0 100644 --- a/packages/app/src/Feed/BookmarkFeed.tsx +++ b/packages/app/src/Feed/BookmarkFeed.tsx @@ -1,4 +1,4 @@ -import { HexKey, Lists } from "@snort/nostr"; +import { HexKey, Lists } from "System"; import useNotelistSubscription from "Hooks/useNotelistSubscription"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Feed/EventFeed.ts b/packages/app/src/Feed/EventFeed.ts index fca62014..6ee13f7a 100644 --- a/packages/app/src/Feed/EventFeed.ts +++ b/packages/app/src/Feed/EventFeed.ts @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { NostrPrefix } from "@snort/nostr"; +import { NostrPrefix } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; import { RequestBuilder, ReplaceableNoteStore } from "System"; diff --git a/packages/app/src/Feed/EventPublisher.ts b/packages/app/src/Feed/EventPublisher.ts index 475ae29d..e2eef45c 100644 --- a/packages/app/src/Feed/EventPublisher.ts +++ b/packages/app/src/Feed/EventPublisher.ts @@ -1,12 +1,13 @@ import { useMemo } from "react"; import useLogin from "Hooks/useLogin"; import { EventPublisher } from "System/EventPublisher"; +import { System } from "index"; export default function useEventPublisher() { const { publicKey, privateKey } = useLogin(); return useMemo(() => { if (publicKey) { - return new EventPublisher(publicKey, privateKey); + return new EventPublisher(System, publicKey, privateKey); } }, [publicKey, privateKey]); } diff --git a/packages/app/src/Feed/FollowersFeed.ts b/packages/app/src/Feed/FollowersFeed.ts index 6acc1ec4..e8095bce 100644 --- a/packages/app/src/Feed/FollowersFeed.ts +++ b/packages/app/src/Feed/FollowersFeed.ts @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { HexKey, EventKind } from "@snort/nostr"; +import { HexKey, EventKind } from "System"; import { PubkeyReplaceableNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; diff --git a/packages/app/src/Feed/FollowsFeed.ts b/packages/app/src/Feed/FollowsFeed.ts index 499af525..b20bef55 100644 --- a/packages/app/src/Feed/FollowsFeed.ts +++ b/packages/app/src/Feed/FollowsFeed.ts @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { HexKey, TaggedRawEvent, EventKind } from "@snort/nostr"; +import { HexKey, TaggedRawEvent, EventKind } from "System"; import { PubkeyReplaceableNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; diff --git a/packages/app/src/Feed/LoginFeed.ts b/packages/app/src/Feed/LoginFeed.ts index 9f329067..bf214d7d 100644 --- a/packages/app/src/Feed/LoginFeed.ts +++ b/packages/app/src/Feed/LoginFeed.ts @@ -1,5 +1,5 @@ import { useEffect, useMemo } from "react"; -import { TaggedRawEvent, Lists, EventKind } from "@snort/nostr"; +import { TaggedRawEvent, Lists, EventKind } from "System"; import debug from "debug"; import { bech32ToHex, getNewest, getNewestEventTagsByKey, unwrap } from "SnortUtils"; diff --git a/packages/app/src/Feed/MuteList.ts b/packages/app/src/Feed/MuteList.ts index d7ac989e..3338c496 100644 --- a/packages/app/src/Feed/MuteList.ts +++ b/packages/app/src/Feed/MuteList.ts @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { HexKey, TaggedRawEvent, Lists, EventKind } from "@snort/nostr"; +import { HexKey, TaggedRawEvent, Lists, EventKind } from "System"; import { getNewest } from "SnortUtils"; import { ParameterizedReplaceableNoteStore, RequestBuilder } from "System"; diff --git a/packages/app/src/Feed/PinnedFeed.tsx b/packages/app/src/Feed/PinnedFeed.tsx index f8c6b5af..f55fe40d 100644 --- a/packages/app/src/Feed/PinnedFeed.tsx +++ b/packages/app/src/Feed/PinnedFeed.tsx @@ -1,4 +1,4 @@ -import { HexKey, Lists } from "@snort/nostr"; +import { HexKey, Lists } from "System"; import useNotelistSubscription from "Hooks/useNotelistSubscription"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Feed/RelayState.ts b/packages/app/src/Feed/RelayState.ts index beef8218..830ccb66 100644 --- a/packages/app/src/Feed/RelayState.ts +++ b/packages/app/src/Feed/RelayState.ts @@ -1,6 +1,6 @@ import { useSyncExternalStore } from "react"; -import { StateSnapshot } from "@snort/nostr"; -import { System } from "System"; +import { StateSnapshot } from "System"; +import { System } from "index"; const noop = () => { return () => undefined; diff --git a/packages/app/src/Feed/RelaysFeed.tsx b/packages/app/src/Feed/RelaysFeed.tsx index 4d6d30cb..677f5586 100644 --- a/packages/app/src/Feed/RelaysFeed.tsx +++ b/packages/app/src/Feed/RelaysFeed.tsx @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { HexKey, FullRelaySettings, EventKind } from "@snort/nostr"; +import { HexKey, FullRelaySettings, EventKind } from "System"; import { RequestBuilder } from "System"; import { ReplaceableNoteStore } from "System/NoteCollection"; diff --git a/packages/app/src/Feed/RelaysFeedFollows.tsx b/packages/app/src/Feed/RelaysFeedFollows.tsx index 42566875..128fe11f 100644 --- a/packages/app/src/Feed/RelaysFeedFollows.tsx +++ b/packages/app/src/Feed/RelaysFeedFollows.tsx @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { HexKey, FullRelaySettings, TaggedRawEvent, RelaySettings, EventKind } from "@snort/nostr"; +import { HexKey, FullRelaySettings, TaggedRawEvent, RelaySettings, EventKind } from "System"; import debug from "debug"; import { sanitizeRelayUrl } from "SnortUtils"; diff --git a/packages/app/src/Feed/ThreadFeed.ts b/packages/app/src/Feed/ThreadFeed.ts index f7a41f7a..adbc14f9 100644 --- a/packages/app/src/Feed/ThreadFeed.ts +++ b/packages/app/src/Feed/ThreadFeed.ts @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState } from "react"; -import { u256, EventKind } from "@snort/nostr"; +import { u256, EventKind } from "System"; import { appendDedupe, NostrLink } from "SnortUtils"; import { FlatNoteStore, RequestBuilder } from "System"; diff --git a/packages/app/src/Feed/TimelineFeed.ts b/packages/app/src/Feed/TimelineFeed.ts index f4edf359..8fb8693f 100644 --- a/packages/app/src/Feed/TimelineFeed.ts +++ b/packages/app/src/Feed/TimelineFeed.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useMemo, useState } from "react"; -import { EventKind, u256 } from "@snort/nostr"; +import { EventKind, u256 } from "System"; import { unixNow, unwrap, tagFilterOfTextRepost } from "SnortUtils"; import { FlatNoteStore, RequestBuilder } from "System"; diff --git a/packages/app/src/Feed/ZapsFeed.ts b/packages/app/src/Feed/ZapsFeed.ts index 38819589..88694a5e 100644 --- a/packages/app/src/Feed/ZapsFeed.ts +++ b/packages/app/src/Feed/ZapsFeed.ts @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { HexKey, EventKind } from "@snort/nostr"; +import { HexKey, EventKind } from "System"; import { parseZap } from "Element/Zap"; import { FlatNoteStore, RequestBuilder } from "System"; diff --git a/packages/app/src/Hooks/useInteractionCache.tsx b/packages/app/src/Hooks/useInteractionCache.tsx index 999331e2..b75e9bf0 100644 --- a/packages/app/src/Hooks/useInteractionCache.tsx +++ b/packages/app/src/Hooks/useInteractionCache.tsx @@ -1,5 +1,5 @@ import { useSyncExternalStore } from "react"; -import { HexKey, u256 } from "@snort/nostr"; +import { HexKey, u256 } from "System"; import { InteractionCache } from "Cache/EventInteractionCache"; import { EventInteraction } from "Db"; diff --git a/packages/app/src/Hooks/useModeration.tsx b/packages/app/src/Hooks/useModeration.tsx index f4532240..2cbd3c7a 100644 --- a/packages/app/src/Hooks/useModeration.tsx +++ b/packages/app/src/Hooks/useModeration.tsx @@ -1,4 +1,4 @@ -import { HexKey } from "@snort/nostr"; +import { HexKey } from "System"; import useEventPublisher from "Feed/EventPublisher"; import useLogin from "Hooks/useLogin"; import { setBlocked, setMuted } from "Login"; diff --git a/packages/app/src/Hooks/useNotelistSubscription.ts b/packages/app/src/Hooks/useNotelistSubscription.ts index 5e82d6a6..55aa7f95 100644 --- a/packages/app/src/Hooks/useNotelistSubscription.ts +++ b/packages/app/src/Hooks/useNotelistSubscription.ts @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { HexKey, Lists, EventKind } from "@snort/nostr"; +import { HexKey, Lists, EventKind } from "System"; import { FlatNoteStore, ParameterizedReplaceableNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; diff --git a/packages/app/src/Hooks/useRequestBuilder.tsx b/packages/app/src/Hooks/useRequestBuilder.tsx index cf8ae450..70d28030 100644 --- a/packages/app/src/Hooks/useRequestBuilder.tsx +++ b/packages/app/src/Hooks/useRequestBuilder.tsx @@ -1,7 +1,8 @@ import { useSyncExternalStore } from "react"; -import { RequestBuilder, System } from "System"; +import { RequestBuilder } from "System"; import { EmptySnapshot, NoteStore, StoreSnapshot } from "System/NoteCollection"; import { unwrap } from "SnortUtils"; +import { System } from "index"; const useRequestBuilder = >( type: { new (): TStore }, diff --git a/packages/app/src/Hooks/useSystemState.tsx b/packages/app/src/Hooks/useSystemState.tsx index a9aa8b94..971675be 100644 --- a/packages/app/src/Hooks/useSystemState.tsx +++ b/packages/app/src/Hooks/useSystemState.tsx @@ -1,5 +1,6 @@ import { useSyncExternalStore } from "react"; -import { System, SystemSnapshot } from "System"; +import { SystemSnapshot } from "System"; +import { System } from "index"; export default function useSystemState() { return useSyncExternalStore( diff --git a/packages/app/src/Hooks/useUserProfile.ts b/packages/app/src/Hooks/useUserProfile.ts index f04245a6..42d68310 100644 --- a/packages/app/src/Hooks/useUserProfile.ts +++ b/packages/app/src/Hooks/useUserProfile.ts @@ -1,9 +1,9 @@ import { useEffect, useSyncExternalStore } from "react"; -import { HexKey } from "@snort/nostr"; +import { HexKey } from "System"; import { MetadataCache } from "Cache"; import { UserCache } from "Cache/UserCache"; -import { ProfileLoader } from "System/ProfileCache"; +import { ProfileLoader } from "index"; export function useUserProfile(pubKey?: HexKey): MetadataCache | undefined { const user = useSyncExternalStore( diff --git a/packages/app/src/LNURL.ts b/packages/app/src/LNURL.ts index 1c3c3e01..9a0e75d7 100644 --- a/packages/app/src/LNURL.ts +++ b/packages/app/src/LNURL.ts @@ -1,4 +1,4 @@ -import { HexKey, RawEvent } from "@snort/nostr"; +import { HexKey, RawEvent } from "System"; import { EmailRegex } from "Const"; import { bech32ToText, unwrap } from "SnortUtils"; diff --git a/packages/app/src/Login/Functions.ts b/packages/app/src/Login/Functions.ts index 112b3fbd..ac9c37f2 100644 --- a/packages/app/src/Login/Functions.ts +++ b/packages/app/src/Login/Functions.ts @@ -1,4 +1,4 @@ -import { HexKey, RelaySettings } from "@snort/nostr"; +import { HexKey, RelaySettings } from "System"; import * as secp from "@noble/curves/secp256k1"; import * as utils from "@noble/curves/abstract/utils"; @@ -8,6 +8,7 @@ import { generateBip39Entropy, entropyToPrivateKey } from "nip6"; import { bech32ToHex, dedupeById, randomSample, sanitizeRelayUrl, unixNowMs, unwrap } from "SnortUtils"; import { SubscriptionEvent } from "Subscription"; import { EventPublisher } from "System/EventPublisher"; +import { System } from "index"; export function setRelays(state: LoginSession, relays: Record, createdAt: number) { if (state.relays.timestamp >= createdAt) { @@ -78,7 +79,7 @@ export async function generateNewLogin() { } const publicKey = utils.bytesToHex(secp.schnorr.getPublicKey(privateKey)); - const publisher = new EventPublisher(publicKey, privateKey); + const publisher = new EventPublisher(System, publicKey, privateKey); const ev = await publisher.contactList([bech32ToHex(SnortPubKey), publicKey], newRelays); publisher.broadcast(ev); diff --git a/packages/app/src/Login/LoginSession.ts b/packages/app/src/Login/LoginSession.ts index ab7dd421..95736dd6 100644 --- a/packages/app/src/Login/LoginSession.ts +++ b/packages/app/src/Login/LoginSession.ts @@ -1,4 +1,4 @@ -import { HexKey, RelaySettings, u256 } from "@snort/nostr"; +import { HexKey, RelaySettings, u256 } from "System"; import { UserPreferences } from "Login"; import { SubscriptionEvent } from "Subscription"; diff --git a/packages/app/src/Login/MultiAccountStore.ts b/packages/app/src/Login/MultiAccountStore.ts index a7c5d5be..ddbb4a02 100644 --- a/packages/app/src/Login/MultiAccountStore.ts +++ b/packages/app/src/Login/MultiAccountStore.ts @@ -1,7 +1,7 @@ import * as secp from "@noble/curves/secp256k1"; import * as utils from "@noble/curves/abstract/utils"; -import { HexKey, RelaySettings } from "@snort/nostr"; +import { HexKey, RelaySettings } from "System"; import { DefaultRelays } from "Const"; import ExternalStore from "ExternalStore"; diff --git a/packages/app/src/Nip05/SnortServiceProvider.ts b/packages/app/src/Nip05/SnortServiceProvider.ts index c4d58a1d..fe23ef2a 100644 --- a/packages/app/src/Nip05/SnortServiceProvider.ts +++ b/packages/app/src/Nip05/SnortServiceProvider.ts @@ -1,4 +1,4 @@ -import { EventKind } from "@snort/nostr"; +import { EventKind } from "System"; import { EventPublisher } from "System/EventPublisher"; import { ServiceError, ServiceProvider } from "./ServiceProvider"; diff --git a/packages/app/src/Notifications.ts b/packages/app/src/Notifications.ts index 01a6b746..09360995 100644 --- a/packages/app/src/Notifications.ts +++ b/packages/app/src/Notifications.ts @@ -1,7 +1,6 @@ import Nostrich from "nostrich.webp"; -import { TaggedRawEvent } from "@snort/nostr"; -import { EventKind } from "@snort/nostr"; +import { TaggedRawEvent, EventKind } from "System"; import { MetadataCache } from "Cache"; import { getDisplayName } from "Element/ProfileImage"; import { MentionRegex } from "Const"; diff --git a/packages/app/src/Pages/DonatePage.tsx b/packages/app/src/Pages/DonatePage.tsx index f5334df3..b871f7ac 100644 --- a/packages/app/src/Pages/DonatePage.tsx +++ b/packages/app/src/Pages/DonatePage.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; import { FormattedMessage } from "react-intl"; -import { HexKey } from "@snort/nostr"; +import { HexKey } from "System"; import { ApiHost, KieranPubKey, SnortPubKey } from "Const"; import ProfilePreview from "Element/ProfilePreview"; diff --git a/packages/app/src/Pages/Layout.tsx b/packages/app/src/Pages/Layout.tsx index 10a5e0e0..76dc35a7 100644 --- a/packages/app/src/Pages/Layout.tsx +++ b/packages/app/src/Pages/Layout.tsx @@ -9,7 +9,7 @@ import messages from "./messages"; import Icon from "Icons/Icon"; import { RootState } from "State/Store"; import { setShow, reset } from "State/NoteCreator"; -import { System } from "System"; +import { System } from "index"; import useLoginFeed from "Feed/LoginFeed"; import { totalUnread } from "Pages/MessagesPage"; import useModeration from "Hooks/useModeration"; diff --git a/packages/app/src/Pages/LoginPage.tsx b/packages/app/src/Pages/LoginPage.tsx index 3f64c537..faf6e957 100644 --- a/packages/app/src/Pages/LoginPage.tsx +++ b/packages/app/src/Pages/LoginPage.tsx @@ -3,7 +3,7 @@ import "./LoginPage.css"; import { CSSProperties, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { useIntl, FormattedMessage } from "react-intl"; -import { HexKey } from "@snort/nostr"; +import { HexKey } from "System"; import { bech32ToHex, unwrap } from "SnortUtils"; import ZapButton from "Element/ZapButton"; diff --git a/packages/app/src/Pages/MessagesPage.tsx b/packages/app/src/Pages/MessagesPage.tsx index 15ca8d5f..831fddd3 100644 --- a/packages/app/src/Pages/MessagesPage.tsx +++ b/packages/app/src/Pages/MessagesPage.tsx @@ -1,7 +1,7 @@ import React, { useMemo, useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { useNavigate } from "react-router-dom"; -import { HexKey, RawEvent, NostrPrefix } from "@snort/nostr"; +import { HexKey, RawEvent, NostrPrefix } from "System"; import UnreadCount from "Element/UnreadCount"; import ProfileImage, { getDisplayName } from "Element/ProfileImage"; diff --git a/packages/app/src/Pages/NostrLinkHandler.tsx b/packages/app/src/Pages/NostrLinkHandler.tsx index 7c977ab6..bc2397e0 100644 --- a/packages/app/src/Pages/NostrLinkHandler.tsx +++ b/packages/app/src/Pages/NostrLinkHandler.tsx @@ -1,4 +1,4 @@ -import { NostrPrefix } from "@snort/nostr"; +import { NostrPrefix } from "System"; import { useEffect, useState } from "react"; import { FormattedMessage } from "react-intl"; import { useNavigate, useParams } from "react-router-dom"; @@ -6,7 +6,7 @@ import { useNavigate, useParams } from "react-router-dom"; import Spinner from "Icons/Spinner"; import { parseNostrLink, profileLink } from "SnortUtils"; import { getNip05PubKey } from "Pages/LoginPage"; -import { System } from "System"; +import { System } from "index"; export default function NostrLinkHandler() { const params = useParams(); diff --git a/packages/app/src/Pages/ProfilePage.tsx b/packages/app/src/Pages/ProfilePage.tsx index c58fc6b7..25adf365 100644 --- a/packages/app/src/Pages/ProfilePage.tsx +++ b/packages/app/src/Pages/ProfilePage.tsx @@ -2,7 +2,7 @@ import "./ProfilePage.css"; import { useEffect, useState } from "react"; import { useIntl, FormattedMessage } from "react-intl"; import { useNavigate, useParams } from "react-router-dom"; -import { encodeTLV, EventKind, HexKey, NostrPrefix } from "@snort/nostr"; +import { encodeTLV, EventKind, HexKey, NostrPrefix } from "System"; import { parseNostrLink, getReactions, unwrap } from "SnortUtils"; import { formatShort } from "Number"; diff --git a/packages/app/src/Pages/Root.tsx b/packages/app/src/Pages/Root.tsx index 2ca863b8..10de36e0 100644 --- a/packages/app/src/Pages/Root.tsx +++ b/packages/app/src/Pages/Root.tsx @@ -5,7 +5,7 @@ import { useIntl, FormattedMessage } from "react-intl"; import Tabs, { Tab } from "Element/Tabs"; import Timeline from "Element/Timeline"; -import { System } from "System"; +import { System } from "index"; import { TimelineSubject } from "Feed/TimelineFeed"; import { debounce, getRelayName, sha256, unixNow, unwrap } from "SnortUtils"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Pages/SearchPage.tsx b/packages/app/src/Pages/SearchPage.tsx index 7e18cd66..f7268402 100644 --- a/packages/app/src/Pages/SearchPage.tsx +++ b/packages/app/src/Pages/SearchPage.tsx @@ -4,9 +4,8 @@ import Timeline from "Element/Timeline"; import { Tab, TabElement } from "Element/Tabs"; import { useEffect, useState } from "react"; import { debounce } from "SnortUtils"; -import { router } from "index"; +import { System, router } from "index"; import { SearchRelays } from "Const"; -import { System } from "System"; import TrendingUsers from "Element/TrendingUsers"; import TrendingNotes from "Element/TrendingPosts"; diff --git a/packages/app/src/Pages/ZapPool.tsx b/packages/app/src/Pages/ZapPool.tsx index ca753c0d..431d0af3 100644 --- a/packages/app/src/Pages/ZapPool.tsx +++ b/packages/app/src/Pages/ZapPool.tsx @@ -6,13 +6,13 @@ import { FormattedMessage, FormattedNumber } from "react-intl"; import { SnortPubKey } from "Const"; import ProfilePreview from "Element/ProfilePreview"; import useLogin from "Hooks/useLogin"; -import { System } from "System"; import { UploaderServices } from "Upload"; import { bech32ToHex, getRelayName, unwrap } from "SnortUtils"; import { ZapPoolController, ZapPoolRecipient, ZapPoolRecipientType } from "ZapPoolController"; import { useUserProfile } from "Hooks/useUserProfile"; import AsyncButton from "Element/AsyncButton"; import { useWallet } from "Wallet"; +import { System } from "index"; const DataProviders = [ { diff --git a/packages/app/src/Pages/settings/Keys.tsx b/packages/app/src/Pages/settings/Keys.tsx index 6882fdde..d8871bcc 100644 --- a/packages/app/src/Pages/settings/Keys.tsx +++ b/packages/app/src/Pages/settings/Keys.tsx @@ -1,6 +1,6 @@ import "./Keys.css"; import { FormattedMessage } from "react-intl"; -import { encodeTLV, NostrPrefix } from "@snort/nostr"; +import { encodeTLV, NostrPrefix } from "System"; import Copy from "Element/Copy"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Pages/settings/RelayInfo.tsx b/packages/app/src/Pages/settings/RelayInfo.tsx index a02cca5d..fc6d9a2f 100644 --- a/packages/app/src/Pages/settings/RelayInfo.tsx +++ b/packages/app/src/Pages/settings/RelayInfo.tsx @@ -3,7 +3,7 @@ import ProfilePreview from "Element/ProfilePreview"; import useRelayState from "Feed/RelayState"; import { useNavigate, useParams } from "react-router-dom"; import { parseId, unwrap } from "SnortUtils"; -import { System } from "System"; +import { System } from "index"; import { removeRelay } from "Login"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Pages/settings/Relays.tsx b/packages/app/src/Pages/settings/Relays.tsx index 0cf5b6b2..52f992bf 100644 --- a/packages/app/src/Pages/settings/Relays.tsx +++ b/packages/app/src/Pages/settings/Relays.tsx @@ -4,7 +4,7 @@ import { FormattedMessage } from "react-intl"; import { randomSample, unixNowMs } from "SnortUtils"; import Relay from "Element/Relay"; import useEventPublisher from "Feed/EventPublisher"; -import { System } from "System"; +import { System } from "index"; import useLogin from "Hooks/useLogin"; import { setRelays } from "Login"; diff --git a/packages/app/src/SnortApi.ts b/packages/app/src/SnortApi.ts index 404e7d78..75dde20a 100644 --- a/packages/app/src/SnortApi.ts +++ b/packages/app/src/SnortApi.ts @@ -1,4 +1,4 @@ -import { EventKind } from "@snort/nostr"; +import { EventKind } from "System"; import { ApiHost } from "Const"; import { SubscriptionType } from "Subscription"; import { EventPublisher } from "System/EventPublisher"; diff --git a/packages/app/src/SnortUtils/Utils.test.ts b/packages/app/src/SnortUtils/Utils.test.ts index 83f10ff1..e40c6979 100644 --- a/packages/app/src/SnortUtils/Utils.test.ts +++ b/packages/app/src/SnortUtils/Utils.test.ts @@ -1,4 +1,4 @@ -import { NostrPrefix } from "@snort/nostr"; +import { NostrPrefix } from "System"; import { parseNostrLink, tryParseNostrLink } from "."; import { splitByUrl, magnetURIDecode, getRelayName } from "."; import { describe, expect } from "@jest/globals"; diff --git a/packages/app/src/SnortUtils/index.ts b/packages/app/src/SnortUtils/index.ts index 7147666f..42980866 100644 --- a/packages/app/src/SnortUtils/index.ts +++ b/packages/app/src/SnortUtils/index.ts @@ -16,7 +16,7 @@ import { decodeTLV, TLVEntryType, RawEvent, -} from "@snort/nostr"; +} from "System"; import { MetadataCache } from "Cache"; import NostrLink from "Element/NostrLink"; diff --git a/packages/app/src/State/NoteCreator.ts b/packages/app/src/State/NoteCreator.ts index 43aaee19..e4941bb6 100644 --- a/packages/app/src/State/NoteCreator.ts +++ b/packages/app/src/State/NoteCreator.ts @@ -1,5 +1,5 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { RawEvent, TaggedRawEvent } from "@snort/nostr"; +import { RawEvent, TaggedRawEvent } from "System"; interface NoteCreatorStore { show: boolean; diff --git a/packages/app/src/State/ReBroadcast.ts b/packages/app/src/State/ReBroadcast.ts index acb1ddec..97b12d57 100644 --- a/packages/app/src/State/ReBroadcast.ts +++ b/packages/app/src/State/ReBroadcast.ts @@ -1,5 +1,5 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { RawEvent } from "@snort/nostr"; +import { RawEvent } from "System"; interface ReBroadcastStore { show: boolean; diff --git a/packages/app/src/System/Connection.ts b/packages/app/src/System/Connection.ts new file mode 100644 index 00000000..56727d58 --- /dev/null +++ b/packages/app/src/System/Connection.ts @@ -0,0 +1,448 @@ +import { v4 as uuid } from "uuid"; + +import { DefaultConnectTimeout } from "./Const"; +import { ConnectionStats } from "./ConnectionStats"; +import { RawEvent, ReqCommand, TaggedRawEvent, u256 } from "./Nostr"; +import { RelayInfo } from "./RelayInfo"; +import { unwrap } from "./Util"; + +export type CustomHook = (state: Readonly) => void; +export type AuthHandler = (challenge: string, relay: string) => Promise; + +/** + * Relay settings + */ +export interface RelaySettings { + read: boolean; + write: boolean; +} + +/** + * Snapshot of connection stats + */ +export interface StateSnapshot { + connected: boolean; + disconnects: number; + avgLatency: number; + events: { + received: number; + send: number; + }; + info?: RelayInfo; + pendingRequests: Array; + activeRequests: Array; + id: string; +} + +export class Connection { + Id: string; + Address: string; + Socket: WebSocket | null = null; + + PendingRaw: Array = []; + PendingRequests: Array<{ + cmd: ReqCommand; + cb: () => void; + }> = []; + ActiveRequests = new Set(); + + Settings: RelaySettings; + Info?: RelayInfo; + ConnectTimeout: number = DefaultConnectTimeout; + Stats: ConnectionStats = new ConnectionStats(); + StateHooks: Map = new Map(); + HasStateChange: boolean = true; + CurrentState: StateSnapshot; + LastState: Readonly; + IsClosed: boolean; + ReconnectTimer: ReturnType | null; + EventsCallback: Map void>; + OnConnected?: () => void; + OnEvent?: (sub: string, e: TaggedRawEvent) => void; + OnEose?: (sub: string) => void; + OnDisconnect?: (id: string) => void; + Auth?: AuthHandler; + AwaitingAuth: Map; + Authed = false; + Ephemeral: boolean; + EphemeralTimeout: ReturnType | undefined; + Down = true; + + constructor(addr: string, options: RelaySettings, auth?: AuthHandler, ephemeral: boolean = false) { + this.Id = uuid(); + this.Address = addr; + this.Settings = options; + this.CurrentState = { + connected: false, + disconnects: 0, + avgLatency: 0, + events: { + received: 0, + send: 0, + }, + } as StateSnapshot; + this.LastState = Object.freeze({ ...this.CurrentState }); + this.IsClosed = false; + this.ReconnectTimer = null; + this.EventsCallback = new Map(); + this.AwaitingAuth = new Map(); + this.Auth = auth; + this.Ephemeral = ephemeral; + } + + ResetEphemeralTimeout() { + if (this.EphemeralTimeout) { + clearTimeout(this.EphemeralTimeout); + } + if (this.Ephemeral) { + this.EphemeralTimeout = setTimeout(() => { + this.Close(); + }, 30_000); + } + } + + async Connect() { + try { + if (this.Info === undefined) { + const u = new URL(this.Address); + const rsp = await fetch(`${u.protocol === "wss:" ? "https:" : "http:"}//${u.host}`, { + headers: { + accept: "application/nostr+json", + }, + }); + if (rsp.ok) { + const data = await rsp.json(); + for (const [k, v] of Object.entries(data)) { + if (v === "unset" || v === "" || v === "~") { + data[k] = undefined; + } + } + this.Info = data; + } + } + } catch (e) { + console.warn("Could not load relay information", e); + } + + if (this.Socket) { + this.Id = uuid(); + this.Socket.onopen = null; + this.Socket.onmessage = null; + this.Socket.onerror = null; + this.Socket.onclose = null; + } + this.IsClosed = false; + this.Socket = new WebSocket(this.Address); + this.Socket.onopen = () => this.OnOpen(); + this.Socket.onmessage = e => this.OnMessage(e); + this.Socket.onerror = e => this.OnError(e); + this.Socket.onclose = e => this.OnClose(e); + } + + Close() { + this.IsClosed = true; + if (this.ReconnectTimer !== null) { + clearTimeout(this.ReconnectTimer); + this.ReconnectTimer = null; + } + this.Socket?.close(); + this.#UpdateState(); + } + + OnOpen() { + this.ConnectTimeout = DefaultConnectTimeout; + console.log(`[${this.Address}] Open!`); + this.Down = false; + if (this.Ephemeral) { + this.ResetEphemeralTimeout(); + } + this.OnConnected?.(); + this.#sendPendingRaw(); + } + + OnClose(e: CloseEvent) { + if (!this.IsClosed) { + this.ConnectTimeout = this.ConnectTimeout * 2; + console.log( + `[${this.Address}] Closed (${e.reason}), trying again in ${(this.ConnectTimeout / 1000) + .toFixed(0) + .toLocaleString()} sec` + ); + this.ReconnectTimer = setTimeout(() => { + this.Connect(); + }, this.ConnectTimeout); + this.Stats.Disconnects++; + } else { + console.log(`[${this.Address}] Closed!`); + this.ReconnectTimer = null; + } + + this.OnDisconnect?.(this.Id); + this.#ResetQueues(); + // reset connection Id on disconnect, for query-tracking + this.Id = uuid(); + this.#UpdateState(); + } + + OnMessage(e: MessageEvent) { + if (e.data.length > 0) { + const msg = JSON.parse(e.data); + const tag = msg[0]; + switch (tag) { + case "AUTH": { + this._OnAuthAsync(msg[1]) + .then(() => this.#sendPendingRaw()) + .catch(console.error); + this.Stats.EventsReceived++; + this.#UpdateState(); + break; + } + case "EVENT": { + this.OnEvent?.(msg[1], { + ...msg[2], + relays: [this.Address], + }); + this.Stats.EventsReceived++; + this.#UpdateState(); + break; + } + case "EOSE": { + this.OnEose?.(msg[1]); + break; + } + case "OK": { + // feedback to broadcast call + console.debug(`${this.Address} OK: `, msg); + const id = msg[1]; + if (this.EventsCallback.has(id)) { + const cb = unwrap(this.EventsCallback.get(id)); + this.EventsCallback.delete(id); + cb(msg); + } + break; + } + case "NOTICE": { + console.warn(`[${this.Address}] NOTICE: ${msg[1]}`); + break; + } + default: { + console.warn(`Unknown tag: ${tag}`); + break; + } + } + } + } + + OnError(e: Event) { + console.error(e); + this.#UpdateState(); + } + + /** + * Send event on this connection + */ + SendEvent(e: RawEvent) { + if (!this.Settings.write) { + return; + } + const req = ["EVENT", e]; + this.#SendJson(req); + this.Stats.EventsSent++; + this.#UpdateState(); + } + + /** + * Send event on this connection and wait for OK response + */ + async SendAsync(e: RawEvent, timeout = 5000) { + return new Promise(resolve => { + if (!this.Settings.write) { + resolve(); + return; + } + const t = setTimeout(() => { + resolve(); + }, timeout); + this.EventsCallback.set(e.id, () => { + clearTimeout(t); + resolve(); + }); + + const req = ["EVENT", e]; + this.#SendJson(req); + this.Stats.EventsSent++; + this.#UpdateState(); + }); + } + + /** + * Hook status for connection + */ + StatusHook(fnHook: CustomHook) { + const id = uuid(); + this.StateHooks.set(id, fnHook); + return () => { + this.StateHooks.delete(id); + }; + } + + /** + * Returns the current state of this connection + */ + GetState() { + if (this.HasStateChange) { + this.LastState = Object.freeze({ ...this.CurrentState }); + this.HasStateChange = false; + } + return this.LastState; + } + + /** + * Using relay document to determine if this relay supports a feature + */ + SupportsNip(n: number) { + return this.Info?.supported_nips?.some(a => a === n) ?? false; + } + + /** + * Queue or send command to the relay + * @param cmd The REQ to send to the server + */ + QueueReq(cmd: ReqCommand, cbSent: () => void) { + if (this.ActiveRequests.size >= this.#maxSubscriptions) { + this.PendingRequests.push({ + cmd, + cb: cbSent, + }); + console.debug("Queuing:", this.Address, cmd); + } else { + this.ActiveRequests.add(cmd[1]); + this.#SendJson(cmd); + cbSent(); + } + this.#UpdateState(); + } + + CloseReq(id: string) { + if (this.ActiveRequests.delete(id)) { + this.#SendJson(["CLOSE", id]); + this.OnEose?.(id); + this.#SendQueuedRequests(); + } + this.#UpdateState(); + } + + #SendQueuedRequests() { + const canSend = this.#maxSubscriptions - this.ActiveRequests.size; + if (canSend > 0) { + for (let x = 0; x < canSend; x++) { + const p = this.PendingRequests.shift(); + if (p) { + this.ActiveRequests.add(p.cmd[1]); + this.#SendJson(p.cmd); + p.cb(); + console.debug("Sent pending REQ", this.Address, p.cmd); + } + } + } + } + + #ResetQueues() { + this.ActiveRequests.clear(); + this.PendingRequests = []; + this.PendingRaw = []; + this.#UpdateState(); + } + + #UpdateState() { + this.CurrentState.connected = this.Socket?.readyState === WebSocket.OPEN; + this.CurrentState.events.received = this.Stats.EventsReceived; + this.CurrentState.events.send = this.Stats.EventsSent; + this.CurrentState.avgLatency = + this.Stats.Latency.length > 0 ? this.Stats.Latency.reduce((acc, v) => acc + v, 0) / this.Stats.Latency.length : 0; + this.CurrentState.disconnects = this.Stats.Disconnects; + this.CurrentState.info = this.Info; + this.CurrentState.id = this.Id; + this.CurrentState.pendingRequests = [...this.PendingRequests.map(a => a.cmd[1])]; + this.CurrentState.activeRequests = [...this.ActiveRequests]; + this.Stats.Latency = this.Stats.Latency.slice(-20); // trim + this.HasStateChange = true; + this.#NotifyState(); + } + + #NotifyState() { + const state = this.GetState(); + for (const [, h] of this.StateHooks) { + h(state); + } + } + + #SendJson(obj: object) { + const authPending = !this.Authed && (this.AwaitingAuth.size > 0 || this.Info?.limitation?.auth_required === true); + if (this.Socket?.readyState !== WebSocket.OPEN || authPending) { + this.PendingRaw.push(obj); + if (this.Socket?.readyState === WebSocket.CLOSED && this.Ephemeral && this.IsClosed) { + this.Connect(); + } + return false; + } + + this.#sendPendingRaw(); + this.#sendOnWire(obj); + } + + #sendPendingRaw() { + while (this.PendingRaw.length > 0) { + const next = this.PendingRaw.shift(); + if (next) { + this.#sendOnWire(next); + } + } + } + + #sendOnWire(obj: unknown) { + if (this.Socket?.readyState !== WebSocket.OPEN) { + throw new Error(`Socket is not open, state is ${this.Socket?.readyState}`); + } + const json = JSON.stringify(obj); + this.Socket.send(json); + return true; + } + + async _OnAuthAsync(challenge: string): Promise { + const authCleanup = () => { + this.AwaitingAuth.delete(challenge); + }; + if (!this.Auth) { + throw new Error("Auth hook not registered"); + } + this.AwaitingAuth.set(challenge, true); + const authEvent = await this.Auth(challenge, this.Address); + return new Promise(resolve => { + if (!authEvent) { + authCleanup(); + return Promise.reject("no event"); + } + + const t = setTimeout(() => { + authCleanup(); + resolve(); + }, 10_000); + + this.EventsCallback.set(authEvent.id, (msg: boolean[]) => { + clearTimeout(t); + authCleanup(); + if (msg.length > 3 && msg[2] === true) { + this.Authed = true; + } + resolve(); + }); + + this.#sendOnWire(["AUTH", authEvent]); + }); + } + + get #maxSubscriptions() { + return this.Info?.limitation?.max_subscriptions ?? 25; + } +} diff --git a/packages/app/src/System/ConnectionStats.ts b/packages/app/src/System/ConnectionStats.ts new file mode 100644 index 00000000..d1248f2d --- /dev/null +++ b/packages/app/src/System/ConnectionStats.ts @@ -0,0 +1,34 @@ +/** + * Stats class for tracking metrics per connection + */ +export class ConnectionStats { + /** + * Last n records of how long between REQ->EOSE + */ + Latency: number[] = []; + + /** + * Total number of REQ's sent on this connection + */ + Subs: number = 0; + + /** + * Count of REQ which took too long and where abandoned + */ + SubsTimeout: number = 0; + + /** + * Total number of EVENT messages received + */ + EventsReceived: number = 0; + + /** + * Total number of EVENT messages sent + */ + EventsSent: number = 0; + + /** + * Total number of times this connection was lost + */ + Disconnects: number = 0; +} diff --git a/packages/app/src/System/Const.ts b/packages/app/src/System/Const.ts new file mode 100644 index 00000000..e3502ce0 --- /dev/null +++ b/packages/app/src/System/Const.ts @@ -0,0 +1,4 @@ +/** + * Websocket re-connect timeout + */ +export const DefaultConnectTimeout = 2000; diff --git a/packages/app/src/System/EventBuilder.ts b/packages/app/src/System/EventBuilder.ts index 0c353d77..ea7c1df8 100644 --- a/packages/app/src/System/EventBuilder.ts +++ b/packages/app/src/System/EventBuilder.ts @@ -1,4 +1,4 @@ -import { EventKind, HexKey, NostrPrefix, RawEvent } from "@snort/nostr"; +import { EventKind, HexKey, NostrPrefix, RawEvent } from "System"; import { HashtagRegex } from "Const"; import { getPublicKey, parseNostrLink, unixNow } from "SnortUtils"; import { EventExt } from "./EventExt"; diff --git a/packages/app/src/System/EventExt.ts b/packages/app/src/System/EventExt.ts index 305e34db..ca2b34ee 100644 --- a/packages/app/src/System/EventExt.ts +++ b/packages/app/src/System/EventExt.ts @@ -1,6 +1,6 @@ import * as secp from "@noble/curves/secp256k1"; import * as utils from "@noble/curves/abstract/utils"; -import { EventKind, HexKey, RawEvent, Tag } from "@snort/nostr"; +import { EventKind, HexKey, RawEvent, Tag } from "System"; import base64 from "@protobufjs/base64"; import { sha256, unixNow } from "SnortUtils"; diff --git a/packages/app/src/System/EventKind.ts b/packages/app/src/System/EventKind.ts new file mode 100644 index 00000000..512a1f0a --- /dev/null +++ b/packages/app/src/System/EventKind.ts @@ -0,0 +1,29 @@ +enum EventKind { + Unknown = -1, + SetMetadata = 0, + TextNote = 1, + RecommendServer = 2, + ContactList = 3, // NIP-02 + DirectMessage = 4, // NIP-04 + Deletion = 5, // NIP-09 + Repost = 6, // NIP-18 + Reaction = 7, // NIP-25 + BadgeAward = 8, // NIP-58 + SnortSubscriptions = 1000, // NIP-XX + Polls = 6969, // NIP-69 + FileHeader = 1063, // NIP-94 + Relays = 10002, // NIP-65 + Ephemeral = 20_000, + Auth = 22242, // NIP-42 + PubkeyLists = 30000, // NIP-51a + NoteLists = 30001, // NIP-51b + TagLists = 30002, // NIP-51c + Badge = 30009, // NIP-58 + ProfileBadges = 30008, // NIP-58 + ZapstrTrack = 31337, + ZapRequest = 9734, // NIP 57 + ZapReceipt = 9735, // NIP 57 + HttpAuthentication = 27235, // NIP XX - HTTP Authentication +} + +export default EventKind; diff --git a/packages/app/src/System/EventPublisher.ts b/packages/app/src/System/EventPublisher.ts index 68e556dc..e77f4533 100644 --- a/packages/app/src/System/EventPublisher.ts +++ b/packages/app/src/System/EventPublisher.ts @@ -10,10 +10,9 @@ import { TaggedRawEvent, u256, UserMetadata, -} from "@snort/nostr"; +} from "System"; import { DefaultRelays } from "Const"; -import { System } from "System"; import { unwrap } from "SnortUtils"; import { EventBuilder } from "./EventBuilder"; import { EventExt } from "./EventExt"; @@ -23,11 +22,34 @@ const Nip7Queue: Array = []; processWorkQueue(Nip7Queue); export type EventBuilderHook = (ev: EventBuilder) => EventBuilder; +declare global { + interface Window { + nostr?: { + getPublicKey: () => Promise; + signEvent: (event: T) => Promise; + + getRelays?: () => Promise>; + + nip04?: { + encrypt?: (pubkey: HexKey, plaintext: string) => Promise; + decrypt?: (pubkey: HexKey, ciphertext: string) => Promise; + }; + }; + } +} + +interface SystemInterface { + BroadcastEvent(ev: RawEvent): void; + WriteOnceToRelay(relay: string, ev: RawEvent): Promise; +} + export class EventPublisher { + #system: SystemInterface; #pubKey: string; #privateKey?: string; - constructor(pubKey: string, privKey?: string) { + constructor(system: SystemInterface, pubKey: string, privKey?: string) { + this.#system = system; if (privKey) { this.#privateKey = privKey; this.#pubKey = utils.bytesToHex(secp.schnorr.getPublicKey(privKey)); @@ -97,7 +119,7 @@ export class EventPublisher { broadcast(ev: RawEvent) { console.debug(ev); - System.BroadcastEvent(ev); + this.#system.BroadcastEvent(ev); } /** @@ -107,7 +129,7 @@ export class EventPublisher { */ broadcastForBootstrap(ev: RawEvent) { for (const [k] of DefaultRelays) { - System.WriteOnceToRelay(k, ev); + this.#system.WriteOnceToRelay(k, ev); } } @@ -116,7 +138,7 @@ export class EventPublisher { */ broadcastAll(ev: RawEvent, relays: string[]) { for (const k of relays) { - System.WriteOnceToRelay(k, ev); + this.#system.WriteOnceToRelay(k, ev); } } diff --git a/packages/app/src/System/GossipModel.ts b/packages/app/src/System/GossipModel.ts index 914f57a6..3a6db619 100644 --- a/packages/app/src/System/GossipModel.ts +++ b/packages/app/src/System/GossipModel.ts @@ -1,4 +1,4 @@ -import { FullRelaySettings, RawReqFilter } from "@snort/nostr"; +import { FullRelaySettings, RawReqFilter } from "System"; import { unwrap } from "SnortUtils"; import debug from "debug"; diff --git a/packages/app/src/System/Links.ts b/packages/app/src/System/Links.ts new file mode 100644 index 00000000..fd854032 --- /dev/null +++ b/packages/app/src/System/Links.ts @@ -0,0 +1,88 @@ +import * as utils from "@noble/curves/abstract/utils"; +import { bech32 } from "bech32"; +import { HexKey } from "./Nostr"; + +export enum NostrPrefix { + PublicKey = "npub", + PrivateKey = "nsec", + Note = "note", + + // TLV prefixes + Profile = "nprofile", + Event = "nevent", + Relay = "nrelay", + Address = "naddr", +} + +export enum TLVEntryType { + Special = 0, + Relay = 1, + Author = 2, + Kind = 3, +} + +export interface TLVEntry { + type: TLVEntryType; + length: number; + value: string | HexKey | number; +} + +export function encodeTLV(prefix: NostrPrefix, id: string, relays?: string[], kind?: number, author?: string) { + const enc = new TextEncoder(); + const buf = prefix === NostrPrefix.Address ? enc.encode(id) : utils.hexToBytes(id); + + const tl0 = [0, buf.length, ...buf]; + const tl1 = + relays + ?.map(a => { + const data = enc.encode(a); + return [1, data.length, ...data]; + }) + .flat() ?? []; + + const tl2 = author ? [2, 32, ...utils.hexToBytes(author)] : []; + const tl3 = kind ? [3, 4, ...new Uint8Array(new Uint32Array([kind]).buffer).reverse()] : []; + + return bech32.encode(prefix, bech32.toWords([...tl0, ...tl1, ...tl2, ...tl3]), 1_000); +} + +export function decodeTLV(str: string) { + const decoded = bech32.decode(str, 1_000); + const data = bech32.fromWords(decoded.words); + + const entries: TLVEntry[] = []; + let x = 0; + while (x < data.length) { + const t = data[x]; + const l = data[x + 1]; + const v = data.slice(x + 2, x + 2 + l); + entries.push({ + type: t, + length: l, + value: decodeTLVEntry(t, decoded.prefix, new Uint8Array(v)), + }); + x += 2 + l; + } + return entries; +} + +function decodeTLVEntry(type: TLVEntryType, prefix: string, data: Uint8Array) { + switch (type) { + case TLVEntryType.Special: { + if (prefix === NostrPrefix.Address) { + return new TextDecoder("ASCII").decode(data); + } else { + return utils.bytesToHex(data); + } + } + case TLVEntryType.Author: { + return utils.bytesToHex(data); + } + case TLVEntryType.Kind: { + return new Uint32Array(new Uint8Array(data.reverse()).buffer)[0]; + } + case TLVEntryType.Relay: { + return new TextDecoder("ASCII").decode(data); + } + } +} diff --git a/packages/app/src/System/Nips.ts b/packages/app/src/System/Nips.ts new file mode 100644 index 00000000..decdfa3c --- /dev/null +++ b/packages/app/src/System/Nips.ts @@ -0,0 +1,3 @@ +export enum Nips { + Search = 50, +} diff --git a/packages/app/src/System/Nostr.ts b/packages/app/src/System/Nostr.ts new file mode 100644 index 00000000..8e36ae3b --- /dev/null +++ b/packages/app/src/System/Nostr.ts @@ -0,0 +1,84 @@ +import { RelaySettings } from "./Connection"; + +export type RawEvent = { + id: u256; + pubkey: HexKey; + created_at: number; + kind: number; + tags: Array>; + content: string; + sig: string; +}; + +export interface TaggedRawEvent extends RawEvent { + /** + * A list of relays this event was seen on + */ + relays: string[]; +} + +/** + * Basic raw key as hex + */ +export type HexKey = string; + +/** + * Optional HexKey + */ +export type MaybeHexKey = HexKey | undefined; + +/** + * A 256bit hex id + */ +export type u256 = string; + +export type ReqCommand = [cmd: "REQ", id: string, ...filters: Array]; + +/** + * Raw REQ filter object + */ +export type RawReqFilter = { + ids?: u256[]; + authors?: u256[]; + kinds?: number[]; + "#e"?: u256[]; + "#p"?: u256[]; + "#t"?: string[]; + "#d"?: string[]; + "#r"?: string[]; + search?: string; + since?: number; + until?: number; + limit?: number; +}; + +/** + * Medatadata event content + */ +export type UserMetadata = { + name?: string; + display_name?: string; + about?: string; + picture?: string; + website?: string; + banner?: string; + nip05?: string; + lud06?: string; + lud16?: string; +}; + +/** + * NIP-51 list types + */ +export enum Lists { + Muted = "mute", + Pinned = "pin", + Bookmarked = "bookmark", + Followed = "follow", + Badges = "profile_badges", +} + +export interface FullRelaySettings { + url: string; + settings: RelaySettings; +} diff --git a/packages/app/src/System/NoteCollection.test.ts b/packages/app/src/System/NoteCollection.test.ts index 8df3559a..00eb6086 100644 --- a/packages/app/src/System/NoteCollection.test.ts +++ b/packages/app/src/System/NoteCollection.test.ts @@ -1,4 +1,4 @@ -import { TaggedRawEvent } from "@snort/nostr"; +import { TaggedRawEvent } from "System"; import { describe, expect } from "@jest/globals"; import { FlatNoteStore, ReplaceableNoteStore } from "./NoteCollection"; diff --git a/packages/app/src/System/NoteCollection.ts b/packages/app/src/System/NoteCollection.ts index da7efa64..b740cb96 100644 --- a/packages/app/src/System/NoteCollection.ts +++ b/packages/app/src/System/NoteCollection.ts @@ -1,4 +1,4 @@ -import { TaggedRawEvent, u256 } from "@snort/nostr"; +import { TaggedRawEvent, u256 } from "System"; import { appendDedupe, findTag } from "SnortUtils"; export interface StoreSnapshot { diff --git a/packages/app/src/System/ProfileCache.ts b/packages/app/src/System/ProfileCache.ts index 00a6fb55..da5fe5e8 100644 --- a/packages/app/src/System/ProfileCache.ts +++ b/packages/app/src/System/ProfileCache.ts @@ -1,12 +1,14 @@ -import { EventKind, HexKey, TaggedRawEvent } from "@snort/nostr"; +import { EventKind, HexKey, NostrSystem, TaggedRawEvent } from "System"; import { ProfileCacheExpire } from "Const"; import { mapEventToProfile, MetadataCache } from "Cache"; import { UserCache } from "Cache/UserCache"; -import { PubkeyReplaceableNoteStore, RequestBuilder, System } from "System"; +import { PubkeyReplaceableNoteStore, RequestBuilder } from "System"; import { unixNowMs } from "SnortUtils"; import debug from "debug"; -class ProfileLoaderService { +export class ProfileLoaderService { + #system: NostrSystem; + /** * List of pubkeys to fetch metadata for */ @@ -14,7 +16,8 @@ class ProfileLoaderService { readonly #log = debug("ProfileCache"); - constructor() { + constructor(system: NostrSystem) { + this.#system = system; this.#FetchMetadata(); } @@ -70,7 +73,7 @@ class ProfileLoaderService { .authors([...missing]); const newProfiles = new Set(); - const q = System.Query(PubkeyReplaceableNoteStore, sub); + const q = this.#system.Query(PubkeyReplaceableNoteStore, sub); // never release this callback, it will stop firing anyway after eose const releaseOnEvent = q.onEvent(async e => { for (const pe of e) { @@ -118,5 +121,3 @@ class ProfileLoaderService { setTimeout(() => this.#FetchMetadata(), 500); } } - -export const ProfileLoader = new ProfileLoaderService(); diff --git a/packages/app/src/System/Query.test.ts b/packages/app/src/System/Query.test.ts index cc4dbb1b..d6617896 100644 --- a/packages/app/src/System/Query.test.ts +++ b/packages/app/src/System/Query.test.ts @@ -1,4 +1,4 @@ -import { Connection } from "@snort/nostr"; +import { Connection } from "System"; import { describe, expect } from "@jest/globals"; import { Query, QueryBase } from "./Query"; import { getRandomValues } from "crypto"; diff --git a/packages/app/src/System/Query.ts b/packages/app/src/System/Query.ts index b3090dd9..4521d1b6 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/app/src/System/Query.ts @@ -1,6 +1,6 @@ import { v4 as uuid } from "uuid"; import debug from "debug"; -import { Connection, RawReqFilter, Nips } from "@snort/nostr"; +import { Connection, RawReqFilter, Nips } from "System"; import { unixNowMs, unwrap } from "SnortUtils"; import { NoteStore } from "./NoteCollection"; import { mergeSimilar } from "./RequestMerger"; diff --git a/packages/app/src/System/RelayInfo.ts b/packages/app/src/System/RelayInfo.ts new file mode 100644 index 00000000..984af50b --- /dev/null +++ b/packages/app/src/System/RelayInfo.ts @@ -0,0 +1,16 @@ +export interface RelayInfo { + name?: string; + description?: string; + pubkey?: string; + contact?: string; + supported_nips?: number[]; + software?: string; + version?: string; + limitation?: { + payment_required: boolean; + max_subscriptions: number; + max_filters: number; + max_event_tags: number; + auth_required: boolean; + }; +} diff --git a/packages/app/src/System/RequestBuilder.ts b/packages/app/src/System/RequestBuilder.ts index 74e923a1..56a49e85 100644 --- a/packages/app/src/System/RequestBuilder.ts +++ b/packages/app/src/System/RequestBuilder.ts @@ -1,4 +1,4 @@ -import { RawReqFilter, u256, HexKey, EventKind } from "@snort/nostr"; +import { RawReqFilter, u256, HexKey, EventKind } from "System"; import { appendDedupe, dedupe } from "SnortUtils"; import { QueryBase } from "./Query"; import { diffFilters } from "./RequestSplitter"; @@ -245,7 +245,7 @@ export class RequestFilterBuilder { return [ { filters: [this.filter], - relay: "*", + relay: "", strategy: RequestStrategy.DefaultRelays, }, ]; diff --git a/packages/app/src/System/RequestMerger.test.ts b/packages/app/src/System/RequestMerger.test.ts index a3b308a6..4eac9ab7 100644 --- a/packages/app/src/System/RequestMerger.test.ts +++ b/packages/app/src/System/RequestMerger.test.ts @@ -1,5 +1,5 @@ -import { RawReqFilter } from "@snort/nostr"; -import { mergeSimilar } from "./RequestMerger"; +import { RawReqFilter } from "System"; +import { filterIncludes, mergeSimilar } from "./RequestMerger"; describe("RequestMerger", () => { it("should simple merge authors", () => { @@ -41,4 +41,16 @@ describe("RequestMerger", () => { }, ]); }); + + it("filterIncludes", () => { + const bigger = { + authors: ["a", "b", "c"], + since: 99, + } as RawReqFilter; + const smaller = { + authors: ["c"], + since: 100, + } as RawReqFilter; + expect(filterIncludes(bigger, smaller)).toBe(true); + }); }); diff --git a/packages/app/src/System/RequestMerger.ts b/packages/app/src/System/RequestMerger.ts index de12b992..79cfe1e8 100644 --- a/packages/app/src/System/RequestMerger.ts +++ b/packages/app/src/System/RequestMerger.ts @@ -1,4 +1,4 @@ -import { RawReqFilter } from "@snort/nostr"; +import { RawReqFilter } from "System"; export function mergeSimilar(filters: Array): Array { const hasCriticalKeySet = (a: RawReqFilter) => { @@ -28,3 +28,31 @@ function simpleMerge(filters: Array) { return result as RawReqFilter; } + +/** + * Check if a filter includes another filter, as in the bigger filter will include the same results as the samller filter + * @param bigger + * @param smaller + * @returns + */ +export function filterIncludes(bigger: RawReqFilter, smaller: RawReqFilter) { + const outside = bigger as Record | number>; + for (const [k, v] of Object.entries(smaller)) { + if (outside[k] === undefined) { + return false; + } + if (Array.isArray(v) && v.some(a => !(outside[k] as Array).includes(a))) { + return false; + } + if (typeof v === "number") { + if (k === "since" && (outside[k] as number) > v) { + return false; + } + if (k === "until" && (outside[k] as number) < v) { + return false; + } + // limit cannot be checked and is ignored + } + } + return true; +} diff --git a/packages/app/src/System/RequestSplitter.test.ts b/packages/app/src/System/RequestSplitter.test.ts index 49c2d0cb..979a0330 100644 --- a/packages/app/src/System/RequestSplitter.test.ts +++ b/packages/app/src/System/RequestSplitter.test.ts @@ -1,4 +1,4 @@ -import { RawReqFilter } from "@snort/nostr"; +import { RawReqFilter } from "System"; import { describe, expect } from "@jest/globals"; import { diffFilters } from "./RequestSplitter"; diff --git a/packages/app/src/System/RequestSplitter.ts b/packages/app/src/System/RequestSplitter.ts index 69fb17fe..44fc2f91 100644 --- a/packages/app/src/System/RequestSplitter.ts +++ b/packages/app/src/System/RequestSplitter.ts @@ -1,4 +1,4 @@ -import { RawReqFilter } from "@snort/nostr"; +import { RawReqFilter } from "System"; // Critical keys changing means the entire filter has changed export const CriticalKeys = ["since", "until", "limit"]; @@ -19,7 +19,7 @@ export function diffFilters(a: Array, b: Array) { const thisArray = v as Array; const added = thisArray.filter(a => !prevArray?.includes(a)); // support adding new values to array, removing values is ignored since we only care about getting new values - result[i] = { ...result[i], [k]: added.length === 0 ? prevArray ?? thisArray : added }; + result[i] = { ...result[i], [k]: added.length === 0 ? prevArray ?? [] : added }; if (added.length > 0) { anyChanged = true; } diff --git a/packages/app/src/System/Tag.ts b/packages/app/src/System/Tag.ts new file mode 100644 index 00000000..a006be49 --- /dev/null +++ b/packages/app/src/System/Tag.ts @@ -0,0 +1,88 @@ +import { HexKey, u256 } from "./Nostr"; +import { unwrap } from "./Util"; + +export default class Tag { + Original: string[]; + Key: string; + Event?: u256; + PubKey?: HexKey; + Relay?: string; + Marker?: string; + Hashtag?: string; + DTag?: string; + ATag?: string; + Index: number; + Invalid: boolean; + LNURL?: string; + + constructor(tag: string[], index: number) { + this.Original = tag; + this.Key = tag[0]; + this.Index = index; + this.Invalid = false; + + switch (this.Key) { + case "e": { + // ["e", , , ] + this.Event = tag[1]; + this.Relay = tag.length > 2 ? tag[2] : undefined; + this.Marker = tag.length > 3 ? tag[3] : undefined; + if (!this.Event) { + this.Invalid = true; + } + break; + } + case "p": { + // ["p", ] + this.PubKey = tag[1]; + if (!this.PubKey) { + this.Invalid = true; + } + break; + } + case "d": { + this.DTag = tag[1]; + break; + } + case "a": { + this.ATag = tag[1]; + break; + } + case "t": { + this.Hashtag = tag[1]; + break; + } + case "delegation": { + this.PubKey = tag[1]; + break; + } + case "zap": { + this.LNURL = tag[1]; + break; + } + } + } + + ToObject(): string[] | null { + switch (this.Key) { + case "e": { + let ret = ["e", this.Event, this.Relay, this.Marker]; + const trimEnd = ret.reverse().findIndex(a => a !== undefined); + ret = ret.reverse().slice(0, ret.length - trimEnd); + return ret; + } + case "p": { + return this.PubKey ? ["p", this.PubKey] : null; + } + case "t": { + return ["t", unwrap(this.Hashtag)]; + } + case "d": { + return ["d", unwrap(this.DTag)]; + } + default: { + return this.Original; + } + } + } +} diff --git a/packages/app/src/System/Util.ts b/packages/app/src/System/Util.ts new file mode 100644 index 00000000..6d50c763 --- /dev/null +++ b/packages/app/src/System/Util.ts @@ -0,0 +1,34 @@ +import * as utils from "@noble/curves/abstract/utils"; +import { bech32 } from "bech32"; + +export function unwrap(v: T | undefined | null): T { + if (v === undefined || v === null) { + throw new Error("missing value"); + } + return v; +} + +/** + * Convert hex to bech32 + */ +export function hexToBech32(hrp: string, hex?: string) { + if (typeof hex !== "string" || hex.length === 0 || hex.length % 2 !== 0) { + return ""; + } + + try { + const buf = utils.hexToBytes(hex); + return bech32.encode(hrp, bech32.toWords(buf)); + } catch (e) { + console.warn("Invalid hex", hex, e); + return ""; + } +} + +export function sanitizeRelayUrl(url: string) { + try { + return new URL(url).toString(); + } catch { + // ignore + } +} diff --git a/packages/app/src/System/index.ts b/packages/app/src/System/index.ts index 239321c5..f6e75275 100644 --- a/packages/app/src/System/index.ts +++ b/packages/app/src/System/index.ts @@ -1,7 +1,8 @@ -import { AuthHandler, TaggedRawEvent, RelaySettings, Connection, RawReqFilter, RawEvent } from "@snort/nostr"; import debug from "debug"; import { sanitizeRelayUrl, unixNowMs, unwrap } from "SnortUtils"; +import { RawEvent, RawReqFilter, TaggedRawEvent } from "./Nostr"; +import { AuthHandler, Connection, RelaySettings, StateSnapshot } from "./Connection"; import { RequestBuilder } from "./RequestBuilder"; import { EventBuilder } from "./EventBuilder"; import { @@ -11,11 +12,16 @@ import { ParameterizedReplaceableNoteStore, ReplaceableNoteStore, } from "./NoteCollection"; -import { diffFilters } from "./RequestSplitter"; import { Query, QueryBase } from "./Query"; -import { splitAllByWriteRelays } from "./GossipModel"; import ExternalStore from "ExternalStore"; -import { UserRelays } from "Cache/UserRelayCache"; +import { RelayCache } from "./GossipModel"; + +export { default as EventKind } from "./EventKind"; +export * from "./Nostr"; +export * from "./Links"; +export { default as Tag } from "./Tag"; +export * from "./Nips"; +export * from "./RelayInfo"; export { NoteStore, @@ -26,6 +32,10 @@ export { ReplaceableNoteStore, Query, EventBuilder, + AuthHandler, + Connection, + RelaySettings, + StateSnapshot, }; export interface SystemSnapshot { @@ -60,13 +70,12 @@ export class NostrSystem extends ExternalStore { HandleAuth?: AuthHandler; #log = debug("System"); - #relayCache = { - get: (pk?: string) => UserRelays.getFromCache(pk)?.relays, - }; + #relayCache: RelayCache; - constructor() { + constructor(relayCache: RelayCache) { super(); this.Sockets = new Map(); + this.#relayCache = relayCache; this.#cleanup(); } @@ -202,15 +211,11 @@ export class NostrSystem extends ExternalStore { return existing.feed as Readonly; } else { for (const subQ of filters) { - this.SendQuery( - existing, - { - id: `${existing.id}-${existing.subQueryCounter++}`, - filters: subQ.filters, - relays: [subQ.relay], - }, - (q, s, c) => q.sendToRelay(c, s) - ); + this.SendQuery(existing, { + id: `${existing.id}-${existing.subQueryCounter++}`, + filters: subQ.filters, + relays: subQ.relay ? [subQ.relay] : undefined, + }); } this.notifyChange(); return existing.feed as Readonly; @@ -226,15 +231,11 @@ export class NostrSystem extends ExternalStore { this.Queries.set(req.id, q); for (const subQ of filters) { - this.SendQuery( - q, - { - id: `${q.id}-${q.subQueryCounter++}`, - filters: subQ.filters, - relays: [subQ.relay], - }, - (q, s, c) => q.sendToRelay(c, s) - ); + this.SendQuery(q, { + id: `${q.id}-${q.subQueryCounter++}`, + filters: subQ.filters, + relays: subQ.relay ? [subQ.relay] : undefined, + }); } this.notifyChange(); return q.feed as Readonly; @@ -248,17 +249,17 @@ export class NostrSystem extends ExternalStore { } } - async SendQuery(q: Query, qSend: QueryBase, qSender: (q: Query, qSend: QueryBase, c: Connection) => void) { + async SendQuery(q: Query, qSend: QueryBase) { if (qSend.relays && qSend.relays.length > 0) { for (const r of qSend.relays) { this.#log("Sending query to %s %O", r, qSend); const s = this.Sockets.get(r); if (s) { - qSender(q, qSend, s); + q.sendToRelay(s, qSend); } else { const nc = await this.ConnectEphemeralRelay(r); if (nc) { - qSender(q, qSend, nc); + q.sendToRelay(nc, qSend); } else { console.warn("Failed to connect to new relay for:", r, q); } @@ -267,7 +268,7 @@ export class NostrSystem extends ExternalStore { } else { for (const [, s] of this.Sockets) { if (!s.Ephemeral) { - qSender(q, qSend, s); + q.sendToRelay(s, qSend); } } } @@ -329,5 +330,3 @@ export class NostrSystem extends ExternalStore { setTimeout(() => this.#cleanup(), 1_000); } } - -export const System = new NostrSystem(); diff --git a/packages/app/src/Upload/VoidCat.ts b/packages/app/src/Upload/VoidCat.ts index 60a46952..269d57cc 100644 --- a/packages/app/src/Upload/VoidCat.ts +++ b/packages/app/src/Upload/VoidCat.ts @@ -1,4 +1,4 @@ -import { EventKind } from "@snort/nostr"; +import { EventKind } from "System"; import { VoidApi } from "@void-cat/api"; import { FileExtensionRegex, VoidCatHost } from "Const"; diff --git a/packages/app/src/Upload/index.ts b/packages/app/src/Upload/index.ts index 623f0ad4..9ea2cbcc 100644 --- a/packages/app/src/Upload/index.ts +++ b/packages/app/src/Upload/index.ts @@ -1,5 +1,5 @@ import useLogin from "Hooks/useLogin"; -import { RawEvent } from "@snort/nostr"; +import { RawEvent } from "System"; import NostrBuild from "Upload/NostrBuild"; import VoidCat from "Upload/VoidCat"; diff --git a/packages/app/src/Wallet/NostrWalletConnect.ts b/packages/app/src/Wallet/NostrWalletConnect.ts index 5979b957..b447e81d 100644 --- a/packages/app/src/Wallet/NostrWalletConnect.ts +++ b/packages/app/src/Wallet/NostrWalletConnect.ts @@ -1,4 +1,4 @@ -import { Connection, EventKind, RawEvent } from "@snort/nostr"; +import { Connection, EventKind, RawEvent } from "System"; import { EventBuilder } from "System"; import { EventExt } from "System/EventExt"; import { LNWallet, WalletError, WalletErrorCode, WalletInfo, WalletInvoice, WalletInvoiceState } from "Wallet"; diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index 9ab1b151..3561d7e8 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -35,7 +35,21 @@ import DebugPage from "Pages/Debug"; import { db } from "Db"; import { preload } from "Cache"; import { LoginStore } from "Login"; -import { System } from "System"; +import { UserRelays } from "Cache/UserRelayCache"; +import { NostrSystem } from "System"; +import { ProfileLoaderService } from "System/ProfileCache"; + +/** + * Singleton nostr system + */ +export const System = new NostrSystem({ + get: pk => UserRelays.getFromCache(pk)?.relays, +}); + +/** + * Singleton user profile loader + */ +export const ProfileLoader = new ProfileLoaderService(System); // @ts-expect-error Setting webpack nonce window.__webpack_nonce__ = "ZmlhdGphZiBzYWlkIHNub3J0LnNvY2lhbCBpcyBwcmV0dHkgZ29vZCwgd2UgbWFkZSBpdCE="; From 86ec7f41d7ca4be1f0b0480525a1bcda3fbed080 Mon Sep 17 00:00:00 2001 From: Kieran Date: Tue, 30 May 2023 14:50:30 +0100 Subject: [PATCH 14/24] delete legacy code from nostr pkg --- packages/nostr/src/index.ts | 1 - packages/nostr/src/legacy/.prettierrc | 1 - packages/nostr/src/legacy/Connection.ts | 463 ------------------- packages/nostr/src/legacy/ConnectionStats.ts | 34 -- packages/nostr/src/legacy/Const.ts | 4 - packages/nostr/src/legacy/EventKind.ts | 29 -- packages/nostr/src/legacy/Links.ts | 88 ---- packages/nostr/src/legacy/Nips.ts | 3 - packages/nostr/src/legacy/RelayInfo.ts | 16 - packages/nostr/src/legacy/Tag.ts | 88 ---- packages/nostr/src/legacy/Util.ts | 34 -- packages/nostr/src/legacy/index.ts | 89 ---- packages/nostr/test/setup.ts | 3 +- 13 files changed, 1 insertion(+), 852 deletions(-) delete mode 100644 packages/nostr/src/legacy/.prettierrc delete mode 100644 packages/nostr/src/legacy/Connection.ts delete mode 100644 packages/nostr/src/legacy/ConnectionStats.ts delete mode 100644 packages/nostr/src/legacy/Const.ts delete mode 100644 packages/nostr/src/legacy/EventKind.ts delete mode 100644 packages/nostr/src/legacy/Links.ts delete mode 100644 packages/nostr/src/legacy/Nips.ts delete mode 100644 packages/nostr/src/legacy/RelayInfo.ts delete mode 100644 packages/nostr/src/legacy/Tag.ts delete mode 100644 packages/nostr/src/legacy/Util.ts delete mode 100644 packages/nostr/src/legacy/index.ts diff --git a/packages/nostr/src/index.ts b/packages/nostr/src/index.ts index 2e897582..5a4d885e 100644 --- a/packages/nostr/src/index.ts +++ b/packages/nostr/src/index.ts @@ -1,4 +1,3 @@ -export * from "./legacy" import "./nostr-object" // TODO This file should only contain re-exports and only re-export what is needed diff --git a/packages/nostr/src/legacy/.prettierrc b/packages/nostr/src/legacy/.prettierrc deleted file mode 100644 index 0967ef42..00000000 --- a/packages/nostr/src/legacy/.prettierrc +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/nostr/src/legacy/Connection.ts b/packages/nostr/src/legacy/Connection.ts deleted file mode 100644 index c9eff0c9..00000000 --- a/packages/nostr/src/legacy/Connection.ts +++ /dev/null @@ -1,463 +0,0 @@ -import { v4 as uuid } from "uuid"; - -import { DefaultConnectTimeout } from "./Const"; -import { ConnectionStats } from "./ConnectionStats"; -import { RawEvent, ReqCommand, TaggedRawEvent, u256 } from "./index"; -import { RelayInfo } from "./RelayInfo"; -import { unwrap } from "./Util"; - -export type CustomHook = (state: Readonly) => void; -export type AuthHandler = ( - challenge: string, - relay: string -) => Promise; - -/** - * Relay settings - */ -export interface RelaySettings { - read: boolean; - write: boolean; -} - -/** - * Snapshot of connection stats - */ -export interface StateSnapshot { - connected: boolean; - disconnects: number; - avgLatency: number; - events: { - received: number; - send: number; - }; - info?: RelayInfo; - pendingRequests: Array; - activeRequests: Array; - id: string; -} - -export class Connection { - Id: string; - Address: string; - Socket: WebSocket | null = null; - - PendingRaw: Array = []; - PendingRequests: Array<{ - cmd: ReqCommand, - cb: () => void - }> = []; - ActiveRequests = new Set(); - - Settings: RelaySettings; - Info?: RelayInfo; - ConnectTimeout: number = DefaultConnectTimeout; - Stats: ConnectionStats = new ConnectionStats(); - StateHooks: Map = new Map(); - HasStateChange: boolean = true; - CurrentState: StateSnapshot; - LastState: Readonly; - IsClosed: boolean; - ReconnectTimer: ReturnType | null; - EventsCallback: Map void>; - OnConnected?: () => void; - OnEvent?: (sub: string, e: TaggedRawEvent) => void; - OnEose?: (sub: string) => void; - OnDisconnect?: (id: string) => void; - Auth?: AuthHandler; - AwaitingAuth: Map; - Authed = false; - Ephemeral: boolean; - EphemeralTimeout: ReturnType | undefined; - Down = true; - - constructor( - addr: string, - options: RelaySettings, - auth?: AuthHandler, - ephemeral: boolean = false - ) { - this.Id = uuid(); - this.Address = addr; - this.Settings = options; - this.CurrentState = { - connected: false, - disconnects: 0, - avgLatency: 0, - events: { - received: 0, - send: 0, - }, - } as StateSnapshot; - this.LastState = Object.freeze({ ...this.CurrentState }); - this.IsClosed = false; - this.ReconnectTimer = null; - this.EventsCallback = new Map(); - this.AwaitingAuth = new Map(); - this.Auth = auth; - this.Ephemeral = ephemeral; - } - - ResetEphemeralTimeout() { - if (this.EphemeralTimeout) { - clearTimeout(this.EphemeralTimeout); - } - if (this.Ephemeral) { - this.EphemeralTimeout = setTimeout(() => { - this.Close(); - }, 30_000); - } - } - - async Connect() { - try { - if (this.Info === undefined) { - const u = new URL(this.Address); - const rsp = await fetch( - `${u.protocol === "wss:" ? "https:" : "http:"}//${u.host}`, - { - headers: { - accept: "application/nostr+json", - }, - } - ); - if (rsp.ok) { - const data = await rsp.json(); - for (const [k, v] of Object.entries(data)) { - if (v === "unset" || v === "" || v === "~") { - data[k] = undefined; - } - } - this.Info = data; - } - } - } catch (e) { - console.warn("Could not load relay information", e); - } - - if (this.Socket) { - this.Id = uuid(); - this.Socket.onopen = null; - this.Socket.onmessage = null; - this.Socket.onerror = null; - this.Socket.onclose = null; - } - this.IsClosed = false; - this.Socket = new WebSocket(this.Address); - this.Socket.onopen = () => this.OnOpen(); - this.Socket.onmessage = (e) => this.OnMessage(e); - this.Socket.onerror = (e) => this.OnError(e); - this.Socket.onclose = (e) => this.OnClose(e); - } - - Close() { - this.IsClosed = true; - if (this.ReconnectTimer !== null) { - clearTimeout(this.ReconnectTimer); - this.ReconnectTimer = null; - } - this.Socket?.close(); - this.#UpdateState(); - } - - OnOpen() { - this.ConnectTimeout = DefaultConnectTimeout; - console.log(`[${this.Address}] Open!`); - this.Down = false; - if (this.Ephemeral) { - this.ResetEphemeralTimeout(); - } - this.OnConnected?.(); - this.#sendPendingRaw(); - } - - OnClose(e: CloseEvent) { - if (!this.IsClosed) { - this.ConnectTimeout = this.ConnectTimeout * 2; - console.log( - `[${this.Address}] Closed (${e.reason}), trying again in ${( - this.ConnectTimeout / 1000 - ) - .toFixed(0) - .toLocaleString()} sec` - ); - this.ReconnectTimer = setTimeout(() => { - this.Connect(); - }, this.ConnectTimeout); - this.Stats.Disconnects++; - } else { - console.log(`[${this.Address}] Closed!`); - this.ReconnectTimer = null; - } - - this.OnDisconnect?.(this.Id); - this.#ResetQueues(); - // reset connection Id on disconnect, for query-tracking - this.Id = uuid(); - this.#UpdateState(); - } - - OnMessage(e: MessageEvent) { - if (e.data.length > 0) { - const msg = JSON.parse(e.data); - const tag = msg[0]; - switch (tag) { - case "AUTH": { - this._OnAuthAsync(msg[1]) - .then(() => this.#sendPendingRaw()) - .catch(console.error); - this.Stats.EventsReceived++; - this.#UpdateState(); - break; - } - case "EVENT": { - this.OnEvent?.(msg[1], { - ...msg[2], - relays: [this.Address] - }); - this.Stats.EventsReceived++; - this.#UpdateState(); - break; - } - case "EOSE": { - this.OnEose?.(msg[1]); - break; - } - case "OK": { - // feedback to broadcast call - console.debug(`${this.Address} OK: `, msg); - const id = msg[1]; - if (this.EventsCallback.has(id)) { - const cb = unwrap(this.EventsCallback.get(id)); - this.EventsCallback.delete(id); - cb(msg); - } - break; - } - case "NOTICE": { - console.warn(`[${this.Address}] NOTICE: ${msg[1]}`); - break; - } - default: { - console.warn(`Unknown tag: ${tag}`); - break; - } - } - } - } - - OnError(e: Event) { - console.error(e); - this.#UpdateState(); - } - - /** - * Send event on this connection - */ - SendEvent(e: RawEvent) { - if (!this.Settings.write) { - return; - } - const req = ["EVENT", e]; - this.#SendJson(req); - this.Stats.EventsSent++; - this.#UpdateState(); - } - - /** - * Send event on this connection and wait for OK response - */ - async SendAsync(e: RawEvent, timeout = 5000) { - return new Promise((resolve) => { - if (!this.Settings.write) { - resolve(); - return; - } - const t = setTimeout(() => { - resolve(); - }, timeout); - this.EventsCallback.set(e.id, () => { - clearTimeout(t); - resolve(); - }); - - const req = ["EVENT", e]; - this.#SendJson(req); - this.Stats.EventsSent++; - this.#UpdateState(); - }); - } - - /** - * Hook status for connection - */ - StatusHook(fnHook: CustomHook) { - const id = uuid(); - this.StateHooks.set(id, fnHook); - return () => { - this.StateHooks.delete(id); - }; - } - - /** - * Returns the current state of this connection - */ - GetState() { - if (this.HasStateChange) { - this.LastState = Object.freeze({ ...this.CurrentState }); - this.HasStateChange = false; - } - return this.LastState; - } - - /** - * Using relay document to determine if this relay supports a feature - */ - SupportsNip(n: number) { - return this.Info?.supported_nips?.some((a) => a === n) ?? false; - } - - /** - * Queue or send command to the relay - * @param cmd The REQ to send to the server - */ - QueueReq(cmd: ReqCommand, cbSent: () => void) { - if (this.ActiveRequests.size >= this.#maxSubscriptions) { - this.PendingRequests.push({ - cmd, cb: cbSent - }); - console.debug("Queuing:", this.Address, cmd); - } else { - this.ActiveRequests.add(cmd[1]); - this.#SendJson(cmd); - cbSent(); - } - this.#UpdateState(); - } - - CloseReq(id: string) { - if (this.ActiveRequests.delete(id)) { - this.#SendJson(["CLOSE", id]); - this.OnEose?.(id); - this.#SendQueuedRequests(); - } - this.#UpdateState(); - } - - #SendQueuedRequests() { - const canSend = this.#maxSubscriptions - this.ActiveRequests.size; - if (canSend > 0) { - for (let x = 0; x < canSend; x++) { - const p = this.PendingRequests.shift(); - if (p) { - this.ActiveRequests.add(p.cmd[1]); - this.#SendJson(p.cmd); - p.cb(); - console.debug("Sent pending REQ", this.Address, p.cmd); - } - } - } - } - - #ResetQueues() { - this.ActiveRequests.clear(); - this.PendingRequests = []; - this.PendingRaw = []; - this.#UpdateState(); - } - - #UpdateState() { - this.CurrentState.connected = this.Socket?.readyState === WebSocket.OPEN; - this.CurrentState.events.received = this.Stats.EventsReceived; - this.CurrentState.events.send = this.Stats.EventsSent; - this.CurrentState.avgLatency = - this.Stats.Latency.length > 0 - ? this.Stats.Latency.reduce((acc, v) => acc + v, 0) / - this.Stats.Latency.length - : 0; - this.CurrentState.disconnects = this.Stats.Disconnects; - this.CurrentState.info = this.Info; - this.CurrentState.id = this.Id; - this.CurrentState.pendingRequests = [...this.PendingRequests.map(a => a.cmd[1])]; - this.CurrentState.activeRequests = [...this.ActiveRequests]; - this.Stats.Latency = this.Stats.Latency.slice(-20); // trim - this.HasStateChange = true; - this.#NotifyState(); - } - - #NotifyState() { - const state = this.GetState(); - for (const [, h] of this.StateHooks) { - h(state); - } - } - - #SendJson(obj: object) { - const authPending = !this.Authed && (this.AwaitingAuth.size > 0 || this.Info?.limitation?.auth_required === true); - if (this.Socket?.readyState !== WebSocket.OPEN || authPending) { - this.PendingRaw.push(obj); - if (this.Socket?.readyState === WebSocket.CLOSED && this.Ephemeral && this.IsClosed) { - this.Connect() - } - return false; - } - - this.#sendPendingRaw(); - this.#sendOnWire(obj); - } - - #sendPendingRaw() { - while (this.PendingRaw.length > 0) { - const next = this.PendingRaw.shift(); - if (next) { - this.#sendOnWire(next); - } - } - } - - #sendOnWire(obj: unknown) { - if (this.Socket?.readyState !== WebSocket.OPEN) { - throw new Error(`Socket is not open, state is ${this.Socket?.readyState}`); - } - const json = JSON.stringify(obj); - this.Socket.send(json); - return true; - } - - async _OnAuthAsync(challenge: string): Promise { - const authCleanup = () => { - this.AwaitingAuth.delete(challenge); - }; - if (!this.Auth) { - throw new Error("Auth hook not registered"); - } - this.AwaitingAuth.set(challenge, true); - const authEvent = await this.Auth(challenge, this.Address); - return new Promise((resolve) => { - if (!authEvent) { - authCleanup(); - return Promise.reject("no event"); - } - - const t = setTimeout(() => { - authCleanup(); - resolve(); - }, 10_000); - - this.EventsCallback.set(authEvent.id, (msg: boolean[]) => { - clearTimeout(t); - authCleanup(); - if (msg.length > 3 && msg[2] === true) { - this.Authed = true; - } - resolve(); - }); - - this.#sendOnWire(["AUTH", authEvent]); - }); - } - - get #maxSubscriptions() { - return this.Info?.limitation?.max_subscriptions ?? 25; - } -} diff --git a/packages/nostr/src/legacy/ConnectionStats.ts b/packages/nostr/src/legacy/ConnectionStats.ts deleted file mode 100644 index d1248f2d..00000000 --- a/packages/nostr/src/legacy/ConnectionStats.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Stats class for tracking metrics per connection - */ -export class ConnectionStats { - /** - * Last n records of how long between REQ->EOSE - */ - Latency: number[] = []; - - /** - * Total number of REQ's sent on this connection - */ - Subs: number = 0; - - /** - * Count of REQ which took too long and where abandoned - */ - SubsTimeout: number = 0; - - /** - * Total number of EVENT messages received - */ - EventsReceived: number = 0; - - /** - * Total number of EVENT messages sent - */ - EventsSent: number = 0; - - /** - * Total number of times this connection was lost - */ - Disconnects: number = 0; -} diff --git a/packages/nostr/src/legacy/Const.ts b/packages/nostr/src/legacy/Const.ts deleted file mode 100644 index e3502ce0..00000000 --- a/packages/nostr/src/legacy/Const.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Websocket re-connect timeout - */ -export const DefaultConnectTimeout = 2000; diff --git a/packages/nostr/src/legacy/EventKind.ts b/packages/nostr/src/legacy/EventKind.ts deleted file mode 100644 index 512a1f0a..00000000 --- a/packages/nostr/src/legacy/EventKind.ts +++ /dev/null @@ -1,29 +0,0 @@ -enum EventKind { - Unknown = -1, - SetMetadata = 0, - TextNote = 1, - RecommendServer = 2, - ContactList = 3, // NIP-02 - DirectMessage = 4, // NIP-04 - Deletion = 5, // NIP-09 - Repost = 6, // NIP-18 - Reaction = 7, // NIP-25 - BadgeAward = 8, // NIP-58 - SnortSubscriptions = 1000, // NIP-XX - Polls = 6969, // NIP-69 - FileHeader = 1063, // NIP-94 - Relays = 10002, // NIP-65 - Ephemeral = 20_000, - Auth = 22242, // NIP-42 - PubkeyLists = 30000, // NIP-51a - NoteLists = 30001, // NIP-51b - TagLists = 30002, // NIP-51c - Badge = 30009, // NIP-58 - ProfileBadges = 30008, // NIP-58 - ZapstrTrack = 31337, - ZapRequest = 9734, // NIP 57 - ZapReceipt = 9735, // NIP 57 - HttpAuthentication = 27235, // NIP XX - HTTP Authentication -} - -export default EventKind; diff --git a/packages/nostr/src/legacy/Links.ts b/packages/nostr/src/legacy/Links.ts deleted file mode 100644 index 8f3d4a35..00000000 --- a/packages/nostr/src/legacy/Links.ts +++ /dev/null @@ -1,88 +0,0 @@ -import * as utils from "@noble/curves/abstract/utils"; -import { bech32 } from "bech32"; -import { HexKey } from "."; - -export enum NostrPrefix { - PublicKey = "npub", - PrivateKey = "nsec", - Note = "note", - - // TLV prefixes - Profile = "nprofile", - Event = "nevent", - Relay = "nrelay", - Address = "naddr", -} - -export enum TLVEntryType { - Special = 0, - Relay = 1, - Author = 2, - Kind = 3, -} - -export interface TLVEntry { - type: TLVEntryType; - length: number; - value: string | HexKey | number; -} - -export function encodeTLV(prefix: NostrPrefix, id: string, relays?: string[], kind?: number, author?: string) { - const enc = new TextEncoder(); - const buf = prefix === NostrPrefix.Address ? enc.encode(id) : utils.hexToBytes(id); - - const tl0 = [0, buf.length, ...buf]; - const tl1 = - relays - ?.map((a) => { - const data = enc.encode(a); - return [1, data.length, ...data]; - }) - .flat() ?? []; - - const tl2 = author ? [2, 32, ...utils.hexToBytes(author)] : []; - const tl3 = kind ? [3, 4, ...new Uint8Array(new Uint32Array([kind]).buffer).reverse()] : [] - - return bech32.encode(prefix, bech32.toWords([...tl0, ...tl1, ...tl2, ...tl3]), 1_000); -} - -export function decodeTLV(str: string) { - const decoded = bech32.decode(str, 1_000); - const data = bech32.fromWords(decoded.words); - - const entries: TLVEntry[] = []; - let x = 0; - while (x < data.length) { - const t = data[x]; - const l = data[x + 1]; - const v = data.slice(x + 2, x + 2 + l); - entries.push({ - type: t, - length: l, - value: decodeTLVEntry(t, decoded.prefix, new Uint8Array(v)), - }); - x += 2 + l; - } - return entries; -} - -function decodeTLVEntry(type: TLVEntryType, prefix: string, data: Uint8Array) { - switch (type) { - case TLVEntryType.Special: { - if (prefix === NostrPrefix.Address) { - return new TextDecoder("ASCII").decode(data); - } else { - return utils.bytesToHex(data); - } - } - case TLVEntryType.Author: { - return utils.bytesToHex(data); - } - case TLVEntryType.Kind: { - return new Uint32Array(new Uint8Array(data.reverse()).buffer)[0]; - } - case TLVEntryType.Relay: { - return new TextDecoder("ASCII").decode(data); - } - } -} diff --git a/packages/nostr/src/legacy/Nips.ts b/packages/nostr/src/legacy/Nips.ts deleted file mode 100644 index decdfa3c..00000000 --- a/packages/nostr/src/legacy/Nips.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum Nips { - Search = 50, -} diff --git a/packages/nostr/src/legacy/RelayInfo.ts b/packages/nostr/src/legacy/RelayInfo.ts deleted file mode 100644 index 438fe41f..00000000 --- a/packages/nostr/src/legacy/RelayInfo.ts +++ /dev/null @@ -1,16 +0,0 @@ -export interface RelayInfo { - name?: string; - description?: string; - pubkey?: string; - contact?: string; - supported_nips?: number[]; - software?: string; - version?: string; - limitation?: { - payment_required: boolean; - max_subscriptions: number; - max_filters: number; - max_event_tags: number; - auth_required: boolean - }; -} diff --git a/packages/nostr/src/legacy/Tag.ts b/packages/nostr/src/legacy/Tag.ts deleted file mode 100644 index b3ecfb98..00000000 --- a/packages/nostr/src/legacy/Tag.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { HexKey, u256 } from "./index"; -import { unwrap } from "./Util"; - -export default class Tag { - Original: string[]; - Key: string; - Event?: u256; - PubKey?: HexKey; - Relay?: string; - Marker?: string; - Hashtag?: string; - DTag?: string; - ATag?: string; - Index: number; - Invalid: boolean; - LNURL?: string; - - constructor(tag: string[], index: number) { - this.Original = tag; - this.Key = tag[0]; - this.Index = index; - this.Invalid = false; - - switch (this.Key) { - case "e": { - // ["e", , , ] - this.Event = tag[1]; - this.Relay = tag.length > 2 ? tag[2] : undefined; - this.Marker = tag.length > 3 ? tag[3] : undefined; - if (!this.Event) { - this.Invalid = true; - } - break; - } - case "p": { - // ["p", ] - this.PubKey = tag[1]; - if (!this.PubKey) { - this.Invalid = true; - } - break; - } - case "d": { - this.DTag = tag[1]; - break; - } - case "a": { - this.ATag = tag[1]; - break; - } - case "t": { - this.Hashtag = tag[1]; - break; - } - case "delegation": { - this.PubKey = tag[1]; - break; - } - case "zap": { - this.LNURL = tag[1]; - break; - } - } - } - - ToObject(): string[] | null { - switch (this.Key) { - case "e": { - let ret = ["e", this.Event, this.Relay, this.Marker]; - const trimEnd = ret.reverse().findIndex((a) => a !== undefined); - ret = ret.reverse().slice(0, ret.length - trimEnd); - return ret; - } - case "p": { - return this.PubKey ? ["p", this.PubKey] : null; - } - case "t": { - return ["t", unwrap(this.Hashtag)]; - } - case "d": { - return ["d", unwrap(this.DTag)]; - } - default: { - return this.Original; - } - } - } -} diff --git a/packages/nostr/src/legacy/Util.ts b/packages/nostr/src/legacy/Util.ts deleted file mode 100644 index dcc7147c..00000000 --- a/packages/nostr/src/legacy/Util.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as utils from "@noble/curves/abstract/utils"; -import { bech32 } from "bech32"; - -export function unwrap(v: T | undefined | null): T { - if (v === undefined || v === null) { - throw new Error("missing value"); - } - return v; -} - -/** - * Convert hex to bech32 - */ -export function hexToBech32(hrp: string, hex?: string) { - if (typeof hex !== "string" || hex.length === 0 || hex.length % 2 !== 0) { - return ""; - } - - try { - const buf = utils.hexToBytes(hex); - return bech32.encode(hrp, bech32.toWords(buf)); - } catch (e) { - console.warn("Invalid hex", hex, e); - return ""; - } -} - -export function sanitizeRelayUrl(url: string) { - try { - return new URL(url).toString(); - } catch { - // ignore - } -} \ No newline at end of file diff --git a/packages/nostr/src/legacy/index.ts b/packages/nostr/src/legacy/index.ts deleted file mode 100644 index 61f9af03..00000000 --- a/packages/nostr/src/legacy/index.ts +++ /dev/null @@ -1,89 +0,0 @@ -export * from "./Connection"; -export { default as EventKind } from "./EventKind"; -export { default as Tag } from "./Tag"; -export * from "./Links"; -export * from "./Nips"; - -import { RelaySettings } from "."; -export type RawEvent = { - id: u256; - pubkey: HexKey; - created_at: number; - kind: number; - tags: Array>; - content: string; - sig: string; -}; - -export interface TaggedRawEvent extends RawEvent { - /** - * A list of relays this event was seen on - */ - relays: string[]; -} - -/** - * Basic raw key as hex - */ -export type HexKey = string; - -/** - * Optional HexKey - */ -export type MaybeHexKey = HexKey | undefined; - -/** - * A 256bit hex id - */ -export type u256 = string; - -export type ReqCommand = [cmd: "REQ", id: string, ...filters: Array]; - -/** - * Raw REQ filter object - */ -export type RawReqFilter = { - ids?: u256[]; - authors?: u256[]; - kinds?: number[]; - "#e"?: u256[]; - "#p"?: u256[]; - "#t"?: string[]; - "#d"?: string[]; - "#r"?: string[]; - search?: string; - since?: number; - until?: number; - limit?: number; -}; - -/** - * Medatadata event content - */ -export type UserMetadata = { - name?: string; - display_name?: string; - about?: string; - picture?: string; - website?: string; - banner?: string; - nip05?: string; - lud06?: string; - lud16?: string; -}; - -/** - * NIP-51 list types - */ -export enum Lists { - Muted = "mute", - Pinned = "pin", - Bookmarked = "bookmark", - Followed = "follow", - Badges = "profile_badges", -} - -export interface FullRelaySettings { - url: string; - settings: RelaySettings; -} diff --git a/packages/nostr/test/setup.ts b/packages/nostr/test/setup.ts index 0d096bbc..2a079c77 100644 --- a/packages/nostr/test/setup.ts +++ b/packages/nostr/test/setup.ts @@ -8,8 +8,7 @@ import { parsePublicKey, PublicKey, } from "../src/crypto" -import { RawEvent } from "../src" -import { signEvent, Unsigned } from "../src/event" +import { RawEvent, signEvent, Unsigned } from "../src/event" export const relayUrl = new URL("ws://localhost:12648") From 25e7f68dcea898167be48f307d147d4afb9f8aa8 Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 1 Jun 2023 09:54:25 +0100 Subject: [PATCH 15/24] send help --- packages/app/src/Cache/UserRelayCache.ts | 2 +- packages/app/src/Element/Relay.tsx | 4 +- packages/app/src/Element/SubDebug.tsx | 4 +- packages/app/src/ExternalStore.ts | 14 +- packages/app/src/Feed/RelayState.ts | 16 +- packages/app/src/Hooks/useRequestBuilder.tsx | 4 +- packages/app/src/Pages/Layout.tsx | 6 +- packages/app/src/Pages/NostrLinkHandler.tsx | 4 - packages/app/src/Pages/Root.tsx | 6 +- packages/app/src/Pages/SearchPage.tsx | 2 +- packages/app/src/Pages/ZapPool.tsx | 17 +- packages/app/src/Pages/settings/RelayInfo.tsx | 6 +- packages/app/src/Pages/settings/Relays.tsx | 4 +- packages/app/src/System/Connection.ts | 109 ++---- packages/app/src/System/EventPublisher.ts | 6 +- packages/app/src/System/NostrSystem.ts | 249 +++++++++++++ packages/app/src/System/ProfileCache.ts | 17 +- packages/app/src/System/Query.test.ts | 72 +++- packages/app/src/System/Query.ts | 51 +-- .../app/src/System/RequestBuilder.test.ts | 5 +- packages/app/src/System/RequestBuilder.ts | 29 +- .../app/src/System/RequestMatcher.test.ts | 23 ++ packages/app/src/System/RequestMatcher.ts | 20 ++ packages/app/src/System/RequestMerger.test.ts | 17 +- packages/app/src/System/RequestMerger.ts | 9 +- .../app/src/System/RequestSplitter.test.ts | 31 +- packages/app/src/System/RequestSplitter.ts | 32 ++ packages/app/src/System/SystemWorker.ts | 69 ++++ packages/app/src/System/Util.ts | 8 + packages/app/src/System/index.ts | 330 ++---------------- packages/app/src/System/worker.ts | 21 ++ packages/app/src/index.tsx | 4 +- 32 files changed, 690 insertions(+), 501 deletions(-) create mode 100644 packages/app/src/System/NostrSystem.ts create mode 100644 packages/app/src/System/RequestMatcher.test.ts create mode 100644 packages/app/src/System/RequestMatcher.ts create mode 100644 packages/app/src/System/SystemWorker.ts create mode 100644 packages/app/src/System/worker.ts diff --git a/packages/app/src/Cache/UserRelayCache.ts b/packages/app/src/Cache/UserRelayCache.ts index 37e38036..c0c9fb5a 100644 --- a/packages/app/src/Cache/UserRelayCache.ts +++ b/packages/app/src/Cache/UserRelayCache.ts @@ -1,7 +1,7 @@ import { db, UsersRelays } from "Db"; import FeedCache from "./FeedCache"; -class UsersRelaysCache extends FeedCache { +export class UsersRelaysCache extends FeedCache { constructor() { super("UserRelays", db.userRelays); } diff --git a/packages/app/src/Element/Relay.tsx b/packages/app/src/Element/Relay.tsx index e4779802..e79ed1ab 100644 --- a/packages/app/src/Element/Relay.tsx +++ b/packages/app/src/Element/Relay.tsx @@ -20,7 +20,9 @@ export interface RelayProps { export default function Relay(props: RelayProps) { const navigate = useNavigate(); const login = useLogin(); - const relaySettings = unwrap(login.relays.item[props.addr] ?? System.Sockets.get(props.addr)?.Settings ?? {}); + const relaySettings = unwrap( + login.relays.item[props.addr] ?? System.Sockets.find(a => a.address === props.addr)?.settings ?? {} + ); const state = useRelayState(props.addr); const name = useMemo(() => getRelayName(props.addr), [props.addr]); diff --git a/packages/app/src/Element/SubDebug.tsx b/packages/app/src/Element/SubDebug.tsx index 66982fba..acf16e47 100644 --- a/packages/app/src/Element/SubDebug.tsx +++ b/packages/app/src/Element/SubDebug.tsx @@ -66,8 +66,8 @@ const SubDebug = () => { return ( <> Connections: - {[...System.Sockets.keys()].map(k => ( - + {System.Sockets.map(k => ( + ))} ); diff --git a/packages/app/src/ExternalStore.ts b/packages/app/src/ExternalStore.ts index f9b77a88..4b1dedea 100644 --- a/packages/app/src/ExternalStore.ts +++ b/packages/app/src/ExternalStore.ts @@ -1,18 +1,18 @@ -type HookFn = () => void; +type HookFn = (e?: TSnapshot) => void; -interface HookFilter { - fn: HookFn; +interface HookFilter { + fn: HookFn; } /** * Simple React hookable store with manual change notifications */ export default abstract class ExternalStore { - #hooks: Array = []; + #hooks: Array> = []; #snapshot: Readonly = {} as Readonly; #changed = true; - hook(fn: HookFn) { + hook(fn: HookFn) { this.#hooks.push({ fn, }); @@ -32,9 +32,9 @@ export default abstract class ExternalStore { return this.#snapshot; } - protected notifyChange() { + protected notifyChange(sn?: TSnapshot) { this.#changed = true; - this.#hooks.forEach(h => h.fn()); + this.#hooks.forEach(h => h.fn(sn)); } abstract takeSnapshot(): TSnapshot; diff --git a/packages/app/src/Feed/RelayState.ts b/packages/app/src/Feed/RelayState.ts index 830ccb66..c5c5c5a1 100644 --- a/packages/app/src/Feed/RelayState.ts +++ b/packages/app/src/Feed/RelayState.ts @@ -1,18 +1,6 @@ -import { useSyncExternalStore } from "react"; -import { StateSnapshot } from "System"; import { System } from "index"; -const noop = () => { - return () => undefined; -}; -const noopState = (): StateSnapshot | undefined => { - return undefined; -}; - export default function useRelayState(addr: string) { - const c = System.Sockets.get(addr); - return useSyncExternalStore( - c?.StatusHook.bind(c) ?? noop, - c?.GetState.bind(c) ?? noopState - ); + const c = System.Sockets.find(a => a.address === addr); + return c; } diff --git a/packages/app/src/Hooks/useRequestBuilder.tsx b/packages/app/src/Hooks/useRequestBuilder.tsx index 70d28030..5f7a37f7 100644 --- a/packages/app/src/Hooks/useRequestBuilder.tsx +++ b/packages/app/src/Hooks/useRequestBuilder.tsx @@ -10,7 +10,7 @@ const useRequestBuilder = { const subscribe = (onChanged: () => void) => { - const store = System.Query(type, rb); + const store = (System.Query(type, rb)?.feed as TStore) ?? new type(); let t: ReturnType | undefined; const release = store.hook(() => { if (!t) { @@ -24,7 +24,7 @@ const useRequestBuilder = { if (rb?.id) { - System.CancelQuery(rb.id); + System.GetQuery(rb.id)?.cancel(); } release(); }; diff --git a/packages/app/src/Pages/Layout.tsx b/packages/app/src/Pages/Layout.tsx index 76dc35a7..6c450958 100644 --- a/packages/app/src/Pages/Layout.tsx +++ b/packages/app/src/Pages/Layout.tsx @@ -75,9 +75,9 @@ export default function Layout() { for (const [k, v] of Object.entries(relays.item)) { await System.ConnectToRelay(k, v); } - for (const [k, c] of System.Sockets) { - if (!relays.item[k] && !c.Ephemeral) { - System.DisconnectRelay(k); + for (const v of System.Sockets) { + if (!relays.item[v.address] && !v.ephemeral) { + System.DisconnectRelay(v.address); } } })(); diff --git a/packages/app/src/Pages/NostrLinkHandler.tsx b/packages/app/src/Pages/NostrLinkHandler.tsx index bc2397e0..3a4d966d 100644 --- a/packages/app/src/Pages/NostrLinkHandler.tsx +++ b/packages/app/src/Pages/NostrLinkHandler.tsx @@ -6,7 +6,6 @@ import { useNavigate, useParams } from "react-router-dom"; import Spinner from "Icons/Spinner"; import { parseNostrLink, profileLink } from "SnortUtils"; import { getNip05PubKey } from "Pages/LoginPage"; -import { System } from "index"; export default function NostrLinkHandler() { const params = useParams(); @@ -18,9 +17,6 @@ export default function NostrLinkHandler() { async function handleLink(link: string) { const nav = parseNostrLink(link); if (nav) { - if ((nav.relays?.length ?? 0) > 0) { - nav.relays?.map(a => System.ConnectEphemeralRelay(a)); - } if (nav.type === NostrPrefix.Event || nav.type === NostrPrefix.Note || nav.type === NostrPrefix.Address) { navigate(`/e/${nav.encode()}`); } else if (nav.type === NostrPrefix.PublicKey || nav.type === NostrPrefix.Profile) { diff --git a/packages/app/src/Pages/Root.tsx b/packages/app/src/Pages/Root.tsx index 10de36e0..74c63627 100644 --- a/packages/app/src/Pages/Root.tsx +++ b/packages/app/src/Pages/Root.tsx @@ -170,10 +170,10 @@ const GlobalTab = () => { useEffect(() => { return debounce(500, () => { const ret: RelayOption[] = []; - System.Sockets.forEach((v, k) => { + System.Sockets.forEach(v => { ret.push({ - url: k, - paid: v.Info?.limitation?.payment_required ?? false, + url: v.address, + paid: v.info?.limitation?.payment_required ?? false, }); }); ret.sort(a => (a.paid ? -1 : 1)); diff --git a/packages/app/src/Pages/SearchPage.tsx b/packages/app/src/Pages/SearchPage.tsx index f7268402..28ddd82b 100644 --- a/packages/app/src/Pages/SearchPage.tsx +++ b/packages/app/src/Pages/SearchPage.tsx @@ -42,7 +42,7 @@ const SearchPage = () => { useEffect(() => { const addedRelays: string[] = []; for (const [k, v] of SearchRelays) { - if (!System.Sockets.has(k)) { + if (!System.Sockets.some(v => v.address === k)) { System.ConnectToRelay(k, v); addedRelays.push(k); } diff --git a/packages/app/src/Pages/ZapPool.tsx b/packages/app/src/Pages/ZapPool.tsx index 431d0af3..44614edd 100644 --- a/packages/app/src/Pages/ZapPool.tsx +++ b/packages/app/src/Pages/ZapPool.tsx @@ -78,15 +78,14 @@ export default function ZapPoolPage() { const { wallet } = useWallet(); const relayConnections = useMemo(() => { - return [...System.Sockets.values()] - .map(a => { - if (a.Info?.pubkey && !a.Ephemeral) { - return { - address: a.Address, - pubkey: a.Info.pubkey, - }; - } - }) + return System.Sockets.map(a => { + if (a.info?.pubkey && !a.ephemeral) { + return { + address: a.address, + pubkey: a.info.pubkey, + }; + } + }) .filter(a => a !== undefined) .map(unwrap); }, [login.relays]); diff --git a/packages/app/src/Pages/settings/RelayInfo.tsx b/packages/app/src/Pages/settings/RelayInfo.tsx index fc6d9a2f..cf292887 100644 --- a/packages/app/src/Pages/settings/RelayInfo.tsx +++ b/packages/app/src/Pages/settings/RelayInfo.tsx @@ -14,8 +14,8 @@ const RelayInfo = () => { const navigate = useNavigate(); const login = useLogin(); - const conn = Array.from(System.Sockets.values()).find(a => a.Id === params.id); - const stats = useRelayState(conn?.Address ?? ""); + const conn = System.Sockets.find(a => a.id === params.id); + const stats = useRelayState(conn?.address ?? ""); return ( <> @@ -105,7 +105,7 @@ const RelayInfo = () => {
{ - removeRelay(login, unwrap(conn).Address); + removeRelay(login, unwrap(conn).address); navigate("/settings/relays"); }}> diff --git a/packages/app/src/Pages/settings/Relays.tsx b/packages/app/src/Pages/settings/Relays.tsx index 52f992bf..78b490c7 100644 --- a/packages/app/src/Pages/settings/Relays.tsx +++ b/packages/app/src/Pages/settings/Relays.tsx @@ -16,7 +16,7 @@ const RelaySettingsPage = () => { const [newRelay, setNewRelay] = useState(); const otherConnections = useMemo(() => { - return [...System.Sockets.keys()].filter(a => relays.item[a] === undefined); + return System.Sockets.filter(a => relays.item[a.address] === undefined); }, [relays]); async function saveRelays() { @@ -98,7 +98,7 @@ const RelaySettingsPage = () => {
{otherConnections.map(a => ( - + ))}
diff --git a/packages/app/src/System/Connection.ts b/packages/app/src/System/Connection.ts index 56727d58..75e2832b 100644 --- a/packages/app/src/System/Connection.ts +++ b/packages/app/src/System/Connection.ts @@ -5,8 +5,8 @@ import { ConnectionStats } from "./ConnectionStats"; import { RawEvent, ReqCommand, TaggedRawEvent, u256 } from "./Nostr"; import { RelayInfo } from "./RelayInfo"; import { unwrap } from "./Util"; +import ExternalStore from "ExternalStore"; -export type CustomHook = (state: Readonly) => void; export type AuthHandler = (challenge: string, relay: string) => Promise; /** @@ -20,7 +20,7 @@ export interface RelaySettings { /** * Snapshot of connection stats */ -export interface StateSnapshot { +export interface ConnectionStateSnapshot { connected: boolean; disconnects: number; avgLatency: number; @@ -28,13 +28,16 @@ export interface StateSnapshot { received: number; send: number; }; + settings?: RelaySettings; info?: RelayInfo; pendingRequests: Array; activeRequests: Array; id: string; + ephemeral: boolean; + address: string; } -export class Connection { +export class Connection extends ExternalStore { Id: string; Address: string; Socket: WebSocket | null = null; @@ -50,10 +53,7 @@ export class Connection { Info?: RelayInfo; ConnectTimeout: number = DefaultConnectTimeout; Stats: ConnectionStats = new ConnectionStats(); - StateHooks: Map = new Map(); HasStateChange: boolean = true; - CurrentState: StateSnapshot; - LastState: Readonly; IsClosed: boolean; ReconnectTimer: ReturnType | null; EventsCallback: Map void>; @@ -69,19 +69,10 @@ export class Connection { Down = true; constructor(addr: string, options: RelaySettings, auth?: AuthHandler, ephemeral: boolean = false) { + super(); this.Id = uuid(); this.Address = addr; this.Settings = options; - this.CurrentState = { - connected: false, - disconnects: 0, - avgLatency: 0, - events: { - received: 0, - send: 0, - }, - } as StateSnapshot; - this.LastState = Object.freeze({ ...this.CurrentState }); this.IsClosed = false; this.ReconnectTimer = null; this.EventsCallback = new Map(); @@ -146,7 +137,7 @@ export class Connection { this.ReconnectTimer = null; } this.Socket?.close(); - this.#UpdateState(); + this.notifyChange(); } OnOpen() { @@ -181,7 +172,7 @@ export class Connection { this.#ResetQueues(); // reset connection Id on disconnect, for query-tracking this.Id = uuid(); - this.#UpdateState(); + this.notifyChange(); } OnMessage(e: MessageEvent) { @@ -194,7 +185,7 @@ export class Connection { .then(() => this.#sendPendingRaw()) .catch(console.error); this.Stats.EventsReceived++; - this.#UpdateState(); + this.notifyChange(); break; } case "EVENT": { @@ -203,7 +194,7 @@ export class Connection { relays: [this.Address], }); this.Stats.EventsReceived++; - this.#UpdateState(); + this.notifyChange(); break; } case "EOSE": { @@ -235,7 +226,7 @@ export class Connection { OnError(e: Event) { console.error(e); - this.#UpdateState(); + this.notifyChange(); } /** @@ -248,7 +239,7 @@ export class Connection { const req = ["EVENT", e]; this.#SendJson(req); this.Stats.EventsSent++; - this.#UpdateState(); + this.notifyChange(); } /** @@ -271,32 +262,10 @@ export class Connection { const req = ["EVENT", e]; this.#SendJson(req); this.Stats.EventsSent++; - this.#UpdateState(); + this.notifyChange(); }); } - /** - * Hook status for connection - */ - StatusHook(fnHook: CustomHook) { - const id = uuid(); - this.StateHooks.set(id, fnHook); - return () => { - this.StateHooks.delete(id); - }; - } - - /** - * Returns the current state of this connection - */ - GetState() { - if (this.HasStateChange) { - this.LastState = Object.freeze({ ...this.CurrentState }); - this.HasStateChange = false; - } - return this.LastState; - } - /** * Using relay document to determine if this relay supports a feature */ @@ -320,7 +289,7 @@ export class Connection { this.#SendJson(cmd); cbSent(); } - this.#UpdateState(); + this.notifyChange(); } CloseReq(id: string) { @@ -329,7 +298,28 @@ export class Connection { this.OnEose?.(id); this.#SendQueuedRequests(); } - this.#UpdateState(); + this.notifyChange(); + } + + takeSnapshot(): ConnectionStateSnapshot { + return { + connected: this.Socket?.readyState === WebSocket.OPEN, + events: { + received: this.Stats.EventsReceived, + send: this.Stats.EventsSent, + }, + avgLatency: + this.Stats.Latency.length > 0 + ? this.Stats.Latency.reduce((acc, v) => acc + v, 0) / this.Stats.Latency.length + : 0, + disconnects: this.Stats.Disconnects, + info: this.Info, + id: this.Id, + pendingRequests: [...this.PendingRequests.map(a => a.cmd[1])], + activeRequests: [...this.ActiveRequests], + ephemeral: this.Ephemeral, + address: this.Address, + }; } #SendQueuedRequests() { @@ -351,30 +341,7 @@ export class Connection { this.ActiveRequests.clear(); this.PendingRequests = []; this.PendingRaw = []; - this.#UpdateState(); - } - - #UpdateState() { - this.CurrentState.connected = this.Socket?.readyState === WebSocket.OPEN; - this.CurrentState.events.received = this.Stats.EventsReceived; - this.CurrentState.events.send = this.Stats.EventsSent; - this.CurrentState.avgLatency = - this.Stats.Latency.length > 0 ? this.Stats.Latency.reduce((acc, v) => acc + v, 0) / this.Stats.Latency.length : 0; - this.CurrentState.disconnects = this.Stats.Disconnects; - this.CurrentState.info = this.Info; - this.CurrentState.id = this.Id; - this.CurrentState.pendingRequests = [...this.PendingRequests.map(a => a.cmd[1])]; - this.CurrentState.activeRequests = [...this.ActiveRequests]; - this.Stats.Latency = this.Stats.Latency.slice(-20); // trim - this.HasStateChange = true; - this.#NotifyState(); - } - - #NotifyState() { - const state = this.GetState(); - for (const [, h] of this.StateHooks) { - h(state); - } + this.notifyChange(); } #SendJson(obj: object) { diff --git a/packages/app/src/System/EventPublisher.ts b/packages/app/src/System/EventPublisher.ts index e77f4533..5fecf80f 100644 --- a/packages/app/src/System/EventPublisher.ts +++ b/packages/app/src/System/EventPublisher.ts @@ -7,6 +7,7 @@ import { Lists, RawEvent, RelaySettings, + SystemInterface, TaggedRawEvent, u256, UserMetadata, @@ -38,11 +39,6 @@ declare global { } } -interface SystemInterface { - BroadcastEvent(ev: RawEvent): void; - WriteOnceToRelay(relay: string, ev: RawEvent): Promise; -} - export class EventPublisher { #system: SystemInterface; #pubKey: string; diff --git a/packages/app/src/System/NostrSystem.ts b/packages/app/src/System/NostrSystem.ts new file mode 100644 index 00000000..9af2e9b2 --- /dev/null +++ b/packages/app/src/System/NostrSystem.ts @@ -0,0 +1,249 @@ +import debug from "debug"; +import { v4 as uuid } from "uuid"; + +import ExternalStore from "ExternalStore"; +import { RawEvent, RawReqFilter, TaggedRawEvent } from "./Nostr"; +import { AuthHandler, Connection, RelaySettings, ConnectionStateSnapshot } from "./Connection"; +import { Query, QueryBase } from "./Query"; +import { RelayCache } from "./GossipModel"; +import { NoteStore } from "./NoteCollection"; +import { BuiltRawReqFilter, RequestBuilder } from "./RequestBuilder"; +import { unwrap, sanitizeRelayUrl, unixNowMs } from "./Util"; +import { SystemInterface, SystemSnapshot } from "System"; + +/** + * Manages nostr content retrieval system + */ +export class NostrSystem extends ExternalStore implements SystemInterface { + /** + * All currently connected websockets + */ + #sockets = new Map(); + + /** + * All active queries + */ + Queries: Map = new Map(); + + /** + * Handler function for NIP-42 + */ + HandleAuth?: AuthHandler; + + #log = debug("System"); + #relayCache: RelayCache; + + constructor(relayCache: RelayCache) { + super(); + this.#relayCache = relayCache; + this.#cleanup(); + } + + get Sockets(): ConnectionStateSnapshot[] { + return [...this.#sockets.values()].map(a => a.snapshot()); + } + + /** + * Connect to a NOSTR relay if not already connected + */ + async ConnectToRelay(address: string, options: RelaySettings) { + try { + const addr = unwrap(sanitizeRelayUrl(address)); + if (!this.#sockets.has(addr)) { + const c = new Connection(addr, options, this.HandleAuth?.bind(this)); + this.#sockets.set(addr, c); + c.OnEvent = (s, e) => this.OnEvent(s, e); + c.OnEose = s => this.OnEndOfStoredEvents(c, s); + c.OnDisconnect = id => this.OnRelayDisconnect(id); + await c.Connect(); + } else { + // update settings if already connected + unwrap(this.#sockets.get(addr)).Settings = options; + } + } catch (e) { + console.error(e); + } + } + + OnRelayDisconnect(id: string) { + for (const [, q] of this.Queries) { + q.connectionLost(id); + } + } + + OnEndOfStoredEvents(c: Readonly, sub: string) { + for (const [, v] of this.Queries) { + v.eose(sub, c); + } + } + + OnEvent(sub: string, ev: TaggedRawEvent) { + for (const [, v] of this.Queries) { + v.onEvent(sub, ev); + } + } + + /** + * + * @param address Relay address URL + */ + async ConnectEphemeralRelay(address: string): Promise { + try { + const addr = unwrap(sanitizeRelayUrl(address)); + if (!this.#sockets.has(addr)) { + const c = new Connection(addr, { read: true, write: false }, this.HandleAuth?.bind(this), true); + this.#sockets.set(addr, c); + c.OnEvent = (s, e) => this.OnEvent(s, e); + c.OnEose = s => this.OnEndOfStoredEvents(c, s); + c.OnDisconnect = id => this.OnRelayDisconnect(id); + await c.Connect(); + return c; + } + } catch (e) { + console.error(e); + } + } + + /** + * Disconnect from a relay + */ + DisconnectRelay(address: string) { + const c = this.#sockets.get(address); + if (c) { + this.#sockets.delete(address); + c.Close(); + } + } + + GetQuery(id: string): Query | undefined { + return this.Queries.get(id); + } + + Query(type: { new (): T }, req: RequestBuilder | null): Query | undefined { + if (!req) return; + + const existing = this.Queries.get(req.id); + if (existing) { + const filters = req.buildDiff(this.#relayCache, existing.filters); + if (filters.length === 0 && !req.options?.skipDiff) { + return existing; + } else { + for (const subQ of filters) { + this.SendQuery(existing, subQ).then(qta => + qta.forEach(v => this.#log("New QT from diff %s %s %O from: %O", req.id, v.id, v.filters, existing.filters)) + ); + } + this.notifyChange(); + return existing; + } + } else { + const store = new type(); + + const filters = req.build(this.#relayCache); + const q = new Query(req.id, store); + if (req.options?.leaveOpen) { + q.leaveOpen = req.options.leaveOpen; + } + + this.Queries.set(req.id, q); + for (const subQ of filters) { + this.SendQuery(q, subQ).then(qta => + qta.forEach(v => this.#log("New QT from diff %s %s %O", req.id, v.id, v.filters)) + ); + } + this.notifyChange(); + return q; + } + } + + async SendQuery(q: Query, qSend: BuiltRawReqFilter) { + if (qSend.relay) { + this.#log("Sending query to %s %O", qSend.relay, qSend); + const s = this.#sockets.get(qSend.relay); + if (s) { + const qt = q.sendToRelay(s, qSend); + if (qt) { + return [qt]; + } + } else { + const nc = await this.ConnectEphemeralRelay(qSend.relay); + if (nc) { + const qt = q.sendToRelay(nc, qSend); + if (qt) { + return [qt]; + } + } else { + console.warn("Failed to connect to new relay for:", qSend.relay, q); + } + } + } else { + const ret = []; + for (const [, s] of this.#sockets) { + if (!s.Ephemeral) { + const qt = q.sendToRelay(s, qSend); + if (qt) { + ret.push(qt); + } + } + } + return ret; + } + return []; + } + + /** + * Send events to writable relays + */ + BroadcastEvent(ev: RawEvent) { + for (const [, s] of this.#sockets) { + s.SendEvent(ev); + } + } + + /** + * Write an event to a relay then disconnect + */ + async WriteOnceToRelay(address: string, ev: RawEvent) { + return new Promise((resolve, reject) => { + const c = new Connection(address, { write: true, read: false }, this.HandleAuth, true); + + const t = setTimeout(reject, 5_000); + c.OnConnected = async () => { + clearTimeout(t); + await c.SendAsync(ev); + c.Close(); + resolve(); + }; + c.Connect(); + }); + } + + takeSnapshot(): SystemSnapshot { + return { + queries: [...this.Queries.values()].map(a => { + return { + id: a.id, + filters: a.filters, + closing: a.closing, + subFilters: [], + }; + }), + }; + } + + #cleanup() { + const now = unixNowMs(); + let changed = false; + for (const [k, v] of this.Queries) { + if (v.closingAt && v.closingAt < now) { + v.sendClose(); + this.Queries.delete(k); + changed = true; + } + } + if (changed) { + this.notifyChange(); + } + setTimeout(() => this.#cleanup(), 1_000); + } +} diff --git a/packages/app/src/System/ProfileCache.ts b/packages/app/src/System/ProfileCache.ts index da5fe5e8..fe49f371 100644 --- a/packages/app/src/System/ProfileCache.ts +++ b/packages/app/src/System/ProfileCache.ts @@ -1,4 +1,4 @@ -import { EventKind, HexKey, NostrSystem, TaggedRawEvent } from "System"; +import { EventKind, HexKey, SystemInterface, TaggedRawEvent } from "System"; import { ProfileCacheExpire } from "Const"; import { mapEventToProfile, MetadataCache } from "Cache"; import { UserCache } from "Cache/UserCache"; @@ -7,7 +7,7 @@ import { unixNowMs } from "SnortUtils"; import debug from "debug"; export class ProfileLoaderService { - #system: NostrSystem; + #system: SystemInterface; /** * List of pubkeys to fetch metadata for @@ -16,7 +16,7 @@ export class ProfileLoaderService { readonly #log = debug("ProfileCache"); - constructor(system: NostrSystem) { + constructor(system: SystemInterface) { this.#system = system; this.#FetchMetadata(); } @@ -74,8 +74,9 @@ export class ProfileLoaderService { const newProfiles = new Set(); const q = this.#system.Query(PubkeyReplaceableNoteStore, sub); + const feed = (q?.feed as PubkeyReplaceableNoteStore) ?? new PubkeyReplaceableNoteStore(); // never release this callback, it will stop firing anyway after eose - const releaseOnEvent = q.onEvent(async e => { + const releaseOnEvent = feed.onEvent(async e => { for (const pe of e) { newProfiles.add(pe.id); await this.onProfileEvent(pe); @@ -83,17 +84,17 @@ export class ProfileLoaderService { }); const results = await new Promise>>(resolve => { let timeout: ReturnType | undefined = undefined; - const release = q.hook(() => { - if (!q.loading) { + const release = feed.hook(() => { + if (!feed.loading) { clearTimeout(timeout); - resolve(q.getSnapshotData() ?? []); + resolve(feed.getSnapshotData() ?? []); this.#log("Profiles finished: %s", sub.id); release(); } }); timeout = setTimeout(() => { release(); - resolve(q.getSnapshotData() ?? []); + resolve(feed.getSnapshotData() ?? []); this.#log("Profiles timeout: %s", sub.id); }, 5_000); }); diff --git a/packages/app/src/System/Query.test.ts b/packages/app/src/System/Query.test.ts index d6617896..29a7477c 100644 --- a/packages/app/src/System/Query.test.ts +++ b/packages/app/src/System/Query.test.ts @@ -3,6 +3,7 @@ import { describe, expect } from "@jest/globals"; import { Query, QueryBase } from "./Query"; import { getRandomValues } from "crypto"; import { FlatNoteStore } from "./NoteCollection"; +import { RequestStrategy } from "./RequestBuilder"; window.crypto = {} as any; window.crypto.getRandomValues = getRandomValues as any; @@ -21,43 +22,88 @@ describe("query", () => { const c3 = new Connection("wss://three.com", opt); c3.Down = false; - q.sendToRelay(c1, { - id: "test", + const f = { + relay: "", + strategy: RequestStrategy.DefaultRelays, filters: [ { kinds: [1], authors: ["test"], }, ], - }); - q.sendToRelay(c2); - q.sendToRelay(c3); + }; + const qt1 = q.sendToRelay(c1, f); + const qt2 = q.sendToRelay(c2, f); + const qt3 = q.sendToRelay(c3, f); expect(q.progress).toBe(0); - q.eose(q.id, c1); + q.eose(qt1!.id, c1); expect(q.progress).toBe(1 / 3); - q.eose(q.id, c1); + q.eose(qt1!.id, c1); expect(q.progress).toBe(1 / 3); - q.eose(q.id, c2); + q.eose(qt2!.id, c2); expect(q.progress).toBe(2 / 3); - q.eose(q.id, c3); + q.eose(qt3!.id, c3); expect(q.progress).toBe(1); const qs = { - id: "test-1", + relay: "", + strategy: RequestStrategy.DefaultRelays, filters: [ { kinds: [1], authors: ["test-sub"], }, ], - } as QueryBase; - q.sendToRelay(c1, qs); + }; + const qt = q.sendToRelay(c1, qs); expect(q.progress).toBe(3 / 4); - q.eose(qs.id, c1); + q.eose(qt!.id, c1); expect(q.progress).toBe(1); q.sendToRelay(c2, qs); expect(q.progress).toBe(4 / 5); }); + + it("should merge all sub-query filters", () => { + const q = new Query("test", new FlatNoteStore()); + const c0 = new Connection("wss://test.com", { read: true, write: true }); + q.sendToRelay(c0, { + filters: [ + { + authors: ["a"], + kinds: [1], + }, + ], + relay: "", + strategy: RequestStrategy.DefaultRelays, + }); + q.sendToRelay(c0, { + filters: [ + { + authors: ["b"], + kinds: [1, 2], + }, + ], + relay: "", + strategy: RequestStrategy.DefaultRelays, + }); + q.sendToRelay(c0, { + filters: [ + { + authors: ["c"], + kinds: [2], + }, + ], + relay: "", + strategy: RequestStrategy.DefaultRelays, + }); + + expect(q.filters).toEqual([ + { + authors: ["a", "b", "c"], + kinds: [1, 2], + }, + ]); + }); }); diff --git a/packages/app/src/System/Query.ts b/packages/app/src/System/Query.ts index 4521d1b6..305ebe77 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/app/src/System/Query.ts @@ -1,9 +1,11 @@ import { v4 as uuid } from "uuid"; import debug from "debug"; -import { Connection, RawReqFilter, Nips } from "System"; +import { Connection, RawReqFilter, Nips, TaggedRawEvent } from "System"; import { unixNowMs, unwrap } from "SnortUtils"; import { NoteStore } from "./NoteCollection"; -import { mergeSimilar } from "./RequestMerger"; +import { simpleMerge } from "./RequestMerger"; +import { eventMatchesFilter } from "./RequestMatcher"; +import { BuiltRawReqFilter } from "./RequestBuilder"; /** * Tracing for relay query status @@ -19,7 +21,6 @@ class QueryTrace { readonly #fnProgress: () => void; constructor( - readonly subId: string, readonly relay: string, readonly filters: Array, readonly connId: string, @@ -51,7 +52,7 @@ class QueryTrace { sendClose() { this.close = unixNowMs(); - this.#fnClose(this.subId); + this.#fnClose(this.id); this.#fnProgress(); } @@ -135,7 +136,6 @@ export class Query implements QueryBase { */ #feed: NoteStore; - subQueryCounter = 0; #log = debug("Query"); constructor(id: string, feed: NoteStore) { @@ -152,32 +152,37 @@ export class Query implements QueryBase { return this.#cancelTimeout; } + get filters() { + const filters = this.#tracing.flatMap(a => a.filters); + return [simpleMerge(filters)]; + } + get feed() { return this.#feed; } - get filters() { - const filters = this.#tracing.flatMap(a => a.filters); - return mergeSimilar(filters); + onEvent(sub: string, e: TaggedRawEvent) { + for (const t of this.#tracing) { + if (t.id === sub) { + this.feed.add(e); + break; + } + } } cancel() { this.#cancelTimeout = unixNowMs() + 5_000; } - unCancel() { - this.#cancelTimeout = undefined; - } - cleanup() { this.#stopCheckTraces(); } - sendToRelay(c: Connection, subq?: QueryBase) { - if (!this.#canSendQuery(c, subq ?? this)) { + sendToRelay(c: Connection, subq: BuiltRawReqFilter) { + if (!this.#canSendQuery(c, subq)) { return; } - this.#sendQueryInternal(c, subq ?? this); + return this.#sendQueryInternal(c, subq); } connectionLost(id: string) { @@ -192,7 +197,7 @@ export class Query implements QueryBase { } eose(sub: string, conn: Readonly) { - const qt = this.#tracing.find(a => a.subId === sub && a.connId === conn.Id); + const qt = this.#tracing.find(a => a.id === sub && a.connId === conn.Id); qt?.gotEose(); if (!this.leaveOpen) { qt?.sendClose(); @@ -235,12 +240,12 @@ export class Query implements QueryBase { }, 500); } - #canSendQuery(c: Connection, q: QueryBase) { - if (q.relays && !q.relays.includes(c.Address)) { + #canSendQuery(c: Connection, q: BuiltRawReqFilter) { + if (q.relay && q.relay !== c.Address) { return false; } - if ((q.relays?.length ?? 0) === 0 && c.Ephemeral) { - this.#log("Cant send non-specific REQ to ephemeral connection %O %O %O", q, q.relays, c); + if (!q.relay && c.Ephemeral) { + this.#log("Cant send non-specific REQ to ephemeral connection %O %O %O", q, q.relay, c); return false; } if (q.filters.some(a => a.search) && !c.SupportsNip(Nips.Search)) { @@ -250,9 +255,8 @@ export class Query implements QueryBase { return true; } - #sendQueryInternal(c: Connection, q: QueryBase) { + #sendQueryInternal(c: Connection, q: BuiltRawReqFilter) { const qt = new QueryTrace( - q.id, c.Address, q.filters, c.Id, @@ -260,6 +264,7 @@ export class Query implements QueryBase { () => this.#onProgress() ); this.#tracing.push(qt); - c.QueueReq(["REQ", q.id, ...q.filters], () => qt.sentToRelay()); + c.QueueReq(["REQ", qt.id, ...q.filters], () => qt.sentToRelay()); + return qt; } } diff --git a/packages/app/src/System/RequestBuilder.test.ts b/packages/app/src/System/RequestBuilder.test.ts index 079d36c5..e2546cda 100644 --- a/packages/app/src/System/RequestBuilder.test.ts +++ b/packages/app/src/System/RequestBuilder.test.ts @@ -88,10 +88,7 @@ describe("RequestBuilder", () => { f0.authors(["a"]); expect(a).toEqual([{}]); - const b = rb.buildDiff(DummyCache, { - filters: a, - id: "test", - }); + const b = rb.buildDiff(DummyCache, a); expect(b).toMatchObject([ { filters: [{ authors: ["a"] }], diff --git a/packages/app/src/System/RequestBuilder.ts b/packages/app/src/System/RequestBuilder.ts index 56a49e85..2ee6e7ef 100644 --- a/packages/app/src/System/RequestBuilder.ts +++ b/packages/app/src/System/RequestBuilder.ts @@ -1,9 +1,7 @@ import { RawReqFilter, u256, HexKey, EventKind } from "System"; import { appendDedupe, dedupe } from "SnortUtils"; -import { QueryBase } from "./Query"; import { diffFilters } from "./RequestSplitter"; import { RelayCache, splitAllByWriteRelays, splitByWriteRelays } from "./GossipModel"; -import { mergeSimilar } from "./RequestMerger"; /** * Which strategy is used when building REQ filters @@ -84,8 +82,8 @@ export class RequestBuilder { } build(relays: RelayCache): Array { - const expanded = this.#builders.map(a => a.build(relays)).flat(); - return this.#mergeSimilar(expanded); + const expanded = this.#builders.flatMap(a => a.build(relays, this.id)); + return this.#groupByRelay(expanded); } /** @@ -93,11 +91,10 @@ export class RequestBuilder { * @param q All previous filters merged * @returns */ - buildDiff(relays: RelayCache, q: QueryBase): Array { + buildDiff(relays: RelayCache, filters: Array): Array { const next = this.buildRaw(); - const diff = diffFilters(q.filters, next); + const diff = diffFilters(filters, next); if (diff.changed) { - console.debug("DIFF", q.filters, next, diff); return splitAllByWriteRelays(relays, diff.filters).map(a => { return { strategy: RequestStrategy.AuthorsRelays, @@ -114,7 +111,7 @@ export class RequestBuilder { * @param expanded * @returns */ - #mergeSimilar(expanded: Array) { + #groupByRelay(expanded: Array) { const relayMerged = expanded.reduce((acc, v) => { const existing = acc.get(v.relay); if (existing) { @@ -125,14 +122,12 @@ export class RequestBuilder { return acc; }, new Map>()); - const filtersSquashed = [...relayMerged.values()].flatMap(a => { - return mergeSimilar(a.flatMap(b => b.filters)).map(b => { - return { - filters: [b], - relay: a[0].relay, - strategy: a[0].strategy, - } as BuiltRawReqFilter; - }); + const filtersSquashed = [...relayMerged.values()].map(a => { + return { + filters: a.flatMap(b => b.filters), + relay: a[0].relay, + strategy: a[0].strategy, + } as BuiltRawReqFilter; }); return filtersSquashed; @@ -211,7 +206,7 @@ export class RequestFilterBuilder { /** * Build/expand this filter into a set of relay specific queries */ - build(relays: RelayCache): Array { + build(relays: RelayCache, id: string): Array { // when querying for specific event ids with relay hints // take the first approach which is to split the filter by relay if (this.#filter.ids && this.#relayHints.size > 0) { diff --git a/packages/app/src/System/RequestMatcher.test.ts b/packages/app/src/System/RequestMatcher.test.ts new file mode 100644 index 00000000..49754ff5 --- /dev/null +++ b/packages/app/src/System/RequestMatcher.test.ts @@ -0,0 +1,23 @@ +import { eventMatchesFilter } from "./RequestMatcher"; + +describe("RequestMatcher", () => { + it("should match simple filter", () => { + const ev = { + id: "test", + kind: 1, + pubkey: "pubkey", + created_at: 99, + tags: [], + content: "test", + sig: "", + }; + const filter = { + ids: ["test"], + authors: ["pubkey", "other"], + kinds: [1, 2, 3], + since: 1, + before: 100, + }; + expect(eventMatchesFilter(ev, filter)).toBe(true); + }); +}); diff --git a/packages/app/src/System/RequestMatcher.ts b/packages/app/src/System/RequestMatcher.ts new file mode 100644 index 00000000..e8ffc450 --- /dev/null +++ b/packages/app/src/System/RequestMatcher.ts @@ -0,0 +1,20 @@ +import { RawEvent, RawReqFilter } from "./Nostr"; + +export function eventMatchesFilter(ev: RawEvent, filter: RawReqFilter) { + if (!(filter.ids?.includes(ev.id) ?? false)) { + return false; + } + if (!(filter.authors?.includes(ev.pubkey) ?? false)) { + return false; + } + if (!(filter.kinds?.includes(ev.kind) ?? false)) { + return false; + } + if (filter.since && ev.created_at < filter.since) { + return false; + } + if (filter.until && ev.created_at > filter.until) { + return false; + } + return true; +} diff --git a/packages/app/src/System/RequestMerger.test.ts b/packages/app/src/System/RequestMerger.test.ts index 4eac9ab7..2de15e7d 100644 --- a/packages/app/src/System/RequestMerger.test.ts +++ b/packages/app/src/System/RequestMerger.test.ts @@ -1,5 +1,5 @@ import { RawReqFilter } from "System"; -import { filterIncludes, mergeSimilar } from "./RequestMerger"; +import { filterIncludes, mergeSimilar, simpleMerge } from "./RequestMerger"; describe("RequestMerger", () => { it("should simple merge authors", () => { @@ -53,4 +53,19 @@ describe("RequestMerger", () => { } as RawReqFilter; expect(filterIncludes(bigger, smaller)).toBe(true); }); + + it("simpleMerge", () => { + const a = { + authors: ["a", "b", "c"], + since: 99, + } as RawReqFilter; + const b = { + authors: ["c", "d", "e"], + since: 100, + } as RawReqFilter; + expect(simpleMerge([a, b])).toEqual({ + authors: ["a", "b", "c", "d", "e"], + since: 100, + }); + }); }); diff --git a/packages/app/src/System/RequestMerger.ts b/packages/app/src/System/RequestMerger.ts index 79cfe1e8..a33f0226 100644 --- a/packages/app/src/System/RequestMerger.ts +++ b/packages/app/src/System/RequestMerger.ts @@ -9,7 +9,12 @@ export function mergeSimilar(filters: Array): Array return [...(canEasilyMerge.length > 0 ? [simpleMerge(canEasilyMerge)] : []), ...cannotMerge]; } -function simpleMerge(filters: Array) { +/** + * Simply flatten all filters into one + * @param filters + * @returns + */ +export function simpleMerge(filters: Array) { const result: any = {}; filters.forEach(filter => { @@ -21,7 +26,7 @@ function simpleMerge(filters: Array) { result[key] = [...new Set([...result[key], ...value])]; } } else { - throw new Error("Cannot simple merge with non-array filter properties"); + result[key] = value; } }); }); diff --git a/packages/app/src/System/RequestSplitter.test.ts b/packages/app/src/System/RequestSplitter.test.ts index 979a0330..10e78a6b 100644 --- a/packages/app/src/System/RequestSplitter.test.ts +++ b/packages/app/src/System/RequestSplitter.test.ts @@ -1,6 +1,6 @@ import { RawReqFilter } from "System"; import { describe, expect } from "@jest/globals"; -import { diffFilters } from "./RequestSplitter"; +import { diffFilters, expandFilter } from "./RequestSplitter"; describe("RequestSplitter", () => { test("single filter add value", () => { @@ -72,4 +72,33 @@ describe("RequestSplitter", () => { changed: true, }); }); + test("expand filter", () => { + const a = { + authors: ["a", "b", "c"], + kinds: [1, 2, 3], + ids: ["x", "y"], + since: 99, + limit: 10, + }; + expect(expandFilter(a)).toEqual([ + { authors: ["a"], kinds: [1], ids: ["x"], since: 99, limit: 10 }, + { authors: ["a"], kinds: [1], ids: ["y"], since: 99, limit: 10 }, + { authors: ["a"], kinds: [2], ids: ["x"], since: 99, limit: 10 }, + { authors: ["a"], kinds: [2], ids: ["y"], since: 99, limit: 10 }, + { authors: ["a"], kinds: [3], ids: ["x"], since: 99, limit: 10 }, + { authors: ["a"], kinds: [3], ids: ["y"], since: 99, limit: 10 }, + { authors: ["b"], kinds: [1], ids: ["x"], since: 99, limit: 10 }, + { authors: ["b"], kinds: [1], ids: ["y"], since: 99, limit: 10 }, + { authors: ["b"], kinds: [2], ids: ["x"], since: 99, limit: 10 }, + { authors: ["b"], kinds: [2], ids: ["y"], since: 99, limit: 10 }, + { authors: ["b"], kinds: [3], ids: ["x"], since: 99, limit: 10 }, + { authors: ["b"], kinds: [3], ids: ["y"], since: 99, limit: 10 }, + { authors: ["c"], kinds: [1], ids: ["x"], since: 99, limit: 10 }, + { authors: ["c"], kinds: [1], ids: ["y"], since: 99, limit: 10 }, + { authors: ["c"], kinds: [2], ids: ["x"], since: 99, limit: 10 }, + { authors: ["c"], kinds: [2], ids: ["y"], since: 99, limit: 10 }, + { authors: ["c"], kinds: [3], ids: ["x"], since: 99, limit: 10 }, + { authors: ["c"], kinds: [3], ids: ["y"], since: 99, limit: 10 }, + ]); + }); }); diff --git a/packages/app/src/System/RequestSplitter.ts b/packages/app/src/System/RequestSplitter.ts index 44fc2f91..2c942ff9 100644 --- a/packages/app/src/System/RequestSplitter.ts +++ b/packages/app/src/System/RequestSplitter.ts @@ -42,3 +42,35 @@ export function diffFilters(a: Array, b: Array) { changed: anyChanged, }; } + +/** + * Expand a filter into its most fine grained form + */ +export function expandFilter(f: RawReqFilter): Array { + const ret: Array = []; + const src = Object.entries(f); + const keys = src.filter(([, v]) => Array.isArray(v)).map(a => a[0]); + const props = src.filter(([, v]) => !Array.isArray(v)); + + function generateCombinations(index: number, currentCombination: RawReqFilter) { + if (index === keys.length) { + ret.push(currentCombination); + return; + } + + const key = keys[index]; + const values = (f as Record>)[key]; + + for (let i = 0; i < values.length; i++) { + const value = values[i]; + const updatedCombination = { ...currentCombination, [key]: [value] }; + generateCombinations(index + 1, updatedCombination); + } + } + + generateCombinations(0, { + ...Object.fromEntries(props), + }); + + return ret; +} diff --git a/packages/app/src/System/SystemWorker.ts b/packages/app/src/System/SystemWorker.ts new file mode 100644 index 00000000..eae78c98 --- /dev/null +++ b/packages/app/src/System/SystemWorker.ts @@ -0,0 +1,69 @@ +import ExternalStore from "ExternalStore"; +import { + NoteStore, + Query, + RawEvent, + RelaySettings, + RequestBuilder, + SystemSnapshot, + SystemInterface, + ConnectionStateSnapshot, + AuthHandler, +} from "System"; + +export class SystemWorker extends ExternalStore implements SystemInterface { + #port: MessagePort; + + constructor() { + super(); + if ("SharedWorker" in window) { + const worker = new SharedWorker("/system.js"); + this.#port = worker.port; + this.#port.onmessage = m => this.#onMessage(m); + } else { + throw new Error("SharedWorker is not supported"); + } + } + + HandleAuth?: AuthHandler; + + get Sockets(): ConnectionStateSnapshot[] { + throw new Error("Method not implemented."); + } + + Query(type: new () => T, req: RequestBuilder | null): Query | undefined { + throw new Error("Method not implemented."); + } + + CancelQuery(sub: string): void { + throw new Error("Method not implemented."); + } + + GetQuery(sub: string): Query | undefined { + throw new Error("Method not implemented."); + } + + ConnectToRelay(address: string, options: RelaySettings): Promise { + throw new Error("Method not implemented."); + } + + DisconnectRelay(address: string): void { + throw new Error("Method not implemented."); + } + + BroadcastEvent(ev: RawEvent): void { + throw new Error("Method not implemented."); + } + + WriteOnceToRelay(relay: string, ev: RawEvent): Promise { + throw new Error("Method not implemented."); + } + + takeSnapshot(): SystemSnapshot { + throw new Error("Method not implemented."); + } + + #onMessage(e: MessageEvent) { + console.debug(e); + } +} diff --git a/packages/app/src/System/Util.ts b/packages/app/src/System/Util.ts index 6d50c763..0e417814 100644 --- a/packages/app/src/System/Util.ts +++ b/packages/app/src/System/Util.ts @@ -32,3 +32,11 @@ export function sanitizeRelayUrl(url: string) { // ignore } } + +export function unixNow() { + return Math.floor(unixNowMs() / 1000); +} + +export function unixNowMs() { + return new Date().getTime(); +} diff --git a/packages/app/src/System/index.ts b/packages/app/src/System/index.ts index f6e75275..1bb1380e 100644 --- a/packages/app/src/System/index.ts +++ b/packages/app/src/System/index.ts @@ -1,8 +1,4 @@ -import debug from "debug"; - -import { sanitizeRelayUrl, unixNowMs, unwrap } from "SnortUtils"; -import { RawEvent, RawReqFilter, TaggedRawEvent } from "./Nostr"; -import { AuthHandler, Connection, RelaySettings, StateSnapshot } from "./Connection"; +import { AuthHandler, Connection, RelaySettings, ConnectionStateSnapshot } from "./Connection"; import { RequestBuilder } from "./RequestBuilder"; import { EventBuilder } from "./EventBuilder"; import { @@ -12,10 +8,10 @@ import { ParameterizedReplaceableNoteStore, ReplaceableNoteStore, } from "./NoteCollection"; -import { Query, QueryBase } from "./Query"; -import ExternalStore from "ExternalStore"; -import { RelayCache } from "./GossipModel"; +import { Query } from "./Query"; +import { RawEvent, RawReqFilter } from "./Nostr"; +export * from "./NostrSystem"; export { default as EventKind } from "./EventKind"; export * from "./Nostr"; export * from "./Links"; @@ -23,6 +19,29 @@ export { default as Tag } from "./Tag"; export * from "./Nips"; export * from "./RelayInfo"; +export interface SystemInterface { + /** + * Handler function for NIP-42 + */ + HandleAuth?: AuthHandler; + get Sockets(): Array; + GetQuery(id: string): Query | undefined; + Query(type: { new (): T }, req: RequestBuilder | null): Query | undefined; + ConnectToRelay(address: string, options: RelaySettings): Promise; + DisconnectRelay(address: string): void; + BroadcastEvent(ev: RawEvent): void; + WriteOnceToRelay(relay: string, ev: RawEvent): Promise; +} + +export interface SystemSnapshot { + queries: Array<{ + id: string; + filters: Array; + subFilters: Array; + closing: boolean; + }>; +} + export { NoteStore, RequestBuilder, @@ -35,298 +54,5 @@ export { AuthHandler, Connection, RelaySettings, - StateSnapshot, + ConnectionStateSnapshot, }; - -export interface SystemSnapshot { - queries: Array<{ - id: string; - filters: Array; - subFilters: Array; - closing: boolean; - }>; -} - -export type HookSystemSnapshotRelease = () => void; -export type HookSystemSnapshot = () => void; - -/** - * Manages nostr content retrieval system - */ -export class NostrSystem extends ExternalStore { - /** - * All currently connected websockets - */ - Sockets: Map; - - /** - * All active queries - */ - Queries: Map = new Map(); - - /** - * Handler function for NIP-42 - */ - HandleAuth?: AuthHandler; - - #log = debug("System"); - #relayCache: RelayCache; - - constructor(relayCache: RelayCache) { - super(); - this.Sockets = new Map(); - this.#relayCache = relayCache; - this.#cleanup(); - } - - /** - * Connect to a NOSTR relay if not already connected - */ - async ConnectToRelay(address: string, options: RelaySettings) { - try { - const addr = unwrap(sanitizeRelayUrl(address)); - if (!this.Sockets.has(addr)) { - const c = new Connection(addr, options, this.HandleAuth?.bind(this)); - this.Sockets.set(addr, c); - c.OnEvent = (s, e) => this.OnEvent(s, e); - c.OnEose = s => this.OnEndOfStoredEvents(c, s); - c.OnDisconnect = id => this.OnRelayDisconnect(id); - c.OnConnected = () => { - for (const [, q] of this.Queries) { - q.sendToRelay(c, q); - } - }; - await c.Connect(); - } else { - // update settings if already connected - unwrap(this.Sockets.get(addr)).Settings = options; - } - } catch (e) { - console.error(e); - } - } - - OnRelayDisconnect(id: string) { - for (const [, q] of this.Queries) { - q.connectionLost(id); - } - } - - OnEndOfStoredEvents(c: Readonly, sub: string) { - const q = this.GetQuery(sub); - if (q) { - q.eose(sub, c); - } - } - - OnEvent(sub: string, ev: TaggedRawEvent) { - const q = this.GetQuery(sub); - if (q?.feed) { - q.feed.add(ev); - } - } - - GetQuery(sub: string) { - const subFilterId = /-\d+$/i; - if (sub.match(subFilterId)) { - // feed events back into parent query - sub = sub.split(subFilterId)[0]; - } - return this.Queries.get(sub); - } - - /** - * - * @param address Relay address URL - */ - async ConnectEphemeralRelay(address: string): Promise { - try { - const addr = unwrap(sanitizeRelayUrl(address)); - if (!this.Sockets.has(addr)) { - const c = new Connection(addr, { read: true, write: false }, this.HandleAuth?.bind(this), true); - this.Sockets.set(addr, c); - c.OnEvent = (s, e) => this.OnEvent(s, e); - c.OnEose = s => this.OnEndOfStoredEvents(c, s); - c.OnDisconnect = id => this.OnRelayDisconnect(id); - c.OnConnected = () => { - for (const [, q] of this.Queries) { - if (q.progress !== 1) { - q.sendToRelay(c, q); - } - } - }; - await c.Connect(); - return c; - } - } catch (e) { - console.error(e); - } - } - - /** - * Disconnect from a relay - */ - DisconnectRelay(address: string) { - const c = this.Sockets.get(address); - if (c) { - this.Sockets.delete(address); - c.Close(); - } - } - - Query(type: { new (): T }, req: RequestBuilder | null): Readonly { - /** - * ## Notes - * - * Given a set of existing filters: - * ["REQ", "1", { kinds: [0, 7], authors: [...], since: now()-1hr, until: now() }] - * ["REQ", "2", { kinds: [0, 7], authors: [...], since: now(), limit: 0 }] - * - * ## Problem 1: - * Assume we now want to update sub "1" with a new set of authors, - * what should we do, should we close sub "1" and send the new set or create another - * subscription with the new pubkeys (diff) - * - * Creating a new subscription sounds great but also is a problem when relays limit - * active subscriptions, maybe we should instead queue the new - * subscription (assuming that we expect to close at EOSE) - * - * ## Problem 2: - * When multiple filters a specifid in a single filter but only 1 filter changes, - * ~~same as above~~ - * - * Seems reasonable to do "Queue Diff", should also be possible to collapse multiple - * pending filters for the same subscription - */ - - if (!req) return new type(); - - const existing = this.Queries.get(req.id); - if (existing) { - const filters = req.buildDiff(this.#relayCache, existing); - existing.unCancel(); - - if (filters.length === 0 && !req.options?.skipDiff) { - this.notifyChange(); - return existing.feed as Readonly; - } else { - for (const subQ of filters) { - this.SendQuery(existing, { - id: `${existing.id}-${existing.subQueryCounter++}`, - filters: subQ.filters, - relays: subQ.relay ? [subQ.relay] : undefined, - }); - } - this.notifyChange(); - return existing.feed as Readonly; - } - } else { - const store = new type(); - - const filters = req.build(this.#relayCache); - const q = new Query(req.id, store); - if (req.options?.leaveOpen) { - q.leaveOpen = req.options.leaveOpen; - } - - this.Queries.set(req.id, q); - for (const subQ of filters) { - this.SendQuery(q, { - id: `${q.id}-${q.subQueryCounter++}`, - filters: subQ.filters, - relays: subQ.relay ? [subQ.relay] : undefined, - }); - } - this.notifyChange(); - return q.feed as Readonly; - } - } - - CancelQuery(sub: string) { - const q = this.Queries.get(sub); - if (q) { - q.cancel(); - } - } - - async SendQuery(q: Query, qSend: QueryBase) { - if (qSend.relays && qSend.relays.length > 0) { - for (const r of qSend.relays) { - this.#log("Sending query to %s %O", r, qSend); - const s = this.Sockets.get(r); - if (s) { - q.sendToRelay(s, qSend); - } else { - const nc = await this.ConnectEphemeralRelay(r); - if (nc) { - q.sendToRelay(nc, qSend); - } else { - console.warn("Failed to connect to new relay for:", r, q); - } - } - } - } else { - for (const [, s] of this.Sockets) { - if (!s.Ephemeral) { - q.sendToRelay(s, qSend); - } - } - } - } - - /** - * Send events to writable relays - */ - BroadcastEvent(ev: RawEvent) { - for (const [, s] of this.Sockets) { - s.SendEvent(ev); - } - } - - /** - * Write an event to a relay then disconnect - */ - async WriteOnceToRelay(address: string, ev: RawEvent) { - return new Promise((resolve, reject) => { - const c = new Connection(address, { write: true, read: false }, this.HandleAuth, true); - - const t = setTimeout(reject, 5_000); - c.OnConnected = async () => { - clearTimeout(t); - await c.SendAsync(ev); - c.Close(); - resolve(); - }; - c.Connect(); - }); - } - - takeSnapshot(): SystemSnapshot { - return { - queries: [...this.Queries.values()].map(a => { - return { - id: a.id, - filters: a.filters, - closing: a.closing, - subFilters: [], - }; - }), - }; - } - - #cleanup() { - const now = unixNowMs(); - let changed = false; - for (const [k, v] of this.Queries) { - if (v.closingAt && v.closingAt < now) { - v.sendClose(); - this.Queries.delete(k); - changed = true; - } - } - if (changed) { - this.notifyChange(); - } - setTimeout(() => this.#cleanup(), 1_000); - } -} diff --git a/packages/app/src/System/worker.ts b/packages/app/src/System/worker.ts new file mode 100644 index 00000000..4f47dad8 --- /dev/null +++ b/packages/app/src/System/worker.ts @@ -0,0 +1,21 @@ +/// +import { UsersRelaysCache } from "Cache/UserRelayCache"; +import { NostrSystem } from "."; +declare const self: SharedWorkerGlobalScope; + +const RelayCache = new UsersRelaysCache(); +const System = new NostrSystem({ + get: pk => RelayCache.getFromCache(pk)?.relays, +}); + +self.onconnect = e => { + const port = e.ports[0]; + + port.addEventListener("message", async e1 => { + console.debug(e1); + const [cmd, ...others] = e1.data; + switch (cmd) { + } + }); + port.start(); +}; diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index 3561d7e8..8dfb2a8a 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -35,9 +35,9 @@ import DebugPage from "Pages/Debug"; import { db } from "Db"; import { preload } from "Cache"; import { LoginStore } from "Login"; -import { UserRelays } from "Cache/UserRelayCache"; -import { NostrSystem } from "System"; import { ProfileLoaderService } from "System/ProfileCache"; +import { NostrSystem } from "System"; +import { UserRelays } from "Cache/UserRelayCache"; /** * Singleton nostr system From ae6618f0ed1de0f18cd4239d70815a1039679b0f Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 1 Jun 2023 22:03:28 +0100 Subject: [PATCH 16/24] improve diff filters fix tests expander/compressor filter mangler --- packages/app/src/Cache/DMCache.ts | 10 +- packages/app/src/Cache/index.ts | 4 +- packages/app/src/Db/index.ts | 6 +- packages/app/src/Element/NostrFileHeader.tsx | 4 +- packages/app/src/Element/NoteReaction.tsx | 4 +- packages/app/src/Element/PubkeyList.tsx | 4 +- packages/app/src/Element/SendSats.tsx | 4 +- packages/app/src/Element/SubDebug.tsx | 11 +- packages/app/src/Element/TrendingPosts.tsx | 4 +- packages/app/src/Element/WriteDm.tsx | 4 +- packages/app/src/Element/ZapstrEmbed.tsx | 4 +- packages/app/src/External/NostrBand.ts | 6 +- packages/app/src/LNURL.ts | 4 +- packages/app/src/Pages/MessagesPage.tsx | 18 +-- packages/app/src/SnortUtils/index.ts | 4 +- packages/app/src/State/NoteCreator.ts | 10 +- packages/app/src/State/ReBroadcast.ts | 6 +- packages/app/src/System/Connection.ts | 8 +- packages/app/src/System/EventBuilder.ts | 4 +- packages/app/src/System/EventExt.ts | 14 +-- packages/app/src/System/EventPublisher.ts | 16 +-- packages/app/src/System/GossipModel.ts | 12 +- packages/app/src/System/Nostr.ts | 12 +- packages/app/src/System/NostrSystem.ts | 6 +- packages/app/src/System/Query.ts | 6 +- packages/app/src/System/RequestBuilder.ts | 15 +-- .../app/src/System/RequestExpander.test.ts | 34 ++++++ packages/app/src/System/RequestExpander.ts | 48 ++++++++ packages/app/src/System/RequestMatcher.ts | 4 +- packages/app/src/System/RequestMerger.test.ts | 62 ++++++++-- packages/app/src/System/RequestMerger.ts | 108 ++++++++++++++++-- .../app/src/System/RequestSplitter.test.ts | 87 ++++++-------- packages/app/src/System/RequestSplitter.ts | 82 ++----------- packages/app/src/System/SystemWorker.ts | 6 +- packages/app/src/System/Util.test.ts | 72 ++++++++++++ packages/app/src/System/Util.ts | 44 +++++++ packages/app/src/System/index.ts | 10 +- packages/app/src/Upload/index.ts | 4 +- packages/app/src/Wallet/NostrWalletConnect.ts | 4 +- 39 files changed, 504 insertions(+), 261 deletions(-) create mode 100644 packages/app/src/System/RequestExpander.test.ts create mode 100644 packages/app/src/System/RequestExpander.ts create mode 100644 packages/app/src/System/Util.test.ts diff --git a/packages/app/src/Cache/DMCache.ts b/packages/app/src/Cache/DMCache.ts index cacfd379..7cbce8df 100644 --- a/packages/app/src/Cache/DMCache.ts +++ b/packages/app/src/Cache/DMCache.ts @@ -1,13 +1,13 @@ -import { RawEvent } from "System"; +import { NostrEvent } from "System"; import { db } from "Db"; import FeedCache from "./FeedCache"; -class DMCache extends FeedCache { +class DMCache extends FeedCache { constructor() { super("DMCache", db.dms); } - key(of: RawEvent): string { + key(of: NostrEvent): string { return of.id; } @@ -23,11 +23,11 @@ class DMCache extends FeedCache { return ret; } - allDms(): Array { + allDms(): Array { return [...this.cache.values()]; } - takeSnapshot(): Array { + takeSnapshot(): Array { return this.allDms(); } } diff --git a/packages/app/src/Cache/index.ts b/packages/app/src/Cache/index.ts index 02941f3d..0f0cabb4 100644 --- a/packages/app/src/Cache/index.ts +++ b/packages/app/src/Cache/index.ts @@ -1,4 +1,4 @@ -import { HexKey, RawEvent, UserMetadata } from "System"; +import { HexKey, NostrEvent, UserMetadata } from "System"; import { hexToBech32, unixNowMs } from "SnortUtils"; import { DmCache } from "./DMCache"; import { InteractionCache } from "./EventInteractionCache"; @@ -37,7 +37,7 @@ export interface MetadataCache extends UserMetadata { isNostrAddressValid: boolean; } -export function mapEventToProfile(ev: RawEvent) { +export function mapEventToProfile(ev: NostrEvent) { try { const data: UserMetadata = JSON.parse(ev.content); return { diff --git a/packages/app/src/Db/index.ts b/packages/app/src/Db/index.ts index 25f0e788..9a72e8f3 100644 --- a/packages/app/src/Db/index.ts +++ b/packages/app/src/Db/index.ts @@ -1,5 +1,5 @@ import Dexie, { Table } from "dexie"; -import { FullRelaySettings, HexKey, RawEvent, u256 } from "System"; +import { FullRelaySettings, HexKey, NostrEvent, u256 } from "System"; import { MetadataCache } from "Cache"; export const NAME = "snortDB"; @@ -48,8 +48,8 @@ export class SnortDB extends Dexie { users!: Table; relayMetrics!: Table; userRelays!: Table; - events!: Table; - dms!: Table; + events!: Table; + dms!: Table; eventInteraction!: Table; constructor() { diff --git a/packages/app/src/Element/NostrFileHeader.tsx b/packages/app/src/Element/NostrFileHeader.tsx index 49531b0c..512d09df 100644 --- a/packages/app/src/Element/NostrFileHeader.tsx +++ b/packages/app/src/Element/NostrFileHeader.tsx @@ -1,5 +1,5 @@ import { FormattedMessage } from "react-intl"; -import { RawEvent } from "System"; +import { NostrEvent } from "System"; import { findTag, NostrLink } from "SnortUtils"; import useEventFeed from "Feed/EventFeed"; @@ -14,7 +14,7 @@ export default function NostrFileHeader({ link }: { link: NostrLink }) { return ; } -export function NostrFileElement({ ev }: { ev: RawEvent }) { +export function NostrFileElement({ ev }: { ev: NostrEvent }) { // assume image or embed which can be rendered by the hypertext kind // todo: make use of hash // todo: use magnet or other links if present diff --git a/packages/app/src/Element/NoteReaction.tsx b/packages/app/src/Element/NoteReaction.tsx index 236b3772..1849c904 100644 --- a/packages/app/src/Element/NoteReaction.tsx +++ b/packages/app/src/Element/NoteReaction.tsx @@ -1,7 +1,7 @@ import "./NoteReaction.css"; import { Link } from "react-router-dom"; import { useMemo } from "react"; -import { EventKind, RawEvent, TaggedRawEvent, NostrPrefix } from "System"; +import { EventKind, NostrEvent, TaggedRawEvent, NostrPrefix } from "System"; import Note from "Element/Note"; import ProfileImage from "Element/ProfileImage"; @@ -43,7 +43,7 @@ export default function NoteReaction(props: NoteReactionProps) { function extractRoot() { if (ev?.kind === EventKind.Repost && ev.content.length > 0 && ev.content !== "#[0]") { try { - const r: RawEvent = JSON.parse(ev.content); + const r: NostrEvent = JSON.parse(ev.content); return r as TaggedRawEvent; } catch (e) { console.error("Could not load reposted content", e); diff --git a/packages/app/src/Element/PubkeyList.tsx b/packages/app/src/Element/PubkeyList.tsx index 17a3b161..6350af3f 100644 --- a/packages/app/src/Element/PubkeyList.tsx +++ b/packages/app/src/Element/PubkeyList.tsx @@ -1,8 +1,8 @@ -import { RawEvent } from "System"; +import { NostrEvent } from "System"; import { dedupe } from "SnortUtils"; import FollowListBase from "./FollowListBase"; -export default function PubkeyList({ ev, className }: { ev: RawEvent; className?: string }) { +export default function PubkeyList({ ev, className }: { ev: NostrEvent; className?: string }) { const ids = dedupe(ev.tags.filter(a => a[0] === "p").map(a => a[1])); return ; } diff --git a/packages/app/src/Element/SendSats.tsx b/packages/app/src/Element/SendSats.tsx index 93d8e240..765c5929 100644 --- a/packages/app/src/Element/SendSats.tsx +++ b/packages/app/src/Element/SendSats.tsx @@ -2,7 +2,7 @@ import "./SendSats.css"; import React, { useEffect, useMemo, useState } from "react"; import { useIntl, FormattedMessage } from "react-intl"; -import { HexKey, RawEvent } from "System"; +import { HexKey, NostrEvent } from "System"; import { System } from "index"; import { formatShort } from "Number"; import Icon from "Icons/Icon"; @@ -125,7 +125,7 @@ export default function SendSats(props: SendSatsProps) { async function loadInvoice() { if (!amount || !handler || !publisher) return null; - let zap: RawEvent | undefined; + let zap: NostrEvent | undefined; if (author && zapType !== ZapType.NonZap) { const relays = Object.keys(login.relays.item); diff --git a/packages/app/src/Element/SubDebug.tsx b/packages/app/src/Element/SubDebug.tsx index acf16e47..5ce1d17f 100644 --- a/packages/app/src/Element/SubDebug.tsx +++ b/packages/app/src/Element/SubDebug.tsx @@ -5,7 +5,7 @@ import useRelayState from "Feed/RelayState"; import Tabs, { Tab } from "Element/Tabs"; import { unwrap } from "SnortUtils"; import useSystemState from "Hooks/useSystemState"; -import { RawReqFilter } from "System"; +import { ReqFilter } from "System"; import { useCopy } from "useCopy"; import { System } from "index"; @@ -18,7 +18,7 @@ function Queries() { const qs = useSystemState(); const { copy } = useCopy(); - function countElements(filters: Array) { + function countElements(filters: Array) { let total = 0; for (const f of filters) { for (const v of Object.values(f)) { @@ -30,12 +30,7 @@ function Queries() { return total; } - function queryInfo(q: { - id: string; - filters: Array; - closing: boolean; - subFilters: Array; - }) { + function queryInfo(q: { id: string; filters: Array; closing: boolean; subFilters: Array }) { return (
{q.closing ? {q.id} : <>{q.id}} diff --git a/packages/app/src/Element/TrendingPosts.tsx b/packages/app/src/Element/TrendingPosts.tsx index 98ceee3d..c46c6e73 100644 --- a/packages/app/src/Element/TrendingPosts.tsx +++ b/packages/app/src/Element/TrendingPosts.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { RawEvent, TaggedRawEvent } from "System"; +import { NostrEvent, TaggedRawEvent } from "System"; import { FormattedMessage } from "react-intl"; import PageSpinner from "Element/PageSpinner"; @@ -7,7 +7,7 @@ import Note from "Element/Note"; import NostrBandApi from "External/NostrBand"; export default function TrendingNotes() { - const [posts, setPosts] = useState>(); + const [posts, setPosts] = useState>(); async function loadTrendingNotes() { const api = new NostrBandApi(); diff --git a/packages/app/src/Element/WriteDm.tsx b/packages/app/src/Element/WriteDm.tsx index 4a594f2e..445437bd 100644 --- a/packages/app/src/Element/WriteDm.tsx +++ b/packages/app/src/Element/WriteDm.tsx @@ -1,4 +1,4 @@ -import { encodeTLV, NostrPrefix, RawEvent } from "System"; +import { encodeTLV, NostrPrefix, NostrEvent } from "System"; import useEventPublisher from "Feed/EventPublisher"; import Icon from "Icons/Icon"; import Spinner from "Icons/Spinner"; @@ -11,7 +11,7 @@ export default function WriteDm({ chatPubKey }: { chatPubKey: string }) { const [msg, setMsg] = useState(""); const [sending, setSending] = useState(false); const [uploading, setUploading] = useState(false); - const [otherEvents, setOtherEvents] = useState>([]); + const [otherEvents, setOtherEvents] = useState>([]); const [error, setError] = useState(""); const publisher = useEventPublisher(); const uploader = useFileUpload(); diff --git a/packages/app/src/Element/ZapstrEmbed.tsx b/packages/app/src/Element/ZapstrEmbed.tsx index 84068e39..d5ca7f01 100644 --- a/packages/app/src/Element/ZapstrEmbed.tsx +++ b/packages/app/src/Element/ZapstrEmbed.tsx @@ -1,12 +1,12 @@ import "./ZapstrEmbed.css"; import { Link } from "react-router-dom"; -import { encodeTLV, NostrPrefix, RawEvent } from "System"; +import { encodeTLV, NostrPrefix, NostrEvent } from "System"; import { ProxyImg } from "Element/ProxyImg"; import ProfileImage from "Element/ProfileImage"; import { FormattedMessage } from "react-intl"; -export default function ZapstrEmbed({ ev }: { ev: RawEvent }) { +export default function ZapstrEmbed({ ev }: { ev: NostrEvent }) { const media = ev.tags.find(a => a[0] === "media"); const cover = ev.tags.find(a => a[0] === "cover"); const subject = ev.tags.find(a => a[0] === "subject"); diff --git a/packages/app/src/External/NostrBand.ts b/packages/app/src/External/NostrBand.ts index eeb00c9a..8615b61e 100644 --- a/packages/app/src/External/NostrBand.ts +++ b/packages/app/src/External/NostrBand.ts @@ -1,4 +1,4 @@ -import { RawEvent } from "System"; +import { NostrEvent } from "System"; export interface TrendingUser { pubkey: string; @@ -9,8 +9,8 @@ export interface TrendingUserResponse { } export interface TrendingNote { - event: RawEvent; - author: RawEvent; // kind0 event + event: NostrEvent; + author: NostrEvent; // kind0 event } export interface TrendingNoteResponse { diff --git a/packages/app/src/LNURL.ts b/packages/app/src/LNURL.ts index 9a0e75d7..4138bb93 100644 --- a/packages/app/src/LNURL.ts +++ b/packages/app/src/LNURL.ts @@ -1,4 +1,4 @@ -import { HexKey, RawEvent } from "System"; +import { HexKey, NostrEvent } from "System"; import { EmailRegex } from "Const"; import { bech32ToText, unwrap } from "SnortUtils"; @@ -119,7 +119,7 @@ export class LNURL { * @param zap * @returns */ - async getInvoice(amount: number, comment?: string, zap?: RawEvent) { + async getInvoice(amount: number, comment?: string, zap?: NostrEvent) { const callback = new URL(unwrap(this.#service?.callback)); const query = new Map(); diff --git a/packages/app/src/Pages/MessagesPage.tsx b/packages/app/src/Pages/MessagesPage.tsx index 831fddd3..34f83c7d 100644 --- a/packages/app/src/Pages/MessagesPage.tsx +++ b/packages/app/src/Pages/MessagesPage.tsx @@ -1,7 +1,7 @@ import React, { useMemo, useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { useNavigate } from "react-router-dom"; -import { HexKey, RawEvent, NostrPrefix } from "System"; +import { HexKey, NostrEvent, NostrPrefix } from "System"; import UnreadCount from "Element/UnreadCount"; import ProfileImage, { getDisplayName } from "Element/ProfileImage"; @@ -162,30 +162,30 @@ export function setLastReadDm(pk: HexKey) { window.localStorage.setItem(k, now.toString()); } -export function dmTo(e: RawEvent) { +export function dmTo(e: NostrEvent) { const firstP = e.tags.find(b => b[0] === "p"); return unwrap(firstP?.[1]); } -export function isToSelf(e: Readonly, pk: HexKey) { +export function isToSelf(e: Readonly, pk: HexKey) { return e.pubkey === pk && dmTo(e) === pk; } -export function dmsInChat(dms: readonly RawEvent[], pk: HexKey) { +export function dmsInChat(dms: readonly NostrEvent[], pk: HexKey) { return dms.filter(a => a.pubkey === pk || dmTo(a) === pk); } -export function totalUnread(dms: RawEvent[], myPubKey: HexKey) { +export function totalUnread(dms: NostrEvent[], myPubKey: HexKey) { return extractChats(dms, myPubKey).reduce((acc, v) => (acc += v.unreadMessages), 0); } -function unreadDms(dms: RawEvent[], myPubKey: HexKey, pk: HexKey) { +function unreadDms(dms: NostrEvent[], myPubKey: HexKey, pk: HexKey) { if (pk === myPubKey) return 0; const lastRead = lastReadDm(pk); return dmsInChat(dms, pk).filter(a => a.created_at >= lastRead && a.pubkey !== myPubKey).length; } -function newestMessage(dms: readonly RawEvent[], myPubKey: HexKey, pk: HexKey) { +function newestMessage(dms: readonly NostrEvent[], myPubKey: HexKey, pk: HexKey) { if (pk === myPubKey) { return dmsInChat( dms.filter(d => isToSelf(d, myPubKey)), @@ -196,11 +196,11 @@ function newestMessage(dms: readonly RawEvent[], myPubKey: HexKey, pk: HexKey) { return dmsInChat(dms, pk).reduce((acc, v) => (acc = v.created_at > acc ? v.created_at : acc), 0); } -export function dmsForLogin(dms: readonly RawEvent[], myPubKey: HexKey) { +export function dmsForLogin(dms: readonly NostrEvent[], myPubKey: HexKey) { return dms.filter(a => a.pubkey === myPubKey || (a.pubkey !== myPubKey && dmTo(a) === myPubKey)); } -export function extractChats(dms: RawEvent[], myPubKey: HexKey) { +export function extractChats(dms: NostrEvent[], myPubKey: HexKey) { const myDms = dmsForLogin(dms, myPubKey); const keys = myDms.map(a => [a.pubkey, dmTo(a)]).flat(); const filteredKeys = dedupe(keys); diff --git a/packages/app/src/SnortUtils/index.ts b/packages/app/src/SnortUtils/index.ts index 42980866..5ff04ed4 100644 --- a/packages/app/src/SnortUtils/index.ts +++ b/packages/app/src/SnortUtils/index.ts @@ -15,7 +15,7 @@ import { NostrPrefix, decodeTLV, TLVEntryType, - RawEvent, + NostrEvent, } from "System"; import { MetadataCache } from "Cache"; import NostrLink from "Element/NostrLink"; @@ -482,7 +482,7 @@ export function chunks(arr: T[], length: number) { return result; } -export function findTag(e: RawEvent, tag: string) { +export function findTag(e: NostrEvent, tag: string) { const maybeTag = e.tags.find(evTag => { return evTag[0] === tag; }); diff --git a/packages/app/src/State/NoteCreator.ts b/packages/app/src/State/NoteCreator.ts index e4941bb6..f0bf02fd 100644 --- a/packages/app/src/State/NoteCreator.ts +++ b/packages/app/src/State/NoteCreator.ts @@ -1,19 +1,19 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { RawEvent, TaggedRawEvent } from "System"; +import { NostrEvent, TaggedRawEvent } from "System"; interface NoteCreatorStore { show: boolean; note: string; error: string; active: boolean; - preview?: RawEvent; + preview?: NostrEvent; replyTo?: TaggedRawEvent; showAdvanced: boolean; selectedCustomRelays: false | Array; zapForward: string; sensitive: string; pollOptions?: Array; - otherEvents: Array; + otherEvents: Array; } const InitState: NoteCreatorStore = { @@ -44,7 +44,7 @@ const NoteCreatorSlice = createSlice({ setActive: (state, action: PayloadAction) => { state.active = action.payload; }, - setPreview: (state, action: PayloadAction) => { + setPreview: (state, action: PayloadAction) => { state.preview = action.payload; }, setReplyTo: (state, action: PayloadAction) => { @@ -65,7 +65,7 @@ const NoteCreatorSlice = createSlice({ setPollOptions: (state, action: PayloadAction | undefined>) => { state.pollOptions = action.payload; }, - setOtherEvents: (state, action: PayloadAction>) => { + setOtherEvents: (state, action: PayloadAction>) => { state.otherEvents = action.payload; }, reset: () => InitState, diff --git a/packages/app/src/State/ReBroadcast.ts b/packages/app/src/State/ReBroadcast.ts index 97b12d57..76d0165d 100644 --- a/packages/app/src/State/ReBroadcast.ts +++ b/packages/app/src/State/ReBroadcast.ts @@ -1,10 +1,10 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { RawEvent } from "System"; +import { NostrEvent } from "System"; interface ReBroadcastStore { show: boolean; selectedCustomRelays: false | Array; - note?: RawEvent; + note?: NostrEvent; } const InitState: ReBroadcastStore = { @@ -19,7 +19,7 @@ const ReBroadcastSlice = createSlice({ setShow: (state, action: PayloadAction) => { state.show = action.payload; }, - setNote: (state, action: PayloadAction) => { + setNote: (state, action: PayloadAction) => { state.note = action.payload; }, setSelectedCustomRelays: (state, action: PayloadAction>) => { diff --git a/packages/app/src/System/Connection.ts b/packages/app/src/System/Connection.ts index 75e2832b..109410e6 100644 --- a/packages/app/src/System/Connection.ts +++ b/packages/app/src/System/Connection.ts @@ -2,12 +2,12 @@ import { v4 as uuid } from "uuid"; import { DefaultConnectTimeout } from "./Const"; import { ConnectionStats } from "./ConnectionStats"; -import { RawEvent, ReqCommand, TaggedRawEvent, u256 } from "./Nostr"; +import { NostrEvent, ReqCommand, TaggedRawEvent, u256 } from "./Nostr"; import { RelayInfo } from "./RelayInfo"; import { unwrap } from "./Util"; import ExternalStore from "ExternalStore"; -export type AuthHandler = (challenge: string, relay: string) => Promise; +export type AuthHandler = (challenge: string, relay: string) => Promise; /** * Relay settings @@ -232,7 +232,7 @@ export class Connection extends ExternalStore { /** * Send event on this connection */ - SendEvent(e: RawEvent) { + SendEvent(e: NostrEvent) { if (!this.Settings.write) { return; } @@ -245,7 +245,7 @@ export class Connection extends ExternalStore { /** * Send event on this connection and wait for OK response */ - async SendAsync(e: RawEvent, timeout = 5000) { + async SendAsync(e: NostrEvent, timeout = 5000) { return new Promise(resolve => { if (!this.Settings.write) { resolve(); diff --git a/packages/app/src/System/EventBuilder.ts b/packages/app/src/System/EventBuilder.ts index ea7c1df8..73a285e1 100644 --- a/packages/app/src/System/EventBuilder.ts +++ b/packages/app/src/System/EventBuilder.ts @@ -1,4 +1,4 @@ -import { EventKind, HexKey, NostrPrefix, RawEvent } from "System"; +import { EventKind, HexKey, NostrPrefix, NostrEvent } from "System"; import { HashtagRegex } from "Const"; import { getPublicKey, parseNostrLink, unixNow } from "SnortUtils"; import { EventExt } from "./EventExt"; @@ -63,7 +63,7 @@ export class EventBuilder { kind: this.#kind, created_at: this.#createdAt ?? unixNow(), tags: this.#tags, - } as RawEvent; + } as NostrEvent; ev.id = EventExt.createId(ev); return ev; } diff --git a/packages/app/src/System/EventExt.ts b/packages/app/src/System/EventExt.ts index ca2b34ee..ebbf0f15 100644 --- a/packages/app/src/System/EventExt.ts +++ b/packages/app/src/System/EventExt.ts @@ -1,6 +1,6 @@ import * as secp from "@noble/curves/secp256k1"; import * as utils from "@noble/curves/abstract/utils"; -import { EventKind, HexKey, RawEvent, Tag } from "System"; +import { EventKind, HexKey, NostrEvent, Tag } from "System"; import base64 from "@protobufjs/base64"; import { sha256, unixNow } from "SnortUtils"; @@ -15,7 +15,7 @@ export abstract class EventExt { /** * Get the pub key of the creator of this event NIP-26 */ - static getRootPubKey(e: RawEvent): HexKey { + static getRootPubKey(e: NostrEvent): HexKey { const delegation = e.tags.find(a => a[0] === "delegation"); if (delegation?.[1]) { return delegation[1]; @@ -26,7 +26,7 @@ export abstract class EventExt { /** * Sign this message with a private key */ - static async sign(e: RawEvent, key: HexKey) { + static async sign(e: NostrEvent, key: HexKey) { e.id = this.createId(e); const sig = await secp.schnorr.sign(e.id, key); @@ -40,13 +40,13 @@ export abstract class EventExt { * Check the signature of this message * @returns True if valid signature */ - static async verify(e: RawEvent) { + static async verify(e: NostrEvent) { const id = this.createId(e); const result = await secp.schnorr.verify(e.sig, id, e.pubkey); return result; } - static createId(e: RawEvent) { + static createId(e: NostrEvent) { const payload = [0, e.pubkey, e.created_at, e.kind, e.tags, e.content]; const hash = sha256(JSON.stringify(payload)); @@ -69,10 +69,10 @@ export abstract class EventExt { tags: [], id: "", sig: "", - } as RawEvent; + } as NostrEvent; } - static extractThread(ev: RawEvent) { + static extractThread(ev: NostrEvent) { const isThread = ev.tags.some(a => (a[0] === "e" && a[3] !== "mention") || a[0] == "a"); if (!isThread) { return undefined; diff --git a/packages/app/src/System/EventPublisher.ts b/packages/app/src/System/EventPublisher.ts index 5fecf80f..8aa29bad 100644 --- a/packages/app/src/System/EventPublisher.ts +++ b/packages/app/src/System/EventPublisher.ts @@ -5,7 +5,7 @@ import { FullRelaySettings, HexKey, Lists, - RawEvent, + NostrEvent, RelaySettings, SystemInterface, TaggedRawEvent, @@ -27,7 +27,7 @@ declare global { interface Window { nostr?: { getPublicKey: () => Promise; - signEvent: (event: T) => Promise; + signEvent: (event: T) => Promise; getRelays?: () => Promise>; @@ -113,7 +113,7 @@ export class EventPublisher { return await this.#sign(eb); } - broadcast(ev: RawEvent) { + broadcast(ev: NostrEvent) { console.debug(ev); this.#system.BroadcastEvent(ev); } @@ -123,7 +123,7 @@ export class EventPublisher { * If a user removes all the DefaultRelays from their relay list and saves that relay list, * When they open the site again we wont see that updated relay list and so it will appear to reset back to the previous state */ - broadcastForBootstrap(ev: RawEvent) { + broadcastForBootstrap(ev: NostrEvent) { for (const [k] of DefaultRelays) { this.#system.WriteOnceToRelay(k, ev); } @@ -132,7 +132,7 @@ export class EventPublisher { /** * Write event to all given relays. */ - broadcastAll(ev: RawEvent, relays: string[]) { + broadcastAll(ev: NostrEvent, relays: string[]) { for (const k of relays) { this.#system.WriteOnceToRelay(k, ev); } @@ -249,7 +249,7 @@ export class EventPublisher { return await this.#sign(eb); } - async react(evRef: RawEvent, content = "+") { + async react(evRef: NostrEvent, content = "+") { const eb = this.#eb(EventKind.Reaction); eb.content(content); eb.tag(["e", evRef.id]); @@ -298,14 +298,14 @@ export class EventPublisher { /** * Repost a note (NIP-18) */ - async repost(note: RawEvent) { + async repost(note: NostrEvent) { const eb = this.#eb(EventKind.Repost); eb.tag(["e", note.id, ""]); eb.tag(["p", note.pubkey]); return await this.#sign(eb); } - async decryptDm(note: RawEvent) { + async decryptDm(note: NostrEvent) { if (note.pubkey !== this.#pubKey && !note.tags.some(a => a[1] === this.#pubKey)) { throw new Error("Can't decrypt, DM does not belong to this user"); } diff --git a/packages/app/src/System/GossipModel.ts b/packages/app/src/System/GossipModel.ts index 3a6db619..5cdd2d41 100644 --- a/packages/app/src/System/GossipModel.ts +++ b/packages/app/src/System/GossipModel.ts @@ -1,4 +1,4 @@ -import { FullRelaySettings, RawReqFilter } from "System"; +import { FullRelaySettings, ReqFilter } from "System"; import { unwrap } from "SnortUtils"; import debug from "debug"; @@ -6,19 +6,19 @@ const PickNRelays = 2; export interface RelayTaggedFilter { relay: string; - filter: RawReqFilter; + filter: ReqFilter; } export interface RelayTaggedFilters { relay: string; - filters: Array; + filters: Array; } export interface RelayCache { get(pubkey?: string): Array | undefined; } -export function splitAllByWriteRelays(cache: RelayCache, filters: Array) { +export function splitAllByWriteRelays(cache: RelayCache, filters: Array) { const allSplit = filters .map(a => splitByWriteRelays(cache, a)) .reduce((acc, v) => { @@ -31,7 +31,7 @@ export function splitAllByWriteRelays(cache: RelayCache, filters: Array>()); + }, new Map>()); return [...allSplit.entries()].map(([k, v]) => { return { @@ -46,7 +46,7 @@ export function splitAllByWriteRelays(cache: RelayCache, filters: Array { +export function splitByWriteRelays(cache: RelayCache, filter: ReqFilter): Array { if ((filter.authors?.length ?? 0) === 0) return [ { diff --git a/packages/app/src/System/Nostr.ts b/packages/app/src/System/Nostr.ts index 8e36ae3b..86e66597 100644 --- a/packages/app/src/System/Nostr.ts +++ b/packages/app/src/System/Nostr.ts @@ -1,6 +1,6 @@ import { RelaySettings } from "./Connection"; -export type RawEvent = { +export interface NostrEvent { id: u256; pubkey: HexKey; created_at: number; @@ -8,9 +8,9 @@ export type RawEvent = { tags: Array>; content: string; sig: string; -}; +} -export interface TaggedRawEvent extends RawEvent { +export interface TaggedRawEvent extends NostrEvent { /** * A list of relays this event was seen on */ @@ -32,12 +32,12 @@ export type MaybeHexKey = HexKey | undefined; */ export type u256 = string; -export type ReqCommand = [cmd: "REQ", id: string, ...filters: Array]; +export type ReqCommand = [cmd: "REQ", id: string, ...filters: Array]; /** * Raw REQ filter object */ -export type RawReqFilter = { +export interface ReqFilter { ids?: u256[]; authors?: u256[]; kinds?: number[]; @@ -50,7 +50,7 @@ export type RawReqFilter = { since?: number; until?: number; limit?: number; -}; +} /** * Medatadata event content diff --git a/packages/app/src/System/NostrSystem.ts b/packages/app/src/System/NostrSystem.ts index 9af2e9b2..16c9f14b 100644 --- a/packages/app/src/System/NostrSystem.ts +++ b/packages/app/src/System/NostrSystem.ts @@ -2,7 +2,7 @@ import debug from "debug"; import { v4 as uuid } from "uuid"; import ExternalStore from "ExternalStore"; -import { RawEvent, RawReqFilter, TaggedRawEvent } from "./Nostr"; +import { NostrEvent, ReqFilter, TaggedRawEvent } from "./Nostr"; import { AuthHandler, Connection, RelaySettings, ConnectionStateSnapshot } from "./Connection"; import { Query, QueryBase } from "./Query"; import { RelayCache } from "./GossipModel"; @@ -194,7 +194,7 @@ export class NostrSystem extends ExternalStore implements System /** * Send events to writable relays */ - BroadcastEvent(ev: RawEvent) { + BroadcastEvent(ev: NostrEvent) { for (const [, s] of this.#sockets) { s.SendEvent(ev); } @@ -203,7 +203,7 @@ export class NostrSystem extends ExternalStore implements System /** * Write an event to a relay then disconnect */ - async WriteOnceToRelay(address: string, ev: RawEvent) { + async WriteOnceToRelay(address: string, ev: NostrEvent) { return new Promise((resolve, reject) => { const c = new Connection(address, { write: true, read: false }, this.HandleAuth, true); diff --git a/packages/app/src/System/Query.ts b/packages/app/src/System/Query.ts index 305ebe77..065eb7ba 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/app/src/System/Query.ts @@ -1,6 +1,6 @@ import { v4 as uuid } from "uuid"; import debug from "debug"; -import { Connection, RawReqFilter, Nips, TaggedRawEvent } from "System"; +import { Connection, ReqFilter, Nips, TaggedRawEvent } from "System"; import { unixNowMs, unwrap } from "SnortUtils"; import { NoteStore } from "./NoteCollection"; import { simpleMerge } from "./RequestMerger"; @@ -22,7 +22,7 @@ class QueryTrace { constructor( readonly relay: string, - readonly filters: Array, + readonly filters: Array, readonly connId: string, fnClose: (id: string) => void, fnProgress: () => void @@ -94,7 +94,7 @@ export interface QueryBase { /** * The query payload (REQ filters) */ - filters: Array; + filters: Array; /** * List of relays to send this query to diff --git a/packages/app/src/System/RequestBuilder.ts b/packages/app/src/System/RequestBuilder.ts index 2ee6e7ef..dfbbb8b5 100644 --- a/packages/app/src/System/RequestBuilder.ts +++ b/packages/app/src/System/RequestBuilder.ts @@ -1,7 +1,8 @@ -import { RawReqFilter, u256, HexKey, EventKind } from "System"; +import { ReqFilter, u256, HexKey, EventKind } from "System"; import { appendDedupe, dedupe } from "SnortUtils"; import { diffFilters } from "./RequestSplitter"; import { RelayCache, splitAllByWriteRelays, splitByWriteRelays } from "./GossipModel"; +import { mergeSimilar } from "./RequestMerger"; /** * Which strategy is used when building REQ filters @@ -28,7 +29,7 @@ export enum RequestStrategy { * A built REQ filter ready for sending to System */ export interface BuiltRawReqFilter { - filters: Array; + filters: Array; relay: string; strategy: RequestStrategy; } @@ -77,7 +78,7 @@ export class RequestBuilder { return this; } - buildRaw(): Array { + buildRaw(): Array { return this.#builders.map(f => f.filter); } @@ -91,11 +92,11 @@ export class RequestBuilder { * @param q All previous filters merged * @returns */ - buildDiff(relays: RelayCache, filters: Array): Array { + buildDiff(relays: RelayCache, filters: Array): Array { const next = this.buildRaw(); const diff = diffFilters(filters, next); if (diff.changed) { - return splitAllByWriteRelays(relays, diff.filters).map(a => { + return splitAllByWriteRelays(relays, diff.added).map(a => { return { strategy: RequestStrategy.AuthorsRelays, filters: a.filters, @@ -124,7 +125,7 @@ export class RequestBuilder { const filtersSquashed = [...relayMerged.values()].map(a => { return { - filters: a.flatMap(b => b.filters), + filters: mergeSimilar(a.flatMap(b => b.filters)), relay: a[0].relay, strategy: a[0].strategy, } as BuiltRawReqFilter; @@ -138,7 +139,7 @@ export class RequestBuilder { * Builder class for a single request filter */ export class RequestFilterBuilder { - #filter: RawReqFilter = {}; + #filter: ReqFilter = {}; #relayHints = new Map>(); get filter() { diff --git a/packages/app/src/System/RequestExpander.test.ts b/packages/app/src/System/RequestExpander.test.ts new file mode 100644 index 00000000..0ccb58d1 --- /dev/null +++ b/packages/app/src/System/RequestExpander.test.ts @@ -0,0 +1,34 @@ +import { expandFilter } from "./RequestExpander"; + +describe("RequestExpander", () => { + test("expand filter", () => { + const a = { + authors: ["a", "b", "c"], + kinds: [1, 2, 3], + ids: ["x", "y"], + "#p": ["a"], + since: 99, + limit: 10, + }; + expect(expandFilter(a)).toEqual([ + { authors: "a", kinds: 1, ids: "x", "#p": "a", since: 99, limit: 10 }, + { authors: "a", kinds: 1, ids: "y", "#p": "a", since: 99, limit: 10 }, + { authors: "a", kinds: 2, ids: "x", "#p": "a", since: 99, limit: 10 }, + { authors: "a", kinds: 2, ids: "y", "#p": "a", since: 99, limit: 10 }, + { authors: "a", kinds: 3, ids: "x", "#p": "a", since: 99, limit: 10 }, + { authors: "a", kinds: 3, ids: "y", "#p": "a", since: 99, limit: 10 }, + { authors: "b", kinds: 1, ids: "x", "#p": "a", since: 99, limit: 10 }, + { authors: "b", kinds: 1, ids: "y", "#p": "a", since: 99, limit: 10 }, + { authors: "b", kinds: 2, ids: "x", "#p": "a", since: 99, limit: 10 }, + { authors: "b", kinds: 2, ids: "y", "#p": "a", since: 99, limit: 10 }, + { authors: "b", kinds: 3, ids: "x", "#p": "a", since: 99, limit: 10 }, + { authors: "b", kinds: 3, ids: "y", "#p": "a", since: 99, limit: 10 }, + { authors: "c", kinds: 1, ids: "x", "#p": "a", since: 99, limit: 10 }, + { authors: "c", kinds: 1, ids: "y", "#p": "a", since: 99, limit: 10 }, + { authors: "c", kinds: 2, ids: "x", "#p": "a", since: 99, limit: 10 }, + { authors: "c", kinds: 2, ids: "y", "#p": "a", since: 99, limit: 10 }, + { authors: "c", kinds: 3, ids: "x", "#p": "a", since: 99, limit: 10 }, + { authors: "c", kinds: 3, ids: "y", "#p": "a", since: 99, limit: 10 }, + ]); + }); +}); diff --git a/packages/app/src/System/RequestExpander.ts b/packages/app/src/System/RequestExpander.ts new file mode 100644 index 00000000..8aff4238 --- /dev/null +++ b/packages/app/src/System/RequestExpander.ts @@ -0,0 +1,48 @@ +import { u256, ReqFilter } from "./Nostr"; + +export interface FlatReqFilter { + ids?: u256; + authors?: u256; + kinds?: number; + "#e"?: u256; + "#p"?: u256; + "#t"?: string; + "#d"?: string; + "#r"?: string; + search?: string; + since?: number; + until?: number; + limit?: number; +} + +/** + * Expand a filter into its most fine grained form + */ +export function expandFilter(f: ReqFilter): Array { + const ret: Array = []; + const src = Object.entries(f); + const keys = src.filter(([, v]) => Array.isArray(v)).map(a => a[0]); + const props = src.filter(([, v]) => !Array.isArray(v)); + + function generateCombinations(index: number, currentCombination: FlatReqFilter) { + if (index === keys.length) { + ret.push(currentCombination); + return; + } + + const key = keys[index]; + const values = (f as Record>)[key]; + + for (let i = 0; i < values.length; i++) { + const value = values[i]; + const updatedCombination = { ...currentCombination, [key]: value }; + generateCombinations(index + 1, updatedCombination); + } + } + + generateCombinations(0, { + ...Object.fromEntries(props), + }); + + return ret; +} diff --git a/packages/app/src/System/RequestMatcher.ts b/packages/app/src/System/RequestMatcher.ts index e8ffc450..8845702d 100644 --- a/packages/app/src/System/RequestMatcher.ts +++ b/packages/app/src/System/RequestMatcher.ts @@ -1,6 +1,6 @@ -import { RawEvent, RawReqFilter } from "./Nostr"; +import { NostrEvent, ReqFilter } from "./Nostr"; -export function eventMatchesFilter(ev: RawEvent, filter: RawReqFilter) { +export function eventMatchesFilter(ev: NostrEvent, filter: ReqFilter) { if (!(filter.ids?.includes(ev.id) ?? false)) { return false; } diff --git a/packages/app/src/System/RequestMerger.test.ts b/packages/app/src/System/RequestMerger.test.ts index 2de15e7d..46260789 100644 --- a/packages/app/src/System/RequestMerger.test.ts +++ b/packages/app/src/System/RequestMerger.test.ts @@ -1,17 +1,19 @@ -import { RawReqFilter } from "System"; -import { filterIncludes, mergeSimilar, simpleMerge } from "./RequestMerger"; +import { ReqFilter } from "System"; +import { filterIncludes, flatMerge, mergeSimilar, simpleMerge } from "./RequestMerger"; +import { FlatReqFilter, expandFilter } from "./RequestExpander"; +import { distance } from "./Util"; describe("RequestMerger", () => { it("should simple merge authors", () => { const a = { authors: ["a"], - } as RawReqFilter; + } as ReqFilter; const b = { authors: ["b"], - } as RawReqFilter; + } as ReqFilter; const merged = mergeSimilar([a, b]); - expect(merged).toMatchObject([ + expect(merged).toEqual([ { authors: ["a", "b"], }, @@ -21,17 +23,17 @@ describe("RequestMerger", () => { it("should append non-mergable filters", () => { const a = { authors: ["a"], - } as RawReqFilter; + } as ReqFilter; const b = { authors: ["b"], - } as RawReqFilter; + } as ReqFilter; const c = { limit: 5, authors: ["a"], }; const merged = mergeSimilar([a, b, c]); - expect(merged).toMatchObject([ + expect(merged).toEqual([ { authors: ["a", "b"], }, @@ -46,11 +48,11 @@ describe("RequestMerger", () => { const bigger = { authors: ["a", "b", "c"], since: 99, - } as RawReqFilter; + } as ReqFilter; const smaller = { authors: ["c"], since: 100, - } as RawReqFilter; + } as ReqFilter; expect(filterIncludes(bigger, smaller)).toBe(true); }); @@ -58,14 +60,50 @@ describe("RequestMerger", () => { const a = { authors: ["a", "b", "c"], since: 99, - } as RawReqFilter; + } as ReqFilter; const b = { authors: ["c", "d", "e"], since: 100, - } as RawReqFilter; + } as ReqFilter; expect(simpleMerge([a, b])).toEqual({ authors: ["a", "b", "c", "d", "e"], since: 100, }); }); }); + +describe("flatMerge", () => { + it("should flat merge simple", () => { + const input = [ + { ids: 0, authors: "a" }, + { ids: 0, authors: "b" }, + { kinds: 1 }, + { kinds: 2 }, + { ids: 0, authors: "c" }, + { authors: "c", kinds: 1 }, + { authors: "c", limit: 100 }, + { ids: 1, authors: "c" }, + ] as Array; + const output = [ + { ids: [0], authors: ["a", "b", "c"] }, + { kinds: [1, 2] }, + { authors: ["c"], kinds: [1] }, + { authors: ["c"], limit: 100 }, + { ids: [1], authors: ["c"] }, + ] as Array; + + expect(flatMerge(input)).toEqual(output); + }); + + it("should expand and flat merge complex same", () => { + const input = [ + { kinds: [1, 6969, 6], authors: ["kieran", "snort", "c", "d", "e"], since: 1, until: 100 }, + { kinds: [4], authors: ["kieran"] }, + { kinds: [4], "#p": ["kieran"] }, + { kinds: [1000], authors: ["snort"], "#p": ["kieran"] }, + ] as Array; + + const dut = flatMerge(input.flatMap(expandFilter).sort(() => (Math.random() > 0.5 ? 1 : -1))); + expect(dut.every(a => input.some(b => distance(b, a) === 0))).toEqual(true); + }); +}); diff --git a/packages/app/src/System/RequestMerger.ts b/packages/app/src/System/RequestMerger.ts index a33f0226..acc359ab 100644 --- a/packages/app/src/System/RequestMerger.ts +++ b/packages/app/src/System/RequestMerger.ts @@ -1,12 +1,40 @@ -import { RawReqFilter } from "System"; +import { ReqFilter } from "System"; +import { FlatReqFilter } from "./RequestExpander"; +import { distance } from "./Util"; -export function mergeSimilar(filters: Array): Array { - const hasCriticalKeySet = (a: RawReqFilter) => { - return a.limit !== undefined || a.since !== undefined || a.until !== undefined; - }; - const canEasilyMerge = filters.filter(a => !hasCriticalKeySet(a)); - const cannotMerge = filters.filter(a => hasCriticalKeySet(a)); - return [...(canEasilyMerge.length > 0 ? [simpleMerge(canEasilyMerge)] : []), ...cannotMerge]; +/** + * Keys which can change the entire meaning of the filter outside the array types + */ +const DiscriminatorKeys = ["since", "until", "limit", "search"]; + +export function canMergeFilters(a: any, b: any): boolean { + for (const key of DiscriminatorKeys) { + if (key in a || key in b) { + if (a[key] !== b[key]) { + return false; + } + } + } + + return true; +} + +export function mergeSimilar(filters: Array): Array { + const ret = []; + + while (filters.length > 0) { + const current = filters.shift()!; + const mergeSet = [current]; + for (let i = 0; i < filters.length; i++) { + const f = filters[i]; + if (mergeSet.every(v => canMergeFilters(v, f) && distance(v, f) === 1)) { + mergeSet.push(filters.splice(i, 1)[0]); + i--; + } + } + ret.push(simpleMerge(mergeSet)); + } + return ret; } /** @@ -14,7 +42,7 @@ export function mergeSimilar(filters: Array): Array * @param filters * @returns */ -export function simpleMerge(filters: Array) { +export function simpleMerge(filters: Array) { const result: any = {}; filters.forEach(filter => { @@ -31,7 +59,7 @@ export function simpleMerge(filters: Array) { }); }); - return result as RawReqFilter; + return result as ReqFilter; } /** @@ -40,7 +68,7 @@ export function simpleMerge(filters: Array) { * @param smaller * @returns */ -export function filterIncludes(bigger: RawReqFilter, smaller: RawReqFilter) { +export function filterIncludes(bigger: ReqFilter, smaller: ReqFilter) { const outside = bigger as Record | number>; for (const [k, v] of Object.entries(smaller)) { if (outside[k] === undefined) { @@ -61,3 +89,61 @@ export function filterIncludes(bigger: RawReqFilter, smaller: RawReqFilter) { } return true; } + +/** + * Merge expanded flat filters into combined concise filters + * @param all + * @returns + */ +export function flatMerge(all: Array): Array { + let ret: Array = []; + + // to compute filters which can be merged we need to calucate the distance change between each filter + // then we can merge filters which are exactly 1 change diff from each other + + function mergeFiltersInSet(filters: Array) { + const result: any = {}; + + filters.forEach(f => { + const filter = f as Record; + Object.entries(filter).forEach(([key, value]) => { + if (!DiscriminatorKeys.includes(key)) { + if (result[key] === undefined) { + result[key] = [value]; + } else { + result[key] = [...new Set([...result[key], value])]; + } + } else { + result[key] = value; + } + }); + }); + + return result as ReqFilter; + } + + // reducer, kinda verbose + while (all.length > 0) { + const currentFilter = all.shift()!; + const mergeSet = [currentFilter]; + + for (let i = 0; i < all.length; i++) { + const f = all[i]; + + if (mergeSet.every(a => canMergeFilters(a, f) && distance(a, f) === 1)) { + mergeSet.push(all.splice(i, 1)[0]); + i--; + } + } + ret.push(mergeFiltersInSet(mergeSet)); + } + + while (true) { + const n = mergeSimilar([...ret]); + if (n.length === ret.length) { + break; + } + ret = n; + } + return ret; +} diff --git a/packages/app/src/System/RequestSplitter.test.ts b/packages/app/src/System/RequestSplitter.test.ts index 10e78a6b..e6caa5a7 100644 --- a/packages/app/src/System/RequestSplitter.test.ts +++ b/packages/app/src/System/RequestSplitter.test.ts @@ -1,104 +1,87 @@ -import { RawReqFilter } from "System"; +import { ReqFilter } from "System"; import { describe, expect } from "@jest/globals"; -import { diffFilters, expandFilter } from "./RequestSplitter"; +import { diffFilters } from "./RequestSplitter"; describe("RequestSplitter", () => { test("single filter add value", () => { - const a: Array = [{ kinds: [0], authors: ["a"] }]; - const b: Array = [{ kinds: [0], authors: ["a", "b"] }]; + const a: Array = [{ kinds: [0], authors: ["a"] }]; + const b: Array = [{ kinds: [0], authors: ["a", "b"] }]; const diff = diffFilters(a, b); - expect(diff).toEqual({ filters: [{ kinds: [0], authors: ["b"] }], changed: true }); + expect(diff).toEqual({ + added: [{ kinds: [0], authors: ["b"] }], + removed: [], + changed: true, + }); }); test("single filter remove value", () => { - const a: Array = [{ kinds: [0], authors: ["a"] }]; - const b: Array = [{ kinds: [0], authors: ["b"] }]; + const a: Array = [{ kinds: [0], authors: ["a"] }]; + const b: Array = [{ kinds: [0], authors: ["b"] }]; const diff = diffFilters(a, b); - expect(diff).toEqual({ filters: [{ kinds: [0], authors: ["b"] }], changed: true }); + expect(diff).toEqual({ + added: [{ kinds: [0], authors: ["b"] }], + removed: [{ kinds: [0], authors: ["a"] }], + changed: true, + }); }); test("single filter change critical key", () => { - const a: Array = [{ kinds: [0], authors: ["a"], since: 100 }]; - const b: Array = [{ kinds: [0], authors: ["a", "b"], since: 101 }]; + const a: Array = [{ kinds: [0], authors: ["a"], since: 100 }]; + const b: Array = [{ kinds: [0], authors: ["a", "b"], since: 101 }]; const diff = diffFilters(a, b); - expect(diff).toEqual({ filters: [{ kinds: [0], authors: ["a", "b"], since: 101 }], changed: true }); + expect(diff).toEqual({ + added: [{ kinds: [0], authors: ["a", "b"], since: 101 }], + removed: [{ kinds: [0], authors: ["a"], since: 100 }], + changed: true, + }); }); test("multiple filter add value", () => { - const a: Array = [ + const a: Array = [ { kinds: [0], authors: ["a"] }, { kinds: [69], authors: ["a"] }, ]; - const b: Array = [ + const b: Array = [ { kinds: [0], authors: ["a", "b"] }, { kinds: [69], authors: ["a", "c"] }, ]; const diff = diffFilters(a, b); expect(diff).toEqual({ - filters: [ + added: [ { kinds: [0], authors: ["b"] }, { kinds: [69], authors: ["c"] }, ], + removed: [], changed: true, }); }); test("multiple filter remove value", () => { - const a: Array = [ + const a: Array = [ { kinds: [0], authors: ["a"] }, { kinds: [69], authors: ["a"] }, ]; - const b: Array = [ + const b: Array = [ { kinds: [0], authors: ["b"] }, { kinds: [69], authors: ["c"] }, ]; const diff = diffFilters(a, b); expect(diff).toEqual({ - filters: [ + added: [ { kinds: [0], authors: ["b"] }, { kinds: [69], authors: ["c"] }, ], + removed: [{ kinds: [0, 69], authors: ["a"] }], changed: true, }); }); test("add filter", () => { - const a: Array = [{ kinds: [0], authors: ["a"] }]; - const b: Array = [ + const a: Array = [{ kinds: [0], authors: ["a"] }]; + const b: Array = [ { kinds: [0], authors: ["a"] }, { kinds: [69], authors: ["c"] }, ]; const diff = diffFilters(a, b); expect(diff).toEqual({ - filters: [ - { kinds: [0], authors: ["a"] }, - { kinds: [69], authors: ["c"] }, - ], + added: [{ kinds: [69], authors: ["c"] }], + removed: [], changed: true, }); }); - test("expand filter", () => { - const a = { - authors: ["a", "b", "c"], - kinds: [1, 2, 3], - ids: ["x", "y"], - since: 99, - limit: 10, - }; - expect(expandFilter(a)).toEqual([ - { authors: ["a"], kinds: [1], ids: ["x"], since: 99, limit: 10 }, - { authors: ["a"], kinds: [1], ids: ["y"], since: 99, limit: 10 }, - { authors: ["a"], kinds: [2], ids: ["x"], since: 99, limit: 10 }, - { authors: ["a"], kinds: [2], ids: ["y"], since: 99, limit: 10 }, - { authors: ["a"], kinds: [3], ids: ["x"], since: 99, limit: 10 }, - { authors: ["a"], kinds: [3], ids: ["y"], since: 99, limit: 10 }, - { authors: ["b"], kinds: [1], ids: ["x"], since: 99, limit: 10 }, - { authors: ["b"], kinds: [1], ids: ["y"], since: 99, limit: 10 }, - { authors: ["b"], kinds: [2], ids: ["x"], since: 99, limit: 10 }, - { authors: ["b"], kinds: [2], ids: ["y"], since: 99, limit: 10 }, - { authors: ["b"], kinds: [3], ids: ["x"], since: 99, limit: 10 }, - { authors: ["b"], kinds: [3], ids: ["y"], since: 99, limit: 10 }, - { authors: ["c"], kinds: [1], ids: ["x"], since: 99, limit: 10 }, - { authors: ["c"], kinds: [1], ids: ["y"], since: 99, limit: 10 }, - { authors: ["c"], kinds: [2], ids: ["x"], since: 99, limit: 10 }, - { authors: ["c"], kinds: [2], ids: ["y"], since: 99, limit: 10 }, - { authors: ["c"], kinds: [3], ids: ["x"], since: 99, limit: 10 }, - { authors: ["c"], kinds: [3], ids: ["y"], since: 99, limit: 10 }, - ]); - }); }); diff --git a/packages/app/src/System/RequestSplitter.ts b/packages/app/src/System/RequestSplitter.ts index 2c942ff9..e1a3d653 100644 --- a/packages/app/src/System/RequestSplitter.ts +++ b/packages/app/src/System/RequestSplitter.ts @@ -1,76 +1,18 @@ -import { RawReqFilter } from "System"; +import { ReqFilter } from "System"; +import { deepEqual } from "./Util"; +import { expandFilter } from "./RequestExpander"; +import { flatMerge } from "./RequestMerger"; -// Critical keys changing means the entire filter has changed -export const CriticalKeys = ["since", "until", "limit"]; +export function diffFilters(prev: Array, next: Array) { + const prevExpanded = prev.flatMap(expandFilter); + const nextExpanded = next.flatMap(expandFilter); -export function diffFilters(a: Array, b: Array) { - const result: Array = []; - let anyChanged = false; - for (const [i, bN] of b.entries()) { - const prev: Record = a[i]; - if (!prev) { - result.push(bN); - anyChanged = true; - } else { - let anyCriticalKeyChanged = false; - for (const [k, v] of Object.entries(bN)) { - if (Array.isArray(v)) { - const prevArray = prev[k] as Array | undefined; - const thisArray = v as Array; - const added = thisArray.filter(a => !prevArray?.includes(a)); - // support adding new values to array, removing values is ignored since we only care about getting new values - result[i] = { ...result[i], [k]: added.length === 0 ? prevArray ?? [] : added }; - if (added.length > 0) { - anyChanged = true; - } - } else if (prev[k] !== v) { - result[i] = { ...result[i], [k]: v }; - if (CriticalKeys.includes(k)) { - anyCriticalKeyChanged = anyChanged = true; - break; - } - } - } - if (anyCriticalKeyChanged) { - result[i] = bN; - } - } - } + const added = flatMerge(nextExpanded.filter(a => !prevExpanded.some(b => deepEqual(a, b)))); + const removed = flatMerge(prevExpanded.filter(a => !nextExpanded.some(b => deepEqual(a, b)))); return { - filters: result, - changed: anyChanged, + added, + removed, + changed: added.length > 0 || removed.length > 0, }; } - -/** - * Expand a filter into its most fine grained form - */ -export function expandFilter(f: RawReqFilter): Array { - const ret: Array = []; - const src = Object.entries(f); - const keys = src.filter(([, v]) => Array.isArray(v)).map(a => a[0]); - const props = src.filter(([, v]) => !Array.isArray(v)); - - function generateCombinations(index: number, currentCombination: RawReqFilter) { - if (index === keys.length) { - ret.push(currentCombination); - return; - } - - const key = keys[index]; - const values = (f as Record>)[key]; - - for (let i = 0; i < values.length; i++) { - const value = values[i]; - const updatedCombination = { ...currentCombination, [key]: [value] }; - generateCombinations(index + 1, updatedCombination); - } - } - - generateCombinations(0, { - ...Object.fromEntries(props), - }); - - return ret; -} diff --git a/packages/app/src/System/SystemWorker.ts b/packages/app/src/System/SystemWorker.ts index eae78c98..fb80e02a 100644 --- a/packages/app/src/System/SystemWorker.ts +++ b/packages/app/src/System/SystemWorker.ts @@ -2,7 +2,7 @@ import ExternalStore from "ExternalStore"; import { NoteStore, Query, - RawEvent, + NostrEvent, RelaySettings, RequestBuilder, SystemSnapshot, @@ -51,11 +51,11 @@ export class SystemWorker extends ExternalStore implements Syste throw new Error("Method not implemented."); } - BroadcastEvent(ev: RawEvent): void { + BroadcastEvent(ev: NostrEvent): void { throw new Error("Method not implemented."); } - WriteOnceToRelay(relay: string, ev: RawEvent): Promise { + WriteOnceToRelay(relay: string, ev: NostrEvent): Promise { throw new Error("Method not implemented."); } diff --git a/packages/app/src/System/Util.test.ts b/packages/app/src/System/Util.test.ts new file mode 100644 index 00000000..14812019 --- /dev/null +++ b/packages/app/src/System/Util.test.ts @@ -0,0 +1,72 @@ +import { distance } from "./Util"; + +describe("distance", () => { + it("should have 0 distance", () => { + const a = { + ids: "a", + }; + const b = { + ids: "a", + }; + expect(distance(a, b)).toEqual(0); + }); + it("should have 1 distance", () => { + const a = { + ids: "a", + }; + const b = { + ids: "b", + }; + expect(distance(a, b)).toEqual(1); + }); + it("should have 10 distance", () => { + const a = { + ids: "a", + }; + const b = { + ids: "a", + kinds: 1, + }; + expect(distance(a, b)).toEqual(10); + }); + it("should have 11 distance", () => { + const a = { + ids: "a", + }; + const b = { + ids: "b", + kinds: 1, + }; + expect(distance(a, b)).toEqual(11); + }); + it("should have 1 distance, arrays", () => { + const a = { + since: 1, + until: 100, + kinds: [1], + authors: ["kieran", "snort", "c", "d", "e"], + }; + const b = { + since: 1, + until: 100, + kinds: [6969], + authors: ["kieran", "snort", "c", "d", "e"], + }; + expect(distance(a, b)).toEqual(1); + }); + it("should have 1 distance, array change extra", () => { + const a = { + since: 1, + until: 100, + kinds: [1], + authors: ["f", "kieran", "snort", "c", "d"], + }; + const b = { + since: 1, + until: 100, + kinds: [1], + authors: ["kieran", "snort", "c", "d", "e"], + }; + expect(distance(a, b)).toEqual(1); + }); +}); diff --git a/packages/app/src/System/Util.ts b/packages/app/src/System/Util.ts index 0e417814..c5698739 100644 --- a/packages/app/src/System/Util.ts +++ b/packages/app/src/System/Util.ts @@ -40,3 +40,47 @@ export function unixNow() { export function unixNowMs() { return new Date().getTime(); } + +export function deepEqual(x: any, y: any): boolean { + const ok = Object.keys, + tx = typeof x, + ty = typeof y; + + return x && y && tx === "object" && tx === ty + ? ok(x).length === ok(y).length && ok(x).every(key => deepEqual(x[key], y[key])) + : x === y; +} + +/** + * Compute the "distance" between two objects by comparing their difference in properties + * Missing/Added keys result in +10 distance + * This is not recursive + */ +export function distance(a: any, b: any): number { + const keys1 = Object.keys(a); + const keys2 = Object.keys(b); + const maxKeys = keys1.length > keys2.length ? keys1 : keys2; + + let distance = 0; + for (const key of maxKeys) { + if (key in a && key in b) { + if (Array.isArray(a[key]) && Array.isArray(b[key])) { + const aa = a[key] as Array; + const bb = b[key] as Array; + if (aa.length === bb.length) { + if (aa.some(v => !bb.includes(v))) { + distance++; + } + } else { + distance++; + } + } else if (a[key] !== b[key]) { + distance++; + } + } else { + distance += 10; + } + } + + return distance; +} diff --git a/packages/app/src/System/index.ts b/packages/app/src/System/index.ts index 1bb1380e..46be1f89 100644 --- a/packages/app/src/System/index.ts +++ b/packages/app/src/System/index.ts @@ -9,7 +9,7 @@ import { ReplaceableNoteStore, } from "./NoteCollection"; import { Query } from "./Query"; -import { RawEvent, RawReqFilter } from "./Nostr"; +import { NostrEvent, ReqFilter } from "./Nostr"; export * from "./NostrSystem"; export { default as EventKind } from "./EventKind"; @@ -29,15 +29,15 @@ export interface SystemInterface { Query(type: { new (): T }, req: RequestBuilder | null): Query | undefined; ConnectToRelay(address: string, options: RelaySettings): Promise; DisconnectRelay(address: string): void; - BroadcastEvent(ev: RawEvent): void; - WriteOnceToRelay(relay: string, ev: RawEvent): Promise; + BroadcastEvent(ev: NostrEvent): void; + WriteOnceToRelay(relay: string, ev: NostrEvent): Promise; } export interface SystemSnapshot { queries: Array<{ id: string; - filters: Array; - subFilters: Array; + filters: Array; + subFilters: Array; closing: boolean; }>; } diff --git a/packages/app/src/Upload/index.ts b/packages/app/src/Upload/index.ts index 9ea2cbcc..8cf57586 100644 --- a/packages/app/src/Upload/index.ts +++ b/packages/app/src/Upload/index.ts @@ -1,5 +1,5 @@ import useLogin from "Hooks/useLogin"; -import { RawEvent } from "System"; +import { NostrEvent } from "System"; import NostrBuild from "Upload/NostrBuild"; import VoidCat from "Upload/VoidCat"; @@ -14,7 +14,7 @@ export interface UploadResult { /** * NIP-94 File Header */ - header?: RawEvent; + header?: NostrEvent; } /** diff --git a/packages/app/src/Wallet/NostrWalletConnect.ts b/packages/app/src/Wallet/NostrWalletConnect.ts index b447e81d..3b4abe53 100644 --- a/packages/app/src/Wallet/NostrWalletConnect.ts +++ b/packages/app/src/Wallet/NostrWalletConnect.ts @@ -1,4 +1,4 @@ -import { Connection, EventKind, RawEvent } from "System"; +import { Connection, EventKind, NostrEvent } from "System"; import { EventBuilder } from "System"; import { EventExt } from "System/EventExt"; import { LNWallet, WalletError, WalletErrorCode, WalletInfo, WalletInvoice, WalletInvoiceState } from "Wallet"; @@ -123,7 +123,7 @@ export class NostrConnectWallet implements LNWallet { return Promise.resolve([]); } - async #onReply(sub: string, e: RawEvent) { + async #onReply(sub: string, e: NostrEvent) { if (sub === "info") { const pending = this.#commandQueue.get("info"); if (!pending) { From 8e6a1ecbc25973dda7b456c7d2a42e576cf347f2 Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 8 Jun 2023 05:39:10 +0100 Subject: [PATCH 17/24] optimize --- packages/app/package.json | 2 +- packages/app/src/Hooks/useRequestBuilder.tsx | 36 +++++++------------ packages/app/src/System/NostrSystem.ts | 6 ++-- packages/app/src/System/NoteCollection.ts | 10 ++++-- packages/app/src/System/Query.test.ts | 8 +++-- packages/app/src/System/Query.ts | 16 +++++++-- packages/app/src/System/RequestMerger.test.ts | 1 + packages/app/src/System/RequestMerger.ts | 32 +++++++++++++---- 8 files changed, 70 insertions(+), 41 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index d14e6a8d..8bd40d54 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -48,7 +48,7 @@ "scripts": { "start": "webpack serve", "build": "webpack --node-env=production", - "test": "jest", + "test": "jest --runInBand", "intl-extract": "formatjs extract 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file src/lang.json --flatten true", "intl-compile": "formatjs compile src/lang.json --out-file src/translations/en.json", "format": "prettier --write .", diff --git a/packages/app/src/Hooks/useRequestBuilder.tsx b/packages/app/src/Hooks/useRequestBuilder.tsx index 5f7a37f7..04923ed3 100644 --- a/packages/app/src/Hooks/useRequestBuilder.tsx +++ b/packages/app/src/Hooks/useRequestBuilder.tsx @@ -6,35 +6,25 @@ import { System } from "index"; const useRequestBuilder = >( type: { new (): TStore }, - rb: RequestBuilder | null, - debounced?: number + rb: RequestBuilder | null ) => { const subscribe = (onChanged: () => void) => { - const store = (System.Query(type, rb)?.feed as TStore) ?? new type(); - let t: ReturnType | undefined; - const release = store.hook(() => { - if (!t) { - t = setTimeout(() => { - clearTimeout(t); - t = undefined; - onChanged(); - }, debounced ?? 500); - } - }); - + if (rb) { + const q = System.Query(type, rb); + const release = q.feed.hook(onChanged); + return () => { + q.cancel(); + release(); + }; + } return () => { - if (rb?.id) { - System.GetQuery(rb.id)?.cancel(); - } - release(); + // noop }; }; const getState = (): StoreSnapshot => { - if (rb?.id) { - const q = System.GetQuery(rb.id); - if (q) { - return unwrap(q).feed?.snapshot as StoreSnapshot; - } + const q = System.GetQuery(rb?.id ?? ""); + if (q) { + return unwrap(q).feed?.snapshot as StoreSnapshot; } return EmptySnapshot as StoreSnapshot; }; diff --git a/packages/app/src/System/NostrSystem.ts b/packages/app/src/System/NostrSystem.ts index 16c9f14b..925fc35e 100644 --- a/packages/app/src/System/NostrSystem.ts +++ b/packages/app/src/System/NostrSystem.ts @@ -119,13 +119,11 @@ export class NostrSystem extends ExternalStore implements System return this.Queries.get(id); } - Query(type: { new (): T }, req: RequestBuilder | null): Query | undefined { - if (!req) return; - + Query(type: { new (): T }, req: RequestBuilder): Query { const existing = this.Queries.get(req.id); if (existing) { const filters = req.buildDiff(this.#relayCache, existing.filters); - if (filters.length === 0 && !req.options?.skipDiff) { + if (filters.length === 0 && !!req.options?.skipDiff) { return existing; } else { for (const subQ of filters) { diff --git a/packages/app/src/System/NoteCollection.ts b/packages/app/src/System/NoteCollection.ts index b740cb96..85974f14 100644 --- a/packages/app/src/System/NoteCollection.ts +++ b/packages/app/src/System/NoteCollection.ts @@ -57,6 +57,7 @@ export abstract class HookedNoteStore i data: undefined, }; #needsSnapshot = true; + #nextNotifyTimer?: ReturnType; get snapshot() { this.#updateSnapshot(); @@ -106,8 +107,13 @@ export abstract class HookedNoteStore i protected onChange(changes: Readonly>): void { this.#needsSnapshot = true; - for (const hk of this.#hooks) { - hk(); + if (!this.#nextNotifyTimer) { + this.#nextNotifyTimer = setTimeout(() => { + this.#nextNotifyTimer = undefined; + for (const hk of this.#hooks) { + hk(); + } + }, 500); } if (changes.length > 0) { for (const hkE of this.#eventHooks) { diff --git a/packages/app/src/System/Query.test.ts b/packages/app/src/System/Query.test.ts index 29a7477c..ad80a1a4 100644 --- a/packages/app/src/System/Query.test.ts +++ b/packages/app/src/System/Query.test.ts @@ -101,8 +101,12 @@ describe("query", () => { expect(q.filters).toEqual([ { - authors: ["a", "b", "c"], - kinds: [1, 2], + authors: ["a", "b"], + kinds: [1], + }, + { + authors: ["b", "c"], + kinds: [2], }, ]); }); diff --git a/packages/app/src/System/Query.ts b/packages/app/src/System/Query.ts index 065eb7ba..b6e14700 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/app/src/System/Query.ts @@ -3,9 +3,10 @@ import debug from "debug"; import { Connection, ReqFilter, Nips, TaggedRawEvent } from "System"; import { unixNowMs, unwrap } from "SnortUtils"; import { NoteStore } from "./NoteCollection"; -import { simpleMerge } from "./RequestMerger"; +import { flatMerge, mergeSimilar, simpleMerge } from "./RequestMerger"; import { eventMatchesFilter } from "./RequestMatcher"; import { BuiltRawReqFilter } from "./RequestBuilder"; +import { expandFilter } from "./RequestExpander"; /** * Tracing for relay query status @@ -137,6 +138,7 @@ export class Query implements QueryBase { #feed: NoteStore; #log = debug("Query"); + #allFilters: Array = []; constructor(id: string, feed: NoteStore) { this.id = id; @@ -152,9 +154,11 @@ export class Query implements QueryBase { return this.#cancelTimeout; } + /** + * Recompute the complete set of compressed filters from all query traces + */ get filters() { - const filters = this.#tracing.flatMap(a => a.filters); - return [simpleMerge(filters)]; + return this.#allFilters; } get feed() { @@ -264,7 +268,13 @@ export class Query implements QueryBase { () => this.#onProgress() ); this.#tracing.push(qt); + this.#reComputeFilters(); c.QueueReq(["REQ", qt.id, ...q.filters], () => qt.sentToRelay()); return qt; } + #reComputeFilters() { + console.time("reComputeFilters"); + this.#allFilters = flatMerge(this.#tracing.flatMap(a => a.filters).flatMap(expandFilter)); + console.timeEnd("reComputeFilters"); + } } diff --git a/packages/app/src/System/RequestMerger.test.ts b/packages/app/src/System/RequestMerger.test.ts index 46260789..b2020d36 100644 --- a/packages/app/src/System/RequestMerger.test.ts +++ b/packages/app/src/System/RequestMerger.test.ts @@ -79,6 +79,7 @@ describe("flatMerge", () => { { ids: 0, authors: "b" }, { kinds: 1 }, { kinds: 2 }, + { kinds: 2 }, { ids: 0, authors: "c" }, { authors: "c", kinds: 1 }, { authors: "c", limit: 100 }, diff --git a/packages/app/src/System/RequestMerger.ts b/packages/app/src/System/RequestMerger.ts index acc359ab..0a4ba5e1 100644 --- a/packages/app/src/System/RequestMerger.ts +++ b/packages/app/src/System/RequestMerger.ts @@ -7,19 +7,35 @@ import { distance } from "./Util"; */ const DiscriminatorKeys = ["since", "until", "limit", "search"]; -export function canMergeFilters(a: any, b: any): boolean { +export function canMergeFilters(a: FlatReqFilter, b: FlatReqFilter): boolean { + const aObj = a as Record; + const bObj = b as Record; for (const key of DiscriminatorKeys) { - if (key in a || key in b) { - if (a[key] !== b[key]) { + if (key in aObj || key in bObj) { + if (aObj[key] !== bObj[key]) { return false; } } } + const keys1 = Object.keys(aObj); + const keys2 = Object.keys(bObj); + const maxKeys = keys1.length > keys2.length ? keys1 : keys2; - return true; + let distance = 0; + for (const key of maxKeys) { + if (key in aObj && key in bObj) { + if (aObj[key] !== bObj[key]) { + distance++; + } + } else { + return false; + } + } + return distance <= 1; } export function mergeSimilar(filters: Array): Array { + console.time("mergeSimilar"); const ret = []; while (filters.length > 0) { @@ -27,13 +43,14 @@ export function mergeSimilar(filters: Array): Array { const mergeSet = [current]; for (let i = 0; i < filters.length; i++) { const f = filters[i]; - if (mergeSet.every(v => canMergeFilters(v, f) && distance(v, f) === 1)) { + if (mergeSet.every(v => canMergeFilters(v, f))) { mergeSet.push(filters.splice(i, 1)[0]); i--; } } ret.push(simpleMerge(mergeSet)); } + console.timeEnd("mergeSimilar"); return ret; } @@ -96,6 +113,7 @@ export function filterIncludes(bigger: ReqFilter, smaller: ReqFilter) { * @returns */ export function flatMerge(all: Array): Array { + console.time("flatMerge"); let ret: Array = []; // to compute filters which can be merged we need to calucate the distance change between each filter @@ -130,7 +148,7 @@ export function flatMerge(all: Array): Array { for (let i = 0; i < all.length; i++) { const f = all[i]; - if (mergeSet.every(a => canMergeFilters(a, f) && distance(a, f) === 1)) { + if (mergeSet.every(a => canMergeFilters(a, f))) { mergeSet.push(all.splice(i, 1)[0]); i--; } @@ -145,5 +163,7 @@ export function flatMerge(all: Array): Array { } ret = n; } + console.timeEnd("flatMerge"); + console.debug(ret); return ret; } From 2b80109e3bfdc831ae61dcd5e0f6818d5369613f Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 8 Jun 2023 06:27:27 +0100 Subject: [PATCH 18/24] fix query close bug --- packages/app/src/Element/SubDebug.tsx | 4 +-- packages/app/src/Hooks/useRequestBuilder.tsx | 1 + packages/app/src/System/NostrSystem.ts | 15 ++++------ packages/app/src/System/Query.ts | 27 ++++++++++-------- packages/app/src/System/RequestMerger.ts | 29 ++++++-------------- packages/app/src/System/index.ts | 1 - 6 files changed, 33 insertions(+), 44 deletions(-) diff --git a/packages/app/src/Element/SubDebug.tsx b/packages/app/src/Element/SubDebug.tsx index 5ce1d17f..73375741 100644 --- a/packages/app/src/Element/SubDebug.tsx +++ b/packages/app/src/Element/SubDebug.tsx @@ -30,10 +30,10 @@ function Queries() { return total; } - function queryInfo(q: { id: string; filters: Array; closing: boolean; subFilters: Array }) { + function queryInfo(q: { id: string; filters: Array; subFilters: Array }) { return (
- {q.closing ? {q.id} : <>{q.id}} + {q.id}
copy(JSON.stringify(q.filters))} className="pointer">   Filters: {q.filters.length} ({countElements(q.filters)} elements) diff --git a/packages/app/src/Hooks/useRequestBuilder.tsx b/packages/app/src/Hooks/useRequestBuilder.tsx index 04923ed3..6d2fb8a7 100644 --- a/packages/app/src/Hooks/useRequestBuilder.tsx +++ b/packages/app/src/Hooks/useRequestBuilder.tsx @@ -12,6 +12,7 @@ const useRequestBuilder = (type, rb); const release = q.feed.hook(onChanged); + q.uncancel(); return () => { q.cancel(); release(); diff --git a/packages/app/src/System/NostrSystem.ts b/packages/app/src/System/NostrSystem.ts index 925fc35e..a34a641f 100644 --- a/packages/app/src/System/NostrSystem.ts +++ b/packages/app/src/System/NostrSystem.ts @@ -122,7 +122,9 @@ export class NostrSystem extends ExternalStore implements System Query(type: { new (): T }, req: RequestBuilder): Query { const existing = this.Queries.get(req.id); if (existing) { - const filters = req.buildDiff(this.#relayCache, existing.filters); + const filters = !req.options?.skipDiff + ? req.buildDiff(this.#relayCache, existing.filters) + : req.build(this.#relayCache); if (filters.length === 0 && !!req.options?.skipDiff) { return existing; } else { @@ -138,11 +140,7 @@ export class NostrSystem extends ExternalStore implements System const store = new type(); const filters = req.build(this.#relayCache); - const q = new Query(req.id, store); - if (req.options?.leaveOpen) { - q.leaveOpen = req.options.leaveOpen; - } - + const q = new Query(req.id, store, req.options?.leaveOpen); this.Queries.set(req.id, q); for (const subQ of filters) { this.SendQuery(q, subQ).then(qta => @@ -222,7 +220,6 @@ export class NostrSystem extends ExternalStore implements System return { id: a.id, filters: a.filters, - closing: a.closing, subFilters: [], }; }), @@ -230,12 +227,12 @@ export class NostrSystem extends ExternalStore implements System } #cleanup() { - const now = unixNowMs(); let changed = false; for (const [k, v] of this.Queries) { - if (v.closingAt && v.closingAt < now) { + if (v.canRemove()) { v.sendClose(); this.Queries.delete(k); + this.#log("Deleted query %s", k); changed = true; } } diff --git a/packages/app/src/System/Query.ts b/packages/app/src/System/Query.ts index b6e14700..581d37f1 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/app/src/System/Query.ts @@ -120,12 +120,12 @@ export class Query implements QueryBase { /** * Leave the query open until its removed */ - leaveOpen = false; + #leaveOpen = false; /** * Time when this query can be removed */ - #cancelTimeout?: number; + #cancelAt?: number; /** * Timer used to track tracing status @@ -140,18 +140,15 @@ export class Query implements QueryBase { #log = debug("Query"); #allFilters: Array = []; - constructor(id: string, feed: NoteStore) { + constructor(id: string, feed: NoteStore, leaveOpen?: boolean) { this.id = id; this.#feed = feed; + this.#leaveOpen = leaveOpen ?? false; this.#checkTraces(); } - get closing() { - return this.#cancelTimeout !== undefined; - } - - get closingAt() { - return this.#cancelTimeout; + canRemove() { + return this.#cancelAt !== undefined && this.#cancelAt < unixNowMs(); } /** @@ -174,8 +171,15 @@ export class Query implements QueryBase { } } + /** + * This function should be called when this Query object and FeedStore is no longer needed + */ cancel() { - this.#cancelTimeout = unixNowMs() + 5_000; + this.#cancelAt = unixNowMs() + 5_000; + } + + uncancel() { + this.#cancelAt = undefined; } cleanup() { @@ -203,7 +207,7 @@ export class Query implements QueryBase { eose(sub: string, conn: Readonly) { const qt = this.#tracing.find(a => a.id === sub && a.connId === conn.Id); qt?.gotEose(); - if (!this.leaveOpen) { + if (!this.#leaveOpen) { qt?.sendClose(); } } @@ -272,6 +276,7 @@ export class Query implements QueryBase { c.QueueReq(["REQ", qt.id, ...q.filters], () => qt.sentToRelay()); return qt; } + #reComputeFilters() { console.time("reComputeFilters"); this.#allFilters = flatMerge(this.#tracing.flatMap(a => a.filters).flatMap(expandFilter)); diff --git a/packages/app/src/System/RequestMerger.ts b/packages/app/src/System/RequestMerger.ts index 0a4ba5e1..bd588c25 100644 --- a/packages/app/src/System/RequestMerger.ts +++ b/packages/app/src/System/RequestMerger.ts @@ -7,7 +7,7 @@ import { distance } from "./Util"; */ const DiscriminatorKeys = ["since", "until", "limit", "search"]; -export function canMergeFilters(a: FlatReqFilter, b: FlatReqFilter): boolean { +export function canMergeFilters(a: FlatReqFilter | ReqFilter, b: FlatReqFilter | ReqFilter): boolean { const aObj = a as Record; const bObj = b as Record; for (const key of DiscriminatorKeys) { @@ -17,34 +17,21 @@ export function canMergeFilters(a: FlatReqFilter, b: FlatReqFilter): boolean { } } } - const keys1 = Object.keys(aObj); - const keys2 = Object.keys(bObj); - const maxKeys = keys1.length > keys2.length ? keys1 : keys2; - - let distance = 0; - for (const key of maxKeys) { - if (key in aObj && key in bObj) { - if (aObj[key] !== bObj[key]) { - distance++; - } - } else { - return false; - } - } - return distance <= 1; + return distance(aObj, bObj) <= 1; } export function mergeSimilar(filters: Array): Array { console.time("mergeSimilar"); const ret = []; - while (filters.length > 0) { - const current = filters.shift()!; + const fCopy = [...filters]; + while (fCopy.length > 0) { + const current = fCopy.shift()!; const mergeSet = [current]; - for (let i = 0; i < filters.length; i++) { - const f = filters[i]; + for (let i = 0; i < fCopy.length; i++) { + const f = fCopy[i]; if (mergeSet.every(v => canMergeFilters(v, f))) { - mergeSet.push(filters.splice(i, 1)[0]); + mergeSet.push(fCopy.splice(i, 1)[0]); i--; } } diff --git a/packages/app/src/System/index.ts b/packages/app/src/System/index.ts index 46be1f89..fd40e81e 100644 --- a/packages/app/src/System/index.ts +++ b/packages/app/src/System/index.ts @@ -38,7 +38,6 @@ export interface SystemSnapshot { id: string; filters: Array; subFilters: Array; - closing: boolean; }>; } From 81ccb95d828c39597dd6f6b277cf6882a4adbbcb Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 8 Jun 2023 12:45:23 +0200 Subject: [PATCH 19/24] move to pkg --- packages/app/.gitignore | 2 + packages/app/src/Cache/DMCache.ts | 2 +- packages/app/src/Cache/UserCache.ts | 2 +- packages/app/src/Cache/index.ts | 49 --- packages/app/src/Const.ts | 4 +- packages/app/src/Db/index.ts | 3 +- packages/app/src/Element/Avatar.tsx | 2 +- packages/app/src/Element/BadgeList.tsx | 2 +- packages/app/src/Element/BlockButton.tsx | 2 +- packages/app/src/Element/Bookmarks.tsx | 2 +- packages/app/src/Element/DM.tsx | 2 +- packages/app/src/Element/DmWindow.tsx | 2 +- packages/app/src/Element/FollowButton.tsx | 2 +- packages/app/src/Element/FollowListBase.tsx | 2 +- packages/app/src/Element/Mention.tsx | 2 +- packages/app/src/Element/MuteButton.tsx | 2 +- packages/app/src/Element/MutedList.tsx | 2 +- packages/app/src/Element/Nip05.tsx | 2 +- packages/app/src/Element/Nip5Service.tsx | 4 +- packages/app/src/Element/NostrFileHeader.tsx | 4 +- packages/app/src/Element/NostrLink.tsx | 3 +- packages/app/src/Element/Note.tsx | 3 +- packages/app/src/Element/NoteCreator.tsx | 3 +- packages/app/src/Element/NoteFooter.tsx | 2 +- packages/app/src/Element/NoteQuote.tsx | 2 +- packages/app/src/Element/NoteReaction.tsx | 3 +- packages/app/src/Element/Poll.tsx | 2 +- packages/app/src/Element/ProfileImage.tsx | 3 +- packages/app/src/Element/ProfilePreview.tsx | 2 +- packages/app/src/Element/PubkeyList.tsx | 2 +- packages/app/src/Element/Reactions.tsx | 2 +- packages/app/src/Element/Relay.tsx | 2 +- packages/app/src/Element/RelaysMetadata.tsx | 2 +- packages/app/src/Element/SendSats.tsx | 3 +- packages/app/src/Element/SubDebug.tsx | 2 +- .../app/src/Element/SuggestedProfiles.tsx | 2 +- packages/app/src/Element/Text.tsx | 4 +- packages/app/src/Element/Textarea.tsx | 3 +- packages/app/src/Element/Thread.tsx | 13 +- packages/app/src/Element/Timeline.tsx | 2 +- packages/app/src/Element/TrendingPosts.tsx | 2 +- packages/app/src/Element/TrendingUsers.tsx | 2 +- packages/app/src/Element/Username.tsx | 2 +- packages/app/src/Element/WriteDm.tsx | 2 +- packages/app/src/Element/Zap.tsx | 2 +- packages/app/src/Element/ZapButton.tsx | 2 +- packages/app/src/Element/ZapstrEmbed.tsx | 2 +- packages/app/src/External/NostrBand.ts | 2 +- packages/app/src/Feed/BadgesFeed.ts | 4 +- packages/app/src/Feed/BookmarkFeed.tsx | 2 +- packages/app/src/Feed/EventFeed.ts | 5 +- packages/app/src/Feed/EventPublisher.ts | 2 +- packages/app/src/Feed/FollowersFeed.ts | 3 +- packages/app/src/Feed/FollowsFeed.ts | 3 +- packages/app/src/Feed/LoginFeed.ts | 3 +- packages/app/src/Feed/MuteList.ts | 10 +- packages/app/src/Feed/PinnedFeed.tsx | 2 +- packages/app/src/Feed/RelaysFeed.tsx | 4 +- packages/app/src/Feed/RelaysFeedFollows.tsx | 11 +- packages/app/src/Feed/ThreadFeed.ts | 5 +- packages/app/src/Feed/TimelineFeed.ts | 3 +- packages/app/src/Feed/ZapsFeed.ts | 3 +- .../app/src/Hooks/useInteractionCache.tsx | 2 +- packages/app/src/Hooks/useModeration.tsx | 2 +- .../app/src/Hooks/useNotelistSubscription.ts | 10 +- packages/app/src/Hooks/useRequestBuilder.tsx | 3 +- packages/app/src/Hooks/useSystemState.tsx | 2 +- packages/app/src/Hooks/useUserProfile.ts | 3 +- packages/app/src/LNURL.ts | 2 +- packages/app/src/Login/Functions.ts | 3 +- packages/app/src/Login/LoginSession.ts | 2 +- packages/app/src/Login/MultiAccountStore.ts | 2 +- .../app/src/Nip05/SnortServiceProvider.ts | 3 +- packages/app/src/Notifications.ts | 3 +- packages/app/src/Pages/DonatePage.tsx | 2 +- packages/app/src/Pages/LoginPage.tsx | 2 +- packages/app/src/Pages/MessagesPage.tsx | 2 +- packages/app/src/Pages/NostrLinkHandler.tsx | 4 +- packages/app/src/Pages/ProfilePage.tsx | 4 +- packages/app/src/Pages/new/ProfileSetup.tsx | 3 +- packages/app/src/Pages/settings/Keys.tsx | 2 +- packages/app/src/Pages/settings/Profile.tsx | 3 +- packages/app/src/Pages/settings/Relays.tsx | 1 - packages/app/src/SnortApi.ts | 3 +- packages/app/src/SnortUtils/Utils.test.ts | 47 +-- packages/app/src/SnortUtils/index.ts | 114 +----- packages/app/src/State/NoteCreator.ts | 2 +- packages/app/src/State/ReBroadcast.ts | 2 +- packages/app/src/System/Const.ts | 4 - packages/app/src/System/Util.test.ts | 72 ---- packages/app/src/Tasks/Nip5Task.tsx | 2 +- packages/app/src/Tasks/index.ts | 2 +- packages/app/src/Upload/VoidCat.ts | 3 +- packages/app/src/Upload/index.ts | 2 +- packages/app/src/Wallet/NostrWalletConnect.ts | 4 +- packages/app/src/index.tsx | 7 +- packages/system/.npmignore | 5 + packages/system/dist/Connection.d.ts | 90 +++++ packages/system/dist/Connection.d.ts.map | 1 + packages/system/dist/Connection.js | 343 ++++++++++++++++++ packages/system/dist/Connection.js.map | 1 + packages/system/dist/ConnectionStats.d.ts | 30 ++ packages/system/dist/ConnectionStats.d.ts.map | 1 + packages/system/dist/ConnectionStats.js | 36 ++ packages/system/dist/ConnectionStats.js.map | 1 + packages/system/dist/Const.d.ts | 13 + packages/system/dist/Const.d.ts.map | 1 + packages/system/dist/Const.js | 17 + packages/system/dist/Const.js.map | 1 + packages/system/dist/EventBuilder.d.ts | 20 + packages/system/dist/EventBuilder.d.ts.map | 1 + packages/system/dist/EventBuilder.js | 113 ++++++ packages/system/dist/EventBuilder.js.map | 1 + packages/system/dist/EventExt.d.ts | 42 +++ packages/system/dist/EventExt.d.ts.map | 1 + packages/system/dist/EventExt.js | 175 +++++++++ packages/system/dist/EventExt.js.map | 1 + packages/system/dist/EventKind.d.ts | 29 ++ packages/system/dist/EventKind.d.ts.map | 1 + packages/system/dist/EventKind.js | 32 ++ packages/system/dist/EventKind.js.map | 1 + packages/system/dist/EventPublisher.d.ts | 66 ++++ packages/system/dist/EventPublisher.d.ts.map | 1 + packages/system/dist/EventPublisher.js | 295 +++++++++++++++ packages/system/dist/EventPublisher.js.map | 1 + packages/system/dist/ExternalStore.d.ts | 13 + packages/system/dist/ExternalStore.d.ts.map | 1 + packages/system/dist/ExternalStore.js | 49 +++ packages/system/dist/ExternalStore.js.map | 1 + packages/system/dist/GossipModel.d.ts | 20 + packages/system/dist/GossipModel.d.ts.map | 1 + packages/system/dist/GossipModel.js | 102 ++++++ packages/system/dist/GossipModel.js.map | 1 + packages/system/dist/Links.d.ts | 24 ++ packages/system/dist/Links.d.ts.map | 1 + packages/system/dist/Links.js | 102 ++++++ packages/system/dist/Links.js.map | 1 + packages/system/dist/Nips.d.ts | 4 + packages/system/dist/Nips.d.ts.map | 1 + packages/system/dist/Nips.js | 8 + packages/system/dist/Nips.js.map | 1 + packages/system/dist/Nostr.d.ts | 75 ++++ packages/system/dist/Nostr.d.ts.map | 1 + packages/system/dist/Nostr.js | 15 + packages/system/dist/Nostr.js.map | 1 + packages/system/dist/NostrLink.d.ts | 13 + packages/system/dist/NostrLink.d.ts.map | 1 + packages/system/dist/NostrLink.js | 110 ++++++ packages/system/dist/NostrLink.js.map | 1 + packages/system/dist/NostrSystem.d.ts | 75 ++++ packages/system/dist/NostrSystem.d.ts.map | 1 + packages/system/dist/NostrSystem.js | 237 ++++++++++++ packages/system/dist/NostrSystem.js.map | 1 + packages/system/dist/NoteCollection.d.ts | 91 +++++ packages/system/dist/NoteCollection.d.ts.map | 1 + packages/system/dist/NoteCollection.js | 240 ++++++++++++ packages/system/dist/NoteCollection.js.map | 1 + packages/system/dist/ProfileCache.d.ts | 20 + packages/system/dist/ProfileCache.d.ts.map | 1 + packages/system/dist/ProfileCache.js | 129 +++++++ packages/system/dist/ProfileCache.js.map | 1 + packages/system/dist/Query.d.ts | 86 +++++ packages/system/dist/Query.d.ts.map | 1 + packages/system/dist/Query.js | 228 ++++++++++++ packages/system/dist/Query.js.map | 1 + packages/system/dist/RelayInfo.d.ts | 17 + packages/system/dist/RelayInfo.d.ts.map | 1 + packages/system/dist/RelayInfo.js | 3 + packages/system/dist/RelayInfo.js.map | 1 + packages/system/dist/RequestBuilder.d.ts | 93 +++++ packages/system/dist/RequestBuilder.d.ts.map | 1 + packages/system/dist/RequestBuilder.js | 225 ++++++++++++ packages/system/dist/RequestBuilder.js.map | 1 + packages/system/dist/RequestExpander.d.ts | 20 + packages/system/dist/RequestExpander.d.ts.map | 1 + packages/system/dist/RequestExpander.js | 31 ++ packages/system/dist/RequestExpander.js.map | 1 + packages/system/dist/RequestMatcher.d.ts | 3 + packages/system/dist/RequestMatcher.d.ts.map | 1 + packages/system/dist/RequestMatcher.js | 23 ++ packages/system/dist/RequestMatcher.js.map | 1 + packages/system/dist/RequestMerger.d.ts | 24 ++ packages/system/dist/RequestMerger.d.ts.map | 1 + packages/system/dist/RequestMerger.js | 150 ++++++++ packages/system/dist/RequestMerger.js.map | 1 + packages/system/dist/RequestSplitter.d.ts | 7 + packages/system/dist/RequestSplitter.d.ts.map | 1 + packages/system/dist/RequestSplitter.js | 19 + packages/system/dist/RequestSplitter.js.map | 1 + packages/system/dist/SystemWorker.d.ts | 22 ++ packages/system/dist/SystemWorker.d.ts.map | 1 + packages/system/dist/SystemWorker.js | 66 ++++ packages/system/dist/SystemWorker.js.map | 1 + packages/system/dist/Tag.d.ts | 18 + packages/system/dist/Tag.d.ts.map | 1 + packages/system/dist/Tag.js | 75 ++++ packages/system/dist/Tag.js.map | 1 + packages/system/dist/Util.d.ts | 23 ++ packages/system/dist/Util.d.ts.map | 1 + packages/system/dist/Util.js | 148 ++++++++ packages/system/dist/Util.js.map | 1 + packages/system/dist/WorkQueue.d.ts | 8 + packages/system/dist/WorkQueue.d.ts.map | 1 + packages/system/dist/WorkQueue.js | 30 ++ packages/system/dist/WorkQueue.js.map | 1 + packages/system/dist/cache/index.d.ts | 48 +++ packages/system/dist/cache/index.d.ts.map | 1 + packages/system/dist/cache/index.js | 21 ++ packages/system/dist/cache/index.js.map | 1 + packages/system/dist/index.d.ts | 44 +++ packages/system/dist/index.d.ts.map | 1 + packages/system/dist/index.js | 39 ++ packages/system/dist/index.js.map | 1 + packages/system/jest.config.js | 9 + packages/system/package.json | 29 ++ packages/system/snort-system-1.0.0.tgz | Bin 0 -> 56011 bytes .../src/System => system/src}/Connection.ts | 2 +- .../System => system/src}/ConnectionStats.ts | 0 packages/system/src/Const.ts | 16 + .../src/System => system/src}/EventBuilder.ts | 7 +- .../src/System => system/src}/EventExt.ts | 15 +- .../src/System => system/src}/EventKind.ts | 0 .../System => system/src}/EventPublisher.ts | 18 +- packages/system/src/ExternalStore.ts | 41 +++ .../src/System => system/src}/GossipModel.ts | 4 +- .../{app/src/System => system/src}/Links.ts | 0 .../{app/src/System => system/src}/Nips.ts | 0 .../{app/src/System => system/src}/Nostr.ts | 0 packages/system/src/NostrLink.ts | 110 ++++++ .../src/System => system/src}/NostrSystem.ts | 11 +- .../System => system/src}/NoteCollection.ts | 4 +- .../src/System => system/src}/ProfileCache.ts | 24 +- .../{app/src/System => system/src}/Query.ts | 7 +- .../src/System => system/src}/RelayInfo.ts | 0 .../System => system/src}/RequestBuilder.ts | 4 +- .../System => system/src}/RequestExpander.ts | 0 .../System => system/src}/RequestMatcher.ts | 0 .../System => system/src}/RequestMerger.ts | 2 +- .../System => system/src}/RequestSplitter.ts | 2 +- .../src/System => system/src}/SystemWorker.ts | 19 +- .../{app/src/System => system/src}/Tag.ts | 0 .../{app/src/System => system/src}/Util.ts | 36 ++ packages/system/src/WorkQueue.ts | 30 ++ packages/system/src/cache/index.ts | 68 ++++ .../{app/src/System => system/src}/index.ts | 39 +- .../tests}/NoteCollection.test.ts | 4 +- .../src/System => system/tests}/Query.test.ts | 8 +- .../tests}/RequestBuilder.test.ts | 4 +- .../tests}/RequestExpander.test.ts | 2 +- .../tests}/RequestMatcher.test.ts | 2 +- .../tests}/RequestMerger.test.ts | 8 +- .../tests}/RequestSplitter.test.ts | 4 +- packages/system/tests/Util.test.ts | 117 ++++++ packages/system/tests/setupTests.ts | 3 + packages/system/tsconfig.json | 18 + packages/{app/src/System => system}/worker.ts | 2 +- 256 files changed, 4856 insertions(+), 529 deletions(-) delete mode 100644 packages/app/src/System/Const.ts delete mode 100644 packages/app/src/System/Util.test.ts create mode 100644 packages/system/.npmignore create mode 100644 packages/system/dist/Connection.d.ts create mode 100644 packages/system/dist/Connection.d.ts.map create mode 100644 packages/system/dist/Connection.js create mode 100644 packages/system/dist/Connection.js.map create mode 100644 packages/system/dist/ConnectionStats.d.ts create mode 100644 packages/system/dist/ConnectionStats.d.ts.map create mode 100644 packages/system/dist/ConnectionStats.js create mode 100644 packages/system/dist/ConnectionStats.js.map create mode 100644 packages/system/dist/Const.d.ts create mode 100644 packages/system/dist/Const.d.ts.map create mode 100644 packages/system/dist/Const.js create mode 100644 packages/system/dist/Const.js.map create mode 100644 packages/system/dist/EventBuilder.d.ts create mode 100644 packages/system/dist/EventBuilder.d.ts.map create mode 100644 packages/system/dist/EventBuilder.js create mode 100644 packages/system/dist/EventBuilder.js.map create mode 100644 packages/system/dist/EventExt.d.ts create mode 100644 packages/system/dist/EventExt.d.ts.map create mode 100644 packages/system/dist/EventExt.js create mode 100644 packages/system/dist/EventExt.js.map create mode 100644 packages/system/dist/EventKind.d.ts create mode 100644 packages/system/dist/EventKind.d.ts.map create mode 100644 packages/system/dist/EventKind.js create mode 100644 packages/system/dist/EventKind.js.map create mode 100644 packages/system/dist/EventPublisher.d.ts create mode 100644 packages/system/dist/EventPublisher.d.ts.map create mode 100644 packages/system/dist/EventPublisher.js create mode 100644 packages/system/dist/EventPublisher.js.map create mode 100644 packages/system/dist/ExternalStore.d.ts create mode 100644 packages/system/dist/ExternalStore.d.ts.map create mode 100644 packages/system/dist/ExternalStore.js create mode 100644 packages/system/dist/ExternalStore.js.map create mode 100644 packages/system/dist/GossipModel.d.ts create mode 100644 packages/system/dist/GossipModel.d.ts.map create mode 100644 packages/system/dist/GossipModel.js create mode 100644 packages/system/dist/GossipModel.js.map create mode 100644 packages/system/dist/Links.d.ts create mode 100644 packages/system/dist/Links.d.ts.map create mode 100644 packages/system/dist/Links.js create mode 100644 packages/system/dist/Links.js.map create mode 100644 packages/system/dist/Nips.d.ts create mode 100644 packages/system/dist/Nips.d.ts.map create mode 100644 packages/system/dist/Nips.js create mode 100644 packages/system/dist/Nips.js.map create mode 100644 packages/system/dist/Nostr.d.ts create mode 100644 packages/system/dist/Nostr.d.ts.map create mode 100644 packages/system/dist/Nostr.js create mode 100644 packages/system/dist/Nostr.js.map create mode 100644 packages/system/dist/NostrLink.d.ts create mode 100644 packages/system/dist/NostrLink.d.ts.map create mode 100644 packages/system/dist/NostrLink.js create mode 100644 packages/system/dist/NostrLink.js.map create mode 100644 packages/system/dist/NostrSystem.d.ts create mode 100644 packages/system/dist/NostrSystem.d.ts.map create mode 100644 packages/system/dist/NostrSystem.js create mode 100644 packages/system/dist/NostrSystem.js.map create mode 100644 packages/system/dist/NoteCollection.d.ts create mode 100644 packages/system/dist/NoteCollection.d.ts.map create mode 100644 packages/system/dist/NoteCollection.js create mode 100644 packages/system/dist/NoteCollection.js.map create mode 100644 packages/system/dist/ProfileCache.d.ts create mode 100644 packages/system/dist/ProfileCache.d.ts.map create mode 100644 packages/system/dist/ProfileCache.js create mode 100644 packages/system/dist/ProfileCache.js.map create mode 100644 packages/system/dist/Query.d.ts create mode 100644 packages/system/dist/Query.d.ts.map create mode 100644 packages/system/dist/Query.js create mode 100644 packages/system/dist/Query.js.map create mode 100644 packages/system/dist/RelayInfo.d.ts create mode 100644 packages/system/dist/RelayInfo.d.ts.map create mode 100644 packages/system/dist/RelayInfo.js create mode 100644 packages/system/dist/RelayInfo.js.map create mode 100644 packages/system/dist/RequestBuilder.d.ts create mode 100644 packages/system/dist/RequestBuilder.d.ts.map create mode 100644 packages/system/dist/RequestBuilder.js create mode 100644 packages/system/dist/RequestBuilder.js.map create mode 100644 packages/system/dist/RequestExpander.d.ts create mode 100644 packages/system/dist/RequestExpander.d.ts.map create mode 100644 packages/system/dist/RequestExpander.js create mode 100644 packages/system/dist/RequestExpander.js.map create mode 100644 packages/system/dist/RequestMatcher.d.ts create mode 100644 packages/system/dist/RequestMatcher.d.ts.map create mode 100644 packages/system/dist/RequestMatcher.js create mode 100644 packages/system/dist/RequestMatcher.js.map create mode 100644 packages/system/dist/RequestMerger.d.ts create mode 100644 packages/system/dist/RequestMerger.d.ts.map create mode 100644 packages/system/dist/RequestMerger.js create mode 100644 packages/system/dist/RequestMerger.js.map create mode 100644 packages/system/dist/RequestSplitter.d.ts create mode 100644 packages/system/dist/RequestSplitter.d.ts.map create mode 100644 packages/system/dist/RequestSplitter.js create mode 100644 packages/system/dist/RequestSplitter.js.map create mode 100644 packages/system/dist/SystemWorker.d.ts create mode 100644 packages/system/dist/SystemWorker.d.ts.map create mode 100644 packages/system/dist/SystemWorker.js create mode 100644 packages/system/dist/SystemWorker.js.map create mode 100644 packages/system/dist/Tag.d.ts create mode 100644 packages/system/dist/Tag.d.ts.map create mode 100644 packages/system/dist/Tag.js create mode 100644 packages/system/dist/Tag.js.map create mode 100644 packages/system/dist/Util.d.ts create mode 100644 packages/system/dist/Util.d.ts.map create mode 100644 packages/system/dist/Util.js create mode 100644 packages/system/dist/Util.js.map create mode 100644 packages/system/dist/WorkQueue.d.ts create mode 100644 packages/system/dist/WorkQueue.d.ts.map create mode 100644 packages/system/dist/WorkQueue.js create mode 100644 packages/system/dist/WorkQueue.js.map create mode 100644 packages/system/dist/cache/index.d.ts create mode 100644 packages/system/dist/cache/index.d.ts.map create mode 100644 packages/system/dist/cache/index.js create mode 100644 packages/system/dist/cache/index.js.map create mode 100644 packages/system/dist/index.d.ts create mode 100644 packages/system/dist/index.d.ts.map create mode 100644 packages/system/dist/index.js create mode 100644 packages/system/dist/index.js.map create mode 100644 packages/system/jest.config.js create mode 100644 packages/system/package.json create mode 100644 packages/system/snort-system-1.0.0.tgz rename packages/{app/src/System => system/src}/Connection.ts (99%) rename packages/{app/src/System => system/src}/ConnectionStats.ts (100%) create mode 100644 packages/system/src/Const.ts rename packages/{app/src/System => system/src}/EventBuilder.ts (91%) rename packages/{app/src/System => system/src}/EventExt.ts (92%) rename packages/{app/src/System => system/src}/EventKind.ts (100%) rename packages/{app/src/System => system/src}/EventPublisher.ts (93%) create mode 100644 packages/system/src/ExternalStore.ts rename packages/{app/src/System => system/src}/GossipModel.ts (96%) rename packages/{app/src/System => system/src}/Links.ts (100%) rename packages/{app/src/System => system/src}/Nips.ts (100%) rename packages/{app/src/System => system/src}/Nostr.ts (100%) create mode 100644 packages/system/src/NostrLink.ts rename packages/{app/src/System => system/src}/NostrSystem.ts (95%) rename packages/{app/src/System => system/src}/NoteCollection.ts (98%) rename packages/{app/src/System => system/src}/ProfileCache.ts (83%) rename packages/{app/src/System => system/src}/Query.ts (96%) rename packages/{app/src/System => system/src}/RelayInfo.ts (100%) rename packages/{app/src/System => system/src}/RequestBuilder.ts (98%) rename packages/{app/src/System => system/src}/RequestExpander.ts (100%) rename packages/{app/src/System => system/src}/RequestMatcher.ts (100%) rename packages/{app/src/System => system/src}/RequestMerger.ts (99%) rename packages/{app/src/System => system/src}/RequestSplitter.ts (94%) rename packages/{app/src/System => system/src}/SystemWorker.ts (80%) rename packages/{app/src/System => system/src}/Tag.ts (100%) rename packages/{app/src/System => system/src}/Util.ts (68%) create mode 100644 packages/system/src/WorkQueue.ts create mode 100644 packages/system/src/cache/index.ts rename packages/{app/src/System => system/src}/index.ts (58%) rename packages/{app/src/System => system/tests}/NoteCollection.test.ts (93%) rename packages/{app/src/System => system/tests}/Query.test.ts (92%) rename packages/{app/src/System => system/tests}/RequestBuilder.test.ts (97%) rename packages/{app/src/System => system/tests}/RequestExpander.test.ts (96%) rename packages/{app/src/System => system/tests}/RequestMatcher.test.ts (88%) rename packages/{app/src/System => system/tests}/RequestMerger.test.ts (93%) rename packages/{app/src/System => system/tests}/RequestSplitter.test.ts (96%) create mode 100644 packages/system/tests/Util.test.ts create mode 100644 packages/system/tests/setupTests.ts create mode 100644 packages/system/tsconfig.json rename packages/{app/src/System => system}/worker.ts (88%) diff --git a/packages/app/.gitignore b/packages/app/.gitignore index 3a1bea68..3486a8a6 100644 --- a/packages/app/.gitignore +++ b/packages/app/.gitignore @@ -23,3 +23,5 @@ yarn-debug.log* yarn-error.log* .idea + +dist/ diff --git a/packages/app/src/Cache/DMCache.ts b/packages/app/src/Cache/DMCache.ts index 7cbce8df..4041a0fc 100644 --- a/packages/app/src/Cache/DMCache.ts +++ b/packages/app/src/Cache/DMCache.ts @@ -1,4 +1,4 @@ -import { NostrEvent } from "System"; +import { NostrEvent } from "@snort/system"; import { db } from "Db"; import FeedCache from "./FeedCache"; diff --git a/packages/app/src/Cache/UserCache.ts b/packages/app/src/Cache/UserCache.ts index 731a7422..1b9ce04b 100644 --- a/packages/app/src/Cache/UserCache.ts +++ b/packages/app/src/Cache/UserCache.ts @@ -1,6 +1,6 @@ import FeedCache from "Cache/FeedCache"; import { db } from "Db"; -import { MetadataCache } from "Cache"; +import { MetadataCache } from "@snort/system"; import { LNURL } from "LNURL"; import { fetchNip05Pubkey } from "Nip05/Verifier"; diff --git a/packages/app/src/Cache/index.ts b/packages/app/src/Cache/index.ts index 0f0cabb4..ec3f6c0f 100644 --- a/packages/app/src/Cache/index.ts +++ b/packages/app/src/Cache/index.ts @@ -1,57 +1,8 @@ -import { HexKey, NostrEvent, UserMetadata } from "System"; -import { hexToBech32, unixNowMs } from "SnortUtils"; import { DmCache } from "./DMCache"; import { InteractionCache } from "./EventInteractionCache"; import { UserCache } from "./UserCache"; import { UserRelays } from "./UserRelayCache"; -export interface MetadataCache extends UserMetadata { - /** - * When the object was saved in cache - */ - loaded: number; - - /** - * When the source metadata event was created - */ - created: number; - - /** - * The pubkey of the owner of this metadata - */ - pubkey: HexKey; - - /** - * The bech32 encoded pubkey - */ - npub: string; - - /** - * Pubkey of zapper service - */ - zapService?: HexKey; - - /** - * If the nip05 is valid for this user - */ - isNostrAddressValid: boolean; -} - -export function mapEventToProfile(ev: NostrEvent) { - try { - const data: UserMetadata = JSON.parse(ev.content); - return { - ...data, - pubkey: ev.pubkey, - npub: hexToBech32("npub", ev.pubkey), - created: ev.created_at, - loaded: unixNowMs(), - } as MetadataCache; - } catch (e) { - console.error("Failed to parse JSON", ev, e); - } -} - export async function preload(follows?: Array) { const preloads = [ UserCache.preload(follows), diff --git a/packages/app/src/Const.ts b/packages/app/src/Const.ts index e16d2262..db961bc6 100644 --- a/packages/app/src/Const.ts +++ b/packages/app/src/Const.ts @@ -1,6 +1,4 @@ -import { UserRelays } from "Cache/UserRelayCache"; -import { NostrSystem, RelaySettings } from "System"; -import { ProfileLoaderService } from "System/ProfileCache"; +import { RelaySettings } from "@snort/system"; /** * Add-on api for snort features diff --git a/packages/app/src/Db/index.ts b/packages/app/src/Db/index.ts index 9a72e8f3..cb501536 100644 --- a/packages/app/src/Db/index.ts +++ b/packages/app/src/Db/index.ts @@ -1,6 +1,5 @@ import Dexie, { Table } from "dexie"; -import { FullRelaySettings, HexKey, NostrEvent, u256 } from "System"; -import { MetadataCache } from "Cache"; +import { FullRelaySettings, HexKey, NostrEvent, u256, MetadataCache } from "@snort/system"; export const NAME = "snortDB"; export const VERSION = 8; diff --git a/packages/app/src/Element/Avatar.tsx b/packages/app/src/Element/Avatar.tsx index 576ef169..89fef4aa 100644 --- a/packages/app/src/Element/Avatar.tsx +++ b/packages/app/src/Element/Avatar.tsx @@ -2,7 +2,7 @@ import "./Avatar.css"; import Nostrich from "nostrich.webp"; import { CSSProperties, useEffect, useState } from "react"; -import type { UserMetadata } from "System"; +import type { UserMetadata } from "@snort/system"; import useImgProxy from "Hooks/useImgProxy"; diff --git a/packages/app/src/Element/BadgeList.tsx b/packages/app/src/Element/BadgeList.tsx index e007b685..36088902 100644 --- a/packages/app/src/Element/BadgeList.tsx +++ b/packages/app/src/Element/BadgeList.tsx @@ -3,7 +3,7 @@ import "./BadgeList.css"; import { useState } from "react"; import { FormattedMessage } from "react-intl"; -import { TaggedRawEvent } from "System"; +import { TaggedRawEvent } from "@snort/system"; import { ProxyImg } from "Element/ProxyImg"; import Icon from "Icons/Icon"; diff --git a/packages/app/src/Element/BlockButton.tsx b/packages/app/src/Element/BlockButton.tsx index 10c4859f..3245d76d 100644 --- a/packages/app/src/Element/BlockButton.tsx +++ b/packages/app/src/Element/BlockButton.tsx @@ -1,5 +1,5 @@ import { FormattedMessage } from "react-intl"; -import { HexKey } from "System"; +import { HexKey } from "@snort/system"; import useModeration from "Hooks/useModeration"; import messages from "./messages"; diff --git a/packages/app/src/Element/Bookmarks.tsx b/packages/app/src/Element/Bookmarks.tsx index 04680f64..118cf917 100644 --- a/packages/app/src/Element/Bookmarks.tsx +++ b/packages/app/src/Element/Bookmarks.tsx @@ -1,6 +1,6 @@ import { useState, useMemo, ChangeEvent } from "react"; import { FormattedMessage } from "react-intl"; -import { HexKey, TaggedRawEvent } from "System"; +import { HexKey, TaggedRawEvent } from "@snort/system"; import Note from "Element/Note"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Element/DM.tsx b/packages/app/src/Element/DM.tsx index de058fee..2a8e9efa 100644 --- a/packages/app/src/Element/DM.tsx +++ b/packages/app/src/Element/DM.tsx @@ -2,7 +2,7 @@ import "./DM.css"; import { useEffect, useState } from "react"; import { useIntl } from "react-intl"; import { useInView } from "react-intersection-observer"; -import { TaggedRawEvent } from "System"; +import { TaggedRawEvent } from "@snort/system"; import useEventPublisher from "Feed/EventPublisher"; import NoteTime from "Element/NoteTime"; diff --git a/packages/app/src/Element/DmWindow.tsx b/packages/app/src/Element/DmWindow.tsx index 827984b7..b56e95c0 100644 --- a/packages/app/src/Element/DmWindow.tsx +++ b/packages/app/src/Element/DmWindow.tsx @@ -1,6 +1,6 @@ import "./DmWindow.css"; import { useEffect, useMemo, useRef } from "react"; -import { TaggedRawEvent } from "System"; +import { TaggedRawEvent } from "@snort/system"; import ProfileImage from "Element/ProfileImage"; import DM from "Element/DM"; diff --git a/packages/app/src/Element/FollowButton.tsx b/packages/app/src/Element/FollowButton.tsx index e284bc83..72e3fb33 100644 --- a/packages/app/src/Element/FollowButton.tsx +++ b/packages/app/src/Element/FollowButton.tsx @@ -1,6 +1,6 @@ import "./FollowButton.css"; import { FormattedMessage } from "react-intl"; -import { HexKey } from "System"; +import { HexKey } from "@snort/system"; import useEventPublisher from "Feed/EventPublisher"; import { parseId } from "SnortUtils"; diff --git a/packages/app/src/Element/FollowListBase.tsx b/packages/app/src/Element/FollowListBase.tsx index e98ef131..fc0b09dc 100644 --- a/packages/app/src/Element/FollowListBase.tsx +++ b/packages/app/src/Element/FollowListBase.tsx @@ -1,6 +1,6 @@ import { ReactNode } from "react"; import { FormattedMessage } from "react-intl"; -import { HexKey } from "System"; +import { HexKey } from "@snort/system"; import useEventPublisher from "Feed/EventPublisher"; import ProfilePreview from "Element/ProfilePreview"; diff --git a/packages/app/src/Element/Mention.tsx b/packages/app/src/Element/Mention.tsx index b7a8af60..9fe4723e 100644 --- a/packages/app/src/Element/Mention.tsx +++ b/packages/app/src/Element/Mention.tsx @@ -1,6 +1,6 @@ import { useMemo } from "react"; import { Link } from "react-router-dom"; -import { HexKey } from "System"; +import { HexKey } from "@snort/system"; import { useUserProfile } from "Hooks/useUserProfile"; import { profileLink } from "SnortUtils"; diff --git a/packages/app/src/Element/MuteButton.tsx b/packages/app/src/Element/MuteButton.tsx index 7f8d492f..b95d1e21 100644 --- a/packages/app/src/Element/MuteButton.tsx +++ b/packages/app/src/Element/MuteButton.tsx @@ -1,5 +1,5 @@ import { FormattedMessage } from "react-intl"; -import { HexKey } from "System"; +import { HexKey } from "@snort/system"; import useModeration from "Hooks/useModeration"; import messages from "./messages"; diff --git a/packages/app/src/Element/MutedList.tsx b/packages/app/src/Element/MutedList.tsx index 6fa9044c..b3ecb946 100644 --- a/packages/app/src/Element/MutedList.tsx +++ b/packages/app/src/Element/MutedList.tsx @@ -1,5 +1,5 @@ import { FormattedMessage } from "react-intl"; -import { HexKey } from "System"; +import { HexKey } from "@snort/system"; import MuteButton from "Element/MuteButton"; import ProfilePreview from "Element/ProfilePreview"; import useModeration from "Hooks/useModeration"; diff --git a/packages/app/src/Element/Nip05.tsx b/packages/app/src/Element/Nip05.tsx index e3e14170..5e1de7ab 100644 --- a/packages/app/src/Element/Nip05.tsx +++ b/packages/app/src/Element/Nip05.tsx @@ -1,5 +1,5 @@ import "./Nip05.css"; -import { HexKey } from "System"; +import { HexKey } from "@snort/system"; import Icon from "Icons/Icon"; import { useUserProfile } from "Hooks/useUserProfile"; diff --git a/packages/app/src/Element/Nip5Service.tsx b/packages/app/src/Element/Nip5Service.tsx index 122c249b..3eb16982 100644 --- a/packages/app/src/Element/Nip5Service.tsx +++ b/packages/app/src/Element/Nip5Service.tsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState, ChangeEvent } from "react"; import { useIntl, FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; -import { UserMetadata } from "System"; +import { UserMetadata, mapEventToProfile } from "@snort/system"; import { unwrap } from "SnortUtils"; import { formatShort } from "Number"; @@ -22,7 +22,7 @@ import useEventPublisher from "Feed/EventPublisher"; import { debounce } from "SnortUtils"; import useLogin from "Hooks/useLogin"; import SnortServiceProvider from "Nip05/SnortServiceProvider"; -import { mapEventToProfile, UserCache } from "Cache"; +import { UserCache } from "Cache"; import messages from "./messages"; diff --git a/packages/app/src/Element/NostrFileHeader.tsx b/packages/app/src/Element/NostrFileHeader.tsx index 512d09df..b4efdc0d 100644 --- a/packages/app/src/Element/NostrFileHeader.tsx +++ b/packages/app/src/Element/NostrFileHeader.tsx @@ -1,7 +1,7 @@ import { FormattedMessage } from "react-intl"; -import { NostrEvent } from "System"; +import { NostrEvent, NostrLink } from "@snort/system"; -import { findTag, NostrLink } from "SnortUtils"; +import { findTag } from "SnortUtils"; import useEventFeed from "Feed/EventFeed"; import PageSpinner from "Element/PageSpinner"; import Reveal from "Element/Reveal"; diff --git a/packages/app/src/Element/NostrLink.tsx b/packages/app/src/Element/NostrLink.tsx index 64efbde8..28ea734a 100644 --- a/packages/app/src/Element/NostrLink.tsx +++ b/packages/app/src/Element/NostrLink.tsx @@ -1,8 +1,7 @@ import { Link } from "react-router-dom"; -import { NostrPrefix } from "System"; +import { NostrPrefix, parseNostrLink } from "@snort/system"; import Mention from "Element/Mention"; -import { parseNostrLink } from "SnortUtils"; import NoteQuote from "Element/NoteQuote"; export default function NostrLink({ link, depth }: { link: string; depth?: number }) { diff --git a/packages/app/src/Element/Note.tsx b/packages/app/src/Element/Note.tsx index 5a582f6a..10115754 100644 --- a/packages/app/src/Element/Note.tsx +++ b/packages/app/src/Element/Note.tsx @@ -3,7 +3,7 @@ import React, { useMemo, useState, useLayoutEffect, ReactNode } from "react"; import { useNavigate, Link } from "react-router-dom"; import { useInView } from "react-intersection-observer"; import { useIntl, FormattedMessage } from "react-intl"; -import { TaggedRawEvent, HexKey, EventKind, NostrPrefix, Lists } from "System"; +import { TaggedRawEvent, HexKey, EventKind, NostrPrefix, Lists, EventExt } from "@snort/system"; import useEventPublisher from "Feed/EventPublisher"; import Icon from "Icons/Icon"; @@ -26,7 +26,6 @@ import Reveal from "Element/Reveal"; import useModeration from "Hooks/useModeration"; import { UserCache } from "Cache/UserCache"; import Poll from "Element/Poll"; -import { EventExt } from "System/EventExt"; import useLogin from "Hooks/useLogin"; import { setBookmarked, setPinned } from "Login"; import { NostrFileElement } from "Element/NostrFileHeader"; diff --git a/packages/app/src/Element/NoteCreator.tsx b/packages/app/src/Element/NoteCreator.tsx index 6a4a2a4a..958cc179 100644 --- a/packages/app/src/Element/NoteCreator.tsx +++ b/packages/app/src/Element/NoteCreator.tsx @@ -1,7 +1,7 @@ import "./NoteCreator.css"; import { FormattedMessage, useIntl } from "react-intl"; import { useDispatch, useSelector } from "react-redux"; -import { encodeTLV, EventKind, NostrPrefix, TaggedRawEvent } from "System"; +import { encodeTLV, EventKind, NostrPrefix, TaggedRawEvent, EventBuilder } from "@snort/system"; import Icon from "Icons/Icon"; import useEventPublisher from "Feed/EventPublisher"; @@ -31,7 +31,6 @@ import { LNURL } from "LNURL"; import messages from "./messages"; import { ClipboardEventHandler, useState } from "react"; import Spinner from "Icons/Spinner"; -import { EventBuilder } from "System"; import { Menu, MenuItem } from "@szhsin/react-menu"; import { LoginStore } from "Login"; import { getCurrentSubscription } from "Subscription"; diff --git a/packages/app/src/Element/NoteFooter.tsx b/packages/app/src/Element/NoteFooter.tsx index 25fef378..800c4d31 100644 --- a/packages/app/src/Element/NoteFooter.tsx +++ b/packages/app/src/Element/NoteFooter.tsx @@ -3,7 +3,7 @@ import { useSelector, useDispatch } from "react-redux"; import { useIntl, FormattedMessage } from "react-intl"; import { Menu, MenuItem } from "@szhsin/react-menu"; import { useLongPress } from "use-long-press"; -import { TaggedRawEvent, HexKey, u256, encodeTLV, NostrPrefix, Lists } from "System"; +import { TaggedRawEvent, HexKey, u256, encodeTLV, NostrPrefix, Lists } from "@snort/system"; import Icon from "Icons/Icon"; import Spinner from "Icons/Spinner"; diff --git a/packages/app/src/Element/NoteQuote.tsx b/packages/app/src/Element/NoteQuote.tsx index 31cca7ba..262b1ca6 100644 --- a/packages/app/src/Element/NoteQuote.tsx +++ b/packages/app/src/Element/NoteQuote.tsx @@ -1,5 +1,5 @@ import useEventFeed from "Feed/EventFeed"; -import { NostrLink } from "SnortUtils"; +import { NostrLink } from "@snort/system"; import Note from "Element/Note"; import PageSpinner from "Element/PageSpinner"; diff --git a/packages/app/src/Element/NoteReaction.tsx b/packages/app/src/Element/NoteReaction.tsx index 1849c904..00301dac 100644 --- a/packages/app/src/Element/NoteReaction.tsx +++ b/packages/app/src/Element/NoteReaction.tsx @@ -1,14 +1,13 @@ import "./NoteReaction.css"; import { Link } from "react-router-dom"; import { useMemo } from "react"; -import { EventKind, NostrEvent, TaggedRawEvent, NostrPrefix } from "System"; +import { EventKind, NostrEvent, TaggedRawEvent, NostrPrefix, EventExt } from "@snort/system"; import Note from "Element/Note"; import ProfileImage from "Element/ProfileImage"; import { eventLink, hexToBech32 } from "SnortUtils"; import NoteTime from "Element/NoteTime"; import useModeration from "Hooks/useModeration"; -import { EventExt } from "System/EventExt"; export interface NoteReactionProps { data: TaggedRawEvent; diff --git a/packages/app/src/Element/Poll.tsx b/packages/app/src/Element/Poll.tsx index bb29730e..4ef7dcf9 100644 --- a/packages/app/src/Element/Poll.tsx +++ b/packages/app/src/Element/Poll.tsx @@ -1,4 +1,4 @@ -import { TaggedRawEvent } from "System"; +import { TaggedRawEvent } from "@snort/system"; import { useState } from "react"; import { FormattedMessage, FormattedNumber, useIntl } from "react-intl"; diff --git a/packages/app/src/Element/ProfileImage.tsx b/packages/app/src/Element/ProfileImage.tsx index 96c5e15a..5745b9c7 100644 --- a/packages/app/src/Element/ProfileImage.tsx +++ b/packages/app/src/Element/ProfileImage.tsx @@ -1,13 +1,12 @@ import "./ProfileImage.css"; import React, { useMemo } from "react"; -import { HexKey, NostrPrefix } from "System"; +import { HexKey, NostrPrefix, MetadataCache } from "@snort/system"; import { useUserProfile } from "Hooks/useUserProfile"; import { hexToBech32, profileLink } from "SnortUtils"; import Avatar from "Element/Avatar"; import Nip05 from "Element/Nip05"; -import { MetadataCache } from "Cache"; import { Link } from "react-router-dom"; export interface ProfileImageProps { diff --git a/packages/app/src/Element/ProfilePreview.tsx b/packages/app/src/Element/ProfilePreview.tsx index 15f02c05..6397e778 100644 --- a/packages/app/src/Element/ProfilePreview.tsx +++ b/packages/app/src/Element/ProfilePreview.tsx @@ -4,7 +4,7 @@ import { ReactNode } from "react"; import ProfileImage from "Element/ProfileImage"; import FollowButton from "Element/FollowButton"; import { useUserProfile } from "Hooks/useUserProfile"; -import { HexKey } from "System"; +import { HexKey } from "@snort/system"; import { useInView } from "react-intersection-observer"; export interface ProfilePreviewProps { diff --git a/packages/app/src/Element/PubkeyList.tsx b/packages/app/src/Element/PubkeyList.tsx index 6350af3f..5812e6fa 100644 --- a/packages/app/src/Element/PubkeyList.tsx +++ b/packages/app/src/Element/PubkeyList.tsx @@ -1,4 +1,4 @@ -import { NostrEvent } from "System"; +import { NostrEvent } from "@snort/system"; import { dedupe } from "SnortUtils"; import FollowListBase from "./FollowListBase"; diff --git a/packages/app/src/Element/Reactions.tsx b/packages/app/src/Element/Reactions.tsx index 2dcfbe30..8e723277 100644 --- a/packages/app/src/Element/Reactions.tsx +++ b/packages/app/src/Element/Reactions.tsx @@ -2,7 +2,7 @@ import "./Reactions.css"; import { useState, useMemo, useEffect } from "react"; import { useIntl, FormattedMessage } from "react-intl"; -import { TaggedRawEvent } from "System"; +import { TaggedRawEvent } from "@snort/system"; import { formatShort } from "Number"; import Icon from "Icons/Icon"; diff --git a/packages/app/src/Element/Relay.tsx b/packages/app/src/Element/Relay.tsx index e79ed1ab..ab3d3de9 100644 --- a/packages/app/src/Element/Relay.tsx +++ b/packages/app/src/Element/Relay.tsx @@ -2,7 +2,7 @@ import "./Relay.css"; import { useMemo } from "react"; import { FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; -import { RelaySettings } from "System"; +import { RelaySettings } from "@snort/system"; import useRelayState from "Feed/RelayState"; import { System } from "index"; diff --git a/packages/app/src/Element/RelaysMetadata.tsx b/packages/app/src/Element/RelaysMetadata.tsx index 8973a428..5df1631e 100644 --- a/packages/app/src/Element/RelaysMetadata.tsx +++ b/packages/app/src/Element/RelaysMetadata.tsx @@ -2,7 +2,7 @@ import "./RelaysMetadata.css"; import Nostrich from "nostrich.webp"; import { useState } from "react"; -import { FullRelaySettings } from "System"; +import { FullRelaySettings } from "@snort/system"; import Icon from "Icons/Icon"; const RelayFavicon = ({ url }: { url: string }) => { diff --git a/packages/app/src/Element/SendSats.tsx b/packages/app/src/Element/SendSats.tsx index 765c5929..1c55269d 100644 --- a/packages/app/src/Element/SendSats.tsx +++ b/packages/app/src/Element/SendSats.tsx @@ -2,7 +2,7 @@ import "./SendSats.css"; import React, { useEffect, useMemo, useState } from "react"; import { useIntl, FormattedMessage } from "react-intl"; -import { HexKey, NostrEvent } from "System"; +import { HexKey, NostrEvent, EventPublisher } from "@snort/system"; import { System } from "index"; import { formatShort } from "Number"; import Icon from "Icons/Icon"; @@ -16,7 +16,6 @@ import { chunks, debounce } from "SnortUtils"; import { useWallet } from "Wallet"; import useLogin from "Hooks/useLogin"; import { generateRandomKey } from "Login"; -import { EventPublisher } from "System/EventPublisher"; import { ZapPoolController } from "ZapPoolController"; import messages from "./messages"; diff --git a/packages/app/src/Element/SubDebug.tsx b/packages/app/src/Element/SubDebug.tsx index 73375741..bf1707fe 100644 --- a/packages/app/src/Element/SubDebug.tsx +++ b/packages/app/src/Element/SubDebug.tsx @@ -5,7 +5,7 @@ import useRelayState from "Feed/RelayState"; import Tabs, { Tab } from "Element/Tabs"; import { unwrap } from "SnortUtils"; import useSystemState from "Hooks/useSystemState"; -import { ReqFilter } from "System"; +import { ReqFilter } from "@snort/system"; import { useCopy } from "useCopy"; import { System } from "index"; diff --git a/packages/app/src/Element/SuggestedProfiles.tsx b/packages/app/src/Element/SuggestedProfiles.tsx index 5cb06808..7d86420c 100644 --- a/packages/app/src/Element/SuggestedProfiles.tsx +++ b/packages/app/src/Element/SuggestedProfiles.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { HexKey, NostrPrefix } from "System"; +import { HexKey, NostrPrefix } from "@snort/system"; import { FormattedMessage } from "react-intl"; import FollowListBase from "Element/FollowListBase"; diff --git a/packages/app/src/Element/Text.tsx b/packages/app/src/Element/Text.tsx index 89ede775..779c35b8 100644 --- a/packages/app/src/Element/Text.tsx +++ b/packages/app/src/Element/Text.tsx @@ -1,10 +1,10 @@ import "./Text.css"; import { useMemo } from "react"; import { Link, useLocation } from "react-router-dom"; -import { HexKey, NostrPrefix } from "System"; +import { HexKey, NostrPrefix, validateNostrLink } from "@snort/system"; import { MentionRegex, InvoiceRegex, HashtagRegex, CashuRegex } from "Const"; -import { eventLink, hexToBech32, splitByUrl, validateNostrLink } from "SnortUtils"; +import { eventLink, hexToBech32, splitByUrl } from "SnortUtils"; import Invoice from "Element/Invoice"; import Hashtag from "Element/Hashtag"; import Mention from "Element/Mention"; diff --git a/packages/app/src/Element/Textarea.tsx b/packages/app/src/Element/Textarea.tsx index c92c497e..352ade10 100644 --- a/packages/app/src/Element/Textarea.tsx +++ b/packages/app/src/Element/Textarea.tsx @@ -4,12 +4,11 @@ import "./Textarea.css"; import { useIntl } from "react-intl"; import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete"; import TextareaAutosize from "react-textarea-autosize"; -import { NostrPrefix } from "System"; +import { NostrPrefix, MetadataCache } from "@snort/system"; import Avatar from "Element/Avatar"; import Nip05 from "Element/Nip05"; import { hexToBech32 } from "SnortUtils"; -import { MetadataCache } from "Cache"; import { UserCache } from "Cache/UserCache"; import messages from "./messages"; diff --git a/packages/app/src/Element/Thread.tsx b/packages/app/src/Element/Thread.tsx index 2d252382..f30f8966 100644 --- a/packages/app/src/Element/Thread.tsx +++ b/packages/app/src/Element/Thread.tsx @@ -2,10 +2,17 @@ import "./Thread.css"; import { useMemo, useState, ReactNode } from "react"; import { useIntl } from "react-intl"; import { useNavigate, useLocation, Link, useParams } from "react-router-dom"; -import { TaggedRawEvent, u256, EventKind, NostrPrefix } from "System"; -import { EventExt, Thread as ThreadInfo } from "System/EventExt"; +import { + TaggedRawEvent, + u256, + EventKind, + NostrPrefix, + EventExt, + Thread as ThreadInfo, + parseNostrLink, +} from "@snort/system"; -import { eventLink, unwrap, getReactions, parseNostrLink, getAllReactions, findTag } from "SnortUtils"; +import { eventLink, unwrap, getReactions, getAllReactions, findTag } from "SnortUtils"; import BackButton from "Element/BackButton"; import Note from "Element/Note"; import NoteGhost from "Element/NoteGhost"; diff --git a/packages/app/src/Element/Timeline.tsx b/packages/app/src/Element/Timeline.tsx index 1159b1b9..6aa8401e 100644 --- a/packages/app/src/Element/Timeline.tsx +++ b/packages/app/src/Element/Timeline.tsx @@ -2,7 +2,7 @@ import "./Timeline.css"; import { FormattedMessage } from "react-intl"; import { useCallback, useMemo } from "react"; import { useInView } from "react-intersection-observer"; -import { TaggedRawEvent, EventKind, u256 } from "System"; +import { TaggedRawEvent, EventKind, u256 } from "@snort/system"; import Icon from "Icons/Icon"; import { dedupeByPubkey, findTag, tagFilterOfTextRepost } from "SnortUtils"; diff --git a/packages/app/src/Element/TrendingPosts.tsx b/packages/app/src/Element/TrendingPosts.tsx index c46c6e73..863174e6 100644 --- a/packages/app/src/Element/TrendingPosts.tsx +++ b/packages/app/src/Element/TrendingPosts.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { NostrEvent, TaggedRawEvent } from "System"; +import { NostrEvent, TaggedRawEvent } from "@snort/system"; import { FormattedMessage } from "react-intl"; import PageSpinner from "Element/PageSpinner"; diff --git a/packages/app/src/Element/TrendingUsers.tsx b/packages/app/src/Element/TrendingUsers.tsx index 9460b8ab..665acc89 100644 --- a/packages/app/src/Element/TrendingUsers.tsx +++ b/packages/app/src/Element/TrendingUsers.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { HexKey } from "System"; +import { HexKey } from "@snort/system"; import { FormattedMessage } from "react-intl"; import FollowListBase from "Element/FollowListBase"; diff --git a/packages/app/src/Element/Username.tsx b/packages/app/src/Element/Username.tsx index 47a12ca7..3235cd7d 100644 --- a/packages/app/src/Element/Username.tsx +++ b/packages/app/src/Element/Username.tsx @@ -1,7 +1,7 @@ import { MouseEvent } from "react"; import { useNavigate, Link } from "react-router-dom"; -import { HexKey } from "System"; +import { HexKey } from "@snort/system"; import { useUserProfile } from "Hooks/useUserProfile"; import { profileLink } from "SnortUtils"; diff --git a/packages/app/src/Element/WriteDm.tsx b/packages/app/src/Element/WriteDm.tsx index 445437bd..e060c1f7 100644 --- a/packages/app/src/Element/WriteDm.tsx +++ b/packages/app/src/Element/WriteDm.tsx @@ -1,4 +1,4 @@ -import { encodeTLV, NostrPrefix, NostrEvent } from "System"; +import { encodeTLV, NostrPrefix, NostrEvent } from "@snort/system"; import useEventPublisher from "Feed/EventPublisher"; import Icon from "Icons/Icon"; import Spinner from "Icons/Spinner"; diff --git a/packages/app/src/Element/Zap.tsx b/packages/app/src/Element/Zap.tsx index 6a84212b..cb59eee1 100644 --- a/packages/app/src/Element/Zap.tsx +++ b/packages/app/src/Element/Zap.tsx @@ -1,7 +1,7 @@ import "./Zap.css"; import { useMemo } from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import { HexKey, TaggedRawEvent } from "System"; +import { HexKey, TaggedRawEvent } from "@snort/system"; import { decodeInvoice, InvoiceDetails, sha256, unwrap } from "SnortUtils"; import { formatShort } from "Number"; diff --git a/packages/app/src/Element/ZapButton.tsx b/packages/app/src/Element/ZapButton.tsx index ef460d7d..1910fda1 100644 --- a/packages/app/src/Element/ZapButton.tsx +++ b/packages/app/src/Element/ZapButton.tsx @@ -1,6 +1,6 @@ import "./ZapButton.css"; import { useState } from "react"; -import { HexKey } from "System"; +import { HexKey } from "@snort/system"; import { useUserProfile } from "Hooks/useUserProfile"; import SendSats from "Element/SendSats"; diff --git a/packages/app/src/Element/ZapstrEmbed.tsx b/packages/app/src/Element/ZapstrEmbed.tsx index d5ca7f01..844b5b67 100644 --- a/packages/app/src/Element/ZapstrEmbed.tsx +++ b/packages/app/src/Element/ZapstrEmbed.tsx @@ -1,6 +1,6 @@ import "./ZapstrEmbed.css"; import { Link } from "react-router-dom"; -import { encodeTLV, NostrPrefix, NostrEvent } from "System"; +import { encodeTLV, NostrPrefix, NostrEvent } from "@snort/system"; import { ProxyImg } from "Element/ProxyImg"; import ProfileImage from "Element/ProfileImage"; diff --git a/packages/app/src/External/NostrBand.ts b/packages/app/src/External/NostrBand.ts index 8615b61e..8553a460 100644 --- a/packages/app/src/External/NostrBand.ts +++ b/packages/app/src/External/NostrBand.ts @@ -1,4 +1,4 @@ -import { NostrEvent } from "System"; +import { NostrEvent } from "@snort/system"; export interface TrendingUser { pubkey: string; diff --git a/packages/app/src/Feed/BadgesFeed.ts b/packages/app/src/Feed/BadgesFeed.ts index baf0bacf..8397f8ce 100644 --- a/packages/app/src/Feed/BadgesFeed.ts +++ b/packages/app/src/Feed/BadgesFeed.ts @@ -1,9 +1,7 @@ import { useMemo } from "react"; -import { EventKind, HexKey, Lists } from "System"; +import { EventKind, HexKey, Lists, RequestBuilder, FlatNoteStore, ReplaceableNoteStore } from "@snort/system"; import { unwrap, findTag, chunks } from "SnortUtils"; -import { RequestBuilder } from "System"; -import { FlatNoteStore, ReplaceableNoteStore } from "System/NoteCollection"; import useRequestBuilder from "Hooks/useRequestBuilder"; type BadgeAwards = { diff --git a/packages/app/src/Feed/BookmarkFeed.tsx b/packages/app/src/Feed/BookmarkFeed.tsx index 647cd5b0..2c7da28e 100644 --- a/packages/app/src/Feed/BookmarkFeed.tsx +++ b/packages/app/src/Feed/BookmarkFeed.tsx @@ -1,4 +1,4 @@ -import { HexKey, Lists } from "System"; +import { HexKey, Lists } from "@snort/system"; import useNotelistSubscription from "Hooks/useNotelistSubscription"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Feed/EventFeed.ts b/packages/app/src/Feed/EventFeed.ts index 6ee13f7a..2c295271 100644 --- a/packages/app/src/Feed/EventFeed.ts +++ b/packages/app/src/Feed/EventFeed.ts @@ -1,9 +1,8 @@ import { useMemo } from "react"; -import { NostrPrefix } from "System"; +import { NostrPrefix, RequestBuilder, ReplaceableNoteStore, NostrLink } from "@snort/system"; import useRequestBuilder from "Hooks/useRequestBuilder"; -import { RequestBuilder, ReplaceableNoteStore } from "System"; -import { NostrLink, unwrap } from "SnortUtils"; +import { unwrap } from "SnortUtils"; export default function useEventFeed(link: NostrLink) { const sub = useMemo(() => { diff --git a/packages/app/src/Feed/EventPublisher.ts b/packages/app/src/Feed/EventPublisher.ts index e2eef45c..ff238977 100644 --- a/packages/app/src/Feed/EventPublisher.ts +++ b/packages/app/src/Feed/EventPublisher.ts @@ -1,6 +1,6 @@ import { useMemo } from "react"; import useLogin from "Hooks/useLogin"; -import { EventPublisher } from "System/EventPublisher"; +import { EventPublisher } from "@snort/system"; import { System } from "index"; export default function useEventPublisher() { diff --git a/packages/app/src/Feed/FollowersFeed.ts b/packages/app/src/Feed/FollowersFeed.ts index e8095bce..dc2f79b8 100644 --- a/packages/app/src/Feed/FollowersFeed.ts +++ b/packages/app/src/Feed/FollowersFeed.ts @@ -1,7 +1,6 @@ import { useMemo } from "react"; -import { HexKey, EventKind } from "System"; +import { HexKey, EventKind, PubkeyReplaceableNoteStore, RequestBuilder } from "@snort/system"; -import { PubkeyReplaceableNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; export default function useFollowersFeed(pubkey?: HexKey) { diff --git a/packages/app/src/Feed/FollowsFeed.ts b/packages/app/src/Feed/FollowsFeed.ts index b20bef55..5f7b260b 100644 --- a/packages/app/src/Feed/FollowsFeed.ts +++ b/packages/app/src/Feed/FollowsFeed.ts @@ -1,7 +1,6 @@ import { useMemo } from "react"; -import { HexKey, TaggedRawEvent, EventKind } from "System"; +import { HexKey, TaggedRawEvent, EventKind, PubkeyReplaceableNoteStore, RequestBuilder } from "@snort/system"; -import { PubkeyReplaceableNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Feed/LoginFeed.ts b/packages/app/src/Feed/LoginFeed.ts index bf214d7d..bf4a71b6 100644 --- a/packages/app/src/Feed/LoginFeed.ts +++ b/packages/app/src/Feed/LoginFeed.ts @@ -1,5 +1,5 @@ import { useEffect, useMemo } from "react"; -import { TaggedRawEvent, Lists, EventKind } from "System"; +import { TaggedRawEvent, Lists, EventKind, FlatNoteStore, RequestBuilder } from "@snort/system"; import debug from "debug"; import { bech32ToHex, getNewest, getNewestEventTagsByKey, unwrap } from "SnortUtils"; @@ -7,7 +7,6 @@ import { makeNotification, sendNotification } from "Notifications"; import useEventPublisher from "Feed/EventPublisher"; import { getMutedKeys } from "Feed/MuteList"; import useModeration from "Hooks/useModeration"; -import { FlatNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; import { DmCache } from "Cache"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Feed/MuteList.ts b/packages/app/src/Feed/MuteList.ts index 3338c496..5e7c75d5 100644 --- a/packages/app/src/Feed/MuteList.ts +++ b/packages/app/src/Feed/MuteList.ts @@ -1,8 +1,14 @@ import { useMemo } from "react"; -import { HexKey, TaggedRawEvent, Lists, EventKind } from "System"; +import { + HexKey, + TaggedRawEvent, + Lists, + EventKind, + ParameterizedReplaceableNoteStore, + RequestBuilder, +} from "@snort/system"; import { getNewest } from "SnortUtils"; -import { ParameterizedReplaceableNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Feed/PinnedFeed.tsx b/packages/app/src/Feed/PinnedFeed.tsx index f55fe40d..bd714903 100644 --- a/packages/app/src/Feed/PinnedFeed.tsx +++ b/packages/app/src/Feed/PinnedFeed.tsx @@ -1,4 +1,4 @@ -import { HexKey, Lists } from "System"; +import { HexKey, Lists } from "@snort/system"; import useNotelistSubscription from "Hooks/useNotelistSubscription"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Feed/RelaysFeed.tsx b/packages/app/src/Feed/RelaysFeed.tsx index 677f5586..e6fe274e 100644 --- a/packages/app/src/Feed/RelaysFeed.tsx +++ b/packages/app/src/Feed/RelaysFeed.tsx @@ -1,8 +1,6 @@ import { useMemo } from "react"; -import { HexKey, FullRelaySettings, EventKind } from "System"; +import { HexKey, FullRelaySettings, EventKind, RequestBuilder, ReplaceableNoteStore } from "@snort/system"; -import { RequestBuilder } from "System"; -import { ReplaceableNoteStore } from "System/NoteCollection"; import useRequestBuilder from "Hooks/useRequestBuilder"; export default function useRelaysFeed(pubkey?: HexKey) { diff --git a/packages/app/src/Feed/RelaysFeedFollows.tsx b/packages/app/src/Feed/RelaysFeedFollows.tsx index 128fe11f..77367f4d 100644 --- a/packages/app/src/Feed/RelaysFeedFollows.tsx +++ b/packages/app/src/Feed/RelaysFeedFollows.tsx @@ -1,9 +1,16 @@ import { useMemo } from "react"; -import { HexKey, FullRelaySettings, TaggedRawEvent, RelaySettings, EventKind } from "System"; +import { + HexKey, + FullRelaySettings, + TaggedRawEvent, + RelaySettings, + EventKind, + PubkeyReplaceableNoteStore, + RequestBuilder, +} from "@snort/system"; import debug from "debug"; import { sanitizeRelayUrl } from "SnortUtils"; -import { PubkeyReplaceableNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; import { UserRelays } from "Cache/UserRelayCache"; diff --git a/packages/app/src/Feed/ThreadFeed.ts b/packages/app/src/Feed/ThreadFeed.ts index adbc14f9..1b0af776 100644 --- a/packages/app/src/Feed/ThreadFeed.ts +++ b/packages/app/src/Feed/ThreadFeed.ts @@ -1,8 +1,7 @@ import { useEffect, useMemo, useState } from "react"; -import { u256, EventKind } from "System"; +import { u256, EventKind, NostrLink, FlatNoteStore, RequestBuilder } from "@snort/system"; -import { appendDedupe, NostrLink } from "SnortUtils"; -import { FlatNoteStore, RequestBuilder } from "System"; +import { appendDedupe } from "SnortUtils"; import useRequestBuilder from "Hooks/useRequestBuilder"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Feed/TimelineFeed.ts b/packages/app/src/Feed/TimelineFeed.ts index 8fb8693f..9b8e439b 100644 --- a/packages/app/src/Feed/TimelineFeed.ts +++ b/packages/app/src/Feed/TimelineFeed.ts @@ -1,8 +1,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; -import { EventKind, u256 } from "System"; +import { EventKind, u256, FlatNoteStore, RequestBuilder } from "@snort/system"; import { unixNow, unwrap, tagFilterOfTextRepost } from "SnortUtils"; -import { FlatNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; import useTimelineWindow from "Hooks/useTimelineWindow"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Feed/ZapsFeed.ts b/packages/app/src/Feed/ZapsFeed.ts index 88694a5e..6fd52ba4 100644 --- a/packages/app/src/Feed/ZapsFeed.ts +++ b/packages/app/src/Feed/ZapsFeed.ts @@ -1,8 +1,7 @@ import { useMemo } from "react"; -import { HexKey, EventKind } from "System"; +import { HexKey, EventKind, FlatNoteStore, RequestBuilder } from "@snort/system"; import { parseZap } from "Element/Zap"; -import { FlatNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; export default function useZapsFeed(pubkey?: HexKey) { diff --git a/packages/app/src/Hooks/useInteractionCache.tsx b/packages/app/src/Hooks/useInteractionCache.tsx index b75e9bf0..46a7521d 100644 --- a/packages/app/src/Hooks/useInteractionCache.tsx +++ b/packages/app/src/Hooks/useInteractionCache.tsx @@ -1,5 +1,5 @@ import { useSyncExternalStore } from "react"; -import { HexKey, u256 } from "System"; +import { HexKey, u256 } from "@snort/system"; import { InteractionCache } from "Cache/EventInteractionCache"; import { EventInteraction } from "Db"; diff --git a/packages/app/src/Hooks/useModeration.tsx b/packages/app/src/Hooks/useModeration.tsx index 2cbd3c7a..3a530c5a 100644 --- a/packages/app/src/Hooks/useModeration.tsx +++ b/packages/app/src/Hooks/useModeration.tsx @@ -1,4 +1,4 @@ -import { HexKey } from "System"; +import { HexKey } from "@snort/system"; import useEventPublisher from "Feed/EventPublisher"; import useLogin from "Hooks/useLogin"; import { setBlocked, setMuted } from "Login"; diff --git a/packages/app/src/Hooks/useNotelistSubscription.ts b/packages/app/src/Hooks/useNotelistSubscription.ts index 55aa7f95..f9e1eee2 100644 --- a/packages/app/src/Hooks/useNotelistSubscription.ts +++ b/packages/app/src/Hooks/useNotelistSubscription.ts @@ -1,7 +1,13 @@ import { useMemo } from "react"; -import { HexKey, Lists, EventKind } from "System"; +import { + HexKey, + Lists, + EventKind, + FlatNoteStore, + ParameterizedReplaceableNoteStore, + RequestBuilder, +} from "@snort/system"; -import { FlatNoteStore, ParameterizedReplaceableNoteStore, RequestBuilder } from "System"; import useRequestBuilder from "Hooks/useRequestBuilder"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Hooks/useRequestBuilder.tsx b/packages/app/src/Hooks/useRequestBuilder.tsx index 6d2fb8a7..d982ed9c 100644 --- a/packages/app/src/Hooks/useRequestBuilder.tsx +++ b/packages/app/src/Hooks/useRequestBuilder.tsx @@ -1,6 +1,5 @@ import { useSyncExternalStore } from "react"; -import { RequestBuilder } from "System"; -import { EmptySnapshot, NoteStore, StoreSnapshot } from "System/NoteCollection"; +import { RequestBuilder, EmptySnapshot, NoteStore, StoreSnapshot } from "@snort/system"; import { unwrap } from "SnortUtils"; import { System } from "index"; diff --git a/packages/app/src/Hooks/useSystemState.tsx b/packages/app/src/Hooks/useSystemState.tsx index 971675be..78932907 100644 --- a/packages/app/src/Hooks/useSystemState.tsx +++ b/packages/app/src/Hooks/useSystemState.tsx @@ -1,5 +1,5 @@ import { useSyncExternalStore } from "react"; -import { SystemSnapshot } from "System"; +import { SystemSnapshot } from "@snort/system"; import { System } from "index"; export default function useSystemState() { diff --git a/packages/app/src/Hooks/useUserProfile.ts b/packages/app/src/Hooks/useUserProfile.ts index 42d68310..3909774a 100644 --- a/packages/app/src/Hooks/useUserProfile.ts +++ b/packages/app/src/Hooks/useUserProfile.ts @@ -1,7 +1,6 @@ import { useEffect, useSyncExternalStore } from "react"; -import { HexKey } from "System"; -import { MetadataCache } from "Cache"; +import { HexKey, MetadataCache } from "@snort/system"; import { UserCache } from "Cache/UserCache"; import { ProfileLoader } from "index"; diff --git a/packages/app/src/LNURL.ts b/packages/app/src/LNURL.ts index 4138bb93..42978610 100644 --- a/packages/app/src/LNURL.ts +++ b/packages/app/src/LNURL.ts @@ -1,4 +1,4 @@ -import { HexKey, NostrEvent } from "System"; +import { HexKey, NostrEvent } from "@snort/system"; import { EmailRegex } from "Const"; import { bech32ToText, unwrap } from "SnortUtils"; diff --git a/packages/app/src/Login/Functions.ts b/packages/app/src/Login/Functions.ts index ac9c37f2..1ba6ffcf 100644 --- a/packages/app/src/Login/Functions.ts +++ b/packages/app/src/Login/Functions.ts @@ -1,4 +1,4 @@ -import { HexKey, RelaySettings } from "System"; +import { HexKey, RelaySettings, EventPublisher } from "@snort/system"; import * as secp from "@noble/curves/secp256k1"; import * as utils from "@noble/curves/abstract/utils"; @@ -7,7 +7,6 @@ import { LoginStore, UserPreferences, LoginSession } from "Login"; import { generateBip39Entropy, entropyToPrivateKey } from "nip6"; import { bech32ToHex, dedupeById, randomSample, sanitizeRelayUrl, unixNowMs, unwrap } from "SnortUtils"; import { SubscriptionEvent } from "Subscription"; -import { EventPublisher } from "System/EventPublisher"; import { System } from "index"; export function setRelays(state: LoginSession, relays: Record, createdAt: number) { diff --git a/packages/app/src/Login/LoginSession.ts b/packages/app/src/Login/LoginSession.ts index 95736dd6..3a7fb288 100644 --- a/packages/app/src/Login/LoginSession.ts +++ b/packages/app/src/Login/LoginSession.ts @@ -1,4 +1,4 @@ -import { HexKey, RelaySettings, u256 } from "System"; +import { HexKey, RelaySettings, u256 } from "@snort/system"; import { UserPreferences } from "Login"; import { SubscriptionEvent } from "Subscription"; diff --git a/packages/app/src/Login/MultiAccountStore.ts b/packages/app/src/Login/MultiAccountStore.ts index ddbb4a02..70faea98 100644 --- a/packages/app/src/Login/MultiAccountStore.ts +++ b/packages/app/src/Login/MultiAccountStore.ts @@ -1,7 +1,7 @@ import * as secp from "@noble/curves/secp256k1"; import * as utils from "@noble/curves/abstract/utils"; -import { HexKey, RelaySettings } from "System"; +import { HexKey, RelaySettings } from "@snort/system"; import { DefaultRelays } from "Const"; import ExternalStore from "ExternalStore"; diff --git a/packages/app/src/Nip05/SnortServiceProvider.ts b/packages/app/src/Nip05/SnortServiceProvider.ts index fe23ef2a..d6efa314 100644 --- a/packages/app/src/Nip05/SnortServiceProvider.ts +++ b/packages/app/src/Nip05/SnortServiceProvider.ts @@ -1,5 +1,4 @@ -import { EventKind } from "System"; -import { EventPublisher } from "System/EventPublisher"; +import { EventKind, EventPublisher } from "@snort/system"; import { ServiceError, ServiceProvider } from "./ServiceProvider"; export interface ManageHandle { diff --git a/packages/app/src/Notifications.ts b/packages/app/src/Notifications.ts index 09360995..46a4fdef 100644 --- a/packages/app/src/Notifications.ts +++ b/packages/app/src/Notifications.ts @@ -1,7 +1,6 @@ import Nostrich from "nostrich.webp"; -import { TaggedRawEvent, EventKind } from "System"; -import { MetadataCache } from "Cache"; +import { TaggedRawEvent, EventKind, MetadataCache } from "@snort/system"; import { getDisplayName } from "Element/ProfileImage"; import { MentionRegex } from "Const"; import { tagFilterOfTextRepost, unwrap } from "SnortUtils"; diff --git a/packages/app/src/Pages/DonatePage.tsx b/packages/app/src/Pages/DonatePage.tsx index b871f7ac..64517db8 100644 --- a/packages/app/src/Pages/DonatePage.tsx +++ b/packages/app/src/Pages/DonatePage.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; import { FormattedMessage } from "react-intl"; -import { HexKey } from "System"; +import { HexKey } from "@snort/system"; import { ApiHost, KieranPubKey, SnortPubKey } from "Const"; import ProfilePreview from "Element/ProfilePreview"; diff --git a/packages/app/src/Pages/LoginPage.tsx b/packages/app/src/Pages/LoginPage.tsx index faf6e957..be38fea4 100644 --- a/packages/app/src/Pages/LoginPage.tsx +++ b/packages/app/src/Pages/LoginPage.tsx @@ -3,7 +3,7 @@ import "./LoginPage.css"; import { CSSProperties, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { useIntl, FormattedMessage } from "react-intl"; -import { HexKey } from "System"; +import { HexKey } from "@snort/system"; import { bech32ToHex, unwrap } from "SnortUtils"; import ZapButton from "Element/ZapButton"; diff --git a/packages/app/src/Pages/MessagesPage.tsx b/packages/app/src/Pages/MessagesPage.tsx index 34f83c7d..3aa0e6e4 100644 --- a/packages/app/src/Pages/MessagesPage.tsx +++ b/packages/app/src/Pages/MessagesPage.tsx @@ -1,7 +1,7 @@ import React, { useMemo, useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { useNavigate } from "react-router-dom"; -import { HexKey, NostrEvent, NostrPrefix } from "System"; +import { HexKey, NostrEvent, NostrPrefix } from "@snort/system"; import UnreadCount from "Element/UnreadCount"; import ProfileImage, { getDisplayName } from "Element/ProfileImage"; diff --git a/packages/app/src/Pages/NostrLinkHandler.tsx b/packages/app/src/Pages/NostrLinkHandler.tsx index 3a4d966d..ea8f57c5 100644 --- a/packages/app/src/Pages/NostrLinkHandler.tsx +++ b/packages/app/src/Pages/NostrLinkHandler.tsx @@ -1,10 +1,10 @@ -import { NostrPrefix } from "System"; +import { NostrPrefix, parseNostrLink } from "@snort/system"; import { useEffect, useState } from "react"; import { FormattedMessage } from "react-intl"; import { useNavigate, useParams } from "react-router-dom"; import Spinner from "Icons/Spinner"; -import { parseNostrLink, profileLink } from "SnortUtils"; +import { profileLink } from "SnortUtils"; import { getNip05PubKey } from "Pages/LoginPage"; export default function NostrLinkHandler() { diff --git a/packages/app/src/Pages/ProfilePage.tsx b/packages/app/src/Pages/ProfilePage.tsx index 25adf365..551b0c8f 100644 --- a/packages/app/src/Pages/ProfilePage.tsx +++ b/packages/app/src/Pages/ProfilePage.tsx @@ -2,9 +2,9 @@ import "./ProfilePage.css"; import { useEffect, useState } from "react"; import { useIntl, FormattedMessage } from "react-intl"; import { useNavigate, useParams } from "react-router-dom"; -import { encodeTLV, EventKind, HexKey, NostrPrefix } from "System"; +import { encodeTLV, EventKind, HexKey, NostrPrefix, parseNostrLink } from "@snort/system"; -import { parseNostrLink, getReactions, unwrap } from "SnortUtils"; +import { getReactions, unwrap } from "SnortUtils"; import { formatShort } from "Number"; import Note from "Element/Note"; import Bookmarks from "Element/Bookmarks"; diff --git a/packages/app/src/Pages/new/ProfileSetup.tsx b/packages/app/src/Pages/new/ProfileSetup.tsx index 20d6864d..26f5f23a 100644 --- a/packages/app/src/Pages/new/ProfileSetup.tsx +++ b/packages/app/src/Pages/new/ProfileSetup.tsx @@ -1,12 +1,13 @@ import { useEffect, useState } from "react"; import { useIntl, FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; +import { mapEventToProfile } from "@snort/system"; import Logo from "Element/Logo"; import useEventPublisher from "Feed/EventPublisher"; import useLogin from "Hooks/useLogin"; import { useUserProfile } from "Hooks/useUserProfile"; -import { mapEventToProfile, UserCache } from "Cache"; +import { UserCache } from "Cache"; import AvatarEditor from "Element/AvatarEditor"; import messages from "./messages"; diff --git a/packages/app/src/Pages/settings/Keys.tsx b/packages/app/src/Pages/settings/Keys.tsx index d8871bcc..00958e95 100644 --- a/packages/app/src/Pages/settings/Keys.tsx +++ b/packages/app/src/Pages/settings/Keys.tsx @@ -1,6 +1,6 @@ import "./Keys.css"; import { FormattedMessage } from "react-intl"; -import { encodeTLV, NostrPrefix } from "System"; +import { encodeTLV, NostrPrefix } from "@snort/system"; import Copy from "Element/Copy"; import useLogin from "Hooks/useLogin"; diff --git a/packages/app/src/Pages/settings/Profile.tsx b/packages/app/src/Pages/settings/Profile.tsx index 5ce684e4..1918036c 100644 --- a/packages/app/src/Pages/settings/Profile.tsx +++ b/packages/app/src/Pages/settings/Profile.tsx @@ -3,13 +3,14 @@ import Nostrich from "nostrich.webp"; import { useEffect, useState } from "react"; import { FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; +import { mapEventToProfile } from "@snort/system"; import useEventPublisher from "Feed/EventPublisher"; import { useUserProfile } from "Hooks/useUserProfile"; import { openFile } from "SnortUtils"; import useFileUpload from "Upload"; import AsyncButton from "Element/AsyncButton"; -import { mapEventToProfile, UserCache } from "Cache"; +import { UserCache } from "Cache"; import useLogin from "Hooks/useLogin"; import AvatarEditor from "Element/AvatarEditor"; import Icon from "Icons/Icon"; diff --git a/packages/app/src/Pages/settings/Relays.tsx b/packages/app/src/Pages/settings/Relays.tsx index 78b490c7..d5338309 100644 --- a/packages/app/src/Pages/settings/Relays.tsx +++ b/packages/app/src/Pages/settings/Relays.tsx @@ -23,7 +23,6 @@ const RelaySettingsPage = () => { if (publisher) { const ev = await publisher.contactList(login.follows.item, login.relays.item); publisher.broadcast(ev); - publisher.broadcastForBootstrap(ev); try { const onlineRelays = await fetch("https://api.nostr.watch/v1/online").then(r => r.json()); const relayList = await publisher.relayList(login.relays.item); diff --git a/packages/app/src/SnortApi.ts b/packages/app/src/SnortApi.ts index 75dde20a..498346f1 100644 --- a/packages/app/src/SnortApi.ts +++ b/packages/app/src/SnortApi.ts @@ -1,7 +1,6 @@ -import { EventKind } from "System"; +import { EventKind, EventPublisher } from "@snort/system"; import { ApiHost } from "Const"; import { SubscriptionType } from "Subscription"; -import { EventPublisher } from "System/EventPublisher"; export interface RevenueToday { donations: number; diff --git a/packages/app/src/SnortUtils/Utils.test.ts b/packages/app/src/SnortUtils/Utils.test.ts index e40c6979..b91d02ae 100644 --- a/packages/app/src/SnortUtils/Utils.test.ts +++ b/packages/app/src/SnortUtils/Utils.test.ts @@ -1,5 +1,4 @@ -import { NostrPrefix } from "System"; -import { parseNostrLink, tryParseNostrLink } from "."; +import { NostrPrefix } from "@snort/system"; import { splitByUrl, magnetURIDecode, getRelayName } from "."; import { describe, expect } from "@jest/globals"; @@ -93,47 +92,3 @@ describe("getRelayName", () => { expect(output).toEqual("relay.example2.com?broadcast=true"); }); }); - -describe("tryParseNostrLink", () => { - it("is a valid nostr link", () => { - expect(parseNostrLink("nostr:npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg")).toMatchObject({ - id: "7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e", - type: NostrPrefix.PublicKey, - }); - expect(parseNostrLink("web+nostr:npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg")).toMatchObject({ - id: "7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e", - type: NostrPrefix.PublicKey, - }); - expect(parseNostrLink("nostr:note15449edq4qa5wzgqvh8td0q0dp6hwtes4pknsrm7eygeenhlj99xsq94wu9")).toMatchObject({ - id: "a56a5cb4150768e1200cb9d6d781ed0eaee5e6150da701efd9223399dff2294d", - type: NostrPrefix.Note, - }); - expect( - parseNostrLink( - "nostr:nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p" - ) - ).toMatchObject({ - id: "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", - type: NostrPrefix.Profile, - relays: ["wss://r.x.com", "wss://djbas.sadkb.com"], - }); - expect(parseNostrLink("nostr:nevent1qqs226juks2sw68pyqxtn4khs8ksath9uc2smfcpalvjyvuemlezjngrd87dq")).toMatchObject({ - id: "a56a5cb4150768e1200cb9d6d781ed0eaee5e6150da701efd9223399dff2294d", - type: NostrPrefix.Event, - }); - expect( - parseNostrLink( - "nostr:naddr1qqzkjurnw4ksz9thwden5te0wfjkccte9ehx7um5wghx7un8qgs2d90kkcq3nk2jry62dyf50k0h36rhpdtd594my40w9pkal876jxgrqsqqqa28pccpzu" - ) - ).toMatchObject({ - id: "ipsum", - type: NostrPrefix.Address, - relays: ["wss://relay.nostr.org"], - author: "a695f6b60119d9521934a691347d9f78e8770b56da16bb255ee286ddf9fda919", - kind: 30023, - }); - }); - test.each(["nostr:npub", "web+nostr:npub", "nostr:nevent1xxx"])("should return false for invalid nostr links", lb => { - expect(tryParseNostrLink(lb)).toBeUndefined(); - }); -}); diff --git a/packages/app/src/SnortUtils/index.ts b/packages/app/src/SnortUtils/index.ts index 5ff04ed4..df89d9f5 100644 --- a/packages/app/src/SnortUtils/index.ts +++ b/packages/app/src/SnortUtils/index.ts @@ -13,12 +13,9 @@ import { EventKind, encodeTLV, NostrPrefix, - decodeTLV, - TLVEntryType, NostrEvent, -} from "System"; -import { MetadataCache } from "Cache"; -import NostrLink from "Element/NostrLink"; + MetadataCache, +} from "@snort/system"; export const sha256 = (str: string | Uint8Array): u256 => { return utils.bytesToHex(hash(str)); @@ -506,113 +503,6 @@ export function getUrlHostname(url?: string) { } } -export interface NostrLink { - type: NostrPrefix; - id: string; - kind?: number; - author?: string; - relays?: Array; - encode(): string; -} - -export function validateNostrLink(link: string): boolean { - try { - const parsedLink = parseNostrLink(link); - if (!parsedLink) { - return false; - } - if (parsedLink.type === NostrPrefix.PublicKey || parsedLink.type === NostrPrefix.Note) { - return parsedLink.id.length === 64; - } - - return true; - } catch { - return false; - } -} - -export function tryParseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLink | undefined { - try { - return parseNostrLink(link, prefixHint); - } catch { - return undefined; - } -} - -export function parseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLink { - const entity = link.startsWith("web+nostr:") || link.startsWith("nostr:") ? link.split(":")[1] : link; - - const isPrefix = (prefix: NostrPrefix) => { - return entity.startsWith(prefix); - }; - - if (isPrefix(NostrPrefix.PublicKey)) { - const id = bech32ToHex(entity); - if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); - return { - type: NostrPrefix.PublicKey, - id: id, - encode: () => hexToBech32(NostrPrefix.PublicKey, id), - }; - } else if (isPrefix(NostrPrefix.Note)) { - const id = bech32ToHex(entity); - if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); - return { - type: NostrPrefix.Note, - id: id, - encode: () => hexToBech32(NostrPrefix.Note, id), - }; - } else if (isPrefix(NostrPrefix.Profile) || isPrefix(NostrPrefix.Event) || isPrefix(NostrPrefix.Address)) { - const decoded = decodeTLV(entity); - - const id = decoded.find(a => a.type === TLVEntryType.Special)?.value as string; - const relays = decoded.filter(a => a.type === TLVEntryType.Relay).map(a => a.value as string); - const author = decoded.find(a => a.type === TLVEntryType.Author)?.value as string; - const kind = decoded.find(a => a.type === TLVEntryType.Kind)?.value as number; - - const encode = () => { - return entity; // return original - }; - if (isPrefix(NostrPrefix.Profile)) { - if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); - return { - type: NostrPrefix.Profile, - id, - relays, - kind, - author, - encode, - }; - } else if (isPrefix(NostrPrefix.Event)) { - if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); - return { - type: NostrPrefix.Event, - id, - relays, - kind, - author, - encode, - }; - } else if (isPrefix(NostrPrefix.Address)) { - return { - type: NostrPrefix.Address, - id, - relays, - kind, - author, - encode, - }; - } - } else if (prefixHint) { - return { - type: prefixHint, - id: link, - encode: () => hexToBech32(prefixHint, link), - }; - } - throw new Error("Invalid nostr link"); -} - export function sanitizeRelayUrl(url: string) { try { return new URL(url).toString(); diff --git a/packages/app/src/State/NoteCreator.ts b/packages/app/src/State/NoteCreator.ts index f0bf02fd..e6a8dda8 100644 --- a/packages/app/src/State/NoteCreator.ts +++ b/packages/app/src/State/NoteCreator.ts @@ -1,5 +1,5 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { NostrEvent, TaggedRawEvent } from "System"; +import { NostrEvent, TaggedRawEvent } from "@snort/system"; interface NoteCreatorStore { show: boolean; diff --git a/packages/app/src/State/ReBroadcast.ts b/packages/app/src/State/ReBroadcast.ts index 76d0165d..23cc253b 100644 --- a/packages/app/src/State/ReBroadcast.ts +++ b/packages/app/src/State/ReBroadcast.ts @@ -1,5 +1,5 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { NostrEvent } from "System"; +import { NostrEvent } from "@snort/system"; interface ReBroadcastStore { show: boolean; diff --git a/packages/app/src/System/Const.ts b/packages/app/src/System/Const.ts deleted file mode 100644 index e3502ce0..00000000 --- a/packages/app/src/System/Const.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Websocket re-connect timeout - */ -export const DefaultConnectTimeout = 2000; diff --git a/packages/app/src/System/Util.test.ts b/packages/app/src/System/Util.test.ts deleted file mode 100644 index 14812019..00000000 --- a/packages/app/src/System/Util.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { distance } from "./Util"; - -describe("distance", () => { - it("should have 0 distance", () => { - const a = { - ids: "a", - }; - const b = { - ids: "a", - }; - expect(distance(a, b)).toEqual(0); - }); - it("should have 1 distance", () => { - const a = { - ids: "a", - }; - const b = { - ids: "b", - }; - expect(distance(a, b)).toEqual(1); - }); - it("should have 10 distance", () => { - const a = { - ids: "a", - }; - const b = { - ids: "a", - kinds: 1, - }; - expect(distance(a, b)).toEqual(10); - }); - it("should have 11 distance", () => { - const a = { - ids: "a", - }; - const b = { - ids: "b", - kinds: 1, - }; - expect(distance(a, b)).toEqual(11); - }); - it("should have 1 distance, arrays", () => { - const a = { - since: 1, - until: 100, - kinds: [1], - authors: ["kieran", "snort", "c", "d", "e"], - }; - const b = { - since: 1, - until: 100, - kinds: [6969], - authors: ["kieran", "snort", "c", "d", "e"], - }; - expect(distance(a, b)).toEqual(1); - }); - it("should have 1 distance, array change extra", () => { - const a = { - since: 1, - until: 100, - kinds: [1], - authors: ["f", "kieran", "snort", "c", "d"], - }; - const b = { - since: 1, - until: 100, - kinds: [1], - authors: ["kieran", "snort", "c", "d", "e"], - }; - expect(distance(a, b)).toEqual(1); - }); -}); diff --git a/packages/app/src/Tasks/Nip5Task.tsx b/packages/app/src/Tasks/Nip5Task.tsx index fd711a24..c737ca61 100644 --- a/packages/app/src/Tasks/Nip5Task.tsx +++ b/packages/app/src/Tasks/Nip5Task.tsx @@ -1,6 +1,6 @@ import { FormattedMessage } from "react-intl"; import { Link } from "react-router-dom"; -import { MetadataCache } from "Cache"; +import { MetadataCache } from "@snort/system"; import { BaseUITask } from "Tasks"; export class Nip5Task extends BaseUITask { diff --git a/packages/app/src/Tasks/index.ts b/packages/app/src/Tasks/index.ts index 384c80af..c966d1e6 100644 --- a/packages/app/src/Tasks/index.ts +++ b/packages/app/src/Tasks/index.ts @@ -1,4 +1,4 @@ -import { MetadataCache } from "Cache"; +import { MetadataCache } from "@snort/system"; export interface UITask { id: string; diff --git a/packages/app/src/Upload/VoidCat.ts b/packages/app/src/Upload/VoidCat.ts index 269d57cc..8eb8a11a 100644 --- a/packages/app/src/Upload/VoidCat.ts +++ b/packages/app/src/Upload/VoidCat.ts @@ -1,8 +1,7 @@ -import { EventKind } from "System"; +import { EventKind, EventPublisher } from "@snort/system"; import { VoidApi } from "@void-cat/api"; import { FileExtensionRegex, VoidCatHost } from "Const"; -import { EventPublisher } from "System/EventPublisher"; import { UploadResult } from "Upload"; import { magnetURIDecode } from "SnortUtils"; diff --git a/packages/app/src/Upload/index.ts b/packages/app/src/Upload/index.ts index 8cf57586..ce993af4 100644 --- a/packages/app/src/Upload/index.ts +++ b/packages/app/src/Upload/index.ts @@ -1,5 +1,5 @@ import useLogin from "Hooks/useLogin"; -import { NostrEvent } from "System"; +import { NostrEvent } from "@snort/system"; import NostrBuild from "Upload/NostrBuild"; import VoidCat from "Upload/VoidCat"; diff --git a/packages/app/src/Wallet/NostrWalletConnect.ts b/packages/app/src/Wallet/NostrWalletConnect.ts index 3b4abe53..8a94a523 100644 --- a/packages/app/src/Wallet/NostrWalletConnect.ts +++ b/packages/app/src/Wallet/NostrWalletConnect.ts @@ -1,6 +1,4 @@ -import { Connection, EventKind, NostrEvent } from "System"; -import { EventBuilder } from "System"; -import { EventExt } from "System/EventExt"; +import { Connection, EventKind, NostrEvent, EventBuilder, EventExt } from "@snort/system"; import { LNWallet, WalletError, WalletErrorCode, WalletInfo, WalletInvoice, WalletInvoiceState } from "Wallet"; import debug from "debug"; diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index 8dfb2a8a..e7f30e0a 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -33,10 +33,9 @@ import { SubscribeRoutes } from "Pages/subscribe"; import ZapPoolPage from "Pages/ZapPool"; import DebugPage from "Pages/Debug"; import { db } from "Db"; -import { preload } from "Cache"; +import { preload, UserCache } from "Cache"; import { LoginStore } from "Login"; -import { ProfileLoaderService } from "System/ProfileCache"; -import { NostrSystem } from "System"; +import { NostrSystem, ProfileLoaderService } from "@snort/system"; import { UserRelays } from "Cache/UserRelayCache"; /** @@ -49,7 +48,7 @@ export const System = new NostrSystem({ /** * Singleton user profile loader */ -export const ProfileLoader = new ProfileLoaderService(System); +export const ProfileLoader = new ProfileLoaderService(System, UserCache); // @ts-expect-error Setting webpack nonce window.__webpack_nonce__ = "ZmlhdGphZiBzYWlkIHNub3J0LnNvY2lhbCBpcyBwcmV0dHkgZ29vZCwgd2UgbWFkZSBpdCE="; diff --git a/packages/system/.npmignore b/packages/system/.npmignore new file mode 100644 index 00000000..9b5e52ea --- /dev/null +++ b/packages/system/.npmignore @@ -0,0 +1,5 @@ +tests/ +src/ +*.tgz +jest.config.js +worker.ts \ No newline at end of file diff --git a/packages/system/dist/Connection.d.ts b/packages/system/dist/Connection.d.ts new file mode 100644 index 00000000..b8205e0c --- /dev/null +++ b/packages/system/dist/Connection.d.ts @@ -0,0 +1,90 @@ +import { ConnectionStats } from "./ConnectionStats"; +import { NostrEvent, ReqCommand, TaggedRawEvent, u256 } from "./Nostr"; +import { RelayInfo } from "./RelayInfo"; +import ExternalStore from "./ExternalStore"; +export type AuthHandler = (challenge: string, relay: string) => Promise; +/** + * Relay settings + */ +export interface RelaySettings { + read: boolean; + write: boolean; +} +/** + * Snapshot of connection stats + */ +export interface ConnectionStateSnapshot { + connected: boolean; + disconnects: number; + avgLatency: number; + events: { + received: number; + send: number; + }; + settings?: RelaySettings; + info?: RelayInfo; + pendingRequests: Array; + activeRequests: Array; + id: string; + ephemeral: boolean; + address: string; +} +export declare class Connection extends ExternalStore { + #private; + Id: string; + Address: string; + Socket: WebSocket | null; + PendingRaw: Array; + PendingRequests: Array<{ + cmd: ReqCommand; + cb: () => void; + }>; + ActiveRequests: Set; + Settings: RelaySettings; + Info?: RelayInfo; + ConnectTimeout: number; + Stats: ConnectionStats; + HasStateChange: boolean; + IsClosed: boolean; + ReconnectTimer: ReturnType | null; + EventsCallback: Map void>; + OnConnected?: () => void; + OnEvent?: (sub: string, e: TaggedRawEvent) => void; + OnEose?: (sub: string) => void; + OnDisconnect?: (id: string) => void; + Auth?: AuthHandler; + AwaitingAuth: Map; + Authed: boolean; + Ephemeral: boolean; + EphemeralTimeout: ReturnType | undefined; + Down: boolean; + constructor(addr: string, options: RelaySettings, auth?: AuthHandler, ephemeral?: boolean); + ResetEphemeralTimeout(): void; + Connect(): Promise; + Close(): void; + OnOpen(): void; + OnClose(e: CloseEvent): void; + OnMessage(e: MessageEvent): void; + OnError(e: Event): void; + /** + * Send event on this connection + */ + SendEvent(e: NostrEvent): void; + /** + * Send event on this connection and wait for OK response + */ + SendAsync(e: NostrEvent, timeout?: number): Promise; + /** + * Using relay document to determine if this relay supports a feature + */ + SupportsNip(n: number): boolean; + /** + * Queue or send command to the relay + * @param cmd The REQ to send to the server + */ + QueueReq(cmd: ReqCommand, cbSent: () => void): void; + CloseReq(id: string): void; + takeSnapshot(): ConnectionStateSnapshot; + _OnAuthAsync(challenge: string): Promise; +} +//# sourceMappingURL=Connection.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Connection.d.ts.map b/packages/system/dist/Connection.d.ts.map new file mode 100644 index 00000000..61f78755 --- /dev/null +++ b/packages/system/dist/Connection.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Connection.d.ts","sourceRoot":"","sources":["../src/Connection.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/Connection.js b/packages/system/dist/Connection.js new file mode 100644 index 00000000..a33148da --- /dev/null +++ b/packages/system/dist/Connection.js @@ -0,0 +1,343 @@ +"use strict"; +var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +var _Connection_instances, _Connection_SendQueuedRequests, _Connection_ResetQueues, _Connection_SendJson, _Connection_sendPendingRaw, _Connection_sendOnWire, _Connection_maxSubscriptions_get; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Connection = void 0; +const uuid_1 = require("uuid"); +const Const_1 = require("./Const"); +const ConnectionStats_1 = require("./ConnectionStats"); +const Util_1 = require("./Util"); +const ExternalStore_1 = __importDefault(require("./ExternalStore")); +class Connection extends ExternalStore_1.default { + constructor(addr, options, auth, ephemeral = false) { + super(); + _Connection_instances.add(this); + this.Socket = null; + this.PendingRaw = []; + this.PendingRequests = []; + this.ActiveRequests = new Set(); + this.ConnectTimeout = Const_1.DefaultConnectTimeout; + this.Stats = new ConnectionStats_1.ConnectionStats(); + this.HasStateChange = true; + this.Authed = false; + this.Down = true; + this.Id = (0, uuid_1.v4)(); + this.Address = addr; + this.Settings = options; + this.IsClosed = false; + this.ReconnectTimer = null; + this.EventsCallback = new Map(); + this.AwaitingAuth = new Map(); + this.Auth = auth; + this.Ephemeral = ephemeral; + } + ResetEphemeralTimeout() { + if (this.EphemeralTimeout) { + clearTimeout(this.EphemeralTimeout); + } + if (this.Ephemeral) { + this.EphemeralTimeout = setTimeout(() => { + this.Close(); + }, 30000); + } + } + async Connect() { + try { + if (this.Info === undefined) { + const u = new URL(this.Address); + const rsp = await fetch(`${u.protocol === "wss:" ? "https:" : "http:"}//${u.host}`, { + headers: { + accept: "application/nostr+json", + }, + }); + if (rsp.ok) { + const data = await rsp.json(); + for (const [k, v] of Object.entries(data)) { + if (v === "unset" || v === "" || v === "~") { + data[k] = undefined; + } + } + this.Info = data; + } + } + } + catch (e) { + console.warn("Could not load relay information", e); + } + if (this.Socket) { + this.Id = (0, uuid_1.v4)(); + this.Socket.onopen = null; + this.Socket.onmessage = null; + this.Socket.onerror = null; + this.Socket.onclose = null; + } + this.IsClosed = false; + this.Socket = new WebSocket(this.Address); + this.Socket.onopen = () => this.OnOpen(); + this.Socket.onmessage = e => this.OnMessage(e); + this.Socket.onerror = e => this.OnError(e); + this.Socket.onclose = e => this.OnClose(e); + } + Close() { + this.IsClosed = true; + if (this.ReconnectTimer !== null) { + clearTimeout(this.ReconnectTimer); + this.ReconnectTimer = null; + } + this.Socket?.close(); + this.notifyChange(); + } + OnOpen() { + this.ConnectTimeout = Const_1.DefaultConnectTimeout; + console.log(`[${this.Address}] Open!`); + this.Down = false; + if (this.Ephemeral) { + this.ResetEphemeralTimeout(); + } + this.OnConnected?.(); + __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_sendPendingRaw).call(this); + } + OnClose(e) { + if (!this.IsClosed) { + this.ConnectTimeout = this.ConnectTimeout * 2; + console.log(`[${this.Address}] Closed (${e.reason}), trying again in ${(this.ConnectTimeout / 1000) + .toFixed(0) + .toLocaleString()} sec`); + this.ReconnectTimer = setTimeout(() => { + this.Connect(); + }, this.ConnectTimeout); + this.Stats.Disconnects++; + } + else { + console.log(`[${this.Address}] Closed!`); + this.ReconnectTimer = null; + } + this.OnDisconnect?.(this.Id); + __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_ResetQueues).call(this); + // reset connection Id on disconnect, for query-tracking + this.Id = (0, uuid_1.v4)(); + this.notifyChange(); + } + OnMessage(e) { + if (e.data.length > 0) { + const msg = JSON.parse(e.data); + const tag = msg[0]; + switch (tag) { + case "AUTH": { + this._OnAuthAsync(msg[1]) + .then(() => __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_sendPendingRaw).call(this)) + .catch(console.error); + this.Stats.EventsReceived++; + this.notifyChange(); + break; + } + case "EVENT": { + this.OnEvent?.(msg[1], { + ...msg[2], + relays: [this.Address], + }); + this.Stats.EventsReceived++; + this.notifyChange(); + break; + } + case "EOSE": { + this.OnEose?.(msg[1]); + break; + } + case "OK": { + // feedback to broadcast call + console.debug(`${this.Address} OK: `, msg); + const id = msg[1]; + if (this.EventsCallback.has(id)) { + const cb = (0, Util_1.unwrap)(this.EventsCallback.get(id)); + this.EventsCallback.delete(id); + cb(msg); + } + break; + } + case "NOTICE": { + console.warn(`[${this.Address}] NOTICE: ${msg[1]}`); + break; + } + default: { + console.warn(`Unknown tag: ${tag}`); + break; + } + } + } + } + OnError(e) { + console.error(e); + this.notifyChange(); + } + /** + * Send event on this connection + */ + SendEvent(e) { + if (!this.Settings.write) { + return; + } + const req = ["EVENT", e]; + __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_SendJson).call(this, req); + this.Stats.EventsSent++; + this.notifyChange(); + } + /** + * Send event on this connection and wait for OK response + */ + async SendAsync(e, timeout = 5000) { + return new Promise(resolve => { + if (!this.Settings.write) { + resolve(); + return; + } + const t = setTimeout(() => { + resolve(); + }, timeout); + this.EventsCallback.set(e.id, () => { + clearTimeout(t); + resolve(); + }); + const req = ["EVENT", e]; + __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_SendJson).call(this, req); + this.Stats.EventsSent++; + this.notifyChange(); + }); + } + /** + * Using relay document to determine if this relay supports a feature + */ + SupportsNip(n) { + return this.Info?.supported_nips?.some(a => a === n) ?? false; + } + /** + * Queue or send command to the relay + * @param cmd The REQ to send to the server + */ + QueueReq(cmd, cbSent) { + if (this.ActiveRequests.size >= __classPrivateFieldGet(this, _Connection_instances, "a", _Connection_maxSubscriptions_get)) { + this.PendingRequests.push({ + cmd, + cb: cbSent, + }); + console.debug("Queuing:", this.Address, cmd); + } + else { + this.ActiveRequests.add(cmd[1]); + __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_SendJson).call(this, cmd); + cbSent(); + } + this.notifyChange(); + } + CloseReq(id) { + if (this.ActiveRequests.delete(id)) { + __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_SendJson).call(this, ["CLOSE", id]); + this.OnEose?.(id); + __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_SendQueuedRequests).call(this); + } + this.notifyChange(); + } + takeSnapshot() { + return { + connected: this.Socket?.readyState === WebSocket.OPEN, + events: { + received: this.Stats.EventsReceived, + send: this.Stats.EventsSent, + }, + avgLatency: this.Stats.Latency.length > 0 + ? this.Stats.Latency.reduce((acc, v) => acc + v, 0) / this.Stats.Latency.length + : 0, + disconnects: this.Stats.Disconnects, + info: this.Info, + id: this.Id, + pendingRequests: [...this.PendingRequests.map(a => a.cmd[1])], + activeRequests: [...this.ActiveRequests], + ephemeral: this.Ephemeral, + address: this.Address, + }; + } + async _OnAuthAsync(challenge) { + const authCleanup = () => { + this.AwaitingAuth.delete(challenge); + }; + if (!this.Auth) { + throw new Error("Auth hook not registered"); + } + this.AwaitingAuth.set(challenge, true); + const authEvent = await this.Auth(challenge, this.Address); + return new Promise(resolve => { + if (!authEvent) { + authCleanup(); + return Promise.reject("no event"); + } + const t = setTimeout(() => { + authCleanup(); + resolve(); + }, 10000); + this.EventsCallback.set(authEvent.id, (msg) => { + clearTimeout(t); + authCleanup(); + if (msg.length > 3 && msg[2] === true) { + this.Authed = true; + } + resolve(); + }); + __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_sendOnWire).call(this, ["AUTH", authEvent]); + }); + } +} +exports.Connection = Connection; +_Connection_instances = new WeakSet(), _Connection_SendQueuedRequests = function _Connection_SendQueuedRequests() { + const canSend = __classPrivateFieldGet(this, _Connection_instances, "a", _Connection_maxSubscriptions_get) - this.ActiveRequests.size; + if (canSend > 0) { + for (let x = 0; x < canSend; x++) { + const p = this.PendingRequests.shift(); + if (p) { + this.ActiveRequests.add(p.cmd[1]); + __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_SendJson).call(this, p.cmd); + p.cb(); + console.debug("Sent pending REQ", this.Address, p.cmd); + } + } + } +}, _Connection_ResetQueues = function _Connection_ResetQueues() { + this.ActiveRequests.clear(); + this.PendingRequests = []; + this.PendingRaw = []; + this.notifyChange(); +}, _Connection_SendJson = function _Connection_SendJson(obj) { + const authPending = !this.Authed && (this.AwaitingAuth.size > 0 || this.Info?.limitation?.auth_required === true); + if (this.Socket?.readyState !== WebSocket.OPEN || authPending) { + this.PendingRaw.push(obj); + if (this.Socket?.readyState === WebSocket.CLOSED && this.Ephemeral && this.IsClosed) { + this.Connect(); + } + return false; + } + __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_sendPendingRaw).call(this); + __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_sendOnWire).call(this, obj); +}, _Connection_sendPendingRaw = function _Connection_sendPendingRaw() { + while (this.PendingRaw.length > 0) { + const next = this.PendingRaw.shift(); + if (next) { + __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_sendOnWire).call(this, next); + } + } +}, _Connection_sendOnWire = function _Connection_sendOnWire(obj) { + if (this.Socket?.readyState !== WebSocket.OPEN) { + throw new Error(`Socket is not open, state is ${this.Socket?.readyState}`); + } + const json = JSON.stringify(obj); + this.Socket.send(json); + return true; +}, _Connection_maxSubscriptions_get = function _Connection_maxSubscriptions_get() { + return this.Info?.limitation?.max_subscriptions ?? 25; +}; +//# sourceMappingURL=Connection.js.map \ No newline at end of file diff --git a/packages/system/dist/Connection.js.map b/packages/system/dist/Connection.js.map new file mode 100644 index 00000000..67233092 --- /dev/null +++ b/packages/system/dist/Connection.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Connection.js","sourceRoot":"","sources":["../src/Connection.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/ConnectionStats.d.ts b/packages/system/dist/ConnectionStats.d.ts new file mode 100644 index 00000000..43be570b --- /dev/null +++ b/packages/system/dist/ConnectionStats.d.ts @@ -0,0 +1,30 @@ +/** + * Stats class for tracking metrics per connection + */ +export declare class ConnectionStats { + /** + * Last n records of how long between REQ->EOSE + */ + Latency: number[]; + /** + * Total number of REQ's sent on this connection + */ + Subs: number; + /** + * Count of REQ which took too long and where abandoned + */ + SubsTimeout: number; + /** + * Total number of EVENT messages received + */ + EventsReceived: number; + /** + * Total number of EVENT messages sent + */ + EventsSent: number; + /** + * Total number of times this connection was lost + */ + Disconnects: number; +} +//# sourceMappingURL=ConnectionStats.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/ConnectionStats.d.ts.map b/packages/system/dist/ConnectionStats.d.ts.map new file mode 100644 index 00000000..359e6887 --- /dev/null +++ b/packages/system/dist/ConnectionStats.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ConnectionStats.d.ts","sourceRoot":"","sources":["../src/ConnectionStats.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,eAAe;IAC1B;;OAEG;IACH,OAAO,EAAE,MAAM,EAAE,CAAM;IAEvB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAK;IAEjB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAK;IAExB;;OAEG;IACH,cAAc,EAAE,MAAM,CAAK;IAE3B;;OAEG;IACH,UAAU,EAAE,MAAM,CAAK;IAEvB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAK;CACzB"} \ No newline at end of file diff --git a/packages/system/dist/ConnectionStats.js b/packages/system/dist/ConnectionStats.js new file mode 100644 index 00000000..334cba6d --- /dev/null +++ b/packages/system/dist/ConnectionStats.js @@ -0,0 +1,36 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ConnectionStats = void 0; +/** + * Stats class for tracking metrics per connection + */ +class ConnectionStats { + constructor() { + /** + * Last n records of how long between REQ->EOSE + */ + this.Latency = []; + /** + * Total number of REQ's sent on this connection + */ + this.Subs = 0; + /** + * Count of REQ which took too long and where abandoned + */ + this.SubsTimeout = 0; + /** + * Total number of EVENT messages received + */ + this.EventsReceived = 0; + /** + * Total number of EVENT messages sent + */ + this.EventsSent = 0; + /** + * Total number of times this connection was lost + */ + this.Disconnects = 0; + } +} +exports.ConnectionStats = ConnectionStats; +//# sourceMappingURL=ConnectionStats.js.map \ No newline at end of file diff --git a/packages/system/dist/ConnectionStats.js.map b/packages/system/dist/ConnectionStats.js.map new file mode 100644 index 00000000..90da8778 --- /dev/null +++ b/packages/system/dist/ConnectionStats.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ConnectionStats.js","sourceRoot":"","sources":["../src/ConnectionStats.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,MAAa,eAAe;IAA5B;QACE;;WAEG;QACH,YAAO,GAAa,EAAE,CAAC;QAEvB;;WAEG;QACH,SAAI,GAAW,CAAC,CAAC;QAEjB;;WAEG;QACH,gBAAW,GAAW,CAAC,CAAC;QAExB;;WAEG;QACH,mBAAc,GAAW,CAAC,CAAC;QAE3B;;WAEG;QACH,eAAU,GAAW,CAAC,CAAC;QAEvB;;WAEG;QACH,gBAAW,GAAW,CAAC,CAAC;IAC1B,CAAC;CAAA;AA9BD,0CA8BC"} \ No newline at end of file diff --git a/packages/system/dist/Const.d.ts b/packages/system/dist/Const.d.ts new file mode 100644 index 00000000..2bf82921 --- /dev/null +++ b/packages/system/dist/Const.d.ts @@ -0,0 +1,13 @@ +/** + * Websocket re-connect timeout + */ +export declare const DefaultConnectTimeout = 2000; +/** + * Hashtag regex + */ +export declare const HashtagRegex: RegExp; +/** + * How long profile cache should be considered valid for + */ +export declare const ProfileCacheExpire: number; +//# sourceMappingURL=Const.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Const.d.ts.map b/packages/system/dist/Const.d.ts.map new file mode 100644 index 00000000..222ed02a --- /dev/null +++ b/packages/system/dist/Const.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Const.d.ts","sourceRoot":"","sources":["../src/Const.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C;;GAEG;AAEH,eAAO,MAAM,YAAY,QAA4C,CAAC;AAGtE;;GAEG;AACF,eAAO,MAAM,kBAAkB,QAAsB,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/Const.js b/packages/system/dist/Const.js new file mode 100644 index 00000000..79573a84 --- /dev/null +++ b/packages/system/dist/Const.js @@ -0,0 +1,17 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ProfileCacheExpire = exports.HashtagRegex = exports.DefaultConnectTimeout = void 0; +/** + * Websocket re-connect timeout + */ +exports.DefaultConnectTimeout = 2000; +/** + * Hashtag regex + */ +// eslint-disable-next-line no-useless-escape +exports.HashtagRegex = /(#[^\s!@#$%^&*()=+.\/,\[{\]};:'"?><]+)/g; +/** + * How long profile cache should be considered valid for + */ +exports.ProfileCacheExpire = 1000 * 60 * 60 * 6; +//# sourceMappingURL=Const.js.map \ No newline at end of file diff --git a/packages/system/dist/Const.js.map b/packages/system/dist/Const.js.map new file mode 100644 index 00000000..113dff7b --- /dev/null +++ b/packages/system/dist/Const.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Const.js","sourceRoot":"","sources":["../src/Const.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACU,QAAA,qBAAqB,GAAG,IAAI,CAAC;AAE1C;;GAEG;AACH,6CAA6C;AAChC,QAAA,YAAY,GAAG,yCAAyC,CAAC;AAGtE;;GAEG;AACW,QAAA,kBAAkB,GAAG,IAAK,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/EventBuilder.d.ts b/packages/system/dist/EventBuilder.d.ts new file mode 100644 index 00000000..66b979b2 --- /dev/null +++ b/packages/system/dist/EventBuilder.d.ts @@ -0,0 +1,20 @@ +import { EventKind, HexKey, NostrEvent } from "."; +export declare class EventBuilder { + #private; + kind(k: EventKind): this; + content(c: string): this; + createdAt(n: number): this; + pubKey(k: string): this; + tag(t: Array): EventBuilder; + /** + * Extract mentions + */ + processContent(): this; + build(): NostrEvent; + /** + * Build and sign event + * @param pk Private key to sign event with + */ + buildAndSign(pk: HexKey): Promise; +} +//# sourceMappingURL=EventBuilder.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/EventBuilder.d.ts.map b/packages/system/dist/EventBuilder.d.ts.map new file mode 100644 index 00000000..05679aaa --- /dev/null +++ b/packages/system/dist/EventBuilder.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"EventBuilder.d.ts","sourceRoot":"","sources":["../src/EventBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAe,UAAU,EAAE,MAAM,GAAG,CAAC;AAM/D,qBAAa,YAAY;;IAOvB,IAAI,CAAC,CAAC,EAAE,SAAS;IAKjB,OAAO,CAAC,CAAC,EAAE,MAAM;IAKjB,SAAS,CAAC,CAAC,EAAE,MAAM;IAKnB,MAAM,CAAC,CAAC,EAAE,MAAM;IAKhB,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,YAAY;IAOnC;;OAEG;IACH,cAAc;IAcd,KAAK;IAcL;;;OAGG;IACG,YAAY,CAAC,EAAE,EAAE,MAAM;CAgC9B"} \ No newline at end of file diff --git a/packages/system/dist/EventBuilder.js b/packages/system/dist/EventBuilder.js new file mode 100644 index 00000000..cc346664 --- /dev/null +++ b/packages/system/dist/EventBuilder.js @@ -0,0 +1,113 @@ +"use strict"; +var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; +}; +var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +}; +var _EventBuilder_instances, _EventBuilder_kind, _EventBuilder_content, _EventBuilder_createdAt, _EventBuilder_pubkey, _EventBuilder_tags, _EventBuilder_validate, _EventBuilder_replaceMention, _EventBuilder_addHashtag; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EventBuilder = void 0; +const _1 = require("."); +const Const_1 = require("./Const"); +const Util_1 = require("./Util"); +const EventExt_1 = require("./EventExt"); +const NostrLink_1 = require("./NostrLink"); +class EventBuilder { + constructor() { + _EventBuilder_instances.add(this); + _EventBuilder_kind.set(this, void 0); + _EventBuilder_content.set(this, void 0); + _EventBuilder_createdAt.set(this, void 0); + _EventBuilder_pubkey.set(this, void 0); + _EventBuilder_tags.set(this, []); + } + kind(k) { + __classPrivateFieldSet(this, _EventBuilder_kind, k, "f"); + return this; + } + content(c) { + __classPrivateFieldSet(this, _EventBuilder_content, c, "f"); + return this; + } + createdAt(n) { + __classPrivateFieldSet(this, _EventBuilder_createdAt, n, "f"); + return this; + } + pubKey(k) { + __classPrivateFieldSet(this, _EventBuilder_pubkey, k, "f"); + return this; + } + tag(t) { + const duplicate = __classPrivateFieldGet(this, _EventBuilder_tags, "f").some(a => a.length === t.length && a.every((b, i) => b !== a[i])); + if (duplicate) + return this; + __classPrivateFieldGet(this, _EventBuilder_tags, "f").push(t); + return this; + } + /** + * Extract mentions + */ + processContent() { + if (__classPrivateFieldGet(this, _EventBuilder_content, "f")) { + __classPrivateFieldSet(this, _EventBuilder_content, __classPrivateFieldGet(this, _EventBuilder_content, "f").replace(/@n(pub|profile|event|ote|addr|)1[acdefghjklmnpqrstuvwxyz023456789]+/g, m => __classPrivateFieldGet(this, _EventBuilder_instances, "m", _EventBuilder_replaceMention).call(this, m)), "f"); + const hashTags = [...__classPrivateFieldGet(this, _EventBuilder_content, "f").matchAll(Const_1.HashtagRegex)]; + hashTags.map(hashTag => { + __classPrivateFieldGet(this, _EventBuilder_instances, "m", _EventBuilder_addHashtag).call(this, hashTag[0]); + }); + } + return this; + } + build() { + __classPrivateFieldGet(this, _EventBuilder_instances, "m", _EventBuilder_validate).call(this); + const ev = { + id: "", + pubkey: __classPrivateFieldGet(this, _EventBuilder_pubkey, "f") ?? "", + content: __classPrivateFieldGet(this, _EventBuilder_content, "f") ?? "", + kind: __classPrivateFieldGet(this, _EventBuilder_kind, "f"), + created_at: __classPrivateFieldGet(this, _EventBuilder_createdAt, "f") ?? (0, Util_1.unixNow)(), + tags: __classPrivateFieldGet(this, _EventBuilder_tags, "f"), + }; + ev.id = EventExt_1.EventExt.createId(ev); + return ev; + } + /** + * Build and sign event + * @param pk Private key to sign event with + */ + async buildAndSign(pk) { + const ev = this.pubKey((0, Util_1.getPublicKey)(pk)).build(); + await EventExt_1.EventExt.sign(ev, pk); + return ev; + } +} +exports.EventBuilder = EventBuilder; +_EventBuilder_kind = new WeakMap(), _EventBuilder_content = new WeakMap(), _EventBuilder_createdAt = new WeakMap(), _EventBuilder_pubkey = new WeakMap(), _EventBuilder_tags = new WeakMap(), _EventBuilder_instances = new WeakSet(), _EventBuilder_validate = function _EventBuilder_validate() { + if (__classPrivateFieldGet(this, _EventBuilder_kind, "f") === undefined) { + throw new Error("Kind must be set"); + } + if (__classPrivateFieldGet(this, _EventBuilder_pubkey, "f") === undefined) { + throw new Error("Pubkey must be set"); + } +}, _EventBuilder_replaceMention = function _EventBuilder_replaceMention(match) { + const npub = match.slice(1); + const link = (0, NostrLink_1.parseNostrLink)(npub); + if (link) { + if (link.type === _1.NostrPrefix.Profile || link.type === _1.NostrPrefix.PublicKey) { + this.tag(["p", link.id]); + } + return `nostr:${link.encode()}`; + } + else { + return match; + } +}, _EventBuilder_addHashtag = function _EventBuilder_addHashtag(match) { + const tag = match.slice(1); + this.tag(["t", tag.toLowerCase()]); +}; +//# sourceMappingURL=EventBuilder.js.map \ No newline at end of file diff --git a/packages/system/dist/EventBuilder.js.map b/packages/system/dist/EventBuilder.js.map new file mode 100644 index 00000000..81650cdd --- /dev/null +++ b/packages/system/dist/EventBuilder.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EventBuilder.js","sourceRoot":"","sources":["../src/EventBuilder.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/EventExt.d.ts b/packages/system/dist/EventExt.d.ts new file mode 100644 index 00000000..055c952c --- /dev/null +++ b/packages/system/dist/EventExt.d.ts @@ -0,0 +1,42 @@ +import { EventKind, HexKey, NostrEvent, Tag } from "."; +export interface Thread { + root?: Tag; + replyTo?: Tag; + mentions: Array; + pubKeys: Array; +} +export declare abstract class EventExt { + #private; + /** + * Get the pub key of the creator of this event NIP-26 + */ + static getRootPubKey(e: NostrEvent): HexKey; + /** + * Sign this message with a private key + */ + static sign(e: NostrEvent, key: HexKey): void; + /** + * Check the signature of this message + * @returns True if valid signature + */ + static verify(e: NostrEvent): boolean; + static createId(e: NostrEvent): string; + /** + * Create a new event for a specific pubkey + */ + static forPubKey(pk: HexKey, kind: EventKind): NostrEvent; + static extractThread(ev: NostrEvent): Thread | undefined; + /** + * Encrypt the given message content + */ + static encryptData(content: string, pubkey: HexKey, privkey: HexKey): Promise; + /** + * Decrypt the content of the message + */ + static decryptData(cyphertext: string, privkey: HexKey, pubkey: HexKey): Promise; + /** + * Decrypt the content of this message in place + */ + static decryptDm(content: string, privkey: HexKey, pubkey: HexKey): Promise; +} +//# sourceMappingURL=EventExt.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/EventExt.d.ts.map b/packages/system/dist/EventExt.d.ts.map new file mode 100644 index 00000000..bb09e5ef --- /dev/null +++ b/packages/system/dist/EventExt.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"EventExt.d.ts","sourceRoot":"","sources":["../src/EventExt.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC;AAIvD,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IACrB,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACxB;AAED,8BAAsB,QAAQ;;IAC5B;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM;IAS3C;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM;IAUtC;;;OAGG;IACH,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU;IAM3B,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,UAAU;IAW7B;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS;IAY5C,MAAM,CAAC,aAAa,CAAC,EAAE,EAAE,UAAU;IAqCnC;;OAEG;WACU,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAgBzE;;OAEG;WACU,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAoB5E;;OAEG;WACU,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CASxE"} \ No newline at end of file diff --git a/packages/system/dist/EventExt.js b/packages/system/dist/EventExt.js new file mode 100644 index 00000000..eb019d83 --- /dev/null +++ b/packages/system/dist/EventExt.js @@ -0,0 +1,175 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +var _a, _EventExt_getDmSharedKey; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EventExt = void 0; +const secp = __importStar(require("@noble/curves/secp256k1")); +const utils = __importStar(require("@noble/curves/abstract/utils")); +const _1 = require("."); +const base64_1 = __importDefault(require("@protobufjs/base64")); +const Util_1 = require("./Util"); +class EventExt { + /** + * Get the pub key of the creator of this event NIP-26 + */ + static getRootPubKey(e) { + const delegation = e.tags.find(a => a[0] === "delegation"); + if (delegation?.[1]) { + // todo: verify sig + return delegation[1]; + } + return e.pubkey; + } + /** + * Sign this message with a private key + */ + static sign(e, key) { + e.id = this.createId(e); + const sig = secp.schnorr.sign(e.id, key); + e.sig = utils.bytesToHex(sig); + if (!(secp.schnorr.verify(e.sig, e.id, e.pubkey))) { + throw new Error("Signing failed"); + } + } + /** + * Check the signature of this message + * @returns True if valid signature + */ + static verify(e) { + const id = this.createId(e); + const result = secp.schnorr.verify(e.sig, id, e.pubkey); + return result; + } + static createId(e) { + const payload = [0, e.pubkey, e.created_at, e.kind, e.tags, e.content]; + const hash = (0, Util_1.sha256)(JSON.stringify(payload)); + if (e.id !== "" && hash !== e.id) { + console.debug(payload); + throw new Error("ID doesnt match!"); + } + return hash; + } + /** + * Create a new event for a specific pubkey + */ + static forPubKey(pk, kind) { + return { + pubkey: pk, + kind: kind, + created_at: (0, Util_1.unixNow)(), + content: "", + tags: [], + id: "", + sig: "", + }; + } + static extractThread(ev) { + const isThread = ev.tags.some(a => (a[0] === "e" && a[3] !== "mention") || a[0] == "a"); + if (!isThread) { + return undefined; + } + const shouldWriteMarkers = ev.kind === _1.EventKind.TextNote; + const ret = { + mentions: [], + pubKeys: [], + }; + const eTags = ev.tags.filter(a => a[0] === "e" || a[0] === "a").map((v, i) => new _1.Tag(v, i)); + const marked = eTags.some(a => a.Marker !== undefined); + if (!marked) { + ret.root = eTags[0]; + ret.root.Marker = shouldWriteMarkers ? "root" : undefined; + if (eTags.length > 1) { + ret.replyTo = eTags[1]; + ret.replyTo.Marker = shouldWriteMarkers ? "reply" : undefined; + } + if (eTags.length > 2) { + ret.mentions = eTags.slice(2); + if (shouldWriteMarkers) { + ret.mentions.forEach(a => (a.Marker = "mention")); + } + } + } + else { + const root = eTags.find(a => a.Marker === "root"); + const reply = eTags.find(a => a.Marker === "reply"); + ret.root = root; + ret.replyTo = reply; + ret.mentions = eTags.filter(a => a.Marker === "mention"); + } + ret.pubKeys = Array.from(new Set(ev.tags.filter(a => a[0] === "p").map(a => a[1]))); + return ret; + } + /** + * Encrypt the given message content + */ + static async encryptData(content, pubkey, privkey) { + const key = await __classPrivateFieldGet(this, _a, "m", _EventExt_getDmSharedKey).call(this, pubkey, privkey); + const iv = window.crypto.getRandomValues(new Uint8Array(16)); + const data = new TextEncoder().encode(content); + const result = await window.crypto.subtle.encrypt({ + name: "AES-CBC", + iv: iv, + }, key, data); + const uData = new Uint8Array(result); + return `${base64_1.default.encode(uData, 0, result.byteLength)}?iv=${base64_1.default.encode(iv, 0, 16)}`; + } + /** + * Decrypt the content of the message + */ + static async decryptData(cyphertext, privkey, pubkey) { + const key = await __classPrivateFieldGet(this, _a, "m", _EventExt_getDmSharedKey).call(this, pubkey, privkey); + const cSplit = cyphertext.split("?iv="); + const data = new Uint8Array(base64_1.default.length(cSplit[0])); + base64_1.default.decode(cSplit[0], data, 0); + const iv = new Uint8Array(base64_1.default.length(cSplit[1])); + base64_1.default.decode(cSplit[1], iv, 0); + const result = await window.crypto.subtle.decrypt({ + name: "AES-CBC", + iv: iv, + }, key, data); + return new TextDecoder().decode(result); + } + /** + * Decrypt the content of this message in place + */ + static async decryptDm(content, privkey, pubkey) { + return await this.decryptData(content, privkey, pubkey); + } +} +exports.EventExt = EventExt; +_a = EventExt, _EventExt_getDmSharedKey = async function _EventExt_getDmSharedKey(pubkey, privkey) { + const sharedPoint = secp.secp256k1.getSharedSecret(privkey, "02" + pubkey); + const sharedX = sharedPoint.slice(1, 33); + return await window.crypto.subtle.importKey("raw", sharedX, { name: "AES-CBC" }, false, ["encrypt", "decrypt"]); +}; +//# sourceMappingURL=EventExt.js.map \ No newline at end of file diff --git a/packages/system/dist/EventExt.js.map b/packages/system/dist/EventExt.js.map new file mode 100644 index 00000000..1af29fd9 --- /dev/null +++ b/packages/system/dist/EventExt.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EventExt.js","sourceRoot":"","sources":["../src/EventExt.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8DAAgD;AAChD,oEAAsD;AACtD,wBAAuD;AACvD,gEAAwC;AACxC,iCAAyC;AASzC,MAAsB,QAAQ;IAC5B;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,CAAa;QAChC,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC;QAC3D,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE;YACnB,mBAAmB;YACnB,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;SACtB;QACD,OAAO,CAAC,CAAC,MAAM,CAAC;IAClB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,CAAa,EAAE,GAAW;QACpC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAExB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QACzC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE;YACjD,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;SACnC;IACH,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,MAAM,CAAC,CAAa;QACzB,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;QACxD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,CAAC,QAAQ,CAAC,CAAa;QAC3B,MAAM,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;QAEvE,MAAM,IAAI,GAAG,IAAA,aAAM,EAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,IAAI,KAAK,CAAC,CAAC,EAAE,EAAE;YAChC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;SACrC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,EAAU,EAAE,IAAe;QAC1C,OAAO;YACL,MAAM,EAAE,EAAE;YACV,IAAI,EAAE,IAAI;YACV,UAAU,EAAE,IAAA,cAAO,GAAE;YACrB,OAAO,EAAE,EAAE;YACX,IAAI,EAAE,EAAE;YACR,EAAE,EAAE,EAAE;YACN,GAAG,EAAE,EAAE;SACM,CAAC;IAClB,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,EAAc;QACjC,MAAM,QAAQ,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;QACxF,IAAI,CAAC,QAAQ,EAAE;YACb,OAAO,SAAS,CAAC;SAClB;QAED,MAAM,kBAAkB,GAAG,EAAE,CAAC,IAAI,KAAK,YAAS,CAAC,QAAQ,CAAC;QAC1D,MAAM,GAAG,GAAG;YACV,QAAQ,EAAE,EAAE;YACZ,OAAO,EAAE,EAAE;SACF,CAAC;QACZ,MAAM,KAAK,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,MAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7F,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM,EAAE;YACX,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACpB,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,kBAAkB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;YAC1D,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;gBACpB,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBACvB,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,kBAAkB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;aAC/D;YACD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;gBACpB,GAAG,CAAC,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBAC9B,IAAI,kBAAkB,EAAE;oBACtB,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;iBACnD;aACF;SACF;aAAM;YACL,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;YAClD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC;YACpD,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;YAChB,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC;YACpB,GAAG,CAAC,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;SAC1D;QACD,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACpF,OAAO,GAAG,CAAC;IACb,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,OAAe,EAAE,MAAc,EAAE,OAAe;QACvE,MAAM,GAAG,GAAG,MAAM,uBAAA,IAAI,oCAAgB,MAApB,IAAI,EAAiB,MAAM,EAAE,OAAO,CAAC,CAAC;QACxD,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7D,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAC/C;YACE,IAAI,EAAE,SAAS;YACf,EAAE,EAAE,EAAE;SACP,EACD,GAAG,EACH,IAAI,CACL,CAAC;QACF,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;QACrC,OAAO,GAAG,gBAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,OAAO,gBAAM,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACxF,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,UAAkB,EAAE,OAAe,EAAE,MAAc;QAC1E,MAAM,GAAG,GAAG,MAAM,uBAAA,IAAI,oCAAgB,MAApB,IAAI,EAAiB,MAAM,EAAE,OAAO,CAAC,CAAC;QACxD,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,gBAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACtD,gBAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAElC,MAAM,EAAE,GAAG,IAAI,UAAU,CAAC,gBAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACpD,gBAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QAEhC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAC/C;YACE,IAAI,EAAE,SAAS;YACf,EAAE,EAAE,EAAE;SACP,EACD,GAAG,EACH,IAAI,CACL,CAAC;QACF,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,OAAe,EAAE,OAAe,EAAE,MAAc;QACrE,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1D,CAAC;CAOF;AAzJD,4BAyJC;0CALQ,KAAK,mCAAiB,MAAc,EAAE,OAAe;IAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,OAAO,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;IAC3E,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzC,OAAO,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;AAClH,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/EventKind.d.ts b/packages/system/dist/EventKind.d.ts new file mode 100644 index 00000000..620047bd --- /dev/null +++ b/packages/system/dist/EventKind.d.ts @@ -0,0 +1,29 @@ +declare enum EventKind { + Unknown = -1, + SetMetadata = 0, + TextNote = 1, + RecommendServer = 2, + ContactList = 3, + DirectMessage = 4, + Deletion = 5, + Repost = 6, + Reaction = 7, + BadgeAward = 8, + SnortSubscriptions = 1000, + Polls = 6969, + FileHeader = 1063, + Relays = 10002, + Ephemeral = 20000, + Auth = 22242, + PubkeyLists = 30000, + NoteLists = 30001, + TagLists = 30002, + Badge = 30009, + ProfileBadges = 30008, + ZapstrTrack = 31337, + ZapRequest = 9734, + ZapReceipt = 9735, + HttpAuthentication = 27235 +} +export default EventKind; +//# sourceMappingURL=EventKind.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/EventKind.d.ts.map b/packages/system/dist/EventKind.d.ts.map new file mode 100644 index 00000000..2cf2c3fd --- /dev/null +++ b/packages/system/dist/EventKind.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"EventKind.d.ts","sourceRoot":"","sources":["../src/EventKind.ts"],"names":[],"mappings":"AAAA,aAAK,SAAS;IACZ,OAAO,KAAK;IACZ,WAAW,IAAI;IACf,QAAQ,IAAI;IACZ,eAAe,IAAI;IACnB,WAAW,IAAI;IACf,aAAa,IAAI;IACjB,QAAQ,IAAI;IACZ,MAAM,IAAI;IACV,QAAQ,IAAI;IACZ,UAAU,IAAI;IACd,kBAAkB,OAAO;IACzB,KAAK,OAAO;IACZ,UAAU,OAAO;IACjB,MAAM,QAAQ;IACd,SAAS,QAAS;IAClB,IAAI,QAAQ;IACZ,WAAW,QAAQ;IACnB,SAAS,QAAQ;IACjB,QAAQ,QAAQ;IAChB,KAAK,QAAQ;IACb,aAAa,QAAQ;IACrB,WAAW,QAAQ;IACnB,UAAU,OAAO;IACjB,UAAU,OAAO;IACjB,kBAAkB,QAAQ;CAC3B;AAED,eAAe,SAAS,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/EventKind.js b/packages/system/dist/EventKind.js new file mode 100644 index 00000000..d4a5c88a --- /dev/null +++ b/packages/system/dist/EventKind.js @@ -0,0 +1,32 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var EventKind; +(function (EventKind) { + EventKind[EventKind["Unknown"] = -1] = "Unknown"; + EventKind[EventKind["SetMetadata"] = 0] = "SetMetadata"; + EventKind[EventKind["TextNote"] = 1] = "TextNote"; + EventKind[EventKind["RecommendServer"] = 2] = "RecommendServer"; + EventKind[EventKind["ContactList"] = 3] = "ContactList"; + EventKind[EventKind["DirectMessage"] = 4] = "DirectMessage"; + EventKind[EventKind["Deletion"] = 5] = "Deletion"; + EventKind[EventKind["Repost"] = 6] = "Repost"; + EventKind[EventKind["Reaction"] = 7] = "Reaction"; + EventKind[EventKind["BadgeAward"] = 8] = "BadgeAward"; + EventKind[EventKind["SnortSubscriptions"] = 1000] = "SnortSubscriptions"; + EventKind[EventKind["Polls"] = 6969] = "Polls"; + EventKind[EventKind["FileHeader"] = 1063] = "FileHeader"; + EventKind[EventKind["Relays"] = 10002] = "Relays"; + EventKind[EventKind["Ephemeral"] = 20000] = "Ephemeral"; + EventKind[EventKind["Auth"] = 22242] = "Auth"; + EventKind[EventKind["PubkeyLists"] = 30000] = "PubkeyLists"; + EventKind[EventKind["NoteLists"] = 30001] = "NoteLists"; + EventKind[EventKind["TagLists"] = 30002] = "TagLists"; + EventKind[EventKind["Badge"] = 30009] = "Badge"; + EventKind[EventKind["ProfileBadges"] = 30008] = "ProfileBadges"; + EventKind[EventKind["ZapstrTrack"] = 31337] = "ZapstrTrack"; + EventKind[EventKind["ZapRequest"] = 9734] = "ZapRequest"; + EventKind[EventKind["ZapReceipt"] = 9735] = "ZapReceipt"; + EventKind[EventKind["HttpAuthentication"] = 27235] = "HttpAuthentication"; +})(EventKind || (EventKind = {})); +exports.default = EventKind; +//# sourceMappingURL=EventKind.js.map \ No newline at end of file diff --git a/packages/system/dist/EventKind.js.map b/packages/system/dist/EventKind.js.map new file mode 100644 index 00000000..4cdf3259 --- /dev/null +++ b/packages/system/dist/EventKind.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EventKind.js","sourceRoot":"","sources":["../src/EventKind.ts"],"names":[],"mappings":";;AAAA,IAAK,SA0BJ;AA1BD,WAAK,SAAS;IACZ,gDAAY,CAAA;IACZ,uDAAe,CAAA;IACf,iDAAY,CAAA;IACZ,+DAAmB,CAAA;IACnB,uDAAe,CAAA;IACf,2DAAiB,CAAA;IACjB,iDAAY,CAAA;IACZ,6CAAU,CAAA;IACV,iDAAY,CAAA;IACZ,qDAAc,CAAA;IACd,wEAAyB,CAAA;IACzB,8CAAY,CAAA;IACZ,wDAAiB,CAAA;IACjB,iDAAc,CAAA;IACd,uDAAkB,CAAA;IAClB,6CAAY,CAAA;IACZ,2DAAmB,CAAA;IACnB,uDAAiB,CAAA;IACjB,qDAAgB,CAAA;IAChB,+CAAa,CAAA;IACb,+DAAqB,CAAA;IACrB,2DAAmB,CAAA;IACnB,wDAAiB,CAAA;IACjB,wDAAiB,CAAA;IACjB,yEAA0B,CAAA;AAC5B,CAAC,EA1BI,SAAS,KAAT,SAAS,QA0Bb;AAED,kBAAe,SAAS,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/EventPublisher.d.ts b/packages/system/dist/EventPublisher.d.ts new file mode 100644 index 00000000..d121228e --- /dev/null +++ b/packages/system/dist/EventPublisher.d.ts @@ -0,0 +1,66 @@ +import { FullRelaySettings, HexKey, Lists, NostrEvent, RelaySettings, SystemInterface, TaggedRawEvent, u256, UserMetadata } from "."; +import { EventBuilder } from "./EventBuilder"; +export type EventBuilderHook = (ev: EventBuilder) => EventBuilder; +declare global { + interface Window { + nostr?: { + getPublicKey: () => Promise; + signEvent: (event: T) => Promise; + getRelays?: () => Promise>; + nip04?: { + encrypt?: (pubkey: HexKey, plaintext: string) => Promise; + decrypt?: (pubkey: HexKey, ciphertext: string) => Promise; + }; + }; + } +} +export declare class EventPublisher { + #private; + constructor(system: SystemInterface, pubKey: string, privKey?: string); + nip4Encrypt(content: string, key: HexKey): Promise; + nip4Decrypt(content: string, otherKey: HexKey): Promise; + nip42Auth(challenge: string, relay: string): Promise; + broadcast(ev: NostrEvent): void; + /** + * Write event to all given relays. + */ + broadcastAll(ev: NostrEvent, relays: string[]): void; + muted(keys: HexKey[], priv: HexKey[]): Promise; + noteList(notes: u256[], list: Lists): Promise; + tags(tags: string[]): Promise; + metadata(obj: UserMetadata): Promise; + /** + * Create a basic text note + */ + note(msg: string, fnExtra?: EventBuilderHook): Promise; + /** + * Create a zap request event for a given target event/profile + * @param amount Millisats amout! + * @param author Author pubkey to tag in the zap + * @param note Note Id to tag in the zap + * @param msg Custom message to be included in the zap + */ + zap(amount: number, author: HexKey, relays: Array, note?: HexKey, msg?: string, fnExtra?: EventBuilderHook): Promise; + /** + * Reply to a note + */ + reply(replyTo: TaggedRawEvent, msg: string, fnExtra?: EventBuilderHook): Promise; + react(evRef: NostrEvent, content?: string): Promise; + relayList(relays: Array | Record): Promise; + contactList(follows: Array, relays: Record): Promise; + /** + * Delete an event (NIP-09) + */ + delete(id: u256): Promise; + /** + * Repost a note (NIP-18) + */ + repost(note: NostrEvent): Promise; + decryptDm(note: NostrEvent): Promise; + sendDm(content: string, to: HexKey): Promise; + generic(fnHook: EventBuilderHook): Promise; +} +//# sourceMappingURL=EventPublisher.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/EventPublisher.d.ts.map b/packages/system/dist/EventPublisher.d.ts.map new file mode 100644 index 00000000..10509c14 --- /dev/null +++ b/packages/system/dist/EventPublisher.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"EventPublisher.d.ts","sourceRoot":"","sources":["../src/EventPublisher.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/EventPublisher.js b/packages/system/dist/EventPublisher.js new file mode 100644 index 00000000..e392994a --- /dev/null +++ b/packages/system/dist/EventPublisher.js @@ -0,0 +1,295 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; +}; +var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +}; +var _EventPublisher_instances, _EventPublisher_system, _EventPublisher_pubKey, _EventPublisher_privateKey, _EventPublisher_hasNip07_get, _EventPublisher_eb, _EventPublisher_sign; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EventPublisher = void 0; +const secp = __importStar(require("@noble/curves/secp256k1")); +const utils = __importStar(require("@noble/curves/abstract/utils")); +const _1 = require("."); +const Util_1 = require("./Util"); +const EventBuilder_1 = require("./EventBuilder"); +const EventExt_1 = require("./EventExt"); +const WorkQueue_1 = require("./WorkQueue"); +const Nip7Queue = []; +(0, WorkQueue_1.processWorkQueue)(Nip7Queue); +class EventPublisher { + constructor(system, pubKey, privKey) { + _EventPublisher_instances.add(this); + _EventPublisher_system.set(this, void 0); + _EventPublisher_pubKey.set(this, void 0); + _EventPublisher_privateKey.set(this, void 0); + __classPrivateFieldSet(this, _EventPublisher_system, system, "f"); + if (privKey) { + __classPrivateFieldSet(this, _EventPublisher_privateKey, privKey, "f"); + __classPrivateFieldSet(this, _EventPublisher_pubKey, utils.bytesToHex(secp.schnorr.getPublicKey(privKey)), "f"); + } + else { + __classPrivateFieldSet(this, _EventPublisher_pubKey, pubKey, "f"); + } + } + async nip4Encrypt(content, key) { + if (__classPrivateFieldGet(this, _EventPublisher_instances, "a", _EventPublisher_hasNip07_get) && !__classPrivateFieldGet(this, _EventPublisher_privateKey, "f")) { + const nip7PubKey = await (0, WorkQueue_1.barrierQueue)(Nip7Queue, () => (0, Util_1.unwrap)(window.nostr).getPublicKey()); + if (nip7PubKey !== __classPrivateFieldGet(this, _EventPublisher_pubKey, "f")) { + throw new Error("Can't encrypt content, NIP-07 pubkey does not match"); + } + return await (0, WorkQueue_1.barrierQueue)(Nip7Queue, () => (0, Util_1.unwrap)(window.nostr?.nip04?.encrypt).call(window.nostr?.nip04, key, content)); + } + else if (__classPrivateFieldGet(this, _EventPublisher_privateKey, "f")) { + return await EventExt_1.EventExt.encryptData(content, key, __classPrivateFieldGet(this, _EventPublisher_privateKey, "f")); + } + else { + throw new Error("Can't encrypt content, no private keys available"); + } + } + async nip4Decrypt(content, otherKey) { + if (__classPrivateFieldGet(this, _EventPublisher_instances, "a", _EventPublisher_hasNip07_get) && !__classPrivateFieldGet(this, _EventPublisher_privateKey, "f") && window.nostr?.nip04?.decrypt) { + return await (0, WorkQueue_1.barrierQueue)(Nip7Queue, () => (0, Util_1.unwrap)(window.nostr?.nip04?.decrypt).call(window.nostr?.nip04, otherKey, content)); + } + else if (__classPrivateFieldGet(this, _EventPublisher_privateKey, "f")) { + return await EventExt_1.EventExt.decryptDm(content, __classPrivateFieldGet(this, _EventPublisher_privateKey, "f"), otherKey); + } + else { + throw new Error("Can't decrypt content, no private keys available"); + } + } + async nip42Auth(challenge, relay) { + const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.Auth); + eb.tag(["relay", relay]); + eb.tag(["challenge", challenge]); + return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); + } + broadcast(ev) { + console.debug(ev); + __classPrivateFieldGet(this, _EventPublisher_system, "f").BroadcastEvent(ev); + } + /** + * Write event to all given relays. + */ + broadcastAll(ev, relays) { + for (const k of relays) { + __classPrivateFieldGet(this, _EventPublisher_system, "f").WriteOnceToRelay(k, ev); + } + } + async muted(keys, priv) { + const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.PubkeyLists); + eb.tag(["d", _1.Lists.Muted]); + keys.forEach(p => { + eb.tag(["p", p]); + }); + if (priv.length > 0) { + const ps = priv.map(p => ["p", p]); + const plaintext = JSON.stringify(ps); + eb.content(await this.nip4Encrypt(plaintext, __classPrivateFieldGet(this, _EventPublisher_pubKey, "f"))); + } + return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); + } + async noteList(notes, list) { + const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.NoteLists); + eb.tag(["d", list]); + notes.forEach(n => { + eb.tag(["e", n]); + }); + return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); + } + async tags(tags) { + const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.TagLists); + eb.tag(["d", _1.Lists.Followed]); + tags.forEach(t => { + eb.tag(["t", t]); + }); + return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); + } + async metadata(obj) { + const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.SetMetadata); + eb.content(JSON.stringify(obj)); + return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); + } + /** + * Create a basic text note + */ + async note(msg, fnExtra) { + const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.TextNote); + eb.content(msg); + eb.processContent(); + fnExtra?.(eb); + return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); + } + /** + * Create a zap request event for a given target event/profile + * @param amount Millisats amout! + * @param author Author pubkey to tag in the zap + * @param note Note Id to tag in the zap + * @param msg Custom message to be included in the zap + */ + async zap(amount, author, relays, note, msg, fnExtra) { + const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.ZapRequest); + eb.content(msg ?? ""); + if (note) { + eb.tag(["e", note]); + } + eb.tag(["p", author]); + eb.tag(["relays", ...relays.map(a => a.trim())]); + eb.tag(["amount", amount.toString()]); + eb.processContent(); + fnExtra?.(eb); + return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); + } + /** + * Reply to a note + */ + async reply(replyTo, msg, fnExtra) { + const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.TextNote); + eb.content(msg); + const thread = EventExt_1.EventExt.extractThread(replyTo); + if (thread) { + if (thread.root || thread.replyTo) { + eb.tag(["e", thread.root?.Event ?? thread.replyTo?.Event ?? "", "", "root"]); + } + eb.tag(["e", replyTo.id, replyTo.relays?.[0] ?? "", "reply"]); + eb.tag(["p", replyTo.pubkey]); + for (const pk of thread.pubKeys) { + if (pk === __classPrivateFieldGet(this, _EventPublisher_pubKey, "f")) { + continue; + } + eb.tag(["p", pk]); + } + } + else { + eb.tag(["e", replyTo.id, "", "reply"]); + // dont tag self in replies + if (replyTo.pubkey !== __classPrivateFieldGet(this, _EventPublisher_pubKey, "f")) { + eb.tag(["p", replyTo.pubkey]); + } + } + eb.processContent(); + fnExtra?.(eb); + return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); + } + async react(evRef, content = "+") { + const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.Reaction); + eb.content(content); + eb.tag(["e", evRef.id]); + eb.tag(["p", evRef.pubkey]); + return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); + } + async relayList(relays) { + if (!Array.isArray(relays)) { + relays = Object.entries(relays).map(([k, v]) => ({ + url: k, + settings: v, + })); + } + const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.Relays); + for (const rx of relays) { + const rTag = ["r", rx.url]; + if (rx.settings.read && !rx.settings.write) { + rTag.push("read"); + } + if (rx.settings.write && !rx.settings.read) { + rTag.push("write"); + } + eb.tag(rTag); + } + return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); + } + async contactList(follows, relays) { + const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.ContactList); + eb.content(JSON.stringify(relays)); + const temp = new Set(follows.filter(a => a.length === 64).map(a => a.toLowerCase())); + temp.forEach(a => eb.tag(["p", a])); + return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); + } + /** + * Delete an event (NIP-09) + */ + async delete(id) { + const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.Deletion); + eb.tag(["e", id]); + return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); + } + /** + * Repost a note (NIP-18) + */ + async repost(note) { + const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.Repost); + eb.tag(["e", note.id, ""]); + eb.tag(["p", note.pubkey]); + return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); + } + async decryptDm(note) { + if (note.pubkey !== __classPrivateFieldGet(this, _EventPublisher_pubKey, "f") && !note.tags.some(a => a[1] === __classPrivateFieldGet(this, _EventPublisher_pubKey, "f"))) { + throw new Error("Can't decrypt, DM does not belong to this user"); + } + const otherPubKey = note.pubkey === __classPrivateFieldGet(this, _EventPublisher_pubKey, "f") ? (0, Util_1.unwrap)(note.tags.find(a => a[0] === "p")?.[1]) : note.pubkey; + return await this.nip4Decrypt(note.content, otherPubKey); + } + async sendDm(content, to) { + const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.DirectMessage); + eb.content(await this.nip4Encrypt(content, to)); + eb.tag(["p", to]); + return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); + } + async generic(fnHook) { + const eb = new EventBuilder_1.EventBuilder(); + eb.pubKey(__classPrivateFieldGet(this, _EventPublisher_pubKey, "f")); + fnHook(eb); + return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); + } +} +exports.EventPublisher = EventPublisher; +_EventPublisher_system = new WeakMap(), _EventPublisher_pubKey = new WeakMap(), _EventPublisher_privateKey = new WeakMap(), _EventPublisher_instances = new WeakSet(), _EventPublisher_hasNip07_get = function _EventPublisher_hasNip07_get() { + return "nostr" in window; +}, _EventPublisher_eb = function _EventPublisher_eb(k) { + const eb = new EventBuilder_1.EventBuilder(); + return eb.pubKey(__classPrivateFieldGet(this, _EventPublisher_pubKey, "f")).kind(k); +}, _EventPublisher_sign = async function _EventPublisher_sign(eb) { + if (__classPrivateFieldGet(this, _EventPublisher_instances, "a", _EventPublisher_hasNip07_get) && !__classPrivateFieldGet(this, _EventPublisher_privateKey, "f")) { + const nip7PubKey = await (0, WorkQueue_1.barrierQueue)(Nip7Queue, () => (0, Util_1.unwrap)(window.nostr).getPublicKey()); + if (nip7PubKey !== __classPrivateFieldGet(this, _EventPublisher_pubKey, "f")) { + throw new Error("Can't sign event, NIP-07 pubkey does not match"); + } + const ev = eb.build(); + return await (0, WorkQueue_1.barrierQueue)(Nip7Queue, () => (0, Util_1.unwrap)(window.nostr).signEvent(ev)); + } + else if (__classPrivateFieldGet(this, _EventPublisher_privateKey, "f")) { + return await eb.buildAndSign(__classPrivateFieldGet(this, _EventPublisher_privateKey, "f")); + } + else { + throw new Error("Can't sign event, no private keys available"); + } +}; +//# sourceMappingURL=EventPublisher.js.map \ No newline at end of file diff --git a/packages/system/dist/EventPublisher.js.map b/packages/system/dist/EventPublisher.js.map new file mode 100644 index 00000000..1f95e3ad --- /dev/null +++ b/packages/system/dist/EventPublisher.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EventPublisher.js","sourceRoot":"","sources":["../src/EventPublisher.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/ExternalStore.d.ts b/packages/system/dist/ExternalStore.d.ts new file mode 100644 index 00000000..2c483f6d --- /dev/null +++ b/packages/system/dist/ExternalStore.d.ts @@ -0,0 +1,13 @@ +type HookFn = (e?: TSnapshot) => void; +/** + * Simple React hookable store with manual change notifications + */ +export default abstract class ExternalStore { + #private; + hook(fn: HookFn): () => void; + snapshot(): Readonly; + protected notifyChange(sn?: TSnapshot): void; + abstract takeSnapshot(): TSnapshot; +} +export {}; +//# sourceMappingURL=ExternalStore.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/ExternalStore.d.ts.map b/packages/system/dist/ExternalStore.d.ts.map new file mode 100644 index 00000000..9c439d8d --- /dev/null +++ b/packages/system/dist/ExternalStore.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ExternalStore.d.ts","sourceRoot":"","sources":["../src/ExternalStore.ts"],"names":[],"mappings":"AAAA,KAAK,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;AAMjD;;GAEG;AACH,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,aAAa,CAAC,SAAS;;IAKnD,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC;IAY1B,QAAQ;IAQR,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,SAAS;IAKrC,QAAQ,CAAC,YAAY,IAAI,SAAS;CACnC"} \ No newline at end of file diff --git a/packages/system/dist/ExternalStore.js b/packages/system/dist/ExternalStore.js new file mode 100644 index 00000000..47daafe4 --- /dev/null +++ b/packages/system/dist/ExternalStore.js @@ -0,0 +1,49 @@ +"use strict"; +var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +}; +var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; +}; +var _ExternalStore_hooks, _ExternalStore_snapshot, _ExternalStore_changed; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Simple React hookable store with manual change notifications + */ +class ExternalStore { + constructor() { + _ExternalStore_hooks.set(this, []); + _ExternalStore_snapshot.set(this, {}); + _ExternalStore_changed.set(this, true); + } + hook(fn) { + __classPrivateFieldGet(this, _ExternalStore_hooks, "f").push({ + fn, + }); + return () => { + const idx = __classPrivateFieldGet(this, _ExternalStore_hooks, "f").findIndex(a => a.fn === fn); + if (idx >= 0) { + __classPrivateFieldGet(this, _ExternalStore_hooks, "f").splice(idx, 1); + } + }; + } + snapshot() { + if (__classPrivateFieldGet(this, _ExternalStore_changed, "f")) { + __classPrivateFieldSet(this, _ExternalStore_snapshot, this.takeSnapshot(), "f"); + __classPrivateFieldSet(this, _ExternalStore_changed, false, "f"); + } + return __classPrivateFieldGet(this, _ExternalStore_snapshot, "f"); + } + notifyChange(sn) { + __classPrivateFieldSet(this, _ExternalStore_changed, true, "f"); + __classPrivateFieldGet(this, _ExternalStore_hooks, "f").forEach(h => h.fn(sn)); + } +} +exports.default = ExternalStore; +_ExternalStore_hooks = new WeakMap(), _ExternalStore_snapshot = new WeakMap(), _ExternalStore_changed = new WeakMap(); +//# sourceMappingURL=ExternalStore.js.map \ No newline at end of file diff --git a/packages/system/dist/ExternalStore.js.map b/packages/system/dist/ExternalStore.js.map new file mode 100644 index 00000000..483f5f23 --- /dev/null +++ b/packages/system/dist/ExternalStore.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ExternalStore.js","sourceRoot":"","sources":["../src/ExternalStore.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAMA;;GAEG;AACH,MAA8B,aAAa;IAA3C;QACE,+BAAuC,EAAE,EAAC;QAC1C,kCAAiC,EAAyB,EAAC;QAC3D,iCAAW,IAAI,EAAC;IA4BlB,CAAC;IA1BC,IAAI,CAAC,EAAqB;QACxB,uBAAA,IAAI,4BAAO,CAAC,IAAI,CAAC;YACf,EAAE;SACH,CAAC,CAAC;QACH,OAAO,GAAG,EAAE;YACV,MAAM,GAAG,GAAG,uBAAA,IAAI,4BAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YACpD,IAAI,GAAG,IAAI,CAAC,EAAE;gBACZ,uBAAA,IAAI,4BAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;aAC5B;QACH,CAAC,CAAC;IACJ,CAAC;IAED,QAAQ;QACN,IAAI,uBAAA,IAAI,8BAAS,EAAE;YACjB,uBAAA,IAAI,2BAAa,IAAI,CAAC,YAAY,EAAE,MAAA,CAAC;YACrC,uBAAA,IAAI,0BAAY,KAAK,MAAA,CAAC;SACvB;QACD,OAAO,uBAAA,IAAI,+BAAU,CAAC;IACxB,CAAC;IAES,YAAY,CAAC,EAAc;QACnC,uBAAA,IAAI,0BAAY,IAAI,MAAA,CAAC;QACrB,uBAAA,IAAI,4BAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,CAAC;CAGF;AA/BD,gCA+BC"} \ No newline at end of file diff --git a/packages/system/dist/GossipModel.d.ts b/packages/system/dist/GossipModel.d.ts new file mode 100644 index 00000000..a4e1c8b8 --- /dev/null +++ b/packages/system/dist/GossipModel.d.ts @@ -0,0 +1,20 @@ +import { FullRelaySettings, ReqFilter } from "."; +export interface RelayTaggedFilter { + relay: string; + filter: ReqFilter; +} +export interface RelayTaggedFilters { + relay: string; + filters: Array; +} +export interface RelayCache { + get(pubkey?: string): Array | undefined; +} +export declare function splitAllByWriteRelays(cache: RelayCache, filters: Array): RelayTaggedFilters[]; +/** + * Split filters by authors + * @param filter + * @returns + */ +export declare function splitByWriteRelays(cache: RelayCache, filter: ReqFilter): Array; +//# sourceMappingURL=GossipModel.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/GossipModel.d.ts.map b/packages/system/dist/GossipModel.d.ts.map new file mode 100644 index 00000000..3328b5e2 --- /dev/null +++ b/packages/system/dist/GossipModel.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"GossipModel.d.ts","sourceRoot":"","sources":["../src/GossipModel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC;AAMjD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,iBAAiB,CAAC,GAAG,SAAS,CAAC;CAC5D;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,wBAqBjF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,GAAG,KAAK,CAAC,iBAAiB,CAAC,CAoEjG"} \ No newline at end of file diff --git a/packages/system/dist/GossipModel.js b/packages/system/dist/GossipModel.js new file mode 100644 index 00000000..1668805e --- /dev/null +++ b/packages/system/dist/GossipModel.js @@ -0,0 +1,102 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.splitByWriteRelays = exports.splitAllByWriteRelays = void 0; +const Util_1 = require("./Util"); +const debug_1 = __importDefault(require("debug")); +const PickNRelays = 2; +function splitAllByWriteRelays(cache, filters) { + const allSplit = filters + .map(a => splitByWriteRelays(cache, a)) + .reduce((acc, v) => { + for (const vn of v) { + const existing = acc.get(vn.relay); + if (existing) { + existing.push(vn.filter); + } + else { + acc.set(vn.relay, [vn.filter]); + } + } + return acc; + }, new Map()); + return [...allSplit.entries()].map(([k, v]) => { + return { + relay: k, + filters: v, + }; + }); +} +exports.splitAllByWriteRelays = splitAllByWriteRelays; +/** + * Split filters by authors + * @param filter + * @returns + */ +function splitByWriteRelays(cache, filter) { + if ((filter.authors?.length ?? 0) === 0) + return [ + { + relay: "", + filter, + }, + ]; + const allRelays = (0, Util_1.unwrap)(filter.authors).map(a => { + return { + key: a, + relays: cache.get(a)?.filter(a => a.settings.write), + }; + }); + const missing = allRelays.filter(a => a.relays === undefined); + const hasRelays = allRelays.filter(a => a.relays !== undefined); + const relayUserMap = hasRelays.reduce((acc, v) => { + for (const r of (0, Util_1.unwrap)(v.relays)) { + if (!acc.has(r.url)) { + acc.set(r.url, new Set([v.key])); + } + else { + (0, Util_1.unwrap)(acc.get(r.url)).add(v.key); + } + } + return acc; + }, new Map()); + // selection algo will just pick relays with the most users + const topRelays = [...relayUserMap.entries()].sort(([, v], [, v1]) => v1.size - v.size); + // - count keys per relay + // - pick n top relays + // - map keys per relay (for subscription filter) + const userPickedRelays = (0, Util_1.unwrap)(filter.authors).map(k => { + // pick top 3 relays for this key + const relaysForKey = topRelays + .filter(([, v]) => v.has(k)) + .slice(0, PickNRelays) + .map(([k]) => k); + return { k, relaysForKey }; + }); + const pickedRelays = new Set(userPickedRelays.map(a => a.relaysForKey).flat()); + const picked = [...pickedRelays].map(a => { + const keysOnPickedRelay = new Set(userPickedRelays.filter(b => b.relaysForKey.includes(a)).map(b => b.k)); + return { + relay: a, + filter: { + ...filter, + authors: [...keysOnPickedRelay], + }, + }; + }); + if (missing.length > 0) { + picked.push({ + relay: "", + filter: { + ...filter, + authors: missing.map(a => a.key), + }, + }); + } + (0, debug_1.default)("GOSSIP")("Picked %o", picked); + return picked; +} +exports.splitByWriteRelays = splitByWriteRelays; +//# sourceMappingURL=GossipModel.js.map \ No newline at end of file diff --git a/packages/system/dist/GossipModel.js.map b/packages/system/dist/GossipModel.js.map new file mode 100644 index 00000000..0bddddb1 --- /dev/null +++ b/packages/system/dist/GossipModel.js.map @@ -0,0 +1 @@ +{"version":3,"file":"GossipModel.js","sourceRoot":"","sources":["../src/GossipModel.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/Links.d.ts b/packages/system/dist/Links.d.ts new file mode 100644 index 00000000..bd3ed4c0 --- /dev/null +++ b/packages/system/dist/Links.d.ts @@ -0,0 +1,24 @@ +import { HexKey } from "./Nostr"; +export declare enum NostrPrefix { + PublicKey = "npub", + PrivateKey = "nsec", + Note = "note", + Profile = "nprofile", + Event = "nevent", + Relay = "nrelay", + Address = "naddr" +} +export declare enum TLVEntryType { + Special = 0, + Relay = 1, + Author = 2, + Kind = 3 +} +export interface TLVEntry { + type: TLVEntryType; + length: number; + value: string | HexKey | number; +} +export declare function encodeTLV(prefix: NostrPrefix, id: string, relays?: string[], kind?: number, author?: string): string; +export declare function decodeTLV(str: string): TLVEntry[]; +//# sourceMappingURL=Links.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Links.d.ts.map b/packages/system/dist/Links.d.ts.map new file mode 100644 index 00000000..a7158f31 --- /dev/null +++ b/packages/system/dist/Links.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Links.d.ts","sourceRoot":"","sources":["../src/Links.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,oBAAY,WAAW;IACrB,SAAS,SAAS;IAClB,UAAU,SAAS;IACnB,IAAI,SAAS;IAGb,OAAO,aAAa;IACpB,KAAK,WAAW;IAChB,KAAK,WAAW;IAChB,OAAO,UAAU;CAClB;AAED,oBAAY,YAAY;IACtB,OAAO,IAAI;IACX,KAAK,IAAI;IACT,MAAM,IAAI;IACV,IAAI,IAAI;CACT;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CACjC;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,UAiB3G;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,cAkBpC"} \ No newline at end of file diff --git a/packages/system/dist/Links.js b/packages/system/dist/Links.js new file mode 100644 index 00000000..727ae9de --- /dev/null +++ b/packages/system/dist/Links.js @@ -0,0 +1,102 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.decodeTLV = exports.encodeTLV = exports.TLVEntryType = exports.NostrPrefix = void 0; +const utils = __importStar(require("@noble/curves/abstract/utils")); +const bech32_1 = require("bech32"); +var NostrPrefix; +(function (NostrPrefix) { + NostrPrefix["PublicKey"] = "npub"; + NostrPrefix["PrivateKey"] = "nsec"; + NostrPrefix["Note"] = "note"; + // TLV prefixes + NostrPrefix["Profile"] = "nprofile"; + NostrPrefix["Event"] = "nevent"; + NostrPrefix["Relay"] = "nrelay"; + NostrPrefix["Address"] = "naddr"; +})(NostrPrefix = exports.NostrPrefix || (exports.NostrPrefix = {})); +var TLVEntryType; +(function (TLVEntryType) { + TLVEntryType[TLVEntryType["Special"] = 0] = "Special"; + TLVEntryType[TLVEntryType["Relay"] = 1] = "Relay"; + TLVEntryType[TLVEntryType["Author"] = 2] = "Author"; + TLVEntryType[TLVEntryType["Kind"] = 3] = "Kind"; +})(TLVEntryType = exports.TLVEntryType || (exports.TLVEntryType = {})); +function encodeTLV(prefix, id, relays, kind, author) { + const enc = new TextEncoder(); + const buf = prefix === NostrPrefix.Address ? enc.encode(id) : utils.hexToBytes(id); + const tl0 = [0, buf.length, ...buf]; + const tl1 = relays + ?.map(a => { + const data = enc.encode(a); + return [1, data.length, ...data]; + }) + .flat() ?? []; + const tl2 = author ? [2, 32, ...utils.hexToBytes(author)] : []; + const tl3 = kind ? [3, 4, ...new Uint8Array(new Uint32Array([kind]).buffer).reverse()] : []; + return bech32_1.bech32.encode(prefix, bech32_1.bech32.toWords([...tl0, ...tl1, ...tl2, ...tl3]), 1000); +} +exports.encodeTLV = encodeTLV; +function decodeTLV(str) { + const decoded = bech32_1.bech32.decode(str, 1000); + const data = bech32_1.bech32.fromWords(decoded.words); + const entries = []; + let x = 0; + while (x < data.length) { + const t = data[x]; + const l = data[x + 1]; + const v = data.slice(x + 2, x + 2 + l); + entries.push({ + type: t, + length: l, + value: decodeTLVEntry(t, decoded.prefix, new Uint8Array(v)), + }); + x += 2 + l; + } + return entries; +} +exports.decodeTLV = decodeTLV; +function decodeTLVEntry(type, prefix, data) { + switch (type) { + case TLVEntryType.Special: { + if (prefix === NostrPrefix.Address) { + return new TextDecoder("ASCII").decode(data); + } + else { + return utils.bytesToHex(data); + } + } + case TLVEntryType.Author: { + return utils.bytesToHex(data); + } + case TLVEntryType.Kind: { + return new Uint32Array(new Uint8Array(data.reverse()).buffer)[0]; + } + case TLVEntryType.Relay: { + return new TextDecoder("ASCII").decode(data); + } + } +} +//# sourceMappingURL=Links.js.map \ No newline at end of file diff --git a/packages/system/dist/Links.js.map b/packages/system/dist/Links.js.map new file mode 100644 index 00000000..277242a7 --- /dev/null +++ b/packages/system/dist/Links.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Links.js","sourceRoot":"","sources":["../src/Links.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/Nips.d.ts b/packages/system/dist/Nips.d.ts new file mode 100644 index 00000000..e3486652 --- /dev/null +++ b/packages/system/dist/Nips.d.ts @@ -0,0 +1,4 @@ +export declare enum Nips { + Search = 50 +} +//# sourceMappingURL=Nips.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Nips.d.ts.map b/packages/system/dist/Nips.d.ts.map new file mode 100644 index 00000000..42dcd779 --- /dev/null +++ b/packages/system/dist/Nips.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Nips.d.ts","sourceRoot":"","sources":["../src/Nips.ts"],"names":[],"mappings":"AAAA,oBAAY,IAAI;IACd,MAAM,KAAK;CACZ"} \ No newline at end of file diff --git a/packages/system/dist/Nips.js b/packages/system/dist/Nips.js new file mode 100644 index 00000000..80b98f7c --- /dev/null +++ b/packages/system/dist/Nips.js @@ -0,0 +1,8 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Nips = void 0; +var Nips; +(function (Nips) { + Nips[Nips["Search"] = 50] = "Search"; +})(Nips = exports.Nips || (exports.Nips = {})); +//# sourceMappingURL=Nips.js.map \ No newline at end of file diff --git a/packages/system/dist/Nips.js.map b/packages/system/dist/Nips.js.map new file mode 100644 index 00000000..b5276f39 --- /dev/null +++ b/packages/system/dist/Nips.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Nips.js","sourceRoot":"","sources":["../src/Nips.ts"],"names":[],"mappings":";;;AAAA,IAAY,IAEX;AAFD,WAAY,IAAI;IACd,oCAAW,CAAA;AACb,CAAC,EAFW,IAAI,GAAJ,YAAI,KAAJ,YAAI,QAEf"} \ No newline at end of file diff --git a/packages/system/dist/Nostr.d.ts b/packages/system/dist/Nostr.d.ts new file mode 100644 index 00000000..040af7b8 --- /dev/null +++ b/packages/system/dist/Nostr.d.ts @@ -0,0 +1,75 @@ +import { RelaySettings } from "./Connection"; +export interface NostrEvent { + id: u256; + pubkey: HexKey; + created_at: number; + kind: number; + tags: Array>; + content: string; + sig: string; +} +export interface TaggedRawEvent extends NostrEvent { + /** + * A list of relays this event was seen on + */ + relays: string[]; +} +/** + * Basic raw key as hex + */ +export type HexKey = string; +/** + * Optional HexKey + */ +export type MaybeHexKey = HexKey | undefined; +/** + * A 256bit hex id + */ +export type u256 = string; +export type ReqCommand = [cmd: "REQ", id: string, ...filters: Array]; +/** + * Raw REQ filter object + */ +export interface ReqFilter { + ids?: u256[]; + authors?: u256[]; + kinds?: number[]; + "#e"?: u256[]; + "#p"?: u256[]; + "#t"?: string[]; + "#d"?: string[]; + "#r"?: string[]; + search?: string; + since?: number; + until?: number; + limit?: number; +} +/** + * Medatadata event content + */ +export type UserMetadata = { + name?: string; + display_name?: string; + about?: string; + picture?: string; + website?: string; + banner?: string; + nip05?: string; + lud06?: string; + lud16?: string; +}; +/** + * NIP-51 list types + */ +export declare enum Lists { + Muted = "mute", + Pinned = "pin", + Bookmarked = "bookmark", + Followed = "follow", + Badges = "profile_badges" +} +export interface FullRelaySettings { + url: string; + settings: RelaySettings; +} +//# sourceMappingURL=Nostr.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Nostr.d.ts.map b/packages/system/dist/Nostr.d.ts.map new file mode 100644 index 00000000..6e003def --- /dev/null +++ b/packages/system/dist/Nostr.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Nostr.d.ts","sourceRoot":"","sources":["../src/Nostr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD;;OAEG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;AAE5B;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC;AAE1B,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC;IACb,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;IACd,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,oBAAY,KAAK;IACf,KAAK,SAAS;IACd,MAAM,QAAQ;IACd,UAAU,aAAa;IACvB,QAAQ,WAAW;IACnB,MAAM,mBAAmB;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,aAAa,CAAC;CACzB"} \ No newline at end of file diff --git a/packages/system/dist/Nostr.js b/packages/system/dist/Nostr.js new file mode 100644 index 00000000..5c2cad16 --- /dev/null +++ b/packages/system/dist/Nostr.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Lists = void 0; +/** + * NIP-51 list types + */ +var Lists; +(function (Lists) { + Lists["Muted"] = "mute"; + Lists["Pinned"] = "pin"; + Lists["Bookmarked"] = "bookmark"; + Lists["Followed"] = "follow"; + Lists["Badges"] = "profile_badges"; +})(Lists = exports.Lists || (exports.Lists = {})); +//# sourceMappingURL=Nostr.js.map \ No newline at end of file diff --git a/packages/system/dist/Nostr.js.map b/packages/system/dist/Nostr.js.map new file mode 100644 index 00000000..1100ff69 --- /dev/null +++ b/packages/system/dist/Nostr.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Nostr.js","sourceRoot":"","sources":["../src/Nostr.ts"],"names":[],"mappings":";;;AAqEA;;GAEG;AACH,IAAY,KAMX;AAND,WAAY,KAAK;IACf,uBAAc,CAAA;IACd,uBAAc,CAAA;IACd,gCAAuB,CAAA;IACvB,4BAAmB,CAAA;IACnB,kCAAyB,CAAA;AAC3B,CAAC,EANW,KAAK,GAAL,aAAK,KAAL,aAAK,QAMhB"} \ No newline at end of file diff --git a/packages/system/dist/NostrLink.d.ts b/packages/system/dist/NostrLink.d.ts new file mode 100644 index 00000000..8d22c067 --- /dev/null +++ b/packages/system/dist/NostrLink.d.ts @@ -0,0 +1,13 @@ +import { NostrPrefix } from "."; +export interface NostrLink { + type: NostrPrefix; + id: string; + kind?: number; + author?: string; + relays?: Array; + encode(): string; +} +export declare function validateNostrLink(link: string): boolean; +export declare function tryParseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLink | undefined; +export declare function parseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLink; +//# sourceMappingURL=NostrLink.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/NostrLink.d.ts.map b/packages/system/dist/NostrLink.d.ts.map new file mode 100644 index 00000000..ab1d2006 --- /dev/null +++ b/packages/system/dist/NostrLink.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"NostrLink.d.ts","sourceRoot":"","sources":["../src/NostrLink.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAA2B,MAAM,GAAG,CAAC;AAEzD,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvB,MAAM,IAAI,MAAM,CAAC;CAClB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAcvD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,WAAW,GAAG,SAAS,GAAG,SAAS,CAM/F;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,WAAW,GAAG,SAAS,CAwEhF"} \ No newline at end of file diff --git a/packages/system/dist/NostrLink.js b/packages/system/dist/NostrLink.js new file mode 100644 index 00000000..18cecd68 --- /dev/null +++ b/packages/system/dist/NostrLink.js @@ -0,0 +1,110 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.parseNostrLink = exports.tryParseNostrLink = exports.validateNostrLink = void 0; +const Util_1 = require("./Util"); +const _1 = require("."); +function validateNostrLink(link) { + try { + const parsedLink = parseNostrLink(link); + if (!parsedLink) { + return false; + } + if (parsedLink.type === _1.NostrPrefix.PublicKey || parsedLink.type === _1.NostrPrefix.Note) { + return parsedLink.id.length === 64; + } + return true; + } + catch { + return false; + } +} +exports.validateNostrLink = validateNostrLink; +function tryParseNostrLink(link, prefixHint) { + try { + return parseNostrLink(link, prefixHint); + } + catch { + return undefined; + } +} +exports.tryParseNostrLink = tryParseNostrLink; +function parseNostrLink(link, prefixHint) { + const entity = link.startsWith("web+nostr:") || link.startsWith("nostr:") ? link.split(":")[1] : link; + const isPrefix = (prefix) => { + return entity.startsWith(prefix); + }; + if (isPrefix(_1.NostrPrefix.PublicKey)) { + const id = (0, Util_1.bech32ToHex)(entity); + if (id.length !== 64) + throw new Error("Invalid nostr link, must contain 32 byte id"); + return { + type: _1.NostrPrefix.PublicKey, + id: id, + encode: () => (0, Util_1.hexToBech32)(_1.NostrPrefix.PublicKey, id), + }; + } + else if (isPrefix(_1.NostrPrefix.Note)) { + const id = (0, Util_1.bech32ToHex)(entity); + if (id.length !== 64) + throw new Error("Invalid nostr link, must contain 32 byte id"); + return { + type: _1.NostrPrefix.Note, + id: id, + encode: () => (0, Util_1.hexToBech32)(_1.NostrPrefix.Note, id), + }; + } + else if (isPrefix(_1.NostrPrefix.Profile) || isPrefix(_1.NostrPrefix.Event) || isPrefix(_1.NostrPrefix.Address)) { + const decoded = (0, _1.decodeTLV)(entity); + const id = decoded.find(a => a.type === _1.TLVEntryType.Special)?.value; + const relays = decoded.filter(a => a.type === _1.TLVEntryType.Relay).map(a => a.value); + const author = decoded.find(a => a.type === _1.TLVEntryType.Author)?.value; + const kind = decoded.find(a => a.type === _1.TLVEntryType.Kind)?.value; + const encode = () => { + return entity; // return original + }; + if (isPrefix(_1.NostrPrefix.Profile)) { + if (id.length !== 64) + throw new Error("Invalid nostr link, must contain 32 byte id"); + return { + type: _1.NostrPrefix.Profile, + id, + relays, + kind, + author, + encode, + }; + } + else if (isPrefix(_1.NostrPrefix.Event)) { + if (id.length !== 64) + throw new Error("Invalid nostr link, must contain 32 byte id"); + return { + type: _1.NostrPrefix.Event, + id, + relays, + kind, + author, + encode, + }; + } + else if (isPrefix(_1.NostrPrefix.Address)) { + return { + type: _1.NostrPrefix.Address, + id, + relays, + kind, + author, + encode, + }; + } + } + else if (prefixHint) { + return { + type: prefixHint, + id: link, + encode: () => (0, Util_1.hexToBech32)(prefixHint, link), + }; + } + throw new Error("Invalid nostr link"); +} +exports.parseNostrLink = parseNostrLink; +//# sourceMappingURL=NostrLink.js.map \ No newline at end of file diff --git a/packages/system/dist/NostrLink.js.map b/packages/system/dist/NostrLink.js.map new file mode 100644 index 00000000..8ad349a3 --- /dev/null +++ b/packages/system/dist/NostrLink.js.map @@ -0,0 +1 @@ +{"version":3,"file":"NostrLink.js","sourceRoot":"","sources":["../src/NostrLink.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/NostrSystem.d.ts b/packages/system/dist/NostrSystem.d.ts new file mode 100644 index 00000000..c684a9be --- /dev/null +++ b/packages/system/dist/NostrSystem.d.ts @@ -0,0 +1,75 @@ +import ExternalStore from "./ExternalStore"; +import { NostrEvent, TaggedRawEvent } from "./Nostr"; +import { AuthHandler, Connection, RelaySettings, ConnectionStateSnapshot } from "./Connection"; +import { Query } from "./Query"; +import { RelayCache } from "./GossipModel"; +import { NoteStore } from "./NoteCollection"; +import { BuiltRawReqFilter, RequestBuilder } from "./RequestBuilder"; +import { SystemInterface, SystemSnapshot } from "."; +/** + * Manages nostr content retrieval system + */ +export declare class NostrSystem extends ExternalStore implements SystemInterface { + #private; + /** + * All active queries + */ + Queries: Map; + /** + * Handler function for NIP-42 + */ + HandleAuth?: AuthHandler; + constructor(relayCache: RelayCache); + get Sockets(): ConnectionStateSnapshot[]; + /** + * Connect to a NOSTR relay if not already connected + */ + ConnectToRelay(address: string, options: RelaySettings): Promise; + OnRelayDisconnect(id: string): void; + OnEndOfStoredEvents(c: Readonly, sub: string): void; + OnEvent(sub: string, ev: TaggedRawEvent): void; + /** + * + * @param address Relay address URL + */ + ConnectEphemeralRelay(address: string): Promise; + /** + * Disconnect from a relay + */ + DisconnectRelay(address: string): void; + GetQuery(id: string): Query | undefined; + Query(type: { + new (): T; + }, req: RequestBuilder): Query; + SendQuery(q: Query, qSend: BuiltRawReqFilter): Promise<{ + readonly id: string; + readonly start: number; + sent?: number | undefined; + eose?: number | undefined; + close?: number | undefined; + "__#9@#wasForceClosed": boolean; + readonly "__#9@#fnClose": (id: string) => void; + readonly "__#9@#fnProgress": () => void; + readonly relay: string; + readonly filters: import("./Nostr").ReqFilter[]; + readonly connId: string; + sentToRelay(): void; + gotEose(): void; + forceEose(): void; + sendClose(): void; + readonly queued: number; + readonly runtime: number; + readonly responseTime: number; + readonly finished: boolean; + }[]>; + /** + * Send events to writable relays + */ + BroadcastEvent(ev: NostrEvent): void; + /** + * Write an event to a relay then disconnect + */ + WriteOnceToRelay(address: string, ev: NostrEvent): Promise; + takeSnapshot(): SystemSnapshot; +} +//# sourceMappingURL=NostrSystem.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/NostrSystem.d.ts.map b/packages/system/dist/NostrSystem.d.ts.map new file mode 100644 index 00000000..6cfd8efc --- /dev/null +++ b/packages/system/dist/NostrSystem.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"NostrSystem.d.ts","sourceRoot":"","sources":["../src/NostrSystem.ts"],"names":[],"mappings":"AAEA,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAC/F,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAErE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,GAAG,CAAC;AAEpD;;GAEG;AACH,qBAAa,WAAY,SAAQ,aAAa,CAAC,cAAc,CAAE,YAAW,eAAe;;IAMvF;;OAEG;IACH,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAa;IAExC;;OAEG;IACH,UAAU,CAAC,EAAE,WAAW,CAAC;gBAKb,UAAU,EAAE,UAAU;IAMlC,IAAI,OAAO,IAAI,uBAAuB,EAAE,CAEvC;IAED;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa;IAmB5D,iBAAiB,CAAC,EAAE,EAAE,MAAM;IAM5B,mBAAmB,CAAC,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,MAAM;IAMxD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc;IAMvC;;;OAGG;IACG,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAiB7E;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM;IAQ/B,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAIvC,KAAK,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,EAAE;QAAE,QAAQ,CAAC,CAAA;KAAE,EAAE,GAAG,EAAE,cAAc,GAAG,KAAK;IAiCrE,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB;;;;;;;;;;;;;;;;;;;;;IAmClD;;OAEG;IACH,cAAc,CAAC,EAAE,EAAE,UAAU;IAM7B;;OAEG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU;IAetD,YAAY,IAAI,cAAc;CA2B/B"} \ No newline at end of file diff --git a/packages/system/dist/NostrSystem.js b/packages/system/dist/NostrSystem.js new file mode 100644 index 00000000..305b539d --- /dev/null +++ b/packages/system/dist/NostrSystem.js @@ -0,0 +1,237 @@ +"use strict"; +var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; +}; +var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +var _NostrSystem_instances, _NostrSystem_sockets, _NostrSystem_log, _NostrSystem_relayCache, _NostrSystem_cleanup; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.NostrSystem = void 0; +const debug_1 = __importDefault(require("debug")); +const ExternalStore_1 = __importDefault(require("./ExternalStore")); +const Connection_1 = require("./Connection"); +const Query_1 = require("./Query"); +const Util_1 = require("./Util"); +/** + * Manages nostr content retrieval system + */ +class NostrSystem extends ExternalStore_1.default { + constructor(relayCache) { + super(); + _NostrSystem_instances.add(this); + /** + * All currently connected websockets + */ + _NostrSystem_sockets.set(this, new Map()); + /** + * All active queries + */ + this.Queries = new Map(); + _NostrSystem_log.set(this, (0, debug_1.default)("System")); + _NostrSystem_relayCache.set(this, void 0); + __classPrivateFieldSet(this, _NostrSystem_relayCache, relayCache, "f"); + __classPrivateFieldGet(this, _NostrSystem_instances, "m", _NostrSystem_cleanup).call(this); + } + get Sockets() { + return [...__classPrivateFieldGet(this, _NostrSystem_sockets, "f").values()].map(a => a.snapshot()); + } + /** + * Connect to a NOSTR relay if not already connected + */ + async ConnectToRelay(address, options) { + try { + const addr = (0, Util_1.unwrap)((0, Util_1.sanitizeRelayUrl)(address)); + if (!__classPrivateFieldGet(this, _NostrSystem_sockets, "f").has(addr)) { + const c = new Connection_1.Connection(addr, options, this.HandleAuth?.bind(this)); + __classPrivateFieldGet(this, _NostrSystem_sockets, "f").set(addr, c); + c.OnEvent = (s, e) => this.OnEvent(s, e); + c.OnEose = s => this.OnEndOfStoredEvents(c, s); + c.OnDisconnect = id => this.OnRelayDisconnect(id); + await c.Connect(); + } + else { + // update settings if already connected + (0, Util_1.unwrap)(__classPrivateFieldGet(this, _NostrSystem_sockets, "f").get(addr)).Settings = options; + } + } + catch (e) { + console.error(e); + } + } + OnRelayDisconnect(id) { + for (const [, q] of this.Queries) { + q.connectionLost(id); + } + } + OnEndOfStoredEvents(c, sub) { + for (const [, v] of this.Queries) { + v.eose(sub, c); + } + } + OnEvent(sub, ev) { + for (const [, v] of this.Queries) { + v.onEvent(sub, ev); + } + } + /** + * + * @param address Relay address URL + */ + async ConnectEphemeralRelay(address) { + try { + const addr = (0, Util_1.unwrap)((0, Util_1.sanitizeRelayUrl)(address)); + if (!__classPrivateFieldGet(this, _NostrSystem_sockets, "f").has(addr)) { + const c = new Connection_1.Connection(addr, { read: true, write: false }, this.HandleAuth?.bind(this), true); + __classPrivateFieldGet(this, _NostrSystem_sockets, "f").set(addr, c); + c.OnEvent = (s, e) => this.OnEvent(s, e); + c.OnEose = s => this.OnEndOfStoredEvents(c, s); + c.OnDisconnect = id => this.OnRelayDisconnect(id); + await c.Connect(); + return c; + } + } + catch (e) { + console.error(e); + } + } + /** + * Disconnect from a relay + */ + DisconnectRelay(address) { + const c = __classPrivateFieldGet(this, _NostrSystem_sockets, "f").get(address); + if (c) { + __classPrivateFieldGet(this, _NostrSystem_sockets, "f").delete(address); + c.Close(); + } + } + GetQuery(id) { + return this.Queries.get(id); + } + Query(type, req) { + const existing = this.Queries.get(req.id); + if (existing) { + const filters = !req.options?.skipDiff + ? req.buildDiff(__classPrivateFieldGet(this, _NostrSystem_relayCache, "f"), existing.filters) + : req.build(__classPrivateFieldGet(this, _NostrSystem_relayCache, "f")); + if (filters.length === 0 && !!req.options?.skipDiff) { + return existing; + } + else { + for (const subQ of filters) { + this.SendQuery(existing, subQ).then(qta => qta.forEach(v => __classPrivateFieldGet(this, _NostrSystem_log, "f").call(this, "New QT from diff %s %s %O from: %O", req.id, v.id, v.filters, existing.filters))); + } + this.notifyChange(); + return existing; + } + } + else { + const store = new type(); + const filters = req.build(__classPrivateFieldGet(this, _NostrSystem_relayCache, "f")); + const q = new Query_1.Query(req.id, store, req.options?.leaveOpen); + this.Queries.set(req.id, q); + for (const subQ of filters) { + this.SendQuery(q, subQ).then(qta => qta.forEach(v => __classPrivateFieldGet(this, _NostrSystem_log, "f").call(this, "New QT from diff %s %s %O", req.id, v.id, v.filters))); + } + this.notifyChange(); + return q; + } + } + async SendQuery(q, qSend) { + if (qSend.relay) { + __classPrivateFieldGet(this, _NostrSystem_log, "f").call(this, "Sending query to %s %O", qSend.relay, qSend); + const s = __classPrivateFieldGet(this, _NostrSystem_sockets, "f").get(qSend.relay); + if (s) { + const qt = q.sendToRelay(s, qSend); + if (qt) { + return [qt]; + } + } + else { + const nc = await this.ConnectEphemeralRelay(qSend.relay); + if (nc) { + const qt = q.sendToRelay(nc, qSend); + if (qt) { + return [qt]; + } + } + else { + console.warn("Failed to connect to new relay for:", qSend.relay, q); + } + } + } + else { + const ret = []; + for (const [, s] of __classPrivateFieldGet(this, _NostrSystem_sockets, "f")) { + if (!s.Ephemeral) { + const qt = q.sendToRelay(s, qSend); + if (qt) { + ret.push(qt); + } + } + } + return ret; + } + return []; + } + /** + * Send events to writable relays + */ + BroadcastEvent(ev) { + for (const [, s] of __classPrivateFieldGet(this, _NostrSystem_sockets, "f")) { + s.SendEvent(ev); + } + } + /** + * Write an event to a relay then disconnect + */ + async WriteOnceToRelay(address, ev) { + return new Promise((resolve, reject) => { + const c = new Connection_1.Connection(address, { write: true, read: false }, this.HandleAuth, true); + const t = setTimeout(reject, 5000); + c.OnConnected = async () => { + clearTimeout(t); + await c.SendAsync(ev); + c.Close(); + resolve(); + }; + c.Connect(); + }); + } + takeSnapshot() { + return { + queries: [...this.Queries.values()].map(a => { + return { + id: a.id, + filters: a.filters, + subFilters: [], + }; + }), + }; + } +} +exports.NostrSystem = NostrSystem; +_NostrSystem_sockets = new WeakMap(), _NostrSystem_log = new WeakMap(), _NostrSystem_relayCache = new WeakMap(), _NostrSystem_instances = new WeakSet(), _NostrSystem_cleanup = function _NostrSystem_cleanup() { + let changed = false; + for (const [k, v] of this.Queries) { + if (v.canRemove()) { + v.sendClose(); + this.Queries.delete(k); + __classPrivateFieldGet(this, _NostrSystem_log, "f").call(this, "Deleted query %s", k); + changed = true; + } + } + if (changed) { + this.notifyChange(); + } + setTimeout(() => __classPrivateFieldGet(this, _NostrSystem_instances, "m", _NostrSystem_cleanup).call(this), 1000); +}; +//# sourceMappingURL=NostrSystem.js.map \ No newline at end of file diff --git a/packages/system/dist/NostrSystem.js.map b/packages/system/dist/NostrSystem.js.map new file mode 100644 index 00000000..52f75a20 --- /dev/null +++ b/packages/system/dist/NostrSystem.js.map @@ -0,0 +1 @@ +{"version":3,"file":"NostrSystem.js","sourceRoot":"","sources":["../src/NostrSystem.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,kDAA0B;AAE1B,oEAA4C;AAE5C,6CAA+F;AAC/F,mCAAgC;AAIhC,iCAAkD;AAGlD;;GAEG;AACH,MAAa,WAAY,SAAQ,uBAA6B;IAmB5D,YAAY,UAAsB;QAChC,KAAK,EAAE,CAAC;;QAnBV;;WAEG;QACH,+BAAW,IAAI,GAAG,EAAsB,EAAC;QAEzC;;WAEG;QACH,YAAO,GAAuB,IAAI,GAAG,EAAE,CAAC;QAOxC,2BAAO,IAAA,eAAK,EAAC,QAAQ,CAAC,EAAC;QACvB,0CAAwB;QAItB,uBAAA,IAAI,2BAAe,UAAU,MAAA,CAAC;QAC9B,uBAAA,IAAI,oDAAS,MAAb,IAAI,CAAW,CAAC;IAClB,CAAC;IAED,IAAI,OAAO;QACT,OAAO,CAAC,GAAG,uBAAA,IAAI,4BAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,OAAe,EAAE,OAAsB;QAC1D,IAAI;YACF,MAAM,IAAI,GAAG,IAAA,aAAM,EAAC,IAAA,uBAAgB,EAAC,OAAO,CAAC,CAAC,CAAC;YAC/C,IAAI,CAAC,uBAAA,IAAI,4BAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;gBAC5B,MAAM,CAAC,GAAG,IAAI,uBAAU,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBACrE,uBAAA,IAAI,4BAAS,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAC3B,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBACzC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC/C,CAAC,CAAC,YAAY,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;gBAClD,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;aACnB;iBAAM;gBACL,uCAAuC;gBACvC,IAAA,aAAM,EAAC,uBAAA,IAAI,4BAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,GAAG,OAAO,CAAC;aACpD;SACF;QAAC,OAAO,CAAC,EAAE;YACV,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SAClB;IACH,CAAC;IAED,iBAAiB,CAAC,EAAU;QAC1B,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE;YAChC,CAAC,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;SACtB;IACH,CAAC;IAED,mBAAmB,CAAC,CAAuB,EAAE,GAAW;QACtD,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE;YAChC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;SAChB;IACH,CAAC;IAED,OAAO,CAAC,GAAW,EAAE,EAAkB;QACrC,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE;YAChC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;SACpB;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,qBAAqB,CAAC,OAAe;QACzC,IAAI;YACF,MAAM,IAAI,GAAG,IAAA,aAAM,EAAC,IAAA,uBAAgB,EAAC,OAAO,CAAC,CAAC,CAAC;YAC/C,IAAI,CAAC,uBAAA,IAAI,4BAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;gBAC5B,MAAM,CAAC,GAAG,IAAI,uBAAU,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;gBAChG,uBAAA,IAAI,4BAAS,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAC3B,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBACzC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC/C,CAAC,CAAC,YAAY,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;gBAClD,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;gBAClB,OAAO,CAAC,CAAC;aACV;SACF;QAAC,OAAO,CAAC,EAAE;YACV,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SAClB;IACH,CAAC;IAED;;OAEG;IACH,eAAe,CAAC,OAAe;QAC7B,MAAM,CAAC,GAAG,uBAAA,IAAI,4BAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,EAAE;YACL,uBAAA,IAAI,4BAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC9B,CAAC,CAAC,KAAK,EAAE,CAAC;SACX;IACH,CAAC;IAED,QAAQ,CAAC,EAAU;QACjB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,CAAsB,IAAmB,EAAE,GAAmB;QACjE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1C,IAAI,QAAQ,EAAE;YACZ,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ;gBACpC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,uBAAA,IAAI,+BAAY,EAAE,QAAQ,CAAC,OAAO,CAAC;gBACnD,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,uBAAA,IAAI,+BAAY,CAAC,CAAC;YAChC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE;gBACnD,OAAO,QAAQ,CAAC;aACjB;iBAAM;gBACL,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE;oBAC1B,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CACxC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,uBAAA,IAAI,wBAAK,MAAT,IAAI,EAAM,oCAAoC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAC7G,CAAC;iBACH;gBACD,IAAI,CAAC,YAAY,EAAE,CAAC;gBACpB,OAAO,QAAQ,CAAC;aACjB;SACF;aAAM;YACL,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC;YAEzB,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,uBAAA,IAAI,+BAAY,CAAC,CAAC;YAC5C,MAAM,CAAC,GAAG,IAAI,aAAK,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAC3D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC5B,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE;gBAC1B,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CACjC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,uBAAA,IAAI,wBAAK,MAAT,IAAI,EAAM,2BAA2B,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAClF,CAAC;aACH;YACD,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,OAAO,CAAC,CAAC;SACV;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,CAAQ,EAAE,KAAwB;QAChD,IAAI,KAAK,CAAC,KAAK,EAAE;YACf,uBAAA,IAAI,wBAAK,MAAT,IAAI,EAAM,wBAAwB,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YACxD,MAAM,CAAC,GAAG,uBAAA,IAAI,4BAAS,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACzC,IAAI,CAAC,EAAE;gBACL,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gBACnC,IAAI,EAAE,EAAE;oBACN,OAAO,CAAC,EAAE,CAAC,CAAC;iBACb;aACF;iBAAM;gBACL,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACzD,IAAI,EAAE,EAAE;oBACN,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;oBACpC,IAAI,EAAE,EAAE;wBACN,OAAO,CAAC,EAAE,CAAC,CAAC;qBACb;iBACF;qBAAM;oBACL,OAAO,CAAC,IAAI,CAAC,qCAAqC,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;iBACrE;aACF;SACF;aAAM;YACL,MAAM,GAAG,GAAG,EAAE,CAAC;YACf,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,uBAAA,IAAI,4BAAS,EAAE;gBACjC,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE;oBAChB,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;oBACnC,IAAI,EAAE,EAAE;wBACN,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;qBACd;iBACF;aACF;YACD,OAAO,GAAG,CAAC;SACZ;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED;;OAEG;IACH,cAAc,CAAC,EAAc;QAC3B,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,uBAAA,IAAI,4BAAS,EAAE;YACjC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;SACjB;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,gBAAgB,CAAC,OAAe,EAAE,EAAc;QACpD,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3C,MAAM,CAAC,GAAG,IAAI,uBAAU,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;YAEvF,MAAM,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,IAAK,CAAC,CAAC;YACpC,CAAC,CAAC,WAAW,GAAG,KAAK,IAAI,EAAE;gBACzB,YAAY,CAAC,CAAC,CAAC,CAAC;gBAChB,MAAM,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;gBACtB,CAAC,CAAC,KAAK,EAAE,CAAC;gBACV,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC;YACF,CAAC,CAAC,OAAO,EAAE,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IAED,YAAY;QACV,OAAO;YACL,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;gBAC1C,OAAO;oBACL,EAAE,EAAE,CAAC,CAAC,EAAE;oBACR,OAAO,EAAE,CAAC,CAAC,OAAO;oBAClB,UAAU,EAAE,EAAE;iBACf,CAAC;YACJ,CAAC,CAAC;SACH,CAAC;IACJ,CAAC;CAiBF;AAnOD,kCAmOC;;IAdG,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE;QACjC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE;YACjB,CAAC,CAAC,SAAS,EAAE,CAAC;YACd,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACvB,uBAAA,IAAI,wBAAK,MAAT,IAAI,EAAM,kBAAkB,EAAE,CAAC,CAAC,CAAC;YACjC,OAAO,GAAG,IAAI,CAAC;SAChB;KACF;IACD,IAAI,OAAO,EAAE;QACX,IAAI,CAAC,YAAY,EAAE,CAAC;KACrB;IACD,UAAU,CAAC,GAAG,EAAE,CAAC,uBAAA,IAAI,oDAAS,MAAb,IAAI,CAAW,EAAE,IAAK,CAAC,CAAC;AAC3C,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/NoteCollection.d.ts b/packages/system/dist/NoteCollection.d.ts new file mode 100644 index 00000000..47f8c80c --- /dev/null +++ b/packages/system/dist/NoteCollection.d.ts @@ -0,0 +1,91 @@ +import { TaggedRawEvent } from "."; +export interface StoreSnapshot { + data: TSnapshot | undefined; + clear: () => void; + loading: () => boolean; + add: (ev: Readonly | Readonly>) => void; +} +export declare const EmptySnapshot: StoreSnapshot; +export type NoteStoreSnapshotData = Readonly> | Readonly; +export type NoteStoreHook = () => void; +export type NoteStoreHookRelease = () => void; +export type OnEventCallback = (e: Readonly>) => void; +export type OnEventCallbackRelease = () => void; +export type OnEoseCallback = (c: string) => void; +export type OnEoseCallbackRelease = () => void; +/** + * Generic note store interface + */ +export declare abstract class NoteStore { + abstract add(ev: Readonly | Readonly>): void; + abstract clear(): void; + abstract hook(cb: NoteStoreHook): NoteStoreHookRelease; + abstract getSnapshotData(): NoteStoreSnapshotData | undefined; + abstract onEvent(cb: OnEventCallback): OnEventCallbackRelease; + abstract get snapshot(): StoreSnapshot; + abstract get loading(): boolean; + abstract set loading(v: boolean); +} +export declare abstract class HookedNoteStore implements NoteStore { + #private; + get snapshot(): StoreSnapshot; + get loading(): boolean; + set loading(v: boolean); + abstract add(ev: Readonly | Readonly>): void; + abstract clear(): void; + hook(cb: NoteStoreHook): NoteStoreHookRelease; + getSnapshotData(): TSnapshot | undefined; + onEvent(cb: OnEventCallback): OnEventCallbackRelease; + protected abstract takeSnapshot(): TSnapshot | undefined; + protected onChange(changes: Readonly>): void; +} +/** + * A simple flat container of events with no duplicates + */ +export declare class FlatNoteStore extends HookedNoteStore>> { + #private; + add(ev: TaggedRawEvent | Array): void; + clear(): void; + takeSnapshot(): TaggedRawEvent[]; +} +/** + * A note store that holds a single replaceable event for a given user defined key generator function + */ +export declare class KeyedReplaceableNoteStore extends HookedNoteStore>> { + #private; + constructor(fn: (ev: TaggedRawEvent) => string); + add(ev: TaggedRawEvent | Array): void; + clear(): void; + takeSnapshot(): TaggedRawEvent[]; +} +/** + * A note store that holds a single replaceable event + */ +export declare class ReplaceableNoteStore extends HookedNoteStore> { + #private; + add(ev: TaggedRawEvent | Array): void; + clear(): void; + takeSnapshot(): Readonly<{ + relays: string[]; + id: string; + pubkey: string; + created_at: number; + kind: number; + tags: string[][]; + content: string; + sig: string; + }> | undefined; +} +/** + * A note store that holds a single replaceable event per pubkey + */ +export declare class PubkeyReplaceableNoteStore extends KeyedReplaceableNoteStore { + constructor(); +} +/** + * A note store that holds a single replaceable event per "pubkey-dtag" + */ +export declare class ParameterizedReplaceableNoteStore extends KeyedReplaceableNoteStore { + constructor(); +} +//# sourceMappingURL=NoteCollection.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/NoteCollection.d.ts.map b/packages/system/dist/NoteCollection.d.ts.map new file mode 100644 index 00000000..ffe731e5 --- /dev/null +++ b/packages/system/dist/NoteCollection.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"NoteCollection.d.ts","sourceRoot":"","sources":["../src/NoteCollection.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/NoteCollection.js b/packages/system/dist/NoteCollection.js new file mode 100644 index 00000000..70e7376c --- /dev/null +++ b/packages/system/dist/NoteCollection.js @@ -0,0 +1,240 @@ +"use strict"; +var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +}; +var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; +}; +var _HookedNoteStore_instances, _HookedNoteStore_hooks, _HookedNoteStore_eventHooks, _HookedNoteStore_loading, _HookedNoteStore_storeSnapshot, _HookedNoteStore_needsSnapshot, _HookedNoteStore_nextNotifyTimer, _HookedNoteStore_updateSnapshot, _FlatNoteStore_events, _FlatNoteStore_ids, _KeyedReplaceableNoteStore_keyFn, _KeyedReplaceableNoteStore_events, _ReplaceableNoteStore_event; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ParameterizedReplaceableNoteStore = exports.PubkeyReplaceableNoteStore = exports.ReplaceableNoteStore = exports.KeyedReplaceableNoteStore = exports.FlatNoteStore = exports.HookedNoteStore = exports.NoteStore = exports.EmptySnapshot = void 0; +const Util_1 = require("./Util"); +exports.EmptySnapshot = { + data: undefined, + clear: () => { + // empty + }, + loading: () => true, + add: () => { + // empty + }, +}; +/** + * Generic note store interface + */ +class NoteStore { +} +exports.NoteStore = NoteStore; +class HookedNoteStore { + constructor() { + _HookedNoteStore_instances.add(this); + _HookedNoteStore_hooks.set(this, []); + _HookedNoteStore_eventHooks.set(this, []); + _HookedNoteStore_loading.set(this, true); + _HookedNoteStore_storeSnapshot.set(this, { + clear: () => this.clear(), + loading: () => this.loading, + add: ev => this.add(ev), + data: undefined, + }); + _HookedNoteStore_needsSnapshot.set(this, true); + _HookedNoteStore_nextNotifyTimer.set(this, void 0); + } + get snapshot() { + __classPrivateFieldGet(this, _HookedNoteStore_instances, "m", _HookedNoteStore_updateSnapshot).call(this); + return __classPrivateFieldGet(this, _HookedNoteStore_storeSnapshot, "f"); + } + get loading() { + return __classPrivateFieldGet(this, _HookedNoteStore_loading, "f"); + } + set loading(v) { + __classPrivateFieldSet(this, _HookedNoteStore_loading, v, "f"); + this.onChange([]); + } + hook(cb) { + __classPrivateFieldGet(this, _HookedNoteStore_hooks, "f").push(cb); + return () => { + const idx = __classPrivateFieldGet(this, _HookedNoteStore_hooks, "f").findIndex(a => a === cb); + __classPrivateFieldGet(this, _HookedNoteStore_hooks, "f").splice(idx, 1); + }; + } + getSnapshotData() { + __classPrivateFieldGet(this, _HookedNoteStore_instances, "m", _HookedNoteStore_updateSnapshot).call(this); + return __classPrivateFieldGet(this, _HookedNoteStore_storeSnapshot, "f").data; + } + onEvent(cb) { + const existing = __classPrivateFieldGet(this, _HookedNoteStore_eventHooks, "f").find(a => a === cb); + if (!existing) { + __classPrivateFieldGet(this, _HookedNoteStore_eventHooks, "f").push(cb); + return () => { + const idx = __classPrivateFieldGet(this, _HookedNoteStore_eventHooks, "f").findIndex(a => a === cb); + __classPrivateFieldGet(this, _HookedNoteStore_eventHooks, "f").splice(idx, 1); + }; + } + return () => { + //noop + }; + } + onChange(changes) { + __classPrivateFieldSet(this, _HookedNoteStore_needsSnapshot, true, "f"); + if (!__classPrivateFieldGet(this, _HookedNoteStore_nextNotifyTimer, "f")) { + __classPrivateFieldSet(this, _HookedNoteStore_nextNotifyTimer, setTimeout(() => { + __classPrivateFieldSet(this, _HookedNoteStore_nextNotifyTimer, undefined, "f"); + for (const hk of __classPrivateFieldGet(this, _HookedNoteStore_hooks, "f")) { + hk(); + } + }, 500), "f"); + } + if (changes.length > 0) { + for (const hkE of __classPrivateFieldGet(this, _HookedNoteStore_eventHooks, "f")) { + hkE(changes); + } + } + } +} +exports.HookedNoteStore = HookedNoteStore; +_HookedNoteStore_hooks = new WeakMap(), _HookedNoteStore_eventHooks = new WeakMap(), _HookedNoteStore_loading = new WeakMap(), _HookedNoteStore_storeSnapshot = new WeakMap(), _HookedNoteStore_needsSnapshot = new WeakMap(), _HookedNoteStore_nextNotifyTimer = new WeakMap(), _HookedNoteStore_instances = new WeakSet(), _HookedNoteStore_updateSnapshot = function _HookedNoteStore_updateSnapshot() { + if (__classPrivateFieldGet(this, _HookedNoteStore_needsSnapshot, "f")) { + __classPrivateFieldSet(this, _HookedNoteStore_storeSnapshot, { + ...__classPrivateFieldGet(this, _HookedNoteStore_storeSnapshot, "f"), + data: this.takeSnapshot(), + }, "f"); + __classPrivateFieldSet(this, _HookedNoteStore_needsSnapshot, false, "f"); + } +}; +/** + * A simple flat container of events with no duplicates + */ +class FlatNoteStore extends HookedNoteStore { + constructor() { + super(...arguments); + _FlatNoteStore_events.set(this, []); + _FlatNoteStore_ids.set(this, new Set()); + } + add(ev) { + ev = Array.isArray(ev) ? ev : [ev]; + const changes = []; + ev.forEach(a => { + if (!__classPrivateFieldGet(this, _FlatNoteStore_ids, "f").has(a.id)) { + __classPrivateFieldGet(this, _FlatNoteStore_events, "f").push(a); + __classPrivateFieldGet(this, _FlatNoteStore_ids, "f").add(a.id); + changes.push(a); + } + else { + const existing = __classPrivateFieldGet(this, _FlatNoteStore_events, "f").find(b => b.id === a.id); + if (existing) { + existing.relays = (0, Util_1.appendDedupe)(existing.relays, a.relays); + } + } + }); + if (changes.length > 0) { + this.onChange(changes); + } + } + clear() { + __classPrivateFieldSet(this, _FlatNoteStore_events, [], "f"); + __classPrivateFieldGet(this, _FlatNoteStore_ids, "f").clear(); + this.onChange([]); + } + takeSnapshot() { + return [...__classPrivateFieldGet(this, _FlatNoteStore_events, "f")]; + } +} +exports.FlatNoteStore = FlatNoteStore; +_FlatNoteStore_events = new WeakMap(), _FlatNoteStore_ids = new WeakMap(); +/** + * A note store that holds a single replaceable event for a given user defined key generator function + */ +class KeyedReplaceableNoteStore extends HookedNoteStore { + constructor(fn) { + super(); + _KeyedReplaceableNoteStore_keyFn.set(this, void 0); + _KeyedReplaceableNoteStore_events.set(this, new Map()); + __classPrivateFieldSet(this, _KeyedReplaceableNoteStore_keyFn, fn, "f"); + } + add(ev) { + ev = Array.isArray(ev) ? ev : [ev]; + const changes = []; + ev.forEach(a => { + const keyOnEvent = __classPrivateFieldGet(this, _KeyedReplaceableNoteStore_keyFn, "f").call(this, a); + const existingCreated = __classPrivateFieldGet(this, _KeyedReplaceableNoteStore_events, "f").get(keyOnEvent)?.created_at ?? 0; + if (a.created_at > existingCreated) { + __classPrivateFieldGet(this, _KeyedReplaceableNoteStore_events, "f").set(keyOnEvent, a); + changes.push(a); + } + }); + if (changes.length > 0) { + this.onChange(changes); + } + } + clear() { + __classPrivateFieldGet(this, _KeyedReplaceableNoteStore_events, "f").clear(); + this.onChange([]); + } + takeSnapshot() { + return [...__classPrivateFieldGet(this, _KeyedReplaceableNoteStore_events, "f").values()]; + } +} +exports.KeyedReplaceableNoteStore = KeyedReplaceableNoteStore; +_KeyedReplaceableNoteStore_keyFn = new WeakMap(), _KeyedReplaceableNoteStore_events = new WeakMap(); +/** + * A note store that holds a single replaceable event + */ +class ReplaceableNoteStore extends HookedNoteStore { + constructor() { + super(...arguments); + _ReplaceableNoteStore_event.set(this, void 0); + } + add(ev) { + ev = Array.isArray(ev) ? ev : [ev]; + const changes = []; + ev.forEach(a => { + const existingCreated = __classPrivateFieldGet(this, _ReplaceableNoteStore_event, "f")?.created_at ?? 0; + if (a.created_at > existingCreated) { + __classPrivateFieldSet(this, _ReplaceableNoteStore_event, a, "f"); + changes.push(a); + } + }); + if (changes.length > 0) { + this.onChange(changes); + } + } + clear() { + __classPrivateFieldSet(this, _ReplaceableNoteStore_event, undefined, "f"); + this.onChange([]); + } + takeSnapshot() { + if (__classPrivateFieldGet(this, _ReplaceableNoteStore_event, "f")) { + return Object.freeze({ ...__classPrivateFieldGet(this, _ReplaceableNoteStore_event, "f") }); + } + } +} +exports.ReplaceableNoteStore = ReplaceableNoteStore; +_ReplaceableNoteStore_event = new WeakMap(); +/** + * A note store that holds a single replaceable event per pubkey + */ +class PubkeyReplaceableNoteStore extends KeyedReplaceableNoteStore { + constructor() { + super(e => e.pubkey); + } +} +exports.PubkeyReplaceableNoteStore = PubkeyReplaceableNoteStore; +/** + * A note store that holds a single replaceable event per "pubkey-dtag" + */ +class ParameterizedReplaceableNoteStore extends KeyedReplaceableNoteStore { + constructor() { + super(ev => { + const dTag = (0, Util_1.findTag)(ev, "d"); + return `${ev.pubkey}-${dTag}`; + }); + } +} +exports.ParameterizedReplaceableNoteStore = ParameterizedReplaceableNoteStore; +//# sourceMappingURL=NoteCollection.js.map \ No newline at end of file diff --git a/packages/system/dist/NoteCollection.js.map b/packages/system/dist/NoteCollection.js.map new file mode 100644 index 00000000..b9630ff9 --- /dev/null +++ b/packages/system/dist/NoteCollection.js.map @@ -0,0 +1 @@ +{"version":3,"file":"NoteCollection.js","sourceRoot":"","sources":["../src/NoteCollection.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AACA,iCAA+C;AASlC,QAAA,aAAa,GAAG;IAC3B,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,GAAG,EAAE;QACV,QAAQ;IACV,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI;IACnB,GAAG,EAAE,GAAG,EAAE;QACR,QAAQ;IACV,CAAC;CAC8B,CAAC;AAUlC;;GAEG;AACH,MAAsB,SAAS;CAc9B;AAdD,8BAcC;AAED,MAAsB,eAAe;IAArC;;QACE,iCAA+B,EAAE,EAAC;QAClC,sCAAsC,EAAE,EAAC;QACzC,mCAAW,IAAI,EAAC;QAChB,yCAA2C;YACzC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE;YACzB,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO;YAC3B,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,IAAI,EAAE,SAAS;SAChB,EAAC;QACF,yCAAiB,IAAI,EAAC;QACtB,mDAAiD;IA0EnD,CAAC;IAxEC,IAAI,QAAQ;QACV,uBAAA,IAAI,mEAAgB,MAApB,IAAI,CAAkB,CAAC;QACvB,OAAO,uBAAA,IAAI,sCAAe,CAAC;IAC7B,CAAC;IAED,IAAI,OAAO;QACT,OAAO,uBAAA,IAAI,gCAAS,CAAC;IACvB,CAAC;IAED,IAAI,OAAO,CAAC,CAAU;QACpB,uBAAA,IAAI,4BAAY,CAAC,MAAA,CAAC;QAClB,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACpB,CAAC;IAKD,IAAI,CAAC,EAAiB;QACpB,uBAAA,IAAI,8BAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,GAAG,EAAE;YACV,MAAM,GAAG,GAAG,uBAAA,IAAI,8BAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YACjD,uBAAA,IAAI,8BAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC7B,CAAC,CAAC;IACJ,CAAC;IAED,eAAe;QACb,uBAAA,IAAI,mEAAgB,MAApB,IAAI,CAAkB,CAAC;QACvB,OAAO,uBAAA,IAAI,sCAAe,CAAC,IAAI,CAAC;IAClC,CAAC;IAED,OAAO,CAAC,EAAmB;QACzB,MAAM,QAAQ,GAAG,uBAAA,IAAI,mCAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,QAAQ,EAAE;YACb,uBAAA,IAAI,mCAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC1B,OAAO,GAAG,EAAE;gBACV,MAAM,GAAG,GAAG,uBAAA,IAAI,mCAAY,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;gBACtD,uBAAA,IAAI,mCAAY,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YAClC,CAAC,CAAC;SACH;QACD,OAAO,GAAG,EAAE;YACV,MAAM;QACR,CAAC,CAAC;IACJ,CAAC;IAIS,QAAQ,CAAC,OAAwC;QACzD,uBAAA,IAAI,kCAAkB,IAAI,MAAA,CAAC;QAC3B,IAAI,CAAC,uBAAA,IAAI,wCAAiB,EAAE;YAC1B,uBAAA,IAAI,oCAAoB,UAAU,CAAC,GAAG,EAAE;gBACtC,uBAAA,IAAI,oCAAoB,SAAS,MAAA,CAAC;gBAClC,KAAK,MAAM,EAAE,IAAI,uBAAA,IAAI,8BAAO,EAAE;oBAC5B,EAAE,EAAE,CAAC;iBACN;YACH,CAAC,EAAE,GAAG,CAAC,MAAA,CAAC;SACT;QACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE;YACtB,KAAK,MAAM,GAAG,IAAI,uBAAA,IAAI,mCAAY,EAAE;gBAClC,GAAG,CAAC,OAAO,CAAC,CAAC;aACd;SACF;IACH,CAAC;CAWF;AArFD,0CAqFC;;IARG,IAAI,uBAAA,IAAI,sCAAe,EAAE;QACvB,uBAAA,IAAI,kCAAkB;YACpB,GAAG,uBAAA,IAAI,sCAAe;YACtB,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;SAC1B,MAAA,CAAC;QACF,uBAAA,IAAI,kCAAkB,KAAK,MAAA,CAAC;KAC7B;AACH,CAAC;AAGH;;GAEG;AACH,MAAa,aAAc,SAAQ,eAAgD;IAAnF;;QACE,gCAAiC,EAAE,EAAC;QACpC,6BAAkB,IAAI,GAAG,EAAE,EAAC;IAgC9B,CAAC;IA9BC,GAAG,CAAC,EAA0C;QAC5C,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,OAAO,GAA0B,EAAE,CAAC;QAC1C,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;YACb,IAAI,CAAC,uBAAA,IAAI,0BAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;gBACxB,uBAAA,IAAI,6BAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACrB,uBAAA,IAAI,0BAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aACjB;iBAAM;gBACL,MAAM,QAAQ,GAAG,uBAAA,IAAI,6BAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;gBACvD,IAAI,QAAQ,EAAE;oBACZ,QAAQ,CAAC,MAAM,GAAG,IAAA,mBAAY,EAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;iBAC3D;aACF;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE;YACtB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;SACxB;IACH,CAAC;IAED,KAAK;QACH,uBAAA,IAAI,yBAAW,EAAE,MAAA,CAAC;QAClB,uBAAA,IAAI,0BAAK,CAAC,KAAK,EAAE,CAAC;QAClB,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACpB,CAAC;IAED,YAAY;QACV,OAAO,CAAC,GAAG,uBAAA,IAAI,6BAAQ,CAAC,CAAC;IAC3B,CAAC;CACF;AAlCD,sCAkCC;;AAED;;GAEG;AACH,MAAa,yBAA0B,SAAQ,eAAgD;IAI7F,YAAY,EAAkC;QAC5C,KAAK,EAAE,CAAC;QAJV,mDAAuC;QACvC,4CAAuC,IAAI,GAAG,EAAE,EAAC;QAI/C,uBAAA,IAAI,oCAAU,EAAE,MAAA,CAAC;IACnB,CAAC;IAED,GAAG,CAAC,EAA0C;QAC5C,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,OAAO,GAA0B,EAAE,CAAC;QAC1C,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;YACb,MAAM,UAAU,GAAG,uBAAA,IAAI,wCAAO,MAAX,IAAI,EAAQ,CAAC,CAAC,CAAC;YAClC,MAAM,eAAe,GAAG,uBAAA,IAAI,yCAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,UAAU,IAAI,CAAC,CAAC;YACtE,IAAI,CAAC,CAAC,UAAU,GAAG,eAAe,EAAE;gBAClC,uBAAA,IAAI,yCAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;gBAChC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aACjB;QACH,CAAC,CAAC,CAAC;QACH,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE;YACtB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;SACxB;IACH,CAAC;IAED,KAAK;QACH,uBAAA,IAAI,yCAAQ,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACpB,CAAC;IAED,YAAY;QACV,OAAO,CAAC,GAAG,uBAAA,IAAI,yCAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACpC,CAAC;CACF;AAjCD,8DAiCC;;AAED;;GAEG;AACH,MAAa,oBAAqB,SAAQ,eAAyC;IAAnF;;QACE,8CAAwB;IA2B1B,CAAC;IAzBC,GAAG,CAAC,EAA0C;QAC5C,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,OAAO,GAA0B,EAAE,CAAC;QAC1C,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;YACb,MAAM,eAAe,GAAG,uBAAA,IAAI,mCAAO,EAAE,UAAU,IAAI,CAAC,CAAC;YACrD,IAAI,CAAC,CAAC,UAAU,GAAG,eAAe,EAAE;gBAClC,uBAAA,IAAI,+BAAU,CAAC,MAAA,CAAC;gBAChB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aACjB;QACH,CAAC,CAAC,CAAC;QACH,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE;YACtB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;SACxB;IACH,CAAC;IAED,KAAK;QACH,uBAAA,IAAI,+BAAU,SAAS,MAAA,CAAC;QACxB,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACpB,CAAC;IAED,YAAY;QACV,IAAI,uBAAA,IAAI,mCAAO,EAAE;YACf,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,uBAAA,IAAI,mCAAO,EAAE,CAAC,CAAC;SAC1C;IACH,CAAC;CACF;AA5BD,oDA4BC;;AAED;;GAEG;AACH,MAAa,0BAA2B,SAAQ,yBAAyB;IACvE;QACE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACvB,CAAC;CACF;AAJD,gEAIC;AAED;;GAEG;AACH,MAAa,iCAAkC,SAAQ,yBAAyB;IAC9E;QACE,KAAK,CAAC,EAAE,CAAC,EAAE;YACT,MAAM,IAAI,GAAG,IAAA,cAAO,EAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YAC9B,OAAO,GAAG,EAAE,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;QAChC,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAPD,8EAOC"} \ No newline at end of file diff --git a/packages/system/dist/ProfileCache.d.ts b/packages/system/dist/ProfileCache.d.ts new file mode 100644 index 00000000..87f4e53f --- /dev/null +++ b/packages/system/dist/ProfileCache.d.ts @@ -0,0 +1,20 @@ +import { HexKey, SystemInterface, TaggedRawEvent } from "."; +import { CacheStore, MetadataCache } from "./cache"; +export declare class ProfileLoaderService { + #private; + /** + * List of pubkeys to fetch metadata for + */ + WantsMetadata: Set; + constructor(system: SystemInterface, cache: CacheStore); + /** + * Request profile metadata for a set of pubkeys + */ + TrackMetadata(pk: HexKey | Array): void; + /** + * Stop tracking metadata for a set of pubkeys + */ + UntrackMetadata(pk: HexKey | Array): void; + onProfileEvent(e: Readonly): Promise; +} +//# sourceMappingURL=ProfileCache.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/ProfileCache.d.ts.map b/packages/system/dist/ProfileCache.d.ts.map new file mode 100644 index 00000000..acbcf017 --- /dev/null +++ b/packages/system/dist/ProfileCache.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ProfileCache.d.ts","sourceRoot":"","sources":["../src/ProfileCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,MAAM,EAAE,eAAe,EAAE,cAAc,EAA8C,MAAM,GAAG,CAAC;AAEnH,OAAO,EAAE,UAAU,EAAqB,aAAa,EAAE,MAAM,SAAS,CAAC;AAIvE,qBAAa,oBAAoB;;IAI/B;;OAEG;IACH,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAa;gBAI3B,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC;IAMrE;;OAEG;IACH,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAUxC;;OAEG;IACH,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAQpC,cAAc,CAAC,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC;CA4EjD"} \ No newline at end of file diff --git a/packages/system/dist/ProfileCache.js b/packages/system/dist/ProfileCache.js new file mode 100644 index 00000000..98105241 --- /dev/null +++ b/packages/system/dist/ProfileCache.js @@ -0,0 +1,129 @@ +"use strict"; +var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; +}; +var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +var _ProfileLoaderService_instances, _ProfileLoaderService_system, _ProfileLoaderService_cache, _ProfileLoaderService_log, _ProfileLoaderService_FetchMetadata; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ProfileLoaderService = void 0; +const _1 = require("."); +const Const_1 = require("./Const"); +const cache_1 = require("./cache"); +const Util_1 = require("./Util"); +const debug_1 = __importDefault(require("debug")); +class ProfileLoaderService { + constructor(system, cache) { + _ProfileLoaderService_instances.add(this); + _ProfileLoaderService_system.set(this, void 0); + _ProfileLoaderService_cache.set(this, void 0); + /** + * List of pubkeys to fetch metadata for + */ + this.WantsMetadata = new Set(); + _ProfileLoaderService_log.set(this, (0, debug_1.default)("ProfileCache")); + __classPrivateFieldSet(this, _ProfileLoaderService_system, system, "f"); + __classPrivateFieldSet(this, _ProfileLoaderService_cache, cache, "f"); + __classPrivateFieldGet(this, _ProfileLoaderService_instances, "m", _ProfileLoaderService_FetchMetadata).call(this); + } + /** + * Request profile metadata for a set of pubkeys + */ + TrackMetadata(pk) { + const bufferNow = []; + for (const p of Array.isArray(pk) ? pk : [pk]) { + if (p.length > 0 && this.WantsMetadata.add(p)) { + bufferNow.push(p); + } + } + __classPrivateFieldGet(this, _ProfileLoaderService_cache, "f").buffer(bufferNow); + } + /** + * Stop tracking metadata for a set of pubkeys + */ + UntrackMetadata(pk) { + for (const p of Array.isArray(pk) ? pk : [pk]) { + if (p.length > 0) { + this.WantsMetadata.delete(p); + } + } + } + async onProfileEvent(e) { + const profile = (0, cache_1.mapEventToProfile)(e); + if (profile) { + await __classPrivateFieldGet(this, _ProfileLoaderService_cache, "f").update(profile); + } + } +} +exports.ProfileLoaderService = ProfileLoaderService; +_ProfileLoaderService_system = new WeakMap(), _ProfileLoaderService_cache = new WeakMap(), _ProfileLoaderService_log = new WeakMap(), _ProfileLoaderService_instances = new WeakSet(), _ProfileLoaderService_FetchMetadata = async function _ProfileLoaderService_FetchMetadata() { + const missingFromCache = await __classPrivateFieldGet(this, _ProfileLoaderService_cache, "f").buffer([...this.WantsMetadata]); + const expire = (0, Util_1.unixNowMs)() - Const_1.ProfileCacheExpire; + const expired = [...this.WantsMetadata] + .filter(a => !missingFromCache.includes(a)) + .filter(a => (__classPrivateFieldGet(this, _ProfileLoaderService_cache, "f").getFromCache(a)?.loaded ?? 0) < expire); + const missing = new Set([...missingFromCache, ...expired]); + if (missing.size > 0) { + __classPrivateFieldGet(this, _ProfileLoaderService_log, "f").call(this, "Wants profiles: %d missing, %d expired", missingFromCache.length, expired.length); + const sub = new _1.RequestBuilder("profiles"); + sub + .withOptions({ + skipDiff: true, + }) + .withFilter() + .kinds([_1.EventKind.SetMetadata]) + .authors([...missing]); + const newProfiles = new Set(); + const q = __classPrivateFieldGet(this, _ProfileLoaderService_system, "f").Query(_1.PubkeyReplaceableNoteStore, sub); + const feed = q?.feed ?? new _1.PubkeyReplaceableNoteStore(); + // never release this callback, it will stop firing anyway after eose + const releaseOnEvent = feed.onEvent(async (e) => { + for (const pe of e) { + newProfiles.add(pe.id); + await this.onProfileEvent(pe); + } + }); + const results = await new Promise(resolve => { + let timeout = undefined; + const release = feed.hook(() => { + if (!feed.loading) { + clearTimeout(timeout); + resolve(feed.getSnapshotData() ?? []); + __classPrivateFieldGet(this, _ProfileLoaderService_log, "f").call(this, "Profiles finished: %s", sub.id); + release(); + } + }); + timeout = setTimeout(() => { + release(); + resolve(feed.getSnapshotData() ?? []); + __classPrivateFieldGet(this, _ProfileLoaderService_log, "f").call(this, "Profiles timeout: %s", sub.id); + }, 5000); + }); + releaseOnEvent(); + const couldNotFetch = [...missing].filter(a => !results.some(b => b.pubkey === a)); + if (couldNotFetch.length > 0) { + __classPrivateFieldGet(this, _ProfileLoaderService_log, "f").call(this, "No profiles: %o", couldNotFetch); + const empty = couldNotFetch.map(a => __classPrivateFieldGet(this, _ProfileLoaderService_cache, "f").update({ + pubkey: a, + loaded: (0, Util_1.unixNowMs)() - Const_1.ProfileCacheExpire + 5000, + created: 69, + })); + await Promise.all(empty); + } + // When we fetch an expired profile and its the same as what we already have + // onEvent is not fired and the loaded timestamp never gets updated + const expiredSame = results.filter(a => !newProfiles.has(a.id) && expired.includes(a.pubkey)); + await Promise.all(expiredSame.map(v => this.onProfileEvent(v))); + } + setTimeout(() => __classPrivateFieldGet(this, _ProfileLoaderService_instances, "m", _ProfileLoaderService_FetchMetadata).call(this), 500); +}; +//# sourceMappingURL=ProfileCache.js.map \ No newline at end of file diff --git a/packages/system/dist/ProfileCache.js.map b/packages/system/dist/ProfileCache.js.map new file mode 100644 index 00000000..2722da34 --- /dev/null +++ b/packages/system/dist/ProfileCache.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ProfileCache.js","sourceRoot":"","sources":["../src/ProfileCache.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/Query.d.ts b/packages/system/dist/Query.d.ts new file mode 100644 index 00000000..03469640 --- /dev/null +++ b/packages/system/dist/Query.d.ts @@ -0,0 +1,86 @@ +import { Connection, ReqFilter, TaggedRawEvent } from "."; +import { NoteStore } from "./NoteCollection"; +import { BuiltRawReqFilter } from "./RequestBuilder"; +/** + * Tracing for relay query status + */ +declare class QueryTrace { + #private; + readonly relay: string; + readonly filters: Array; + readonly connId: string; + readonly id: string; + readonly start: number; + sent?: number; + eose?: number; + close?: number; + constructor(relay: string, filters: Array, connId: string, fnClose: (id: string) => void, fnProgress: () => void); + sentToRelay(): void; + gotEose(): void; + forceEose(): void; + sendClose(): void; + /** + * Time spent in queue + */ + get queued(): number; + /** + * Total query runtime + */ + get runtime(): number; + /** + * Total time spent waiting for relay to respond + */ + get responseTime(): number; + /** + * If tracing is finished, we got EOSE or timeout + */ + get finished(): boolean; +} +export interface QueryBase { + /** + * Uniquie ID of this query + */ + id: string; + /** + * The query payload (REQ filters) + */ + filters: Array; + /** + * List of relays to send this query to + */ + relays?: Array; +} +/** + * Active or queued query on the system + */ +export declare class Query implements QueryBase { + #private; + /** + * Uniquie ID of this query + */ + id: string; + constructor(id: string, feed: NoteStore, leaveOpen?: boolean); + canRemove(): boolean; + /** + * Recompute the complete set of compressed filters from all query traces + */ + get filters(): ReqFilter[]; + get feed(): NoteStore; + onEvent(sub: string, e: TaggedRawEvent): void; + /** + * This function should be called when this Query object and FeedStore is no longer needed + */ + cancel(): void; + uncancel(): void; + cleanup(): void; + sendToRelay(c: Connection, subq: BuiltRawReqFilter): QueryTrace | undefined; + connectionLost(id: string): void; + sendClose(): void; + eose(sub: string, conn: Readonly): void; + /** + * Get the progress to EOSE, can be used to determine when we should load more content + */ + get progress(): number; +} +export {}; +//# sourceMappingURL=Query.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Query.d.ts.map b/packages/system/dist/Query.d.ts.map new file mode 100644 index 00000000..e3da2f8c --- /dev/null +++ b/packages/system/dist/Query.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Query.d.ts","sourceRoot":"","sources":["../src/Query.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAQ,cAAc,EAAE,MAAM,GAAG,CAAC;AAEhE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD;;GAEG;AACH,cAAM,UAAU;;IAWZ,QAAQ,CAAC,KAAK,EAAE,MAAM;IACtB,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM;IAZzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;gBAMJ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,EACzB,MAAM,EAAE,MAAM,EACvB,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,EAC7B,UAAU,EAAE,MAAM,IAAI;IAQxB,WAAW;IAKX,OAAO;IAKP,SAAS;IAOT,SAAS;IAMT;;OAEG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,OAAO,WAEV;IAED;;OAEG;IACH,IAAI,YAAY,WAEf;IAED;;OAEG;IACH,IAAI,QAAQ,YAEX;CACF;AAED,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAE1B;;OAEG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACxB;AAED;;GAEG;AACH,qBAAa,KAAM,YAAW,SAAS;;IACrC;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;gBA8BC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,OAAO;IAO5D,SAAS;IAIT;;OAEG;IACH,IAAI,OAAO,gBAEV;IAED,IAAI,IAAI,cAEP;IAED,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,cAAc;IAStC;;OAEG;IACH,MAAM;IAIN,QAAQ;IAIR,OAAO;IAIP,WAAW,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,iBAAiB;IAOlD,cAAc,CAAC,EAAE,EAAE,MAAM;IAIzB,SAAS;IAOT,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC;IAQ5C;;OAEG;IACH,IAAI,QAAQ,WAMX;CA6DF"} \ No newline at end of file diff --git a/packages/system/dist/Query.js b/packages/system/dist/Query.js new file mode 100644 index 00000000..78b291fa --- /dev/null +++ b/packages/system/dist/Query.js @@ -0,0 +1,228 @@ +"use strict"; +var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; +}; +var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +var _QueryTrace_wasForceClosed, _QueryTrace_fnClose, _QueryTrace_fnProgress, _Query_instances, _Query_tracing, _Query_leaveOpen, _Query_cancelAt, _Query_checkTrace, _Query_feed, _Query_log, _Query_allFilters, _Query_onProgress, _Query_stopCheckTraces, _Query_checkTraces, _Query_canSendQuery, _Query_sendQueryInternal, _Query_reComputeFilters; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Query = void 0; +const uuid_1 = require("uuid"); +const debug_1 = __importDefault(require("debug")); +const _1 = require("."); +const Util_1 = require("./Util"); +const RequestMerger_1 = require("./RequestMerger"); +const RequestExpander_1 = require("./RequestExpander"); +/** + * Tracing for relay query status + */ +class QueryTrace { + constructor(relay, filters, connId, fnClose, fnProgress) { + this.relay = relay; + this.filters = filters; + this.connId = connId; + _QueryTrace_wasForceClosed.set(this, false); + _QueryTrace_fnClose.set(this, void 0); + _QueryTrace_fnProgress.set(this, void 0); + this.id = (0, uuid_1.v4)(); + this.start = (0, Util_1.unixNowMs)(); + __classPrivateFieldSet(this, _QueryTrace_fnClose, fnClose, "f"); + __classPrivateFieldSet(this, _QueryTrace_fnProgress, fnProgress, "f"); + } + sentToRelay() { + this.sent = (0, Util_1.unixNowMs)(); + __classPrivateFieldGet(this, _QueryTrace_fnProgress, "f").call(this); + } + gotEose() { + this.eose = (0, Util_1.unixNowMs)(); + __classPrivateFieldGet(this, _QueryTrace_fnProgress, "f").call(this); + } + forceEose() { + this.eose = (0, Util_1.unixNowMs)(); + __classPrivateFieldSet(this, _QueryTrace_wasForceClosed, true, "f"); + __classPrivateFieldGet(this, _QueryTrace_fnProgress, "f").call(this); + this.sendClose(); + } + sendClose() { + this.close = (0, Util_1.unixNowMs)(); + __classPrivateFieldGet(this, _QueryTrace_fnClose, "f").call(this, this.id); + __classPrivateFieldGet(this, _QueryTrace_fnProgress, "f").call(this); + } + /** + * Time spent in queue + */ + get queued() { + return (this.sent === undefined ? (0, Util_1.unixNowMs)() : __classPrivateFieldGet(this, _QueryTrace_wasForceClosed, "f") ? (0, Util_1.unwrap)(this.eose) : this.sent) - this.start; + } + /** + * Total query runtime + */ + get runtime() { + return (this.eose === undefined ? (0, Util_1.unixNowMs)() : this.eose) - this.start; + } + /** + * Total time spent waiting for relay to respond + */ + get responseTime() { + return this.finished ? (0, Util_1.unwrap)(this.eose) - (0, Util_1.unwrap)(this.sent) : 0; + } + /** + * If tracing is finished, we got EOSE or timeout + */ + get finished() { + return this.eose !== undefined; + } +} +_QueryTrace_wasForceClosed = new WeakMap(), _QueryTrace_fnClose = new WeakMap(), _QueryTrace_fnProgress = new WeakMap(); +/** + * Active or queued query on the system + */ +class Query { + constructor(id, feed, leaveOpen) { + _Query_instances.add(this); + /** + * Which relays this query has already been executed on + */ + _Query_tracing.set(this, []); + /** + * Leave the query open until its removed + */ + _Query_leaveOpen.set(this, false); + /** + * Time when this query can be removed + */ + _Query_cancelAt.set(this, void 0); + /** + * Timer used to track tracing status + */ + _Query_checkTrace.set(this, void 0); + /** + * Feed object which collects events + */ + _Query_feed.set(this, void 0); + _Query_log.set(this, (0, debug_1.default)("Query")); + _Query_allFilters.set(this, []); + this.id = id; + __classPrivateFieldSet(this, _Query_feed, feed, "f"); + __classPrivateFieldSet(this, _Query_leaveOpen, leaveOpen ?? false, "f"); + __classPrivateFieldGet(this, _Query_instances, "m", _Query_checkTraces).call(this); + } + canRemove() { + return __classPrivateFieldGet(this, _Query_cancelAt, "f") !== undefined && __classPrivateFieldGet(this, _Query_cancelAt, "f") < (0, Util_1.unixNowMs)(); + } + /** + * Recompute the complete set of compressed filters from all query traces + */ + get filters() { + return __classPrivateFieldGet(this, _Query_allFilters, "f"); + } + get feed() { + return __classPrivateFieldGet(this, _Query_feed, "f"); + } + onEvent(sub, e) { + for (const t of __classPrivateFieldGet(this, _Query_tracing, "f")) { + if (t.id === sub) { + this.feed.add(e); + break; + } + } + } + /** + * This function should be called when this Query object and FeedStore is no longer needed + */ + cancel() { + __classPrivateFieldSet(this, _Query_cancelAt, (0, Util_1.unixNowMs)() + 5000, "f"); + } + uncancel() { + __classPrivateFieldSet(this, _Query_cancelAt, undefined, "f"); + } + cleanup() { + __classPrivateFieldGet(this, _Query_instances, "m", _Query_stopCheckTraces).call(this); + } + sendToRelay(c, subq) { + if (!__classPrivateFieldGet(this, _Query_instances, "m", _Query_canSendQuery).call(this, c, subq)) { + return; + } + return __classPrivateFieldGet(this, _Query_instances, "m", _Query_sendQueryInternal).call(this, c, subq); + } + connectionLost(id) { + __classPrivateFieldGet(this, _Query_tracing, "f").filter(a => a.connId == id).forEach(a => a.forceEose()); + } + sendClose() { + for (const qt of __classPrivateFieldGet(this, _Query_tracing, "f")) { + qt.sendClose(); + } + this.cleanup(); + } + eose(sub, conn) { + const qt = __classPrivateFieldGet(this, _Query_tracing, "f").find(a => a.id === sub && a.connId === conn.Id); + qt?.gotEose(); + if (!__classPrivateFieldGet(this, _Query_leaveOpen, "f")) { + qt?.sendClose(); + } + } + /** + * Get the progress to EOSE, can be used to determine when we should load more content + */ + get progress() { + const thisProgress = __classPrivateFieldGet(this, _Query_tracing, "f").reduce((acc, v) => (acc += v.finished ? 1 : 0), 0) / __classPrivateFieldGet(this, _Query_tracing, "f").length; + if (isNaN(thisProgress)) { + return 0; + } + return thisProgress; + } +} +exports.Query = Query; +_Query_tracing = new WeakMap(), _Query_leaveOpen = new WeakMap(), _Query_cancelAt = new WeakMap(), _Query_checkTrace = new WeakMap(), _Query_feed = new WeakMap(), _Query_log = new WeakMap(), _Query_allFilters = new WeakMap(), _Query_instances = new WeakSet(), _Query_onProgress = function _Query_onProgress() { + const isFinished = this.progress === 1; + if (this.feed.loading !== isFinished) { + __classPrivateFieldGet(this, _Query_log, "f").call(this, "%s loading=%s, progress=%d", this.id, this.feed.loading, this.progress); + this.feed.loading = isFinished; + } +}, _Query_stopCheckTraces = function _Query_stopCheckTraces() { + if (__classPrivateFieldGet(this, _Query_checkTrace, "f")) { + clearInterval(__classPrivateFieldGet(this, _Query_checkTrace, "f")); + } +}, _Query_checkTraces = function _Query_checkTraces() { + __classPrivateFieldGet(this, _Query_instances, "m", _Query_stopCheckTraces).call(this); + __classPrivateFieldSet(this, _Query_checkTrace, setInterval(() => { + for (const v of __classPrivateFieldGet(this, _Query_tracing, "f")) { + if (v.runtime > 5000 && !v.finished) { + v.forceEose(); + } + } + }, 500), "f"); +}, _Query_canSendQuery = function _Query_canSendQuery(c, q) { + if (q.relay && q.relay !== c.Address) { + return false; + } + if (!q.relay && c.Ephemeral) { + __classPrivateFieldGet(this, _Query_log, "f").call(this, "Cant send non-specific REQ to ephemeral connection %O %O %O", q, q.relay, c); + return false; + } + if (q.filters.some(a => a.search) && !c.SupportsNip(_1.Nips.Search)) { + __classPrivateFieldGet(this, _Query_log, "f").call(this, "Cant send REQ to non-search relay", c.Address); + return false; + } + return true; +}, _Query_sendQueryInternal = function _Query_sendQueryInternal(c, q) { + const qt = new QueryTrace(c.Address, q.filters, c.Id, x => c.CloseReq(x), () => __classPrivateFieldGet(this, _Query_instances, "m", _Query_onProgress).call(this)); + __classPrivateFieldGet(this, _Query_tracing, "f").push(qt); + __classPrivateFieldGet(this, _Query_instances, "m", _Query_reComputeFilters).call(this); + c.QueueReq(["REQ", qt.id, ...q.filters], () => qt.sentToRelay()); + return qt; +}, _Query_reComputeFilters = function _Query_reComputeFilters() { + console.time("reComputeFilters"); + __classPrivateFieldSet(this, _Query_allFilters, (0, RequestMerger_1.flatMerge)(__classPrivateFieldGet(this, _Query_tracing, "f").flatMap(a => a.filters).flatMap(RequestExpander_1.expandFilter)), "f"); + console.timeEnd("reComputeFilters"); +}; +//# sourceMappingURL=Query.js.map \ No newline at end of file diff --git a/packages/system/dist/Query.js.map b/packages/system/dist/Query.js.map new file mode 100644 index 00000000..56d1e707 --- /dev/null +++ b/packages/system/dist/Query.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Query.js","sourceRoot":"","sources":["../src/Query.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/RelayInfo.d.ts b/packages/system/dist/RelayInfo.d.ts new file mode 100644 index 00000000..0ffe9742 --- /dev/null +++ b/packages/system/dist/RelayInfo.d.ts @@ -0,0 +1,17 @@ +export interface RelayInfo { + name?: string; + description?: string; + pubkey?: string; + contact?: string; + supported_nips?: number[]; + software?: string; + version?: string; + limitation?: { + payment_required: boolean; + max_subscriptions: number; + max_filters: number; + max_event_tags: number; + auth_required: boolean; + }; +} +//# sourceMappingURL=RelayInfo.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/RelayInfo.d.ts.map b/packages/system/dist/RelayInfo.d.ts.map new file mode 100644 index 00000000..c52f7495 --- /dev/null +++ b/packages/system/dist/RelayInfo.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"RelayInfo.d.ts","sourceRoot":"","sources":["../src/RelayInfo.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE;QACX,gBAAgB,EAAE,OAAO,CAAC;QAC1B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,OAAO,CAAC;KACxB,CAAC;CACH"} \ No newline at end of file diff --git a/packages/system/dist/RelayInfo.js b/packages/system/dist/RelayInfo.js new file mode 100644 index 00000000..0591ac98 --- /dev/null +++ b/packages/system/dist/RelayInfo.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=RelayInfo.js.map \ No newline at end of file diff --git a/packages/system/dist/RelayInfo.js.map b/packages/system/dist/RelayInfo.js.map new file mode 100644 index 00000000..4fff9c16 --- /dev/null +++ b/packages/system/dist/RelayInfo.js.map @@ -0,0 +1 @@ +{"version":3,"file":"RelayInfo.js","sourceRoot":"","sources":["../src/RelayInfo.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/packages/system/dist/RequestBuilder.d.ts b/packages/system/dist/RequestBuilder.d.ts new file mode 100644 index 00000000..94ef5649 --- /dev/null +++ b/packages/system/dist/RequestBuilder.d.ts @@ -0,0 +1,93 @@ +import { ReqFilter, u256, HexKey, EventKind } from "."; +import { RelayCache } from "./GossipModel"; +/** + * Which strategy is used when building REQ filters + */ +export declare enum RequestStrategy { + /** + * Use the users default relays to fetch events, + * this is the fallback option when there is no better way to query a given filter set + */ + DefaultRelays = 1, + /** + * Using a cached copy of the authors relay lists NIP-65, split a given set of request filters by pubkey + */ + AuthorsRelays = 2, + /** + * Relay hints are usually provided when using replies + */ + RelayHintedEventIds = 3 +} +/** + * A built REQ filter ready for sending to System + */ +export interface BuiltRawReqFilter { + filters: Array; + relay: string; + strategy: RequestStrategy; +} +export interface RequestBuilderOptions { + leaveOpen?: boolean; + relays?: Array; + /** + * Do not apply diff logic and always use full filters for query + */ + skipDiff?: boolean; +} +/** + * Nostr REQ builder + */ +export declare class RequestBuilder { + #private; + id: string; + constructor(id: string); + get numFilters(): number; + get options(): RequestBuilderOptions | undefined; + withFilter(): RequestFilterBuilder; + withOptions(opt: RequestBuilderOptions): this; + buildRaw(): Array; + build(relays: RelayCache): Array; + /** + * Detects a change in request from a previous set of filters + * @param q All previous filters merged + * @returns + */ + buildDiff(relays: RelayCache, filters: Array): Array; +} +/** + * Builder class for a single request filter + */ +export declare class RequestFilterBuilder { + #private; + get filter(): { + ids?: string[] | undefined; /** + * Relay hints are usually provided when using replies + */ + authors?: string[] | undefined; + kinds?: number[] | undefined; + "#e"?: string[] | undefined; + "#p"?: string[] | undefined; + "#t"?: string[] | undefined; + "#d"?: string[] | undefined; + "#r"?: string[] | undefined; + search?: string | undefined; + since?: number | undefined; + until?: number | undefined; + limit?: number | undefined; + }; + get relayHints(): Map; + ids(ids: Array): this; + id(id: u256, relay?: string): this; + authors(authors?: Array): this; + kinds(kinds?: Array): this; + since(since?: number): this; + until(until?: number): this; + limit(limit?: number): this; + tag(key: "e" | "p" | "d" | "t" | "r", value?: Array): this; + search(keyword?: string): this; + /** + * Build/expand this filter into a set of relay specific queries + */ + build(relays: RelayCache, id: string): Array; +} +//# sourceMappingURL=RequestBuilder.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/RequestBuilder.d.ts.map b/packages/system/dist/RequestBuilder.d.ts.map new file mode 100644 index 00000000..5b037973 --- /dev/null +++ b/packages/system/dist/RequestBuilder.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"RequestBuilder.d.ts","sourceRoot":"","sources":["../src/RequestBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC;AAGvD,OAAO,EAAE,UAAU,EAA6C,MAAM,eAAe,CAAC;AAGtF;;GAEG;AACH,oBAAY,eAAe;IACzB;;;OAGG;IACH,aAAa,IAAI;IAEjB;;OAEG;IACH,aAAa,IAAI;IAEjB;;OAEG;IACH,mBAAmB,IAAI;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,cAAc;;IACzB,EAAE,EAAE,MAAM,CAAC;gBAIC,EAAE,EAAE,MAAM;IAKtB,IAAI,UAAU,WAEb;IAED,IAAI,OAAO,sCAEV;IAED,UAAU;IAMV,WAAW,CAAC,GAAG,EAAE,qBAAqB;IAQtC,QAAQ,IAAI,KAAK,CAAC,SAAS,CAAC;IAI5B,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,KAAK,CAAC,iBAAiB,CAAC;IAKnD;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,iBAAiB,CAAC;CAyCnF;AAED;;GAEG;AACH,qBAAa,oBAAoB;;IAI/B,IAAI,MAAM;oCA3HV;;WAEG;;;;;;;;;;;;MA2HF;IAED,IAAI,UAAU,0BAEb;IAED,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC;IAKpB,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,MAAM;IAO3B,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC;IAM/B,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAM9B,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM;IAMpB,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM;IAMpB,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM;IAMpB,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC;IAM3D,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM;IAMvB;;OAEG;IACH,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,KAAK,CAAC,iBAAiB,CAAC;CAuChE"} \ No newline at end of file diff --git a/packages/system/dist/RequestBuilder.js b/packages/system/dist/RequestBuilder.js new file mode 100644 index 00000000..b6cdc0b7 --- /dev/null +++ b/packages/system/dist/RequestBuilder.js @@ -0,0 +1,225 @@ +"use strict"; +var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; +}; +var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +}; +var _RequestBuilder_instances, _RequestBuilder_builders, _RequestBuilder_options, _RequestBuilder_groupByRelay, _RequestFilterBuilder_filter, _RequestFilterBuilder_relayHints; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RequestFilterBuilder = exports.RequestBuilder = exports.RequestStrategy = void 0; +const Util_1 = require("./Util"); +const RequestSplitter_1 = require("./RequestSplitter"); +const GossipModel_1 = require("./GossipModel"); +const RequestMerger_1 = require("./RequestMerger"); +/** + * Which strategy is used when building REQ filters + */ +var RequestStrategy; +(function (RequestStrategy) { + /** + * Use the users default relays to fetch events, + * this is the fallback option when there is no better way to query a given filter set + */ + RequestStrategy[RequestStrategy["DefaultRelays"] = 1] = "DefaultRelays"; + /** + * Using a cached copy of the authors relay lists NIP-65, split a given set of request filters by pubkey + */ + RequestStrategy[RequestStrategy["AuthorsRelays"] = 2] = "AuthorsRelays"; + /** + * Relay hints are usually provided when using replies + */ + RequestStrategy[RequestStrategy["RelayHintedEventIds"] = 3] = "RelayHintedEventIds"; +})(RequestStrategy = exports.RequestStrategy || (exports.RequestStrategy = {})); +/** + * Nostr REQ builder + */ +class RequestBuilder { + constructor(id) { + _RequestBuilder_instances.add(this); + _RequestBuilder_builders.set(this, void 0); + _RequestBuilder_options.set(this, void 0); + this.id = id; + __classPrivateFieldSet(this, _RequestBuilder_builders, [], "f"); + } + get numFilters() { + return __classPrivateFieldGet(this, _RequestBuilder_builders, "f").length; + } + get options() { + return __classPrivateFieldGet(this, _RequestBuilder_options, "f"); + } + withFilter() { + const ret = new RequestFilterBuilder(); + __classPrivateFieldGet(this, _RequestBuilder_builders, "f").push(ret); + return ret; + } + withOptions(opt) { + __classPrivateFieldSet(this, _RequestBuilder_options, { + ...__classPrivateFieldGet(this, _RequestBuilder_options, "f"), + ...opt, + }, "f"); + return this; + } + buildRaw() { + return __classPrivateFieldGet(this, _RequestBuilder_builders, "f").map(f => f.filter); + } + build(relays) { + const expanded = __classPrivateFieldGet(this, _RequestBuilder_builders, "f").flatMap(a => a.build(relays, this.id)); + return __classPrivateFieldGet(this, _RequestBuilder_instances, "m", _RequestBuilder_groupByRelay).call(this, expanded); + } + /** + * Detects a change in request from a previous set of filters + * @param q All previous filters merged + * @returns + */ + buildDiff(relays, filters) { + const next = this.buildRaw(); + const diff = (0, RequestSplitter_1.diffFilters)(filters, next); + if (diff.changed) { + return (0, GossipModel_1.splitAllByWriteRelays)(relays, diff.added).map(a => { + return { + strategy: RequestStrategy.AuthorsRelays, + filters: a.filters, + relay: a.relay, + }; + }); + } + return []; + } +} +exports.RequestBuilder = RequestBuilder; +_RequestBuilder_builders = new WeakMap(), _RequestBuilder_options = new WeakMap(), _RequestBuilder_instances = new WeakSet(), _RequestBuilder_groupByRelay = function _RequestBuilder_groupByRelay(expanded) { + const relayMerged = expanded.reduce((acc, v) => { + const existing = acc.get(v.relay); + if (existing) { + existing.push(v); + } + else { + acc.set(v.relay, [v]); + } + return acc; + }, new Map()); + const filtersSquashed = [...relayMerged.values()].map(a => { + return { + filters: (0, RequestMerger_1.mergeSimilar)(a.flatMap(b => b.filters)), + relay: a[0].relay, + strategy: a[0].strategy, + }; + }); + return filtersSquashed; +}; +/** + * Builder class for a single request filter + */ +class RequestFilterBuilder { + constructor() { + _RequestFilterBuilder_filter.set(this, {}); + _RequestFilterBuilder_relayHints.set(this, new Map()); + } + get filter() { + return { ...__classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f") }; + } + get relayHints() { + return new Map(__classPrivateFieldGet(this, _RequestFilterBuilder_relayHints, "f")); + } + ids(ids) { + __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").ids = (0, Util_1.appendDedupe)(__classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").ids, ids); + return this; + } + id(id, relay) { + if (relay) { + __classPrivateFieldGet(this, _RequestFilterBuilder_relayHints, "f").set(id, (0, Util_1.appendDedupe)(__classPrivateFieldGet(this, _RequestFilterBuilder_relayHints, "f").get(id), [relay])); + } + return this.ids([id]); + } + authors(authors) { + if (!authors) + return this; + __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").authors = (0, Util_1.appendDedupe)(__classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").authors, authors); + return this; + } + kinds(kinds) { + if (!kinds) + return this; + __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").kinds = (0, Util_1.appendDedupe)(__classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").kinds, kinds); + return this; + } + since(since) { + if (!since) + return this; + __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").since = since; + return this; + } + until(until) { + if (!until) + return this; + __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").until = until; + return this; + } + limit(limit) { + if (!limit) + return this; + __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").limit = limit; + return this; + } + tag(key, value) { + if (!value) + return this; + __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f")[`#${key}`] = value; + return this; + } + search(keyword) { + if (!keyword) + return this; + __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").search = keyword; + return this; + } + /** + * Build/expand this filter into a set of relay specific queries + */ + build(relays, id) { + // when querying for specific event ids with relay hints + // take the first approach which is to split the filter by relay + if (__classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").ids && __classPrivateFieldGet(this, _RequestFilterBuilder_relayHints, "f").size > 0) { + const relays = (0, Util_1.dedupe)([...__classPrivateFieldGet(this, _RequestFilterBuilder_relayHints, "f").values()].flat()); + return relays.map(r => { + return { + filters: [ + { + ...__classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f"), + ids: [...__classPrivateFieldGet(this, _RequestFilterBuilder_relayHints, "f").entries()].filter(([, v]) => v.includes(r)).map(([k]) => k), + }, + ], + relay: r, + strategy: RequestStrategy.RelayHintedEventIds, + }; + }); + } + // If any authors are set use the gossip model to fetch data for each author + if (__classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").authors) { + const split = (0, GossipModel_1.splitByWriteRelays)(relays, __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f")); + return split.map(a => { + return { + filters: [a.filter], + relay: a.relay, + strategy: RequestStrategy.AuthorsRelays, + }; + }); + } + return [ + { + filters: [this.filter], + relay: "", + strategy: RequestStrategy.DefaultRelays, + }, + ]; + } +} +exports.RequestFilterBuilder = RequestFilterBuilder; +_RequestFilterBuilder_filter = new WeakMap(), _RequestFilterBuilder_relayHints = new WeakMap(); +//# sourceMappingURL=RequestBuilder.js.map \ No newline at end of file diff --git a/packages/system/dist/RequestBuilder.js.map b/packages/system/dist/RequestBuilder.js.map new file mode 100644 index 00000000..74865223 --- /dev/null +++ b/packages/system/dist/RequestBuilder.js.map @@ -0,0 +1 @@ +{"version":3,"file":"RequestBuilder.js","sourceRoot":"","sources":["../src/RequestBuilder.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/RequestExpander.d.ts b/packages/system/dist/RequestExpander.d.ts new file mode 100644 index 00000000..83b92600 --- /dev/null +++ b/packages/system/dist/RequestExpander.d.ts @@ -0,0 +1,20 @@ +import { u256, ReqFilter } from "./Nostr"; +export interface FlatReqFilter { + ids?: u256; + authors?: u256; + kinds?: number; + "#e"?: u256; + "#p"?: u256; + "#t"?: string; + "#d"?: string; + "#r"?: string; + search?: string; + since?: number; + until?: number; + limit?: number; +} +/** + * Expand a filter into its most fine grained form + */ +export declare function expandFilter(f: ReqFilter): Array; +//# sourceMappingURL=RequestExpander.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/RequestExpander.d.ts.map b/packages/system/dist/RequestExpander.d.ts.map new file mode 100644 index 00000000..2f0419f8 --- /dev/null +++ b/packages/system/dist/RequestExpander.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"RequestExpander.d.ts","sourceRoot":"","sources":["../src/RequestExpander.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC,aAAa,CAAC,CA2B/D"} \ No newline at end of file diff --git a/packages/system/dist/RequestExpander.js b/packages/system/dist/RequestExpander.js new file mode 100644 index 00000000..5cab53de --- /dev/null +++ b/packages/system/dist/RequestExpander.js @@ -0,0 +1,31 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.expandFilter = void 0; +/** + * Expand a filter into its most fine grained form + */ +function expandFilter(f) { + const ret = []; + const src = Object.entries(f); + const keys = src.filter(([, v]) => Array.isArray(v)).map(a => a[0]); + const props = src.filter(([, v]) => !Array.isArray(v)); + function generateCombinations(index, currentCombination) { + if (index === keys.length) { + ret.push(currentCombination); + return; + } + const key = keys[index]; + const values = f[key]; + for (let i = 0; i < values.length; i++) { + const value = values[i]; + const updatedCombination = { ...currentCombination, [key]: value }; + generateCombinations(index + 1, updatedCombination); + } + } + generateCombinations(0, { + ...Object.fromEntries(props), + }); + return ret; +} +exports.expandFilter = expandFilter; +//# sourceMappingURL=RequestExpander.js.map \ No newline at end of file diff --git a/packages/system/dist/RequestExpander.js.map b/packages/system/dist/RequestExpander.js.map new file mode 100644 index 00000000..a347f38d --- /dev/null +++ b/packages/system/dist/RequestExpander.js.map @@ -0,0 +1 @@ +{"version":3,"file":"RequestExpander.js","sourceRoot":"","sources":["../src/RequestExpander.ts"],"names":[],"mappings":";;;AAiBA;;GAEG;AACH,SAAgB,YAAY,CAAC,CAAY;IACvC,MAAM,GAAG,GAAyB,EAAE,CAAC;IACrC,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvD,SAAS,oBAAoB,CAAC,KAAa,EAAE,kBAAiC;QAC5E,IAAI,KAAK,KAAK,IAAI,CAAC,MAAM,EAAE;YACzB,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;YAC7B,OAAO;SACR;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,MAAM,MAAM,GAAI,CAA4C,CAAC,GAAG,CAAC,CAAC;QAElE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YACtC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,kBAAkB,GAAG,EAAE,GAAG,kBAAkB,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;YACnE,oBAAoB,CAAC,KAAK,GAAG,CAAC,EAAE,kBAAkB,CAAC,CAAC;SACrD;IACH,CAAC;IAED,oBAAoB,CAAC,CAAC,EAAE;QACtB,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC;KAC7B,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC;AA3BD,oCA2BC"} \ No newline at end of file diff --git a/packages/system/dist/RequestMatcher.d.ts b/packages/system/dist/RequestMatcher.d.ts new file mode 100644 index 00000000..82948dca --- /dev/null +++ b/packages/system/dist/RequestMatcher.d.ts @@ -0,0 +1,3 @@ +import { NostrEvent, ReqFilter } from "./Nostr"; +export declare function eventMatchesFilter(ev: NostrEvent, filter: ReqFilter): boolean; +//# sourceMappingURL=RequestMatcher.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/RequestMatcher.d.ts.map b/packages/system/dist/RequestMatcher.d.ts.map new file mode 100644 index 00000000..e0dd95f6 --- /dev/null +++ b/packages/system/dist/RequestMatcher.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"RequestMatcher.d.ts","sourceRoot":"","sources":["../src/RequestMatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEhD,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,WAiBnE"} \ No newline at end of file diff --git a/packages/system/dist/RequestMatcher.js b/packages/system/dist/RequestMatcher.js new file mode 100644 index 00000000..81583f43 --- /dev/null +++ b/packages/system/dist/RequestMatcher.js @@ -0,0 +1,23 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.eventMatchesFilter = void 0; +function eventMatchesFilter(ev, filter) { + if (!(filter.ids?.includes(ev.id) ?? false)) { + return false; + } + if (!(filter.authors?.includes(ev.pubkey) ?? false)) { + return false; + } + if (!(filter.kinds?.includes(ev.kind) ?? false)) { + return false; + } + if (filter.since && ev.created_at < filter.since) { + return false; + } + if (filter.until && ev.created_at > filter.until) { + return false; + } + return true; +} +exports.eventMatchesFilter = eventMatchesFilter; +//# sourceMappingURL=RequestMatcher.js.map \ No newline at end of file diff --git a/packages/system/dist/RequestMatcher.js.map b/packages/system/dist/RequestMatcher.js.map new file mode 100644 index 00000000..dcb35ecd --- /dev/null +++ b/packages/system/dist/RequestMatcher.js.map @@ -0,0 +1 @@ +{"version":3,"file":"RequestMatcher.js","sourceRoot":"","sources":["../src/RequestMatcher.ts"],"names":[],"mappings":";;;AAEA,SAAgB,kBAAkB,CAAC,EAAc,EAAE,MAAiB;IAClE,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,EAAE;QAC3C,OAAO,KAAK,CAAC;KACd;IACD,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE;QACnD,OAAO,KAAK,CAAC;KACd;IACD,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,EAAE;QAC/C,OAAO,KAAK,CAAC;KACd;IACD,IAAI,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,UAAU,GAAG,MAAM,CAAC,KAAK,EAAE;QAChD,OAAO,KAAK,CAAC;KACd;IACD,IAAI,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,UAAU,GAAG,MAAM,CAAC,KAAK,EAAE;QAChD,OAAO,KAAK,CAAC;KACd;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAjBD,gDAiBC"} \ No newline at end of file diff --git a/packages/system/dist/RequestMerger.d.ts b/packages/system/dist/RequestMerger.d.ts new file mode 100644 index 00000000..47d16a22 --- /dev/null +++ b/packages/system/dist/RequestMerger.d.ts @@ -0,0 +1,24 @@ +import { ReqFilter } from "."; +import { FlatReqFilter } from "./RequestExpander"; +export declare function canMergeFilters(a: FlatReqFilter | ReqFilter, b: FlatReqFilter | ReqFilter): boolean; +export declare function mergeSimilar(filters: Array): Array; +/** + * Simply flatten all filters into one + * @param filters + * @returns + */ +export declare function simpleMerge(filters: Array): ReqFilter; +/** + * Check if a filter includes another filter, as in the bigger filter will include the same results as the samller filter + * @param bigger + * @param smaller + * @returns + */ +export declare function filterIncludes(bigger: ReqFilter, smaller: ReqFilter): boolean; +/** + * Merge expanded flat filters into combined concise filters + * @param all + * @returns + */ +export declare function flatMerge(all: Array): Array; +//# sourceMappingURL=RequestMerger.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/RequestMerger.d.ts.map b/packages/system/dist/RequestMerger.d.ts.map new file mode 100644 index 00000000..bdc4e93b --- /dev/null +++ b/packages/system/dist/RequestMerger.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"RequestMerger.d.ts","sourceRoot":"","sources":["../src/RequestMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAQlD,wBAAgB,eAAe,CAAC,CAAC,EAAE,aAAa,GAAG,SAAS,EAAE,CAAC,EAAE,aAAa,GAAG,SAAS,GAAG,OAAO,CAWnG;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAmBxE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,aAkBpD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,WAoBnE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAsDrE"} \ No newline at end of file diff --git a/packages/system/dist/RequestMerger.js b/packages/system/dist/RequestMerger.js new file mode 100644 index 00000000..15dfd91c --- /dev/null +++ b/packages/system/dist/RequestMerger.js @@ -0,0 +1,150 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.flatMerge = exports.filterIncludes = exports.simpleMerge = exports.mergeSimilar = exports.canMergeFilters = void 0; +const Util_1 = require("./Util"); +/** + * Keys which can change the entire meaning of the filter outside the array types + */ +const DiscriminatorKeys = ["since", "until", "limit", "search"]; +function canMergeFilters(a, b) { + const aObj = a; + const bObj = b; + for (const key of DiscriminatorKeys) { + if (key in aObj || key in bObj) { + if (aObj[key] !== bObj[key]) { + return false; + } + } + } + return (0, Util_1.distance)(aObj, bObj) <= 1; +} +exports.canMergeFilters = canMergeFilters; +function mergeSimilar(filters) { + console.time("mergeSimilar"); + const ret = []; + const fCopy = [...filters]; + while (fCopy.length > 0) { + const current = fCopy.shift(); + const mergeSet = [current]; + for (let i = 0; i < fCopy.length; i++) { + const f = fCopy[i]; + if (mergeSet.every(v => canMergeFilters(v, f))) { + mergeSet.push(fCopy.splice(i, 1)[0]); + i--; + } + } + ret.push(simpleMerge(mergeSet)); + } + console.timeEnd("mergeSimilar"); + return ret; +} +exports.mergeSimilar = mergeSimilar; +/** + * Simply flatten all filters into one + * @param filters + * @returns + */ +function simpleMerge(filters) { + const result = {}; + filters.forEach(filter => { + Object.entries(filter).forEach(([key, value]) => { + if (Array.isArray(value)) { + if (result[key] === undefined) { + result[key] = [...value]; + } + else { + result[key] = [...new Set([...result[key], ...value])]; + } + } + else { + result[key] = value; + } + }); + }); + return result; +} +exports.simpleMerge = simpleMerge; +/** + * Check if a filter includes another filter, as in the bigger filter will include the same results as the samller filter + * @param bigger + * @param smaller + * @returns + */ +function filterIncludes(bigger, smaller) { + const outside = bigger; + for (const [k, v] of Object.entries(smaller)) { + if (outside[k] === undefined) { + return false; + } + if (Array.isArray(v) && v.some(a => !outside[k].includes(a))) { + return false; + } + if (typeof v === "number") { + if (k === "since" && outside[k] > v) { + return false; + } + if (k === "until" && outside[k] < v) { + return false; + } + // limit cannot be checked and is ignored + } + } + return true; +} +exports.filterIncludes = filterIncludes; +/** + * Merge expanded flat filters into combined concise filters + * @param all + * @returns + */ +function flatMerge(all) { + console.time("flatMerge"); + let ret = []; + // to compute filters which can be merged we need to calucate the distance change between each filter + // then we can merge filters which are exactly 1 change diff from each other + function mergeFiltersInSet(filters) { + const result = {}; + filters.forEach(f => { + const filter = f; + Object.entries(filter).forEach(([key, value]) => { + if (!DiscriminatorKeys.includes(key)) { + if (result[key] === undefined) { + result[key] = [value]; + } + else { + result[key] = [...new Set([...result[key], value])]; + } + } + else { + result[key] = value; + } + }); + }); + return result; + } + // reducer, kinda verbose + while (all.length > 0) { + const currentFilter = all.shift(); + const mergeSet = [currentFilter]; + for (let i = 0; i < all.length; i++) { + const f = all[i]; + if (mergeSet.every(a => canMergeFilters(a, f))) { + mergeSet.push(all.splice(i, 1)[0]); + i--; + } + } + ret.push(mergeFiltersInSet(mergeSet)); + } + while (true) { + const n = mergeSimilar([...ret]); + if (n.length === ret.length) { + break; + } + ret = n; + } + console.timeEnd("flatMerge"); + console.debug(ret); + return ret; +} +exports.flatMerge = flatMerge; +//# sourceMappingURL=RequestMerger.js.map \ No newline at end of file diff --git a/packages/system/dist/RequestMerger.js.map b/packages/system/dist/RequestMerger.js.map new file mode 100644 index 00000000..80069d59 --- /dev/null +++ b/packages/system/dist/RequestMerger.js.map @@ -0,0 +1 @@ +{"version":3,"file":"RequestMerger.js","sourceRoot":"","sources":["../src/RequestMerger.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/RequestSplitter.d.ts b/packages/system/dist/RequestSplitter.d.ts new file mode 100644 index 00000000..85e70230 --- /dev/null +++ b/packages/system/dist/RequestSplitter.d.ts @@ -0,0 +1,7 @@ +import { ReqFilter } from "."; +export declare function diffFilters(prev: Array, next: Array): { + added: ReqFilter[]; + removed: ReqFilter[]; + changed: boolean; +}; +//# sourceMappingURL=RequestSplitter.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/RequestSplitter.d.ts.map b/packages/system/dist/RequestSplitter.d.ts.map new file mode 100644 index 00000000..ad0a50ef --- /dev/null +++ b/packages/system/dist/RequestSplitter.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"RequestSplitter.d.ts","sourceRoot":"","sources":["../src/RequestSplitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC;AAK9B,wBAAgB,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;;;;EAYzE"} \ No newline at end of file diff --git a/packages/system/dist/RequestSplitter.js b/packages/system/dist/RequestSplitter.js new file mode 100644 index 00000000..13488b3b --- /dev/null +++ b/packages/system/dist/RequestSplitter.js @@ -0,0 +1,19 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.diffFilters = void 0; +const Util_1 = require("./Util"); +const RequestExpander_1 = require("./RequestExpander"); +const RequestMerger_1 = require("./RequestMerger"); +function diffFilters(prev, next) { + const prevExpanded = prev.flatMap(RequestExpander_1.expandFilter); + const nextExpanded = next.flatMap(RequestExpander_1.expandFilter); + const added = (0, RequestMerger_1.flatMerge)(nextExpanded.filter(a => !prevExpanded.some(b => (0, Util_1.deepEqual)(a, b)))); + const removed = (0, RequestMerger_1.flatMerge)(prevExpanded.filter(a => !nextExpanded.some(b => (0, Util_1.deepEqual)(a, b)))); + return { + added, + removed, + changed: added.length > 0 || removed.length > 0, + }; +} +exports.diffFilters = diffFilters; +//# sourceMappingURL=RequestSplitter.js.map \ No newline at end of file diff --git a/packages/system/dist/RequestSplitter.js.map b/packages/system/dist/RequestSplitter.js.map new file mode 100644 index 00000000..71499c0e --- /dev/null +++ b/packages/system/dist/RequestSplitter.js.map @@ -0,0 +1 @@ +{"version":3,"file":"RequestSplitter.js","sourceRoot":"","sources":["../src/RequestSplitter.ts"],"names":[],"mappings":";;;AACA,iCAAmC;AACnC,uDAAiD;AACjD,mDAA4C;AAE5C,SAAgB,WAAW,CAAC,IAAsB,EAAE,IAAsB;IACxE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,8BAAY,CAAC,CAAC;IAChD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,8BAAY,CAAC,CAAC;IAEhD,MAAM,KAAK,GAAG,IAAA,yBAAS,EAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAA,gBAAS,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5F,MAAM,OAAO,GAAG,IAAA,yBAAS,EAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAA,gBAAS,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE9F,OAAO;QACL,KAAK;QACL,OAAO;QACP,OAAO,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;KAChD,CAAC;AACJ,CAAC;AAZD,kCAYC"} \ No newline at end of file diff --git a/packages/system/dist/SystemWorker.d.ts b/packages/system/dist/SystemWorker.d.ts new file mode 100644 index 00000000..f9224e50 --- /dev/null +++ b/packages/system/dist/SystemWorker.d.ts @@ -0,0 +1,22 @@ +import { SystemSnapshot, SystemInterface } from "."; +import { AuthHandler, ConnectionStateSnapshot, RelaySettings } from "./Connection"; +import ExternalStore from "./ExternalStore"; +import { NostrEvent } from "./Nostr"; +import { NoteStore } from "./NoteCollection"; +import { Query } from "./Query"; +import { RequestBuilder } from "./RequestBuilder"; +export declare class SystemWorker extends ExternalStore implements SystemInterface { + #private; + constructor(); + HandleAuth?: AuthHandler; + get Sockets(): ConnectionStateSnapshot[]; + Query(type: new () => T, req: RequestBuilder | null): Query | undefined; + CancelQuery(sub: string): void; + GetQuery(sub: string): Query | undefined; + ConnectToRelay(address: string, options: RelaySettings): Promise; + DisconnectRelay(address: string): void; + BroadcastEvent(ev: NostrEvent): void; + WriteOnceToRelay(relay: string, ev: NostrEvent): Promise; + takeSnapshot(): SystemSnapshot; +} +//# sourceMappingURL=SystemWorker.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/SystemWorker.d.ts.map b/packages/system/dist/SystemWorker.d.ts.map new file mode 100644 index 00000000..d6581294 --- /dev/null +++ b/packages/system/dist/SystemWorker.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"SystemWorker.d.ts","sourceRoot":"","sources":["../src/SystemWorker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,GAAG,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,qBAAa,YAAa,SAAQ,aAAa,CAAC,cAAc,CAAE,YAAW,eAAe;;;IAcxF,UAAU,CAAC,EAAE,WAAW,CAAC;IAEzB,IAAI,OAAO,IAAI,uBAAuB,EAAE,CAEvC;IAED,KAAK,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,EAAE,UAAU,CAAC,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,GAAG,KAAK,GAAG,SAAS;IAI5F,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI9B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAIxC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAItC,cAAc,CAAC,EAAE,EAAE,UAAU,GAAG,IAAI;IAIpC,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9D,YAAY,IAAI,cAAc;CAO/B"} \ No newline at end of file diff --git a/packages/system/dist/SystemWorker.js b/packages/system/dist/SystemWorker.js new file mode 100644 index 00000000..703bd8e1 --- /dev/null +++ b/packages/system/dist/SystemWorker.js @@ -0,0 +1,66 @@ +"use strict"; +var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { + if (kind === "m") throw new TypeError("Private method is not writable"); + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); + return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; +}; +var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +var _SystemWorker_instances, _SystemWorker_port, _SystemWorker_onMessage; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SystemWorker = void 0; +const ExternalStore_1 = __importDefault(require("./ExternalStore")); +class SystemWorker extends ExternalStore_1.default { + constructor() { + super(); + _SystemWorker_instances.add(this); + _SystemWorker_port.set(this, void 0); + if ("SharedWorker" in window) { + const worker = new SharedWorker("/system.js"); + __classPrivateFieldSet(this, _SystemWorker_port, worker.port, "f"); + __classPrivateFieldGet(this, _SystemWorker_port, "f").onmessage = m => __classPrivateFieldGet(this, _SystemWorker_instances, "m", _SystemWorker_onMessage).call(this, m); + } + else { + throw new Error("SharedWorker is not supported"); + } + } + get Sockets() { + throw new Error("Method not implemented."); + } + Query(type, req) { + throw new Error("Method not implemented."); + } + CancelQuery(sub) { + throw new Error("Method not implemented."); + } + GetQuery(sub) { + throw new Error("Method not implemented."); + } + ConnectToRelay(address, options) { + throw new Error("Method not implemented."); + } + DisconnectRelay(address) { + throw new Error("Method not implemented."); + } + BroadcastEvent(ev) { + throw new Error("Method not implemented."); + } + WriteOnceToRelay(relay, ev) { + throw new Error("Method not implemented."); + } + takeSnapshot() { + throw new Error("Method not implemented."); + } +} +exports.SystemWorker = SystemWorker; +_SystemWorker_port = new WeakMap(), _SystemWorker_instances = new WeakSet(), _SystemWorker_onMessage = function _SystemWorker_onMessage(e) { + console.debug(e); +}; +//# sourceMappingURL=SystemWorker.js.map \ No newline at end of file diff --git a/packages/system/dist/SystemWorker.js.map b/packages/system/dist/SystemWorker.js.map new file mode 100644 index 00000000..9b87a7f2 --- /dev/null +++ b/packages/system/dist/SystemWorker.js.map @@ -0,0 +1 @@ +{"version":3,"file":"SystemWorker.js","sourceRoot":"","sources":["../src/SystemWorker.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAEA,oEAA4C;AAM5C,MAAa,YAAa,SAAQ,uBAA6B;IAG7D;QACE,KAAK,EAAE,CAAC;;QAHV,qCAAmB;QAIjB,IAAI,cAAc,IAAI,MAAM,EAAE;YAC5B,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,YAAY,CAAC,CAAC;YAC9C,uBAAA,IAAI,sBAAS,MAAM,CAAC,IAAI,MAAA,CAAC;YACzB,uBAAA,IAAI,0BAAM,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,CAAC,uBAAA,IAAI,wDAAW,MAAf,IAAI,EAAY,CAAC,CAAC,CAAC;SAChD;aAAM;YACL,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;SAClD;IACH,CAAC;IAID,IAAI,OAAO;QACT,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAsB,IAAiB,EAAE,GAA0B;QACtE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,WAAW,CAAC,GAAW;QACrB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,QAAQ,CAAC,GAAW;QAClB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,cAAc,CAAC,OAAe,EAAE,OAAsB;QACpD,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,eAAe,CAAC,OAAe;QAC7B,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,cAAc,CAAC,EAAc;QAC3B,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,gBAAgB,CAAC,KAAa,EAAE,EAAc;QAC5C,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,YAAY;QACV,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;CAKF;AAvDD,oCAuDC;wIAHY,CAAoB;IAC7B,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnB,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/Tag.d.ts b/packages/system/dist/Tag.d.ts new file mode 100644 index 00000000..d94498cb --- /dev/null +++ b/packages/system/dist/Tag.d.ts @@ -0,0 +1,18 @@ +import { HexKey, u256 } from "./Nostr"; +export default class Tag { + Original: string[]; + Key: string; + Event?: u256; + PubKey?: HexKey; + Relay?: string; + Marker?: string; + Hashtag?: string; + DTag?: string; + ATag?: string; + Index: number; + Invalid: boolean; + LNURL?: string; + constructor(tag: string[], index: number); + ToObject(): string[] | null; +} +//# sourceMappingURL=Tag.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Tag.d.ts.map b/packages/system/dist/Tag.d.ts.map new file mode 100644 index 00000000..a961ec79 --- /dev/null +++ b/packages/system/dist/Tag.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Tag.d.ts","sourceRoot":"","sources":["../src/Tag.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAGvC,MAAM,CAAC,OAAO,OAAO,GAAG;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;gBAEH,GAAG,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM;IAgDxC,QAAQ,IAAI,MAAM,EAAE,GAAG,IAAI;CAsB5B"} \ No newline at end of file diff --git a/packages/system/dist/Tag.js b/packages/system/dist/Tag.js new file mode 100644 index 00000000..3a0b281f --- /dev/null +++ b/packages/system/dist/Tag.js @@ -0,0 +1,75 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const Util_1 = require("./Util"); +class Tag { + constructor(tag, index) { + this.Original = tag; + this.Key = tag[0]; + this.Index = index; + this.Invalid = false; + switch (this.Key) { + case "e": { + // ["e", , , ] + this.Event = tag[1]; + this.Relay = tag.length > 2 ? tag[2] : undefined; + this.Marker = tag.length > 3 ? tag[3] : undefined; + if (!this.Event) { + this.Invalid = true; + } + break; + } + case "p": { + // ["p", ] + this.PubKey = tag[1]; + if (!this.PubKey) { + this.Invalid = true; + } + break; + } + case "d": { + this.DTag = tag[1]; + break; + } + case "a": { + this.ATag = tag[1]; + break; + } + case "t": { + this.Hashtag = tag[1]; + break; + } + case "delegation": { + this.PubKey = tag[1]; + break; + } + case "zap": { + this.LNURL = tag[1]; + break; + } + } + } + ToObject() { + switch (this.Key) { + case "e": { + let ret = ["e", this.Event, this.Relay, this.Marker]; + const trimEnd = ret.reverse().findIndex(a => a !== undefined); + ret = ret.reverse().slice(0, ret.length - trimEnd); + return ret; + } + case "p": { + return this.PubKey ? ["p", this.PubKey] : null; + } + case "t": { + return ["t", (0, Util_1.unwrap)(this.Hashtag)]; + } + case "d": { + return ["d", (0, Util_1.unwrap)(this.DTag)]; + } + default: { + return this.Original; + } + } + } +} +exports.default = Tag; +//# sourceMappingURL=Tag.js.map \ No newline at end of file diff --git a/packages/system/dist/Tag.js.map b/packages/system/dist/Tag.js.map new file mode 100644 index 00000000..1e6461c5 --- /dev/null +++ b/packages/system/dist/Tag.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Tag.js","sourceRoot":"","sources":["../src/Tag.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/Util.d.ts b/packages/system/dist/Util.d.ts new file mode 100644 index 00000000..b62acff4 --- /dev/null +++ b/packages/system/dist/Util.d.ts @@ -0,0 +1,23 @@ +import { NostrEvent, u256 } from "./Nostr"; +export declare function unwrap(v: T | undefined | null): T; +/** + * Convert hex to bech32 + */ +export declare function hexToBech32(hrp: string, hex?: string): string; +export declare function sanitizeRelayUrl(url: string): string | undefined; +export declare function unixNow(): number; +export declare function unixNowMs(): number; +export declare function deepEqual(x: any, y: any): boolean; +/** + * Compute the "distance" between two objects by comparing their difference in properties + * Missing/Added keys result in +10 distance + * This is not recursive + */ +export declare function distance(a: any, b: any): number; +export declare function dedupe(v: Array): T[]; +export declare function appendDedupe(a?: Array, b?: Array): T[]; +export declare function findTag(e: NostrEvent, tag: string): string | undefined; +export declare const sha256: (str: string | Uint8Array) => u256; +export declare function getPublicKey(privKey: string): string; +export declare function bech32ToHex(str: string): string; +//# sourceMappingURL=Util.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Util.d.ts.map b/packages/system/dist/Util.d.ts.map new file mode 100644 index 00000000..e7c9a5a5 --- /dev/null +++ b/packages/system/dist/Util.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Util.d.ts","sourceRoot":"","sources":["../src/Util.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE3C,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,CAAC,CAKpD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,UAYpD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,sBAM3C;AAED,wBAAgB,OAAO,WAEtB;AAED,wBAAgB,SAAS,WAExB;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,OAAO,CAQjD;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,MAAM,CA2B/C;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,OAEpC;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,OAEzD;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,sBAKjD;AAED,eAAO,MAAM,MAAM,QAAS,MAAM,GAAG,UAAU,KAAG,IAEjD,CAAA;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,UAE3C;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,UAQtC"} \ No newline at end of file diff --git a/packages/system/dist/Util.js b/packages/system/dist/Util.js new file mode 100644 index 00000000..92f0d069 --- /dev/null +++ b/packages/system/dist/Util.js @@ -0,0 +1,148 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.bech32ToHex = exports.getPublicKey = exports.sha256 = exports.findTag = exports.appendDedupe = exports.dedupe = exports.distance = exports.deepEqual = exports.unixNowMs = exports.unixNow = exports.sanitizeRelayUrl = exports.hexToBech32 = exports.unwrap = void 0; +const utils = __importStar(require("@noble/curves/abstract/utils")); +const secp = __importStar(require("@noble/curves/secp256k1")); +const sha256_1 = require("@noble/hashes/sha256"); +const bech32_1 = require("bech32"); +function unwrap(v) { + if (v === undefined || v === null) { + throw new Error("missing value"); + } + return v; +} +exports.unwrap = unwrap; +/** + * Convert hex to bech32 + */ +function hexToBech32(hrp, hex) { + if (typeof hex !== "string" || hex.length === 0 || hex.length % 2 !== 0) { + return ""; + } + try { + const buf = utils.hexToBytes(hex); + return bech32_1.bech32.encode(hrp, bech32_1.bech32.toWords(buf)); + } + catch (e) { + console.warn("Invalid hex", hex, e); + return ""; + } +} +exports.hexToBech32 = hexToBech32; +function sanitizeRelayUrl(url) { + try { + return new URL(url).toString(); + } + catch { + // ignore + } +} +exports.sanitizeRelayUrl = sanitizeRelayUrl; +function unixNow() { + return Math.floor(unixNowMs() / 1000); +} +exports.unixNow = unixNow; +function unixNowMs() { + return new Date().getTime(); +} +exports.unixNowMs = unixNowMs; +function deepEqual(x, y) { + const ok = Object.keys, tx = typeof x, ty = typeof y; + return x && y && tx === "object" && tx === ty + ? ok(x).length === ok(y).length && ok(x).every(key => deepEqual(x[key], y[key])) + : x === y; +} +exports.deepEqual = deepEqual; +/** + * Compute the "distance" between two objects by comparing their difference in properties + * Missing/Added keys result in +10 distance + * This is not recursive + */ +function distance(a, b) { + const keys1 = Object.keys(a); + const keys2 = Object.keys(b); + const maxKeys = keys1.length > keys2.length ? keys1 : keys2; + let distance = 0; + for (const key of maxKeys) { + if (key in a && key in b) { + if (Array.isArray(a[key]) && Array.isArray(b[key])) { + const aa = a[key]; + const bb = b[key]; + if (aa.length === bb.length) { + if (aa.some(v => !bb.includes(v))) { + distance++; + } + } + else { + distance++; + } + } + else if (a[key] !== b[key]) { + distance++; + } + } + else { + distance += 10; + } + } + return distance; +} +exports.distance = distance; +function dedupe(v) { + return [...new Set(v)]; +} +exports.dedupe = dedupe; +function appendDedupe(a, b) { + return dedupe([...(a ?? []), ...(b ?? [])]); +} +exports.appendDedupe = appendDedupe; +function findTag(e, tag) { + const maybeTag = e.tags.find(evTag => { + return evTag[0] === tag; + }); + return maybeTag && maybeTag[1]; +} +exports.findTag = findTag; +const sha256 = (str) => { + return utils.bytesToHex((0, sha256_1.sha256)(str)); +}; +exports.sha256 = sha256; +function getPublicKey(privKey) { + return utils.bytesToHex(secp.schnorr.getPublicKey(privKey)); +} +exports.getPublicKey = getPublicKey; +function bech32ToHex(str) { + try { + const nKey = bech32_1.bech32.decode(str, 1000); + const buff = bech32_1.bech32.fromWords(nKey.words); + return utils.bytesToHex(Uint8Array.from(buff)); + } + catch (e) { + return str; + } +} +exports.bech32ToHex = bech32ToHex; +//# sourceMappingURL=Util.js.map \ No newline at end of file diff --git a/packages/system/dist/Util.js.map b/packages/system/dist/Util.js.map new file mode 100644 index 00000000..9846635c --- /dev/null +++ b/packages/system/dist/Util.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Util.js","sourceRoot":"","sources":["../src/Util.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/WorkQueue.d.ts b/packages/system/dist/WorkQueue.d.ts new file mode 100644 index 00000000..3b1df622 --- /dev/null +++ b/packages/system/dist/WorkQueue.d.ts @@ -0,0 +1,8 @@ +export interface WorkQueueItem { + next: () => Promise; + resolve(v: unknown): void; + reject(e: unknown): void; +} +export declare function processWorkQueue(queue?: Array, queueDelay?: number): Promise; +export declare const barrierQueue: (queue: Array, then: () => Promise) => Promise; +//# sourceMappingURL=WorkQueue.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/WorkQueue.d.ts.map b/packages/system/dist/WorkQueue.d.ts.map new file mode 100644 index 00000000..2f91432e --- /dev/null +++ b/packages/system/dist/WorkQueue.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"WorkQueue.d.ts","sourceRoot":"","sources":["../src/WorkQueue.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC1B;AAED,wBAAsB,gBAAgB,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,EAAE,UAAU,SAAM,iBAapF;AAED,eAAO,MAAM,YAAY,aAAoB,MAAM,aAAa,CAAC,uCAQhE,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/WorkQueue.js b/packages/system/dist/WorkQueue.js new file mode 100644 index 00000000..b9d0b0d6 --- /dev/null +++ b/packages/system/dist/WorkQueue.js @@ -0,0 +1,30 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.barrierQueue = exports.processWorkQueue = void 0; +async function processWorkQueue(queue, queueDelay = 200) { + while (queue && queue.length > 0) { + const v = queue.shift(); + if (v) { + try { + const ret = await v.next(); + v.resolve(ret); + } + catch (e) { + v.reject(e); + } + } + } + setTimeout(() => processWorkQueue(queue, queueDelay), queueDelay); +} +exports.processWorkQueue = processWorkQueue; +const barrierQueue = async (queue, then) => { + return new Promise((resolve, reject) => { + queue.push({ + next: then, + resolve, + reject, + }); + }); +}; +exports.barrierQueue = barrierQueue; +//# sourceMappingURL=WorkQueue.js.map \ No newline at end of file diff --git a/packages/system/dist/WorkQueue.js.map b/packages/system/dist/WorkQueue.js.map new file mode 100644 index 00000000..8f34b037 --- /dev/null +++ b/packages/system/dist/WorkQueue.js.map @@ -0,0 +1 @@ +{"version":3,"file":"WorkQueue.js","sourceRoot":"","sources":["../src/WorkQueue.ts"],"names":[],"mappings":";;;AAMO,KAAK,UAAU,gBAAgB,CAAC,KAA4B,EAAE,UAAU,GAAG,GAAG;IACnF,OAAO,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;QAChC,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;QACxB,IAAI,CAAC,EAAE;YACL,IAAI;gBACF,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC3B,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;aAChB;YAAC,OAAO,CAAC,EAAE;gBACV,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;aACb;SACF;KACF;IACD,UAAU,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,UAAU,CAAC,CAAC;AACpE,CAAC;AAbD,4CAaC;AAEM,MAAM,YAAY,GAAG,KAAK,EAAK,KAA2B,EAAE,IAAsB,EAAc,EAAE;IACvG,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACxC,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,IAAI;YACV,OAAO;YACP,MAAM;SACP,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AARW,QAAA,YAAY,gBAQvB"} \ No newline at end of file diff --git a/packages/system/dist/cache/index.d.ts b/packages/system/dist/cache/index.d.ts new file mode 100644 index 00000000..f8fc473f --- /dev/null +++ b/packages/system/dist/cache/index.d.ts @@ -0,0 +1,48 @@ +import { HexKey, NostrEvent, UserMetadata } from ".."; +export interface MetadataCache extends UserMetadata { + /** + * When the object was saved in cache + */ + loaded: number; + /** + * When the source metadata event was created + */ + created: number; + /** + * The pubkey of the owner of this metadata + */ + pubkey: HexKey; + /** + * The bech32 encoded pubkey + */ + npub: string; + /** + * Pubkey of zapper service + */ + zapService?: HexKey; + /** + * If the nip05 is valid for this user + */ + isNostrAddressValid: boolean; +} +export declare function mapEventToProfile(ev: NostrEvent): MetadataCache | undefined; +export interface CacheStore { + preload(): Promise; + getFromCache(key?: string): T | undefined; + get(key?: string): Promise; + bulkGet(keys: Array): Promise>; + set(obj: T): Promise; + bulkSet(obj: Array): Promise; + update(m: TCachedWithCreated): Promise<"new" | "updated" | "refresh" | "no_change">; + /** + * Loads a list of rows from disk cache + * @param keys List of ids to load + * @returns Keys that do not exist on disk cache + */ + buffer(keys: Array): Promise>; + clear(): Promise; +} +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/cache/index.d.ts.map b/packages/system/dist/cache/index.d.ts.map new file mode 100644 index 00000000..65d321a7 --- /dev/null +++ b/packages/system/dist/cache/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAGtD,MAAM,WAAW,aAAc,SAAQ,YAAY;IACjD;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,UAAU,6BAa/C;AAED,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;IAC1C,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC1C,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,CAAC,kBAAkB,SAAS,CAAC,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC,CAAA;IAEvJ;;;;OAIG;IACH,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAEpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB"} \ No newline at end of file diff --git a/packages/system/dist/cache/index.js b/packages/system/dist/cache/index.js new file mode 100644 index 00000000..f21265bd --- /dev/null +++ b/packages/system/dist/cache/index.js @@ -0,0 +1,21 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.mapEventToProfile = void 0; +const Util_1 = require("../Util"); +function mapEventToProfile(ev) { + try { + const data = JSON.parse(ev.content); + return { + ...data, + pubkey: ev.pubkey, + npub: (0, Util_1.hexToBech32)("npub", ev.pubkey), + created: ev.created_at, + loaded: (0, Util_1.unixNowMs)(), + }; + } + catch (e) { + console.error("Failed to parse JSON", ev, e); + } +} +exports.mapEventToProfile = mapEventToProfile; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/system/dist/cache/index.js.map b/packages/system/dist/cache/index.js.map new file mode 100644 index 00000000..6660f61b --- /dev/null +++ b/packages/system/dist/cache/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":";;;AACA,kCAAiD;AAkCjD,SAAgB,iBAAiB,CAAC,EAAc;IAC9C,IAAI;QACF,MAAM,IAAI,GAAiB,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;QAClD,OAAO;YACL,GAAG,IAAI;YACP,MAAM,EAAE,EAAE,CAAC,MAAM;YACjB,IAAI,EAAE,IAAA,kBAAW,EAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC;YACpC,OAAO,EAAE,EAAE,CAAC,UAAU;YACtB,MAAM,EAAE,IAAA,gBAAS,GAAE;SACH,CAAC;KACpB;IAAC,OAAO,CAAC,EAAE;QACV,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;KAC9C;AACH,CAAC;AAbD,8CAaC"} \ No newline at end of file diff --git a/packages/system/dist/index.d.ts b/packages/system/dist/index.d.ts new file mode 100644 index 00000000..08c37550 --- /dev/null +++ b/packages/system/dist/index.d.ts @@ -0,0 +1,44 @@ +import { AuthHandler, RelaySettings, ConnectionStateSnapshot } from "./Connection"; +import { RequestBuilder } from "./RequestBuilder"; +import { NoteStore } from "./NoteCollection"; +import { Query } from "./Query"; +import { NostrEvent, ReqFilter } from "./Nostr"; +export * from "./NostrSystem"; +export { default as EventKind } from "./EventKind"; +export * from "./Nostr"; +export * from "./Links"; +export { default as Tag } from "./Tag"; +export * from "./Nips"; +export * from "./RelayInfo"; +export * from "./EventExt"; +export * from "./Connection"; +export * from "./NoteCollection"; +export * from "./RequestBuilder"; +export * from "./EventPublisher"; +export * from "./EventBuilder"; +export * from "./NostrLink"; +export * from "./cache"; +export * from "./ProfileCache"; +export interface SystemInterface { + /** + * Handler function for NIP-42 + */ + HandleAuth?: AuthHandler; + get Sockets(): Array; + GetQuery(id: string): Query | undefined; + Query(type: { + new (): T; + }, req: RequestBuilder | null): Query | undefined; + ConnectToRelay(address: string, options: RelaySettings): Promise; + DisconnectRelay(address: string): void; + BroadcastEvent(ev: NostrEvent): void; + WriteOnceToRelay(relay: string, ev: NostrEvent): Promise; +} +export interface SystemSnapshot { + queries: Array<{ + id: string; + filters: Array; + subFilters: Array; + }>; +} +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/index.d.ts.map b/packages/system/dist/index.d.ts.map new file mode 100644 index 00000000..d9004955 --- /dev/null +++ b/packages/system/dist/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEhD,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,OAAO,CAAC;AACvC,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAE/B,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,IAAI,OAAO,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC9C,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;IACxC,KAAK,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,EAAE;QAAE,QAAO,CAAC,CAAA;KAAE,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,GAAG,KAAK,GAAG,SAAS,CAAC;IAC9F,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,cAAc,CAAC,EAAE,EAAE,UAAU,GAAG,IAAI,CAAC;IACrC,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,KAAK,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QAC1B,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;KAC9B,CAAC,CAAC;CACJ"} \ No newline at end of file diff --git a/packages/system/dist/index.js b/packages/system/dist/index.js new file mode 100644 index 00000000..9e8c83e3 --- /dev/null +++ b/packages/system/dist/index.js @@ -0,0 +1,39 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Tag = exports.EventKind = void 0; +__exportStar(require("./NostrSystem"), exports); +var EventKind_1 = require("./EventKind"); +Object.defineProperty(exports, "EventKind", { enumerable: true, get: function () { return __importDefault(EventKind_1).default; } }); +__exportStar(require("./Nostr"), exports); +__exportStar(require("./Links"), exports); +var Tag_1 = require("./Tag"); +Object.defineProperty(exports, "Tag", { enumerable: true, get: function () { return __importDefault(Tag_1).default; } }); +__exportStar(require("./Nips"), exports); +__exportStar(require("./RelayInfo"), exports); +__exportStar(require("./EventExt"), exports); +__exportStar(require("./Connection"), exports); +__exportStar(require("./NoteCollection"), exports); +__exportStar(require("./RequestBuilder"), exports); +__exportStar(require("./EventPublisher"), exports); +__exportStar(require("./EventBuilder"), exports); +__exportStar(require("./NostrLink"), exports); +__exportStar(require("./cache"), exports); +__exportStar(require("./ProfileCache"), exports); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/system/dist/index.js.map b/packages/system/dist/index.js.map new file mode 100644 index 00000000..a0e12b3a --- /dev/null +++ b/packages/system/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAMA,gDAA8B;AAC9B,yCAAmD;AAA1C,uHAAA,OAAO,OAAa;AAC7B,0CAAwB;AACxB,0CAAwB;AACxB,6BAAuC;AAA9B,2GAAA,OAAO,OAAO;AACvB,yCAAuB;AACvB,8CAA4B;AAC5B,6CAA2B;AAC3B,+CAA6B;AAC7B,mDAAiC;AACjC,mDAAiC;AACjC,mDAAiC;AACjC,iDAA+B;AAC/B,8CAA4B;AAC5B,0CAAwB;AACxB,iDAA+B"} \ No newline at end of file diff --git a/packages/system/jest.config.js b/packages/system/jest.config.js new file mode 100644 index 00000000..1ccd5800 --- /dev/null +++ b/packages/system/jest.config.js @@ -0,0 +1,9 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + bail: true, + preset: "ts-jest", + testEnvironment: "jsdom", + roots: ["src"], + moduleDirectories: ["src"], + setupFiles: ["./tests/setupTests.ts"], +}; diff --git a/packages/system/package.json b/packages/system/package.json new file mode 100644 index 00000000..ecdd59f6 --- /dev/null +++ b/packages/system/package.json @@ -0,0 +1,29 @@ +{ + "name": "@snort/system", + "version": "1.0.0", + "description": "Snort nostr system package", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "repository": "https://git.v0l.io/Kieran/snort", + "author": "Kieran", + "license": "GPLv3", + "scripts": { + "build": "rm -rf dist && tsc", + "test": "jest" + }, + "devDependencies": { + "@jest/globals": "^29.5.0", + "@types/jest": "^29.5.1", + "jest": "^29.5.0", + "jest-environment-jsdom": "^29.5.0", + "ts-jest": "^29.1.0", + "typescript": "^5.0.4" + }, + "dependencies": { + "@noble/curves": "^1.0.0", + "@protobufjs/base64": "^1.1.2", + "bech32": "^2.0.0", + "debug": "^4.3.4", + "uuid": "^9.0.0" + } +} diff --git a/packages/system/snort-system-1.0.0.tgz b/packages/system/snort-system-1.0.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..3384474ebdb187e6e3ff3ba8e0f4713a867d1303 GIT binary patch literal 56011 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 literal 0 HcmV?d00001 diff --git a/packages/app/src/System/Connection.ts b/packages/system/src/Connection.ts similarity index 99% rename from packages/app/src/System/Connection.ts rename to packages/system/src/Connection.ts index 109410e6..c4e11052 100644 --- a/packages/app/src/System/Connection.ts +++ b/packages/system/src/Connection.ts @@ -5,7 +5,7 @@ import { ConnectionStats } from "./ConnectionStats"; import { NostrEvent, ReqCommand, TaggedRawEvent, u256 } from "./Nostr"; import { RelayInfo } from "./RelayInfo"; import { unwrap } from "./Util"; -import ExternalStore from "ExternalStore"; +import ExternalStore from "./ExternalStore"; export type AuthHandler = (challenge: string, relay: string) => Promise; diff --git a/packages/app/src/System/ConnectionStats.ts b/packages/system/src/ConnectionStats.ts similarity index 100% rename from packages/app/src/System/ConnectionStats.ts rename to packages/system/src/ConnectionStats.ts diff --git a/packages/system/src/Const.ts b/packages/system/src/Const.ts new file mode 100644 index 00000000..f2e94a65 --- /dev/null +++ b/packages/system/src/Const.ts @@ -0,0 +1,16 @@ +/** + * Websocket re-connect timeout + */ +export const DefaultConnectTimeout = 2000; + +/** + * Hashtag regex + */ +// eslint-disable-next-line no-useless-escape +export const HashtagRegex = /(#[^\s!@#$%^&*()=+.\/,\[{\]};:'"?><]+)/g; + + +/** + * How long profile cache should be considered valid for + */ + export const ProfileCacheExpire = 1_000 * 60 * 60 * 6; \ No newline at end of file diff --git a/packages/app/src/System/EventBuilder.ts b/packages/system/src/EventBuilder.ts similarity index 91% rename from packages/app/src/System/EventBuilder.ts rename to packages/system/src/EventBuilder.ts index 73a285e1..d59bd647 100644 --- a/packages/app/src/System/EventBuilder.ts +++ b/packages/system/src/EventBuilder.ts @@ -1,7 +1,8 @@ -import { EventKind, HexKey, NostrPrefix, NostrEvent } from "System"; -import { HashtagRegex } from "Const"; -import { getPublicKey, parseNostrLink, unixNow } from "SnortUtils"; +import { EventKind, HexKey, NostrPrefix, NostrEvent } from "."; +import { HashtagRegex } from "./Const"; +import { getPublicKey, unixNow } from "./Util"; import { EventExt } from "./EventExt"; +import { parseNostrLink } from "./NostrLink"; export class EventBuilder { #kind?: EventKind; diff --git a/packages/app/src/System/EventExt.ts b/packages/system/src/EventExt.ts similarity index 92% rename from packages/app/src/System/EventExt.ts rename to packages/system/src/EventExt.ts index ebbf0f15..4df025ad 100644 --- a/packages/app/src/System/EventExt.ts +++ b/packages/system/src/EventExt.ts @@ -1,8 +1,8 @@ import * as secp from "@noble/curves/secp256k1"; import * as utils from "@noble/curves/abstract/utils"; -import { EventKind, HexKey, NostrEvent, Tag } from "System"; +import { EventKind, HexKey, NostrEvent, Tag } from "."; import base64 from "@protobufjs/base64"; -import { sha256, unixNow } from "SnortUtils"; +import { sha256, unixNow } from "./Util"; export interface Thread { root?: Tag; @@ -18,6 +18,7 @@ export abstract class EventExt { static getRootPubKey(e: NostrEvent): HexKey { const delegation = e.tags.find(a => a[0] === "delegation"); if (delegation?.[1]) { + // todo: verify sig return delegation[1]; } return e.pubkey; @@ -26,12 +27,12 @@ export abstract class EventExt { /** * Sign this message with a private key */ - static async sign(e: NostrEvent, key: HexKey) { + static sign(e: NostrEvent, key: HexKey) { e.id = this.createId(e); - const sig = await secp.schnorr.sign(e.id, key); + const sig = secp.schnorr.sign(e.id, key); e.sig = utils.bytesToHex(sig); - if (!(await secp.schnorr.verify(e.sig, e.id, e.pubkey))) { + if (!(secp.schnorr.verify(e.sig, e.id, e.pubkey))) { throw new Error("Signing failed"); } } @@ -40,9 +41,9 @@ export abstract class EventExt { * Check the signature of this message * @returns True if valid signature */ - static async verify(e: NostrEvent) { + static verify(e: NostrEvent) { const id = this.createId(e); - const result = await secp.schnorr.verify(e.sig, id, e.pubkey); + const result = secp.schnorr.verify(e.sig, id, e.pubkey); return result; } diff --git a/packages/app/src/System/EventKind.ts b/packages/system/src/EventKind.ts similarity index 100% rename from packages/app/src/System/EventKind.ts rename to packages/system/src/EventKind.ts diff --git a/packages/app/src/System/EventPublisher.ts b/packages/system/src/EventPublisher.ts similarity index 93% rename from packages/app/src/System/EventPublisher.ts rename to packages/system/src/EventPublisher.ts index 8aa29bad..b452bf48 100644 --- a/packages/app/src/System/EventPublisher.ts +++ b/packages/system/src/EventPublisher.ts @@ -11,13 +11,12 @@ import { TaggedRawEvent, u256, UserMetadata, -} from "System"; +} from "."; -import { DefaultRelays } from "Const"; -import { unwrap } from "SnortUtils"; +import { unwrap } from "./Util"; import { EventBuilder } from "./EventBuilder"; import { EventExt } from "./EventExt"; -import { barrierQueue, processWorkQueue, WorkQueueItem } from "WorkQueue"; +import { barrierQueue, processWorkQueue, WorkQueueItem } from "./WorkQueue"; const Nip7Queue: Array = []; processWorkQueue(Nip7Queue); @@ -118,17 +117,6 @@ export class EventPublisher { this.#system.BroadcastEvent(ev); } - /** - * Write event to DefaultRelays, this is important for profiles / relay lists to prevent bugs - * If a user removes all the DefaultRelays from their relay list and saves that relay list, - * When they open the site again we wont see that updated relay list and so it will appear to reset back to the previous state - */ - broadcastForBootstrap(ev: NostrEvent) { - for (const [k] of DefaultRelays) { - this.#system.WriteOnceToRelay(k, ev); - } - } - /** * Write event to all given relays. */ diff --git a/packages/system/src/ExternalStore.ts b/packages/system/src/ExternalStore.ts new file mode 100644 index 00000000..4b1dedea --- /dev/null +++ b/packages/system/src/ExternalStore.ts @@ -0,0 +1,41 @@ +type HookFn = (e?: TSnapshot) => void; + +interface HookFilter { + fn: HookFn; +} + +/** + * Simple React hookable store with manual change notifications + */ +export default abstract class ExternalStore { + #hooks: Array> = []; + #snapshot: Readonly = {} as Readonly; + #changed = true; + + hook(fn: HookFn) { + this.#hooks.push({ + fn, + }); + return () => { + const idx = this.#hooks.findIndex(a => a.fn === fn); + if (idx >= 0) { + this.#hooks.splice(idx, 1); + } + }; + } + + snapshot() { + if (this.#changed) { + this.#snapshot = this.takeSnapshot(); + this.#changed = false; + } + return this.#snapshot; + } + + protected notifyChange(sn?: TSnapshot) { + this.#changed = true; + this.#hooks.forEach(h => h.fn(sn)); + } + + abstract takeSnapshot(): TSnapshot; +} diff --git a/packages/app/src/System/GossipModel.ts b/packages/system/src/GossipModel.ts similarity index 96% rename from packages/app/src/System/GossipModel.ts rename to packages/system/src/GossipModel.ts index 5cdd2d41..07f8817e 100644 --- a/packages/app/src/System/GossipModel.ts +++ b/packages/system/src/GossipModel.ts @@ -1,5 +1,5 @@ -import { FullRelaySettings, ReqFilter } from "System"; -import { unwrap } from "SnortUtils"; +import { FullRelaySettings, ReqFilter } from "."; +import { unwrap } from "./Util"; import debug from "debug"; const PickNRelays = 2; diff --git a/packages/app/src/System/Links.ts b/packages/system/src/Links.ts similarity index 100% rename from packages/app/src/System/Links.ts rename to packages/system/src/Links.ts diff --git a/packages/app/src/System/Nips.ts b/packages/system/src/Nips.ts similarity index 100% rename from packages/app/src/System/Nips.ts rename to packages/system/src/Nips.ts diff --git a/packages/app/src/System/Nostr.ts b/packages/system/src/Nostr.ts similarity index 100% rename from packages/app/src/System/Nostr.ts rename to packages/system/src/Nostr.ts diff --git a/packages/system/src/NostrLink.ts b/packages/system/src/NostrLink.ts new file mode 100644 index 00000000..cb663b3b --- /dev/null +++ b/packages/system/src/NostrLink.ts @@ -0,0 +1,110 @@ +import { bech32ToHex, hexToBech32 } from "./Util"; +import { NostrPrefix, decodeTLV, TLVEntryType } from "."; + +export interface NostrLink { + type: NostrPrefix; + id: string; + kind?: number; + author?: string; + relays?: Array; + encode(): string; + } + + export function validateNostrLink(link: string): boolean { + try { + const parsedLink = parseNostrLink(link); + if (!parsedLink) { + return false; + } + if (parsedLink.type === NostrPrefix.PublicKey || parsedLink.type === NostrPrefix.Note) { + return parsedLink.id.length === 64; + } + + return true; + } catch { + return false; + } + } + + export function tryParseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLink | undefined { + try { + return parseNostrLink(link, prefixHint); + } catch { + return undefined; + } + } + + export function parseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLink { + const entity = link.startsWith("web+nostr:") || link.startsWith("nostr:") ? link.split(":")[1] : link; + + const isPrefix = (prefix: NostrPrefix) => { + return entity.startsWith(prefix); + }; + + if (isPrefix(NostrPrefix.PublicKey)) { + const id = bech32ToHex(entity); + if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); + return { + type: NostrPrefix.PublicKey, + id: id, + encode: () => hexToBech32(NostrPrefix.PublicKey, id), + }; + } else if (isPrefix(NostrPrefix.Note)) { + const id = bech32ToHex(entity); + if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); + return { + type: NostrPrefix.Note, + id: id, + encode: () => hexToBech32(NostrPrefix.Note, id), + }; + } else if (isPrefix(NostrPrefix.Profile) || isPrefix(NostrPrefix.Event) || isPrefix(NostrPrefix.Address)) { + const decoded = decodeTLV(entity); + + const id = decoded.find(a => a.type === TLVEntryType.Special)?.value as string; + const relays = decoded.filter(a => a.type === TLVEntryType.Relay).map(a => a.value as string); + const author = decoded.find(a => a.type === TLVEntryType.Author)?.value as string; + const kind = decoded.find(a => a.type === TLVEntryType.Kind)?.value as number; + + const encode = () => { + return entity; // return original + }; + if (isPrefix(NostrPrefix.Profile)) { + if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); + return { + type: NostrPrefix.Profile, + id, + relays, + kind, + author, + encode, + }; + } else if (isPrefix(NostrPrefix.Event)) { + if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); + return { + type: NostrPrefix.Event, + id, + relays, + kind, + author, + encode, + }; + } else if (isPrefix(NostrPrefix.Address)) { + return { + type: NostrPrefix.Address, + id, + relays, + kind, + author, + encode, + }; + } + } else if (prefixHint) { + return { + type: prefixHint, + id: link, + encode: () => hexToBech32(prefixHint, link), + }; + } + throw new Error("Invalid nostr link"); + } + \ No newline at end of file diff --git a/packages/app/src/System/NostrSystem.ts b/packages/system/src/NostrSystem.ts similarity index 95% rename from packages/app/src/System/NostrSystem.ts rename to packages/system/src/NostrSystem.ts index a34a641f..c20b96a8 100644 --- a/packages/app/src/System/NostrSystem.ts +++ b/packages/system/src/NostrSystem.ts @@ -1,15 +1,14 @@ import debug from "debug"; -import { v4 as uuid } from "uuid"; -import ExternalStore from "ExternalStore"; -import { NostrEvent, ReqFilter, TaggedRawEvent } from "./Nostr"; +import ExternalStore from "./ExternalStore"; +import { NostrEvent, TaggedRawEvent } from "./Nostr"; import { AuthHandler, Connection, RelaySettings, ConnectionStateSnapshot } from "./Connection"; -import { Query, QueryBase } from "./Query"; +import { Query } from "./Query"; import { RelayCache } from "./GossipModel"; import { NoteStore } from "./NoteCollection"; import { BuiltRawReqFilter, RequestBuilder } from "./RequestBuilder"; -import { unwrap, sanitizeRelayUrl, unixNowMs } from "./Util"; -import { SystemInterface, SystemSnapshot } from "System"; +import { unwrap, sanitizeRelayUrl } from "./Util"; +import { SystemInterface, SystemSnapshot } from "."; /** * Manages nostr content retrieval system diff --git a/packages/app/src/System/NoteCollection.ts b/packages/system/src/NoteCollection.ts similarity index 98% rename from packages/app/src/System/NoteCollection.ts rename to packages/system/src/NoteCollection.ts index 85974f14..f33e41f7 100644 --- a/packages/app/src/System/NoteCollection.ts +++ b/packages/system/src/NoteCollection.ts @@ -1,5 +1,5 @@ -import { TaggedRawEvent, u256 } from "System"; -import { appendDedupe, findTag } from "SnortUtils"; +import { TaggedRawEvent, u256 } from "."; +import { appendDedupe, findTag } from "./Util"; export interface StoreSnapshot { data: TSnapshot | undefined; diff --git a/packages/app/src/System/ProfileCache.ts b/packages/system/src/ProfileCache.ts similarity index 83% rename from packages/app/src/System/ProfileCache.ts rename to packages/system/src/ProfileCache.ts index fe49f371..7a21c6c9 100644 --- a/packages/app/src/System/ProfileCache.ts +++ b/packages/system/src/ProfileCache.ts @@ -1,13 +1,12 @@ -import { EventKind, HexKey, SystemInterface, TaggedRawEvent } from "System"; -import { ProfileCacheExpire } from "Const"; -import { mapEventToProfile, MetadataCache } from "Cache"; -import { UserCache } from "Cache/UserCache"; -import { PubkeyReplaceableNoteStore, RequestBuilder } from "System"; -import { unixNowMs } from "SnortUtils"; +import { EventKind, HexKey, SystemInterface, TaggedRawEvent, PubkeyReplaceableNoteStore, RequestBuilder } from "."; +import { ProfileCacheExpire } from "./Const"; +import { CacheStore, mapEventToProfile, MetadataCache } from "./cache"; +import { unixNowMs } from "./Util"; import debug from "debug"; export class ProfileLoaderService { #system: SystemInterface; + #cache: CacheStore; /** * List of pubkeys to fetch metadata for @@ -16,8 +15,9 @@ export class ProfileLoaderService { readonly #log = debug("ProfileCache"); - constructor(system: SystemInterface) { + constructor(system: SystemInterface, cache: CacheStore) { this.#system = system; + this.#cache = cache; this.#FetchMetadata(); } @@ -31,7 +31,7 @@ export class ProfileLoaderService { bufferNow.push(p); } } - UserCache.buffer(bufferNow); + this.#cache.buffer(bufferNow); } /** @@ -48,17 +48,17 @@ export class ProfileLoaderService { async onProfileEvent(e: Readonly) { const profile = mapEventToProfile(e); if (profile) { - await UserCache.update(profile); + await this.#cache.update(profile); } } async #FetchMetadata() { - const missingFromCache = await UserCache.buffer([...this.WantsMetadata]); + const missingFromCache = await this.#cache.buffer([...this.WantsMetadata]); const expire = unixNowMs() - ProfileCacheExpire; const expired = [...this.WantsMetadata] .filter(a => !missingFromCache.includes(a)) - .filter(a => (UserCache.getFromCache(a)?.loaded ?? 0) < expire); + .filter(a => (this.#cache.getFromCache(a)?.loaded ?? 0) < expire); const missing = new Set([...missingFromCache, ...expired]); if (missing.size > 0) { this.#log("Wants profiles: %d missing, %d expired", missingFromCache.length, expired.length); @@ -104,7 +104,7 @@ export class ProfileLoaderService { if (couldNotFetch.length > 0) { this.#log("No profiles: %o", couldNotFetch); const empty = couldNotFetch.map(a => - UserCache.update({ + this.#cache.update({ pubkey: a, loaded: unixNowMs() - ProfileCacheExpire + 5_000, // expire in 5s created: 69, diff --git a/packages/app/src/System/Query.ts b/packages/system/src/Query.ts similarity index 96% rename from packages/app/src/System/Query.ts rename to packages/system/src/Query.ts index 581d37f1..4ec3a4df 100644 --- a/packages/app/src/System/Query.ts +++ b/packages/system/src/Query.ts @@ -1,10 +1,9 @@ import { v4 as uuid } from "uuid"; import debug from "debug"; -import { Connection, ReqFilter, Nips, TaggedRawEvent } from "System"; -import { unixNowMs, unwrap } from "SnortUtils"; +import { Connection, ReqFilter, Nips, TaggedRawEvent } from "."; +import { unixNowMs, unwrap } from "./Util"; import { NoteStore } from "./NoteCollection"; -import { flatMerge, mergeSimilar, simpleMerge } from "./RequestMerger"; -import { eventMatchesFilter } from "./RequestMatcher"; +import { flatMerge } from "./RequestMerger"; import { BuiltRawReqFilter } from "./RequestBuilder"; import { expandFilter } from "./RequestExpander"; diff --git a/packages/app/src/System/RelayInfo.ts b/packages/system/src/RelayInfo.ts similarity index 100% rename from packages/app/src/System/RelayInfo.ts rename to packages/system/src/RelayInfo.ts diff --git a/packages/app/src/System/RequestBuilder.ts b/packages/system/src/RequestBuilder.ts similarity index 98% rename from packages/app/src/System/RequestBuilder.ts rename to packages/system/src/RequestBuilder.ts index dfbbb8b5..a565da9a 100644 --- a/packages/app/src/System/RequestBuilder.ts +++ b/packages/system/src/RequestBuilder.ts @@ -1,5 +1,5 @@ -import { ReqFilter, u256, HexKey, EventKind } from "System"; -import { appendDedupe, dedupe } from "SnortUtils"; +import { ReqFilter, u256, HexKey, EventKind } from "."; +import { appendDedupe, dedupe } from "./Util"; import { diffFilters } from "./RequestSplitter"; import { RelayCache, splitAllByWriteRelays, splitByWriteRelays } from "./GossipModel"; import { mergeSimilar } from "./RequestMerger"; diff --git a/packages/app/src/System/RequestExpander.ts b/packages/system/src/RequestExpander.ts similarity index 100% rename from packages/app/src/System/RequestExpander.ts rename to packages/system/src/RequestExpander.ts diff --git a/packages/app/src/System/RequestMatcher.ts b/packages/system/src/RequestMatcher.ts similarity index 100% rename from packages/app/src/System/RequestMatcher.ts rename to packages/system/src/RequestMatcher.ts diff --git a/packages/app/src/System/RequestMerger.ts b/packages/system/src/RequestMerger.ts similarity index 99% rename from packages/app/src/System/RequestMerger.ts rename to packages/system/src/RequestMerger.ts index bd588c25..db0403fe 100644 --- a/packages/app/src/System/RequestMerger.ts +++ b/packages/system/src/RequestMerger.ts @@ -1,4 +1,4 @@ -import { ReqFilter } from "System"; +import { ReqFilter } from "."; import { FlatReqFilter } from "./RequestExpander"; import { distance } from "./Util"; diff --git a/packages/app/src/System/RequestSplitter.ts b/packages/system/src/RequestSplitter.ts similarity index 94% rename from packages/app/src/System/RequestSplitter.ts rename to packages/system/src/RequestSplitter.ts index e1a3d653..2231d8b5 100644 --- a/packages/app/src/System/RequestSplitter.ts +++ b/packages/system/src/RequestSplitter.ts @@ -1,4 +1,4 @@ -import { ReqFilter } from "System"; +import { ReqFilter } from "."; import { deepEqual } from "./Util"; import { expandFilter } from "./RequestExpander"; import { flatMerge } from "./RequestMerger"; diff --git a/packages/app/src/System/SystemWorker.ts b/packages/system/src/SystemWorker.ts similarity index 80% rename from packages/app/src/System/SystemWorker.ts rename to packages/system/src/SystemWorker.ts index fb80e02a..ab9dfaf3 100644 --- a/packages/app/src/System/SystemWorker.ts +++ b/packages/system/src/SystemWorker.ts @@ -1,15 +1,10 @@ -import ExternalStore from "ExternalStore"; -import { - NoteStore, - Query, - NostrEvent, - RelaySettings, - RequestBuilder, - SystemSnapshot, - SystemInterface, - ConnectionStateSnapshot, - AuthHandler, -} from "System"; +import { SystemSnapshot, SystemInterface } from "."; +import { AuthHandler, ConnectionStateSnapshot, RelaySettings } from "./Connection"; +import ExternalStore from "./ExternalStore"; +import { NostrEvent } from "./Nostr"; +import { NoteStore } from "./NoteCollection"; +import { Query } from "./Query"; +import { RequestBuilder } from "./RequestBuilder"; export class SystemWorker extends ExternalStore implements SystemInterface { #port: MessagePort; diff --git a/packages/app/src/System/Tag.ts b/packages/system/src/Tag.ts similarity index 100% rename from packages/app/src/System/Tag.ts rename to packages/system/src/Tag.ts diff --git a/packages/app/src/System/Util.ts b/packages/system/src/Util.ts similarity index 68% rename from packages/app/src/System/Util.ts rename to packages/system/src/Util.ts index c5698739..a0053736 100644 --- a/packages/app/src/System/Util.ts +++ b/packages/system/src/Util.ts @@ -1,5 +1,8 @@ import * as utils from "@noble/curves/abstract/utils"; +import * as secp from "@noble/curves/secp256k1"; +import { sha256 as sha2 } from "@noble/hashes/sha256"; import { bech32 } from "bech32"; +import { NostrEvent, u256 } from "./Nostr"; export function unwrap(v: T | undefined | null): T { if (v === undefined || v === null) { @@ -84,3 +87,36 @@ export function distance(a: any, b: any): number { return distance; } + +export function dedupe(v: Array) { + return [...new Set(v)]; +} + +export function appendDedupe(a?: Array, b?: Array) { + return dedupe([...(a ?? []), ...(b ?? [])]); +} + +export function findTag(e: NostrEvent, tag: string) { + const maybeTag = e.tags.find(evTag => { + return evTag[0] === tag; + }); + return maybeTag && maybeTag[1]; +} + +export const sha256 = (str: string | Uint8Array): u256 => { + return utils.bytesToHex(sha2(str)); +} + +export function getPublicKey(privKey: string) { + return utils.bytesToHex(secp.schnorr.getPublicKey(privKey)); +} + +export function bech32ToHex(str: string) { + try { + const nKey = bech32.decode(str, 1_000); + const buff = bech32.fromWords(nKey.words); + return utils.bytesToHex(Uint8Array.from(buff)); + } catch (e) { + return str; + } +} diff --git a/packages/system/src/WorkQueue.ts b/packages/system/src/WorkQueue.ts new file mode 100644 index 00000000..37df22e0 --- /dev/null +++ b/packages/system/src/WorkQueue.ts @@ -0,0 +1,30 @@ +export interface WorkQueueItem { + next: () => Promise; + resolve(v: unknown): void; + reject(e: unknown): void; +} + +export async function processWorkQueue(queue?: Array, queueDelay = 200) { + while (queue && queue.length > 0) { + const v = queue.shift(); + if (v) { + try { + const ret = await v.next(); + v.resolve(ret); + } catch (e) { + v.reject(e); + } + } + } + setTimeout(() => processWorkQueue(queue, queueDelay), queueDelay); +} + +export const barrierQueue = async (queue: Array, then: () => Promise): Promise => { + return new Promise((resolve, reject) => { + queue.push({ + next: then, + resolve, + reject, + }); + }); +}; diff --git a/packages/system/src/cache/index.ts b/packages/system/src/cache/index.ts new file mode 100644 index 00000000..431318a0 --- /dev/null +++ b/packages/system/src/cache/index.ts @@ -0,0 +1,68 @@ +import { HexKey, NostrEvent, UserMetadata } from ".."; +import { hexToBech32, unixNowMs } from "../Util"; + +export interface MetadataCache extends UserMetadata { + /** + * When the object was saved in cache + */ + loaded: number; + + /** + * When the source metadata event was created + */ + created: number; + + /** + * The pubkey of the owner of this metadata + */ + pubkey: HexKey; + + /** + * The bech32 encoded pubkey + */ + npub: string; + + /** + * Pubkey of zapper service + */ + zapService?: HexKey; + + /** + * If the nip05 is valid for this user + */ + isNostrAddressValid: boolean; +} + +export function mapEventToProfile(ev: NostrEvent) { + try { + const data: UserMetadata = JSON.parse(ev.content); + return { + ...data, + pubkey: ev.pubkey, + npub: hexToBech32("npub", ev.pubkey), + created: ev.created_at, + loaded: unixNowMs(), + } as MetadataCache; + } catch (e) { + console.error("Failed to parse JSON", ev, e); + } +} + +export interface CacheStore { + preload(): Promise; + getFromCache(key?: string): T | undefined; + get(key?: string): Promise; + bulkGet(keys: Array): Promise>; + set(obj: T): Promise; + bulkSet(obj: Array): Promise; + update(m: TCachedWithCreated): Promise<"new" | "updated" | "refresh" | "no_change"> + + /** + * Loads a list of rows from disk cache + * @param keys List of ids to load + * @returns Keys that do not exist on disk cache + */ + buffer(keys: Array): Promise>; + + clear(): Promise; +} diff --git a/packages/app/src/System/index.ts b/packages/system/src/index.ts similarity index 58% rename from packages/app/src/System/index.ts rename to packages/system/src/index.ts index fd40e81e..4f11456c 100644 --- a/packages/app/src/System/index.ts +++ b/packages/system/src/index.ts @@ -1,13 +1,6 @@ -import { AuthHandler, Connection, RelaySettings, ConnectionStateSnapshot } from "./Connection"; +import { AuthHandler, RelaySettings, ConnectionStateSnapshot } from "./Connection"; import { RequestBuilder } from "./RequestBuilder"; -import { EventBuilder } from "./EventBuilder"; -import { - FlatNoteStore, - NoteStore, - PubkeyReplaceableNoteStore, - ParameterizedReplaceableNoteStore, - ReplaceableNoteStore, -} from "./NoteCollection"; +import { NoteStore } from "./NoteCollection"; import { Query } from "./Query"; import { NostrEvent, ReqFilter } from "./Nostr"; @@ -18,6 +11,15 @@ export * from "./Links"; export { default as Tag } from "./Tag"; export * from "./Nips"; export * from "./RelayInfo"; +export * from "./EventExt"; +export * from "./Connection"; +export * from "./NoteCollection"; +export * from "./RequestBuilder"; +export * from "./EventPublisher"; +export * from "./EventBuilder"; +export * from "./NostrLink"; +export * from "./cache"; +export * from "./ProfileCache"; export interface SystemInterface { /** @@ -26,7 +28,7 @@ export interface SystemInterface { HandleAuth?: AuthHandler; get Sockets(): Array; GetQuery(id: string): Query | undefined; - Query(type: { new (): T }, req: RequestBuilder | null): Query | undefined; + Query(type: { new(): T }, req: RequestBuilder | null): Query | undefined; ConnectToRelay(address: string, options: RelaySettings): Promise; DisconnectRelay(address: string): void; BroadcastEvent(ev: NostrEvent): void; @@ -39,19 +41,4 @@ export interface SystemSnapshot { filters: Array; subFilters: Array; }>; -} - -export { - NoteStore, - RequestBuilder, - FlatNoteStore, - PubkeyReplaceableNoteStore, - ParameterizedReplaceableNoteStore, - ReplaceableNoteStore, - Query, - EventBuilder, - AuthHandler, - Connection, - RelaySettings, - ConnectionStateSnapshot, -}; +} \ No newline at end of file diff --git a/packages/app/src/System/NoteCollection.test.ts b/packages/system/tests/NoteCollection.test.ts similarity index 93% rename from packages/app/src/System/NoteCollection.test.ts rename to packages/system/tests/NoteCollection.test.ts index 00eb6086..613bc63d 100644 --- a/packages/app/src/System/NoteCollection.test.ts +++ b/packages/system/tests/NoteCollection.test.ts @@ -1,6 +1,6 @@ -import { TaggedRawEvent } from "System"; +import { TaggedRawEvent } from "../src/Nostr"; import { describe, expect } from "@jest/globals"; -import { FlatNoteStore, ReplaceableNoteStore } from "./NoteCollection"; +import { FlatNoteStore, ReplaceableNoteStore } from "../src/NoteCollection"; describe("NoteStore", () => { describe("flat", () => { diff --git a/packages/app/src/System/Query.test.ts b/packages/system/tests/Query.test.ts similarity index 92% rename from packages/app/src/System/Query.test.ts rename to packages/system/tests/Query.test.ts index ad80a1a4..3c026319 100644 --- a/packages/app/src/System/Query.test.ts +++ b/packages/system/tests/Query.test.ts @@ -1,9 +1,9 @@ -import { Connection } from "System"; +import { Connection } from "../src"; import { describe, expect } from "@jest/globals"; -import { Query, QueryBase } from "./Query"; +import { Query } from "../src/Query"; import { getRandomValues } from "crypto"; -import { FlatNoteStore } from "./NoteCollection"; -import { RequestStrategy } from "./RequestBuilder"; +import { FlatNoteStore } from "../src/NoteCollection"; +import { RequestStrategy } from "../src/RequestBuilder"; window.crypto = {} as any; window.crypto.getRandomValues = getRandomValues as any; diff --git a/packages/app/src/System/RequestBuilder.test.ts b/packages/system/tests/RequestBuilder.test.ts similarity index 97% rename from packages/app/src/System/RequestBuilder.test.ts rename to packages/system/tests/RequestBuilder.test.ts index e2546cda..b0ec6f99 100644 --- a/packages/app/src/System/RequestBuilder.test.ts +++ b/packages/system/tests/RequestBuilder.test.ts @@ -1,5 +1,5 @@ -import { RelayCache } from "./GossipModel"; -import { RequestBuilder, RequestStrategy } from "./RequestBuilder"; +import { RelayCache } from "../src/GossipModel"; +import { RequestBuilder, RequestStrategy } from "../src/RequestBuilder"; import { describe, expect } from "@jest/globals"; const DummyCache = { diff --git a/packages/app/src/System/RequestExpander.test.ts b/packages/system/tests/RequestExpander.test.ts similarity index 96% rename from packages/app/src/System/RequestExpander.test.ts rename to packages/system/tests/RequestExpander.test.ts index 0ccb58d1..d87faf7b 100644 --- a/packages/app/src/System/RequestExpander.test.ts +++ b/packages/system/tests/RequestExpander.test.ts @@ -1,4 +1,4 @@ -import { expandFilter } from "./RequestExpander"; +import { expandFilter } from "../src/RequestExpander"; describe("RequestExpander", () => { test("expand filter", () => { diff --git a/packages/app/src/System/RequestMatcher.test.ts b/packages/system/tests/RequestMatcher.test.ts similarity index 88% rename from packages/app/src/System/RequestMatcher.test.ts rename to packages/system/tests/RequestMatcher.test.ts index 49754ff5..0b7a4b39 100644 --- a/packages/app/src/System/RequestMatcher.test.ts +++ b/packages/system/tests/RequestMatcher.test.ts @@ -1,4 +1,4 @@ -import { eventMatchesFilter } from "./RequestMatcher"; +import { eventMatchesFilter } from "../src/RequestMatcher"; describe("RequestMatcher", () => { it("should match simple filter", () => { diff --git a/packages/app/src/System/RequestMerger.test.ts b/packages/system/tests/RequestMerger.test.ts similarity index 93% rename from packages/app/src/System/RequestMerger.test.ts rename to packages/system/tests/RequestMerger.test.ts index b2020d36..d9ac51cf 100644 --- a/packages/app/src/System/RequestMerger.test.ts +++ b/packages/system/tests/RequestMerger.test.ts @@ -1,7 +1,7 @@ -import { ReqFilter } from "System"; -import { filterIncludes, flatMerge, mergeSimilar, simpleMerge } from "./RequestMerger"; -import { FlatReqFilter, expandFilter } from "./RequestExpander"; -import { distance } from "./Util"; +import { ReqFilter } from "../src"; +import { filterIncludes, flatMerge, mergeSimilar, simpleMerge } from "../src/RequestMerger"; +import { FlatReqFilter, expandFilter } from "../src/RequestExpander"; +import { distance } from "../src/Util"; describe("RequestMerger", () => { it("should simple merge authors", () => { diff --git a/packages/app/src/System/RequestSplitter.test.ts b/packages/system/tests/RequestSplitter.test.ts similarity index 96% rename from packages/app/src/System/RequestSplitter.test.ts rename to packages/system/tests/RequestSplitter.test.ts index e6caa5a7..7e639e34 100644 --- a/packages/app/src/System/RequestSplitter.test.ts +++ b/packages/system/tests/RequestSplitter.test.ts @@ -1,6 +1,6 @@ -import { ReqFilter } from "System"; +import { ReqFilter } from "../src"; import { describe, expect } from "@jest/globals"; -import { diffFilters } from "./RequestSplitter"; +import { diffFilters } from "../src/RequestSplitter"; describe("RequestSplitter", () => { test("single filter add value", () => { diff --git a/packages/system/tests/Util.test.ts b/packages/system/tests/Util.test.ts new file mode 100644 index 00000000..baebd7a2 --- /dev/null +++ b/packages/system/tests/Util.test.ts @@ -0,0 +1,117 @@ +import { distance } from "../src/Util"; + +describe("distance", () => { + it("should have 0 distance", () => { + const a = { + ids: "a", + }; + const b = { + ids: "a", + }; + expect(distance(a, b)).toEqual(0); + }); + it("should have 1 distance", () => { + const a = { + ids: "a", + }; + const b = { + ids: "b", + }; + expect(distance(a, b)).toEqual(1); + }); + it("should have 10 distance", () => { + const a = { + ids: "a", + }; + const b = { + ids: "a", + kinds: 1, + }; + expect(distance(a, b)).toEqual(10); + }); + it("should have 11 distance", () => { + const a = { + ids: "a", + }; + const b = { + ids: "b", + kinds: 1, + }; + expect(distance(a, b)).toEqual(11); + }); + it("should have 1 distance, arrays", () => { + const a = { + since: 1, + until: 100, + kinds: [1], + authors: ["kieran", "snort", "c", "d", "e"], + }; + const b = { + since: 1, + until: 100, + kinds: [6969], + authors: ["kieran", "snort", "c", "d", "e"], + }; + expect(distance(a, b)).toEqual(1); + }); + it("should have 1 distance, array change extra", () => { + const a = { + since: 1, + until: 100, + kinds: [1], + authors: ["f", "kieran", "snort", "c", "d"], + }; + const b = { + since: 1, + until: 100, + kinds: [1], + authors: ["kieran", "snort", "c", "d", "e"], + }; + expect(distance(a, b)).toEqual(1); + }); +}); + + +describe("tryParseNostrLink", () => { + it("is a valid nostr link", () => { + expect(parseNostrLink("nostr:npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg")).toMatchObject({ + id: "7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e", + type: NostrPrefix.PublicKey, + }); + expect(parseNostrLink("web+nostr:npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg")).toMatchObject({ + id: "7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e", + type: NostrPrefix.PublicKey, + }); + expect(parseNostrLink("nostr:note15449edq4qa5wzgqvh8td0q0dp6hwtes4pknsrm7eygeenhlj99xsq94wu9")).toMatchObject({ + id: "a56a5cb4150768e1200cb9d6d781ed0eaee5e6150da701efd9223399dff2294d", + type: NostrPrefix.Note, + }); + expect( + parseNostrLink( + "nostr:nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p" + ) + ).toMatchObject({ + id: "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", + type: NostrPrefix.Profile, + relays: ["wss://r.x.com", "wss://djbas.sadkb.com"], + }); + expect(parseNostrLink("nostr:nevent1qqs226juks2sw68pyqxtn4khs8ksath9uc2smfcpalvjyvuemlezjngrd87dq")).toMatchObject({ + id: "a56a5cb4150768e1200cb9d6d781ed0eaee5e6150da701efd9223399dff2294d", + type: NostrPrefix.Event, + }); + expect( + parseNostrLink( + "nostr:naddr1qqzkjurnw4ksz9thwden5te0wfjkccte9ehx7um5wghx7un8qgs2d90kkcq3nk2jry62dyf50k0h36rhpdtd594my40w9pkal876jxgrqsqqqa28pccpzu" + ) + ).toMatchObject({ + id: "ipsum", + type: NostrPrefix.Address, + relays: ["wss://relay.nostr.org"], + author: "a695f6b60119d9521934a691347d9f78e8770b56da16bb255ee286ddf9fda919", + kind: 30023, + }); + }); + test.each(["nostr:npub", "web+nostr:npub", "nostr:nevent1xxx"])("should return false for invalid nostr links", lb => { + expect(tryParseNostrLink(lb)).toBeUndefined(); + }); +}); diff --git a/packages/system/tests/setupTests.ts b/packages/system/tests/setupTests.ts new file mode 100644 index 00000000..4930f245 --- /dev/null +++ b/packages/system/tests/setupTests.ts @@ -0,0 +1,3 @@ +import { TextEncoder, TextDecoder } from "util"; + +Object.assign(global, { TextDecoder, TextEncoder }); diff --git a/packages/system/tsconfig.json b/packages/system/tsconfig.json new file mode 100644 index 00000000..dca184be --- /dev/null +++ b/packages/system/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "baseUrl": "src", + "target": "ES2020", + "moduleResolution": "node", + "esModuleInterop": true, + "noImplicitOverride": true, + "module": "CommonJS", + "strict": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "dist", + "skipLibCheck": true + }, + "include": ["src"], + "files": ["src/index.ts"] +} diff --git a/packages/app/src/System/worker.ts b/packages/system/worker.ts similarity index 88% rename from packages/app/src/System/worker.ts rename to packages/system/worker.ts index 4f47dad8..88315188 100644 --- a/packages/app/src/System/worker.ts +++ b/packages/system/worker.ts @@ -1,5 +1,5 @@ /// -import { UsersRelaysCache } from "Cache/UserRelayCache"; +import { UsersRelaysCache } from "../Cache/UserRelayCache"; import { NostrSystem } from "."; declare const self: SharedWorkerGlobalScope; From ae655dfc69fddb365629681157ecc26eef1610eb Mon Sep 17 00:00:00 2001 From: Kieran Date: Fri, 9 Jun 2023 00:43:21 +0200 Subject: [PATCH 20/24] cleanup --- .gitignore | 3 + packages/system/.npmignore | 3 +- packages/system/dist/Connection.d.ts | 90 ----- packages/system/dist/Connection.d.ts.map | 1 - packages/system/dist/Connection.js | 343 ------------------ packages/system/dist/Connection.js.map | 1 - packages/system/dist/ConnectionStats.d.ts | 30 -- packages/system/dist/ConnectionStats.d.ts.map | 1 - packages/system/dist/ConnectionStats.js | 36 -- packages/system/dist/ConnectionStats.js.map | 1 - packages/system/dist/Const.d.ts | 13 - packages/system/dist/Const.d.ts.map | 1 - packages/system/dist/Const.js | 17 - packages/system/dist/Const.js.map | 1 - packages/system/dist/EventBuilder.d.ts | 20 - packages/system/dist/EventBuilder.d.ts.map | 1 - packages/system/dist/EventBuilder.js | 113 ------ packages/system/dist/EventBuilder.js.map | 1 - packages/system/dist/EventExt.d.ts | 42 --- packages/system/dist/EventExt.d.ts.map | 1 - packages/system/dist/EventExt.js | 175 --------- packages/system/dist/EventExt.js.map | 1 - packages/system/dist/EventKind.d.ts | 29 -- packages/system/dist/EventKind.d.ts.map | 1 - packages/system/dist/EventKind.js | 32 -- packages/system/dist/EventKind.js.map | 1 - packages/system/dist/EventPublisher.d.ts | 66 ---- packages/system/dist/EventPublisher.d.ts.map | 1 - packages/system/dist/EventPublisher.js | 295 --------------- packages/system/dist/EventPublisher.js.map | 1 - packages/system/dist/ExternalStore.d.ts | 13 - packages/system/dist/ExternalStore.d.ts.map | 1 - packages/system/dist/ExternalStore.js | 49 --- packages/system/dist/ExternalStore.js.map | 1 - packages/system/dist/GossipModel.d.ts | 20 - packages/system/dist/GossipModel.d.ts.map | 1 - packages/system/dist/GossipModel.js | 102 ------ packages/system/dist/GossipModel.js.map | 1 - packages/system/dist/Links.d.ts | 24 -- packages/system/dist/Links.d.ts.map | 1 - packages/system/dist/Links.js | 102 ------ packages/system/dist/Links.js.map | 1 - packages/system/dist/Nips.d.ts | 4 - packages/system/dist/Nips.d.ts.map | 1 - packages/system/dist/Nips.js | 8 - packages/system/dist/Nips.js.map | 1 - packages/system/dist/Nostr.d.ts | 75 ---- packages/system/dist/Nostr.d.ts.map | 1 - packages/system/dist/Nostr.js | 15 - packages/system/dist/Nostr.js.map | 1 - packages/system/dist/NostrLink.d.ts | 13 - packages/system/dist/NostrLink.d.ts.map | 1 - packages/system/dist/NostrLink.js | 110 ------ packages/system/dist/NostrLink.js.map | 1 - packages/system/dist/NostrSystem.d.ts | 75 ---- packages/system/dist/NostrSystem.d.ts.map | 1 - packages/system/dist/NostrSystem.js | 237 ------------ packages/system/dist/NostrSystem.js.map | 1 - packages/system/dist/NoteCollection.d.ts | 91 ----- packages/system/dist/NoteCollection.d.ts.map | 1 - packages/system/dist/NoteCollection.js | 240 ------------ packages/system/dist/NoteCollection.js.map | 1 - packages/system/dist/ProfileCache.d.ts | 20 - packages/system/dist/ProfileCache.d.ts.map | 1 - packages/system/dist/ProfileCache.js | 129 ------- packages/system/dist/ProfileCache.js.map | 1 - packages/system/dist/Query.d.ts | 86 ----- packages/system/dist/Query.d.ts.map | 1 - packages/system/dist/Query.js | 228 ------------ packages/system/dist/Query.js.map | 1 - packages/system/dist/RelayInfo.d.ts | 17 - packages/system/dist/RelayInfo.d.ts.map | 1 - packages/system/dist/RelayInfo.js | 3 - packages/system/dist/RelayInfo.js.map | 1 - packages/system/dist/RequestBuilder.d.ts | 93 ----- packages/system/dist/RequestBuilder.d.ts.map | 1 - packages/system/dist/RequestBuilder.js | 225 ------------ packages/system/dist/RequestBuilder.js.map | 1 - packages/system/dist/RequestExpander.d.ts | 20 - packages/system/dist/RequestExpander.d.ts.map | 1 - packages/system/dist/RequestExpander.js | 31 -- packages/system/dist/RequestExpander.js.map | 1 - packages/system/dist/RequestMatcher.d.ts | 3 - packages/system/dist/RequestMatcher.d.ts.map | 1 - packages/system/dist/RequestMatcher.js | 23 -- packages/system/dist/RequestMatcher.js.map | 1 - packages/system/dist/RequestMerger.d.ts | 24 -- packages/system/dist/RequestMerger.d.ts.map | 1 - packages/system/dist/RequestMerger.js | 150 -------- packages/system/dist/RequestMerger.js.map | 1 - packages/system/dist/RequestSplitter.d.ts | 7 - packages/system/dist/RequestSplitter.d.ts.map | 1 - packages/system/dist/RequestSplitter.js | 19 - packages/system/dist/RequestSplitter.js.map | 1 - packages/system/dist/SystemWorker.d.ts | 22 -- packages/system/dist/SystemWorker.d.ts.map | 1 - packages/system/dist/SystemWorker.js | 66 ---- packages/system/dist/SystemWorker.js.map | 1 - packages/system/dist/Tag.d.ts | 18 - packages/system/dist/Tag.d.ts.map | 1 - packages/system/dist/Tag.js | 75 ---- packages/system/dist/Tag.js.map | 1 - packages/system/dist/Util.d.ts | 23 -- packages/system/dist/Util.d.ts.map | 1 - packages/system/dist/Util.js | 148 -------- packages/system/dist/Util.js.map | 1 - packages/system/dist/WorkQueue.d.ts | 8 - packages/system/dist/WorkQueue.d.ts.map | 1 - packages/system/dist/WorkQueue.js | 30 -- packages/system/dist/WorkQueue.js.map | 1 - packages/system/dist/cache/index.d.ts | 48 --- packages/system/dist/cache/index.d.ts.map | 1 - packages/system/dist/cache/index.js | 21 -- packages/system/dist/cache/index.js.map | 1 - packages/system/dist/index.d.ts | 44 --- packages/system/dist/index.d.ts.map | 1 - packages/system/dist/index.js | 39 -- packages/system/dist/index.js.map | 1 - packages/system/package.json | 10 +- packages/system/snort-system-1.0.0.tgz | Bin 56011 -> 0 bytes packages/system/src/RequestMerger.ts | 66 +++- packages/system/src/RequestSplitter.ts | 6 +- packages/system/src/Util.ts | 46 +-- 123 files changed, 93 insertions(+), 4198 deletions(-) delete mode 100644 packages/system/dist/Connection.d.ts delete mode 100644 packages/system/dist/Connection.d.ts.map delete mode 100644 packages/system/dist/Connection.js delete mode 100644 packages/system/dist/Connection.js.map delete mode 100644 packages/system/dist/ConnectionStats.d.ts delete mode 100644 packages/system/dist/ConnectionStats.d.ts.map delete mode 100644 packages/system/dist/ConnectionStats.js delete mode 100644 packages/system/dist/ConnectionStats.js.map delete mode 100644 packages/system/dist/Const.d.ts delete mode 100644 packages/system/dist/Const.d.ts.map delete mode 100644 packages/system/dist/Const.js delete mode 100644 packages/system/dist/Const.js.map delete mode 100644 packages/system/dist/EventBuilder.d.ts delete mode 100644 packages/system/dist/EventBuilder.d.ts.map delete mode 100644 packages/system/dist/EventBuilder.js delete mode 100644 packages/system/dist/EventBuilder.js.map delete mode 100644 packages/system/dist/EventExt.d.ts delete mode 100644 packages/system/dist/EventExt.d.ts.map delete mode 100644 packages/system/dist/EventExt.js delete mode 100644 packages/system/dist/EventExt.js.map delete mode 100644 packages/system/dist/EventKind.d.ts delete mode 100644 packages/system/dist/EventKind.d.ts.map delete mode 100644 packages/system/dist/EventKind.js delete mode 100644 packages/system/dist/EventKind.js.map delete mode 100644 packages/system/dist/EventPublisher.d.ts delete mode 100644 packages/system/dist/EventPublisher.d.ts.map delete mode 100644 packages/system/dist/EventPublisher.js delete mode 100644 packages/system/dist/EventPublisher.js.map delete mode 100644 packages/system/dist/ExternalStore.d.ts delete mode 100644 packages/system/dist/ExternalStore.d.ts.map delete mode 100644 packages/system/dist/ExternalStore.js delete mode 100644 packages/system/dist/ExternalStore.js.map delete mode 100644 packages/system/dist/GossipModel.d.ts delete mode 100644 packages/system/dist/GossipModel.d.ts.map delete mode 100644 packages/system/dist/GossipModel.js delete mode 100644 packages/system/dist/GossipModel.js.map delete mode 100644 packages/system/dist/Links.d.ts delete mode 100644 packages/system/dist/Links.d.ts.map delete mode 100644 packages/system/dist/Links.js delete mode 100644 packages/system/dist/Links.js.map delete mode 100644 packages/system/dist/Nips.d.ts delete mode 100644 packages/system/dist/Nips.d.ts.map delete mode 100644 packages/system/dist/Nips.js delete mode 100644 packages/system/dist/Nips.js.map delete mode 100644 packages/system/dist/Nostr.d.ts delete mode 100644 packages/system/dist/Nostr.d.ts.map delete mode 100644 packages/system/dist/Nostr.js delete mode 100644 packages/system/dist/Nostr.js.map delete mode 100644 packages/system/dist/NostrLink.d.ts delete mode 100644 packages/system/dist/NostrLink.d.ts.map delete mode 100644 packages/system/dist/NostrLink.js delete mode 100644 packages/system/dist/NostrLink.js.map delete mode 100644 packages/system/dist/NostrSystem.d.ts delete mode 100644 packages/system/dist/NostrSystem.d.ts.map delete mode 100644 packages/system/dist/NostrSystem.js delete mode 100644 packages/system/dist/NostrSystem.js.map delete mode 100644 packages/system/dist/NoteCollection.d.ts delete mode 100644 packages/system/dist/NoteCollection.d.ts.map delete mode 100644 packages/system/dist/NoteCollection.js delete mode 100644 packages/system/dist/NoteCollection.js.map delete mode 100644 packages/system/dist/ProfileCache.d.ts delete mode 100644 packages/system/dist/ProfileCache.d.ts.map delete mode 100644 packages/system/dist/ProfileCache.js delete mode 100644 packages/system/dist/ProfileCache.js.map delete mode 100644 packages/system/dist/Query.d.ts delete mode 100644 packages/system/dist/Query.d.ts.map delete mode 100644 packages/system/dist/Query.js delete mode 100644 packages/system/dist/Query.js.map delete mode 100644 packages/system/dist/RelayInfo.d.ts delete mode 100644 packages/system/dist/RelayInfo.d.ts.map delete mode 100644 packages/system/dist/RelayInfo.js delete mode 100644 packages/system/dist/RelayInfo.js.map delete mode 100644 packages/system/dist/RequestBuilder.d.ts delete mode 100644 packages/system/dist/RequestBuilder.d.ts.map delete mode 100644 packages/system/dist/RequestBuilder.js delete mode 100644 packages/system/dist/RequestBuilder.js.map delete mode 100644 packages/system/dist/RequestExpander.d.ts delete mode 100644 packages/system/dist/RequestExpander.d.ts.map delete mode 100644 packages/system/dist/RequestExpander.js delete mode 100644 packages/system/dist/RequestExpander.js.map delete mode 100644 packages/system/dist/RequestMatcher.d.ts delete mode 100644 packages/system/dist/RequestMatcher.d.ts.map delete mode 100644 packages/system/dist/RequestMatcher.js delete mode 100644 packages/system/dist/RequestMatcher.js.map delete mode 100644 packages/system/dist/RequestMerger.d.ts delete mode 100644 packages/system/dist/RequestMerger.d.ts.map delete mode 100644 packages/system/dist/RequestMerger.js delete mode 100644 packages/system/dist/RequestMerger.js.map delete mode 100644 packages/system/dist/RequestSplitter.d.ts delete mode 100644 packages/system/dist/RequestSplitter.d.ts.map delete mode 100644 packages/system/dist/RequestSplitter.js delete mode 100644 packages/system/dist/RequestSplitter.js.map delete mode 100644 packages/system/dist/SystemWorker.d.ts delete mode 100644 packages/system/dist/SystemWorker.d.ts.map delete mode 100644 packages/system/dist/SystemWorker.js delete mode 100644 packages/system/dist/SystemWorker.js.map delete mode 100644 packages/system/dist/Tag.d.ts delete mode 100644 packages/system/dist/Tag.d.ts.map delete mode 100644 packages/system/dist/Tag.js delete mode 100644 packages/system/dist/Tag.js.map delete mode 100644 packages/system/dist/Util.d.ts delete mode 100644 packages/system/dist/Util.d.ts.map delete mode 100644 packages/system/dist/Util.js delete mode 100644 packages/system/dist/Util.js.map delete mode 100644 packages/system/dist/WorkQueue.d.ts delete mode 100644 packages/system/dist/WorkQueue.d.ts.map delete mode 100644 packages/system/dist/WorkQueue.js delete mode 100644 packages/system/dist/WorkQueue.js.map delete mode 100644 packages/system/dist/cache/index.d.ts delete mode 100644 packages/system/dist/cache/index.d.ts.map delete mode 100644 packages/system/dist/cache/index.js delete mode 100644 packages/system/dist/cache/index.js.map delete mode 100644 packages/system/dist/index.d.ts delete mode 100644 packages/system/dist/index.d.ts.map delete mode 100644 packages/system/dist/index.js delete mode 100644 packages/system/dist/index.js.map delete mode 100644 packages/system/snort-system-1.0.0.tgz diff --git a/.gitignore b/.gitignore index 5bd99317..3fb207a9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ node_modules/ .idea .yarn yarn.lock +dist/ +*.tgz +*.log \ No newline at end of file diff --git a/packages/system/.npmignore b/packages/system/.npmignore index 9b5e52ea..91aa0ab2 100644 --- a/packages/system/.npmignore +++ b/packages/system/.npmignore @@ -2,4 +2,5 @@ tests/ src/ *.tgz jest.config.js -worker.ts \ No newline at end of file +worker.ts +yarn* \ No newline at end of file diff --git a/packages/system/dist/Connection.d.ts b/packages/system/dist/Connection.d.ts deleted file mode 100644 index b8205e0c..00000000 --- a/packages/system/dist/Connection.d.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { ConnectionStats } from "./ConnectionStats"; -import { NostrEvent, ReqCommand, TaggedRawEvent, u256 } from "./Nostr"; -import { RelayInfo } from "./RelayInfo"; -import ExternalStore from "./ExternalStore"; -export type AuthHandler = (challenge: string, relay: string) => Promise; -/** - * Relay settings - */ -export interface RelaySettings { - read: boolean; - write: boolean; -} -/** - * Snapshot of connection stats - */ -export interface ConnectionStateSnapshot { - connected: boolean; - disconnects: number; - avgLatency: number; - events: { - received: number; - send: number; - }; - settings?: RelaySettings; - info?: RelayInfo; - pendingRequests: Array; - activeRequests: Array; - id: string; - ephemeral: boolean; - address: string; -} -export declare class Connection extends ExternalStore { - #private; - Id: string; - Address: string; - Socket: WebSocket | null; - PendingRaw: Array; - PendingRequests: Array<{ - cmd: ReqCommand; - cb: () => void; - }>; - ActiveRequests: Set; - Settings: RelaySettings; - Info?: RelayInfo; - ConnectTimeout: number; - Stats: ConnectionStats; - HasStateChange: boolean; - IsClosed: boolean; - ReconnectTimer: ReturnType | null; - EventsCallback: Map void>; - OnConnected?: () => void; - OnEvent?: (sub: string, e: TaggedRawEvent) => void; - OnEose?: (sub: string) => void; - OnDisconnect?: (id: string) => void; - Auth?: AuthHandler; - AwaitingAuth: Map; - Authed: boolean; - Ephemeral: boolean; - EphemeralTimeout: ReturnType | undefined; - Down: boolean; - constructor(addr: string, options: RelaySettings, auth?: AuthHandler, ephemeral?: boolean); - ResetEphemeralTimeout(): void; - Connect(): Promise; - Close(): void; - OnOpen(): void; - OnClose(e: CloseEvent): void; - OnMessage(e: MessageEvent): void; - OnError(e: Event): void; - /** - * Send event on this connection - */ - SendEvent(e: NostrEvent): void; - /** - * Send event on this connection and wait for OK response - */ - SendAsync(e: NostrEvent, timeout?: number): Promise; - /** - * Using relay document to determine if this relay supports a feature - */ - SupportsNip(n: number): boolean; - /** - * Queue or send command to the relay - * @param cmd The REQ to send to the server - */ - QueueReq(cmd: ReqCommand, cbSent: () => void): void; - CloseReq(id: string): void; - takeSnapshot(): ConnectionStateSnapshot; - _OnAuthAsync(challenge: string): Promise; -} -//# sourceMappingURL=Connection.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Connection.d.ts.map b/packages/system/dist/Connection.d.ts.map deleted file mode 100644 index 61f78755..00000000 --- a/packages/system/dist/Connection.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Connection.d.ts","sourceRoot":"","sources":["../src/Connection.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/Connection.js b/packages/system/dist/Connection.js deleted file mode 100644 index a33148da..00000000 --- a/packages/system/dist/Connection.js +++ /dev/null @@ -1,343 +0,0 @@ -"use strict"; -var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); - return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -var _Connection_instances, _Connection_SendQueuedRequests, _Connection_ResetQueues, _Connection_SendJson, _Connection_sendPendingRaw, _Connection_sendOnWire, _Connection_maxSubscriptions_get; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Connection = void 0; -const uuid_1 = require("uuid"); -const Const_1 = require("./Const"); -const ConnectionStats_1 = require("./ConnectionStats"); -const Util_1 = require("./Util"); -const ExternalStore_1 = __importDefault(require("./ExternalStore")); -class Connection extends ExternalStore_1.default { - constructor(addr, options, auth, ephemeral = false) { - super(); - _Connection_instances.add(this); - this.Socket = null; - this.PendingRaw = []; - this.PendingRequests = []; - this.ActiveRequests = new Set(); - this.ConnectTimeout = Const_1.DefaultConnectTimeout; - this.Stats = new ConnectionStats_1.ConnectionStats(); - this.HasStateChange = true; - this.Authed = false; - this.Down = true; - this.Id = (0, uuid_1.v4)(); - this.Address = addr; - this.Settings = options; - this.IsClosed = false; - this.ReconnectTimer = null; - this.EventsCallback = new Map(); - this.AwaitingAuth = new Map(); - this.Auth = auth; - this.Ephemeral = ephemeral; - } - ResetEphemeralTimeout() { - if (this.EphemeralTimeout) { - clearTimeout(this.EphemeralTimeout); - } - if (this.Ephemeral) { - this.EphemeralTimeout = setTimeout(() => { - this.Close(); - }, 30000); - } - } - async Connect() { - try { - if (this.Info === undefined) { - const u = new URL(this.Address); - const rsp = await fetch(`${u.protocol === "wss:" ? "https:" : "http:"}//${u.host}`, { - headers: { - accept: "application/nostr+json", - }, - }); - if (rsp.ok) { - const data = await rsp.json(); - for (const [k, v] of Object.entries(data)) { - if (v === "unset" || v === "" || v === "~") { - data[k] = undefined; - } - } - this.Info = data; - } - } - } - catch (e) { - console.warn("Could not load relay information", e); - } - if (this.Socket) { - this.Id = (0, uuid_1.v4)(); - this.Socket.onopen = null; - this.Socket.onmessage = null; - this.Socket.onerror = null; - this.Socket.onclose = null; - } - this.IsClosed = false; - this.Socket = new WebSocket(this.Address); - this.Socket.onopen = () => this.OnOpen(); - this.Socket.onmessage = e => this.OnMessage(e); - this.Socket.onerror = e => this.OnError(e); - this.Socket.onclose = e => this.OnClose(e); - } - Close() { - this.IsClosed = true; - if (this.ReconnectTimer !== null) { - clearTimeout(this.ReconnectTimer); - this.ReconnectTimer = null; - } - this.Socket?.close(); - this.notifyChange(); - } - OnOpen() { - this.ConnectTimeout = Const_1.DefaultConnectTimeout; - console.log(`[${this.Address}] Open!`); - this.Down = false; - if (this.Ephemeral) { - this.ResetEphemeralTimeout(); - } - this.OnConnected?.(); - __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_sendPendingRaw).call(this); - } - OnClose(e) { - if (!this.IsClosed) { - this.ConnectTimeout = this.ConnectTimeout * 2; - console.log(`[${this.Address}] Closed (${e.reason}), trying again in ${(this.ConnectTimeout / 1000) - .toFixed(0) - .toLocaleString()} sec`); - this.ReconnectTimer = setTimeout(() => { - this.Connect(); - }, this.ConnectTimeout); - this.Stats.Disconnects++; - } - else { - console.log(`[${this.Address}] Closed!`); - this.ReconnectTimer = null; - } - this.OnDisconnect?.(this.Id); - __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_ResetQueues).call(this); - // reset connection Id on disconnect, for query-tracking - this.Id = (0, uuid_1.v4)(); - this.notifyChange(); - } - OnMessage(e) { - if (e.data.length > 0) { - const msg = JSON.parse(e.data); - const tag = msg[0]; - switch (tag) { - case "AUTH": { - this._OnAuthAsync(msg[1]) - .then(() => __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_sendPendingRaw).call(this)) - .catch(console.error); - this.Stats.EventsReceived++; - this.notifyChange(); - break; - } - case "EVENT": { - this.OnEvent?.(msg[1], { - ...msg[2], - relays: [this.Address], - }); - this.Stats.EventsReceived++; - this.notifyChange(); - break; - } - case "EOSE": { - this.OnEose?.(msg[1]); - break; - } - case "OK": { - // feedback to broadcast call - console.debug(`${this.Address} OK: `, msg); - const id = msg[1]; - if (this.EventsCallback.has(id)) { - const cb = (0, Util_1.unwrap)(this.EventsCallback.get(id)); - this.EventsCallback.delete(id); - cb(msg); - } - break; - } - case "NOTICE": { - console.warn(`[${this.Address}] NOTICE: ${msg[1]}`); - break; - } - default: { - console.warn(`Unknown tag: ${tag}`); - break; - } - } - } - } - OnError(e) { - console.error(e); - this.notifyChange(); - } - /** - * Send event on this connection - */ - SendEvent(e) { - if (!this.Settings.write) { - return; - } - const req = ["EVENT", e]; - __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_SendJson).call(this, req); - this.Stats.EventsSent++; - this.notifyChange(); - } - /** - * Send event on this connection and wait for OK response - */ - async SendAsync(e, timeout = 5000) { - return new Promise(resolve => { - if (!this.Settings.write) { - resolve(); - return; - } - const t = setTimeout(() => { - resolve(); - }, timeout); - this.EventsCallback.set(e.id, () => { - clearTimeout(t); - resolve(); - }); - const req = ["EVENT", e]; - __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_SendJson).call(this, req); - this.Stats.EventsSent++; - this.notifyChange(); - }); - } - /** - * Using relay document to determine if this relay supports a feature - */ - SupportsNip(n) { - return this.Info?.supported_nips?.some(a => a === n) ?? false; - } - /** - * Queue or send command to the relay - * @param cmd The REQ to send to the server - */ - QueueReq(cmd, cbSent) { - if (this.ActiveRequests.size >= __classPrivateFieldGet(this, _Connection_instances, "a", _Connection_maxSubscriptions_get)) { - this.PendingRequests.push({ - cmd, - cb: cbSent, - }); - console.debug("Queuing:", this.Address, cmd); - } - else { - this.ActiveRequests.add(cmd[1]); - __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_SendJson).call(this, cmd); - cbSent(); - } - this.notifyChange(); - } - CloseReq(id) { - if (this.ActiveRequests.delete(id)) { - __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_SendJson).call(this, ["CLOSE", id]); - this.OnEose?.(id); - __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_SendQueuedRequests).call(this); - } - this.notifyChange(); - } - takeSnapshot() { - return { - connected: this.Socket?.readyState === WebSocket.OPEN, - events: { - received: this.Stats.EventsReceived, - send: this.Stats.EventsSent, - }, - avgLatency: this.Stats.Latency.length > 0 - ? this.Stats.Latency.reduce((acc, v) => acc + v, 0) / this.Stats.Latency.length - : 0, - disconnects: this.Stats.Disconnects, - info: this.Info, - id: this.Id, - pendingRequests: [...this.PendingRequests.map(a => a.cmd[1])], - activeRequests: [...this.ActiveRequests], - ephemeral: this.Ephemeral, - address: this.Address, - }; - } - async _OnAuthAsync(challenge) { - const authCleanup = () => { - this.AwaitingAuth.delete(challenge); - }; - if (!this.Auth) { - throw new Error("Auth hook not registered"); - } - this.AwaitingAuth.set(challenge, true); - const authEvent = await this.Auth(challenge, this.Address); - return new Promise(resolve => { - if (!authEvent) { - authCleanup(); - return Promise.reject("no event"); - } - const t = setTimeout(() => { - authCleanup(); - resolve(); - }, 10000); - this.EventsCallback.set(authEvent.id, (msg) => { - clearTimeout(t); - authCleanup(); - if (msg.length > 3 && msg[2] === true) { - this.Authed = true; - } - resolve(); - }); - __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_sendOnWire).call(this, ["AUTH", authEvent]); - }); - } -} -exports.Connection = Connection; -_Connection_instances = new WeakSet(), _Connection_SendQueuedRequests = function _Connection_SendQueuedRequests() { - const canSend = __classPrivateFieldGet(this, _Connection_instances, "a", _Connection_maxSubscriptions_get) - this.ActiveRequests.size; - if (canSend > 0) { - for (let x = 0; x < canSend; x++) { - const p = this.PendingRequests.shift(); - if (p) { - this.ActiveRequests.add(p.cmd[1]); - __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_SendJson).call(this, p.cmd); - p.cb(); - console.debug("Sent pending REQ", this.Address, p.cmd); - } - } - } -}, _Connection_ResetQueues = function _Connection_ResetQueues() { - this.ActiveRequests.clear(); - this.PendingRequests = []; - this.PendingRaw = []; - this.notifyChange(); -}, _Connection_SendJson = function _Connection_SendJson(obj) { - const authPending = !this.Authed && (this.AwaitingAuth.size > 0 || this.Info?.limitation?.auth_required === true); - if (this.Socket?.readyState !== WebSocket.OPEN || authPending) { - this.PendingRaw.push(obj); - if (this.Socket?.readyState === WebSocket.CLOSED && this.Ephemeral && this.IsClosed) { - this.Connect(); - } - return false; - } - __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_sendPendingRaw).call(this); - __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_sendOnWire).call(this, obj); -}, _Connection_sendPendingRaw = function _Connection_sendPendingRaw() { - while (this.PendingRaw.length > 0) { - const next = this.PendingRaw.shift(); - if (next) { - __classPrivateFieldGet(this, _Connection_instances, "m", _Connection_sendOnWire).call(this, next); - } - } -}, _Connection_sendOnWire = function _Connection_sendOnWire(obj) { - if (this.Socket?.readyState !== WebSocket.OPEN) { - throw new Error(`Socket is not open, state is ${this.Socket?.readyState}`); - } - const json = JSON.stringify(obj); - this.Socket.send(json); - return true; -}, _Connection_maxSubscriptions_get = function _Connection_maxSubscriptions_get() { - return this.Info?.limitation?.max_subscriptions ?? 25; -}; -//# sourceMappingURL=Connection.js.map \ No newline at end of file diff --git a/packages/system/dist/Connection.js.map b/packages/system/dist/Connection.js.map deleted file mode 100644 index 67233092..00000000 --- a/packages/system/dist/Connection.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Connection.js","sourceRoot":"","sources":["../src/Connection.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/ConnectionStats.d.ts b/packages/system/dist/ConnectionStats.d.ts deleted file mode 100644 index 43be570b..00000000 --- a/packages/system/dist/ConnectionStats.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Stats class for tracking metrics per connection - */ -export declare class ConnectionStats { - /** - * Last n records of how long between REQ->EOSE - */ - Latency: number[]; - /** - * Total number of REQ's sent on this connection - */ - Subs: number; - /** - * Count of REQ which took too long and where abandoned - */ - SubsTimeout: number; - /** - * Total number of EVENT messages received - */ - EventsReceived: number; - /** - * Total number of EVENT messages sent - */ - EventsSent: number; - /** - * Total number of times this connection was lost - */ - Disconnects: number; -} -//# sourceMappingURL=ConnectionStats.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/ConnectionStats.d.ts.map b/packages/system/dist/ConnectionStats.d.ts.map deleted file mode 100644 index 359e6887..00000000 --- a/packages/system/dist/ConnectionStats.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ConnectionStats.d.ts","sourceRoot":"","sources":["../src/ConnectionStats.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,eAAe;IAC1B;;OAEG;IACH,OAAO,EAAE,MAAM,EAAE,CAAM;IAEvB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAK;IAEjB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAK;IAExB;;OAEG;IACH,cAAc,EAAE,MAAM,CAAK;IAE3B;;OAEG;IACH,UAAU,EAAE,MAAM,CAAK;IAEvB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAK;CACzB"} \ No newline at end of file diff --git a/packages/system/dist/ConnectionStats.js b/packages/system/dist/ConnectionStats.js deleted file mode 100644 index 334cba6d..00000000 --- a/packages/system/dist/ConnectionStats.js +++ /dev/null @@ -1,36 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ConnectionStats = void 0; -/** - * Stats class for tracking metrics per connection - */ -class ConnectionStats { - constructor() { - /** - * Last n records of how long between REQ->EOSE - */ - this.Latency = []; - /** - * Total number of REQ's sent on this connection - */ - this.Subs = 0; - /** - * Count of REQ which took too long and where abandoned - */ - this.SubsTimeout = 0; - /** - * Total number of EVENT messages received - */ - this.EventsReceived = 0; - /** - * Total number of EVENT messages sent - */ - this.EventsSent = 0; - /** - * Total number of times this connection was lost - */ - this.Disconnects = 0; - } -} -exports.ConnectionStats = ConnectionStats; -//# sourceMappingURL=ConnectionStats.js.map \ No newline at end of file diff --git a/packages/system/dist/ConnectionStats.js.map b/packages/system/dist/ConnectionStats.js.map deleted file mode 100644 index 90da8778..00000000 --- a/packages/system/dist/ConnectionStats.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ConnectionStats.js","sourceRoot":"","sources":["../src/ConnectionStats.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,MAAa,eAAe;IAA5B;QACE;;WAEG;QACH,YAAO,GAAa,EAAE,CAAC;QAEvB;;WAEG;QACH,SAAI,GAAW,CAAC,CAAC;QAEjB;;WAEG;QACH,gBAAW,GAAW,CAAC,CAAC;QAExB;;WAEG;QACH,mBAAc,GAAW,CAAC,CAAC;QAE3B;;WAEG;QACH,eAAU,GAAW,CAAC,CAAC;QAEvB;;WAEG;QACH,gBAAW,GAAW,CAAC,CAAC;IAC1B,CAAC;CAAA;AA9BD,0CA8BC"} \ No newline at end of file diff --git a/packages/system/dist/Const.d.ts b/packages/system/dist/Const.d.ts deleted file mode 100644 index 2bf82921..00000000 --- a/packages/system/dist/Const.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Websocket re-connect timeout - */ -export declare const DefaultConnectTimeout = 2000; -/** - * Hashtag regex - */ -export declare const HashtagRegex: RegExp; -/** - * How long profile cache should be considered valid for - */ -export declare const ProfileCacheExpire: number; -//# sourceMappingURL=Const.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Const.d.ts.map b/packages/system/dist/Const.d.ts.map deleted file mode 100644 index 222ed02a..00000000 --- a/packages/system/dist/Const.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Const.d.ts","sourceRoot":"","sources":["../src/Const.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C;;GAEG;AAEH,eAAO,MAAM,YAAY,QAA4C,CAAC;AAGtE;;GAEG;AACF,eAAO,MAAM,kBAAkB,QAAsB,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/Const.js b/packages/system/dist/Const.js deleted file mode 100644 index 79573a84..00000000 --- a/packages/system/dist/Const.js +++ /dev/null @@ -1,17 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ProfileCacheExpire = exports.HashtagRegex = exports.DefaultConnectTimeout = void 0; -/** - * Websocket re-connect timeout - */ -exports.DefaultConnectTimeout = 2000; -/** - * Hashtag regex - */ -// eslint-disable-next-line no-useless-escape -exports.HashtagRegex = /(#[^\s!@#$%^&*()=+.\/,\[{\]};:'"?><]+)/g; -/** - * How long profile cache should be considered valid for - */ -exports.ProfileCacheExpire = 1000 * 60 * 60 * 6; -//# sourceMappingURL=Const.js.map \ No newline at end of file diff --git a/packages/system/dist/Const.js.map b/packages/system/dist/Const.js.map deleted file mode 100644 index 113dff7b..00000000 --- a/packages/system/dist/Const.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Const.js","sourceRoot":"","sources":["../src/Const.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACU,QAAA,qBAAqB,GAAG,IAAI,CAAC;AAE1C;;GAEG;AACH,6CAA6C;AAChC,QAAA,YAAY,GAAG,yCAAyC,CAAC;AAGtE;;GAEG;AACW,QAAA,kBAAkB,GAAG,IAAK,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/EventBuilder.d.ts b/packages/system/dist/EventBuilder.d.ts deleted file mode 100644 index 66b979b2..00000000 --- a/packages/system/dist/EventBuilder.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { EventKind, HexKey, NostrEvent } from "."; -export declare class EventBuilder { - #private; - kind(k: EventKind): this; - content(c: string): this; - createdAt(n: number): this; - pubKey(k: string): this; - tag(t: Array): EventBuilder; - /** - * Extract mentions - */ - processContent(): this; - build(): NostrEvent; - /** - * Build and sign event - * @param pk Private key to sign event with - */ - buildAndSign(pk: HexKey): Promise; -} -//# sourceMappingURL=EventBuilder.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/EventBuilder.d.ts.map b/packages/system/dist/EventBuilder.d.ts.map deleted file mode 100644 index 05679aaa..00000000 --- a/packages/system/dist/EventBuilder.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EventBuilder.d.ts","sourceRoot":"","sources":["../src/EventBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAe,UAAU,EAAE,MAAM,GAAG,CAAC;AAM/D,qBAAa,YAAY;;IAOvB,IAAI,CAAC,CAAC,EAAE,SAAS;IAKjB,OAAO,CAAC,CAAC,EAAE,MAAM;IAKjB,SAAS,CAAC,CAAC,EAAE,MAAM;IAKnB,MAAM,CAAC,CAAC,EAAE,MAAM;IAKhB,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,YAAY;IAOnC;;OAEG;IACH,cAAc;IAcd,KAAK;IAcL;;;OAGG;IACG,YAAY,CAAC,EAAE,EAAE,MAAM;CAgC9B"} \ No newline at end of file diff --git a/packages/system/dist/EventBuilder.js b/packages/system/dist/EventBuilder.js deleted file mode 100644 index cc346664..00000000 --- a/packages/system/dist/EventBuilder.js +++ /dev/null @@ -1,113 +0,0 @@ -"use strict"; -var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { - if (kind === "m") throw new TypeError("Private method is not writable"); - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); - return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; -}; -var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); - return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -}; -var _EventBuilder_instances, _EventBuilder_kind, _EventBuilder_content, _EventBuilder_createdAt, _EventBuilder_pubkey, _EventBuilder_tags, _EventBuilder_validate, _EventBuilder_replaceMention, _EventBuilder_addHashtag; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EventBuilder = void 0; -const _1 = require("."); -const Const_1 = require("./Const"); -const Util_1 = require("./Util"); -const EventExt_1 = require("./EventExt"); -const NostrLink_1 = require("./NostrLink"); -class EventBuilder { - constructor() { - _EventBuilder_instances.add(this); - _EventBuilder_kind.set(this, void 0); - _EventBuilder_content.set(this, void 0); - _EventBuilder_createdAt.set(this, void 0); - _EventBuilder_pubkey.set(this, void 0); - _EventBuilder_tags.set(this, []); - } - kind(k) { - __classPrivateFieldSet(this, _EventBuilder_kind, k, "f"); - return this; - } - content(c) { - __classPrivateFieldSet(this, _EventBuilder_content, c, "f"); - return this; - } - createdAt(n) { - __classPrivateFieldSet(this, _EventBuilder_createdAt, n, "f"); - return this; - } - pubKey(k) { - __classPrivateFieldSet(this, _EventBuilder_pubkey, k, "f"); - return this; - } - tag(t) { - const duplicate = __classPrivateFieldGet(this, _EventBuilder_tags, "f").some(a => a.length === t.length && a.every((b, i) => b !== a[i])); - if (duplicate) - return this; - __classPrivateFieldGet(this, _EventBuilder_tags, "f").push(t); - return this; - } - /** - * Extract mentions - */ - processContent() { - if (__classPrivateFieldGet(this, _EventBuilder_content, "f")) { - __classPrivateFieldSet(this, _EventBuilder_content, __classPrivateFieldGet(this, _EventBuilder_content, "f").replace(/@n(pub|profile|event|ote|addr|)1[acdefghjklmnpqrstuvwxyz023456789]+/g, m => __classPrivateFieldGet(this, _EventBuilder_instances, "m", _EventBuilder_replaceMention).call(this, m)), "f"); - const hashTags = [...__classPrivateFieldGet(this, _EventBuilder_content, "f").matchAll(Const_1.HashtagRegex)]; - hashTags.map(hashTag => { - __classPrivateFieldGet(this, _EventBuilder_instances, "m", _EventBuilder_addHashtag).call(this, hashTag[0]); - }); - } - return this; - } - build() { - __classPrivateFieldGet(this, _EventBuilder_instances, "m", _EventBuilder_validate).call(this); - const ev = { - id: "", - pubkey: __classPrivateFieldGet(this, _EventBuilder_pubkey, "f") ?? "", - content: __classPrivateFieldGet(this, _EventBuilder_content, "f") ?? "", - kind: __classPrivateFieldGet(this, _EventBuilder_kind, "f"), - created_at: __classPrivateFieldGet(this, _EventBuilder_createdAt, "f") ?? (0, Util_1.unixNow)(), - tags: __classPrivateFieldGet(this, _EventBuilder_tags, "f"), - }; - ev.id = EventExt_1.EventExt.createId(ev); - return ev; - } - /** - * Build and sign event - * @param pk Private key to sign event with - */ - async buildAndSign(pk) { - const ev = this.pubKey((0, Util_1.getPublicKey)(pk)).build(); - await EventExt_1.EventExt.sign(ev, pk); - return ev; - } -} -exports.EventBuilder = EventBuilder; -_EventBuilder_kind = new WeakMap(), _EventBuilder_content = new WeakMap(), _EventBuilder_createdAt = new WeakMap(), _EventBuilder_pubkey = new WeakMap(), _EventBuilder_tags = new WeakMap(), _EventBuilder_instances = new WeakSet(), _EventBuilder_validate = function _EventBuilder_validate() { - if (__classPrivateFieldGet(this, _EventBuilder_kind, "f") === undefined) { - throw new Error("Kind must be set"); - } - if (__classPrivateFieldGet(this, _EventBuilder_pubkey, "f") === undefined) { - throw new Error("Pubkey must be set"); - } -}, _EventBuilder_replaceMention = function _EventBuilder_replaceMention(match) { - const npub = match.slice(1); - const link = (0, NostrLink_1.parseNostrLink)(npub); - if (link) { - if (link.type === _1.NostrPrefix.Profile || link.type === _1.NostrPrefix.PublicKey) { - this.tag(["p", link.id]); - } - return `nostr:${link.encode()}`; - } - else { - return match; - } -}, _EventBuilder_addHashtag = function _EventBuilder_addHashtag(match) { - const tag = match.slice(1); - this.tag(["t", tag.toLowerCase()]); -}; -//# sourceMappingURL=EventBuilder.js.map \ No newline at end of file diff --git a/packages/system/dist/EventBuilder.js.map b/packages/system/dist/EventBuilder.js.map deleted file mode 100644 index 81650cdd..00000000 --- a/packages/system/dist/EventBuilder.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EventBuilder.js","sourceRoot":"","sources":["../src/EventBuilder.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/EventExt.d.ts b/packages/system/dist/EventExt.d.ts deleted file mode 100644 index 055c952c..00000000 --- a/packages/system/dist/EventExt.d.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { EventKind, HexKey, NostrEvent, Tag } from "."; -export interface Thread { - root?: Tag; - replyTo?: Tag; - mentions: Array; - pubKeys: Array; -} -export declare abstract class EventExt { - #private; - /** - * Get the pub key of the creator of this event NIP-26 - */ - static getRootPubKey(e: NostrEvent): HexKey; - /** - * Sign this message with a private key - */ - static sign(e: NostrEvent, key: HexKey): void; - /** - * Check the signature of this message - * @returns True if valid signature - */ - static verify(e: NostrEvent): boolean; - static createId(e: NostrEvent): string; - /** - * Create a new event for a specific pubkey - */ - static forPubKey(pk: HexKey, kind: EventKind): NostrEvent; - static extractThread(ev: NostrEvent): Thread | undefined; - /** - * Encrypt the given message content - */ - static encryptData(content: string, pubkey: HexKey, privkey: HexKey): Promise; - /** - * Decrypt the content of the message - */ - static decryptData(cyphertext: string, privkey: HexKey, pubkey: HexKey): Promise; - /** - * Decrypt the content of this message in place - */ - static decryptDm(content: string, privkey: HexKey, pubkey: HexKey): Promise; -} -//# sourceMappingURL=EventExt.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/EventExt.d.ts.map b/packages/system/dist/EventExt.d.ts.map deleted file mode 100644 index bb09e5ef..00000000 --- a/packages/system/dist/EventExt.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EventExt.d.ts","sourceRoot":"","sources":["../src/EventExt.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC;AAIvD,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IACrB,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACxB;AAED,8BAAsB,QAAQ;;IAC5B;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM;IAS3C;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM;IAUtC;;;OAGG;IACH,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU;IAM3B,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,UAAU;IAW7B;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS;IAY5C,MAAM,CAAC,aAAa,CAAC,EAAE,EAAE,UAAU;IAqCnC;;OAEG;WACU,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAgBzE;;OAEG;WACU,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAoB5E;;OAEG;WACU,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CASxE"} \ No newline at end of file diff --git a/packages/system/dist/EventExt.js b/packages/system/dist/EventExt.js deleted file mode 100644 index eb019d83..00000000 --- a/packages/system/dist/EventExt.js +++ /dev/null @@ -1,175 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); - return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -var _a, _EventExt_getDmSharedKey; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EventExt = void 0; -const secp = __importStar(require("@noble/curves/secp256k1")); -const utils = __importStar(require("@noble/curves/abstract/utils")); -const _1 = require("."); -const base64_1 = __importDefault(require("@protobufjs/base64")); -const Util_1 = require("./Util"); -class EventExt { - /** - * Get the pub key of the creator of this event NIP-26 - */ - static getRootPubKey(e) { - const delegation = e.tags.find(a => a[0] === "delegation"); - if (delegation?.[1]) { - // todo: verify sig - return delegation[1]; - } - return e.pubkey; - } - /** - * Sign this message with a private key - */ - static sign(e, key) { - e.id = this.createId(e); - const sig = secp.schnorr.sign(e.id, key); - e.sig = utils.bytesToHex(sig); - if (!(secp.schnorr.verify(e.sig, e.id, e.pubkey))) { - throw new Error("Signing failed"); - } - } - /** - * Check the signature of this message - * @returns True if valid signature - */ - static verify(e) { - const id = this.createId(e); - const result = secp.schnorr.verify(e.sig, id, e.pubkey); - return result; - } - static createId(e) { - const payload = [0, e.pubkey, e.created_at, e.kind, e.tags, e.content]; - const hash = (0, Util_1.sha256)(JSON.stringify(payload)); - if (e.id !== "" && hash !== e.id) { - console.debug(payload); - throw new Error("ID doesnt match!"); - } - return hash; - } - /** - * Create a new event for a specific pubkey - */ - static forPubKey(pk, kind) { - return { - pubkey: pk, - kind: kind, - created_at: (0, Util_1.unixNow)(), - content: "", - tags: [], - id: "", - sig: "", - }; - } - static extractThread(ev) { - const isThread = ev.tags.some(a => (a[0] === "e" && a[3] !== "mention") || a[0] == "a"); - if (!isThread) { - return undefined; - } - const shouldWriteMarkers = ev.kind === _1.EventKind.TextNote; - const ret = { - mentions: [], - pubKeys: [], - }; - const eTags = ev.tags.filter(a => a[0] === "e" || a[0] === "a").map((v, i) => new _1.Tag(v, i)); - const marked = eTags.some(a => a.Marker !== undefined); - if (!marked) { - ret.root = eTags[0]; - ret.root.Marker = shouldWriteMarkers ? "root" : undefined; - if (eTags.length > 1) { - ret.replyTo = eTags[1]; - ret.replyTo.Marker = shouldWriteMarkers ? "reply" : undefined; - } - if (eTags.length > 2) { - ret.mentions = eTags.slice(2); - if (shouldWriteMarkers) { - ret.mentions.forEach(a => (a.Marker = "mention")); - } - } - } - else { - const root = eTags.find(a => a.Marker === "root"); - const reply = eTags.find(a => a.Marker === "reply"); - ret.root = root; - ret.replyTo = reply; - ret.mentions = eTags.filter(a => a.Marker === "mention"); - } - ret.pubKeys = Array.from(new Set(ev.tags.filter(a => a[0] === "p").map(a => a[1]))); - return ret; - } - /** - * Encrypt the given message content - */ - static async encryptData(content, pubkey, privkey) { - const key = await __classPrivateFieldGet(this, _a, "m", _EventExt_getDmSharedKey).call(this, pubkey, privkey); - const iv = window.crypto.getRandomValues(new Uint8Array(16)); - const data = new TextEncoder().encode(content); - const result = await window.crypto.subtle.encrypt({ - name: "AES-CBC", - iv: iv, - }, key, data); - const uData = new Uint8Array(result); - return `${base64_1.default.encode(uData, 0, result.byteLength)}?iv=${base64_1.default.encode(iv, 0, 16)}`; - } - /** - * Decrypt the content of the message - */ - static async decryptData(cyphertext, privkey, pubkey) { - const key = await __classPrivateFieldGet(this, _a, "m", _EventExt_getDmSharedKey).call(this, pubkey, privkey); - const cSplit = cyphertext.split("?iv="); - const data = new Uint8Array(base64_1.default.length(cSplit[0])); - base64_1.default.decode(cSplit[0], data, 0); - const iv = new Uint8Array(base64_1.default.length(cSplit[1])); - base64_1.default.decode(cSplit[1], iv, 0); - const result = await window.crypto.subtle.decrypt({ - name: "AES-CBC", - iv: iv, - }, key, data); - return new TextDecoder().decode(result); - } - /** - * Decrypt the content of this message in place - */ - static async decryptDm(content, privkey, pubkey) { - return await this.decryptData(content, privkey, pubkey); - } -} -exports.EventExt = EventExt; -_a = EventExt, _EventExt_getDmSharedKey = async function _EventExt_getDmSharedKey(pubkey, privkey) { - const sharedPoint = secp.secp256k1.getSharedSecret(privkey, "02" + pubkey); - const sharedX = sharedPoint.slice(1, 33); - return await window.crypto.subtle.importKey("raw", sharedX, { name: "AES-CBC" }, false, ["encrypt", "decrypt"]); -}; -//# sourceMappingURL=EventExt.js.map \ No newline at end of file diff --git a/packages/system/dist/EventExt.js.map b/packages/system/dist/EventExt.js.map deleted file mode 100644 index 1af29fd9..00000000 --- a/packages/system/dist/EventExt.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EventExt.js","sourceRoot":"","sources":["../src/EventExt.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/EventKind.d.ts b/packages/system/dist/EventKind.d.ts deleted file mode 100644 index 620047bd..00000000 --- a/packages/system/dist/EventKind.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -declare enum EventKind { - Unknown = -1, - SetMetadata = 0, - TextNote = 1, - RecommendServer = 2, - ContactList = 3, - DirectMessage = 4, - Deletion = 5, - Repost = 6, - Reaction = 7, - BadgeAward = 8, - SnortSubscriptions = 1000, - Polls = 6969, - FileHeader = 1063, - Relays = 10002, - Ephemeral = 20000, - Auth = 22242, - PubkeyLists = 30000, - NoteLists = 30001, - TagLists = 30002, - Badge = 30009, - ProfileBadges = 30008, - ZapstrTrack = 31337, - ZapRequest = 9734, - ZapReceipt = 9735, - HttpAuthentication = 27235 -} -export default EventKind; -//# sourceMappingURL=EventKind.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/EventKind.d.ts.map b/packages/system/dist/EventKind.d.ts.map deleted file mode 100644 index 2cf2c3fd..00000000 --- a/packages/system/dist/EventKind.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EventKind.d.ts","sourceRoot":"","sources":["../src/EventKind.ts"],"names":[],"mappings":"AAAA,aAAK,SAAS;IACZ,OAAO,KAAK;IACZ,WAAW,IAAI;IACf,QAAQ,IAAI;IACZ,eAAe,IAAI;IACnB,WAAW,IAAI;IACf,aAAa,IAAI;IACjB,QAAQ,IAAI;IACZ,MAAM,IAAI;IACV,QAAQ,IAAI;IACZ,UAAU,IAAI;IACd,kBAAkB,OAAO;IACzB,KAAK,OAAO;IACZ,UAAU,OAAO;IACjB,MAAM,QAAQ;IACd,SAAS,QAAS;IAClB,IAAI,QAAQ;IACZ,WAAW,QAAQ;IACnB,SAAS,QAAQ;IACjB,QAAQ,QAAQ;IAChB,KAAK,QAAQ;IACb,aAAa,QAAQ;IACrB,WAAW,QAAQ;IACnB,UAAU,OAAO;IACjB,UAAU,OAAO;IACjB,kBAAkB,QAAQ;CAC3B;AAED,eAAe,SAAS,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/EventKind.js b/packages/system/dist/EventKind.js deleted file mode 100644 index d4a5c88a..00000000 --- a/packages/system/dist/EventKind.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var EventKind; -(function (EventKind) { - EventKind[EventKind["Unknown"] = -1] = "Unknown"; - EventKind[EventKind["SetMetadata"] = 0] = "SetMetadata"; - EventKind[EventKind["TextNote"] = 1] = "TextNote"; - EventKind[EventKind["RecommendServer"] = 2] = "RecommendServer"; - EventKind[EventKind["ContactList"] = 3] = "ContactList"; - EventKind[EventKind["DirectMessage"] = 4] = "DirectMessage"; - EventKind[EventKind["Deletion"] = 5] = "Deletion"; - EventKind[EventKind["Repost"] = 6] = "Repost"; - EventKind[EventKind["Reaction"] = 7] = "Reaction"; - EventKind[EventKind["BadgeAward"] = 8] = "BadgeAward"; - EventKind[EventKind["SnortSubscriptions"] = 1000] = "SnortSubscriptions"; - EventKind[EventKind["Polls"] = 6969] = "Polls"; - EventKind[EventKind["FileHeader"] = 1063] = "FileHeader"; - EventKind[EventKind["Relays"] = 10002] = "Relays"; - EventKind[EventKind["Ephemeral"] = 20000] = "Ephemeral"; - EventKind[EventKind["Auth"] = 22242] = "Auth"; - EventKind[EventKind["PubkeyLists"] = 30000] = "PubkeyLists"; - EventKind[EventKind["NoteLists"] = 30001] = "NoteLists"; - EventKind[EventKind["TagLists"] = 30002] = "TagLists"; - EventKind[EventKind["Badge"] = 30009] = "Badge"; - EventKind[EventKind["ProfileBadges"] = 30008] = "ProfileBadges"; - EventKind[EventKind["ZapstrTrack"] = 31337] = "ZapstrTrack"; - EventKind[EventKind["ZapRequest"] = 9734] = "ZapRequest"; - EventKind[EventKind["ZapReceipt"] = 9735] = "ZapReceipt"; - EventKind[EventKind["HttpAuthentication"] = 27235] = "HttpAuthentication"; -})(EventKind || (EventKind = {})); -exports.default = EventKind; -//# sourceMappingURL=EventKind.js.map \ No newline at end of file diff --git a/packages/system/dist/EventKind.js.map b/packages/system/dist/EventKind.js.map deleted file mode 100644 index 4cdf3259..00000000 --- a/packages/system/dist/EventKind.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EventKind.js","sourceRoot":"","sources":["../src/EventKind.ts"],"names":[],"mappings":";;AAAA,IAAK,SA0BJ;AA1BD,WAAK,SAAS;IACZ,gDAAY,CAAA;IACZ,uDAAe,CAAA;IACf,iDAAY,CAAA;IACZ,+DAAmB,CAAA;IACnB,uDAAe,CAAA;IACf,2DAAiB,CAAA;IACjB,iDAAY,CAAA;IACZ,6CAAU,CAAA;IACV,iDAAY,CAAA;IACZ,qDAAc,CAAA;IACd,wEAAyB,CAAA;IACzB,8CAAY,CAAA;IACZ,wDAAiB,CAAA;IACjB,iDAAc,CAAA;IACd,uDAAkB,CAAA;IAClB,6CAAY,CAAA;IACZ,2DAAmB,CAAA;IACnB,uDAAiB,CAAA;IACjB,qDAAgB,CAAA;IAChB,+CAAa,CAAA;IACb,+DAAqB,CAAA;IACrB,2DAAmB,CAAA;IACnB,wDAAiB,CAAA;IACjB,wDAAiB,CAAA;IACjB,yEAA0B,CAAA;AAC5B,CAAC,EA1BI,SAAS,KAAT,SAAS,QA0Bb;AAED,kBAAe,SAAS,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/EventPublisher.d.ts b/packages/system/dist/EventPublisher.d.ts deleted file mode 100644 index d121228e..00000000 --- a/packages/system/dist/EventPublisher.d.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { FullRelaySettings, HexKey, Lists, NostrEvent, RelaySettings, SystemInterface, TaggedRawEvent, u256, UserMetadata } from "."; -import { EventBuilder } from "./EventBuilder"; -export type EventBuilderHook = (ev: EventBuilder) => EventBuilder; -declare global { - interface Window { - nostr?: { - getPublicKey: () => Promise; - signEvent: (event: T) => Promise; - getRelays?: () => Promise>; - nip04?: { - encrypt?: (pubkey: HexKey, plaintext: string) => Promise; - decrypt?: (pubkey: HexKey, ciphertext: string) => Promise; - }; - }; - } -} -export declare class EventPublisher { - #private; - constructor(system: SystemInterface, pubKey: string, privKey?: string); - nip4Encrypt(content: string, key: HexKey): Promise; - nip4Decrypt(content: string, otherKey: HexKey): Promise; - nip42Auth(challenge: string, relay: string): Promise; - broadcast(ev: NostrEvent): void; - /** - * Write event to all given relays. - */ - broadcastAll(ev: NostrEvent, relays: string[]): void; - muted(keys: HexKey[], priv: HexKey[]): Promise; - noteList(notes: u256[], list: Lists): Promise; - tags(tags: string[]): Promise; - metadata(obj: UserMetadata): Promise; - /** - * Create a basic text note - */ - note(msg: string, fnExtra?: EventBuilderHook): Promise; - /** - * Create a zap request event for a given target event/profile - * @param amount Millisats amout! - * @param author Author pubkey to tag in the zap - * @param note Note Id to tag in the zap - * @param msg Custom message to be included in the zap - */ - zap(amount: number, author: HexKey, relays: Array, note?: HexKey, msg?: string, fnExtra?: EventBuilderHook): Promise; - /** - * Reply to a note - */ - reply(replyTo: TaggedRawEvent, msg: string, fnExtra?: EventBuilderHook): Promise; - react(evRef: NostrEvent, content?: string): Promise; - relayList(relays: Array | Record): Promise; - contactList(follows: Array, relays: Record): Promise; - /** - * Delete an event (NIP-09) - */ - delete(id: u256): Promise; - /** - * Repost a note (NIP-18) - */ - repost(note: NostrEvent): Promise; - decryptDm(note: NostrEvent): Promise; - sendDm(content: string, to: HexKey): Promise; - generic(fnHook: EventBuilderHook): Promise; -} -//# sourceMappingURL=EventPublisher.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/EventPublisher.d.ts.map b/packages/system/dist/EventPublisher.d.ts.map deleted file mode 100644 index 10509c14..00000000 --- a/packages/system/dist/EventPublisher.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EventPublisher.d.ts","sourceRoot":"","sources":["../src/EventPublisher.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/EventPublisher.js b/packages/system/dist/EventPublisher.js deleted file mode 100644 index e392994a..00000000 --- a/packages/system/dist/EventPublisher.js +++ /dev/null @@ -1,295 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { - if (kind === "m") throw new TypeError("Private method is not writable"); - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); - return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; -}; -var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); - return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -}; -var _EventPublisher_instances, _EventPublisher_system, _EventPublisher_pubKey, _EventPublisher_privateKey, _EventPublisher_hasNip07_get, _EventPublisher_eb, _EventPublisher_sign; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EventPublisher = void 0; -const secp = __importStar(require("@noble/curves/secp256k1")); -const utils = __importStar(require("@noble/curves/abstract/utils")); -const _1 = require("."); -const Util_1 = require("./Util"); -const EventBuilder_1 = require("./EventBuilder"); -const EventExt_1 = require("./EventExt"); -const WorkQueue_1 = require("./WorkQueue"); -const Nip7Queue = []; -(0, WorkQueue_1.processWorkQueue)(Nip7Queue); -class EventPublisher { - constructor(system, pubKey, privKey) { - _EventPublisher_instances.add(this); - _EventPublisher_system.set(this, void 0); - _EventPublisher_pubKey.set(this, void 0); - _EventPublisher_privateKey.set(this, void 0); - __classPrivateFieldSet(this, _EventPublisher_system, system, "f"); - if (privKey) { - __classPrivateFieldSet(this, _EventPublisher_privateKey, privKey, "f"); - __classPrivateFieldSet(this, _EventPublisher_pubKey, utils.bytesToHex(secp.schnorr.getPublicKey(privKey)), "f"); - } - else { - __classPrivateFieldSet(this, _EventPublisher_pubKey, pubKey, "f"); - } - } - async nip4Encrypt(content, key) { - if (__classPrivateFieldGet(this, _EventPublisher_instances, "a", _EventPublisher_hasNip07_get) && !__classPrivateFieldGet(this, _EventPublisher_privateKey, "f")) { - const nip7PubKey = await (0, WorkQueue_1.barrierQueue)(Nip7Queue, () => (0, Util_1.unwrap)(window.nostr).getPublicKey()); - if (nip7PubKey !== __classPrivateFieldGet(this, _EventPublisher_pubKey, "f")) { - throw new Error("Can't encrypt content, NIP-07 pubkey does not match"); - } - return await (0, WorkQueue_1.barrierQueue)(Nip7Queue, () => (0, Util_1.unwrap)(window.nostr?.nip04?.encrypt).call(window.nostr?.nip04, key, content)); - } - else if (__classPrivateFieldGet(this, _EventPublisher_privateKey, "f")) { - return await EventExt_1.EventExt.encryptData(content, key, __classPrivateFieldGet(this, _EventPublisher_privateKey, "f")); - } - else { - throw new Error("Can't encrypt content, no private keys available"); - } - } - async nip4Decrypt(content, otherKey) { - if (__classPrivateFieldGet(this, _EventPublisher_instances, "a", _EventPublisher_hasNip07_get) && !__classPrivateFieldGet(this, _EventPublisher_privateKey, "f") && window.nostr?.nip04?.decrypt) { - return await (0, WorkQueue_1.barrierQueue)(Nip7Queue, () => (0, Util_1.unwrap)(window.nostr?.nip04?.decrypt).call(window.nostr?.nip04, otherKey, content)); - } - else if (__classPrivateFieldGet(this, _EventPublisher_privateKey, "f")) { - return await EventExt_1.EventExt.decryptDm(content, __classPrivateFieldGet(this, _EventPublisher_privateKey, "f"), otherKey); - } - else { - throw new Error("Can't decrypt content, no private keys available"); - } - } - async nip42Auth(challenge, relay) { - const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.Auth); - eb.tag(["relay", relay]); - eb.tag(["challenge", challenge]); - return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); - } - broadcast(ev) { - console.debug(ev); - __classPrivateFieldGet(this, _EventPublisher_system, "f").BroadcastEvent(ev); - } - /** - * Write event to all given relays. - */ - broadcastAll(ev, relays) { - for (const k of relays) { - __classPrivateFieldGet(this, _EventPublisher_system, "f").WriteOnceToRelay(k, ev); - } - } - async muted(keys, priv) { - const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.PubkeyLists); - eb.tag(["d", _1.Lists.Muted]); - keys.forEach(p => { - eb.tag(["p", p]); - }); - if (priv.length > 0) { - const ps = priv.map(p => ["p", p]); - const plaintext = JSON.stringify(ps); - eb.content(await this.nip4Encrypt(plaintext, __classPrivateFieldGet(this, _EventPublisher_pubKey, "f"))); - } - return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); - } - async noteList(notes, list) { - const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.NoteLists); - eb.tag(["d", list]); - notes.forEach(n => { - eb.tag(["e", n]); - }); - return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); - } - async tags(tags) { - const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.TagLists); - eb.tag(["d", _1.Lists.Followed]); - tags.forEach(t => { - eb.tag(["t", t]); - }); - return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); - } - async metadata(obj) { - const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.SetMetadata); - eb.content(JSON.stringify(obj)); - return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); - } - /** - * Create a basic text note - */ - async note(msg, fnExtra) { - const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.TextNote); - eb.content(msg); - eb.processContent(); - fnExtra?.(eb); - return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); - } - /** - * Create a zap request event for a given target event/profile - * @param amount Millisats amout! - * @param author Author pubkey to tag in the zap - * @param note Note Id to tag in the zap - * @param msg Custom message to be included in the zap - */ - async zap(amount, author, relays, note, msg, fnExtra) { - const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.ZapRequest); - eb.content(msg ?? ""); - if (note) { - eb.tag(["e", note]); - } - eb.tag(["p", author]); - eb.tag(["relays", ...relays.map(a => a.trim())]); - eb.tag(["amount", amount.toString()]); - eb.processContent(); - fnExtra?.(eb); - return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); - } - /** - * Reply to a note - */ - async reply(replyTo, msg, fnExtra) { - const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.TextNote); - eb.content(msg); - const thread = EventExt_1.EventExt.extractThread(replyTo); - if (thread) { - if (thread.root || thread.replyTo) { - eb.tag(["e", thread.root?.Event ?? thread.replyTo?.Event ?? "", "", "root"]); - } - eb.tag(["e", replyTo.id, replyTo.relays?.[0] ?? "", "reply"]); - eb.tag(["p", replyTo.pubkey]); - for (const pk of thread.pubKeys) { - if (pk === __classPrivateFieldGet(this, _EventPublisher_pubKey, "f")) { - continue; - } - eb.tag(["p", pk]); - } - } - else { - eb.tag(["e", replyTo.id, "", "reply"]); - // dont tag self in replies - if (replyTo.pubkey !== __classPrivateFieldGet(this, _EventPublisher_pubKey, "f")) { - eb.tag(["p", replyTo.pubkey]); - } - } - eb.processContent(); - fnExtra?.(eb); - return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); - } - async react(evRef, content = "+") { - const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.Reaction); - eb.content(content); - eb.tag(["e", evRef.id]); - eb.tag(["p", evRef.pubkey]); - return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); - } - async relayList(relays) { - if (!Array.isArray(relays)) { - relays = Object.entries(relays).map(([k, v]) => ({ - url: k, - settings: v, - })); - } - const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.Relays); - for (const rx of relays) { - const rTag = ["r", rx.url]; - if (rx.settings.read && !rx.settings.write) { - rTag.push("read"); - } - if (rx.settings.write && !rx.settings.read) { - rTag.push("write"); - } - eb.tag(rTag); - } - return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); - } - async contactList(follows, relays) { - const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.ContactList); - eb.content(JSON.stringify(relays)); - const temp = new Set(follows.filter(a => a.length === 64).map(a => a.toLowerCase())); - temp.forEach(a => eb.tag(["p", a])); - return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); - } - /** - * Delete an event (NIP-09) - */ - async delete(id) { - const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.Deletion); - eb.tag(["e", id]); - return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); - } - /** - * Repost a note (NIP-18) - */ - async repost(note) { - const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.Repost); - eb.tag(["e", note.id, ""]); - eb.tag(["p", note.pubkey]); - return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); - } - async decryptDm(note) { - if (note.pubkey !== __classPrivateFieldGet(this, _EventPublisher_pubKey, "f") && !note.tags.some(a => a[1] === __classPrivateFieldGet(this, _EventPublisher_pubKey, "f"))) { - throw new Error("Can't decrypt, DM does not belong to this user"); - } - const otherPubKey = note.pubkey === __classPrivateFieldGet(this, _EventPublisher_pubKey, "f") ? (0, Util_1.unwrap)(note.tags.find(a => a[0] === "p")?.[1]) : note.pubkey; - return await this.nip4Decrypt(note.content, otherPubKey); - } - async sendDm(content, to) { - const eb = __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_eb).call(this, _1.EventKind.DirectMessage); - eb.content(await this.nip4Encrypt(content, to)); - eb.tag(["p", to]); - return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); - } - async generic(fnHook) { - const eb = new EventBuilder_1.EventBuilder(); - eb.pubKey(__classPrivateFieldGet(this, _EventPublisher_pubKey, "f")); - fnHook(eb); - return await __classPrivateFieldGet(this, _EventPublisher_instances, "m", _EventPublisher_sign).call(this, eb); - } -} -exports.EventPublisher = EventPublisher; -_EventPublisher_system = new WeakMap(), _EventPublisher_pubKey = new WeakMap(), _EventPublisher_privateKey = new WeakMap(), _EventPublisher_instances = new WeakSet(), _EventPublisher_hasNip07_get = function _EventPublisher_hasNip07_get() { - return "nostr" in window; -}, _EventPublisher_eb = function _EventPublisher_eb(k) { - const eb = new EventBuilder_1.EventBuilder(); - return eb.pubKey(__classPrivateFieldGet(this, _EventPublisher_pubKey, "f")).kind(k); -}, _EventPublisher_sign = async function _EventPublisher_sign(eb) { - if (__classPrivateFieldGet(this, _EventPublisher_instances, "a", _EventPublisher_hasNip07_get) && !__classPrivateFieldGet(this, _EventPublisher_privateKey, "f")) { - const nip7PubKey = await (0, WorkQueue_1.barrierQueue)(Nip7Queue, () => (0, Util_1.unwrap)(window.nostr).getPublicKey()); - if (nip7PubKey !== __classPrivateFieldGet(this, _EventPublisher_pubKey, "f")) { - throw new Error("Can't sign event, NIP-07 pubkey does not match"); - } - const ev = eb.build(); - return await (0, WorkQueue_1.barrierQueue)(Nip7Queue, () => (0, Util_1.unwrap)(window.nostr).signEvent(ev)); - } - else if (__classPrivateFieldGet(this, _EventPublisher_privateKey, "f")) { - return await eb.buildAndSign(__classPrivateFieldGet(this, _EventPublisher_privateKey, "f")); - } - else { - throw new Error("Can't sign event, no private keys available"); - } -}; -//# sourceMappingURL=EventPublisher.js.map \ No newline at end of file diff --git a/packages/system/dist/EventPublisher.js.map b/packages/system/dist/EventPublisher.js.map deleted file mode 100644 index 1f95e3ad..00000000 --- a/packages/system/dist/EventPublisher.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EventPublisher.js","sourceRoot":"","sources":["../src/EventPublisher.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/ExternalStore.d.ts b/packages/system/dist/ExternalStore.d.ts deleted file mode 100644 index 2c483f6d..00000000 --- a/packages/system/dist/ExternalStore.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -type HookFn = (e?: TSnapshot) => void; -/** - * Simple React hookable store with manual change notifications - */ -export default abstract class ExternalStore { - #private; - hook(fn: HookFn): () => void; - snapshot(): Readonly; - protected notifyChange(sn?: TSnapshot): void; - abstract takeSnapshot(): TSnapshot; -} -export {}; -//# sourceMappingURL=ExternalStore.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/ExternalStore.d.ts.map b/packages/system/dist/ExternalStore.d.ts.map deleted file mode 100644 index 9c439d8d..00000000 --- a/packages/system/dist/ExternalStore.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ExternalStore.d.ts","sourceRoot":"","sources":["../src/ExternalStore.ts"],"names":[],"mappings":"AAAA,KAAK,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;AAMjD;;GAEG;AACH,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,aAAa,CAAC,SAAS;;IAKnD,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC;IAY1B,QAAQ;IAQR,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,SAAS;IAKrC,QAAQ,CAAC,YAAY,IAAI,SAAS;CACnC"} \ No newline at end of file diff --git a/packages/system/dist/ExternalStore.js b/packages/system/dist/ExternalStore.js deleted file mode 100644 index 47daafe4..00000000 --- a/packages/system/dist/ExternalStore.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; -var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); - return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -}; -var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { - if (kind === "m") throw new TypeError("Private method is not writable"); - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); - return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; -}; -var _ExternalStore_hooks, _ExternalStore_snapshot, _ExternalStore_changed; -Object.defineProperty(exports, "__esModule", { value: true }); -/** - * Simple React hookable store with manual change notifications - */ -class ExternalStore { - constructor() { - _ExternalStore_hooks.set(this, []); - _ExternalStore_snapshot.set(this, {}); - _ExternalStore_changed.set(this, true); - } - hook(fn) { - __classPrivateFieldGet(this, _ExternalStore_hooks, "f").push({ - fn, - }); - return () => { - const idx = __classPrivateFieldGet(this, _ExternalStore_hooks, "f").findIndex(a => a.fn === fn); - if (idx >= 0) { - __classPrivateFieldGet(this, _ExternalStore_hooks, "f").splice(idx, 1); - } - }; - } - snapshot() { - if (__classPrivateFieldGet(this, _ExternalStore_changed, "f")) { - __classPrivateFieldSet(this, _ExternalStore_snapshot, this.takeSnapshot(), "f"); - __classPrivateFieldSet(this, _ExternalStore_changed, false, "f"); - } - return __classPrivateFieldGet(this, _ExternalStore_snapshot, "f"); - } - notifyChange(sn) { - __classPrivateFieldSet(this, _ExternalStore_changed, true, "f"); - __classPrivateFieldGet(this, _ExternalStore_hooks, "f").forEach(h => h.fn(sn)); - } -} -exports.default = ExternalStore; -_ExternalStore_hooks = new WeakMap(), _ExternalStore_snapshot = new WeakMap(), _ExternalStore_changed = new WeakMap(); -//# sourceMappingURL=ExternalStore.js.map \ No newline at end of file diff --git a/packages/system/dist/ExternalStore.js.map b/packages/system/dist/ExternalStore.js.map deleted file mode 100644 index 483f5f23..00000000 --- a/packages/system/dist/ExternalStore.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ExternalStore.js","sourceRoot":"","sources":["../src/ExternalStore.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAMA;;GAEG;AACH,MAA8B,aAAa;IAA3C;QACE,+BAAuC,EAAE,EAAC;QAC1C,kCAAiC,EAAyB,EAAC;QAC3D,iCAAW,IAAI,EAAC;IA4BlB,CAAC;IA1BC,IAAI,CAAC,EAAqB;QACxB,uBAAA,IAAI,4BAAO,CAAC,IAAI,CAAC;YACf,EAAE;SACH,CAAC,CAAC;QACH,OAAO,GAAG,EAAE;YACV,MAAM,GAAG,GAAG,uBAAA,IAAI,4BAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YACpD,IAAI,GAAG,IAAI,CAAC,EAAE;gBACZ,uBAAA,IAAI,4BAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;aAC5B;QACH,CAAC,CAAC;IACJ,CAAC;IAED,QAAQ;QACN,IAAI,uBAAA,IAAI,8BAAS,EAAE;YACjB,uBAAA,IAAI,2BAAa,IAAI,CAAC,YAAY,EAAE,MAAA,CAAC;YACrC,uBAAA,IAAI,0BAAY,KAAK,MAAA,CAAC;SACvB;QACD,OAAO,uBAAA,IAAI,+BAAU,CAAC;IACxB,CAAC;IAES,YAAY,CAAC,EAAc;QACnC,uBAAA,IAAI,0BAAY,IAAI,MAAA,CAAC;QACrB,uBAAA,IAAI,4BAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,CAAC;CAGF;AA/BD,gCA+BC"} \ No newline at end of file diff --git a/packages/system/dist/GossipModel.d.ts b/packages/system/dist/GossipModel.d.ts deleted file mode 100644 index a4e1c8b8..00000000 --- a/packages/system/dist/GossipModel.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { FullRelaySettings, ReqFilter } from "."; -export interface RelayTaggedFilter { - relay: string; - filter: ReqFilter; -} -export interface RelayTaggedFilters { - relay: string; - filters: Array; -} -export interface RelayCache { - get(pubkey?: string): Array | undefined; -} -export declare function splitAllByWriteRelays(cache: RelayCache, filters: Array): RelayTaggedFilters[]; -/** - * Split filters by authors - * @param filter - * @returns - */ -export declare function splitByWriteRelays(cache: RelayCache, filter: ReqFilter): Array; -//# sourceMappingURL=GossipModel.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/GossipModel.d.ts.map b/packages/system/dist/GossipModel.d.ts.map deleted file mode 100644 index 3328b5e2..00000000 --- a/packages/system/dist/GossipModel.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"GossipModel.d.ts","sourceRoot":"","sources":["../src/GossipModel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC;AAMjD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,iBAAiB,CAAC,GAAG,SAAS,CAAC;CAC5D;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,wBAqBjF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,GAAG,KAAK,CAAC,iBAAiB,CAAC,CAoEjG"} \ No newline at end of file diff --git a/packages/system/dist/GossipModel.js b/packages/system/dist/GossipModel.js deleted file mode 100644 index 1668805e..00000000 --- a/packages/system/dist/GossipModel.js +++ /dev/null @@ -1,102 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.splitByWriteRelays = exports.splitAllByWriteRelays = void 0; -const Util_1 = require("./Util"); -const debug_1 = __importDefault(require("debug")); -const PickNRelays = 2; -function splitAllByWriteRelays(cache, filters) { - const allSplit = filters - .map(a => splitByWriteRelays(cache, a)) - .reduce((acc, v) => { - for (const vn of v) { - const existing = acc.get(vn.relay); - if (existing) { - existing.push(vn.filter); - } - else { - acc.set(vn.relay, [vn.filter]); - } - } - return acc; - }, new Map()); - return [...allSplit.entries()].map(([k, v]) => { - return { - relay: k, - filters: v, - }; - }); -} -exports.splitAllByWriteRelays = splitAllByWriteRelays; -/** - * Split filters by authors - * @param filter - * @returns - */ -function splitByWriteRelays(cache, filter) { - if ((filter.authors?.length ?? 0) === 0) - return [ - { - relay: "", - filter, - }, - ]; - const allRelays = (0, Util_1.unwrap)(filter.authors).map(a => { - return { - key: a, - relays: cache.get(a)?.filter(a => a.settings.write), - }; - }); - const missing = allRelays.filter(a => a.relays === undefined); - const hasRelays = allRelays.filter(a => a.relays !== undefined); - const relayUserMap = hasRelays.reduce((acc, v) => { - for (const r of (0, Util_1.unwrap)(v.relays)) { - if (!acc.has(r.url)) { - acc.set(r.url, new Set([v.key])); - } - else { - (0, Util_1.unwrap)(acc.get(r.url)).add(v.key); - } - } - return acc; - }, new Map()); - // selection algo will just pick relays with the most users - const topRelays = [...relayUserMap.entries()].sort(([, v], [, v1]) => v1.size - v.size); - // - count keys per relay - // - pick n top relays - // - map keys per relay (for subscription filter) - const userPickedRelays = (0, Util_1.unwrap)(filter.authors).map(k => { - // pick top 3 relays for this key - const relaysForKey = topRelays - .filter(([, v]) => v.has(k)) - .slice(0, PickNRelays) - .map(([k]) => k); - return { k, relaysForKey }; - }); - const pickedRelays = new Set(userPickedRelays.map(a => a.relaysForKey).flat()); - const picked = [...pickedRelays].map(a => { - const keysOnPickedRelay = new Set(userPickedRelays.filter(b => b.relaysForKey.includes(a)).map(b => b.k)); - return { - relay: a, - filter: { - ...filter, - authors: [...keysOnPickedRelay], - }, - }; - }); - if (missing.length > 0) { - picked.push({ - relay: "", - filter: { - ...filter, - authors: missing.map(a => a.key), - }, - }); - } - (0, debug_1.default)("GOSSIP")("Picked %o", picked); - return picked; -} -exports.splitByWriteRelays = splitByWriteRelays; -//# sourceMappingURL=GossipModel.js.map \ No newline at end of file diff --git a/packages/system/dist/GossipModel.js.map b/packages/system/dist/GossipModel.js.map deleted file mode 100644 index 0bddddb1..00000000 --- a/packages/system/dist/GossipModel.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"GossipModel.js","sourceRoot":"","sources":["../src/GossipModel.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/Links.d.ts b/packages/system/dist/Links.d.ts deleted file mode 100644 index bd3ed4c0..00000000 --- a/packages/system/dist/Links.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { HexKey } from "./Nostr"; -export declare enum NostrPrefix { - PublicKey = "npub", - PrivateKey = "nsec", - Note = "note", - Profile = "nprofile", - Event = "nevent", - Relay = "nrelay", - Address = "naddr" -} -export declare enum TLVEntryType { - Special = 0, - Relay = 1, - Author = 2, - Kind = 3 -} -export interface TLVEntry { - type: TLVEntryType; - length: number; - value: string | HexKey | number; -} -export declare function encodeTLV(prefix: NostrPrefix, id: string, relays?: string[], kind?: number, author?: string): string; -export declare function decodeTLV(str: string): TLVEntry[]; -//# sourceMappingURL=Links.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Links.d.ts.map b/packages/system/dist/Links.d.ts.map deleted file mode 100644 index a7158f31..00000000 --- a/packages/system/dist/Links.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Links.d.ts","sourceRoot":"","sources":["../src/Links.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,oBAAY,WAAW;IACrB,SAAS,SAAS;IAClB,UAAU,SAAS;IACnB,IAAI,SAAS;IAGb,OAAO,aAAa;IACpB,KAAK,WAAW;IAChB,KAAK,WAAW;IAChB,OAAO,UAAU;CAClB;AAED,oBAAY,YAAY;IACtB,OAAO,IAAI;IACX,KAAK,IAAI;IACT,MAAM,IAAI;IACV,IAAI,IAAI;CACT;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CACjC;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,UAiB3G;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,cAkBpC"} \ No newline at end of file diff --git a/packages/system/dist/Links.js b/packages/system/dist/Links.js deleted file mode 100644 index 727ae9de..00000000 --- a/packages/system/dist/Links.js +++ /dev/null @@ -1,102 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.decodeTLV = exports.encodeTLV = exports.TLVEntryType = exports.NostrPrefix = void 0; -const utils = __importStar(require("@noble/curves/abstract/utils")); -const bech32_1 = require("bech32"); -var NostrPrefix; -(function (NostrPrefix) { - NostrPrefix["PublicKey"] = "npub"; - NostrPrefix["PrivateKey"] = "nsec"; - NostrPrefix["Note"] = "note"; - // TLV prefixes - NostrPrefix["Profile"] = "nprofile"; - NostrPrefix["Event"] = "nevent"; - NostrPrefix["Relay"] = "nrelay"; - NostrPrefix["Address"] = "naddr"; -})(NostrPrefix = exports.NostrPrefix || (exports.NostrPrefix = {})); -var TLVEntryType; -(function (TLVEntryType) { - TLVEntryType[TLVEntryType["Special"] = 0] = "Special"; - TLVEntryType[TLVEntryType["Relay"] = 1] = "Relay"; - TLVEntryType[TLVEntryType["Author"] = 2] = "Author"; - TLVEntryType[TLVEntryType["Kind"] = 3] = "Kind"; -})(TLVEntryType = exports.TLVEntryType || (exports.TLVEntryType = {})); -function encodeTLV(prefix, id, relays, kind, author) { - const enc = new TextEncoder(); - const buf = prefix === NostrPrefix.Address ? enc.encode(id) : utils.hexToBytes(id); - const tl0 = [0, buf.length, ...buf]; - const tl1 = relays - ?.map(a => { - const data = enc.encode(a); - return [1, data.length, ...data]; - }) - .flat() ?? []; - const tl2 = author ? [2, 32, ...utils.hexToBytes(author)] : []; - const tl3 = kind ? [3, 4, ...new Uint8Array(new Uint32Array([kind]).buffer).reverse()] : []; - return bech32_1.bech32.encode(prefix, bech32_1.bech32.toWords([...tl0, ...tl1, ...tl2, ...tl3]), 1000); -} -exports.encodeTLV = encodeTLV; -function decodeTLV(str) { - const decoded = bech32_1.bech32.decode(str, 1000); - const data = bech32_1.bech32.fromWords(decoded.words); - const entries = []; - let x = 0; - while (x < data.length) { - const t = data[x]; - const l = data[x + 1]; - const v = data.slice(x + 2, x + 2 + l); - entries.push({ - type: t, - length: l, - value: decodeTLVEntry(t, decoded.prefix, new Uint8Array(v)), - }); - x += 2 + l; - } - return entries; -} -exports.decodeTLV = decodeTLV; -function decodeTLVEntry(type, prefix, data) { - switch (type) { - case TLVEntryType.Special: { - if (prefix === NostrPrefix.Address) { - return new TextDecoder("ASCII").decode(data); - } - else { - return utils.bytesToHex(data); - } - } - case TLVEntryType.Author: { - return utils.bytesToHex(data); - } - case TLVEntryType.Kind: { - return new Uint32Array(new Uint8Array(data.reverse()).buffer)[0]; - } - case TLVEntryType.Relay: { - return new TextDecoder("ASCII").decode(data); - } - } -} -//# sourceMappingURL=Links.js.map \ No newline at end of file diff --git a/packages/system/dist/Links.js.map b/packages/system/dist/Links.js.map deleted file mode 100644 index 277242a7..00000000 --- a/packages/system/dist/Links.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Links.js","sourceRoot":"","sources":["../src/Links.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,oEAAsD;AACtD,mCAAgC;AAGhC,IAAY,WAUX;AAVD,WAAY,WAAW;IACrB,iCAAkB,CAAA;IAClB,kCAAmB,CAAA;IACnB,4BAAa,CAAA;IAEb,eAAe;IACf,mCAAoB,CAAA;IACpB,+BAAgB,CAAA;IAChB,+BAAgB,CAAA;IAChB,gCAAiB,CAAA;AACnB,CAAC,EAVW,WAAW,GAAX,mBAAW,KAAX,mBAAW,QAUtB;AAED,IAAY,YAKX;AALD,WAAY,YAAY;IACtB,qDAAW,CAAA;IACX,iDAAS,CAAA;IACT,mDAAU,CAAA;IACV,+CAAQ,CAAA;AACV,CAAC,EALW,YAAY,GAAZ,oBAAY,KAAZ,oBAAY,QAKvB;AAQD,SAAgB,SAAS,CAAC,MAAmB,EAAE,EAAU,EAAE,MAAiB,EAAE,IAAa,EAAE,MAAe;IAC1G,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAG,MAAM,KAAK,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAEnF,MAAM,GAAG,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;IACpC,MAAM,GAAG,GACP,MAAM;QACJ,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE;QACR,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC3B,OAAO,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;IACnC,CAAC,CAAC;SACD,IAAI,EAAE,IAAI,EAAE,CAAC;IAElB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,UAAU,CAAC,IAAI,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE5F,OAAO,eAAM,CAAC,MAAM,CAAC,MAAM,EAAE,eAAM,CAAC,OAAO,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC,EAAE,IAAK,CAAC,CAAC;AACxF,CAAC;AAjBD,8BAiBC;AAED,SAAgB,SAAS,CAAC,GAAW;IACnC,MAAM,OAAO,GAAG,eAAM,CAAC,MAAM,CAAC,GAAG,EAAE,IAAK,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,eAAM,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAE7C,MAAM,OAAO,GAAe,EAAE,CAAC;IAC/B,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE;QACtB,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACtB,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,CAAC;YACP,MAAM,EAAE,CAAC;YACT,KAAK,EAAE,cAAc,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;SAC5D,CAAC,CAAC;QACH,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;KACZ;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAlBD,8BAkBC;AAED,SAAS,cAAc,CAAC,IAAkB,EAAE,MAAc,EAAE,IAAgB;IAC1E,QAAQ,IAAI,EAAE;QACZ,KAAK,YAAY,CAAC,OAAO,CAAC,CAAC;YACzB,IAAI,MAAM,KAAK,WAAW,CAAC,OAAO,EAAE;gBAClC,OAAO,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;aAC9C;iBAAM;gBACL,OAAO,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;aAC/B;SACF;QACD,KAAK,YAAY,CAAC,MAAM,CAAC,CAAC;YACxB,OAAO,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;SAC/B;QACD,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;YACtB,OAAO,IAAI,WAAW,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;SAClE;QACD,KAAK,YAAY,CAAC,KAAK,CAAC,CAAC;YACvB,OAAO,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;SAC9C;KACF;AACH,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/Nips.d.ts b/packages/system/dist/Nips.d.ts deleted file mode 100644 index e3486652..00000000 --- a/packages/system/dist/Nips.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export declare enum Nips { - Search = 50 -} -//# sourceMappingURL=Nips.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Nips.d.ts.map b/packages/system/dist/Nips.d.ts.map deleted file mode 100644 index 42dcd779..00000000 --- a/packages/system/dist/Nips.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Nips.d.ts","sourceRoot":"","sources":["../src/Nips.ts"],"names":[],"mappings":"AAAA,oBAAY,IAAI;IACd,MAAM,KAAK;CACZ"} \ No newline at end of file diff --git a/packages/system/dist/Nips.js b/packages/system/dist/Nips.js deleted file mode 100644 index 80b98f7c..00000000 --- a/packages/system/dist/Nips.js +++ /dev/null @@ -1,8 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Nips = void 0; -var Nips; -(function (Nips) { - Nips[Nips["Search"] = 50] = "Search"; -})(Nips = exports.Nips || (exports.Nips = {})); -//# sourceMappingURL=Nips.js.map \ No newline at end of file diff --git a/packages/system/dist/Nips.js.map b/packages/system/dist/Nips.js.map deleted file mode 100644 index b5276f39..00000000 --- a/packages/system/dist/Nips.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Nips.js","sourceRoot":"","sources":["../src/Nips.ts"],"names":[],"mappings":";;;AAAA,IAAY,IAEX;AAFD,WAAY,IAAI;IACd,oCAAW,CAAA;AACb,CAAC,EAFW,IAAI,GAAJ,YAAI,KAAJ,YAAI,QAEf"} \ No newline at end of file diff --git a/packages/system/dist/Nostr.d.ts b/packages/system/dist/Nostr.d.ts deleted file mode 100644 index 040af7b8..00000000 --- a/packages/system/dist/Nostr.d.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { RelaySettings } from "./Connection"; -export interface NostrEvent { - id: u256; - pubkey: HexKey; - created_at: number; - kind: number; - tags: Array>; - content: string; - sig: string; -} -export interface TaggedRawEvent extends NostrEvent { - /** - * A list of relays this event was seen on - */ - relays: string[]; -} -/** - * Basic raw key as hex - */ -export type HexKey = string; -/** - * Optional HexKey - */ -export type MaybeHexKey = HexKey | undefined; -/** - * A 256bit hex id - */ -export type u256 = string; -export type ReqCommand = [cmd: "REQ", id: string, ...filters: Array]; -/** - * Raw REQ filter object - */ -export interface ReqFilter { - ids?: u256[]; - authors?: u256[]; - kinds?: number[]; - "#e"?: u256[]; - "#p"?: u256[]; - "#t"?: string[]; - "#d"?: string[]; - "#r"?: string[]; - search?: string; - since?: number; - until?: number; - limit?: number; -} -/** - * Medatadata event content - */ -export type UserMetadata = { - name?: string; - display_name?: string; - about?: string; - picture?: string; - website?: string; - banner?: string; - nip05?: string; - lud06?: string; - lud16?: string; -}; -/** - * NIP-51 list types - */ -export declare enum Lists { - Muted = "mute", - Pinned = "pin", - Bookmarked = "bookmark", - Followed = "follow", - Badges = "profile_badges" -} -export interface FullRelaySettings { - url: string; - settings: RelaySettings; -} -//# sourceMappingURL=Nostr.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Nostr.d.ts.map b/packages/system/dist/Nostr.d.ts.map deleted file mode 100644 index 6e003def..00000000 --- a/packages/system/dist/Nostr.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Nostr.d.ts","sourceRoot":"","sources":["../src/Nostr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD;;OAEG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;AAE5B;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC;AAE1B,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC;IACb,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;IACd,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,oBAAY,KAAK;IACf,KAAK,SAAS;IACd,MAAM,QAAQ;IACd,UAAU,aAAa;IACvB,QAAQ,WAAW;IACnB,MAAM,mBAAmB;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,aAAa,CAAC;CACzB"} \ No newline at end of file diff --git a/packages/system/dist/Nostr.js b/packages/system/dist/Nostr.js deleted file mode 100644 index 5c2cad16..00000000 --- a/packages/system/dist/Nostr.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Lists = void 0; -/** - * NIP-51 list types - */ -var Lists; -(function (Lists) { - Lists["Muted"] = "mute"; - Lists["Pinned"] = "pin"; - Lists["Bookmarked"] = "bookmark"; - Lists["Followed"] = "follow"; - Lists["Badges"] = "profile_badges"; -})(Lists = exports.Lists || (exports.Lists = {})); -//# sourceMappingURL=Nostr.js.map \ No newline at end of file diff --git a/packages/system/dist/Nostr.js.map b/packages/system/dist/Nostr.js.map deleted file mode 100644 index 1100ff69..00000000 --- a/packages/system/dist/Nostr.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Nostr.js","sourceRoot":"","sources":["../src/Nostr.ts"],"names":[],"mappings":";;;AAqEA;;GAEG;AACH,IAAY,KAMX;AAND,WAAY,KAAK;IACf,uBAAc,CAAA;IACd,uBAAc,CAAA;IACd,gCAAuB,CAAA;IACvB,4BAAmB,CAAA;IACnB,kCAAyB,CAAA;AAC3B,CAAC,EANW,KAAK,GAAL,aAAK,KAAL,aAAK,QAMhB"} \ No newline at end of file diff --git a/packages/system/dist/NostrLink.d.ts b/packages/system/dist/NostrLink.d.ts deleted file mode 100644 index 8d22c067..00000000 --- a/packages/system/dist/NostrLink.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NostrPrefix } from "."; -export interface NostrLink { - type: NostrPrefix; - id: string; - kind?: number; - author?: string; - relays?: Array; - encode(): string; -} -export declare function validateNostrLink(link: string): boolean; -export declare function tryParseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLink | undefined; -export declare function parseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLink; -//# sourceMappingURL=NostrLink.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/NostrLink.d.ts.map b/packages/system/dist/NostrLink.d.ts.map deleted file mode 100644 index ab1d2006..00000000 --- a/packages/system/dist/NostrLink.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"NostrLink.d.ts","sourceRoot":"","sources":["../src/NostrLink.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAA2B,MAAM,GAAG,CAAC;AAEzD,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvB,MAAM,IAAI,MAAM,CAAC;CAClB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAcvD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,WAAW,GAAG,SAAS,GAAG,SAAS,CAM/F;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,WAAW,GAAG,SAAS,CAwEhF"} \ No newline at end of file diff --git a/packages/system/dist/NostrLink.js b/packages/system/dist/NostrLink.js deleted file mode 100644 index 18cecd68..00000000 --- a/packages/system/dist/NostrLink.js +++ /dev/null @@ -1,110 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.parseNostrLink = exports.tryParseNostrLink = exports.validateNostrLink = void 0; -const Util_1 = require("./Util"); -const _1 = require("."); -function validateNostrLink(link) { - try { - const parsedLink = parseNostrLink(link); - if (!parsedLink) { - return false; - } - if (parsedLink.type === _1.NostrPrefix.PublicKey || parsedLink.type === _1.NostrPrefix.Note) { - return parsedLink.id.length === 64; - } - return true; - } - catch { - return false; - } -} -exports.validateNostrLink = validateNostrLink; -function tryParseNostrLink(link, prefixHint) { - try { - return parseNostrLink(link, prefixHint); - } - catch { - return undefined; - } -} -exports.tryParseNostrLink = tryParseNostrLink; -function parseNostrLink(link, prefixHint) { - const entity = link.startsWith("web+nostr:") || link.startsWith("nostr:") ? link.split(":")[1] : link; - const isPrefix = (prefix) => { - return entity.startsWith(prefix); - }; - if (isPrefix(_1.NostrPrefix.PublicKey)) { - const id = (0, Util_1.bech32ToHex)(entity); - if (id.length !== 64) - throw new Error("Invalid nostr link, must contain 32 byte id"); - return { - type: _1.NostrPrefix.PublicKey, - id: id, - encode: () => (0, Util_1.hexToBech32)(_1.NostrPrefix.PublicKey, id), - }; - } - else if (isPrefix(_1.NostrPrefix.Note)) { - const id = (0, Util_1.bech32ToHex)(entity); - if (id.length !== 64) - throw new Error("Invalid nostr link, must contain 32 byte id"); - return { - type: _1.NostrPrefix.Note, - id: id, - encode: () => (0, Util_1.hexToBech32)(_1.NostrPrefix.Note, id), - }; - } - else if (isPrefix(_1.NostrPrefix.Profile) || isPrefix(_1.NostrPrefix.Event) || isPrefix(_1.NostrPrefix.Address)) { - const decoded = (0, _1.decodeTLV)(entity); - const id = decoded.find(a => a.type === _1.TLVEntryType.Special)?.value; - const relays = decoded.filter(a => a.type === _1.TLVEntryType.Relay).map(a => a.value); - const author = decoded.find(a => a.type === _1.TLVEntryType.Author)?.value; - const kind = decoded.find(a => a.type === _1.TLVEntryType.Kind)?.value; - const encode = () => { - return entity; // return original - }; - if (isPrefix(_1.NostrPrefix.Profile)) { - if (id.length !== 64) - throw new Error("Invalid nostr link, must contain 32 byte id"); - return { - type: _1.NostrPrefix.Profile, - id, - relays, - kind, - author, - encode, - }; - } - else if (isPrefix(_1.NostrPrefix.Event)) { - if (id.length !== 64) - throw new Error("Invalid nostr link, must contain 32 byte id"); - return { - type: _1.NostrPrefix.Event, - id, - relays, - kind, - author, - encode, - }; - } - else if (isPrefix(_1.NostrPrefix.Address)) { - return { - type: _1.NostrPrefix.Address, - id, - relays, - kind, - author, - encode, - }; - } - } - else if (prefixHint) { - return { - type: prefixHint, - id: link, - encode: () => (0, Util_1.hexToBech32)(prefixHint, link), - }; - } - throw new Error("Invalid nostr link"); -} -exports.parseNostrLink = parseNostrLink; -//# sourceMappingURL=NostrLink.js.map \ No newline at end of file diff --git a/packages/system/dist/NostrLink.js.map b/packages/system/dist/NostrLink.js.map deleted file mode 100644 index 8ad349a3..00000000 --- a/packages/system/dist/NostrLink.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"NostrLink.js","sourceRoot":"","sources":["../src/NostrLink.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/NostrSystem.d.ts b/packages/system/dist/NostrSystem.d.ts deleted file mode 100644 index c684a9be..00000000 --- a/packages/system/dist/NostrSystem.d.ts +++ /dev/null @@ -1,75 +0,0 @@ -import ExternalStore from "./ExternalStore"; -import { NostrEvent, TaggedRawEvent } from "./Nostr"; -import { AuthHandler, Connection, RelaySettings, ConnectionStateSnapshot } from "./Connection"; -import { Query } from "./Query"; -import { RelayCache } from "./GossipModel"; -import { NoteStore } from "./NoteCollection"; -import { BuiltRawReqFilter, RequestBuilder } from "./RequestBuilder"; -import { SystemInterface, SystemSnapshot } from "."; -/** - * Manages nostr content retrieval system - */ -export declare class NostrSystem extends ExternalStore implements SystemInterface { - #private; - /** - * All active queries - */ - Queries: Map; - /** - * Handler function for NIP-42 - */ - HandleAuth?: AuthHandler; - constructor(relayCache: RelayCache); - get Sockets(): ConnectionStateSnapshot[]; - /** - * Connect to a NOSTR relay if not already connected - */ - ConnectToRelay(address: string, options: RelaySettings): Promise; - OnRelayDisconnect(id: string): void; - OnEndOfStoredEvents(c: Readonly, sub: string): void; - OnEvent(sub: string, ev: TaggedRawEvent): void; - /** - * - * @param address Relay address URL - */ - ConnectEphemeralRelay(address: string): Promise; - /** - * Disconnect from a relay - */ - DisconnectRelay(address: string): void; - GetQuery(id: string): Query | undefined; - Query(type: { - new (): T; - }, req: RequestBuilder): Query; - SendQuery(q: Query, qSend: BuiltRawReqFilter): Promise<{ - readonly id: string; - readonly start: number; - sent?: number | undefined; - eose?: number | undefined; - close?: number | undefined; - "__#9@#wasForceClosed": boolean; - readonly "__#9@#fnClose": (id: string) => void; - readonly "__#9@#fnProgress": () => void; - readonly relay: string; - readonly filters: import("./Nostr").ReqFilter[]; - readonly connId: string; - sentToRelay(): void; - gotEose(): void; - forceEose(): void; - sendClose(): void; - readonly queued: number; - readonly runtime: number; - readonly responseTime: number; - readonly finished: boolean; - }[]>; - /** - * Send events to writable relays - */ - BroadcastEvent(ev: NostrEvent): void; - /** - * Write an event to a relay then disconnect - */ - WriteOnceToRelay(address: string, ev: NostrEvent): Promise; - takeSnapshot(): SystemSnapshot; -} -//# sourceMappingURL=NostrSystem.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/NostrSystem.d.ts.map b/packages/system/dist/NostrSystem.d.ts.map deleted file mode 100644 index 6cfd8efc..00000000 --- a/packages/system/dist/NostrSystem.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"NostrSystem.d.ts","sourceRoot":"","sources":["../src/NostrSystem.ts"],"names":[],"mappings":"AAEA,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAC/F,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAErE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,GAAG,CAAC;AAEpD;;GAEG;AACH,qBAAa,WAAY,SAAQ,aAAa,CAAC,cAAc,CAAE,YAAW,eAAe;;IAMvF;;OAEG;IACH,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAa;IAExC;;OAEG;IACH,UAAU,CAAC,EAAE,WAAW,CAAC;gBAKb,UAAU,EAAE,UAAU;IAMlC,IAAI,OAAO,IAAI,uBAAuB,EAAE,CAEvC;IAED;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa;IAmB5D,iBAAiB,CAAC,EAAE,EAAE,MAAM;IAM5B,mBAAmB,CAAC,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,MAAM;IAMxD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc;IAMvC;;;OAGG;IACG,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAiB7E;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM;IAQ/B,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAIvC,KAAK,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,EAAE;QAAE,QAAQ,CAAC,CAAA;KAAE,EAAE,GAAG,EAAE,cAAc,GAAG,KAAK;IAiCrE,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB;;;;;;;;;;;;;;;;;;;;;IAmClD;;OAEG;IACH,cAAc,CAAC,EAAE,EAAE,UAAU;IAM7B;;OAEG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU;IAetD,YAAY,IAAI,cAAc;CA2B/B"} \ No newline at end of file diff --git a/packages/system/dist/NostrSystem.js b/packages/system/dist/NostrSystem.js deleted file mode 100644 index 305b539d..00000000 --- a/packages/system/dist/NostrSystem.js +++ /dev/null @@ -1,237 +0,0 @@ -"use strict"; -var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { - if (kind === "m") throw new TypeError("Private method is not writable"); - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); - return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; -}; -var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); - return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -var _NostrSystem_instances, _NostrSystem_sockets, _NostrSystem_log, _NostrSystem_relayCache, _NostrSystem_cleanup; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.NostrSystem = void 0; -const debug_1 = __importDefault(require("debug")); -const ExternalStore_1 = __importDefault(require("./ExternalStore")); -const Connection_1 = require("./Connection"); -const Query_1 = require("./Query"); -const Util_1 = require("./Util"); -/** - * Manages nostr content retrieval system - */ -class NostrSystem extends ExternalStore_1.default { - constructor(relayCache) { - super(); - _NostrSystem_instances.add(this); - /** - * All currently connected websockets - */ - _NostrSystem_sockets.set(this, new Map()); - /** - * All active queries - */ - this.Queries = new Map(); - _NostrSystem_log.set(this, (0, debug_1.default)("System")); - _NostrSystem_relayCache.set(this, void 0); - __classPrivateFieldSet(this, _NostrSystem_relayCache, relayCache, "f"); - __classPrivateFieldGet(this, _NostrSystem_instances, "m", _NostrSystem_cleanup).call(this); - } - get Sockets() { - return [...__classPrivateFieldGet(this, _NostrSystem_sockets, "f").values()].map(a => a.snapshot()); - } - /** - * Connect to a NOSTR relay if not already connected - */ - async ConnectToRelay(address, options) { - try { - const addr = (0, Util_1.unwrap)((0, Util_1.sanitizeRelayUrl)(address)); - if (!__classPrivateFieldGet(this, _NostrSystem_sockets, "f").has(addr)) { - const c = new Connection_1.Connection(addr, options, this.HandleAuth?.bind(this)); - __classPrivateFieldGet(this, _NostrSystem_sockets, "f").set(addr, c); - c.OnEvent = (s, e) => this.OnEvent(s, e); - c.OnEose = s => this.OnEndOfStoredEvents(c, s); - c.OnDisconnect = id => this.OnRelayDisconnect(id); - await c.Connect(); - } - else { - // update settings if already connected - (0, Util_1.unwrap)(__classPrivateFieldGet(this, _NostrSystem_sockets, "f").get(addr)).Settings = options; - } - } - catch (e) { - console.error(e); - } - } - OnRelayDisconnect(id) { - for (const [, q] of this.Queries) { - q.connectionLost(id); - } - } - OnEndOfStoredEvents(c, sub) { - for (const [, v] of this.Queries) { - v.eose(sub, c); - } - } - OnEvent(sub, ev) { - for (const [, v] of this.Queries) { - v.onEvent(sub, ev); - } - } - /** - * - * @param address Relay address URL - */ - async ConnectEphemeralRelay(address) { - try { - const addr = (0, Util_1.unwrap)((0, Util_1.sanitizeRelayUrl)(address)); - if (!__classPrivateFieldGet(this, _NostrSystem_sockets, "f").has(addr)) { - const c = new Connection_1.Connection(addr, { read: true, write: false }, this.HandleAuth?.bind(this), true); - __classPrivateFieldGet(this, _NostrSystem_sockets, "f").set(addr, c); - c.OnEvent = (s, e) => this.OnEvent(s, e); - c.OnEose = s => this.OnEndOfStoredEvents(c, s); - c.OnDisconnect = id => this.OnRelayDisconnect(id); - await c.Connect(); - return c; - } - } - catch (e) { - console.error(e); - } - } - /** - * Disconnect from a relay - */ - DisconnectRelay(address) { - const c = __classPrivateFieldGet(this, _NostrSystem_sockets, "f").get(address); - if (c) { - __classPrivateFieldGet(this, _NostrSystem_sockets, "f").delete(address); - c.Close(); - } - } - GetQuery(id) { - return this.Queries.get(id); - } - Query(type, req) { - const existing = this.Queries.get(req.id); - if (existing) { - const filters = !req.options?.skipDiff - ? req.buildDiff(__classPrivateFieldGet(this, _NostrSystem_relayCache, "f"), existing.filters) - : req.build(__classPrivateFieldGet(this, _NostrSystem_relayCache, "f")); - if (filters.length === 0 && !!req.options?.skipDiff) { - return existing; - } - else { - for (const subQ of filters) { - this.SendQuery(existing, subQ).then(qta => qta.forEach(v => __classPrivateFieldGet(this, _NostrSystem_log, "f").call(this, "New QT from diff %s %s %O from: %O", req.id, v.id, v.filters, existing.filters))); - } - this.notifyChange(); - return existing; - } - } - else { - const store = new type(); - const filters = req.build(__classPrivateFieldGet(this, _NostrSystem_relayCache, "f")); - const q = new Query_1.Query(req.id, store, req.options?.leaveOpen); - this.Queries.set(req.id, q); - for (const subQ of filters) { - this.SendQuery(q, subQ).then(qta => qta.forEach(v => __classPrivateFieldGet(this, _NostrSystem_log, "f").call(this, "New QT from diff %s %s %O", req.id, v.id, v.filters))); - } - this.notifyChange(); - return q; - } - } - async SendQuery(q, qSend) { - if (qSend.relay) { - __classPrivateFieldGet(this, _NostrSystem_log, "f").call(this, "Sending query to %s %O", qSend.relay, qSend); - const s = __classPrivateFieldGet(this, _NostrSystem_sockets, "f").get(qSend.relay); - if (s) { - const qt = q.sendToRelay(s, qSend); - if (qt) { - return [qt]; - } - } - else { - const nc = await this.ConnectEphemeralRelay(qSend.relay); - if (nc) { - const qt = q.sendToRelay(nc, qSend); - if (qt) { - return [qt]; - } - } - else { - console.warn("Failed to connect to new relay for:", qSend.relay, q); - } - } - } - else { - const ret = []; - for (const [, s] of __classPrivateFieldGet(this, _NostrSystem_sockets, "f")) { - if (!s.Ephemeral) { - const qt = q.sendToRelay(s, qSend); - if (qt) { - ret.push(qt); - } - } - } - return ret; - } - return []; - } - /** - * Send events to writable relays - */ - BroadcastEvent(ev) { - for (const [, s] of __classPrivateFieldGet(this, _NostrSystem_sockets, "f")) { - s.SendEvent(ev); - } - } - /** - * Write an event to a relay then disconnect - */ - async WriteOnceToRelay(address, ev) { - return new Promise((resolve, reject) => { - const c = new Connection_1.Connection(address, { write: true, read: false }, this.HandleAuth, true); - const t = setTimeout(reject, 5000); - c.OnConnected = async () => { - clearTimeout(t); - await c.SendAsync(ev); - c.Close(); - resolve(); - }; - c.Connect(); - }); - } - takeSnapshot() { - return { - queries: [...this.Queries.values()].map(a => { - return { - id: a.id, - filters: a.filters, - subFilters: [], - }; - }), - }; - } -} -exports.NostrSystem = NostrSystem; -_NostrSystem_sockets = new WeakMap(), _NostrSystem_log = new WeakMap(), _NostrSystem_relayCache = new WeakMap(), _NostrSystem_instances = new WeakSet(), _NostrSystem_cleanup = function _NostrSystem_cleanup() { - let changed = false; - for (const [k, v] of this.Queries) { - if (v.canRemove()) { - v.sendClose(); - this.Queries.delete(k); - __classPrivateFieldGet(this, _NostrSystem_log, "f").call(this, "Deleted query %s", k); - changed = true; - } - } - if (changed) { - this.notifyChange(); - } - setTimeout(() => __classPrivateFieldGet(this, _NostrSystem_instances, "m", _NostrSystem_cleanup).call(this), 1000); -}; -//# sourceMappingURL=NostrSystem.js.map \ No newline at end of file diff --git a/packages/system/dist/NostrSystem.js.map b/packages/system/dist/NostrSystem.js.map deleted file mode 100644 index 52f75a20..00000000 --- a/packages/system/dist/NostrSystem.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"NostrSystem.js","sourceRoot":"","sources":["../src/NostrSystem.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/NoteCollection.d.ts b/packages/system/dist/NoteCollection.d.ts deleted file mode 100644 index 47f8c80c..00000000 --- a/packages/system/dist/NoteCollection.d.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { TaggedRawEvent } from "."; -export interface StoreSnapshot { - data: TSnapshot | undefined; - clear: () => void; - loading: () => boolean; - add: (ev: Readonly | Readonly>) => void; -} -export declare const EmptySnapshot: StoreSnapshot; -export type NoteStoreSnapshotData = Readonly> | Readonly; -export type NoteStoreHook = () => void; -export type NoteStoreHookRelease = () => void; -export type OnEventCallback = (e: Readonly>) => void; -export type OnEventCallbackRelease = () => void; -export type OnEoseCallback = (c: string) => void; -export type OnEoseCallbackRelease = () => void; -/** - * Generic note store interface - */ -export declare abstract class NoteStore { - abstract add(ev: Readonly | Readonly>): void; - abstract clear(): void; - abstract hook(cb: NoteStoreHook): NoteStoreHookRelease; - abstract getSnapshotData(): NoteStoreSnapshotData | undefined; - abstract onEvent(cb: OnEventCallback): OnEventCallbackRelease; - abstract get snapshot(): StoreSnapshot; - abstract get loading(): boolean; - abstract set loading(v: boolean); -} -export declare abstract class HookedNoteStore implements NoteStore { - #private; - get snapshot(): StoreSnapshot; - get loading(): boolean; - set loading(v: boolean); - abstract add(ev: Readonly | Readonly>): void; - abstract clear(): void; - hook(cb: NoteStoreHook): NoteStoreHookRelease; - getSnapshotData(): TSnapshot | undefined; - onEvent(cb: OnEventCallback): OnEventCallbackRelease; - protected abstract takeSnapshot(): TSnapshot | undefined; - protected onChange(changes: Readonly>): void; -} -/** - * A simple flat container of events with no duplicates - */ -export declare class FlatNoteStore extends HookedNoteStore>> { - #private; - add(ev: TaggedRawEvent | Array): void; - clear(): void; - takeSnapshot(): TaggedRawEvent[]; -} -/** - * A note store that holds a single replaceable event for a given user defined key generator function - */ -export declare class KeyedReplaceableNoteStore extends HookedNoteStore>> { - #private; - constructor(fn: (ev: TaggedRawEvent) => string); - add(ev: TaggedRawEvent | Array): void; - clear(): void; - takeSnapshot(): TaggedRawEvent[]; -} -/** - * A note store that holds a single replaceable event - */ -export declare class ReplaceableNoteStore extends HookedNoteStore> { - #private; - add(ev: TaggedRawEvent | Array): void; - clear(): void; - takeSnapshot(): Readonly<{ - relays: string[]; - id: string; - pubkey: string; - created_at: number; - kind: number; - tags: string[][]; - content: string; - sig: string; - }> | undefined; -} -/** - * A note store that holds a single replaceable event per pubkey - */ -export declare class PubkeyReplaceableNoteStore extends KeyedReplaceableNoteStore { - constructor(); -} -/** - * A note store that holds a single replaceable event per "pubkey-dtag" - */ -export declare class ParameterizedReplaceableNoteStore extends KeyedReplaceableNoteStore { - constructor(); -} -//# sourceMappingURL=NoteCollection.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/NoteCollection.d.ts.map b/packages/system/dist/NoteCollection.d.ts.map deleted file mode 100644 index ffe731e5..00000000 --- a/packages/system/dist/NoteCollection.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"NoteCollection.d.ts","sourceRoot":"","sources":["../src/NoteCollection.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/NoteCollection.js b/packages/system/dist/NoteCollection.js deleted file mode 100644 index 70e7376c..00000000 --- a/packages/system/dist/NoteCollection.js +++ /dev/null @@ -1,240 +0,0 @@ -"use strict"; -var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); - return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -}; -var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { - if (kind === "m") throw new TypeError("Private method is not writable"); - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); - return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; -}; -var _HookedNoteStore_instances, _HookedNoteStore_hooks, _HookedNoteStore_eventHooks, _HookedNoteStore_loading, _HookedNoteStore_storeSnapshot, _HookedNoteStore_needsSnapshot, _HookedNoteStore_nextNotifyTimer, _HookedNoteStore_updateSnapshot, _FlatNoteStore_events, _FlatNoteStore_ids, _KeyedReplaceableNoteStore_keyFn, _KeyedReplaceableNoteStore_events, _ReplaceableNoteStore_event; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ParameterizedReplaceableNoteStore = exports.PubkeyReplaceableNoteStore = exports.ReplaceableNoteStore = exports.KeyedReplaceableNoteStore = exports.FlatNoteStore = exports.HookedNoteStore = exports.NoteStore = exports.EmptySnapshot = void 0; -const Util_1 = require("./Util"); -exports.EmptySnapshot = { - data: undefined, - clear: () => { - // empty - }, - loading: () => true, - add: () => { - // empty - }, -}; -/** - * Generic note store interface - */ -class NoteStore { -} -exports.NoteStore = NoteStore; -class HookedNoteStore { - constructor() { - _HookedNoteStore_instances.add(this); - _HookedNoteStore_hooks.set(this, []); - _HookedNoteStore_eventHooks.set(this, []); - _HookedNoteStore_loading.set(this, true); - _HookedNoteStore_storeSnapshot.set(this, { - clear: () => this.clear(), - loading: () => this.loading, - add: ev => this.add(ev), - data: undefined, - }); - _HookedNoteStore_needsSnapshot.set(this, true); - _HookedNoteStore_nextNotifyTimer.set(this, void 0); - } - get snapshot() { - __classPrivateFieldGet(this, _HookedNoteStore_instances, "m", _HookedNoteStore_updateSnapshot).call(this); - return __classPrivateFieldGet(this, _HookedNoteStore_storeSnapshot, "f"); - } - get loading() { - return __classPrivateFieldGet(this, _HookedNoteStore_loading, "f"); - } - set loading(v) { - __classPrivateFieldSet(this, _HookedNoteStore_loading, v, "f"); - this.onChange([]); - } - hook(cb) { - __classPrivateFieldGet(this, _HookedNoteStore_hooks, "f").push(cb); - return () => { - const idx = __classPrivateFieldGet(this, _HookedNoteStore_hooks, "f").findIndex(a => a === cb); - __classPrivateFieldGet(this, _HookedNoteStore_hooks, "f").splice(idx, 1); - }; - } - getSnapshotData() { - __classPrivateFieldGet(this, _HookedNoteStore_instances, "m", _HookedNoteStore_updateSnapshot).call(this); - return __classPrivateFieldGet(this, _HookedNoteStore_storeSnapshot, "f").data; - } - onEvent(cb) { - const existing = __classPrivateFieldGet(this, _HookedNoteStore_eventHooks, "f").find(a => a === cb); - if (!existing) { - __classPrivateFieldGet(this, _HookedNoteStore_eventHooks, "f").push(cb); - return () => { - const idx = __classPrivateFieldGet(this, _HookedNoteStore_eventHooks, "f").findIndex(a => a === cb); - __classPrivateFieldGet(this, _HookedNoteStore_eventHooks, "f").splice(idx, 1); - }; - } - return () => { - //noop - }; - } - onChange(changes) { - __classPrivateFieldSet(this, _HookedNoteStore_needsSnapshot, true, "f"); - if (!__classPrivateFieldGet(this, _HookedNoteStore_nextNotifyTimer, "f")) { - __classPrivateFieldSet(this, _HookedNoteStore_nextNotifyTimer, setTimeout(() => { - __classPrivateFieldSet(this, _HookedNoteStore_nextNotifyTimer, undefined, "f"); - for (const hk of __classPrivateFieldGet(this, _HookedNoteStore_hooks, "f")) { - hk(); - } - }, 500), "f"); - } - if (changes.length > 0) { - for (const hkE of __classPrivateFieldGet(this, _HookedNoteStore_eventHooks, "f")) { - hkE(changes); - } - } - } -} -exports.HookedNoteStore = HookedNoteStore; -_HookedNoteStore_hooks = new WeakMap(), _HookedNoteStore_eventHooks = new WeakMap(), _HookedNoteStore_loading = new WeakMap(), _HookedNoteStore_storeSnapshot = new WeakMap(), _HookedNoteStore_needsSnapshot = new WeakMap(), _HookedNoteStore_nextNotifyTimer = new WeakMap(), _HookedNoteStore_instances = new WeakSet(), _HookedNoteStore_updateSnapshot = function _HookedNoteStore_updateSnapshot() { - if (__classPrivateFieldGet(this, _HookedNoteStore_needsSnapshot, "f")) { - __classPrivateFieldSet(this, _HookedNoteStore_storeSnapshot, { - ...__classPrivateFieldGet(this, _HookedNoteStore_storeSnapshot, "f"), - data: this.takeSnapshot(), - }, "f"); - __classPrivateFieldSet(this, _HookedNoteStore_needsSnapshot, false, "f"); - } -}; -/** - * A simple flat container of events with no duplicates - */ -class FlatNoteStore extends HookedNoteStore { - constructor() { - super(...arguments); - _FlatNoteStore_events.set(this, []); - _FlatNoteStore_ids.set(this, new Set()); - } - add(ev) { - ev = Array.isArray(ev) ? ev : [ev]; - const changes = []; - ev.forEach(a => { - if (!__classPrivateFieldGet(this, _FlatNoteStore_ids, "f").has(a.id)) { - __classPrivateFieldGet(this, _FlatNoteStore_events, "f").push(a); - __classPrivateFieldGet(this, _FlatNoteStore_ids, "f").add(a.id); - changes.push(a); - } - else { - const existing = __classPrivateFieldGet(this, _FlatNoteStore_events, "f").find(b => b.id === a.id); - if (existing) { - existing.relays = (0, Util_1.appendDedupe)(existing.relays, a.relays); - } - } - }); - if (changes.length > 0) { - this.onChange(changes); - } - } - clear() { - __classPrivateFieldSet(this, _FlatNoteStore_events, [], "f"); - __classPrivateFieldGet(this, _FlatNoteStore_ids, "f").clear(); - this.onChange([]); - } - takeSnapshot() { - return [...__classPrivateFieldGet(this, _FlatNoteStore_events, "f")]; - } -} -exports.FlatNoteStore = FlatNoteStore; -_FlatNoteStore_events = new WeakMap(), _FlatNoteStore_ids = new WeakMap(); -/** - * A note store that holds a single replaceable event for a given user defined key generator function - */ -class KeyedReplaceableNoteStore extends HookedNoteStore { - constructor(fn) { - super(); - _KeyedReplaceableNoteStore_keyFn.set(this, void 0); - _KeyedReplaceableNoteStore_events.set(this, new Map()); - __classPrivateFieldSet(this, _KeyedReplaceableNoteStore_keyFn, fn, "f"); - } - add(ev) { - ev = Array.isArray(ev) ? ev : [ev]; - const changes = []; - ev.forEach(a => { - const keyOnEvent = __classPrivateFieldGet(this, _KeyedReplaceableNoteStore_keyFn, "f").call(this, a); - const existingCreated = __classPrivateFieldGet(this, _KeyedReplaceableNoteStore_events, "f").get(keyOnEvent)?.created_at ?? 0; - if (a.created_at > existingCreated) { - __classPrivateFieldGet(this, _KeyedReplaceableNoteStore_events, "f").set(keyOnEvent, a); - changes.push(a); - } - }); - if (changes.length > 0) { - this.onChange(changes); - } - } - clear() { - __classPrivateFieldGet(this, _KeyedReplaceableNoteStore_events, "f").clear(); - this.onChange([]); - } - takeSnapshot() { - return [...__classPrivateFieldGet(this, _KeyedReplaceableNoteStore_events, "f").values()]; - } -} -exports.KeyedReplaceableNoteStore = KeyedReplaceableNoteStore; -_KeyedReplaceableNoteStore_keyFn = new WeakMap(), _KeyedReplaceableNoteStore_events = new WeakMap(); -/** - * A note store that holds a single replaceable event - */ -class ReplaceableNoteStore extends HookedNoteStore { - constructor() { - super(...arguments); - _ReplaceableNoteStore_event.set(this, void 0); - } - add(ev) { - ev = Array.isArray(ev) ? ev : [ev]; - const changes = []; - ev.forEach(a => { - const existingCreated = __classPrivateFieldGet(this, _ReplaceableNoteStore_event, "f")?.created_at ?? 0; - if (a.created_at > existingCreated) { - __classPrivateFieldSet(this, _ReplaceableNoteStore_event, a, "f"); - changes.push(a); - } - }); - if (changes.length > 0) { - this.onChange(changes); - } - } - clear() { - __classPrivateFieldSet(this, _ReplaceableNoteStore_event, undefined, "f"); - this.onChange([]); - } - takeSnapshot() { - if (__classPrivateFieldGet(this, _ReplaceableNoteStore_event, "f")) { - return Object.freeze({ ...__classPrivateFieldGet(this, _ReplaceableNoteStore_event, "f") }); - } - } -} -exports.ReplaceableNoteStore = ReplaceableNoteStore; -_ReplaceableNoteStore_event = new WeakMap(); -/** - * A note store that holds a single replaceable event per pubkey - */ -class PubkeyReplaceableNoteStore extends KeyedReplaceableNoteStore { - constructor() { - super(e => e.pubkey); - } -} -exports.PubkeyReplaceableNoteStore = PubkeyReplaceableNoteStore; -/** - * A note store that holds a single replaceable event per "pubkey-dtag" - */ -class ParameterizedReplaceableNoteStore extends KeyedReplaceableNoteStore { - constructor() { - super(ev => { - const dTag = (0, Util_1.findTag)(ev, "d"); - return `${ev.pubkey}-${dTag}`; - }); - } -} -exports.ParameterizedReplaceableNoteStore = ParameterizedReplaceableNoteStore; -//# sourceMappingURL=NoteCollection.js.map \ No newline at end of file diff --git a/packages/system/dist/NoteCollection.js.map b/packages/system/dist/NoteCollection.js.map deleted file mode 100644 index b9630ff9..00000000 --- a/packages/system/dist/NoteCollection.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"NoteCollection.js","sourceRoot":"","sources":["../src/NoteCollection.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/ProfileCache.d.ts b/packages/system/dist/ProfileCache.d.ts deleted file mode 100644 index 87f4e53f..00000000 --- a/packages/system/dist/ProfileCache.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { HexKey, SystemInterface, TaggedRawEvent } from "."; -import { CacheStore, MetadataCache } from "./cache"; -export declare class ProfileLoaderService { - #private; - /** - * List of pubkeys to fetch metadata for - */ - WantsMetadata: Set; - constructor(system: SystemInterface, cache: CacheStore); - /** - * Request profile metadata for a set of pubkeys - */ - TrackMetadata(pk: HexKey | Array): void; - /** - * Stop tracking metadata for a set of pubkeys - */ - UntrackMetadata(pk: HexKey | Array): void; - onProfileEvent(e: Readonly): Promise; -} -//# sourceMappingURL=ProfileCache.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/ProfileCache.d.ts.map b/packages/system/dist/ProfileCache.d.ts.map deleted file mode 100644 index acbcf017..00000000 --- a/packages/system/dist/ProfileCache.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ProfileCache.d.ts","sourceRoot":"","sources":["../src/ProfileCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,MAAM,EAAE,eAAe,EAAE,cAAc,EAA8C,MAAM,GAAG,CAAC;AAEnH,OAAO,EAAE,UAAU,EAAqB,aAAa,EAAE,MAAM,SAAS,CAAC;AAIvE,qBAAa,oBAAoB;;IAI/B;;OAEG;IACH,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAa;gBAI3B,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC;IAMrE;;OAEG;IACH,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAUxC;;OAEG;IACH,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAQpC,cAAc,CAAC,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC;CA4EjD"} \ No newline at end of file diff --git a/packages/system/dist/ProfileCache.js b/packages/system/dist/ProfileCache.js deleted file mode 100644 index 98105241..00000000 --- a/packages/system/dist/ProfileCache.js +++ /dev/null @@ -1,129 +0,0 @@ -"use strict"; -var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { - if (kind === "m") throw new TypeError("Private method is not writable"); - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); - return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; -}; -var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); - return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -var _ProfileLoaderService_instances, _ProfileLoaderService_system, _ProfileLoaderService_cache, _ProfileLoaderService_log, _ProfileLoaderService_FetchMetadata; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ProfileLoaderService = void 0; -const _1 = require("."); -const Const_1 = require("./Const"); -const cache_1 = require("./cache"); -const Util_1 = require("./Util"); -const debug_1 = __importDefault(require("debug")); -class ProfileLoaderService { - constructor(system, cache) { - _ProfileLoaderService_instances.add(this); - _ProfileLoaderService_system.set(this, void 0); - _ProfileLoaderService_cache.set(this, void 0); - /** - * List of pubkeys to fetch metadata for - */ - this.WantsMetadata = new Set(); - _ProfileLoaderService_log.set(this, (0, debug_1.default)("ProfileCache")); - __classPrivateFieldSet(this, _ProfileLoaderService_system, system, "f"); - __classPrivateFieldSet(this, _ProfileLoaderService_cache, cache, "f"); - __classPrivateFieldGet(this, _ProfileLoaderService_instances, "m", _ProfileLoaderService_FetchMetadata).call(this); - } - /** - * Request profile metadata for a set of pubkeys - */ - TrackMetadata(pk) { - const bufferNow = []; - for (const p of Array.isArray(pk) ? pk : [pk]) { - if (p.length > 0 && this.WantsMetadata.add(p)) { - bufferNow.push(p); - } - } - __classPrivateFieldGet(this, _ProfileLoaderService_cache, "f").buffer(bufferNow); - } - /** - * Stop tracking metadata for a set of pubkeys - */ - UntrackMetadata(pk) { - for (const p of Array.isArray(pk) ? pk : [pk]) { - if (p.length > 0) { - this.WantsMetadata.delete(p); - } - } - } - async onProfileEvent(e) { - const profile = (0, cache_1.mapEventToProfile)(e); - if (profile) { - await __classPrivateFieldGet(this, _ProfileLoaderService_cache, "f").update(profile); - } - } -} -exports.ProfileLoaderService = ProfileLoaderService; -_ProfileLoaderService_system = new WeakMap(), _ProfileLoaderService_cache = new WeakMap(), _ProfileLoaderService_log = new WeakMap(), _ProfileLoaderService_instances = new WeakSet(), _ProfileLoaderService_FetchMetadata = async function _ProfileLoaderService_FetchMetadata() { - const missingFromCache = await __classPrivateFieldGet(this, _ProfileLoaderService_cache, "f").buffer([...this.WantsMetadata]); - const expire = (0, Util_1.unixNowMs)() - Const_1.ProfileCacheExpire; - const expired = [...this.WantsMetadata] - .filter(a => !missingFromCache.includes(a)) - .filter(a => (__classPrivateFieldGet(this, _ProfileLoaderService_cache, "f").getFromCache(a)?.loaded ?? 0) < expire); - const missing = new Set([...missingFromCache, ...expired]); - if (missing.size > 0) { - __classPrivateFieldGet(this, _ProfileLoaderService_log, "f").call(this, "Wants profiles: %d missing, %d expired", missingFromCache.length, expired.length); - const sub = new _1.RequestBuilder("profiles"); - sub - .withOptions({ - skipDiff: true, - }) - .withFilter() - .kinds([_1.EventKind.SetMetadata]) - .authors([...missing]); - const newProfiles = new Set(); - const q = __classPrivateFieldGet(this, _ProfileLoaderService_system, "f").Query(_1.PubkeyReplaceableNoteStore, sub); - const feed = q?.feed ?? new _1.PubkeyReplaceableNoteStore(); - // never release this callback, it will stop firing anyway after eose - const releaseOnEvent = feed.onEvent(async (e) => { - for (const pe of e) { - newProfiles.add(pe.id); - await this.onProfileEvent(pe); - } - }); - const results = await new Promise(resolve => { - let timeout = undefined; - const release = feed.hook(() => { - if (!feed.loading) { - clearTimeout(timeout); - resolve(feed.getSnapshotData() ?? []); - __classPrivateFieldGet(this, _ProfileLoaderService_log, "f").call(this, "Profiles finished: %s", sub.id); - release(); - } - }); - timeout = setTimeout(() => { - release(); - resolve(feed.getSnapshotData() ?? []); - __classPrivateFieldGet(this, _ProfileLoaderService_log, "f").call(this, "Profiles timeout: %s", sub.id); - }, 5000); - }); - releaseOnEvent(); - const couldNotFetch = [...missing].filter(a => !results.some(b => b.pubkey === a)); - if (couldNotFetch.length > 0) { - __classPrivateFieldGet(this, _ProfileLoaderService_log, "f").call(this, "No profiles: %o", couldNotFetch); - const empty = couldNotFetch.map(a => __classPrivateFieldGet(this, _ProfileLoaderService_cache, "f").update({ - pubkey: a, - loaded: (0, Util_1.unixNowMs)() - Const_1.ProfileCacheExpire + 5000, - created: 69, - })); - await Promise.all(empty); - } - // When we fetch an expired profile and its the same as what we already have - // onEvent is not fired and the loaded timestamp never gets updated - const expiredSame = results.filter(a => !newProfiles.has(a.id) && expired.includes(a.pubkey)); - await Promise.all(expiredSame.map(v => this.onProfileEvent(v))); - } - setTimeout(() => __classPrivateFieldGet(this, _ProfileLoaderService_instances, "m", _ProfileLoaderService_FetchMetadata).call(this), 500); -}; -//# sourceMappingURL=ProfileCache.js.map \ No newline at end of file diff --git a/packages/system/dist/ProfileCache.js.map b/packages/system/dist/ProfileCache.js.map deleted file mode 100644 index 2722da34..00000000 --- a/packages/system/dist/ProfileCache.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ProfileCache.js","sourceRoot":"","sources":["../src/ProfileCache.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/Query.d.ts b/packages/system/dist/Query.d.ts deleted file mode 100644 index 03469640..00000000 --- a/packages/system/dist/Query.d.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { Connection, ReqFilter, TaggedRawEvent } from "."; -import { NoteStore } from "./NoteCollection"; -import { BuiltRawReqFilter } from "./RequestBuilder"; -/** - * Tracing for relay query status - */ -declare class QueryTrace { - #private; - readonly relay: string; - readonly filters: Array; - readonly connId: string; - readonly id: string; - readonly start: number; - sent?: number; - eose?: number; - close?: number; - constructor(relay: string, filters: Array, connId: string, fnClose: (id: string) => void, fnProgress: () => void); - sentToRelay(): void; - gotEose(): void; - forceEose(): void; - sendClose(): void; - /** - * Time spent in queue - */ - get queued(): number; - /** - * Total query runtime - */ - get runtime(): number; - /** - * Total time spent waiting for relay to respond - */ - get responseTime(): number; - /** - * If tracing is finished, we got EOSE or timeout - */ - get finished(): boolean; -} -export interface QueryBase { - /** - * Uniquie ID of this query - */ - id: string; - /** - * The query payload (REQ filters) - */ - filters: Array; - /** - * List of relays to send this query to - */ - relays?: Array; -} -/** - * Active or queued query on the system - */ -export declare class Query implements QueryBase { - #private; - /** - * Uniquie ID of this query - */ - id: string; - constructor(id: string, feed: NoteStore, leaveOpen?: boolean); - canRemove(): boolean; - /** - * Recompute the complete set of compressed filters from all query traces - */ - get filters(): ReqFilter[]; - get feed(): NoteStore; - onEvent(sub: string, e: TaggedRawEvent): void; - /** - * This function should be called when this Query object and FeedStore is no longer needed - */ - cancel(): void; - uncancel(): void; - cleanup(): void; - sendToRelay(c: Connection, subq: BuiltRawReqFilter): QueryTrace | undefined; - connectionLost(id: string): void; - sendClose(): void; - eose(sub: string, conn: Readonly): void; - /** - * Get the progress to EOSE, can be used to determine when we should load more content - */ - get progress(): number; -} -export {}; -//# sourceMappingURL=Query.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Query.d.ts.map b/packages/system/dist/Query.d.ts.map deleted file mode 100644 index e3da2f8c..00000000 --- a/packages/system/dist/Query.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Query.d.ts","sourceRoot":"","sources":["../src/Query.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAQ,cAAc,EAAE,MAAM,GAAG,CAAC;AAEhE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD;;GAEG;AACH,cAAM,UAAU;;IAWZ,QAAQ,CAAC,KAAK,EAAE,MAAM;IACtB,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM;IAZzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;gBAMJ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,EACzB,MAAM,EAAE,MAAM,EACvB,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,EAC7B,UAAU,EAAE,MAAM,IAAI;IAQxB,WAAW;IAKX,OAAO;IAKP,SAAS;IAOT,SAAS;IAMT;;OAEG;IACH,IAAI,MAAM,WAET;IAED;;OAEG;IACH,IAAI,OAAO,WAEV;IAED;;OAEG;IACH,IAAI,YAAY,WAEf;IAED;;OAEG;IACH,IAAI,QAAQ,YAEX;CACF;AAED,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAE1B;;OAEG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACxB;AAED;;GAEG;AACH,qBAAa,KAAM,YAAW,SAAS;;IACrC;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;gBA8BC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,OAAO;IAO5D,SAAS;IAIT;;OAEG;IACH,IAAI,OAAO,gBAEV;IAED,IAAI,IAAI,cAEP;IAED,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,cAAc;IAStC;;OAEG;IACH,MAAM;IAIN,QAAQ;IAIR,OAAO;IAIP,WAAW,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,iBAAiB;IAOlD,cAAc,CAAC,EAAE,EAAE,MAAM;IAIzB,SAAS;IAOT,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC;IAQ5C;;OAEG;IACH,IAAI,QAAQ,WAMX;CA6DF"} \ No newline at end of file diff --git a/packages/system/dist/Query.js b/packages/system/dist/Query.js deleted file mode 100644 index 78b291fa..00000000 --- a/packages/system/dist/Query.js +++ /dev/null @@ -1,228 +0,0 @@ -"use strict"; -var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { - if (kind === "m") throw new TypeError("Private method is not writable"); - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); - return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; -}; -var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); - return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -var _QueryTrace_wasForceClosed, _QueryTrace_fnClose, _QueryTrace_fnProgress, _Query_instances, _Query_tracing, _Query_leaveOpen, _Query_cancelAt, _Query_checkTrace, _Query_feed, _Query_log, _Query_allFilters, _Query_onProgress, _Query_stopCheckTraces, _Query_checkTraces, _Query_canSendQuery, _Query_sendQueryInternal, _Query_reComputeFilters; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Query = void 0; -const uuid_1 = require("uuid"); -const debug_1 = __importDefault(require("debug")); -const _1 = require("."); -const Util_1 = require("./Util"); -const RequestMerger_1 = require("./RequestMerger"); -const RequestExpander_1 = require("./RequestExpander"); -/** - * Tracing for relay query status - */ -class QueryTrace { - constructor(relay, filters, connId, fnClose, fnProgress) { - this.relay = relay; - this.filters = filters; - this.connId = connId; - _QueryTrace_wasForceClosed.set(this, false); - _QueryTrace_fnClose.set(this, void 0); - _QueryTrace_fnProgress.set(this, void 0); - this.id = (0, uuid_1.v4)(); - this.start = (0, Util_1.unixNowMs)(); - __classPrivateFieldSet(this, _QueryTrace_fnClose, fnClose, "f"); - __classPrivateFieldSet(this, _QueryTrace_fnProgress, fnProgress, "f"); - } - sentToRelay() { - this.sent = (0, Util_1.unixNowMs)(); - __classPrivateFieldGet(this, _QueryTrace_fnProgress, "f").call(this); - } - gotEose() { - this.eose = (0, Util_1.unixNowMs)(); - __classPrivateFieldGet(this, _QueryTrace_fnProgress, "f").call(this); - } - forceEose() { - this.eose = (0, Util_1.unixNowMs)(); - __classPrivateFieldSet(this, _QueryTrace_wasForceClosed, true, "f"); - __classPrivateFieldGet(this, _QueryTrace_fnProgress, "f").call(this); - this.sendClose(); - } - sendClose() { - this.close = (0, Util_1.unixNowMs)(); - __classPrivateFieldGet(this, _QueryTrace_fnClose, "f").call(this, this.id); - __classPrivateFieldGet(this, _QueryTrace_fnProgress, "f").call(this); - } - /** - * Time spent in queue - */ - get queued() { - return (this.sent === undefined ? (0, Util_1.unixNowMs)() : __classPrivateFieldGet(this, _QueryTrace_wasForceClosed, "f") ? (0, Util_1.unwrap)(this.eose) : this.sent) - this.start; - } - /** - * Total query runtime - */ - get runtime() { - return (this.eose === undefined ? (0, Util_1.unixNowMs)() : this.eose) - this.start; - } - /** - * Total time spent waiting for relay to respond - */ - get responseTime() { - return this.finished ? (0, Util_1.unwrap)(this.eose) - (0, Util_1.unwrap)(this.sent) : 0; - } - /** - * If tracing is finished, we got EOSE or timeout - */ - get finished() { - return this.eose !== undefined; - } -} -_QueryTrace_wasForceClosed = new WeakMap(), _QueryTrace_fnClose = new WeakMap(), _QueryTrace_fnProgress = new WeakMap(); -/** - * Active or queued query on the system - */ -class Query { - constructor(id, feed, leaveOpen) { - _Query_instances.add(this); - /** - * Which relays this query has already been executed on - */ - _Query_tracing.set(this, []); - /** - * Leave the query open until its removed - */ - _Query_leaveOpen.set(this, false); - /** - * Time when this query can be removed - */ - _Query_cancelAt.set(this, void 0); - /** - * Timer used to track tracing status - */ - _Query_checkTrace.set(this, void 0); - /** - * Feed object which collects events - */ - _Query_feed.set(this, void 0); - _Query_log.set(this, (0, debug_1.default)("Query")); - _Query_allFilters.set(this, []); - this.id = id; - __classPrivateFieldSet(this, _Query_feed, feed, "f"); - __classPrivateFieldSet(this, _Query_leaveOpen, leaveOpen ?? false, "f"); - __classPrivateFieldGet(this, _Query_instances, "m", _Query_checkTraces).call(this); - } - canRemove() { - return __classPrivateFieldGet(this, _Query_cancelAt, "f") !== undefined && __classPrivateFieldGet(this, _Query_cancelAt, "f") < (0, Util_1.unixNowMs)(); - } - /** - * Recompute the complete set of compressed filters from all query traces - */ - get filters() { - return __classPrivateFieldGet(this, _Query_allFilters, "f"); - } - get feed() { - return __classPrivateFieldGet(this, _Query_feed, "f"); - } - onEvent(sub, e) { - for (const t of __classPrivateFieldGet(this, _Query_tracing, "f")) { - if (t.id === sub) { - this.feed.add(e); - break; - } - } - } - /** - * This function should be called when this Query object and FeedStore is no longer needed - */ - cancel() { - __classPrivateFieldSet(this, _Query_cancelAt, (0, Util_1.unixNowMs)() + 5000, "f"); - } - uncancel() { - __classPrivateFieldSet(this, _Query_cancelAt, undefined, "f"); - } - cleanup() { - __classPrivateFieldGet(this, _Query_instances, "m", _Query_stopCheckTraces).call(this); - } - sendToRelay(c, subq) { - if (!__classPrivateFieldGet(this, _Query_instances, "m", _Query_canSendQuery).call(this, c, subq)) { - return; - } - return __classPrivateFieldGet(this, _Query_instances, "m", _Query_sendQueryInternal).call(this, c, subq); - } - connectionLost(id) { - __classPrivateFieldGet(this, _Query_tracing, "f").filter(a => a.connId == id).forEach(a => a.forceEose()); - } - sendClose() { - for (const qt of __classPrivateFieldGet(this, _Query_tracing, "f")) { - qt.sendClose(); - } - this.cleanup(); - } - eose(sub, conn) { - const qt = __classPrivateFieldGet(this, _Query_tracing, "f").find(a => a.id === sub && a.connId === conn.Id); - qt?.gotEose(); - if (!__classPrivateFieldGet(this, _Query_leaveOpen, "f")) { - qt?.sendClose(); - } - } - /** - * Get the progress to EOSE, can be used to determine when we should load more content - */ - get progress() { - const thisProgress = __classPrivateFieldGet(this, _Query_tracing, "f").reduce((acc, v) => (acc += v.finished ? 1 : 0), 0) / __classPrivateFieldGet(this, _Query_tracing, "f").length; - if (isNaN(thisProgress)) { - return 0; - } - return thisProgress; - } -} -exports.Query = Query; -_Query_tracing = new WeakMap(), _Query_leaveOpen = new WeakMap(), _Query_cancelAt = new WeakMap(), _Query_checkTrace = new WeakMap(), _Query_feed = new WeakMap(), _Query_log = new WeakMap(), _Query_allFilters = new WeakMap(), _Query_instances = new WeakSet(), _Query_onProgress = function _Query_onProgress() { - const isFinished = this.progress === 1; - if (this.feed.loading !== isFinished) { - __classPrivateFieldGet(this, _Query_log, "f").call(this, "%s loading=%s, progress=%d", this.id, this.feed.loading, this.progress); - this.feed.loading = isFinished; - } -}, _Query_stopCheckTraces = function _Query_stopCheckTraces() { - if (__classPrivateFieldGet(this, _Query_checkTrace, "f")) { - clearInterval(__classPrivateFieldGet(this, _Query_checkTrace, "f")); - } -}, _Query_checkTraces = function _Query_checkTraces() { - __classPrivateFieldGet(this, _Query_instances, "m", _Query_stopCheckTraces).call(this); - __classPrivateFieldSet(this, _Query_checkTrace, setInterval(() => { - for (const v of __classPrivateFieldGet(this, _Query_tracing, "f")) { - if (v.runtime > 5000 && !v.finished) { - v.forceEose(); - } - } - }, 500), "f"); -}, _Query_canSendQuery = function _Query_canSendQuery(c, q) { - if (q.relay && q.relay !== c.Address) { - return false; - } - if (!q.relay && c.Ephemeral) { - __classPrivateFieldGet(this, _Query_log, "f").call(this, "Cant send non-specific REQ to ephemeral connection %O %O %O", q, q.relay, c); - return false; - } - if (q.filters.some(a => a.search) && !c.SupportsNip(_1.Nips.Search)) { - __classPrivateFieldGet(this, _Query_log, "f").call(this, "Cant send REQ to non-search relay", c.Address); - return false; - } - return true; -}, _Query_sendQueryInternal = function _Query_sendQueryInternal(c, q) { - const qt = new QueryTrace(c.Address, q.filters, c.Id, x => c.CloseReq(x), () => __classPrivateFieldGet(this, _Query_instances, "m", _Query_onProgress).call(this)); - __classPrivateFieldGet(this, _Query_tracing, "f").push(qt); - __classPrivateFieldGet(this, _Query_instances, "m", _Query_reComputeFilters).call(this); - c.QueueReq(["REQ", qt.id, ...q.filters], () => qt.sentToRelay()); - return qt; -}, _Query_reComputeFilters = function _Query_reComputeFilters() { - console.time("reComputeFilters"); - __classPrivateFieldSet(this, _Query_allFilters, (0, RequestMerger_1.flatMerge)(__classPrivateFieldGet(this, _Query_tracing, "f").flatMap(a => a.filters).flatMap(RequestExpander_1.expandFilter)), "f"); - console.timeEnd("reComputeFilters"); -}; -//# sourceMappingURL=Query.js.map \ No newline at end of file diff --git a/packages/system/dist/Query.js.map b/packages/system/dist/Query.js.map deleted file mode 100644 index 56d1e707..00000000 --- a/packages/system/dist/Query.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Query.js","sourceRoot":"","sources":["../src/Query.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,+BAAkC;AAClC,kDAA0B;AAC1B,wBAAgE;AAChE,iCAA2C;AAE3C,mDAA4C;AAE5C,uDAAiD;AAEjD;;GAEG;AACH,MAAM,UAAU;IAUd,YACW,KAAa,EACb,OAAyB,EACzB,MAAc,EACvB,OAA6B,EAC7B,UAAsB;QAJb,UAAK,GAAL,KAAK,CAAQ;QACb,YAAO,GAAP,OAAO,CAAkB;QACzB,WAAM,GAAN,MAAM,CAAQ;QAPzB,qCAAkB,KAAK,EAAC;QACf,sCAA+B;QAC/B,yCAAwB;QAS/B,IAAI,CAAC,EAAE,GAAG,IAAA,SAAI,GAAE,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,IAAA,gBAAS,GAAE,CAAC;QACzB,uBAAA,IAAI,uBAAY,OAAO,MAAA,CAAC;QACxB,uBAAA,IAAI,0BAAe,UAAU,MAAA,CAAC;IAChC,CAAC;IAED,WAAW;QACT,IAAI,CAAC,IAAI,GAAG,IAAA,gBAAS,GAAE,CAAC;QACxB,uBAAA,IAAI,8BAAY,MAAhB,IAAI,CAAc,CAAC;IACrB,CAAC;IAED,OAAO;QACL,IAAI,CAAC,IAAI,GAAG,IAAA,gBAAS,GAAE,CAAC;QACxB,uBAAA,IAAI,8BAAY,MAAhB,IAAI,CAAc,CAAC;IACrB,CAAC;IAED,SAAS;QACP,IAAI,CAAC,IAAI,GAAG,IAAA,gBAAS,GAAE,CAAC;QACxB,uBAAA,IAAI,8BAAmB,IAAI,MAAA,CAAC;QAC5B,uBAAA,IAAI,8BAAY,MAAhB,IAAI,CAAc,CAAC;QACnB,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAED,SAAS;QACP,IAAI,CAAC,KAAK,GAAG,IAAA,gBAAS,GAAE,CAAC;QACzB,uBAAA,IAAI,2BAAS,MAAb,IAAI,EAAU,IAAI,CAAC,EAAE,CAAC,CAAC;QACvB,uBAAA,IAAI,8BAAY,MAAhB,IAAI,CAAc,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,IAAI,MAAM;QACR,OAAO,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAA,gBAAS,GAAE,CAAC,CAAC,CAAC,uBAAA,IAAI,kCAAgB,CAAC,CAAC,CAAC,IAAA,aAAM,EAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;IACrH,CAAC;IAED;;OAEG;IACH,IAAI,OAAO;QACT,OAAO,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAA,gBAAS,GAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;IAC1E,CAAC;IAED;;OAEG;IACH,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAA,aAAM,EAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAA,aAAM,EAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,CAAC;IAED;;OAEG;IACH,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC;IACjC,CAAC;CACF;;AAmBD;;GAEG;AACH,MAAa,KAAK;IAkChB,YAAY,EAAU,EAAE,IAAe,EAAE,SAAmB;;QA5B5D;;WAEG;QACH,yBAA8B,EAAE,EAAC;QAEjC;;WAEG;QACH,2BAAa,KAAK,EAAC;QAEnB;;WAEG;QACH,kCAAmB;QAEnB;;WAEG;QACH,oCAA6C;QAE7C;;WAEG;QACH,8BAAiB;QAEjB,qBAAO,IAAA,eAAK,EAAC,OAAO,CAAC,EAAC;QACtB,4BAAgC,EAAE,EAAC;QAGjC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,uBAAA,IAAI,eAAS,IAAI,MAAA,CAAC;QAClB,uBAAA,IAAI,oBAAc,SAAS,IAAI,KAAK,MAAA,CAAC;QACrC,uBAAA,IAAI,4CAAa,MAAjB,IAAI,CAAe,CAAC;IACtB,CAAC;IAED,SAAS;QACP,OAAO,uBAAA,IAAI,uBAAU,KAAK,SAAS,IAAI,uBAAA,IAAI,uBAAU,GAAG,IAAA,gBAAS,GAAE,CAAC;IACtE,CAAC;IAED;;OAEG;IACH,IAAI,OAAO;QACT,OAAO,uBAAA,IAAI,yBAAY,CAAC;IAC1B,CAAC;IAED,IAAI,IAAI;QACN,OAAO,uBAAA,IAAI,mBAAM,CAAC;IACpB,CAAC;IAED,OAAO,CAAC,GAAW,EAAE,CAAiB;QACpC,KAAK,MAAM,CAAC,IAAI,uBAAA,IAAI,sBAAS,EAAE;YAC7B,IAAI,CAAC,CAAC,EAAE,KAAK,GAAG,EAAE;gBAChB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACjB,MAAM;aACP;SACF;IACH,CAAC;IAED;;OAEG;IACH,MAAM;QACJ,uBAAA,IAAI,mBAAa,IAAA,gBAAS,GAAE,GAAG,IAAK,MAAA,CAAC;IACvC,CAAC;IAED,QAAQ;QACN,uBAAA,IAAI,mBAAa,SAAS,MAAA,CAAC;IAC7B,CAAC;IAED,OAAO;QACL,uBAAA,IAAI,gDAAiB,MAArB,IAAI,CAAmB,CAAC;IAC1B,CAAC;IAED,WAAW,CAAC,CAAa,EAAE,IAAuB;QAChD,IAAI,CAAC,uBAAA,IAAI,6CAAc,MAAlB,IAAI,EAAe,CAAC,EAAE,IAAI,CAAC,EAAE;YAChC,OAAO;SACR;QACD,OAAO,uBAAA,IAAI,kDAAmB,MAAvB,IAAI,EAAoB,CAAC,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,cAAc,CAAC,EAAU;QACvB,uBAAA,IAAI,sBAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,SAAS;QACP,KAAK,MAAM,EAAE,IAAI,uBAAA,IAAI,sBAAS,EAAE;YAC9B,EAAE,CAAC,SAAS,EAAE,CAAC;SAChB;QACD,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAED,IAAI,CAAC,GAAW,EAAE,IAA0B;QAC1C,MAAM,EAAE,GAAG,uBAAA,IAAI,sBAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;QACzE,EAAE,EAAE,OAAO,EAAE,CAAC;QACd,IAAI,CAAC,uBAAA,IAAI,wBAAW,EAAE;YACpB,EAAE,EAAE,SAAS,EAAE,CAAC;SACjB;IACH,CAAC;IAED;;OAEG;IACH,IAAI,QAAQ;QACV,MAAM,YAAY,GAAG,uBAAA,IAAI,sBAAS,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,uBAAA,IAAI,sBAAS,CAAC,MAAM,CAAC;QAC7G,IAAI,KAAK,CAAC,YAAY,CAAC,EAAE;YACvB,OAAO,CAAC,CAAC;SACV;QACD,OAAO,YAAY,CAAC;IACtB,CAAC;CA6DF;AAhLD,sBAgLC;;IA1DG,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,KAAK,CAAC,CAAC;IACvC,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,KAAK,UAAU,EAAE;QACpC,uBAAA,IAAI,kBAAK,MAAT,IAAI,EAAM,4BAA4B,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnF,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,UAAU,CAAC;KAChC;AACH,CAAC;IAGC,IAAI,uBAAA,IAAI,yBAAY,EAAE;QACpB,aAAa,CAAC,uBAAA,IAAI,yBAAY,CAAC,CAAC;KACjC;AACH,CAAC;IAGC,uBAAA,IAAI,gDAAiB,MAArB,IAAI,CAAmB,CAAC;IACxB,uBAAA,IAAI,qBAAe,WAAW,CAAC,GAAG,EAAE;QAClC,KAAK,MAAM,CAAC,IAAI,uBAAA,IAAI,sBAAS,EAAE;YAC7B,IAAI,CAAC,CAAC,OAAO,GAAG,IAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE;gBACpC,CAAC,CAAC,SAAS,EAAE,CAAC;aACf;SACF;IACH,CAAC,EAAE,GAAG,CAAC,MAAA,CAAC;AACV,CAAC,qDAEa,CAAa,EAAE,CAAoB;IAC/C,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,OAAO,EAAE;QACpC,OAAO,KAAK,CAAC;KACd;IACD,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE;QAC3B,uBAAA,IAAI,kBAAK,MAAT,IAAI,EAAM,6DAA6D,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QACxF,OAAO,KAAK,CAAC;KACd;IACD,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,OAAI,CAAC,MAAM,CAAC,EAAE;QAChE,uBAAA,IAAI,kBAAK,MAAT,IAAI,EAAM,mCAAmC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;QAC1D,OAAO,KAAK,CAAC;KACd;IACD,OAAO,IAAI,CAAC;AACd,CAAC,+DAEkB,CAAa,EAAE,CAAoB;IACpD,MAAM,EAAE,GAAG,IAAI,UAAU,CACvB,CAAC,CAAC,OAAO,EACT,CAAC,CAAC,OAAO,EACT,CAAC,CAAC,EAAE,EACJ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAClB,GAAG,EAAE,CAAC,uBAAA,IAAI,2CAAY,MAAhB,IAAI,CAAc,CACzB,CAAC;IACF,uBAAA,IAAI,sBAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvB,uBAAA,IAAI,iDAAkB,MAAtB,IAAI,CAAoB,CAAC;IACzB,CAAC,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IACjE,OAAO,EAAE,CAAC;AACZ,CAAC;IAGC,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACjC,uBAAA,IAAI,qBAAe,IAAA,yBAAS,EAAC,uBAAA,IAAI,sBAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,8BAAY,CAAC,CAAC,MAAA,CAAC;IAC1F,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;AACtC,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/RelayInfo.d.ts b/packages/system/dist/RelayInfo.d.ts deleted file mode 100644 index 0ffe9742..00000000 --- a/packages/system/dist/RelayInfo.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -export interface RelayInfo { - name?: string; - description?: string; - pubkey?: string; - contact?: string; - supported_nips?: number[]; - software?: string; - version?: string; - limitation?: { - payment_required: boolean; - max_subscriptions: number; - max_filters: number; - max_event_tags: number; - auth_required: boolean; - }; -} -//# sourceMappingURL=RelayInfo.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/RelayInfo.d.ts.map b/packages/system/dist/RelayInfo.d.ts.map deleted file mode 100644 index c52f7495..00000000 --- a/packages/system/dist/RelayInfo.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"RelayInfo.d.ts","sourceRoot":"","sources":["../src/RelayInfo.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE;QACX,gBAAgB,EAAE,OAAO,CAAC;QAC1B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,OAAO,CAAC;KACxB,CAAC;CACH"} \ No newline at end of file diff --git a/packages/system/dist/RelayInfo.js b/packages/system/dist/RelayInfo.js deleted file mode 100644 index 0591ac98..00000000 --- a/packages/system/dist/RelayInfo.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -//# sourceMappingURL=RelayInfo.js.map \ No newline at end of file diff --git a/packages/system/dist/RelayInfo.js.map b/packages/system/dist/RelayInfo.js.map deleted file mode 100644 index 4fff9c16..00000000 --- a/packages/system/dist/RelayInfo.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"RelayInfo.js","sourceRoot":"","sources":["../src/RelayInfo.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/packages/system/dist/RequestBuilder.d.ts b/packages/system/dist/RequestBuilder.d.ts deleted file mode 100644 index 94ef5649..00000000 --- a/packages/system/dist/RequestBuilder.d.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { ReqFilter, u256, HexKey, EventKind } from "."; -import { RelayCache } from "./GossipModel"; -/** - * Which strategy is used when building REQ filters - */ -export declare enum RequestStrategy { - /** - * Use the users default relays to fetch events, - * this is the fallback option when there is no better way to query a given filter set - */ - DefaultRelays = 1, - /** - * Using a cached copy of the authors relay lists NIP-65, split a given set of request filters by pubkey - */ - AuthorsRelays = 2, - /** - * Relay hints are usually provided when using replies - */ - RelayHintedEventIds = 3 -} -/** - * A built REQ filter ready for sending to System - */ -export interface BuiltRawReqFilter { - filters: Array; - relay: string; - strategy: RequestStrategy; -} -export interface RequestBuilderOptions { - leaveOpen?: boolean; - relays?: Array; - /** - * Do not apply diff logic and always use full filters for query - */ - skipDiff?: boolean; -} -/** - * Nostr REQ builder - */ -export declare class RequestBuilder { - #private; - id: string; - constructor(id: string); - get numFilters(): number; - get options(): RequestBuilderOptions | undefined; - withFilter(): RequestFilterBuilder; - withOptions(opt: RequestBuilderOptions): this; - buildRaw(): Array; - build(relays: RelayCache): Array; - /** - * Detects a change in request from a previous set of filters - * @param q All previous filters merged - * @returns - */ - buildDiff(relays: RelayCache, filters: Array): Array; -} -/** - * Builder class for a single request filter - */ -export declare class RequestFilterBuilder { - #private; - get filter(): { - ids?: string[] | undefined; /** - * Relay hints are usually provided when using replies - */ - authors?: string[] | undefined; - kinds?: number[] | undefined; - "#e"?: string[] | undefined; - "#p"?: string[] | undefined; - "#t"?: string[] | undefined; - "#d"?: string[] | undefined; - "#r"?: string[] | undefined; - search?: string | undefined; - since?: number | undefined; - until?: number | undefined; - limit?: number | undefined; - }; - get relayHints(): Map; - ids(ids: Array): this; - id(id: u256, relay?: string): this; - authors(authors?: Array): this; - kinds(kinds?: Array): this; - since(since?: number): this; - until(until?: number): this; - limit(limit?: number): this; - tag(key: "e" | "p" | "d" | "t" | "r", value?: Array): this; - search(keyword?: string): this; - /** - * Build/expand this filter into a set of relay specific queries - */ - build(relays: RelayCache, id: string): Array; -} -//# sourceMappingURL=RequestBuilder.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/RequestBuilder.d.ts.map b/packages/system/dist/RequestBuilder.d.ts.map deleted file mode 100644 index 5b037973..00000000 --- a/packages/system/dist/RequestBuilder.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"RequestBuilder.d.ts","sourceRoot":"","sources":["../src/RequestBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC;AAGvD,OAAO,EAAE,UAAU,EAA6C,MAAM,eAAe,CAAC;AAGtF;;GAEG;AACH,oBAAY,eAAe;IACzB;;;OAGG;IACH,aAAa,IAAI;IAEjB;;OAEG;IACH,aAAa,IAAI;IAEjB;;OAEG;IACH,mBAAmB,IAAI;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,cAAc;;IACzB,EAAE,EAAE,MAAM,CAAC;gBAIC,EAAE,EAAE,MAAM;IAKtB,IAAI,UAAU,WAEb;IAED,IAAI,OAAO,sCAEV;IAED,UAAU;IAMV,WAAW,CAAC,GAAG,EAAE,qBAAqB;IAQtC,QAAQ,IAAI,KAAK,CAAC,SAAS,CAAC;IAI5B,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,KAAK,CAAC,iBAAiB,CAAC;IAKnD;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,iBAAiB,CAAC;CAyCnF;AAED;;GAEG;AACH,qBAAa,oBAAoB;;IAI/B,IAAI,MAAM;oCA3HV;;WAEG;;;;;;;;;;;;MA2HF;IAED,IAAI,UAAU,0BAEb;IAED,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC;IAKpB,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,MAAM;IAO3B,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC;IAM/B,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAM9B,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM;IAMpB,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM;IAMpB,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM;IAMpB,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC;IAM3D,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM;IAMvB;;OAEG;IACH,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,KAAK,CAAC,iBAAiB,CAAC;CAuChE"} \ No newline at end of file diff --git a/packages/system/dist/RequestBuilder.js b/packages/system/dist/RequestBuilder.js deleted file mode 100644 index b6cdc0b7..00000000 --- a/packages/system/dist/RequestBuilder.js +++ /dev/null @@ -1,225 +0,0 @@ -"use strict"; -var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { - if (kind === "m") throw new TypeError("Private method is not writable"); - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); - return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; -}; -var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); - return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -}; -var _RequestBuilder_instances, _RequestBuilder_builders, _RequestBuilder_options, _RequestBuilder_groupByRelay, _RequestFilterBuilder_filter, _RequestFilterBuilder_relayHints; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RequestFilterBuilder = exports.RequestBuilder = exports.RequestStrategy = void 0; -const Util_1 = require("./Util"); -const RequestSplitter_1 = require("./RequestSplitter"); -const GossipModel_1 = require("./GossipModel"); -const RequestMerger_1 = require("./RequestMerger"); -/** - * Which strategy is used when building REQ filters - */ -var RequestStrategy; -(function (RequestStrategy) { - /** - * Use the users default relays to fetch events, - * this is the fallback option when there is no better way to query a given filter set - */ - RequestStrategy[RequestStrategy["DefaultRelays"] = 1] = "DefaultRelays"; - /** - * Using a cached copy of the authors relay lists NIP-65, split a given set of request filters by pubkey - */ - RequestStrategy[RequestStrategy["AuthorsRelays"] = 2] = "AuthorsRelays"; - /** - * Relay hints are usually provided when using replies - */ - RequestStrategy[RequestStrategy["RelayHintedEventIds"] = 3] = "RelayHintedEventIds"; -})(RequestStrategy = exports.RequestStrategy || (exports.RequestStrategy = {})); -/** - * Nostr REQ builder - */ -class RequestBuilder { - constructor(id) { - _RequestBuilder_instances.add(this); - _RequestBuilder_builders.set(this, void 0); - _RequestBuilder_options.set(this, void 0); - this.id = id; - __classPrivateFieldSet(this, _RequestBuilder_builders, [], "f"); - } - get numFilters() { - return __classPrivateFieldGet(this, _RequestBuilder_builders, "f").length; - } - get options() { - return __classPrivateFieldGet(this, _RequestBuilder_options, "f"); - } - withFilter() { - const ret = new RequestFilterBuilder(); - __classPrivateFieldGet(this, _RequestBuilder_builders, "f").push(ret); - return ret; - } - withOptions(opt) { - __classPrivateFieldSet(this, _RequestBuilder_options, { - ...__classPrivateFieldGet(this, _RequestBuilder_options, "f"), - ...opt, - }, "f"); - return this; - } - buildRaw() { - return __classPrivateFieldGet(this, _RequestBuilder_builders, "f").map(f => f.filter); - } - build(relays) { - const expanded = __classPrivateFieldGet(this, _RequestBuilder_builders, "f").flatMap(a => a.build(relays, this.id)); - return __classPrivateFieldGet(this, _RequestBuilder_instances, "m", _RequestBuilder_groupByRelay).call(this, expanded); - } - /** - * Detects a change in request from a previous set of filters - * @param q All previous filters merged - * @returns - */ - buildDiff(relays, filters) { - const next = this.buildRaw(); - const diff = (0, RequestSplitter_1.diffFilters)(filters, next); - if (diff.changed) { - return (0, GossipModel_1.splitAllByWriteRelays)(relays, diff.added).map(a => { - return { - strategy: RequestStrategy.AuthorsRelays, - filters: a.filters, - relay: a.relay, - }; - }); - } - return []; - } -} -exports.RequestBuilder = RequestBuilder; -_RequestBuilder_builders = new WeakMap(), _RequestBuilder_options = new WeakMap(), _RequestBuilder_instances = new WeakSet(), _RequestBuilder_groupByRelay = function _RequestBuilder_groupByRelay(expanded) { - const relayMerged = expanded.reduce((acc, v) => { - const existing = acc.get(v.relay); - if (existing) { - existing.push(v); - } - else { - acc.set(v.relay, [v]); - } - return acc; - }, new Map()); - const filtersSquashed = [...relayMerged.values()].map(a => { - return { - filters: (0, RequestMerger_1.mergeSimilar)(a.flatMap(b => b.filters)), - relay: a[0].relay, - strategy: a[0].strategy, - }; - }); - return filtersSquashed; -}; -/** - * Builder class for a single request filter - */ -class RequestFilterBuilder { - constructor() { - _RequestFilterBuilder_filter.set(this, {}); - _RequestFilterBuilder_relayHints.set(this, new Map()); - } - get filter() { - return { ...__classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f") }; - } - get relayHints() { - return new Map(__classPrivateFieldGet(this, _RequestFilterBuilder_relayHints, "f")); - } - ids(ids) { - __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").ids = (0, Util_1.appendDedupe)(__classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").ids, ids); - return this; - } - id(id, relay) { - if (relay) { - __classPrivateFieldGet(this, _RequestFilterBuilder_relayHints, "f").set(id, (0, Util_1.appendDedupe)(__classPrivateFieldGet(this, _RequestFilterBuilder_relayHints, "f").get(id), [relay])); - } - return this.ids([id]); - } - authors(authors) { - if (!authors) - return this; - __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").authors = (0, Util_1.appendDedupe)(__classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").authors, authors); - return this; - } - kinds(kinds) { - if (!kinds) - return this; - __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").kinds = (0, Util_1.appendDedupe)(__classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").kinds, kinds); - return this; - } - since(since) { - if (!since) - return this; - __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").since = since; - return this; - } - until(until) { - if (!until) - return this; - __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").until = until; - return this; - } - limit(limit) { - if (!limit) - return this; - __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").limit = limit; - return this; - } - tag(key, value) { - if (!value) - return this; - __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f")[`#${key}`] = value; - return this; - } - search(keyword) { - if (!keyword) - return this; - __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").search = keyword; - return this; - } - /** - * Build/expand this filter into a set of relay specific queries - */ - build(relays, id) { - // when querying for specific event ids with relay hints - // take the first approach which is to split the filter by relay - if (__classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").ids && __classPrivateFieldGet(this, _RequestFilterBuilder_relayHints, "f").size > 0) { - const relays = (0, Util_1.dedupe)([...__classPrivateFieldGet(this, _RequestFilterBuilder_relayHints, "f").values()].flat()); - return relays.map(r => { - return { - filters: [ - { - ...__classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f"), - ids: [...__classPrivateFieldGet(this, _RequestFilterBuilder_relayHints, "f").entries()].filter(([, v]) => v.includes(r)).map(([k]) => k), - }, - ], - relay: r, - strategy: RequestStrategy.RelayHintedEventIds, - }; - }); - } - // If any authors are set use the gossip model to fetch data for each author - if (__classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f").authors) { - const split = (0, GossipModel_1.splitByWriteRelays)(relays, __classPrivateFieldGet(this, _RequestFilterBuilder_filter, "f")); - return split.map(a => { - return { - filters: [a.filter], - relay: a.relay, - strategy: RequestStrategy.AuthorsRelays, - }; - }); - } - return [ - { - filters: [this.filter], - relay: "", - strategy: RequestStrategy.DefaultRelays, - }, - ]; - } -} -exports.RequestFilterBuilder = RequestFilterBuilder; -_RequestFilterBuilder_filter = new WeakMap(), _RequestFilterBuilder_relayHints = new WeakMap(); -//# sourceMappingURL=RequestBuilder.js.map \ No newline at end of file diff --git a/packages/system/dist/RequestBuilder.js.map b/packages/system/dist/RequestBuilder.js.map deleted file mode 100644 index 74865223..00000000 --- a/packages/system/dist/RequestBuilder.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"RequestBuilder.js","sourceRoot":"","sources":["../src/RequestBuilder.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/RequestExpander.d.ts b/packages/system/dist/RequestExpander.d.ts deleted file mode 100644 index 83b92600..00000000 --- a/packages/system/dist/RequestExpander.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { u256, ReqFilter } from "./Nostr"; -export interface FlatReqFilter { - ids?: u256; - authors?: u256; - kinds?: number; - "#e"?: u256; - "#p"?: u256; - "#t"?: string; - "#d"?: string; - "#r"?: string; - search?: string; - since?: number; - until?: number; - limit?: number; -} -/** - * Expand a filter into its most fine grained form - */ -export declare function expandFilter(f: ReqFilter): Array; -//# sourceMappingURL=RequestExpander.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/RequestExpander.d.ts.map b/packages/system/dist/RequestExpander.d.ts.map deleted file mode 100644 index 2f0419f8..00000000 --- a/packages/system/dist/RequestExpander.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"RequestExpander.d.ts","sourceRoot":"","sources":["../src/RequestExpander.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC,aAAa,CAAC,CA2B/D"} \ No newline at end of file diff --git a/packages/system/dist/RequestExpander.js b/packages/system/dist/RequestExpander.js deleted file mode 100644 index 5cab53de..00000000 --- a/packages/system/dist/RequestExpander.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.expandFilter = void 0; -/** - * Expand a filter into its most fine grained form - */ -function expandFilter(f) { - const ret = []; - const src = Object.entries(f); - const keys = src.filter(([, v]) => Array.isArray(v)).map(a => a[0]); - const props = src.filter(([, v]) => !Array.isArray(v)); - function generateCombinations(index, currentCombination) { - if (index === keys.length) { - ret.push(currentCombination); - return; - } - const key = keys[index]; - const values = f[key]; - for (let i = 0; i < values.length; i++) { - const value = values[i]; - const updatedCombination = { ...currentCombination, [key]: value }; - generateCombinations(index + 1, updatedCombination); - } - } - generateCombinations(0, { - ...Object.fromEntries(props), - }); - return ret; -} -exports.expandFilter = expandFilter; -//# sourceMappingURL=RequestExpander.js.map \ No newline at end of file diff --git a/packages/system/dist/RequestExpander.js.map b/packages/system/dist/RequestExpander.js.map deleted file mode 100644 index a347f38d..00000000 --- a/packages/system/dist/RequestExpander.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"RequestExpander.js","sourceRoot":"","sources":["../src/RequestExpander.ts"],"names":[],"mappings":";;;AAiBA;;GAEG;AACH,SAAgB,YAAY,CAAC,CAAY;IACvC,MAAM,GAAG,GAAyB,EAAE,CAAC;IACrC,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvD,SAAS,oBAAoB,CAAC,KAAa,EAAE,kBAAiC;QAC5E,IAAI,KAAK,KAAK,IAAI,CAAC,MAAM,EAAE;YACzB,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;YAC7B,OAAO;SACR;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,MAAM,MAAM,GAAI,CAA4C,CAAC,GAAG,CAAC,CAAC;QAElE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YACtC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,kBAAkB,GAAG,EAAE,GAAG,kBAAkB,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;YACnE,oBAAoB,CAAC,KAAK,GAAG,CAAC,EAAE,kBAAkB,CAAC,CAAC;SACrD;IACH,CAAC;IAED,oBAAoB,CAAC,CAAC,EAAE;QACtB,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC;KAC7B,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC;AA3BD,oCA2BC"} \ No newline at end of file diff --git a/packages/system/dist/RequestMatcher.d.ts b/packages/system/dist/RequestMatcher.d.ts deleted file mode 100644 index 82948dca..00000000 --- a/packages/system/dist/RequestMatcher.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { NostrEvent, ReqFilter } from "./Nostr"; -export declare function eventMatchesFilter(ev: NostrEvent, filter: ReqFilter): boolean; -//# sourceMappingURL=RequestMatcher.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/RequestMatcher.d.ts.map b/packages/system/dist/RequestMatcher.d.ts.map deleted file mode 100644 index e0dd95f6..00000000 --- a/packages/system/dist/RequestMatcher.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"RequestMatcher.d.ts","sourceRoot":"","sources":["../src/RequestMatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEhD,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,WAiBnE"} \ No newline at end of file diff --git a/packages/system/dist/RequestMatcher.js b/packages/system/dist/RequestMatcher.js deleted file mode 100644 index 81583f43..00000000 --- a/packages/system/dist/RequestMatcher.js +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.eventMatchesFilter = void 0; -function eventMatchesFilter(ev, filter) { - if (!(filter.ids?.includes(ev.id) ?? false)) { - return false; - } - if (!(filter.authors?.includes(ev.pubkey) ?? false)) { - return false; - } - if (!(filter.kinds?.includes(ev.kind) ?? false)) { - return false; - } - if (filter.since && ev.created_at < filter.since) { - return false; - } - if (filter.until && ev.created_at > filter.until) { - return false; - } - return true; -} -exports.eventMatchesFilter = eventMatchesFilter; -//# sourceMappingURL=RequestMatcher.js.map \ No newline at end of file diff --git a/packages/system/dist/RequestMatcher.js.map b/packages/system/dist/RequestMatcher.js.map deleted file mode 100644 index dcb35ecd..00000000 --- a/packages/system/dist/RequestMatcher.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"RequestMatcher.js","sourceRoot":"","sources":["../src/RequestMatcher.ts"],"names":[],"mappings":";;;AAEA,SAAgB,kBAAkB,CAAC,EAAc,EAAE,MAAiB;IAClE,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,EAAE;QAC3C,OAAO,KAAK,CAAC;KACd;IACD,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE;QACnD,OAAO,KAAK,CAAC;KACd;IACD,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,EAAE;QAC/C,OAAO,KAAK,CAAC;KACd;IACD,IAAI,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,UAAU,GAAG,MAAM,CAAC,KAAK,EAAE;QAChD,OAAO,KAAK,CAAC;KACd;IACD,IAAI,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,UAAU,GAAG,MAAM,CAAC,KAAK,EAAE;QAChD,OAAO,KAAK,CAAC;KACd;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAjBD,gDAiBC"} \ No newline at end of file diff --git a/packages/system/dist/RequestMerger.d.ts b/packages/system/dist/RequestMerger.d.ts deleted file mode 100644 index 47d16a22..00000000 --- a/packages/system/dist/RequestMerger.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ReqFilter } from "."; -import { FlatReqFilter } from "./RequestExpander"; -export declare function canMergeFilters(a: FlatReqFilter | ReqFilter, b: FlatReqFilter | ReqFilter): boolean; -export declare function mergeSimilar(filters: Array): Array; -/** - * Simply flatten all filters into one - * @param filters - * @returns - */ -export declare function simpleMerge(filters: Array): ReqFilter; -/** - * Check if a filter includes another filter, as in the bigger filter will include the same results as the samller filter - * @param bigger - * @param smaller - * @returns - */ -export declare function filterIncludes(bigger: ReqFilter, smaller: ReqFilter): boolean; -/** - * Merge expanded flat filters into combined concise filters - * @param all - * @returns - */ -export declare function flatMerge(all: Array): Array; -//# sourceMappingURL=RequestMerger.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/RequestMerger.d.ts.map b/packages/system/dist/RequestMerger.d.ts.map deleted file mode 100644 index bdc4e93b..00000000 --- a/packages/system/dist/RequestMerger.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"RequestMerger.d.ts","sourceRoot":"","sources":["../src/RequestMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAQlD,wBAAgB,eAAe,CAAC,CAAC,EAAE,aAAa,GAAG,SAAS,EAAE,CAAC,EAAE,aAAa,GAAG,SAAS,GAAG,OAAO,CAWnG;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAmBxE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,aAkBpD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,WAoBnE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAsDrE"} \ No newline at end of file diff --git a/packages/system/dist/RequestMerger.js b/packages/system/dist/RequestMerger.js deleted file mode 100644 index 15dfd91c..00000000 --- a/packages/system/dist/RequestMerger.js +++ /dev/null @@ -1,150 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.flatMerge = exports.filterIncludes = exports.simpleMerge = exports.mergeSimilar = exports.canMergeFilters = void 0; -const Util_1 = require("./Util"); -/** - * Keys which can change the entire meaning of the filter outside the array types - */ -const DiscriminatorKeys = ["since", "until", "limit", "search"]; -function canMergeFilters(a, b) { - const aObj = a; - const bObj = b; - for (const key of DiscriminatorKeys) { - if (key in aObj || key in bObj) { - if (aObj[key] !== bObj[key]) { - return false; - } - } - } - return (0, Util_1.distance)(aObj, bObj) <= 1; -} -exports.canMergeFilters = canMergeFilters; -function mergeSimilar(filters) { - console.time("mergeSimilar"); - const ret = []; - const fCopy = [...filters]; - while (fCopy.length > 0) { - const current = fCopy.shift(); - const mergeSet = [current]; - for (let i = 0; i < fCopy.length; i++) { - const f = fCopy[i]; - if (mergeSet.every(v => canMergeFilters(v, f))) { - mergeSet.push(fCopy.splice(i, 1)[0]); - i--; - } - } - ret.push(simpleMerge(mergeSet)); - } - console.timeEnd("mergeSimilar"); - return ret; -} -exports.mergeSimilar = mergeSimilar; -/** - * Simply flatten all filters into one - * @param filters - * @returns - */ -function simpleMerge(filters) { - const result = {}; - filters.forEach(filter => { - Object.entries(filter).forEach(([key, value]) => { - if (Array.isArray(value)) { - if (result[key] === undefined) { - result[key] = [...value]; - } - else { - result[key] = [...new Set([...result[key], ...value])]; - } - } - else { - result[key] = value; - } - }); - }); - return result; -} -exports.simpleMerge = simpleMerge; -/** - * Check if a filter includes another filter, as in the bigger filter will include the same results as the samller filter - * @param bigger - * @param smaller - * @returns - */ -function filterIncludes(bigger, smaller) { - const outside = bigger; - for (const [k, v] of Object.entries(smaller)) { - if (outside[k] === undefined) { - return false; - } - if (Array.isArray(v) && v.some(a => !outside[k].includes(a))) { - return false; - } - if (typeof v === "number") { - if (k === "since" && outside[k] > v) { - return false; - } - if (k === "until" && outside[k] < v) { - return false; - } - // limit cannot be checked and is ignored - } - } - return true; -} -exports.filterIncludes = filterIncludes; -/** - * Merge expanded flat filters into combined concise filters - * @param all - * @returns - */ -function flatMerge(all) { - console.time("flatMerge"); - let ret = []; - // to compute filters which can be merged we need to calucate the distance change between each filter - // then we can merge filters which are exactly 1 change diff from each other - function mergeFiltersInSet(filters) { - const result = {}; - filters.forEach(f => { - const filter = f; - Object.entries(filter).forEach(([key, value]) => { - if (!DiscriminatorKeys.includes(key)) { - if (result[key] === undefined) { - result[key] = [value]; - } - else { - result[key] = [...new Set([...result[key], value])]; - } - } - else { - result[key] = value; - } - }); - }); - return result; - } - // reducer, kinda verbose - while (all.length > 0) { - const currentFilter = all.shift(); - const mergeSet = [currentFilter]; - for (let i = 0; i < all.length; i++) { - const f = all[i]; - if (mergeSet.every(a => canMergeFilters(a, f))) { - mergeSet.push(all.splice(i, 1)[0]); - i--; - } - } - ret.push(mergeFiltersInSet(mergeSet)); - } - while (true) { - const n = mergeSimilar([...ret]); - if (n.length === ret.length) { - break; - } - ret = n; - } - console.timeEnd("flatMerge"); - console.debug(ret); - return ret; -} -exports.flatMerge = flatMerge; -//# sourceMappingURL=RequestMerger.js.map \ No newline at end of file diff --git a/packages/system/dist/RequestMerger.js.map b/packages/system/dist/RequestMerger.js.map deleted file mode 100644 index 80069d59..00000000 --- a/packages/system/dist/RequestMerger.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"RequestMerger.js","sourceRoot":"","sources":["../src/RequestMerger.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/RequestSplitter.d.ts b/packages/system/dist/RequestSplitter.d.ts deleted file mode 100644 index 85e70230..00000000 --- a/packages/system/dist/RequestSplitter.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ReqFilter } from "."; -export declare function diffFilters(prev: Array, next: Array): { - added: ReqFilter[]; - removed: ReqFilter[]; - changed: boolean; -}; -//# sourceMappingURL=RequestSplitter.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/RequestSplitter.d.ts.map b/packages/system/dist/RequestSplitter.d.ts.map deleted file mode 100644 index ad0a50ef..00000000 --- a/packages/system/dist/RequestSplitter.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"RequestSplitter.d.ts","sourceRoot":"","sources":["../src/RequestSplitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC;AAK9B,wBAAgB,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;;;;EAYzE"} \ No newline at end of file diff --git a/packages/system/dist/RequestSplitter.js b/packages/system/dist/RequestSplitter.js deleted file mode 100644 index 13488b3b..00000000 --- a/packages/system/dist/RequestSplitter.js +++ /dev/null @@ -1,19 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.diffFilters = void 0; -const Util_1 = require("./Util"); -const RequestExpander_1 = require("./RequestExpander"); -const RequestMerger_1 = require("./RequestMerger"); -function diffFilters(prev, next) { - const prevExpanded = prev.flatMap(RequestExpander_1.expandFilter); - const nextExpanded = next.flatMap(RequestExpander_1.expandFilter); - const added = (0, RequestMerger_1.flatMerge)(nextExpanded.filter(a => !prevExpanded.some(b => (0, Util_1.deepEqual)(a, b)))); - const removed = (0, RequestMerger_1.flatMerge)(prevExpanded.filter(a => !nextExpanded.some(b => (0, Util_1.deepEqual)(a, b)))); - return { - added, - removed, - changed: added.length > 0 || removed.length > 0, - }; -} -exports.diffFilters = diffFilters; -//# sourceMappingURL=RequestSplitter.js.map \ No newline at end of file diff --git a/packages/system/dist/RequestSplitter.js.map b/packages/system/dist/RequestSplitter.js.map deleted file mode 100644 index 71499c0e..00000000 --- a/packages/system/dist/RequestSplitter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"RequestSplitter.js","sourceRoot":"","sources":["../src/RequestSplitter.ts"],"names":[],"mappings":";;;AACA,iCAAmC;AACnC,uDAAiD;AACjD,mDAA4C;AAE5C,SAAgB,WAAW,CAAC,IAAsB,EAAE,IAAsB;IACxE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,8BAAY,CAAC,CAAC;IAChD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,8BAAY,CAAC,CAAC;IAEhD,MAAM,KAAK,GAAG,IAAA,yBAAS,EAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAA,gBAAS,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5F,MAAM,OAAO,GAAG,IAAA,yBAAS,EAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAA,gBAAS,EAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE9F,OAAO;QACL,KAAK;QACL,OAAO;QACP,OAAO,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;KAChD,CAAC;AACJ,CAAC;AAZD,kCAYC"} \ No newline at end of file diff --git a/packages/system/dist/SystemWorker.d.ts b/packages/system/dist/SystemWorker.d.ts deleted file mode 100644 index f9224e50..00000000 --- a/packages/system/dist/SystemWorker.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { SystemSnapshot, SystemInterface } from "."; -import { AuthHandler, ConnectionStateSnapshot, RelaySettings } from "./Connection"; -import ExternalStore from "./ExternalStore"; -import { NostrEvent } from "./Nostr"; -import { NoteStore } from "./NoteCollection"; -import { Query } from "./Query"; -import { RequestBuilder } from "./RequestBuilder"; -export declare class SystemWorker extends ExternalStore implements SystemInterface { - #private; - constructor(); - HandleAuth?: AuthHandler; - get Sockets(): ConnectionStateSnapshot[]; - Query(type: new () => T, req: RequestBuilder | null): Query | undefined; - CancelQuery(sub: string): void; - GetQuery(sub: string): Query | undefined; - ConnectToRelay(address: string, options: RelaySettings): Promise; - DisconnectRelay(address: string): void; - BroadcastEvent(ev: NostrEvent): void; - WriteOnceToRelay(relay: string, ev: NostrEvent): Promise; - takeSnapshot(): SystemSnapshot; -} -//# sourceMappingURL=SystemWorker.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/SystemWorker.d.ts.map b/packages/system/dist/SystemWorker.d.ts.map deleted file mode 100644 index d6581294..00000000 --- a/packages/system/dist/SystemWorker.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"SystemWorker.d.ts","sourceRoot":"","sources":["../src/SystemWorker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,GAAG,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,qBAAa,YAAa,SAAQ,aAAa,CAAC,cAAc,CAAE,YAAW,eAAe;;;IAcxF,UAAU,CAAC,EAAE,WAAW,CAAC;IAEzB,IAAI,OAAO,IAAI,uBAAuB,EAAE,CAEvC;IAED,KAAK,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,EAAE,UAAU,CAAC,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,GAAG,KAAK,GAAG,SAAS;IAI5F,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI9B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAIxC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAItC,cAAc,CAAC,EAAE,EAAE,UAAU,GAAG,IAAI;IAIpC,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9D,YAAY,IAAI,cAAc;CAO/B"} \ No newline at end of file diff --git a/packages/system/dist/SystemWorker.js b/packages/system/dist/SystemWorker.js deleted file mode 100644 index 703bd8e1..00000000 --- a/packages/system/dist/SystemWorker.js +++ /dev/null @@ -1,66 +0,0 @@ -"use strict"; -var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { - if (kind === "m") throw new TypeError("Private method is not writable"); - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); - return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; -}; -var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); - return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -var _SystemWorker_instances, _SystemWorker_port, _SystemWorker_onMessage; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SystemWorker = void 0; -const ExternalStore_1 = __importDefault(require("./ExternalStore")); -class SystemWorker extends ExternalStore_1.default { - constructor() { - super(); - _SystemWorker_instances.add(this); - _SystemWorker_port.set(this, void 0); - if ("SharedWorker" in window) { - const worker = new SharedWorker("/system.js"); - __classPrivateFieldSet(this, _SystemWorker_port, worker.port, "f"); - __classPrivateFieldGet(this, _SystemWorker_port, "f").onmessage = m => __classPrivateFieldGet(this, _SystemWorker_instances, "m", _SystemWorker_onMessage).call(this, m); - } - else { - throw new Error("SharedWorker is not supported"); - } - } - get Sockets() { - throw new Error("Method not implemented."); - } - Query(type, req) { - throw new Error("Method not implemented."); - } - CancelQuery(sub) { - throw new Error("Method not implemented."); - } - GetQuery(sub) { - throw new Error("Method not implemented."); - } - ConnectToRelay(address, options) { - throw new Error("Method not implemented."); - } - DisconnectRelay(address) { - throw new Error("Method not implemented."); - } - BroadcastEvent(ev) { - throw new Error("Method not implemented."); - } - WriteOnceToRelay(relay, ev) { - throw new Error("Method not implemented."); - } - takeSnapshot() { - throw new Error("Method not implemented."); - } -} -exports.SystemWorker = SystemWorker; -_SystemWorker_port = new WeakMap(), _SystemWorker_instances = new WeakSet(), _SystemWorker_onMessage = function _SystemWorker_onMessage(e) { - console.debug(e); -}; -//# sourceMappingURL=SystemWorker.js.map \ No newline at end of file diff --git a/packages/system/dist/SystemWorker.js.map b/packages/system/dist/SystemWorker.js.map deleted file mode 100644 index 9b87a7f2..00000000 --- a/packages/system/dist/SystemWorker.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"SystemWorker.js","sourceRoot":"","sources":["../src/SystemWorker.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAEA,oEAA4C;AAM5C,MAAa,YAAa,SAAQ,uBAA6B;IAG7D;QACE,KAAK,EAAE,CAAC;;QAHV,qCAAmB;QAIjB,IAAI,cAAc,IAAI,MAAM,EAAE;YAC5B,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,YAAY,CAAC,CAAC;YAC9C,uBAAA,IAAI,sBAAS,MAAM,CAAC,IAAI,MAAA,CAAC;YACzB,uBAAA,IAAI,0BAAM,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,CAAC,uBAAA,IAAI,wDAAW,MAAf,IAAI,EAAY,CAAC,CAAC,CAAC;SAChD;aAAM;YACL,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;SAClD;IACH,CAAC;IAID,IAAI,OAAO;QACT,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAsB,IAAiB,EAAE,GAA0B;QACtE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,WAAW,CAAC,GAAW;QACrB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,QAAQ,CAAC,GAAW;QAClB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,cAAc,CAAC,OAAe,EAAE,OAAsB;QACpD,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,eAAe,CAAC,OAAe;QAC7B,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,cAAc,CAAC,EAAc;QAC3B,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,gBAAgB,CAAC,KAAa,EAAE,EAAc;QAC5C,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,YAAY;QACV,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;CAKF;AAvDD,oCAuDC;wIAHY,CAAoB;IAC7B,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnB,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/Tag.d.ts b/packages/system/dist/Tag.d.ts deleted file mode 100644 index d94498cb..00000000 --- a/packages/system/dist/Tag.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { HexKey, u256 } from "./Nostr"; -export default class Tag { - Original: string[]; - Key: string; - Event?: u256; - PubKey?: HexKey; - Relay?: string; - Marker?: string; - Hashtag?: string; - DTag?: string; - ATag?: string; - Index: number; - Invalid: boolean; - LNURL?: string; - constructor(tag: string[], index: number); - ToObject(): string[] | null; -} -//# sourceMappingURL=Tag.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Tag.d.ts.map b/packages/system/dist/Tag.d.ts.map deleted file mode 100644 index a961ec79..00000000 --- a/packages/system/dist/Tag.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Tag.d.ts","sourceRoot":"","sources":["../src/Tag.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAGvC,MAAM,CAAC,OAAO,OAAO,GAAG;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;gBAEH,GAAG,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM;IAgDxC,QAAQ,IAAI,MAAM,EAAE,GAAG,IAAI;CAsB5B"} \ No newline at end of file diff --git a/packages/system/dist/Tag.js b/packages/system/dist/Tag.js deleted file mode 100644 index 3a0b281f..00000000 --- a/packages/system/dist/Tag.js +++ /dev/null @@ -1,75 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const Util_1 = require("./Util"); -class Tag { - constructor(tag, index) { - this.Original = tag; - this.Key = tag[0]; - this.Index = index; - this.Invalid = false; - switch (this.Key) { - case "e": { - // ["e", , , ] - this.Event = tag[1]; - this.Relay = tag.length > 2 ? tag[2] : undefined; - this.Marker = tag.length > 3 ? tag[3] : undefined; - if (!this.Event) { - this.Invalid = true; - } - break; - } - case "p": { - // ["p", ] - this.PubKey = tag[1]; - if (!this.PubKey) { - this.Invalid = true; - } - break; - } - case "d": { - this.DTag = tag[1]; - break; - } - case "a": { - this.ATag = tag[1]; - break; - } - case "t": { - this.Hashtag = tag[1]; - break; - } - case "delegation": { - this.PubKey = tag[1]; - break; - } - case "zap": { - this.LNURL = tag[1]; - break; - } - } - } - ToObject() { - switch (this.Key) { - case "e": { - let ret = ["e", this.Event, this.Relay, this.Marker]; - const trimEnd = ret.reverse().findIndex(a => a !== undefined); - ret = ret.reverse().slice(0, ret.length - trimEnd); - return ret; - } - case "p": { - return this.PubKey ? ["p", this.PubKey] : null; - } - case "t": { - return ["t", (0, Util_1.unwrap)(this.Hashtag)]; - } - case "d": { - return ["d", (0, Util_1.unwrap)(this.DTag)]; - } - default: { - return this.Original; - } - } - } -} -exports.default = Tag; -//# sourceMappingURL=Tag.js.map \ No newline at end of file diff --git a/packages/system/dist/Tag.js.map b/packages/system/dist/Tag.js.map deleted file mode 100644 index 1e6461c5..00000000 --- a/packages/system/dist/Tag.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Tag.js","sourceRoot":"","sources":["../src/Tag.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/Util.d.ts b/packages/system/dist/Util.d.ts deleted file mode 100644 index b62acff4..00000000 --- a/packages/system/dist/Util.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { NostrEvent, u256 } from "./Nostr"; -export declare function unwrap(v: T | undefined | null): T; -/** - * Convert hex to bech32 - */ -export declare function hexToBech32(hrp: string, hex?: string): string; -export declare function sanitizeRelayUrl(url: string): string | undefined; -export declare function unixNow(): number; -export declare function unixNowMs(): number; -export declare function deepEqual(x: any, y: any): boolean; -/** - * Compute the "distance" between two objects by comparing their difference in properties - * Missing/Added keys result in +10 distance - * This is not recursive - */ -export declare function distance(a: any, b: any): number; -export declare function dedupe(v: Array): T[]; -export declare function appendDedupe(a?: Array, b?: Array): T[]; -export declare function findTag(e: NostrEvent, tag: string): string | undefined; -export declare const sha256: (str: string | Uint8Array) => u256; -export declare function getPublicKey(privKey: string): string; -export declare function bech32ToHex(str: string): string; -//# sourceMappingURL=Util.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/Util.d.ts.map b/packages/system/dist/Util.d.ts.map deleted file mode 100644 index e7c9a5a5..00000000 --- a/packages/system/dist/Util.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Util.d.ts","sourceRoot":"","sources":["../src/Util.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE3C,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,CAAC,CAKpD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,UAYpD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,sBAM3C;AAED,wBAAgB,OAAO,WAEtB;AAED,wBAAgB,SAAS,WAExB;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,OAAO,CAQjD;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,MAAM,CA2B/C;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,OAEpC;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,OAEzD;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,sBAKjD;AAED,eAAO,MAAM,MAAM,QAAS,MAAM,GAAG,UAAU,KAAG,IAEjD,CAAA;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,UAE3C;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,UAQtC"} \ No newline at end of file diff --git a/packages/system/dist/Util.js b/packages/system/dist/Util.js deleted file mode 100644 index 92f0d069..00000000 --- a/packages/system/dist/Util.js +++ /dev/null @@ -1,148 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.bech32ToHex = exports.getPublicKey = exports.sha256 = exports.findTag = exports.appendDedupe = exports.dedupe = exports.distance = exports.deepEqual = exports.unixNowMs = exports.unixNow = exports.sanitizeRelayUrl = exports.hexToBech32 = exports.unwrap = void 0; -const utils = __importStar(require("@noble/curves/abstract/utils")); -const secp = __importStar(require("@noble/curves/secp256k1")); -const sha256_1 = require("@noble/hashes/sha256"); -const bech32_1 = require("bech32"); -function unwrap(v) { - if (v === undefined || v === null) { - throw new Error("missing value"); - } - return v; -} -exports.unwrap = unwrap; -/** - * Convert hex to bech32 - */ -function hexToBech32(hrp, hex) { - if (typeof hex !== "string" || hex.length === 0 || hex.length % 2 !== 0) { - return ""; - } - try { - const buf = utils.hexToBytes(hex); - return bech32_1.bech32.encode(hrp, bech32_1.bech32.toWords(buf)); - } - catch (e) { - console.warn("Invalid hex", hex, e); - return ""; - } -} -exports.hexToBech32 = hexToBech32; -function sanitizeRelayUrl(url) { - try { - return new URL(url).toString(); - } - catch { - // ignore - } -} -exports.sanitizeRelayUrl = sanitizeRelayUrl; -function unixNow() { - return Math.floor(unixNowMs() / 1000); -} -exports.unixNow = unixNow; -function unixNowMs() { - return new Date().getTime(); -} -exports.unixNowMs = unixNowMs; -function deepEqual(x, y) { - const ok = Object.keys, tx = typeof x, ty = typeof y; - return x && y && tx === "object" && tx === ty - ? ok(x).length === ok(y).length && ok(x).every(key => deepEqual(x[key], y[key])) - : x === y; -} -exports.deepEqual = deepEqual; -/** - * Compute the "distance" between two objects by comparing their difference in properties - * Missing/Added keys result in +10 distance - * This is not recursive - */ -function distance(a, b) { - const keys1 = Object.keys(a); - const keys2 = Object.keys(b); - const maxKeys = keys1.length > keys2.length ? keys1 : keys2; - let distance = 0; - for (const key of maxKeys) { - if (key in a && key in b) { - if (Array.isArray(a[key]) && Array.isArray(b[key])) { - const aa = a[key]; - const bb = b[key]; - if (aa.length === bb.length) { - if (aa.some(v => !bb.includes(v))) { - distance++; - } - } - else { - distance++; - } - } - else if (a[key] !== b[key]) { - distance++; - } - } - else { - distance += 10; - } - } - return distance; -} -exports.distance = distance; -function dedupe(v) { - return [...new Set(v)]; -} -exports.dedupe = dedupe; -function appendDedupe(a, b) { - return dedupe([...(a ?? []), ...(b ?? [])]); -} -exports.appendDedupe = appendDedupe; -function findTag(e, tag) { - const maybeTag = e.tags.find(evTag => { - return evTag[0] === tag; - }); - return maybeTag && maybeTag[1]; -} -exports.findTag = findTag; -const sha256 = (str) => { - return utils.bytesToHex((0, sha256_1.sha256)(str)); -}; -exports.sha256 = sha256; -function getPublicKey(privKey) { - return utils.bytesToHex(secp.schnorr.getPublicKey(privKey)); -} -exports.getPublicKey = getPublicKey; -function bech32ToHex(str) { - try { - const nKey = bech32_1.bech32.decode(str, 1000); - const buff = bech32_1.bech32.fromWords(nKey.words); - return utils.bytesToHex(Uint8Array.from(buff)); - } - catch (e) { - return str; - } -} -exports.bech32ToHex = bech32ToHex; -//# sourceMappingURL=Util.js.map \ No newline at end of file diff --git a/packages/system/dist/Util.js.map b/packages/system/dist/Util.js.map deleted file mode 100644 index 9846635c..00000000 --- a/packages/system/dist/Util.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Util.js","sourceRoot":"","sources":["../src/Util.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/system/dist/WorkQueue.d.ts b/packages/system/dist/WorkQueue.d.ts deleted file mode 100644 index 3b1df622..00000000 --- a/packages/system/dist/WorkQueue.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface WorkQueueItem { - next: () => Promise; - resolve(v: unknown): void; - reject(e: unknown): void; -} -export declare function processWorkQueue(queue?: Array, queueDelay?: number): Promise; -export declare const barrierQueue: (queue: Array, then: () => Promise) => Promise; -//# sourceMappingURL=WorkQueue.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/WorkQueue.d.ts.map b/packages/system/dist/WorkQueue.d.ts.map deleted file mode 100644 index 2f91432e..00000000 --- a/packages/system/dist/WorkQueue.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"WorkQueue.d.ts","sourceRoot":"","sources":["../src/WorkQueue.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC1B;AAED,wBAAsB,gBAAgB,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,EAAE,UAAU,SAAM,iBAapF;AAED,eAAO,MAAM,YAAY,aAAoB,MAAM,aAAa,CAAC,uCAQhE,CAAC"} \ No newline at end of file diff --git a/packages/system/dist/WorkQueue.js b/packages/system/dist/WorkQueue.js deleted file mode 100644 index b9d0b0d6..00000000 --- a/packages/system/dist/WorkQueue.js +++ /dev/null @@ -1,30 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.barrierQueue = exports.processWorkQueue = void 0; -async function processWorkQueue(queue, queueDelay = 200) { - while (queue && queue.length > 0) { - const v = queue.shift(); - if (v) { - try { - const ret = await v.next(); - v.resolve(ret); - } - catch (e) { - v.reject(e); - } - } - } - setTimeout(() => processWorkQueue(queue, queueDelay), queueDelay); -} -exports.processWorkQueue = processWorkQueue; -const barrierQueue = async (queue, then) => { - return new Promise((resolve, reject) => { - queue.push({ - next: then, - resolve, - reject, - }); - }); -}; -exports.barrierQueue = barrierQueue; -//# sourceMappingURL=WorkQueue.js.map \ No newline at end of file diff --git a/packages/system/dist/WorkQueue.js.map b/packages/system/dist/WorkQueue.js.map deleted file mode 100644 index 8f34b037..00000000 --- a/packages/system/dist/WorkQueue.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"WorkQueue.js","sourceRoot":"","sources":["../src/WorkQueue.ts"],"names":[],"mappings":";;;AAMO,KAAK,UAAU,gBAAgB,CAAC,KAA4B,EAAE,UAAU,GAAG,GAAG;IACnF,OAAO,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;QAChC,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;QACxB,IAAI,CAAC,EAAE;YACL,IAAI;gBACF,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC3B,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;aAChB;YAAC,OAAO,CAAC,EAAE;gBACV,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;aACb;SACF;KACF;IACD,UAAU,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,UAAU,CAAC,CAAC;AACpE,CAAC;AAbD,4CAaC;AAEM,MAAM,YAAY,GAAG,KAAK,EAAK,KAA2B,EAAE,IAAsB,EAAc,EAAE;IACvG,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACxC,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,IAAI;YACV,OAAO;YACP,MAAM;SACP,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AARW,QAAA,YAAY,gBAQvB"} \ No newline at end of file diff --git a/packages/system/dist/cache/index.d.ts b/packages/system/dist/cache/index.d.ts deleted file mode 100644 index f8fc473f..00000000 --- a/packages/system/dist/cache/index.d.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { HexKey, NostrEvent, UserMetadata } from ".."; -export interface MetadataCache extends UserMetadata { - /** - * When the object was saved in cache - */ - loaded: number; - /** - * When the source metadata event was created - */ - created: number; - /** - * The pubkey of the owner of this metadata - */ - pubkey: HexKey; - /** - * The bech32 encoded pubkey - */ - npub: string; - /** - * Pubkey of zapper service - */ - zapService?: HexKey; - /** - * If the nip05 is valid for this user - */ - isNostrAddressValid: boolean; -} -export declare function mapEventToProfile(ev: NostrEvent): MetadataCache | undefined; -export interface CacheStore { - preload(): Promise; - getFromCache(key?: string): T | undefined; - get(key?: string): Promise; - bulkGet(keys: Array): Promise>; - set(obj: T): Promise; - bulkSet(obj: Array): Promise; - update(m: TCachedWithCreated): Promise<"new" | "updated" | "refresh" | "no_change">; - /** - * Loads a list of rows from disk cache - * @param keys List of ids to load - * @returns Keys that do not exist on disk cache - */ - buffer(keys: Array): Promise>; - clear(): Promise; -} -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/cache/index.d.ts.map b/packages/system/dist/cache/index.d.ts.map deleted file mode 100644 index 65d321a7..00000000 --- a/packages/system/dist/cache/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAGtD,MAAM,WAAW,aAAc,SAAQ,YAAY;IACjD;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,UAAU,6BAa/C;AAED,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;IAC1C,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC1C,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,CAAC,kBAAkB,SAAS,CAAC,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC,CAAA;IAEvJ;;;;OAIG;IACH,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAEpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB"} \ No newline at end of file diff --git a/packages/system/dist/cache/index.js b/packages/system/dist/cache/index.js deleted file mode 100644 index f21265bd..00000000 --- a/packages/system/dist/cache/index.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.mapEventToProfile = void 0; -const Util_1 = require("../Util"); -function mapEventToProfile(ev) { - try { - const data = JSON.parse(ev.content); - return { - ...data, - pubkey: ev.pubkey, - npub: (0, Util_1.hexToBech32)("npub", ev.pubkey), - created: ev.created_at, - loaded: (0, Util_1.unixNowMs)(), - }; - } - catch (e) { - console.error("Failed to parse JSON", ev, e); - } -} -exports.mapEventToProfile = mapEventToProfile; -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/system/dist/cache/index.js.map b/packages/system/dist/cache/index.js.map deleted file mode 100644 index 6660f61b..00000000 --- a/packages/system/dist/cache/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":";;;AACA,kCAAiD;AAkCjD,SAAgB,iBAAiB,CAAC,EAAc;IAC9C,IAAI;QACF,MAAM,IAAI,GAAiB,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;QAClD,OAAO;YACL,GAAG,IAAI;YACP,MAAM,EAAE,EAAE,CAAC,MAAM;YACjB,IAAI,EAAE,IAAA,kBAAW,EAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC;YACpC,OAAO,EAAE,EAAE,CAAC,UAAU;YACtB,MAAM,EAAE,IAAA,gBAAS,GAAE;SACH,CAAC;KACpB;IAAC,OAAO,CAAC,EAAE;QACV,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;KAC9C;AACH,CAAC;AAbD,8CAaC"} \ No newline at end of file diff --git a/packages/system/dist/index.d.ts b/packages/system/dist/index.d.ts deleted file mode 100644 index 08c37550..00000000 --- a/packages/system/dist/index.d.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { AuthHandler, RelaySettings, ConnectionStateSnapshot } from "./Connection"; -import { RequestBuilder } from "./RequestBuilder"; -import { NoteStore } from "./NoteCollection"; -import { Query } from "./Query"; -import { NostrEvent, ReqFilter } from "./Nostr"; -export * from "./NostrSystem"; -export { default as EventKind } from "./EventKind"; -export * from "./Nostr"; -export * from "./Links"; -export { default as Tag } from "./Tag"; -export * from "./Nips"; -export * from "./RelayInfo"; -export * from "./EventExt"; -export * from "./Connection"; -export * from "./NoteCollection"; -export * from "./RequestBuilder"; -export * from "./EventPublisher"; -export * from "./EventBuilder"; -export * from "./NostrLink"; -export * from "./cache"; -export * from "./ProfileCache"; -export interface SystemInterface { - /** - * Handler function for NIP-42 - */ - HandleAuth?: AuthHandler; - get Sockets(): Array; - GetQuery(id: string): Query | undefined; - Query(type: { - new (): T; - }, req: RequestBuilder | null): Query | undefined; - ConnectToRelay(address: string, options: RelaySettings): Promise; - DisconnectRelay(address: string): void; - BroadcastEvent(ev: NostrEvent): void; - WriteOnceToRelay(relay: string, ev: NostrEvent): Promise; -} -export interface SystemSnapshot { - queries: Array<{ - id: string; - filters: Array; - subFilters: Array; - }>; -} -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/packages/system/dist/index.d.ts.map b/packages/system/dist/index.d.ts.map deleted file mode 100644 index d9004955..00000000 --- a/packages/system/dist/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEhD,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,OAAO,CAAC;AACvC,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAE/B,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,IAAI,OAAO,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC9C,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;IACxC,KAAK,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,EAAE;QAAE,QAAO,CAAC,CAAA;KAAE,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,GAAG,KAAK,GAAG,SAAS,CAAC;IAC9F,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,cAAc,CAAC,EAAE,EAAE,UAAU,GAAG,IAAI,CAAC;IACrC,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,KAAK,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QAC1B,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;KAC9B,CAAC,CAAC;CACJ"} \ No newline at end of file diff --git a/packages/system/dist/index.js b/packages/system/dist/index.js deleted file mode 100644 index 9e8c83e3..00000000 --- a/packages/system/dist/index.js +++ /dev/null @@ -1,39 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Tag = exports.EventKind = void 0; -__exportStar(require("./NostrSystem"), exports); -var EventKind_1 = require("./EventKind"); -Object.defineProperty(exports, "EventKind", { enumerable: true, get: function () { return __importDefault(EventKind_1).default; } }); -__exportStar(require("./Nostr"), exports); -__exportStar(require("./Links"), exports); -var Tag_1 = require("./Tag"); -Object.defineProperty(exports, "Tag", { enumerable: true, get: function () { return __importDefault(Tag_1).default; } }); -__exportStar(require("./Nips"), exports); -__exportStar(require("./RelayInfo"), exports); -__exportStar(require("./EventExt"), exports); -__exportStar(require("./Connection"), exports); -__exportStar(require("./NoteCollection"), exports); -__exportStar(require("./RequestBuilder"), exports); -__exportStar(require("./EventPublisher"), exports); -__exportStar(require("./EventBuilder"), exports); -__exportStar(require("./NostrLink"), exports); -__exportStar(require("./cache"), exports); -__exportStar(require("./ProfileCache"), exports); -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/system/dist/index.js.map b/packages/system/dist/index.js.map deleted file mode 100644 index a0e12b3a..00000000 --- a/packages/system/dist/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAMA,gDAA8B;AAC9B,yCAAmD;AAA1C,uHAAA,OAAO,OAAa;AAC7B,0CAAwB;AACxB,0CAAwB;AACxB,6BAAuC;AAA9B,2GAAA,OAAO,OAAO;AACvB,yCAAuB;AACvB,8CAA4B;AAC5B,6CAA2B;AAC3B,+CAA6B;AAC7B,mDAAiC;AACjC,mDAAiC;AACjC,mDAAiC;AACjC,iDAA+B;AAC/B,8CAA4B;AAC5B,0CAAwB;AACxB,iDAA+B"} \ No newline at end of file diff --git a/packages/system/package.json b/packages/system/package.json index ecdd59f6..38616bfe 100644 --- a/packages/system/package.json +++ b/packages/system/package.json @@ -1,16 +1,20 @@ { "name": "@snort/system", - "version": "1.0.0", + "version": "1.0.1", "description": "Snort nostr system package", "main": "dist/index.js", "types": "dist/index.d.ts", "repository": "https://git.v0l.io/Kieran/snort", - "author": "Kieran", - "license": "GPLv3", + "author": "v0l", + "license": "GPL-3.0-or-later", "scripts": { "build": "rm -rf dist && tsc", "test": "jest" }, + "files": [ + "src", + "dist" + ], "devDependencies": { "@jest/globals": "^29.5.0", "@types/jest": "^29.5.1", diff --git a/packages/system/snort-system-1.0.0.tgz b/packages/system/snort-system-1.0.0.tgz deleted file mode 100644 index 3384474ebdb187e6e3ff3ba8e0f4713a867d1303..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56011 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 diff --git a/packages/system/src/RequestMerger.ts b/packages/system/src/RequestMerger.ts index db0403fe..900a1fbc 100644 --- a/packages/system/src/RequestMerger.ts +++ b/packages/system/src/RequestMerger.ts @@ -1,6 +1,5 @@ import { ReqFilter } from "."; import { FlatReqFilter } from "./RequestExpander"; -import { distance } from "./Util"; /** * Keys which can change the entire meaning of the filter outside the array types @@ -17,7 +16,70 @@ export function canMergeFilters(a: FlatReqFilter | ReqFilter, b: FlatReqFilter | } } } - return distance(aObj, bObj) <= 1; + let flag = false; + if (!equalProp(a.ids, b.ids)) { + flag = true; + } + if (!equalProp(a.authors, b.authors)) { + if (flag) return false; + flag = true; + } + if (!equalProp(a.kinds, b.kinds)) { + if (flag) return false; + flag = true; + } + if (!equalProp(a.limit, b.limit)) { + if (flag) return false; + flag = true; + } + if (!equalProp(a.until, b.until)) { + if (flag) return false; + flag = true; + } + if (!equalProp(a.since, b.since)) { + if (flag) return false; + flag = true; + } + if (!equalProp(a.search, b.search)) { + if (flag) return false; + flag = true; + } + if (!equalProp(a["#e"], b["#e"])) { + if (flag) return false; + flag = true; + } + if (!equalProp(a["#p"], b["#p"])) { + if (flag) return false; + flag = true; + } + if (!equalProp(a["#d"], b["#d"])) { + if (flag) return false; + flag = true; + } + if (!equalProp(a["#r"], b["#r"])) { + if (flag) return false; + flag = true; + } + if (!equalProp(a["#t"], b["#t"])) { + if (flag) return false; + flag = true; + } + return true; +} + +function equalProp(a: string | number | Array | undefined, b: string | number | Array | undefined) { + if ((a !== undefined && b === undefined) || (a === undefined && b !== undefined)) { + return false; + } + if (Array.isArray(a) && Array.isArray(b)) { + if (a.length !== b.length) { + return false; + } + if (!a.every(v => b.includes(v))) { + return false; + } + } + return a === b; } export function mergeSimilar(filters: Array): Array { diff --git a/packages/system/src/RequestSplitter.ts b/packages/system/src/RequestSplitter.ts index 2231d8b5..0210a442 100644 --- a/packages/system/src/RequestSplitter.ts +++ b/packages/system/src/RequestSplitter.ts @@ -1,5 +1,5 @@ import { ReqFilter } from "."; -import { deepEqual } from "./Util"; +import { flatReqFilterEq } from "./Util"; import { expandFilter } from "./RequestExpander"; import { flatMerge } from "./RequestMerger"; @@ -7,8 +7,8 @@ export function diffFilters(prev: Array, next: Array) { const prevExpanded = prev.flatMap(expandFilter); const nextExpanded = next.flatMap(expandFilter); - const added = flatMerge(nextExpanded.filter(a => !prevExpanded.some(b => deepEqual(a, b)))); - const removed = flatMerge(prevExpanded.filter(a => !nextExpanded.some(b => deepEqual(a, b)))); + const added = flatMerge(nextExpanded.filter(a => !prevExpanded.some(b => flatReqFilterEq(a, b)))); + const removed = flatMerge(prevExpanded.filter(a => !nextExpanded.some(b => flatReqFilterEq(a, b)))); return { added, diff --git a/packages/system/src/Util.ts b/packages/system/src/Util.ts index a0053736..5d177f71 100644 --- a/packages/system/src/Util.ts +++ b/packages/system/src/Util.ts @@ -3,6 +3,7 @@ import * as secp from "@noble/curves/secp256k1"; import { sha256 as sha2 } from "@noble/hashes/sha256"; import { bech32 } from "bech32"; import { NostrEvent, u256 } from "./Nostr"; +import { FlatReqFilter } from "RequestExpander"; export function unwrap(v: T | undefined | null): T { if (v === undefined || v === null) { @@ -54,38 +55,19 @@ export function deepEqual(x: any, y: any): boolean { : x === y; } -/** - * Compute the "distance" between two objects by comparing their difference in properties - * Missing/Added keys result in +10 distance - * This is not recursive - */ -export function distance(a: any, b: any): number { - const keys1 = Object.keys(a); - const keys2 = Object.keys(b); - const maxKeys = keys1.length > keys2.length ? keys1 : keys2; - - let distance = 0; - for (const key of maxKeys) { - if (key in a && key in b) { - if (Array.isArray(a[key]) && Array.isArray(b[key])) { - const aa = a[key] as Array; - const bb = b[key] as Array; - if (aa.length === bb.length) { - if (aa.some(v => !bb.includes(v))) { - distance++; - } - } else { - distance++; - } - } else if (a[key] !== b[key]) { - distance++; - } - } else { - distance += 10; - } - } - - return distance; +export function flatReqFilterEq(a: FlatReqFilter, b: FlatReqFilter): boolean { + return a.ids === b.ids + && a.kinds === b.kinds + && a.authors === b.authors + && a.limit === b.limit + && a.since === b.since + && a.until === b.until + && a.search === b.search + && a["#e"] === b["#e"] + && a["#p"] === b["#p"] + && a["#t"] === b["#t"] + && a["#d"] === b["#d"] + && a["#r"] === b["#r"]; } export function dedupe(v: Array) { From 7bfe2f0c915ac2beb27d000868d099857a01e209 Mon Sep 17 00:00:00 2001 From: Kieran Date: Mon, 12 Jun 2023 14:15:45 +0100 Subject: [PATCH 21/24] optimize --- packages/system/jest.config.js | 4 +- packages/system/src/Connection.ts | 2 +- packages/system/src/EventBuilder.ts | 2 +- packages/system/src/EventExt.ts | 2 +- packages/system/src/EventPublisher.ts | 2 +- packages/system/src/GossipModel.ts | 2 +- packages/system/src/NostrLink.ts | 2 +- packages/system/src/NostrSystem.ts | 12 +- packages/system/src/NoteCollection.ts | 2 +- packages/system/src/ProfileCache.ts | 2 +- packages/system/src/Query.ts | 16 +-- packages/system/src/RequestBuilder.ts | 16 ++- packages/system/src/RequestMerger.ts | 103 +++--------------- packages/system/src/RequestSplitter.ts | 21 ++-- packages/system/src/Tag.ts | 2 +- packages/system/src/{Util.ts => Utils.ts} | 68 +++++++++++- packages/system/src/cache/index.ts | 12 +- packages/system/tests/RequestBuilder.test.ts | 3 +- packages/system/tests/RequestMerger.test.ts | 76 ++++++++++++- packages/system/tests/RequestSplitter.test.ts | 13 ++- packages/system/tests/Util.test.ts | 75 +------------ packages/system/tsconfig.json | 4 +- 22 files changed, 221 insertions(+), 220 deletions(-) rename packages/system/src/{Util.ts => Utils.ts} (54%) diff --git a/packages/system/jest.config.js b/packages/system/jest.config.js index 1ccd5800..04dee89c 100644 --- a/packages/system/jest.config.js +++ b/packages/system/jest.config.js @@ -3,7 +3,7 @@ module.exports = { bail: true, preset: "ts-jest", testEnvironment: "jsdom", - roots: ["src"], - moduleDirectories: ["src"], + roots: ["src", "tests"], + moduleDirectories: ["src", "node_modules"], setupFiles: ["./tests/setupTests.ts"], }; diff --git a/packages/system/src/Connection.ts b/packages/system/src/Connection.ts index c4e11052..0c2c0347 100644 --- a/packages/system/src/Connection.ts +++ b/packages/system/src/Connection.ts @@ -4,7 +4,7 @@ import { DefaultConnectTimeout } from "./Const"; import { ConnectionStats } from "./ConnectionStats"; import { NostrEvent, ReqCommand, TaggedRawEvent, u256 } from "./Nostr"; import { RelayInfo } from "./RelayInfo"; -import { unwrap } from "./Util"; +import { unwrap } from "./Utils"; import ExternalStore from "./ExternalStore"; export type AuthHandler = (challenge: string, relay: string) => Promise; diff --git a/packages/system/src/EventBuilder.ts b/packages/system/src/EventBuilder.ts index d59bd647..a581c71e 100644 --- a/packages/system/src/EventBuilder.ts +++ b/packages/system/src/EventBuilder.ts @@ -1,6 +1,6 @@ import { EventKind, HexKey, NostrPrefix, NostrEvent } from "."; import { HashtagRegex } from "./Const"; -import { getPublicKey, unixNow } from "./Util"; +import { getPublicKey, unixNow } from "./Utils"; import { EventExt } from "./EventExt"; import { parseNostrLink } from "./NostrLink"; diff --git a/packages/system/src/EventExt.ts b/packages/system/src/EventExt.ts index 4df025ad..c4fa03c8 100644 --- a/packages/system/src/EventExt.ts +++ b/packages/system/src/EventExt.ts @@ -2,7 +2,7 @@ import * as secp from "@noble/curves/secp256k1"; import * as utils from "@noble/curves/abstract/utils"; import { EventKind, HexKey, NostrEvent, Tag } from "."; import base64 from "@protobufjs/base64"; -import { sha256, unixNow } from "./Util"; +import { sha256, unixNow } from "./Utils"; export interface Thread { root?: Tag; diff --git a/packages/system/src/EventPublisher.ts b/packages/system/src/EventPublisher.ts index b452bf48..5f388ddc 100644 --- a/packages/system/src/EventPublisher.ts +++ b/packages/system/src/EventPublisher.ts @@ -13,7 +13,7 @@ import { UserMetadata, } from "."; -import { unwrap } from "./Util"; +import { unwrap } from "./Utils"; import { EventBuilder } from "./EventBuilder"; import { EventExt } from "./EventExt"; import { barrierQueue, processWorkQueue, WorkQueueItem } from "./WorkQueue"; diff --git a/packages/system/src/GossipModel.ts b/packages/system/src/GossipModel.ts index 07f8817e..18aa1e7c 100644 --- a/packages/system/src/GossipModel.ts +++ b/packages/system/src/GossipModel.ts @@ -1,5 +1,5 @@ import { FullRelaySettings, ReqFilter } from "."; -import { unwrap } from "./Util"; +import { unwrap } from "./Utils"; import debug from "debug"; const PickNRelays = 2; diff --git a/packages/system/src/NostrLink.ts b/packages/system/src/NostrLink.ts index cb663b3b..d866c279 100644 --- a/packages/system/src/NostrLink.ts +++ b/packages/system/src/NostrLink.ts @@ -1,4 +1,4 @@ -import { bech32ToHex, hexToBech32 } from "./Util"; +import { bech32ToHex, hexToBech32 } from "./Utils"; import { NostrPrefix, decodeTLV, TLVEntryType } from "."; export interface NostrLink { diff --git a/packages/system/src/NostrSystem.ts b/packages/system/src/NostrSystem.ts index c20b96a8..4ecc1e33 100644 --- a/packages/system/src/NostrSystem.ts +++ b/packages/system/src/NostrSystem.ts @@ -7,7 +7,7 @@ import { Query } from "./Query"; import { RelayCache } from "./GossipModel"; import { NoteStore } from "./NoteCollection"; import { BuiltRawReqFilter, RequestBuilder } from "./RequestBuilder"; -import { unwrap, sanitizeRelayUrl } from "./Util"; +import { unwrap, sanitizeRelayUrl } from "./Utils"; import { SystemInterface, SystemSnapshot } from "."; /** @@ -122,15 +122,13 @@ export class NostrSystem extends ExternalStore implements System const existing = this.Queries.get(req.id); if (existing) { const filters = !req.options?.skipDiff - ? req.buildDiff(this.#relayCache, existing.filters) + ? req.buildDiff(this.#relayCache, existing.flatFilters) : req.build(this.#relayCache); if (filters.length === 0 && !!req.options?.skipDiff) { return existing; } else { for (const subQ of filters) { - this.SendQuery(existing, subQ).then(qta => - qta.forEach(v => this.#log("New QT from diff %s %s %O from: %O", req.id, v.id, v.filters, existing.filters)) - ); + this.SendQuery(existing, subQ); } this.notifyChange(); return existing; @@ -142,9 +140,7 @@ export class NostrSystem extends ExternalStore implements System const q = new Query(req.id, store, req.options?.leaveOpen); this.Queries.set(req.id, q); for (const subQ of filters) { - this.SendQuery(q, subQ).then(qta => - qta.forEach(v => this.#log("New QT from diff %s %s %O", req.id, v.id, v.filters)) - ); + this.SendQuery(q, subQ); } this.notifyChange(); return q; diff --git a/packages/system/src/NoteCollection.ts b/packages/system/src/NoteCollection.ts index f33e41f7..75fa8647 100644 --- a/packages/system/src/NoteCollection.ts +++ b/packages/system/src/NoteCollection.ts @@ -1,5 +1,5 @@ import { TaggedRawEvent, u256 } from "."; -import { appendDedupe, findTag } from "./Util"; +import { appendDedupe, findTag } from "./Utils"; export interface StoreSnapshot { data: TSnapshot | undefined; diff --git a/packages/system/src/ProfileCache.ts b/packages/system/src/ProfileCache.ts index 7a21c6c9..9c59972d 100644 --- a/packages/system/src/ProfileCache.ts +++ b/packages/system/src/ProfileCache.ts @@ -1,7 +1,7 @@ import { EventKind, HexKey, SystemInterface, TaggedRawEvent, PubkeyReplaceableNoteStore, RequestBuilder } from "."; import { ProfileCacheExpire } from "./Const"; import { CacheStore, mapEventToProfile, MetadataCache } from "./cache"; -import { unixNowMs } from "./Util"; +import { unixNowMs } from "./Utils"; import debug from "debug"; export class ProfileLoaderService { diff --git a/packages/system/src/Query.ts b/packages/system/src/Query.ts index 4ec3a4df..f56d7baa 100644 --- a/packages/system/src/Query.ts +++ b/packages/system/src/Query.ts @@ -1,7 +1,7 @@ import { v4 as uuid } from "uuid"; import debug from "debug"; import { Connection, ReqFilter, Nips, TaggedRawEvent } from "."; -import { unixNowMs, unwrap } from "./Util"; +import { unixNowMs, unwrap } from "./Utils"; import { NoteStore } from "./NoteCollection"; import { flatMerge } from "./RequestMerger"; import { BuiltRawReqFilter } from "./RequestBuilder"; @@ -137,7 +137,6 @@ export class Query implements QueryBase { #feed: NoteStore; #log = debug("Query"); - #allFilters: Array = []; constructor(id: string, feed: NoteStore, leaveOpen?: boolean) { this.id = id; @@ -154,7 +153,11 @@ export class Query implements QueryBase { * Recompute the complete set of compressed filters from all query traces */ get filters() { - return this.#allFilters; + return flatMerge(this.flatFilters); + } + + get flatFilters() { + return this.#tracing.flatMap(a => a.filters).flatMap(expandFilter); } get feed() { @@ -271,14 +274,7 @@ export class Query implements QueryBase { () => this.#onProgress() ); this.#tracing.push(qt); - this.#reComputeFilters(); c.QueueReq(["REQ", qt.id, ...q.filters], () => qt.sentToRelay()); return qt; } - - #reComputeFilters() { - console.time("reComputeFilters"); - this.#allFilters = flatMerge(this.#tracing.flatMap(a => a.filters).flatMap(expandFilter)); - console.timeEnd("reComputeFilters"); - } } diff --git a/packages/system/src/RequestBuilder.ts b/packages/system/src/RequestBuilder.ts index a565da9a..bfc4e2e6 100644 --- a/packages/system/src/RequestBuilder.ts +++ b/packages/system/src/RequestBuilder.ts @@ -1,8 +1,10 @@ import { ReqFilter, u256, HexKey, EventKind } from "."; -import { appendDedupe, dedupe } from "./Util"; +import { appendDedupe, dedupe, unixNowMs } from "./Utils"; import { diffFilters } from "./RequestSplitter"; import { RelayCache, splitAllByWriteRelays, splitByWriteRelays } from "./GossipModel"; import { mergeSimilar } from "./RequestMerger"; +import { FlatReqFilter, expandFilter } from "./RequestExpander"; +import debug from "debug"; /** * Which strategy is used when building REQ filters @@ -92,10 +94,18 @@ export class RequestBuilder { * @param q All previous filters merged * @returns */ - buildDiff(relays: RelayCache, filters: Array): Array { - const next = this.buildRaw(); + buildDiff(relays: RelayCache, filters: Array): Array { + const start = unixNowMs(); + const next = this.#builders.flatMap(f => expandFilter(f.filter)) const diff = diffFilters(filters, next); + const ts = (unixNowMs() - start); + const log = debug("buildDiff"); + log("%s %d ms", this.id, ts); + if (ts > 200) { + console.warn(diff, filters); + } if (diff.changed) { + log(diff); return splitAllByWriteRelays(relays, diff.added).map(a => { return { strategy: RequestStrategy.AuthorsRelays, diff --git a/packages/system/src/RequestMerger.ts b/packages/system/src/RequestMerger.ts index 900a1fbc..7dde780f 100644 --- a/packages/system/src/RequestMerger.ts +++ b/packages/system/src/RequestMerger.ts @@ -1,3 +1,4 @@ +import { distance } from "./Utils"; import { ReqFilter } from "."; import { FlatReqFilter } from "./RequestExpander"; @@ -16,74 +17,10 @@ export function canMergeFilters(a: FlatReqFilter | ReqFilter, b: FlatReqFilter | } } } - let flag = false; - if (!equalProp(a.ids, b.ids)) { - flag = true; - } - if (!equalProp(a.authors, b.authors)) { - if (flag) return false; - flag = true; - } - if (!equalProp(a.kinds, b.kinds)) { - if (flag) return false; - flag = true; - } - if (!equalProp(a.limit, b.limit)) { - if (flag) return false; - flag = true; - } - if (!equalProp(a.until, b.until)) { - if (flag) return false; - flag = true; - } - if (!equalProp(a.since, b.since)) { - if (flag) return false; - flag = true; - } - if (!equalProp(a.search, b.search)) { - if (flag) return false; - flag = true; - } - if (!equalProp(a["#e"], b["#e"])) { - if (flag) return false; - flag = true; - } - if (!equalProp(a["#p"], b["#p"])) { - if (flag) return false; - flag = true; - } - if (!equalProp(a["#d"], b["#d"])) { - if (flag) return false; - flag = true; - } - if (!equalProp(a["#r"], b["#r"])) { - if (flag) return false; - flag = true; - } - if (!equalProp(a["#t"], b["#t"])) { - if (flag) return false; - flag = true; - } - return true; -} - -function equalProp(a: string | number | Array | undefined, b: string | number | Array | undefined) { - if ((a !== undefined && b === undefined) || (a === undefined && b !== undefined)) { - return false; - } - if (Array.isArray(a) && Array.isArray(b)) { - if (a.length !== b.length) { - return false; - } - if (!a.every(v => b.includes(v))) { - return false; - } - } - return a === b; + return distance(a, b) <= 1; } export function mergeSimilar(filters: Array): Array { - console.time("mergeSimilar"); const ret = []; const fCopy = [...filters]; @@ -92,14 +29,13 @@ export function mergeSimilar(filters: Array): Array { const mergeSet = [current]; for (let i = 0; i < fCopy.length; i++) { const f = fCopy[i]; - if (mergeSet.every(v => canMergeFilters(v, f))) { + if (!mergeSet.some(v => !canMergeFilters(v, f))) { mergeSet.push(fCopy.splice(i, 1)[0]); i--; } } ret.push(simpleMerge(mergeSet)); } - console.timeEnd("mergeSimilar"); return ret; } @@ -117,7 +53,8 @@ export function simpleMerge(filters: Array) { if (result[key] === undefined) { result[key] = [...value]; } else { - result[key] = [...new Set([...result[key], ...value])]; + const toAdd = value.filter(a => !result[key].includes(a)); + result[key].push(...toAdd); } } else { result[key] = value; @@ -162,31 +99,25 @@ export function filterIncludes(bigger: ReqFilter, smaller: ReqFilter) { * @returns */ export function flatMerge(all: Array): Array { - console.time("flatMerge"); let ret: Array = []; // to compute filters which can be merged we need to calucate the distance change between each filter // then we can merge filters which are exactly 1 change diff from each other function mergeFiltersInSet(filters: Array) { - const result: any = {}; - - filters.forEach(f => { - const filter = f as Record; - Object.entries(filter).forEach(([key, value]) => { - if (!DiscriminatorKeys.includes(key)) { - if (result[key] === undefined) { - result[key] = [value]; - } else { - result[key] = [...new Set([...result[key], value])]; - } + return filters.reduce((acc, a) => { + Object.entries(a).forEach(([k, v]) => { + if (DiscriminatorKeys.includes(k)) { + acc[k] = v; } else { - result[key] = value; + acc[k] ??= []; + if (!acc[k].includes(v)) { + acc[k].push(v); + } } - }); - }); - - return result as ReqFilter; + }) + return acc; + }, {} as any) as ReqFilter; } // reducer, kinda verbose @@ -212,7 +143,5 @@ export function flatMerge(all: Array): Array { } ret = n; } - console.timeEnd("flatMerge"); - console.debug(ret); return ret; } diff --git a/packages/system/src/RequestSplitter.ts b/packages/system/src/RequestSplitter.ts index 0210a442..7f430895 100644 --- a/packages/system/src/RequestSplitter.ts +++ b/packages/system/src/RequestSplitter.ts @@ -1,18 +1,15 @@ -import { ReqFilter } from "."; -import { flatReqFilterEq } from "./Util"; -import { expandFilter } from "./RequestExpander"; +import { flatFilterEq } from "./Utils"; +import { FlatReqFilter } from "./RequestExpander"; import { flatMerge } from "./RequestMerger"; -export function diffFilters(prev: Array, next: Array) { - const prevExpanded = prev.flatMap(expandFilter); - const nextExpanded = next.flatMap(expandFilter); - - const added = flatMerge(nextExpanded.filter(a => !prevExpanded.some(b => flatReqFilterEq(a, b)))); - const removed = flatMerge(prevExpanded.filter(a => !nextExpanded.some(b => flatReqFilterEq(a, b)))); +export function diffFilters(prev: Array, next: Array, calcRemoved?: boolean) { + const added = next.filter(a => !prev.some(b => flatFilterEq(a, b))); + const removed = calcRemoved ? prev.filter(a => !next.some(b => flatFilterEq(a, b))) : []; + const changed = added.length > 0 || removed.length > 0; return { - added, - removed, - changed: added.length > 0 || removed.length > 0, + added: changed ? flatMerge(added) : [], + removed: changed ? flatMerge(removed) : [], + changed, }; } diff --git a/packages/system/src/Tag.ts b/packages/system/src/Tag.ts index a006be49..b08128bd 100644 --- a/packages/system/src/Tag.ts +++ b/packages/system/src/Tag.ts @@ -1,5 +1,5 @@ import { HexKey, u256 } from "./Nostr"; -import { unwrap } from "./Util"; +import { unwrap } from "./Utils"; export default class Tag { Original: string[]; diff --git a/packages/system/src/Util.ts b/packages/system/src/Utils.ts similarity index 54% rename from packages/system/src/Util.ts rename to packages/system/src/Utils.ts index 5d177f71..38c1938f 100644 --- a/packages/system/src/Util.ts +++ b/packages/system/src/Utils.ts @@ -2,7 +2,7 @@ import * as utils from "@noble/curves/abstract/utils"; import * as secp from "@noble/curves/secp256k1"; import { sha256 as sha2 } from "@noble/hashes/sha256"; import { bech32 } from "bech32"; -import { NostrEvent, u256 } from "./Nostr"; +import { NostrEvent, ReqFilter, u256 } from "./Nostr"; import { FlatReqFilter } from "RequestExpander"; export function unwrap(v: T | undefined | null): T { @@ -55,7 +55,22 @@ export function deepEqual(x: any, y: any): boolean { : x === y; } -export function flatReqFilterEq(a: FlatReqFilter, b: FlatReqFilter): boolean { +export function reqFilterEq(a: FlatReqFilter | ReqFilter, b: FlatReqFilter | ReqFilter): boolean { + return equalProp(a.ids, b.ids) + && equalProp(a.kinds, b.kinds) + && equalProp(a.authors, b.authors) + && equalProp(a.limit, b.limit) + && equalProp(a.since, b.since) + && equalProp(a.until, b.until) + && equalProp(a.search, b.search) + && equalProp(a["#e"], b["#e"]) + && equalProp(a["#p"], b["#p"]) + && equalProp(a["#t"], b["#t"]) + && equalProp(a["#d"], b["#d"]) + && equalProp(a["#r"], b["#r"]); +} + +export function flatFilterEq(a: FlatReqFilter, b: FlatReqFilter): boolean { return a.ids === b.ids && a.kinds === b.kinds && a.authors === b.authors @@ -70,6 +85,55 @@ export function flatReqFilterEq(a: FlatReqFilter, b: FlatReqFilter): boolean { && a["#r"] === b["#r"]; } +export function equalProp(a: string | number | Array | undefined, b: string | number | Array | undefined) { + if ((a !== undefined && b === undefined) || (a === undefined && b !== undefined)) { + return false; + } + if (Array.isArray(a) && Array.isArray(b)) { + if (a.length !== b.length) { + return false; + } + if (!a.every(v => b.includes(v))) { + return false; + } + } + return a === b; +} + +/** + * Compute the "distance" between two objects by comparing their difference in properties + * Missing/Added keys result in +10 distance + * This is not recursive + */ +export function distance(a: any, b: any): number { + const keys1 = Object.keys(a); + const keys2 = Object.keys(b); + const maxKeys = keys1.length > keys2.length ? keys1 : keys2; + + let distance = 0; + for (const key of maxKeys) { + if (key in a && key in b) { + if (Array.isArray(a[key]) && Array.isArray(b[key])) { + const aa = a[key] as Array; + const bb = b[key] as Array; + if (aa.length === bb.length) { + if (aa.some(v => !bb.includes(v))) { + distance++; + } + } else { + distance++; + } + } else if (a[key] !== b[key]) { + distance++; + } + } else { + distance += 10; + } + } + + return distance; +} + export function dedupe(v: Array) { return [...new Set(v)]; } diff --git a/packages/system/src/cache/index.ts b/packages/system/src/cache/index.ts index 431318a0..408c1ab7 100644 --- a/packages/system/src/cache/index.ts +++ b/packages/system/src/cache/index.ts @@ -1,5 +1,5 @@ import { HexKey, NostrEvent, UserMetadata } from ".."; -import { hexToBech32, unixNowMs } from "../Util"; +import { hexToBech32, unixNowMs } from "../Utils"; export interface MetadataCache extends UserMetadata { /** @@ -36,13 +36,21 @@ export interface MetadataCache extends UserMetadata { export function mapEventToProfile(ev: NostrEvent) { try { const data: UserMetadata = JSON.parse(ev.content); - return { + let ret = { ...data, pubkey: ev.pubkey, npub: hexToBech32("npub", ev.pubkey), created: ev.created_at, loaded: unixNowMs(), } as MetadataCache; + + // sanitize non-string/number + for (const [k, v] of Object.entries(ret)) { + if (typeof v !== "number" && typeof v !== "string") { + (ret as any)[k] = undefined; + } + } + return ret; } catch (e) { console.error("Failed to parse JSON", ev, e); } diff --git a/packages/system/tests/RequestBuilder.test.ts b/packages/system/tests/RequestBuilder.test.ts index b0ec6f99..3289900b 100644 --- a/packages/system/tests/RequestBuilder.test.ts +++ b/packages/system/tests/RequestBuilder.test.ts @@ -1,6 +1,7 @@ import { RelayCache } from "../src/GossipModel"; import { RequestBuilder, RequestStrategy } from "../src/RequestBuilder"; import { describe, expect } from "@jest/globals"; +import { expandFilter } from "../src/RequestExpander"; const DummyCache = { get: (pk?: string) => { @@ -88,7 +89,7 @@ describe("RequestBuilder", () => { f0.authors(["a"]); expect(a).toEqual([{}]); - const b = rb.buildDiff(DummyCache, a); + const b = rb.buildDiff(DummyCache, a.flatMap(expandFilter)); expect(b).toMatchObject([ { filters: [{ authors: ["a"] }], diff --git a/packages/system/tests/RequestMerger.test.ts b/packages/system/tests/RequestMerger.test.ts index d9ac51cf..f9ac464d 100644 --- a/packages/system/tests/RequestMerger.test.ts +++ b/packages/system/tests/RequestMerger.test.ts @@ -1,7 +1,6 @@ import { ReqFilter } from "../src"; -import { filterIncludes, flatMerge, mergeSimilar, simpleMerge } from "../src/RequestMerger"; +import { canMergeFilters, filterIncludes, flatMerge, mergeSimilar, simpleMerge } from "../src/RequestMerger"; import { FlatReqFilter, expandFilter } from "../src/RequestExpander"; -import { distance } from "../src/Util"; describe("RequestMerger", () => { it("should simple merge authors", () => { @@ -105,6 +104,77 @@ describe("flatMerge", () => { ] as Array; const dut = flatMerge(input.flatMap(expandFilter).sort(() => (Math.random() > 0.5 ? 1 : -1))); - expect(dut.every(a => input.some(b => distance(b, a) === 0))).toEqual(true); + expect(dut.every(a => input.some(b => canMergeFilters(b, a) === false))).toEqual(true); }); }); + +describe('canMerge', () => { + it("should have 0 distance", () => { + const a = { + ids: "a", + }; + const b = { + ids: "a", + }; + expect(canMergeFilters(a, b)).toEqual(true); + }); + it("should have 1 distance", () => { + const a = { + ids: "a", + }; + const b = { + ids: "b", + }; + expect(canMergeFilters(a, b)).toEqual(true); + }); + it("should have 10 distance", () => { + const a = { + ids: "a", + }; + const b = { + ids: "a", + kinds: 1, + }; + expect(canMergeFilters(a, b)).toEqual(false); + }); + it("should have 11 distance", () => { + const a = { + ids: "a", + }; + const b = { + ids: "b", + kinds: 1, + }; + expect(canMergeFilters(a, b)).toEqual(false); + }); + it("should have 1 distance, arrays", () => { + const a = { + since: 1, + until: 100, + kinds: [1], + authors: ["kieran", "snort", "c", "d", "e"], + }; + const b = { + since: 1, + until: 100, + kinds: [6969], + authors: ["kieran", "snort", "c", "d", "e"], + }; + expect(canMergeFilters(a, b)).toEqual(true); + }); + it("should have 1 distance, array change extra", () => { + const a = { + since: 1, + until: 100, + kinds: [1], + authors: ["f", "kieran", "snort", "c", "d"], + }; + const b = { + since: 1, + until: 100, + kinds: [1], + authors: ["kieran", "snort", "c", "d", "e"], + }; + expect(canMergeFilters(a, b)).toEqual(true); + }); +}) diff --git a/packages/system/tests/RequestSplitter.test.ts b/packages/system/tests/RequestSplitter.test.ts index 7e639e34..9d3ac867 100644 --- a/packages/system/tests/RequestSplitter.test.ts +++ b/packages/system/tests/RequestSplitter.test.ts @@ -1,12 +1,13 @@ import { ReqFilter } from "../src"; import { describe, expect } from "@jest/globals"; import { diffFilters } from "../src/RequestSplitter"; +import { expandFilter } from "../src/RequestExpander"; describe("RequestSplitter", () => { test("single filter add value", () => { const a: Array = [{ kinds: [0], authors: ["a"] }]; const b: Array = [{ kinds: [0], authors: ["a", "b"] }]; - const diff = diffFilters(a, b); + const diff = diffFilters(a.flatMap(expandFilter), b.flatMap(expandFilter), true); expect(diff).toEqual({ added: [{ kinds: [0], authors: ["b"] }], removed: [], @@ -16,7 +17,7 @@ describe("RequestSplitter", () => { test("single filter remove value", () => { const a: Array = [{ kinds: [0], authors: ["a"] }]; const b: Array = [{ kinds: [0], authors: ["b"] }]; - const diff = diffFilters(a, b); + const diff = diffFilters(a.flatMap(expandFilter), b.flatMap(expandFilter), true); expect(diff).toEqual({ added: [{ kinds: [0], authors: ["b"] }], removed: [{ kinds: [0], authors: ["a"] }], @@ -26,7 +27,7 @@ describe("RequestSplitter", () => { test("single filter change critical key", () => { const a: Array = [{ kinds: [0], authors: ["a"], since: 100 }]; const b: Array = [{ kinds: [0], authors: ["a", "b"], since: 101 }]; - const diff = diffFilters(a, b); + const diff = diffFilters(a.flatMap(expandFilter), b.flatMap(expandFilter), true); expect(diff).toEqual({ added: [{ kinds: [0], authors: ["a", "b"], since: 101 }], removed: [{ kinds: [0], authors: ["a"], since: 100 }], @@ -42,7 +43,7 @@ describe("RequestSplitter", () => { { kinds: [0], authors: ["a", "b"] }, { kinds: [69], authors: ["a", "c"] }, ]; - const diff = diffFilters(a, b); + const diff = diffFilters(a.flatMap(expandFilter), b.flatMap(expandFilter), true); expect(diff).toEqual({ added: [ { kinds: [0], authors: ["b"] }, @@ -61,7 +62,7 @@ describe("RequestSplitter", () => { { kinds: [0], authors: ["b"] }, { kinds: [69], authors: ["c"] }, ]; - const diff = diffFilters(a, b); + const diff = diffFilters(a.flatMap(expandFilter), b.flatMap(expandFilter), true); expect(diff).toEqual({ added: [ { kinds: [0], authors: ["b"] }, @@ -77,7 +78,7 @@ describe("RequestSplitter", () => { { kinds: [0], authors: ["a"] }, { kinds: [69], authors: ["c"] }, ]; - const diff = diffFilters(a, b); + const diff = diffFilters(a.flatMap(expandFilter), b.flatMap(expandFilter), true); expect(diff).toEqual({ added: [{ kinds: [69], authors: ["c"] }], removed: [], diff --git a/packages/system/tests/Util.test.ts b/packages/system/tests/Util.test.ts index baebd7a2..9f1499b8 100644 --- a/packages/system/tests/Util.test.ts +++ b/packages/system/tests/Util.test.ts @@ -1,76 +1,5 @@ -import { distance } from "../src/Util"; - -describe("distance", () => { - it("should have 0 distance", () => { - const a = { - ids: "a", - }; - const b = { - ids: "a", - }; - expect(distance(a, b)).toEqual(0); - }); - it("should have 1 distance", () => { - const a = { - ids: "a", - }; - const b = { - ids: "b", - }; - expect(distance(a, b)).toEqual(1); - }); - it("should have 10 distance", () => { - const a = { - ids: "a", - }; - const b = { - ids: "a", - kinds: 1, - }; - expect(distance(a, b)).toEqual(10); - }); - it("should have 11 distance", () => { - const a = { - ids: "a", - }; - const b = { - ids: "b", - kinds: 1, - }; - expect(distance(a, b)).toEqual(11); - }); - it("should have 1 distance, arrays", () => { - const a = { - since: 1, - until: 100, - kinds: [1], - authors: ["kieran", "snort", "c", "d", "e"], - }; - const b = { - since: 1, - until: 100, - kinds: [6969], - authors: ["kieran", "snort", "c", "d", "e"], - }; - expect(distance(a, b)).toEqual(1); - }); - it("should have 1 distance, array change extra", () => { - const a = { - since: 1, - until: 100, - kinds: [1], - authors: ["f", "kieran", "snort", "c", "d"], - }; - const b = { - since: 1, - until: 100, - kinds: [1], - authors: ["kieran", "snort", "c", "d", "e"], - }; - expect(distance(a, b)).toEqual(1); - }); -}); - +import { NostrPrefix } from "../src/Links"; +import { parseNostrLink, tryParseNostrLink } from "../src/NostrLink"; describe("tryParseNostrLink", () => { it("is a valid nostr link", () => { diff --git a/packages/system/tsconfig.json b/packages/system/tsconfig.json index dca184be..b57c5cd3 100644 --- a/packages/system/tsconfig.json +++ b/packages/system/tsconfig.json @@ -9,10 +9,10 @@ "strict": true, "declaration": true, "declarationMap": true, - "sourceMap": true, + "inlineSourceMap": true, "outDir": "dist", "skipLibCheck": true }, - "include": ["src"], + "include": ["src/**/*.ts"], "files": ["src/index.ts"] } From 2ef0172de9fa2cc6447b92d0abb218d9f272e11f Mon Sep 17 00:00:00 2001 From: Kieran Date: Mon, 12 Jun 2023 14:22:33 +0100 Subject: [PATCH 22/24] fix build --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c5407974..d05a6602 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "packages/*" ], "scripts": { - "build": "yarn workspace @snort/nostr build && yarn workspace @snort/app build", - "start": "yarn workspace @snort/nostr build && yarn workspace @snort/app start", + "build": "yarn workspace @snort/system build && yarn workspace @snort/app build", + "start": "yarn workspace @snort/system build && yarn workspace @snort/app start", "test": "yarn workspace @snort/app test" }, "devDependencies": { From 7da4f350a693afae1f3e91be52b48cc157818994 Mon Sep 17 00:00:00 2001 From: Kieran Date: Mon, 12 Jun 2023 21:55:50 +0100 Subject: [PATCH 23/24] bugfix --- packages/app/src/Feed/TimelineFeed.ts | 2 +- packages/app/src/index.tsx | 2 +- packages/system/src/Utils.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/app/src/Feed/TimelineFeed.ts b/packages/app/src/Feed/TimelineFeed.ts index 9b8e439b..c08badba 100644 --- a/packages/app/src/Feed/TimelineFeed.ts +++ b/packages/app/src/Feed/TimelineFeed.ts @@ -133,7 +133,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel }, [options.relay]); const subNext = useMemo(() => { - const rb = new RequestBuilder(`timeline-related:${subject.type}`); + const rb = new RequestBuilder(`timeline-related:${subject.type}:${subject.discriminator}`); if (trackingEvents.length > 0) { rb.withFilter() .kinds( diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index e7f30e0a..b91d73f6 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -67,7 +67,7 @@ export const router = createBrowserRouter([ } for (const [k, v] of Object.entries(login.relays.item)) { - await System.ConnectToRelay(k, v); + System.ConnectToRelay(k, v); } try { if ("registerProtocolHandler" in window.navigator) { diff --git a/packages/system/src/Utils.ts b/packages/system/src/Utils.ts index 38c1938f..40651e49 100644 --- a/packages/system/src/Utils.ts +++ b/packages/system/src/Utils.ts @@ -73,12 +73,12 @@ export function reqFilterEq(a: FlatReqFilter | ReqFilter, b: FlatReqFilter | Req export function flatFilterEq(a: FlatReqFilter, b: FlatReqFilter): boolean { return a.ids === b.ids && a.kinds === b.kinds + && a["#e"] === b["#e"] && a.authors === b.authors && a.limit === b.limit && a.since === b.since && a.until === b.until && a.search === b.search - && a["#e"] === b["#e"] && a["#p"] === b["#p"] && a["#t"] === b["#t"] && a["#d"] === b["#d"] From e0218eff9a5de0a27f86c021460cd8323224f67e Mon Sep 17 00:00:00 2001 From: Kieran Date: Tue, 13 Jun 2023 10:07:59 +0100 Subject: [PATCH 24/24] bugfix tag --- packages/app/src/Pages/Root.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/Pages/Root.tsx b/packages/app/src/Pages/Root.tsx index 821470d7..7c854650 100644 --- a/packages/app/src/Pages/Root.tsx +++ b/packages/app/src/Pages/Root.tsx @@ -64,7 +64,7 @@ export default function RootPage() { switch (pTab) { case "conversations": { - return RootTab.NotesAndReplies; + return RootTab.Conversations; } case "global": { return RootTab.Global;