Compare commits

..

1 Commits

Author SHA1 Message Date
fiatjaf
d46c5f947c fix tests, .seenOn() method for pools. 2023-02-09 15:11:09 -03:00
6 changed files with 71 additions and 163 deletions

View File

@@ -104,6 +104,9 @@ let pub = relay.publish(event)
pub.on('ok', () => {
console.log(`${relay.url} has accepted our event`)
})
pub.on('seen', () => {
console.log(`we saw the event on ${relay.url}`)
})
pub.on('failed', reason => {
console.log(`failed to publish to ${relay.url}: ${reason}`)
})
@@ -125,7 +128,7 @@ import 'websocket-polyfill'
### Interacting with multiple relays
```js
import {SimplePool} from 'nostr-tools'
import {pool} from 'nostr-tools'
const pool = new SimplePool()
@@ -290,11 +293,6 @@ Please consult the tests or [the source code](https://github.com/fiatjaf/nostr-t
</script>
```
## Plumbing
1. Install [`just`](https://just.systems/)
2. `just -l`
## License
Public domain.

View File

@@ -16,31 +16,23 @@ export enum Kind {
ChannelMetadata = 41,
ChannelMessage = 42,
ChannelHideMessage = 43,
ChannelMuteUser = 44,
Report = 1984,
ZapRequest = 9734,
Zap = 9735,
RelayList = 10002,
ClientAuth = 22242,
Article = 30023
ChannelMuteUser = 44
}
export type EventTemplate = {
export type Event = {
id?: string
sig?: string
kind: Kind
tags: string[][]
pubkey: string
content: string
created_at: number
}
export type Event = EventTemplate & {
pubkey: string
id: string
sig: string
}
export function getBlankEvent(): EventTemplate {
export function getBlankEvent(): Event {
return {
kind: 255,
pubkey: '',
content: '',
tags: [],
created_at: 0
@@ -67,7 +59,6 @@ export function getEventHash(event: Event): string {
}
export function validateEvent(event: Event): boolean {
if (typeof event !== 'object') 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,7 +9,6 @@ export * as nip05 from './nip05'
export * as nip06 from './nip06'
export * as nip19 from './nip19'
export * as nip26 from './nip26'
export * as nip57 from './nip57'
export * as fj from './fakejson'
export * as utils from './utils'

107
nip57.ts
View File

@@ -1,107 +0,0 @@
import {bech32} from '@scure/base'
import {Event, EventTemplate} from './event'
import {utf8Decoder} from './utils'
var _fetch: any
try {
_fetch = fetch
} catch {}
export function useFetchImplementation(fetchImplementation: any) {
_fetch = fetchImplementation
}
export async function getZapEndpoint(metadata: Event): Promise<null | string> {
try {
let lnurl: string = ''
let {lud06, lud16} = JSON.parse(metadata.content)
if (lud06) {
let {words} = bech32.decode(lud06, 1000)
let data = bech32.fromWords(words)
lnurl = utf8Decoder.decode(data)
} else if (lud16) {
let [name, domain] = lud16.split('@')
lnurl = `https://${domain}/.well-known/lnurlp/${name}`
} else {
return null
}
let res = await _fetch(lnurl)
let body = await res.json()
if (body.allowsNostr && body.nostrPubkey) {
return body.callback
}
} catch (err) {
/*-*/
}
return null
}
export function makeZapRequest({
profile,
event,
amount,
relays,
comment = ''
}: {
profile: string
event: string | null
amount: string
comment: string
relays: string[]
}): EventTemplate {
let zr = {
kind: 9734,
created_at: Math.round(Date.now() / 1000),
content: comment,
tags: [
['p', profile],
['amount', amount],
['relays', ...relays]
]
}
if (event) {
zr.tags.push(['e', event])
}
return zr
}
export function makeZapReceipt({
zapRequest,
preimage,
bolt11,
paidAt
}: {
zapRequest: string
preimage: string | null
bolt11: string
paidAt: Date
}): EventTemplate {
let zr: Event = JSON.parse(zapRequest)
let tagsFromZapRequest = zr.tags.filter(
([t]) => t === 'e' || t === 'p' || t === 'a'
)
let zap = {
kind: 9735,
created_at: Math.round(paidAt.getTime() / 1000),
content: '',
tags: [
...tagsFromZapRequest,
['bolt11', bolt11],
['description', zapRequest]
]
}
if (preimage) {
zap.tags.push(['preimage', preimage])
}
return zap
}

View File

@@ -1,6 +1,6 @@
{
"name": "nostr-tools",
"version": "1.4.2",
"version": "1.3.2",
"description": "Tools for making a Nostr client.",
"repository": {
"type": "git",
@@ -9,12 +9,11 @@
"main": "lib/nostr.cjs.js",
"module": "lib/nostr.esm.js",
"dependencies": {
"@noble/hashes": "1.0.0",
"@noble/secp256k1": "^1.7.1",
"@noble/hashes": "^0.5.7",
"@noble/secp256k1": "^1.7.0",
"@scure/base": "^1.1.1",
"@scure/bip32": "^1.1.5",
"@scure/bip39": "^1.1.1",
"prettier": "^2.8.4"
"@scure/bip32": "^1.1.1",
"@scure/bip39": "^1.1.0"
},
"keywords": [
"decentralization",
@@ -24,20 +23,20 @@
"nostr"
],
"devDependencies": {
"@types/node": "^18.13.0",
"@typescript-eslint/eslint-plugin": "^5.51.0",
"@typescript-eslint/parser": "^5.51.0",
"@types/node": "^18.0.3",
"@typescript-eslint/eslint-plugin": "^5.46.1",
"@typescript-eslint/parser": "^5.46.1",
"esbuild": "0.16.9",
"esbuild-plugin-alias": "^0.2.1",
"eslint": "^8.33.0",
"eslint": "^8.30.0",
"eslint-plugin-babel": "^5.3.1",
"esm-loader-typescript": "^1.0.3",
"esm-loader-typescript": "^1.0.1",
"events": "^3.3.0",
"jest": "^29.4.2",
"node-fetch": "^2.6.9",
"ts-jest": "^29.0.5",
"jest": "^29.3.1",
"node-fetch": "2",
"ts-jest": "^29.0.3",
"tsd": "^0.22.0",
"typescript": "^4.9.5",
"typescript": "^4.9.4",
"websocket-polyfill": "^0.0.3"
}
}

View File

@@ -19,8 +19,8 @@ export type Relay = {
off: (type: RelayEvent, cb: any) => void
}
export type Pub = {
on: (type: 'ok' | 'failed', cb: any) => void
off: (type: 'ok' | 'failed', cb: any) => void
on: (type: 'ok' | 'seen' | 'failed', cb: any) => void
off: (type: 'ok' | 'seen' | 'failed', cb: any) => void
}
export type Sub = {
sub: (filters: Filter[], opts: SubscriptionOptions) => Sub
@@ -38,6 +38,10 @@ export type SubscriptionOptions = {
export function relayInit(url: string): Relay {
var ws: WebSocket
var resolveClose: () => void
var setOpen: (value: PromiseLike<void> | void) => void
var untilOpen = new Promise<void>(resolve => {
setOpen = resolve
})
var openSubs: {[id: string]: {filters: Filter[]} & SubscriptionOptions} = {}
var listeners: {
connect: Array<() => void>
@@ -70,6 +74,7 @@ export function relayInit(url: string): Relay {
ws.onopen = () => {
listeners.connect.forEach(cb => cb())
setOpen()
resolve()
}
ws.onerror = () => {
@@ -135,22 +140,15 @@ export function relayInit(url: string): Relay {
return
case 'EOSE': {
let id = data[1]
if (id in subListeners) {
subListeners[id].eose.forEach(cb => cb())
subListeners[id].eose = [] // 'eose' only happens once per sub, so stop listeners here
}
;(subListeners[id]?.eose || []).forEach(cb => cb())
return
}
case 'OK': {
let id: string = data[1]
let ok: boolean = data[2]
let reason: string = data[3] || ''
if (id in pubListeners) {
if (ok) pubListeners[id].ok.forEach(cb => cb())
else pubListeners[id].failed.forEach(cb => cb(reason))
pubListeners[id].ok = [] // 'ok' only happens once per pub, so stop listeners here
pubListeners[id].failed = []
}
if (ok) pubListeners[id]?.ok.forEach(cb => cb())
else pubListeners[id]?.failed.forEach(cb => cb(reason))
return
}
case 'NOTICE':
@@ -173,6 +171,7 @@ export function relayInit(url: string): Relay {
async function trySend(params: [string, ...any]) {
let msg = JSON.stringify(params)
await untilOpen
try {
ws.send(msg)
} catch (err) {
@@ -272,17 +271,50 @@ export function relayInit(url: string): Relay {
if (!event.id) throw new Error(`event ${event} has no id`)
let id = event.id
var sent = false
var mustMonitor = false
trySend(['EVENT', event])
.then(() => {
sent = true
if (mustMonitor) {
startMonitoring()
mustMonitor = false
}
})
.catch(() => {})
const startMonitoring = () => {
let monitor = sub([{ids: [id]}], {
id: `monitor-${id.slice(0, 5)}`
})
let willUnsub = setTimeout(() => {
;(pubListeners[id]?.failed || []).forEach(cb =>
cb('event not seen after 5 seconds')
)
monitor.unsub()
}, 5000)
monitor.on('event', () => {
clearTimeout(willUnsub)
;(pubListeners[id]?.seen || []).forEach(cb => cb())
})
}
return {
on: (type: 'ok' | 'failed', cb: any) => {
on: (type: 'ok' | 'seen' | 'failed', cb: any) => {
pubListeners[id] = pubListeners[id] || {
ok: [],
seen: [],
failed: []
}
pubListeners[id][type].push(cb)
if (type === 'seen') {
if (sent) startMonitoring()
else mustMonitor = true
}
},
off: (type: 'ok' | 'failed', cb: any) => {
off: (type: 'ok' | 'seen' | 'failed', cb: any) => {
let listeners = pubListeners[id]
if (!listeners) return
let idx = listeners[type].indexOf(cb)
@@ -292,10 +324,6 @@ export function relayInit(url: string): Relay {
},
connect,
close(): Promise<void> {
listeners = {connect: [], disconnect: [], error: [], notice: []}
subListeners = {}
pubListeners = {}
if (ws.readyState > 1) return Promise.resolve()
ws.close()
return new Promise<void>(resolve => {