mirror of
https://github.com/nbd-wtf/nostr-tools.git
synced 2025-12-09 16:48:50 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
498c1603b0 | ||
|
|
4cfc67e294 | ||
|
|
da51418f04 | ||
|
|
75df47421f | ||
|
|
1cfe705baf | ||
|
|
566437fe2e | ||
|
|
5d6c2b9e5d | ||
|
|
a43f2a708c |
17
README.md
17
README.md
@@ -66,18 +66,18 @@ const sub = relay.subscribe([
|
|||||||
let sk = generateSecretKey()
|
let sk = generateSecretKey()
|
||||||
let pk = getPublicKey(sk)
|
let pk = getPublicKey(sk)
|
||||||
|
|
||||||
let sub = relay.sub([
|
relay.sub([
|
||||||
{
|
{
|
||||||
kinds: [1],
|
kinds: [1],
|
||||||
authors: [pk],
|
authors: [pk],
|
||||||
},
|
},
|
||||||
])
|
], {
|
||||||
|
onevent(event) {
|
||||||
sub.on('event', event => {
|
|
||||||
console.log('got event:', event)
|
console.log('got event:', event)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let event = {
|
let eventTemplate = {
|
||||||
kind: 1,
|
kind: 1,
|
||||||
created_at: Math.floor(Date.now() / 1000),
|
created_at: Math.floor(Date.now() / 1000),
|
||||||
tags: [],
|
tags: [],
|
||||||
@@ -85,14 +85,9 @@ let event = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this assigns the pubkey, calculates the event id and signs the event in a single step
|
// this assigns the pubkey, calculates the event id and signs the event in a single step
|
||||||
const signedEvent = finalizeEvent(event, sk)
|
const signedEvent = finalizeEvent(eventTemplate, sk)
|
||||||
await relay.publish(signedEvent)
|
await relay.publish(signedEvent)
|
||||||
|
|
||||||
let events = await relay.list([{ kinds: [0, 1] }])
|
|
||||||
let event = await relay.get({
|
|
||||||
ids: ['44e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245'],
|
|
||||||
})
|
|
||||||
|
|
||||||
relay.close()
|
relay.close()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* global WebSocket */
|
/* global WebSocket */
|
||||||
|
|
||||||
import type { Event, EventTemplate, Nostr } from './core.ts'
|
import type { Event, EventTemplate, VerifiedEvent, Nostr } from './core.ts'
|
||||||
import { matchFilters, type Filter } from './filter.ts'
|
import { matchFilters, type Filter } from './filter.ts'
|
||||||
import { getHex64, getSubscriptionId } from './fakejson.ts'
|
import { getHex64, getSubscriptionId } from './fakejson.ts'
|
||||||
import { Queue, normalizeURL } from './utils.ts'
|
import { Queue, normalizeURL } from './utils.ts'
|
||||||
@@ -218,11 +218,14 @@ export class AbstractRelay {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async auth(signAuthEvent: (authEvent: EventTemplate) => Promise<void>) {
|
public async auth(signAuthEvent: (evt: EventTemplate) => Promise<VerifiedEvent>) {
|
||||||
if (!this.challenge) throw new Error("can't perform auth, no challenge was received")
|
if (!this.challenge) throw new Error("can't perform auth, no challenge was received")
|
||||||
const evt = makeAuthEvent(this.url, this.challenge)
|
const evt = await signAuthEvent(makeAuthEvent(this.url, this.challenge))
|
||||||
await signAuthEvent(evt)
|
const ret = new Promise<string>((resolve, reject) => {
|
||||||
|
this.openEventPublishes.set(evt.id, { resolve, reject })
|
||||||
|
})
|
||||||
this.send('["AUTH",' + JSON.stringify(evt) + ']')
|
this.send('["AUTH",' + JSON.stringify(evt) + ']')
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
public async publish(event: Event): Promise<string> {
|
public async publish(event: Event): Promise<string> {
|
||||||
|
|||||||
14
build.js
14
build.js
@@ -28,12 +28,7 @@ esbuild
|
|||||||
format: 'esm',
|
format: 'esm',
|
||||||
packages: 'external',
|
packages: 'external',
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => console.log('esm build success.'))
|
||||||
const packageJson = JSON.stringify({ type: 'module' })
|
|
||||||
fs.writeFileSync(`${__dirname}/lib/esm/package.json`, packageJson, 'utf8')
|
|
||||||
|
|
||||||
console.log('esm build success.')
|
|
||||||
})
|
|
||||||
|
|
||||||
esbuild
|
esbuild
|
||||||
.build({
|
.build({
|
||||||
@@ -42,7 +37,12 @@ esbuild
|
|||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
packages: 'external',
|
packages: 'external',
|
||||||
})
|
})
|
||||||
.then(() => console.log('cjs build success.'))
|
.then(() => {
|
||||||
|
const packageJson = JSON.stringify({ type: 'commonjs' })
|
||||||
|
fs.writeFileSync(`${__dirname}/lib/cjs/package.json`, packageJson, 'utf8')
|
||||||
|
|
||||||
|
console.log('cjs build success.')
|
||||||
|
})
|
||||||
|
|
||||||
esbuild
|
esbuild
|
||||||
.build({
|
.build({
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'
|
import { verifiedSymbol, type Event, type Nostr, VerifiedEvent } from './core.ts'
|
||||||
|
|
||||||
export async function yieldThread() {
|
export async function yieldThread() {
|
||||||
return new Promise(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
const ch = new MessageChannel()
|
const ch = new MessageChannel()
|
||||||
|
const handler = () => {
|
||||||
|
// @ts-ignore (typescript thinks this property should be called `removeListener`, but in fact it's `removeEventListener`)
|
||||||
|
ch.port1.removeEventListener('message', handler)
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
// @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)
|
// @ts-ignore (typescript thinks this property should be called `addListener`, but in fact it's `addEventListener`)
|
||||||
ch.port1.addEventListener('message', resolve)
|
ch.port1.addEventListener('message', handler)
|
||||||
ch.port2.postMessage(0)
|
ch.port2.postMessage(0)
|
||||||
ch.port1.start()
|
ch.port1.start()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -78,13 +78,13 @@ test('encode and decode naddr', () => {
|
|||||||
test('encode and decode nevent', () => {
|
test('encode and decode nevent', () => {
|
||||||
let pk = getPublicKey(generateSecretKey())
|
let pk = getPublicKey(generateSecretKey())
|
||||||
let relays = ['wss://relay.nostr.example.mydomain.example.com', 'wss://nostr.banana.com']
|
let relays = ['wss://relay.nostr.example.mydomain.example.com', 'wss://nostr.banana.com']
|
||||||
let naddr = neventEncode({
|
let nevent = neventEncode({
|
||||||
id: pk,
|
id: pk,
|
||||||
relays,
|
relays,
|
||||||
kind: 30023,
|
kind: 30023,
|
||||||
})
|
})
|
||||||
expect(naddr).toMatch(/nevent1\w+/)
|
expect(nevent).toMatch(/nevent1\w+/)
|
||||||
let { type, data } = decode(naddr)
|
let { type, data } = decode(nevent)
|
||||||
expect(type).toEqual('nevent')
|
expect(type).toEqual('nevent')
|
||||||
const pointer = data as EventPointer
|
const pointer = data as EventPointer
|
||||||
expect(pointer.id).toEqual(pk)
|
expect(pointer.id).toEqual(pk)
|
||||||
@@ -95,13 +95,13 @@ test('encode and decode nevent', () => {
|
|||||||
test('encode and decode nevent with kind 0', () => {
|
test('encode and decode nevent with kind 0', () => {
|
||||||
let pk = getPublicKey(generateSecretKey())
|
let pk = getPublicKey(generateSecretKey())
|
||||||
let relays = ['wss://relay.nostr.example.mydomain.example.com', 'wss://nostr.banana.com']
|
let relays = ['wss://relay.nostr.example.mydomain.example.com', 'wss://nostr.banana.com']
|
||||||
let naddr = neventEncode({
|
let nevent = neventEncode({
|
||||||
id: pk,
|
id: pk,
|
||||||
relays,
|
relays,
|
||||||
kind: 0,
|
kind: 0,
|
||||||
})
|
})
|
||||||
expect(naddr).toMatch(/nevent1\w+/)
|
expect(nevent).toMatch(/nevent1\w+/)
|
||||||
let { type, data } = decode(naddr)
|
let { type, data } = decode(nevent)
|
||||||
expect(type).toEqual('nevent')
|
expect(type).toEqual('nevent')
|
||||||
const pointer = data as EventPointer
|
const pointer = data as EventPointer
|
||||||
expect(pointer.id).toEqual(pk)
|
expect(pointer.id).toEqual(pk)
|
||||||
@@ -109,6 +109,25 @@ test('encode and decode nevent with kind 0', () => {
|
|||||||
expect(pointer.kind).toEqual(0)
|
expect(pointer.kind).toEqual(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('encode and decode naddr with empty "d"', () => {
|
||||||
|
let pk = getPublicKey(generateSecretKey())
|
||||||
|
let relays = ['wss://relay.nostr.example.mydomain.example.com', 'wss://nostr.banana.com']
|
||||||
|
let naddr = naddrEncode({
|
||||||
|
identifier: '',
|
||||||
|
pubkey: pk,
|
||||||
|
relays,
|
||||||
|
kind: 3,
|
||||||
|
})
|
||||||
|
expect(naddr).toMatch(/naddr\w+/)
|
||||||
|
let { type, data } = decode(naddr)
|
||||||
|
expect(type).toEqual('naddr')
|
||||||
|
const pointer = data as AddressPointer
|
||||||
|
expect(pointer.identifier).toEqual('')
|
||||||
|
expect(pointer.relays).toContain(relays[0])
|
||||||
|
expect(pointer.kind).toEqual(3)
|
||||||
|
expect(pointer.pubkey).toEqual(pk)
|
||||||
|
})
|
||||||
|
|
||||||
test('decode naddr from habla.news', () => {
|
test('decode naddr from habla.news', () => {
|
||||||
let { type, data } = decode(
|
let { type, data } = decode(
|
||||||
'naddr1qq98yetxv4ex2mnrv4esygrl54h466tz4v0re4pyuavvxqptsejl0vxcmnhfl60z3rth2xkpjspsgqqqw4rsf34vl5',
|
'naddr1qq98yetxv4ex2mnrv4esygrl54h466tz4v0re4pyuavvxqptsejl0vxcmnhfl60z3rth2xkpjspsgqqqw4rsf34vl5',
|
||||||
|
|||||||
5
nip19.ts
5
nip19.ts
@@ -149,7 +149,6 @@ function parseTLV(data: Uint8Array): TLV {
|
|||||||
while (rest.length > 0) {
|
while (rest.length > 0) {
|
||||||
let t = rest[0]
|
let t = rest[0]
|
||||||
let l = rest[1]
|
let l = rest[1]
|
||||||
if (!l) throw new Error(`malformed TLV ${t}`)
|
|
||||||
let v = rest.slice(2, 2 + l)
|
let v = rest.slice(2, 2 + l)
|
||||||
rest = rest.slice(2 + l)
|
rest = rest.slice(2 + l)
|
||||||
if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)
|
if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)
|
||||||
@@ -227,7 +226,9 @@ export function nrelayEncode(url: string): `nrelay1${string}` {
|
|||||||
function encodeTLV(tlv: TLV): Uint8Array {
|
function encodeTLV(tlv: TLV): Uint8Array {
|
||||||
let entries: Uint8Array[] = []
|
let entries: Uint8Array[] = []
|
||||||
|
|
||||||
Object.entries(tlv).forEach(([t, vs]) => {
|
Object.entries(tlv)
|
||||||
|
.reverse()
|
||||||
|
.forEach(([t, vs]) => {
|
||||||
vs.forEach(v => {
|
vs.forEach(v => {
|
||||||
let entry = new Uint8Array(v.length + 2)
|
let entry = new Uint8Array(v.length + 2)
|
||||||
entry.set([parseInt(t)], 0)
|
entry.set([parseInt(t)], 0)
|
||||||
|
|||||||
@@ -242,10 +242,11 @@ describe('validateZapRequest', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('makeZapReceipt', () => {
|
describe('makeZapReceipt', () => {
|
||||||
test('returns a valid Zap receipt with a preimage', () => {
|
|
||||||
const privateKey = generateSecretKey()
|
const privateKey = generateSecretKey()
|
||||||
const publicKey = getPublicKey(privateKey)
|
const publicKey = getPublicKey(privateKey)
|
||||||
|
const target = 'efeb5d6e74ce6ffea6cae4094a9f29c26b5c56d7b44fae9f490f3410fd708c45'
|
||||||
|
|
||||||
|
test('returns a valid Zap receipt with a preimage', () => {
|
||||||
const zapRequest = JSON.stringify(
|
const zapRequest = JSON.stringify(
|
||||||
finalizeEvent(
|
finalizeEvent(
|
||||||
{
|
{
|
||||||
@@ -253,7 +254,7 @@ describe('makeZapReceipt', () => {
|
|||||||
created_at: Date.now() / 1000,
|
created_at: Date.now() / 1000,
|
||||||
content: 'content',
|
content: 'content',
|
||||||
tags: [
|
tags: [
|
||||||
['p', publicKey],
|
['p', target],
|
||||||
['amount', '100'],
|
['amount', '100'],
|
||||||
['relays', 'relay1', 'relay2'],
|
['relays', 'relay1', 'relay2'],
|
||||||
],
|
],
|
||||||
@@ -274,16 +275,14 @@ describe('makeZapReceipt', () => {
|
|||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
['bolt11', bolt11],
|
['bolt11', bolt11],
|
||||||
['description', zapRequest],
|
['description', zapRequest],
|
||||||
['p', publicKey],
|
['p', target],
|
||||||
|
['P', publicKey],
|
||||||
['preimage', preimage],
|
['preimage', preimage],
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('returns a valid Zap receipt without a preimage', () => {
|
test('returns a valid Zap receipt without a preimage', () => {
|
||||||
const privateKey = generateSecretKey()
|
|
||||||
const publicKey = getPublicKey(privateKey)
|
|
||||||
|
|
||||||
const zapRequest = JSON.stringify(
|
const zapRequest = JSON.stringify(
|
||||||
finalizeEvent(
|
finalizeEvent(
|
||||||
{
|
{
|
||||||
@@ -291,7 +290,7 @@ describe('makeZapReceipt', () => {
|
|||||||
created_at: Date.now() / 1000,
|
created_at: Date.now() / 1000,
|
||||||
content: 'content',
|
content: 'content',
|
||||||
tags: [
|
tags: [
|
||||||
['p', publicKey],
|
['p', target],
|
||||||
['amount', '100'],
|
['amount', '100'],
|
||||||
['relays', 'relay1', 'relay2'],
|
['relays', 'relay1', 'relay2'],
|
||||||
],
|
],
|
||||||
@@ -311,7 +310,8 @@ describe('makeZapReceipt', () => {
|
|||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
['bolt11', bolt11],
|
['bolt11', bolt11],
|
||||||
['description', zapRequest],
|
['description', zapRequest],
|
||||||
['p', publicKey],
|
['p', target],
|
||||||
|
['P', publicKey],
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
expect(JSON.stringify(result.tags)).not.toContain('preimage')
|
expect(JSON.stringify(result.tags)).not.toContain('preimage')
|
||||||
|
|||||||
2
nip57.ts
2
nip57.ts
@@ -119,7 +119,7 @@ export function makeZapReceipt({
|
|||||||
kind: 9735,
|
kind: 9735,
|
||||||
created_at: Math.round(paidAt.getTime() / 1000),
|
created_at: Math.round(paidAt.getTime() / 1000),
|
||||||
content: '',
|
content: '',
|
||||||
tags: [...tagsFromZapRequest, ['bolt11', bolt11], ['description', zapRequest]],
|
tags: [...tagsFromZapRequest, ['P', zr.pubkey], ['bolt11', bolt11], ['description', zapRequest]],
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preimage) {
|
if (preimage) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"name": "nostr-tools",
|
"name": "nostr-tools",
|
||||||
"version": "2.1.0",
|
"version": "2.1.2",
|
||||||
"description": "Tools for making a Nostr client.",
|
"description": "Tools for making a Nostr client.",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
Reference in New Issue
Block a user