From 3bf97740acceb6711cff10468487e9c6e46adecd Mon Sep 17 00:00:00 2001 From: Sandwich <299465+dskvr@users.noreply.github.com> Date: Fri, 24 Jan 2025 08:40:26 +0700 Subject: [PATCH] add more data to aggregates --- .../components/lists/table/DataTable.svelte | 2 +- .../lib/components/partials/Debugger.svelte | 6 +- .../components/partials/MonitorActions.svelte | 7 +- apps/gui/src/lib/config/dataTable/geocodes.ts | 9 +- apps/gui/src/lib/config/dataTable/isps.ts | 4 +- apps/gui/src/lib/config/dataTable/monitors.ts | 25 -- .../gui/src/lib/config/dataTable/operators.ts | 14 +- apps/gui/src/lib/stores/geocodes.ts | 26 +- apps/gui/src/lib/stores/isps.ts | 30 ++- apps/gui/src/lib/utils/cache.ts | 57 ++++ apps/gui/src/lib/utils/lifecycle.ts | 3 +- apps/gui/src/routes/+layout.svelte | 6 +- apps/gui/src/routes/monitors/+page.svelte | 4 +- apps/gui/src/routes/operators/+page.svelte | 2 + apps/gui/src/routes/preferences/+page.svelte | 2 +- apps/gui/src/routes/relays/+page.svelte | 2 +- apps/nocapd/package.json | 2 +- apps/nocapd/src/daemon.js | 4 +- internal/announce/package.json | 4 +- internal/announce/src/index.ts | 14 +- internal/publisher/src/Publisher.test.ts | 1 - internal/publisher/src/Publisher.ts | 8 +- internal/publisher/src/kinds/Kind10166.ts | 11 +- libraries/nocap-nip66/README.md | 0 libraries/nocap-nip66/package.json | 53 ---- libraries/nocap-nip66/src/Transform.test.js | 172 ------------ libraries/nocap-nip66/src/Transform.ts | 71 ----- libraries/nocap-nip66/src/index.ts | 2 - libraries/nocap-nip66/src/kinds/0.ts | 65 ----- libraries/nocap-nip66/src/kinds/10166.ts | 120 --------- libraries/nocap-nip66/src/kinds/30166.test.js | 205 -------------- libraries/nocap-nip66/src/kinds/30166.ts | 249 ------------------ libraries/nocap-nip66/src/kinds/index.ts | 1 - libraries/nocap-nip66/src/types/global.d.ts | 16 -- libraries/nocap-nip66/tsconfig.json | 20 -- libraries/nocap-nip66/webpack.config.js | 64 ----- libraries/nocap/package.json | 7 +- pnpm-lock.yaml | 17 +- 38 files changed, 183 insertions(+), 1122 deletions(-) delete mode 100644 libraries/nocap-nip66/README.md delete mode 100644 libraries/nocap-nip66/package.json delete mode 100644 libraries/nocap-nip66/src/Transform.test.js delete mode 100644 libraries/nocap-nip66/src/Transform.ts delete mode 100644 libraries/nocap-nip66/src/index.ts delete mode 100644 libraries/nocap-nip66/src/kinds/0.ts delete mode 100644 libraries/nocap-nip66/src/kinds/10166.ts delete mode 100644 libraries/nocap-nip66/src/kinds/30166.test.js delete mode 100644 libraries/nocap-nip66/src/kinds/30166.ts delete mode 100644 libraries/nocap-nip66/src/kinds/index.ts delete mode 100644 libraries/nocap-nip66/src/types/global.d.ts delete mode 100644 libraries/nocap-nip66/tsconfig.json delete mode 100644 libraries/nocap-nip66/webpack.config.js diff --git a/apps/gui/src/lib/components/lists/table/DataTable.svelte b/apps/gui/src/lib/components/lists/table/DataTable.svelte index d4490b2e..b94d8106 100644 --- a/apps/gui/src/lib/components/lists/table/DataTable.svelte +++ b/apps/gui/src/lib/components/lists/table/DataTable.svelte @@ -201,7 +201,7 @@ } - + diff --git a/apps/gui/src/lib/components/partials/Debugger.svelte b/apps/gui/src/lib/components/partials/Debugger.svelte index 6a9a7726..948485ad 100644 --- a/apps/gui/src/lib/components/partials/Debugger.svelte +++ b/apps/gui/src/lib/components/partials/Debugger.svelte @@ -10,6 +10,8 @@ import { route66 } from "$lib/stores"; import { Value } from "svelte-radix"; import { shouldSync as _shouldSync } from "$lib/stores/app"; + import { eventsStoreMemoryRelay } from "$lib/stores/memory-relays/memory-relay-events"; + import { calculateSize, type ObjectSizeType } from "$lib/utils/cache"; const debug: Writable> = writable(new Map()); @@ -104,10 +106,12 @@ const debugStores = () => { const eventKeys = Array.from($events?.keys?.()) ?? []; const eventsArray = Array.from(eventKeys); + const measure: ObjectSizeType = calculateSize($events) addDebug('store:events', eventKeys?.length || 0); + addDebug('store:events:size', `${measure.size.toFixed(2)}${measure.unit}`); - [0,1,3,10002,10166,30166].forEach( kind => { + [0,1,3,10002,10166, 30166].forEach( kind => { addDebug(`store:events:${kind}`, eventsArray.filter( (key: string) => { const parts = key.split(':'); return parts[1] === kind.toString(); diff --git a/apps/gui/src/lib/components/partials/MonitorActions.svelte b/apps/gui/src/lib/components/partials/MonitorActions.svelte index a24f3679..881844df 100644 --- a/apps/gui/src/lib/components/partials/MonitorActions.svelte +++ b/apps/gui/src/lib/components/partials/MonitorActions.svelte @@ -30,9 +30,9 @@ onMount(() => { toggleEnableMonitor = async () => { + disabled.set(true); const { publishEventsToMemoryRelay } = await import('$lib/stores/events-helpers.js'); const resumer = await pauseLiveSync(); - disabled.set(true); if(monitor?.enabled) { monitor.disable(); ([...$eventsArray] as IEvent[]).filter( event => event.pubkey === monitor.pubkey).forEach( event => { @@ -40,9 +40,8 @@ $events.delete(key) }); events.set($events); - disabled.set(false); await resumer(); - + disabled.set(false); } else { monitor.enable() @@ -62,8 +61,8 @@ publishEventsToMemoryRelay(events) } await $route66?.services?.monitors?.sync(options, { onevents }) - disabled.set(false); await resumer(); + disabled.set(false); } $route66?.services?.monitors?.manager?.updateMonitor?.(monitor) diff --git a/apps/gui/src/lib/config/dataTable/geocodes.ts b/apps/gui/src/lib/config/dataTable/geocodes.ts index d4d2bea2..99679ea9 100644 --- a/apps/gui/src/lib/config/dataTable/geocodes.ts +++ b/apps/gui/src/lib/config/dataTable/geocodes.ts @@ -6,8 +6,8 @@ import { getCountryName } from '$lib/stores/iso3166'; export const columnsDisable: DataKeys = ['id'] export const filtersDisable: DataKeys = [] -export const columnsShow: DataKeys = ['geocode', 'count', 'percent'] -export const filtersShow: DataKeys = [] +export const columnsShow: DataKeys = ['geocode', 'count', 'percent', 'softwaresCount'] +export const filtersShow: DataKeys = ['count', 'softwaresCount', 'softwares'] export const humanReadableNames: NameFormatter = {}; @@ -46,6 +46,11 @@ export const filterFormatters: Formatters = { if(typeof software !== 'string') return '-'; software = makeSoftwareReadable(software); return truncateWithEllipsis(software, 33); + }, + softwares: (software: string) => { + if(typeof software !== 'string') return '-'; + software = makeSoftwareReadable(software); + return truncateWithEllipsis(software, 33); } } diff --git a/apps/gui/src/lib/config/dataTable/isps.ts b/apps/gui/src/lib/config/dataTable/isps.ts index b1e14392..48924463 100644 --- a/apps/gui/src/lib/config/dataTable/isps.ts +++ b/apps/gui/src/lib/config/dataTable/isps.ts @@ -6,8 +6,8 @@ import { getCountryName } from '$lib/stores/iso3166'; export const columnsDisable: DataKeys = ['id'] export const filtersDisable: DataKeys = [] -export const columnsShow: DataKeys = ['prettyName', 'asname', 'as', 'count', 'percent'] -export const filtersShow: DataKeys = [] +export const columnsShow: DataKeys = ['prettyName', 'asname', 'as', 'count', 'percent', 'softwaresCount'] +export const filtersShow: DataKeys = ['softwares'] export const humanReadableNames: NameFormatter = {}; diff --git a/apps/gui/src/lib/config/dataTable/monitors.ts b/apps/gui/src/lib/config/dataTable/monitors.ts index 54f8280e..9657e12c 100644 --- a/apps/gui/src/lib/config/dataTable/monitors.ts +++ b/apps/gui/src/lib/config/dataTable/monitors.ts @@ -1,4 +1,3 @@ -import { relaySpeedGroupResolver, SpeedGroupBars, SpeedGroupColors, SpeedGroups } from '$lib/stores/checks.js'; import { inactiveDisabledMonitorChecksCount, monitors, monitorsMap } from '$lib/stores/monitors.js'; import type { Monitor } from "@nostrwatch/route66/models" import { PFP } from '$lib/utils/pfp.js'; @@ -7,30 +6,6 @@ import { formatSeconds, timeAgo } from '$lib/utils/time.js'; import { validNip05s } from '$lib/stores/nip05s.js'; import { activeMonitorChecksCount } from '$lib/stores'; -type Resolver = (input: any) => any - -class SpeedGroupResolver { - private resolver: Resolver = () => SpeedGroups.Mid; - private unsubscribe: () => void; - - constructor() { - this.unsubscribe = relaySpeedGroupResolver.subscribe((fn: Resolver) => { - this.resolver = fn; - }); - } - - resolve(input: number): SpeedGroups { - return this.resolver(input); - } - - dispose() { - this.unsubscribe(); - } -} - -const speedGroupResolver = new SpeedGroupResolver(); - - export type NameFormatter = Record; export type Formatters = Record; diff --git a/apps/gui/src/lib/config/dataTable/operators.ts b/apps/gui/src/lib/config/dataTable/operators.ts index 1e8b25e8..b51ecf39 100644 --- a/apps/gui/src/lib/config/dataTable/operators.ts +++ b/apps/gui/src/lib/config/dataTable/operators.ts @@ -1,11 +1,12 @@ import type { DataKeys, Formatters, NameFormatter } from '$lib/components/lists/table/DataTableTypes'; import { makeSoftwareReadable } from '$lib/synonyms/software'; -export const columnsDisable: DataKeys = [] -export const filtersDisable: DataKeys = [] export const columnsShow: DataKeys = ['name', 'about', 'reference', 'relaysCount', 'softwaresCount', 'ispsCount'] -export const filtersShow: DataKeys = [] +export const filtersShow: DataKeys = ['softwares', 'isps'] + +export const columnsDisable: DataKeys = [] +export const filtersDisable: DataKeys = [] export const humanReadableNames: NameFormatter = {}; @@ -56,7 +57,12 @@ export const filterFormatters: Formatters = { if(typeof software !== 'string') return '-'; software = makeSoftwareReadable(software); return truncateWithEllipsis(software, 33); - } + }, + softwares: (software: string) => { + if(typeof software !== 'string') return '-'; + software = makeSoftwareReadable(software); + return truncateWithEllipsis(software, 33); + }, } export const tableRowStyler = (row: Record) => { diff --git a/apps/gui/src/lib/stores/geocodes.ts b/apps/gui/src/lib/stores/geocodes.ts index 82ee43ab..910140c1 100644 --- a/apps/gui/src/lib/stores/geocodes.ts +++ b/apps/gui/src/lib/stores/geocodes.ts @@ -7,6 +7,7 @@ import { StateManager } from '@nostrwatch/route66'; import { Nip66CheckEvent } from '@nostrwatch/route66/models'; import type { lte } from 'lodash'; import { doAggregateCache } from './app.js'; +import softwares from '../config/dataTable/softwares.js'; export const geocodes: Readable = derived( relayCheckAggregates, @@ -69,9 +70,26 @@ export const relaysByGeo: Readable> = derived( return relaysByGeo; }) +export const softwaresByGeo: Readable> = derived( + [relayCheckAggregates], + ([$relayCheckAggregates]) => { + const softwaresByGeo: Map> = new Map(); + $relayCheckAggregates.forEach((relayCheck) => { + const geocode = relayCheck?.geocode || 'unknown'; + const softwares = softwaresByGeo.get(geocode) || new Set(); + if((softwares as Set).has(relayCheck.software)) return; + (softwares as Set).add(relayCheck.software); + softwaresByGeo.set(geocode, softwares); + }); + softwaresByGeo.forEach((softwares, geocode) => { + softwaresByGeo.set(geocode, Array.from(softwares)); + }) + return softwaresByGeo as Map; +}) + export const geoRows = derived( - [geocodes, geocodeCounts, geocodePercentages, relaysByGeo], - ([$geocodes, $geocodeCounts, $geocodePercentages, $relaysByGeo]) => { + [geocodes, geocodeCounts, geocodePercentages, relaysByGeo, softwaresByGeo], + ([$geocodes, $geocodeCounts, $geocodePercentages, $relaysByGeo, $softwaresByGeo]) => { const rows: Record = []; $geocodes.forEach((geocode: string) => { const id = geocode; @@ -79,7 +97,9 @@ export const geoRows = derived( const percent = $geocodePercentages.get(geocode) || 0; const relays = $relaysByGeo.get(geocode) || []; const relaysCount = relays.length || 0; - rows.push({ id, geocode, count, percent, relays, relaysCount }); + const softwares = $softwaresByGeo.get(geocode) || []; + const softwaresCount = softwares.length || 0; + rows.push({ id, geocode, count, percent, relays, relaysCount, softwares, softwaresCount }); }); return rows; } diff --git a/apps/gui/src/lib/stores/isps.ts b/apps/gui/src/lib/stores/isps.ts index 21bb1e8b..4b55e5fe 100644 --- a/apps/gui/src/lib/stores/isps.ts +++ b/apps/gui/src/lib/stores/isps.ts @@ -4,6 +4,7 @@ import { eventsArray } from './events.js'; import { relayCheckAggregates, relayChecks } from './checks.js'; import { StateManager } from '@nostrwatch/route66'; import { doAggregateCache } from './app.js'; +import softwares from '../config/dataTable/softwares.js'; export type StoreIsp = { title: string; @@ -69,12 +70,33 @@ export const ispPercentages = derived(ispCounts, ($ispCounts) => { return percentages; }); +export const softwaresByIsp: Readable> = derived( + [relayCheckAggregates], + ([$relayCheckAggregates]) => { + const softwaresByIsp: Map> = new Map(); + $relayCheckAggregates.forEach((relayCheck) => { + const isp = relayCheck?.isp || 'unknown'; + const softwares = softwaresByIsp.get(isp) || new Set(); + if((softwares as Set).has(relayCheck.software)) return; + (softwares as Set).add(relayCheck.software); + softwaresByIsp.set(isp, softwares); + }); + console.log(softwaresByIsp, 'softwaresByIsp') + softwaresByIsp.forEach((softwares, isp) => { + softwaresByIsp.set(isp, Array.from(softwares)); + }) + return softwaresByIsp as Map; + } +); + export const ispRows = derived( - [isps, ispCounts, ispPercentages], - ([$isps, $ispCounts, $ispPercentages]) => { + [isps, ispCounts, ispPercentages, softwaresByIsp], + ([$isps, $ispCounts, $ispPercentages, $softwaresByIsp]) => { const rows = $isps.map((isp) => { const count = $ispCounts.get(isp.title) || 0; const percent = $ispPercentages.get(isp.title) || 0; + const softwares = $softwaresByIsp.get(isp.title) || []; + const softwaresCount = softwares?.length; const { title:prettyName, asname, as } = isp; return { id: as, @@ -82,7 +104,9 @@ export const ispRows = derived( asname, as, count, - percent + percent, + softwares, + softwaresCount }; }); return rows; diff --git a/apps/gui/src/lib/utils/cache.ts b/apps/gui/src/lib/utils/cache.ts index 7081f7ff..cfddb35c 100644 --- a/apps/gui/src/lib/utils/cache.ts +++ b/apps/gui/src/lib/utils/cache.ts @@ -102,4 +102,61 @@ export const getLocalStorageUsage = (maxSizeMB: number = 5): LocalStorageUsage = maxSizeMB, percentageUsed, }; +} + +export type ObjectSizeType = { size: number, unit: 'bytes' | 'KB' | 'MB' } + +export const calculateSize = (input: any): ObjectSizeType => { + function getSizeInBytes(value: any): number { + const objectList = new Set(); + const stack = [value]; + let bytes = 0; + + while (stack.length) { + const currentValue = stack.pop(); + + if (currentValue === null || currentValue === undefined) { + bytes += 0; + } else if (typeof currentValue === 'boolean') { + bytes += 4; + } else if (typeof currentValue === 'string') { + bytes += currentValue.length * 2; + } else if (typeof currentValue === 'number') { + bytes += 8; + } else if (typeof currentValue === 'object') { + if (!objectList.has(currentValue)) { + objectList.add(currentValue); + for (const key in currentValue) { + if (currentValue.hasOwnProperty(key)) { + bytes += key.length * 2; + stack.push(currentValue[key]); + } + } + + if (currentValue instanceof Map) { + currentValue.forEach((v, k) => { + stack.push(k); + stack.push(v); + }); + } else if (currentValue instanceof Set) { + currentValue.forEach(v => stack.push(v)); + } else if (Array.isArray(currentValue)) { + stack.push(...currentValue); + } + } + } + } + + return bytes; + } + + const bytes = getSizeInBytes(input); + + if (bytes >= 1024 * 1024) { + return { size: bytes / (1024 * 1024), unit: 'MB' }; + } else if (bytes >= 1024) { + return { size: bytes / 1024, unit: 'KB' }; + } else { + return { size: bytes, unit: 'bytes' }; + } } \ No newline at end of file diff --git a/apps/gui/src/lib/utils/lifecycle.ts b/apps/gui/src/lib/utils/lifecycle.ts index e8c02b8f..c443dc74 100644 --- a/apps/gui/src/lib/utils/lifecycle.ts +++ b/apps/gui/src/lib/utils/lifecycle.ts @@ -151,11 +151,9 @@ export const bootstrapMonitorChecks = async () => { export const bootstrap = async () => { //console.log('bootstrap') if(!$route66){ - //console.log('bootstrap:no instance') $route66 = await instance(); } await $route66.ready(); - //console.log('bootstrap:ready') bindBootstrapEmitters(); const onevents = (events: IEvent[]) => { for(const event of events){ @@ -339,6 +337,7 @@ export const seedMetaFromCache = async () => { if(!$route66) { $route66 = await instance(); } + await $route66.ready(); if(!$route66) return; if(get(isSeeded)) return; if(!hasBeenBoostrapped()) return; diff --git a/apps/gui/src/routes/+layout.svelte b/apps/gui/src/routes/+layout.svelte index 41946368..5211b82d 100755 --- a/apps/gui/src/routes/+layout.svelte +++ b/apps/gui/src/routes/+layout.svelte @@ -8,7 +8,7 @@ import { doBootstrap } from '$lib/stores/routines.js'; import Header from '$lib/components/layout/Header.svelte'; - import { instance, bootstrap, seedChecksFromCache } from '$lib/utils/lifecycle'; + import { instance, bootstrap, seedFromCache } from '$lib/utils/lifecycle'; import { writable, type Writable, get } from 'svelte/store'; import { navigating } from '$app/stores'; import type Route66 from '@nostrwatch/route66'; @@ -77,7 +77,7 @@ setTabState('leader'); try { await boot(); - seedChecksFromCache() + seedFromCache() //console.log('Leader tab: DB initialized.'); } catch (error) { console.error('[Lifecycle] Error in onLeaderAcquired:', error); @@ -128,7 +128,7 @@ if (!get(doBootstrap)) { try { (await instance()).monitorService.ensureMonitorsActive(); - await seedChecksFromCache(); + await seedFromCache(); appState.set('running'); //console.log('Data seeded from cache.'); } catch (error) { diff --git a/apps/gui/src/routes/monitors/+page.svelte b/apps/gui/src/routes/monitors/+page.svelte index 746be589..3df3ed0f 100644 --- a/apps/gui/src/routes/monitors/+page.svelte +++ b/apps/gui/src/routes/monitors/+page.svelte @@ -13,6 +13,7 @@ import { writable, type Writable } from 'svelte/store'; import { type DataTableConfig, defaultDataTableConfig } from '$lib/components/lists/table/DataTableTypes'; import builtInTableConfig from '$lib/config/dataTable/monitors.js' + import { stringify } from 'json-source-map'; const tableKey: string = 'monitors' @@ -49,6 +50,7 @@ $: warnHasLessThanRecommendedMonitors = countEnabledMonitors < 3; $: warnHasMoreThanRecommendedMonitors = countEnabledMonitors > 8; + {#if $ready} {#if $monitorsSorted.length} {#if criticalHasNoMonitorsEnabled} @@ -87,6 +89,6 @@ {/if} - + {/if} \ No newline at end of file diff --git a/apps/gui/src/routes/operators/+page.svelte b/apps/gui/src/routes/operators/+page.svelte index 30da4a9a..0ad35b75 100644 --- a/apps/gui/src/routes/operators/+page.svelte +++ b/apps/gui/src/routes/operators/+page.svelte @@ -12,6 +12,7 @@ import { operatorsPubkeys } from '$lib/stores/operators'; import { operatorsUserInstances } from '$lib/stores/operators'; import { bootstrapOperatorMeta } from '$lib/utils/lifecycle'; + import { seedMetaFromCache } from '$lib/utils/lifecycle'; let DataTable: DataTableType; const componentsLoaded: Writable = writable(false); @@ -55,6 +56,7 @@ if (typeof window === 'undefined' || typeof navigator === 'undefined') return; doBootstrap.set(true) doAggregateCache.set(true) + seedMetaFromCache() loadComponents().then(() => { setConfig() bootstrapOperatorMeta() diff --git a/apps/gui/src/routes/preferences/+page.svelte b/apps/gui/src/routes/preferences/+page.svelte index 686f9dc0..253f8406 100644 --- a/apps/gui/src/routes/preferences/+page.svelte +++ b/apps/gui/src/routes/preferences/+page.svelte @@ -22,4 +22,4 @@ {/if} - \ No newline at end of file + \ No newline at end of file diff --git a/apps/gui/src/routes/relays/+page.svelte b/apps/gui/src/routes/relays/+page.svelte index d63335a2..b81a51c2 100644 --- a/apps/gui/src/routes/relays/+page.svelte +++ b/apps/gui/src/routes/relays/+page.svelte @@ -64,7 +64,7 @@
{#if $ready} - + {/if}
diff --git a/apps/nocapd/package.json b/apps/nocapd/package.json index 4553764d..3ed8382e 100644 --- a/apps/nocapd/package.json +++ b/apps/nocapd/package.json @@ -2,7 +2,7 @@ "name": "@nostrwatch/nocapd", "type": "module", "version": "1.12.2", - "main": "index.js", + "main": "src/index.js", "license": "MIT", "dependencies": { "@nostr-fetch/adapter-nostr-tools": "0.14.1", diff --git a/apps/nocapd/src/daemon.js b/apps/nocapd/src/daemon.js index be469d2d..8b04ba14 100644 --- a/apps/nocapd/src/daemon.js +++ b/apps/nocapd/src/daemon.js @@ -9,7 +9,7 @@ import { NocapdQueue, BullMQ } from '@nostrwatch/controlflow' import Logger from '@nostrwatch/logger' import relaycache, { Schemas } from '@nostrwatch/nwcache' import { bootstrap } from '@nostrwatch/seed' -import { parseRelayNetwork, delay, loadConfig, RedisConnectionDetails, parseUrl } from "@nostrwatch/utils" +import { delay, loadConfig, RedisConnectionDetails, parseUrl } from "@nostrwatch/utils" import { NWWorker } from './classes/Worker.js' import { ShortBus } from './classes/ShortBus.js' @@ -108,11 +108,13 @@ const maybeAnnounce = async () => { "nocapd.checks.options.timeout": "timeouts", "nocapd.checks.options.expires": "frequency", "nocapd.checks.enabled": "checks", + "nocapd.networks": "networks", "monitor.geo": "geo", "monitor.owner": "owner", "publisher.to_relays": "relays", "monitor.info": "profile" } + const conf = mapper(config, map) conf.frequency = timestring(conf.frequency, 's').toString() const announce = new AnnounceMonitor(conf, process.env.DAEMON_PUBKEY) diff --git a/internal/announce/package.json b/internal/announce/package.json index 64c8a2af..f89067e6 100644 --- a/internal/announce/package.json +++ b/internal/announce/package.json @@ -15,7 +15,9 @@ "dependencies": { "@nostrwatch/logger": "workspace:^", "@nostrwatch/utils": "workspace:^", - "chalk": "^5.3.0", + "chalk": "^5.3.0" + }, + "peerDependencies": { "nostr-tools": "2.1.4" }, "devDependencies": { diff --git a/internal/announce/src/index.ts b/internal/announce/src/index.ts index 344190dd..587ab9a8 100644 --- a/internal/announce/src/index.ts +++ b/internal/announce/src/index.ts @@ -1,6 +1,6 @@ import chalk from 'chalk' -import { verifyEvent, finalizeEvent, SimplePool, Event } from "nostr-tools"; +import { verifyEvent } from "nostr-tools"; import { Publisher, Kind10166, Kind0, Kind10002 } from "@nostrwatch/publisher"; import Logger from "@nostrwatch/logger"; const log = new Logger('@nostrwatch/announce') @@ -15,7 +15,7 @@ interface AnnounceMonitorOptions { geo?: object; kinds?: number[]; timeouts?: object; - counts?: number[]; + networks?: string[]; checks?: string[]; owner?: string; frequency?: string; @@ -39,12 +39,11 @@ export class AnnounceMonitor { } setup(options: AnnounceMonitorOptions): void { - // Destructuring options with default values const { geo = {}, kinds = [], timeouts = {}, - counts = [], + networks = {}, checks = [], owner = '', frequency = '', @@ -57,21 +56,20 @@ export class AnnounceMonitor { if (!(geo instanceof Object)) throw new Error("geo must be object"); if (!(timeouts instanceof Object)) throw new Error("timeouts must be object"); if (!(kinds instanceof Array)) throw new Error("kinds must be array"); - if (!(counts instanceof Array)) throw new Error("counts must be array"); if (!(checks instanceof Array)) throw new Error("checks must be array"); + if (!(networks instanceof Array)) throw new Error("checks must be array"); if (typeof owner !== "string") throw new Error("owner must be string"); if (typeof frequency !== "string") throw new Error("frequency must be string"); if( !(relays instanceof Array) ) throw new Error("relays must be an array"); if( !(profile instanceof Object) ) throw new Error("profile must be an object"); - // Assigning the validated options to class properties this.monReg.geo = geo; this.monReg.kinds = kinds; this.monReg.timeouts = timeouts; - this.monReg.counts = counts; this.monReg.owner = owner; this.monReg.frequency = frequency; + this.monReg.networks = networks; this.monReg.checks = AnnounceMonitor.formatChecks(checks) this.monRelays = relays; @@ -80,7 +78,7 @@ export class AnnounceMonitor { static formatChecks(checks: Array): Array { if(checks.includes('all')) - return ['open', 'read', 'write', 'info', 'dns', 'geo', 'ssl'] + return ['websocket', 'ws', 'info', 'dns', 'geo', 'ssl'] return checks } diff --git a/internal/publisher/src/Publisher.test.ts b/internal/publisher/src/Publisher.test.ts index 25e30910..9b371c44 100644 --- a/internal/publisher/src/Publisher.test.ts +++ b/internal/publisher/src/Publisher.test.ts @@ -1,4 +1,3 @@ -// tests/Publisher.test.ts import { describe, it, expect, beforeEach, vi } from 'vitest'; import { Publisher } from '../src/Publisher'; import wsAdapter from '@nostrwatch/publisher-nostrtools'; diff --git a/internal/publisher/src/Publisher.ts b/internal/publisher/src/Publisher.ts index 7cc441f7..b76aa2fa 100644 --- a/internal/publisher/src/Publisher.ts +++ b/internal/publisher/src/Publisher.ts @@ -1,4 +1,3 @@ -// src/Publisher.ts import Logger from '@nostrwatch/logger'; import { isClassInstance } from '@nostrwatch/utils'; @@ -24,7 +23,12 @@ export class Publisher { new config.wsAdapter(relays, config?.wsConf || {}) }); } else { - this.ws = isClassInstance(config?.wsAdapter) ? config?.wsAdapter : new config.wsAdapter(relays, config?.wsConf || {}); + try { + this.ws = isClassInstance(config?.wsAdapter) ? config?.wsAdapter : new config.wsAdapter(relays, config?.wsConf || {}); + } + catch(e){ + this.logger.error(`Publisher::constructor(): Error: ${e}`); + } } } diff --git a/internal/publisher/src/kinds/Kind10166.ts b/internal/publisher/src/kinds/Kind10166.ts index 57f419d2..601aabbf 100644 --- a/internal/publisher/src/kinds/Kind10166.ts +++ b/internal/publisher/src/kinds/Kind10166.ts @@ -15,9 +15,8 @@ interface Timeouts { interface Kind10166Data { frequency?: string; - // owner?: string; // Uncomment if needed + networks?: string[]; kinds?: number[]; - counts?: number[]; checks?: string[]; timeouts?: Timeouts; geo?: GeoData; @@ -71,9 +70,9 @@ export class Kind10166 extends Event { }); } - if (data.counts) { - data.counts.map(count => count.toString()).forEach(countStr => { - tags.push(['n', countStr]); + if(data.networks && Array.isArray(data.networks)) { + data.networks.forEach(network => { + tags.push(['n', network]); }); } @@ -95,6 +94,8 @@ export class Kind10166 extends Event { tags = [...tags, ...geoTags]; } + + return tags; } diff --git a/libraries/nocap-nip66/README.md b/libraries/nocap-nip66/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/libraries/nocap-nip66/package.json b/libraries/nocap-nip66/package.json deleted file mode 100644 index 34981fda..00000000 --- a/libraries/nocap-nip66/package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "@nostrwatch/nocap-route66", - "version": "1.0.0", - "description": "A library for transforming NOCAP output into Nostr events of kind 30166", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "scripts": { - "build": "webpack --config webpack.config.js", - "build:browser": "webpack --config webpack.config.js --env target=web", - "build:node": "webpack --config webpack.config.js --env target=node", - "test": "vitest", - "lint": "eslint . --ext .ts", - "clean": "rimraf dist" - }, - "repository": { - "type": "git", - "url": "https://github.com/yourusername/nocapd-route66.git" - }, - "keywords": [ - "nostr", - "route66", - "nocap", - "event", - "library" - ], - "author": "Your Name ", - "license": "MIT", - "dependencies": { - "@nostrwatch/logger": "*", - "@nostrwatch/nocap": "workspace:^", - "nostr-geotags": "0.7.1", - "nostr-tools": "2.7.2" - }, - "devDependencies": { - "@types/node": "^20.0.0", - "eslint": "^8.0.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-prettier": "^5.0.0", - "prettier": "^3.4.2", - "rimraf": "^5.0.0", - "ts-loader": "^9.0.0", - "typescript": "^5.0.0", - "vitest": "^0.34.1", - "webpack": "^5.0.0", - "webpack-cli": "^5.0.0" - }, - "files": [ - "dist/**/*" - ], - "engines": { - "node": ">=14.0.0" - } -} diff --git a/libraries/nocap-nip66/src/Transform.test.js b/libraries/nocap-nip66/src/Transform.test.js deleted file mode 100644 index d111825f..00000000 --- a/libraries/nocap-nip66/src/Transform.test.js +++ /dev/null @@ -1,172 +0,0 @@ -// Transform.test.ts -import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { Transform } from './Transform'; - -// Mock dependencies if necessary -vi.mock('@nostrwatch/logger', () => ({ - Logger: class { - err = vi.fn(); - info = vi.fn(); - warn = vi.fn(); - }, -})); - -vi.mock('nostr-tools', () => ({ - getEventHash: vi.fn().mockReturnValue('mocked_event_hash'), -})); - -describe('Transform Class', () => { - const pubkey = 'abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890'; - let baseInstance: Transform; - - // Create a concrete subclass for testing - class TestBase extends Transform { - constructor(kind: number, pubkey: string) { - super(kind, pubkey); - } - - generateTags(check: any): string[][] { - return [['tag1', 'value1'], ['tag2', 'value2']]; - } - } - - beforeEach(() => { - baseInstance = new TestBase(9999, pubkey); - }); - - describe('Initialization', () => { - it('should create an instance of Transform', () => { - expect(baseInstance).toBeInstanceOf(Transform); - expect(baseInstance.kind).toBe(9999); - expect(baseInstance.pubkey).toBe(pubkey); - expect(baseInstance.logger).toBeDefined(); - }); - - it('should throw an error if kind is undefined', () => { - expect(() => new TestBase(undefined as unknown as number, pubkey)).toThrow( - 'Kind must be defined' - ); - }); - - it('should throw an error if pubkey is undefined', () => { - expect(() => new TestBase(9999, undefined as unknown as string)).toThrow( - 'DAEMON_PUBKEY must be defined' - ); - }); - }); - - describe('tpl()', () => { - it('should return a template with default values', () => { - const tpl = baseInstance.tpl(); - expect(tpl).toEqual({ - id: null, - pubkey, - kind: 9999, - created_at: expect.any(Number), - tags: [], - content: '', - }); - }); - - it('should include data if provided', () => { - const data = { - checked_at: 1620000000, - content: 'Test Content', - tags: [['test', 'tag']], - }; - const tpl = baseInstance.tpl(data); - expect(tpl).toEqual({ - id: null, - pubkey, - kind: 9999, - created_at: 1620000000, - tags: [['test', 'tag']], - content: 'Test Content', - }); - }); - }); - - describe('generateEvent()', () => { - it('should generate an event with correct properties', () => { - const data = { - url: 'wss://example.com', - info: { data: { key: 'value' } }, - }; - const event = baseInstance.generateEvent(data); - expect(event.id).toBe('mocked_event_hash'); - expect(event.pubkey).toBe(pubkey); - expect(event.kind).toBe(9999); - expect(event.created_at).toBeGreaterThan(0); - expect(event.tags).toEqual([['tag1', 'value1'], ['tag2', 'value2']]); - expect(event.content).toBe(JSON.stringify({ key: 'value' })); - }); - - it('should handle errors in content serialization', () => { - const data = { - url: "wss://someurl.xyz", - info: { data: undefined }, - }; - // Simulate error in JSON.stringify - const originalStringify = JSON.stringify; - JSON.stringify = () => { - throw new Error('Serialization error'); - }; - - const event = baseInstance.generateEvent(data); - expect(event.content).toBe('{}'); - // Restore JSON.stringify - JSON.stringify = originalStringify; - }); - }); - - describe('dedupLabels()', () => { - it('should deduplicate labels correctly', () => { - const tags = [ - ['L', 'label1'], - ['l', 'value1', 'label1'], - ['l', 'value2', 'label1'], - ['l', 'value1', 'label1'], // Duplicate - ['L', 'label2'], - ['l', 'value3', 'label2'], - ]; - const dedupedTags = baseInstance.dedupLabels(tags); - expect(dedupedTags).toEqual([ - ['L', 'label1'], - ['l', 'value1', 'label1'], - ['l', 'value2', 'label1'], - ['L', 'label2'], - ['l', 'value3', 'label2'], - ]); - }); - }); - - describe('removeLabels()', () => { - it('should remove labels correctly', () => { - const tags = [ - ['L', 'label1'], - ['l', 'value1', 'label1'], - ['t', 'tag1'], - ['l', 'value2', 'label1'], - ['L', 'label2'], - ['p', 'pubkey'], - ]; - const filteredTags = baseInstance.removeLabels(tags); - expect(filteredTags).toEqual([ - ['t', 'tag1'], - ['p', 'pubkey'], - ]); - }); - }); - - describe('json()', () => { - it('should return the current event', () => { - const data = { - url: 'wss://example.com', - }; - baseInstance.generateEvent(data); - const event = baseInstance.json(); - expect(event).toBeDefined(); - expect(event.id).toBe('mocked_event_hash'); - }); - }); -}); diff --git a/libraries/nocap-nip66/src/Transform.ts b/libraries/nocap-nip66/src/Transform.ts deleted file mode 100644 index 5005c518..00000000 --- a/libraries/nocap-nip66/src/Transform.ts +++ /dev/null @@ -1,71 +0,0 @@ -// KindBase.ts -import { - getEventHash, -} from 'nostr-tools'; -import Logger from '@nostrwatch/logger'; -import { IResult } from '@nostrwatch/nocap'; - -export abstract class Transform { - kind: number; - pubkey: string; - logger: Logger; - event: any; - - constructor(kind: number, pubkey: string) { - if (typeof kind === 'undefined' || kind === null) { - throw new Error('Kind must be defined'); - } - if (!pubkey) { - throw new Error('DAEMON_PUBKEY must be defined'); - } - this.kind = kind; - this.pubkey = pubkey; - this.logger = new Logger(`@nostrwatch/publisher/event: ${kind}`); - } - - tpl(data?: any) { - const id = null; - const pubkey = this.pubkey; - const kind = this.kind; - const created_at = data?.checked_at - ? data.checked_at - : Math.round(Date.now() / 1000); - const content = data?.content ?? ''; - const tags = data?.tags ?? []; - - return { id, pubkey, kind, created_at, tags, content }; - } - - json() { - return this.event; - } - - generateEvent(data: IResult) { - this.event = this._generateEvent(data); - this.event.id = getEventHash(this.event); - return this.event; - } - - protected _generateEvent(data: IResult) { - let content = '{}'; - const tags = this.generateTags(data); - const nip11 = String(data.info?.data); - - if (nip11) { - try { - content = JSON.stringify(nip11); - } catch (e) { - this.logger.err(`generateEvent(): Error: ${e}`); - this.logger.info(nip11); - } - } - - return { - ...this.tpl(), - content, - tags, - }; - } - - abstract generateTags(check: IResult): string[][]; -} diff --git a/libraries/nocap-nip66/src/index.ts b/libraries/nocap-nip66/src/index.ts deleted file mode 100644 index 1310cb76..00000000 --- a/libraries/nocap-nip66/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { Transform } from './Transform'; -export { Kind30166 } from './kinds'; \ No newline at end of file diff --git a/libraries/nocap-nip66/src/kinds/0.ts b/libraries/nocap-nip66/src/kinds/0.ts deleted file mode 100644 index e5619162..00000000 --- a/libraries/nocap-nip66/src/kinds/0.ts +++ /dev/null @@ -1,65 +0,0 @@ -// import { IResult } from '@nostrwatch/nocap'; -// import { Transform } from '../Transform'; - -// /** -// * Represents a specific kind of event with additional content generation capabilities. -// */ -// export class Kind0 extends Transform { - - -// constructor(pubkey: string) { -// super(0, pubkey); -// this.discoverable = { pubkey: true }; -// this.human_readable = false; -// this.machine_readable = true; -// } - -// generateTags(check: IResult): string[][] { -// let tags: string[][] = []; -// return tags; -// } - -// /** -// * Generates an event with tags based on provided data. -// * @param {IResult} data The data to generate content from. -// * @returns {object} The generated event. -// */ -// generateEvent(data: IResult): Record { -// let tags: string[][] = []; -// const content = Kind0.generateContent(data); - -// const event = { -// ...this.tpl(), -// content, -// tags -// }; - -// return event; -// } - -// /** -// * Generates content for the event based on provided data. -// * @param {object} data The data to generate content from. -// * @returns {string} The generated content. -// */ -// static generateContent(data: Record): string { -// let content = ""; -// try { -// content = JSON.stringify(data); -// } catch (e) { -// console.dir(`Kind0::generateContent(): Error: ${e}`); -// throw new Error('Was not able to stringify data for kind 0 content field.'); -// } -// if (!content) content = "{}"; -// return content; -// } - -// /** -// * Parses the content of an event. -// * @param {Record} event The event to parse. -// * @returns {any} The parsed content. -// */ -// static parse(event: Record): any { -// return JSON.parse(event.content); -// } -// } diff --git a/libraries/nocap-nip66/src/kinds/10166.ts b/libraries/nocap-nip66/src/kinds/10166.ts deleted file mode 100644 index c56d8ae1..00000000 --- a/libraries/nocap-nip66/src/kinds/10166.ts +++ /dev/null @@ -1,120 +0,0 @@ -// import { Transform } from '../Transform'; -// import ngeotags from 'nostr-geotags'; - -// export interface GeoData { -// isp?: string; -// as?: string; -// asname?: string; -// [key: string]: any; -// } - -// /** -// * Represents a specific kind of publisher with additional tagging capabilities. -// */ -// export class Kind10166 extends Transform { -// constructor(pubkey: string) { -// super(10166, pubkey); -// } - -// /** -// * Generates an event with tags based on provided data. -// * @param {MonitorData} data The data to generate tags from. -// * @returns {string[][]} The generated tags. -// */ -// generateTags(data: MonitorData): string[][] { -// let tags: string[][] = []; - -// tags = this.addFrequencyTags(tags, data); -// tags = this.addKindsTags(tags, data); -// tags = this.addCountsTags(tags, data); -// tags = this.addChecksTags(tags, data); -// tags = this.addTimeoutTags(tags, data); -// tags = this.addGeoTags(tags, data.geo?.data); - -// return tags; -// } - -// private addFrequencyTags(tags: string[][], data: MonitorData): string[][] { -// if (data?.frequency) { -// tags.push(['frequency', data.frequency]); -// } -// return tags; -// } - -// private addKindsTags(tags: string[][], data: MonitorData): string[][] { -// if (data?.kinds) { -// data.kinds.map(kind => kind.toString()).forEach(kind => tags.push(['k', kind])); -// } -// return tags; -// } - -// private addCountsTags(tags: string[][], data: MonitorData): string[][] { -// if (data?.counts) { -// data.counts.map(count => count.toString()).forEach(count => tags.push(['n', count])); -// } -// return tags; -// } - -// private addChecksTags(tags: string[][], data: MonitorData): string[][] { -// if (data?.checks) { -// data.checks.map(check => check.toString()).forEach(check => tags.push(['c', check])); -// } -// return tags; -// } - -// private addTimeoutTags(tags: string[][], data: MonitorData): string[][] { -// if (data?.timeouts) { -// Object.keys(data.timeouts || {}).forEach(key => { -// tags.push(['timeout', key, data.timeouts[key].toString()]); -// }); -// } -// return tags; -// } - -// private addGeoTags(tags: string[][], geoData?: GeoData[]): string[][] { -// if (!geoData || !Array.isArray(geoData)) return tags; - -// let ispTags: string[][] = []; -// let geoTags: string[][] = []; - -// geoData.forEach((geo) => { -// ispTags = this.addGeoIspTags(ispTags, geo); -// geoTags = this.addGeoLocationTags(geoTags, geo); -// }); - -// geoTags = [...this.removeLabels(geoTags), ...this.dedupLabels(geoTags)]; -// ispTags = this.dedupLabels(ispTags); -// tags.push(...ispTags, ...geoTags); -// return tags; -// } - -// private addGeoIspTags(tags: string[][], geo: GeoData): string[][] { -// const ispFields = [ -// { key: 'isp', label: 'host.isp' }, -// { key: 'as', label: 'host.as' }, -// { key: 'asname', label: 'host.asn' }, -// ]; - -// ispFields.forEach(({ key, label }) => { -// if (geo[key]) { -// tags.push(['L', label]); -// tags.push(['l', geo[key], label]); -// } -// }); - -// return tags; -// } - -// private addGeoLocationTags(tags: string[][], geo: GeoData): string[][] { -// const gOpts = { -// isoAsNamespace: false, -// geohash: true, -// gps: false, -// countryCode: true, -// countryName: true, -// regionCode: true, -// }; -// const geoTags = ngeotags(geo, gOpts) as string[][]; -// return [...tags, ...geoTags]; -// } -// } diff --git a/libraries/nocap-nip66/src/kinds/30166.test.js b/libraries/nocap-nip66/src/kinds/30166.test.js deleted file mode 100644 index eb2be21f..00000000 --- a/libraries/nocap-nip66/src/kinds/30166.test.js +++ /dev/null @@ -1,205 +0,0 @@ -// Kind30166.test.ts -import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { Kind30166 } from './30166'; -import ngeotags from 'nostr-geotags'; - -vi.mock('nostr-geotags', () => ({ - default: vi.fn(), -})); - -describe('Kind30166', () => { - const pubkey = 'abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890'; - let kind30166: Kind30166; - - beforeEach(() => { - kind30166 = new Kind30166(pubkey); - }); - - describe('Initialization', () => { - it('should create an instance of Kind30166', () => { - expect(kind30166).toBeInstanceOf(Kind30166); - expect(kind30166.kind).toBe(30166); - }); - }); - - describe('generateEvent()', () => { - it('should generate event with empty content if no nip11 data', () => { - const checkData = { - url: 'wss://example.com', - }; - const event = kind30166.generateEvent(checkData); - expect(event.content).toBe('{}'); - expect(event.tags).toBeDefined(); - expect(event.tags.length).toBeGreaterThan(0); - }); - - it('should generate event with nip11 content', () => { - const nip11Data = { name: 'Test NIP11' }; - const checkData = { - url: 'wss://example.com', - info: { data: nip11Data }, - }; - const event = kind30166.generateEvent(checkData); - expect(event.content).toBe(JSON.stringify(nip11Data)); - }); - }); - - describe('generateTags()', () => { - it('should add basic tags correctly', () => { - const checkData = { - url: 'wss://example.com', - open: { duration: 100.5 }, - read: { duration: 200 }, - write: { duration: 300 }, - }; - const tags = kind30166.generateTags(checkData); - expect(tags).toContainEqual(['d', 'wss://example.com']); - expect(tags).toContainEqual(['rtt-open', '101']); - expect(tags).toContainEqual(['rtt-read', '200']); - expect(tags).toContainEqual(['rtt-write', '300']); - }); - - it('should add network tag if present', () => { - const checkData = { - url: 'wss://example.com', - network: 'mainnet', - }; - const tags = kind30166.generateTags(checkData); - expect(tags).toContainEqual(['n', 'mainnet']); - }); - - describe('Info Tags', () => { - it('should handle info tags correctly', () => { - const infoData = { - pubkey: pubkey, - supported_nips: [1, 2], - language_tags: ['en', 'es'], - tags: ['tag1', 'tag2'], - limitation: { auth_required: true, payment_required: false }, - software: 'TestSoftware', - version: '1.0.0', - }; - const checkData = { - url: 'wss://example.com', - info: { data: infoData }, - }; - const tags = kind30166.generateTags(checkData); - - expect(tags).toContainEqual(['p', pubkey]); - expect(tags).toContainEqual(['N', '1']); - expect(tags).toContainEqual(['N', '2']); - expect(tags).toContainEqual(['L', 'ISO-639-1']); - expect(tags).toContainEqual(['l', 'en', 'ISO-639-1']); - expect(tags).toContainEqual(['l', 'es', 'ISO-639-1']); - expect(tags).toContainEqual(['t', 'tag1']); - expect(tags).toContainEqual(['t', 'tag2']); - expect(tags).toContainEqual(['R', 'auth']); - expect(tags).toContainEqual(['R', '!payment']); - expect(tags).toContainEqual(['s', 'TestSoftware']); - expect(tags).toContainEqual(['L', 'nip11.version']); - expect(tags).toContainEqual(['l', '1.0.0', 'nip11.version']); - }); - }); - - describe('SSL Tags', () => { - it('should handle SSL tags correctly for wss protocol', () => { - const sslData = { - valid_from: new Date(Date.now() - 100000).toISOString(), - valid_to: new Date(Date.now() + 100000).toISOString(), - }; - const checkData = { - url: 'wss://example.com', - ssl: { data: sslData }, - }; - const tags = kind30166.generateTags(checkData); - expect(tags).toContainEqual(['R', 'ssl']); - }); - - it('should handle SSL tags correctly for non-wss protocol', () => { - const checkData = { - url: 'ws://example.com', - }; - const tags = kind30166.generateTags(checkData); - expect(tags).toContainEqual(['R', '!ssl']); - }); - }); - - describe('DNS Tags', () => { - it('should handle DNS tags correctly', () => { - const dnsData = { - ipv4: ['192.168.1.1'], - ipv6: ['::1'], - }; - const checkData = { - url: 'wss://example.com', - dns: { data: dnsData }, - duration: 100, - }; - const tags = kind30166.generateTags(checkData); - - expect(tags).toContainEqual(['L', 'dns.ipv4']); - expect(tags).toContainEqual(['l', '192.168.1.1', 'dns.ipv4']); - expect(tags).toContainEqual(['L', 'dns.ipv6']); - expect(tags).toContainEqual(['l', '::1', 'dns.ipv6']); - }); - }); - - describe('Geo Tags', () => { - it('should handle geo tags correctly', () => { - const geoData = [ - { - isp: 'Test ISP', - as: 'AS12345', - asname: 'Test AS Name', - countryCode: 'US', - countryName: 'United States', - regionCode: 'CA', - }, - ]; - const checkData = { - url: 'wss://example.com', - geo: { data: geoData }, - }; - - // Mock ngeotags response - const mockNgeotags = ngeotags as any; - mockNgeotags.mockReturnValue([ - ['L', 'geo'], - ['l', 'US', 'geo'], - ]); - - const tags = kind30166.generateTags(checkData); - - expect(tags).toContainEqual(['L', 'host.isp']); - expect(tags).toContainEqual(['l', 'Test ISP', 'host.isp']); - expect(tags).toContainEqual(['L', 'host.as']); - expect(tags).toContainEqual(['l', 'AS12345', 'host.as']); - expect(tags).toContainEqual(['L', 'host.asn']); - expect(tags).toContainEqual(['l', 'Test AS Name', 'host.asn']); - expect(tags).toContainEqual(['L', 'geo']); - expect(tags).toContainEqual(['l', 'US', 'geo']); - }); - }); - - describe('Label Deduplication', () => { - it('should deduplicate labels correctly', () => { - const tags = [ - ['L', 'label1'], - ['l', 'value1', 'label1'], - ['l', 'value2', 'label1'], - ['l', 'value1', 'label1'], // Duplicate - ['L', 'label2'], - ['l', 'value3', 'label2'], - ]; - const dedupedTags = kind30166.dedupLabels(tags); - expect(dedupedTags).toEqual([ - ['L', 'label1'], - ['l', 'value1', 'label1'], - ['l', 'value2', 'label1'], - ['L', 'label2'], - ['l', 'value3', 'label2'], - ]); - }); - }); - }); -}); diff --git a/libraries/nocap-nip66/src/kinds/30166.ts b/libraries/nocap-nip66/src/kinds/30166.ts deleted file mode 100644 index 2be8fb54..00000000 --- a/libraries/nocap-nip66/src/kinds/30166.ts +++ /dev/null @@ -1,249 +0,0 @@ - // Kind30166.ts - import { Transform } from '../Transform'; - import ngeotags from 'nostr-geotags'; - - import { IResult, IResultDataData } from "@nostrwatch/nocap" - - export interface GeoData { - isp?: string; - as?: string; - asname?: string; - [key: string]: any; - } - - export class Kind30166 extends Transform { - private child: boolean = false; - - constructor(pubkey: string) { - super(30166, pubkey); - } - - generateTags(check: IResult): string[][] { - this.child = check?.parent ? true : false; - - let tags: string[][] = []; - const { protocol } = new URL(check.url); - - tags = this.addBasicTags(tags, check); - tags = this.addNetworkTags(tags, check); - tags = this.addInfoTags(tags, check.info?.data); - tags = this.addGeoTags(tags, check.geo?.data); - - // if(!this.child) - tags = this.addSslTags(tags, check.ssl?.data, protocol); - tags = this.addDnsTags(tags, check.dns?.data); - - if(this.child) - tags = this.addParentReferenceTags(tags, check); - - tags.push(['l', 'draft7', 'route66.draft']); - - return this.dedupLabels(tags); - } - - private addParentReferenceTags(tags: string[][], check: IResult): string[][] { - tags.push(['a', `30166:${this.pubkey}:${check.parent}`]) - return tags - } - - private addBasicTags(tags: string[][], check: IResult): string[][] { - tags.push(['d', check.url]); - - const durations: Array<'open' | 'read' | 'write'> = ['open', 'read', 'write']; - durations.forEach((type) => { - const duration = check[type]?.duration; - if (duration && duration > 0) { - tags.push([`rtt-${type}`, String(Math.round(duration))]); - } - }); - return tags - } - - private addNetworkTags(tags: string[][], check: IResult): string[][] { - if (check.network) { - tags.push(['n', check.network]); - } - return tags - } - - private addInfoTags(tags: string[][], info?: any): string[][] { - if (!info) return tags; - this.addPubkeyTag(tags, info.pubkey); - this.addSupportedNips(tags, info.supported_nips); - this.addLanguageTags(tags, info.language_tags); - this.addCustomTags(tags, info.tags); - this.addLimitationTags(tags, info.limitation); - this.addSoftwareTags(tags, info.software, info.version); - return tags - } - - private addPubkeyTag(tags: string[][], pubkey?: string): string[][] { - if (pubkey && /^[0-9a-f]{64}$/.test(pubkey)) { - tags.push(['p', pubkey]); - } - return tags - } - - private addSupportedNips(tags: string[][], nips?: number[]): string[][] { - if (nips) { - nips.forEach((nip) => tags.push(['N', String(nip)])); - } - return tags - } - - private addLanguageTags(tags: string[][], languages?: string[]): string[][] { - if (languages) { - tags.push(['L', 'ISO-639-1']); - languages.forEach((lang) => tags.push(['l', lang, 'ISO-639-1'])); - } - - return tags - } - - private addCustomTags(tags: string[][], customTags?: string[]): string[][] { - if (customTags) { - customTags.forEach((tag) => tags.push(['t', tag])); - } - - return tags - } - - private addLimitationTags( - tags: string[][], - limitation?: { auth_required?: boolean; payment_required?: boolean } - ): string[][] - { - if (limitation) { - tags.push(['R', limitation.auth_required ? 'auth' : '!auth']); - tags.push(['R', limitation.payment_required ? 'payment' : '!payment']); - } - - return tags - } - - private addSoftwareTags( - tags: string[][], - software?: string, - version?: string - ): string[][] - { - if (software) { - tags.push(['s', software]); - } - if (version) { - tags.push(['L', 'nip11.version']); - tags.push(['l', version, 'nip11.version']); - } - - return tags - } - - private addSslTags( - tags: string[][], - sslData: any, - protocol: string - ): string[][] { - if (protocol === 'wss:' && sslData) { - const validFrom = new Date(sslData.valid_from).getTime(); - const validTo = new Date(sslData.valid_to).getTime(); - const isValid = validFrom < Date.now() && validTo > Date.now(); - tags.push(['R', isValid ? 'ssl' : '!ssl']); - } else { - tags.push(['R', '!ssl']); - } - - return tags - } - - private addDnsTags(tags: string[][], dnsData: any): string[][] { - if (!dnsData) return tags; - - ['ipv4', 'ipv6'].forEach((type) => { - if (dnsData[type]?.length) { - tags.push(['L', `dns.${type}`]); - dnsData[type].forEach((ip: string) => - tags.push(['l', ip, `dns.${type}`]) - ); - } - }); - - return tags - } - - private addGeoTags(tags: string[][], geoData?: IResultDataData): string[][] { - if (!geoData || !Array.isArray(geoData)) return tags; - - let ispTags: string[][] = []; - let geoTags: string[][] = []; - - geoData.forEach((geo) => { - ispTags = this.addGeoIspTags(ispTags, geo); - geoTags = this.addGeoLocationTags(geoTags, geo); - }); - - geoTags = [...this.removeLabels(geoTags), ...this.dedupLabels(geoTags)]; - ispTags = this.dedupLabels(ispTags); - tags.push(...ispTags, ...geoTags); - return tags - } - - private addGeoIspTags(tags: string[][], geo: GeoData): string[][] { - const ispFields = [ - { key: 'isp', label: 'host.isp' }, - { key: 'as', label: 'host.as' }, - { key: 'asname', label: 'host.asn' }, - ]; - - ispFields.forEach(({ key, label }) => { - if (geo[key]) { - tags.push(['L', label]); - tags.push(['l', geo[key], label]); - } - }); - - return tags; - } - - private addGeoLocationTags(tags: string[][], geo: GeoData): string[][] { - const gOpts = { - isoAsNamespace: false, - geohash: true, - gps: false, - countryCode: true, - countryName: true, - regionCode: true, - }; - const geoTags = ngeotags(geo, gOpts) as string[][]; - return [...tags, ...geoTags]; - } - - dedupLabels(tags: string[][]): string[][] { - const dedupedTags: string[][] = []; - const keys: Map> = new Map(); - - tags.forEach((item) => { - if (item[0] === 'L') { - const key = item[1]; - if (!keys.has(key)) { - keys.set(key, new Set()); - dedupedTags.push(item); - } - } else if (item[0] === 'l') { - const key = item[2]; - const value = item[1]; - if (keys.has(key) && !keys.get(key)!.has(value)) { - keys.get(key)!.add(value); - dedupedTags.push(item); - } - } else { - dedupedTags.push(item); - } - }); - - return dedupedTags; - } - - removeLabels(tags: string[][]): string[][] { - return tags.filter((t) => t[0] !== 'l' && t[0] !== 'L'); - } - } diff --git a/libraries/nocap-nip66/src/kinds/index.ts b/libraries/nocap-nip66/src/kinds/index.ts deleted file mode 100644 index debfc468..00000000 --- a/libraries/nocap-nip66/src/kinds/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Kind30166 } from './30166'; \ No newline at end of file diff --git a/libraries/nocap-nip66/src/types/global.d.ts b/libraries/nocap-nip66/src/types/global.d.ts deleted file mode 100644 index b5de711b..00000000 --- a/libraries/nocap-nip66/src/types/global.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -declare module '@nostrwatch/logger' { - export default class Logger { - constructor(name: string, log_level?: string, split_logs?: boolean); - - logger: createLogger.LoggerInstance; - log_level: string; - split_logs: boolean; - - fatal(message: string): void; - error(message: string): void; - err(message: string): void; - warn(message: string): void; - info(message: string): void; - debug(message: string): void; - } -} \ No newline at end of file diff --git a/libraries/nocap-nip66/tsconfig.json b/libraries/nocap-nip66/tsconfig.json deleted file mode 100644 index dfd8893c..00000000 --- a/libraries/nocap-nip66/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compilerOptions": { - "target": "ES6", // Target ECMAScript 6 - "module": "ES6", // Use ES6 module system - "lib": ["ES6", "DOM"], // Include ES6 and DOM libraries - "declaration": true, // Generate .d.ts files - "sourceMap": true, // Generate source maps - "outDir": "./dist", // Output directory - "strict": true, // Enable strict type-checking options - "esModuleInterop": true, // Enable compatibility with CommonJS modules - "skipLibCheck": true, // Skip type checking of declaration files - "forceConsistentCasingInFileNames": true, - "moduleResolution": "node", // Resolve modules using Node.js style - "allowSyntheticDefaultImports": true, // Allow default imports from modules with no default export - "resolveJsonModule": true, // Include modules imported with '.json' extension - "types": ["node"] - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "**/*/*.test.js"] -} diff --git a/libraries/nocap-nip66/webpack.config.js b/libraries/nocap-nip66/webpack.config.js deleted file mode 100644 index 7f6ecbf0..00000000 --- a/libraries/nocap-nip66/webpack.config.js +++ /dev/null @@ -1,64 +0,0 @@ -const path = require('path'); - -/** - * Base configuration shared between both targets. - */ -const baseConfig = { - entry: './src/index.ts', - devtool: 'source-map', - module: { - rules: [ - { - test: /\.ts$/, - use: 'ts-loader', - exclude: /node_modules/, - }, - ], - }, - resolve: { - extensions: ['.ts', '.js'], - }, - externals: { - 'nostr-tools': 'nostr-tools', - 'nostr-geotags': 'nostr-geotags', - '@nostrwatch/logger': '@nostrwatch/logger', - }, - optimization: { - usedExports: false, - minimize: false, - }, -}; - -/** - * Configuration for the browser build. - */ -const browserConfig = { - ...baseConfig, - target: 'web', - output: { - path: path.resolve(__dirname, 'dist', 'browser'), - filename: 'index.js', - library: { - name: 'NocapdNip66', - type: 'umd', - }, - globalObject: 'this', - }, -}; - -/** - * Configuration for the Node.js build. - */ -const nodeConfig = { - ...baseConfig, - target: 'node', - output: { - path: path.resolve(__dirname, 'dist', 'node'), - filename: 'index.js', - library: { - type: 'commonjs2', - }, - }, -}; - -module.exports = [browserConfig, nodeConfig]; diff --git a/libraries/nocap/package.json b/libraries/nocap/package.json index d92194d0..46ed973e 100644 --- a/libraries/nocap/package.json +++ b/libraries/nocap/package.json @@ -4,14 +4,11 @@ "license": "MIT", "type": "module", "entry": "src/index.ts", - "main": "dist/server/cjs/index.cjs.js", - "module": "dist/server/index.js", - "types": "./dist/web/index.d.ts", "exports": { ".": { + "types": "./dist/server/types/index.d.ts", "import": "./dist/server/index.js", - "require": "./dist/server/cjs/index.cjs.js", - "types": "./dist/server/types/index.d.ts" + "require": "./dist/server/cjs/index.cjs.js" }, "./web": { "types": "./dist/web/index.d.ts", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index da82a890..409fc24d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -321,7 +321,7 @@ importers: version: link:../../libraries/nocap/adapters/default/ZEveryAdapterDefault '@nostrwatch/nocap-route66': specifier: workspace:^ - version: link:../../libraries/nocap-nip66 + version: link:../../libraries/nocap-route66 '@nostrwatch/nwcache': specifier: workspace:^ version: link:../../internal/nwcache @@ -1327,7 +1327,7 @@ importers: specifier: 0.2.2 version: 0.2.2(vitest@0.34.6) - libraries/nocap-nip66: + libraries/nocap-route66: dependencies: '@nostrwatch/logger': specifier: '*' @@ -1363,6 +1363,9 @@ importers: ts-loader: specifier: ^9.0.0 version: 9.5.1(typescript@5.7.2)(webpack@5.96.1) + tsc-alias: + specifier: ^1.8.10 + version: 1.8.10 typescript: specifier: ^5.0.0 version: 5.7.2 @@ -15319,7 +15322,7 @@ snapshots: '@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4)(webpack@5.96.1)': dependencies: - webpack: 5.96.1(esbuild@0.24.2)(webpack-cli@5.1.4) + webpack: 5.96.1(esbuild@0.24.0)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack@5.96.1) '@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.93.0)': @@ -15329,7 +15332,7 @@ snapshots: '@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.96.1)': dependencies: - webpack: 5.96.1(esbuild@0.24.2)(webpack-cli@5.1.4) + webpack: 5.96.1(esbuild@0.24.0)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack@5.96.1) '@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack@5.93.0)': @@ -15339,7 +15342,7 @@ snapshots: '@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack@5.96.1)': dependencies: - webpack: 5.96.1(esbuild@0.24.2)(webpack-cli@5.1.4) + webpack: 5.96.1(esbuild@0.24.0)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack@5.96.1) '@xtuc/ieee754@1.2.0': {} @@ -21522,7 +21525,7 @@ snapshots: semver: 7.6.3 source-map: 0.7.4 typescript: 5.7.2 - webpack: 5.96.1(esbuild@0.24.2)(webpack-cli@5.1.4) + webpack: 5.96.1(esbuild@0.24.0)(webpack-cli@5.1.4) ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.15))(@types/node@16.18.123)(typescript@4.9.4): dependencies: @@ -22343,7 +22346,7 @@ snapshots: import-local: 3.2.0 interpret: 3.1.1 rechoir: 0.8.0 - webpack: 5.96.1(esbuild@0.24.2)(webpack-cli@5.1.4) + webpack: 5.96.1(esbuild@0.24.0)(webpack-cli@5.1.4) webpack-merge: 5.10.0 webpack-merge@5.10.0: