diff --git a/apps/nocapd/package.json b/apps/nocapd/package.json index f1a6f635..3089b35d 100644 --- a/apps/nocapd/package.json +++ b/apps/nocapd/package.json @@ -9,10 +9,10 @@ "@nostrwatch/announce": "0.1.0", "@nostrwatch/controlflow": "0.2.1", "@nostrwatch/logger": "0.0.6", - "@nostrwatch/nocap": "0.5.2", + "@nostrwatch/nocap": "0.5.3", "@nostrwatch/nocap-every-adapter-default": "1.4.0", "@nostrwatch/nwcache": "0.1.2", - "@nostrwatch/publisher": "0.4.3", + "@nostrwatch/publisher": "0.5.1", "@nostrwatch/seed": "0.0.2", "@nostrwatch/utils": "0.1.3", "bluebird": "3.7.2", diff --git a/apps/nocapd/src/classes/Worker.js b/apps/nocapd/src/classes/Worker.js index 96344850..8afe0f3e 100644 --- a/apps/nocapd/src/classes/Worker.js +++ b/apps/nocapd/src/classes/Worker.js @@ -133,14 +133,8 @@ export class NWWorker { async on_success(result){ if(this.hard_stop) return this.log.debug(`on_success(): ${result.url}`) - if(this.config?.publisher?.kinds?.includes(30066) ){ - const publish30066 = new Publish.Kind30066() - await publish30066.one( result ).catch(this.log.error) - } - if(this.config?.publisher?.kinds?.includes(30166) ){ - const publish30166 = new Publish.Kind30166() - await publish30166.one( result ).catch(this.log.error) - } + const publish30166 = new Publish.Kind30166() + await publish30166.one( result ).catch(this.log.error) } async on_fail(result){ @@ -269,10 +263,8 @@ export class NWWorker { async counts(){ const counts = await this.$.queue.getJobCounts() - if(counts.prioritized + counts.active > 0) { - this.log.info(chalk.magenta.bold(`=== [queue stats] active: ${counts.active} - completed: ${ counts.completed } - failed: ${counts.failed} - prioritized: ${counts.prioritized} - delayed: ${counts.delayed} - waiting: ${counts.waiting} - paused: ${counts.paused} - total: ${counts.completed} / ${counts.active} + ${counts.waiting + counts.prioritized} ===`)) - this.show_cache_counts() - } + this.log.info(chalk.magenta.bold(`=== [queue stats] active: ${counts.active} - completed: ${ counts.completed } - failed: ${counts.failed} - prioritized: ${counts.prioritized} - delayed: ${counts.delayed} - waiting: ${counts.waiting} - paused: ${counts.paused} - total: ${counts.completed} / ${counts.active} + ${counts.waiting + counts.prioritized} ===`)) + this.show_cache_counts() return counts } @@ -302,7 +294,9 @@ export class NWWorker { } getPriority(relay){ - const {group, retries} = this.relayMeta.get(relay) + const relayMeta = this.relayMeta.get(relay) + if(!relayMeta) return this.priority + const {group, retries} = relayMeta const format = i => Math.ceil(i) if(group === 'online') return format(this.priority/2) @@ -431,13 +425,15 @@ export class NWWorker { } show_cache_counts(){ - let cacheMessage = '' - cacheMessage += `=== [cache stats] online: ${this.cache_counts.online} - ` - cacheMessage += `online & expired: ${this.cache_counts.onlineExpired} - ` - cacheMessage += `expired: ${this.cache_counts.expired} - ` - cacheMessage += `unchecked: ${this.cache_counts.unchecked} - ` - cacheMessage += `total: ${this.cache_counts.allRelays} ===` - this.log.info(chalk.blue.bold(cacheMessage)); + this.getRelays().then( () => { + let cacheMessage = '' + cacheMessage += `=== [cache stats] online: ${this.cache_counts.online} - ` + cacheMessage += `online & expired: ${this.cache_counts.onlineExpired} - ` + cacheMessage += `expired: ${this.cache_counts.expired} - ` + cacheMessage += `unchecked: ${this.cache_counts.unchecked} - ` + cacheMessage += `total: ${this.cache_counts.allRelays} ===` + this.log.info(chalk.blue.bold(cacheMessage)); + }) } qualifyNetwork(url){ diff --git a/apps/nocapd/src/daemon.js b/apps/nocapd/src/daemon.js index ee341d71..964b28bb 100644 --- a/apps/nocapd/src/daemon.js +++ b/apps/nocapd/src/daemon.js @@ -34,15 +34,15 @@ const populateQueue = async () => { const checkQueue = async () => { const counts = await $q.checker.counts() const enqueue = counts.prioritized + counts.active - if(enqueue > 0) return log.debug(`checkQueue(): ${$q.queue.name}: ${enqueue} events active`) + if(enqueue > 0) { + return log.debug(`checkQueue(): ${$q.queue.name}: ${enqueue} events active`) + } populateQueue() } const setIntervals = () => { - // intervalSyncRelays = setInterval( syncRelaysIn, timestring(config?.nocapd?.seed?.options?.events?.interval, "ms") || timestring("1h", "ms")) schedulePopulator() scheduleSyncRelays() - // intervalPopulate = setInterval( checkQueue, timestring( config?.nocapd?.checks?.options?.interval, "ms" )) } @@ -59,7 +59,6 @@ const initWorker = async () => { .set( 'worker' , new BullMQ.Worker($q.queue.name, $q.route_work.bind($q), { concurrency, connection, ...queueOpts() } ) ) - $q.queue.on('drained', () => { log.info(`queue was fucking drained.`) }) await $q.checker.drainSmart() setIntervals() await populateQueue() @@ -78,6 +77,7 @@ const stop = async(signal) => { $q.worker.pause() log.info(`shutdown progress: $q.queue.pause()`) $q.queue.pause() + log.info(`shutdown progress: close lmdb`) rcache.$.close() log.info(`shutdown progress: $q.queue.drain()`) $q.queue.drain() @@ -103,13 +103,13 @@ const maybeAnnounce = async () => { const announce = new AnnounceMonitor(conf) announce.generate() announce.sign( process.env.DAEMON_PRIVKEY ) - await announce.publish( conf.relays ) + await announce.publish( conf.relays ).catch(log.warn) } const scheduleSeconds = async (name, intervalMs, cb) => { const active = await $q.queue.getActive(); const jobIds = active.map(job => job.id); - console.log('Active Job IDs:', jobIds); + // console.log('Active Job IDs:', jobIds); log.info(`${name}: scheduling to fire every ${timestring(intervalMs, "s")} seconds`) const rule = new schedule.RecurrenceRule(); @@ -192,7 +192,7 @@ const globalHandlers = () => { } async function gracefulShutdown(signal) { - console.log(`Received ${signal}`); + // console.log(`Received ${signal}`); await stop(signal) process.exit(9); } @@ -205,9 +205,10 @@ export const Nocapd = async () => { rcache = relaycache(process.env.NWCACHE_PATH || './.lmdb') await migrate(rcache) await delay(1000) - // await maybeAnnounce() + await maybeAnnounce() await maybeBootstrap() $q = await initWorker() + $q.worker.on('drained', populateQueue) // setInterval( async () => { // const active = await $q.queue.getActive(); // const jobIds = active.map(job => job.id); diff --git a/apps/trawler/src/publish.js b/apps/trawler/src/publish.js index c58ebac3..37e38109 100644 --- a/apps/trawler/src/publish.js +++ b/apps/trawler/src/publish.js @@ -3,7 +3,7 @@ import rcache from "./relaydb.js" import config from "./config.js" import { lastPublishedId } from "./utils.js" -const p30066 = new Publish.Kind30066() +const p30166 = new Publish.Kind30166() const filterRelayProperties = (relay) => { const relay_ = {} @@ -20,13 +20,13 @@ const filterRelaysProperties = (relays) => { return relays.map( filterRelayProperties ) } -const relayIsExpired = (relay) => { - const lastPublished = rcache.cachetime.get.one( lastPublishedId(relay.url) ); - const expiry = eval(config?.trawler?.publisher?.expiry) || 4 * 60 * 60 * 10000; - if (!lastPublished) return true; - if (lastPublished < new Date() - expiry) return true; - return false; -} +// const relayIsExpired = (relay) => { +// const lastPublished = rcache.cachetime.get.one( lastPublishedId(relay.url) ); +// const expiry = eval(config?.trawler?.publisher?.expiry) || 4 * 60 * 60 * 10000; +// if (!lastPublished) return true; +// if (lastPublished < new Date() - expiry) return true; +// return false; +// } const updatePublishTimes = async (relays=[]) => { for await ( const relay of relays ) { @@ -37,15 +37,15 @@ const updatePublishTimes = async (relays=[]) => { export const publishOne = async (relay) => { relay = filterRelayProperties(relay) if(!relay) throw new Error('publishOne(): relay must be defined') - await p30066.one(relay) + await p30166.one(relay) } export const publishMany = async (relays = []) => { relays = filterRelaysProperties(relays) - const filteredRelays = relays.filter(relayIsExpired); - if (!filteredRelays.length) return; - await p30066.many(filteredRelays); - await updatePublishTimes(filteredRelays); + // const filteredRelays = relays.filter(relayIsExpired); + if (!relays.length) return; + await p30166.many(relays); + // await updatePublishTimes(relays) } export const publishAll = async () => { diff --git a/apps/trawler/src/trawler.js b/apps/trawler/src/trawler.js index 837e251e..03981100 100644 --- a/apps/trawler/src/trawler.js +++ b/apps/trawler/src/trawler.js @@ -17,6 +17,8 @@ import { SyncQueue, TrawlQueue } from "@nostrwatch/controlflow" const logger = new Logger('trawler') +const ignore = ["wss://nostr.searx.is/"] + const { $Queue:$SyncQueue, $QueueEvents:$SyncEvents } = SyncQueue() const { $Queue:$TrawlQueue, $QueueEvents:$TrawlEvents } = TrawlQueue() @@ -29,10 +31,10 @@ let promises, const addRelaysToCache = async (relayList) => { const ids = [] - relayList.forEach(async (relayObj) => { + for (const relayObj of relayList) { ids.push(await rcache.relay.insertIfNotExists(relayObj)) - }) - return ids + } + return ids.filter(id => id !== undefined) } const noteInCache = async (ev, relay, lastEvent) => { @@ -86,14 +88,14 @@ export const trawl = async function($job){ listCount = 0 $currentJob = $job - const relays = $job.data.relays + const relays = $job.data.relays.filter( relay => !ignore.includes(relay.url) ) const pool = new SimplePool(); const fetcher = NostrFetcher.withCustomPool(simplePoolAdapter(pool)); + const kinds = [ 2, 10002 ] relays.forEach( async (relay) => { promises.push(new Promise( async (resolve) => { - let lastEvent = 0 let since = await determineSince(relay) $job.updateProgress({ type: 'resuming', source: relay, since }) @@ -101,23 +103,39 @@ export const trawl = async function($job){ const it = await fetcher.allEventsIterator( [ relay ], - { kinds: [ 2, 10002 ] }, + { kinds }, { since }, { sort: true } ) for await (const ev of it) { + if(!kinds.includes(ev.kind)) { + continue + } lastEvent = setLastEvent(ev, since, lastEvent) if( await noteInCache(ev, relay, lastEvent) ) { - console.log('noteInCache() true') continue } const relayList = await relaysFromRelayList(ev) - const cacheIds = addRelaysToCache(relayList) if(relayList === false) continue listCount++ + const cacheIds = await addRelaysToCache(relayList) + + for( const id of cacheIds ) { + relaysPersisted.add(rcache.relay.get.one(id)?.url) + } + deferPersist[ev.id] = async () => await rcache.note.set.one(ev) + + if(cacheIds.length === 0) { + await deferPersist[ev.id]() + delete deferPersist[ev.id] + continue + } + + console.log(`found ${cacheIds.length} new relays`) + const roundtrip = { requestedBy: process.env.DAEMON_PUBKEY, source: relay, @@ -126,6 +144,7 @@ export const trawl = async function($job){ } if(config?.trawler?.sync?.out?.events) roundtrip.syncEventsCallback = syncEventsCallback + const data = jobData(relayList, roundtrip) await sync.relays.out(data) } @@ -139,8 +158,10 @@ export const trawl = async function($job){ })) }) await Promise.allSettled(Object.values(promises)) - console.log(`relays persisted: ${[...relaysPersisted]}`) - return [...relaysPersisted] + if(relaysPersisted.size > 0 ){ + console.log(`${relaysPersisted.size} relays persisted: ${[...relaysPersisted]}`) + } + return [...Array.from(relaysPersisted)] } const watchQueue = () => { @@ -172,5 +193,4 @@ const finish = async (result, roundtrip) => { } if(deferPersist?.[eventId]) delete deferPersist[eventId] -} - +} \ No newline at end of file diff --git a/internal/announce/src/index.ts b/internal/announce/src/index.ts index 7183dad5..0ff215fe 100644 --- a/internal/announce/src/index.ts +++ b/internal/announce/src/index.ts @@ -82,25 +82,31 @@ export class AnnounceMonitor { generate(): any { const $monReg = new Kind10166() - this.events["10166"] = $monReg.generateEvent({...this.monReg}) + $monReg.generateEvent({...this.monReg}) + this.events["10166"] = $monReg const $monRelays = new Kind10002() - if(this.monRelays.length) - this.events["10002"] = $monRelays.generateEvent([...this.monRelays]) + if(this.monRelays.length) { + $monRelays.generateEvent([...this.monRelays]) + this.events["10002"] = $monRelays + } const $monProfile = new Kind0() - if(Object.keys(this.monProfile).length) - this.events["0"] = $monProfile.generateEvent({...this.monProfile}) + if(Object.keys(this.monProfile).length) { + $monProfile.generateEvent({...this.monProfile}) + this.events["0"] = $monProfile + } return this.events } sign(sk: Uint8Array): any { if(!this.events) throw new Error("Event has not yet been generated (run generate() first)") - const signed: Event[] = [] - Object.keys(this.events).forEach( kind => { - this.events[kind] = finalizeEvent(this.events[kind], sk) + Object.values(this.events).forEach( publisher => { + console.log(publisher.signEvent()) + this.events[publisher.kind] = publisher.signEvent() }) - return signed + console.log(this.events[publisher.kind]) + return this.events[publisher.kind] } async publish( relays: string[] ): Promise { diff --git a/internal/nwcache/mixins/cachetime.js b/internal/nwcache/mixins/cachetime.js index 57e9f1c9..c268fcc0 100644 --- a/internal/nwcache/mixins/cachetime.js +++ b/internal/nwcache/mixins/cachetime.js @@ -40,7 +40,7 @@ export default class CacheTimeMixin { return [...this.db.$.select(IDS).from( this.schema ).where( where )] || [] }, one: (key, select=null) => { - select = this.parseSelect(select) + // select = this.parseSelect(select) if(!key.includes(this.id())) key = this.id(key) return this.db.$.get( key )?.v diff --git a/internal/nwcache/mixins/relay.js b/internal/nwcache/mixins/relay.js index b0931685..39dce27c 100644 --- a/internal/nwcache/mixins/relay.js +++ b/internal/nwcache/mixins/relay.js @@ -51,7 +51,7 @@ export default class RelayMixin { async patch(RelayFieldsObj) { this.validate(RelayFieldsObj) - const current = await this.db.$.get(relayId(RelayFieldsObj.url)) + const current = this.db.$.get(relayId(RelayFieldsObj.url)) if(!current) throw new Error(`Cannot patch because ${RelayFieldsObj.url} does not exist`) RelayFieldsObj.url = new URL(RelayFieldsObj.url).toString() diff --git a/internal/nwcache/mixins/retry.js b/internal/nwcache/mixins/retry.js index 421bd6ee..e21b0596 100644 --- a/internal/nwcache/mixins/retry.js +++ b/internal/nwcache/mixins/retry.js @@ -1,8 +1,5 @@ -import { operators } from "lmdb-oql"; import { Retry } from "../schemas.js" -const { $eq, $gte } = operators - export default class RetryMixin { constructor(db) { this.db = db; @@ -29,13 +26,13 @@ export default class RetryMixin { async increment(key, amt=1){ key = this.inferKey(key) - const current = await this.get(key) + const current = this.get(key) return this.set(key, current? current + amt: amt) } async decrement(key, amt=1){ key = this.inferKey(key) - const current = await this.get(key) + const current = this.get(key) return this.set(key, current? current - amt: -amt) } } \ No newline at end of file diff --git a/internal/nwcache/mixins/service.js b/internal/nwcache/mixins/service.js index ca5d2f99..c417b3f1 100644 --- a/internal/nwcache/mixins/service.js +++ b/internal/nwcache/mixins/service.js @@ -21,7 +21,7 @@ class ServiceChecker { } async is(service, status) { - const result = await this.db.$.get(serviceId(service)) + const result = this.db.$.get(serviceId(service)) if(result?.online === status) return true } diff --git a/internal/publisher/index.js b/internal/publisher/index.js index 050caadc..aff3b74b 100644 --- a/internal/publisher/index.js +++ b/internal/publisher/index.js @@ -4,20 +4,17 @@ export { Publisher } from './src/Publisher.js' export { Kind0 } from './src/kinds/Kind0.js' export { Kind10002 } from './src/kinds/Kind10002.js' export { Kind10166 } from './src/kinds/Kind10166.js' -export { Kind30066 } from './src/kinds/Kind30066.js' export { Kind30166 } from './src/kinds/Kind30166.js' //import for default export import { Kind0 } from './src/kinds/Kind0.js' import { Kind10002 } from './src/kinds/Kind10002.js' import { Kind10166 } from './src/kinds/Kind10166.js' -import { Kind30066 } from './src/kinds/Kind30066.js' import { Kind30166 } from './src/kinds/Kind30166.js' export default { Kind0, Kind10002, Kind10166, - Kind30066, Kind30166 } \ No newline at end of file diff --git a/internal/publisher/package.json b/internal/publisher/package.json index 774f2ca4..4e0896f6 100644 --- a/internal/publisher/package.json +++ b/internal/publisher/package.json @@ -1,6 +1,6 @@ { "name": "@nostrwatch/publisher", - "version": "0.4.3", + "version": "0.5.1", "type": "module", "description": "Library for publishing nostr.watch relay status and publisher registration events", "main": "index.js", diff --git a/internal/publisher/src/Publisher.js b/internal/publisher/src/Publisher.js index 90bf6415..19850e34 100644 --- a/internal/publisher/src/Publisher.js +++ b/internal/publisher/src/Publisher.js @@ -24,8 +24,8 @@ async function writeObjectToFile(obj) { export class Publisher { - constructor(key="generic"){ - this.logger = new Logger(`publisher[${key}]`) + constructor(key){ + this.logger = new Logger(`@nostrwatch/publisher: ${key}`) } tpl(){ @@ -40,29 +40,9 @@ export class Publisher { } } - // generateEvent(data){ - // this.logger.warn('generateEvent(): has not been implemented by subclass, using generic functions') - // const staticClass = eval(`kind${this.kind}`) - // let tags = [], - // content = "" - // if(staticClass?.generateTags) - // tags = Kind30066.generateTags(data) - - // if(staticClass?.generateContent) - // content = Kind30066.generateContent(data) - - // const event = { - // ...this.tpl(), - // content, - // tags - // } - - // return event - // } - - - generateEvent(){ - return this.tpl(30066) + generateEvent(data){ + data + return this.tpl(30166) } generateEvents(relays){ @@ -83,7 +63,7 @@ export class Publisher { return event } catch(e) { this.logger.err(`signEvent(): Error: ${e}`) - // console.log(event) + this.logger.info(event) } } @@ -96,12 +76,12 @@ export class Publisher { } async publishEvent(signedEvent){ - // writeObjectToFile(signedEvent); const pool = new SimplePool(); const relays = config.publisher.to_relays let pubs = pool.publish(relays, signedEvent) - // await Promise.all( pubs ).catch( e => { log.error(`publishEvent(): Error: ${e}`) }) - return Promise.allSettled( pubs ).catch( e => { log.error(`publishEvent(): Error: ${e}`) } ) + const res = await Promise.allSettled( pubs ).catch( e => { log.error(`publishEvent(): Error: ${e}`) } ) + pool.close(relays) + return res } async publishEvents(signedEvents){ @@ -116,8 +96,8 @@ export class Publisher { export class PublisherNocap extends Publisher { - constructor(){ - super() + constructor(key="generic"){ + super(key) this.logger = new Logger('publisher[nocap]') } @@ -133,7 +113,7 @@ export class PublisherNocap extends Publisher { const unsignedEvent = this.generateEvent(relay) signedEvents.push(this.signEvent(unsignedEvent)) } - await this.publishEvents(signedEvents).catch( e => { log.error(`PublisherNocap::many(): Error: ${e}`) }) + await this.publishEvents(signedEvents).catch( e => { this.logger.error(`PublisherNocap::many(): Error: ${e}`) }) } } diff --git a/internal/publisher/src/kinds/Kind10166.js b/internal/publisher/src/kinds/Kind10166.js index 227937db..3ba261bb 100644 --- a/internal/publisher/src/kinds/Kind10166.js +++ b/internal/publisher/src/kinds/Kind10166.js @@ -1,3 +1,4 @@ +import { getEventHash } from 'nostr-tools'; import { Publisher } from '../Publisher.js'; import ngeotags from 'nostr-geotags'; @@ -22,6 +23,8 @@ export class Kind10166 extends Publisher { ...this.tpl(), tags }; + let id = getEventHash(event) + event.id = id return event; } @@ -35,8 +38,8 @@ export class Kind10166 extends Publisher { let tags = []; if(data?.frequency) tags.push(['frequency', data.frequency]); - if(data?.owner) - tags.push(['o', data.owner]); + // if(data?.owner) + // tags.push(['o', data.owner]); if(data?.kinds) data?.kinds.map(kind => kind.toString()).forEach(kind => tags.push(["k", kind])); if(data?.counts) @@ -45,8 +48,8 @@ export class Kind10166 extends Publisher { data?.checks.map(check => check.toString()).forEach(check => tags.push(["c", check])); if(data?.timeouts) Object.keys(data?.timeouts || []).map(key => [ key, data.timeouts[key] ]).forEach(timeout => tags.push([ "timeout", timeout[0], timeout[1].toString() ] )); - if(data?.geo && data.geo instanceof Object) - tags = [...tags, ...ngeotags(data.geo, geoOpts)]; + // if(data?.geo && data.geo instanceof Object) + // tags = [...tags, ...ngeotags(data.geo, geoOpts)]; return tags; } diff --git a/internal/publisher/src/kinds/Kind1066.js b/internal/publisher/src/kinds/Kind1066.js index 4dde4945..6a02644d 100644 --- a/internal/publisher/src/kinds/Kind1066.js +++ b/internal/publisher/src/kinds/Kind1066.js @@ -34,7 +34,7 @@ export class Kind1066 extends Publisher { tags.push(['r', data.url]) - const aTag = ['a', `30066:${data.pubkey}:${data.url}`] + const aTag = ['a', `30166:${data.pubkey}:${data.url}`] if(data?.relayHint) aTag.push(data.relayHint) tags.push(aTag) diff --git a/internal/publisher/src/kinds/Kind30066.js b/internal/publisher/src/kinds/Kind30066.js deleted file mode 100644 index 3718a20c..00000000 --- a/internal/publisher/src/kinds/Kind30066.js +++ /dev/null @@ -1,233 +0,0 @@ -import mapper from 'object-mapper' -import ngeohash from 'ngeohash' -// import { ParseEvent } from '@nostrwatch/parse' - -import { PublisherNocap } from '../Publisher.js' - -export class Kind30066 extends PublisherNocap { - constructor(){ - const KIND = 30066 - super(KIND) - this.kind = KIND - this.discoverable = {tags: 'd'} - this.human_readable = true - this.machine_readable = true - } - - generateEvent(data){ - let tags = Kind30066.generateTags(data) - const content = Kind30066.generateContent(data) - - const event = { - ...this.tpl(), - content, - tags - } - - return event - } - - static generateContent(data){ - let content = '' - if(data?.info?.data){ - try { - content = JSON.stringify(data.info.data) - } catch(e) {} - } - return content - } - - static generateTags(data){ - let tags = [] - - const isRtt = data?.open?.data - const isDns = Object.keys(data?.dns?.data || {})?.length > 0 - const isInfo = Object.keys(data?.info?.data || {})?.length > 0 - const isGeo = Object.keys(data?.geo?.data || {})?.length > 0 - const isSsl = Object.keys(data?.ssl?.data || {})?.length > 0 - - if(!data?.url) - throw new Error('URL is required in result') - - tags.push( ['d', data.url] ) - - if(data?.network) - tags.push( ['other', 'network', data.network] ) - if(isRtt) { - if(data.open?.data) - tags.push([ 'rtt', 'open', data.open.duration.toString() ]) - if(data?.read?.data) - tags.push([ 'rtt', 'read', data.read.duration.toString() ]) - if(data?.write?.data) - tags.push([ 'rtt', 'write', data.write.duration.toString() ]) - } - - if(isInfo){ - if(data?.info){ - try { - content = JSON.stringify(data.info) - } catch(e) {} - } - - if(data?.info?.data?.name) - tags.push(['nip11', 'name', data.info.data.name]) - - if(data?.info?.data?.description) - tags.push(['nip11', 'desc', data.info.data.description]) - - if(data?.info?.data?.limitation?.payment_required === true) - tags.push(['nip11', 'payment_required', 'true']) - else - tags.push(['nip11', 'payment_required', 'false']) - - if(data?.info?.data?.limitation?.auth_required === true) - tags.push(['nip11', 'auth_required', 'true']) - else - tags.push(['nip11', 'auth_required', 'false']) - if(data?.info?.data?.pubkey && typeof data?.info?.data?.pubkey === 'string') - tags.push(['nip11', 'pubkey', data.info.data.pubkey]) - if(data?.info?.data?.contact) - tags.push(['nip11', 'contact', data.info.data.contact]) - if(data?.info?.data?.software) - tags.push(['nip11', 'software', data.info.data.software]) - if(data?.info?.data?.version) - tags.push(['nip11', 'version', data.info.data.version]) - if(data?.info?.data?.supported_nips instanceof Array && data.info.data.supported_nips.length) - tags.push(['nip11', 'supported_nips', ...data.info.data.supported_nips.map(n => n.toString())]) - if(data?.info?.data?.tags) - tags.push(['nip11', 'tags', ...data.info.data.tags]) - if(data?.info?.data?.language_tags) - tags.push(['nip11', 'language_tags', ...data.info.data.language_tags ]) - } - - if(isDns){ - if(data.dns?.data?.ipv4?.length) - tags.push(['dns', 'ipv4', data.dns.data.ipv4[data.dns.data.ipv4.length-1]]) - if(data.dns?.data?.ipv6?.length) - tags.push(['dns', 'ipv6', data.dns.data.ipv6[data.dns.data.ipv6.length-1]]) - } - - if(isGeo){ - data.geo.data = transformGeoResult(data.geo.data) - if (typeof data.geo.data?.lat === 'number' && typeof data.geo.data?.lon === 'number') - data.geo.data.geohash = ngeohash.encode(data.geo.data.lat, data.geo.data.lon) - //technically associated to DNS but often found via IP->GEO - if(data.geo.data?.as) - tags.push(['dns', 'as', data.geo.data?.as]) - if(data.geo.data?.asname) - tags.push(['dns', 'asname', data.geo.data?.asname]) - if(data.geo.data?.isp) - tags.push(['dns', 'isp', data.geo.data?.isp]) - if(data.geo.data?.isMobile) - tags.push(['dns', 'is_mobile', data.geo.data?.isMobile? 'true': 'false']) - if(data.geo.data?.isProxy) - tags.push(['dns', 'is_proxy', data.geo.data?.isProxy? 'true': 'false']) - // - const geoIgnore = ['ip', 'as', 'asname', 'isp', 'is_mobile', 'is_proxy'] - for(const prop in data.geo.data){ - let val = data.geo.data[prop] - if(geoIgnore.includes(prop)) continue - if(val instanceof Boolean){ - tags.push( [ 'geo', prop, val? 'true': 'false' ] ) - } - if(typeof val === 'string' && val.length){ - if( prop === 'regionCode' ) val = `${data.geo.data.countryCode}-${val}` - tags.push( [ 'geo', prop, val ] ) - } - if(val instanceof Number || isFloat(val)){ - tags.push( [ 'geo', prop, val.toString() ] ) - } - } - } - - if(isSsl){ - const sslIgnore = ['valid', 'days_remaining'] - const sslData = transformSslResult(data.ssl.data) - for(const prop in sslData){ - if(sslIgnore.includes(prop)) continue - - const val = sslData[prop] - - if(val instanceof Array){ - tags.push( [ 'ssl', prop, ...val ] ) - continue - } - - if(val instanceof Number){ - tags.push( [ 'ssl', prop, val.toString() ] ) - } - - if(typeof val === 'string' && isTimeString(val)){ - tags.push( [ 'ssl', prop, ( Math.round(new Date(val).getTime()/1000) ).toString() ]) - } - else if(typeof val === 'string'){ - tags.push( [ 'ssl', prop, val ] ) - } - - if(val instanceof Boolean){ - tags.push( [ 'ssl', prop, val? 'true': 'false' ] ) - } - } - } - - return tags - } - - _parse(){ - Kind30066.parse(this.event) - } - - static parse(event){ - return ParseEvent.groupedKeys(event) - } - -} - -const transformGeoResult = geo => { - const map = { - "as": "as", - "asname": "asname", - "isp": "isp", - "mobile": "isMobile", - "proxy": "isProxy", - "lat": "lat", - "lon": "lon", - "query": "ip", - "timezone": "tz", - "district": "district", - "city": "cityName", - "region": "regionCode", - "regionName": "regionName", - "country": "countryName", - "countryCode": "countryCode", - // "continent": "contentName", - // "continentCode": "continentCode" - } - return mapper(geo, map) -} - -const transformSslResult = ssl => { - const map = { - "modulus": "modulus", - "subjectaltname": "subject_alt_name", - "exponent": "exponent", - "valid_from": "valid_from", - "valid_to": "valid_to", - "fingerprint": "fingerprint", - "fingerprint256": "fingerprint256", - "fingerprint512": "fingerprint512", - "ext_key_usage": "ext_key_usage", - "serialNumber": "serial_number", - "pemEncoded": "pem_encoded" - } - return mapper(ssl, map) -} - - -const isTimeString = (str) => { - return /\b(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\s+\d{4}\s+GMT\b/.test(str); -} - -function isFloat(value) { - return typeof value === 'number' && !Number.isInteger(value); -} \ No newline at end of file diff --git a/internal/publisher/src/kinds/Kind30166.js b/internal/publisher/src/kinds/Kind30166.js index 3cdef172..2c3d5156 100644 --- a/internal/publisher/src/kinds/Kind30166.js +++ b/internal/publisher/src/kinds/Kind30166.js @@ -1,11 +1,8 @@ -import mapper from 'object-mapper' -import ngeotags from 'nostr-geotags' - import { PublisherNocap } from '../Publisher.js' export class Kind30166 extends PublisherNocap { constructor(){ - const KIND =30166 + const KIND = 30166 super(KIND) this.kind = KIND this.discoverable = {tags: ['d', 'n', 'l', 'N', 's', 't', 'R']} @@ -14,9 +11,7 @@ export class Kind30166 extends PublisherNocap { } generateEvent(data){ - const tags = Kind30166.generateTags(data) - return { ...this.tpl(), tags @@ -29,94 +24,117 @@ export class Kind30166 extends PublisherNocap { let tags = [] tags.push(['d', data.url]) + + if( data?.open?.data ){ + tags.push(['rtt-open', String(data?.open?.duration)]) + } + + if( data?.read?.data ){ + tags.push(['rtt-read', String(data?.read?.duration)]) + } + + if( data?.write?.data ){ + tags.push(['rtt-write', String(data?.write?.duration)]) + } - if(data?.network){ + if (data?.network){ tags.push(['n', data.network]) } - if(data?.info){ + + if (data?.info){ + if (data?.info?.data?.pubkey && typeof data?.info?.data?.pubkey === 'string'){ + tags.push(['p', data.info.data.pubkey]) + } + for(const nip in data.info.data.supported_nips){ tags.push(['N', String(nip)]) } for(const lang in data.info.data.language_tags){ - tags.push(['l', String(lang)]) + //TODO: validate language tags, attempt transform on invalids. + tags.push(['L', 'ISO-639-1']) + tags.push(['l', String(lang), 'ISO-639-1']) } for(const tag in data.info.data.tags){ tags.push(['t', String(tag)]) } - } - - if(data?.info?.data?.limitation?.auth_required === true){ - tags.push(['R', 'auth']) - } - else { - tags.push(['R', '!auth']) - } - - if(data?.info?.data?.limitation?.payment_required === true){ - tags.push(['R', 'payment']) - } - else { - tags.push(['R', '!payment']) + + if (data?.info?.data?.limitation?.auth_required === true){ + tags.push(['R', 'auth']) + } + else { + tags.push(['R', '!auth']) + } + + if (data?.info?.data?.limitation?.payment_required === true){ + tags.push(['R', 'payment']) + } + else { + tags.push(['R', '!payment']) + } + + if (data?.info?.data?.software){ + tags.push(['s', data.info.data.software]) + } + + if (data?.info?.data?.version ){ + tags.push(['L', 'nip11.version']) + tags.push(['l', data.info.data.version, 'nip11.version']) + } } - - if(data?.ssl && protocol === 'wss:') { - const current = data.ssl.data.valid_from < Date.now() && data.ssl.data.valid_to > Date.now() + if (data?.ssl && protocol === 'wss:') { + const validFrom = new Date(data.ssl.data.valid_from).getTime() + const validTo = new Date(data.ssl.data.valid_to).getTime() + const current = validFrom < Date.now() && validTo > Date.now() tags.push(['R', current ? 'ssl' : '!ssl']) } else if(protocol !== 'wss:') { tags.push(['R', '!ssl']) } - - if(data?.info?.data?.software){ - tags.push(['s', data.info.data.software]) + + if (data?.dns?.data?.ipv4?.length){ + tags.push(['L', 'dns.ipv4']) + for(const ipv4 of data.dns.data.ipv4){ + tags.push(['l', ipv4, 'dns.ipv4']) + } } - - if(data?.geo?.data && Object.keys(data.geo.data).length > 0){ - const geod = transformGeoResult(data.geo.data) - tags = [...tags, ...ngeotags(geod, { iso31662: true, iso31663: true, cityName: true })] + + if (data?.dns?.data?.ipv6?.length){ + tags.push(['L', 'dns.ipv6']) + for(const ipv6 of data.dns.data.ipv6){ + tags.push(['l', ipv6, 'dns.ipv6']) + } } - return tags - } + if (data?.geo?.isp){ + tags.push(['L', 'host.isp']) + tags.push(['l', data.dns?.data?.isp, 'host.isp']) + } - parse(event){ - return { - url: event.tags.find(tag => tag[0] === 'd')?.[1], - network: event.tags.find(tag => tag[0] === 'n')?.[1], - info: { - supported_nips: event.tags.filter(tag => tag[0] === 'N').map(tag => parseInt(tag[1])), - language_tags: event.tags.filter(tag => tag[0] === 'l').map(tag => tag[1]), - tags: event.tags.filter(tag => tag[0] === 't').map(tag => tag[1]), - limitation: { - auth_required: event.tags.find(tag => tag[0] === 'R' && tag[1].includes('auth')).map( tag => tag[1].includes('!')? false: true ), - payment_required: event.tags.find(tag => tag[0] === 'R' && tag[1].includes('payment')).map( tag => tag[1].includes('!')? false: true ) - }, - software: event.tags.find(tag => tag[0] === 's')?.[1] - }, - ssl: event.tags.find(tag => tag[0] === 'R' && tag[1].includes('ssl')).map( tag => tag[1].includes('!')? false: true ), - geo: ngeotags.parse(event.tags) + if (data?.geo?.as){ + tags.push(['L', 'host.as']) + tags.push(['l', data.geo?.data?.as, 'host.as']) } - } -} - -const transformGeoResult = geo => { - const map = { - "as": "asn", - "asname": "as", - "isp": "isp", - "city": "cityName", - "countryCode": "countryCode", - "country": "countryName", - "regionName": "regionName", - // "continent": "contentName", - // "continentCode": "continentCode", - "lat": "lat", - "lon": "lon", - "query": "ip" + if (data?.geo?.asn){ + tags.push(['L', 'host.asn']) + tags.push(['l', data.geo?.data?.asname, 'host.asn']) + } + + if (data?.geo?.data?.countryCode){ + tags.push(['L', 'ISO-3166-1:alpha-2']) + tags.push(['l', data?.geo?.data?.countryCode, 'ISO-3166-1:alpha-2']) + } + + if (data?.geo?.data?.cityName){ + tags.push(['L', 'watch.nostr.cityName']) + tags.push(['l', data?.geo?.data?.cityName, 'watch.nostr.cityName']) + } + + tags.push(['l', 'draft7', 'nip66.draft']) + + return tags } - return mapper(geo, map) } \ No newline at end of file diff --git a/internal/redis/.gitignore b/internal/redis/.gitignore index 2bfafae7..eac5fa1d 100644 --- a/internal/redis/.gitignore +++ b/internal/redis/.gitignore @@ -1 +1 @@ -bullboard.js +bullboard.js \ No newline at end of file diff --git a/libraries/kit/src/fetchers/monitor-relay-fetcher.test.ts b/libraries/kit/src/fetchers/monitor-relay-fetcher.test.ts deleted file mode 100644 index a9b2cf37..00000000 --- a/libraries/kit/src/fetchers/monitor-relay-fetcher.test.ts +++ /dev/null @@ -1,244 +0,0 @@ -// import { describe, it, expect, beforeEach, afterEach, vi} from 'vitest'; - -// import NDK, { NDKKind, NDKRelayDiscovery, NDKRelayMeta } from '@nostr-dev-kit/ndk'; -// import type { NDKFilter } from '@nostr-dev-kit/ndk'; - -// import type { NDKEvent, NostrEvent } from '@nostr-dev-kit/ndk'; -// import { MonitorRelayFetcher } from './monitor-relay-fetcher.js'; - -// const EVENT_10166 = {"id":"dcc56a6b1b38ab4372bb95a9095a16406f501ae6abb17763a31bc2c4a33f2964","kind":10166,"pubkey":"abcde937081142db0d50d29bf92792d4ee9b3d79a83c483453171a6004711832","created_at":1710936428,"content":"","tags":[["frequency","3600"],["o","9bbabc5e36297b6f7d15dd21b90ef85b2f1cb80e15c37fcc0c7f6c05acfd0019"],["k","30066"],["k","30166"],["c","open"],["c","read"],["c","write"],["c","info"],["c","dns"],["c","geo"],["c","ssl"],["timeout","open","30000"],["timeout","read","30000"],["timeout","write","30000"],["G","geohash"],["g","u0yjjee20","geohash"],["g","u0yjjee2","geohash"],["g","u0yjjee","geohash"],["g","u0yjje","geohash"],["g","u0yjj","geohash"],["g","u0yj","geohash"],["g","u0y","geohash"],["g","u0","geohash"],["g","u","geohash"],["G","countryCode"],["g","DE","countryCode"],["g","DEU","countryCode"],["G","countryName"],["g","Germany","countryName"],["G","cityName"],["g","Frankfurt am Main","cityName"]],"sig":"fabe5d4019e8bc4f9c9940bccc13fe07cae5eeb3fb3d03dd11756e35871558cf301c053a71db7f5828a7f41b3479514b6ca7cea658159db3eae7a3544db89f36"}; -// const EVENT_30166_A: NostrEvent = {"id":"0b44170d80ae592345f87f49b466d2f3391f53f4bb954e0cf95a65eae9242281","kind":30166,"pubkey":"abcde937081142db0d50d29bf92792d4ee9b3d79a83c483453171a6004711832","created_at":1709611188,"content":"","tags":[["d","wss://relay.weloveit.info/"],["n","clearnet"],["N","0"],["N","1"],["N","2"],["N","3"],["N","4"],["N","5"],["N","6"],["N","7"],["N","8"],["N","9"],["N","10"],["N","11"],["R","!auth"],["R","!payment"],["s","git+https://github.com/hoytech/strfry.git"],["G","geohash"],["g","u1422u57b","geohash"],["g","u1422u57","geohash"],["g","u1422u5","geohash"],["g","u1422u","geohash"],["g","u1422","geohash"],["g","u142","geohash"],["g","u14","geohash"],["g","u1","geohash"],["g","u","geohash"],["G","countryCode"],["g","FR","countryCode"],["g","FRA","countryCode"],["G","countryName"],["g","France","countryName"],["G","regionCode"],["g","FR-HDF","regionCode"]],"sig":"531e6339e1a5df2761f5e9eb0c12d6b0aa9273c522aba3ab164dcbe802d53e553acf454f5790932ebaa87004b02aee45d10354e9fda153f5f539f98211c38975"}; -// const EVENT_30166_B: NostrEvent = {"id":"ade1b3b33b9a0bf98ce851a343d99bab39fa0491cf341b2b622d7f70786ff071","kind":30166,"pubkey":"abcde937081142db0d50d29bf92792d4ee9b3d79a83c483453171a6004711832","created_at":1709769876,"content":"","tags":[["d","wss://africa.nostr.joburg/"],["n","clearnet"],["N","0"],["N","1"],["N","2"],["N","3"],["N","4"],["N","5"],["N","6"],["N","7"],["N","8"],["N","9"],["N","10"],["N","11"],["N","12"],["N","13"],["R","!auth"],["R","!payment"],["s","git+https://github.com/Cameri/nostream.git"],["G","geohash"],["g","ke7fy2z3v","geohash"],["g","ke7fy2z3","geohash"],["g","ke7fy2z","geohash"],["g","ke7fy2","geohash"],["g","ke7fy","geohash"],["g","ke7f","geohash"],["g","ke7","geohash"],["g","ke","geohash"],["g","k","geohash"],["G","countryCode"],["g","ZA","countryCode"],["g","ZAF","countryCode"],["G","countryName"],["g","South Africa","countryName"],["G","regionCode"],["g","ZA-GP","regionCode"]],"sig":"0a90ae93b3f3964072cd2eb30c949505980aa2f9df3c1c61912b41e84c0af67079e7df2719f3fc7d3914d8e8c81519228da50f3f71498ec5c323779c8c82a694"}; -// const EVENT_30066_A: NostrEvent = {"id":"e60efd752d5e86e7b0241db0cf0c11033ab8942a931d3f1704e9db55792b8756","kind":30066,"pubkey":"abcde937081142db0d50d29bf92792d4ee9b3d79a83c483453171a6004711832","created_at":1709611187,"content":"{\"contact\":\"https://relay.weloveit.info\",\"description\":\"strfry relay , streaming to relayable and others , contact me for access.\",\"name\":\"relay.weloveit.info\",\"pubkey\":\"386058f50fb3ab679f9bcae74d731dea693874688d3064a504ef5f0fd5cdecb9\",\"software\":\"git+https://github.com/hoytech/strfry.git\",\"supported_nips\":[1,2,4,9,11,12,16,20,22,28,33,40],\"version\":\"0.9.6-7-g7196547\"}","tags":[["d","wss://relay.weloveit.info/"],["other","network","clearnet"],["rtt","open","142"],["rtt","read","83"],["rtt","write","113"],["nip11","name","relay.weloveit.info"],["nip11","desc","strfry relay , streaming to relayable and others , contact me for access."],["nip11","payment_required","false"],["nip11","auth_required","false"],["nip11","pubkey","386058f50fb3ab679f9bcae74d731dea693874688d3064a504ef5f0fd5cdecb9"],["nip11","contact","https://relay.weloveit.info"],["nip11","software","git+https://github.com/hoytech/strfry.git"],["nip11","version","0.9.6-7-g7196547"],["nip11","supported_nips","1","2","4","9","11","12","16","20","22","28","33","40"],["dns","ipv4","146.59.155.110"],["dns","as","AS16276 OVH SAS"],["dns","asname","OVH"],["dns","isp","OVH SAS"],["geo","lat","50.6916"],["geo","lon","3.20151"],["geo","tz","Europe/Paris"],["geo","cityName","Roubaix"],["geo","regionCode","FR-HDF"],["geo","regionName","Hauts-de-France"],["geo","countryName","France"],["geo","countryCode","FR"],["geo","geohash","u1422u57b"],["ssl","subject_alt_name","DNS:relay.weloveit.info"],["ssl","valid_from","1705363200"],["ssl","valid_to","1713225599"],["ssl","fingerprint","A9:7D:CA:AA:25:51:9E:A3:6E:16:E8:BB:3D:CC:00:2D:4D:C8:8D:BA"],["ssl","fingerprint256","BD:B2:F7:28:E1:2C:F7:09:EA:D6:75:E6:9E:11:C6:06:EA:DD:C2:C2:10:65:A8:32:73:C0:0A:0A:05:6F:87:BD"],["ssl","fingerprint512","FE:2F:8D:0D:97:51:D2:B6:C1:76:1C:79:C9:2D:57:17:30:FB:58:82:F7:93:D3:F9:A8:D7:AD:2F:8B:F4:F2:70:89:50:18:20:22:FC:71:07:E5:29:71:E4:D1:9F:76:8D:C9:60:82:A0:68:CA:6D:23:EE:73:80:56:1C:88:A5:32"],["ssl","ext_key_usage","1.3.6.1.5.5.7.3.1","1.3.6.1.5.5.7.3.2"],["ssl","serial_number","2E8151DE5B55B92EF908B1EF2FA18A37"],["ssl","pem_encoded","-----BEGIN CERTIFICATE-----\nMIIEBzCCA4ygAwIBAgIQLoFR3ltVuS75CLHvL6GKNzAKBggqhkjOPQQDAzBLMQsw\nCQYDVQQGEwJBVDEQMA4GA1UEChMHWmVyb1NTTDEqMCgGA1UEAxMhWmVyb1NTTCBF\nQ0MgRG9tYWluIFNlY3VyZSBTaXRlIENBMB4XDTI0MDExNjAwMDAwMFoXDTI0MDQx\nNTIzNTk1OVowHjEcMBoGA1UEAxMTcmVsYXkud2Vsb3ZlaXQuaW5mbzBZMBMGByqG\nSM49AgEGCCqGSM49AwEHA0IABF04E1HPJ75PHu74Y2AA1bIZm99vcf6IpZwj88NS\nPOQwj2wmYN1inrOXbSngKOutVUj4/2/j8eMzCLRhWv7lgNmjggJ9MIICeTAfBgNV\nHSMEGDAWgBQPa+ZLzjlHrvZ+kB558DCRkshfozAdBgNVHQ4EFgQUXOC7H6t0mUqV\ntyx2NkiSyDO5lZQwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwHQYDVR0l\nBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEkGA1UdIARCMEAwNAYLKwYBBAGyMQEC\nAk4wJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9DUFMwCAYGZ4EM\nAQIBMIGIBggrBgEFBQcBAQR8MHowSwYIKwYBBQUHMAKGP2h0dHA6Ly96ZXJvc3Ns\nLmNydC5zZWN0aWdvLmNvbS9aZXJvU1NMRUNDRG9tYWluU2VjdXJlU2l0ZUNBLmNy\ndDArBggrBgEFBQcwAYYfaHR0cDovL3plcm9zc2wub2NzcC5zZWN0aWdvLmNvbTCC\nAQIGCisGAQQB1nkCBAIEgfMEgfAA7gB1AHb/iD8KtvuVUcJhzPWHujS0pM27Kdxo\nQgqf5mdMWjp0AAABjRSStXcAAAQDAEYwRAIgS01TIZYssKiTp+54o9LBjJDatHKS\npaL4A58h/PIxHTACIGwcFbcgRFI94PmwAc4JEoAwN2wSQGdip20Us2VRKTifAHUA\nO1N3dT4tuYBOizBbBv5AO2fYT8P0x70ADS1yb+H61BcAAAGNFJK2NwAABAMARjBE\nAiASIS2oD1AhciolkakcfasDxiZusBIi4DlUIZcCaanlMwIgHXcenP539DY42R8T\nYuUMG38v7gmY4tXGq4e92Ui68zQwHgYDVR0RBBcwFYITcmVsYXkud2Vsb3ZlaXQu\naW5mbzAKBggqhkjOPQQDAwNpADBmAjEAyFjwjAhWlQZsEWBk30jiBjKhd16+/jr/\nXpKuWIWH44Ed1Dl1w5zb5likWLl1115nAjEAq9NuO9VV0GEjQx3htxpEmRvrRGpp\nD7rqgGDhNGwRWU7TqFr/lgdTB+OCf885ioiz\n-----END CERTIFICATE-----"]],"sig":"eade37a0867cbff922134cad6484ada02a1659123c343b7b9d6902d1d5a94eee8f619fdff4b8728a3a2dddb09871a566637958087d6ece531268a73ac68cddb2"}; -// const EVENT_30066_B: NostrEvent = {"id":"6ade1873b48669a5a0ae908f4b972be6daa581b8c09fb58d78b4e075b83ec206","kind":30066,"pubkey":"abcde937081142db0d50d29bf92792d4ee9b3d79a83c483453171a6004711832","created_at":1709716068,"content":"{\"contact\":\"pastagringo@fractalized.net\",\"description\":\"Plebs are the white blood cells of the Bitcoin network, helping combat FUD (Fear, Uncertainty, Doubt) by being a vocal participant; helping educate and on-board no-coiners. Most plebs are Bitcoin maximalists, meaning they are proponents of supporting the adoption and growth of Bitcoin; not broader crypto. Bitcoin is secured by laser-eyed plebs. Few understand this.\",\"name\":\"relay.plebes.fans\",\"pubkey\":\"b12b632c887f0c871d140d37bcb6e7c1e1a80264d0b7de8255aa1951d9e1ff79\",\"software\":\"git+https://github.com/hoytech/strfry.git\",\"supported_nips\":[1,2,4,9,11,12,16,20,22,28,33,40],\"version\":\"0.9.6\"}","tags":[["d","wss://relay.plebes.fans/"],["other","network","clearnet"],["rtt","open","174"],["rtt","read","68"],["rtt","write","81"],["nip11","name","relay.plebes.fans"],["nip11","desc","Plebs are the white blood cells of the Bitcoin network, helping combat FUD (Fear, Uncertainty, Doubt) by being a vocal participant; helping educate and on-board no-coiners. Most plebs are Bitcoin maximalists, meaning they are proponents of supporting the adoption and growth of Bitcoin; not broader crypto. Bitcoin is secured by laser-eyed plebs. Few understand this."],["nip11","payment_required","false"],["nip11","auth_required","false"],["nip11","pubkey","b12b632c887f0c871d140d37bcb6e7c1e1a80264d0b7de8255aa1951d9e1ff79"],["nip11","contact","pastagringo@fractalized.net"],["nip11","software","git+https://github.com/hoytech/strfry.git"],["nip11","version","0.9.6"],["nip11","supported_nips","1","2","4","9","11","12","16","20","22","28","33","40"],["dns","ipv4","89.33.85.208"],["dns","as","AS199654 Oxide Group Limited"],["dns","asname","OXIDE-GROUP-LIMITED"],["dns","isp","Oxide Group Limited"],["dns","is_mobile","true"],["geo","lat","50.8955"],["geo","lon","6.06862"],["geo","tz","Europe/Amsterdam"],["geo","cityName","Eygelshoven"],["geo","regionCode","NL-LI"],["geo","regionName","Limburg"],["geo","countryName","The Netherlands"],["geo","countryCode","NL"],["geo","geohash","u1h3d15pc"],["ssl","subject_alt_name","DNS:*.plebes.fans, DNS:plebes.fans"],["ssl","valid_from","1708852049"],["ssl","valid_to","1716628048"],["ssl","fingerprint","11:E7:95:B1:D9:BD:2F:54:27:D4:F7:06:A5:E8:7E:EF:96:6C:68:D2"],["ssl","fingerprint256","BC:B5:4B:3C:16:5F:84:04:07:D2:B9:C8:72:4F:9D:28:EA:91:BF:28:ED:CE:E9:27:F2:F7:D4:76:61:C1:F1:B5"],["ssl","fingerprint512","56:37:D5:BC:71:02:89:FF:9F:41:3D:76:45:05:C6:10:4E:42:94:9A:6E:A9:3A:84:13:A3:B5:31:06:25:30:C9:19:1B:20:AB:34:E4:9B:E4:BF:25:17:C1:5F:A1:20:17:AA:DF:53:D2:61:02:F4:85:3F:05:68:61:D2:22:4E:F0"],["ssl","ext_key_usage","1.3.6.1.5.5.7.3.1","1.3.6.1.5.5.7.3.2"],["ssl","serial_number","03F4EA556F5EA3D1B34A02F13DB63981D7E8"],["ssl","pem_encoded","-----BEGIN CERTIFICATE-----\nMIIERjCCAy6gAwIBAgISA/TqVW9eo9GzSgLxPbY5gdfoMA0GCSqGSIb3DQEBCwUA\nMDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD\nEwJSMzAeFw0yNDAyMjUwOTA3MjlaFw0yNDA1MjUwOTA3MjhaMBgxFjAUBgNVBAMM\nDSoucGxlYmVzLmZhbnMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATKMGI0DlBW92gv\njdsJ6F9tvp93u5Fr/xr/e8jtTqG77e01d+Uj0eUodbECdUiJEjp5CWw/+18aac3/\nYb1Ml5lxCwAPuFIyLn43NxLdUYLTrz9FWfkwIntFbs4bFs8lO4KjggIcMIICGDAO\nBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwG\nA1UdEwEB/wQCMAAwHQYDVR0OBBYEFCeOv1UaN9QfxKiRmpFBHMrgrwy3MB8GA1Ud\nIwQYMBaAFBQusxe3WFbLrlAJQOYfr52LFMLGMFUGCCsGAQUFBwEBBEkwRzAhBggr\nBgEFBQcwAYYVaHR0cDovL3IzLm8ubGVuY3Iub3JnMCIGCCsGAQUFBzAChhZodHRw\nOi8vcjMuaS5sZW5jci5vcmcvMCUGA1UdEQQeMByCDSoucGxlYmVzLmZhbnOCC3Bs\nZWJlcy5mYW5zMBMGA1UdIAQMMAowCAYGZ4EMAQIBMIIBBAYKKwYBBAHWeQIEAgSB\n9QSB8gDwAHYAouK/1h7eLy8HoNZObTen3GVDsMa1LqLat4r4mm31F9gAAAGN37vm\n2AAABAMARzBFAiEA5MYKc2sU6MivaOHyM7h8lhdtq7sCjvFc892BSIgT50gCICB7\nWRQqV3KpDVrKqqxlv3u/QmeOsEb479xFcDP0NErGAHYA7s3QZNXbGs7FXLedtM0T\nojKHRny87N7DUUhZRnEftZsAAAGN37vmzgAABAMARzBFAiAH0TdER3FsIMrmKfZH\nBnzgCHy0Z1POZrpVZQSNy48ILwIhAL3xq0V4xCrCipONS6ugbkAvTAWAmyeTQIlr\nLfudeziFMA0GCSqGSIb3DQEBCwUAA4IBAQCkh0cvEV+PvrOj1i/j/rUJ6OMqEF8w\nxCn+l+4Ed1vKs8eDisZczNQoZLofzoXr7+cPCSA5ocdRErIVH8jMhqpsvVR09Kmn\n6SriIV+a8V+ZR7OaQE1/eymCmSWYvPG1+P6pYnmCB/wpitqIs4nFoSLLgdD+5Lne\nOaypNqgoGvV87hzxwoi9xLT3Kxj6JJ7m4UHwnwUfgTZ3W3uwbK70TiVlM7xIXBak\naZp1zbedD1Vvjz/QYDeVC3CS4cMXRQmDZQjH2wjxaNT5LaVHoVO2ftu4OnyJH73C\nheqtjv8Auxk5x50Q1KvYTkSPKgYopT8oV70OBmPtsGAwO3XVR6Okby+S\n-----END CERTIFICATE-----"]],"sig":"7a3cf106ca1be0f07b68b61f7e5bd47c93de9eeb71f643d36d6dd2a574dfde0f5e1a28bee1a355b5d9e4b44ba6244a364739af8039567d52689861f1e56be87f"}; - -// let NDKEVENT_30066_1: NDKRelayMeta, -// NDKEVENT_30066_2: NDKRelayMeta, -// NDKEVENT_30166_1: NDKRelayDiscovery, -// NDKEVENT_30166_2: NDKRelayDiscovery; - -// export const setupMetaEvents = (ndk: NDK): NDKRelayMeta[] => { -// return [ -// new NDKRelayMeta(ndk, EVENT_30066_A), -// new NDKRelayMeta(ndk, EVENT_30066_B) -// ]; -// }; - -// export const setupDiscoveryEvents = (ndk: NDK): NDKRelayDiscovery[] => { -// return [ -// new NDKRelayDiscovery(ndk, EVENT_30166_A), -// new NDKRelayDiscovery(ndk, EVENT_30166_B) -// ]; -// }; - -// export const testEventsMeta = [ EVENT_30066_A, EVENT_30066_B ]; -// export const testEventsDiscovery = [ EVENT_30166_A, EVENT_30166_B ]; -// export const testEvents = [ ...testEventsMeta, ...testEventsDiscovery ]; - -// const explicitRelayUrls = ["wss://history.nostr.watch", "wss://purplepag.es"]; - -// describe('MonitorRelayFetcher', () => { - -// let MonitorRelayFetcher: MonitorRelayFetcher; -// const ndk = new NDK({ explicitRelayUrls }); -// [ NDKEVENT_30066_1 , NDKEVENT_30066_2 ] = setupMetaEvents(ndk); -// [ NDKEVENT_30166_1, NDKEVENT_30166_2 ] = setupDiscoveryEvents(ndk); - -// const fetchEventsMock = vi.spyOn(ndk, "fetchEvents"); -// // const fetchEventMock = vi.spyOn(ndk, "fetchEvent"); -// const mockConsoleError = vi.spyOn(console, 'error').mockImplementation(() => {}); - -// beforeEach(() => { -// MonitorRelayFetcher = new MonitorRelayFetcher(ndk, EVENT_10166); -// }); - -// describe('getters', ()=>{ -// it('should get pubkey', () => { -// expect(MonitorRelayFetcher.pubkey).toBe("abcde937081142db0d50d29bf92792d4ee9b3d79a83c483453171a6004711832"); -// }); - -// it('should get frequency', () => { -// expect(MonitorRelayFetcher.frequency).toBe(3600); -// }); - -// it('should get operator', () => { -// expect(MonitorRelayFetcher.pubkey).toBe("9bbabc5e36297b6f7d15dd21b90ef85b2f1cb80e15c37fcc0c7f6c05acfd0019"); -// }); - -// it('should get timeouts', () => { -// expect(MonitorRelayFetcher.timeout).toEqual({ -// open: 30000, -// read: 30000, -// write: 30000 -// }); -// }); - -// it('should get kinds', () => { -// expect(MonitorRelayFetcher.kinds).toContain(NDKKind.RelayMeta); -// expect(MonitorRelayFetcher.kinds).toContain(NDKKind.RelayDiscovery); -// }); - -// // it('should get geo', () => { -// // expect(MonitorRelayFetcher.getGeo()).toEqual( expect.arrayContaining([["g", "u0yjjee20", "geohash"]])); -// // }); - -// }); - -// describe('setters', ()=>{ -// it('should set pubkey', () => { -// expect(MonitorRelayFetcher.pubkey).toBe("abcde937081142db0d50d29bf92792d4ee9b3d79a83c483453171a6004711832"); -// }); - -// it('should set and get frequency correctly', () => { -// expect(MonitorRelayFetcher.frequency).toBe(3600); -// }); - -// it('should set operator correctly', () => { -// expect(MonitorRelayFetcher.pubkey).toBe("9bbabc5e36297b6f7d15dd21b90ef85b2f1cb80e15c37fcc0c7f6c05acfd0019"); -// }); - -// it('should set timeouts correctly', () => { -// expect(MonitorRelayFetcher.timeout).toEqual({ -// open: 30000, -// read: 30000, -// write: 30000 -// }); -// }); - -// it('should set kinds correctly', () => { -// // MonitorRelayFetcher.kinds = [NDKKind.RelayMeta, NDKKind.RelayDiscovery]; -// expect(MonitorRelayFetcher.kinds).toContain(NDKKind.RelayMeta); -// expect(MonitorRelayFetcher.kinds).toContain(NDKKind.RelayDiscovery); -// }); - -// // it('should set timeouts correctly', () => { -// // expect(MonitorRelayFetcher.getGeo()).toEqual( expect.arrayContaining([["g", "u0yjjee20", "geohash"]])); -// // }); -// }); - -// describe('@private', () => { -// let MonitorRelayFetcher: MonitorRelayFetcher; - -// beforeEach(() => { -// MonitorRelayFetcher = new MonitorRelayFetcher(ndk); -// }); - -// afterEach(() => { -// vi.resetAllMocks(); -// }); - -// describe('reduceRelayEventsToRelayStrings', () => { -// it('should return a relay list', async () => { -// const result = MonitorRelayFetcher['_reduceRelayEventsToRelayStrings'](new Set([NDKEVENT_30166_1, NDKEVENT_30166_2]) as Set); -// expect(result).toBeInstanceOf(Set); -// expect(result).toEqual(new Set(['wss://relay.weloveit.info/', 'wss://africa.nostr.joburg/'])); -// }); -// }); - -// describe('invalidRelayFetch', () => { -// it('should return undefined and log error', () => { -// const result = MonitorRelayFetcher['_invalidRelayFetch']("testMethod", "testError"); -// expect(result).toBeUndefined(); -// expect(mockConsoleError).toHaveBeenCalledWith("testMethod: testError"); -// }); -// }); - -// describe('nip66Filter', () => { -// it('should generate correct filter based on input kinds', () => { -// const filter = MonitorRelayFetcher['_nip66Filter']( [NDKKind.RelayMeta], { limit: 1 }, { "#n": ["clearnet"] } ); -// expect(filter).toHaveProperty('kinds', expect.arrayContaining([NDKKind.RelayMeta])); -// expect(filter).toHaveProperty('limit', 1); -// expect(filter).toHaveProperty('#n', expect.arrayContaining(["clearnet"])); -// }); -// }); -// }); - -// describe('@public', () => { - -// beforeEach(() => { -// monitorRelayFetcher = new MonitorRelayFetcher(ndk, EVENT_10166); -// fetchEventsMock.mockImplementation((filter): Promise> => { -// let result: NDKEvent[] = [NDKEVENT_30066_1, NDKEVENT_30066_2, NDKEVENT_30166_1, NDKEVENT_30166_2]; -// filter = filter as NDKFilter; -// const kinds = filter?.kinds?.map(kind => kind as number); -// result = result.filter( event => kinds?.includes(event.kind as number) ); -// const f = filter as NDKFilter; -// if (f instanceof Object) { -// if (f?.["#s"]) { -// result = result -// .filter(event => 'software' in event && f?.["#s"]?.includes((event as any)?.software || "")); -// } -// if (f?.["#d"]) { -// result = result.filter(event => 'url' in event && f?.["#d"]?.includes((event as any)?.url || "")); -// } -// } -// return Promise.resolve(new Set(result)); -// }); -// }); - -// describe('isMonitorValid',()=>{ -// it('should return true when all conditions are met', () => { -// expect(monitorRelayFetcher.isMonitorValid()).toBe(true); -// }); - -// it('should return false when not all conditions are met', () => { -// MonitorRelayFetcher.frequency = undefined; -// expect(MonitorRelayFetcher.isMonitorValid()).toBe(false); -// }); -// }); - -// describe('fetchOnlineRelays', ()=>{ -// it('should return a set of relay events', async () => { -// const result = await MonitorRelayFetcher['fetchOnlineRelays'](); -// expect(result).toBeInstanceOf(Set); -// expect(result?.size).toBe(2); -// expect(result).toEqual(new Set(['wss://relay.weloveit.info/', 'wss://africa.nostr.joburg/'])); -// }); - -// it('should return a filtered set of relay events', async () => { -// const filter: NDKFilter = { "#s": ['git+https://github.com/Cameri/nostream.git'] }; -// const result = await MonitorRelayFetcher.fetchOnlineRelays(filter); -// expect(result).toBeInstanceOf(Set); -// expect(result?.size).toBe(1); -// expect(result).toEqual(new Set(['wss://africa.nostr.joburg/'])); -// }); -// }); - -// describe('fetchRelayMeta', ()=>{ -// it('should return a NDKRelayMeta[] with length=1 when given one relay', async () => { -// const result = await MonitorRelayFetcher.fetchRelayMeta('wss://relay.weloveit.info/'); -// const _result = Array.from(result || []); -// expect(_result?.length).toEqual(1); -// expect(_result?.[0]?.url).toEqual('wss://relay.weloveit.info/'); -// }); - -// it('should return a NDKRelayMeta[] with length=2 given two relays', async () => { -// const result = await MonitorRelayFetcher.fetchRelayMeta(['wss://relay.plebes.fans/', 'wss://relay.weloveit.info/']); -// const _result = Array.from(result || []); -// expect(_result?.length).toEqual(2); -// expect(_result?.[0]?.url).toEqual('wss://relay.weloveit.info/'); -// expect(_result?.[1]?.url).toEqual('wss://relay.plebes.fans/'); -// }); -// }); - -// describe('fetchOnlineRelaysMeta', ()=>{ -// it('should return a set of NDKRelayMeta events', async () => { -// const result = await MonitorRelayFetcher.fetchOnlineRelaysMeta(); -// const _result = Array.from(result || []); -// expect(_result?.length).toEqual(2); -// expect(_result?.[0]?.url).toEqual('wss://relay.weloveit.info/'); -// expect(_result?.[1]?.url).toEqual('wss://relay.plebes.fans/'); -// }); -// }); -// }); - -// describe('@static', () => { -// describe('from', () => { -// it('should return a MonitorRelayFetcher instance', () => { -// const newRelayMonitor = MonitorRelayFetcher.from(NDKEVENT_30066_1); -// expect(newRelayMonitor).toBeInstanceOf(MonitorRelayFetcher); -// expect(newRelayMonitor?.checks).toBeDefined(); -// }); -// }); -// }); -// }); \ No newline at end of file diff --git a/libraries/schemata/nip11/.gitignore b/libraries/nip11/.gitignore similarity index 100% rename from libraries/schemata/nip11/.gitignore rename to libraries/nip11/.gitignore diff --git a/libraries/nip11/README.md b/libraries/nip11/README.md new file mode 100644 index 00000000..b593c750 --- /dev/null +++ b/libraries/nip11/README.md @@ -0,0 +1,17 @@ +# @nostrwatch/nip11 + +A `JSON-SCHEMA` for the `NIP-11` standard that includes code generation. Generates packages for [zod](./packages/zod) and [Flow](./packages/flow/) as a bonus. + +# Install + +``` +npm install @nostrwatch/nip11 +pnpm install @nostrwatch/nip11 +yarn add @nostrwatch/nip11 +``` + +# Usage +``` +import Nip11 from "@nostrwatch/nip11" + +nip11Json = { ... } diff --git a/libraries/nip11/package.json b/libraries/nip11/package.json new file mode 100644 index 00000000..1a0124dc --- /dev/null +++ b/libraries/nip11/package.json @@ -0,0 +1,40 @@ +{ + "name": "@nostrwatch/nip11", + "type": "module", + "version": "0.0.1", + "main": "dist/bundle.js", + "license": "MIT", + "dependencies": { + "@nostrwatch/nip11-schema": "0.0.1", + "ajv": "8.17.1", + "quicktype": "23.0.170", + "quicktype-core": "23.0.170", + "webpack": "5.93.0", + "webpack-cli": "5.1.4", + "yaml": "2.4.5" + }, + "devDependencies": { + "ts-loader": "^9.2.6", + "typedoc": "^0.22.15", + "typescript": "^4.5.2", + "vitest": "^0.20.2", + "yaml-convert": "1.0.1", + "yaml-loader": "0.8.1" + }, + "scripts": { + "json-schema": "node ./scripts/json-schema.js", + "qt": "yarn convert && yarn qt:types && yarn qt:ts && yarn qt:flow && yarn qt:zod", + "qt:types": "quicktype --src dist/schema.json -o src/types.ts --src-lang schema --just-types", + "qt:ts": "quicktype -s schema ./dist/schema.json -o src/serialize.ts --lang ts", + "qt:flow": "quicktype -s schema ./dist/schema.json -o ./packages/flow/src/index.ts --lang flow", + "qt:zod": "quicktype -s schema ./dist/schema.json -o ./packages/zod/src/index.ts --lang typescript-zod", + "clean": "rimraf src/**/*.js", + "build": "yarn clean && yarn json-schema && yarn qt && tsc && webpack --config webpack.config.mjs", + "convert": "yaml-convert --input src/schema.yaml --output dist/schema.json", + "test": "vitest", + "docs": "typedoc" + }, + "peerDependencies": { + "typescript": "^4.5.2" + } +} diff --git a/libraries/schemata/nip11/packages/flow/package.json b/libraries/nip11/packages/flow/package.json similarity index 100% rename from libraries/schemata/nip11/packages/flow/package.json rename to libraries/nip11/packages/flow/package.json diff --git a/libraries/nip11/packages/zod/README.md b/libraries/nip11/packages/zod/README.md new file mode 100644 index 00000000..e69de29b diff --git a/libraries/schemata/nip11/packages/zod/package.json b/libraries/nip11/packages/zod/package.json similarity index 100% rename from libraries/schemata/nip11/packages/zod/package.json rename to libraries/nip11/packages/zod/package.json diff --git a/libraries/nip11/scripts/json-schema.js b/libraries/nip11/scripts/json-schema.js new file mode 100755 index 00000000..a5dcfdac --- /dev/null +++ b/libraries/nip11/scripts/json-schema.js @@ -0,0 +1,9 @@ +import fs from 'fs/promises' + +import Nip11Schema from '@nostrwatch/nip11-schema' + +const main = async () => { + await fs.writeFile('./dist/schema.json', JSON.stringify(Nip11Schema), 'utf8'); +} + +main(); \ No newline at end of file diff --git a/libraries/nip11/src/index.ts b/libraries/nip11/src/index.ts new file mode 100644 index 00000000..bea3a8fb --- /dev/null +++ b/libraries/nip11/src/index.ts @@ -0,0 +1,54 @@ +import Nip11Schema from '@nostrwatch/nip11-schema' + +import { Types as Nip11Type } from './types'; +import * as Serialize from './serialize'; + +export { Nip11Type, Serialize }; + +import Ajv, { ValidateFunction } from 'ajv'; + +type Schema = object; + +class Nip11 { + private ajv: Ajv; + private schema: Schema | null = Nip11Schema; + private validator: ValidateFunction | null | undefined; + private nip11: Nip11Type | null = null; + + constructor(nip11?: Nip11Type) { + this.ajv = new Ajv(); + if(nip11) this.nip11 = nip11 + if(!this.schema) return + this.validator = this.ajv.compile(this.schema); + } + + /** + * Validate a NIP-11 "Information Document" JSON. Will validate the NIP-11 schema provided during instantiation if it exists, otherwise, accepts JSON as a parameter + * + * @param data (optional) + * + * @returns + */ + async validate(json?: any): Promise<{ valid: boolean; errors: ValidateFunction['errors'] }> { + if(json === undefined) { + if(!this?.nip11) { + throw new Error('No NIP-11 JSON provided as argument and no NIP-11 schema was provided during instantiation. Cannot validate (either instantiate with NIP-11 json or call this method with NIP-11 json).') + } + json = this.nip11 + } + if (!this.validator) { + throw new Error('Validator not initialized. Ensure schema is loaded correctly.'); + } + const valid = this.validator(json); + return { valid, errors: this.validator.errors }; + } + + async deserialize(json: string): Promise { + return Serialize.Convert.toSerialize(json); + } + + async serialize(json: Nip11Type): Promise { + return Serialize.Convert.serializeToJson(json); + } + +} \ No newline at end of file diff --git a/libraries/nip11/src/schema.yaml b/libraries/nip11/src/schema.yaml new file mode 100644 index 00000000..69161c81 --- /dev/null +++ b/libraries/nip11/src/schema.yaml @@ -0,0 +1,133 @@ +$id: "https://nip11.schemata.nostr.watch" +$schema: "http://json-schema.org/draft-07/schema#" +title: "NIP-11" +type: "object" +additionalProperties: false +required: + - name + - description + - pubkey + - contact + - supported_nips + - software + - version +properties: + name: + type: "string" + description: + type: "string" + pubkey: + type: "string" + contact: + type: "string" + supported_nips: + type: "array" + items: + type: "number" + software: + type: "string" + version: + type: "string" + retention: + type: "array" + items: + $ref: "#/$defs/retent" + relay_country: + type: "array" + items: + type: "string" + icon: + anyOf: + - $ref: "#/$defs/saneUrl" + - format: "hostname" + language_tags: + type: "array" + items: + type: "string" + tags: + type: "array" + items: + type: "string" + posting_policy: + anyOf: + - $ref: "#/$defs/saneUrl" + - format: "hostname" + limitation: + type: "object" + additionalProperties: false + properties: + max_message_length: + type: "number" + max_subscriptions: + type: "number" + max_filters: + type: "number" + max_limit: + type: "number" + max_subid_length: + type: "number" + max_event_tags: + type: "number" + max_content_length: + type: "number" + min_pow_difficulty: + type: "number" + auth_required: + type: "boolean" + payment_required: + type: "boolean" + restricted_writes: + type: "boolean" + created_at_lower_limit: + type: "number" + created_at_upper_limit: + type: "number" + payments_url: + anyOf: + - $ref: "#/$defs/saneUrl" + - format: "hostname" + fees: + type: "object" + additionalProperties: false + properties: + admission: + $ref: "#/$defs/fee" + subscription: + $ref: "#/$defs/fee" + publication: + $ref: "#/$defs/fee" + +$defs: + saneUrl: + format: "uri" + pattern: "^https?://" + fee: + type: "array" + items: + type: "object" + properties: + amount: + type: "number" + unit: + type: "string" + period: + type: "number" + kinds: + type: "array" + items: + type: "number" + retent: + type: "object" + properties: + kinds: + type: "array" + items: + anyOf: + - type: "number" + - type: "array" + items: + type: "number" + count: + type: "number" + time: + type: "number" diff --git a/libraries/nip11/tsconfig.json b/libraries/nip11/tsconfig.json new file mode 100644 index 00000000..dccc6b8e --- /dev/null +++ b/libraries/nip11/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "Node", + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/libraries/schemata/nip11/webpack.config.mjs b/libraries/nip11/webpack.config.mjs similarity index 100% rename from libraries/schemata/nip11/webpack.config.mjs rename to libraries/nip11/webpack.config.mjs diff --git a/libraries/nocap/adapters/default/EveryAdapterDefault/package.json b/libraries/nocap/adapters/default/EveryAdapterDefault/package.json index 4c7e8642..d1bcb383 100644 --- a/libraries/nocap/adapters/default/EveryAdapterDefault/package.json +++ b/libraries/nocap/adapters/default/EveryAdapterDefault/package.json @@ -1,6 +1,6 @@ { "name": "@nostrwatch/nocap-every-adapter-default", - "version": "1.4.0", + "version": "1.4.1", "type": "module", "main": "index.js", "license": "MIT", diff --git a/libraries/nocap/adapters/default/SslAdapterDefault/index.js b/libraries/nocap/adapters/default/SslAdapterDefault/index.js index 892557a5..dff03c2e 100644 --- a/libraries/nocap/adapters/default/SslAdapterDefault/index.js +++ b/libraries/nocap/adapters/default/SslAdapterDefault/index.js @@ -15,7 +15,10 @@ class SslAdapterDefault { const hostname = url.hostname; const timeout = this.$.config?.timeout.ssl || 1000; + console.log("SERIOUSLY WTF", this.$.url, url.protocol ); + if(url.protocol === 'ws:'){ + this.$.logger.warn('Cannot check SSL for unsecured websocket.'); return this.$.finish('ssl', { status: "error", message: "Cannot check SSL for unsecured websocket.", data: {} }); } diff --git a/libraries/nocap/adapters/default/SslAdapterDefault/package.json b/libraries/nocap/adapters/default/SslAdapterDefault/package.json index b4f06bc8..b7018c87 100644 --- a/libraries/nocap/adapters/default/SslAdapterDefault/package.json +++ b/libraries/nocap/adapters/default/SslAdapterDefault/package.json @@ -1,6 +1,6 @@ { "name": "@nostrwatch/nocap-ssl-adapter-default", - "version": "1.1.0", + "version": "1.4.1", "main": "index.js", "type": "module", "license": "MIT", diff --git a/libraries/nocap/package.json b/libraries/nocap/package.json index 61b0466f..57745ba3 100644 --- a/libraries/nocap/package.json +++ b/libraries/nocap/package.json @@ -1,6 +1,6 @@ { "name": "@nostrwatch/nocap", - "version": "0.5.2", + "version": "0.5.3", "main": "src/index.js", "type": "module", "license": "MIT", @@ -10,7 +10,7 @@ }, "dependencies": { "@nostrwatch/logger": "0.0.6", - "@nostrwatch/nocap-every-adapter-default": "1.3.2", + "@nostrwatch/nocap-every-adapter-default": "1.4.1", "fetch-h2": "3.0.2", "get-ssl-certificate": "2.3.3", "jest": "29.7.0", diff --git a/libraries/nocap/src/classes/Base.js b/libraries/nocap/src/classes/Base.js index 32a96cb1..fa4a1a14 100644 --- a/libraries/nocap/src/classes/Base.js +++ b/libraries/nocap/src/classes/Base.js @@ -63,7 +63,7 @@ export default class Base { this.timeouts = new TimeoutHelper(this.session) this.latency = new LatencyHelper(this.session) this.promises = new DeferredWrapper(this.session, this.timeouts) - this.logger = new Logger(this.url, this.config.logLevel) + this.logger = new Logger(`@nostrwatch/nocap: ${this.url}`, this.config.logLevel) this.count = new Counter(this.session, [...this.checks]) } diff --git a/libraries/nocap/src/classes/DeferredWrapper.js b/libraries/nocap/src/classes/DeferredWrapper.js index 59851875..f674a202 100644 --- a/libraries/nocap/src/classes/DeferredWrapper.js +++ b/libraries/nocap/src/classes/DeferredWrapper.js @@ -7,7 +7,7 @@ export class DeferredWrapper { this.promises = {} this.timeout = $timeout this.$session = $session - this.logger = new Logger(this.$session.url) + this.logger = new Logger(`@nostrwatch/nocap::Deferred: ${this.$session.url}`) } add(key, timeout, timeoutCb){ diff --git a/libraries/nocap/src/interfaces/ConfigInterface.js b/libraries/nocap/src/interfaces/ConfigInterface.js index b8ba78a3..d0d84da8 100644 --- a/libraries/nocap/src/interfaces/ConfigInterface.js +++ b/libraries/nocap/src/interfaces/ConfigInterface.js @@ -4,13 +4,13 @@ export const ConfigDefaults = { logLevel: 'info', checked_by: '', timeout: { - open: 10000, - read: 10000, - write: 10000, - info: 10000, - dns: 5000, - geo: 5000, - ssl: 5000 + open: 5000, + read: 5000, + write: 5000, + info: 5000, + dns: 2000, + geo: 1000, + ssl: 1000 }, tor: {}, adapterOptions: { diff --git a/libraries/schemata/nip11/package.json b/libraries/schemata/nip11/package.json index 9eec159a..3ff95441 100644 --- a/libraries/schemata/nip11/package.json +++ b/libraries/schemata/nip11/package.json @@ -1,37 +1,20 @@ { - "name": "@nostrwatch/nip11", - "type": "module", + "name": "@nostrwatch/nip11-schema", "version": "0.0.1", - "main": "dist/bundle.js", + "main": "index.js", "license": "MIT", "dependencies": { - "ajv": "8.17.1", - "quicktype": "23.0.170", + "json-loader": "0.5.7", + "ts-loader": "9.5.1", + "typescript": "5.5.4", "webpack": "5.93.0", "webpack-cli": "5.1.4", - "yaml": "2.4.5" - }, - "devDependencies": { - "ts-loader": "^9.2.6", - "typedoc": "^0.22.15", - "typescript": "^4.5.2", - "vitest": "^0.20.2", - "yaml-convert": "1.0.1", - "yaml-loader": "0.8.1" + "webpack-merge": "6.0.1", + "webpack-node-externals": "3.0.0", + "yaml-convert": "1.0.1" }, "scripts": { - "qt": "yarn convert && yarn qt:types && yarn qt:ts && yarn qt:flow && yarn qt:zod", - "qt:types": "quicktype --src dist/schema.json -o src/types.ts --src-lang schema --just-types", - "qt:ts": "quicktype -s schema ./dist/schema.json -o src/serialize.ts --lang ts", - "qt:flow": "quicktype -s schema ./dist/schema.json -o ./packages/flow/src/index.ts --lang flow", - "qt:zod": "quicktype -s schema ./dist/schema.json -o ./packages/zod/src/index.ts --lang typescript-zod", - "clean": "rimraf src/**/*.js", - "build": "yarn clean && yarn qt && tsc && webpack --config webpack.config.mjs", - "convert": "yaml-convert --input src/schema.yaml --output dist/schema.json", - "test": "vitest", - "docs": "typedoc" - }, - "peerDependencies": { - "typescript": "^4.5.2" + "prebuild": "mkdir -p dist && yaml-convert --input src/schema.yaml --output src/schema.json && cp src/schema.json dist/schema.json", + "build": "tsc && webpack --mode development --stats verbose" } } diff --git a/libraries/schemata/nip11/src/index.ts b/libraries/schemata/nip11/src/index.ts index cbcacd4b..af2b6729 100644 --- a/libraries/schemata/nip11/src/index.ts +++ b/libraries/schemata/nip11/src/index.ts @@ -1,94 +1,2 @@ -export * as Nip11Types from './types'; -export * as Nip11Serialize from './serialize'; - -import Ajv, { ValidateFunction } from 'ajv'; -import * as YAML from 'yaml'; - - -let fs: typeof import('fs').promises | undefined; -if (typeof window === 'undefined') { - fs = require('fs').promises; -} - -declare const define: { - (deps: string[], factory: (...modules: any[]) => any): void; - amd?: boolean; -}; - -type Schema = object; - -declare global { - interface Window { - Ajv: typeof Ajv; - YAML: typeof YAML; - nip11: typeof nip11; - } -} - -async function loadSchema(schemaPath: string): Promise { - if (typeof fetch !== 'undefined') { - const response = await fetch(schemaPath); - return YAML.parse(await response.text()); - } else if (fs) { - const schemaContent = await fs.readFile(schemaPath, 'utf8'); - return YAML.parse(schemaContent); - } else { - throw new Error('Cannot load schema in this environment'); - } -} - -class nip11 { - private ajv: Ajv; - private schemaPath: string; - private schema: Schema | null; - private validator: ValidateFunction | null; - - constructor(schemaPath: string) { - this.ajv = new Ajv(); - this.schemaPath = schemaPath; - this.schema = null; - this.validator = null; - } - - async loadSchema(): Promise { - this.schema = await loadSchema(this.schemaPath); - if (this.schema) { - this.validator = this.ajv.compile(this.schema); - } else { - throw new Error('Schema could not be loaded or is invalid'); - } - } - - async validate(data: any): Promise<{ valid: boolean; errors: ValidateFunction['errors'] }> { - if (!this.schema) { - await this.loadSchema(); - } - if (!this.validator) { - throw new Error('Validator not initialized. Ensure schema is loaded correctly.'); - } - const valid = this.validator(data); - return { valid, errors: this.validator.errors }; - } - - -} - - -(function (root: any, factory: (Ajv: any, YAML: any, fs?: any) => any) { - if (typeof define === 'function' && define.amd) { - // AMD - define(['ajv', 'yaml'], factory); - } else if (typeof module === 'object' && module.exports) { - // Node.js - module.exports = factory(require('ajv'), require('yaml'), require('fs').promises); - } else { - // Browser globals - if (root !== undefined) { - root.nip11 = factory(root.Ajv, root.YAML); - } else { - throw new Error('Global object "root" is undefined.'); - } - } -}(typeof self !== 'undefined' ? self : this, function (Ajv: any, YAML: any, fs?: any) { - return nip11; -})); +import * as schema from '../src/schema.json'; +export default schema \ No newline at end of file diff --git a/libraries/schemata/nip11/src/schema.json b/libraries/schemata/nip11/src/schema.json new file mode 100644 index 00000000..7b49c3ac --- /dev/null +++ b/libraries/schemata/nip11/src/schema.json @@ -0,0 +1 @@ +{"$id":"https://nip11.schemata.nostr.watch","$schema":"http://json-schema.org/draft-07/schema#","title":"NIP-11","type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"pubkey":{"type":"string"},"contact":{"type":"string"},"supported_nips":{"type":"array","items":{"type":"number"}},"software":{"type":"string"},"version":{"type":"string"},"retention":{"type":"array","items":{"$ref":"#/$defs/retent"}},"relay_country":{"type":"array","items":{"type":"string"}},"icon":{"anyOf":[{"$ref":"#/$defs/saneUrl"},{"format":"hostname"}]},"language_tags":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"}},"posting_policy":{"anyOf":[{"$ref":"#/$defs/saneUrl"},{"format":"hostname"}]},"limitation":{"type":"object","properties":{"max_message_length":{"type":"number"},"max_subscriptions":{"type":"number"},"max_filters":{"type":"number"},"max_limit":{"type":"number"},"max_subid_length":{"type":"number"},"max_event_tags":{"type":"number"},"max_content_length":{"type":"number"},"min_pow_difficulty":{"type":"number"},"auth_required":{"type":"boolean"},"payment_required":{"type":"boolean"},"restricted_writes":{"type":"boolean"},"created_at_lower_limit":{"type":"number"},"created_at_upper_limit":{"type":"number"}}},"payments_url":{"anyOf":[{"$ref":"#/$defs/saneUrl"},{"format":"hostname"}]},"fees":{"properties":{"admission":{"$ref":"#/$defs/fee"},"subscription":{"$ref":"#/$defs/fee"},"publication":{"$ref":"#/$defs/fee"}}}},"required":["name","description","pubkey","contact","supported_nips","software","version"],"$defs":{"saneUrl":{"format":"uri","pattern":"^https?://"},"fee":{"type":"array","items":{"type":"object","properties":{"amount":{"type":"number"},"unit":{"type":"string"},"period":{"type":"number"},"kinds":{"type":"array","items":{"type":"number"}}}}},"retent":{"type":"object","properties":{"kinds":{"type":"array","items":{"anyOf":[{"type":"number"},{"type":"array","items":{"type":"number"}}]}},"count":{"type":"number"},"time":{"type":"number"}}}}} \ No newline at end of file diff --git a/libraries/schemata/nip11/tsconfig.json b/libraries/schemata/nip11/tsconfig.json index dccc6b8e..67a3f050 100644 --- a/libraries/schemata/nip11/tsconfig.json +++ b/libraries/schemata/nip11/tsconfig.json @@ -1,15 +1,12 @@ { "compilerOptions": { - "target": "ES2020", - "module": "ESNext", - "moduleResolution": "Node", - "outDir": "./dist", - "rootDir": "./src", - "strict": true, + "module": "esnext", + "target": "esnext", + "moduleResolution": "node", "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true + "resolveJsonModule": true, + "outDir": "./dist", + "rootDir": "./src" }, - "include": ["src"], - "exclude": ["node_modules", "dist"] -} + "include": ["src/**/*"] +} \ No newline at end of file diff --git a/libraries/schemata/nip11/webpack.config.js b/libraries/schemata/nip11/webpack.config.js new file mode 100644 index 00000000..ac51e6c9 --- /dev/null +++ b/libraries/schemata/nip11/webpack.config.js @@ -0,0 +1,25 @@ +const path = require('path'); + +module.exports = { + entry: path.resolve(__dirname, 'src', 'index.ts'), // Ensures absolute path resolution + output: { + path: path.resolve(__dirname, 'dist'), // Explicitly resolves to your project's dist directory + filename: 'bundle.js' + }, + module: { + rules: [ + { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/, + }, + { + test: /\.json$/, + type: 'json' // Handles JSON files correctly + } + ] + }, + resolve: { + extensions: ['.tsx', '.ts', '.js', '.json'] + } +}; diff --git a/package.json b/package.json index 40d658de..7dd67890 100644 --- a/package.json +++ b/package.json @@ -21,13 +21,15 @@ }, "workspaces": [ "apps/*", + "demos/*", + "internal/*", + "libraries/*", "libraries/nocap/adapters/**/*", "libraries/kit/adapters/**/*", - "libraries/*", "libraries/adapters/**/*", - "demos/*", - "internal/*", + "libraries/validators/**/*", "libraries/schemata/**/*" + ], "useWorkspaces": true, "main": "index.js", diff --git a/yarn.lock b/yarn.lock index 88394829..da762623 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28,22 +28,6 @@ resolved "https://registry.npmjs.org/@anywhichway/cartesian-product/-/cartesian-product-1.0.5.tgz#9b77e0b9b6dbcd3e8613fc886c721763a978e44a" integrity sha512-vMwd2tED87521ZFJxnI/34idEWbm3l4/ueJt0VDSJrIEdWPUtzrQ8O3da6vpYjC24RP9SAW5ymd7kOHOj0HLdw== -"@babel/cli@7.24.8": - version "7.24.8" - resolved "https://registry.npmjs.org/@babel/cli/-/cli-7.24.8.tgz#79eaa55a69c77cafbea3e87537fd1df5a5a2edf8" - integrity sha512-isdp+G6DpRyKc+3Gqxy2rjzgF7Zj9K0mzLNnxz+E/fgeag8qT3vVulX4gY9dGO1q0y+0lUv6V3a+uhUzMzrwXg== - dependencies: - "@jridgewell/trace-mapping" "^0.3.25" - commander "^6.2.0" - convert-source-map "^2.0.0" - fs-readdir-recursive "^1.1.0" - glob "^7.2.0" - make-dir "^2.1.0" - slash "^2.0.0" - optionalDependencies: - "@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3" - chokidar "^3.4.0" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" @@ -57,7 +41,7 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.9.tgz#53eee4e68f1c1d0282aa0eb05ddb02d033fc43a0" integrity sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng== -"@babel/core@7.24.9", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": version "7.24.9" resolved "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz#dc07c9d307162c97fa9484ea997ade65841c7c82" integrity sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg== @@ -170,7 +154,7 @@ resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== -"@babel/helper-validator-option@^7.24.7", "@babel/helper-validator-option@^7.24.8": +"@babel/helper-validator-option@^7.24.8": version "7.24.8" resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz#3725cdeea8b480e86d34df15304806a06975e33d" integrity sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q== @@ -219,13 +203,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-flow@^7.24.7": - version "7.24.7" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.7.tgz#d1759e84dd4b437cf9fae69b4c06c41d7625bfb7" - integrity sha512-9G8GYT/dxn/D1IIKOUBmGX0mnmj46mGH9NnZyJLwtCpgh5f7D2VbuKodb+2s9m1Yavh1s7ASQN8lf0eqrb1LTw== - dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" @@ -303,14 +280,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-flow-strip-types@^7.24.7": - version "7.24.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.7.tgz#ae454e62219288fbb734541ab00389bfb13c063e" - integrity sha512-cjRKJ7FobOH2eakx7Ja+KpJRj8+y+/SiB3ooYm/n2UJfxu0oEaOoxOinitkJcPqv9KxS0kxTGPUaR7L2XcXDXA== - dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/plugin-syntax-flow" "^7.24.7" - "@babel/plugin-transform-modules-commonjs@7.23.3": version "7.23.3" resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz#661ae831b9577e52be57dd8356b734f9700b53b4" @@ -320,15 +289,6 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-simple-access" "^7.22.5" -"@babel/preset-flow@7.24.7": - version "7.24.7" - resolved "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.24.7.tgz#eef5cb8e05e97a448fc50c16826f5612fe512c06" - integrity sha512-NL3Lo0NorCU607zU3NwRyJbpaB6E3t0xtd3LfAQKDfkeX4/ggcDXvkmkW42QWT5owUeW/jAe4hn+2qvkV1IbfQ== - dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/helper-validator-option" "^7.24.7" - "@babel/plugin-transform-flow-strip-types" "^7.24.7" - "@babel/template@^7.24.7", "@babel/template@^7.3.3": version "7.24.7" resolved "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz#02efcee317d0609d2c07117cb70ef8fb17ab7315" @@ -1718,11 +1678,6 @@ resolved "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz#0aa5502d547b57abfc4ac492de68e2006e417242" integrity sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ== -"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": - version "2.1.8-no-fsevents.3" - resolved "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" - integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ== - "@noble/ciphers@0.2.0": version "0.2.0" resolved "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.2.0.tgz#a12cda60f3cf1ab5d7c77068c3711d2366649ed7" @@ -1895,6 +1850,34 @@ "@noble/curves" "^1.1.0" "@noble/hashes" "^1.2.0" +"@nostrwatch/nocap-every-adapter-default@1.4.0": + version "1.4.0" + resolved "https://registry.npmjs.org/@nostrwatch/nocap-every-adapter-default/-/nocap-every-adapter-default-1.4.0.tgz#591b11fb7b421668313ae647c280b499e04ff50e" + integrity sha512-oTxBD68XYvsKpK6t6Ev4wMpiK/bxvGu+8VClWebRmPEqemCVbnqwA+CORlbIrwGYj1WIOUPwoObjxMdqI1LybA== + dependencies: + "@nostrwatch/nocap-dns-adapter-default" "1.0.3" + "@nostrwatch/nocap-geo-adapter-default" "1.0.3" + "@nostrwatch/nocap-info-adapter-default" "1.1.0" + "@nostrwatch/nocap-ssl-adapter-default" "1.1.0" + "@nostrwatch/nocap-websocket-adapter-default" "1.3.1" + "@nostrwatch/nocap-websocket-browser-adapter-default" "0.0.1" + +"@nostrwatch/nocap-ssl-adapter-default@1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@nostrwatch/nocap-ssl-adapter-default/-/nocap-ssl-adapter-default-1.1.0.tgz#9a3ac0fa4cc573ac67ab1dc72b4d7ee18b32cc9a" + integrity sha512-TkSPzOrg2pn810gMX2EissJPBchrDQczd327qMvUCjrYUq7vFcpEQVNvd/9lDMGhK5q/e53gkID9CRfcgf5Spg== + dependencies: + get-ssl-cert "2.3.3" + ssl-checker "2.0.8" + +"@nostrwatch/publisher@0.4.3": + version "0.4.3" + resolved "https://registry.npmjs.org/@nostrwatch/publisher/-/publisher-0.4.3.tgz#4c26eef5fb281ead3bce5433248ddf7e2cbde130" + integrity sha512-EhgHaR5c3y/urgdyE3iT8iDR6YPDYHaL1UYxvFfWw2I4LixlcIlHT0Z5R71mjamFbOCAcrJrxLUS+MARqehmLw== + dependencies: + nostr-geotags "^0.5.0" + nostr-tools "1.17.0" + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -2030,10 +2013,10 @@ "@noble/hashes" "~1.3.0" "@scure/base" "~1.1.0" -"@shikijs/core@1.11.0": - version "1.11.0" - resolved "https://registry.npmjs.org/@shikijs/core/-/core-1.11.0.tgz#a07a55037ba74f13b860698149bda03382b74cc1" - integrity sha512-VbEhDAhT/2ozO0TPr5/ZQBO/NWLqtk4ZiBf6NplYpF38mKjNfMMied5fNEfIfYfN+cdKvhDB4VMcKvG/g9c3zg== +"@shikijs/core@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@shikijs/core/-/core-1.11.1.tgz#a102cf56f32fa8cf3ceb9f918f2da5511782efe7" + integrity sha512-Qsn8h15SWgv5TDRoDmiHNzdQO2BxDe86Yq6vIHf5T0cCvmfmccJKIzHtep8bQO9HMBZYCtCBzaXdd1MnxZBPSg== dependencies: "@types/hast" "^3.0.4" @@ -2671,9 +2654,9 @@ "@types/estree" "*" "@types/eslint@*": - version "8.56.10" - resolved "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz#eb2370a73bf04a901eeba8f22595c7ee0f7eb58d" - integrity sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ== + version "9.6.0" + resolved "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz#51d4fe4d0316da9e9f2c80884f2c20ed5fb022ff" + integrity sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -2734,9 +2717,9 @@ integrity sha512-A90x3HMwE1yXbWCnd0ztHzv8rAQPjwTzX2diYI/6OrWm/3oairDaehw5WPWJFgZ+8+J/OuF99IbipmMa2le6tQ== "@types/node@*", "@types/node@^20.11.24": - version "20.14.11" - resolved "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz#09b300423343460455043ddd4d0ded6ac579b74b" - integrity sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA== + version "20.14.12" + resolved "https://registry.npmjs.org/@types/node/-/node-20.14.12.tgz#129d7c3a822cb49fc7ff661235f19cfefd422b49" + integrity sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ== dependencies: undici-types "~5.26.4" @@ -2748,9 +2731,9 @@ undici-types "~5.26.4" "@types/node@^16.9.2": - version "16.18.103" - resolved "https://registry.npmjs.org/@types/node/-/node-16.18.103.tgz#5557c7c32a766fddbec4b933b1d5c365f89b20a4" - integrity sha512-gOAcUSik1nR/CRC3BsK8kr6tbmNIOTpvb1sT+v5Nmmys+Ho8YtnIHP90wEsVK4hTcHndOqPVIlehEGEA5y31bA== + version "16.18.104" + resolved "https://registry.npmjs.org/@types/node/-/node-16.18.104.tgz#33d5f4886c54133af0ff02445e57c5254025ee53" + integrity sha512-OF3keVCbfPlkzxnnDBUZJn1RiCJzKeadjiW0xTEb0G1SUJ5gDVb3qnzZr2T4uIFvsbKJbXy1v2DN7e2zaEY7jQ== "@types/object-mapper@6.2.2": version "6.2.2" @@ -2797,61 +2780,61 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^7.1.1": - version "7.16.1" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.1.tgz#f5f5da52db674b1f2cdb9d5f3644e5b2ec750465" - integrity sha512-SxdPak/5bO0EnGktV05+Hq8oatjAYVY3Zh2bye9pGZy6+jwyR3LG3YKkV4YatlsgqXP28BTeVm9pqwJM96vf2A== + version "7.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.17.0.tgz#c8ed1af1ad2928ede5cdd207f7e3090499e1f77b" + integrity sha512-pyiDhEuLM3PuANxH7uNYan1AaFs5XE0zw1hq69JBvGvE7gSuEoQl1ydtEe/XQeoC3GQxLXyOVa5kNOATgM638A== dependencies: "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "7.16.1" - "@typescript-eslint/type-utils" "7.16.1" - "@typescript-eslint/utils" "7.16.1" - "@typescript-eslint/visitor-keys" "7.16.1" + "@typescript-eslint/scope-manager" "7.17.0" + "@typescript-eslint/type-utils" "7.17.0" + "@typescript-eslint/utils" "7.17.0" + "@typescript-eslint/visitor-keys" "7.17.0" graphemer "^1.4.0" ignore "^5.3.1" natural-compare "^1.4.0" ts-api-utils "^1.3.0" "@typescript-eslint/parser@^7.1.1": - version "7.16.1" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.1.tgz#84c581cf86c8b2becd48d33ddc41a6303d57b274" - integrity sha512-u+1Qx86jfGQ5i4JjK33/FnawZRpsLxRnKzGE6EABZ40KxVT/vWsiZFEBBHjFOljmmV3MBYOHEKi0Jm9hbAOClA== - dependencies: - "@typescript-eslint/scope-manager" "7.16.1" - "@typescript-eslint/types" "7.16.1" - "@typescript-eslint/typescript-estree" "7.16.1" - "@typescript-eslint/visitor-keys" "7.16.1" + version "7.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.17.0.tgz#be8e32c159190cd40a305a2121220eadea5a88e7" + integrity sha512-puiYfGeg5Ydop8eusb/Hy1k7QmOU6X3nvsqCgzrB2K4qMavK//21+PzNE8qeECgNOIoertJPUC1SpegHDI515A== + dependencies: + "@typescript-eslint/scope-manager" "7.17.0" + "@typescript-eslint/types" "7.17.0" + "@typescript-eslint/typescript-estree" "7.17.0" + "@typescript-eslint/visitor-keys" "7.17.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@7.16.1": - version "7.16.1" - resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.1.tgz#2b43041caabf8ddd74512b8b550b9fc53ca3afa1" - integrity sha512-nYpyv6ALte18gbMz323RM+vpFpTjfNdyakbf3nsLvF43uF9KeNC289SUEW3QLZ1xPtyINJ1dIsZOuWuSRIWygw== +"@typescript-eslint/scope-manager@7.17.0": + version "7.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.17.0.tgz#e072d0f914662a7bfd6c058165e3c2b35ea26b9d" + integrity sha512-0P2jTTqyxWp9HiKLu/Vemr2Rg1Xb5B7uHItdVZ6iAenXmPo4SZ86yOPCJwMqpCyaMiEHTNqizHfsbmCFT1x9SA== dependencies: - "@typescript-eslint/types" "7.16.1" - "@typescript-eslint/visitor-keys" "7.16.1" + "@typescript-eslint/types" "7.17.0" + "@typescript-eslint/visitor-keys" "7.17.0" -"@typescript-eslint/type-utils@7.16.1": - version "7.16.1" - resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.1.tgz#4d7ae4f3d9e3c8cbdabae91609b1a431de6aa6ca" - integrity sha512-rbu/H2MWXN4SkjIIyWcmYBjlp55VT+1G3duFOIukTNFxr9PI35pLc2ydwAfejCEitCv4uztA07q0QWanOHC7dA== +"@typescript-eslint/type-utils@7.17.0": + version "7.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.17.0.tgz#c5da78feb134c9c9978cbe89e2b1a589ed22091a" + integrity sha512-XD3aaBt+orgkM/7Cei0XNEm1vwUxQ958AOLALzPlbPqb8C1G8PZK85tND7Jpe69Wualri81PLU+Zc48GVKIMMA== dependencies: - "@typescript-eslint/typescript-estree" "7.16.1" - "@typescript-eslint/utils" "7.16.1" + "@typescript-eslint/typescript-estree" "7.17.0" + "@typescript-eslint/utils" "7.17.0" debug "^4.3.4" ts-api-utils "^1.3.0" -"@typescript-eslint/types@7.16.1": - version "7.16.1" - resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.1.tgz#bbab066276d18e398bc64067b23f1ce84dfc6d8c" - integrity sha512-AQn9XqCzUXd4bAVEsAXM/Izk11Wx2u4H3BAfQVhSfzfDOm/wAON9nP7J5rpkCxts7E5TELmN845xTUCQrD1xIQ== +"@typescript-eslint/types@7.17.0": + version "7.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.17.0.tgz#7ce8185bdf06bc3494e73d143dbf3293111b9cff" + integrity sha512-a29Ir0EbyKTKHnZWbNsrc/gqfIBqYPwj3F2M+jWE/9bqfEHg0AMtXzkbUkOG6QgEScxh2+Pz9OXe11jHDnHR7A== -"@typescript-eslint/typescript-estree@7.16.1": - version "7.16.1" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.1.tgz#9b145ba4fd1dde1986697e1ce57dc501a1736dd3" - integrity sha512-0vFPk8tMjj6apaAZ1HlwM8w7jbghC8jc1aRNJG5vN8Ym5miyhTQGMqU++kuBFDNKe9NcPeZ6x0zfSzV8xC1UlQ== +"@typescript-eslint/typescript-estree@7.17.0": + version "7.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.17.0.tgz#dcab3fea4c07482329dd6107d3c6480e228e4130" + integrity sha512-72I3TGq93t2GoSBWI093wmKo0n6/b7O4j9o8U+f65TVD0FS6bI2180X5eGEr8MA8PhKMvYe9myZJquUT2JkCZw== dependencies: - "@typescript-eslint/types" "7.16.1" - "@typescript-eslint/visitor-keys" "7.16.1" + "@typescript-eslint/types" "7.17.0" + "@typescript-eslint/visitor-keys" "7.17.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" @@ -2859,22 +2842,22 @@ semver "^7.6.0" ts-api-utils "^1.3.0" -"@typescript-eslint/utils@7.16.1": - version "7.16.1" - resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.1.tgz#df42dc8ca5a4603016fd102db0346cdab415cdb7" - integrity sha512-WrFM8nzCowV0he0RlkotGDujx78xudsxnGMBHI88l5J8wEhED6yBwaSLP99ygfrzAjsQvcYQ94quDwI0d7E1fA== +"@typescript-eslint/utils@7.17.0": + version "7.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.17.0.tgz#815cd85b9001845d41b699b0ce4f92d6dfb84902" + integrity sha512-r+JFlm5NdB+JXc7aWWZ3fKSm1gn0pkswEwIYsrGPdsT2GjsRATAKXiNtp3vgAAO1xZhX8alIOEQnNMl3kbTgJw== dependencies: "@eslint-community/eslint-utils" "^4.4.0" - "@typescript-eslint/scope-manager" "7.16.1" - "@typescript-eslint/types" "7.16.1" - "@typescript-eslint/typescript-estree" "7.16.1" + "@typescript-eslint/scope-manager" "7.17.0" + "@typescript-eslint/types" "7.17.0" + "@typescript-eslint/typescript-estree" "7.17.0" -"@typescript-eslint/visitor-keys@7.16.1": - version "7.16.1" - resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.1.tgz#4287bcf44c34df811ff3bb4d269be6cfc7d8c74b" - integrity sha512-Qlzzx4sE4u3FsHTPQAAQFJFNOuqtuY0LFrZHwQ8IHK705XxBiWOFkfKRWu6niB7hwfgnwIpO4jTC75ozW1PHWg== +"@typescript-eslint/visitor-keys@7.17.0": + version "7.17.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.17.0.tgz#680465c734be30969e564b4647f38d6cdf49bfb0" + integrity sha512-RVGC9UhPOCsfCdI9pU++K4nD7to+jTcMIbXTSOcrLqUEW6gF2pU1UUbYJKc9cvcRSK1UDeMJ7pdMxf4bhMpV/A== dependencies: - "@typescript-eslint/types" "7.16.1" + "@typescript-eslint/types" "7.17.0" eslint-visitor-keys "^3.4.3" "@ungap/structured-clone@^1.2.0": @@ -3481,13 +3464,6 @@ babel-plugin-jest-hoist@^29.6.3: "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" -babel-plugin-syntax-hermes-parser@0.23.0: - version "0.23.0" - resolved "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.23.0.tgz#44af4861b3a7761836275bdf375eca12b0f5ccfa" - integrity sha512-JRapv0rcV8n34ZXDzoOPiCg2Q9JJzfklF9Yar+v7gjBuQoXN8mma50E5utjWQTQi//wT9Qcem5mIx5maob5cqA== - dependencies: - hermes-parser "0.23.0" - babel-preset-current-node-syntax@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" @@ -3872,7 +3848,7 @@ check-error@^1.0.3: dependencies: get-func-name "^2.0.2" -chokidar@^3.4.0, chokidar@^3.4.1, chokidar@^3.5.1, chokidar@^3.5.3: +chokidar@^3.4.1, chokidar@^3.5.1, chokidar@^3.5.3: version "3.6.0" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -4037,11 +4013,6 @@ commander@^4.0.0: resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== -commander@^6.2.0: - version "6.2.1" - resolved "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" - integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== - concat-map@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -4418,9 +4389,9 @@ ejs@^3.1.9: jake "^10.8.5" electron-to-chromium@^1.4.820: - version "1.4.832" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.832.tgz#d25882ce0a9237577b039bffa124ecef1822003b" - integrity sha512-cTen3SB0H2SGU7x467NRe1eVcQgcuS6jckKfWJHia2eo0cHIGOqHoAxevIYZD4eRHcWjkvFzo93bi3vJ9W+1lA== + version "1.5.1" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.1.tgz#24640bd4dcfaccb6d82bb4c3f4c7311503241581" + integrity sha512-FKbOCOQ5QRB3VlIbl1LZQefWIYwszlBloaXcY2rbfpu9ioJnNh3TK03YtIDKDo3WKBi8u+YV4+Fn2CkEozgf4w== elliptic@^6.5.3, elliptic@^6.5.5: version "6.5.6" @@ -4456,9 +4427,9 @@ emojis-list@^3.0.0: integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== enhanced-resolve@^5.0.0, enhanced-resolve@^5.17.0: - version "5.17.0" - resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz#d037603789dd9555b89aaec7eb78845c49089bc5" - integrity sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== + version "5.17.1" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -5314,11 +5285,6 @@ fraction.js@^4.3.7: resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== -fs-readdir-recursive@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" - integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -5435,7 +5401,7 @@ glob@^10.3.10, glob@^10.3.7: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" -glob@^7.1.3, glob@^7.1.4, glob@^7.1.7, glob@^7.2.0: +glob@^7.1.3, glob@^7.1.4, glob@^7.1.7: version "7.2.3" resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -5602,18 +5568,6 @@ he@^1.2.0: resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -hermes-estree@0.23.0: - version "0.23.0" - resolved "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.23.0.tgz#89c5419877b9d6bce4bb616821f496f5c5daddbc" - integrity sha512-Rkp0PNLGpORw4ktsttkVbpYJbrYKS3hAnkxu8D9nvQi6LvSbuPa+tYw/t2u3Gjc35lYd/k95YkjqyTcN4zspag== - -hermes-parser@0.23.0: - version "0.23.0" - resolved "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.23.0.tgz#3541907b77ca9e94fd093e8ef0ff97ca5340dee8" - integrity sha512-xLwM4ylfHGwrm+2qXfO1JT/fnqEDGSnpS/9hQ4VLtqTexSviu2ZpBgz07U8jVtndq67qdb/ps0qvaWDZ3fkTyg== - dependencies: - hermes-estree "0.23.0" - hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -6489,6 +6443,11 @@ json-buffer@3.0.1: resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== +json-loader@0.5.7: + version "0.5.7" + resolved "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" + integrity sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w== + json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -6878,14 +6837,6 @@ magicast@^0.3.3: "@babel/types" "^7.24.0" source-map-js "^1.2.0" -make-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - make-dir@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" @@ -7732,11 +7683,6 @@ pify@^2.3.0: resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - pino-abstract-transport@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz#97f9f2631931e242da531b5c66d3079c12c9d1b5" @@ -7875,9 +7821,9 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@^8.4.18, postcss@^8.4.23, postcss@^8.4.32, postcss@^8.4.38, postcss@^8.4.39: - version "8.4.39" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz#aa3c94998b61d3a9c259efa51db4b392e1bde0e3" - integrity sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw== + version "8.4.40" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz#eb81f2a4dd7668ed869a6db25999e02e9ad909d8" + integrity sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q== dependencies: nanoid "^3.3.7" picocolors "^1.0.1" @@ -8371,11 +8317,6 @@ secure-json-parse@^2.7.0: resolved "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== -semver@^5.6.0: - version "5.7.2" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" @@ -8457,11 +8398,11 @@ shiki@^0.10.1: vscode-textmate "5.2.0" shiki@^1.9.0: - version "1.11.0" - resolved "https://registry.npmjs.org/shiki/-/shiki-1.11.0.tgz#fad958667d7dc8e58f58e7c6ebd24d75d7c4e199" - integrity sha512-NqH/O1zRHvnuk/WfSL6b7+DtI7/kkMMSQGlZhm9DyzSU+SoIHhaw/fBZMr+zp9R8KjdIzkk3JKSC6hORuGDyng== + version "1.11.1" + resolved "https://registry.npmjs.org/shiki/-/shiki-1.11.1.tgz#6c06c5fcf55f1dac2db2596af935fef6a41a209d" + integrity sha512-VHD3Q0EBXaaa245jqayBe5zQyMQUdXBFjmGr9MpDaDpAKRMYn7Ff00DM5MLk26UyKjnml3yQ0O2HNX7PtYVNFQ== dependencies: - "@shikijs/core" "1.11.0" + "@shikijs/core" "1.11.1" "@types/hast" "^3.0.4" side-channel@^1.0.6: @@ -8503,11 +8444,6 @@ sisteransi@^1.0.5: resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== -slash@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" - integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== - slash@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -9202,7 +9138,7 @@ ts-interface-checker@^0.1.9: resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== -ts-loader@^9.2.6: +ts-loader@9.5.1, ts-loader@^9.2.6: version "9.5.1" resolved "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz#63d5912a86312f1fbe32cef0859fb8b2193d9b89" integrity sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg== @@ -9302,11 +9238,16 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.8: +type-detect@4.0.8: version "4.0.8" resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-detect@^4.0.0, type-detect@^4.0.8: + version "4.1.0" + resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c" + integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw== + type-fest@^0.20.2: version "0.20.2" resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" @@ -9380,10 +9321,10 @@ typescript@5.5.2: resolved "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz#c26f023cb0054e657ce04f72583ea2d85f8d0507" integrity sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew== -typescript@5.5.3, typescript@^5.0.0, typescript@^5.0.3, typescript@^5.3.3: - version "5.5.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz#e1b0a3c394190838a0b168e771b0ad56a0af0faa" - integrity sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ== +typescript@5.5.4, typescript@^5.0.0, typescript@^5.0.3, typescript@^5.3.3: + version "5.5.4" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" + integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== typical@^4.0.0: version "4.0.0" @@ -9622,9 +9563,9 @@ vite@5.3.1: fsevents "~2.3.2" "vite@^3.0.0 || ^4.0.0 || ^5.0.0-0", "vite@^3.1.0 || ^4.0.0 || ^5.0.0-0", vite@^5.0.0, vite@^5.0.3: - version "5.3.4" - resolved "https://registry.npmjs.org/vite/-/vite-5.3.4.tgz#b36ebd47c8a5e3a8727046375d5f10bf9fdf8715" - integrity sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA== + version "5.3.5" + resolved "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz#b847f846fb2b6cb6f6f4ed50a830186138cb83d8" + integrity sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA== dependencies: esbuild "^0.21.3" postcss "^8.4.39" @@ -9795,6 +9736,15 @@ webpack-cli@5.1.4: rechoir "^0.8.0" webpack-merge "^5.7.3" +webpack-merge@6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz#50c776868e080574725abc5869bd6e4ef0a16c6a" + integrity sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.1" + webpack-merge@^5.7.3: version "5.10.0" resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" @@ -9804,6 +9754,11 @@ webpack-merge@^5.7.3: flat "^5.0.2" wildcard "^2.0.0" +webpack-node-externals@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz#1a3407c158d547a9feb4229a9e3385b7b60c9917" + integrity sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ== + webpack-sources@^3.2.3: version "3.2.3" resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" @@ -9909,7 +9864,7 @@ why-is-node-running@^2.2.2: siginfo "^2.0.0" stackback "0.0.2" -wildcard@^2.0.0: +wildcard@^2.0.0, wildcard@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== @@ -10031,7 +9986,7 @@ yaml-loader@0.8.1: loader-utils "^2.0.0" yaml "^2.0.0" -yaml@2.4.5, yaml@^2.0.0, yaml@^2.3.4, yaml@^2.4.1, yaml@^2.4.2, yaml@^2.4.5: +yaml@2.4.5: version "2.4.5" resolved "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz#60630b206dd6d84df97003d33fc1ddf6296cca5e" integrity sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg== @@ -10041,6 +9996,11 @@ yaml@^1.10.2: resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.0.0, yaml@^2.3.4, yaml@^2.4.1, yaml@^2.4.2, yaml@^2.4.5: + version "2.5.0" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz#c6165a721cf8000e91c36490a41d7be25176cf5d" + integrity sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw== + yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" @@ -10078,8 +10038,3 @@ yocto-queue@^1.0.0: version "1.1.1" resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110" integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== - -zod@3.23.8: - version "3.23.8" - resolved "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" - integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==