From 60fe3246ca970ae2662731d1a01ddddebff62242 Mon Sep 17 00:00:00 2001 From: dskvr Date: Mon, 9 Jan 2023 19:42:44 +0100 Subject: [PATCH] task queue functional with 1 task type --- .gitignore | 1 + package.json | 3 +- scripts/canonicals.js | 213 ++++++++++++++++++ src/components/relays/nav/RelaysFindNav.vue | 6 +- src/components/relays/nav/RelaysNav.vue | 13 +- src/components/relays/tasks/HistoryTask.vue | 14 +- src/components/relays/tasks/RefreshTask.vue | 93 ++++---- .../relays/tasks/RelayCanonicalsTask.vue | 7 + src/components/relays/tasks/TasksManager.vue | 96 ++++++++ src/shared/computed.js | 2 +- src/store/relays.js | 29 +-- src/store/tasks.js | 56 +++-- yarn.lock | 28 +-- 13 files changed, 457 insertions(+), 104 deletions(-) create mode 100644 scripts/canonicals.js create mode 100644 src/components/relays/tasks/RelayCanonicalsTask.vue create mode 100644 src/components/relays/tasks/TasksManager.vue diff --git a/.gitignore b/.gitignore index 5b1cffb..b8181c3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ public/main.js lib/nostr-relay-inspector dist yarn-error.log +.env \ No newline at end of file diff --git a/package.json b/package.json index 2d9bf5e..2d9c9be 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "array-timsort": "1.0.3", "country-code-emoji": "2.3.0", "cross-fetch": "3.1.5", + "dotenv": "16.0.3", "fast-safe-stringify": "2.1.1", "js-yaml": "4.1.0", "leaflet": "1.9.3", @@ -32,7 +33,7 @@ "node-fetch": "3.3.0", "nostr": "0.2.5", "nostr-relay-inspector": "0.0.23", - "nostr-tools": "1.0.1", + "nostr-tools": "1.1.1", "object-sizeof": "1.6.3", "onion-regex": "2.0.8", "path-segments": "0.1.1", diff --git a/scripts/canonicals.js b/scripts/canonicals.js new file mode 100644 index 0000000..f9f47aa --- /dev/null +++ b/scripts/canonicals.js @@ -0,0 +1,213 @@ +require('dotenv').config() + +const fs = require('fs'), + yaml= require('js-yaml'), + crypto = require('crypto'), + nostrTools = require('nostr-tools'), + { RelayPool } = require('nostr'), + { validateEvent, verifySignature, signEvent, getEventHash, getPublicKey } = nostrTools, + + uniques = new Set() + +let relays = yaml.load(fs.readFileSync('./relays.yaml', 'utf8')).relays, + canonicals = new Object(), + missing = new Array(), + hashes = new Object(), + discovered = true, + totalSent = 0, + oks = 0, + notices = 0 + +const pool = RelayPool(relays, {reconnect: false}) + +pool + .on('ok', (Relay) => { + oks++ + // console.log('OK', Relay.url) + }) + .on('notice', (Relay, notice) => { + notices++ + // console.log('NOTICE', Relay.url, notice) + }) + +async function run(){ + // setup() + // deletions() + // await process.exit() + await discover() + // process.exit() + // console.log(`wtf`, relays.length) + // console.log(`hashes`, Object.keys(hashes).length) + await sieve() + + setInterval( ()=> { + console.log('status', '\ntotal sent:', totalSent, '\noks:', oks, '\nnotices:', notices, '\n\n') + }, 60000) + await broadcast() + + + process.exit() + +} + +function setup(){ + const event = { + "id": "a2640e8a6640c595942ccf290eae404ac58569b59af5c8c8e3334d9cf809fff6", + "pubkey": "b3b0d247f66bf40c4c9f4ce721abfe1fd3b7529fbc1ea5e64d5f0f8df3a4b6e6", + "created_at": 1673275222, + "kind": 1, + "tags": [], + "content": "<3 to all the relays", + "sig": "e536be52a04f95c54e5cc82caafb9b25c8d47e00182c0eac0b6b678b482710288cc7fd85c62b0f97f5ed33dfbd7e15555c9bfeac059794767e414666d807f9cf" + } + + pool.send(['EVENT', event]) +} + +async function discover(){ + + + console.log('relays', relays.length) + + return new Promise(resolve => { + const subid = crypto.randomBytes(40).toString('hex') + + pool + .on('open', Relay => { + console.log('open', Relay.url) + Relay.subscribe(subid, {limit: relays.length, kinds:[1], "#t": ['canonical'], authors:[ getPublicKey(process.env.PRIVATE_KEY) ] }, ) + relays.forEach( relay => { + hashes[hash(relay)] = relay + // Relay.subscribe(`subid_${relay}`, {limit: 1, kinds:[1], authors:[ getPublicKey(process.env.PRIVATE_KEY) ] }, ) + }) + + }) + .on('event', (Relay, _subid, event) => { + if(!discovered){ + // console.log('published event found', event.id) + } + if(_subid.includes(subid) && discovered) { + // console.log('event', event.content, event.id) + + if(uniques.has(event.id)) + return + + const relayHash = event.tags.map( tag => tag[0]=='h' ? tag[1] : false )[0] + + if(!relayHash) + return + + // console.log('relay hash', Relay.url, relayHash) + + const relay = hashes?.[relayHash] + + uniques.add(event.id) + canonicals[relay] = event + } + }) + + setTimeout( () => { + // pool.close() + discovered = false + resolve(relays) + }, 10*1000 ) + }) +} + +async function sieve(){ + console.log('filtering relays', relays.length) + checkMissing() + console.log('missing', missing.length) + return +} + +function checkMissing(){ + missing = new Array() + relays.forEach( relay => { + // console.log('check missing', relay, (canonicals?.[relay] instanceof Object) ) + if( !(canonicals?.[relay] instanceof Object) ) + missing.push(relay) + }) +} + +async function broadcast(){ + for(let i=0;i setTimeout(resolve, ms) ) +} + +function hash(relay){ + return crypto.createHash('md5').update(relay).digest('hex'); +} + +run() \ No newline at end of file diff --git a/src/components/relays/nav/RelaysFindNav.vue b/src/components/relays/nav/RelaysFindNav.vue index 98eb0af..55cdded 100644 --- a/src/components/relays/nav/RelaysFindNav.vue +++ b/src/components/relays/nav/RelaysFindNav.vue @@ -15,7 +15,7 @@
- +
@@ -52,7 +52,7 @@ import { defineComponent } from 'vue' //pinia import { setupStore } from '@/store' //components -import RelaysSearchFilter from '@/components/relays/blocks/RelaysSearchFilter.vue' +// import RelaysSearchFilter from '@/components/relays/blocks/RelaysSearchFilter.vue' //nav conf import { items } from './config/relays.find.yaml' //shared methods @@ -67,7 +67,7 @@ export default defineComponent({ title: "nostr.watch registry & network status", name: 'RelaysFindNav', components: { - RelaysSearchFilter, + // RelaysSearchFilter, Disclosure, DisclosureButton, DisclosurePanel, Bars3Icon, XMarkIcon, // PreferencesComponent, diff --git a/src/components/relays/nav/RelaysNav.vue b/src/components/relays/nav/RelaysNav.vue index 524dc19..e3229d2 100644 --- a/src/components/relays/nav/RelaysNav.vue +++ b/src/components/relays/nav/RelaysNav.vue @@ -18,10 +18,9 @@
- -
+ + @@ -35,8 +34,8 @@ import RelaysLib from '@/shared/relays-lib.js' import { setupNavData, mountNav, setActiveContent, loadNavContent, routeValid, parseHash, contentIsActive } from '@/shared/hash-router.js' // import RefreshTask from '@/components/relays/tasks/RefreshTask.vue' -const RefreshTask = defineAsyncComponent(() => - import("@/components/relays/tasks/RefreshTask.vue" /* webpackChunkName: "RefreshTask" */) +const TasksManager = defineAsyncComponent(() => + import("@/components/relays/tasks/TasksManager.vue" /* webpackChunkName: "TasksManager" */) ); @@ -44,7 +43,7 @@ export default defineComponent({ title: "nostr.watch registry & network status", name: 'RelaysNav', components: { - RefreshTask, + TasksManager, }, props: { resultsProp: { diff --git a/src/components/relays/tasks/HistoryTask.vue b/src/components/relays/tasks/HistoryTask.vue index 68c55ec..11cc686 100644 --- a/src/components/relays/tasks/HistoryTask.vue +++ b/src/components/relays/tasks/HistoryTask.vue @@ -265,18 +265,18 @@ export default defineComponent({ methods: Object.assign(RelaysLib, { invalidate: async function(){ - if( !this.store.tasks.isAnyProcessing && this.isExpired() ) { - this.store.tasks.startProcessing('history') + if( !this.store.tasks.isAnyProcessing && this.isExpired('relays/history') ) { + this.store.tasks.startProcessing('relays/history') this.store.stats.set( 'nips', this.collateSupportedNips ) - this.store.tasks.addProcessed('history', 'nips') + this.store.tasks.addProcessed('relays/history', 'nips') this.store.stats.set( 'continents', this.collateContinents ) - this.store.tasks.addProcessed('history', 'continents') + this.store.tasks.addProcessed('relays/history', 'continents') this.store.stats.set( 'countries', this.collateCountries ) - this.store.tasks.addProcessed( 'history', 'countries' ) + this.store.tasks.addProcessed( 'relays/history', 'countries' ) this.remoteTask = await this.historicalData() - this.store.tasks.addProcessed( 'history', 'firstSeen' ) + this.store.tasks.addProcessed( 'relays/history', 'firstSeen' ) // this.store.stats.setHistory(remoteTask) - this.store.tasks.finishProcessing( 'history' ) + this.store.tasks.finishProcessing( 'relays/history' ) } }, collateSoftware(){ diff --git a/src/components/relays/tasks/RefreshTask.vue b/src/components/relays/tasks/RefreshTask.vue index 7063606..4990982 100644 --- a/src/components/relays/tasks/RefreshTask.vue +++ b/src/components/relays/tasks/RefreshTask.vue @@ -63,10 +63,11 @@ const localMethods = { } }, - addToQueue: function(id, fn){ + addToQueue: function(id, fn, unique){ this.store.tasks.addJob({ id: id, - handler: fn.bind(this) + handler: fn, + unique: unique }) }, @@ -102,54 +103,60 @@ const localMethods = { }, invalidate: async function(force, single){ - console.log('expired', !this.store.tasks.getLastUpdate('relays'), Date.now() - this.store.tasks.getLastUpdate('relays') > this.store.prefs.expireAfter) - if( (!this.isExpired && !force) ) + // console.log('expired', !this.store.tasks.getLastUpdate('relays'), Date.now() - this.store.tasks.getLastUpdate('relays') > this.store.prefs.expireAfter) + if( (!this.isExpired('relays') && !force) ) return - console.log('windowActive', this.windowActive) + // console.log('windowActive', this.windowActive) if(!this.windowActive) return - this.store.tasks.startProcessing('relays') + this.addToQueue('relays', async () => { + const relays = this.relays.filter( relay => !this.store.tasks.isProcessed('relays', relay) ) + + console.log('unprocessed relays', + this.relays.filter( relay => !this.store.tasks.getProcessed('relays').includes(relay))) + + if(single) { + await this.check(single) + } + else { + for(let index = 0; index < relays.length; index++) { + await this.delay(this.averageLatency) + const relay = relays[index] + this.check(relay) + .then((result) => this.completeRelay(relay, result) ) + // .then( async () => { + // // this.history = await History() + // }) + .catch( () => this.completeRelay(relay) ) + } + } + }, true) + + console.log('queue', this.store.tasks.getActive) - const relays = this.relays.filter( relay => !this.store.tasks.isProcessed('relays', relay) ) - - console.log('unprocessed relays', - this.relays.filter( relay => !this.store.tasks.getProcessed('relays').includes(relay))) - - if(single) { - await this.check(single) - } - else { - for(let index = 0; index < relays.length; index++) { - await this.delay(this.averageLatency) - const relay = relays[index] - this.check(relay) - .then((result) => { - if(this.store.tasks.isProcessed('relays', relay)) - return - - this.store.tasks.addProcessed('relays', result.url) - - this.results[result.url] = result - this.setCache(result) - - if(this.store.tasks.getProcessed('relays').length >= this.relays.length) - this.completeAll() - - return this.results - }) - .then( async () => { - // this.history = await History() - }) - .catch( err => console.error(err) ) - } - } + }, + + completeRelay: function(relay, result){ + if(this.store.tasks.isProcessed('relays', relay)) + return + + this.store.tasks.addProcessed('relays', relay) + + if(result) { + this.results[relay] = result + this.setCache(result) + } + + if(this.store.tasks.getProcessed('relays').length >= this.relays.length) + this.completeAll() }, completeAll: function(){ //console.log('completed') - this.store.tasks.finishProcessing('relays') + this.store.tasks.completeJob() + // this.store.tasks.finishProcessing('relays') this.store.tasks.updateNow('relays') this.store.relays.setAggregateCache('public', Object.keys(this.results).filter( result => this.results[result].aggregate === 'public' )) this.store.relays.setAggregateCache('restricted', Object.keys(this.results).filter( result => this.results[result].aggregate === 'restricted' )) @@ -164,7 +171,7 @@ const localMethods = { checkLatency: true, getInfo: true, getIdentities: true, - // debug: true, + debug: true, connectTimeout: this.getDynamicTimeout, readTimeout: this.getDynamicTimeout, writeTimeout: this.getDynamicTimeout, @@ -184,6 +191,7 @@ const localMethods = { }) .on('close', () => { //console.log(`${relay.url} has closed`) + reject() }) .on('error', () => { reject() @@ -265,6 +273,9 @@ export default defineComponent({ }, mounted(){ this.migrateLegacy() + + console.log('is processing', this.store.tasks.isProcessing(`relays`)) + if(this.store.tasks.isProcessing(`relays`)) this.invalidate(true) else diff --git a/src/components/relays/tasks/RelayCanonicalsTask.vue b/src/components/relays/tasks/RelayCanonicalsTask.vue new file mode 100644 index 0000000..2383635 --- /dev/null +++ b/src/components/relays/tasks/RelayCanonicalsTask.vue @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/src/components/relays/tasks/TasksManager.vue b/src/components/relays/tasks/TasksManager.vue new file mode 100644 index 0000000..a57dbc9 --- /dev/null +++ b/src/components/relays/tasks/TasksManager.vue @@ -0,0 +1,96 @@ + + + \ No newline at end of file diff --git a/src/shared/computed.js b/src/shared/computed.js index cca2f96..5f37b00 100644 --- a/src/shared/computed.js +++ b/src/shared/computed.js @@ -1,5 +1,5 @@ export default { isExpired: function(){ - return !this.store.tasks.getLastUpdate('relays') || Date.now() - this.store.tasks.getLastUpdate('relays') > this.store.prefs.expireAfter + return (slug) => !this.store.tasks.getLastUpdate(slug) || Date.now() - this.store.tasks.getLastUpdate(slug) > this.store.prefs.expireAfter }, } \ No newline at end of file diff --git a/src/store/relays.js b/src/store/relays.js index d7c4c39..a0a2608 100644 --- a/src/store/relays.js +++ b/src/store/relays.js @@ -7,14 +7,15 @@ export const useRelaysStore = defineStore('relays', { urls: new Array(), // results: new Object(), geo: new Object(), - lastUpdate: {}, + lastUpdate: null, count: new Object(), processing: false, processedRelays: new Set(), favorites: new Array(), aggregates: {}, aggregatesAreSet: false, - cached: new Object() + cached: new Object(), + canonicals: new Object() }), getters: { getAll: (state) => state.urls, @@ -25,7 +26,7 @@ export const useRelaysStore = defineStore('relays', { return state.favorites return state.urls.filter( (relay) => results?.[relay]?.aggregate == aggregate) }, - getByAggregate: (state) => (aggregate) => { + getByAggregate: state => aggregate => { return state.urls .filter( (relay) => state.results?.[relay]?.aggregate == aggregate) }, @@ -33,20 +34,22 @@ export const useRelaysStore = defineStore('relays', { // getResults: (state) => state.results, // getResult: (state) => (relayUrl) => state.results?.[relayUrl], - getGeo: (state) => (relayUrl) => state.geo[relayUrl], + getGeo: state => relayUrl => state.geo[relayUrl], - getLastUpdate: (state) => state.lastUpdate, + getLastUpdate: state => state.lastUpdate, - getCount: (state) => (type) => state.count[type], - getCounts: (state) => state.count, + getCount: state => type => state.count[type], + getCounts: state => state.count, - getAggregate: (state) => (which) => state.aggregates[which], - areAggregatesSet: (state) => state.aggregatesAreSet, + getAggregate: state => which => state.aggregates[which], + areAggregatesSet: state => state.aggregatesAreSet, - getFavorites: (state) => state.favorites, - isFavorite: (state) => (relayUrl) => state.favorites.includes(relayUrl), + getFavorites: state => state.favorites, + isFavorite: state => relayUrl => state.favorites.includes(relayUrl), - getAggregateCache: (state) => (aggregate) => state.cached[aggregate] instanceof Array ? state.cached[aggregate] : [], + getAggregateCache: state => aggregate => state.cached[aggregate] instanceof Array ? state.cached[aggregate] : [], + + getCanonical: state => relay => state.canonicals[relay] }, actions: { addRelay(relayUrl){ this.urls.push(relayUrl) }, @@ -62,7 +65,7 @@ export const useRelaysStore = defineStore('relays', { setGeo(geo){ this.geo = geo }, - updateNow(key){ this.lastUpdate[key] = Date.now() }, + setStat(type, value){ this.count[type] = value diff --git a/src/store/tasks.js b/src/store/tasks.js index 1921fb7..5cd288a 100644 --- a/src/store/tasks.js +++ b/src/store/tasks.js @@ -4,15 +4,15 @@ export const useTaskStore = defineStore('tasks', { state: () => ({ lastUpdate: new Object(), + //processing cache + processing: new Object(), + processed: new Object(), + currentTask: new String(), + //queue pending: new Array(), completed: new Array(), active: new Object(), - - //legacy - processing: new Object(), - processed: new Object(), - currentTask: new Object(), }), getters: { getLastUpdate: (state) => (key) => state.lastUpdate[key], @@ -25,39 +25,54 @@ export const useTaskStore = defineStore('tasks', { }, isProcessing: (state) => (key) => state.processing[key], isProcessed: (state) => (key, relay) => state.getProcessed(key).includes(relay), - isAnyProcessing: (state) => Object.keys(state.processing).filter( key => state.processing[key] ), + isAnyProcessing: (state) => Object.keys(state.processing).filter( key => state.processing[key] ).length ? true : false, //queue/lists getPending: (state) => state.pending, getActive: (state) => state.active, getActiveSlug: (state) => state.active.id, getCompleted: (state) => state.completed, + //queue/states isActive: (state) => Object.keys( state.active ).length > 0, isIdle: (state) => Object.keys( state.active ).length == 0, arePending: (state) => state.pending.length > 0, - // - // getRate: (state) => (key) => state.rate[key], + }, actions: { updateNow(key){ this.lastUpdate[key] = Date.now() }, //queue addJob(job){ + if(job?.unique){ + let exists + exists = this.active.id === job.id + if(!exists) + exists = this.pending.filter( j => j.id === job.id).length ? true : false + if(exists) + return + } this.pending.push(job) if( this.isIdle ) this.startNextJob() }, startNextJob(){ - if( this.isActive ) - this.completed.push(this.active) + console.log('task, isactive?', this.isActive) if( this.arePending ) { this.active = this.pending[0] this.pending.shift() + this.startProcessing(this.active) } else { + console.log('completing active...') this.active = {} } }, + completeJob(){ + console.log('compelteJob', this.active.id, this.active) + this.finishProcessing(this.active.id) + this.completed.push(this.active) + this.startNextJob() + }, clearJobs(type){ this[type] = new Array() }, @@ -69,14 +84,15 @@ export const useTaskStore = defineStore('tasks', { this.pending.splice( index, 1 ) }, //legacy - startProcessing(key) { - this.processing[key] = true - this.currentTask[key] = key + startProcessing(job) { + this.addJob(job) + this.processing[job.id] = true + this.currentTask = job.id }, finishProcessing(key) { this.processed[key] = new Array() this.processing[key] = false - this.currentTask[key] = null + this.currentTask = null }, addProcessed(key, relay){ if( !(this.processed[key] instanceof Array) ) @@ -86,4 +102,16 @@ export const useTaskStore = defineStore('tasks', { }, }, + share: { + // An array of fields that the plugin will ignore. + omit: ['pending', 'completed', 'active'], + // Override global config for this store. + enable: true, + }, +}, +{ + persistedState: { + // includePaths: ['lastUpdate', 'processed', 'processing', 'currentTask'] + excludePaths: ['pending', 'completed', 'active'], + } }) \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 488101a..a4d4ba2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1302,7 +1302,7 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== -"@scure/base@~1.1.0": +"@scure/base@^1.1.1", "@scure/base@~1.1.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== @@ -2503,11 +2503,6 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-arraybuffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" - integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== - base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -2525,11 +2520,6 @@ batch@0.6.1: resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== -bech32@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355" - integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== - big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -3732,6 +3722,11 @@ dotenv-expand@^5.1.0: resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== +dotenv@16.0.3: + version "16.0.3" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" + integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== + dotenv@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" @@ -6410,17 +6405,16 @@ nostr-relay-inspector@0.0.23: tape "5.6.1" yaml-loader "0.8.0" -nostr-tools@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.0.1.tgz#acf21758fc745674ed047e7dc219b30baf489005" - integrity sha512-URBNadrVq4qSmndzd4clZqubze4y/LB8cdzzen9mNwlFh3ICDdWp7TCShTbLEZQYPPSVoOe2n13l77jzcVvH/w== +nostr-tools@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.1.1.tgz#2be4cd650bc0a4d20650b6cf46fee451c9f565b8" + integrity sha512-mxgjbHR6nx2ACBNa2tBpeM/glsPWqxHPT1Kszx/XfzL+kUdi1Gm3Xz1UcaODQ2F84IFtCKNLO+aF31ZfTAhSYQ== dependencies: "@noble/hashes" "^0.5.7" "@noble/secp256k1" "^1.7.0" + "@scure/base" "^1.1.1" "@scure/bip32" "^1.1.1" "@scure/bip39" "^1.1.0" - base64-arraybuffer "^1.0.2" - bech32 "^2.0.0" nostr@0.2.5: version "0.2.5"