diff --git a/src/util/network.ts b/src/util/network.ts index 8764b60..884e869 100644 --- a/src/util/network.ts +++ b/src/util/network.ts @@ -2,7 +2,7 @@ import { modifyMutable, produce } from "solid-js/store"; import { UnsignedEvent, Event, Nostr, VerifiedEvent } from "nostr-tools/core"; import { normalizeURL as nostrNormalizeURL } from "nostr-tools/utils"; import { Filter, matchFilter } from "nostr-tools/filter"; -import { SubCloser, AbstractPoolConstructorOptions, SubscribeManyParams as SubscribeManyParamsDefault } from "nostr-tools/pool"; +import { SubCloser, SubscribeManyParams as SubscribeManyParamsDefault } from "nostr-tools/pool"; import { AbstractRelay as AbstractRelay, SubscriptionParams, Subscription, type AbstractRelayConstructorOptions } from "nostr-tools/abstract-relay"; import { getEventHash, verifyEvent } from "nostr-tools/pure"; import { Relay, RelayRecord } from "nostr-tools/relay"; @@ -41,7 +41,7 @@ class PrioritizedPool { private eventsCount: { [relay: string]: number; } = {}; private eventsTs: { [key: string]: number; } = {}; - constructor(opts: AbstractPoolConstructorOptions) { + constructor(opts: AbstractRelayConstructorOptions) { this.verifyEvent = opts.verifyEvent this._WebSocket = opts.websocketImplementation } @@ -53,15 +53,14 @@ class PrioritizedPool { verifyEvent: this.verifyEvent, websocketImplementation: this._WebSocket, }); - relay.connectionTimeout = SHORT_TIMEOUT; - relay._onauth = _ => { - if (store.readRelays.includes(url)) { - this.updateRelayInfo(url, { readAuth: true }); - } - }; this.relays.set(url, relay); } - await relay.connect() + // The connection timeout is now passed per connect() call, and relays + // that require NIP-42 AUTH to read are detected from the subscription + // close reason (see handleClose) — nostr-tools no longer exposes the + // connectionTimeout property or the private _onauth hook the previous + // detection relied on. + await relay.connect({ timeout: SHORT_TIMEOUT }) return relay } @@ -105,7 +104,7 @@ class PrioritizedPool { if (loggedIn) { const pk = signersStore.active!.pk; - externalRelays = await window.nostr!.getRelays(); + externalRelays = window.nostr!.getRelays ? await window.nostr!.getRelays() : {}; if (!relaysAreOk(externalRelays)) { const profileRelays = await find('profileRelays', IDBKeyRange.only(pk)); if (profileRelays) { diff --git a/src/util/nip27.ts b/src/util/nip27.ts new file mode 100644 index 0000000..eb54259 --- /dev/null +++ b/src/util/nip27.ts @@ -0,0 +1,50 @@ +// Minimal NIP-27 reference matcher/replacer. +// +// nostr-tools removed the `matchAll` / `replaceAll` helpers from its `nip27` +// module — current versions export only `parse`. ZapThreads still needs the +// old behaviour (find `nostr:`-prefixed NIP-19 references in text, then decode +// or replace them), so the small amount it used is vendored here. +// +// Behaviour mirrors nostr-tools' pre-removal `nip27`: it matches NIP-21 +// `nostr:` URIs only — bare entities are normalised to `nostr:` beforehand +// in `ui.ts` (see BAD_NIP27_REGEX). Decode failures are skipped rather than +// thrown, so a malformed reference can never break comment rendering. + +import { decode } from "nostr-tools/nip19"; + +export type Nip27Match = { + uri: string; // full match, e.g. "nostr:npub1..." + value: string; // the bech32 entity, e.g. "npub1..." + decoded: ReturnType; // nip19 decode result +}; + +// NIP-21 `nostr:` URI wrapping a bech32 entity. The bech32 body pattern +// mirrors nostr-tools' own BECH32_REGEX. +const BECH32 = /[\x21-\x7E]{1,83}1[023456789acdefghjklmnpqrstuvwxyz]{6,}/; + +// A fresh RegExp per call — global regexes carry `lastIndex` state. +const nostrUriRegex = () => new RegExp(`\\bnostr:(${BECH32.source})\\b`, "g"); + +export function* matchAll(content: string): Iterable { + for (const match of content.matchAll(nostrUriRegex())) { + try { + const [uri, value] = match; + yield { uri, value, decoded: decode(value) }; + } catch (_e) { + // not a valid NIP-19 entity — skip + } + } +} + +export function replaceAll( + content: string, + replacer: (match: Nip27Match) => string, +): string { + return content.replace(nostrUriRegex(), (uri, value) => { + try { + return replacer({ uri, value, decoded: decode(value) }); + } catch (_e) { + return uri; + } + }); +} diff --git a/src/util/ui.ts b/src/util/ui.ts index 255c4a3..e62665a 100644 --- a/src/util/ui.ts +++ b/src/util/ui.ts @@ -4,7 +4,7 @@ import { decode } from "nostr-tools/nip19"; import { Filter } from "nostr-tools/filter"; import { Event } from "nostr-tools/core"; import { ShortTextNote, Metadata, Highlights, Reaction, Zap, Report, CommunityDefinition } from "nostr-tools/kinds"; -import { matchAll, replaceAll } from "nostr-tools/nip27"; +import { matchAll, replaceAll } from "./nip27.ts"; import { Remarkable } from 'remarkable'; import { linkify } from 'remarkable/linkify'; import { findAll, save } from "./db.ts";