diff --git a/package.json b/package.json index 3a7e4be..cbace54 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "myrelay.site", "type": "module", - "version": "0.2.9", + "version": "0.2.10", "main": "index.js", "repository": "git@github.com:sandwichfarm/myrelay.site.git", "author": "dskvr ", diff --git a/src/lib/components/blocks/repeatable/feed/feed.svelte b/src/lib/components/blocks/repeatable/feed/feed.svelte index c138582..1c026ec 100644 --- a/src/lib/components/blocks/repeatable/feed/feed.svelte +++ b/src/lib/components/blocks/repeatable/feed/feed.svelte @@ -83,10 +83,10 @@ {:else} diff --git a/src/lib/components/blocks/unique/map/map.svelte b/src/lib/components/blocks/unique/map/map.svelte index 75370bf..22ddaeb 100644 --- a/src/lib/components/blocks/unique/map/map.svelte +++ b/src/lib/components/blocks/unique/map/map.svelte @@ -86,7 +86,7 @@ const setMonitor = (monitor: MRPMonitor) => { const dd = monitor.dd - const rtt = $MRP.nostr?.monitors?.metaEventsFor(monitor.pubkey)?.[0].rtt?.open || undefined + const rtt = $MRP.nostr?.monitors?.discoveryEventsFor(monitor.pubkey)?.[0]?.rttOpen || undefined const point: MapPoint = { id: monitor.geohash, @@ -98,12 +98,12 @@ brightness: 0.1 } - const link: MapLink = { + const link: MapLink = { source: $relayMapPoint.id || $relayMapPoint, target: point.id || point, color: 'black', cursor: 'crosshair' - } + } $monitorMapPoints.push(point) $monitorLinks.push(link) @@ -180,7 +180,8 @@ updateMapData() } - $MRP.signal.on('monitors:ready', setMapPoints) + $MRP?.$.signal.on('monitors:ready', setMapPoints) + onMount( () => { if($MRP.nostr.monitors.isComplete) { setMapPoints() diff --git a/src/lib/components/blocks/unique/map/monitors.svelte b/src/lib/components/blocks/unique/map/monitors.svelte index 88d8f71..7ad25b3 100644 --- a/src/lib/components/blocks/unique/map/monitors.svelte +++ b/src/lib/components/blocks/unique/map/monitors.svelte @@ -3,7 +3,9 @@ import { MY_RELAY_PAGE } from '$lib/contextKeys'; import { Badge } from "$lib/components/ui/badge/index.js"; import 'nostr-zap' - import type { MRPMonitors } from "$lib/core/monitors"; + // import type { MRPMonitors } from "$lib/core/monitors"; + import type { MyRelayPage } from '$lib/core/MRP'; + import type { Writable } from 'svelte/store'; export let key: string; export let mapChange: () => void @@ -12,9 +14,8 @@ export let setMonitors: () => void export let resetMonitors: () => void export let updateMapData: () => void - - const MRP = getContext(MY_RELAY_PAGE); + const MRP: Writable = getContext(MY_RELAY_PAGE); const hovered = (monitor: MRPMonitor | boolean): undefined => { if(monitorVis !== 'onhover') return diff --git a/src/lib/core/MRPMonitors.ts b/src/lib/core/MRPMonitors.ts index dbdfd4b..69af88d 100644 --- a/src/lib/core/MRPMonitors.ts +++ b/src/lib/core/MRPMonitors.ts @@ -1,13 +1,10 @@ import NDK, { NDKRelaySet, type NDKEvent, NDKRelay, type NDKUserProfile, NDKUser } from "@nostr-dev-kit/ndk"; import { RelayMonitor } from "./kinds/monitor"; -import { RelayMeta } from "./kinds/relay-meta"; import { RelayDiscovery } from "./kinds/relay-discovery"; -import type { RelayMetaParsed } from "./kinds/relay-meta"; import type { DD } from "./kinds/geocoded"; import type { MRPState } from "./MRP"; import { MRPData } from "./MRPData"; -type RelayMetaDictionary = Record type RelayDiscoveryDictionary = Record export class MRPMonitors extends MRPData { @@ -19,8 +16,7 @@ export class MRPMonitors extends MRPData { private relaySet: NDKRelaySet; private _monitors: Set = new Set(); private _monitorEvents: Set = new Set(); - private _relayMeta: RelayMetaDictionary = {}; - private _relayDiscovery: RelayDiscoveryDictionary = {}; + public _relayDiscovery: RelayDiscoveryDictionary = {}; private _operators: Record = {}; private _url: string; @@ -34,16 +30,8 @@ export class MRPMonitors extends MRPData { async init(){ this.begin() - const metaEvents = await this.$.ndk.fetchEvents({ kinds: [30066], "#d": [new URL(this._url).toString()], since: this.livenessThreshold }, null, this.relaySet) - const dedupedMetaEvents = new Set(Array.from(metaEvents).map((event: NDKEvent) => new RelayMeta(this.$.ndk, event.rawEvent()))) - Array.from(dedupedMetaEvents).forEach((event: RelayMeta) => { - if(!this._relayMeta[event.pubkey]){ - this._relayMeta[event.pubkey] = [] - } - this._relayMeta[event.pubkey].push(event) - }) - const discoveryEvents = await this.$.ndk.fetchEvents({ kinds: [30166], "#d": [new URL(this._url).toString()], since: this.livenessThreshold }, null, this.relaySet).catch( err => this.error(err) ) + const discoveryEvents = await this?.$?.ndk?.fetchEvents({ kinds: [30166], "#d": [new URL(this._url).toString()], since: this.livenessThreshold }, null, this.relaySet).catch( err => this.error(err) ) const dedupedDiscoveryEvents = new Set(Array.from(discoveryEvents).map((event: NDKEvent) => new RelayDiscovery(this.$.ndk, event.rawEvent()))) Array.from(dedupedDiscoveryEvents).forEach((event: RelayDiscovery) => { if(!this._relayDiscovery[event.pubkey]){ @@ -52,7 +40,7 @@ export class MRPMonitors extends MRPData { this._relayDiscovery[event.pubkey].push(event) }) - const authors = Array.from(new Set(this.getAllRelayMeta().map((event: NDKEvent) => event.pubkey) )) + const authors = Array.from(new Set(this.getAllRelayDiscovery().map((event: NDKEvent) => event.pubkey) )) const monitorEvents: Set | void = await this.$.ndk.fetchEvents({ kinds: [10166], authors }, null, this.relaySet).catch( err => this.error(err) ) @@ -76,21 +64,12 @@ export class MRPMonitors extends MRPData { if(!this.isError) this.complete(true) } - getRtt(): RelayMetaParsed | undefined { - let rtt: RelayMetaParsed | undefined; - for (const event of this.getAllRelayMeta()) { - if(rtt) break - rtt = event?.rtt - } - return rtt - } - getRelayDD() { let dd: DD | undefined; for (const event of this.getAllRelayDiscovery()) { - if(dd) break if(!event?.dd) continue dd = event.dd + if(dd) break } return dd } @@ -106,10 +85,6 @@ export class MRPMonitors extends MRPData { return dd } - metaEventsFor(pubkey: string): RelayMeta[] | undefined { - return this._relayMeta[pubkey] - } - discoveryEventsFor(pubkey: string): RelayDiscovery[] | undefined { return this._relayDiscovery[pubkey] } @@ -118,14 +93,6 @@ export class MRPMonitors extends MRPData { return this._monitors.size } - getAllRelayMeta(): RelayMeta[] { - let result: RelayMeta[] = [] - Object.keys(this._relayMeta).forEach((key) => { - result = result.concat(Array.from(this._relayMeta[key])) - }) - return result - } - getAllRelayDiscovery(): RelayDiscovery[] { let result: RelayDiscovery[] = [] Object.keys(this._relayDiscovery).forEach((key) => { @@ -134,10 +101,6 @@ export class MRPMonitors extends MRPData { return result } - countReports(): number { - return this.getAllRelayMeta().length - } - get operators(): Record { return this._operators } @@ -150,9 +113,6 @@ export class MRPMonitors extends MRPData { return this._monitors } - get relayMetaEvents(): RelayMetaDictionary { - return this._relayMeta - } get relayDiscovery(): RelayDiscoveryDictionary { return this._relayDiscovery diff --git a/src/lib/core/MRPNostr.ts b/src/lib/core/MRPNostr.ts index 6e27703..eb03d16 100644 --- a/src/lib/core/MRPNostr.ts +++ b/src/lib/core/MRPNostr.ts @@ -37,6 +37,7 @@ export class MRPNostr { } public async init(){ + await this.ndk.connect(); await this._relay.init() this.monitors.init() } @@ -46,15 +47,15 @@ export class MRPNostr { const user = await this.signer.user(); this.user = new MRPUser(this.$, user, 'currentUser'); await this.user.init(); - await this.ndk.connect(); + this._authed = true; - this.ndk.emit('mrp:login') + this.$.signal.emit('mrp:login') } public logout(){ this.user = undefined this._authed = false - this.ndk.emit('mrp:logout') + this.$.signal.emit('mrp:logout') } async toggleRelay(relay: string){ @@ -92,8 +93,6 @@ export class MRPNostr { return this._authed } - - get monitors(): MRPMonitors { return this._monitors } @@ -150,15 +149,4 @@ export class MRPNostr { this._user = user } - // public async sign (kind: NDKKind, event: NDKEvent | NostrEvent ){ - // if(event as NostrEvent){ - // return this.ndk.sign(kind, event as NostrEvent) - // } - // return this.ndk.sign(kind, event as NDKEvent) - // } - - // public async publish ( event: NDKEvent ) { - // return this.ndk.publish(event as NDKEvent) - // } - } \ No newline at end of file diff --git a/src/lib/core/kinds/app-config.ts b/src/lib/core/kinds/app-config.ts index 9b5075a..6c4511c 100644 --- a/src/lib/core/kinds/app-config.ts +++ b/src/lib/core/kinds/app-config.ts @@ -141,7 +141,7 @@ export class AppConfig extends NDKEvent { } configDiffersFromDefault(): boolean { - console.log(this.configHash, this._configDefaultHash, this.configHash !== this._configDefaultHash) + // console.log(this.configHash, this._configDefaultHash, this.configHash !== this._configDefaultHash) return this.configHash !== this._configDefaultHash; } diff --git a/src/lib/core/kinds/geocoded.ts b/src/lib/core/kinds/geocoded.ts index 44085e0..f092202 100644 --- a/src/lib/core/kinds/geocoded.ts +++ b/src/lib/core/kinds/geocoded.ts @@ -40,7 +40,7 @@ export class NDKEventGeoCoded extends NDKEvent { private static readonly EARTH_RADIUS: number = 6371; // km private static readonly GEOHASH_PRECISION: number = 12; private static readonly BASE32: string = '0123456789bcdefghjkmnpqrstuvwxyz'; - private static readonly geohashFilterFn = (tag: NDKTag) => tag[0] === 'g' && (tag[2] === "gh" || tag[2] === "geohash" || tag.length === 2); //`g` tags with a length of 2 are NIP-52 geohashes + private static readonly geohashFilterFn = (tag: NDKTag) => tag[0] === 'g' && (tag.length === 2 || tag['2'] === 'gh' || tag['2'] === 'geohash'); //`g` tags with a length of 2 are NIP-52 geohashes private _dd: DD | undefined; @@ -181,18 +181,18 @@ export class NDKEventGeoCoded extends NDKEvent { } get countryCode(): string[] | undefined { - return this.tagValuesByMarker("g", "countryCode"); + return this.tagValuesByMarker("l", "countryCode"); } set countryCode(values: string[]) { - this.removeTagByMarker("g", "countryCode"); + this.removeTagByMarker("l", "countryCode"); values.forEach(value => { - this._setGeoTag("countryCode", value); + this.addTag("l", value, "countryCode"); }); } get countryName(): string | undefined { - return this.tagValueByMarker("g", "countryName"); + return this.tagValueByMarker("l", "countryName"); } set countryName(value: string) { @@ -200,7 +200,7 @@ export class NDKEventGeoCoded extends NDKEvent { } get regionCode(): string | undefined { - return this.tagValueByMarker("g", "regionCode"); + return this.tagValueByMarker("l", "regionCode"); } set regionCode(value: string) { @@ -212,26 +212,27 @@ export class NDKEventGeoCoded extends NDKEvent { } get regionName(): string | undefined { - return this.tagValueByMarker("g", "regionName"); + return this.tagValueByMarker("l", "regionName"); } set continentName(value: string) { + this._setGeoTag("continentName", value); } get continentName(): string | undefined { - return this.tagValueByMarker("g", "continentName"); + return this.tagValueByMarker("l", "continentName"); } - get geo(): NDKTag[] { - return [...this.getMatchingTags("G"), ...this.getMatchingTags("g")]; - } + // get geo(): NDKTag[] { + // return [...this.getMatchingTags("G"), ...this.getMatchingTags("g")]; + // } - set geo(tags: NDKTag[]) { - this.removeTag("G"); - this.removeTag("g"); - tags.forEach(tag => this.tags.push(tag)); - } + // set geo(tags: NDKTag[]) { + // this.removeTag("G"); + // this.removeTag("g"); + // tags.forEach(tag => this.tags.push(tag)); + // } private set lat(value: number) { if(!this._dd) this._dd = { lat: 0, lon: 0 } as DD; diff --git a/src/lib/core/kinds/monitor.ts b/src/lib/core/kinds/monitor.ts index f24f294..fca63e6 100644 --- a/src/lib/core/kinds/monitor.ts +++ b/src/lib/core/kinds/monitor.ts @@ -5,13 +5,11 @@ import type { NostrEvent, NDKFilter, NDKUserProfile } from "@nostr-dev-kit/ndk"; import { NDKEventGeoCoded } from "./geocoded.js"; import type { FetchNearbyRelayOptions } from "./geocoded.js"; -import type { RelayMeta } from "./relay-meta.js"; import type { RelayDiscovery, RelayDiscoveryFilters } from "./relay-discovery.js"; export type RelayListSet = Set | undefined export type RelayMonitorSet = Set | undefined export type RelayDiscoveryResult = Set | undefined -export type RelayMetaSet = Set | undefined export type RelayMonitorCriterias = { kinds: number[], @@ -242,9 +240,6 @@ export class RelayMonitor extends NDKEventGeoCoded { if(this.kinds.includes(NDKKind.RelayDiscovery)) { kinds.push(NDKKind.RelayDiscovery); } - if(this.kinds.includes(NDKKind.RelayMeta)) { - kinds.push(NDKKind.RelayMeta); - } const filter: NDKFilter = this._nip66Filter(kinds, { limit: 1 } as NDKFilter); @@ -307,11 +302,8 @@ export class RelayMonitor extends NDKEventGeoCoded { */ async fetchOnlineRelays( filter?: NDKFilter ): Promise { this._maybeWarnInvalid(); - if( ![NDKKind.RelayMeta, NDKKind.RelayDiscovery].some(value => this.kinds.includes(value)) ) { - return this._invalidRelayFetch(`RelayMonitor.fetchOnlineRelays()`, `${this.pubkey} does not publish kind ${NDKKind.RelayMeta} or ${NDKKind.RelayDiscovery}`); - } - const kinds: NDKKind[] = [this.kinds.includes(NDKKind.RelayDiscovery )? NDKKind.RelayDiscovery: NDKKind.RelayMeta]; + const kinds: NDKKind[] = [ NDKKind.RelayDiscovery ]; const _filter: NDKFilter = this._nip66Filter(kinds, filter); return new Promise((resolve, reject) => { @@ -335,11 +327,8 @@ export class RelayMonitor extends NDKEventGeoCoded { */ async fetchOnlineRelaysBy( indexedTags: RelayDiscoveryFilters, filter?: NDKFilter ): Promise { this._maybeWarnInvalid(); - if( ![NDKKind.RelayMeta, NDKKind.RelayDiscovery].some(value => this.kinds.includes(value)) ) { - return this._invalidRelayFetch(`RelayMonitor.fetchOnlineRelaysBy()`, `${this.pubkey} does not publish kind ${NDKKind.RelayMeta} or ${NDKKind.RelayDiscovery}`); - } - const kinds = [this.kinds.includes(NDKKind.RelayDiscovery )? NDKKind.RelayDiscovery: NDKKind.RelayMeta]; + const kinds = [NDKKind.RelayDiscovery]; const _filter: NDKFilter = this._nip66Filter(kinds, filter, indexedTags as NDKFilter); return new Promise((resolve, reject ) => { @@ -351,58 +340,28 @@ export class RelayMonitor extends NDKEventGeoCoded { }); } - /** - * Fetches metadata for a specific relay or relays. - * - * @param {string[] | string} relays A string or array of strings representing the relay(s) to fetch metadata for. - * @returns A promise that resolves to the `RelayMetasResult` object(s) - * - * @public - * @async - */ - async fetchRelayMeta( relays: string[] | string ): Promise { - this._maybeWarnInvalid(); - if( !this.kinds.includes(NDKKind.RelayMeta) ) { - return this._invalidRelayFetch(`RelayMonitor.fetchRelayMetaData()`, `${this.pubkey} does not publish kind ${NDKKind.RelayMeta}`); - } - - if(!Array.isArray(relays)) { - relays = [relays]; - } - const kinds: NDKKind[] = [NDKKind.RelayMeta]; - const filter: NDKFilter = this._nip66Filter(kinds, undefined, { "#d": relays } as NDKFilter); - - return new Promise((resolve, reject) => { - this.ndk?.fetchEvents(filter) - .then((events: Set) => { - resolve(new Set(Array.from(events)) as RelayMetaSet); - }) - .catch(reject); - }); - } - /** * Fetches metadata for online relays, optionally applying an additional filter. * * @param {NDKFilter} filter An optional `NDKFilter` object to apply additional filtering criteria. - * @returns A promise that resolves to a `RelayMetaSet` or undefined if the operation fails. + * @returns A promise that resolves to a `Set` or undefined if the operation fails. * * @public * @async */ - async fetchOnlineRelaysMeta( filter?: NDKFilter ): Promise { + async fetchOnlineDiscovery( filter?: NDKFilter ): Promise> { this._maybeWarnInvalid(); - if( !this.kinds.includes(NDKKind.RelayMeta) ) { - return this._invalidRelayFetch(`RelayMonitor.fetchOnlineRelaysMeta()`, `${this.pubkey} does not publish kind ${NDKKind.RelayMeta}`); + if( !this.kinds.includes(NDKKind.RelayDiscovery) ) { + return this._invalidRelayFetch(`RelayMonitor.fetchOnlineRelaysMeta()`, `${this.pubkey} does not publish kind ${NDKKind.RelayDiscovery}`); } - const kinds: NDKKind[] = [NDKKind.RelayMeta]; + const kinds: NDKKind[] = [NDKKind.RelayDiscovery]; const _filter: NDKFilter = this._nip66Filter(kinds, filter); return new Promise((resolve, reject) => { this.ndk?.fetchEvents(_filter) .then((events: Set) => { - resolve(new Set(Array.from(events) as RelayMeta[])); + resolve(new Set(Array.from(events) as RelayDiscovery[])); }) .catch(reject); }); @@ -413,18 +372,18 @@ export class RelayMonitor extends NDKEventGeoCoded { * * @param {RelayDiscoveryFilters} indexedTags A `RelayDiscoveryTags` value representing the tag to filter by. * @param {NDKFilter} filter A string or array of strings representing the key(s) to filter by. - * @returns Promise resolves to an array of `RelayMeta` objects. + * @returns Promise resolves to an array of `RelayDiscovery` objects. * * @public * @async */ - async fetchOnlineRelaysMetaBy( indexedTags: RelayDiscoveryFilters, filter?: NDKFilter ): Promise { + async fetchOnlineRelaysDiscoveryBy( indexedTags: RelayDiscoveryFilters, filter?: NDKFilter ): Promise> { this._maybeWarnInvalid(); - const _filter = this._nip66Filter([NDKKind.RelayMeta], filter, indexedTags as NDKFilter); + const _filter = this._nip66Filter([NDKKind.RelayDiscovery], filter, indexedTags as NDKFilter); return new Promise((resolve, reject) => { - this.fetchOnlineRelaysMeta(_filter) + this.fetchOnlineDiscovery(_filter) .then( ( events ) => { resolve( events ); }) @@ -436,7 +395,7 @@ export class RelayMonitor extends NDKEventGeoCoded { * Fetches relay discovery events for online relays, optionally applying an additional filter. * * @param {NDKFilter} filter An optional `NDKFilter` object to apply additional filtering criteria. - * @returns Promise resolves to a `RelayMetaSet` or undefined if the operation fails. + * @returns Promise resolves to a `Set` or undefined if the operation fails. * * @public * @async @@ -444,7 +403,7 @@ export class RelayMonitor extends NDKEventGeoCoded { async fetchOnlineRelaysDiscovery( filter?: NDKFilter ): Promise { this._maybeWarnInvalid(); if( !this.kinds.includes(NDKKind.RelayDiscovery) ) { - return this._invalidRelayFetch(`RelayMonitor.fetchOnlineRelaysMeta()`, `${this.pubkey} does not publish kind ${NDKKind.RelayMeta}`); + return this._invalidRelayFetch(`RelayMonitor.fetchOnlineRelaysMeta()`, `${this.pubkey} does not publish kind ${NDKKind.RelayDiscovery}`); } const kinds: NDKKind[] = [NDKKind.RelayDiscovery]; @@ -494,12 +453,12 @@ export class RelayMonitor extends NDKEventGeoCoded { /** * Reduces a set of `NDKEvent` objects to a list of relay strings. * - * @param {Set} events A set of `NDKEvent` objects. + * @param {Set} events A set of `NDKEvent` objects. * @returns Promise resolves to a list of relay strings or undefined. * * @private */ - private _reduceRelayEventsToRelayStrings( events: Set ): RelayListSet { + private _reduceRelayEventsToRelayStrings( events: Set ): RelayListSet { if(typeof events === 'undefined') { return new Set() as RelayListSet; } diff --git a/src/lib/core/kinds/relay-discovery.ts b/src/lib/core/kinds/relay-discovery.ts index 43040ae..c148ae7 100644 --- a/src/lib/core/kinds/relay-discovery.ts +++ b/src/lib/core/kinds/relay-discovery.ts @@ -46,6 +46,24 @@ export class RelayDiscovery extends NDKEventGeoCoded { return new RelayDiscovery(event.ndk, event.rawEvent()); } + get rttOpen(): number | undefined { + const rtt = this.tags.find(tag => tag[0] === 'rtt-open')?.[1] + if(!rtt) return + return parseInt(rtt); + } + + get rttRead(): number | undefined { + const rtt = this.tags.find(tag => tag[0] === 'rtt-read')?.[1] + if(!rtt) return + return parseInt(rtt); + } + + get rttWrite(): number | undefined { + const rtt = this.tags.find(tag => tag[0] === 'rtt-write')?.[1] + if(!rtt) return + return parseInt(rtt); + } + get url(): string | undefined { return this.tagValue("d"); } diff --git a/src/lib/core/kinds/relay-meta.ts b/src/lib/core/kinds/relay-meta.ts deleted file mode 100644 index 29cd1fc..0000000 --- a/src/lib/core/kinds/relay-meta.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; -import type NDK from "@nostr-dev-kit/ndk"; -import type {NostrEvent} from "@nostr-dev-kit/ndk"; - -export type RelayMetaParsed = Record | undefined -export type RelayMetaParsedAll = Record - -export type RelayMetaData = RelayMetaDataArray | RelayMetaDataValue -export type RelayMetaDataArray = RelayMetaDataValue[] -export type RelayMetaDataValue = number | string | boolean | undefined - -const REGEX_IPV4: RegExp = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/; -const REGEX_GEOHASH: RegExp = /^[0-9b-hjkmnp-z]{1,12}$/; - -/** - * RelayMeta [NIP-66] - * - * Supports the following tags: - * - d: url - * - other: { [key: string]: RelayMetaData } - * - rtt: { [key: string]: number } - * - nip11: { [key: string]: RelayMetaData } - * - dns: { [key: string]: RelayMetaData } - * - geo: { [key: string]: RelayMetaData } - * - ssl: { [key: string]: RelayMetaData } - * - * @author sandwichfarm - * @summary NIP-66 RelayMeta - * @extends NDKEvent - */ -export class RelayMeta extends NDKEvent { - static readonly groups = ['other', 'rtt', 'nip11', 'dns', 'geo', 'ssl', 'counts']; - - constructor(ndk: NDK | undefined, rawEvent?: NostrEvent) { - super(ndk, rawEvent); - this.kind ??= 30066; - } - - static from(event: NDKEvent): RelayMeta { - return new RelayMeta(event.ndk, event.rawEvent()); - } - - get url(): string | undefined { - return this.tagValue("d"); - } - - set url(value: string | undefined) { - this.removeTag("d"); - if (value) { - this.tags.push(["d", value]); - } - } - - get other(): RelayMetaParsed { - return this.getGroupedTag("other"); - } - - set other(values: { [key: string]: RelayMetaData } ) { - this.setGroupedTag("other", values); - } - - get rtt(): RelayMetaParsed { - return this.getGroupedTag("rtt"); - } - - set rtt(values: { [key: string]: number } ) { - this.setGroupedTag("rtt", values); - } - - get nip11(): RelayMetaParsed { - return this.getGroupedTag("nip11"); - } - - set nip11(values: { [key: string]: RelayMetaData } ) { - this.setGroupedTag("nip11", values); - } - - get dns(): RelayMetaParsed { - return this.getGroupedTag("dns"); - } - - set dns(values: { [key: string]: RelayMetaData } ) { - this.setGroupedTag("dns", values); - } - - get geo(): RelayMetaParsed { - return this.getGroupedTag("geo"); - } - - set geo(values: { [key: string]: RelayMetaData } ) { - this.setGroupedTag("geo", values); - } - - get ssl(): RelayMetaParsed { - return this.getGroupedTag("ssl"); - } - - set ssl(values: { [key: string]: RelayMetaData } ) { - this.setGroupedTag("ssl", values); - } - - get counts(): RelayMetaParsed { - return this.getGroupedTag("counts"); - } - - set counts(values: { [key: string]: RelayMetaData } ) { - this.setGroupedTag("counts", values); - } - - /** - * Retrieves all metadata grouped by their respective key. - * - * @returns An object where each key is a metadata category (e.g., 'rtt', 'nip11', 'dns', 'geo', 'ssl', 'counts'), and the value is the parsed metadata for that category. - */ - get all(): RelayMetaParsedAll { - const result: RelayMetaParsedAll = {}; - RelayMeta.groups.forEach(group => { - const data = this.getGroupedTag(group); - if(data){ - result[group] = data; - } - }); - return result; - } - - /** - * Groups tags by a specific group and parses their values. - * - * @param group The key sto group tags by. - * @returns An object where each key is a tag within the specified group and its value is either a single metadata value or an array of metadata values, all cast to their appropriate types. - * - * @private - */ - private getGroupedTag( group: string ): RelayMetaParsed { - const tags = this.tags.filter(tag => tag[0] === group); - const data: RelayMetaParsed = {}; - - tags.forEach(_tag => { - const tag = [..._tag]; - tag.shift(); - const [key, ...values] = tag; - if (!data?.[key]) { - data[key] = values.length === 1 - ? this.castValue(values[0]) - : values.map((v) => this.castValue(v)) as RelayMetaDataArray; - } - else { - data[key] = [ ...values.map((v) => this.castValue(v, key)) ]; - } - }); - Object.keys(data).forEach( key => { - if(typeof data[key] === 'undefined'){ - delete data[key]; - } - }); - return data; - } - - /** - * Sets or updates tags grouped by a specific group with provided values. - * - * @param group The category of the tags to set or update. - * @param values An object where keys represent the tag names within the group, and the values are the data to be set for those tags. - * - * @private - */ - private setGroupedTag(group: string, values: { [key: string]: RelayMetaData }) { - this.removeTag(group); // Assuming this removes all tags starting with `group` - Object.entries(values).forEach(([key, value]) => { - if (Array.isArray(value)) { - value.forEach(val => this.tags.push([group, key, String(val)])); - } else { - this.tags.push([group, key, String(value)]); - } - }); - } - - /** - * Casts a tag value to its appropriate type based on its content and optionally a key. - * - * @param value The string value of the tag to cast. - * @param key Optionally, the key associated with the value, which may influence the casting (e.g., 'geohash'). - * @returns The value cast to either a boolean, number, or string, or left as undefined if it cannot be reliably cast. - * - * @private - */ - private castValue( value: string, key?: string ): RelayMetaDataValue { - if (value.toLowerCase() === "true") { - return true; - } - if (value.toLowerCase() === "false") { - return false; - } - //don't cast IP as float - if (REGEX_IPV4.test(value)) { - return value; - } - const maybeGeohash = REGEX_GEOHASH.test(value); - if ( key === 'geohash' ){ - if( !maybeGeohash ){ - return undefined; - } - return value; - } - const asFloat = parseFloat(value); - if (!isNaN(asFloat) && isFinite(asFloat) && String(asFloat) === value) { - return asFloat; - } - return value; - } -} \ No newline at end of file