Compare commits

..

12 Commits

Author SHA1 Message Date
fiatjaf
086f8830e3 catch fetch error on nip05. 2023-03-02 08:21:17 -03:00
Egge
e48d722227 Fixed readme for publishing with pool 2023-03-01 15:29:24 -03:00
Fernando López Guevara
0d77013aab chore(dx): add format script 💅 (#128) 2023-02-28 12:19:10 -03:00
BilligsterUser
4c415280aa ci emit types on publish 2023-02-27 22:17:52 -03:00
fiatjaf
4188aaf7c8 just type-check 2023-02-27 19:53:12 -03:00
BilligsterUser
673f4abab8 add type definition
fixes #138
2023-02-27 19:51:19 -03:00
BilligsterUser
bcefaa0757 update repo url 2023-02-27 19:49:00 -03:00
fiatjaf
649af36a86 one more nip19 test. 2023-02-27 16:10:26 -03:00
Simon
96a6f7af87 ensure kind has type 'number' in validateEvent 2023-02-27 14:22:36 -03:00
fiatjaf
a4c713efcb fix readme example.
fixes https://github.com/nbd-wtf/nostr-tools/issues/136
2023-02-27 12:43:24 -03:00
fiatjaf
9d345a8f01 configurable list and get timeout on relay. 2023-02-26 21:23:09 -03:00
fiatjaf
c362212778 make pool.publish() return a single Pub object. 2023-02-26 17:44:51 -03:00
13 changed files with 98 additions and 31 deletions

View File

@@ -16,6 +16,7 @@ jobs:
- run: just install-dependencies
- run: just build
- run: just test
- run: just emit-types
- uses: JS-DevTools/npm-publish@v1
with:
token: ${{ secrets.NPM_TOKEN }}

View File

@@ -10,7 +10,6 @@ Only depends on _@scure_ and _@noble_ packages.
npm install nostr-tools # or yarn add nostr-tools
```
## Usage
### Generating a private key and a public key
@@ -138,11 +137,16 @@ const pool = new SimplePool()
let relays = ['wss://relay.example.com', 'wss://relay.example2.com']
let relay = await pool.ensureRelay('wss://relay.example3.com')
let sub = pool.sub([...relays, relay], [{
authors: ['32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245']
}])
let sub = pool.sub(
[...relays, 'wss://relay.example3.com'],
[
{
authors: [
'32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245'
]
}
]
)
sub.on('event', event => {
// this will only be called once the first time the event is received
@@ -150,11 +154,10 @@ sub.on('event', event => {
})
let pubs = pool.publish(relays, newEvent)
pubs.forEach(pub =>
pub.on('ok', () => {
// ...
})
)
pubs.on('ok', () => {
// this may be called multiple times, once for every relay that accepts the event
// ...
})
let events = await pool.list(relays, [{kinds: [0, 1]}])
let event = await pool.get(relays, {

View File

@@ -17,7 +17,7 @@ esbuild
packages: 'external'
})
.then(() => {
const packageJson = JSON.stringify({ type: 'module' })
const packageJson = JSON.stringify({type: 'module'})
fs.writeFileSync(`${__dirname}/lib/esm/package.json`, packageJson, 'utf8')
console.log('esm build success.')

View File

@@ -80,6 +80,7 @@ export function getEventHash(event: UnsignedEvent): string {
export function validateEvent(event: UnsignedEvent): boolean {
if (typeof event !== 'object') return false
if (typeof event.kind !== 'number') return false
if (typeof event.content !== 'string') return false
if (typeof event.created_at !== 'number') return false
if (typeof event.pubkey !== 'string') return false

View File

@@ -9,8 +9,15 @@ build:
test: build
jest
testOnly file: build
test-only file: build
jest {{file}}
emit-types:
tsc
publish: build
emit-types
npm publish
format:
prettier --plugin-search-dir . --write .

View File

@@ -38,9 +38,14 @@ export async function queryProfile(
if (!name.match(/^[A-Za-z0-9-_]+$/)) return null
let res = await (
await _fetch(`https://${domain}/.well-known/nostr.json?name=${name}`)
).json()
let res
try {
res = await (
await _fetch(`https://${domain}/.well-known/nostr.json?name=${name}`)
).json()
} catch (err) {
return null
}
if (!res?.names?.[name]) return null

View File

@@ -4,12 +4,16 @@ const {nip06} = require('./lib/nostr.cjs')
test('generate private key from a mnemonic', async () => {
const mnemonic = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong'
const privateKey = nip06.privateKeyFromSeedWords(mnemonic)
expect(privateKey).toEqual('c26cf31d8ba425b555ca27d00ca71b5008004f2f662470f8c8131822ec129fe2')
expect(privateKey).toEqual(
'c26cf31d8ba425b555ca27d00ca71b5008004f2f662470f8c8131822ec129fe2'
)
})
test('generate private key from a mnemonic and passphrase', async () => {
const mnemonic = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong'
const passphrase = '123'
const privateKey = nip06.privateKeyFromSeedWords(mnemonic, passphrase)
expect(privateKey).toEqual('55a22b8203273d0aaf24c22c8fbe99608e70c524b17265641074281c8b978ae4')
expect(privateKey).toEqual(
'55a22b8203273d0aaf24c22c8fbe99608e70c524b17265641074281c8b978ae4'
)
})

View File

@@ -7,7 +7,10 @@ import {
} from '@scure/bip39'
import {HDKey} from '@scure/bip32'
export function privateKeyFromSeedWords(mnemonic: string, passphrase?: string): string {
export function privateKeyFromSeedWords(
mnemonic: string,
passphrase?: string
): string {
let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))
let privateKey = root.derive(`m/44'/1237'/0'/0/0`).privateKey
if (!privateKey) throw new Error('could not derive private key')

View File

@@ -48,6 +48,7 @@ test('encode and decode naddr', () => {
identifier: 'banana'
})
expect(naddr).toMatch(/naddr1\w+/)
console.log(naddr)
let {type, data} = nip19.decode(naddr)
expect(type).toEqual('naddr')
expect(data.pubkey).toEqual(pk)
@@ -57,7 +58,7 @@ test('encode and decode naddr', () => {
expect(data.identifier).toEqual('banana')
})
test('encode and decode naddr from habla.news', () => {
test('decode naddr from habla.news', () => {
let {type, data} = nip19.decode(
'naddr1qq98yetxv4ex2mnrv4esygrl54h466tz4v0re4pyuavvxqptsejl0vxcmnhfl60z3rth2xkpjspsgqqqw4rsf34vl5'
)
@@ -68,3 +69,20 @@ test('encode and decode naddr from habla.news', () => {
expect(data.kind).toEqual(30023)
expect(data.identifier).toEqual('references')
})
test('decode naddr from go-nostr with different TLV ordering', () => {
let {type, data} = nip19.decode(
'naddr1qqrxyctwv9hxzq3q80cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsxpqqqp65wqfwwaehxw309aex2mrp0yhxummnw3ezuetcv9khqmr99ekhjer0d4skjm3wv4uxzmtsd3jjucm0d5q3vamnwvaz7tmwdaehgu3wvfskuctwvyhxxmmd0zfmwx'
)
expect(type).toEqual('naddr')
expect(data.pubkey).toEqual(
'3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d'
)
expect(data.relays).toContain(
'wss://relay.nostr.example.mydomain.example.com'
)
expect(data.relays).toContain('wss://nostr.banana.com')
expect(data.kind).toEqual(30023)
expect(data.identifier).toEqual('banana')
})

View File

@@ -1,11 +1,15 @@
{
"name": "nostr-tools",
"version": "1.6.3",
"version": "1.7.2",
"description": "Tools for making a Nostr client.",
"repository": {
"type": "git",
"url": "https://github.com/fiatjaf/nostr-tools.git"
"url": "https://github.com/nbd-wtf/nostr-tools.git"
},
"files": [
"./lib/**/*"
],
"types": "./lib/index.d.ts",
"main": "lib/nostr.cjs.js",
"module": "lib/esm/nostr.mjs",
"exports": {

23
pool.ts
View File

@@ -29,7 +29,10 @@ export class SimplePool {
const existing = this._conn[nm]
if (existing) return existing
const relay = relayInit(nm)
const relay = relayInit(nm, {
getTimeout: this.getTimeout * 0.9,
listTimeout: this.getTimeout * 0.9
})
this._conn[nm] = relay
await relay.connect()
@@ -155,13 +158,23 @@ export class SimplePool {
})
}
publish(relays: string[], event: Event): Pub[] {
return relays.map(relay => {
publish(relays: string[], event: Event): Pub {
let pubs = relays.map(relay => {
let r = this._conn[normalizeURL(relay)]
if (!r) return badPub(relay)
let s = r.publish(event)
return s
return r.publish(event)
})
return {
on(type, cb) {
pubs.forEach((pub, i) => {
pub.on(type, () => cb(relays[i]))
})
},
off() {
// do nothing here, FIXME
}
}
}
seenOn(id: string): string[] {

View File

@@ -35,7 +35,15 @@ export type SubscriptionOptions = {
alreadyHaveEvent?: null | ((id: string, relay: string) => boolean)
}
export function relayInit(url: string): Relay {
export function relayInit(
url: string,
options: {
getTimeout?: number
listTimeout?: number
} = {}
): Relay {
let {listTimeout = 3000, getTimeout = 3000} = options
var ws: WebSocket
var openSubs: {[id: string]: {filters: Filter[]} & SubscriptionOptions} = {}
var listeners: {
@@ -252,7 +260,7 @@ export function relayInit(url: string): Relay {
let timeout = setTimeout(() => {
s.unsub()
resolve(events)
}, 1500)
}, listTimeout)
s.on('eose', () => {
s.unsub()
clearTimeout(timeout)
@@ -268,7 +276,7 @@ export function relayInit(url: string): Relay {
let timeout = setTimeout(() => {
s.unsub()
resolve(null)
}, 1500)
}, getTimeout)
s.on('event', (event: Event) => {
s.unsub()
clearTimeout(timeout)

View File

@@ -9,7 +9,7 @@
"skipLibCheck": true,
"esModuleInterop": true,
"emitDeclarationOnly": true,
"outDir": "dist",
"outDir": "lib",
"rootDir": "."
}
}